[
  {
    "path": ".editorconfig",
    "content": "# EditorConfig is awesome: https://EditorConfig.org\nroot = true\n\n[*]\nend_of_line = lf\ninsert_final_newline = true\n\nindent_style = tab\ntab_width = 4\n\ncharset = utf-8\ntrim_trailing_whitespace = true\n\n[*.java]\nij_java_continuation_indent_size = 8\nij_java_use_single_class_imports = true\nij_java_class_count_to_use_import_on_demand = 99\nij_java_names_count_to_use_import_on_demand = 99\nij_java_packages_to_use_import_on_demand = *\n\n[*.kt]\nij_kotlin_continuation_indent_size = 8\nij_kotlin_name_count_to_use_star_import = 99\nij_kotlin_name_count_to_use_star_import_for_members = 99\nij_kotlin_packages_to_use_import_on_demand = *\n\n[*.yml]\nindent_style = space\nindent_size = 2\n\n[*.bat]\nend_of_line = crlf\n"
  },
  {
    "path": ".gitattributes",
    "content": "*       text=auto eol=lf\n\n*.java  text eol=lf diff=java\n*.kt    text eol=lf diff=kotlin\n*.kts   text eol=lf diff=kotlin\n\ngradlew text eol=lf\n\n*.bat   text eol=crlf\n\n*.png   binary\n*.jar   binary\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/decompilation-issue.yml",
    "content": "name: Decompilation issue\ndescription: Create a report to help us improve jadx decompiler\ntitle: '[core] '\nlabels:\n  - Core\n  - bug\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        **Checks before submit**\n        - check [Troubleshooting Q&A](https://github.com/skylot/jadx/wiki/Troubleshooting-Q&A) section on wiki\n        - try [latest unstable build](https://nightly.link/skylot/jadx/workflows/build-artifacts/master), maybe issue already fixed\n        - search existing issues by exception message\n  - type: textarea\n    id: details\n    attributes:\n      label: Issue details\n      placeholder: >-\n        Describe issue\n    validations:\n      required: true\n  - type: textarea\n    id: logs\n    attributes:\n      label: Relevant log output or stacktrace\n      description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.\n      render: java\n  - type: textarea\n    id: sample\n    attributes:\n      label: Provide sample and class/method full name\n      description: |\n        - sample: attach or provide a link\n        - full name of class or method with issue\n        - other details which may help to reproduce issue\n  - type: input\n    id: jadx-version\n    attributes:\n      label: Jadx version\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.yml",
    "content": "name: Feature Request\ndescription: Suggest an idea for jadx\ntitle: '[feature] '\nlabels:\n  - 'new feature'\nbody:\n  - type: textarea\n    id: details\n    attributes:\n      label: Describe your idea\n      placeholder: Feature details\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/jadx-gui-issue.yml",
    "content": "name: jadx-gui issue\ndescription: Create a bug report about issue found in jadx-gui\ntitle: '[gui] '\nlabels:\n  - GUI\n  - bug\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        **Checks before submit**\n        - check [Troubleshooting Q&A](https://github.com/skylot/jadx/wiki/Troubleshooting-Q&A) section on wiki\n        - try [latest unstable build](https://nightly.link/skylot/jadx/workflows/build-artifacts/master), maybe issue already fixed\n        - search existing issues by exception message\n  - type: textarea\n    id: details\n    attributes:\n      label: Issue details\n      placeholder: Describe issue and how to reproduce it\n    validations:\n      required: true\n  - type: input\n    id: jadx-version\n    attributes:\n      label: Jadx version\n      placeholder: check `Help->About`\n    validations:\n      required: true\n  - type: input\n    id: java-version\n    attributes:\n      label: Java version\n      placeholder: check `Help->About`\n    validations:\n      required: true\n  - type: checkboxes\n    id: os\n    attributes:\n      label: OS\n      options:\n        - label: Windows\n        - label: Linux\n        - label: macOS\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  # Set update schedule for GitHub Actions\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": ":exclamation: Please review the [guidelines for contributing](https://github.com/skylot/jadx/blob/master/CONTRIBUTING.md#Pull-Request-Process)\n\n### Description\nPlease describe your pull request.\nReference issue it fixes.\n"
  },
  {
    "path": ".github/workflows/build-artifacts.yml",
    "content": "name: Build Artifacts\n\non:\n  push:\n    branches: [ master, build-test ]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - name: Set up JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: temurin\n          java-version: 25\n\n      - name: Set jadx version\n        run: |\n          JADX_REV=$(git rev-list --count HEAD)\n          JADX_VERSION=\"r${JADX_REV}.${GITHUB_SHA:0:7}\"\n          echo \"JADX_VERSION=$JADX_VERSION\" >> $GITHUB_ENV\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Build\n        run: ./gradlew dist distWin\n        env:\n          JADX_BUILD_JAVA_VERSION: 11\n\n      - name: Save bundle artifact\n        uses: actions/upload-artifact@v7\n        with:\n          name: ${{ format('jadx-{0}', env.JADX_VERSION) }}\n          # Waiting fix for https://github.com/actions/upload-artifact/issues/39 to upload zip file\n          # Upload unpacked files for now\n          path: build/jadx/**/*\n          if-no-files-found: error\n          retention-days: 14\n\n      - name: Save Windows bundle artifact\n        uses: actions/upload-artifact@v7\n        with:\n          name: ${{ format('jadx-gui-{0}-no-jre-win', env.JADX_VERSION) }}\n          # Upload unpacked files for now\n          path: jadx-gui/build/jadx-gui-win/*\n          if-no-files-found: error\n          retention-days: 14\n\n  build-win-bundle:\n    runs-on: windows-latest\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - name: Set up JDK\n        uses: oracle-actions/setup-java@v1\n        with:\n          release: 25\n\n      - name: Print Java version\n        shell: bash\n        run: java -version\n\n      - name: Set jadx version\n        shell: bash\n        run: |\n          JADX_REV=$(git rev-list --count HEAD)\n          JADX_VERSION=\"r${JADX_REV}.${GITHUB_SHA:0:7}\"\n          echo \"JADX_VERSION=$JADX_VERSION\" >> $GITHUB_ENV\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Build\n        run: ./gradlew dist -PbundleJRE=true\n\n      - name: Save Windows with JRE bundle artifact\n        uses: actions/upload-artifact@v7\n        with:\n          name: ${{ format('jadx-gui-{0}-with-jre-win', env.JADX_VERSION) }}\n          # Upload unpacked files for now\n          path: jadx-gui/build/jadx-gui-with-jre-win/*\n          if-no-files-found: error\n          retention-days: 14\n"
  },
  {
    "path": ".github/workflows/build-test.yml",
    "content": "name: Build Test\n\non:\n  push:\n    branches: [ master, build-test ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n  tests:\n    strategy:\n      matrix:\n        os: [ ubuntu-latest, windows-latest ]\n\n    runs-on: ${{ matrix.os }}\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Set up JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: temurin\n          java-version: 25\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Build\n        run: ./gradlew build dist distWin\n        env:\n          JADX_BUILD_JAVA_VERSION: 11\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non:\n  push:\n    tags:\n      - \"v*.*.*\"\n\n# additional permissions for provided GitHub token to create new release\npermissions:\n  contents: write\n\njobs:\n  build-release-win-bundle:\n    runs-on: windows-latest\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Set up JDK\n        uses: oracle-actions/setup-java@v1\n        with:\n          release: 25\n\n      - name: Set jadx version\n        uses: actions/github-script@v8\n        with:\n          script: |\n            const jadxVersion = context.ref.split('/').pop().substring(1)\n            core.exportVariable('JADX_VERSION', jadxVersion);\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Build\n        run: ./gradlew dist -PbundleJRE=true\n\n      - name: Save JRE bundle artifact\n        uses: actions/upload-artifact@v7\n        with:\n          name: ${{ format('jadx-gui-{0}-with-jre-win', env.JADX_VERSION) }}\n          path: ${{ format('build/distWinWithJre/jadx-gui-{0}-with-jre-win.zip', env.JADX_VERSION) }}\n          if-no-files-found: error\n          retention-days: 1\n\n  release:\n    needs: build-release-win-bundle\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Set up JDK\n        uses: actions/setup-java@v5\n        with:\n          distribution: temurin\n          java-version: 25\n\n      - name: Set jadx version and release name\n        uses: actions/github-script@v8\n        with:\n          script: |\n            const jadxVersion = context.ref.split('/').pop().substring(1)\n            core.exportVariable('JADX_VERSION', jadxVersion);\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Build\n        run: ./gradlew dist distWin\n        env:\n          JADX_BUILD_JAVA_VERSION: 11\n\n      - name: Download Windows JRE bundle\n        uses: actions/download-artifact@v8\n        with:\n          name: ${{ format('jadx-gui-{0}-with-jre-win', env.JADX_VERSION) }}\n          path: ${{ format('build/jadx-gui-{0}-with-jre-win', env.JADX_VERSION) }}\n\n      - run: |\n          cd build\n          pwd\n          ls -l\n          ls jadx-gui-*-with-jre-win\n          mv jadx-gui-*-with-jre-win/jadx-gui-*-with-jre-win.zip .\n          mv distWin/jadx-gui-*-win.zip .\n          ls -l *.zip\n\n      - name: Release\n        uses: softprops/action-gh-release@v2\n        with:\n          name: ${{ env.JADX_VERSION }}\n          draft: true\n          fail_on_unmatched_files: true\n          files: build/jadx-*.zip\n"
  },
  {
    "path": ".gitignore",
    "content": "# Eclipse files\n.classpath\n.project\n.settings/\n\n# IntelliJ Idea files\n.idea/\n.run/\nout/\n*.iml\n*.ipr\n*.iws\n.attach_pid*\n*.hprof\n\n**/.DS_Store\n\nbin/\ntarget/\nbuild/\nclasses/\nidea/\n.gradle/\n.kotlin/\nnode_modules/\n.vscode/\n\njadx-output/\n*-tmp/\n**/tmp/\n*.jobf\n*.jadx\n\n*.class\n*.jar\n*.dump\n*.log\n*.cfg\n*.orig\nquark.json\n\ncliff.toml\njadx-gui/src/main/resources/logback.xml\n"
  },
  {
    "path": ".gitlab-ci.yml",
    "content": "variables:\n  GRADLE_OPTS: \"-Dorg.gradle.daemon=false\"\n  TERM: \"dumb\"\n\nbefore_script:\n  - chmod +x gradlew\n\nstages:\n  - test\n\nbuild-test:\n  stage: test\n  image: eclipse-temurin:21\n  script: JADX_BUILD_JAVA_VERSION=11 JADX_TEST_JAVA_VERSION=11 ./gradlew clean build dist distWin\n"
  },
  {
    "path": ".jitpack.yml",
    "content": "jdk:\n  - openjdk11\ninstall:\n  - echo \"Jitpack is not supported. Use artifacts from Maven Central (https://search.maven.org/search?q=jadx), check usage help at https://github.com/skylot/jadx/wiki/Use-jadx-as-a-library\"\n  - ./gradlew intentional-fail\n"
  },
  {
    "path": ".typos.toml",
    "content": "# Config for 'typos' spellchecker (https://github.com/crate-ci/typos)\n\n[default.extend-words]\nIPUT = \"IPUT\"\nLaf = \"Laf\"\nDarcula=\"Darcula\"\n\n[default]\nextend-ignore-identifiers-re = [\n    \"finaly\", # intentional package name\n]\n\n[files]\nextend-exclude = [\n    \"config/\",\n    \"jadx-core/src/main/resources/\",\n    \"jadx-core/src/test/\",\n    \"jadx-gui/src/main/resources/i18n/\",\n    \"!jadx-gui/src/main/resources/i18n/Messages_en_US.properties\",\n]"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to make participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n  advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies within all project spaces, and it also applies when\nan individual is representing the project or its community in public spaces.\nExamples of representing a project or community include using an official\nproject e-mail address, posting via an official social media account, or acting\nas an appointed representative at an online or offline event. Representation of\na project may be further defined and clarified by project maintainers.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nPlease note, we have [code of conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project.\n\n## Open Issue\n\n1. Before proceed, please do:\n    - check [Troubleshooting Q&A](https://github.com/skylot/jadx/wiki/Troubleshooting-Q&A) section on wiki\n    - search existing issues by exception message\n\n2. Describe error:\n    - full name of method or class with error\n    - full java stacktrace (no need to copy method fallback code (commented pseudocode))\n    - **IMPORTANT!:** attach or provide link to apk file (double check apk version)\n\n\t  **Note**: GitHub don't allow attaching files with `.apk` extension, but you can change extension by adding `.zip` at the end :)\n\n\n## Pull Request Process\n\n1. Please don't submit any code style fixes or dependencies updates changes.\n\n1. Use only features and API from Java 11 or below.\n\n1. Make sure your code is correctly formatted, see description here: [Code Formatting](https://github.com/skylot/jadx/wiki/Code-Formatting).\n\n1. Make sure your changes are passing build: `./gradlew clean build dist`\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "NOTICE",
    "content": "The majority of jadx is written and copyrighted by me (Skylot)\nand released under the Apache 2.0 license (see LICENSE file for full license text):\n\n*******************************************************************************\nCopyright 2015, Skylot\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*******************************************************************************\n\n\nVarious portions of the code including dx library are taken from\nthe Android Open Source Project, and are used in accordance with\nthe following license:\n\n*******************************************************************************\nCopyright (C) 2007 The Android Open Source Project\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*******************************************************************************\n\n\nOther binary libraries used in 'jadx'\n=====================================\n\nJCommander library (http://jcommander.org/) released under the following license:\n\n*******************************************************************************\nCopyright 2012, Cedric Beust\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*******************************************************************************\n\n\nSLF4J source code and binaries are distributed under the following license:\n\n*******************************************************************************\nCopyright (c) 2004-2011 QOS.ch\n All rights reserved.\n\n Permission is hereby granted, free  of charge, to any person obtaining\n a  copy  of this  software  and  associated  documentation files  (the\n \"Software\"), to  deal in  the Software without  restriction, including\n without limitation  the rights to  use, copy, modify,  merge, publish,\n distribute,  sublicense, and/or sell  copies of  the Software,  and to\n permit persons to whom the Software  is furnished to do so, subject to\n the following conditions:\n\n The  above  copyright  notice  and  this permission  notice  shall  be\n included in all copies or substantial portions of the Software.\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\n NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION\n WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*******************************************************************************\n\n\nLogback source code and binaries are dual-licensed under the EPL v1.0 and the LGPL 2.1, or more formally:\n\n*******************************************************************************\nLogback: the reliable, generic, fast and flexible logging framework.\nCopyright (C) 1999-2012, QOS.ch. All rights reserved.\n\nThis program and the accompanying materials are dual-licensed under\neither the terms of the Eclipse Public License v1.0 as published by\nthe Eclipse Foundation\n\n  or (per the licensee's choosing)\n\nunder the terms of the GNU Lesser General Public License version 2.1\nas published by the Free Software Foundation.\n*******************************************************************************\n\n\nASM library:\n\n*******************************************************************************\nCopyright (c) 2000-2011 INRIA, France Telecom\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n1. Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n\n2. 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\n3. Neither the name of the copyright holders nor the names of its\n   contributors may be used to endorse or promote products derived from\n   this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\nTHE POSSIBILITY OF SUCH DAMAGE.\n*******************************************************************************\n\n\n\nJadx-gui components\n===================\n\nRSyntaxTextArea library (https://github.com/bobbylight/RSyntaxTextArea)\nlicensed under modified BSD license:\n\n*******************************************************************************\nCopyright (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.\n*******************************************************************************\n\n\nConcurrent Trees (https://code.google.com/p/concurrent-trees/)\nlicenced under Apache License 2.0:\n\n*******************************************************************************\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*******************************************************************************\n\n\nImage Viewer (https://github.com/kazocsaba/imageviewer)\n\n*******************************************************************************\nCopyright (c) 2008-2012 Kazó Csaba\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*******************************************************************************\n\nJFontChooser Component - http://sourceforge.jp/projects/jfontchooser/\n\nIcons copied from several places:\n - Eclipse Project (JDT UI) - licensed under EPL v1.0 (http://www.eclipse.org/legal/epl-v10.html)\n - famfamfam silk icon set (http://www.famfamfam.com/lab/icons/silk/) - licensed\n     under Creative Commons Attribution 2.5 License (http://creativecommons.org/licenses/by/2.5/)\n"
  },
  {
    "path": "README.md",
    "content": "<img src=\"https://raw.githubusercontent.com/skylot/jadx/master/jadx-gui/src/main/resources/logos/jadx-logo.png\" width=\"64\" align=\"left\" />\n\n## JADX\n\n![Build status](https://img.shields.io/github/actions/workflow/status/skylot/jadx/build-artifacts.yml)\n![GitHub contributors](https://img.shields.io/github/contributors/skylot/jadx)\n![GitHub all releases](https://img.shields.io/github/downloads/skylot/jadx/total)\n![GitHub release (latest by SemVer)](https://img.shields.io/github/downloads/skylot/jadx/latest/total)\n![Latest release](https://img.shields.io/github/release/skylot/jadx.svg)\n[![Maven Central](https://img.shields.io/maven-central/v/io.github.skylot/jadx-core)](https://search.maven.org/search?q=g:io.github.skylot%20AND%20jadx)\n![Java 11+](https://img.shields.io/badge/Java-11%2B-blue)\n[![License](http://img.shields.io/:license-apache-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html)\n\n**jadx** - Dex to Java decompiler\n\nCommand line and GUI tools for producing Java source code from Android Dex and Apk files\n\n> [!WARNING]\n> Please note that in most cases **jadx** can't decompile all 100% of the code, so errors will occur.<br />\n> Check [Troubleshooting guide](https://github.com/skylot/jadx/wiki/Troubleshooting-Q&A#decompilation-issues) for workarounds.\n\n**Main features:**\n- decompile Dalvik bytecode to Java code from APK, dex, aar, aab and zip files\n- decode `AndroidManifest.xml` and other resources from `resources.arsc`\n- deobfuscator included\n\n**jadx-gui features:**\n- view decompiled code with highlighted syntax\n- jump to declaration\n- find usage\n- full text search\n- smali debugger, check [wiki page](https://github.com/skylot/jadx/wiki/Smali-debugger) for setup and usage\n\nJadx-gui key bindings can be found [here](https://github.com/skylot/jadx/wiki/JADX-GUI-Key-bindings)\n\nSee these features in action here: [jadx-gui features overview](https://github.com/skylot/jadx/wiki/jadx-gui-features-overview)\n\n<img src=\"https://user-images.githubusercontent.com/118523/142730720-839f017e-38db-423e-b53f-39f5f0a0316f.png\" width=\"700\"/>\n\n### Download\n- release\n  from [github: ![Latest release](https://img.shields.io/github/release/skylot/jadx.svg)](https://github.com/skylot/jadx/releases/latest)\n- latest [unstable build ![GitHub commits since tagged version (branch)](https://img.shields.io/github/commits-since/skylot/jadx/latest/master)](https://nightly.link/skylot/jadx/workflows/build-artifacts/master)\n\nAfter download unpack zip file go to `bin` directory and run:\n- `jadx` - command line version\n- `jadx-gui` - UI version\n\nOn Windows run `.bat` files with double-click\\\n**Note:** ensure you have installed Java 11 or later 64-bit version.\nFor Windows, you can download it from [oracle.com](https://www.oracle.com/java/technologies/downloads/#jdk17-windows) (select x64 Installer).\n\n### Install\n- Arch Linux\n  [![Arch Linux package](https://img.shields.io/archlinux/v/extra/any/jadx)](https://archlinux.org/packages/extra/any/jadx/)\n  [![AUR Version](https://img.shields.io/aur/version/jadx-git)](https://aur.archlinux.org/packages/jadx-git)\n  ```bash\n  sudo pacman -S jadx\n  ```\n- macOS\n  [![homebrew version](https://img.shields.io/homebrew/v/jadx)](https://formulae.brew.sh/formula/jadx)\n  ```bash\n  brew install jadx\n  ```\n- Flathub\n  [![Flathub Version](https://img.shields.io/flathub/v/com.github.skylot.jadx)](https://flathub.org/apps/com.github.skylot.jadx)\n  ```bash\n  flatpak install flathub com.github.skylot.jadx\n  ```\n\n### Use jadx as a library\nYou can use jadx in your java projects, check details on [wiki page](https://github.com/skylot/jadx/wiki/Use-jadx-as-a-library)\n\n### Build from source\nJDK 11 or higher must be installed:\n```\ngit clone https://github.com/skylot/jadx.git\ncd jadx\n./gradlew dist\n```\n\n(on Windows, use `gradlew.bat` instead of `./gradlew`)\n\nScripts for run jadx will be placed in `build/jadx/bin`\nand also packed to `build/jadx-<version>.zip`\n\n### Usage\n```\njadx[-gui] [command] [options] <input files> (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab, .xapk, .apkm, .jadx.kts)\ncommands (use '<command> --help' for command options):\n  plugins\t  - manage jadx plugins\n\noptions:\n  -d, --output-dir                              - output directory\n  -ds, --output-dir-src                         - output directory for sources\n  -dr, --output-dir-res                         - output directory for resources\n  -r, --no-res                                  - do not decode resources\n  -s, --no-src                                  - do not decompile source code\n  -j, --threads-count                           - processing threads count, default: 16\n  --single-class                                - decompile a single class, full name, raw or alias\n  --single-class-output                         - file or dir for write if decompile a single class\n  --output-format                               - can be 'java' or 'json', default: java\n  -e, --export-gradle                           - save as gradle project (set '--export-gradle-type' to 'auto')\n  --export-gradle-type                          - Gradle project template for export:\n                                                   'auto' - detect automatically\n                                                   'android-app' - Android Application (apk)\n                                                   'android-library' - Android Library (aar)\n                                                   'simple-java' - simple Java\n  -m, --decompilation-mode                      - code output mode:\n                                                   'auto' - trying best options (default)\n                                                   'restructure' - restore code structure (normal java code)\n                                                   'simple' - simplified instructions (linear, with goto's)\n                                                   'fallback' - raw instructions without modifications\n  --show-bad-code                               - show inconsistent code (incorrectly decompiled)\n  --no-xml-pretty-print                         - do not prettify XML\n  --no-imports                                  - disable use of imports, always write entire package name\n  --no-debug-info                               - disable debug info parsing and processing\n  --add-debug-lines                             - add comments with debug line numbers if available\n  --no-inline-anonymous                         - disable anonymous classes inline\n  --no-inline-methods                           - disable methods inline\n  --no-move-inner-classes                       - disable move inner classes into parent\n  --no-inline-kotlin-lambda                     - disable inline for Kotlin lambdas\n  --no-finally                                  - don't extract finally block\n  --no-restore-switch-over-string               - don't restore switch over string\n  --no-replace-consts                           - don't replace constant value with matching constant field\n  --escape-unicode                              - escape non latin characters in strings (with \\u)\n  --respect-bytecode-access-modifiers           - don't change original access modifiers\n  --mappings-path                               - deobfuscation mappings file or directory. Allowed formats: Tiny and Tiny v2 (both '.tiny'), Enigma (.mapping) or Enigma directory\n  --mappings-mode                               - set mode for handling the deobfuscation mapping file:\n                                                   'read' - just read, user can always save manually (default)\n                                                   'read-and-autosave-every-change' - read and autosave after every change\n                                                   'read-and-autosave-before-closing' - read and autosave before exiting the app or closing the project\n                                                   'ignore' - don't read or save (can be used to skip loading mapping files referenced in the project file)\n  --deobf                                       - activate deobfuscation\n  --deobf-min                                   - min length of name, renamed if shorter, default: 3\n  --deobf-max                                   - max length of name, renamed if longer, default: 64\n  --deobf-whitelist                             - space separated list of classes (full name) and packages (ends with '.*') to exclude from deobfuscation, default: android.support.v4.* android.support.v7.* android.support.v4.os.* android.support.annotation.Px androidx.core.os.* androidx.annotation.Px\n  --deobf-cfg-file                              - deobfuscation mappings file used for JADX auto-generated names (in the JOBF file format), default: same dir and name as input file with '.jobf' extension\n  --deobf-cfg-file-mode                         - set mode for handling the JADX auto-generated names' deobfuscation map file:\n                                                   'read' - read if found, don't save (default)\n                                                   'read-or-save' - read if found, save otherwise (don't overwrite)\n                                                   'overwrite' - don't read, always save\n                                                   'ignore' - don't read and don't save\n  --deobf-res-name-source                       - better name source for resources:\n                                                   'auto' - automatically select best name (default)\n                                                   'resources' - use resources names\n                                                   'code' - use R class fields names\n  --use-source-name-as-class-name-alias         - use source name as class name alias:\n                                                   'always' - always use source name if it's available\n                                                   'if-better' - use source name if it seems better than the current one\n                                                   'never' - never use source name, even if it's available\n  --source-name-repeat-limit                    - allow using source name if it appears less than a limit number, default: 10\n  --use-kotlin-methods-for-var-names            - use kotlin intrinsic methods to rename variables, values: disable, apply, apply-and-hide, default: apply\n  --use-headers-for-detect-resource-extensions  - Use headers for detect resource extensions if resource obfuscated\n  --rename-flags                                - fix options (comma-separated list of):\n                                                   'case' - fix case sensitivity issues (according to --fs-case-sensitive option),\n                                                   'valid' - rename java identifiers to make them valid,\n                                                   'printable' - remove non-printable chars from identifiers,\n                                                  or single 'none' - to disable all renames\n                                                  or single 'all' - to enable all (default)\n  --integer-format                              - how integers are displayed:\n                                                   'auto' - automatically select (default)\n                                                   'decimal' - use decimal\n                                                   'hexadecimal' - use hexadecimal\n  --type-update-limit                           - type update limit count (per one instruction), default: 10\n  --fs-case-sensitive                           - treat filesystem as case sensitive, false by default\n  --cfg                                         - save methods control flow graph to dot file\n  --raw-cfg                                     - save methods control flow graph (use raw instructions)\n  -f, --fallback                                - set '--decompilation-mode' to 'fallback' (deprecated)\n  --use-dx                                      - use dx/d8 to convert java bytecode\n  --comments-level                              - set code comments level, values: error, warn, info, debug, user-only, none, default: info\n  --log-level                                   - set log level, values: quiet, progress, error, warn, info, debug, default: progress\n  -v, --verbose                                 - verbose output (set --log-level to DEBUG)\n  -q, --quiet                                   - turn off output (set --log-level to QUIET)\n  --disable-plugins                             - comma separated list of plugin ids to disable\n  --config <config-ref>                         - load configuration from file, <config-ref> can be:\n                                                   path to '.json' file\n                                                   short name - uses file with this name from config directory\n                                                   'none' - to disable config loading\n  --save-config <config-ref>                    - save current options into configuration file and exit, <config-ref> can be:\n                                                   empty - for default config\n                                                   path to '.json' file\n                                                   short name - file will be saved in config directory\n  --print-files                                 - print files and directories used by jadx (config, cache, temp)\n  --version                                     - print jadx version\n  -h, --help                                    - print this help\n\nPlugin options (-P<name>=<value>):\n  dex-input: Load .dex and .apk files\n    - dex-input.verify-checksum                 - verify dex file checksum before load, values: [yes, no], default: yes\n  java-convert: Convert .class, .jar and .aar files to dex\n    - java-convert.mode                         - convert mode, values: [dx, d8, both], default: both\n    - java-convert.d8-desugar                   - use desugar in d8, values: [yes, no], default: no\n  kotlin-metadata: Use kotlin.Metadata annotation for code generation\n    - kotlin-metadata.class-alias               - rename class alias, values: [yes, no], default: yes\n    - kotlin-metadata.method-args               - rename function arguments, values: [yes, no], default: yes\n    - kotlin-metadata.fields                    - rename fields, values: [yes, no], default: yes\n    - kotlin-metadata.companion                 - rename companion object, values: [yes, no], default: yes\n    - kotlin-metadata.data-class                - add data class modifier, values: [yes, no], default: yes\n    - kotlin-metadata.to-string                 - rename fields using toString, values: [yes, no], default: yes\n    - kotlin-metadata.getters                   - rename simple getters to field names, values: [yes, no], default: yes\n  kotlin-smap: Use kotlin.SourceDebugExtension annotation for rename class alias\n    - kotlin-smap.class-alias-source-dbg        - rename class alias from SourceDebugExtension, values: [yes, no], default: no\n  rename-mappings: various mappings support\n    - rename-mappings.format                    - mapping format, values: [AUTO, TINY_FILE, TINY_2_FILE, ENIGMA_FILE, ENIGMA_DIR, PROGUARD_FILE, SRG_FILE, XSRG_FILE, JAM_FILE, CSRG_FILE, TSRG_FILE, TSRG_2_FILE, INTELLIJ_MIGRATION_MAP_FILE, RECAF_SIMPLE_FILE, JOBF_FILE], default: AUTO\n    - rename-mappings.invert                    - invert mapping on load, values: [yes, no], default: no\n  smali-input: Load .smali files\n    - smali-input.api-level                     - Android API level, default: 27\n\nEnvironment variables:\n  JADX_DISABLE_XML_SECURITY - set to 'true' to disable all security checks for XML files\n  JADX_DISABLE_ZIP_SECURITY - set to 'true' to disable all security checks for zip files\n  JADX_ZIP_MAX_ENTRIES_COUNT - maximum allowed number of entries in zip files (default: 100 000)\n  JADX_CONFIG_DIR - custom config directory, using system by default\n  JADX_CACHE_DIR - custom cache directory, using system by default\n  JADX_TMP_DIR - custom temp directory, using system by default\n\nExamples:\n  jadx -d out classes.dex\n  jadx --rename-flags \"none\" classes.dex\n  jadx --rename-flags \"valid, printable\" classes.dex\n  jadx --log-level ERROR app.apk\n  jadx -Pdex-input.verify-checksum=no app.apk\n```\nThese options also work in jadx-gui running from command line and override options from preferences' dialog\n\nUsage for `plugins` command\n```\nusage: plugins [options]\noptions:\n  -i, --install <locationId>      - install plugin with locationId\n  -j, --install-jar <path-to.jar> - install plugin from jar file\n  -l, --list                      - list installed plugins\n  -a, --available                 - list available plugins from jadx-plugins-list (aka marketplace)\n  -u, --update                    - update installed plugins\n  --uninstall <pluginId>          - uninstall plugin with pluginId\n  --disable <pluginId>            - disable plugin with pluginId\n  --enable <pluginId>             - enable plugin with pluginId\n  --list-all                      - list all plugins including bundled and dropins\n  --list-versions <locationId>    - fetch latest versions of plugin from locationId (will download all artefacts, limited to 10)\n  -h, --help                      - print this help\n```\n\n\n### Troubleshooting\nPlease check wiki page [Troubleshooting Q&A](https://github.com/skylot/jadx/wiki/Troubleshooting-Q&A)\n\n### Contributing\nTo support this project you can:\n  - Post thoughts about new features/optimizations that important to you\n  - Submit decompilation issues, please read before proceed: [Open issue](CONTRIBUTING.md#Open-Issue)\n  - Open pull request, please follow these rules: [Pull Request Process](CONTRIBUTING.md#Pull-Request-Process)\n\n---------------------------------------\n*Licensed under the Apache 2.0 License*\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Reporting a Vulnerability\n\nTo report a security issue, please open a [new security advisory](https://github.com/skylot/jadx/security/advisories/new).\nPlease fill the steps you took to create the issue, affected versions, and, if known, mitigations for the issue.\nWe will check and respond within 3 working days.\nIf the issue is confirmed as a vulnerability, we will apply required mitigations at the next release.\n"
  },
  {
    "path": "build.gradle.kts",
    "content": "import com.diffplug.gradle.spotless.FormatExtension\nimport com.diffplug.gradle.spotless.SpotlessExtension\nimport com.diffplug.spotless.LineEnding\nimport com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask\nimport org.gradle.nativeplatform.platform.internal.DefaultNativePlatform\nimport java.util.Locale\n\nplugins {\n\tid(\"com.github.ben-manes.versions\") version \"0.53.0\"\n\tid(\"se.patrikerdes.use-latest-versions\") version \"0.2.19\"\n\tid(\"com.diffplug.spotless\") version \"6.25.0\"\n}\n\nval jadxVersion by extra { System.getenv(\"JADX_VERSION\") ?: \"dev\" }\nprintln(\"jadx version: $jadxVersion\")\nversion = jadxVersion\n\nval jadxBuildJavaVersion by extra { getBuildJavaVersion() }\n\nfun getBuildJavaVersion(): Int? {\n\tval envVarName = \"JADX_BUILD_JAVA_VERSION\"\n\tval buildJavaVer = System.getenv(envVarName)?.toInt() ?: return null\n\tif (buildJavaVer < 11) {\n\t\tthrow GradleException(\"'$envVarName' can't be set to lower than 11\")\n\t}\n\tprintln(\"Set Java toolchain for jadx build to version '$buildJavaVer'\")\n\treturn buildJavaVer\n}\n\nallprojects {\n\tapply(plugin = \"java\")\n\tapply(plugin = \"checkstyle\")\n\tapply(plugin = \"com.diffplug.spotless\")\n\tapply(plugin = \"com.github.ben-manes.versions\")\n\tapply(plugin = \"se.patrikerdes.use-latest-versions\")\n\n\trepositories {\n\t\tmavenCentral()\n\t}\n\n\tconfigure<SpotlessExtension> {\n\t\tjava {\n\t\t\timportOrderFile(\"$rootDir/config/code-formatter/eclipse.importorder\")\n\t\t\teclipse().configFile(\"$rootDir/config/code-formatter/eclipse.xml\")\n\t\t\tremoveUnusedImports()\n\t\t\tcommonFormatOptions()\n\t\t}\n\t\tkotlin {\n\t\t\tktlint().editorConfigOverride(mapOf(\"indent_style\" to \"tab\"))\n\t\t\tcommonFormatOptions()\n\t\t}\n\t\tkotlinGradle {\n\t\t\tktlint()\n\t\t\tcommonFormatOptions()\n\t\t}\n\t\tformat(\"misc\") {\n\t\t\ttarget(\"**/*.gradle\", \"**/*.xml\", \"**/.gitignore\", \"**/.properties\")\n\t\t\ttargetExclude(\".gradle/**\", \".idea/**\", \"*/build/**\")\n\t\t\tcommonFormatOptions()\n\t\t}\n\t}\n\n\ttasks.named<DependencyUpdatesTask>(\"dependencyUpdates\") {\n\t\trejectVersionIf {\n\t\t\t// disallow release candidates as upgradable versions from stable versions\n\t\t\tisNonStable(candidate.version) && !isNonStable(currentVersion)\n\t\t}\n\t}\n}\n\nfun FormatExtension.commonFormatOptions() {\n\tlineEndings = LineEnding.UNIX\n\tencoding = Charsets.UTF_8\n\ttrimTrailingWhitespace()\n\tendWithNewline()\n}\n\nfun isNonStable(version: String): Boolean {\n\tval stableKeyword = listOf(\"RELEASE\", \"FINAL\", \"GA\").any { version.uppercase(Locale.getDefault()).contains(it) }\n\tval regex = \"^[0-9,.v-]+(-r)?$\".toRegex()\n\tval isStable = stableKeyword || regex.matches(version)\n\treturn isStable.not()\n}\n\nval distWinConfiguration: Configuration by configurations.creating {\n\tisCanBeConsumed = false\n}\nval distWinWithJreConfiguration: Configuration by configurations.creating {\n\tisCanBeConsumed = false\n}\ndependencies {\n\tdistWinConfiguration(project(\":jadx-gui\", \"distWinConfiguration\"))\n\tdistWinWithJreConfiguration(project(\":jadx-gui\", \"distWinWithJreConfiguration\"))\n}\n\nval copyArtifacts by tasks.registering(Copy::class) {\n\tval jarCliPattern = \"jadx-cli-(.*)-all.jar\".toPattern()\n\tfrom(tasks.getByPath(\":jadx-cli:installShadowDist\")) {\n\t\texclude(\"**/*.jar\")\n\t\tfilter { line ->\n\t\t\tjarCliPattern.matcher(line).replaceAll(\"jadx-$1-all.jar\")\n\t\t\t\t.replace(\"-jar \\\"\\\\\\\"\\$CLASSPATH\\\\\\\"\\\"\", \"-cp \\\"\\\\\\\"\\$CLASSPATH\\\\\\\"\\\" jadx.cli.JadxCLI\")\n\t\t\t\t.replace(\"-jar \\\"%CLASSPATH%\\\"\", \"-cp \\\"%CLASSPATH%\\\" jadx.cli.JadxCLI\")\n\t\t}\n\t}\n\tval jarGuiPattern = \"jadx-gui-(.*)-all.jar\".toPattern()\n\tfrom(tasks.getByPath(\":jadx-gui:installShadowDist\")) {\n\t\texclude(\"**/*.jar\")\n\t\tfilter { line -> jarGuiPattern.matcher(line).replaceAll(\"jadx-$1-all.jar\") }\n\t}\n\tfrom(tasks.getByPath(\":jadx-gui:installShadowDist\")) {\n\t\tinclude(\"**/*.jar\")\n\t\trename(\"jadx-gui-(.*)-all.jar\", \"jadx-$1-all.jar\")\n\t}\n\tfrom(layout.projectDirectory) {\n\t\tinclude(\"README.md\")\n\t\tinclude(\"LICENSE\")\n\t}\n\tinto(layout.buildDirectory.dir(\"jadx\"))\n\tduplicatesStrategy = DuplicatesStrategy.EXCLUDE\n}\n\nval pack by tasks.registering(Zip::class) {\n\tfrom(copyArtifacts)\n\tarchiveFileName.set(\"jadx-$jadxVersion.zip\")\n\tdestinationDirectory.set(layout.buildDirectory)\n}\n\nval distWin by tasks.registering(Zip::class) {\n\tgroup = \"jadx\"\n\tdescription = \"Build Windows bundle\"\n\n\tfrom(distWinConfiguration)\n\n\tdestinationDirectory.set(layout.buildDirectory.dir(\"distWin\"))\n\tarchiveFileName.set(\"jadx-gui-$jadxVersion-win.zip\")\n\tduplicatesStrategy = DuplicatesStrategy.EXCLUDE\n}\n\nval distWinWithJre by tasks.registering(Zip::class) {\n\tdescription = \"Build Windows with JRE bundle\"\n\n\tfrom(distWinWithJreConfiguration)\n\n\tdestinationDirectory.set(layout.buildDirectory.dir(\"distWinWithJre\"))\n\tarchiveFileName.set(\"jadx-gui-$jadxVersion-with-jre-win.zip\")\n\tduplicatesStrategy = DuplicatesStrategy.EXCLUDE\n}\n\nval dist by tasks.registering {\n\tgroup = \"jadx\"\n\tdescription = \"Build jadx distribution zip bundles\"\n\n\tdependsOn(pack)\n\n\tval os = DefaultNativePlatform.getCurrentOperatingSystem()\n\tif (os.isWindows) {\n\t\tif (project.hasProperty(\"bundleJRE\")) {\n\t\t\tprintln(\"Build win bundle with JRE\")\n\t\t\tdependsOn(distWinWithJre)\n\t\t} else {\n\t\t\tdependsOn(distWin)\n\t\t}\n\t}\n}\n\nval cleanBuildDir by tasks.registering(Delete::class) {\n\tdelete(layout.buildDirectory)\n}\ntasks.getByName(\"clean\").dependsOn(cleanBuildDir)\n"
  },
  {
    "path": "buildSrc/build.gradle.kts",
    "content": "plugins {\n\t`kotlin-dsl`\n}\n\ndependencies {\n\timplementation(\"org.jetbrains.kotlin:kotlin-gradle-plugin:2.3.10\")\n\n\timplementation(\"org.openrewrite:plugin:6.19.1\")\n}\n\nrepositories {\n\tgradlePluginPortal()\n}\n"
  },
  {
    "path": "buildSrc/src/main/kotlin/jadx-java.gradle.kts",
    "content": "import org.gradle.api.tasks.testing.logging.TestExceptionFormat\n\nplugins {\n\tjava\n\tcheckstyle\n\n\tid(\"jadx-rewrite\")\n}\n\nval jadxVersion: String by rootProject.extra\nval jadxBuildJavaVersion: Int? by rootProject.extra\n\ngroup = \"io.github.skylot\"\nversion = jadxVersion\n\ndependencies {\n\timplementation(\"org.slf4j:slf4j-api:2.0.17\")\n\tcompileOnly(\"org.jetbrains:annotations:26.0.2\")\n\n\ttestImplementation(\"ch.qos.logback:logback-classic:1.5.22\")\n\ttestImplementation(\"org.assertj:assertj-core:3.27.6\")\n\n\ttestImplementation(\"org.junit.jupiter:junit-jupiter:5.13.3\")\n\ttestRuntimeOnly(\"org.junit.platform:junit-platform-launcher\")\n\n\ttestCompileOnly(\"org.jetbrains:annotations:26.0.2\")\n}\n\nrepositories {\n\tmavenCentral()\n\t// required for: aapt-proto, r8, smali\n\tgoogle()\n}\n\njava {\n\tjadxBuildJavaVersion?.let { buildJavaVer ->\n\t\ttoolchain {\n\t\t\tlanguageVersion = JavaLanguageVersion.of(buildJavaVer)\n\t\t}\n\t}\n\tsourceCompatibility = JavaVersion.VERSION_11\n\ttargetCompatibility = JavaVersion.VERSION_11\n}\n\ntasks {\n\tcompileJava {\n\t\toptions.encoding = \"UTF-8\"\n\t\t// options.compilerArgs = listOf(\"-Xlint:deprecation\")\n\t}\n\tjar {\n\t\tmanifest {\n\t\t\tattributes(\"jadx-version\" to jadxVersion)\n\t\t}\n\t}\n\ttest {\n\t\tuseJUnitPlatform()\n\t\tmaxParallelForks = Runtime.getRuntime().availableProcessors()\n\t\ttestLogging {\n\t\t\tshowExceptions = true\n\t\t\texceptionFormat = TestExceptionFormat.FULL\n\t\t\tshowCauses = true\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/kotlin/jadx-kotlin.gradle.kts",
    "content": "import org.jetbrains.kotlin.gradle.dsl.JvmTarget\n\nplugins {\n\tid(\"jadx-java\")\n\tid(\"org.jetbrains.kotlin.jvm\")\n}\n\ndependencies {\n\timplementation(kotlin(\"stdlib\"))\n\timplementation(kotlin(\"reflect\")) // don't work from plugin classloader\n}\n\nkotlin {\n\tcompilerOptions {\n\t\tjvmTarget.set(JvmTarget.JVM_11)\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/kotlin/jadx-library.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-java\")\n\tid(\"java-library\")\n\tid(\"maven-publish\")\n\tid(\"signing\")\n}\n\nval jadxVersion: String by rootProject.extra\n\ngroup = \"io.github.skylot\"\nversion = jadxVersion\n\njava {\n\twithJavadocJar()\n\twithSourcesJar()\n}\n\npublishing {\n\tpublications {\n\t\tcreate<MavenPublication>(\"mavenJava\") {\n\t\t\tartifactId = project.name\n\t\t\tfrom(components[\"java\"])\n\t\t\tversionMapping {\n\t\t\t\tusage(\"java-api\") {\n\t\t\t\t\tfromResolutionOf(\"runtimeClasspath\")\n\t\t\t\t}\n\t\t\t\tusage(\"java-runtime\") {\n\t\t\t\t\tfromResolutionResult()\n\t\t\t\t}\n\t\t\t}\n\t\t\tpom {\n\t\t\t\tname.set(project.name)\n\t\t\t\tdescription.set(project.description ?: \"Dex to Java decompiler\")\n\t\t\t\turl.set(\"https://github.com/skylot/jadx\")\n\t\t\t\tlicenses {\n\t\t\t\t\tlicense {\n\t\t\t\t\t\tname.set(\"The Apache License, Version 2.0\")\n\t\t\t\t\t\turl.set(\"http://www.apache.org/licenses/LICENSE-2.0.txt\")\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdevelopers {\n\t\t\t\t\tdeveloper {\n\t\t\t\t\t\tid.set(\"skylot\")\n\t\t\t\t\t\tname.set(\"Skylot\")\n\t\t\t\t\t\temail.set(project.properties[\"libEmail\"].toString())\n\t\t\t\t\t\turl.set(\"https://github.com/skylot\")\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tscm {\n\t\t\t\t\tconnection.set(\"scm:git:git://github.com/skylot/jadx.git\")\n\t\t\t\t\tdeveloperConnection.set(\"scm:git:ssh://github.com:skylot/jadx.git\")\n\t\t\t\t\turl.set(\"https://github.com/skylot/jadx\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\trepositories {\n\t\tmaven {\n\t\t\tval releasesRepoUrl = uri(\"https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/\")\n\t\t\tval snapshotsRepoUrl = uri(\"https://s01.oss.sonatype.org/content/repositories/snapshots/\")\n\t\t\turl = if (version.toString().endsWith(\"SNAPSHOT\")) snapshotsRepoUrl else releasesRepoUrl\n\t\t\tcredentials {\n\t\t\t\tusername = project.properties[\"ossrhUser\"].toString()\n\t\t\t\tpassword = project.properties[\"ossrhPassword\"].toString()\n\t\t\t}\n\t\t}\n\t}\n}\n\nsigning {\n\tisRequired = gradle.taskGraph.hasTask(\"publish\")\n\tsign(publishing.publications[\"mavenJava\"])\n}\n\n\ntasks.javadoc {\n\tval stdOptions = options as StandardJavadocDocletOptions\n\tstdOptions.addBooleanOption(\"html5\", true)\n\t// disable 'missing' warnings\n\tstdOptions.addStringOption(\"Xdoclint:all,-missing\", \"-quiet\")\n}\n"
  },
  {
    "path": "buildSrc/src/main/kotlin/jadx-rewrite.gradle.kts",
    "content": "plugins {\n\tid(\"org.openrewrite.rewrite\")\n}\n\nrepositories {\n\tmavenCentral()\n}\n\ndependencies {\n\trewrite(\"org.openrewrite.recipe:rewrite-testing-frameworks:3.24.0\")\n\trewrite(\"org.openrewrite.recipe:rewrite-logging-frameworks:3.20.0\")\n\trewrite(\"org.openrewrite.recipe:rewrite-migrate-java:3.24.0\")\n\trewrite(\"org.openrewrite.recipe:rewrite-static-analysis:2.24.0\")\n}\n\ntasks {\n\trewrite {\n\t\t// exclusion(\"src/test/java/jadx/tests/integration\")\n\n\t\t// activeRecipe(\"org.openrewrite.java.migrate.Java8toJava11\")\n\n\t\t// checkstyle auto fix\n\t\t// activeRecipe(\"org.openrewrite.staticanalysis.CodeCleanup\")\n\t\t// setCheckstyleConfigFile(file(\"$rootDir/config/checkstyle/checkstyle.xml\"))\n\n\t\t// logging\n\t\t// activeRecipe(\"org.openrewrite.java.logging.slf4j.Slf4jBestPractices\")\n\t\t// activeRecipe(\"org.openrewrite.java.logging.slf4j.LoggersNamedForEnclosingClass\")\n\t\t// activeRecipe(\"org.openrewrite.java.logging.slf4j.ParameterizedLogging\")\n\t\t// activeRecipe(\"org.openrewrite.java.logging.PrintStackTraceToLogError\")\n\n\t\t// testing\n\t\tactiveRecipe(\"org.openrewrite.java.testing.assertj.Assertj\")\n\t}\n}\n"
  },
  {
    "path": "config/checkstyle/checkstyle.xml",
    "content": "<?xml version=\"1.0\" ?>\n\n<!DOCTYPE module PUBLIC\n\t\t\"-//Puppy Crawl//DTD Check Configuration 1.2//EN\"\n\t\t\"http://www.puppycrawl.com/dtds/configuration_1_2.dtd\">\n\n<module name=\"Checker\">\n\t<property name=\"fileExtensions\" value=\"java, properties, xml\"/>\n\t<property name=\"charset\" value=\"UTF-8\"/>\n\n\t<module name=\"TreeWalker\">\n\t\t<property name=\"tabWidth\" value=\"4\"/>\n\t\t<module name=\"RegexpSinglelineJava\">\n\t\t\t<property name=\"format\" value=\"^\\t* \"/>\n\t\t\t<property name=\"message\" value=\"Indent must use tab characters\"/>\n\t\t\t<property name=\"ignoreComments\" value=\"true\"/>\n\t\t</module>\n\t\t<module name=\"RegexpSinglelineJava\">\n\t\t\t<property name=\"format\" value=\"^(?!\\s+\\* $).*?\\s+$\"/>\n\t\t\t<property name=\"message\" value=\"Line has trailing spaces.\"/>\n\t\t</module>\n\t\t<module name=\"AvoidEscapedUnicodeCharacters\">\n\t\t\t<property name=\"allowEscapesForControlCharacters\" value=\"true\"/>\n\t\t\t<property name=\"allowByTailComment\" value=\"true\"/>\n\t\t\t<property name=\"allowNonPrintableEscapes\" value=\"true\"/>\n\t\t</module>\n\n\t\t<module name=\"EmptyLineSeparator\">\n\t\t\t<property name=\"allowNoEmptyLineBetweenFields\" value=\"true\"/>\n\t\t\t<property name=\"allowMultipleEmptyLines\" value=\"false\"/>\n\t\t</module>\n\n\t\t<!-- whitespaces -->\n\t\t<module name=\"SingleSpaceSeparator\"/>\n\t\t<module name=\"GenericWhitespace\"/>\n\t\t<module name=\"MethodParamPad\"/>\n\t\t<module name=\"NoWhitespaceBefore\"/>\n\t\t<module name=\"OperatorWrap\"/>\n\t\t<module name=\"ParenPad\"/>\n\t\t<module name=\"TypecastParenPad\"/>\n\t\t<module name=\"WhitespaceAfter\"/>\n\t\t<module name=\"WhitespaceAround\">\n\t\t\t<property name=\"allowEmptyMethods\" value=\"true\"/>\n\t\t</module>\n\t\t<!-- <module name=\"EmptyForIteratorPad\"/> -->\n\t\t<!-- <module name=\"NoWhitespaceAfter\"/>-->\n\n\t\t<module name=\"NoLineWrap\"/>\n\n\t\t<module name=\"IllegalImport\"/> <!-- defaults to sun.* packages -->\n\t\t<module name=\"RedundantImport\"/>\n\t\t<module name=\"UnusedImports\"/>\n\t\t<!-- <module name=\"AvoidStarImport\"/> -->\n\n\t\t<module name=\"NeedBraces\"/>\n\t\t<module name=\"LeftCurly\"/>\n\t\t<module name=\"RightCurly\"/>\n\t\t<module name=\"EmptyCatchBlock\">\n\t\t\t<property name=\"exceptionVariableName\" value=\"expected|ignore\"/>\n\t\t</module>\n\n\t\t<!-- naming -->\n\t\t<module name=\"PackageName\"/>\n\t\t<module name=\"TypeName\"/>\n\t\t<module name=\"InterfaceTypeParameterName\"/>\n\t\t<module name=\"ClassTypeParameterName\"/>\n\t\t<module name=\"StaticVariableName\"/>\n\t\t<module name=\"ConstantName\"/>\n\t\t<module name=\"MemberName\"/>\n\t\t<module name=\"MethodName\"/>\n\t\t<module name=\"MethodTypeParameterName\"/>\n\t\t<module name=\"ParameterName\"/>\n\t\t<module name=\"LambdaParameterName\"/>\n\t\t<module name=\"LocalVariableName\"/>\n\t\t<module name=\"LocalFinalVariableName\"/>\n\t\t<module name=\"CatchParameterName\"/>\n\t\t<!-- <module name=\"HiddenField\"/> -->\n\n\t\t<!-- annotations -->\n\t\t<module name=\"AnnotationLocation\"/>\n\t\t<module name=\"AnnotationUseStyle\">\n\t\t\t<property name=\"elementStyle\" value=\"compact\"/>\n\t\t</module>\n\t\t<module name=\"MissingOverride\"/>\n\t\t<!-- <module name=\"MissingDeprecated\"/> -->\n\n\t\t<module name=\"ModifierOrder\"/>\n\t\t<!-- <module name=\"RedundantModifier\"/> -->\n\t\t<!-- <module name=\"ParameterNumber\"/> -->\n\n\t\t<module name=\"EmptyStatement\"/>\n\t\t<module name=\"DefaultComesLast\"/>\n\t\t<module name=\"EqualsHashCode\"/>\n\t\t<module name=\"FallThrough\"/>\n\t\t<!-- <module name=\"IllegalCatch\"/> -->\n\t\t<module name=\"IllegalThrows\"/>\n\t\t<module name=\"IllegalType\"/>\n\t\t<module name=\"InnerAssignment\"/>\n\t\t<module name=\"MultipleVariableDeclarations\"/>\n\t\t<module name=\"NoClone\"/>\n\t\t<module name=\"NoFinalizer\"/>\n\t\t<module name=\"OneStatementPerLine\"/>\n\t\t<module name=\"PackageDeclaration\"/>\n\t\t<module name=\"StringLiteralEquality\"/>\n\n\t\t<!-- design -->\n\t\t<module name=\"OneTopLevelClass\"/>\n\t\t<module name=\"MutableException\"/>\n\t\t<module name=\"InterfaceIsType\"/>\n\t\t<module name=\"ThrowsCount\">\n\t\t\t<property name=\"max\" value=\"2\"/>\n\t\t</module>\n\n\t\t<!-- misc -->\n\t\t<module name=\"ArrayTypeStyle\"/>\n\t\t<module name=\"OuterTypeFilename\"/>\n\n\t\t<!-- sizes -->\n\t\t<module name=\"OuterTypeNumber\"/>\n\n\t\t<module name=\"SuppressWarningsHolder\"/>\n\n\t\t<module name=\"IllegalType\">\n\t\t\t<property name=\"illegalClassNames\" value=\"java.util.ArrayList, java.util.HashMap, java.util.HashSet,\n\t\t\t\t java.util.LinkedHashMap, java.util.LinkedHashSet, java.util.TreeMap, java.util.TreeSet\"/>\n\t\t</module>\n\t\t<module name=\"IllegalImport\">\n\t\t\t<property name=\"illegalClasses\" value=\"jadx.core.utils.DebugUtils\"/>\n\t\t\t<!-- don't use nullable annotations from RxJava -->\n\t\t\t<property name=\"illegalClasses\" value=\"io.reactivex.rxjava3.annotations.NonNull\"/>\n\t\t\t<property name=\"illegalClasses\" value=\"io.reactivex.rxjava3.annotations.Nullable\"/>\n\t\t</module>\n\t\t<module name=\"RegexpSinglelineJava\">\n\t\t\t<property name=\"id\" value=\"printstacktrace\"/>\n\t\t\t<property name=\"format\" value=\"\\.printStackTrace\\(\\)\"/>\n\t\t\t<property name=\"ignoreComments\" value=\"true\"/>\n\t\t\t<property name=\"message\"\n\t\t\t\t\t  value=\"Using Throwable.printStackTrace() is forbidden. Use logger to print exception\"/>\n\t\t</module>\n\t</module>\n\n\t<module name=\"NewlineAtEndOfFile\"/>\n\t<module name=\"SuppressWarningsFilter\"/>\n</module>\n"
  },
  {
    "path": "config/code-formatter/eclipse.importorder",
    "content": "#Import Order\n0=java\n1=javax\n2=org\n3=com\n4=\n5=jadx\n6=\\#\n"
  },
  {
    "path": "config/code-formatter/eclipse.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<profiles version=\"23\">\n    <profile kind=\"CodeFormatterProfile\" name=\"jadx eclipse\" version=\"23\">\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_ellipsis\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_javadoc_comments\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indentation.size\" value=\"4\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.align_with_spaces\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation\" value=\"2\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_package\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.indent_root_tags\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.enabling_tag\" value=\"@formatter:on\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_record_components\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_logical_operator\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_shift_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_type_parameters\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_compact_loops\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_switch_case_arrow_operator\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_unary_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation\" value=\"separate_lines_if_wrapped\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_ellipsis\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant\" value=\"49\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.text_block_indentation\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.align_type_members_on_columns\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_assignment\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_module_statements\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_permitted_types\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block_in_case\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_header\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_type_annotations\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_method_declaration\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines\" value=\"2147483647\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_resources_in_try\" value=\"80\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_source_code\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_field\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_method\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_not_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_html\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_compact_if\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_empty_lines\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_type_arguments\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_unary_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package\" value=\"49\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_label\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_permitted_types_in_type_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.indent_tag_description\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_record_constructor\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_string_concatenation\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_multiple_fields\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_array_initializer\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_shift_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_shift_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_additive_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.join_lines_in_comments\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_relational_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_import_groups\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_logical_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_imports\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.disabling_tag\" value=\"@formatter:off\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_enum_constants\" value=\"48\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_imports\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_switch_body_block_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_block\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_arrow\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.line_length\" value=\"100\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.use_on_off_tags\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_method_body_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_method_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_additive_operator\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_relational_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_lambda_body\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.compact_else_if\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_relational_operator\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_additive_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_line_comments\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.align_selector_in_method_invocation_on_expression_first_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_record_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_conditional_expression\" value=\"48\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type\" value=\"49\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable\" value=\"49\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_additive_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.join_wrapped_lines\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field\" value=\"49\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_conditional_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_shift_operator\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_code_block_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.tabulation.size\" value=\"4\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer\" value=\"2\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_assignment_operator\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_switch\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method\" value=\"49\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_assertion_message\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_member_type\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_logical_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_relational_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_block_comments\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_body\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_logical_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_permitted_types\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_constant\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_type_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_package\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.indent_parameter_description\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.tabulation.char\" value=\"tab\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_string_concatenation\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.lineSplit\" value=\"140\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch\" value=\"insert\"/>\n    </profile>\n</profiles>\n"
  },
  {
    "path": "config/jflex/.gitignore",
    "content": "SmaliTokenMaker.java\n"
  },
  {
    "path": "config/jflex/README.md",
    "content": "Refer to the following instructions to modify and use to generate SmaliTokenMarker\n\n```shell\njflex SmaliTokenMaker.flex --skel skeleton.default\n```\n"
  },
  {
    "path": "config/jflex/SmaliTokenMaker.flex",
    "content": "/*\n * Generated on 11/22/21, 8:58 PM\n */\npackage jadx.gui.ui.codearea;\n\nimport java.io.*;\nimport javax.swing.text.Segment;\n\nimport org.fife.ui.rsyntaxtextarea.*;\n\n\n/*\n * 用于Smali代码高亮\n * MartinKay@qq.com\n */\n%%\n\n%public\n%class SmaliTokenMaker\n%extends AbstractJFlexCTokenMaker\n%unicode\n/* Case sensitive */\n%type org.fife.ui.rsyntaxtextarea.Token\n\n\n%{\n\n\n\t/**\n\t * Constructor.  This must be here because JFlex does not generate a\n\t * no-parameter constructor.\n\t */\n\tpublic SmaliTokenMaker() {\n\t}\n\n\n\t/**\n\t * Adds the token specified to the current linked list of tokens.\n\t *\n\t * @param tokenType The token's type.\n\t * @see #addToken(int, int, int)\n\t */\n\tprivate void addHyperlinkToken(int start, int end, int tokenType) {\n\t\tint so = start + offsetShift;\n\t\taddToken(zzBuffer, start,end, tokenType, so, true);\n\t}\n\n\n\t/**\n\t * Adds the token specified to the current linked list of tokens.\n\t *\n\t * @param tokenType The token's type.\n\t */\n\tprivate void addToken(int tokenType) {\n\t\taddToken(zzStartRead, zzMarkedPos-1, tokenType);\n\t}\n\n\n\t/**\n\t * Adds the token specified to the current linked list of tokens.\n\t *\n\t * @param tokenType The token's type.\n\t * @see #addHyperlinkToken(int, int, int)\n\t */\n\tprivate void addToken(int start, int end, int tokenType) {\n\t\tint so = start + offsetShift;\n\t\taddToken(zzBuffer, start,end, tokenType, so, false);\n\t}\n\n\n\t/**\n\t * Adds the token specified to the current linked list of tokens.\n\t *\n\t * @param array The character array.\n\t * @param start The starting offset in the array.\n\t * @param end The ending offset in the array.\n\t * @param tokenType The token's type.\n\t * @param startOffset The offset in the document at which this token\n\t *        occurs.\n\t * @param hyperlink Whether this token is a hyperlink.\n\t */\n\tpublic void addToken(char[] array, int start, int end, int tokenType,\n\t\t\t\t\t\tint startOffset, boolean hyperlink) {\n\t\tsuper.addToken(array, start,end, tokenType, startOffset, hyperlink);\n\t\tzzStartRead = zzMarkedPos;\n\t}\n\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\tpublic String[] getLineCommentStartAndEnd(int languageIndex) {\n\t\treturn new String[] { \"#\", null };\n\t}\n\n\n\t/**\n\t * Returns the first token in the linked list of tokens generated\n\t * from <code>text</code>.  This method must be implemented by\n\t * subclasses so they can correctly implement syntax highlighting.\n\t *\n\t * @param text The text from which to get tokens.\n\t * @param initialTokenType The token type we should start with.\n\t * @param startOffset The offset into the document at which\n\t *        <code>text</code> starts.\n\t * @return The first <code>Token</code> in a linked list representing\n\t *         the syntax highlighted text.\n\t */\n\tpublic Token getTokenList(Segment text, int initialTokenType, int startOffset) {\n\n\t\tresetTokenList();\n\t\tthis.offsetShift = -text.offset + startOffset;\n\n\t\t// Start off in the proper state.\n\t\tint state = Token.NULL;\n\t\tswitch (initialTokenType) {\n\t\t\t/* No multi-line comments */\n\t\t\t/* No documentation comments */\n\t\t\tdefault:\n\t\t\t\tstate = Token.NULL;\n\t\t}\n\n\t\ts = text;\n\t\ttry {\n\t\t\tyyreset(zzReader);\n\t\t\tyybegin(state);\n\t\t\treturn yylex();\n\t\t} catch (IOException ioe) {\n\t\t\tioe.printStackTrace();\n\t\t\treturn new TokenImpl();\n\t\t}\n\n\t}\n\n\n\t/**\n\t * Refills the input buffer.\n\t *\n\t * @return      <code>true</code> if EOF was reached, otherwise\n\t *              <code>false</code>.\n\t */\n\tprivate boolean zzRefill() {\n\t\treturn zzCurrentPos>=s.offset+s.count;\n\t}\n\n\n\t/**\n\t * Resets the scanner to read from a new input stream.\n\t * Does not close the old reader.\n\t *\n\t * All internal variables are reset, the old input stream\n\t * <b>cannot</b> be reused (internal buffer is discarded and lost).\n\t * Lexical state is set to <tt>YY_INITIAL</tt>.\n\t *\n\t * @param reader   the new input stream\n\t */\n\tpublic final void yyreset(Reader reader) {\n\t\t// 's' has been updated.\n\t\tzzBuffer = s.array;\n\t\t/*\n\t\t * We replaced the line below with the two below it because zzRefill\n\t\t * no longer \"refills\" the buffer (since the way we do it, it's always\n\t\t * \"full\" the first time through, since it points to the segment's\n\t\t * array).  So, we assign zzEndRead here.\n\t\t */\n\t\t//zzStartRead = zzEndRead = s.offset;\n\t\tzzStartRead = s.offset;\n\t\tzzEndRead = zzStartRead + s.count - 1;\n\t\tzzCurrentPos = zzMarkedPos = zzPushbackPos = s.offset;\n\t\tzzLexicalState = YYINITIAL;\n\t\tzzReader = reader;\n\t\tzzAtBOL  = true;\n\t\tzzAtEOF  = false;\n\t}\n\n%}\n\nLetter\t\t\t\t\t\t\t= [A-Za-z]\nLetterOrUnderscore\t\t\t\t= ({Letter}|\"_\")\nNonzeroDigit\t\t\t\t\t\t= [1-9]\nDigit\t\t\t\t\t\t\t= (\"0\"|{NonzeroDigit})\nHexDigit\t\t\t\t\t\t\t= ({Digit}|[A-Fa-f])\nOctalDigit\t\t\t\t\t\t= ([0-7])\nAnyCharacterButApostropheOrBackSlash\t= ([^\\\\'])\nAnyCharacterButDoubleQuoteOrBackSlash\t= ([^\\\\\\\"\\n])\nEscapedSourceCharacter\t\t\t\t= (\"u\"{HexDigit}{HexDigit}{HexDigit}{HexDigit})\nEscape\t\t\t\t\t\t\t= (\"\\\\\"(([btnfr\\\"'\\\\])|([0123]{OctalDigit}?{OctalDigit}?)|({OctalDigit}{OctalDigit}?)|{EscapedSourceCharacter}))\nNonSeparator\t\t\t\t\t\t= ([^\\t\\f\\r\\n\\ \\(\\)\\{\\}\\[\\]\\;\\,\\.\\=\\>\\<\\!\\~\\?\\:\\+\\-\\*\\/\\&\\|\\^\\%\\\"\\']|\"#\"|\"\\\\\")\nIdentifierStart\t\t\t\t\t= ({LetterOrUnderscore}|\"$\")\nIdentifierPart\t\t\t\t\t\t= ({IdentifierStart}|{Digit}|(\"\\\\\"{EscapedSourceCharacter}))\n\nLineTerminator\t\t\t\t= (\\n)\nWhiteSpace\t\t\t\t= ([ \\t\\f]+)\n\nCharLiteral\t= ([\\']({AnyCharacterButApostropheOrBackSlash}|{Escape})[\\'])\nUnclosedCharLiteral\t\t\t= ([\\'][^\\'\\n]*)\nErrorCharLiteral\t\t\t= ({UnclosedCharLiteral}[\\'])\nStringLiteral\t\t\t\t= ([\\\"]({AnyCharacterButDoubleQuoteOrBackSlash}|{Escape})*[\\\"])\nUnclosedStringLiteral\t\t= ([\\\"]([\\\\].|[^\\\\\\\"])*[^\\\"]?)\nErrorStringLiteral\t\t\t= ({UnclosedStringLiteral}[\\\"])\n\n/* No multi-line comments */\n/* No documentation comments */\nLineCommentBegin\t\t\t= \"#\"\n\nIntegerLiteral\t\t\t= ({Digit}+)\nHexLiteral\t\t\t= (0x{HexDigit}+)\nFloatLiteral\t\t\t= (({Digit}+)(\".\"{Digit}+)?(e[+-]?{Digit}+)? | ({Digit}+)?(\".\"{Digit}+)(e[+-]?{Digit}+)?)\nErrorNumberFormat\t\t\t= (({IntegerLiteral}|{HexLiteral}|{FloatLiteral}){NonSeparator}+)\nBooleanLiteral\t\t\t\t= (\"true\"|\"false\")\n\nSeparator\t\t\t\t\t= ([\\(\\)\\{\\}\\[\\]])\nSeparator2\t\t\t\t= ([\\;,.])\n\nIdentifier\t\t\t\t= ({IdentifierStart}{IdentifierPart}*)\n\nURLGenDelim\t\t\t\t= ([:\\/\\?#\\[\\]@])\nURLSubDelim\t\t\t\t= ([\\!\\$&'\\(\\)\\*\\+,;=])\nURLUnreserved\t\t\t= ({LetterOrUnderscore}|{Digit}|[\\-\\.\\~])\nURLCharacter\t\t\t= ({URLGenDelim}|{URLSubDelim}|{URLUnreserved}|[%])\nURLCharacters\t\t\t= ({URLCharacter}*)\nURLEndCharacter\t\t\t= ([\\/\\$]|{Letter}|{Digit})\nURL\t\t\t\t\t\t= (((https?|f(tp|ile))\"://\"|\"www.\")({URLCharacters}{URLEndCharacter})?)\n\n\n/*  Custom Regex  */\n/* fully-qualified name Rules  */\nSimpleName = ([a-zA-Z0-9_$]*)\nQUALIFIED_TYPE_NAME = (\"L\"({SimpleName}{SLASH})*{SimpleName}*\";\")\n/* Types */\nVOID_TYPE = (\"V\")\nBOOLEAN_TYPE = (\"Z\")\nBYTE_TYPE = (\"B\")\nSHORT_TYPE = (\"S\")\nCHAR_TYPE = (\"C\")\nINT_TYPE = (\"I\")\nLONG_TYPE = (\"J\")\nFLOAT_TYPE = (\"F\")\nDOUBLE_TYPE = (\"D\")\n/* Multi Args Types Highlight  */\nMULTI_ARGS_TYPES = (({BOOLEAN_TYPE}|{BYTE_TYPE}|{SHORT_TYPE}|{CHAR_TYPE}|{INT_TYPE}|{LONG_TYPE}|{FLOAT_TYPE}|{DOUBLE_TYPE})+);\n\n/* Types fully-qualified name */\nCOMPOUND_METHOD_ARG_LITERAL = (({BOOLEAN_TYPE}|{BYTE_TYPE}|{SHORT_TYPE}|{CHAR_TYPE}|{INT_TYPE}|{LONG_TYPE}|{FLOAT_TYPE}|{DOUBLE_TYPE})+{QUALIFIED_TYPE_NAME})\n\n\nLBRACK = (\"[\")\nRBRACK = (\"]\")\nLPAREN = (\"(\")\nRPAREN = (\")\")\nLBRACE = (\"{\")\nRBRACE = (\"}\")\nCOLON = (\":\")\nASSIGN = (\"=\")\nDOT = (\".\")\nSUB = (\"-\")\nCOMMA = (\",\")\nSLASH = (\"/\")\nLT = (\"<\")\nGT = (\">\")\nARROW = (\"->\")\nSEMI = (\";\")\nARROW_FUNCTION = ({ARROW}[a-zA-Z_$<>]*)\nCustomSeparator = ({Separator}|{ARROW_FUNCTION})\n\n/* Register */\nVREGISTER = (\"v\"(\"0\"|[1-9])*)\nPREGISTER = (\"p\"(\"0\"|[1-9])*)\n\n/* Flags  */\nFLAG_PSWITCH = (\":pswitch_\"{SimpleName})\nFLAG_PSWITCH_DATA = (\":pswitch_data_\"{SimpleName})\nFLAG_GOTO = (\":goto_\"{SimpleName})\nFLAG_COND = (\":cond_\"{SimpleName})\nFLAG_TRY_START = (\":try_start_\"{SimpleName})\nFLAG_TRY_END = (\":try_end_\"{SimpleName})\nFLAG_CATCH = (\":catch_\"{SimpleName})\nFLAG_CATCHALL = (\":catchall_\"{SimpleName})\nFLAG_ARRAY = (\":array_\"{SimpleName})\n\n/* No string state */\n/* No char state */\n/* No MLC state */\n/* No documentation comment state */\n%state EOL_COMMENT\n\n%%\n\n<YYINITIAL> {\n\n\t/* Keywords Instructions Highlight */\n\"nop\" |\n\"move\" |\n\"move/from16\" |\n\"move/16\" |\n\"move-wide\" |\n\"move-wide/from16\" |\n\"move-wide/16\" |\n\"move-object\" |\n\"move-object/from16\" |\n\"move-object/16\" |\n\"move-result\" |\n\"move-result-wide\" |\n\"move-result-object\" |\n\"move-exception\" |\n\"return-void\" |\n\"return\" |\n\"return-wide\" |\n\"return-object\" |\n\"const/4\" |\n\"const/16\" |\n\"const\" |\n\"const/high16\" |\n\"const-wide/16\" |\n\"const-wide/32\" |\n\"const-wide\" |\n\"const-wide/high16\" |\n\"const-string\" |\n\"const-string/jumbo\" |\n\"const-class\" |\n\"monitor-enter\" |\n\"monitor-exit\" |\n\"check-cast\" |\n\"instance-of\" |\n\"array-length\" |\n\"new-instance\" |\n\"new-array\" |\n\"filled-new-array\" |\n\"filled-new-array/range\" |\n\"fill-array-data\" |\n\"throw\" |\n\"goto\" |\n\"goto/16\" |\n\"goto/32\" |\n\"cmpl-float\" |\n\"cmpg-float\" |\n\"cmpl-double\" |\n\"cmpg-double\" |\n\"cmp-long\" |\n\"if-eq\" |\n\"if-ne\" |\n\"if-lt\" |\n\"if-ge\" |\n\"if-gt\" |\n\"if-le\" |\n\"if-eqz\" |\n\"if-nez\" |\n\"if-ltz\" |\n\"if-gez\" |\n\"if-gtz\" |\n\"if-lez\" |\n\"aget\" |\n\"aget-wide\" |\n\"aget-object\" |\n\"aget-boolean\" |\n\"aget-byte\" |\n\"aget-char\" |\n\"aget-short\" |\n\"aput\" |\n\"aput-wide\" |\n\"aput-object\" |\n\"aput-boolean\" |\n\"aput-byte\" |\n\"aput-char\" |\n\"aput-short\" |\n\"iget\" |\n\"iget-wide\" |\n\"iget-object\" |\n\"iget-boolean\" |\n\"iget-byte\" |\n\"iget-char\" |\n\"iget-short\" |\n\"iput\" |\n\"iput-wide\" |\n\"iput-object\" |\n\"iput-boolean\" |\n\"iput-byte\" |\n\"iput-char\" |\n\"iput-short\" |\n\"sget\" |\n\"sget-wide\" |\n\"sget-object\" |\n\"sget-boolean\" |\n\"sget-byte\" |\n\"sget-char\" |\n\"sget-short\" |\n\"sput\" |\n\"sput-wide\" |\n\"sput-object\" |\n\"sput-boolean\" |\n\"sput-byte\" |\n\"sput-char\" |\n\"sput-short\" |\n\"invoke-virtual\" |\n\"invoke-super\" |\n\"invoke-direct\" |\n\"invoke-static\" |\n\"invoke-interface\" |\n\"invoke-virtual/range\" |\n\"invoke-super/range\" |\n\"invoke-direct/range\" |\n\"invoke-static/range\" |\n\"invoke-interface/range\" |\n\"neg-int\" |\n\"not-int\" |\n\"neg-long\" |\n\"not-long\" |\n\"neg-float\" |\n\"neg-double\" |\n\"int-to-long\" |\n\"int-to-float\" |\n\"int-to-double\" |\n\"long-to-int\" |\n\"long-to-float\" |\n\"long-to-double\" |\n\"float-to-int\" |\n\"float-to-long\" |\n\"float-to-double\" |\n\"double-to-int\" |\n\"double-to-long\" |\n\"double-to-float\" |\n\"int-to-byte\" |\n\"int-to-char\" |\n\"int-to-short\" |\n\"add-int\" |\n\"sub-int\" |\n\"mul-int\" |\n\"div-int\" |\n\"rem-int\" |\n\"and-int\" |\n\"or-int\" |\n\"xor-int\" |\n\"shl-int\" |\n\"shr-int\" |\n\"ushr-int\" |\n\"add-long\" |\n\"sub-long\" |\n\"mul-long\" |\n\"div-long\" |\n\"rem-long\" |\n\"and-long\" |\n\"or-long\" |\n\"xor-long\" |\n\"shl-long\" |\n\"shr-long\" |\n\"ushr-long\" |\n\"add-float\" |\n\"sub-float\" |\n\"mul-float\" |\n\"div-float\" |\n\"rem-float\" |\n\"add-double\" |\n\"sub-double\" |\n\"mul-double\" |\n\"div-double\" |\n\"rem-double\" |\n\"add-int/2addr\" |\n\"sub-int/2addr\" |\n\"mul-int/2addr\" |\n\"div-int/2addr\" |\n\"rem-int/2addr\" |\n\"and-int/2addr\" |\n\"or-int/2addr\" |\n\"xor-int/2addr\" |\n\"shl-int/2addr\" |\n\"shr-int/2addr\" |\n\"ushr-int/2addr\" |\n\"add-long/2addr\" |\n\"sub-long/2addr\" |\n\"mul-long/2addr\" |\n\"div-long/2addr\" |\n\"rem-long/2addr\" |\n\"and-long/2addr\" |\n\"or-long/2addr\" |\n\"xor-long/2addr\" |\n\"shl-long/2addr\" |\n\"shr-long/2addr\" |\n\"ushr-long/2addr\" |\n\"add-float/2addr\" |\n\"sub-float/2addr\" |\n\"mul-float/2addr\" |\n\"div-float/2addr\" |\n\"rem-float/2addr\" |\n\"add-double/2addr\" |\n\"sub-double/2addr\" |\n\"mul-double/2addr\" |\n\"div-double/2addr\" |\n\"rem-double/2addr\" |\n\"add-int/lit16\" |\n\"rsub-int\" |\n\"mul-int/lit16\" |\n\"div-int/lit16\" |\n\"rem-int/lit16\" |\n\"and-int/lit16\" |\n\"or-int/lit16\" |\n\"xor-int/lit16\" |\n\"add-int/lit8\" |\n\"rsub-int/lit8\" |\n\"mul-int/lit8\" |\n\"div-int/lit8\" |\n\"rem-int/lit8\" |\n\"and-int/lit8\" |\n\"or-int/lit8\" |\n\"xor-int/lit8\" |\n\"shl-int/lit8\" |\n\"shr-int/lit8\" |\n\"ushr-int/lit8\" |\n\"invoke-polymorphic\" |\n\"invoke-polymorphic/range\" |\n\"invoke-custom\" |\n\"invoke-custom/range\" |\n\"const-method-handle\" |\n\"const-method-type\" |\n\"packed-switch\" |\n\"sparse-switch\"\t\t{ addToken(Token.FUNCTION); }\n\n\t/* Keywords Modifiers(IDENTIFIER标识符、修饰符) Highlight */\n\"public\" |\n\"private\" |\n\"protected\" |\n\"final\" |\n\"annotation\" |\n\"static\" |\n\"synthetic\" |\n\"constructor\" |\n\"abstract\" |\n\"enum\" |\n\"interface\" |\n\"transient\" |\n\"bridge\" |\n\"declared-synchronized\" |\n\"volatile\" |\n\"strictfp\" |\n\"varargs\" |\n\"native\"\t\t{ addToken(Token.RESERVED_WORD); }\n\n\n\n\n\t/* Keywords Directives Highlight */\n\".method\" |\n\".end method\" |\n\".implements\" |\n\".class\" |\n\".prologue\" |\n\".source\" |\n\".super\" |\n\".field\" |\n\".end field\" |\n\".registers\" |\n\".locals\" |\n\".param\" |\n\".line\" |\n\".catch\" |\n\".catchall\" |\n\".annotation\" |\n\".end annotation\" |\n\".local\" |\n\".end local\" |\n\".restart local\" |\n\".packed-switch\" |\n\".end packed-switch\" |\n\".array-data\" |\n\".end array-data\" |\n\".sparse-switch\" |\n\".end sparse-switch\" |\n\".end param\"\t\t{ addToken(Token.RESERVED_WORD_2); }\n\n\n\t/* VARIABLE Register Highlight */\n{VREGISTER} |\n{PREGISTER}\t\t{ addToken(Token.VARIABLE); }\n\n\n\n\t/* Data types Highlight */\n{QUALIFIED_TYPE_NAME} |\n{COMPOUND_METHOD_ARG_LITERAL} |\n{MULTI_ARGS_TYPES} |\n{QUALIFIED_TYPE_NAME} |\n{VOID_TYPE} |\n{BOOLEAN_TYPE} |\n{BYTE_TYPE} |\n{SHORT_TYPE} |\n{CHAR_TYPE} |\n{INT_TYPE} |\n{LONG_TYPE} |\n{FLOAT_TYPE} |\n{DOUBLE_TYPE} \t{ addToken(Token.DATA_TYPE); }\n\n /* FLAGS */\n{FLAG_PSWITCH} |\n{FLAG_PSWITCH_DATA} |\n{FLAG_GOTO} |\n{FLAG_COND} |\n{FLAG_TRY_START} |\n{FLAG_TRY_END} |\n{FLAG_CATCHALL} |\n{FLAG_ARRAY} |\n{FLAG_CATCH}  \t{ addToken(Token.MARKUP_TAG_NAME); }\n\n\t/* Functions */\n\t/* No functions */\n\n\t{BooleanLiteral}\t\t\t{ addToken(Token.LITERAL_BOOLEAN); }\n\n\t{LineTerminator}\t\t\t\t{ addNullToken(); return firstToken; }\n\n\t{Identifier}\t\t\t\t\t{ addToken(Token.IDENTIFIER); }\n\n\t{WhiteSpace}\t\t\t\t\t{ addToken(Token.WHITESPACE); }\n\n\t/* String/Character literals. */\n\t{CharLiteral}\t\t\t\t{ addToken(Token.LITERAL_CHAR); }\n{UnclosedCharLiteral}\t\t{ addToken(Token.ERROR_CHAR); addNullToken(); return firstToken; }\n{ErrorCharLiteral}\t\t\t{ addToken(Token.ERROR_CHAR); }\n\t{StringLiteral}\t\t\t\t{ addToken(Token.LITERAL_STRING_DOUBLE_QUOTE); }\n{UnclosedStringLiteral}\t\t{ addToken(Token.ERROR_STRING_DOUBLE); addNullToken(); return firstToken; }\n{ErrorStringLiteral}\t\t\t{ addToken(Token.ERROR_STRING_DOUBLE); }\n\n\t/* Comment literals. */\n\t/* No multi-line comments */\n\t/* No documentation comments */\n\t{LineCommentBegin}\t\t\t{ start = zzMarkedPos-1; yybegin(EOL_COMMENT); }\n\n\t/* Separators. */\n\t{CustomSeparator}\t\t\t\t\t{ addToken(Token.SEPARATOR); }\n\t{Separator2}\t\t\t\t\t{ addToken(Token.IDENTIFIER); }\n\n\t/* Operators. */\n\"!\" |\n\";\" |\n\".\" |\n\"=\" |\n\"/\" |\n\"'\" |\n\"(\" |\n\")\" |\n\",\" |\n\"->\" |\n\";->\" |\n\"<\" |\n\">\" |\n\"@\" |\n\"[\" |\n\"]\" |\n\"{\" |\n\"}\"\t\t{ addToken(Token.OPERATOR); }\n\n\t/* Numbers */\n\t{IntegerLiteral}\t\t\t\t{ addToken(Token.LITERAL_NUMBER_DECIMAL_INT); }\n\t{HexLiteral}\t\t\t\t\t{ addToken(Token.LITERAL_NUMBER_HEXADECIMAL); }\n\t{FloatLiteral}\t\t\t\t\t{ addToken(Token.LITERAL_NUMBER_FLOAT); }\n\t{ErrorNumberFormat}\t\t\t{ addToken(Token.ERROR_NUMBER_FORMAT); }\n\n\t/* Ended with a line not in a string or comment. */\n\t<<EOF>>\t\t\t\t\t\t{ addNullToken(); return firstToken; }\n\n\t/* Catch any other (unhandled) characters. */\n\t.\t\t\t\t\t\t\t{ addToken(Token.IDENTIFIER); }\n\n}\n\n\n/* No char state */\n\n/* No string state */\n\n/* No multi-line comment state */\n\n/* No documentation comment state */\n\n<EOL_COMMENT> {\n\t[^hwf\\n]+\t\t\t\t{}\n\t{URL}\t\t\t\t\t{ int temp=zzStartRead; addToken(start,zzStartRead-1, Token.COMMENT_EOL); addHyperlinkToken(temp,zzMarkedPos-1, Token.COMMENT_EOL); start = zzMarkedPos; }\n\t[hwf]\t\t\t\t\t{}\n\t\\n\t\t\t\t\t\t{ addToken(start,zzStartRead-1, Token.COMMENT_EOL); addNullToken(); return firstToken; }\n\t<<EOF>>\t\t\t\t\t{ addToken(start,zzStartRead-1, Token.COMMENT_EOL); addNullToken(); return firstToken; }\n}\n"
  },
  {
    "path": "config/jflex/skeleton.default",
    "content": "\n  /** This character denotes the end of file */\n  public static final int YYEOF = -1;\n\n  /** initial size of the lookahead buffer */\n--- private static final int ZZ_BUFFERSIZE = ...;\n\n  /** lexical states */\n---  lexical states, charmap\n\n  /* error codes */\n  private static final int ZZ_UNKNOWN_ERROR = 0;\n  private static final int ZZ_NO_MATCH = 1;\n  private static final int ZZ_PUSHBACK_2BIG = 2;\n\n  /* error messages for the codes above */\n  private static final String ZZ_ERROR_MSG[] = {\n    \"Unkown internal scanner error\",\n    \"Error: could not match input\",\n    \"Error: pushback value was too large\"\n  };\n\n--- isFinal list\n  /** the input device */\n  private java.io.Reader zzReader;\n\n  /** the current state of the DFA */\n  private int zzState;\n\n  /** the current lexical state */\n  private int zzLexicalState = YYINITIAL;\n\n  /** this buffer contains the current text to be matched and is\n      the source of the yytext() string */\n  private char zzBuffer[];\n\n  /** the textposition at the last accepting state */\n  private int zzMarkedPos;\n\n  /** the textposition at the last state to be included in yytext */\n  private int zzPushbackPos;\n\n  /** the current text position in the buffer */\n  private int zzCurrentPos;\n\n  /** startRead marks the beginning of the yytext() string in the buffer */\n  private int zzStartRead;\n\n  /** endRead marks the last character in the buffer, that has been read\n      from input */\n  private int zzEndRead;\n\n  /** number of newlines encountered up to the start of the matched text */\n  private int yyline;\n\n  /** the number of characters up to the start of the matched text */\n  private int yychar;\n\n  /**\n   * the number of characters from the last newline up to the start of the\n   * matched text\n   */\n  private int yycolumn;\n\n  /**\n   * zzAtBOL == true <=> the scanner is currently at the beginning of a line\n   */\n  private boolean zzAtBOL = true;\n\n  /** zzAtEOF == true <=> the scanner is at the EOF */\n  private boolean zzAtEOF;\n\n--- user class code\n\n  /**\n   * Creates a new scanner\n   * There is also a java.io.InputStream version of this constructor.\n   *\n   * @param   in  the java.io.Reader to read input from.\n   */\n--- constructor declaration\n\n\n  /**\n   * Closes the input stream.\n   */\n  public final void yyclose() throws java.io.IOException {\n    zzAtEOF = true;            /* indicate end of file */\n    zzEndRead = zzStartRead;  /* invalidate buffer    */\n\n    if (zzReader != null)\n      zzReader.close();\n  }\n\n\n  /**\n   * Enters a new lexical state\n   *\n   * @param newState the new lexical state\n   */\n  public final void yybegin(int newState) {\n    zzLexicalState = newState;\n  }\n\n  public final int yystate() {\n    return zzLexicalState;\n  }\n\n  /**\n   * Returns the text matched by the current regular expression.\n   */\n  public final String yytext() {\n    return new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead );\n  }\n\n\n  /**\n   * Returns the character at position <tt>pos</tt> from the\n   * matched text.\n   *\n   * It is equivalent to yytext().charAt(pos), but faster\n   *\n   * @param pos the position of the character to fetch.\n   *            A value from 0 to yylength()-1.\n   *\n   * @return the character at position pos\n   */\n  public final char yycharat(int pos) {\n    return zzBuffer[zzStartRead+pos];\n  }\n\n\n  /**\n   * Returns the length of the matched text region.\n   */\n  public final int yylength() {\n    return zzMarkedPos-zzStartRead;\n  }\n\n\n  /**\n   * Reports an error that occured while scanning.\n   *\n   * In a wellformed scanner (no or only correct usage of\n   * yypushback(int) and a match-all fallback rule) this method\n   * will only be called with things that \"Can't Possibly Happen\".\n   * If this method is called, something is seriously wrong\n   * (e.g. a JFlex bug producing a faulty scanner etc.).\n   *\n   * Usual syntax/scanner level error handling should be done\n   * in error fallback rules.\n   *\n   * @param   errorCode  the code of the errormessage to display\n   */\n--- zzScanError declaration\n    String message;\n    try {\n      message = ZZ_ERROR_MSG[errorCode];\n    }\n    catch (ArrayIndexOutOfBoundsException e) {\n      message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR];\n    }\n\n--- throws clause\n  }\n\n\n  /**\n   * Pushes the specified amount of characters back into the input stream.\n   *\n   * They will be read again by then next call of the scanning method\n   *\n   * @param number  the number of characters to be read again.\n   *                This number must not be greater than yylength()!\n   */\n--- yypushback decl (contains zzScanError exception)\n    if ( number > yylength() )\n      zzScanError(ZZ_PUSHBACK_2BIG);\n\n    zzMarkedPos -= number;\n  }\n\n\n--- zzDoEOF\n  /**\n   * Resumes scanning until the next regular expression is matched,\n   * the end of input is encountered or an I/O-Error occurs.\n   *\n   * @return      the next token\n   * @exception   java.io.IOException  if any I/O-Error occurs\n   */\n--- yylex declaration\n    int zzInput;\n    int zzAction;\n\n    // cached fields:\n    int zzCurrentPosL;\n    int zzMarkedPosL;\n    int zzEndReadL = zzEndRead;\n    char [] zzBufferL = zzBuffer;\n    char [] zzCMapL = ZZ_CMAP;\n\n--- local declarations\n\n    while (true) {\n      zzMarkedPosL = zzMarkedPos;\n\n--- start admin (line, char, col count)\n      zzAction = -1;\n\n      zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;\n\n--- start admin (lexstate etc)\n\n      zzForAction: {\n        while (true) {\n\n--- next input, line, col, char count, next transition, isFinal action\n            zzAction = zzState;\n            zzMarkedPosL = zzCurrentPosL;\n--- line count update\n          }\n\n        }\n      }\n\n      // store back cached position\n      zzMarkedPos = zzMarkedPosL;\n--- char count update\n\n--- actions\n        default:\n          if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {\n            zzAtEOF = true;\n--- eofvalue\n          }\n          else {\n--- no match\n          }\n      }\n    }\n  }\n\n--- main\n\n}\n"
  },
  {
    "path": "contrib/jadx-gui.desktop",
    "content": "[Desktop Entry]\nName=JADX GUI\nComment=Dex to Java decompiler\nIcon=jadx\nExec=jadx-gui %f\nTerminal=false\nType=Application\nCategories=Development;Java;\nKeywords=Java;Decompiler;\nStartupWMClass=jadx-gui-JadxGUI\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionSha256Sum=72f44c9f8ebcb1af43838f45ee5c4aa9c5444898b3468ab3f4af7b6076c5bc3f\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-9.2.1-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "org.gradle.warning.mode=all\norg.gradle.parallel=true\norg.gradle.caching=true\n\n### Disable configuration cache for now: causing issues with spotless and version plugins\n# org.gradle.configuration-cache=true\n# org.gradle.configuration-cache.problems=warn\n\n# Flags for google-java-format (optimize imports by spotless) for Java >= 16.\n# Java < 9 will ignore unsupported flags (thanks to -XX:+IgnoreUnrecognizedVMOptions)\norg.gradle.jvmargs=-XX:+IgnoreUnrecognizedVMOptions \\\n  --add-exports='jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED' \\\n  --add-exports='jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED' \\\n  --add-exports='jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED' \\\n  --add-exports='jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED' \\\n  --add-exports='jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED'\n"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\n\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    if ! command -v java >/dev/null 2>&1\n    then\n        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.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\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='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@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\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\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=\"-Xmx64m\" \"-Xms64m\"\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% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\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 execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 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\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "jadx-cli/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-java\")\n\tid(\"jadx-library\")\n\tid(\"application\")\n\n\t// use shadow only for application scripts, jar will be copied from jadx-gui\n\tid(\"com.gradleup.shadow\") version \"8.3.8\"\n}\n\ndependencies {\n\timplementation(project(\":jadx-core\"))\n\timplementation(project(\":jadx-plugins-tools\"))\n\timplementation(project(\":jadx-commons:jadx-app-commons\"))\n\n\truntimeOnly(project(\":jadx-plugins:jadx-dex-input\"))\n\truntimeOnly(project(\":jadx-plugins:jadx-java-input\"))\n\truntimeOnly(project(\":jadx-plugins:jadx-java-convert\"))\n\truntimeOnly(project(\":jadx-plugins:jadx-smali-input\"))\n\truntimeOnly(project(\":jadx-plugins:jadx-rename-mappings\"))\n\truntimeOnly(project(\":jadx-plugins:jadx-kotlin-metadata\"))\n\truntimeOnly(project(\":jadx-plugins:jadx-kotlin-source-debug-extension\"))\n\truntimeOnly(project(\":jadx-plugins:jadx-xapk-input\"))\n\truntimeOnly(project(\":jadx-plugins:jadx-aab-input\"))\n\truntimeOnly(project(\":jadx-plugins:jadx-apkm-input\"))\n\truntimeOnly(project(\":jadx-plugins:jadx-apks-input\"))\n\n\timplementation(\"org.jcommander:jcommander:2.0\")\n\timplementation(\"ch.qos.logback:logback-classic:1.5.22\")\n\timplementation(\"com.google.code.gson:gson:2.13.2\")\n}\n\napplication {\n\tapplicationName = \"jadx\"\n\tmainClass.set(\"jadx.cli.JadxCLI\")\n\tapplicationDefaultJvmArgs =\n\t\tlistOf(\n\t\t\t\"-XX:+IgnoreUnrecognizedVMOptions\",\n\t\t\t\"-Xms256M\",\n\t\t\t\"-XX:MaxRAMPercentage=70.0\",\n\t\t\t\"-XX:ParallelGCThreads=3\",\n\t\t\t// disable zip checks (#1962)\n\t\t\t\"-Djdk.util.zip.disableZip64ExtraFieldValidation=true\",\n\t\t\t// Foreign API access for 'directories' library (Windows only)\n\t\t\t\"--enable-native-access=ALL-UNNAMED\",\n\t\t)\n\tapplicationDistribution.from(\"$rootDir\") {\n\t\tinclude(\"README.md\")\n\t\tinclude(\"NOTICE\")\n\t\tinclude(\"LICENSE\")\n\t}\n}\n\ntasks.shadowJar {\n\t// shadow jar not needed\n\tconfigurations = listOf()\n}\n"
  },
  {
    "path": "jadx-cli/src/main/java/jadx/cli/JCommanderWrapper.java",
    "content": "package jadx.cli;\n\nimport java.io.PrintStream;\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.function.Supplier;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport com.beust.jcommander.JCommander;\nimport com.beust.jcommander.Parameter;\nimport com.beust.jcommander.ParameterDescription;\nimport com.beust.jcommander.ParameterException;\nimport com.beust.jcommander.Parameterized;\n\nimport jadx.api.JadxDecompiler;\nimport jadx.api.plugins.JadxPluginInfo;\nimport jadx.api.plugins.options.JadxPluginOptions;\nimport jadx.api.plugins.options.OptionDescription;\nimport jadx.core.plugins.JadxPluginManager;\nimport jadx.core.plugins.PluginContext;\nimport jadx.core.utils.Utils;\n\npublic class JCommanderWrapper {\n\tprivate final JCommander jc;\n\tprivate final JadxCLIArgs argsObj;\n\n\tpublic JCommanderWrapper(JadxCLIArgs argsObj) {\n\t\tJCommander.Builder builder = JCommander.newBuilder().addObject(argsObj);\n\t\tbuilder.acceptUnknownOptions(true); // workaround for \"default\" command\n\t\tJadxCLICommands.append(builder);\n\t\tthis.jc = builder.build();\n\t\tthis.argsObj = argsObj;\n\t}\n\n\tpublic boolean parse(String[] args) {\n\t\ttry {\n\t\t\tString[] fixedArgs = fixArgsForEmptySaveConfig(args);\n\t\t\tjc.parse(fixedArgs);\n\t\t\tapplyFiles(argsObj);\n\t\t\treturn true;\n\t\t} catch (ParameterException e) {\n\t\t\tSystem.err.println(\"Arguments parse error: \" + e.getMessage());\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic void overrideProvided(JadxCLIArgs obj) {\n\t\tapplyFiles(obj);\n\t\tfor (ParameterDescription parameter : jc.getParameters()) {\n\t\t\tif (parameter.isAssigned()) {\n\t\t\t\toverrideProperty(obj, parameter);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic boolean processCommands() {\n\t\tString parsedCommand = jc.getParsedCommand();\n\t\tif (parsedCommand == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn JadxCLICommands.process(this, jc, parsedCommand);\n\t}\n\n\t/**\n\t * The main parameter parsing doesn't work if accepting unknown options\n\t */\n\tprivate void applyFiles(JadxCLIArgs argsObj) {\n\t\targsObj.setFiles(jc.getUnknownOptions());\n\t}\n\n\t/**\n\t * Override assigned field value to obj\n\t */\n\tprivate static void overrideProperty(JadxCLIArgs obj, ParameterDescription parameter) {\n\t\tParameterized parameterized = parameter.getParameterized();\n\t\tObject providedValue = parameterized.get(parameter.getObject());\n\t\tObject newValue = mergeValues(parameterized.getType(), providedValue, () -> parameterized.get(obj));\n\t\tparameterized.set(obj, newValue);\n\t}\n\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tprivate static Object mergeValues(Class<?> type, Object value, Supplier<Object> prevValueProvider) {\n\t\tif (type.isAssignableFrom(Map.class)) {\n\t\t\t// merge maps instead replacing whole map\n\t\t\tMap prevMap = (Map) prevValueProvider.get();\n\t\t\treturn Utils.mergeMaps(prevMap, (Map) value); // value map will override keys in prevMap\n\t\t}\n\t\t// simple override\n\t\treturn value;\n\t}\n\n\t/**\n\t * Workaround to allow empty value (i.e. zero arity) for '--save-config' option\n\t * Insert empty string arg if another option start right after this one, or it is a last one.\n\t */\n\tprivate String[] fixArgsForEmptySaveConfig(String[] args) {\n\t\tint len = args.length;\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tString arg = args[i];\n\t\t\tif (arg.equals(\"--save-config\")) {\n\t\t\t\tint next = i + 1;\n\t\t\t\tif (next == len) {\n\t\t\t\t\treturn insertEmptyArg(args, next, true);\n\t\t\t\t}\n\t\t\t\tif (next < len) {\n\t\t\t\t\tString nextArg = args[next];\n\t\t\t\t\tif (nextArg.startsWith(\"-\")) {\n\t\t\t\t\t\treturn insertEmptyArg(args, next, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn args;\n\t}\n\n\tprivate static String[] insertEmptyArg(String[] args, int i, boolean add) {\n\t\tList<String> strings = new ArrayList<>(Arrays.asList(args));\n\t\tif (add) {\n\t\t\tstrings.add(\"\");\n\t\t} else {\n\t\t\tstrings.add(i, \"\");\n\t\t}\n\t\treturn strings.toArray(new String[0]);\n\t}\n\n\tpublic void printUsage() {\n\t\tLogHelper.setLogLevel(LogHelper.LogLevelEnum.ERROR); // mute logger while printing help\n\n\t\t// print usage in not sorted fields order (by default sorted by description)\n\t\tPrintStream out = System.out;\n\t\tout.println();\n\t\tout.println(\"jadx - dex to java decompiler, version: \" + JadxDecompiler.getVersion());\n\t\tout.println();\n\t\tout.println(\"usage: jadx [command] [options] \" + jc.getMainParameterDescription());\n\n\t\tout.println(\"commands (use '<command> --help' for command options):\");\n\t\tfor (String command : jc.getCommands().keySet()) {\n\t\t\tout.println(\"  \" + command + \"\\t  - \" + jc.getUsageFormatter().getCommandDescription(command));\n\t\t}\n\t\tout.println();\n\n\t\tint maxNamesLen = printOptions(jc, out, true);\n\t\tout.println(appendPluginOptions(maxNamesLen));\n\t\tout.println();\n\t\tout.println(\"Environment variables:\");\n\t\tout.println(\"  JADX_DISABLE_XML_SECURITY - set to 'true' to disable all security checks for XML files\");\n\t\tout.println(\"  JADX_DISABLE_ZIP_SECURITY - set to 'true' to disable all security checks for zip files\");\n\t\tout.println(\"  JADX_ZIP_MAX_ENTRIES_COUNT - maximum allowed number of entries in zip files (default: 100 000)\");\n\t\tout.println(\"  JADX_CONFIG_DIR - custom config directory, using system by default\");\n\t\tout.println(\"  JADX_CACHE_DIR - custom cache directory, using system by default\");\n\t\tout.println(\"  JADX_TMP_DIR - custom temp directory, using system by default\");\n\t\tout.println();\n\t\tout.println(\"Examples:\");\n\t\tout.println(\"  jadx -d out classes.dex\");\n\t\tout.println(\"  jadx --rename-flags \\\"none\\\" classes.dex\");\n\t\tout.println(\"  jadx --rename-flags \\\"valid, printable\\\" classes.dex\");\n\t\tout.println(\"  jadx --log-level ERROR app.apk\");\n\t\tout.println(\"  jadx -Pdex-input.verify-checksum=no app.apk\");\n\t}\n\n\tpublic void printUsage(JCommander subCommander) {\n\t\tPrintStream out = System.out;\n\t\tout.println(\"usage: \" + subCommander.getProgramName() + \" [options]\");\n\t\tprintOptions(subCommander, out, false);\n\t}\n\n\tprivate static int printOptions(JCommander jc, PrintStream out, boolean addDefaults) {\n\t\tout.println(\"options:\");\n\n\t\tList<ParameterDescription> params = jc.getParameters();\n\t\tMap<String, ParameterDescription> paramsMap = new HashMap<>(params.size());\n\t\tint maxNamesLen = 0;\n\t\tfor (ParameterDescription p : params) {\n\t\t\tparamsMap.put(p.getParameterized().getName(), p);\n\t\t\tint len = p.getNames().length();\n\t\t\tString valueDesc = getValueDesc(p);\n\t\t\tif (valueDesc != null) {\n\t\t\t\tlen += 1 + valueDesc.length();\n\t\t\t}\n\t\t\tmaxNamesLen = Math.max(maxNamesLen, len);\n\t\t}\n\t\tmaxNamesLen += 3;\n\n\t\tObject args = jc.getObjects().get(0);\n\t\tfor (Field f : getFields(args.getClass())) {\n\t\t\tString name = f.getName();\n\t\t\tParameterDescription p = paramsMap.get(name);\n\t\t\tif (p == null || p.getParameter().hidden()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tStringBuilder opt = new StringBuilder();\n\t\t\topt.append(\"  \").append(p.getNames());\n\t\t\tString valueDesc = getValueDesc(p);\n\t\t\tif (valueDesc != null) {\n\t\t\t\topt.append(' ').append(valueDesc);\n\t\t\t}\n\t\t\taddSpaces(opt, maxNamesLen - opt.length());\n\t\t\tString description = p.getDescription();\n\t\t\tif (description.contains(\"\\n\")) {\n\t\t\t\tString[] lines = description.split(\"\\n\");\n\t\t\t\topt.append(\"- \").append(lines[0]);\n\t\t\t\tfor (int i = 1; i < lines.length; i++) {\n\t\t\t\t\topt.append('\\n');\n\t\t\t\t\taddSpaces(opt, maxNamesLen + 2);\n\t\t\t\t\topt.append(lines[i]);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\topt.append(\"- \").append(description);\n\t\t\t}\n\t\t\tif (addDefaults) {\n\t\t\t\tString defaultValue = getDefaultValue(args, f);\n\t\t\t\tif (defaultValue != null\n\t\t\t\t\t\t&& !defaultValue.isEmpty()\n\t\t\t\t\t\t&& !description.contains(\"(default)\")) {\n\t\t\t\t\topt.append(\", default: \").append(defaultValue);\n\t\t\t\t}\n\t\t\t}\n\t\t\tout.println(opt);\n\t\t}\n\t\treturn maxNamesLen;\n\t}\n\n\tprivate static @Nullable String getValueDesc(ParameterDescription p) {\n\t\tParameter parameterAnnotation = p.getParameterAnnotation();\n\t\treturn parameterAnnotation == null ? null : parameterAnnotation.defaultValueDescription();\n\t}\n\n\t/**\n\t * Get all declared fields of the specified class and all super classes\n\t */\n\tprivate static List<Field> getFields(Class<?> clazz) {\n\t\tList<Field> fieldList = new ArrayList<>();\n\t\twhile (clazz != null) {\n\t\t\tfieldList.addAll(Arrays.asList(clazz.getDeclaredFields()));\n\t\t\tclazz = clazz.getSuperclass();\n\t\t}\n\t\treturn fieldList;\n\t}\n\n\t@Nullable\n\tprivate static String getDefaultValue(Object args, Field f) {\n\t\ttry {\n\t\t\tClass<?> fieldType = f.getType();\n\t\t\tif (fieldType == int.class) {\n\t\t\t\treturn Integer.toString(f.getInt(args));\n\t\t\t}\n\t\t\tif (fieldType == String.class) {\n\t\t\t\treturn (String) f.get(args);\n\t\t\t}\n\t\t\tif (Enum.class.isAssignableFrom(fieldType)) {\n\t\t\t\tEnum<?> val = (Enum<?>) f.get(args);\n\t\t\t\tif (val != null) {\n\t\t\t\t\treturn val.name().toLowerCase(Locale.ROOT);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\t// ignore\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static void addSpaces(StringBuilder str, int count) {\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tstr.append(' ');\n\t\t}\n\t}\n\n\tprivate String appendPluginOptions(int maxNamesLen) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\t// load and init all options plugins to print all options\n\t\ttry (JadxDecompiler decompiler = new JadxDecompiler(argsObj.toJadxArgs())) {\n\t\t\tJadxPluginManager pluginManager = decompiler.getPluginManager();\n\t\t\tpluginManager.load(decompiler.getArgs().getPluginLoader());\n\t\t\tpluginManager.initAll();\n\t\t\ttry {\n\t\t\t\tfor (PluginContext context : pluginManager.getAllPluginContexts()) {\n\t\t\t\t\tJadxPluginOptions options = context.getOptions();\n\t\t\t\t\tif (options != null) {\n\t\t\t\t\t\tappendPlugin(context.getPluginInfo(), context.getOptions(), sb, maxNamesLen);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tpluginManager.unloadAll();\n\t\t\t}\n\t\t}\n\t\tif (sb.length() == 0) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn \"\\nPlugin options (-P<name>=<value>):\" + sb;\n\t}\n\n\tprivate boolean appendPlugin(JadxPluginInfo pluginInfo, JadxPluginOptions options, StringBuilder out, int maxNamesLen) {\n\t\tList<OptionDescription> descs = options.getOptionsDescriptions();\n\t\tif (descs.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tout.append(\"\\n  \");\n\t\tout.append(pluginInfo.getPluginId()).append(\": \").append(pluginInfo.getDescription());\n\t\tfor (OptionDescription desc : descs) {\n\t\t\tStringBuilder opt = new StringBuilder();\n\t\t\topt.append(\"    - \").append(desc.name());\n\t\t\taddSpaces(opt, maxNamesLen - opt.length());\n\t\t\topt.append(\"- \").append(desc.description());\n\t\t\tif (!desc.values().isEmpty()) {\n\t\t\t\topt.append(\", values: \").append(desc.values());\n\t\t\t}\n\t\t\tif (desc.defaultValue() != null) {\n\t\t\t\topt.append(\", default: \").append(desc.defaultValue());\n\t\t\t}\n\t\t\tout.append(\"\\n\").append(opt);\n\t\t}\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/main/java/jadx/cli/JadxAppCommon.java",
    "content": "package jadx.cli;\n\nimport java.util.Set;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.security.JadxSecurityFlag;\nimport jadx.api.security.impl.JadxSecurity;\nimport jadx.commons.app.JadxCommonEnv;\nimport jadx.zip.security.DisabledZipSecurity;\nimport jadx.zip.security.IJadxZipSecurity;\nimport jadx.zip.security.JadxZipSecurity;\n\npublic class JadxAppCommon {\n\n\tpublic static void applyEnvVars(JadxArgs jadxArgs) {\n\t\tSet<JadxSecurityFlag> flags = JadxSecurityFlag.all();\n\t\tIJadxZipSecurity zipSecurity;\n\n\t\tboolean disableXmlSecurity = JadxCommonEnv.getBool(\"JADX_DISABLE_XML_SECURITY\", false);\n\t\tif (disableXmlSecurity) {\n\t\t\tflags.remove(JadxSecurityFlag.SECURE_XML_PARSER);\n\t\t\t// TODO: not related to 'xml security', but kept for compatibility\n\t\t\tflags.remove(JadxSecurityFlag.VERIFY_APP_PACKAGE);\n\t\t}\n\n\t\tboolean disableZipSecurity = JadxCommonEnv.getBool(\"JADX_DISABLE_ZIP_SECURITY\", false);\n\t\tif (disableZipSecurity) {\n\t\t\tflags.remove(JadxSecurityFlag.SECURE_ZIP_READER);\n\t\t\tzipSecurity = DisabledZipSecurity.INSTANCE;\n\t\t} else {\n\t\t\tJadxZipSecurity jadxZipSecurity = new JadxZipSecurity();\n\t\t\tint maxZipEntriesCount = JadxCommonEnv.getInt(\"JADX_ZIP_MAX_ENTRIES_COUNT\", -2);\n\t\t\tif (maxZipEntriesCount != -2) {\n\t\t\t\tjadxZipSecurity.setMaxEntriesCount(maxZipEntriesCount);\n\t\t\t}\n\t\t\tint zipBombMinUncompressedSize = JadxCommonEnv.getInt(\"JADX_ZIP_BOMB_MIN_UNCOMPRESSED_SIZE\", -2);\n\t\t\tif (zipBombMinUncompressedSize != -2) {\n\t\t\t\tjadxZipSecurity.setZipBombMinUncompressedSize(zipBombMinUncompressedSize);\n\t\t\t}\n\t\t\tint setZipBombDetectionFactor = JadxCommonEnv.getInt(\"JADX_ZIP_BOMB_DETECTION_FACTOR\", -2);\n\t\t\tif (setZipBombDetectionFactor != -2) {\n\t\t\t\tjadxZipSecurity.setZipBombDetectionFactor(setZipBombDetectionFactor);\n\t\t\t}\n\t\t\tzipSecurity = jadxZipSecurity;\n\t\t}\n\t\tjadxArgs.setSecurity(new JadxSecurity(flags, zipSecurity));\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/main/java/jadx/cli/JadxCLI.java",
    "content": "package jadx.cli;\n\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.JadxDecompiler;\nimport jadx.api.impl.AnnotatedCodeWriter;\nimport jadx.api.impl.NoOpCodeCache;\nimport jadx.api.impl.SimpleCodeWriter;\nimport jadx.api.usage.impl.EmptyUsageInfoCache;\nimport jadx.cli.LogHelper.LogLevelEnum;\nimport jadx.cli.config.JadxConfigAdapter;\nimport jadx.cli.plugins.JadxFilesGetter;\nimport jadx.core.utils.exceptions.JadxArgsValidateException;\nimport jadx.plugins.tools.JadxExternalPluginsLoader;\n\npublic class JadxCLI {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class);\n\n\tpublic static void main(String[] args) {\n\t\tint result = 1;\n\t\ttry {\n\t\t\tresult = execute(args);\n\t\t} finally {\n\t\t\tSystem.exit(result);\n\t\t}\n\t}\n\n\tpublic static int execute(String[] args) {\n\t\treturn execute(args, null);\n\t}\n\n\tpublic static int execute(String[] args, @Nullable Consumer<JadxArgs> argsMod) {\n\t\ttry {\n\t\t\tJadxCLIArgs cliArgs = JadxCLIArgs.processArgs(args,\n\t\t\t\t\tnew JadxCLIArgs(),\n\t\t\t\t\tnew JadxConfigAdapter<>(JadxCLIArgs.class, \"cli\"));\n\t\t\tif (cliArgs == null) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tJadxArgs jadxArgs = buildArgs(cliArgs);\n\t\t\tif (argsMod != null) {\n\t\t\t\targsMod.accept(jadxArgs);\n\t\t\t}\n\t\t\treturn runSave(jadxArgs, cliArgs);\n\t\t} catch (JadxArgsValidateException e) {\n\t\t\tLOG.error(\"Incorrect arguments: {}\", e.getMessage());\n\t\t\treturn 1;\n\t\t} catch (Throwable e) {\n\t\t\tLOG.error(\"Process error:\", e);\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tprivate static JadxArgs buildArgs(JadxCLIArgs cliArgs) {\n\t\tJadxArgs jadxArgs = cliArgs.toJadxArgs();\n\t\tjadxArgs.setCodeCache(new NoOpCodeCache());\n\t\tjadxArgs.setUsageInfoCache(new EmptyUsageInfoCache());\n\t\tjadxArgs.setPluginLoader(new JadxExternalPluginsLoader());\n\t\tjadxArgs.setFilesGetter(JadxFilesGetter.INSTANCE);\n\t\tinitCodeWriterProvider(jadxArgs);\n\t\tJadxAppCommon.applyEnvVars(jadxArgs);\n\t\treturn jadxArgs;\n\t}\n\n\tprivate static int runSave(JadxArgs jadxArgs, JadxCLIArgs cliArgs) {\n\t\ttry (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) {\n\t\t\tjadx.load();\n\t\t\tif (checkForErrors(jadx)) {\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t\tif (!SingleClassMode.process(jadx, cliArgs)) {\n\t\t\t\tsave(jadx);\n\t\t\t}\n\t\t\tint errorsCount = jadx.getErrorsCount();\n\t\t\tif (errorsCount != 0) {\n\t\t\t\tjadx.printErrorsReport();\n\t\t\t\tLOG.error(\"finished with errors, count: {}\", errorsCount);\n\t\t\t\treturn 3;\n\t\t\t}\n\t\t\tLOG.info(\"done\");\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static void initCodeWriterProvider(JadxArgs jadxArgs) {\n\t\tswitch (jadxArgs.getOutputFormat()) {\n\t\t\tcase JAVA:\n\t\t\t\tjadxArgs.setCodeWriterProvider(SimpleCodeWriter::new);\n\t\t\t\tbreak;\n\t\t\tcase JSON:\n\t\t\t\t// needed for code offsets and source lines\n\t\t\t\tjadxArgs.setCodeWriterProvider(AnnotatedCodeWriter::new);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate static boolean checkForErrors(JadxDecompiler jadx) {\n\t\tif (jadx.getRoot().getClasses().isEmpty()) {\n\t\t\tif (jadx.getArgs().isSkipResources()) {\n\t\t\t\tLOG.error(\"Load failed! No classes for decompile!\");\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (!jadx.getArgs().isSkipSources()) {\n\t\t\t\tLOG.warn(\"No classes to decompile; decoding resources only\");\n\t\t\t\tjadx.getArgs().setSkipSources(true);\n\t\t\t}\n\t\t}\n\t\tint errorsCount = jadx.getErrorsCount();\n\t\tif (errorsCount > 0) {\n\t\t\tLOG.error(\"Loading finished with errors! Count: {}\", errorsCount);\n\t\t\t// continue processing\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static void save(JadxDecompiler jadx) {\n\t\tif (LogHelper.getLogLevel() == LogLevelEnum.QUIET) {\n\t\t\tjadx.save();\n\t\t} else {\n\t\t\tLOG.info(\"processing ...\");\n\t\t\tjadx.save(500, (done, total) -> {\n\t\t\t\tint progress = (int) (done * 100.0 / total);\n\t\t\t\tSystem.out.printf(\"INFO  - progress: %d of %d (%d%%)\\r\", done, total, progress);\n\t\t\t});\n\t\t\t// dumb line clear :)\n\t\t\tSystem.out.print(\"                                                             \\r\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java",
    "content": "package jadx.cli;\n\nimport java.nio.file.Path;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.beust.jcommander.DynamicParameter;\nimport com.beust.jcommander.IStringConverter;\nimport com.beust.jcommander.Parameter;\n\nimport jadx.api.CommentsLevel;\nimport jadx.api.DecompilationMode;\nimport jadx.api.JadxArgs;\nimport jadx.api.JadxArgs.RenameEnum;\nimport jadx.api.JadxArgs.UseKotlinMethodsForVarNames;\nimport jadx.api.JadxDecompiler;\nimport jadx.api.args.GeneratedRenamesMappingFileMode;\nimport jadx.api.args.IntegerFormat;\nimport jadx.api.args.ResourceNameSource;\nimport jadx.api.args.UseSourceNameAsClassNameAlias;\nimport jadx.api.args.UserRenamesMappingsMode;\nimport jadx.cli.config.IJadxConfig;\nimport jadx.cli.config.JadxConfigAdapter;\nimport jadx.cli.config.JadxConfigExclude;\nimport jadx.commons.app.JadxCommonFiles;\nimport jadx.commons.app.JadxTempFiles;\nimport jadx.core.deobf.conditions.DeobfWhitelist;\nimport jadx.core.export.ExportGradleType;\nimport jadx.core.utils.exceptions.JadxArgsValidateException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\n\npublic class JadxCLIArgs implements IJadxConfig {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxCLIArgs.class);\n\n\t@JadxConfigExclude\n\t@Parameter(description = \"<input files> (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab, .xapk, .apkm, .jadx.kts)\")\n\tprotected List<String> files = Collections.emptyList();\n\n\t@JadxConfigExclude\n\t@Parameter(names = { \"-d\", \"--output-dir\" }, description = \"output directory\")\n\tprotected String outDir;\n\n\t@JadxConfigExclude\n\t@Parameter(names = { \"-ds\", \"--output-dir-src\" }, description = \"output directory for sources\")\n\tprotected String outDirSrc;\n\n\t@JadxConfigExclude\n\t@Parameter(names = { \"-dr\", \"--output-dir-res\" }, description = \"output directory for resources\")\n\tprotected String outDirRes;\n\n\t@Parameter(names = { \"-r\", \"--no-res\" }, description = \"do not decode resources\")\n\tprotected boolean skipResources = false;\n\n\t@Parameter(names = { \"-s\", \"--no-src\" }, description = \"do not decompile source code\")\n\tprotected boolean skipSources = false;\n\n\t@Parameter(names = { \"-j\", \"--threads-count\" }, description = \"processing threads count\")\n\tprotected int threadsCount = JadxArgs.DEFAULT_THREADS_COUNT;\n\n\t@JadxConfigExclude\n\t@Parameter(names = { \"--single-class\" }, description = \"decompile a single class, full name, raw or alias\")\n\tprotected String singleClass = null;\n\n\t@JadxConfigExclude\n\t@Parameter(names = { \"--single-class-output\" }, description = \"file or dir for write if decompile a single class\")\n\tprotected String singleClassOutput = null;\n\n\t@Parameter(names = { \"--output-format\" }, description = \"can be 'java' or 'json'\")\n\tprotected String outputFormat = \"java\";\n\n\t@Parameter(names = { \"-e\", \"--export-gradle\" }, description = \"save as gradle project (set '--export-gradle-type' to 'auto')\")\n\tprotected boolean exportAsGradleProject = false;\n\n\t@Parameter(\n\t\t\tnames = { \"--export-gradle-type\" },\n\t\t\tdescription = \"Gradle project template for export:\"\n\t\t\t\t\t+ \"\\n 'auto' - detect automatically\"\n\t\t\t\t\t+ \"\\n 'android-app' - Android Application (apk)\"\n\t\t\t\t\t+ \"\\n 'android-library' - Android Library (aar)\"\n\t\t\t\t\t+ \"\\n 'simple-java' - simple Java\",\n\t\t\tconverter = ExportGradleTypeConverter.class\n\t)\n\tprotected @Nullable ExportGradleType exportGradleType = null;\n\n\t@Parameter(\n\t\t\tnames = { \"-m\", \"--decompilation-mode\" },\n\t\t\tdescription = \"code output mode:\"\n\t\t\t\t\t+ \"\\n 'auto' - trying best options (default)\"\n\t\t\t\t\t+ \"\\n 'restructure' - restore code structure (normal java code)\"\n\t\t\t\t\t+ \"\\n 'simple' - simplified instructions (linear, with goto's)\"\n\t\t\t\t\t+ \"\\n 'fallback' - raw instructions without modifications\",\n\t\t\tconverter = DecompilationModeConverter.class\n\t)\n\tprotected DecompilationMode decompilationMode = DecompilationMode.AUTO;\n\n\t@Parameter(names = { \"--show-bad-code\" }, description = \"show inconsistent code (incorrectly decompiled)\")\n\tprotected boolean showInconsistentCode = false;\n\n\t@Parameter(names = { \"--no-xml-pretty-print\" }, description = \"do not prettify XML\")\n\tprotected boolean skipXmlPrettyPrint = false;\n\n\t@Parameter(names = { \"--no-imports\" }, description = \"disable use of imports, always write entire package name\")\n\tprotected boolean useImports = true;\n\n\t@Parameter(names = { \"--no-debug-info\" }, description = \"disable debug info parsing and processing\")\n\tprotected boolean debugInfo = true;\n\n\t@Parameter(names = { \"--add-debug-lines\" }, description = \"add comments with debug line numbers if available\")\n\tprotected boolean addDebugLines = false;\n\n\t@Parameter(names = { \"--no-inline-anonymous\" }, description = \"disable anonymous classes inline\")\n\tprotected boolean inlineAnonymousClasses = true;\n\n\t@Parameter(names = { \"--no-inline-methods\" }, description = \"disable methods inline\")\n\tprotected boolean inlineMethods = true;\n\n\t@Parameter(names = { \"--no-move-inner-classes\" }, description = \"disable move inner classes into parent\")\n\tprotected boolean moveInnerClasses = true;\n\n\t@Parameter(names = { \"--no-inline-kotlin-lambda\" }, description = \"disable inline for Kotlin lambdas\")\n\tprotected boolean allowInlineKotlinLambda = true;\n\n\t@Parameter(names = \"--no-finally\", description = \"don't extract finally block\")\n\tprotected boolean extractFinally = true;\n\n\t@Parameter(names = \"--no-restore-switch-over-string\", description = \"don't restore switch over string\")\n\tprotected boolean restoreSwitchOverString = true;\n\n\t@Parameter(names = \"--no-replace-consts\", description = \"don't replace constant value with matching constant field\")\n\tprotected boolean replaceConsts = true;\n\n\t@Parameter(names = { \"--escape-unicode\" }, description = \"escape non latin characters in strings (with \\\\u)\")\n\tprotected boolean escapeUnicode = false;\n\n\t@Parameter(names = { \"--respect-bytecode-access-modifiers\" }, description = \"don't change original access modifiers\")\n\tprotected boolean respectBytecodeAccessModifiers = false;\n\n\t@Parameter(\n\t\t\tnames = { \"--mappings-path\" },\n\t\t\tdescription = \"deobfuscation mappings file or directory. Allowed formats: Tiny and Tiny v2 (both '.tiny'), Enigma (.mapping) or Enigma directory\"\n\t)\n\tprotected Path userRenamesMappingsPath;\n\n\t@Parameter(\n\t\t\tnames = { \"--mappings-mode\" },\n\t\t\tdescription = \"set mode for handling the deobfuscation mapping file:\"\n\t\t\t\t\t+ \"\\n 'read' - just read, user can always save manually (default)\"\n\t\t\t\t\t+ \"\\n 'read-and-autosave-every-change' - read and autosave after every change\"\n\t\t\t\t\t+ \"\\n 'read-and-autosave-before-closing' - read and autosave before exiting the app or closing the project\"\n\t\t\t\t\t+ \"\\n 'ignore' - don't read or save (can be used to skip loading mapping files referenced in the project file)\"\n\t)\n\tprotected UserRenamesMappingsMode userRenamesMappingsMode = UserRenamesMappingsMode.getDefault();\n\n\t@Parameter(names = { \"--deobf\" }, description = \"activate deobfuscation\")\n\tprotected boolean deobfuscationOn = false;\n\n\t@Parameter(names = { \"--deobf-min\" }, description = \"min length of name, renamed if shorter\")\n\tprotected int deobfuscationMinLength = 3;\n\n\t@Parameter(names = { \"--deobf-max\" }, description = \"max length of name, renamed if longer\")\n\tprotected int deobfuscationMaxLength = 64;\n\n\t@Parameter(\n\t\t\tnames = { \"--deobf-whitelist\" },\n\t\t\tdescription = \"space separated list of classes (full name) and packages (ends with '.*') to exclude from deobfuscation\"\n\t)\n\tprotected String deobfuscationWhitelistStr = DeobfWhitelist.DEFAULT_STR;\n\n\t@JadxConfigExclude\n\t@Parameter(\n\t\t\tnames = { \"--deobf-cfg-file\" },\n\t\t\tdescription = \"deobfuscation mappings file used for JADX auto-generated names (in the JOBF file format),\"\n\t\t\t\t\t+ \" default: same dir and name as input file with '.jobf' extension\"\n\t)\n\tprotected String generatedRenamesMappingFile;\n\n\t@Parameter(\n\t\t\tnames = { \"--deobf-cfg-file-mode\" },\n\t\t\tdescription = \"set mode for handling the JADX auto-generated names' deobfuscation map file:\"\n\t\t\t\t\t+ \"\\n 'read' - read if found, don't save (default)\"\n\t\t\t\t\t+ \"\\n 'read-or-save' - read if found, save otherwise (don't overwrite)\"\n\t\t\t\t\t+ \"\\n 'overwrite' - don't read, always save\"\n\t\t\t\t\t+ \"\\n 'ignore' - don't read and don't save\",\n\t\t\tconverter = DeobfuscationMapFileModeConverter.class\n\t)\n\tprotected GeneratedRenamesMappingFileMode generatedRenamesMappingFileMode = GeneratedRenamesMappingFileMode.getDefault();\n\n\t@SuppressWarnings(\"DeprecatedIsStillUsed\")\n\t@Parameter(\n\t\t\tnames = { \"--deobf-use-sourcename\" },\n\t\t\tdescription = \"use source file name as class name alias.\"\n\t\t\t\t\t+ \"\\nDEPRECATED, use \\\"--use-source-name-as-class-name-alias\\\" instead\",\n\t\t\thidden = true\n\t)\n\t@Deprecated\n\tprotected Boolean deobfuscationUseSourceNameAsAlias = null;\n\n\t@Parameter(\n\t\t\tnames = { \"--deobf-res-name-source\" },\n\t\t\tdescription = \"better name source for resources:\"\n\t\t\t\t\t+ \"\\n 'auto' - automatically select best name (default)\"\n\t\t\t\t\t+ \"\\n 'resources' - use resources names\"\n\t\t\t\t\t+ \"\\n 'code' - use R class fields names\",\n\t\t\tconverter = ResourceNameSourceConverter.class\n\t)\n\tprotected ResourceNameSource resourceNameSource = ResourceNameSource.AUTO;\n\n\t@Parameter(\n\t\t\tnames = { \"--use-source-name-as-class-name-alias\" },\n\t\t\tdescription = \"use source name as class name alias:\"\n\t\t\t\t\t+ \"\\n 'always' - always use source name if it's available\"\n\t\t\t\t\t+ \"\\n 'if-better' - use source name if it seems better than the current one\"\n\t\t\t\t\t+ \"\\n 'never' - never use source name, even if it's available\",\n\t\t\tconverter = UseSourceNameAsClassNameConverter.class\n\t)\n\tprotected UseSourceNameAsClassNameAlias useSourceNameAsClassNameAlias = null;\n\n\t@Parameter(\n\t\t\tnames = { \"--source-name-repeat-limit\" },\n\t\t\tdescription = \"allow using source name if it appears less than a limit number\"\n\t)\n\tprotected int sourceNameRepeatLimit = 10;\n\n\t@Parameter(\n\t\t\tnames = { \"--use-kotlin-methods-for-var-names\" },\n\t\t\tdescription = \"use kotlin intrinsic methods to rename variables, values: disable, apply, apply-and-hide\",\n\t\t\tconverter = UseKotlinMethodsForVarNamesConverter.class\n\t)\n\tprotected UseKotlinMethodsForVarNames useKotlinMethodsForVarNames = UseKotlinMethodsForVarNames.APPLY;\n\n\t@Parameter(\n\t\t\tnames = { \"--use-headers-for-detect-resource-extensions\" },\n\t\t\tdescription = \"Use headers for detect resource extensions if resource obfuscated\"\n\t)\n\tprotected boolean useHeadersForDetectResourceExtensions = false;\n\n\t@Parameter(\n\t\t\tnames = { \"--rename-flags\" },\n\t\t\tdescription = \"fix options (comma-separated list of):\"\n\t\t\t\t\t+ \"\\n 'case' - fix case sensitivity issues (according to --fs-case-sensitive option),\"\n\t\t\t\t\t+ \"\\n 'valid' - rename java identifiers to make them valid,\"\n\t\t\t\t\t+ \"\\n 'printable' - remove non-printable chars from identifiers,\"\n\t\t\t\t\t+ \"\\nor single 'none' - to disable all renames\"\n\t\t\t\t\t+ \"\\nor single 'all' - to enable all (default)\",\n\t\t\tlistConverter = RenameConverter.class\n\t)\n\tprotected Set<RenameEnum> renameFlags = EnumSet.allOf(RenameEnum.class);\n\n\t@Parameter(\n\t\t\tnames = { \"--integer-format\" },\n\t\t\tdescription = \"how integers are displayed:\"\n\t\t\t\t\t+ \"\\n 'auto' - automatically select (default)\"\n\t\t\t\t\t+ \"\\n 'decimal' - use decimal\"\n\t\t\t\t\t+ \"\\n 'hexadecimal' - use hexadecimal\",\n\t\t\tconverter = IntegerFormatConverter.class\n\t)\n\tprotected IntegerFormat integerFormat = IntegerFormat.AUTO;\n\n\t@Parameter(names = { \"--type-update-limit\" }, description = \"type update limit count (per one instruction)\")\n\tprotected int typeUpdatesLimitCount = 10;\n\n\t@Parameter(names = { \"--fs-case-sensitive\" }, description = \"treat filesystem as case sensitive, false by default\")\n\tprotected boolean fsCaseSensitive = false;\n\n\t@Parameter(names = { \"--cfg\" }, description = \"save methods control flow graph to dot file\")\n\tprotected boolean cfgOutput = false;\n\n\t@Parameter(names = { \"--raw-cfg\" }, description = \"save methods control flow graph (use raw instructions)\")\n\tprotected boolean rawCfgOutput = false;\n\n\t@Parameter(names = { \"-f\", \"--fallback\" }, description = \"set '--decompilation-mode' to 'fallback' (deprecated)\")\n\tprotected boolean fallbackMode = false;\n\n\t@Parameter(names = { \"--use-dx\" }, description = \"use dx/d8 to convert java bytecode\")\n\tprotected boolean useDx = false;\n\n\t@Parameter(\n\t\t\tnames = { \"--comments-level\" },\n\t\t\tdescription = \"set code comments level, values: error, warn, info, debug, user-only, none\",\n\t\t\tconverter = CommentsLevelConverter.class\n\t)\n\tprotected CommentsLevel commentsLevel = CommentsLevel.INFO;\n\n\t@Parameter(\n\t\t\tnames = { \"--log-level\" },\n\t\t\tdescription = \"set log level, values: quiet, progress, error, warn, info, debug\",\n\t\t\tconverter = LogLevelConverter.class\n\t)\n\tprotected LogHelper.LogLevelEnum logLevel = LogHelper.LogLevelEnum.PROGRESS;\n\n\t@JadxConfigExclude\n\t@Parameter(names = { \"-v\", \"--verbose\" }, description = \"verbose output (set --log-level to DEBUG)\")\n\tprotected boolean verbose = false;\n\n\t@JadxConfigExclude\n\t@Parameter(names = { \"-q\", \"--quiet\" }, description = \"turn off output (set --log-level to QUIET)\")\n\tprotected boolean quiet = false;\n\n\t@JadxConfigExclude\n\t@Parameter(names = { \"--disable-plugins\" }, description = \"comma separated list of plugin ids to disable\")\n\tprotected String disablePlugins = \"\";\n\n\t@JadxConfigExclude\n\t@Parameter(\n\t\t\tnames = { \"--config\" },\n\t\t\tdefaultValueDescription = \"<config-ref>\",\n\t\t\tdescription = \"load configuration from file, <config-ref> can be:\"\n\t\t\t\t\t+ \"\\n path to '.json' file\"\n\t\t\t\t\t+ \"\\n short name - uses file with this name from config directory\"\n\t\t\t\t\t+ \"\\n 'none' - to disable config loading\"\n\t)\n\tprotected String config = \"\";\n\n\t@JadxConfigExclude\n\t@Parameter(\n\t\t\tnames = { \"--save-config\" },\n\t\t\tdefaultValueDescription = \"<config-ref>\",\n\t\t\tdescription = \"save current options into configuration file and exit, <config-ref> can be:\"\n\t\t\t\t\t+ \"\\n empty - for default config\"\n\t\t\t\t\t+ \"\\n path to '.json' file\"\n\t\t\t\t\t+ \"\\n short name - file will be saved in config directory\"\n\t)\n\tprotected String saveConfig = null;\n\n\t@JadxConfigExclude\n\t@Parameter(names = { \"--print-files\" }, description = \"print files and directories used by jadx (config, cache, temp)\")\n\tprotected boolean printFiles = false;\n\n\t@JadxConfigExclude\n\t@Parameter(names = { \"--version\" }, description = \"print jadx version\")\n\tprotected boolean printVersion = false;\n\n\t@JadxConfigExclude\n\t@Parameter(names = { \"-h\", \"--help\" }, description = \"print this help\", help = true)\n\tprotected boolean printHelp = false;\n\n\t@DynamicParameter(names = \"-P\", description = \"Plugin options\", hidden = true)\n\tprotected Map<String, String> pluginOptions = new HashMap<>();\n\n\t/**\n\t * Obsolete method without config support,\n\t * prefer {@link #processArgs(String[], JadxCLIArgs, JadxConfigAdapter)}\n\t */\n\tpublic boolean processArgs(String[] args) {\n\t\treturn processArgs(args, this, null) != null;\n\t}\n\n\tpublic static <T extends JadxCLIArgs> @Nullable T processArgs(\n\t\t\tString[] args, T argsObj, @Nullable JadxConfigAdapter<T> configAdapter) {\n\t\tJCommanderWrapper jcw = new JCommanderWrapper(argsObj);\n\t\tif (!jcw.parse(args)) {\n\t\t\treturn null;\n\t\t}\n\t\tapplyArgs(argsObj);\n\n\t\t// process commands and early exit flags\n\t\tif (!argsObj.process(jcw)) {\n\t\t\treturn null;\n\t\t}\n\t\tif (configAdapter != null) {\n\t\t\tif (argsObj.printFiles) {\n\t\t\t\tprintFilesAndDirs(configAdapter.getDefaultConfigFileName());\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (!argsObj.config.equalsIgnoreCase(\"none\")) {\n\t\t\t\t// load config file and merge with command line args\n\t\t\t\ttry {\n\t\t\t\t\tconfigAdapter.useConfigRef(argsObj.config);\n\t\t\t\t\tT configObj = configAdapter.load();\n\t\t\t\t\tif (configObj != null) {\n\t\t\t\t\t\tjcw.overrideProvided(configObj);\n\t\t\t\t\t\targsObj = configObj;\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.error(\"Config load failed, continue with default values\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// verify result object\n\t\targsObj.verify();\n\t\tapplyArgs(argsObj);\n\n\t\t// save config if requested\n\t\tif (argsObj.saveConfig != null) {\n\t\t\tsaveConfig(argsObj, configAdapter);\n\t\t\treturn null;\n\t\t}\n\t\treturn argsObj;\n\t}\n\n\tprivate static <T extends JadxCLIArgs> void applyArgs(T argsObj) {\n\t\t// apply log levels\n\t\tLogHelper.initLogLevel(argsObj);\n\t\tLogHelper.applyLogLevels();\n\t}\n\n\tpublic boolean process(JCommanderWrapper jcw) {\n\t\tif (jcw.processCommands()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (printHelp) {\n\t\t\tjcw.printUsage();\n\t\t\treturn false;\n\t\t}\n\t\tif (printVersion) {\n\t\t\tSystem.out.println(JadxDecompiler.getVersion());\n\t\t\treturn false;\n\t\t}\n\t\t// unknown options added to 'files', run checks\n\t\tfor (String fileName : files) {\n\t\t\tif (fileName.startsWith(\"-\")) {\n\t\t\t\tthrow new JadxArgsValidateException(\"Unknown option: \" + fileName);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static void printFilesAndDirs(String defaultConfigFileName) {\n\t\tSystem.out.println(\"Files and directories used by jadx:\");\n\t\tSystem.out.println(\" - default config file: \" + JadxCommonFiles.getConfigDir().resolve(defaultConfigFileName).toAbsolutePath());\n\t\tSystem.out.println(\" - config directory:    \" + JadxCommonFiles.getConfigDir().toAbsolutePath());\n\t\tSystem.out.println(\" - cache directory:     \" + JadxCommonFiles.getCacheDir().toAbsolutePath());\n\t\tSystem.out.println(\" - temp directory:      \" + JadxTempFiles.getTempRootDir().getParent().toAbsolutePath());\n\t}\n\n\tpublic void verify() {\n\t\tif (threadsCount <= 0) {\n\t\t\tthrow new JadxArgsValidateException(\"Threads count must be positive, got: \" + threadsCount);\n\t\t}\n\t}\n\n\tprivate static <T extends JadxCLIArgs> void saveConfig(T argsObj, @Nullable JadxConfigAdapter<T> configAdapter) {\n\t\tif (configAdapter == null) {\n\t\t\tthrow new JadxRuntimeException(\"Config adapter set to null, can't save config\");\n\t\t}\n\t\tconfigAdapter.useConfigRef(argsObj.saveConfig);\n\t\tconfigAdapter.save(argsObj);\n\t\tSystem.out.println(\"Config saved to \" + configAdapter.getConfigPath().toAbsolutePath());\n\t}\n\n\tpublic JadxArgs toJadxArgs() {\n\t\tJadxArgs args = new JadxArgs();\n\t\targs.setInputFiles(files.stream().map(FileUtils::toFile).collect(Collectors.toList()));\n\t\targs.setOutDir(FileUtils.toFile(outDir));\n\t\targs.setOutDirSrc(FileUtils.toFile(outDirSrc));\n\t\targs.setOutDirRes(FileUtils.toFile(outDirRes));\n\t\targs.setOutputFormat(JadxArgs.OutputFormatEnum.valueOf(outputFormat.toUpperCase()));\n\t\targs.setThreadsCount(threadsCount);\n\t\targs.setSkipSources(skipSources);\n\t\targs.setSkipResources(skipResources);\n\t\tif (fallbackMode) {\n\t\t\targs.setDecompilationMode(DecompilationMode.FALLBACK);\n\t\t} else {\n\t\t\targs.setDecompilationMode(decompilationMode);\n\t\t}\n\t\targs.setShowInconsistentCode(showInconsistentCode);\n\t\targs.setCfgOutput(cfgOutput);\n\t\targs.setRawCFGOutput(rawCfgOutput);\n\t\targs.setReplaceConsts(replaceConsts);\n\t\tif (userRenamesMappingsPath != null) {\n\t\t\targs.setUserRenamesMappingsPath(userRenamesMappingsPath);\n\t\t}\n\t\targs.setUserRenamesMappingsMode(userRenamesMappingsMode);\n\t\targs.setDeobfuscationOn(deobfuscationOn);\n\t\targs.setGeneratedRenamesMappingFile(FileUtils.toFile(generatedRenamesMappingFile));\n\t\targs.setGeneratedRenamesMappingFileMode(generatedRenamesMappingFileMode);\n\t\targs.setDeobfuscationMinLength(deobfuscationMinLength);\n\t\targs.setDeobfuscationMaxLength(deobfuscationMaxLength);\n\t\targs.setDeobfuscationWhitelist(Arrays.asList(deobfuscationWhitelistStr.split(\" \")));\n\t\targs.setUseSourceNameAsClassNameAlias(getUseSourceNameAsClassNameAlias());\n\t\targs.setUseHeadersForDetectResourceExtensions(useHeadersForDetectResourceExtensions);\n\t\targs.setSourceNameRepeatLimit(sourceNameRepeatLimit);\n\t\targs.setUseKotlinMethodsForVarNames(useKotlinMethodsForVarNames);\n\t\targs.setResourceNameSource(resourceNameSource);\n\t\targs.setEscapeUnicode(escapeUnicode);\n\t\targs.setRespectBytecodeAccModifiers(respectBytecodeAccessModifiers);\n\t\targs.setExportGradleType(exportGradleType);\n\t\tif (exportAsGradleProject && exportGradleType == null) {\n\t\t\targs.setExportGradleType(ExportGradleType.AUTO);\n\t\t}\n\t\targs.setSkipXmlPrettyPrint(skipXmlPrettyPrint);\n\t\targs.setUseImports(useImports);\n\t\targs.setDebugInfo(debugInfo);\n\t\targs.setInsertDebugLines(addDebugLines);\n\t\targs.setInlineAnonymousClasses(inlineAnonymousClasses);\n\t\targs.setInlineMethods(inlineMethods);\n\t\targs.setMoveInnerClasses(moveInnerClasses);\n\t\targs.setAllowInlineKotlinLambda(allowInlineKotlinLambda);\n\t\targs.setExtractFinally(extractFinally);\n\t\targs.setRestoreSwitchOverString(restoreSwitchOverString);\n\t\targs.setRenameFlags(buildEnumSetForRenameFlags());\n\t\targs.setFsCaseSensitive(fsCaseSensitive);\n\t\targs.setCommentsLevel(commentsLevel);\n\t\targs.setIntegerFormat(integerFormat);\n\t\targs.setTypeUpdatesLimitCount(typeUpdatesLimitCount);\n\t\targs.setUseDxInput(useDx);\n\t\targs.setPluginOptions(pluginOptions);\n\t\targs.setDisabledPlugins(Arrays.stream(disablePlugins.split(\",\")).map(String::trim).collect(Collectors.toSet()));\n\t\treturn args;\n\t}\n\n\tprivate EnumSet<RenameEnum> buildEnumSetForRenameFlags() {\n\t\tEnumSet<RenameEnum> set = EnumSet.noneOf(RenameEnum.class);\n\t\tset.addAll(renameFlags);\n\t\treturn set;\n\t}\n\n\tpublic List<String> getFiles() {\n\t\treturn files;\n\t}\n\n\tpublic void setFiles(List<String> files) {\n\t\tthis.files = files;\n\t}\n\n\tpublic String getOutDir() {\n\t\treturn outDir;\n\t}\n\n\tpublic String getOutDirSrc() {\n\t\treturn outDirSrc;\n\t}\n\n\tpublic String getOutDirRes() {\n\t\treturn outDirRes;\n\t}\n\n\tpublic String getSingleClass() {\n\t\treturn singleClass;\n\t}\n\n\tpublic String getSingleClassOutput() {\n\t\treturn singleClassOutput;\n\t}\n\n\tpublic boolean isSkipResources() {\n\t\treturn skipResources;\n\t}\n\n\tpublic void setSkipResources(boolean skipResources) {\n\t\tthis.skipResources = skipResources;\n\t}\n\n\tpublic boolean isSkipSources() {\n\t\treturn skipSources;\n\t}\n\n\tpublic void setSkipSources(boolean skipSources) {\n\t\tthis.skipSources = skipSources;\n\t}\n\n\tpublic int getThreadsCount() {\n\t\treturn threadsCount;\n\t}\n\n\tpublic void setThreadsCount(int threadsCount) {\n\t\tthis.threadsCount = threadsCount;\n\t}\n\n\tpublic boolean isFallbackMode() {\n\t\treturn fallbackMode;\n\t}\n\n\tpublic boolean isUseDx() {\n\t\treturn useDx;\n\t}\n\n\tpublic void setUseDx(boolean useDx) {\n\t\tthis.useDx = useDx;\n\t}\n\n\tpublic DecompilationMode getDecompilationMode() {\n\t\treturn decompilationMode;\n\t}\n\n\tpublic void setDecompilationMode(DecompilationMode decompilationMode) {\n\t\tthis.decompilationMode = decompilationMode;\n\t}\n\n\tpublic boolean isShowInconsistentCode() {\n\t\treturn showInconsistentCode;\n\t}\n\n\tpublic void setShowInconsistentCode(boolean showInconsistentCode) {\n\t\tthis.showInconsistentCode = showInconsistentCode;\n\t}\n\n\tpublic boolean isUseImports() {\n\t\treturn useImports;\n\t}\n\n\tpublic void setUseImports(boolean useImports) {\n\t\tthis.useImports = useImports;\n\t}\n\n\tpublic boolean isDebugInfo() {\n\t\treturn debugInfo;\n\t}\n\n\tpublic void setDebugInfo(boolean debugInfo) {\n\t\tthis.debugInfo = debugInfo;\n\t}\n\n\tpublic boolean isAddDebugLines() {\n\t\treturn addDebugLines;\n\t}\n\n\tpublic void setAddDebugLines(boolean addDebugLines) {\n\t\tthis.addDebugLines = addDebugLines;\n\t}\n\n\tpublic boolean isInlineAnonymousClasses() {\n\t\treturn inlineAnonymousClasses;\n\t}\n\n\tpublic void setInlineAnonymousClasses(boolean inlineAnonymousClasses) {\n\t\tthis.inlineAnonymousClasses = inlineAnonymousClasses;\n\t}\n\n\tpublic boolean isInlineMethods() {\n\t\treturn inlineMethods;\n\t}\n\n\tpublic void setInlineMethods(boolean inlineMethods) {\n\t\tthis.inlineMethods = inlineMethods;\n\t}\n\n\tpublic boolean isMoveInnerClasses() {\n\t\treturn moveInnerClasses;\n\t}\n\n\tpublic void setMoveInnerClasses(boolean moveInnerClasses) {\n\t\tthis.moveInnerClasses = moveInnerClasses;\n\t}\n\n\tpublic boolean isAllowInlineKotlinLambda() {\n\t\treturn allowInlineKotlinLambda;\n\t}\n\n\tpublic void setAllowInlineKotlinLambda(boolean allowInlineKotlinLambda) {\n\t\tthis.allowInlineKotlinLambda = allowInlineKotlinLambda;\n\t}\n\n\tpublic boolean isExtractFinally() {\n\t\treturn extractFinally;\n\t}\n\n\tpublic void setExtractFinally(boolean extractFinally) {\n\t\tthis.extractFinally = extractFinally;\n\t}\n\n\tpublic boolean isRestoreSwitchOverString() {\n\t\treturn restoreSwitchOverString;\n\t}\n\n\tpublic void setRestoreSwitchOverString(boolean restoreSwitchOverString) {\n\t\tthis.restoreSwitchOverString = restoreSwitchOverString;\n\t}\n\n\tpublic Path getUserRenamesMappingsPath() {\n\t\treturn userRenamesMappingsPath;\n\t}\n\n\tpublic void setUserRenamesMappingsPath(Path userRenamesMappingsPath) {\n\t\tthis.userRenamesMappingsPath = userRenamesMappingsPath;\n\t}\n\n\tpublic UserRenamesMappingsMode getUserRenamesMappingsMode() {\n\t\treturn userRenamesMappingsMode;\n\t}\n\n\tpublic void setUserRenamesMappingsMode(UserRenamesMappingsMode userRenamesMappingsMode) {\n\t\tthis.userRenamesMappingsMode = userRenamesMappingsMode;\n\t}\n\n\tpublic boolean isDeobfuscationOn() {\n\t\treturn deobfuscationOn;\n\t}\n\n\tpublic void setDeobfuscationOn(boolean deobfuscationOn) {\n\t\tthis.deobfuscationOn = deobfuscationOn;\n\t}\n\n\tpublic int getDeobfuscationMinLength() {\n\t\treturn deobfuscationMinLength;\n\t}\n\n\tpublic void setDeobfuscationMinLength(int deobfuscationMinLength) {\n\t\tthis.deobfuscationMinLength = deobfuscationMinLength;\n\t}\n\n\tpublic int getDeobfuscationMaxLength() {\n\t\treturn deobfuscationMaxLength;\n\t}\n\n\tpublic void setDeobfuscationMaxLength(int deobfuscationMaxLength) {\n\t\tthis.deobfuscationMaxLength = deobfuscationMaxLength;\n\t}\n\n\tpublic String getDeobfuscationWhitelistStr() {\n\t\treturn deobfuscationWhitelistStr;\n\t}\n\n\tpublic void setDeobfuscationWhitelistStr(String deobfuscationWhitelistStr) {\n\t\tthis.deobfuscationWhitelistStr = deobfuscationWhitelistStr;\n\t}\n\n\tpublic String getGeneratedRenamesMappingFile() {\n\t\treturn generatedRenamesMappingFile;\n\t}\n\n\tpublic void setGeneratedRenamesMappingFile(String generatedRenamesMappingFile) {\n\t\tthis.generatedRenamesMappingFile = generatedRenamesMappingFile;\n\t}\n\n\tpublic GeneratedRenamesMappingFileMode getGeneratedRenamesMappingFileMode() {\n\t\treturn generatedRenamesMappingFileMode;\n\t}\n\n\tpublic void setGeneratedRenamesMappingFileMode(GeneratedRenamesMappingFileMode generatedRenamesMappingFileMode) {\n\t\tthis.generatedRenamesMappingFileMode = generatedRenamesMappingFileMode;\n\t}\n\n\tpublic int getSourceNameRepeatLimit() {\n\t\treturn sourceNameRepeatLimit;\n\t}\n\n\tpublic void setSourceNameRepeatLimit(int sourceNameRepeatLimit) {\n\t\tthis.sourceNameRepeatLimit = sourceNameRepeatLimit;\n\t}\n\n\tpublic UseSourceNameAsClassNameAlias getUseSourceNameAsClassNameAlias() {\n\t\tif (useSourceNameAsClassNameAlias != null) {\n\t\t\treturn useSourceNameAsClassNameAlias;\n\t\t} else if (deobfuscationUseSourceNameAsAlias != null) {\n\t\t\t// noinspection deprecation\n\t\t\treturn UseSourceNameAsClassNameAlias.create(deobfuscationUseSourceNameAsAlias);\n\t\t} else {\n\t\t\treturn UseSourceNameAsClassNameAlias.getDefault();\n\t\t}\n\t}\n\n\tpublic void setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias useSourceNameAsClassNameAlias) {\n\t\tthis.useSourceNameAsClassNameAlias = useSourceNameAsClassNameAlias;\n\t}\n\n\t/**\n\t * @deprecated Use {@link #getUseSourceNameAsClassNameAlias()} instead.\n\t */\n\t@Deprecated\n\tpublic boolean isDeobfuscationUseSourceNameAsAlias() {\n\t\treturn getUseSourceNameAsClassNameAlias().toBoolean();\n\t}\n\n\tpublic void setDeobfuscationUseSourceNameAsAlias(Boolean deobfuscationUseSourceNameAsAlias) {\n\t\tthis.deobfuscationUseSourceNameAsAlias = deobfuscationUseSourceNameAsAlias;\n\t}\n\n\tpublic ResourceNameSource getResourceNameSource() {\n\t\treturn resourceNameSource;\n\t}\n\n\tpublic void setResourceNameSource(ResourceNameSource resourceNameSource) {\n\t\tthis.resourceNameSource = resourceNameSource;\n\t}\n\n\tpublic UseKotlinMethodsForVarNames getUseKotlinMethodsForVarNames() {\n\t\treturn useKotlinMethodsForVarNames;\n\t}\n\n\tpublic void setUseKotlinMethodsForVarNames(UseKotlinMethodsForVarNames useKotlinMethodsForVarNames) {\n\t\tthis.useKotlinMethodsForVarNames = useKotlinMethodsForVarNames;\n\t}\n\n\tpublic IntegerFormat getIntegerFormat() {\n\t\treturn integerFormat;\n\t}\n\n\tpublic void setIntegerFormat(IntegerFormat integerFormat) {\n\t\tthis.integerFormat = integerFormat;\n\t}\n\n\tpublic int getTypeUpdatesLimitCount() {\n\t\treturn typeUpdatesLimitCount;\n\t}\n\n\tpublic void setTypeUpdatesLimitCount(int typeUpdatesLimitCount) {\n\t\tthis.typeUpdatesLimitCount = typeUpdatesLimitCount;\n\t}\n\n\tpublic boolean isEscapeUnicode() {\n\t\treturn escapeUnicode;\n\t}\n\n\tpublic void setEscapeUnicode(boolean escapeUnicode) {\n\t\tthis.escapeUnicode = escapeUnicode;\n\t}\n\n\tpublic boolean isCfgOutput() {\n\t\treturn cfgOutput;\n\t}\n\n\tpublic void setCfgOutput(boolean cfgOutput) {\n\t\tthis.cfgOutput = cfgOutput;\n\t}\n\n\tpublic boolean isRawCfgOutput() {\n\t\treturn rawCfgOutput;\n\t}\n\n\tpublic void setRawCfgOutput(boolean rawCfgOutput) {\n\t\tthis.rawCfgOutput = rawCfgOutput;\n\t}\n\n\tpublic boolean isReplaceConsts() {\n\t\treturn replaceConsts;\n\t}\n\n\tpublic void setReplaceConsts(boolean replaceConsts) {\n\t\tthis.replaceConsts = replaceConsts;\n\t}\n\n\tpublic boolean isRespectBytecodeAccessModifiers() {\n\t\treturn respectBytecodeAccessModifiers;\n\t}\n\n\tpublic void setRespectBytecodeAccessModifiers(boolean respectBytecodeAccessModifiers) {\n\t\tthis.respectBytecodeAccessModifiers = respectBytecodeAccessModifiers;\n\t}\n\n\tpublic boolean isExportAsGradleProject() {\n\t\treturn exportAsGradleProject;\n\t}\n\n\tpublic void setExportAsGradleProject(boolean exportAsGradleProject) {\n\t\tthis.exportAsGradleProject = exportAsGradleProject;\n\t}\n\n\tpublic boolean isSkipXmlPrettyPrint() {\n\t\treturn skipXmlPrettyPrint;\n\t}\n\n\tpublic void setSkipXmlPrettyPrint(boolean skipXmlPrettyPrint) {\n\t\tthis.skipXmlPrettyPrint = skipXmlPrettyPrint;\n\t}\n\n\tpublic boolean isRenameCaseSensitive() {\n\t\treturn renameFlags.contains(RenameEnum.CASE);\n\t}\n\n\tpublic boolean isRenameValid() {\n\t\treturn renameFlags.contains(RenameEnum.VALID);\n\t}\n\n\tpublic boolean isRenamePrintable() {\n\t\treturn renameFlags.contains(RenameEnum.PRINTABLE);\n\t}\n\n\tpublic boolean isFsCaseSensitive() {\n\t\treturn fsCaseSensitive;\n\t}\n\n\tpublic void setFsCaseSensitive(boolean fsCaseSensitive) {\n\t\tthis.fsCaseSensitive = fsCaseSensitive;\n\t}\n\n\tpublic boolean isUseHeadersForDetectResourceExtensions() {\n\t\treturn useHeadersForDetectResourceExtensions;\n\t}\n\n\tpublic void setUseHeadersForDetectResourceExtensions(boolean useHeadersForDetectResourceExtensions) {\n\t\tthis.useHeadersForDetectResourceExtensions = useHeadersForDetectResourceExtensions;\n\t}\n\n\tpublic CommentsLevel getCommentsLevel() {\n\t\treturn commentsLevel;\n\t}\n\n\tpublic void setCommentsLevel(CommentsLevel commentsLevel) {\n\t\tthis.commentsLevel = commentsLevel;\n\t}\n\n\tpublic LogHelper.LogLevelEnum getLogLevel() {\n\t\treturn logLevel;\n\t}\n\n\tpublic void setLogLevel(LogHelper.LogLevelEnum logLevel) {\n\t\tthis.logLevel = logLevel;\n\t}\n\n\tpublic Map<String, String> getPluginOptions() {\n\t\treturn pluginOptions;\n\t}\n\n\tpublic void setPluginOptions(Map<String, String> pluginOptions) {\n\t\tthis.pluginOptions = pluginOptions;\n\t}\n\n\tpublic String getDisablePlugins() {\n\t\treturn disablePlugins;\n\t}\n\n\tpublic void setDisablePlugins(String disablePlugins) {\n\t\tthis.disablePlugins = disablePlugins;\n\t}\n\n\tpublic void setExportGradleType(@Nullable ExportGradleType exportGradleType) {\n\t\tthis.exportGradleType = exportGradleType;\n\t}\n\n\tpublic void setOutputFormat(String outputFormat) {\n\t\tthis.outputFormat = outputFormat;\n\t}\n\n\tpublic Set<RenameEnum> getRenameFlags() {\n\t\treturn renameFlags;\n\t}\n\n\tpublic void setRenameFlags(Set<RenameEnum> renameFlags) {\n\t\tthis.renameFlags = renameFlags;\n\t}\n\n\tpublic String getConfig() {\n\t\treturn config;\n\t}\n\n\tstatic class RenameConverter implements IStringConverter<Set<RenameEnum>> {\n\t\tprivate final String paramName;\n\n\t\tRenameConverter(String paramName) {\n\t\t\tthis.paramName = paramName;\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<RenameEnum> convert(String value) {\n\t\t\tif (value.equalsIgnoreCase(\"NONE\")) {\n\t\t\t\treturn EnumSet.noneOf(RenameEnum.class);\n\t\t\t}\n\t\t\tif (value.equalsIgnoreCase(\"ALL\")) {\n\t\t\t\treturn EnumSet.allOf(RenameEnum.class);\n\t\t\t}\n\t\t\tSet<RenameEnum> set = EnumSet.noneOf(RenameEnum.class);\n\t\t\tfor (String s : value.split(\",\")) {\n\t\t\t\ttry {\n\t\t\t\t\tset.add(RenameEnum.valueOf(s.trim().toUpperCase(Locale.ROOT)));\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tthrow new JadxArgsValidateException(\n\t\t\t\t\t\t\t'\\'' + s + \"' is unknown for parameter \" + paramName\n\t\t\t\t\t\t\t\t\t+ \", possible values are \" + enumValuesString(RenameEnum.values()));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn set;\n\t\t}\n\t}\n\n\tpublic static class CommentsLevelConverter extends BaseEnumConverter<CommentsLevel> {\n\t\tpublic CommentsLevelConverter() {\n\t\t\tsuper(CommentsLevel::valueOf, CommentsLevel::values);\n\t\t}\n\t}\n\n\tpublic static class UseKotlinMethodsForVarNamesConverter extends BaseEnumConverter<UseKotlinMethodsForVarNames> {\n\t\tpublic UseKotlinMethodsForVarNamesConverter() {\n\t\t\tsuper(UseKotlinMethodsForVarNames::valueOf, UseKotlinMethodsForVarNames::values);\n\t\t}\n\t}\n\n\tpublic static class DeobfuscationMapFileModeConverter extends BaseEnumConverter<GeneratedRenamesMappingFileMode> {\n\t\tpublic DeobfuscationMapFileModeConverter() {\n\t\t\tsuper(GeneratedRenamesMappingFileMode::valueOf, GeneratedRenamesMappingFileMode::values);\n\t\t}\n\t}\n\n\tpublic static class ResourceNameSourceConverter extends BaseEnumConverter<ResourceNameSource> {\n\t\tpublic ResourceNameSourceConverter() {\n\t\t\tsuper(ResourceNameSource::valueOf, ResourceNameSource::values);\n\t\t}\n\t}\n\n\tpublic static class UseSourceNameAsClassNameConverter extends BaseEnumConverter<UseSourceNameAsClassNameAlias> {\n\t\tpublic UseSourceNameAsClassNameConverter() {\n\t\t\tsuper(UseSourceNameAsClassNameAlias::valueOf, UseSourceNameAsClassNameAlias::values);\n\t\t}\n\t}\n\n\tpublic static class DecompilationModeConverter extends BaseEnumConverter<DecompilationMode> {\n\t\tpublic DecompilationModeConverter() {\n\t\t\tsuper(DecompilationMode::valueOf, DecompilationMode::values);\n\t\t}\n\t}\n\n\tpublic static class ExportGradleTypeConverter extends BaseEnumConverter<ExportGradleType> {\n\t\tpublic ExportGradleTypeConverter() {\n\t\t\tsuper(ExportGradleType::valueOf, ExportGradleType::values);\n\t\t}\n\t}\n\n\tpublic static class LogLevelConverter extends BaseEnumConverter<LogHelper.LogLevelEnum> {\n\t\tpublic LogLevelConverter() {\n\t\t\tsuper(LogHelper.LogLevelEnum::valueOf, LogHelper.LogLevelEnum::values);\n\t\t}\n\t}\n\n\tpublic static class IntegerFormatConverter extends BaseEnumConverter<IntegerFormat> {\n\t\tpublic IntegerFormatConverter() {\n\t\t\tsuper(IntegerFormat::valueOf, IntegerFormat::values);\n\t\t}\n\t}\n\n\tpublic abstract static class BaseEnumConverter<E extends Enum<E>> implements IStringConverter<E> {\n\t\tprivate final Function<String, E> parse;\n\t\tprivate final Supplier<E[]> values;\n\n\t\tpublic BaseEnumConverter(Function<String, E> parse, Supplier<E[]> values) {\n\t\t\tthis.parse = parse;\n\t\t\tthis.values = values;\n\t\t}\n\n\t\t@Override\n\t\tpublic E convert(String value) {\n\t\t\ttry {\n\t\t\t\treturn parse.apply(stringAsEnumName(value));\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new JadxArgsValidateException(\n\t\t\t\t\t\t'\\'' + value + \"' is unknown, possible values are: \" + enumValuesString(values.get()));\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static String enumValuesString(Enum<?>[] values) {\n\t\treturn Stream.of(values)\n\t\t\t\t.map(v -> v.name().replace('_', '-').toLowerCase(Locale.ROOT))\n\t\t\t\t.collect(Collectors.joining(\", \"));\n\t}\n\n\tprivate static String stringAsEnumName(String value) {\n\t\t// inverse of enumValuesString conversion\n\t\treturn value.replace('-', '_').toUpperCase(Locale.ROOT);\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/main/java/jadx/cli/JadxCLICommands.java",
    "content": "package jadx.cli;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport com.beust.jcommander.JCommander;\n\nimport jadx.cli.commands.CommandPlugins;\nimport jadx.cli.commands.ICommand;\nimport jadx.core.utils.exceptions.JadxArgsValidateException;\n\npublic class JadxCLICommands {\n\tprivate static final Map<String, ICommand> COMMANDS_MAP = new LinkedHashMap<>();\n\n\tstatic {\n\t\tJadxCLICommands.register(new CommandPlugins());\n\t}\n\n\tpublic static void register(ICommand command) {\n\t\tCOMMANDS_MAP.put(command.name(), command);\n\t}\n\n\tpublic static void append(JCommander.Builder builder) {\n\t\tCOMMANDS_MAP.forEach(builder::addCommand);\n\t}\n\n\tpublic static boolean process(JCommanderWrapper jcw, JCommander jc, String parsedCommand) {\n\t\tICommand command = COMMANDS_MAP.get(parsedCommand);\n\t\tif (command == null) {\n\t\t\tthrow new JadxArgsValidateException(\"Unknown command: \" + parsedCommand\n\t\t\t\t\t+ \". Expected one of: \" + COMMANDS_MAP.keySet());\n\t\t}\n\t\tJCommander subCommander = jc.getCommands().get(parsedCommand);\n\t\tcommand.process(jcw, subCommander);\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/main/java/jadx/cli/LogHelper.java",
    "content": "package jadx.cli;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.LoggerFactory;\n\nimport ch.qos.logback.classic.Level;\nimport ch.qos.logback.classic.Logger;\n\nimport jadx.api.JadxDecompiler;\n\npublic class LogHelper {\n\tprivate static final org.slf4j.Logger LOG = LoggerFactory.getLogger(LogHelper.class);\n\n\tpublic enum LogLevelEnum {\n\t\tQUIET(Level.OFF),\n\t\tPROGRESS(Level.OFF),\n\t\tERROR(Level.ERROR),\n\t\tWARN(Level.WARN),\n\t\tINFO(Level.INFO),\n\t\tDEBUG(Level.DEBUG);\n\n\t\tprivate final Level level;\n\n\t\tLogLevelEnum(Level level) {\n\t\t\tthis.level = level;\n\t\t}\n\n\t\tpublic Level getLevel() {\n\t\t\treturn level;\n\t\t}\n\t}\n\n\t@Nullable(\"For disable log level control\")\n\tprivate static LogLevelEnum logLevelValue;\n\n\tpublic static void initLogLevel(JadxCLIArgs args) {\n\t\tlogLevelValue = getLogLevelFromArgs(args);\n\t}\n\n\tprivate static LogLevelEnum getLogLevelFromArgs(JadxCLIArgs args) {\n\t\tif (isCustomLogConfig()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (args.quiet) {\n\t\t\targs.logLevel = LogLevelEnum.QUIET;\n\t\t} else if (args.verbose) {\n\t\t\targs.logLevel = LogLevelEnum.DEBUG;\n\t\t}\n\t\treturn args.logLevel;\n\t}\n\n\tpublic static void setLogLevel(LogLevelEnum newLogLevel) {\n\t\tlogLevelValue = newLogLevel;\n\t\tapplyLogLevel(logLevelValue);\n\t}\n\n\tpublic static void applyLogLevels() {\n\t\tif (logLevelValue == null) {\n\t\t\treturn;\n\t\t}\n\t\tapplyLogLevel(logLevelValue);\n\t\tif (logLevelValue == LogLevelEnum.PROGRESS) {\n\t\t\tfixForShowProgress();\n\t\t}\n\t}\n\n\t/**\n\t * Show progress: change to 'INFO' for control classes\n\t */\n\tprivate static void fixForShowProgress() {\n\t\tsetLevelForClass(JadxCLI.class, Level.INFO);\n\t\tsetLevelForClass(JadxDecompiler.class, Level.INFO);\n\t\tsetLevelForClass(SingleClassMode.class, Level.INFO);\n\n\t\t// show warnings and errors from input plugins\n\t\tsetLevelForPackage(\"jadx.plugins.input\", Level.WARN);\n\t}\n\n\tprivate static void applyLogLevel(@NotNull LogLevelEnum logLevel) {\n\t\tLogger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);\n\t\trootLogger.setLevel(logLevel.getLevel());\n\t}\n\n\t@Nullable\n\tpublic static LogLevelEnum getLogLevel() {\n\t\treturn logLevelValue;\n\t}\n\n\tpublic static void setLevelForClass(Class<?> cls, Level level) {\n\t\t((Logger) LoggerFactory.getLogger(cls)).setLevel(level);\n\t}\n\n\tpublic static void setLevelForPackage(String pkgName, Level level) {\n\t\t((Logger) LoggerFactory.getLogger(pkgName)).setLevel(level);\n\t}\n\n\t/**\n\t * Try to detect if user provide custom logback config via -Dlogback.configurationFile=\n\t */\n\tprivate static boolean isCustomLogConfig() {\n\t\ttry {\n\t\t\tString logbackConfig = System.getProperty(\"logback.configurationFile\");\n\t\t\tif (logbackConfig == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tLOG.debug(\"Use custom log config: {}\", logbackConfig);\n\t\t\treturn true;\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to detect custom log config\", e);\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/main/java/jadx/cli/SingleClassMode.java",
    "content": "package jadx.cli;\n\nimport java.io.File;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.JadxDecompiler;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.visitors.SaveCode;\nimport jadx.core.utils.exceptions.JadxArgsValidateException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\n\npublic class SingleClassMode {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SingleClassMode.class);\n\n\tpublic static boolean process(JadxDecompiler jadx, JadxCLIArgs cliArgs) {\n\t\tString singleClass = cliArgs.getSingleClass();\n\t\tString singleClassOutput = cliArgs.getSingleClassOutput();\n\t\tif (singleClass == null && singleClassOutput == null) {\n\t\t\treturn false;\n\t\t}\n\t\tClassNode clsForProcess;\n\t\tif (singleClass != null) {\n\t\t\tclsForProcess = jadx.getRoot().resolveClass(singleClass);\n\t\t\tif (clsForProcess == null) {\n\t\t\t\tclsForProcess = jadx.getRoot().getClasses().stream()\n\t\t\t\t\t\t.filter(cls -> cls.getClassInfo().getAliasFullName().equals(singleClass))\n\t\t\t\t\t\t.findFirst().orElse(null);\n\t\t\t}\n\t\t\tif (clsForProcess == null) {\n\t\t\t\tthrow new JadxArgsValidateException(\"Input class not found: \" + singleClass);\n\t\t\t}\n\t\t\tif (clsForProcess.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\tthrow new JadxArgsValidateException(\"Input class can't be saved by current jadx settings (marked as DONT_GENERATE)\");\n\t\t\t}\n\t\t\tif (clsForProcess.isInner()) {\n\t\t\t\tclsForProcess = clsForProcess.getTopParentClass();\n\t\t\t\tLOG.warn(\"Input class is inner, parent class will be saved: {}\", clsForProcess.getFullName());\n\t\t\t}\n\t\t} else {\n\t\t\t// singleClassOutput is set\n\t\t\t// expect only one class to be loaded\n\t\t\tList<ClassNode> classes = jadx.getRoot().getClasses().stream()\n\t\t\t\t\t.filter(c -> !c.isInner() && !c.contains(AFlag.DONT_GENERATE))\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t\tint size = classes.size();\n\t\t\tif (size == 1) {\n\t\t\t\tclsForProcess = classes.get(0);\n\t\t\t} else {\n\t\t\t\tthrow new JadxArgsValidateException(\"Found \" + size + \" classes, single class output can't be used\");\n\t\t\t}\n\t\t}\n\t\tICodeInfo codeInfo;\n\t\ttry {\n\t\t\tcodeInfo = clsForProcess.decompile();\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Class decompilation failed\", e);\n\t\t}\n\t\tString fileExt = SaveCode.getFileExtension(jadx.getRoot());\n\t\tFile out;\n\t\tif (singleClassOutput == null) {\n\t\t\tout = new File(jadx.getArgs().getOutDirSrc(), clsForProcess.getClassInfo().getAliasFullPath() + fileExt);\n\t\t} else {\n\t\t\tif (singleClassOutput.endsWith(fileExt)) {\n\t\t\t\t// treat as file name\n\t\t\t\tout = new File(singleClassOutput);\n\t\t\t} else {\n\t\t\t\t// treat as directory\n\t\t\t\tout = new File(singleClassOutput, clsForProcess.getShortName() + fileExt);\n\t\t\t}\n\t\t}\n\t\tFile resultOut = FileUtils.prepareFile(out);\n\t\tif (clsForProcess.getClassInfo().hasAlias()) {\n\t\t\tLOG.info(\"Saving class '{}' (alias: '{}') to file '{}'\",\n\t\t\t\t\tclsForProcess.getClassInfo().getFullName(), clsForProcess.getFullName(), resultOut.getAbsolutePath());\n\t\t} else {\n\t\t\tLOG.info(\"Saving class '{}' to file '{}'\", clsForProcess.getFullName(), resultOut.getAbsolutePath());\n\t\t}\n\t\tSaveCode.save(codeInfo.getCodeStr(), resultOut);\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/main/java/jadx/cli/clst/ConvertToClsSet.java",
    "content": "package jadx.cli.clst;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.JadxDecompiler;\nimport jadx.api.args.UseSourceNameAsClassNameAlias;\nimport jadx.core.clsp.ClsSet;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.files.FileUtils;\n\n/**\n * Utility class for convert dex or jar to jadx classes set (.jcst)\n */\npublic class ConvertToClsSet {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ConvertToClsSet.class);\n\n\tpublic static void usage() {\n\t\tLOG.info(\"<android API level (number)> <output .jcst file> <several input dex or jar files> \");\n\t\tLOG.info(\"Arguments to update core.jcst: \"\n\t\t\t\t+ \"<android API level (number)> \"\n\t\t\t\t+ \"<jadx root>/jadx-core/src/main/resources/clst/core.jcst \"\n\t\t\t\t+ \"<sdk_root>/platforms/android-<api level>/android.jar\"\n\t\t\t\t+ \"<sdk_root>/platforms/android-<api level>/optional/android.car.jar \"\n\t\t\t\t+ \"<sdk_root>/platforms/android-<api level>/optional/org.apache.http.legacy.jar\");\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tif (args.length != 5) {\n\t\t\tusage();\n\t\t\tSystem.exit(1);\n\t\t}\n\t\tint androidApiLevel = Integer.parseInt(args[0]);\n\t\tList<Path> inputPaths = Stream.of(args).skip(1).map(Paths::get).collect(Collectors.toList());\n\t\tPath output = inputPaths.remove(0);\n\n\t\tJadxArgs jadxArgs = new JadxArgs();\n\t\tjadxArgs.setInputFiles(FileUtils.toFiles(inputPaths));\n\n\t\t// disable not needed passes executed at prepare stage\n\t\tjadxArgs.setDeobfuscationOn(false);\n\t\tjadxArgs.setRenameFlags(EnumSet.noneOf(JadxArgs.RenameEnum.class));\n\t\tjadxArgs.setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias.NEVER);\n\t\tjadxArgs.setMoveInnerClasses(false);\n\t\tjadxArgs.setInlineAnonymousClasses(false);\n\t\tjadxArgs.setInlineMethods(false);\n\n\t\t// don't require/load class set file\n\t\tjadxArgs.setLoadJadxClsSetFile(false);\n\n\t\ttry (JadxDecompiler decompiler = new JadxDecompiler(jadxArgs)) {\n\t\t\tdecompiler.load();\n\t\t\tRootNode root = decompiler.getRoot();\n\t\t\tClsSet set = new ClsSet(root);\n\t\t\tset.setAndroidApiLevel(androidApiLevel);\n\t\t\tset.loadFrom(root);\n\t\t\tset.save(output);\n\n\t\t\tLOG.info(\"Output: {}\", output);\n\t\t\tLOG.info(\"done\");\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed with error\", e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/main/java/jadx/cli/commands/CommandPlugins.java",
    "content": "package jadx.cli.commands;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport com.beust.jcommander.JCommander;\nimport com.beust.jcommander.Parameter;\nimport com.beust.jcommander.Parameters;\n\nimport jadx.api.plugins.JadxPluginInfo;\nimport jadx.cli.JCommanderWrapper;\nimport jadx.cli.LogHelper;\nimport jadx.core.utils.StringUtils;\nimport jadx.plugins.tools.JadxPluginsList;\nimport jadx.plugins.tools.JadxPluginsTools;\nimport jadx.plugins.tools.data.JadxPluginListEntry;\nimport jadx.plugins.tools.data.JadxPluginMetadata;\nimport jadx.plugins.tools.data.JadxPluginUpdate;\n\n@Parameters(commandDescription = \"manage jadx plugins\")\npublic class CommandPlugins implements ICommand {\n\n\t@Parameter(names = { \"-i\", \"--install\" }, description = \"install plugin with locationId\", defaultValueDescription = \"<locationId>\")\n\tprotected String install;\n\n\t@Parameter(names = { \"-j\", \"--install-jar\" }, description = \"install plugin from jar file\", defaultValueDescription = \"<path-to.jar>\")\n\tprotected String installJar;\n\n\t@Parameter(names = { \"-l\", \"--list\" }, description = \"list installed plugins\")\n\tprotected boolean list;\n\n\t@Parameter(names = { \"-a\", \"--available\" }, description = \"list available plugins from jadx-plugins-list (aka marketplace)\")\n\tprotected boolean available;\n\n\t@Parameter(names = { \"-u\", \"--update\" }, description = \"update installed plugins\")\n\tprotected boolean update;\n\n\t@Parameter(names = { \"--uninstall\" }, description = \"uninstall plugin with pluginId\", defaultValueDescription = \"<pluginId>\")\n\tprotected String uninstall;\n\n\t@Parameter(names = { \"--disable\" }, description = \"disable plugin with pluginId\", defaultValueDescription = \"<pluginId>\")\n\tprotected String disable;\n\n\t@Parameter(names = { \"--enable\" }, description = \"enable plugin with pluginId\", defaultValueDescription = \"<pluginId>\")\n\tprotected String enable;\n\n\t@Parameter(names = { \"--list-all\" }, description = \"list all plugins including bundled and dropins\")\n\tprotected boolean listAll;\n\n\t@Parameter(\n\t\t\tnames = { \"--list-versions\" },\n\t\t\tdescription = \"fetch latest versions of plugin from locationId (will download all artefacts, limited to 10)\",\n\t\t\tdefaultValueDescription = \"<locationId>\"\n\t)\n\tprotected String listVersions;\n\n\t@Parameter(names = { \"-h\", \"--help\" }, description = \"print this help\", help = true)\n\tprotected boolean printHelp = false;\n\n\t@Override\n\tpublic String name() {\n\t\treturn \"plugins\";\n\t}\n\n\t@SuppressWarnings(\"UnnecessaryReturnStatement\")\n\t@Override\n\tpublic void process(JCommanderWrapper jcw, JCommander subCommander) {\n\t\tif (printHelp) {\n\t\t\tjcw.printUsage(subCommander);\n\t\t\treturn;\n\t\t}\n\t\tSet<String> unknownOptions = new HashSet<>(subCommander.getUnknownOptions());\n\t\tboolean verbose = unknownOptions.remove(\"-v\") || unknownOptions.remove(\"--verbose\");\n\t\tLogHelper.setLogLevel(verbose ? LogHelper.LogLevelEnum.DEBUG : LogHelper.LogLevelEnum.INFO);\n\n\t\tif (!unknownOptions.isEmpty()) {\n\t\t\tSystem.out.println(\"Error: found unknown options: \" + unknownOptions);\n\t\t}\n\n\t\tif (install != null) {\n\t\t\tinstallPlugin(install);\n\t\t\treturn;\n\t\t}\n\t\tif (installJar != null) {\n\t\t\tinstallPlugin(\"file:\" + installJar);\n\t\t\treturn;\n\t\t}\n\t\tif (uninstall != null) {\n\t\t\tboolean uninstalled = JadxPluginsTools.getInstance().uninstall(uninstall);\n\t\t\tSystem.out.println(uninstalled ? \"Uninstalled\" : \"Plugin not found\");\n\t\t\treturn;\n\t\t}\n\t\tif (update) {\n\t\t\tList<JadxPluginUpdate> updates = JadxPluginsTools.getInstance().updateAll();\n\t\t\tif (updates.isEmpty()) {\n\t\t\t\tSystem.out.println(\"No updates\");\n\t\t\t} else {\n\t\t\t\tSystem.out.println(\"Installed updates: \" + updates.size());\n\t\t\t\tfor (JadxPluginUpdate update : updates) {\n\t\t\t\t\tSystem.out.println(\"  \" + update.getPluginId() + \": \" + update.getOldVersion() + \" -> \" + update.getNewVersion());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (list) {\n\t\t\tprintPlugins(JadxPluginsTools.getInstance().getInstalled());\n\t\t\treturn;\n\t\t}\n\t\tif (listAll) {\n\t\t\tprintAllPlugins();\n\t\t\treturn;\n\t\t}\n\t\tif (listVersions != null) {\n\t\t\tprintVersions(listVersions, 10);\n\t\t\treturn;\n\t\t}\n\t\tif (available) {\n\t\t\tList<JadxPluginListEntry> availableList = JadxPluginsList.getInstance().get();\n\t\t\tSystem.out.println(\"Available plugins: \" + availableList.size());\n\t\t\tfor (JadxPluginListEntry plugin : availableList) {\n\t\t\t\tSystem.out.println(\" - \" + plugin.getName() + \": \" + plugin.getDescription()\n\t\t\t\t\t\t+ \" (\" + plugin.getLocationId() + \")\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (disable != null) {\n\t\t\tif (JadxPluginsTools.getInstance().changeDisabledStatus(disable, true)) {\n\t\t\t\tSystem.out.println(\"Plugin '\" + disable + \"' disabled.\");\n\t\t\t} else {\n\t\t\t\tSystem.out.println(\"Plugin '\" + disable + \"' already disabled.\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (enable != null) {\n\t\t\tif (JadxPluginsTools.getInstance().changeDisabledStatus(enable, false)) {\n\t\t\t\tSystem.out.println(\"Plugin '\" + enable + \"' enabled.\");\n\t\t\t} else {\n\t\t\t\tSystem.out.println(\"Plugin '\" + enable + \"' already enabled.\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate static void printPlugins(List<JadxPluginMetadata> installed) {\n\t\tSystem.out.println(\"Installed plugins: \" + installed.size());\n\t\tfor (JadxPluginMetadata plugin : installed) {\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tsb.append(\" - \").append(plugin.getPluginId());\n\t\t\tString version = plugin.getVersion();\n\t\t\tif (version != null) {\n\t\t\t\tsb.append(\" (\").append(version).append(')');\n\t\t\t}\n\t\t\tif (plugin.isDisabled()) {\n\t\t\t\tsb.append(\" (disabled)\");\n\t\t\t}\n\t\t\tsb.append(\" - \").append(plugin.getName());\n\t\t\tsb.append(\": \").append(formatDescription(plugin.getDescription()));\n\t\t\tSystem.out.println(sb);\n\t\t}\n\t}\n\n\tprivate void printVersions(String locationId, int limit) {\n\t\tSystem.out.println(\"Loading ...\");\n\t\tList<JadxPluginMetadata> versions = JadxPluginsTools.getInstance().getVersionsByLocation(locationId, 1, limit);\n\t\tif (versions.isEmpty()) {\n\t\t\tSystem.out.println(\"No versions found\");\n\t\t\treturn;\n\t\t}\n\t\tJadxPluginMetadata plugin = versions.get(0);\n\t\tSystem.out.println(\"Versions for plugin id: \" + plugin.getPluginId());\n\t\tfor (JadxPluginMetadata version : versions) {\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tsb.append(\" - \").append(version.getVersion());\n\t\t\tString reqVer = version.getRequiredJadxVersion();\n\t\t\tif (StringUtils.notBlank(reqVer)) {\n\t\t\t\tsb.append(\", require jadx: \").append(reqVer);\n\t\t\t}\n\t\t\tSystem.out.println(sb);\n\t\t}\n\t}\n\n\tprivate static void printAllPlugins() {\n\t\tList<JadxPluginMetadata> installed = JadxPluginsTools.getInstance().getInstalled();\n\t\tprintPlugins(installed);\n\t\tSet<String> installedSet = installed.stream().map(JadxPluginMetadata::getPluginId).collect(Collectors.toSet());\n\n\t\tList<JadxPluginInfo> plugins = JadxPluginsTools.getInstance().getAllPluginsInfo();\n\t\tSystem.out.println(\"Other plugins: \" + plugins.size());\n\t\tfor (JadxPluginInfo plugin : plugins) {\n\t\t\tif (!installedSet.contains(plugin.getPluginId())) {\n\t\t\t\tSystem.out.println(\" - \" + plugin.getPluginId()\n\t\t\t\t\t\t+ \" - \" + plugin.getName()\n\t\t\t\t\t\t+ \": \" + formatDescription(plugin.getDescription()));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static String formatDescription(String desc) {\n\t\tif (desc.contains(\"\\n\")) {\n\t\t\t// remove new lines\n\t\t\tdesc = desc.replaceAll(\"\\\\R+\", \" \");\n\t\t}\n\t\tint maxLen = 512;\n\t\tif (desc.length() > maxLen) {\n\t\t\t// truncate very long descriptions\n\t\t\tdesc = desc.substring(0, maxLen) + \" ...\";\n\t\t}\n\t\treturn desc;\n\t}\n\n\tprivate void installPlugin(String locationId) {\n\t\tJadxPluginMetadata plugin = JadxPluginsTools.getInstance().install(locationId);\n\t\tSystem.out.println(\"Plugin installed: \" + plugin.getPluginId() + \":\" + plugin.getVersion());\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/main/java/jadx/cli/commands/ICommand.java",
    "content": "package jadx.cli.commands;\n\nimport com.beust.jcommander.JCommander;\n\nimport jadx.cli.JCommanderWrapper;\n\npublic interface ICommand {\n\tString name();\n\n\tvoid process(JCommanderWrapper jcw, JCommander subCommander);\n}\n"
  },
  {
    "path": "jadx-cli/src/main/java/jadx/cli/config/IJadxConfig.java",
    "content": "package jadx.cli.config;\n\n/**\n * Marker interface for jadx config objects\n */\npublic interface IJadxConfig {\n}\n"
  },
  {
    "path": "jadx-cli/src/main/java/jadx/cli/config/JadxConfigAdapter.java",
    "content": "package jadx.cli.config;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport com.google.gson.ExclusionStrategy;\nimport com.google.gson.FieldAttributes;\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.stream.JsonReader;\n\nimport jadx.commons.app.JadxCommonFiles;\nimport jadx.core.utils.GsonUtils;\nimport jadx.core.utils.exceptions.JadxArgsValidateException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class JadxConfigAdapter<T extends IJadxConfig> {\n\tprivate static final ExclusionStrategy GSON_EXCLUSION_STRATEGY = new ExclusionStrategy() {\n\t\t@Override\n\t\tpublic boolean shouldSkipField(FieldAttributes f) {\n\t\t\treturn f.getAnnotation(JadxConfigExclude.class) != null;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean shouldSkipClass(Class<?> clazz) {\n\t\t\treturn false;\n\t\t}\n\t};\n\n\tprivate final Class<T> configCls;\n\tprivate final String defaultConfigFileName;\n\tprivate final Gson gson;\n\n\tprivate Path configPath;\n\n\tpublic JadxConfigAdapter(Class<T> configCls, String defaultConfigName) {\n\t\tthis(configCls, defaultConfigName, gsonBuilder -> {\n\t\t});\n\t}\n\n\tpublic JadxConfigAdapter(Class<T> configCls, String defaultConfigName, Consumer<GsonBuilder> applyGsonOptions) {\n\t\tthis.configCls = configCls;\n\t\tthis.defaultConfigFileName = defaultConfigName + \".json\";\n\t\tGsonBuilder gsonBuilder = GsonUtils.defaultGsonBuilder();\n\t\tgsonBuilder.setExclusionStrategies(GSON_EXCLUSION_STRATEGY);\n\t\tapplyGsonOptions.accept(gsonBuilder);\n\t\tthis.gson = gsonBuilder.create();\n\t}\n\n\tpublic void useConfigRef(String configRef) {\n\t\tthis.configPath = resolveConfigRef(configRef);\n\t}\n\n\tpublic Path getConfigPath() {\n\t\treturn configPath;\n\t}\n\n\tpublic String getDefaultConfigFileName() {\n\t\treturn defaultConfigFileName;\n\t}\n\n\tpublic @Nullable T load() {\n\t\tif (!Files.isRegularFile(configPath)) {\n\t\t\t// file not found\n\t\t\treturn null;\n\t\t}\n\t\ttry (JsonReader reader = gson.newJsonReader(Files.newBufferedReader(configPath))) {\n\t\t\treturn gson.fromJson(reader, configCls);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to load config file: \" + configPath, e);\n\t\t}\n\t}\n\n\tpublic void save(T configObject) {\n\t\ttry {\n\t\t\tString jsonStr = gson.toJson(configObject, configCls);\n\t\t\t// don't use stream writer here because serialization errors will corrupt config\n\t\t\tFiles.writeString(configPath, jsonStr);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to save config file: \" + configPath, e);\n\t\t}\n\t}\n\n\tpublic String objectToJsonString(T configObject) {\n\t\treturn gson.toJson(configObject, configCls);\n\t}\n\n\tpublic T jsonStringToObject(String jsonStr) {\n\t\treturn gson.fromJson(jsonStr, configCls);\n\t}\n\n\tprivate Path resolveConfigRef(String configRef) {\n\t\tif (configRef == null || configRef.isEmpty()) {\n\t\t\t// use default config file\n\t\t\treturn JadxCommonFiles.getConfigDir().resolve(defaultConfigFileName);\n\t\t}\n\t\tif (configRef.contains(\"/\") || configRef.contains(\"\\\\\")) {\n\t\t\tif (!configRef.toLowerCase().endsWith(\".json\")) {\n\t\t\t\tthrow new JadxArgsValidateException(\"Config file extension should be '.json'\");\n\t\t\t}\n\t\t\treturn Path.of(configRef);\n\t\t}\n\t\t// treat as a short name\n\t\treturn JadxCommonFiles.getConfigDir().resolve(configRef + \".json\");\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/main/java/jadx/cli/config/JadxConfigExclude.java",
    "content": "package jadx.cli.config;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.FIELD)\npublic @interface JadxConfigExclude {\n}\n"
  },
  {
    "path": "jadx-cli/src/main/java/jadx/cli/plugins/JadxFilesGetter.java",
    "content": "package jadx.cli.plugins;\n\nimport java.nio.file.Path;\n\nimport jadx.commons.app.JadxCommonFiles;\nimport jadx.commons.app.JadxTempFiles;\nimport jadx.core.plugins.files.IJadxFilesGetter;\n\npublic class JadxFilesGetter implements IJadxFilesGetter {\n\n\tpublic static final JadxFilesGetter INSTANCE = new JadxFilesGetter();\n\n\t@Override\n\tpublic Path getConfigDir() {\n\t\treturn JadxCommonFiles.getConfigDir();\n\t}\n\n\t@Override\n\tpublic Path getCacheDir() {\n\t\treturn JadxCommonFiles.getCacheDir();\n\t}\n\n\t@Override\n\tpublic Path getTempDir() {\n\t\treturn JadxTempFiles.getTempRootDir();\n\t}\n\n\tprivate JadxFilesGetter() {\n\t\t// singleton\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/main/java/jadx/cli/tools/ConvertArscFile.java",
    "content": "package jadx.cli.tools;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxArgs;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.android.TextResMapFile;\nimport jadx.core.xmlgen.ResTableBinaryParser;\nimport jadx.zip.IZipEntry;\nimport jadx.zip.ZipContent;\nimport jadx.zip.ZipReader;\n\nimport static jadx.core.utils.files.FileUtils.expandDirs;\n\n/**\n * Utility class for convert '.arsc' to simple text file with mapping id to resource name\n */\npublic class ConvertArscFile {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ConvertArscFile.class);\n\tprivate static int rewritesCount;\n\n\tpublic static void usage() {\n\t\tLOG.info(\"<res-map file> <input .arsc/android.jar files or dir>\");\n\t\tLOG.info(\"\");\n\t\tLOG.info(\"Note: If res-map already exists - it will be merged and updated\");\n\t}\n\n\tpublic static void main(String[] args) throws IOException {\n\t\tif (args.length < 2) {\n\t\t\tusage();\n\t\t\tSystem.exit(1);\n\t\t}\n\t\tList<Path> inputPaths = Stream.of(args).map(Paths::get).collect(Collectors.toList());\n\t\tPath resMapFile = inputPaths.remove(0);\n\t\tList<Path> inputResFiles = filterAndSort(expandDirs(inputPaths));\n\t\tMap<Integer, String> resMap;\n\t\tif (Files.isReadable(resMapFile)) {\n\t\t\tresMap = TextResMapFile.read(resMapFile);\n\t\t} else {\n\t\t\tresMap = new HashMap<>();\n\t\t}\n\t\tLOG.info(\"Input entries count: {}\", resMap.size());\n\n\t\tRootNode root = new RootNode(new JadxArgs()); // not really needed\n\t\tZipReader zipReader = new ZipReader();\n\t\trewritesCount = 0;\n\t\tfor (Path resFile : inputResFiles) {\n\t\t\tResTableBinaryParser resTableParser = new ResTableBinaryParser(root, true);\n\t\t\tif (resFile.getFileName().toString().endsWith(\".jar\")) {\n\t\t\t\t// Load resources.arsc from android.jar\n\t\t\t\ttry (ZipContent zip = zipReader.open(resFile.toFile())) {\n\t\t\t\t\tIZipEntry entry = zip.searchEntry(\"resources.arsc\");\n\t\t\t\t\tif (entry == null) {\n\t\t\t\t\t\tLOG.error(\"Failed to load \\\"resources.arsc\\\" from {}\", resFile);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\ttry (InputStream inputStream = entry.getInputStream()) {\n\t\t\t\t\t\tresTableParser.decode(inputStream);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Load resources.arsc from extracted file\n\t\t\t\ttry (InputStream inputStream = Files.newInputStream(resFile)) {\n\t\t\t\t\tresTableParser.decode(inputStream);\n\t\t\t\t}\n\t\t\t}\n\t\t\tMap<Integer, String> singleResMap = resTableParser.getResStorage().getResourcesNames();\n\t\t\tmergeResMaps(resMap, singleResMap);\n\t\t\tLOG.info(\"{} entries count: {}, after merge: {}\", resFile.getFileName(), singleResMap.size(), resMap.size());\n\t\t}\n\t\tLOG.info(\"Output entries count: {}\", resMap.size());\n\t\tLOG.info(\"Total rewrites count: {}\", rewritesCount);\n\t\tTextResMapFile.write(resMapFile, resMap);\n\t\tLOG.info(\"Result file size: {} B\", resMapFile.toFile().length());\n\t\tLOG.info(\"done\");\n\t}\n\n\tprivate static List<Path> filterAndSort(List<Path> inputPaths) {\n\t\treturn inputPaths.stream()\n\t\t\t\t.filter(p -> {\n\t\t\t\t\tString fileName = p.getFileName().toString();\n\t\t\t\t\treturn fileName.endsWith(\".arsc\") || fileName.endsWith(\".jar\");\n\t\t\t\t})\n\t\t\t\t.sorted()\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tprivate static void mergeResMaps(Map<Integer, String> mainResMap, Map<Integer, String> newResMap) {\n\t\tfor (Map.Entry<Integer, String> entry : newResMap.entrySet()) {\n\t\t\tInteger id = entry.getKey();\n\t\t\tString name = entry.getValue();\n\t\t\tString prevName = mainResMap.put(id, name);\n\t\t\tif (prevName != null && !name.equals(prevName)) {\n\t\t\t\tLOG.debug(\"Rewrite id: {} from: '{}' to: '{}'\", Integer.toHexString(id), prevName, name);\n\t\t\t\trewritesCount++;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/main/resources/logback.xml",
    "content": "<!-- Jadx logger config. Used both in cli and gui -->\n\n<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t\t<encoder>\n\t\t\t<pattern>%-5level - %msg%n</pattern>\n\t\t</encoder>\n\t</appender>\n\n\t<!-- jadx-gui -->\n\t<logger name=\"com.pinterest.ktlint\" level=\"INFO\"/>\n\n\t<root level=\"INFO\">\n\t\t<appender-ref ref=\"STDOUT\"/>\n\t</root>\n</configuration>\n"
  },
  {
    "path": "jadx-cli/src/test/java/jadx/cli/BaseCliIntegrationTest.java",
    "content": "package jadx.cli;\n\nimport java.io.IOException;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.nio.file.Files;\nimport java.nio.file.LinkOption;\nimport java.nio.file.Path;\nimport java.nio.file.PathMatcher;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.loader.JadxBasePluginLoader;\nimport jadx.core.plugins.files.SingleDirFilesGetter;\nimport jadx.core.utils.Utils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\npublic class BaseCliIntegrationTest {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(BaseCliIntegrationTest.class);\n\n\tstatic final PathMatcher LOG_ALL_FILES = path -> {\n\t\tLOG.debug(\"File in result dir: {}\", path);\n\t\treturn true;\n\t};\n\n\t@TempDir\n\tPath testDir;\n\n\tPath outputDir;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\toutputDir = testDir.resolve(\"output\");\n\t}\n\n\tint execJadxCli(String sampleName, String... options) {\n\t\treturn execJadxCli(buildArgs(List.of(options), sampleName));\n\t}\n\n\tint execJadxCli(String[] args) {\n\t\treturn JadxCLI.execute(args, jadxArgs -> {\n\t\t\t// don't use global config and plugins\n\t\t\tjadxArgs.setFilesGetter(new SingleDirFilesGetter(testDir));\n\t\t\tjadxArgs.setPluginLoader(new JadxBasePluginLoader());\n\t\t});\n\t}\n\n\tString[] buildArgs(List<String> options, String... inputSamples) {\n\t\tList<String> args = new ArrayList<>(options);\n\t\targs.add(\"-v\");\n\t\targs.add(\"-d\");\n\t\targs.add(outputDir.toAbsolutePath().toString());\n\n\t\tfor (String inputSample : inputSamples) {\n\t\t\ttry {\n\t\t\t\tURL resource = getClass().getClassLoader().getResource(inputSample);\n\t\t\t\tassertThat(resource).isNotNull();\n\t\t\t\tString sampleFile = resource.toURI().getRawPath();\n\t\t\t\targs.add(sampleFile);\n\t\t\t} catch (URISyntaxException e) {\n\t\t\t\tfail(\"Failed to load sample: \" + inputSample, e);\n\t\t\t}\n\t\t}\n\t\treturn args.toArray(new String[0]);\n\t}\n\n\tvoid decompile(String... inputSamples) throws IOException {\n\t\tint result = execJadxCli(buildArgs(List.of(), inputSamples));\n\t\tassertThat(result).isEqualTo(0);\n\t\tList<Path> resultJavaFiles = collectJavaFilesInDir(outputDir);\n\t\tassertThat(resultJavaFiles).isNotEmpty();\n\n\t\t// do not copy input files as resources\n\t\tfor (Path path : collectFilesInDir(outputDir, LOG_ALL_FILES)) {\n\t\t\tfor (String inputSample : inputSamples) {\n\t\t\t\tassertThat(path.toAbsolutePath().toString()).doesNotContain(inputSample);\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic void printFiles(List<Path> files) {\n\t\tLOG.info(\"Output files (count: {}):\", files.size());\n\t\tfor (Path file : files) {\n\t\t\tLOG.info(\" {}\", file);\n\t\t}\n\t\tLOG.info(\"\");\n\t}\n\n\tString pathToUniformString(Path path) {\n\t\treturn path.toString().replace('\\\\', '/');\n\t}\n\n\tPath printFileContent(Path file) {\n\t\ttry {\n\t\t\tString content = Files.readString(outputDir.resolve(file));\n\t\t\tString spacer = Utils.strRepeat(\"=\", 70);\n\t\t\tLOG.info(\"File content: {}\\n{}\\n{}\\n{}\", file, spacer, content, spacer);\n\t\t\treturn file;\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(\"Failed to load file: \" + file, e);\n\t\t}\n\t}\n\n\tstatic List<Path> collectJavaFilesInDir(Path dir) throws IOException {\n\t\tPathMatcher javaMatcher = dir.getFileSystem().getPathMatcher(\"glob:**.java\");\n\t\treturn collectFilesInDir(dir, javaMatcher);\n\t}\n\n\tstatic List<Path> collectAllFilesInDir(Path dir) throws IOException {\n\t\ttry (Stream<Path> pathStream = Files.walk(dir)) {\n\t\t\tList<Path> files = pathStream\n\t\t\t\t\t.filter(Files::isRegularFile)\n\t\t\t\t\t.map(dir::relativize)\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t\tprintFiles(files);\n\t\t\treturn files;\n\t\t}\n\t}\n\n\tstatic List<Path> collectFilesInDir(Path dir, PathMatcher matcher) throws IOException {\n\t\ttry (Stream<Path> pathStream = Files.walk(dir)) {\n\t\t\tList<Path> files = pathStream\n\t\t\t\t\t.filter(p -> Files.isRegularFile(p, LinkOption.NOFOLLOW_LINKS))\n\t\t\t\t\t.filter(matcher::matches)\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t\tprintFiles(files);\n\t\t\treturn files;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/test/java/jadx/cli/JadxCLIArgsTest.java",
    "content": "package jadx.cli;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport static jadx.core.utils.Utils.newConstStringMap;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class JadxCLIArgsTest {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxCLIArgsTest.class);\n\n\t@Test\n\tpublic void testInvertedBooleanOption() {\n\t\tassertThat(parse(\"--no-replace-consts\").isReplaceConsts()).isFalse();\n\t\tassertThat(parse(\"\").isReplaceConsts()).isTrue();\n\t}\n\n\t@Test\n\tpublic void testEscapeUnicodeOption() {\n\t\tassertThat(parse(\"--escape-unicode\").isEscapeUnicode()).isTrue();\n\t\tassertThat(parse(\"\").isEscapeUnicode()).isFalse();\n\t}\n\n\t@Test\n\tpublic void testSrcOption() {\n\t\tassertThat(parse(\"--no-src\").isSkipSources()).isTrue();\n\t\tassertThat(parse(\"-s\").isSkipSources()).isTrue();\n\t\tassertThat(parse(\"\").isSkipSources()).isFalse();\n\t}\n\n\t@Test\n\tpublic void testOptionsOverride() {\n\t\tassertThat(override(new JadxCLIArgs(), \"--no-imports\").isUseImports()).isFalse();\n\t\tassertThat(override(new JadxCLIArgs(), \"--no-debug-info\").isDebugInfo()).isFalse();\n\t\tassertThat(override(new JadxCLIArgs(), \"\").isUseImports()).isTrue();\n\n\t\tJadxCLIArgs args = new JadxCLIArgs();\n\t\targs.useImports = false;\n\t\tassertThat(override(args, \"--no-imports\").isUseImports()).isFalse();\n\t\targs.debugInfo = false;\n\t\tassertThat(override(args, \"--no-debug-info\").isDebugInfo()).isFalse();\n\n\t\targs = new JadxCLIArgs();\n\t\targs.useImports = false;\n\t\tassertThat(override(args, \"\").isUseImports()).isFalse();\n\t}\n\n\t@Test\n\tpublic void testPluginOptionsOverride() {\n\t\t// add key to empty base map\n\t\tcheckPluginOptionsMerge(\n\t\t\t\tCollections.emptyMap(),\n\t\t\t\t\"-Poption=otherValue\",\n\t\t\t\tnewConstStringMap(\"option\", \"otherValue\"));\n\n\t\t// override one key\n\t\tcheckPluginOptionsMerge(\n\t\t\t\tnewConstStringMap(\"option\", \"value\"),\n\t\t\t\t\"-Poption=otherValue\",\n\t\t\t\tnewConstStringMap(\"option\", \"otherValue\"));\n\n\t\t// merge different keys\n\t\tcheckPluginOptionsMerge(\n\t\t\t\tCollections.singletonMap(\"option1\", \"value1\"),\n\t\t\t\t\"-Poption2=otherValue2\",\n\t\t\t\tnewConstStringMap(\"option1\", \"value1\", \"option2\", \"otherValue2\"));\n\n\t\t// merge and override\n\t\tcheckPluginOptionsMerge(\n\t\t\t\tnewConstStringMap(\"option1\", \"value1\", \"option2\", \"value2\"),\n\t\t\t\t\"-Poption2=otherValue2\",\n\t\t\t\tnewConstStringMap(\"option1\", \"value1\", \"option2\", \"otherValue2\"));\n\t}\n\n\tprivate void checkPluginOptionsMerge(Map<String, String> baseMap, String providedArgs, Map<String, String> expectedMap) {\n\t\tJadxCLIArgs args = new JadxCLIArgs();\n\t\targs.pluginOptions = baseMap;\n\t\tMap<String, String> resultMap = override(args, providedArgs).getPluginOptions();\n\t\tassertThat(resultMap).isEqualTo(expectedMap);\n\t}\n\n\tprivate JadxCLIArgs parse(String... args) {\n\t\treturn parse(new JadxCLIArgs(), args);\n\t}\n\n\tprivate JadxCLIArgs parse(JadxCLIArgs jadxArgs, String... args) {\n\t\treturn check(jadxArgs, jadxArgs.processArgs(args));\n\t}\n\n\tprivate JadxCLIArgs override(JadxCLIArgs jadxArgs, String... args) {\n\t\treturn check(jadxArgs, overrideProvided(jadxArgs, args));\n\t}\n\n\tprivate static boolean overrideProvided(JadxCLIArgs jadxArgs, String[] args) {\n\t\tJCommanderWrapper jcw = new JCommanderWrapper(new JadxCLIArgs());\n\t\tif (!jcw.parse(args)) {\n\t\t\treturn false;\n\t\t}\n\t\tjcw.overrideProvided(jadxArgs);\n\t\treturn jadxArgs.process(jcw);\n\t}\n\n\tprivate static JadxCLIArgs check(JadxCLIArgs jadxArgs, boolean res) {\n\t\tassertThat(res).isTrue();\n\t\tLOG.info(\"Jadx args: {}\", jadxArgs.toJadxArgs());\n\t\treturn jadxArgs;\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/test/java/jadx/cli/RenameConverterTest.java",
    "content": "package jadx.cli;\n\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.JadxArgs.RenameEnum;\nimport jadx.cli.JadxCLIArgs.RenameConverter;\nimport jadx.core.utils.exceptions.JadxArgsValidateException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\npublic class RenameConverterTest {\n\n\tprivate RenameConverter converter;\n\n\t@BeforeEach\n\tpublic void init() {\n\t\tconverter = new RenameConverter(\"someParam\");\n\t}\n\n\t@Test\n\tpublic void all() {\n\t\tSet<RenameEnum> set = converter.convert(\"all\");\n\t\tassertThat(set).hasSize(3);\n\t\tassertThat(set).contains(RenameEnum.CASE);\n\t\tassertThat(set).contains(RenameEnum.VALID);\n\t\tassertThat(set).contains(RenameEnum.PRINTABLE);\n\t}\n\n\t@Test\n\tpublic void none() {\n\t\tSet<RenameEnum> set = converter.convert(\"none\");\n\t\tassertThat(set).isEmpty();\n\t}\n\n\t@Test\n\tpublic void wrong() {\n\t\tJadxArgsValidateException thrown = assertThrows(JadxArgsValidateException.class,\n\t\t\t\t() -> converter.convert(\"wrong\"),\n\t\t\t\t\"Expected convert() to throw, but it didn't\");\n\n\t\tassertThat(thrown.getMessage()).isEqualTo(\"'wrong' is unknown for parameter someParam, possible values are case, valid, printable\");\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/test/java/jadx/cli/TestExport.java",
    "content": "package jadx.cli;\n\nimport org.assertj.core.api.Condition;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestExport extends BaseCliIntegrationTest {\n\n\t@Test\n\tpublic void testBasicExport() throws Exception {\n\t\tint result = execJadxCli(\"samples/small.apk\");\n\t\tassertThat(result).isEqualTo(0);\n\t\tassertThat(collectAllFilesInDir(outputDir))\n\t\t\t\t.map(this::pathToUniformString)\n\t\t\t\t.haveExactly(2, new Condition<>(f -> f.startsWith(\"sources/\") && f.endsWith(\".java\"), \"sources\"))\n\t\t\t\t.haveExactly(10, new Condition<>(f -> f.startsWith(\"resources/\"), \"resources\"))\n\t\t\t\t.haveExactly(1, new Condition<>(f -> f.equals(\"resources/AndroidManifest.xml\"), \"manifest\"))\n\t\t\t\t.hasSize(12);\n\t}\n\n\t@Test\n\tpublic void testGradleExportApk() throws Exception {\n\t\tint result = execJadxCli(\"samples/small.apk\", \"--export-gradle\");\n\t\tassertThat(result).isEqualTo(0);\n\t\tassertThat(collectAllFilesInDir(outputDir))\n\t\t\t\t.describedAs(\"check output files\")\n\t\t\t\t.map(this::pathToUniformString)\n\t\t\t\t.haveExactly(2, new Condition<>(f -> f.endsWith(\".java\"), \"java classes\"))\n\t\t\t\t.haveExactly(0, new Condition<>(f -> f.endsWith(\"classes.dex\"), \"dex files\"))\n\t\t\t\t.hasSize(15);\n\t}\n\n\t@Test\n\tpublic void testGradleExportAAR() throws Exception {\n\t\tint result = execJadxCli(\"samples/test-lib.aar\", \"--export-gradle\");\n\t\tassertThat(result).isEqualTo(0);\n\t\tassertThat(collectAllFilesInDir(outputDir))\n\t\t\t\t.describedAs(\"check output files\")\n\t\t\t\t.map(this::printFileContent)\n\t\t\t\t.map(this::pathToUniformString)\n\t\t\t\t.haveExactly(1, new Condition<>(f -> f.startsWith(\"lib/src/main/java/\") && f.endsWith(\".java\"), \"java\"))\n\t\t\t\t.haveExactly(0, new Condition<>(f -> f.endsWith(\".jar\"), \"jar files\"))\n\t\t\t\t.hasSize(8);\n\t}\n\n\t@Test\n\tpublic void testGradleExportSimpleJava() throws Exception {\n\t\tint result = execJadxCli(\"samples/HelloWorld.class\", \"--export-gradle\");\n\t\tassertThat(result).isEqualTo(0);\n\t\tassertThat(collectAllFilesInDir(outputDir))\n\t\t\t\t.describedAs(\"check output files\")\n\t\t\t\t.map(this::printFileContent)\n\t\t\t\t.map(this::pathToUniformString)\n\t\t\t\t.haveExactly(1, new Condition<>(f -> f.endsWith(\".java\") && f.startsWith(\"app/src/main/java/\"), \"java\"))\n\t\t\t\t.haveExactly(0, new Condition<>(f -> f.endsWith(\".class\"), \"class files\"))\n\t\t\t\t.haveExactly(1, new Condition<>(f -> f.equals(\"settings.gradle.kts\"), \"settings\"))\n\t\t\t\t.haveExactly(1, new Condition<>(f -> f.equals(\"app/build.gradle.kts\"), \"build\"))\n\t\t\t\t.hasSize(3);\n\t}\n\n\t@Test\n\tpublic void testGradleExportInvalidType() throws Exception {\n\t\tint result = execJadxCli(\"samples/HelloWorld.class\", \"--export-gradle-type\", \"android-app\");\n\t\tassertThat(result).isEqualTo(0);\n\t\t// expect output in 'android-app' template, but most fields will be set to UNKNOWN.\n\t\tassertThat(collectAllFilesInDir(outputDir))\n\t\t\t\t.describedAs(\"check output files\")\n\t\t\t\t.map(this::printFileContent)\n\t\t\t\t.map(this::pathToUniformString)\n\t\t\t\t.haveExactly(1, new Condition<>(f -> f.endsWith(\".java\") && f.startsWith(\"app/src/main/java/\"), \"java\"))\n\t\t\t\t.haveExactly(1, new Condition<>(f -> f.equals(\"settings.gradle\"), \"settings\"))\n\t\t\t\t.haveExactly(1, new Condition<>(f -> f.equals(\"build.gradle\"), \"build\"))\n\t\t\t\t.haveExactly(1, new Condition<>(f -> f.equals(\"app/build.gradle\"), \"app build\"))\n\t\t\t\t.hasSize(4);\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/test/java/jadx/cli/TestInput.java",
    "content": "package jadx.cli;\n\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport org.assertj.core.api.Condition;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestInput extends BaseCliIntegrationTest {\n\n\t@Test\n\tpublic void testHelp() {\n\t\tint result = execJadxCli(new String[] { \"--help\" });\n\t\tassertThat(result).isEqualTo(0);\n\t}\n\n\t@Test\n\tpublic void testApkInput() throws Exception {\n\t\tint result = execJadxCli(buildArgs(List.of(), \"samples/small.apk\"));\n\t\tassertThat(result).isEqualTo(0);\n\t\tassertThat(collectAllFilesInDir(outputDir))\n\t\t\t\t.describedAs(\"check output files\")\n\t\t\t\t.map(p -> p.getFileName().toString())\n\t\t\t\t.haveExactly(2, new Condition<>(f -> f.endsWith(\".java\"), \"java classes\"))\n\t\t\t\t.haveExactly(9, new Condition<>(f -> f.endsWith(\".xml\"), \"xml resources\"))\n\t\t\t\t.haveExactly(1, new Condition<>(f -> f.equals(\"AndroidManifest.xml\"), \"manifest\"))\n\t\t\t\t.hasSize(12);\n\t}\n\n\t@Test\n\tpublic void testDexInput() throws Exception {\n\t\tdecompile(\"samples/hello.dex\");\n\t}\n\n\t@Test\n\tpublic void testSmaliInput() throws Exception {\n\t\tdecompile(\"samples/HelloWorld.smali\");\n\t}\n\n\t@Test\n\tpublic void testClassInput() throws Exception {\n\t\tdecompile(\"samples/HelloWorld.class\");\n\t}\n\n\t@Test\n\tpublic void testMultipleInput() throws Exception {\n\t\tdecompile(\"samples/hello.dex\", \"samples/HelloWorld.smali\");\n\t}\n\n\t@Test\n\tpublic void testFallbackMode() throws Exception {\n\t\tint result = execJadxCli(buildArgs(List.of(\"-f\"), \"samples/hello.dex\"));\n\t\tassertThat(result).isEqualTo(0);\n\t\tList<Path> files = collectJavaFilesInDir(outputDir);\n\t\tassertThat(files).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void testSimpleMode() throws Exception {\n\t\tint result = execJadxCli(buildArgs(List.of(\"--decompilation-mode\", \"simple\"), \"samples/hello.dex\"));\n\t\tassertThat(result).isEqualTo(0);\n\t\tList<Path> files = collectJavaFilesInDir(outputDir);\n\t\tassertThat(files).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void testResourceOnly() throws Exception {\n\t\tint result = execJadxCli(buildArgs(List.of(), \"samples/resources-only.apk\"));\n\t\tassertThat(result).isEqualTo(0);\n\t\tList<Path> files = collectFilesInDir(outputDir,\n\t\t\t\tpath -> path.getFileName().toString().equalsIgnoreCase(\"AndroidManifest.xml\"));\n\t\tassertThat(files).isNotEmpty();\n\t}\n}\n"
  },
  {
    "path": "jadx-cli/src/test/java/jadx/plugins/tools/utils/PluginUtilsTest.java",
    "content": "package jadx.plugins.tools.utils;\n\nimport org.junit.jupiter.api.Test;\n\nimport static jadx.plugins.tools.utils.PluginUtils.extractVersion;\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass PluginUtilsTest {\n\n\t@Test\n\tpublic void testExtractVersion() {\n\t\tassertThat(extractVersion(\"plugin-name-v1.2.3.jar\")).isEqualTo(\"1.2.3\");\n\t\tassertThat(extractVersion(\"plugin-name-v1.2.jar\")).isEqualTo(\"1.2\");\n\t\tassertThat(extractVersion(\"1.2.3.jar\")).isEqualTo(\"1.2.3\");\n\t}\n\n}\n"
  },
  {
    "path": "jadx-cli/src/test/resources/samples/HelloWorld.smali",
    "content": ".class Lsmali/HelloWorld;\n.super Ljava/lang/Object;\n.source \"HelloWorld.java\"\n\n.method constructor <init>()V\n    .registers 1\n\n    .line 1\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n.method public static main([Ljava/lang/String;)V\n    .registers 2\n\n    .line 3\n    sget-object p0, Ljava/lang/System;->out:Ljava/io/PrintStream;\n\n    const-string v0, \"Hello, World\"\n\n    invoke-virtual {p0, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n\n    .line 4\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-commons/jadx-app-commons/README.md",
    "content": "## jadx app commons\n\nThis module contains common utilities used in jadx apps (cli and gui) and not needed in jadx-code module:\n- `JadxCommonFiles` - wrapper for `dev.dirs:directories` lib to get\n  'config' and 'cache' directories in cross-platform way\n- `JadxCommonEnv` - utils for work with environment variables\n"
  },
  {
    "path": "jadx-commons/jadx-app-commons/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-library\")\n}\n\ndependencies {\n\timplementation(\"io.get-coursier.util:directories-jni:0.1.4\")\n}\n"
  },
  {
    "path": "jadx-commons/jadx-app-commons/src/main/java/jadx/commons/app/JadxCommonEnv.java",
    "content": "package jadx.commons.app;\n\npublic class JadxCommonEnv {\n\n\tpublic static String get(String varName, String defValue) {\n\t\tString strValue = System.getenv(varName);\n\t\treturn isNullOrEmpty(strValue) ? defValue : strValue;\n\t}\n\n\tpublic static boolean getBool(String varName, boolean defValue) {\n\t\tString strValue = System.getenv(varName);\n\t\tif (isNullOrEmpty(strValue)) {\n\t\t\treturn defValue;\n\t\t}\n\t\treturn strValue.equalsIgnoreCase(\"true\");\n\t}\n\n\tpublic static int getInt(String varName, int defValue) {\n\t\tString strValue = System.getenv(varName);\n\t\tif (isNullOrEmpty(strValue)) {\n\t\t\treturn defValue;\n\t\t}\n\t\treturn Integer.parseInt(strValue);\n\t}\n\n\tprivate static boolean isNullOrEmpty(String value) {\n\t\treturn value == null || value.isEmpty();\n\t}\n}\n"
  },
  {
    "path": "jadx-commons/jadx-app-commons/src/main/java/jadx/commons/app/JadxCommonFiles.java",
    "content": "package jadx.commons.app;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.function.Function;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport dev.dirs.ProjectDirectories;\nimport dev.dirs.impl.Windows;\nimport dev.dirs.impl.WindowsPowerShell;\nimport dev.dirs.jni.WindowsJni;\n\npublic class JadxCommonFiles {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxCommonFiles.class);\n\n\tprivate static final Path CONFIG_DIR;\n\tprivate static final Path CACHE_DIR;\n\n\tpublic static Path getConfigDir() {\n\t\treturn CONFIG_DIR;\n\t}\n\n\tpublic static Path getCacheDir() {\n\t\treturn CACHE_DIR;\n\t}\n\n\tstatic {\n\t\tDirsLoader loader = new DirsLoader();\n\t\tloader.init();\n\t\tCONFIG_DIR = loader.getConfigDir();\n\t\tCACHE_DIR = loader.getCacheDir();\n\t}\n\n\tprivate static final class DirsLoader {\n\t\tprivate @Nullable ProjectDirectories dirs;\n\t\tprivate Path configDir;\n\t\tprivate Path cacheDir;\n\n\t\tpublic void init() {\n\t\t\ttry {\n\t\t\t\tconfigDir = loadEnvDir(\"JADX_CONFIG_DIR\", pd -> pd.configDir);\n\t\t\t\tcacheDir = loadEnvDir(\"JADX_CACHE_DIR\", pd -> pd.cacheDir);\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new RuntimeException(\"Failed to init common directories\", e);\n\t\t\t}\n\t\t}\n\n\t\tprivate Path loadEnvDir(String envVar, Function<ProjectDirectories, String> dirFunc) throws IOException {\n\t\t\tString envDir = JadxCommonEnv.get(envVar, null);\n\t\t\tString dirStr;\n\t\t\tif (envDir != null) {\n\t\t\t\tdirStr = envDir;\n\t\t\t} else {\n\t\t\t\tdirStr = dirFunc.apply(loadDirs());\n\t\t\t}\n\t\t\tPath path = Path.of(dirStr).toAbsolutePath();\n\t\t\tFiles.createDirectories(path);\n\t\t\treturn path;\n\t\t}\n\n\t\tprivate synchronized ProjectDirectories loadDirs() {\n\t\t\tProjectDirectories currentDirs = dirs;\n\t\t\tif (currentDirs != null) {\n\t\t\t\treturn currentDirs;\n\t\t\t}\n\t\t\tLOG.debug(\"Loading system dirs ...\");\n\t\t\tlong start = System.currentTimeMillis();\n\n\t\t\tProjectDirectories loadedDirs = ProjectDirectories.from(\"io.github\", \"skylot\", \"jadx\", DirsLoader::getWinDirs);\n\n\t\t\tif (LOG.isDebugEnabled()) {\n\t\t\t\tLOG.debug(\"Loaded system dirs ({}ms): config: {}, cache: {}\",\n\t\t\t\t\t\tSystem.currentTimeMillis() - start, loadedDirs.configDir, loadedDirs.cacheDir);\n\t\t\t}\n\t\t\tdirs = loadedDirs;\n\t\t\treturn loadedDirs;\n\t\t}\n\n\t\t/**\n\t\t * Return JNI, Foreign or PowerShell implementation\n\t\t */\n\t\tprivate static Windows getWinDirs() {\n\t\t\tWindows defSup = Windows.getDefaultSupplier().get();\n\t\t\tif (defSup instanceof WindowsPowerShell) {\n\t\t\t\tif (JadxSystemInfo.IS_AMD64) {\n\t\t\t\t\t// JNI library compiled for x86-64\n\t\t\t\t\treturn new WindowsJni();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn defSup;\n\t\t}\n\n\t\tpublic Path getCacheDir() {\n\t\t\treturn cacheDir;\n\t\t}\n\n\t\tpublic Path getConfigDir() {\n\t\t\treturn configDir;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-commons/jadx-app-commons/src/main/java/jadx/commons/app/JadxSystemInfo.java",
    "content": "package jadx.commons.app;\n\nimport java.util.Locale;\n\npublic class JadxSystemInfo {\n\tpublic static final String JAVA_VM = System.getProperty(\"java.vm.name\", \"?\");\n\tpublic static final String JAVA_VER = System.getProperty(\"java.version\", \"?\");\n\n\tpublic static final String OS_NAME = System.getProperty(\"os.name\", \"?\");\n\tpublic static final String OS_ARCH = System.getProperty(\"os.arch\", \"?\");\n\tpublic static final String OS_VERSION = System.getProperty(\"os.version\", \"?\");\n\n\tprivate static final String OS_NAME_LOWER = OS_NAME.toLowerCase(Locale.ENGLISH);\n\tpublic static final boolean IS_WINDOWS = OS_NAME_LOWER.startsWith(\"windows\");\n\tpublic static final boolean IS_MAC = OS_NAME_LOWER.startsWith(\"mac\");\n\tpublic static final boolean IS_LINUX = !IS_WINDOWS && !IS_MAC;\n\tpublic static final boolean IS_UNIX = !IS_WINDOWS;\n\n\tprivate static final String OS_ARCH_LOWER = OS_NAME.toLowerCase(Locale.ENGLISH);\n\tpublic static final boolean IS_AMD64 = OS_ARCH_LOWER.equals(\"amd64\");\n\tpublic static final boolean IS_ARM64 = OS_ARCH_LOWER.equals(\"aarch64\");\n\n\tprivate JadxSystemInfo() {\n\t}\n}\n"
  },
  {
    "path": "jadx-commons/jadx-app-commons/src/main/java/jadx/commons/app/JadxTempFiles.java",
    "content": "package jadx.commons.app;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\npublic class JadxTempFiles {\n\tprivate static final String JADX_TMP_INSTANCE_PREFIX = \"jadx-instance-\";\n\n\tprivate static final Path TEMP_ROOT_DIR = createTempRootDir();\n\n\tpublic static Path getTempRootDir() {\n\t\treturn TEMP_ROOT_DIR;\n\t}\n\n\tprivate static Path createTempRootDir() {\n\t\ttry {\n\t\t\tString jadxTmpDir = System.getenv(\"JADX_TMP_DIR\");\n\t\t\tPath dir;\n\t\t\tif (jadxTmpDir != null) {\n\t\t\t\tPath customTmpRootDir = Paths.get(jadxTmpDir);\n\t\t\t\tFiles.createDirectories(customTmpRootDir);\n\t\t\t\tdir = Files.createTempDirectory(customTmpRootDir, JADX_TMP_INSTANCE_PREFIX);\n\t\t\t} else {\n\t\t\t\tdir = Files.createTempDirectory(JADX_TMP_INSTANCE_PREFIX);\n\t\t\t}\n\t\t\tdir.toFile().deleteOnExit();\n\t\t\treturn dir;\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to create temp root directory\", e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/README.md",
    "content": "## jadx zip\n\nCustom zip reader implementation to fight tampering and provide additional security checks\n"
  },
  {
    "path": "jadx-commons/jadx-zip/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-library\")\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/src/main/java/jadx/zip/IZipEntry.java",
    "content": "package jadx.zip;\n\nimport java.io.File;\nimport java.io.InputStream;\n\npublic interface IZipEntry {\n\n\t/**\n\t * Zip entry name\n\t */\n\tString getName();\n\n\t/**\n\t * Uncompressed bytes\n\t */\n\tbyte[] getBytes();\n\n\t/**\n\t * Stream of uncompressed bytes.\n\t */\n\tInputStream getInputStream();\n\n\tlong getCompressedSize();\n\n\tlong getUncompressedSize();\n\n\tboolean isDirectory();\n\n\tFile getZipFile();\n\n\t/**\n\t * Return true if {@link #getBytes()} method is more optimal to use other than\n\t * {@link #getInputStream()}\n\t */\n\tboolean preferBytes();\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/src/main/java/jadx/zip/IZipParser.java",
    "content": "package jadx.zip;\n\nimport java.io.Closeable;\nimport java.io.IOException;\n\npublic interface IZipParser extends Closeable {\n\n\tZipContent open() throws IOException;\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/src/main/java/jadx/zip/ZipContent.java",
    "content": "package jadx.zip;\n\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class ZipContent implements Closeable {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ZipContent.class);\n\n\tprivate final IZipParser zipParser;\n\tprivate final List<IZipEntry> entries;\n\tprivate final Map<String, IZipEntry> entriesMap;\n\n\tpublic ZipContent(IZipParser zipParser, List<IZipEntry> entries) {\n\t\tthis.zipParser = zipParser;\n\t\tthis.entries = entries;\n\t\tthis.entriesMap = buildNameMap(zipParser, entries);\n\t}\n\n\tprivate static Map<String, IZipEntry> buildNameMap(IZipParser zipParser, List<IZipEntry> entries) {\n\t\tMap<String, IZipEntry> map = new HashMap<>(entries.size());\n\t\tfor (IZipEntry entry : entries) {\n\t\t\tString name = entry.getName();\n\t\t\tIZipEntry prevEntry = map.put(name, entry);\n\t\t\tif (prevEntry != null) {\n\t\t\t\tLOG.warn(\"Found duplicate entry: {} in {}\", name, zipParser);\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t}\n\n\tpublic List<IZipEntry> getEntries() {\n\t\treturn entries;\n\t}\n\n\tpublic @Nullable IZipEntry searchEntry(String fileName) {\n\t\treturn entriesMap.get(fileName);\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\tzipParser.close();\n\t}\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/src/main/java/jadx/zip/ZipReader.java",
    "content": "package jadx.zip;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Set;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.zip.fallback.FallbackZipParser;\nimport jadx.zip.parser.JadxZipParser;\nimport jadx.zip.security.IJadxZipSecurity;\nimport jadx.zip.security.JadxZipSecurity;\n\n/**\n * Jadx wrapper to provide custom zip parser ({@link JadxZipParser})\n * with fallback to default Java implementation.\n */\npublic class ZipReader {\n\tprivate final ZipReaderOptions options;\n\n\tpublic ZipReader() {\n\t\tthis(ZipReaderOptions.getDefault());\n\t}\n\n\tpublic ZipReader(Set<ZipReaderFlags> flags) {\n\t\tthis(new ZipReaderOptions(new JadxZipSecurity(), flags));\n\t}\n\n\tpublic ZipReader(IJadxZipSecurity security) {\n\t\tthis(new ZipReaderOptions(security, ZipReaderFlags.none()));\n\t}\n\n\tpublic ZipReader(ZipReaderOptions options) {\n\t\tthis.options = options;\n\t}\n\n\t@SuppressWarnings(\"resource\")\n\tpublic ZipContent open(File zipFile) throws IOException {\n\t\ttry {\n\t\t\tJadxZipParser jadxParser = new JadxZipParser(zipFile, options);\n\t\t\tIZipParser detectedParser = detectParser(zipFile, jadxParser);\n\t\t\tif (detectedParser != jadxParser) {\n\t\t\t\tjadxParser.close();\n\t\t\t}\n\t\t\treturn detectedParser.open();\n\t\t} catch (Exception e) {\n\t\t\tif (options.getFlags().contains(ZipReaderFlags.DONT_USE_FALLBACK)) {\n\t\t\t\tthrow new IOException(\"Failed to open zip: \" + zipFile, e);\n\t\t\t}\n\t\t\t// switch to fallback parser\n\t\t\treturn buildFallbackParser(zipFile).open();\n\t\t}\n\t}\n\n\t/**\n\t * Visit valid entries in a zip file.\n\t * Return not null value from visitor to stop iteration.\n\t */\n\tpublic <R> @Nullable R visitEntries(File file, Function<IZipEntry, R> visitor) {\n\t\ttry (ZipContent content = open(file)) {\n\t\t\tfor (IZipEntry entry : content.getEntries()) {\n\t\t\t\tR result = visitor.apply(entry);\n\t\t\t\tif (result != null) {\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to process zip file: \" + file.getAbsolutePath(), e);\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic void readEntries(File file, BiConsumer<IZipEntry, InputStream> visitor) {\n\t\tvisitEntries(file, entry -> {\n\t\t\tif (!entry.isDirectory()) {\n\t\t\t\ttry (InputStream in = entry.getInputStream()) {\n\t\t\t\t\tvisitor.accept(entry, in);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tthrow new RuntimeException(\"Failed to process zip entry: \" + entry, e);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t}\n\n\tpublic ZipReaderOptions getOptions() {\n\t\treturn options;\n\t}\n\n\tprivate IZipParser detectParser(File zipFile, JadxZipParser jadxParser) {\n\t\tif (zipFile.getName().endsWith(\".apk\")\n\t\t\t\t|| options.getFlags().contains(ZipReaderFlags.DONT_USE_FALLBACK)) {\n\t\t\treturn jadxParser;\n\t\t}\n\t\tif (!jadxParser.canOpen()) {\n\t\t\treturn buildFallbackParser(zipFile);\n\t\t}\n\t\t// default\n\t\tif (options.getFlags().contains(ZipReaderFlags.FALLBACK_AS_DEFAULT)) {\n\t\t\treturn buildFallbackParser(zipFile);\n\t\t}\n\t\treturn jadxParser;\n\t}\n\n\tprivate FallbackZipParser buildFallbackParser(File zipFile) {\n\t\treturn new FallbackZipParser(zipFile, options);\n\t}\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/src/main/java/jadx/zip/ZipReaderFlags.java",
    "content": "package jadx.zip;\n\nimport java.util.EnumSet;\nimport java.util.Set;\n\npublic enum ZipReaderFlags {\n\t/**\n\t * Search all local file headers by signature without reading\n\t * 'central directory' and 'end of central directory' entries\n\t */\n\tIGNORE_CENTRAL_DIR_ENTRIES,\n\n\t/**\n\t * Enable additional checks to verify zip data and report possible tampering\n\t */\n\tREPORT_TAMPERING,\n\n\t/**\n\t * Use fallback (java built-in implementation) parser as default.\n\t * Custom implementation will be used for '*.apk' files only.\n\t */\n\tFALLBACK_AS_DEFAULT,\n\n\t/**\n\t * Use only jadx custom parser and do not switch to fallback on errors.\n\t */\n\tDONT_USE_FALLBACK;\n\n\tpublic static Set<ZipReaderFlags> none() {\n\t\treturn EnumSet.noneOf(ZipReaderFlags.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/src/main/java/jadx/zip/ZipReaderOptions.java",
    "content": "package jadx.zip;\n\nimport java.util.Set;\n\nimport jadx.zip.security.IJadxZipSecurity;\nimport jadx.zip.security.JadxZipSecurity;\n\npublic class ZipReaderOptions {\n\n\tpublic static ZipReaderOptions getDefault() {\n\t\treturn new ZipReaderOptions(new JadxZipSecurity(), ZipReaderFlags.none());\n\t}\n\n\tprivate final IJadxZipSecurity zipSecurity;\n\tprivate final Set<ZipReaderFlags> flags;\n\n\tpublic ZipReaderOptions(IJadxZipSecurity zipSecurity, Set<ZipReaderFlags> flags) {\n\t\tthis.zipSecurity = zipSecurity;\n\t\tthis.flags = flags;\n\t}\n\n\tpublic IJadxZipSecurity getZipSecurity() {\n\t\treturn zipSecurity;\n\t}\n\n\tpublic Set<ZipReaderFlags> getFlags() {\n\t\treturn flags;\n\t}\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/src/main/java/jadx/zip/fallback/FallbackZipEntry.java",
    "content": "package jadx.zip.fallback;\n\nimport java.io.File;\nimport java.io.InputStream;\nimport java.util.zip.ZipEntry;\n\nimport jadx.zip.IZipEntry;\n\npublic class FallbackZipEntry implements IZipEntry {\n\tprivate final FallbackZipParser parser;\n\tprivate final ZipEntry zipEntry;\n\n\tpublic FallbackZipEntry(FallbackZipParser parser, ZipEntry zipEntry) {\n\t\tthis.parser = parser;\n\t\tthis.zipEntry = zipEntry;\n\t}\n\n\tpublic ZipEntry getZipEntry() {\n\t\treturn zipEntry;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn zipEntry.getName();\n\t}\n\n\t@Override\n\tpublic boolean preferBytes() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic byte[] getBytes() {\n\t\treturn parser.getBytes(this);\n\t}\n\n\t@Override\n\tpublic InputStream getInputStream() {\n\t\treturn parser.getInputStream(this);\n\t}\n\n\t@Override\n\tpublic long getCompressedSize() {\n\t\treturn zipEntry.getCompressedSize();\n\t}\n\n\t@Override\n\tpublic long getUncompressedSize() {\n\t\treturn zipEntry.getSize();\n\t}\n\n\t@Override\n\tpublic boolean isDirectory() {\n\t\treturn zipEntry.isDirectory();\n\t}\n\n\t@Override\n\tpublic File getZipFile() {\n\t\treturn parser.getZipFile();\n\t}\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/src/main/java/jadx/zip/fallback/FallbackZipParser.java",
    "content": "package jadx.zip.fallback;\n\nimport java.io.BufferedInputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.zip.IZipEntry;\nimport jadx.zip.IZipParser;\nimport jadx.zip.ZipContent;\nimport jadx.zip.ZipReaderOptions;\nimport jadx.zip.io.LimitedInputStream;\nimport jadx.zip.security.IJadxZipSecurity;\n\npublic class FallbackZipParser implements IZipParser {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(FallbackZipParser.class);\n\tprivate final File file;\n\tprivate final IJadxZipSecurity zipSecurity;\n\tprivate final boolean useLimitedDataStream;\n\n\tprivate ZipFile zipFile;\n\n\tpublic FallbackZipParser(File file, ZipReaderOptions options) {\n\t\tthis.file = file;\n\t\tthis.zipSecurity = options.getZipSecurity();\n\t\tthis.useLimitedDataStream = zipSecurity.useLimitedDataStream();\n\t}\n\n\t@Override\n\tpublic ZipContent open() throws IOException {\n\t\tzipFile = new ZipFile(file);\n\n\t\tint maxEntriesCount = zipSecurity.getMaxEntriesCount();\n\t\tif (maxEntriesCount == -1) {\n\t\t\tmaxEntriesCount = Integer.MAX_VALUE;\n\t\t}\n\n\t\tList<IZipEntry> list = new ArrayList<>();\n\t\tEnumeration<? extends ZipEntry> entries = zipFile.entries();\n\t\twhile (entries.hasMoreElements()) {\n\t\t\tFallbackZipEntry zipEntry = new FallbackZipEntry(this, entries.nextElement());\n\t\t\tif (isValidEntry(zipEntry)) {\n\t\t\t\tlist.add(zipEntry);\n\t\t\t\tif (list.size() > maxEntriesCount) {\n\t\t\t\t\tthrow new IllegalStateException(\"Max entries count limit exceeded: \" + list.size());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn new ZipContent(this, list);\n\t}\n\n\tprivate boolean isValidEntry(IZipEntry zipEntry) {\n\t\tboolean validEntry = zipSecurity.isValidEntry(zipEntry);\n\t\tif (!validEntry) {\n\t\t\tLOG.warn(\"Zip entry '{}' is invalid and excluded from processing\", zipEntry);\n\t\t}\n\t\treturn validEntry;\n\t}\n\n\tpublic byte[] getBytes(FallbackZipEntry entry) {\n\t\ttry (InputStream is = getEntryStream(entry)) {\n\t\t\treturn is.readAllBytes();\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to read bytes for entry: \" + entry.getName(), e);\n\t\t}\n\t}\n\n\tpublic InputStream getInputStream(FallbackZipEntry entry) {\n\t\ttry {\n\t\t\treturn getEntryStream(entry);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to open input stream for entry: \" + entry.getName(), e);\n\t\t}\n\t}\n\n\tprivate InputStream getEntryStream(FallbackZipEntry entry) throws IOException {\n\t\tInputStream entryStream = zipFile.getInputStream(entry.getZipEntry());\n\t\tInputStream stream;\n\t\tif (useLimitedDataStream) {\n\t\t\tstream = new LimitedInputStream(entryStream, entry.getUncompressedSize());\n\t\t} else {\n\t\t\tstream = entryStream;\n\t\t}\n\t\treturn new BufferedInputStream(stream);\n\t}\n\n\tpublic File getZipFile() {\n\t\treturn file;\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\ttry {\n\t\t\tif (zipFile != null) {\n\t\t\t\tzipFile.close();\n\t\t\t}\n\t\t} finally {\n\t\t\tzipFile = null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/src/main/java/jadx/zip/io/ByteBufferBackedInputStream.java",
    "content": "package jadx.zip.io;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.ByteBuffer;\n\npublic class ByteBufferBackedInputStream extends InputStream {\n\tprivate final ByteBuffer buf;\n\tprivate int markedPosition = 0;\n\n\tpublic ByteBufferBackedInputStream(ByteBuffer buf) {\n\t\tthis.buf = buf;\n\t}\n\n\tpublic int read() throws IOException {\n\t\tif (!buf.hasRemaining()) {\n\t\t\treturn -1;\n\t\t}\n\t\treturn buf.get() & 0xFF;\n\t}\n\n\t@SuppressWarnings(\"NullableProblems\")\n\tpublic int read(byte[] bytes, int off, int len) throws IOException {\n\t\tif (!buf.hasRemaining()) {\n\t\t\treturn -1;\n\t\t}\n\t\tint readLen = Math.min(len, buf.remaining());\n\t\tbuf.get(bytes, off, readLen);\n\t\treturn readLen;\n\t}\n\n\t@Override\n\tpublic boolean markSupported() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic synchronized void mark(int unused) {\n\t\tmarkedPosition = buf.position();\n\t}\n\n\t@Override\n\tpublic synchronized void reset() {\n\t\tbuf.position(markedPosition);\n\t}\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/src/main/java/jadx/zip/io/LimitedInputStream.java",
    "content": "package jadx.zip.io;\n\nimport java.io.FilterInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic class LimitedInputStream extends FilterInputStream {\n\tprivate final long maxSize;\n\n\tprivate long currentPos;\n\tprivate long markPos;\n\n\tpublic LimitedInputStream(InputStream in, long maxSize) {\n\t\tsuper(in);\n\t\tthis.maxSize = maxSize;\n\t}\n\n\tprivate void addAndCheckPos(long count) {\n\t\tcurrentPos += count;\n\t\tif (currentPos > maxSize) {\n\t\t\tthrow new IllegalStateException(\"Read limit exceeded\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic int read() throws IOException {\n\t\tint data = super.read();\n\t\tif (data != -1) {\n\t\t\taddAndCheckPos(1);\n\t\t}\n\t\treturn data;\n\t}\n\n\t@SuppressWarnings(\"NullableProblems\")\n\t@Override\n\tpublic int read(byte[] b, int off, int len) throws IOException {\n\t\tint count = super.read(b, off, len);\n\t\tif (count > 0) {\n\t\t\taddAndCheckPos(count);\n\t\t}\n\t\treturn count;\n\t}\n\n\t@Override\n\tpublic long skip(long n) throws IOException {\n\t\tlong skipped = super.skip(n);\n\t\tif (skipped > 0) {\n\t\t\taddAndCheckPos(skipped);\n\t\t}\n\t\treturn skipped;\n\t}\n\n\t@Override\n\tpublic void mark(int readLimit) {\n\t\tsuper.mark(readLimit);\n\t\tmarkPos = currentPos;\n\t}\n\n\t@Override\n\tpublic void reset() throws IOException {\n\t\tsuper.reset();\n\t\tcurrentPos = markPos;\n\t}\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/src/main/java/jadx/zip/parser/JadxZipEntry.java",
    "content": "package jadx.zip.parser;\n\nimport java.io.File;\nimport java.io.InputStream;\n\nimport jadx.zip.IZipEntry;\n\npublic final class JadxZipEntry implements IZipEntry {\n\tprivate final JadxZipParser parser;\n\tprivate final String fileName;\n\tprivate final int compressMethod;\n\tprivate final int entryStart;\n\tprivate final int dataStart;\n\tprivate final long compressedSize;\n\tprivate final long uncompressedSize;\n\n\tJadxZipEntry(JadxZipParser parser, String fileName, int entryStart, int dataStart,\n\t\t\tint compressMethod, long compressedSize, long uncompressedSize) {\n\t\tthis.parser = parser;\n\t\tthis.fileName = fileName;\n\t\tthis.entryStart = entryStart;\n\t\tthis.dataStart = dataStart;\n\t\tthis.compressMethod = compressMethod;\n\t\tthis.compressedSize = compressedSize;\n\t\tthis.uncompressedSize = uncompressedSize;\n\t}\n\n\tpublic boolean isSizesValid() {\n\t\tif (compressedSize <= 0) {\n\t\t\treturn false;\n\t\t}\n\t\tif (uncompressedSize <= 0) {\n\t\t\treturn false;\n\t\t}\n\t\treturn compressedSize <= uncompressedSize;\n\t}\n\n\tpublic String getName() {\n\t\treturn fileName;\n\t}\n\n\t@Override\n\tpublic long getCompressedSize() {\n\t\treturn compressedSize;\n\t}\n\n\t@Override\n\tpublic long getUncompressedSize() {\n\t\treturn uncompressedSize;\n\t}\n\n\t@Override\n\tpublic boolean isDirectory() {\n\t\treturn fileName.endsWith(\"/\");\n\t}\n\n\t@Override\n\tpublic boolean preferBytes() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic byte[] getBytes() {\n\t\treturn parser.getBytes(this);\n\t}\n\n\t@Override\n\tpublic InputStream getInputStream() {\n\t\treturn parser.getInputStream(this);\n\t}\n\n\tpublic int getEntryStart() {\n\t\treturn entryStart;\n\t}\n\n\tpublic int getDataStart() {\n\t\treturn dataStart;\n\t}\n\n\tpublic int getCompressMethod() {\n\t\treturn compressMethod;\n\t}\n\n\t@Override\n\tpublic File getZipFile() {\n\t\treturn parser.getZipFile();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn parser.getZipFile().getName() + ':' + fileName;\n\t}\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/src/main/java/jadx/zip/parser/JadxZipParser.java",
    "content": "package jadx.zip.parser;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.RandomAccessFile;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.channels.FileChannel;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.zip.IZipEntry;\nimport jadx.zip.IZipParser;\nimport jadx.zip.ZipContent;\nimport jadx.zip.ZipReaderFlags;\nimport jadx.zip.ZipReaderOptions;\nimport jadx.zip.fallback.FallbackZipParser;\nimport jadx.zip.io.ByteBufferBackedInputStream;\nimport jadx.zip.io.LimitedInputStream;\nimport jadx.zip.security.IJadxZipSecurity;\n\n/**\n * Custom and simple zip parser to fight tampering.\n * Many zip features aren't supported:\n * - Compression methods other than STORE or DEFLATE\n * - Zip64\n * - Checksum verification\n * - Multi file archives\n */\npublic final class JadxZipParser implements IZipParser {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxZipParser.class);\n\n\tprivate static final byte LOCAL_FILE_HEADER_START = 0x50;\n\tprivate static final int LOCAL_FILE_HEADER_SIGN = 0x04034b50;\n\tprivate static final int CD_SIGN = 0x02014b50;\n\tprivate static final int END_OF_CD_SIGN = 0x06054b50;\n\n\tprivate final File zipFile;\n\tprivate final ZipReaderOptions options;\n\tprivate final IJadxZipSecurity zipSecurity;\n\tprivate final Set<ZipReaderFlags> flags;\n\tprivate final boolean verify;\n\tprivate final boolean useLimitedDataStream;\n\n\tprivate RandomAccessFile file;\n\tprivate FileChannel fileChannel;\n\tprivate ByteBuffer byteBuffer;\n\n\tprivate int endOfCDStart = -2;\n\n\tprivate @Nullable ZipContent fallbackZipContent;\n\n\tpublic JadxZipParser(File zipFile, ZipReaderOptions options) {\n\t\tthis.zipFile = zipFile;\n\t\tthis.options = options;\n\t\tthis.zipSecurity = options.getZipSecurity();\n\t\tthis.flags = options.getFlags();\n\t\tthis.verify = options.getFlags().contains(ZipReaderFlags.REPORT_TAMPERING);\n\t\tthis.useLimitedDataStream = zipSecurity.useLimitedDataStream();\n\t}\n\n\t@Override\n\tpublic ZipContent open() throws IOException {\n\t\tload();\n\t\ttry {\n\t\t\tint maxEntriesCount = zipSecurity.getMaxEntriesCount();\n\t\t\tif (maxEntriesCount == -1) {\n\t\t\t\tmaxEntriesCount = Integer.MAX_VALUE;\n\t\t\t}\n\t\t\tList<IZipEntry> entries;\n\t\t\tif (flags.contains(ZipReaderFlags.IGNORE_CENTRAL_DIR_ENTRIES)) {\n\t\t\t\tentries = searchLocalFileHeaders(maxEntriesCount);\n\t\t\t} else {\n\t\t\t\tentries = loadFromCentralDirs(maxEntriesCount);\n\t\t\t}\n\t\t\treturn new ZipContent(this, entries);\n\t\t} catch (Exception e) {\n\t\t\tif (flags.contains(ZipReaderFlags.DONT_USE_FALLBACK)) {\n\t\t\t\tthrow new IOException(\"Failed to open zip: \" + zipFile + \", error: \" + e.getMessage(), e);\n\t\t\t}\n\t\t\tLOG.warn(\"Zip open failed, switching to fallback parser, zip: {}\", zipFile, e);\n\t\t\treturn initFallbackParser();\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"RedundantIfStatement\")\n\tpublic boolean canOpen() {\n\t\ttry {\n\t\t\tload();\n\t\t\tint eocdStart = searchEndOfCDStart();\n\t\t\tByteBuffer buf = byteBuffer;\n\t\t\tbuf.position(eocdStart + 4);\n\t\t\tint diskNum = readU2(buf);\n\t\t\tif (diskNum == 0xFFFF) {\n\t\t\t\t// Zip64\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Jadx parser can't open zip file: {}\", zipFile, e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate boolean isValidEntry(JadxZipEntry zipEntry) {\n\t\tboolean validEntry = zipSecurity.isValidEntry(zipEntry);\n\t\tif (!validEntry) {\n\t\t\tLOG.warn(\"Zip entry '{}' is invalid and excluded from processing\", zipEntry);\n\t\t}\n\t\treturn validEntry;\n\t}\n\n\tprivate void load() throws IOException {\n\t\tif (byteBuffer != null) {\n\t\t\t// already loaded\n\t\t\treturn;\n\t\t}\n\t\tfile = new RandomAccessFile(zipFile, \"r\");\n\t\tlong size = file.length();\n\t\tif (size >= Integer.MAX_VALUE) {\n\t\t\tthrow new IOException(\"Zip file is too big\");\n\t\t}\n\t\tint fileLen = (int) size;\n\t\tif (fileLen < 100 * 1024 * 1024) {\n\t\t\t// load files smaller than 100MB directly into memory\n\t\t\tbyte[] bytes = new byte[fileLen];\n\t\t\tfile.readFully(bytes);\n\t\t\tbyteBuffer = ByteBuffer.wrap(bytes).asReadOnlyBuffer();\n\t\t\tfile.close();\n\t\t\tfile = null;\n\t\t} else {\n\t\t\t// for big files - use a memory mapped file\n\t\t\tfileChannel = file.getChannel();\n\t\t\tbyteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());\n\t\t}\n\t\tbyteBuffer.order(ByteOrder.LITTLE_ENDIAN);\n\t}\n\n\tprivate List<IZipEntry> searchLocalFileHeaders(int maxEntriesCount) {\n\t\tList<IZipEntry> entries = new ArrayList<>();\n\t\twhile (true) {\n\t\t\tint start = searchEntryStart();\n\t\t\tif (start == -1) {\n\t\t\t\treturn entries;\n\t\t\t}\n\t\t\tJadxZipEntry zipEntry = loadFileEntry(start);\n\t\t\tif (isValidEntry(zipEntry)) {\n\t\t\t\tentries.add(zipEntry);\n\t\t\t\tif (entries.size() > maxEntriesCount) {\n\t\t\t\t\tthrow new IllegalStateException(\"Max entries count limit exceeded: \" + entries.size());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate List<IZipEntry> loadFromCentralDirs(int maxEntriesCount) throws IOException {\n\t\tint eocdStart = searchEndOfCDStart();\n\t\tif (eocdStart < 0) {\n\t\t\tthrow new RuntimeException(\"End of central directory not found\");\n\t\t}\n\t\tByteBuffer buf = byteBuffer;\n\t\tbuf.position(eocdStart + 10);\n\t\tint entriesCount = readU2(buf);\n\t\tbuf.position(eocdStart + 16);\n\t\tint cdOffset = buf.getInt();\n\n\t\tif (entriesCount > maxEntriesCount) {\n\t\t\tthrow new IllegalStateException(\"Max entries count limit exceeded: \" + entriesCount);\n\t\t}\n\t\tList<IZipEntry> entries = new ArrayList<>(entriesCount);\n\t\tbuf.position(cdOffset);\n\t\tfor (int i = 0; i < entriesCount; i++) {\n\t\t\tJadxZipEntry zipEntry = loadCDEntry();\n\t\t\tif (isValidEntry(zipEntry)) {\n\t\t\t\tentries.add(zipEntry);\n\t\t\t}\n\t\t}\n\t\treturn entries;\n\t}\n\n\tprivate JadxZipEntry loadCDEntry() {\n\t\tByteBuffer buf = byteBuffer;\n\t\tint start = buf.position();\n\t\tbuf.position(start + 28);\n\t\tint fileNameLen = readU2(buf);\n\t\tint extraFieldLen = readU2(buf);\n\t\tint commentLen = readU2(buf);\n\t\tbuf.position(start + 42);\n\t\tint fileEntryStart = buf.getInt();\n\t\tint entryEnd = start + 46 + fileNameLen + extraFieldLen + commentLen;\n\t\tJadxZipEntry entry = loadFileEntry(fileEntryStart);\n\t\tif (verify) {\n\t\t\tcompareCDAndLFH(buf, start, entry);\n\t\t}\n\t\tif (!entry.isSizesValid()) {\n\t\t\tentry = fixEntryFromCD(entry, start);\n\t\t}\n\t\tbuf.position(entryEnd);\n\t\treturn entry;\n\t}\n\n\tprivate JadxZipEntry fixEntryFromCD(JadxZipEntry entry, int start) {\n\t\tByteBuffer buf = byteBuffer;\n\t\tbuf.position(start + 10);\n\t\tint comprMethod = readU2(buf);\n\t\tbuf.position(start + 20);\n\t\tint comprSize = buf.getInt();\n\t\tint unComprSize = buf.getInt();\n\t\treturn new JadxZipEntry(this, entry.getName(), start, entry.getDataStart(), comprMethod, comprSize, unComprSize);\n\t}\n\n\tprivate static void compareCDAndLFH(ByteBuffer buf, int start, JadxZipEntry entry) {\n\t\tbuf.position(start + 10);\n\t\tint comprMethod = readU2(buf);\n\t\tif (comprMethod != entry.getCompressMethod()) {\n\t\t\tLOG.warn(\"Compression method differ in CD {} and LFH {} for {}\",\n\t\t\t\t\tcomprMethod, entry.getCompressMethod(), entry);\n\t\t}\n\t\tbuf.position(start + 20);\n\t\tint comprSize = buf.getInt();\n\t\tint unComprSize = buf.getInt();\n\t\tif (comprSize != entry.getCompressedSize()) {\n\t\t\tLOG.warn(\"Compressed size differ in CD {} and LFH {} for {}\",\n\t\t\t\t\tcomprSize, entry.getCompressedSize(), entry);\n\t\t}\n\t\tif (unComprSize != entry.getUncompressedSize()) {\n\t\t\tLOG.warn(\"Uncompressed size differ in CD {} and LFH {} for {}\",\n\t\t\t\t\tunComprSize, entry.getUncompressedSize(), entry);\n\t\t}\n\t}\n\n\tprivate JadxZipEntry loadFileEntry(int start) {\n\t\tByteBuffer buf = byteBuffer;\n\t\tbuf.position(start + 8);\n\t\tint comprMethod = readU2(buf);\n\t\tbuf.position(start + 18);\n\t\tint comprSize = buf.getInt();\n\t\tint unComprSize = buf.getInt();\n\t\tint fileNameLen = readU2(buf);\n\t\tint extraFieldLen = readU2(buf);\n\t\tString fileName = readString(buf, fileNameLen);\n\t\tint dataStart = start + 30 + fileNameLen + extraFieldLen;\n\t\tbuf.position(dataStart + comprSize);\n\t\treturn new JadxZipEntry(this, fileName, start, dataStart, comprMethod, comprSize, unComprSize);\n\t}\n\n\tprivate int searchEndOfCDStart() throws IOException {\n\t\tif (endOfCDStart != -2) {\n\t\t\treturn endOfCDStart;\n\t\t}\n\t\tByteBuffer buf = byteBuffer;\n\t\tint pos = buf.limit() - 22;\n\t\tint minPos = Math.max(0, pos - 0xffff);\n\t\twhile (true) {\n\t\t\tbuf.position(pos);\n\t\t\tint sign = buf.getInt();\n\t\t\tif (sign == END_OF_CD_SIGN) {\n\t\t\t\tendOfCDStart = pos;\n\t\t\t\treturn pos;\n\t\t\t}\n\t\t\tpos--;\n\t\t\tif (pos < minPos) {\n\t\t\t\tthrow new IOException(\"End of central directory record not found\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate int searchEntryStart() {\n\t\tByteBuffer buf = byteBuffer;\n\t\twhile (true) {\n\t\t\tint start = buf.position();\n\t\t\tif (start + 4 > buf.limit()) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tbyte b = buf.get();\n\t\t\tif (b == LOCAL_FILE_HEADER_START) {\n\t\t\t\tbuf.position(start);\n\t\t\t\tint sign = buf.getInt();\n\t\t\t\tif (sign == LOCAL_FILE_HEADER_SIGN) {\n\t\t\t\t\treturn start;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tsynchronized InputStream getInputStream(JadxZipEntry entry) {\n\t\tif (verify) {\n\t\t\tverifyEntry(entry);\n\t\t}\n\t\tInputStream stream;\n\t\tif (entry.getCompressMethod() == 8) {\n\t\t\ttry {\n\t\t\t\tstream = ZipDeflate.decompressEntryToStream(byteBuffer, entry);\n\t\t\t} catch (Exception e) {\n\t\t\t\tentryParseFailed(entry, e);\n\t\t\t\treturn useFallbackParser(entry).getInputStream();\n\t\t\t}\n\t\t} else {\n\t\t\t// treat any other compression methods values as UNCOMPRESSED\n\t\t\tstream = bufferToStream(byteBuffer, entry.getDataStart(), (int) entry.getUncompressedSize());\n\t\t}\n\t\tif (useLimitedDataStream) {\n\t\t\treturn new LimitedInputStream(stream, entry.getUncompressedSize());\n\t\t}\n\t\treturn stream;\n\t}\n\n\tsynchronized byte[] getBytes(JadxZipEntry entry) {\n\t\tif (verify) {\n\t\t\tverifyEntry(entry);\n\t\t}\n\t\tif (entry.getCompressMethod() == 8) {\n\t\t\ttry {\n\t\t\t\treturn ZipDeflate.decompressEntryToBytes(byteBuffer, entry);\n\t\t\t} catch (Exception e) {\n\t\t\t\tentryParseFailed(entry, e);\n\t\t\t\treturn useFallbackParser(entry).getBytes();\n\t\t\t}\n\t\t}\n\t\t// treat any other compression methods values as UNCOMPRESSED\n\t\treturn bufferToBytes(byteBuffer, entry.getDataStart(), (int) entry.getUncompressedSize());\n\t}\n\n\tprivate static void verifyEntry(JadxZipEntry entry) {\n\t\tint compressMethod = entry.getCompressMethod();\n\t\tif (compressMethod == 0) {\n\t\t\tif (entry.getCompressedSize() != entry.getUncompressedSize()) {\n\t\t\t\tLOG.warn(\"Not equal sizes for STORE method: compressed: {}, uncompressed: {}, entry: {}\",\n\t\t\t\t\t\tentry.getCompressedSize(), entry.getUncompressedSize(), entry);\n\t\t\t}\n\t\t} else if (compressMethod != 8) {\n\t\t\tLOG.warn(\"Unknown compress method: {} in entry: {}\", compressMethod, entry);\n\t\t}\n\t}\n\n\tprivate void entryParseFailed(JadxZipEntry entry, Exception e) {\n\t\tif (isEncrypted(entry)) {\n\t\t\tthrow new RuntimeException(\"Entry is encrypted, failed to decompress: \" + entry, e);\n\t\t}\n\t\tif (flags.contains(ZipReaderFlags.DONT_USE_FALLBACK)) {\n\t\t\tthrow new RuntimeException(\"Failed to decompress zip entry: \" + entry + \", error: \" + e.getMessage(), e);\n\t\t}\n\t\tLOG.warn(\"Entry '{}' parse failed, switching to fallback parser\", entry, e);\n\t}\n\n\t@SuppressWarnings(\"resource\")\n\tprivate IZipEntry useFallbackParser(JadxZipEntry entry) {\n\t\tLOG.debug(\"useFallbackParser used for {}\", entry);\n\t\tIZipEntry zipEntry = initFallbackParser().searchEntry(entry.getName());\n\t\tif (zipEntry == null) {\n\t\t\tthrow new RuntimeException(\"Fallback parser can't find entry: \" + entry);\n\t\t}\n\t\treturn zipEntry;\n\t}\n\n\t@SuppressWarnings(\"resource\")\n\tprivate ZipContent initFallbackParser() {\n\t\tif (fallbackZipContent == null) {\n\t\t\ttry {\n\t\t\t\tfallbackZipContent = new FallbackZipParser(zipFile, options).open();\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new RuntimeException(\"Fallback parser failed to open file: \" + zipFile, e);\n\t\t\t}\n\t\t}\n\t\treturn fallbackZipContent;\n\t}\n\n\tprivate boolean isEncrypted(JadxZipEntry entry) {\n\t\tint flags = readFlags(entry);\n\t\treturn (flags & 1) != 0;\n\t}\n\n\tprivate int readFlags(JadxZipEntry entry) {\n\t\tByteBuffer buf = byteBuffer;\n\t\tbuf.position(entry.getEntryStart() + 6);\n\t\treturn readU2(buf);\n\t}\n\n\tstatic byte[] bufferToBytes(ByteBuffer buf, int start, int size) {\n\t\tbyte[] data = new byte[size];\n\t\tbuf.position(start);\n\t\tbuf.get(data);\n\t\treturn data;\n\t}\n\n\tstatic InputStream bufferToStream(ByteBuffer buf, int start, int size) {\n\t\tbuf.position(start);\n\t\tByteBuffer streamBuf = buf.slice();\n\t\tstreamBuf.limit(size);\n\t\treturn new ByteBufferBackedInputStream(streamBuf);\n\t}\n\n\tprivate static int readU2(ByteBuffer buf) {\n\t\treturn buf.getShort() & 0xFFFF;\n\t}\n\n\tprivate static String readString(ByteBuffer buf, int fileNameLen) {\n\t\tbyte[] bytes = new byte[fileNameLen];\n\t\tbuf.get(bytes);\n\t\treturn new String(bytes, StandardCharsets.UTF_8);\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\ttry {\n\t\t\tif (fileChannel != null) {\n\t\t\t\tfileChannel.close();\n\t\t\t}\n\t\t\tif (file != null) {\n\t\t\t\tfile.close();\n\t\t\t}\n\t\t\tif (fallbackZipContent != null) {\n\t\t\t\tfallbackZipContent.close();\n\t\t\t}\n\t\t} finally {\n\t\t\tfileChannel = null;\n\t\t\tfile = null;\n\t\t\tbyteBuffer = null;\n\t\t\tendOfCDStart = -2;\n\t\t\tfallbackZipContent = null;\n\t\t}\n\t}\n\n\tpublic File getZipFile() {\n\t\treturn zipFile;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"JadxZipParser{\" + zipFile + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/src/main/java/jadx/zip/parser/ZipDeflate.java",
    "content": "package jadx.zip.parser;\n\nimport java.io.InputStream;\nimport java.nio.ByteBuffer;\nimport java.util.zip.DataFormatException;\nimport java.util.zip.Inflater;\nimport java.util.zip.InflaterInputStream;\n\nimport static jadx.zip.parser.JadxZipParser.bufferToStream;\n\nfinal class ZipDeflate {\n\tprivate static final int BUFFER_SIZE = 4096;\n\n\tstatic byte[] decompressEntryToBytes(ByteBuffer buf, JadxZipEntry entry) throws DataFormatException {\n\t\tbuf.position(entry.getDataStart());\n\t\tByteBuffer entryBuf = buf.slice();\n\t\tentryBuf.limit((int) entry.getCompressedSize());\n\t\tif (entry.getUncompressedSize() > Integer.MAX_VALUE) {\n\t\t\tthrow new DataFormatException(\"Entry too large: \" + entry.getUncompressedSize());\n\t\t}\n\t\tbyte[] out = new byte[(int) entry.getUncompressedSize()];\n\t\tInflater inflater = new Inflater(true);\n\t\tinflater.setInput(entryBuf);\n\t\tint written = inflater.inflate(out);\n\t\tinflater.end();\n\t\tif (written != out.length) {\n\t\t\tthrow new DataFormatException(\"Unexpected size of decompressed entry: \" + entry\n\t\t\t\t\t+ \", got: \" + written + \", expected: \" + out.length);\n\t\t}\n\t\treturn out;\n\t}\n\n\tstatic InputStream decompressEntryToStream(ByteBuffer buf, JadxZipEntry entry) {\n\t\tInputStream stream = bufferToStream(buf, entry.getDataStart(), (int) entry.getCompressedSize());\n\t\tInflater inflater = new Inflater(true);\n\t\treturn new InflaterInputStream(stream, inflater, BUFFER_SIZE);\n\t}\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/src/main/java/jadx/zip/security/DisabledZipSecurity.java",
    "content": "package jadx.zip.security;\n\nimport java.io.File;\n\nimport jadx.zip.IZipEntry;\n\npublic class DisabledZipSecurity implements IJadxZipSecurity {\n\n\tpublic static final DisabledZipSecurity INSTANCE = new DisabledZipSecurity();\n\n\t@Override\n\tpublic boolean isValidEntry(IZipEntry entry) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isValidEntryName(String entryName) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isInSubDirectory(File baseDir, File file) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean useLimitedDataStream() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int getMaxEntriesCount() {\n\t\treturn -1;\n\t}\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/src/main/java/jadx/zip/security/IJadxZipSecurity.java",
    "content": "package jadx.zip.security;\n\nimport java.io.File;\n\nimport jadx.zip.IZipEntry;\n\npublic interface IJadxZipSecurity {\n\n\t/**\n\t * Check if zip entry is valid and safe to process\n\t */\n\tboolean isValidEntry(IZipEntry entry);\n\n\t/**\n\t * Check if the zip entry name is valid.\n\t * This check should be part of {@link #isValidEntry(IZipEntry)} method.\n\t */\n\tboolean isValidEntryName(String entryName);\n\n\t/**\n\t * Use limited InputStream for entry uncompressed data\n\t */\n\tboolean useLimitedDataStream();\n\n\t/**\n\t * Max entries count expected in a zip file, fail zip open if the limit exceeds.\n\t * Return -1 to disable entries count check.\n\t */\n\tint getMaxEntriesCount();\n\n\t/**\n\t * Check if a file will be inside baseDir after a system resolves its path\n\t */\n\tboolean isInSubDirectory(File baseDir, File file);\n}\n"
  },
  {
    "path": "jadx-commons/jadx-zip/src/main/java/jadx/zip/security/JadxZipSecurity.java",
    "content": "package jadx.zip.security;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.zip.IZipEntry;\n\npublic class JadxZipSecurity implements IJadxZipSecurity {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxZipSecurity.class);\n\n\tprivate static final Path CWD = Paths.get(\".\").toAbsolutePath().normalize();\n\n\t/**\n\t * The size of uncompressed zip entry shouldn't be bigger of compressed in zipBombDetectionFactor\n\t * times\n\t */\n\tprivate int zipBombDetectionFactor = 100;\n\n\t/**\n\t * Zip entries that have an uncompressed size of less than zipBombMinUncompressedSize are considered\n\t * safe\n\t */\n\tprivate int zipBombMinUncompressedSize = 25 * 1024 * 1024;\n\n\tprivate int maxEntriesCount = 100_000;\n\n\tprivate boolean useLimitedDataStream = true;\n\n\t@Override\n\tpublic boolean isValidEntry(IZipEntry entry) {\n\t\treturn isValidEntryName(entry.getName()) && !isZipBomb(entry);\n\t}\n\n\t@Override\n\tpublic boolean useLimitedDataStream() {\n\t\treturn useLimitedDataStream;\n\t}\n\n\t@Override\n\tpublic int getMaxEntriesCount() {\n\t\treturn maxEntriesCount;\n\t}\n\n\t/**\n\t * Checks that entry name contains no any traversals and prevents cases like \"../classes.dex\",\n\t * to limit output only to the specified directory\n\t */\n\t@Override\n\tpublic boolean isValidEntryName(String entryName) {\n\t\tif (entryName.contains(\"..\")) { // quick pre-check\n\t\t\tif (entryName.contains(\"../\") || entryName.contains(\"..\\\\\")) {\n\t\t\t\tLOG.error(\"Path traversal attack detected in entry: '{}'\", entryName);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\t// Path traversal check as presented on\n\t\t// https://www.heise.de/en/background/Secure-Coding-Best-practices-for-using-Java-NIO-against-path-traversal-9996787.html\n\t\ttry {\n\t\t\tPath entryPath = CWD.resolve(entryName).normalize();\n\t\t\tif (entryPath.startsWith(CWD)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\t// check failed\n\t\t\tLOG.error(\"Invalid file name or path traversal attack detected: {} - error: {}\", entryName, e.getMessage());\n\t\t\treturn false;\n\t\t}\n\t\tLOG.error(\"Invalid file name or path traversal attack detected: {}\", entryName);\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isInSubDirectory(File baseDir, File file) {\n\t\ttry {\n\t\t\treturn isInSubDirectoryInternal(baseDir.getCanonicalFile(), file.getCanonicalFile());\n\t\t} catch (IOException e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic boolean isZipBomb(IZipEntry entry) {\n\t\tlong compressedSize = entry.getCompressedSize();\n\t\tlong uncompressedSize = entry.getUncompressedSize();\n\t\tboolean invalidSize = compressedSize < 0 || uncompressedSize < 0;\n\t\tboolean possibleZipBomb = uncompressedSize >= zipBombMinUncompressedSize\n\t\t\t\t&& compressedSize * zipBombDetectionFactor < uncompressedSize;\n\t\tif (invalidSize || possibleZipBomb) {\n\t\t\tLOG.error(\"Potential zip bomb attack detected, invalid sizes: compressed {}, uncompressed {}, name {}\",\n\t\t\t\t\tcompressedSize, uncompressedSize, entry.getName());\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean isInSubDirectoryInternal(File baseDir, File file) {\n\t\tFile current = file;\n\t\twhile (true) {\n\t\t\tif (current == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (current.equals(baseDir)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcurrent = current.getParentFile();\n\t\t}\n\t}\n\n\tpublic void setMaxEntriesCount(int maxEntriesCount) {\n\t\tthis.maxEntriesCount = maxEntriesCount;\n\t}\n\n\tpublic void setZipBombDetectionFactor(int zipBombDetectionFactor) {\n\t\tthis.zipBombDetectionFactor = zipBombDetectionFactor;\n\t}\n\n\tpublic void setZipBombMinUncompressedSize(int zipBombMinUncompressedSize) {\n\t\tthis.zipBombMinUncompressedSize = zipBombMinUncompressedSize;\n\t}\n\n\tpublic void setUseLimitedDataStream(boolean useLimitedDataStream) {\n\t\tthis.useLimitedDataStream = useLimitedDataStream;\n\t}\n\n}\n"
  },
  {
    "path": "jadx-core/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-library\")\n}\n\ndependencies {\n\tapi(project(\":jadx-plugins:jadx-input-api\"))\n\tapi(project(\":jadx-commons:jadx-zip\"))\n\n\timplementation(\"com.google.code.gson:gson:2.13.2\")\n\n\ttestImplementation(\"org.apache.commons:commons-lang3:3.20.0\")\n\n\ttestImplementation(project(\":jadx-plugins:jadx-dex-input\"))\n\t// 'ClassNotFound' error is raised if set as 'testRuntime'\n\t// for the plugins below when running the tests from vscode.\n\ttestImplementation(project(\":jadx-plugins:jadx-smali-input\"))\n\ttestImplementation(project(\":jadx-plugins:jadx-java-convert\"))\n\ttestImplementation(project(\":jadx-plugins:jadx-java-input\"))\n\ttestImplementation(project(\":jadx-plugins:jadx-raung-input\"))\n\n\ttestImplementation(\"org.eclipse.jdt:ecj\") {\n\t\tversion {\n\t\t\tprefer(\"3.33.0\")\n\t\t\tstrictly(\"[3.33, 3.34[\") // from 3.34 compiled with Java 17\n\t\t}\n\t}\n\ttestImplementation(\"tools.profiler:async-profiler:4.2\")\n}\n\nval jadxTestJavaVersion = getTestJavaVersion()\n\nfun getTestJavaVersion(): Int? {\n\tval envVarName = \"JADX_TEST_JAVA_VERSION\"\n\tval testJavaVer = System.getenv(envVarName)?.toInt() ?: return null\n\tval currentJavaVer = java.toolchain.languageVersion.get().asInt()\n\tif (testJavaVer < currentJavaVer) {\n\t\tthrow GradleException(\"'$envVarName' can't be set to lower version than $currentJavaVer\")\n\t}\n\tprintln(\"Set Java toolchain for core tests to version '$testJavaVer'\")\n\treturn testJavaVer\n}\n\ntasks.named<Test>(\"test\") {\n\tjadxTestJavaVersion?.let { testJavaVer ->\n\t\tjavaLauncher =\n\t\t\tjavaToolchains.launcherFor {\n\t\t\t\tlanguageVersion = JavaLanguageVersion.of(testJavaVer)\n\t\t\t}\n\t}\n\n\t// disable cache to allow test's rerun,\n\t// because most tests are integration and depends on plugins and environment\n\toutputs.cacheIf { false }\n\n\t// exclude temp tests\n\texclude(\"**/tmp/*\")\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/CommentsLevel.java",
    "content": "package jadx.api;\n\npublic enum CommentsLevel {\n\tNONE,\n\tUSER_ONLY,\n\tERROR,\n\tWARN,\n\tINFO,\n\tDEBUG;\n\n\tpublic boolean filter(CommentsLevel limit) {\n\t\treturn this.ordinal() <= limit.ordinal();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/DecompilationMode.java",
    "content": "package jadx.api;\n\npublic enum DecompilationMode {\n\t/**\n\t * Trying best options (default)\n\t */\n\tAUTO,\n\n\t/**\n\t * Restore code structure (normal java code)\n\t */\n\tRESTRUCTURE,\n\n\t/**\n\t * Simplified instructions (linear with goto's)\n\t */\n\tSIMPLE,\n\n\t/**\n\t * Raw instructions without modifications\n\t */\n\tFALLBACK;\n\n\tpublic boolean isSpecial() {\n\t\tswitch (this) {\n\t\t\tcase AUTO:\n\t\t\tcase RESTRUCTURE:\n\t\t\t\treturn false;\n\t\t\tcase SIMPLE:\n\t\t\tcase FALLBACK:\n\t\t\t\treturn true;\n\t\t\tdefault:\n\t\t\t\tthrow new RuntimeException(\"Unexpected decompilation mode: \" + this);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/ICodeCache.java",
    "content": "package jadx.api;\n\nimport java.io.Closeable;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\npublic interface ICodeCache extends Closeable {\n\n\tvoid add(String clsFullName, ICodeInfo codeInfo);\n\n\tvoid remove(String clsFullName);\n\n\t@NotNull\n\tICodeInfo get(String clsFullName);\n\n\t@Nullable\n\tString getCode(String clsFullName);\n\n\tboolean contains(String clsFullName);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/ICodeInfo.java",
    "content": "package jadx.api;\n\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.api.metadata.ICodeMetadata;\n\npublic interface ICodeInfo {\n\n\tICodeInfo EMPTY = new SimpleCodeInfo(\"\");\n\n\tString getCodeStr();\n\n\tICodeMetadata getCodeMetadata();\n\n\tboolean hasMetadata();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/ICodeWriter.java",
    "content": "package jadx.api;\n\nimport java.util.Map;\n\nimport org.jetbrains.annotations.ApiStatus;\n\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeNodeRef;\n\npublic interface ICodeWriter {\n\n\tboolean isMetadataSupported();\n\n\tICodeWriter startLine();\n\n\tICodeWriter startLine(char c);\n\n\tICodeWriter startLine(String str);\n\n\tICodeWriter startLineWithNum(int sourceLine);\n\n\tICodeWriter addMultiLine(String str);\n\n\tICodeWriter add(String str);\n\n\tICodeWriter add(char c);\n\n\tICodeWriter add(ICodeWriter code);\n\n\tICodeWriter newLine();\n\n\tICodeWriter addIndent();\n\n\tvoid incIndent();\n\n\tvoid decIndent();\n\n\tint getIndent();\n\n\tvoid setIndent(int indent);\n\n\t/**\n\t * Return current line (only if metadata is supported)\n\t */\n\tint getLine();\n\n\t/**\n\t * Return start line position (only if metadata is supported)\n\t */\n\tint getLineStartPos();\n\n\tvoid attachDefinition(ICodeNodeRef obj);\n\n\tvoid attachAnnotation(ICodeAnnotation obj);\n\n\tvoid attachLineAnnotation(ICodeAnnotation obj);\n\n\tvoid attachSourceLine(int sourceLine);\n\n\tICodeInfo finish();\n\n\tString getCodeStr();\n\n\tint getLength();\n\n\tStringBuilder getRawBuf();\n\n\t@ApiStatus.Internal\n\tMap<Integer, ICodeAnnotation> getRawAnnotations();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/IDecompileScheduler.java",
    "content": "package jadx.api;\n\nimport java.util.List;\n\npublic interface IDecompileScheduler {\n\tList<List<JavaClass>> buildBatches(List<JavaClass> classes);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/JadxArgs.java",
    "content": "package jadx.api;\n\nimport java.io.Closeable;\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.args.GeneratedRenamesMappingFileMode;\nimport jadx.api.args.IntegerFormat;\nimport jadx.api.args.ResourceNameSource;\nimport jadx.api.args.UseSourceNameAsClassNameAlias;\nimport jadx.api.args.UserRenamesMappingsMode;\nimport jadx.api.data.ICodeData;\nimport jadx.api.deobf.IAliasProvider;\nimport jadx.api.deobf.IRenameCondition;\nimport jadx.api.impl.AnnotatedCodeWriter;\nimport jadx.api.impl.InMemoryCodeCache;\nimport jadx.api.plugins.loader.JadxBasePluginLoader;\nimport jadx.api.plugins.loader.JadxPluginLoader;\nimport jadx.api.security.IJadxSecurity;\nimport jadx.api.security.JadxSecurityFlag;\nimport jadx.api.security.impl.JadxSecurity;\nimport jadx.api.usage.IUsageInfoCache;\nimport jadx.api.usage.impl.InMemoryUsageInfoCache;\nimport jadx.core.deobf.DeobfAliasProvider;\nimport jadx.core.deobf.conditions.DeobfWhitelist;\nimport jadx.core.deobf.conditions.JadxRenameConditions;\nimport jadx.core.export.ExportGradleType;\nimport jadx.core.plugins.PluginContext;\nimport jadx.core.plugins.files.IJadxFilesGetter;\nimport jadx.core.plugins.files.TempFilesGetter;\nimport jadx.core.utils.files.FileUtils;\n\npublic class JadxArgs implements Closeable {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxArgs.class);\n\n\tpublic static final int DEFAULT_THREADS_COUNT = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);\n\n\tpublic static final String DEFAULT_NEW_LINE_STR = System.lineSeparator();\n\tpublic static final String DEFAULT_INDENT_STR = \"    \";\n\n\tpublic static final String DEFAULT_OUT_DIR = \"jadx-output\";\n\tpublic static final String DEFAULT_SRC_DIR = \"sources\";\n\tpublic static final String DEFAULT_RES_DIR = \"resources\";\n\n\tprivate List<File> inputFiles = new ArrayList<>(1);\n\n\tprivate File outDir;\n\tprivate File outDirSrc;\n\tprivate File outDirRes;\n\n\tprivate ICodeCache codeCache = new InMemoryCodeCache();\n\n\t/**\n\t * Usage data cache. Saves use places of classes, methods and fields between code reloads.\n\t * Can be set to {@link jadx.api.usage.impl.EmptyUsageInfoCache} if code reload not needed.\n\t */\n\tprivate IUsageInfoCache usageInfoCache = new InMemoryUsageInfoCache();\n\n\tprivate Function<JadxArgs, ICodeWriter> codeWriterProvider = AnnotatedCodeWriter::new;\n\n\tprivate int threadsCount = DEFAULT_THREADS_COUNT;\n\n\tprivate boolean cfgOutput = false;\n\tprivate boolean rawCFGOutput = false;\n\n\tprivate boolean showInconsistentCode = false;\n\n\tprivate boolean useImports = true;\n\tprivate boolean debugInfo = true;\n\tprivate boolean insertDebugLines = false;\n\tprivate boolean extractFinally = true;\n\tprivate boolean inlineAnonymousClasses = true;\n\tprivate boolean inlineMethods = true;\n\tprivate boolean allowInlineKotlinLambda = true;\n\tprivate boolean moveInnerClasses = true;\n\n\tprivate boolean skipResources = false;\n\tprivate boolean skipSources = false;\n\tprivate boolean useHeadersForDetectResourceExtensions;\n\n\t/**\n\t * Predicate that allows to filter the classes to be process based on their full name\n\t */\n\tprivate Predicate<String> classFilter = null;\n\n\t/**\n\t * Save dependencies for classes accepted by {@code classFilter}\n\t */\n\tprivate boolean includeDependencies = false;\n\n\tprivate Path userRenamesMappingsPath = null;\n\tprivate UserRenamesMappingsMode userRenamesMappingsMode = UserRenamesMappingsMode.getDefault();\n\n\tprivate boolean deobfuscationOn = false;\n\tprivate UseSourceNameAsClassNameAlias useSourceNameAsClassNameAlias = UseSourceNameAsClassNameAlias.getDefault();\n\tprivate int sourceNameRepeatLimit = 10;\n\n\tprivate File generatedRenamesMappingFile = null;\n\tprivate GeneratedRenamesMappingFileMode generatedRenamesMappingFileMode = GeneratedRenamesMappingFileMode.getDefault();\n\tprivate ResourceNameSource resourceNameSource = ResourceNameSource.AUTO;\n\n\tprivate int deobfuscationMinLength = 0;\n\tprivate int deobfuscationMaxLength = Integer.MAX_VALUE;\n\n\t/**\n\t * List of classes and packages (ends with '.*') to exclude from deobfuscation\n\t */\n\tprivate List<String> deobfuscationWhitelist = DeobfWhitelist.DEFAULT_LIST;\n\n\t/**\n\t * Nodes alias provider for deobfuscator and rename visitor\n\t */\n\tprivate IAliasProvider aliasProvider = new DeobfAliasProvider();\n\n\t/**\n\t * Condition to rename node in deobfuscator\n\t */\n\tprivate IRenameCondition renameCondition = JadxRenameConditions.buildDefault();\n\n\tprivate boolean escapeUnicode = false;\n\tprivate boolean replaceConsts = true;\n\tprivate boolean respectBytecodeAccModifiers = false;\n\tprivate @Nullable ExportGradleType exportGradleType = null;\n\n\tprivate boolean restoreSwitchOverString = true;\n\n\tprivate boolean skipXmlPrettyPrint = false;\n\n\tprivate boolean fsCaseSensitive;\n\n\tpublic enum RenameEnum {\n\t\tCASE, VALID, PRINTABLE\n\t}\n\n\tprivate Set<RenameEnum> renameFlags = EnumSet.allOf(RenameEnum.class);\n\n\tpublic enum OutputFormatEnum {\n\t\tJAVA, JSON\n\t}\n\n\tprivate OutputFormatEnum outputFormat = OutputFormatEnum.JAVA;\n\n\tprivate DecompilationMode decompilationMode = DecompilationMode.AUTO;\n\n\tprivate ICodeData codeData;\n\n\tprivate String codeNewLineStr = DEFAULT_NEW_LINE_STR;\n\n\tprivate String codeIndentStr = DEFAULT_INDENT_STR;\n\n\tprivate CommentsLevel commentsLevel = CommentsLevel.INFO;\n\n\tprivate IntegerFormat integerFormat = IntegerFormat.AUTO;\n\n\t/**\n\t * Maximum updates allowed total in method per one instruction.\n\t * Should be more or equal 1, default value is 10.\n\t */\n\tprivate int typeUpdatesLimitCount = 10;\n\n\tprivate boolean useDxInput = false;\n\n\tpublic enum UseKotlinMethodsForVarNames {\n\t\tDISABLE, APPLY, APPLY_AND_HIDE\n\t}\n\n\tprivate UseKotlinMethodsForVarNames useKotlinMethodsForVarNames = UseKotlinMethodsForVarNames.APPLY;\n\n\t/**\n\t * Additional files structure info.\n\t * Defaults to tmp dirs.\n\t */\n\tprivate IJadxFilesGetter filesGetter = TempFilesGetter.INSTANCE;\n\n\t/**\n\t * Additional data validation and security checks\n\t */\n\tprivate IJadxSecurity security = new JadxSecurity(JadxSecurityFlag.all());\n\n\t/**\n\t * Don't save files (can be using for performance testing)\n\t */\n\tprivate boolean skipFilesSave = false;\n\n\t/**\n\t * Run additional expensive checks to verify internal invariants and info integrity\n\t */\n\tprivate boolean runDebugChecks = false;\n\n\t/**\n\t * Passes to exclude from processing.\n\t */\n\tprivate final List<String> disabledPasses = new ArrayList<>();\n\n\tprivate Map<String, String> pluginOptions = new HashMap<>();\n\n\tprivate Set<String> disabledPlugins = new HashSet<>();\n\n\tprivate JadxPluginLoader pluginLoader = new JadxBasePluginLoader();\n\n\tprivate boolean loadJadxClsSetFile = true;\n\n\tpublic JadxArgs() {\n\t\t// use default options\n\t}\n\n\tpublic void setRootDir(File rootDir) {\n\t\tsetOutDir(rootDir);\n\t\tsetOutDirSrc(new File(rootDir, DEFAULT_SRC_DIR));\n\t\tsetOutDirRes(new File(rootDir, DEFAULT_RES_DIR));\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\ttry {\n\t\t\tinputFiles = null;\n\t\t\tif (codeCache != null) {\n\t\t\t\tcodeCache.close();\n\t\t\t}\n\t\t\tif (usageInfoCache != null) {\n\t\t\t\tusageInfoCache.close();\n\t\t\t}\n\t\t\tif (pluginLoader != null) {\n\t\t\t\tpluginLoader.close();\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to close JadxArgs\", e);\n\t\t} finally {\n\t\t\tcodeCache = null;\n\t\t\tusageInfoCache = null;\n\t\t}\n\t}\n\n\tpublic List<File> getInputFiles() {\n\t\treturn inputFiles;\n\t}\n\n\tpublic void addInputFile(File inputFile) {\n\t\tthis.inputFiles.add(inputFile);\n\t}\n\n\tpublic void setInputFile(File inputFile) {\n\t\taddInputFile(inputFile);\n\t}\n\n\tpublic void setInputFiles(List<File> inputFiles) {\n\t\tthis.inputFiles = inputFiles;\n\t}\n\n\tpublic File getOutDir() {\n\t\treturn outDir;\n\t}\n\n\tpublic void setOutDir(File outDir) {\n\t\tthis.outDir = outDir;\n\t}\n\n\tpublic File getOutDirSrc() {\n\t\treturn outDirSrc;\n\t}\n\n\tpublic void setOutDirSrc(File outDirSrc) {\n\t\tthis.outDirSrc = outDirSrc;\n\t}\n\n\tpublic File getOutDirRes() {\n\t\treturn outDirRes;\n\t}\n\n\tpublic void setOutDirRes(File outDirRes) {\n\t\tthis.outDirRes = outDirRes;\n\t}\n\n\tpublic int getThreadsCount() {\n\t\treturn threadsCount;\n\t}\n\n\tpublic void setThreadsCount(int threadsCount) {\n\t\tthis.threadsCount = Math.max(1, threadsCount); // make sure threadsCount >= 1\n\t}\n\n\tpublic boolean isCfgOutput() {\n\t\treturn cfgOutput;\n\t}\n\n\tpublic void setCfgOutput(boolean cfgOutput) {\n\t\tthis.cfgOutput = cfgOutput;\n\t}\n\n\tpublic boolean isRawCFGOutput() {\n\t\treturn rawCFGOutput;\n\t}\n\n\tpublic void setRawCFGOutput(boolean rawCFGOutput) {\n\t\tthis.rawCFGOutput = rawCFGOutput;\n\t}\n\n\tpublic boolean isFallbackMode() {\n\t\treturn decompilationMode == DecompilationMode.FALLBACK;\n\t}\n\n\t/**\n\t * Deprecated: use 'decompilation mode' property\n\t */\n\t@Deprecated\n\tpublic void setFallbackMode(boolean fallbackMode) {\n\t\tif (fallbackMode) {\n\t\t\tthis.decompilationMode = DecompilationMode.FALLBACK;\n\t\t}\n\t}\n\n\tpublic boolean isShowInconsistentCode() {\n\t\treturn showInconsistentCode;\n\t}\n\n\tpublic void setShowInconsistentCode(boolean showInconsistentCode) {\n\t\tthis.showInconsistentCode = showInconsistentCode;\n\t}\n\n\tpublic boolean isUseImports() {\n\t\treturn useImports;\n\t}\n\n\tpublic void setUseImports(boolean useImports) {\n\t\tthis.useImports = useImports;\n\t}\n\n\tpublic boolean isDebugInfo() {\n\t\treturn debugInfo;\n\t}\n\n\tpublic void setDebugInfo(boolean debugInfo) {\n\t\tthis.debugInfo = debugInfo;\n\t}\n\n\tpublic boolean isInsertDebugLines() {\n\t\treturn insertDebugLines;\n\t}\n\n\tpublic void setInsertDebugLines(boolean insertDebugLines) {\n\t\tthis.insertDebugLines = insertDebugLines;\n\t}\n\n\tpublic boolean isInlineAnonymousClasses() {\n\t\treturn inlineAnonymousClasses;\n\t}\n\n\tpublic void setInlineAnonymousClasses(boolean inlineAnonymousClasses) {\n\t\tthis.inlineAnonymousClasses = inlineAnonymousClasses;\n\t}\n\n\tpublic boolean isInlineMethods() {\n\t\treturn inlineMethods;\n\t}\n\n\tpublic void setInlineMethods(boolean inlineMethods) {\n\t\tthis.inlineMethods = inlineMethods;\n\t}\n\n\tpublic boolean isAllowInlineKotlinLambda() {\n\t\treturn allowInlineKotlinLambda;\n\t}\n\n\tpublic void setAllowInlineKotlinLambda(boolean allowInlineKotlinLambda) {\n\t\tthis.allowInlineKotlinLambda = allowInlineKotlinLambda;\n\t}\n\n\tpublic boolean isMoveInnerClasses() {\n\t\treturn moveInnerClasses;\n\t}\n\n\tpublic void setMoveInnerClasses(boolean moveInnerClasses) {\n\t\tthis.moveInnerClasses = moveInnerClasses;\n\t}\n\n\tpublic boolean isExtractFinally() {\n\t\treturn extractFinally;\n\t}\n\n\tpublic void setExtractFinally(boolean extractFinally) {\n\t\tthis.extractFinally = extractFinally;\n\t}\n\n\tpublic boolean isSkipResources() {\n\t\treturn skipResources;\n\t}\n\n\tpublic void setSkipResources(boolean skipResources) {\n\t\tthis.skipResources = skipResources;\n\t}\n\n\tpublic boolean isSkipSources() {\n\t\treturn skipSources;\n\t}\n\n\tpublic void setSkipSources(boolean skipSources) {\n\t\tthis.skipSources = skipSources;\n\t}\n\n\tpublic void setIncludeDependencies(boolean includeDependencies) {\n\t\tthis.includeDependencies = includeDependencies;\n\t}\n\n\tpublic boolean isIncludeDependencies() {\n\t\treturn includeDependencies;\n\t}\n\n\tpublic Predicate<String> getClassFilter() {\n\t\treturn classFilter;\n\t}\n\n\tpublic void setClassFilter(Predicate<String> classFilter) {\n\t\tthis.classFilter = classFilter;\n\t}\n\n\tpublic Path getUserRenamesMappingsPath() {\n\t\treturn userRenamesMappingsPath;\n\t}\n\n\tpublic void setUserRenamesMappingsPath(Path path) {\n\t\tthis.userRenamesMappingsPath = path;\n\t}\n\n\tpublic UserRenamesMappingsMode getUserRenamesMappingsMode() {\n\t\treturn userRenamesMappingsMode;\n\t}\n\n\tpublic void setUserRenamesMappingsMode(UserRenamesMappingsMode mode) {\n\t\tthis.userRenamesMappingsMode = mode;\n\t}\n\n\tpublic boolean isDeobfuscationOn() {\n\t\treturn deobfuscationOn;\n\t}\n\n\tpublic void setDeobfuscationOn(boolean deobfuscationOn) {\n\t\tthis.deobfuscationOn = deobfuscationOn;\n\t}\n\n\tpublic boolean isDeobfuscationForceSave() {\n\t\treturn generatedRenamesMappingFileMode == GeneratedRenamesMappingFileMode.OVERWRITE;\n\t}\n\n\tpublic void setDeobfuscationForceSave(boolean deobfuscationForceSave) {\n\t\tif (deobfuscationForceSave) {\n\t\t\tthis.generatedRenamesMappingFileMode = GeneratedRenamesMappingFileMode.OVERWRITE;\n\t\t}\n\t}\n\n\tpublic GeneratedRenamesMappingFileMode getGeneratedRenamesMappingFileMode() {\n\t\treturn generatedRenamesMappingFileMode;\n\t}\n\n\tpublic void setGeneratedRenamesMappingFileMode(GeneratedRenamesMappingFileMode mode) {\n\t\tthis.generatedRenamesMappingFileMode = mode;\n\t}\n\n\tpublic UseSourceNameAsClassNameAlias getUseSourceNameAsClassNameAlias() {\n\t\treturn useSourceNameAsClassNameAlias;\n\t}\n\n\tpublic void setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias useSourceNameAsClassNameAlias) {\n\t\tthis.useSourceNameAsClassNameAlias = useSourceNameAsClassNameAlias;\n\t}\n\n\tpublic int getSourceNameRepeatLimit() {\n\t\treturn sourceNameRepeatLimit;\n\t}\n\n\tpublic void setSourceNameRepeatLimit(int sourceNameRepeatLimit) {\n\t\tthis.sourceNameRepeatLimit = sourceNameRepeatLimit;\n\t}\n\n\t/**\n\t * @deprecated Use {@link #getUseSourceNameAsClassNameAlias()} instead.\n\t */\n\t@Deprecated\n\tpublic boolean isUseSourceNameAsClassAlias() {\n\t\treturn getUseSourceNameAsClassNameAlias().toBoolean();\n\t}\n\n\t/**\n\t * @deprecated Use {@link #setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias)} instead.\n\t */\n\t@Deprecated\n\tpublic void setUseSourceNameAsClassAlias(boolean useSourceNameAsClassAlias) {\n\t\tfinal var useSourceNameAsClassNameAlias = UseSourceNameAsClassNameAlias.create(useSourceNameAsClassAlias);\n\t\tsetUseSourceNameAsClassNameAlias(useSourceNameAsClassNameAlias);\n\t}\n\n\tpublic int getDeobfuscationMinLength() {\n\t\treturn deobfuscationMinLength;\n\t}\n\n\tpublic void setDeobfuscationMinLength(int deobfuscationMinLength) {\n\t\tthis.deobfuscationMinLength = deobfuscationMinLength;\n\t}\n\n\tpublic int getDeobfuscationMaxLength() {\n\t\treturn deobfuscationMaxLength;\n\t}\n\n\tpublic void setDeobfuscationMaxLength(int deobfuscationMaxLength) {\n\t\tthis.deobfuscationMaxLength = deobfuscationMaxLength;\n\t}\n\n\tpublic List<String> getDeobfuscationWhitelist() {\n\t\treturn this.deobfuscationWhitelist;\n\t}\n\n\tpublic void setDeobfuscationWhitelist(List<String> deobfuscationWhitelist) {\n\t\tthis.deobfuscationWhitelist = deobfuscationWhitelist;\n\t}\n\n\tpublic File getGeneratedRenamesMappingFile() {\n\t\treturn generatedRenamesMappingFile;\n\t}\n\n\tpublic void setGeneratedRenamesMappingFile(File file) {\n\t\tthis.generatedRenamesMappingFile = file;\n\t}\n\n\tpublic ResourceNameSource getResourceNameSource() {\n\t\treturn resourceNameSource;\n\t}\n\n\tpublic void setResourceNameSource(ResourceNameSource resourceNameSource) {\n\t\tthis.resourceNameSource = resourceNameSource;\n\t}\n\n\tpublic IAliasProvider getAliasProvider() {\n\t\treturn aliasProvider;\n\t}\n\n\tpublic void setAliasProvider(IAliasProvider aliasProvider) {\n\t\tthis.aliasProvider = aliasProvider;\n\t}\n\n\tpublic IRenameCondition getRenameCondition() {\n\t\treturn renameCondition;\n\t}\n\n\tpublic void setRenameCondition(IRenameCondition renameCondition) {\n\t\tthis.renameCondition = renameCondition;\n\t}\n\n\tpublic boolean isEscapeUnicode() {\n\t\treturn escapeUnicode;\n\t}\n\n\tpublic void setEscapeUnicode(boolean escapeUnicode) {\n\t\tthis.escapeUnicode = escapeUnicode;\n\t}\n\n\tpublic boolean isReplaceConsts() {\n\t\treturn replaceConsts;\n\t}\n\n\tpublic void setReplaceConsts(boolean replaceConsts) {\n\t\tthis.replaceConsts = replaceConsts;\n\t}\n\n\tpublic boolean isRespectBytecodeAccModifiers() {\n\t\treturn respectBytecodeAccModifiers;\n\t}\n\n\tpublic void setRespectBytecodeAccModifiers(boolean respectBytecodeAccModifiers) {\n\t\tthis.respectBytecodeAccModifiers = respectBytecodeAccModifiers;\n\t}\n\n\tpublic boolean isExportAsGradleProject() {\n\t\treturn exportGradleType != null;\n\t}\n\n\tpublic void setExportAsGradleProject(boolean exportAsGradleProject) {\n\t\tif (exportAsGradleProject) {\n\t\t\tif (exportGradleType == null) {\n\t\t\t\texportGradleType = ExportGradleType.AUTO;\n\t\t\t}\n\t\t} else {\n\t\t\texportGradleType = null;\n\t\t}\n\t}\n\n\tpublic @Nullable ExportGradleType getExportGradleType() {\n\t\treturn exportGradleType;\n\t}\n\n\tpublic void setExportGradleType(@Nullable ExportGradleType exportGradleType) {\n\t\tthis.exportGradleType = exportGradleType;\n\t}\n\n\tpublic boolean isRestoreSwitchOverString() {\n\t\treturn restoreSwitchOverString;\n\t}\n\n\tpublic void setRestoreSwitchOverString(boolean restoreSwitchOverString) {\n\t\tthis.restoreSwitchOverString = restoreSwitchOverString;\n\t}\n\n\tpublic boolean isSkipXmlPrettyPrint() {\n\t\treturn skipXmlPrettyPrint;\n\t}\n\n\tpublic void setSkipXmlPrettyPrint(boolean skipXmlPrettyPrint) {\n\t\tthis.skipXmlPrettyPrint = skipXmlPrettyPrint;\n\t}\n\n\tpublic boolean isFsCaseSensitive() {\n\t\treturn fsCaseSensitive;\n\t}\n\n\tpublic void setFsCaseSensitive(boolean fsCaseSensitive) {\n\t\tthis.fsCaseSensitive = fsCaseSensitive;\n\t}\n\n\tpublic boolean isRenameCaseSensitive() {\n\t\treturn renameFlags.contains(RenameEnum.CASE);\n\t}\n\n\tpublic void setRenameCaseSensitive(boolean renameCaseSensitive) {\n\t\tupdateRenameFlag(renameCaseSensitive, RenameEnum.CASE);\n\t}\n\n\tpublic boolean isRenameValid() {\n\t\treturn renameFlags.contains(RenameEnum.VALID);\n\t}\n\n\tpublic void setRenameValid(boolean renameValid) {\n\t\tupdateRenameFlag(renameValid, RenameEnum.VALID);\n\t}\n\n\tpublic boolean isRenamePrintable() {\n\t\treturn renameFlags.contains(RenameEnum.PRINTABLE);\n\t}\n\n\tpublic void setRenamePrintable(boolean renamePrintable) {\n\t\tupdateRenameFlag(renamePrintable, RenameEnum.PRINTABLE);\n\t}\n\n\tprivate void updateRenameFlag(boolean enabled, RenameEnum flag) {\n\t\tif (enabled) {\n\t\t\trenameFlags.add(flag);\n\t\t} else {\n\t\t\trenameFlags.remove(flag);\n\t\t}\n\t}\n\n\tpublic void setRenameFlags(Set<RenameEnum> renameFlags) {\n\t\tthis.renameFlags = renameFlags;\n\t}\n\n\tpublic Set<RenameEnum> getRenameFlags() {\n\t\treturn renameFlags;\n\t}\n\n\tpublic OutputFormatEnum getOutputFormat() {\n\t\treturn outputFormat;\n\t}\n\n\tpublic boolean isJsonOutput() {\n\t\treturn outputFormat == OutputFormatEnum.JSON;\n\t}\n\n\tpublic void setOutputFormat(OutputFormatEnum outputFormat) {\n\t\tthis.outputFormat = outputFormat;\n\t}\n\n\tpublic DecompilationMode getDecompilationMode() {\n\t\treturn decompilationMode;\n\t}\n\n\tpublic void setDecompilationMode(DecompilationMode decompilationMode) {\n\t\tthis.decompilationMode = decompilationMode;\n\t}\n\n\tpublic ICodeCache getCodeCache() {\n\t\treturn codeCache;\n\t}\n\n\tpublic void setCodeCache(ICodeCache codeCache) {\n\t\tthis.codeCache = codeCache;\n\t}\n\n\tpublic Function<JadxArgs, ICodeWriter> getCodeWriterProvider() {\n\t\treturn codeWriterProvider;\n\t}\n\n\tpublic void setCodeWriterProvider(Function<JadxArgs, ICodeWriter> codeWriterProvider) {\n\t\tthis.codeWriterProvider = codeWriterProvider;\n\t}\n\n\tpublic IUsageInfoCache getUsageInfoCache() {\n\t\treturn usageInfoCache;\n\t}\n\n\tpublic void setUsageInfoCache(IUsageInfoCache usageInfoCache) {\n\t\tthis.usageInfoCache = usageInfoCache;\n\t}\n\n\tpublic ICodeData getCodeData() {\n\t\treturn codeData;\n\t}\n\n\tpublic void setCodeData(ICodeData codeData) {\n\t\tthis.codeData = codeData;\n\t}\n\n\tpublic String getCodeIndentStr() {\n\t\treturn codeIndentStr;\n\t}\n\n\tpublic void setCodeIndentStr(String codeIndentStr) {\n\t\tthis.codeIndentStr = codeIndentStr;\n\t}\n\n\tpublic String getCodeNewLineStr() {\n\t\treturn codeNewLineStr;\n\t}\n\n\tpublic void setCodeNewLineStr(String codeNewLineStr) {\n\t\tthis.codeNewLineStr = codeNewLineStr;\n\t}\n\n\tpublic CommentsLevel getCommentsLevel() {\n\t\treturn commentsLevel;\n\t}\n\n\tpublic void setCommentsLevel(CommentsLevel commentsLevel) {\n\t\tthis.commentsLevel = commentsLevel;\n\t}\n\n\tpublic IntegerFormat getIntegerFormat() {\n\t\treturn integerFormat;\n\t}\n\n\tpublic void setIntegerFormat(IntegerFormat format) {\n\t\tthis.integerFormat = format;\n\t}\n\n\tpublic int getTypeUpdatesLimitCount() {\n\t\treturn typeUpdatesLimitCount;\n\t}\n\n\tpublic void setTypeUpdatesLimitCount(int typeUpdatesLimitCount) {\n\t\tthis.typeUpdatesLimitCount = Math.max(1, typeUpdatesLimitCount);\n\t}\n\n\tpublic boolean isUseDxInput() {\n\t\treturn useDxInput;\n\t}\n\n\tpublic void setUseDxInput(boolean useDxInput) {\n\t\tthis.useDxInput = useDxInput;\n\t}\n\n\tpublic UseKotlinMethodsForVarNames getUseKotlinMethodsForVarNames() {\n\t\treturn useKotlinMethodsForVarNames;\n\t}\n\n\tpublic void setUseKotlinMethodsForVarNames(UseKotlinMethodsForVarNames useKotlinMethodsForVarNames) {\n\t\tthis.useKotlinMethodsForVarNames = useKotlinMethodsForVarNames;\n\t}\n\n\tpublic IJadxFilesGetter getFilesGetter() {\n\t\treturn filesGetter;\n\t}\n\n\tpublic void setFilesGetter(IJadxFilesGetter filesGetter) {\n\t\tthis.filesGetter = filesGetter;\n\t}\n\n\tpublic IJadxSecurity getSecurity() {\n\t\treturn security;\n\t}\n\n\tpublic void setSecurity(IJadxSecurity security) {\n\t\tthis.security = security;\n\t}\n\n\tpublic boolean isSkipFilesSave() {\n\t\treturn skipFilesSave;\n\t}\n\n\tpublic void setSkipFilesSave(boolean skipFilesSave) {\n\t\tthis.skipFilesSave = skipFilesSave;\n\t}\n\n\tpublic boolean isRunDebugChecks() {\n\t\treturn runDebugChecks;\n\t}\n\n\tpublic void setRunDebugChecks(boolean runDebugChecks) {\n\t\tthis.runDebugChecks = runDebugChecks;\n\t}\n\n\tpublic List<String> getDisabledPasses() {\n\t\treturn disabledPasses;\n\t}\n\n\tpublic Map<String, String> getPluginOptions() {\n\t\treturn pluginOptions;\n\t}\n\n\tpublic void setPluginOptions(Map<String, String> pluginOptions) {\n\t\tthis.pluginOptions = pluginOptions;\n\t}\n\n\tpublic Set<String> getDisabledPlugins() {\n\t\treturn disabledPlugins;\n\t}\n\n\tpublic void setDisabledPlugins(Set<String> disabledPlugins) {\n\t\tthis.disabledPlugins = disabledPlugins;\n\t}\n\n\tpublic JadxPluginLoader getPluginLoader() {\n\t\treturn pluginLoader;\n\t}\n\n\tpublic void setPluginLoader(JadxPluginLoader pluginLoader) {\n\t\tthis.pluginLoader = pluginLoader;\n\t}\n\n\tpublic boolean isLoadJadxClsSetFile() {\n\t\treturn loadJadxClsSetFile;\n\t}\n\n\tpublic void setLoadJadxClsSetFile(boolean loadJadxClsSetFile) {\n\t\tthis.loadJadxClsSetFile = loadJadxClsSetFile;\n\t}\n\n\tpublic void setUseHeadersForDetectResourceExtensions(boolean useHeadersForDetectResourceExtensions) {\n\t\tthis.useHeadersForDetectResourceExtensions = useHeadersForDetectResourceExtensions;\n\t}\n\n\tpublic boolean isUseHeadersForDetectResourceExtensions() {\n\t\treturn useHeadersForDetectResourceExtensions;\n\t}\n\n\t/**\n\t * Hash of all options that can change result code\n\t */\n\tpublic String makeCodeArgsHash(@Nullable JadxDecompiler decompiler) {\n\t\tString argStr = \"args:\" + decompilationMode + useImports + showInconsistentCode\n\t\t\t\t+ inlineAnonymousClasses + inlineMethods + moveInnerClasses + allowInlineKotlinLambda\n\t\t\t\t+ deobfuscationOn + deobfuscationMinLength + deobfuscationMaxLength + deobfuscationWhitelist\n\t\t\t\t+ useSourceNameAsClassNameAlias + sourceNameRepeatLimit\n\t\t\t\t+ resourceNameSource + useHeadersForDetectResourceExtensions\n\t\t\t\t+ useKotlinMethodsForVarNames\n\t\t\t\t+ insertDebugLines + extractFinally\n\t\t\t\t+ debugInfo + escapeUnicode + replaceConsts + restoreSwitchOverString\n\t\t\t\t+ respectBytecodeAccModifiers + fsCaseSensitive + renameFlags\n\t\t\t\t+ commentsLevel + useDxInput + integerFormat + typeUpdatesLimitCount\n\t\t\t\t+ \"|\" + buildPluginsHash(decompiler);\n\t\treturn FileUtils.md5Sum(argStr);\n\t}\n\n\tprivate static String buildPluginsHash(@Nullable JadxDecompiler decompiler) {\n\t\tif (decompiler == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn decompiler.getPluginManager().getResolvedPluginContexts()\n\t\t\t\t.stream()\n\t\t\t\t.map(PluginContext::getInputsHash)\n\t\t\t\t.collect(Collectors.joining(\":\"));\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"JadxArgs{\" + \"inputFiles=\" + inputFiles\n\t\t\t\t+ \", outDir=\" + outDir\n\t\t\t\t+ \", outDirSrc=\" + outDirSrc\n\t\t\t\t+ \", outDirRes=\" + outDirRes\n\t\t\t\t+ \", threadsCount=\" + threadsCount\n\t\t\t\t+ \", decompilationMode=\" + decompilationMode\n\t\t\t\t+ \", showInconsistentCode=\" + showInconsistentCode\n\t\t\t\t+ \", useImports=\" + useImports\n\t\t\t\t+ \", skipResources=\" + skipResources\n\t\t\t\t+ \", skipSources=\" + skipSources\n\t\t\t\t+ \", includeDependencies=\" + includeDependencies\n\t\t\t\t+ \", userRenamesMappingsPath=\" + userRenamesMappingsPath\n\t\t\t\t+ \", userRenamesMappingsMode=\" + userRenamesMappingsMode\n\t\t\t\t+ \", deobfuscationOn=\" + deobfuscationOn\n\t\t\t\t+ \", generatedRenamesMappingFile=\" + generatedRenamesMappingFile\n\t\t\t\t+ \", generatedRenamesMappingFileMode=\" + generatedRenamesMappingFileMode\n\t\t\t\t+ \", resourceNameSource=\" + resourceNameSource\n\t\t\t\t+ \", useSourceNameAsClassNameAlias=\" + useSourceNameAsClassNameAlias\n\t\t\t\t+ \", sourceNameRepeatLimit=\" + sourceNameRepeatLimit\n\t\t\t\t+ \", useKotlinMethodsForVarNames=\" + useKotlinMethodsForVarNames\n\t\t\t\t+ \", insertDebugLines=\" + insertDebugLines\n\t\t\t\t+ \", extractFinally=\" + extractFinally\n\t\t\t\t+ \", deobfuscationMinLength=\" + deobfuscationMinLength\n\t\t\t\t+ \", deobfuscationMaxLength=\" + deobfuscationMaxLength\n\t\t\t\t+ \", deobfuscationWhitelist=\" + deobfuscationWhitelist\n\t\t\t\t+ \", escapeUnicode=\" + escapeUnicode\n\t\t\t\t+ \", replaceConsts=\" + replaceConsts\n\t\t\t\t+ \", restoreSwitchOverString=\" + restoreSwitchOverString\n\t\t\t\t+ \", respectBytecodeAccModifiers=\" + respectBytecodeAccModifiers\n\t\t\t\t+ \", exportGradleType=\" + exportGradleType\n\t\t\t\t+ \", skipXmlPrettyPrint=\" + skipXmlPrettyPrint\n\t\t\t\t+ \", fsCaseSensitive=\" + fsCaseSensitive\n\t\t\t\t+ \", renameFlags=\" + renameFlags\n\t\t\t\t+ \", outputFormat=\" + outputFormat\n\t\t\t\t+ \", commentsLevel=\" + commentsLevel\n\t\t\t\t+ \", codeCache=\" + codeCache\n\t\t\t\t+ \", codeWriter=\" + codeWriterProvider.apply(this).getClass().getSimpleName()\n\t\t\t\t+ \", useDxInput=\" + useDxInput\n\t\t\t\t+ \", pluginOptions=\" + pluginOptions\n\t\t\t\t+ \", cfgOutput=\" + cfgOutput\n\t\t\t\t+ \", rawCFGOutput=\" + rawCFGOutput\n\t\t\t\t+ \", useHeadersForDetectResourceExtensions=\" + useHeadersForDetectResourceExtensions\n\t\t\t\t+ \", typeUpdatesLimitCount=\" + typeUpdatesLimitCount\n\t\t\t\t+ '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/JadxArgsValidator.java",
    "content": "package jadx.api;\n\nimport java.io.File;\nimport java.util.List;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.exceptions.JadxArgsValidateException;\n\npublic class JadxArgsValidator {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxArgsValidator.class);\n\n\tpublic static void validate(JadxDecompiler jadx) {\n\t\tJadxArgs args = jadx.getArgs();\n\t\tcheckInputFiles(jadx, args);\n\t\tvalidateOutDirs(args);\n\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tLOG.debug(\"Effective jadx args: {}\", args);\n\t\t}\n\t}\n\n\tprivate static void checkInputFiles(JadxDecompiler jadx, JadxArgs args) {\n\t\tList<File> inputFiles = args.getInputFiles();\n\t\tif (inputFiles.isEmpty() && jadx.getCustomCodeLoaders().isEmpty()) {\n\t\t\tthrow new JadxArgsValidateException(\"Please specify input file\");\n\t\t}\n\t\tfor (File file : inputFiles) {\n\t\t\tcheckFile(file);\n\t\t}\n\t}\n\n\tprivate static void validateOutDirs(JadxArgs args) {\n\t\tFile outDir = args.getOutDir();\n\t\tFile srcDir = args.getOutDirSrc();\n\t\tFile resDir = args.getOutDirRes();\n\t\tif (outDir == null) {\n\t\t\tif (srcDir != null) {\n\t\t\t\toutDir = srcDir;\n\t\t\t} else if (resDir != null) {\n\t\t\t\toutDir = resDir;\n\t\t\t} else {\n\t\t\t\toutDir = makeDirFromInput(args);\n\t\t\t}\n\t\t\targs.setOutDir(outDir);\n\t\t}\n\t\tif (srcDir == null) {\n\t\t\targs.setOutDirSrc(new File(args.getOutDir(), JadxArgs.DEFAULT_SRC_DIR));\n\t\t}\n\t\tif (resDir == null) {\n\t\t\targs.setOutDirRes(new File(args.getOutDir(), JadxArgs.DEFAULT_RES_DIR));\n\t\t}\n\n\t\tcheckDir(args.getOutDir(), \"Output\");\n\t\tcheckDir(args.getOutDirSrc(), \"Source output\");\n\t\tcheckDir(args.getOutDirRes(), \"Resources output\");\n\t}\n\n\t@NotNull\n\tprivate static File makeDirFromInput(JadxArgs args) {\n\t\tString outDirName;\n\t\tList<File> inputFiles = args.getInputFiles();\n\t\tif (inputFiles.isEmpty()) {\n\t\t\toutDirName = JadxArgs.DEFAULT_OUT_DIR;\n\t\t} else {\n\t\t\tFile file = inputFiles.get(0);\n\t\t\tString name = file.getName();\n\t\t\tint pos = name.lastIndexOf('.');\n\t\t\tif (pos != -1) {\n\t\t\t\toutDirName = name.substring(0, pos);\n\t\t\t} else {\n\t\t\t\toutDirName = name + '-' + JadxArgs.DEFAULT_OUT_DIR;\n\t\t\t}\n\t\t}\n\t\tLOG.info(\"output directory: {}\", outDirName);\n\t\treturn new File(outDirName);\n\t}\n\n\tprivate static void checkFile(File file) {\n\t\tif (!file.exists()) {\n\t\t\tthrow new JadxArgsValidateException(\"File not found \" + file.getAbsolutePath());\n\t\t}\n\t}\n\n\tprivate static void checkDir(File dir, String desc) {\n\t\tif (dir != null && dir.exists() && !dir.isDirectory()) {\n\t\t\tthrow new JadxArgsValidateException(desc + \" directory exists as file \" + dir);\n\t\t}\n\t}\n\n\tprivate JadxArgsValidator() {\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/JadxDecompiler.java",
    "content": "package jadx.api;\n\nimport java.io.Closeable;\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.ExecutorService;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.ApiStatus;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.metadata.annotations.NodeDeclareRef;\nimport jadx.api.metadata.annotations.VarNode;\nimport jadx.api.metadata.annotations.VarRef;\nimport jadx.api.plugins.CustomResourcesLoader;\nimport jadx.api.plugins.JadxPlugin;\nimport jadx.api.plugins.events.IJadxEvents;\nimport jadx.api.plugins.input.ICodeLoader;\nimport jadx.api.plugins.input.JadxCodeInput;\nimport jadx.api.plugins.pass.JadxPass;\nimport jadx.api.plugins.pass.types.JadxAfterLoadPass;\nimport jadx.api.plugins.pass.types.JadxPassType;\nimport jadx.api.utils.tasks.ITaskExecutor;\nimport jadx.core.Jadx;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.SaveCode;\nimport jadx.core.export.ExportGradle;\nimport jadx.core.export.OutDirs;\nimport jadx.core.plugins.JadxPluginManager;\nimport jadx.core.plugins.PluginContext;\nimport jadx.core.plugins.events.JadxEventsImpl;\nimport jadx.core.utils.DecompilerScheduler;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.core.utils.tasks.TaskExecutor;\nimport jadx.core.xmlgen.ResourcesSaver;\nimport jadx.zip.ZipReader;\n\n/**\n * Jadx API usage example:\n *\n * <pre>\n * <code>\n *\n * JadxArgs args = new JadxArgs();\n * args.getInputFiles().add(new File(\"test.apk\"));\n * args.setOutDir(new File(\"jadx-test-output\"));\n * try (JadxDecompiler jadx = new JadxDecompiler(args)) {\n *    jadx.load();\n *    jadx.save();\n * }\n * </code>\n * </pre>\n * <p>\n * Instead of 'save()' you can iterate over decompiled classes:\n *\n * <pre>\n * <code>\n *\n *  for(JavaClass cls : jadx.getClasses()) {\n *      System.out.println(cls.getCode());\n *  }\n * </code>\n * </pre>\n */\npublic final class JadxDecompiler implements Closeable {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxDecompiler.class);\n\n\tprivate final JadxArgs args;\n\tprivate final JadxPluginManager pluginManager;\n\tprivate final List<ICodeLoader> loadedInputs = new ArrayList<>();\n\tprivate final ZipReader zipReader;\n\n\tprivate RootNode root;\n\tprivate List<JavaClass> classes;\n\tprivate List<ResourceFile> resources;\n\n\tprivate final IDecompileScheduler decompileScheduler = new DecompilerScheduler();\n\tprivate final ResourcesLoader resourcesLoader;\n\n\tprivate final List<ICodeLoader> customCodeLoaders = new ArrayList<>();\n\tprivate final List<CustomResourcesLoader> customResourcesLoaders = new ArrayList<>();\n\tprivate final Map<JadxPassType, List<JadxPass>> customPasses = new HashMap<>();\n\tprivate final List<Closeable> closeableList = new ArrayList<>();\n\n\tprivate IJadxEvents events = new JadxEventsImpl();\n\n\tpublic JadxDecompiler() {\n\t\tthis(new JadxArgs());\n\t}\n\n\tpublic JadxDecompiler(JadxArgs args) {\n\t\tthis.args = Objects.requireNonNull(args);\n\t\tthis.pluginManager = new JadxPluginManager(this);\n\t\tthis.resourcesLoader = new ResourcesLoader(this);\n\t\tthis.zipReader = new ZipReader(args.getSecurity());\n\t}\n\n\tpublic void load() {\n\t\treset();\n\t\tJadxArgsValidator.validate(this);\n\t\tLOG.info(\"loading ...\");\n\t\tFileUtils.updateTempRootDir(args.getFilesGetter().getTempDir());\n\t\tloadPlugins();\n\t\tloadInputFiles();\n\n\t\troot = new RootNode(this);\n\t\troot.init();\n\t\t// load classes and resources\n\t\troot.loadClasses(loadedInputs);\n\t\troot.loadResources(resourcesLoader, getResources());\n\t\troot.finishClassLoad();\n\t\troot.initClassPath();\n\t\t// init passes\n\t\troot.mergePasses(customPasses);\n\t\troot.runPreDecompileStage();\n\t\troot.initPasses();\n\t\tloadFinished();\n\t}\n\n\t/**\n\t * Reload passes and plugins without processing classes and inputs\n\t */\n\tpublic void reloadPasses() {\n\t\tLOG.info(\"reloading (passes only) ...\");\n\t\tcustomPasses.clear();\n\t\troot.resetPasses();\n\t\tevents.reset();\n\t\tunloadPlugins();\n\n\t\tloadPlugins();\n\t\troot.mergePasses(customPasses);\n\t\troot.restartVisitors();\n\t\troot.initPasses();\n\t\tloadFinished();\n\t}\n\n\tprivate void loadInputFiles() {\n\t\tloadedInputs.clear();\n\t\tList<Path> inputPaths = Utils.collectionMap(args.getInputFiles(), File::toPath);\n\t\tList<Path> inputFiles = FileUtils.expandDirs(inputPaths);\n\t\tlong start = System.currentTimeMillis();\n\t\tfor (PluginContext plugin : pluginManager.getResolvedPluginContexts()) {\n\t\t\tfor (JadxCodeInput codeLoader : plugin.getCodeInputs()) {\n\t\t\t\ttry {\n\t\t\t\t\tICodeLoader loader = codeLoader.loadFiles(inputFiles);\n\t\t\t\t\tif (loader != null && !loader.isEmpty()) {\n\t\t\t\t\t\tloadedInputs.add(loader);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.warn(\"Failed to load code for plugin: {}\", plugin, e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tloadedInputs.addAll(customCodeLoaders);\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tLOG.debug(\"Loaded using {} inputs plugin in {} ms\", loadedInputs.size(), System.currentTimeMillis() - start);\n\t\t}\n\t}\n\n\tprivate void reset() {\n\t\tunloadPlugins();\n\t\troot = null;\n\t\tclasses = null;\n\t\tresources = null;\n\t\tevents.reset();\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\treset();\n\t\tcloseAll(loadedInputs);\n\t\tcloseAll(customCodeLoaders);\n\t\tcloseAll(customResourcesLoaders);\n\t\tcloseAll(closeableList);\n\t\tFileUtils.deleteDirIfExists(args.getFilesGetter().getTempDir());\n\t\targs.close();\n\t\tFileUtils.clearTempRootDir();\n\t}\n\n\tprivate void closeAll(List<? extends Closeable> list) {\n\t\ttry {\n\t\t\tfor (Closeable closeable : list) {\n\t\t\t\ttry {\n\t\t\t\t\tcloseable.close();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.warn(\"Fail to close '{}'\", closeable, e);\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tlist.clear();\n\t\t}\n\t}\n\n\tprivate void loadPlugins() {\n\t\tpluginManager.providesSuggestion(\"java-input\", args.isUseDxInput() ? \"java-convert\" : \"java-input\");\n\t\tpluginManager.load(args.getPluginLoader());\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tLOG.debug(\"Resolved plugins: {}\", pluginManager.getResolvedPluginContexts());\n\t\t}\n\t\tpluginManager.initResolved();\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tList<String> passes = customPasses.values().stream().flatMap(Collection::stream)\n\t\t\t\t\t.map(p -> p.getInfo().getName()).collect(Collectors.toList());\n\t\t\tLOG.debug(\"Loaded custom passes: {} {}\", passes.size(), passes);\n\t\t}\n\t}\n\n\tprivate void unloadPlugins() {\n\t\tpluginManager.unloadResolved();\n\t}\n\n\tprivate void loadFinished() {\n\t\tLOG.debug(\"Load finished\");\n\t\tList<JadxPass> list = customPasses.get(JadxAfterLoadPass.TYPE);\n\t\tif (list != null) {\n\t\t\tfor (JadxPass pass : list) {\n\t\t\t\t((JadxAfterLoadPass) pass).init(this);\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tpublic void registerPlugin(JadxPlugin plugin) {\n\t\tpluginManager.register(plugin);\n\t}\n\n\tpublic static String getVersion() {\n\t\treturn Jadx.getVersion();\n\t}\n\n\tpublic void save() {\n\t\tsave(!args.isSkipSources(), !args.isSkipResources());\n\t}\n\n\tpublic interface ProgressListener {\n\t\tvoid progress(long done, long total);\n\t}\n\n\t@SuppressWarnings(\"BusyWait\")\n\tpublic void save(int intervalInMillis, ProgressListener listener) {\n\t\ttry {\n\t\t\tITaskExecutor tasks = getSaveTaskExecutor();\n\t\t\ttasks.execute();\n\t\t\tlong total = tasks.getTasksCount();\n\t\t\twhile (tasks.isRunning()) {\n\t\t\t\tlistener.progress(tasks.getProgress(), total);\n\t\t\t\tThread.sleep(intervalInMillis);\n\t\t\t}\n\t\t} catch (InterruptedException e) {\n\t\t\tLOG.error(\"Save interrupted\", e);\n\t\t\tThread.currentThread().interrupt();\n\t\t}\n\t}\n\n\tpublic void saveSources() {\n\t\tsave(true, false);\n\t}\n\n\tpublic void saveResources() {\n\t\tsave(false, true);\n\t}\n\n\tprivate void save(boolean saveSources, boolean saveResources) {\n\t\tITaskExecutor executor = getSaveTasks(saveSources, saveResources);\n\t\texecutor.execute();\n\t\texecutor.awaitTermination();\n\t}\n\n\tpublic ITaskExecutor getSaveTaskExecutor() {\n\t\treturn getSaveTasks(!args.isSkipSources(), !args.isSkipResources());\n\t}\n\n\t@Deprecated(forRemoval = true)\n\tpublic ExecutorService getSaveExecutor() {\n\t\tITaskExecutor executor = getSaveTaskExecutor();\n\t\texecutor.execute();\n\t\treturn executor.getInternalExecutor();\n\t}\n\n\t@Deprecated(forRemoval = true)\n\tpublic List<Runnable> getSaveTasks() {\n\t\treturn Collections.singletonList(this::save);\n\t}\n\n\tprivate TaskExecutor getSaveTasks(boolean saveSources, boolean saveResources) {\n\t\tif (root == null) {\n\t\t\tthrow new JadxRuntimeException(\"No loaded files\");\n\t\t}\n\t\tOutDirs outDirs;\n\t\tExportGradle gradleExport;\n\t\tif (args.getExportGradleType() != null) {\n\t\t\tgradleExport = new ExportGradle(root, args.getOutDir(), getResources());\n\t\t\toutDirs = gradleExport.init();\n\t\t} else {\n\t\t\tgradleExport = null;\n\t\t\toutDirs = new OutDirs(args.getOutDirSrc(), args.getOutDirRes());\n\t\t\toutDirs.makeDirs();\n\t\t}\n\n\t\tTaskExecutor executor = new TaskExecutor();\n\t\texecutor.setThreadsCount(args.getThreadsCount());\n\t\tif (saveResources) {\n\t\t\t// save resources first because decompilation can stop or fail\n\t\t\tappendResourcesSaveTasks(executor, outDirs.getResOutDir());\n\t\t}\n\t\tif (saveSources) {\n\t\t\tappendSourcesSave(executor, outDirs.getSrcOutDir());\n\t\t}\n\t\tif (gradleExport != null) {\n\t\t\texecutor.addSequentialTask(gradleExport::generateGradleFiles);\n\t\t}\n\t\treturn executor;\n\t}\n\n\tprivate void appendResourcesSaveTasks(ITaskExecutor executor, File outDir) {\n\t\tif (args.isSkipFilesSave()) {\n\t\t\treturn;\n\t\t}\n\t\t// process AndroidManifest.xml first to load complete resource ids table\n\t\tfor (ResourceFile resourceFile : getResources()) {\n\t\t\tif (resourceFile.getType() == ResourceType.MANIFEST) {\n\t\t\t\tnew ResourcesSaver(this, outDir, resourceFile).run();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tSet<String> inputFileNames = args.getInputFiles().stream()\n\t\t\t\t.map(File::getAbsolutePath)\n\t\t\t\t.collect(Collectors.toSet());\n\t\tSet<String> codeSources = collectCodeSources();\n\n\t\tList<Runnable> tasks = new ArrayList<>();\n\t\tfor (ResourceFile resourceFile : getResources()) {\n\t\t\tResourceType resType = resourceFile.getType();\n\t\t\tif (resType == ResourceType.MANIFEST) {\n\t\t\t\t// already processed\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString resOriginalName = resourceFile.getOriginalName();\n\t\t\tif (resType != ResourceType.ARSC && inputFileNames.contains(resOriginalName)) {\n\t\t\t\t// ignore resource made from an input file\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (codeSources.contains(resOriginalName)) {\n\t\t\t\t// don't output code source resources (.dex, .class, etc)\n\t\t\t\t// do not trust file extensions, use only sources set as class inputs\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttasks.add(new ResourcesSaver(this, outDir, resourceFile));\n\t\t}\n\t\texecutor.addParallelTasks(tasks);\n\t}\n\n\tprivate Set<String> collectCodeSources() {\n\t\tSet<String> set = new HashSet<>();\n\t\tfor (ClassNode cls : root.getClasses(true)) {\n\t\t\tif (cls.getClsData() == null) {\n\t\t\t\t// exclude synthetic classes\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString inputFileName = cls.getInputFileName();\n\t\t\tif (inputFileName.endsWith(\".class\")) {\n\t\t\t\t// cut .class name to get source .jar file\n\t\t\t\t// current template: \"<optional input files>:<.jar>:<full class name>\"\n\t\t\t\t// TODO: add property to set file name or reference to resource name\n\t\t\t\tint endIdx = inputFileName.lastIndexOf(':');\n\t\t\t\tif (endIdx != -1) {\n\t\t\t\t\tint startIdx = inputFileName.lastIndexOf(':', endIdx - 1) + 1;\n\t\t\t\t\tinputFileName = inputFileName.substring(startIdx, endIdx);\n\t\t\t\t}\n\t\t\t}\n\t\t\tset.add(inputFileName);\n\t\t}\n\t\treturn set;\n\t}\n\n\tprivate void appendSourcesSave(ITaskExecutor executor, File outDir) {\n\t\tList<JavaClass> classes = getClasses();\n\t\tList<JavaClass> processQueue = filterClasses(classes);\n\t\tList<List<JavaClass>> batches;\n\t\ttry {\n\t\t\tbatches = decompileScheduler.buildBatches(processQueue);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Decompilation batches build failed\", e);\n\t\t}\n\t\tList<Runnable> decompileTasks = new ArrayList<>(batches.size());\n\t\tfor (List<JavaClass> decompileBatch : batches) {\n\t\t\tdecompileTasks.add(() -> {\n\t\t\t\tfor (JavaClass cls : decompileBatch) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tClassNode clsNode = cls.getClassNode();\n\t\t\t\t\t\tICodeInfo code = clsNode.getCode();\n\t\t\t\t\t\tSaveCode.save(outDir, clsNode, code);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLOG.error(\"Error saving class: {}\", cls, e);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\t\t}\n\t\texecutor.addParallelTasks(decompileTasks);\n\t}\n\n\tprivate List<JavaClass> filterClasses(List<JavaClass> classes) {\n\t\tPredicate<String> classFilter = args.getClassFilter();\n\t\tList<JavaClass> list = new ArrayList<>(classes.size());\n\t\tfor (JavaClass cls : classes) {\n\t\t\tClassNode clsNode = cls.getClassNode();\n\t\t\tif (clsNode.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (classFilter != null && !classFilter.test(clsNode.getClassInfo().getFullName())) {\n\t\t\t\tif (!args.isIncludeDependencies()) {\n\t\t\t\t\tclsNode.add(AFlag.DONT_GENERATE);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlist.add(cls);\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic synchronized List<JavaClass> getClasses() {\n\t\tif (root == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tif (classes == null) {\n\t\t\tList<ClassNode> classNodeList = root.getClasses();\n\t\t\tList<JavaClass> clsList = new ArrayList<>(classNodeList.size());\n\t\t\tfor (ClassNode classNode : classNodeList) {\n\t\t\t\tif (!classNode.contains(AFlag.DONT_GENERATE) && !classNode.isInner()) {\n\t\t\t\t\tclsList.add(convertClassNode(classNode));\n\t\t\t\t}\n\t\t\t}\n\t\t\tclasses = Collections.unmodifiableList(clsList);\n\t\t}\n\t\treturn classes;\n\t}\n\n\tpublic List<JavaClass> getClassesWithInners() {\n\t\treturn Utils.collectionMap(root.getClasses(), this::convertClassNode);\n\t}\n\n\tpublic synchronized List<ResourceFile> getResources() {\n\t\tif (resources == null) {\n\t\t\tif (root == null) {\n\t\t\t\treturn Collections.emptyList();\n\t\t\t}\n\t\t\tresources = resourcesLoader.load(root);\n\t\t}\n\t\treturn resources;\n\t}\n\n\tpublic List<JavaPackage> getPackages() {\n\t\treturn Utils.collectionMap(root.getPackages(), this::convertPackageNode);\n\t}\n\n\tpublic int getErrorsCount() {\n\t\tif (root == null) {\n\t\t\treturn 0;\n\t\t}\n\t\treturn root.getErrorsCounter().getErrorCount();\n\t}\n\n\tpublic int getWarnsCount() {\n\t\tif (root == null) {\n\t\t\treturn 0;\n\t\t}\n\t\treturn root.getErrorsCounter().getWarnsCount();\n\t}\n\n\tpublic void printErrorsReport() {\n\t\tif (root == null) {\n\t\t\treturn;\n\t\t}\n\t\troot.getClsp().printMissingClasses();\n\t\troot.getErrorsCounter().printReport();\n\t}\n\n\t/**\n\t * Internal API. Not Stable!\n\t */\n\t@ApiStatus.Internal\n\tpublic RootNode getRoot() {\n\t\treturn root;\n\t}\n\n\t/**\n\t * Get JavaClass by ClassNode without loading and decompilation\n\t */\n\t@ApiStatus.Internal\n\tsynchronized JavaClass convertClassNode(ClassNode cls) {\n\t\tJavaClass javaClass = cls.getJavaNode();\n\t\tif (javaClass == null) {\n\t\t\tjavaClass = cls.isInner()\n\t\t\t\t\t? new JavaClass(cls, convertClassNode(cls.getParentClass()))\n\t\t\t\t\t: new JavaClass(cls, this);\n\t\t\tcls.setJavaNode(javaClass);\n\t\t}\n\t\treturn javaClass;\n\t}\n\n\t@ApiStatus.Internal\n\tsynchronized JavaField convertFieldNode(FieldNode fld) {\n\t\tJavaField javaField = fld.getJavaNode();\n\t\tif (javaField == null) {\n\t\t\tJavaClass parentCls = convertClassNode(fld.getParentClass());\n\t\t\tjavaField = new JavaField(parentCls, fld);\n\t\t\tfld.setJavaNode(javaField);\n\t\t}\n\t\treturn javaField;\n\t}\n\n\t@ApiStatus.Internal\n\tsynchronized JavaMethod convertMethodNode(MethodNode mth) {\n\t\tJavaMethod javaMethod = mth.getJavaNode();\n\t\tif (javaMethod == null) {\n\t\t\tjavaMethod = new JavaMethod(convertClassNode(mth.getParentClass()), mth);\n\t\t\tmth.setJavaNode(javaMethod);\n\t\t}\n\t\treturn javaMethod;\n\t}\n\n\t@ApiStatus.Internal\n\tsynchronized JavaPackage convertPackageNode(PackageNode pkg) {\n\t\tJavaPackage foundPkg = pkg.getJavaNode();\n\t\tif (foundPkg != null) {\n\t\t\treturn foundPkg;\n\t\t}\n\t\tList<JavaClass> clsList = Utils.collectionMap(pkg.getClasses(), this::convertClassNode);\n\t\tList<JavaClass> clsListNoDup = Utils.collectionMap(pkg.getClassesNoDup(), this::convertClassNode);\n\t\tint subPkgsCount = pkg.getSubPackages().size();\n\t\tList<JavaPackage> subPkgs = subPkgsCount == 0 ? Collections.emptyList() : new ArrayList<>(subPkgsCount);\n\t\tJavaPackage javaPkg = new JavaPackage(pkg, clsList, clsListNoDup, subPkgs);\n\t\tif (subPkgsCount != 0) {\n\t\t\t// add subpackages after parent to avoid endless recursion\n\t\t\tfor (PackageNode subPackage : pkg.getSubPackages()) {\n\t\t\t\tsubPkgs.add(convertPackageNode(subPackage));\n\t\t\t}\n\t\t}\n\t\tpkg.setJavaNode(javaPkg);\n\t\treturn javaPkg;\n\t}\n\n\t@Nullable\n\tpublic JavaClass searchJavaClassByOrigFullName(String fullName) {\n\t\treturn getRoot().getClasses().stream()\n\t\t\t\t.filter(cls -> cls.getClassInfo().getFullName().equals(fullName))\n\t\t\t\t.findFirst()\n\t\t\t\t.map(this::convertClassNode)\n\t\t\t\t.orElse(null);\n\t}\n\n\t@Nullable\n\tpublic ClassNode searchClassNodeByOrigFullName(String fullName) {\n\t\treturn getRoot().getClasses().stream()\n\t\t\t\t.filter(cls -> cls.getClassInfo().getFullName().equals(fullName))\n\t\t\t\t.findFirst()\n\t\t\t\t.orElse(null);\n\t}\n\n\t// returns parent if class contains DONT_GENERATE flag.\n\t@Nullable\n\tpublic JavaClass searchJavaClassOrItsParentByOrigFullName(String fullName) {\n\t\tClassNode node = getRoot().getClasses().stream()\n\t\t\t\t.filter(cls -> cls.getClassInfo().getFullName().equals(fullName))\n\t\t\t\t.findFirst()\n\t\t\t\t.orElse(null);\n\t\tif (node != null) {\n\t\t\tif (node.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\treturn convertClassNode(node.getTopParentClass());\n\t\t\t} else {\n\t\t\t\treturn convertClassNode(node);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Nullable\n\tpublic JavaClass searchJavaClassByAliasFullName(String fullName) {\n\t\treturn getRoot().getClasses().stream()\n\t\t\t\t.filter(cls -> cls.getClassInfo().getAliasFullName().equals(fullName))\n\t\t\t\t.findFirst()\n\t\t\t\t.map(this::convertClassNode)\n\t\t\t\t.orElse(null);\n\t}\n\n\t@Nullable\n\tpublic JavaNode getJavaNodeByRef(ICodeNodeRef ann) {\n\t\treturn getJavaNodeByCodeAnnotation(null, ann);\n\t}\n\n\t@Nullable\n\tpublic JavaNode getJavaNodeByCodeAnnotation(@Nullable ICodeInfo codeInfo, @Nullable ICodeAnnotation ann) {\n\t\tif (ann == null) {\n\t\t\treturn null;\n\t\t}\n\t\tswitch (ann.getAnnType()) {\n\t\t\tcase CLASS:\n\t\t\t\treturn convertClassNode((ClassNode) ann);\n\t\t\tcase METHOD:\n\t\t\t\treturn convertMethodNode((MethodNode) ann);\n\t\t\tcase FIELD:\n\t\t\t\treturn convertFieldNode((FieldNode) ann);\n\t\t\tcase PKG:\n\t\t\t\treturn convertPackageNode((PackageNode) ann);\n\t\t\tcase DECLARATION:\n\t\t\t\treturn getJavaNodeByCodeAnnotation(codeInfo, ((NodeDeclareRef) ann).getNode());\n\t\t\tcase VAR:\n\t\t\t\treturn resolveVarNode((VarNode) ann);\n\t\t\tcase VAR_REF:\n\t\t\t\treturn resolveVarRef(codeInfo, (VarRef) ann);\n\t\t\tcase OFFSET:\n\t\t\t\t// offset annotation don't have java node object\n\t\t\t\treturn null;\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unknown annotation type: \" + ann.getAnnType() + \", class: \" + ann.getClass());\n\t\t}\n\t}\n\n\tprivate JavaVariable resolveVarNode(VarNode varNode) {\n\t\tJavaMethod javaNode = convertMethodNode(varNode.getMth());\n\t\treturn new JavaVariable(javaNode, varNode);\n\t}\n\n\t@Nullable\n\tprivate JavaVariable resolveVarRef(ICodeInfo codeInfo, VarRef varRef) {\n\t\tif (codeInfo == null) {\n\t\t\tthrow new JadxRuntimeException(\"Missing code info for resolve VarRef: \" + varRef);\n\t\t}\n\t\tICodeAnnotation varNodeAnn = codeInfo.getCodeMetadata().getAt(varRef.getRefPos());\n\t\tif (varNodeAnn != null && varNodeAnn.getAnnType() == ICodeAnnotation.AnnType.DECLARATION) {\n\t\t\tICodeNodeRef nodeRef = ((NodeDeclareRef) varNodeAnn).getNode();\n\t\t\tif (nodeRef.getAnnType() == ICodeAnnotation.AnnType.VAR) {\n\t\t\t\treturn resolveVarNode((VarNode) nodeRef);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tList<JavaNode> convertNodes(Collection<? extends ICodeNodeRef> nodesList) {\n\t\treturn nodesList.stream()\n\t\t\t\t.map(this::getJavaNodeByRef)\n\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\t@Nullable\n\tpublic JavaNode getJavaNodeAtPosition(ICodeInfo codeInfo, int pos) {\n\t\tICodeAnnotation ann = codeInfo.getCodeMetadata().getAt(pos);\n\t\treturn getJavaNodeByCodeAnnotation(codeInfo, ann);\n\t}\n\n\t@Nullable\n\tpublic JavaNode getClosestJavaNode(ICodeInfo codeInfo, int pos) {\n\t\tICodeAnnotation ann = codeInfo.getCodeMetadata().getClosestUp(pos);\n\t\treturn getJavaNodeByCodeAnnotation(codeInfo, ann);\n\t}\n\n\t@Nullable\n\tpublic JavaNode getEnclosingNode(ICodeInfo codeInfo, int pos) {\n\t\tICodeNodeRef obj = codeInfo.getCodeMetadata().getNodeAt(pos);\n\t\tif (obj == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn getJavaNodeByRef(obj);\n\t}\n\n\tpublic void reloadCodeData() {\n\t\troot.notifyCodeDataListeners();\n\t}\n\n\tpublic JadxArgs getArgs() {\n\t\treturn args;\n\t}\n\n\tpublic JadxPluginManager getPluginManager() {\n\t\treturn pluginManager;\n\t}\n\n\tpublic IDecompileScheduler getDecompileScheduler() {\n\t\treturn decompileScheduler;\n\t}\n\n\tpublic IJadxEvents events() {\n\t\treturn events;\n\t}\n\n\tpublic void setEventsImpl(IJadxEvents eventsImpl) {\n\t\tthis.events = eventsImpl;\n\t}\n\n\tpublic void addCustomCodeLoader(ICodeLoader customCodeLoader) {\n\t\tcustomCodeLoaders.add(customCodeLoader);\n\t}\n\n\tpublic List<ICodeLoader> getCustomCodeLoaders() {\n\t\treturn customCodeLoaders;\n\t}\n\n\tpublic void addCustomResourcesLoader(CustomResourcesLoader loader) {\n\t\tif (customResourcesLoaders.contains(loader)) {\n\t\t\treturn;\n\t\t}\n\t\tcustomResourcesLoaders.add(loader);\n\t}\n\n\tpublic List<CustomResourcesLoader> getCustomResourcesLoaders() {\n\t\treturn customResourcesLoaders;\n\t}\n\n\tpublic void addCustomPass(JadxPass pass) {\n\t\tcustomPasses.computeIfAbsent(pass.getPassType(), l -> new ArrayList<>()).add(pass);\n\t}\n\n\tpublic ResourcesLoader getResourcesLoader() {\n\t\treturn resourcesLoader;\n\t}\n\n\tpublic ZipReader getZipReader() {\n\t\treturn zipReader;\n\t}\n\n\tpublic void addCloseable(Closeable closeable) {\n\t\tcloseableList.add(closeable);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"jadx decompiler \" + getVersion();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/JavaClass.java",
    "content": "package jadx.api;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.ApiStatus;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.AnonymousClassAttr;\nimport jadx.core.dex.attributes.nodes.InlinedAttr;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.ListUtils;\n\npublic final class JavaClass implements JavaNode {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JavaClass.class);\n\n\tprivate final JadxDecompiler decompiler;\n\tprivate final ClassNode cls;\n\tprivate final JavaClass parent;\n\n\tprivate List<JavaClass> innerClasses = Collections.emptyList();\n\tprivate List<JavaClass> inlinedClasses = Collections.emptyList();\n\tprivate List<JavaField> fields = Collections.emptyList();\n\tprivate List<JavaMethod> methods = Collections.emptyList();\n\tprivate boolean listsLoaded;\n\n\tJavaClass(ClassNode classNode, JadxDecompiler decompiler) {\n\t\tthis.decompiler = decompiler;\n\t\tthis.cls = classNode;\n\t\tthis.parent = null;\n\t}\n\n\t/**\n\t * Inner classes constructor\n\t */\n\tJavaClass(ClassNode classNode, JavaClass parent) {\n\t\tthis.decompiler = null;\n\t\tthis.cls = classNode;\n\t\tthis.parent = parent;\n\t}\n\n\tpublic String getCode() {\n\t\treturn getCodeInfo().getCodeStr();\n\t}\n\n\tpublic @NotNull ICodeInfo getCodeInfo() {\n\t\tICodeInfo code = load();\n\t\tif (code != null) {\n\t\t\treturn code;\n\t\t}\n\t\treturn cls.decompile();\n\t}\n\n\tpublic void decompile() {\n\t\tload();\n\t}\n\n\tpublic synchronized ICodeInfo reload() {\n\t\tlistsLoaded = false;\n\t\treturn cls.reloadCode();\n\t}\n\n\tpublic void unload() {\n\t\tlistsLoaded = false;\n\t\tcls.unloadCode();\n\t}\n\n\tpublic boolean isNoCode() {\n\t\treturn cls.contains(AFlag.DONT_GENERATE);\n\t}\n\n\tpublic boolean isInner() {\n\t\treturn cls.isInner();\n\t}\n\n\tpublic synchronized String getSmali() {\n\t\treturn cls.getDisassembledCode();\n\t}\n\n\t@Override\n\tpublic boolean isOwnCodeAnnotation(ICodeAnnotation ann) {\n\t\tif (ann.getAnnType() == ICodeAnnotation.AnnType.CLASS) {\n\t\t\treturn ann.equals(cls);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic ICodeNodeRef getCodeNodeRef() {\n\t\treturn cls;\n\t}\n\n\t/**\n\t * Internal API. Not Stable!\n\t */\n\t@ApiStatus.Internal\n\tpublic ClassNode getClassNode() {\n\t\treturn cls;\n\t}\n\n\t/**\n\t * Decompile class and loads internal lists of fields, methods, etc.\n\t * Do nothing if already loaded.\n\t *\n\t * @return code info if decompilation was executed, null otherwise\n\t */\n\tprivate synchronized @Nullable ICodeInfo load() {\n\t\tif (listsLoaded) {\n\t\t\treturn null;\n\t\t}\n\t\tICodeInfo code;\n\t\tif (cls.getState().isProcessComplete()) {\n\t\t\t// already decompiled -> class internals loaded\n\t\t\tcode = null;\n\t\t} else {\n\t\t\tcode = cls.decompile();\n\t\t}\n\t\tloadLists();\n\t\treturn code;\n\t}\n\n\tprivate void loadLists() {\n\t\tlistsLoaded = true;\n\t\tJadxDecompiler rootDecompiler = getRootDecompiler();\n\t\tint inClsCount = cls.getInnerClasses().size();\n\t\tif (inClsCount != 0) {\n\t\t\tList<JavaClass> list = new ArrayList<>(inClsCount);\n\t\t\tfor (ClassNode inner : cls.getInnerClasses()) {\n\t\t\t\tif (!inner.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\t\tJavaClass javaClass = rootDecompiler.convertClassNode(inner);\n\t\t\t\t\tjavaClass.loadLists();\n\t\t\t\t\tlist.add(javaClass);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.innerClasses = Collections.unmodifiableList(list);\n\t\t}\n\t\tint inlinedClsCount = cls.getInlinedClasses().size();\n\t\tif (inlinedClsCount != 0) {\n\t\t\tList<JavaClass> list = new ArrayList<>(inlinedClsCount);\n\t\t\tfor (ClassNode inner : cls.getInlinedClasses()) {\n\t\t\t\tJavaClass javaClass = rootDecompiler.convertClassNode(inner);\n\t\t\t\tjavaClass.loadLists();\n\t\t\t\tlist.add(javaClass);\n\t\t\t}\n\t\t\tthis.inlinedClasses = Collections.unmodifiableList(list);\n\t\t}\n\n\t\tint fieldsCount = cls.getFields().size();\n\t\tif (fieldsCount != 0) {\n\t\t\tList<JavaField> flds = new ArrayList<>(fieldsCount);\n\t\t\tfor (FieldNode f : cls.getFields()) {\n\t\t\t\tif (!f.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\t\tflds.add(rootDecompiler.convertFieldNode(f));\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.fields = Collections.unmodifiableList(flds);\n\t\t}\n\n\t\tint methodsCount = cls.getMethods().size();\n\t\tif (methodsCount != 0) {\n\t\t\tList<JavaMethod> mths = new ArrayList<>(methodsCount);\n\t\t\tfor (MethodNode m : cls.getMethods()) {\n\t\t\t\tif (!m.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\t\tmths.add(rootDecompiler.convertMethodNode(m));\n\t\t\t\t}\n\t\t\t}\n\t\t\tmths.sort(Comparator.comparing(JavaMethod::getName));\n\t\t\tthis.methods = Collections.unmodifiableList(mths);\n\t\t}\n\t}\n\n\tJadxDecompiler getRootDecompiler() {\n\t\tif (parent != null) {\n\t\t\treturn parent.getRootDecompiler();\n\t\t}\n\t\treturn decompiler;\n\t}\n\n\tpublic ICodeAnnotation getAnnotationAt(int pos) {\n\t\treturn getCodeInfo().getCodeMetadata().getAt(pos);\n\t}\n\n\tpublic Map<Integer, JavaNode> getUsageMap() {\n\t\tMap<Integer, ICodeAnnotation> map = getCodeInfo().getCodeMetadata().getAsMap();\n\t\tif (map.isEmpty() || decompiler == null) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tMap<Integer, JavaNode> resultMap = new HashMap<>(map.size());\n\t\tfor (Map.Entry<Integer, ICodeAnnotation> entry : map.entrySet()) {\n\t\t\tint codePosition = entry.getKey();\n\t\t\tICodeAnnotation obj = entry.getValue();\n\t\t\tif (obj instanceof ICodeNodeRef) {\n\t\t\t\tJavaNode node = getRootDecompiler().getJavaNodeByRef((ICodeNodeRef) obj);\n\t\t\t\tif (node != null) {\n\t\t\t\t\tresultMap.put(codePosition, node);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn resultMap;\n\t}\n\n\tpublic List<Integer> getUsePlacesFor(ICodeInfo codeInfo, JavaNode javaNode) {\n\t\tif (!codeInfo.hasMetadata()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<Integer> result = new ArrayList<>();\n\t\tcodeInfo.getCodeMetadata().searchDown(0, (pos, ann) -> {\n\t\t\tif (javaNode.isOwnCodeAnnotation(ann)) {\n\t\t\t\tresult.add(pos);\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic List<JavaNode> getUseIn() {\n\t\treturn getRootDecompiler().convertNodes(cls.getUseIn());\n\t}\n\n\tpublic Integer getSourceLine(int decompiledLine) {\n\t\treturn getCodeInfo().getCodeMetadata().getLineMapping().get(decompiledLine);\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn cls.getShortName();\n\t}\n\n\t@Override\n\tpublic String getFullName() {\n\t\treturn cls.getFullName();\n\t}\n\n\tpublic String getRawName() {\n\t\treturn cls.getRawName();\n\t}\n\n\tpublic String getPackage() {\n\t\treturn cls.getPackage();\n\t}\n\n\tpublic JavaPackage getJavaPackage() {\n\t\treturn cls.getPackageNode().getJavaNode();\n\t}\n\n\t@Override\n\tpublic JavaClass getDeclaringClass() {\n\t\treturn parent;\n\t}\n\n\tpublic JavaClass getOriginalTopParentClass() {\n\t\treturn parent == null ? this : parent.getOriginalTopParentClass();\n\t}\n\n\t/**\n\t * Return top parent class which contains code of this class.\n\t * Code parent can be different from original parent after move or inline\n\t *\n\t * @return this if already a top class\n\t */\n\t@Override\n\tpublic JavaClass getTopParentClass() {\n\t\tJavaClass codeParent = getCodeParent();\n\t\treturn codeParent == null ? this : codeParent.getTopParentClass();\n\t}\n\n\t/**\n\t * Return parent class which contains code of this class.\n\t * Code parent can be different for original parent after move or inline\n\t */\n\tpublic @Nullable JavaClass getCodeParent() {\n\t\tAnonymousClassAttr anonymousClsAttr = cls.get(AType.ANONYMOUS_CLASS);\n\t\tif (anonymousClsAttr != null) {\n\t\t\t// moved to usage class\n\t\t\treturn getRootDecompiler().convertClassNode(anonymousClsAttr.getOuterCls());\n\t\t}\n\t\tInlinedAttr inlinedAttr = cls.get(AType.INLINED);\n\t\tif (inlinedAttr != null) {\n\t\t\treturn getRootDecompiler().convertClassNode(inlinedAttr.getInlineCls());\n\t\t}\n\t\treturn parent;\n\t}\n\n\tpublic AccessInfo getAccessInfo() {\n\t\treturn cls.getAccessFlags();\n\t}\n\n\tpublic List<JavaClass> getInnerClasses() {\n\t\tload();\n\t\treturn innerClasses;\n\t}\n\n\tpublic List<JavaClass> getInlinedClasses() {\n\t\tload();\n\t\treturn inlinedClasses;\n\t}\n\n\tpublic List<JavaField> getFields() {\n\t\tload();\n\t\treturn fields;\n\t}\n\n\tpublic List<JavaMethod> getMethods() {\n\t\tload();\n\t\treturn methods;\n\t}\n\n\t@Nullable\n\tpublic JavaMethod searchMethodByShortId(String shortId) {\n\t\tMethodNode methodNode = cls.searchMethodByShortId(shortId);\n\t\tif (methodNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn getRootDecompiler().convertMethodNode(methodNode);\n\t}\n\n\tpublic List<JavaClass> getDependencies() {\n\t\tJadxDecompiler d = getRootDecompiler();\n\t\treturn ListUtils.map(cls.getDependencies(), d::convertClassNode);\n\t}\n\n\tpublic int getTotalDepsCount() {\n\t\treturn cls.getTotalDepsCount();\n\t}\n\n\t@Override\n\tpublic void removeAlias() {\n\t\tcls.removeAlias();\n\t}\n\n\t@Override\n\tpublic int getDefPos() {\n\t\treturn cls.getDefPosition();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\treturn this == o || o instanceof JavaClass && cls.equals(((JavaClass) o).cls);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn cls.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getFullName();\n\t}\n\n\t/**\n\t * Detect if calling load() would trigger a potentially expensive decompilation operation.\n\t */\n\tpublic boolean loadingWouldRequireDecompilation() {\n\t\tif (listsLoaded) {\n\t\t\t// lists are already poplulated, so it's safe regardless of the state of the class itself\n\t\t\treturn false;\n\t\t}\n\n\t\tif (cls.getState().isProcessComplete()) {\n\t\t\t// decompilation has already finished\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/JavaField.java",
    "content": "package jadx.api;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.ApiStatus;\n\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.FieldNode;\n\npublic final class JavaField implements JavaNode {\n\n\tprivate final FieldNode field;\n\tprivate final JavaClass parent;\n\n\tJavaField(JavaClass cls, FieldNode f) {\n\t\tthis.field = f;\n\t\tthis.parent = cls;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn field.getAlias();\n\t}\n\n\t@Override\n\tpublic String getFullName() {\n\t\treturn parent.getFullName() + '.' + getName();\n\t}\n\n\tpublic String getRawName() {\n\t\treturn field.getName();\n\t}\n\n\t@Override\n\tpublic JavaClass getDeclaringClass() {\n\t\treturn parent;\n\t}\n\n\t@Override\n\tpublic JavaClass getTopParentClass() {\n\t\treturn parent.getTopParentClass();\n\t}\n\n\tpublic AccessInfo getAccessFlags() {\n\t\treturn field.getAccessFlags();\n\t}\n\n\tpublic ArgType getType() {\n\t\treturn ArgType.tryToResolveClassAlias(field.root(), field.getType());\n\t}\n\n\t@Override\n\tpublic int getDefPos() {\n\t\treturn field.getDefPosition();\n\t}\n\n\t@Override\n\tpublic List<JavaNode> getUseIn() {\n\t\treturn getDeclaringClass().getRootDecompiler().convertNodes(field.getUseIn());\n\t}\n\n\t@Override\n\tpublic void removeAlias() {\n\t\tthis.field.getFieldInfo().removeAlias();\n\t}\n\n\t@Override\n\tpublic boolean isOwnCodeAnnotation(ICodeAnnotation ann) {\n\t\tif (ann.getAnnType() == ICodeAnnotation.AnnType.FIELD) {\n\t\t\treturn ann.equals(field);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic ICodeNodeRef getCodeNodeRef() {\n\t\treturn field;\n\t}\n\n\t/**\n\t * Internal API. Not Stable!\n\t */\n\t@ApiStatus.Internal\n\tpublic FieldNode getFieldNode() {\n\t\treturn field;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn field.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\treturn this == o || o instanceof JavaField && field.equals(((JavaField) o).field);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn field.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/JavaMethod.java",
    "content": "package jadx.api;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.ApiStatus;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.MethodOverrideAttr;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.Utils;\n\npublic final class JavaMethod implements JavaNode {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JavaMethod.class);\n\n\tprivate final MethodNode mth;\n\tprivate final JavaClass parent;\n\n\tJavaMethod(JavaClass cls, MethodNode m) {\n\t\tthis.parent = cls;\n\t\tthis.mth = m;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn mth.getAlias();\n\t}\n\n\t@Override\n\tpublic String getFullName() {\n\t\treturn mth.getMethodInfo().getFullName();\n\t}\n\n\t@Override\n\tpublic JavaClass getDeclaringClass() {\n\t\treturn parent;\n\t}\n\n\t@Override\n\tpublic JavaClass getTopParentClass() {\n\t\treturn parent.getTopParentClass();\n\t}\n\n\tpublic AccessInfo getAccessFlags() {\n\t\treturn mth.getAccessFlags();\n\t}\n\n\tpublic List<ArgType> getArguments() {\n\t\tList<ArgType> infoArgTypes = mth.getMethodInfo().getArgumentsTypes();\n\t\tif (infoArgTypes.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<ArgType> arguments = mth.getArgTypes();\n\t\treturn Utils.collectionMap(arguments,\n\t\t\t\ttype -> ArgType.tryToResolveClassAlias(mth.root(), type));\n\t}\n\n\tpublic ArgType getReturnType() {\n\t\tArgType retType = mth.getReturnType();\n\t\treturn ArgType.tryToResolveClassAlias(mth.root(), retType);\n\t}\n\n\t@Override\n\tpublic List<JavaNode> getUseIn() {\n\t\treturn getDeclaringClass().getRootDecompiler().convertNodes(mth.getUseIn());\n\t}\n\n\tpublic List<JavaNode> getUsed() {\n\t\treturn getDeclaringClass().getRootDecompiler().convertNodes(mth.getUsed());\n\t}\n\n\tpublic List<IMethodRef> getUnresolvedUsed() {\n\t\treturn mth.getUnresolvedUsed();\n\t}\n\n\tpublic boolean callsSelf() {\n\t\treturn mth.callsSelf();\n\t}\n\n\tpublic List<JavaMethod> getOverrideRelatedMethods() {\n\t\tMethodOverrideAttr ovrdAttr = mth.get(AType.METHOD_OVERRIDE);\n\t\tif (ovrdAttr == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tJadxDecompiler decompiler = getDeclaringClass().getRootDecompiler();\n\t\treturn ovrdAttr.getRelatedMthNodes()\n\t\t\t\t.stream()\n\t\t\t\t.map(decompiler::convertMethodNode)\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tpublic boolean isConstructor() {\n\t\treturn mth.getMethodInfo().isConstructor();\n\t}\n\n\tpublic boolean isClassInit() {\n\t\treturn mth.getMethodInfo().isClassInit();\n\t}\n\n\t@Override\n\tpublic int getDefPos() {\n\t\treturn mth.getDefPosition();\n\t}\n\n\tpublic String getCodeStr() {\n\t\treturn mth.getCodeStr();\n\t}\n\n\t@Override\n\tpublic void removeAlias() {\n\t\tthis.mth.getMethodInfo().removeAlias();\n\t}\n\n\t@Override\n\tpublic boolean isOwnCodeAnnotation(ICodeAnnotation ann) {\n\t\tif (ann.getAnnType() == ICodeAnnotation.AnnType.METHOD) {\n\t\t\treturn ann.equals(mth);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic ICodeNodeRef getCodeNodeRef() {\n\t\treturn mth;\n\t}\n\n\t/**\n\t * Internal API. Not Stable!\n\t */\n\t@ApiStatus.Internal\n\tpublic MethodNode getMethodNode() {\n\t\treturn mth;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn mth.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\treturn this == o || o instanceof JavaMethod && mth.equals(((JavaMethod) o).mth);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn mth.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/JavaNode.java",
    "content": "package jadx.api;\n\nimport java.util.List;\n\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeNodeRef;\n\npublic interface JavaNode {\n\n\tICodeNodeRef getCodeNodeRef();\n\n\tString getName();\n\n\tString getFullName();\n\n\tJavaClass getDeclaringClass();\n\n\tJavaClass getTopParentClass();\n\n\tint getDefPos();\n\n\tList<JavaNode> getUseIn();\n\n\tvoid removeAlias();\n\n\tboolean isOwnCodeAnnotation(ICodeAnnotation ann);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/JavaPackage.java",
    "content": "package jadx.api;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.ApiStatus.Internal;\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.core.dex.info.PackageInfo;\nimport jadx.core.dex.nodes.PackageNode;\n\npublic final class JavaPackage implements JavaNode, Comparable<JavaPackage> {\n\tprivate final PackageNode pkgNode;\n\tprivate final List<JavaClass> classes;\n\tprivate final List<JavaClass> clsListNoDup;\n\tprivate final List<JavaPackage> subPkgs;\n\n\tJavaPackage(PackageNode pkgNode, List<JavaClass> classes, List<JavaPackage> subPkgs) {\n\t\tthis(pkgNode, classes, classes, subPkgs);\n\t}\n\n\tJavaPackage(PackageNode pkgNode, List<JavaClass> classes, List<JavaClass> clsListNoDup, List<JavaPackage> subPkgs) {\n\t\tthis.pkgNode = pkgNode;\n\t\tthis.classes = classes;\n\t\tthis.clsListNoDup = clsListNoDup;\n\t\tthis.subPkgs = subPkgs;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn pkgNode.getAliasPkgInfo().getName();\n\t}\n\n\t@Override\n\tpublic String getFullName() {\n\t\treturn pkgNode.getAliasPkgInfo().getFullName();\n\t}\n\n\tpublic String getRawName() {\n\t\treturn pkgNode.getPkgInfo().getName();\n\t}\n\n\tpublic String getRawFullName() {\n\t\treturn pkgNode.getPkgInfo().getFullName();\n\t}\n\n\tpublic List<JavaPackage> getSubPackages() {\n\t\treturn subPkgs;\n\t}\n\n\tpublic List<JavaClass> getClasses() {\n\t\treturn classes;\n\t}\n\n\tpublic List<JavaClass> getClassesNoDup() {\n\t\treturn clsListNoDup;\n\t}\n\n\tpublic boolean isRoot() {\n\t\treturn pkgNode.isRoot();\n\t}\n\n\tpublic boolean isLeaf() {\n\t\treturn pkgNode.isLeaf();\n\t}\n\n\tpublic boolean isDefault() {\n\t\treturn getFullName().isEmpty();\n\t}\n\n\tpublic void rename(String alias) {\n\t\tpkgNode.rename(alias);\n\t}\n\n\t@Override\n\tpublic void removeAlias() {\n\t\tpkgNode.removeAlias();\n\t}\n\n\tpublic boolean isParentRenamed() {\n\t\tPackageInfo parent = pkgNode.getPkgInfo().getParentPkg();\n\t\tPackageInfo aliasParent = pkgNode.getAliasPkgInfo().getParentPkg();\n\t\treturn !Objects.equals(parent, aliasParent);\n\t}\n\n\tpublic boolean isDescendantOf(JavaPackage ancestor) {\n\t\tJavaPackage current = this;\n\t\twhile (current != null) {\n\t\t\tif (ancestor.equals(current)) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (current.getPkgNode().getParentPkg() == null) {\n\t\t\t\tcurrent = null;\n\t\t\t} else {\n\t\t\t\tcurrent = current.getPkgNode().getParentPkg().getJavaNode();\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic ICodeNodeRef getCodeNodeRef() {\n\t\treturn pkgNode;\n\t}\n\n\t@Internal\n\tpublic PackageNode getPkgNode() {\n\t\treturn pkgNode;\n\t}\n\n\t@Override\n\tpublic JavaClass getDeclaringClass() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic JavaClass getTopParentClass() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic int getDefPos() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic List<JavaNode> getUseIn() {\n\t\tList<JavaNode> list = new ArrayList<>();\n\t\taddUseIn(list);\n\t\treturn list;\n\t}\n\n\tpublic void addUseIn(List<JavaNode> list) {\n\t\tlist.addAll(classes);\n\t\tfor (JavaPackage subPkg : subPkgs) {\n\t\t\tsubPkg.addUseIn(list);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isOwnCodeAnnotation(ICodeAnnotation ann) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull JavaPackage o) {\n\t\treturn pkgNode.compareTo(o.pkgNode);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tJavaPackage that = (JavaPackage) o;\n\t\treturn pkgNode.equals(that.pkgNode);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn pkgNode.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn pkgNode.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/JavaVariable.java",
    "content": "package jadx.api;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.jetbrains.annotations.ApiStatus;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.metadata.annotations.VarNode;\nimport jadx.api.metadata.annotations.VarRef;\nimport jadx.core.dex.instructions.args.ArgType;\n\npublic class JavaVariable implements JavaNode {\n\tprivate final JavaMethod mth;\n\tprivate final VarNode varNode;\n\n\tpublic JavaVariable(JavaMethod mth, VarNode varNode) {\n\t\tthis.mth = mth;\n\t\tthis.varNode = varNode;\n\t}\n\n\tpublic JavaMethod getMth() {\n\t\treturn mth;\n\t}\n\n\tpublic int getReg() {\n\t\treturn varNode.getReg();\n\t}\n\n\tpublic int getSsa() {\n\t\treturn varNode.getSsa();\n\t}\n\n\t@Override\n\tpublic @Nullable String getName() {\n\t\treturn varNode.getName();\n\t}\n\n\t@Override\n\tpublic ICodeNodeRef getCodeNodeRef() {\n\t\treturn varNode;\n\t}\n\n\t@ApiStatus.Internal\n\tpublic VarNode getVarNode() {\n\t\treturn varNode;\n\t}\n\n\t@Override\n\tpublic String getFullName() {\n\t\treturn varNode.getType() + \" \" + varNode.getName() + \" (r\" + varNode.getReg() + \"v\" + varNode.getSsa() + \")\";\n\t}\n\n\tpublic ArgType getType() {\n\t\treturn ArgType.tryToResolveClassAlias(mth.getMethodNode().root(), varNode.getType());\n\t}\n\n\t@Override\n\tpublic JavaClass getDeclaringClass() {\n\t\treturn mth.getDeclaringClass();\n\t}\n\n\t@Override\n\tpublic JavaClass getTopParentClass() {\n\t\treturn mth.getTopParentClass();\n\t}\n\n\t@Override\n\tpublic int getDefPos() {\n\t\treturn varNode.getDefPosition();\n\t}\n\n\t@Override\n\tpublic List<JavaNode> getUseIn() {\n\t\treturn Collections.singletonList(mth);\n\t}\n\n\t@Override\n\tpublic void removeAlias() {\n\t\tvarNode.setName(null);\n\t}\n\n\t@Override\n\tpublic boolean isOwnCodeAnnotation(ICodeAnnotation ann) {\n\t\tif (ann.getAnnType() == ICodeAnnotation.AnnType.VAR_REF) {\n\t\t\tVarRef varRef = (VarRef) ann;\n\t\t\treturn varRef.getRefPos() == getDefPos();\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn varNode.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof JavaVariable)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn varNode.equals(((JavaVariable) o).varNode);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/ResourceFile.java",
    "content": "package jadx.api;\n\nimport java.io.File;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.deobf.FileTypeDetector;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.exceptions.JadxException;\nimport jadx.core.xmlgen.ResContainer;\nimport jadx.core.xmlgen.entry.ResourceEntry;\nimport jadx.zip.IZipEntry;\n\npublic class ResourceFile {\n\tprivate final JadxDecompiler decompiler;\n\tprivate final String name;\n\tprivate ResourceType type;\n\n\tprivate @Nullable IZipEntry zipEntry;\n\tprivate String deobfName;\n\n\tpublic static ResourceFile createResourceFile(JadxDecompiler decompiler, File file, ResourceType type) {\n\t\treturn new ResourceFile(decompiler, file.getAbsolutePath(), type);\n\t}\n\n\tpublic static ResourceFile createResourceFile(JadxDecompiler decompiler, String name, ResourceType type) {\n\t\tif (!decompiler.getArgs().getSecurity().isValidEntryName(name)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new ResourceFile(decompiler, name, type);\n\t}\n\n\tprotected ResourceFile(JadxDecompiler decompiler, String name, ResourceType type) {\n\t\tthis.decompiler = decompiler;\n\t\tthis.name = name;\n\t\tthis.type = type;\n\t}\n\n\tpublic String getOriginalName() {\n\t\treturn name;\n\t}\n\n\tpublic String getDeobfName() {\n\t\treturn deobfName != null ? deobfName : name;\n\t}\n\n\tpublic void setDeobfName(String resFullName) {\n\t\tthis.deobfName = resFullName;\n\t}\n\n\tpublic ResourceType getType() {\n\t\treturn type;\n\t}\n\n\tpublic ResContainer loadContent() {\n\t\treturn ResourcesLoader.loadContent(decompiler, this);\n\t}\n\n\tpublic boolean setAlias(ResourceEntry entry, boolean useHeaders) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"res/\").append(entry.getTypeName()).append(entry.getConfig());\n\t\tsb.append(\"/\").append(entry.getKeyName());\n\n\t\tif (useHeaders) {\n\t\t\ttry {\n\t\t\t\tint maxBytesToReadLimit = 4096;\n\t\t\t\tbyte[] bytes = ResourcesLoader.decodeStream(this, (size, is) -> {\n\t\t\t\t\tint bytesToRead;\n\t\t\t\t\tif (size > 0) {\n\t\t\t\t\t\tbytesToRead = (int) Math.min(size, maxBytesToReadLimit);\n\t\t\t\t\t} else if (size == 0) {\n\t\t\t\t\t\tbytesToRead = 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbytesToRead = maxBytesToReadLimit;\n\t\t\t\t\t}\n\t\t\t\t\tif (bytesToRead == 0) {\n\t\t\t\t\t\treturn new byte[0];\n\t\t\t\t\t}\n\t\t\t\t\treturn is.readNBytes(bytesToRead);\n\t\t\t\t});\n\n\t\t\t\tString fileExtension = FileTypeDetector.detectFileExtension(bytes);\n\t\t\t\tif (!StringUtils.isEmpty(fileExtension)) {\n\t\t\t\t\tsb.append(fileExtension);\n\t\t\t\t} else {\n\t\t\t\t\tsb.append(getExtFromName(name));\n\t\t\t\t}\n\t\t\t} catch (JadxException ignored) {\n\t\t\t}\n\t\t} else {\n\t\t\tsb.append(getExtFromName(name));\n\t\t}\n\t\tString alias = sb.toString();\n\t\tif (!alias.equals(name)) {\n\t\t\tsetDeobfName(alias);\n\t\t\ttype = ResourceType.getFileType(alias);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate String getExtFromName(String name) {\n\t\t// the image .9.png extension always saved, when resource shrinking by aapt2\n\t\tif (name.contains(\".9.png\")) {\n\t\t\treturn \".9.png\";\n\t\t}\n\n\t\tint lastDot = name.lastIndexOf('.');\n\t\tif (lastDot != -1) {\n\t\t\treturn name.substring(lastDot);\n\t\t}\n\n\t\treturn \"\";\n\t}\n\n\tpublic @Nullable IZipEntry getZipEntry() {\n\t\treturn zipEntry;\n\t}\n\n\tvoid setZipEntry(@Nullable IZipEntry zipEntry) {\n\t\tthis.zipEntry = zipEntry;\n\t}\n\n\tpublic JadxDecompiler getDecompiler() {\n\t\treturn decompiler;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ResourceFile{name='\" + name + '\\'' + \", type=\" + type + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/ResourceFileContainer.java",
    "content": "package jadx.api;\n\nimport jadx.core.xmlgen.ResContainer;\n\npublic class ResourceFileContainer extends ResourceFile {\n\tprivate final ResContainer container;\n\n\tpublic ResourceFileContainer(String name, ResourceType type, ResContainer container) {\n\t\tsuper(null, name, type);\n\t\tthis.container = container;\n\t}\n\n\t@Override\n\tpublic ResContainer loadContent() {\n\t\treturn container;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/ResourceFileContent.java",
    "content": "package jadx.api;\n\nimport jadx.core.xmlgen.ResContainer;\n\npublic class ResourceFileContent extends ResourceFile {\n\tprivate final ICodeInfo content;\n\n\tpublic ResourceFileContent(String name, ResourceType type, ICodeInfo content) {\n\t\tsuper(null, name, type);\n\t\tthis.content = content;\n\t}\n\n\t@Override\n\tpublic ResContainer loadContent() {\n\t\treturn ResContainer.textResource(getDeobfName(), content);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/ResourceType.java",
    "content": "package jadx.api;\n\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport jadx.api.resources.ResourceContentType;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.api.resources.ResourceContentType.CONTENT_BINARY;\nimport static jadx.api.resources.ResourceContentType.CONTENT_TEXT;\nimport static jadx.api.resources.ResourceContentType.CONTENT_UNKNOWN;\n\npublic enum ResourceType {\n\tCODE(CONTENT_BINARY, \".dex\", \".jar\", \".class\"),\n\tXML(CONTENT_TEXT, \".xml\"),\n\tARSC(CONTENT_TEXT, \".arsc\"),\n\tAPK(CONTENT_BINARY, \".apk\", \".apkm\", \".apks\"),\n\tFONT(CONTENT_BINARY, \".ttf\", \".ttc\", \".otf\"),\n\tIMG(CONTENT_BINARY, \".png\", \".gif\", \".jpg\", \".jpeg\", \".webp\", \".bmp\", \".tiff\"),\n\tARCHIVE(CONTENT_BINARY, \".zip\", \".rar\", \".7zip\", \".7z\", \".arj\", \".tar\", \".gzip\", \".bzip\", \".bzip2\", \".cab\", \".cpio\", \".ar\", \".gz\",\n\t\t\t\".tgz\", \".bz2\"),\n\tVIDEOS(CONTENT_BINARY, \".mp4\", \".mkv\", \".webm\", \".avi\", \".flv\", \".3gp\"),\n\tSOUNDS(CONTENT_BINARY, \".aac\", \".ogg\", \".opus\", \".mp3\", \".wav\", \".wma\", \".mid\", \".midi\"),\n\tJSON(CONTENT_TEXT, \".json\"),\n\tTEXT(CONTENT_TEXT, \".txt\", \".ini\", \".conf\", \".yaml\", \".properties\", \".js\", \".java\", \".kt\", \".md\"),\n\tHTML(CONTENT_TEXT, \".html\", \".htm\"),\n\tLIB(CONTENT_BINARY, \".so\"),\n\tMANIFEST(CONTENT_TEXT),\n\tUNKNOWN_BIN(CONTENT_BINARY, \".bin\"),\n\tUNKNOWN(CONTENT_UNKNOWN);\n\n\tprivate final ResourceContentType contentType;\n\tprivate final String[] exts;\n\n\tResourceType(ResourceContentType contentType, String... exts) {\n\t\tthis.contentType = contentType;\n\t\tthis.exts = exts;\n\t}\n\n\tpublic ResourceContentType getContentType() {\n\t\treturn contentType;\n\t}\n\n\tpublic String[] getExts() {\n\t\treturn exts;\n\t}\n\n\tprivate static final Map<String, ResourceType> EXT_MAP = new HashMap<>();\n\n\tstatic {\n\t\tfor (ResourceType type : ResourceType.values()) {\n\t\t\tfor (String ext : type.getExts()) {\n\t\t\t\tResourceType prev = EXT_MAP.put(ext, type);\n\t\t\t\tif (prev != null) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Duplicate extension in ResourceType: \" + ext);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static ResourceType getFileType(String fileName) {\n\t\tif (fileName.endsWith(\"/resources.pb\")) {\n\t\t\treturn ARSC;\n\t\t}\n\t\tint dot = fileName.lastIndexOf('.');\n\t\tif (dot != -1) {\n\t\t\tString ext = fileName.substring(dot).toLowerCase(Locale.ROOT);\n\t\t\tResourceType resType = EXT_MAP.get(ext);\n\t\t\tif (resType != null) {\n\t\t\t\tif (resType == XML && fileName.equals(\"AndroidManifest.xml\")) {\n\t\t\t\t\treturn MANIFEST;\n\t\t\t\t}\n\t\t\t\treturn resType;\n\t\t\t}\n\t\t}\n\t\treturn UNKNOWN;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/ResourcesLoader.java",
    "content": "package jadx.api;\n\nimport java.io.BufferedInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.api.plugins.CustomResourcesLoader;\nimport jadx.api.plugins.resources.IResContainerFactory;\nimport jadx.api.plugins.resources.IResTableParserProvider;\nimport jadx.api.plugins.resources.IResourcesLoader;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.android.Res9patchStreamDecoder;\nimport jadx.core.utils.exceptions.JadxException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.core.xmlgen.BinaryXMLParser;\nimport jadx.core.xmlgen.IResTableParser;\nimport jadx.core.xmlgen.ResContainer;\nimport jadx.core.xmlgen.ResTableBinaryParserProvider;\nimport jadx.zip.IZipEntry;\nimport jadx.zip.ZipContent;\n\nimport static jadx.core.utils.files.FileUtils.READ_BUFFER_SIZE;\nimport static jadx.core.utils.files.FileUtils.copyStream;\n\n// TODO: move to core package\npublic final class ResourcesLoader implements IResourcesLoader {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ResourcesLoader.class);\n\n\tprivate final JadxDecompiler decompiler;\n\n\tprivate final List<IResTableParserProvider> resTableParserProviders = new ArrayList<>();\n\tprivate final List<IResContainerFactory> resContainerFactories = new ArrayList<>();\n\n\tprivate BinaryXMLParser binaryXmlParser;\n\n\tResourcesLoader(JadxDecompiler decompiler) {\n\t\tthis.decompiler = decompiler;\n\t\tthis.resTableParserProviders.add(new ResTableBinaryParserProvider());\n\t}\n\n\tList<ResourceFile> load(RootNode root) {\n\t\tinit(root);\n\t\tList<File> inputFiles = decompiler.getArgs().getInputFiles();\n\t\tList<ResourceFile> list = new ArrayList<>(inputFiles.size());\n\t\tfor (File file : inputFiles) {\n\t\t\tloadFile(list, file);\n\t\t}\n\t\treturn list;\n\t}\n\n\tprivate void init(RootNode root) {\n\t\tfor (IResTableParserProvider resTableParserProvider : resTableParserProviders) {\n\t\t\ttry {\n\t\t\t\tresTableParserProvider.init(root);\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new JadxRuntimeException(\"Failed to init res table provider: \" + resTableParserProvider);\n\t\t\t}\n\t\t}\n\t\tfor (IResContainerFactory resContainerFactory : resContainerFactories) {\n\t\t\ttry {\n\t\t\t\tresContainerFactory.init(root);\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new JadxRuntimeException(\"Failed to init res container factory: \" + resContainerFactory);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic interface ResourceDecoder<T> {\n\t\tT decode(long size, InputStream is) throws IOException;\n\t}\n\n\t@Override\n\tpublic void addResContainerFactory(IResContainerFactory resContainerFactory) {\n\t\tresContainerFactories.add(resContainerFactory);\n\t}\n\n\t@Override\n\tpublic void addResTableParserProvider(IResTableParserProvider resTableParserProvider) {\n\t\tresTableParserProviders.add(resTableParserProvider);\n\t}\n\n\tpublic static <T> T decodeStream(ResourceFile rf, ResourceDecoder<T> decoder) throws JadxException {\n\t\ttry {\n\t\t\tIZipEntry zipEntry = rf.getZipEntry();\n\t\t\tif (zipEntry != null) {\n\t\t\t\ttry (InputStream inputStream = zipEntry.getInputStream()) {\n\t\t\t\t\treturn decoder.decode(zipEntry.getUncompressedSize(), inputStream);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tFile file = new File(rf.getOriginalName());\n\t\t\t\ttry (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {\n\t\t\t\t\treturn decoder.decode(file.length(), inputStream);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxException(\"Error decode: \" + rf.getOriginalName(), e);\n\t\t}\n\t}\n\n\tstatic ResContainer loadContent(JadxDecompiler jadxRef, ResourceFile rf) {\n\t\ttry {\n\t\t\tResourcesLoader resLoader = jadxRef.getResourcesLoader();\n\t\t\treturn decodeStream(rf, (size, is) -> resLoader.loadContent(rf, is));\n\t\t} catch (JadxException e) {\n\t\t\tLOG.error(\"Decode error\", e);\n\t\t\tICodeWriter cw = jadxRef.getRoot().makeCodeWriter();\n\t\t\tcw.add(\"Error decode \").add(rf.getType().toString().toLowerCase());\n\t\t\tUtils.appendStackTrace(cw, e.getCause());\n\t\t\treturn ResContainer.textResource(rf.getDeobfName(), cw.finish());\n\t\t}\n\t}\n\n\tprivate ResContainer loadContent(ResourceFile resFile, InputStream inputStream) throws IOException {\n\t\tfor (IResContainerFactory customFactory : resContainerFactories) {\n\t\t\tResContainer resContainer = customFactory.create(resFile, inputStream);\n\t\t\tif (resContainer != null) {\n\t\t\t\treturn resContainer;\n\t\t\t}\n\t\t}\n\t\tswitch (resFile.getType()) {\n\t\t\tcase MANIFEST:\n\t\t\tcase XML:\n\t\t\t\tICodeInfo content = loadBinaryXmlParser().parse(inputStream);\n\t\t\t\treturn ResContainer.textResource(resFile.getDeobfName(), content);\n\n\t\t\tcase ARSC:\n\t\t\t\treturn decodeTable(resFile, inputStream).decodeFiles();\n\n\t\t\tcase IMG:\n\t\t\t\treturn decodeImage(resFile, inputStream);\n\n\t\t\tdefault:\n\t\t\t\treturn ResContainer.resourceFileLink(resFile);\n\t\t}\n\t}\n\n\tpublic IResTableParser decodeTable(ResourceFile resFile, InputStream is) throws IOException {\n\t\tif (resFile.getType() != ResourceType.ARSC) {\n\t\t\tthrow new IllegalArgumentException(\"Unexpected resource type for decode: \" + resFile.getType() + \", expect '.pb'/'.arsc'\");\n\t\t}\n\t\tIResTableParser parser = null;\n\t\tfor (IResTableParserProvider provider : resTableParserProviders) {\n\t\t\tparser = provider.getParser(resFile);\n\t\t\tif (parser != null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (parser == null) {\n\t\t\tthrow new JadxRuntimeException(\"Unknown type of resource file: \" + resFile.getOriginalName());\n\t\t}\n\t\tparser.setBaseFileName(resFile.getDeobfName());\n\t\tparser.decode(is);\n\t\treturn parser;\n\t}\n\n\tprivate static ResContainer decodeImage(ResourceFile rf, InputStream inputStream) {\n\t\tString name = rf.getDeobfName();\n\t\tif (name.endsWith(\".9.png\")) {\n\t\t\ttry (ByteArrayOutputStream os = new ByteArrayOutputStream()) {\n\t\t\t\tRes9patchStreamDecoder decoder = new Res9patchStreamDecoder();\n\t\t\t\tif (decoder.decode(inputStream, os)) {\n\t\t\t\t\treturn ResContainer.decodedData(rf.getDeobfName(), os.toByteArray());\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to decode 9-patch png image, path: {}\", name, e);\n\t\t\t}\n\t\t}\n\t\treturn ResContainer.resourceFileLink(rf);\n\t}\n\n\tprivate void loadFile(List<ResourceFile> list, File file) {\n\t\tif (file == null || file.isDirectory()) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Try to load the resources with a custom loader first\n\t\tfor (CustomResourcesLoader loader : decompiler.getCustomResourcesLoaders()) {\n\t\t\tif (loader.load(this, list, file)) {\n\t\t\t\tLOG.debug(\"Custom loader used for {}\", file.getAbsolutePath());\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// If no custom decoder was able to decode the resources, use the default decoder\n\t\tdefaultLoadFile(list, file, \"\");\n\t}\n\n\tpublic void defaultLoadFile(List<ResourceFile> list, File file, String subDir) {\n\t\tif (FileUtils.isZipFile(file)) {\n\t\t\ttry {\n\t\t\t\tZipContent zipContent = decompiler.getZipReader().open(file);\n\t\t\t\t// do not close a zip now, entry content will be read later\n\t\t\t\tdecompiler.addCloseable(zipContent);\n\t\t\t\tfor (IZipEntry entry : zipContent.getEntries()) {\n\t\t\t\t\taddEntry(list, file, entry, subDir);\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new RuntimeException(\"Failed to open zip file: \" + file.getAbsolutePath(), e);\n\t\t\t}\n\t\t} else {\n\t\t\tResourceType type = ResourceType.getFileType(file.getAbsolutePath());\n\t\t\tlist.add(ResourceFile.createResourceFile(decompiler, file, type));\n\t\t}\n\t}\n\n\tpublic void addEntry(List<ResourceFile> list, File zipFile, IZipEntry entry, String subDir) {\n\t\tif (entry.isDirectory()) {\n\t\t\treturn;\n\t\t}\n\t\tString name = entry.getName();\n\t\tResourceType type = ResourceType.getFileType(name);\n\t\tResourceFile rf = ResourceFile.createResourceFile(decompiler, subDir + name, type);\n\t\tif (rf != null) {\n\t\t\trf.setZipEntry(entry);\n\t\t\tlist.add(rf);\n\t\t}\n\t}\n\n\tpublic static ICodeInfo loadToCodeWriter(InputStream is) throws IOException {\n\t\treturn loadToCodeWriter(is, StandardCharsets.UTF_8);\n\t}\n\n\tpublic static ICodeInfo loadToCodeWriter(InputStream is, Charset charset) throws IOException {\n\t\tByteArrayOutputStream baos = new ByteArrayOutputStream(READ_BUFFER_SIZE);\n\t\tcopyStream(is, baos);\n\t\treturn new SimpleCodeInfo(baos.toString(charset));\n\t}\n\n\tprivate synchronized BinaryXMLParser loadBinaryXmlParser() {\n\t\tif (binaryXmlParser == null) {\n\t\t\tbinaryXmlParser = new BinaryXMLParser(decompiler.getRoot());\n\t\t}\n\t\treturn binaryXmlParser;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/args/GeneratedRenamesMappingFileMode.java",
    "content": "package jadx.api.args;\n\npublic enum GeneratedRenamesMappingFileMode {\n\n\t/**\n\t * Load if found, don't save (default)\n\t */\n\tREAD,\n\n\t/**\n\t * Load if found, save only if new (don't overwrite)\n\t */\n\tREAD_OR_SAVE,\n\n\t/**\n\t * Don't load, always save\n\t */\n\tOVERWRITE,\n\n\t/**\n\t * Don't load and don't save\n\t */\n\tIGNORE;\n\n\tpublic static GeneratedRenamesMappingFileMode getDefault() {\n\t\treturn READ;\n\t}\n\n\tpublic boolean shouldRead() {\n\t\treturn this == READ || this == READ_OR_SAVE;\n\t}\n\n\tpublic boolean shouldWrite() {\n\t\treturn this == READ_OR_SAVE || this == OVERWRITE;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/args/IntegerFormat.java",
    "content": "package jadx.api.args;\n\npublic enum IntegerFormat {\n\tAUTO,\n\tDECIMAL,\n\tHEXADECIMAL;\n\n\tpublic boolean isHexadecimal() {\n\t\treturn this == HEXADECIMAL;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/args/ResourceNameSource.java",
    "content": "package jadx.api.args;\n\n/**\n * Resources original name source (for deobfuscation)\n */\npublic enum ResourceNameSource {\n\n\t/**\n\t * Automatically select best name (default)\n\t */\n\tAUTO,\n\n\t/**\n\t * Force use resources provided names\n\t */\n\tRESOURCES,\n\n\t/**\n\t * Force use resources names from R class\n\t */\n\tCODE,\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/args/UseSourceNameAsClassNameAlias.java",
    "content": "package jadx.api.args;\n\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic enum UseSourceNameAsClassNameAlias {\n\tALWAYS,\n\tIF_BETTER,\n\tNEVER;\n\n\tpublic static UseSourceNameAsClassNameAlias getDefault() {\n\t\treturn NEVER;\n\t}\n\n\t/**\n\t * @deprecated Use {@link UseSourceNameAsClassNameAlias} directly.\n\t */\n\t@Deprecated\n\tpublic boolean toBoolean() {\n\t\tswitch (this) {\n\t\t\tcase IF_BETTER:\n\t\t\t\treturn true;\n\t\t\tcase NEVER:\n\t\t\t\treturn false;\n\t\t\tcase ALWAYS:\n\t\t\t\tthrow new JadxRuntimeException(\"No match between \" + this + \" and boolean\");\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unhandled strategy: \" + this);\n\t\t}\n\t}\n\n\t/**\n\t * @deprecated Use {@link UseSourceNameAsClassNameAlias} directly.\n\t */\n\t@Deprecated\n\tpublic static UseSourceNameAsClassNameAlias create(boolean useSourceNameAsAlias) {\n\t\treturn useSourceNameAsAlias ? IF_BETTER : NEVER;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/args/UserRenamesMappingsMode.java",
    "content": "package jadx.api.args;\n\npublic enum UserRenamesMappingsMode {\n\n\t/**\n\t * Just read, user can save manually (default)\n\t */\n\tREAD,\n\n\t/**\n\t * Read and autosave after every change\n\t */\n\tREAD_AND_AUTOSAVE_EVERY_CHANGE,\n\n\t/**\n\t * Read and autosave before exiting the app or closing the project\n\t */\n\tREAD_AND_AUTOSAVE_BEFORE_CLOSING,\n\n\t/**\n\t * Don't load and don't save\n\t */\n\tIGNORE;\n\n\tpublic static UserRenamesMappingsMode getDefault() {\n\t\treturn READ;\n\t}\n\n\tpublic boolean shouldRead() {\n\t\treturn this != IGNORE;\n\t}\n\n\tpublic boolean shouldWrite() {\n\t\treturn this == READ_AND_AUTOSAVE_EVERY_CHANGE || this == READ_AND_AUTOSAVE_BEFORE_CLOSING;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/data/CodeRefType.java",
    "content": "package jadx.api.data;\n\npublic enum CodeRefType {\n\tMTH_ARG,\n\tVAR,\n\tCATCH,\n\tINSN,\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/data/CommentStyle.java",
    "content": "package jadx.api.data;\n\npublic enum CommentStyle {\n\n\t/**\n\t * <pre>\n\t * // comment\n\t * </pre>\n\t */\n\tLINE(\"// \", \"// \", \"\"),\n\n\t// @formatter:off\n\t/**\n\t * <pre>\n\t * /*\n\t *  * comment\n\t *  *&#47;\n\t * </pre>\n\t */\n\t// @formatter:on\n\tBLOCK(\"/*\\n * \", \" * \", \"\\n */\"),\n\n\t/**\n\t * <pre>\n\t * /* comment *&#47;\n\t * </pre>\n\t */\n\tBLOCK_CONDENSED(\"/* \", \" * \", \" */\"),\n\n\t// @formatter:off\n\t/**\n\t * <pre>\n\t * /**\n\t *  * comment\n\t *  *&#47;\n\t * </pre>\n\t */\n\t// @formatter:on\n\tJAVADOC(\"/**\\n * \", \" * \", \"\\n */\"),\n\n\t/**\n\t * <pre>\n\t * /** comment *&#47;\n\t * </pre>\n\t */\n\tJAVADOC_CONDENSED(\"/** \", \" * \", \" */\");\n\n\tprivate final String start;\n\tprivate final String onNewLine;\n\tprivate final String end;\n\n\tCommentStyle(String start, String onNewLine, String end) {\n\t\tthis.start = start;\n\t\tthis.onNewLine = onNewLine;\n\t\tthis.end = end;\n\t}\n\n\tpublic String getStart() {\n\t\treturn start;\n\t}\n\n\tpublic String getOnNewLine() {\n\t\treturn onNewLine;\n\t}\n\n\tpublic String getEnd() {\n\t\treturn end;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/data/ICodeComment.java",
    "content": "package jadx.api.data;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic interface ICodeComment extends Comparable<ICodeComment> {\n\n\tIJavaNodeRef getNodeRef();\n\n\t@Nullable\n\tIJavaCodeRef getCodeRef();\n\n\tString getComment();\n\n\tCommentStyle getStyle();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/data/ICodeData.java",
    "content": "package jadx.api.data;\n\nimport java.util.List;\n\npublic interface ICodeData {\n\n\tList<ICodeComment> getComments();\n\n\tList<ICodeRename> getRenames();\n\n\tboolean isEmpty();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/data/ICodeRename.java",
    "content": "package jadx.api.data;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic interface ICodeRename extends Comparable<ICodeRename> {\n\n\tIJavaNodeRef getNodeRef();\n\n\t@Nullable\n\tIJavaCodeRef getCodeRef();\n\n\tString getNewName();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/data/IJavaCodeRef.java",
    "content": "package jadx.api.data;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic interface IJavaCodeRef extends Comparable<IJavaCodeRef> {\n\n\tCodeRefType getAttachType();\n\n\tint getIndex();\n\n\t@Override\n\tdefault int compareTo(@NotNull IJavaCodeRef o) {\n\t\treturn Integer.compare(getIndex(), o.getIndex());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/data/IJavaNodeRef.java",
    "content": "package jadx.api.data;\n\npublic interface IJavaNodeRef extends Comparable<IJavaNodeRef> {\n\n\tenum RefType {\n\t\tCLASS, FIELD, METHOD, PKG\n\t}\n\n\tRefType getType();\n\n\tString getDeclaringClass();\n\n\tString getShortId();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/data/IRenameNode.java",
    "content": "package jadx.api.data;\n\npublic interface IRenameNode {\n\n\tvoid rename(String newName);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/data/impl/JadxCodeComment.java",
    "content": "package jadx.api.data.impl;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.data.CommentStyle;\nimport jadx.api.data.ICodeComment;\nimport jadx.api.data.IJavaCodeRef;\nimport jadx.api.data.IJavaNodeRef;\n\npublic class JadxCodeComment implements ICodeComment {\n\n\tprivate IJavaNodeRef nodeRef;\n\t@Nullable\n\tprivate IJavaCodeRef codeRef;\n\tprivate String comment;\n\tprivate CommentStyle style = CommentStyle.LINE;\n\n\tpublic JadxCodeComment(IJavaNodeRef nodeRef, String comment) {\n\t\tthis(nodeRef, null, comment);\n\t}\n\n\tpublic JadxCodeComment(IJavaNodeRef nodeRef, String comment, CommentStyle style) {\n\t\tthis(nodeRef, null, comment, style);\n\t}\n\n\tpublic JadxCodeComment(IJavaNodeRef nodeRef, @Nullable IJavaCodeRef codeRef, String comment) {\n\t\tthis(nodeRef, codeRef, comment, CommentStyle.LINE);\n\t}\n\n\tpublic JadxCodeComment(IJavaNodeRef nodeRef, @Nullable IJavaCodeRef codeRef, String comment, CommentStyle style) {\n\t\tthis.nodeRef = nodeRef;\n\t\tthis.codeRef = codeRef;\n\t\tthis.comment = comment;\n\t\tthis.style = style;\n\t}\n\n\tpublic JadxCodeComment() {\n\t\t// for json deserialization\n\t}\n\n\t@Override\n\tpublic IJavaNodeRef getNodeRef() {\n\t\treturn nodeRef;\n\t}\n\n\tpublic void setNodeRef(IJavaNodeRef nodeRef) {\n\t\tthis.nodeRef = nodeRef;\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic IJavaCodeRef getCodeRef() {\n\t\treturn codeRef;\n\t}\n\n\tpublic void setCodeRef(@Nullable IJavaCodeRef codeRef) {\n\t\tthis.codeRef = codeRef;\n\t}\n\n\t@Override\n\tpublic String getComment() {\n\t\treturn comment;\n\t}\n\n\tpublic void setComment(String comment) {\n\t\tthis.comment = comment;\n\t}\n\n\t@Override\n\tpublic CommentStyle getStyle() {\n\t\treturn style;\n\t}\n\n\tpublic void setStyle(CommentStyle style) {\n\t\tthis.style = style;\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull ICodeComment other) {\n\t\tint cmpNodeRef = this.getNodeRef().compareTo(other.getNodeRef());\n\t\tif (cmpNodeRef != 0) {\n\t\t\treturn cmpNodeRef;\n\t\t}\n\t\tif (this.getCodeRef() != null && other.getCodeRef() != null) {\n\t\t\treturn this.getCodeRef().compareTo(other.getCodeRef());\n\t\t}\n\t\treturn this.getComment().compareTo(other.getComment());\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"JadxCodeComment{\" + nodeRef\n\t\t\t\t+ \", ref=\" + codeRef\n\t\t\t\t+ \", comment='\" + comment + '\\''\n\t\t\t\t+ \", style=\" + style\n\t\t\t\t+ '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/data/impl/JadxCodeData.java",
    "content": "package jadx.api.data.impl;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.api.data.ICodeComment;\nimport jadx.api.data.ICodeData;\nimport jadx.api.data.ICodeRename;\n\npublic class JadxCodeData implements ICodeData {\n\tprivate List<ICodeComment> comments = Collections.emptyList();\n\tprivate List<ICodeRename> renames = Collections.emptyList();\n\n\t@Override\n\tpublic List<ICodeComment> getComments() {\n\t\treturn comments;\n\t}\n\n\tpublic void setComments(List<ICodeComment> comments) {\n\t\tthis.comments = comments;\n\t}\n\n\t@Override\n\tpublic List<ICodeRename> getRenames() {\n\t\treturn renames;\n\t}\n\n\tpublic void setRenames(List<ICodeRename> renames) {\n\t\tthis.renames = renames;\n\t}\n\n\t@Override\n\tpublic boolean isEmpty() {\n\t\treturn comments.isEmpty() && renames.isEmpty();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/data/impl/JadxCodeRef.java",
    "content": "package jadx.api.data.impl;\n\nimport jadx.api.JavaVariable;\nimport jadx.api.data.CodeRefType;\nimport jadx.api.data.IJavaCodeRef;\nimport jadx.api.metadata.annotations.VarNode;\n\npublic class JadxCodeRef implements IJavaCodeRef {\n\n\tpublic static JadxCodeRef forInsn(int offset) {\n\t\treturn new JadxCodeRef(CodeRefType.INSN, offset);\n\t}\n\n\tpublic static JadxCodeRef forMthArg(int argIndex) {\n\t\treturn new JadxCodeRef(CodeRefType.MTH_ARG, argIndex);\n\t}\n\n\tpublic static JadxCodeRef forVar(int regNum, int ssaVersion) {\n\t\treturn new JadxCodeRef(CodeRefType.VAR, regNum << 16 | ssaVersion);\n\t}\n\n\tpublic static JadxCodeRef forVar(JavaVariable javaVariable) {\n\t\treturn forVar(javaVariable.getReg(), javaVariable.getSsa());\n\t}\n\n\tpublic static JadxCodeRef forVar(VarNode varNode) {\n\t\treturn forVar(varNode.getReg(), varNode.getSsa());\n\t}\n\n\tpublic static JadxCodeRef forCatch(int handlerOffset) {\n\t\treturn new JadxCodeRef(CodeRefType.CATCH, handlerOffset);\n\t}\n\n\tprivate CodeRefType attachType;\n\tprivate int index;\n\n\tpublic JadxCodeRef(CodeRefType attachType, int index) {\n\t\tthis.attachType = attachType;\n\t\tthis.index = index;\n\t}\n\n\tpublic JadxCodeRef() {\n\t\t// used for json serialization\n\t}\n\n\tpublic CodeRefType getAttachType() {\n\t\treturn attachType;\n\t}\n\n\tpublic void setAttachType(CodeRefType attachType) {\n\t\tthis.attachType = attachType;\n\t}\n\n\t@Override\n\tpublic int getIndex() {\n\t\treturn index;\n\t}\n\n\tpublic void setIndex(int index) {\n\t\tthis.index = index;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof JadxCodeRef)) {\n\t\t\treturn false;\n\t\t}\n\t\tJadxCodeRef other = (JadxCodeRef) o;\n\t\treturn getIndex() == other.getIndex()\n\t\t\t\t&& getAttachType() == other.getAttachType();\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn 31 * getAttachType().hashCode() + getIndex();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"JadxCodeRef{\"\n\t\t\t\t+ \"attachType=\" + attachType\n\t\t\t\t+ \", index=\" + index\n\t\t\t\t+ '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/data/impl/JadxCodeRename.java",
    "content": "package jadx.api.data.impl;\n\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.data.ICodeRename;\nimport jadx.api.data.IJavaCodeRef;\nimport jadx.api.data.IJavaNodeRef;\n\npublic class JadxCodeRename implements ICodeRename {\n\tprivate IJavaNodeRef nodeRef;\n\t@Nullable\n\tprivate IJavaCodeRef codeRef;\n\tprivate String newName;\n\n\tpublic JadxCodeRename(IJavaNodeRef nodeRef, String newName) {\n\t\tthis(nodeRef, null, newName);\n\t}\n\n\tpublic JadxCodeRename(IJavaNodeRef nodeRef, @Nullable IJavaCodeRef codeRef, String newName) {\n\t\tthis.nodeRef = nodeRef;\n\t\tthis.codeRef = codeRef;\n\t\tthis.newName = newName;\n\t}\n\n\tpublic JadxCodeRename() {\n\t\t// used in json serialization\n\t}\n\n\t@Override\n\tpublic IJavaNodeRef getNodeRef() {\n\t\treturn nodeRef;\n\t}\n\n\tpublic void setNodeRef(IJavaNodeRef nodeRef) {\n\t\tthis.nodeRef = nodeRef;\n\t}\n\n\t@Override\n\tpublic IJavaCodeRef getCodeRef() {\n\t\treturn codeRef;\n\t}\n\n\tpublic void setCodeRef(IJavaCodeRef codeRef) {\n\t\tthis.codeRef = codeRef;\n\t}\n\n\t@Override\n\tpublic String getNewName() {\n\t\treturn newName;\n\t}\n\n\tpublic void setNewName(String newName) {\n\t\tthis.newName = newName;\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull ICodeRename other) {\n\t\tint cmpNodeRef = this.getNodeRef().compareTo(other.getNodeRef());\n\t\tif (cmpNodeRef != 0) {\n\t\t\treturn cmpNodeRef;\n\t\t}\n\t\tif (this.getCodeRef() != null && other.getCodeRef() != null) {\n\t\t\treturn this.getCodeRef().compareTo(other.getCodeRef());\n\t\t}\n\t\treturn this.getNewName().compareTo(other.getNewName());\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof ICodeRename)) {\n\t\t\treturn false;\n\t\t}\n\t\tICodeRename other = (ICodeRename) o;\n\t\treturn getNodeRef().equals(other.getNodeRef())\n\t\t\t\t&& Objects.equals(getCodeRef(), other.getCodeRef());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn 31 * getNodeRef().hashCode() + Objects.hashCode(getCodeRef());\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"JadxCodeRename{\" + nodeRef\n\t\t\t\t+ \", codeRef=\" + codeRef\n\t\t\t\t+ \", newName='\" + newName + '\\''\n\t\t\t\t+ '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/data/impl/JadxNodeRef.java",
    "content": "package jadx.api.data.impl;\n\nimport java.util.Comparator;\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.JavaClass;\nimport jadx.api.JavaField;\nimport jadx.api.JavaMethod;\nimport jadx.api.JavaNode;\nimport jadx.api.data.IJavaNodeRef;\n\npublic class JadxNodeRef implements IJavaNodeRef {\n\n\t@Nullable\n\tpublic static JadxNodeRef forJavaNode(JavaNode javaNode) {\n\t\tif (javaNode instanceof JavaClass) {\n\t\t\treturn forCls((JavaClass) javaNode);\n\t\t}\n\t\tif (javaNode instanceof JavaMethod) {\n\t\t\treturn forMth((JavaMethod) javaNode);\n\t\t}\n\t\tif (javaNode instanceof JavaField) {\n\t\t\treturn forFld((JavaField) javaNode);\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static JadxNodeRef forCls(JavaClass cls) {\n\t\treturn new JadxNodeRef(RefType.CLASS, getClassRefStr(cls), null);\n\t}\n\n\tpublic static JadxNodeRef forCls(String clsFullName) {\n\t\treturn new JadxNodeRef(RefType.CLASS, clsFullName, null);\n\t}\n\n\tpublic static JadxNodeRef forMth(JavaMethod mth) {\n\t\treturn new JadxNodeRef(RefType.METHOD,\n\t\t\t\tgetClassRefStr(mth.getDeclaringClass()),\n\t\t\t\tmth.getMethodNode().getMethodInfo().getShortId());\n\t}\n\n\tpublic static JadxNodeRef forFld(JavaField fld) {\n\t\treturn new JadxNodeRef(RefType.FIELD,\n\t\t\t\tgetClassRefStr(fld.getDeclaringClass()),\n\t\t\t\tfld.getFieldNode().getFieldInfo().getShortId());\n\t}\n\n\tpublic static JadxNodeRef forPkg(String pkgFullName) {\n\t\treturn new JadxNodeRef(RefType.PKG, pkgFullName, \"\");\n\t}\n\n\tprivate static String getClassRefStr(JavaClass cls) {\n\t\treturn cls.getClassNode().getClassInfo().getRawName();\n\t}\n\n\tprivate RefType refType;\n\tprivate String declClass;\n\t@Nullable\n\tprivate String shortId;\n\n\tpublic JadxNodeRef(RefType refType, String declClass, @Nullable String shortId) {\n\t\tthis.refType = refType;\n\t\tthis.declClass = declClass;\n\t\tthis.shortId = shortId;\n\t}\n\n\tpublic JadxNodeRef() {\n\t\t// for json deserialization\n\t}\n\n\t@Override\n\tpublic RefType getType() {\n\t\treturn refType;\n\t}\n\n\tpublic void setRefType(RefType refType) {\n\t\tthis.refType = refType;\n\t}\n\n\t@Override\n\tpublic String getDeclaringClass() {\n\t\treturn declClass;\n\t}\n\n\tpublic void setDeclClass(String declClass) {\n\t\tthis.declClass = declClass;\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic String getShortId() {\n\t\treturn shortId;\n\t}\n\n\tpublic void setShortId(@Nullable String shortId) {\n\t\tthis.shortId = shortId;\n\t}\n\n\tprivate static final Comparator<IJavaNodeRef> COMPARATOR = Comparator\n\t\t\t.comparing(IJavaNodeRef::getType)\n\t\t\t.thenComparing(IJavaNodeRef::getDeclaringClass)\n\t\t\t.thenComparing(IJavaNodeRef::getShortId);\n\n\t@Override\n\tpublic int compareTo(@NotNull IJavaNodeRef other) {\n\t\treturn COMPARATOR.compare(this, other);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(refType, declClass, shortId);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof JadxNodeRef)) {\n\t\t\treturn false;\n\t\t}\n\t\tJadxNodeRef that = (JadxNodeRef) o;\n\t\treturn refType == that.refType\n\t\t\t\t&& Objects.equals(declClass, that.declClass)\n\t\t\t\t&& Objects.equals(shortId, that.shortId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tswitch (refType) {\n\t\t\tcase CLASS:\n\t\t\tcase PKG:\n\t\t\t\treturn declClass;\n\t\t\tcase FIELD:\n\t\t\tcase METHOD:\n\t\t\t\treturn declClass + \"->\" + shortId;\n\t\t\tdefault:\n\t\t\t\treturn \"unknown node ref type\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/deobf/IAliasProvider.java",
    "content": "package jadx.api.deobf;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.dex.nodes.RootNode;\n\npublic interface IAliasProvider {\n\n\tdefault void init(RootNode root) {\n\t\t// optional\n\t}\n\n\tString forPackage(PackageNode pkg);\n\n\tString forClass(ClassNode cls);\n\n\tString forField(FieldNode fld);\n\n\tString forMethod(MethodNode mth);\n\n\t/**\n\t * Optional method to set initial max indexes loaded from mapping\n\t */\n\tdefault void initIndexes(int pkg, int cls, int fld, int mth) {\n\t\t// optional\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/deobf/IDeobfCondition.java",
    "content": "package jadx.api.deobf;\n\nimport jadx.api.deobf.impl.CombineDeobfConditions;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.dex.nodes.RootNode;\n\n/**\n * Utility interface to simplify merging several rename conditions to build {@link IRenameCondition}\n * instance with {@link CombineDeobfConditions#combine(IDeobfCondition...)}.\n */\npublic interface IDeobfCondition {\n\n\tenum Action {\n\t\tNO_ACTION,\n\t\tFORCE_RENAME,\n\t\tFORBID_RENAME,\n\t}\n\n\tvoid init(RootNode root);\n\n\tAction check(PackageNode pkg);\n\n\tAction check(ClassNode cls);\n\n\tAction check(FieldNode fld);\n\n\tAction check(MethodNode mth);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/deobf/IRenameCondition.java",
    "content": "package jadx.api.deobf;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.dex.nodes.RootNode;\n\npublic interface IRenameCondition {\n\n\tvoid init(RootNode root);\n\n\tboolean shouldRename(PackageNode pkg);\n\n\tboolean shouldRename(ClassNode cls);\n\n\tboolean shouldRename(FieldNode fld);\n\n\tboolean shouldRename(MethodNode mth);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/deobf/impl/AlwaysRename.java",
    "content": "package jadx.api.deobf.impl;\n\nimport jadx.api.deobf.IRenameCondition;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.dex.nodes.RootNode;\n\npublic class AlwaysRename implements IRenameCondition {\n\n\tpublic static final IRenameCondition INSTANCE = new AlwaysRename();\n\n\tprivate AlwaysRename() {\n\t}\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t}\n\n\t@Override\n\tpublic boolean shouldRename(PackageNode pkg) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean shouldRename(ClassNode cls) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean shouldRename(FieldNode fld) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean shouldRename(MethodNode mth) {\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/deobf/impl/AnyRenameCondition.java",
    "content": "package jadx.api.deobf.impl;\n\nimport java.util.function.BiPredicate;\n\nimport jadx.api.deobf.IRenameCondition;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.IDexNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.dex.nodes.RootNode;\n\npublic class AnyRenameCondition implements IRenameCondition {\n\n\tprivate final BiPredicate<String, IDexNode> predicate;\n\n\tpublic AnyRenameCondition(BiPredicate<String, IDexNode> predicate) {\n\t\tthis.predicate = predicate;\n\t}\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t}\n\n\t@Override\n\tpublic boolean shouldRename(PackageNode pkg) {\n\t\treturn predicate.test(pkg.getAliasPkgInfo().getName(), pkg);\n\t}\n\n\t@Override\n\tpublic boolean shouldRename(ClassNode cls) {\n\t\treturn predicate.test(cls.getAlias(), cls);\n\t}\n\n\t@Override\n\tpublic boolean shouldRename(FieldNode fld) {\n\t\treturn predicate.test(fld.getAlias(), fld);\n\t}\n\n\t@Override\n\tpublic boolean shouldRename(MethodNode mth) {\n\t\treturn predicate.test(mth.getAlias(), mth);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/deobf/impl/CombineDeobfConditions.java",
    "content": "package jadx.api.deobf.impl;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport jadx.api.deobf.IDeobfCondition;\nimport jadx.api.deobf.IRenameCondition;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.dex.nodes.RootNode;\n\npublic class CombineDeobfConditions implements IRenameCondition {\n\n\tpublic static IRenameCondition combine(List<IDeobfCondition> conditions) {\n\t\treturn new CombineDeobfConditions(conditions);\n\t}\n\n\tpublic static IRenameCondition combine(IDeobfCondition... conditions) {\n\t\treturn new CombineDeobfConditions(Arrays.asList(conditions));\n\t}\n\n\tprivate final List<IDeobfCondition> conditions;\n\n\tprivate CombineDeobfConditions(List<IDeobfCondition> conditions) {\n\t\tif (conditions == null || conditions.isEmpty()) {\n\t\t\tthrow new IllegalArgumentException(\"Conditions list can't be empty\");\n\t\t}\n\t\tthis.conditions = conditions;\n\t}\n\n\tprivate boolean combineFunc(Function<IDeobfCondition, IDeobfCondition.Action> check) {\n\t\tfor (IDeobfCondition c : conditions) {\n\t\t\tswitch (check.apply(c)) {\n\t\t\t\tcase NO_ACTION:\n\t\t\t\t\t// ignore\n\t\t\t\t\tbreak;\n\t\t\t\tcase FORCE_RENAME:\n\t\t\t\t\treturn true;\n\t\t\t\tcase FORBID_RENAME:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tconditions.forEach(c -> c.init(root));\n\t}\n\n\t@Override\n\tpublic boolean shouldRename(PackageNode pkg) {\n\t\treturn combineFunc(c -> c.check(pkg));\n\t}\n\n\t@Override\n\tpublic boolean shouldRename(ClassNode cls) {\n\t\treturn combineFunc(c -> c.check(cls));\n\t}\n\n\t@Override\n\tpublic boolean shouldRename(FieldNode fld) {\n\t\treturn combineFunc(c -> c.check(fld));\n\t}\n\n\t@Override\n\tpublic boolean shouldRename(MethodNode mth) {\n\t\treturn combineFunc(c -> c.check(mth));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/gui/tree/ITreeNode.java",
    "content": "package jadx.api.gui.tree;\n\nimport javax.swing.Icon;\nimport javax.swing.tree.TreeNode;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.metadata.ICodeNodeRef;\n\npublic interface ITreeNode extends TreeNode {\n\n\t/**\n\t * Locale independent node identifier\n\t */\n\tString getID();\n\n\t/**\n\t * Node title\n\t */\n\tString getName();\n\n\t/**\n\t * Node icon\n\t */\n\tIcon getIcon();\n\n\t/**\n\t * Related code node reference.\n\t */\n\t@Nullable\n\tICodeNodeRef getCodeNodeRef();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/impl/AnnotatedCodeInfo.java",
    "content": "package jadx.api.impl;\n\nimport java.util.Map;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeMetadata;\nimport jadx.api.metadata.impl.CodeMetadataStorage;\n\npublic class AnnotatedCodeInfo implements ICodeInfo {\n\n\tprivate final String code;\n\tprivate final ICodeMetadata metadata;\n\n\tpublic AnnotatedCodeInfo(String code, Map<Integer, Integer> lineMapping, Map<Integer, ICodeAnnotation> annotations) {\n\t\tthis.code = code;\n\t\tthis.metadata = CodeMetadataStorage.build(lineMapping, annotations);\n\t}\n\n\t@Override\n\tpublic String getCodeStr() {\n\t\treturn code;\n\t}\n\n\t@Override\n\tpublic ICodeMetadata getCodeMetadata() {\n\t\treturn metadata;\n\t}\n\n\t@Override\n\tpublic boolean hasMetadata() {\n\t\treturn metadata != ICodeMetadata.EMPTY;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn code;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/impl/AnnotatedCodeWriter.java",
    "content": "package jadx.api.impl;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.TreeMap;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ICodeWriter;\nimport jadx.api.JadxArgs;\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.metadata.annotations.NodeDeclareRef;\nimport jadx.core.utils.StringUtils;\n\npublic class AnnotatedCodeWriter extends SimpleCodeWriter implements ICodeWriter {\n\n\tprivate int line = 1;\n\tprivate int offset;\n\tprivate Map<Integer, ICodeAnnotation> annotations = Collections.emptyMap();\n\tprivate Map<Integer, Integer> lineMap = Collections.emptyMap();\n\n\tpublic AnnotatedCodeWriter(JadxArgs args) {\n\t\tsuper(args);\n\t}\n\n\t@Override\n\tpublic boolean isMetadataSupported() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic AnnotatedCodeWriter addMultiLine(String str) {\n\t\tif (str.contains(newLineStr)) {\n\t\t\tbuf.append(str.replace(newLineStr, newLineStr + indentStr));\n\t\t\tline += StringUtils.countMatches(str, newLineStr);\n\t\t\toffset = 0;\n\t\t} else {\n\t\t\tbuf.append(str);\n\t\t}\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic AnnotatedCodeWriter add(String str) {\n\t\tbuf.append(str);\n\t\toffset += str.length();\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic AnnotatedCodeWriter add(char c) {\n\t\tbuf.append(c);\n\t\toffset++;\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic ICodeWriter add(ICodeWriter cw) {\n\t\tif (!cw.isMetadataSupported()) {\n\t\t\tbuf.append(cw.getCodeStr());\n\t\t\treturn this;\n\t\t}\n\t\tAnnotatedCodeWriter code = (AnnotatedCodeWriter) cw;\n\t\tline--;\n\t\tint startPos = getLength();\n\t\tfor (Map.Entry<Integer, ICodeAnnotation> entry : code.annotations.entrySet()) {\n\t\t\tint pos = entry.getKey();\n\t\t\tint newPos = startPos + pos;\n\t\t\tattachAnnotation(entry.getValue(), newPos);\n\t\t}\n\t\tfor (Map.Entry<Integer, Integer> entry : code.lineMap.entrySet()) {\n\t\t\tattachSourceLine(line + entry.getKey(), entry.getValue());\n\t\t}\n\t\tline += code.line;\n\t\toffset = code.offset;\n\t\tbuf.append(code.buf);\n\t\treturn this;\n\t}\n\n\t@Override\n\tprotected void addLine() {\n\t\tbuf.append(newLineStr);\n\t\tline++;\n\t\toffset = 0;\n\t}\n\n\t@Override\n\tprotected AnnotatedCodeWriter addLineIndent() {\n\t\tbuf.append(indentStr);\n\t\toffset += indentStr.length();\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic int getLine() {\n\t\treturn line;\n\t}\n\n\t@Override\n\tpublic int getLineStartPos() {\n\t\treturn getLength() - offset;\n\t}\n\n\t@Override\n\tpublic void attachDefinition(ICodeNodeRef obj) {\n\t\tif (obj == null) {\n\t\t\treturn;\n\t\t}\n\t\tattachAnnotation(new NodeDeclareRef(obj));\n\t}\n\n\t@Override\n\tpublic void attachAnnotation(ICodeAnnotation obj) {\n\t\tif (obj == null) {\n\t\t\treturn;\n\t\t}\n\t\tattachAnnotation(obj, getLength());\n\t}\n\n\t@Override\n\tpublic void attachLineAnnotation(ICodeAnnotation obj) {\n\t\tif (obj == null) {\n\t\t\treturn;\n\t\t}\n\t\tattachAnnotation(obj, getLineStartPos());\n\t}\n\n\tprivate void attachAnnotation(ICodeAnnotation obj, int pos) {\n\t\tif (annotations.isEmpty()) {\n\t\t\tannotations = new HashMap<>();\n\t\t}\n\t\tannotations.put(pos, obj);\n\t}\n\n\t@Override\n\tpublic void attachSourceLine(int sourceLine) {\n\t\tif (sourceLine == 0) {\n\t\t\treturn;\n\t\t}\n\t\tattachSourceLine(line, sourceLine);\n\t}\n\n\tprivate void attachSourceLine(int decompiledLine, int sourceLine) {\n\t\tif (lineMap.isEmpty()) {\n\t\t\tlineMap = new TreeMap<>();\n\t\t}\n\t\tlineMap.put(decompiledLine, sourceLine);\n\t}\n\n\t@Override\n\tpublic ICodeInfo finish() {\n\t\tString code = buf.toString();\n\t\tbuf = null;\n\t\treturn new AnnotatedCodeInfo(code, lineMap, annotations);\n\t}\n\n\t@Override\n\tpublic Map<Integer, ICodeAnnotation> getRawAnnotations() {\n\t\treturn annotations;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/impl/DelegateCodeCache.java",
    "content": "package jadx.api.impl;\n\nimport java.io.IOException;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ICodeCache;\nimport jadx.api.ICodeInfo;\n\npublic abstract class DelegateCodeCache implements ICodeCache {\n\n\tprotected final ICodeCache backCache;\n\n\tpublic DelegateCodeCache(ICodeCache backCache) {\n\t\tthis.backCache = backCache;\n\t}\n\n\t@Override\n\tpublic void add(String clsFullName, ICodeInfo codeInfo) {\n\t\tbackCache.add(clsFullName, codeInfo);\n\t}\n\n\t@Override\n\tpublic void remove(String clsFullName) {\n\t\tbackCache.remove(clsFullName);\n\t}\n\n\t@Override\n\tpublic @NotNull ICodeInfo get(String clsFullName) {\n\t\treturn backCache.get(clsFullName);\n\t}\n\n\t@Override\n\t@Nullable\n\tpublic String getCode(String clsFullName) {\n\t\treturn backCache.getCode(clsFullName);\n\t}\n\n\t@Override\n\tpublic boolean contains(String clsFullName) {\n\t\treturn backCache.contains(clsFullName);\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\tbackCache.close();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/impl/InMemoryCodeCache.java",
    "content": "package jadx.api.impl;\n\nimport java.io.IOException;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ICodeCache;\nimport jadx.api.ICodeInfo;\n\npublic class InMemoryCodeCache implements ICodeCache {\n\n\tprivate final Map<String, ICodeInfo> storage = new ConcurrentHashMap<>();\n\n\t@Override\n\tpublic void add(String clsFullName, ICodeInfo codeInfo) {\n\t\tstorage.put(clsFullName, codeInfo);\n\t}\n\n\t@Override\n\tpublic void remove(String clsFullName) {\n\t\tstorage.remove(clsFullName);\n\t}\n\n\t@NotNull\n\t@Override\n\tpublic ICodeInfo get(String clsFullName) {\n\t\tICodeInfo codeInfo = storage.get(clsFullName);\n\t\tif (codeInfo == null) {\n\t\t\treturn ICodeInfo.EMPTY;\n\t\t}\n\t\treturn codeInfo;\n\t}\n\n\t@Override\n\tpublic @Nullable String getCode(String clsFullName) {\n\t\tICodeInfo codeInfo = storage.get(clsFullName);\n\t\tif (codeInfo == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn codeInfo.getCodeStr();\n\t}\n\n\t@Override\n\tpublic boolean contains(String clsFullName) {\n\t\treturn storage.containsKey(clsFullName);\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\tstorage.clear();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"InMemoryCodeCache: size=\" + storage.size();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/impl/NoOpCodeCache.java",
    "content": "package jadx.api.impl;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ICodeCache;\nimport jadx.api.ICodeInfo;\n\npublic class NoOpCodeCache implements ICodeCache {\n\n\tpublic static final NoOpCodeCache INSTANCE = new NoOpCodeCache();\n\n\t@Override\n\tpublic void add(String clsFullName, ICodeInfo codeInfo) {\n\t\t// do nothing\n\t}\n\n\t@Override\n\tpublic void remove(String clsFullName) {\n\t\t// do nothing\n\t}\n\n\t@Override\n\t@NotNull\n\tpublic ICodeInfo get(String clsFullName) {\n\t\treturn ICodeInfo.EMPTY;\n\t}\n\n\t@Override\n\tpublic @Nullable String getCode(String clsFullName) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean contains(String clsFullName) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\t// do nothing\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"NoOpCodeCache\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/impl/SimpleCodeInfo.java",
    "content": "package jadx.api.impl;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.metadata.ICodeMetadata;\n\npublic class SimpleCodeInfo implements ICodeInfo {\n\n\tprivate final String code;\n\n\tpublic SimpleCodeInfo(String code) {\n\t\tthis.code = code;\n\t}\n\n\t@Override\n\tpublic String getCodeStr() {\n\t\treturn code;\n\t}\n\n\t@Override\n\tpublic ICodeMetadata getCodeMetadata() {\n\t\treturn ICodeMetadata.EMPTY;\n\t}\n\n\t@Override\n\tpublic boolean hasMetadata() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn code;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/impl/SimpleCodeWriter.java",
    "content": "package jadx.api.impl;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ICodeWriter;\nimport jadx.api.JadxArgs;\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.core.utils.Utils;\n\n/**\n * CodeWriter implementation without meta information support\n */\npublic class SimpleCodeWriter implements ICodeWriter {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SimpleCodeWriter.class);\n\n\tprotected StringBuilder buf = new StringBuilder();\n\tprotected String indentStr = \"\";\n\tprotected int indent = 0;\n\n\tprotected final boolean insertLineNumbers;\n\tprotected final String singleIndentStr;\n\tprotected final String newLineStr;\n\n\tpublic SimpleCodeWriter(JadxArgs args) {\n\t\tthis.insertLineNumbers = args.isInsertDebugLines();\n\t\tthis.singleIndentStr = args.getCodeIndentStr();\n\t\tthis.newLineStr = args.getCodeNewLineStr();\n\t\tif (insertLineNumbers) {\n\t\t\tincIndent(3);\n\t\t\tadd(indentStr);\n\t\t}\n\t}\n\n\t/**\n\t * Constructor with JadxArgs should be used.\n\t */\n\t@Deprecated\n\tpublic SimpleCodeWriter() {\n\t\tthis.insertLineNumbers = false;\n\t\tthis.singleIndentStr = JadxArgs.DEFAULT_INDENT_STR;\n\t\tthis.newLineStr = JadxArgs.DEFAULT_NEW_LINE_STR;\n\t}\n\n\t@Override\n\tpublic boolean isMetadataSupported() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic SimpleCodeWriter startLine() {\n\t\taddLine();\n\t\taddLineIndent();\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic SimpleCodeWriter startLine(char c) {\n\t\tstartLine();\n\t\tadd(c);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic SimpleCodeWriter startLine(String str) {\n\t\tstartLine();\n\t\tadd(str);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic SimpleCodeWriter startLineWithNum(int sourceLine) {\n\t\tif (sourceLine == 0) {\n\t\t\tstartLine();\n\t\t\treturn this;\n\t\t}\n\t\tif (this.insertLineNumbers) {\n\t\t\tnewLine();\n\t\t\tattachSourceLine(sourceLine);\n\t\t\tint start = getLength();\n\t\t\tadd(\"/* \").add(Integer.toString(sourceLine)).add(\" */ \");\n\t\t\tint len = getLength() - start;\n\t\t\tif (indentStr.length() > len) {\n\t\t\t\tadd(indentStr.substring(len));\n\t\t\t}\n\t\t} else {\n\t\t\tstartLine();\n\t\t\tattachSourceLine(sourceLine);\n\t\t}\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic SimpleCodeWriter addMultiLine(String str) {\n\t\tif (str.contains(newLineStr)) {\n\t\t\tbuf.append(str.replace(newLineStr, newLineStr + indentStr));\n\t\t} else {\n\t\t\tbuf.append(str);\n\t\t}\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic SimpleCodeWriter add(String str) {\n\t\tbuf.append(str);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic SimpleCodeWriter add(char c) {\n\t\tbuf.append(c);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic ICodeWriter add(ICodeWriter cw) {\n\t\tbuf.append(cw.getCodeStr());\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic SimpleCodeWriter newLine() {\n\t\taddLine();\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic SimpleCodeWriter addIndent() {\n\t\tadd(singleIndentStr);\n\t\treturn this;\n\t}\n\n\tprotected void addLine() {\n\t\tbuf.append(newLineStr);\n\t}\n\n\tprotected SimpleCodeWriter addLineIndent() {\n\t\tbuf.append(indentStr);\n\t\treturn this;\n\t}\n\n\tprivate void updateIndent() {\n\t\tthis.indentStr = Utils.strRepeat(singleIndentStr, indent);\n\t}\n\n\t@Override\n\tpublic void incIndent() {\n\t\tincIndent(1);\n\t}\n\n\t@Override\n\tpublic void decIndent() {\n\t\tdecIndent(1);\n\t}\n\n\tprivate void incIndent(int c) {\n\t\tthis.indent += c;\n\t\tupdateIndent();\n\t}\n\n\tprivate void decIndent(int c) {\n\t\tthis.indent -= c;\n\t\tif (this.indent < 0) {\n\t\t\tLOG.warn(\"Indent < 0\");\n\t\t\tthis.indent = 0;\n\t\t}\n\t\tupdateIndent();\n\t}\n\n\t@Override\n\tpublic int getIndent() {\n\t\treturn indent;\n\t}\n\n\t@Override\n\tpublic void setIndent(int indent) {\n\t\tthis.indent = indent;\n\t\tupdateIndent();\n\t}\n\n\t@Override\n\tpublic int getLine() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int getLineStartPos() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void attachDefinition(ICodeNodeRef obj) {\n\t\t// no op\n\t}\n\n\t@Override\n\tpublic void attachAnnotation(ICodeAnnotation obj) {\n\t\t// no op\n\t}\n\n\t@Override\n\tpublic void attachLineAnnotation(ICodeAnnotation obj) {\n\t\t// no op\n\t}\n\n\t@Override\n\tpublic void attachSourceLine(int sourceLine) {\n\t\t// no op\n\t}\n\n\t@Override\n\tpublic ICodeInfo finish() {\n\t\tString code = getStringWithoutFirstEmptyLine();\n\t\tbuf = null;\n\t\treturn new SimpleCodeInfo(code);\n\t}\n\n\tprivate String getStringWithoutFirstEmptyLine() {\n\t\tint len = newLineStr.length();\n\t\tif (buf.length() > len && buf.substring(0, len).equals(newLineStr)) {\n\t\t\treturn buf.substring(len);\n\t\t}\n\t\treturn buf.toString();\n\t}\n\n\t@Override\n\tpublic int getLength() {\n\t\treturn buf.length();\n\t}\n\n\t@Override\n\tpublic StringBuilder getRawBuf() {\n\t\treturn buf;\n\t}\n\n\t@Override\n\tpublic Map<Integer, ICodeAnnotation> getRawAnnotations() {\n\t\treturn Collections.emptyMap();\n\t}\n\n\t@Override\n\tpublic String getCodeStr() {\n\t\treturn buf.toString();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getCodeStr();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/impl/passes/DecompilePassWrapper.java",
    "content": "package jadx.api.impl.passes;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.pass.JadxPass;\nimport jadx.api.plugins.pass.types.JadxDecompilePass;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.utils.exceptions.JadxException;\n\npublic class DecompilePassWrapper extends AbstractVisitor implements IPassWrapperVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DecompilePassWrapper.class);\n\n\tprivate final JadxDecompilePass decompilePass;\n\n\tpublic DecompilePassWrapper(JadxDecompilePass decompilePass) {\n\t\tthis.decompilePass = decompilePass;\n\t}\n\n\t@Override\n\tpublic JadxPass getPass() {\n\t\treturn decompilePass;\n\t}\n\n\t@Override\n\tpublic void init(RootNode root) throws JadxException {\n\t\ttry {\n\t\t\tdecompilePass.init(root);\n\t\t} catch (StackOverflowError | Exception e) {\n\t\t\tLOG.error(\"Error in decompile pass init: {}\", this, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) throws JadxException {\n\t\ttry {\n\t\t\treturn decompilePass.visit(cls);\n\t\t} catch (StackOverflowError | Exception e) {\n\t\t\tcls.addError(\"Error in decompile pass: \" + this, e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\ttry {\n\t\t\tdecompilePass.visit(mth);\n\t\t} catch (StackOverflowError | Exception e) {\n\t\t\tmth.addError(\"Error in decompile pass: \" + this, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn decompilePass.getInfo().getName();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/impl/passes/IPassWrapperVisitor.java",
    "content": "package jadx.api.impl.passes;\n\nimport jadx.api.plugins.pass.JadxPass;\nimport jadx.core.dex.visitors.IDexTreeVisitor;\n\npublic interface IPassWrapperVisitor extends IDexTreeVisitor {\n\n\tJadxPass getPass();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/impl/passes/PreparePassWrapper.java",
    "content": "package jadx.api.impl.passes;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.pass.JadxPass;\nimport jadx.api.plugins.pass.types.JadxPreparePass;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.utils.exceptions.JadxException;\n\npublic class PreparePassWrapper extends AbstractVisitor implements IPassWrapperVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(PreparePassWrapper.class);\n\n\tprivate final JadxPreparePass preparePass;\n\n\tpublic PreparePassWrapper(JadxPreparePass preparePass) {\n\t\tthis.preparePass = preparePass;\n\t}\n\n\t@Override\n\tpublic JadxPass getPass() {\n\t\treturn preparePass;\n\t}\n\n\t@Override\n\tpublic void init(RootNode root) throws JadxException {\n\t\ttry {\n\t\t\tpreparePass.init(root);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Error in prepare pass init: {}\", this, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn preparePass.getInfo().getName();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/metadata/ICodeAnnotation.java",
    "content": "package jadx.api.metadata;\n\npublic interface ICodeAnnotation {\n\n\tenum AnnType {\n\t\tCLASS,\n\t\tFIELD,\n\t\tMETHOD,\n\t\tPKG,\n\t\tVAR,\n\t\tVAR_REF,\n\t\tDECLARATION,\n\t\tOFFSET,\n\t\tEND // class or method body end\n\t}\n\n\tAnnType getAnnType();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/metadata/ICodeMetadata.java",
    "content": "package jadx.api.metadata;\n\nimport java.util.Map;\nimport java.util.function.BiFunction;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.metadata.impl.CodeMetadataStorage;\n\npublic interface ICodeMetadata {\n\n\tICodeMetadata EMPTY = CodeMetadataStorage.empty();\n\n\t@Nullable\n\tICodeAnnotation getAt(int position);\n\n\t@Nullable\n\tICodeAnnotation getClosestUp(int position);\n\n\t@Nullable\n\tICodeAnnotation searchUp(int position, ICodeAnnotation.AnnType annType);\n\n\t@Nullable\n\tICodeAnnotation searchUp(int position, int limitPos, ICodeAnnotation.AnnType annType);\n\n\t/**\n\t * Iterate code annotations from {@code startPos} to smaller positions.\n\t *\n\t * @param visitor\n\t *                return not null value to stop iterations\n\t */\n\t@Nullable\n\t<T> T searchUp(int startPos, BiFunction<Integer, ICodeAnnotation, T> visitor);\n\n\t/**\n\t * Iterate code annotations from {@code startPos} to higher positions.\n\t *\n\t * @param visitor\n\t *                return not null value to stop iterations\n\t */\n\t@Nullable\n\t<T> T searchDown(int startPos, BiFunction<Integer, ICodeAnnotation, T> visitor);\n\n\t/**\n\t * Get current node at position (can be enclosing class or method)\n\t */\n\t@Nullable\n\tICodeNodeRef getNodeAt(int position);\n\n\t/**\n\t * Any definition of class or method below position\n\t */\n\t@Nullable\n\tICodeNodeRef getNodeBelow(int position);\n\n\tMap<Integer, ICodeAnnotation> getAsMap();\n\n\tMap<Integer, Integer> getLineMapping();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/metadata/ICodeNodeRef.java",
    "content": "package jadx.api.metadata;\n\npublic interface ICodeNodeRef extends ICodeAnnotation {\n\tint getDefPosition();\n\n\tvoid setDefPosition(int pos);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/metadata/annotations/InsnCodeOffset.java",
    "content": "package jadx.api.metadata.annotations;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ICodeWriter;\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic class InsnCodeOffset implements ICodeAnnotation {\n\n\tpublic static void attach(ICodeWriter code, InsnNode insn) {\n\t\tif (insn == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (code.isMetadataSupported()) {\n\t\t\tInsnCodeOffset ann = from(insn);\n\t\t\tif (ann != null) {\n\t\t\t\tcode.attachLineAnnotation(ann);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void attach(ICodeWriter code, int offset) {\n\t\tif (offset >= 0 && code.isMetadataSupported()) {\n\t\t\tcode.attachLineAnnotation(new InsnCodeOffset(offset));\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic static InsnCodeOffset from(InsnNode insn) {\n\t\tint offset = insn.getOffset();\n\t\tif (offset < 0) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new InsnCodeOffset(offset);\n\t}\n\n\tprivate final int offset;\n\n\tpublic InsnCodeOffset(int offset) {\n\t\tthis.offset = offset;\n\t}\n\n\tpublic int getOffset() {\n\t\treturn offset;\n\t}\n\n\t@Override\n\tpublic AnnType getAnnType() {\n\t\treturn AnnType.OFFSET;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"offset=\" + offset;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/metadata/annotations/NodeDeclareRef.java",
    "content": "package jadx.api.metadata.annotations;\n\nimport java.util.Objects;\n\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeNodeRef;\n\npublic class NodeDeclareRef implements ICodeAnnotation {\n\n\tprivate final ICodeNodeRef node;\n\n\tprivate int defPos;\n\n\tpublic NodeDeclareRef(ICodeNodeRef node) {\n\t\tthis.node = Objects.requireNonNull(node);\n\t}\n\n\tpublic ICodeNodeRef getNode() {\n\t\treturn node;\n\t}\n\n\tpublic int getDefPos() {\n\t\treturn defPos;\n\t}\n\n\tpublic void setDefPos(int defPos) {\n\t\tthis.defPos = defPos;\n\t}\n\n\t@Override\n\tpublic AnnType getAnnType() {\n\t\treturn AnnType.DECLARATION;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof NodeDeclareRef)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn node.equals(((NodeDeclareRef) o).node);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn node.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"NodeDeclareRef{\" + node + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/metadata/annotations/NodeEnd.java",
    "content": "package jadx.api.metadata.annotations;\n\nimport jadx.api.metadata.ICodeAnnotation;\n\npublic class NodeEnd implements ICodeAnnotation {\n\n\tpublic static final NodeEnd VALUE = new NodeEnd();\n\n\tprivate NodeEnd() {\n\t}\n\n\t@Override\n\tpublic AnnType getAnnType() {\n\t\treturn AnnType.END;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"END\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/metadata/annotations/VarNode.java",
    "content": "package jadx.api.metadata.annotations;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.CodeVar;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.MethodNode;\n\n/**\n * Variable info\n */\npublic class VarNode implements ICodeNodeRef {\n\n\t@Nullable\n\tpublic static VarNode get(MethodNode mth, RegisterArg reg) {\n\t\tSSAVar ssaVar = reg.getSVar();\n\t\tif (ssaVar == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn get(mth, ssaVar);\n\t}\n\n\t@Nullable\n\tpublic static VarNode get(MethodNode mth, CodeVar codeVar) {\n\t\treturn get(mth, codeVar.getAnySsaVar());\n\t}\n\n\t@Nullable\n\tpublic static VarNode get(MethodNode mth, SSAVar ssaVar) {\n\t\tCodeVar codeVar = ssaVar.getCodeVar();\n\t\tif (codeVar.isThis()) {\n\t\t\treturn null;\n\t\t}\n\t\tVarNode cachedVarNode = codeVar.getCachedVarNode();\n\t\tif (cachedVarNode != null) {\n\t\t\treturn cachedVarNode;\n\t\t}\n\t\tVarNode newVarNode = new VarNode(mth, ssaVar);\n\t\tcodeVar.setCachedVarNode(newVarNode);\n\t\treturn newVarNode;\n\t}\n\n\t@Nullable\n\tpublic static ICodeAnnotation getRef(MethodNode mth, RegisterArg reg) {\n\t\tVarNode varNode = get(mth, reg);\n\t\tif (varNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn varNode.getVarRef();\n\t}\n\n\tprivate final MethodNode mth;\n\tprivate final int reg;\n\tprivate final int ssa;\n\tprivate final ArgType type;\n\tprivate @Nullable String name;\n\tprivate int defPos;\n\n\tprivate final VarRef varRef;\n\n\tprotected VarNode(MethodNode mth, SSAVar ssaVar) {\n\t\tthis(mth, ssaVar.getRegNum(), ssaVar.getVersion(),\n\t\t\t\tssaVar.getCodeVar().getType(), ssaVar.getCodeVar().getName());\n\t}\n\n\tpublic VarNode(MethodNode mth, int reg, int ssa, ArgType type, String name) {\n\t\tthis.mth = mth;\n\t\tthis.reg = reg;\n\t\tthis.ssa = ssa;\n\t\tthis.type = type;\n\t\tthis.name = name;\n\t\tthis.varRef = VarRef.fromVarNode(this);\n\t}\n\n\tpublic MethodNode getMth() {\n\t\treturn mth;\n\t}\n\n\tpublic int getReg() {\n\t\treturn reg;\n\t}\n\n\tpublic int getSsa() {\n\t\treturn ssa;\n\t}\n\n\tpublic ArgType getType() {\n\t\treturn type;\n\t}\n\n\t@Nullable\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic VarRef getVarRef() {\n\t\treturn varRef;\n\t}\n\n\t@Override\n\tpublic int getDefPosition() {\n\t\treturn defPos;\n\t}\n\n\t@Override\n\tpublic void setDefPosition(int pos) {\n\t\tthis.defPos = pos;\n\t}\n\n\t@Override\n\tpublic AnnType getAnnType() {\n\t\treturn AnnType.VAR;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint h = 31 * getReg() + getSsa();\n\t\treturn 31 * h + mth.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof VarNode)) {\n\t\t\treturn false;\n\t\t}\n\t\tVarNode other = (VarNode) o;\n\t\treturn getReg() == other.getReg()\n\t\t\t\t&& getSsa() == other.getSsa()\n\t\t\t\t&& getMth().equals(other.getMth());\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"VarNode{r\" + reg + 'v' + ssa + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/metadata/annotations/VarRef.java",
    "content": "package jadx.api.metadata.annotations;\n\nimport jadx.api.metadata.ICodeAnnotation;\n\n/**\n * Variable reference by position of VarNode in code metadata.\n * <br>\n * Because on creation position not yet known,\n * VarRef created using VarNode as a source of ref pos during serialization.\n * <br>\n * On metadata deserialization created with ref pos directly.\n */\npublic abstract class VarRef implements ICodeAnnotation {\n\n\tpublic static VarRef fromPos(int refPos) {\n\t\tif (refPos == 0) {\n\t\t\tthrow new IllegalArgumentException(\"Zero refPos\");\n\t\t}\n\t\treturn new FixedVarRef(refPos);\n\t}\n\n\tpublic static VarRef fromVarNode(VarNode varNode) {\n\t\treturn new RelatedVarRef(varNode);\n\t}\n\n\tpublic abstract int getRefPos();\n\n\t@Override\n\tpublic AnnType getAnnType() {\n\t\treturn AnnType.VAR_REF;\n\t}\n\n\tpublic static final class FixedVarRef extends VarRef {\n\t\tprivate final int refPos;\n\n\t\tpublic FixedVarRef(int refPos) {\n\t\t\tthis.refPos = refPos;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getRefPos() {\n\t\t\treturn refPos;\n\t\t}\n\t}\n\n\tpublic static final class RelatedVarRef extends VarRef {\n\t\tprivate final VarNode varNode;\n\n\t\tpublic RelatedVarRef(VarNode varNode) {\n\t\t\tthis.varNode = varNode;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getRefPos() {\n\t\t\treturn varNode.getDefPosition();\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"VarRef{\" + varNode + \", name=\" + varNode.getName() + \", mth=\" + varNode.getMth() + '}';\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"VarRef{\" + getRefPos() + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/metadata/impl/CodeMetadataStorage.java",
    "content": "package jadx.api.metadata.impl;\n\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Map;\nimport java.util.NavigableMap;\nimport java.util.TreeMap;\nimport java.util.function.BiFunction;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeAnnotation.AnnType;\nimport jadx.api.metadata.ICodeMetadata;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.metadata.annotations.NodeDeclareRef;\nimport jadx.core.utils.Utils;\n\npublic class CodeMetadataStorage implements ICodeMetadata {\n\n\tpublic static ICodeMetadata build(Map<Integer, Integer> lines, Map<Integer, ICodeAnnotation> map) {\n\t\tif (map.isEmpty() && lines.isEmpty()) {\n\t\t\treturn ICodeMetadata.EMPTY;\n\t\t}\n\t\tComparator<Integer> reverseCmp = Comparator.comparingInt(Integer::intValue).reversed();\n\t\tNavigableMap<Integer, ICodeAnnotation> navMap = new TreeMap<>(reverseCmp);\n\t\tnavMap.putAll(map);\n\t\treturn new CodeMetadataStorage(lines, navMap);\n\t}\n\n\tpublic static ICodeMetadata empty() {\n\t\treturn new CodeMetadataStorage(Collections.emptyMap(), Collections.emptyNavigableMap());\n\t}\n\n\t// <decomp file line number> -> <dex debug line number>\n\tprivate final Map<Integer, Integer> lines;\n\n\t// <character index into the file> -> <code annotation>\n\t// the key is what is returned by AbstractCodeArea#getCaretPos() when clicking in a code panel.\n\tprivate final NavigableMap<Integer, ICodeAnnotation> navMap;\n\n\tprivate CodeMetadataStorage(Map<Integer, Integer> lines, NavigableMap<Integer, ICodeAnnotation> navMap) {\n\t\tthis.lines = lines;\n\t\tthis.navMap = navMap;\n\t}\n\n\t@Override\n\tpublic ICodeAnnotation getAt(int position) {\n\t\treturn navMap.get(position);\n\t}\n\n\t@Override\n\tpublic @Nullable ICodeAnnotation getClosestUp(int position) {\n\t\tMap.Entry<Integer, ICodeAnnotation> entryBefore = navMap.higherEntry(position);\n\t\treturn entryBefore != null ? entryBefore.getValue() : null;\n\t}\n\n\t@Override\n\tpublic @Nullable ICodeAnnotation searchUp(int position, AnnType annType) {\n\t\tfor (ICodeAnnotation v : navMap.tailMap(position, true).values()) {\n\t\t\tif (v.getAnnType() == annType) {\n\t\t\t\treturn v;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic @Nullable ICodeAnnotation searchUp(int position, int limitPos, AnnType annType) {\n\t\tfor (ICodeAnnotation v : navMap.subMap(position, true, limitPos, true).values()) {\n\t\t\tif (v.getAnnType() == annType) {\n\t\t\t\treturn v;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic <T> @Nullable T searchUp(int startPos, BiFunction<Integer, ICodeAnnotation, T> visitor) {\n\t\tfor (Map.Entry<Integer, ICodeAnnotation> entry : navMap.tailMap(startPos, true).entrySet()) {\n\t\t\tT value = visitor.apply(entry.getKey(), entry.getValue());\n\t\t\tif (value != null) {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic <T> @Nullable T searchDown(int startPos, BiFunction<Integer, ICodeAnnotation, T> visitor) {\n\t\tNavigableMap<Integer, ICodeAnnotation> map = navMap.headMap(startPos, true).descendingMap();\n\t\tfor (Map.Entry<Integer, ICodeAnnotation> entry : map.entrySet()) {\n\t\t\tT value = visitor.apply(entry.getKey(), entry.getValue());\n\t\t\tif (value != null) {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic ICodeNodeRef getNodeAt(int position) {\n\t\tint nesting = 0;\n\t\tfor (ICodeAnnotation ann : navMap.tailMap(position, true).values()) {\n\t\t\tswitch (ann.getAnnType()) {\n\t\t\t\tcase END:\n\t\t\t\t\tnesting++;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DECLARATION:\n\t\t\t\t\tICodeNodeRef node = ((NodeDeclareRef) ann).getNode();\n\t\t\t\t\tAnnType nodeType = node.getAnnType();\n\t\t\t\t\tif (nodeType == AnnType.CLASS || nodeType == AnnType.METHOD) {\n\t\t\t\t\t\tif (nesting == 0) {\n\t\t\t\t\t\t\treturn node;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tnesting--;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic ICodeNodeRef getNodeBelow(int position) {\n\t\tfor (ICodeAnnotation ann : navMap.headMap(position, true).descendingMap().values()) {\n\t\t\tif (ann.getAnnType() == AnnType.DECLARATION) {\n\t\t\t\tICodeNodeRef node = ((NodeDeclareRef) ann).getNode();\n\t\t\t\tAnnType nodeType = node.getAnnType();\n\t\t\t\tif (nodeType == AnnType.CLASS || nodeType == AnnType.METHOD) {\n\t\t\t\t\treturn node;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic NavigableMap<Integer, ICodeAnnotation> getAsMap() {\n\t\treturn navMap;\n\t}\n\n\t@Override\n\tpublic Map<Integer, Integer> getLineMapping() {\n\t\treturn lines;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"CodeMetadata{\\nlines=\" + lines\n\t\t\t\t+ \"\\nannotations=\\n \" + Utils.listToString(navMap.descendingMap().entrySet(), \"\\n \") + \"\\n}\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/CustomResourcesLoader.java",
    "content": "package jadx.api.plugins;\n\nimport java.io.Closeable;\nimport java.io.File;\nimport java.util.List;\n\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourcesLoader;\n\npublic interface CustomResourcesLoader extends Closeable {\n\t/**\n\t * Load resources from file to list of ResourceFile\n\t *\n\t * @param list list to add loaded resources\n\t * @param file file to load\n\t * @return true if file was loaded\n\t */\n\tboolean load(ResourcesLoader loader, List<ResourceFile> list, File file);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/JadxPlugin.java",
    "content": "package jadx.api.plugins;\n\nimport jadx.api.plugins.pass.types.JadxAfterLoadPass;\nimport jadx.api.plugins.pass.types.JadxPreparePass;\n\n/**\n * Base interface for all jadx plugins\n * <br>\n * To create new plugin implement this interface and add to resources\n * a {@code META-INF/services/jadx.api.plugins.JadxPlugin} file with a full name of your class.\n */\npublic interface JadxPlugin {\n\n\t/**\n\t * Method for provide plugin information, like name and description.\n\t * Can be invoked several times.\n\t */\n\tJadxPluginInfo getPluginInfo();\n\n\t/**\n\t * Init plugin.\n\t * Use {@link JadxPluginContext} to register passes, code inputs and options.\n\t * For long operation, prefer {@link JadxPreparePass} or {@link JadxAfterLoadPass} instead.\n\t */\n\tvoid init(JadxPluginContext context);\n\n\t/**\n\t * Plugin unload handler.\n\t * Can be used to clean up resources on plugin unloading.\n\t */\n\tdefault void unload() {\n\t\t// optional method\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/JadxPluginContext.java",
    "content": "package jadx.api.plugins;\n\nimport java.util.function.Supplier;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.JadxDecompiler;\nimport jadx.api.plugins.data.IJadxFiles;\nimport jadx.api.plugins.data.IJadxPlugins;\nimport jadx.api.plugins.events.IJadxEvents;\nimport jadx.api.plugins.gui.JadxGuiContext;\nimport jadx.api.plugins.input.JadxCodeInput;\nimport jadx.api.plugins.options.JadxPluginOptions;\nimport jadx.api.plugins.pass.JadxPass;\nimport jadx.api.plugins.resources.IResourcesLoader;\nimport jadx.zip.ZipReader;\n\npublic interface JadxPluginContext {\n\n\tJadxArgs getArgs();\n\n\tJadxDecompiler getDecompiler();\n\n\tvoid addPass(JadxPass pass);\n\n\tvoid addCodeInput(JadxCodeInput codeInput);\n\n\tvoid registerOptions(JadxPluginOptions options);\n\n\t/**\n\t * Function to calculate hash of all options which can change output code.\n\t * Hash for input files ({@link JadxArgs#getInputFiles()}) and registered options\n\t * calculated by default implementations.\n\t */\n\tvoid registerInputsHashSupplier(Supplier<String> supplier);\n\n\t/**\n\t * Customize resource loading\n\t */\n\tIResourcesLoader getResourcesLoader();\n\n\t/**\n\t * Access to jadx-gui specific methods\n\t */\n\t@Nullable\n\tJadxGuiContext getGuiContext();\n\n\t/**\n\t * Subscribe and send events\n\t */\n\tIJadxEvents events();\n\n\t/**\n\t * Access to registered plugins and runtime data\n\t */\n\tIJadxPlugins plugins();\n\n\t/**\n\t * Access to plugin specific files and directories\n\t */\n\tIJadxFiles files();\n\n\t/**\n\t * Custom jadx zip reader to fight tampering and provide additional security checks\n\t */\n\tZipReader getZipReader();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/JadxPluginInfo.java",
    "content": "package jadx.api.plugins;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic class JadxPluginInfo {\n\tprivate final String pluginId;\n\tprivate final String name;\n\tprivate final String description;\n\tprivate String homepage;\n\n\t/**\n\t * Conflicting plugins should have the same 'provides' property; only one will be loaded\n\t */\n\tprivate String provides;\n\n\t/**\n\t * Minimum required jadx version to run this plugin.\n\t * <br>\n\t * Format: \"<stable version>, r<revision number of unstable version>\".\n\t * Example: \"1.5.1, r2305\"\n\t *\n\t * @see <a href=\"https://github.com/skylot/jadx/wiki/Jadx-plugins-guide#required-jadx-version\">wiki\n\t *      page</a>\n\t *      for details.\n\t */\n\tprivate @Nullable String requiredJadxVersion;\n\n\tpublic JadxPluginInfo(String id, String name, String description) {\n\t\tthis(id, name, description, \"\", id);\n\t}\n\n\tpublic JadxPluginInfo(String pluginId, String name, String description, String provides) {\n\t\tthis(pluginId, name, description, \"\", provides);\n\t}\n\n\tpublic JadxPluginInfo(String pluginId, String name, String description, String homepage, String provides) {\n\t\tthis.pluginId = pluginId;\n\t\tthis.name = name;\n\t\tthis.description = description;\n\t\tthis.homepage = homepage;\n\t\tthis.provides = provides;\n\t}\n\n\tpublic String getPluginId() {\n\t\treturn pluginId;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic String getDescription() {\n\t\treturn description;\n\t}\n\n\tpublic String getHomepage() {\n\t\treturn homepage;\n\t}\n\n\tpublic void setHomepage(String homepage) {\n\t\tthis.homepage = homepage;\n\t}\n\n\tpublic String getProvides() {\n\t\treturn provides;\n\t}\n\n\tpublic void setProvides(String provides) {\n\t\tthis.provides = provides;\n\t}\n\n\tpublic @Nullable String getRequiredJadxVersion() {\n\t\treturn requiredJadxVersion;\n\t}\n\n\tpublic void setRequiredJadxVersion(@Nullable String requiredJadxVersion) {\n\t\tthis.requiredJadxVersion = requiredJadxVersion;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn pluginId + \": \" + name + \" - '\" + description + '\\'';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/JadxPluginInfoBuilder.java",
    "content": "package jadx.api.plugins;\n\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.plugins.versions.VerifyRequiredVersion;\n\npublic class JadxPluginInfoBuilder {\n\tprivate String pluginId;\n\tprivate String name;\n\tprivate String description;\n\tprivate String homepage = \"\";\n\tprivate @Nullable String requiredJadxVersion;\n\tprivate @Nullable String provides;\n\n\t/**\n\t * Start building method\n\t */\n\tpublic static JadxPluginInfoBuilder pluginId(String pluginId) {\n\t\tJadxPluginInfoBuilder builder = new JadxPluginInfoBuilder();\n\t\tbuilder.pluginId = Objects.requireNonNull(pluginId);\n\t\treturn builder;\n\t}\n\n\tprivate JadxPluginInfoBuilder() {\n\t}\n\n\tpublic JadxPluginInfoBuilder name(String name) {\n\t\tthis.name = Objects.requireNonNull(name);\n\t\treturn this;\n\t}\n\n\tpublic JadxPluginInfoBuilder description(String description) {\n\t\tthis.description = Objects.requireNonNull(description);\n\t\treturn this;\n\t}\n\n\tpublic JadxPluginInfoBuilder homepage(String homepage) {\n\t\tthis.homepage = homepage;\n\t\treturn this;\n\t}\n\n\tpublic JadxPluginInfoBuilder provides(String provides) {\n\t\tthis.provides = provides;\n\t\treturn this;\n\t}\n\n\tpublic JadxPluginInfoBuilder requiredJadxVersion(String versions) {\n\t\tthis.requiredJadxVersion = versions;\n\t\treturn this;\n\t}\n\n\tpublic JadxPluginInfo build() {\n\t\tObjects.requireNonNull(pluginId, \"PluginId is required\");\n\t\tObjects.requireNonNull(name, \"Name is required\");\n\t\tObjects.requireNonNull(description, \"Description is required\");\n\t\tif (provides == null) {\n\t\t\tprovides = pluginId;\n\t\t}\n\t\tif (requiredJadxVersion != null) {\n\t\t\tVerifyRequiredVersion.verify(requiredJadxVersion);\n\t\t}\n\t\tJadxPluginInfo pluginInfo = new JadxPluginInfo(pluginId, name, description, homepage, provides);\n\t\tpluginInfo.setRequiredJadxVersion(requiredJadxVersion);\n\t\treturn pluginInfo;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/data/IJadxFiles.java",
    "content": "package jadx.api.plugins.data;\n\nimport java.nio.file.Path;\n\npublic interface IJadxFiles {\n\n\t/**\n\t * Plugin cache directory.\n\t */\n\tPath getPluginCacheDir();\n\n\t/**\n\t * Plugin config directory.\n\t */\n\tPath getPluginConfigDir();\n\n\t/**\n\t * Plugin temp directory.\n\t */\n\tPath getPluginTempDir();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/data/IJadxPlugins.java",
    "content": "package jadx.api.plugins.data;\n\nimport jadx.api.plugins.JadxPlugin;\n\npublic interface IJadxPlugins {\n\n\tJadxPluginRuntimeData getById(String pluginId);\n\n\tJadxPluginRuntimeData getProviding(String provideId);\n\n\t<P extends JadxPlugin> P getInstance(Class<P> pluginCls);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/data/JadxPluginRuntimeData.java",
    "content": "package jadx.api.plugins.data;\n\nimport java.io.Closeable;\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.JadxPlugin;\nimport jadx.api.plugins.JadxPluginInfo;\nimport jadx.api.plugins.input.ICodeLoader;\nimport jadx.api.plugins.input.JadxCodeInput;\nimport jadx.api.plugins.options.JadxPluginOptions;\n\n/**\n * Runtime plugin data.\n */\npublic interface JadxPluginRuntimeData {\n\tboolean isInitialized();\n\n\tString getPluginId();\n\n\tJadxPlugin getPluginInstance();\n\n\tJadxPluginInfo getPluginInfo();\n\n\tList<JadxCodeInput> getCodeInputs();\n\n\t@Nullable\n\tJadxPluginOptions getOptions();\n\n\tString getInputsHash();\n\n\t/**\n\t * Convenient method to simplify code loading from custom files.\n\t */\n\tICodeLoader loadCodeFiles(List<Path> files, @Nullable Closeable closeable);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/events/IJadxEvent.java",
    "content": "package jadx.api.plugins.events;\n\npublic interface IJadxEvent {\n\n\tJadxEventType<? extends IJadxEvent> getType();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/events/IJadxEvents.java",
    "content": "package jadx.api.plugins.events;\n\nimport java.util.function.Consumer;\n\npublic interface IJadxEvents {\n\n\t/**\n\t * Send an event object.\n\t * For public event types check {@link JadxEvents} class.\n\t */\n\tvoid send(IJadxEvent event);\n\n\t/**\n\t * Register listener for specific event.\n\t * For public event types check {@link JadxEvents} class.\n\t */\n\t<E extends IJadxEvent> void addListener(JadxEventType<E> eventType, Consumer<E> listener);\n\n\t/**\n\t * Remove listener for specific event.\n\t * Listener should be same or equal object.\n\t */\n\t<E extends IJadxEvent> void removeListener(JadxEventType<E> eventType, Consumer<E> listener);\n\n\t/**\n\t * Clear all listeners.\n\t */\n\tvoid reset();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/events/JadxEventType.java",
    "content": "package jadx.api.plugins.events;\n\npublic abstract class JadxEventType<T extends IJadxEvent> {\n\n\tpublic static <E extends IJadxEvent> JadxEventType<E> create() {\n\t\treturn new JadxEventType<>() {\n\t\t};\n\t}\n\n\tpublic static <E extends IJadxEvent> JadxEventType<E> create(String name) {\n\t\treturn new JadxEventType<>() {\n\t\t\t@Override\n\t\t\tpublic String toString() {\n\t\t\t\treturn name;\n\t\t\t}\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/events/JadxEvents.java",
    "content": "package jadx.api.plugins.events;\n\nimport jadx.api.plugins.events.types.NodeRenamedByUser;\nimport jadx.api.plugins.events.types.ReloadProject;\nimport jadx.api.plugins.events.types.ReloadSettingsWindow;\nimport jadx.api.plugins.gui.ISettingsGroup;\nimport jadx.api.plugins.gui.JadxGuiSettings;\n\nimport static jadx.api.plugins.events.JadxEventType.create;\n\n/**\n * Typed and extendable enumeration of event types\n */\npublic class JadxEvents {\n\n\t/**\n\t * Notify about renaming done by user (GUI only).\n\t */\n\tpublic static final JadxEventType<NodeRenamedByUser> NODE_RENAMED_BY_USER = create(\"NODE_RENAMED_BY_USER\");\n\n\t/**\n\t * Request reload of a current project (GUI only).\n\t */\n\tpublic static final JadxEventType<ReloadProject> RELOAD_PROJECT = create(\"RELOAD_PROJECT\");\n\n\t/**\n\t * Request reload of a settings window (GUI only).\n\t * Useful for a reload custom settings group which was set with\n\t * {@link JadxGuiSettings#setCustomSettingsGroup(ISettingsGroup)}.\n\t */\n\tpublic static final JadxEventType<ReloadSettingsWindow> RELOAD_SETTINGS_WINDOW = create(\"RELOAD_SETTINGS_WINDOW\");\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/events/types/NodeRenamedByUser.java",
    "content": "package jadx.api.plugins.events.types;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.plugins.events.IJadxEvent;\nimport jadx.api.plugins.events.JadxEventType;\nimport jadx.api.plugins.events.JadxEvents;\n\npublic class NodeRenamedByUser implements IJadxEvent {\n\n\tprivate final ICodeNodeRef node;\n\tprivate final String oldName;\n\tprivate final String newName;\n\n\t/**\n\t * Optional JRenameNode instance\n\t */\n\tprivate @Nullable Object renameNode;\n\n\t/**\n\t * Request reset name to original\n\t */\n\tprivate boolean resetName = false;\n\n\tpublic NodeRenamedByUser(ICodeNodeRef node, String oldName, String newName) {\n\t\tthis.node = node;\n\t\tthis.oldName = oldName;\n\t\tthis.newName = newName;\n\t}\n\n\tpublic ICodeNodeRef getNode() {\n\t\treturn node;\n\t}\n\n\tpublic String getOldName() {\n\t\treturn oldName;\n\t}\n\n\tpublic String getNewName() {\n\t\treturn newName;\n\t}\n\n\tpublic Object getRenameNode() {\n\t\treturn renameNode;\n\t}\n\n\tpublic void setRenameNode(Object renameNode) {\n\t\tthis.renameNode = renameNode;\n\t}\n\n\tpublic boolean isResetName() {\n\t\treturn resetName;\n\t}\n\n\tpublic void setResetName(boolean resetName) {\n\t\tthis.resetName = resetName;\n\t}\n\n\t@Override\n\tpublic JadxEventType<NodeRenamedByUser> getType() {\n\t\treturn JadxEvents.NODE_RENAMED_BY_USER;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"NodeRenamedByUser{\" + node\n\t\t\t\t+ \", '\" + oldName + \"' -> '\" + newName + '\\''\n\t\t\t\t+ (resetName ? \", reset name\" : \"\")\n\t\t\t\t+ '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/events/types/ReloadProject.java",
    "content": "package jadx.api.plugins.events.types;\n\nimport jadx.api.plugins.events.IJadxEvent;\nimport jadx.api.plugins.events.JadxEventType;\nimport jadx.api.plugins.events.JadxEvents;\n\npublic class ReloadProject implements IJadxEvent {\n\n\tpublic static final ReloadProject EVENT = new ReloadProject();\n\n\tprivate ReloadProject() {\n\t\t// singleton\n\t}\n\n\t@Override\n\tpublic JadxEventType<ReloadProject> getType() {\n\t\treturn JadxEvents.RELOAD_PROJECT;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"RELOAD_PROJECT\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/events/types/ReloadSettingsWindow.java",
    "content": "package jadx.api.plugins.events.types;\n\nimport jadx.api.plugins.events.IJadxEvent;\nimport jadx.api.plugins.events.JadxEventType;\nimport jadx.api.plugins.events.JadxEvents;\n\npublic class ReloadSettingsWindow implements IJadxEvent {\n\n\tpublic static final ReloadSettingsWindow INSTANCE = new ReloadSettingsWindow();\n\n\tprivate ReloadSettingsWindow() {\n\t\t// singleton\n\t}\n\n\t@Override\n\tpublic JadxEventType<ReloadSettingsWindow> getType() {\n\t\treturn JadxEvents.RELOAD_SETTINGS_WINDOW;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"RELOAD_SETTINGS_WINDOW\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/gui/ISettingsGroup.java",
    "content": "package jadx.api.plugins.gui;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.swing.JComponent;\n\n/**\n * Settings page customization\n */\npublic interface ISettingsGroup {\n\n\t/**\n\t * Node name\n\t */\n\tString getTitle();\n\n\t/**\n\t * Custom page component\n\t */\n\tJComponent buildComponent();\n\n\t/**\n\t * Optional child nodes list\n\t */\n\tdefault List<ISettingsGroup> getSubGroups() {\n\t\treturn Collections.emptyList();\n\t}\n\n\t/**\n\t * Settings close handler.\n\t * Apply settings if 'save' param set to true.\n\t * It can be used to clean up resources.\n\t */\n\tdefault void close(boolean save) {\n\t\t// optional method\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/gui/JadxGuiContext.java",
    "content": "package jadx.api.plugins.gui;\n\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport javax.swing.ImageIcon;\nimport javax.swing.JFrame;\nimport javax.swing.KeyStroke;\n\nimport org.jetbrains.annotations.ApiStatus;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.gui.tree.ITreeNode;\nimport jadx.api.metadata.ICodeNodeRef;\n\npublic interface JadxGuiContext {\n\n\t/**\n\t * Run code in UI Thread\n\t */\n\tvoid uiRun(Runnable runnable);\n\n\t/**\n\t * Add global menu entry ('Plugins' section)\n\t */\n\tvoid addMenuAction(String name, Runnable action);\n\n\t/**\n\t * Add code viewer popup menu entry\n\t *\n\t * @param name       entry title\n\t * @param enabled    check if entry should be enabled, called on popup creation\n\t * @param keyBinding optional assigned keybinding {@link KeyStroke#getKeyStroke(String)}\n\t */\n\tvoid addPopupMenuAction(String name,\n\t\t\t@Nullable Function<ICodeNodeRef, Boolean> enabled,\n\t\t\t@Nullable String keyBinding,\n\t\t\tConsumer<ICodeNodeRef> action);\n\n\t/**\n\t * Add popup menu entry for tree node\n\t *\n\t * @param name         entry title\n\t * @param addPredicate check if entry should be added for provided node, called on popup creation\n\t */\n\t@ApiStatus.Experimental\n\tvoid addTreePopupMenuEntry(String name, Predicate<ITreeNode> addPredicate, Consumer<ITreeNode> action);\n\n\t/**\n\t * Attach new key binding to main window\n\t *\n\t * @param id         unique ID string\n\t * @param keyBinding keybinding string {@link KeyStroke#getKeyStroke(String)}\n\t * @param action     runnable action\n\t * @return false if already registered\n\t */\n\tboolean registerGlobalKeyBinding(String id, String keyBinding, Runnable action);\n\n\tvoid copyToClipboard(String str);\n\n\t/**\n\t * Access to GUI settings\n\t */\n\tJadxGuiSettings settings();\n\n\t/**\n\t * Main window component.\n\t * Can be used as a parent for creating new windows or dialogs.\n\t */\n\tJFrame getMainFrame();\n\n\t/**\n\t * Load SVG icon from jadx resources.\n\t * All available icons can be found in \"jadx-gui/src/main/resources/icons\".\n\t * Method is thread-safe.\n\t *\n\t * @param name short name in form: \"category/iconName\", example: \"nodes/publicClass\"\n\t * @return loaded and cached icon, if icon not found returns default icon: \"ui/error\"\n\t */\n\tImageIcon getSVGIcon(String name);\n\n\tICodeNodeRef getNodeUnderCaret();\n\n\tICodeNodeRef getNodeUnderMouse();\n\n\tICodeNodeRef getEnclosingNodeUnderCaret();\n\n\tICodeNodeRef getEnclosingNodeUnderMouse();\n\n\t/**\n\t * Jump to a code ref\n\t *\n\t * @return if successfully jumped to the code ref\n\t */\n\tboolean open(ICodeNodeRef ref);\n\n\t/**\n\t * Open usage dialog for a node\n\t */\n\tvoid openUsageDialog(ICodeNodeRef ref);\n\n\t/**\n\t * Reload code in active tab\n\t */\n\tvoid reloadActiveTab();\n\n\t/**\n\t * Reload code in all open tabs\n\t */\n\tvoid reloadAllTabs();\n\n\t/**\n\t * Save node rename in a project and run all needed UI updates\n\t */\n\tvoid applyNodeRename(ICodeNodeRef node);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/gui/JadxGuiSettings.java",
    "content": "package jadx.api.plugins.gui;\n\nimport java.util.List;\n\nimport jadx.api.plugins.options.OptionDescription;\n\npublic interface JadxGuiSettings {\n\n\t/**\n\t * Set plugin custom settings page\n\t */\n\tvoid setCustomSettingsGroup(ISettingsGroup group);\n\n\t/**\n\t * Helper method to build options group only for provided option list\n\t */\n\tISettingsGroup buildSettingsGroupForOptions(String title, List<OptionDescription> options);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/loader/JadxBasePluginLoader.java",
    "content": "package jadx.api.plugins.loader;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.ServiceLoader;\n\nimport jadx.api.plugins.JadxPlugin;\n\n/**\n * Loading plugins from current classpath\n */\npublic class JadxBasePluginLoader implements JadxPluginLoader {\n\n\t@Override\n\tpublic List<JadxPlugin> load() {\n\t\tList<JadxPlugin> list = new ArrayList<>();\n\t\tServiceLoader<JadxPlugin> plugins = ServiceLoader.load(JadxPlugin.class);\n\t\tfor (JadxPlugin plugin : plugins) {\n\t\t\tlist.add(plugin);\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\t// nothing to close\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/loader/JadxPluginLoader.java",
    "content": "package jadx.api.plugins.loader;\n\nimport java.io.Closeable;\nimport java.util.List;\n\nimport jadx.api.plugins.JadxPlugin;\n\npublic interface JadxPluginLoader extends Closeable {\n\n\tList<JadxPlugin> load();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/options/JadxPluginOptions.java",
    "content": "package jadx.api.plugins.options;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic interface JadxPluginOptions {\n\n\tvoid setOptions(Map<String, String> options);\n\n\tList<OptionDescription> getOptionsDescriptions();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/options/OptionDescription.java",
    "content": "package jadx.api.plugins.options;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic interface OptionDescription {\n\n\tString name();\n\n\tString description();\n\n\t/**\n\t * Possible values.\n\t * Empty if not a limited set\n\t */\n\tList<String> values();\n\n\t/**\n\t * Default value.\n\t * Null if required\n\t */\n\t@Nullable\n\tString defaultValue();\n\n\tdefault OptionType getType() {\n\t\treturn OptionType.STRING;\n\t}\n\n\tdefault Set<OptionFlag> getFlags() {\n\t\treturn Collections.emptySet();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/options/OptionFlag.java",
    "content": "package jadx.api.plugins.options;\n\npublic enum OptionFlag {\n\t/**\n\t * Store in project settings instead global (for jadx-gui)\n\t */\n\tPER_PROJECT,\n\n\t/**\n\t * Do not show this option in jadx-gui (useful if option is configured with custom ui)\n\t */\n\tHIDE_IN_GUI,\n\n\t/**\n\t * Option will be read-only in jadx-gui (can be used for calculated properties)\n\t */\n\tDISABLE_IN_GUI,\n\n\t/**\n\t * Add this flag only if the option does not affect generated code.\n\t * If added, option value change will not cause code cache reset.\n\t */\n\tNOT_CHANGING_CODE,\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/options/OptionType.java",
    "content": "package jadx.api.plugins.options;\n\npublic enum OptionType {\n\tSTRING,\n\tNUMBER,\n\tBOOLEAN\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/options/impl/BaseOptionsParser.java",
    "content": "package jadx.api.plugins.options.impl;\n\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport jadx.api.plugins.options.JadxPluginOptions;\n\n/**\n * Prefer {@link BasePluginOptionsBuilder} as a better way to init and parse options\n */\n@Deprecated\npublic abstract class BaseOptionsParser implements JadxPluginOptions {\n\n\tprotected Map<String, String> options;\n\n\t@Override\n\tpublic void setOptions(Map<String, String> options) {\n\t\tthis.options = options;\n\t\tparseOptions();\n\t}\n\n\tpublic abstract void parseOptions();\n\n\tpublic boolean getBooleanOption(String key, boolean defValue) {\n\t\tString val = options.get(key);\n\t\tif (val == null) {\n\t\t\treturn defValue;\n\t\t}\n\t\tString valLower = val.toLowerCase(Locale.ROOT);\n\t\tif (valLower.equals(\"yes\") || valLower.equals(\"true\")) {\n\t\t\treturn true;\n\t\t}\n\t\tif (valLower.equals(\"no\") || valLower.equals(\"false\")) {\n\t\t\treturn false;\n\t\t}\n\t\tthrow new IllegalArgumentException(\"Unknown value '\" + val + \"' for option '\" + key + \"'\"\n\t\t\t\t+ \", expect: 'yes' or 'no'\");\n\t}\n\n\tpublic <T> T getOption(String key, Function<String, T> parse, T defValue) {\n\t\tString val = options.get(key);\n\t\tif (val == null) {\n\t\t\treturn defValue;\n\t\t}\n\t\treturn parse.apply(val);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/options/impl/BasePluginOptionsBuilder.java",
    "content": "package jadx.api.plugins.options.impl;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.options.JadxPluginOptions;\nimport jadx.api.plugins.options.OptionDescription;\nimport jadx.api.plugins.options.OptionFlag;\nimport jadx.api.plugins.options.OptionType;\n\n/**\n * Base class for {@link JadxPluginOptions} implementation\n * <p>\n * Override {@link BasePluginOptionsBuilder#registerOptions()} method\n * and use *option methods to add option info.\n */\n@SuppressWarnings(\"unused\")\npublic abstract class BasePluginOptionsBuilder implements JadxPluginOptions {\n\n\tprivate final List<OptionData<?>> options = new ArrayList<>();\n\n\tpublic abstract void registerOptions();\n\n\tpublic BasePluginOptionsBuilder() {\n\t\tregisterOptions();\n\t\tfor (OptionData<?> option : options) {\n\t\t\toption.validate();\n\t\t}\n\t}\n\n\tpublic <T> OptionBuilder<T> option(String name) {\n\t\treturn addOption(new OptionData<>(name));\n\t}\n\n\tpublic <T> OptionBuilder<T> option(String name, Class<T> optionType) {\n\t\treturn addOption(new OptionData<>(name));\n\t}\n\n\tpublic OptionBuilder<Boolean> boolOption(String name) {\n\t\treturn addOption(\n\t\t\t\tnew OptionData<Boolean>(name)\n\t\t\t\t\t\t.type(OptionType.BOOLEAN)\n\t\t\t\t\t\t.values(Arrays.asList(Boolean.TRUE, Boolean.FALSE))\n\t\t\t\t\t\t.formatter(b -> b ? \"yes\" : \"no\")\n\t\t\t\t\t\t.parser(val -> parseBoolOption(name, val)));\n\t}\n\n\tpublic OptionBuilder<String> strOption(String name) {\n\t\treturn addOption(\n\t\t\t\tnew OptionData<String>(name)\n\t\t\t\t\t\t.type(OptionType.STRING)\n\t\t\t\t\t\t.formatter(v -> v)\n\t\t\t\t\t\t.parser(v -> v));\n\t}\n\n\tpublic OptionBuilder<Integer> intOption(String name) {\n\t\treturn addOption(\n\t\t\t\tnew OptionData<Integer>(name)\n\t\t\t\t\t\t.type(OptionType.NUMBER)\n\t\t\t\t\t\t.formatter(Object::toString)\n\t\t\t\t\t\t.parser(Integer::parseInt));\n\t}\n\n\tpublic <E extends Enum<?>> OptionBuilder<E> enumOption(String name, E[] values, Function<String, E> valueOf) {\n\t\treturn addOption(\n\t\t\t\tnew OptionData<E>(name)\n\t\t\t\t\t\t.type(OptionType.STRING)\n\t\t\t\t\t\t.values(Arrays.asList(values))\n\t\t\t\t\t\t.formatter(v -> v.name().toLowerCase(Locale.ROOT))\n\t\t\t\t\t\t.parser(v -> valueOf.apply(v.toUpperCase(Locale.ROOT))));\n\t}\n\n\t@Override\n\tpublic void setOptions(Map<String, String> map) {\n\t\tfor (OptionData<?> option : options) {\n\t\t\tparseOption(option, map.get(option.name));\n\t\t}\n\t}\n\n\t@Override\n\tpublic List<OptionDescription> getOptionsDescriptions() {\n\t\treturn Collections.unmodifiableList(options);\n\t}\n\n\tprivate static <T> void parseOption(OptionData<T> option, @Nullable String value) {\n\t\tT parsedValue;\n\t\tif (value == null) {\n\t\t\tparsedValue = option.defaultValue;\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tparsedValue = option.getParser().apply(value);\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new RuntimeException(\"Parse failed for option: \" + option.name + \", value: \" + value, e);\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\toption.getSetter().accept(parsedValue);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Setter invoke failed for option: \" + option.name + \", value: \" + parsedValue, e);\n\t\t}\n\t}\n\n\tprivate static boolean parseBoolOption(String name, String val) {\n\t\tString valLower = val.trim().toLowerCase(Locale.ROOT);\n\t\tif (valLower.equals(\"yes\") || valLower.equals(\"true\")) {\n\t\t\treturn true;\n\t\t}\n\t\tif (valLower.equals(\"no\") || valLower.equals(\"false\")) {\n\t\t\treturn false;\n\t\t}\n\t\tthrow new IllegalArgumentException(\"Unknown value '\" + val + \"' for option '\" + name + \"', expect: 'yes' or 'no'\");\n\t}\n\n\tprivate <T> OptionBuilder<T> addOption(OptionBuilder<T> optionData) {\n\t\tthis.options.add((OptionData<?>) optionData);\n\t\treturn optionData;\n\t}\n\n\tprotected static class OptionData<T> implements OptionDescription, OptionBuilder<T> {\n\t\tprivate final String name;\n\t\tprivate String desc;\n\t\tprivate List<T> values = Collections.emptyList();\n\t\tprivate OptionType type = OptionType.STRING;\n\t\tprivate Set<OptionFlag> flags = EnumSet.noneOf(OptionFlag.class);\n\t\tprivate Function<String, T> parser;\n\t\tprivate Function<T, String> formatter;\n\t\tprivate Consumer<T> setter;\n\t\tprivate T defaultValue;\n\n\t\tpublic OptionData(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\t@Override\n\t\tpublic String name() {\n\t\t\treturn name;\n\t\t}\n\n\t\t@Override\n\t\tpublic String description() {\n\t\t\treturn desc;\n\t\t}\n\n\t\t@Override\n\t\tpublic List<String> values() {\n\t\t\treturn values.stream().map(formatter).collect(Collectors.toList());\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable String defaultValue() {\n\t\t\treturn formatter.apply(defaultValue);\n\t\t}\n\n\t\t@Override\n\t\tpublic OptionType getType() {\n\t\t\treturn type;\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<OptionFlag> getFlags() {\n\t\t\treturn flags;\n\t\t}\n\n\t\t@Override\n\t\tpublic OptionBuilder<T> description(String desc) {\n\t\t\tthis.desc = desc;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic OptionBuilder<T> defaultValue(@Nullable T defValue) {\n\t\t\tthis.defaultValue = defValue;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic OptionBuilder<T> parser(Function<String, T> parser) {\n\t\t\tthis.parser = parser;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic OptionBuilder<T> formatter(Function<T, String> formatter) {\n\t\t\tthis.formatter = formatter;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic OptionBuilder<T> setter(Consumer<T> setter) {\n\t\t\tthis.setter = setter;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic OptionBuilder<T> type(OptionType optionType) {\n\t\t\tthis.type = optionType;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic OptionBuilder<T> flags(OptionFlag... flags) {\n\t\t\tthis.flags = EnumSet.copyOf(Arrays.asList(flags));\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic OptionBuilder<T> values(List<T> values) {\n\t\t\tthis.values = values;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Function<String, T> getParser() {\n\t\t\treturn parser;\n\t\t}\n\n\t\tpublic Function<T, String> getFormatter() {\n\t\t\treturn formatter;\n\t\t}\n\n\t\tpublic Consumer<T> getSetter() {\n\t\t\treturn setter;\n\t\t}\n\n\t\tpublic void validate() {\n\t\t\tif (desc == null || desc.isEmpty()) {\n\t\t\t\tthrow new IllegalArgumentException(\"Description should be set for option: \" + name);\n\t\t\t}\n\t\t\tif (parser == null) {\n\t\t\t\tthrow new IllegalArgumentException(\"Parser should be set for option: \" + name);\n\t\t\t}\n\t\t\tif (formatter == null) {\n\t\t\t\tthrow new IllegalArgumentException(\"Formatter should be set for option: \" + name);\n\t\t\t}\n\t\t\tif (setter == null) {\n\t\t\t\tthrow new IllegalArgumentException(\"Setter should be set for option: \" + name);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/options/impl/JadxOptionDescription.java",
    "content": "package jadx.api.plugins.options.impl;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.options.OptionDescription;\nimport jadx.api.plugins.options.OptionFlag;\nimport jadx.api.plugins.options.OptionType;\n\npublic class JadxOptionDescription implements OptionDescription {\n\n\tpublic static JadxOptionDescription booleanOption(String name, String desc, boolean defaultValue) {\n\t\treturn new JadxOptionDescription(name, desc,\n\t\t\t\tdefaultValue ? \"yes\" : \"no\",\n\t\t\t\tArrays.asList(\"yes\", \"no\"),\n\t\t\t\tOptionType.BOOLEAN);\n\t}\n\n\tprivate final String name;\n\tprivate final String desc;\n\tprivate final String defaultValue;\n\tprivate final List<String> values;\n\tprivate final OptionType type;\n\tprivate final Set<OptionFlag> flags = EnumSet.noneOf(OptionFlag.class);\n\n\tpublic JadxOptionDescription(String name, String desc, @Nullable String defaultValue, List<String> values) {\n\t\tthis(name, desc, defaultValue, values, OptionType.STRING);\n\t}\n\n\tpublic JadxOptionDescription(String name, String desc, @Nullable String defaultValue, List<String> values, OptionType type) {\n\t\tthis.name = name;\n\t\tthis.desc = desc;\n\t\tthis.defaultValue = defaultValue;\n\t\tthis.values = values;\n\t\tthis.type = type;\n\t}\n\n\t@Override\n\tpublic String name() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic String description() {\n\t\treturn desc;\n\t}\n\n\t@Override\n\tpublic @Nullable String defaultValue() {\n\t\treturn defaultValue;\n\t}\n\n\t@Override\n\tpublic List<String> values() {\n\t\treturn values;\n\t}\n\n\t@Override\n\tpublic OptionType getType() {\n\t\treturn type;\n\t}\n\n\t@Override\n\tpublic Set<OptionFlag> getFlags() {\n\t\treturn flags;\n\t}\n\n\tpublic JadxOptionDescription withFlag(OptionFlag flag) {\n\t\tthis.flags.add(flag);\n\t\treturn this;\n\t}\n\n\tpublic JadxOptionDescription withFlags(OptionFlag... flags) {\n\t\tCollections.addAll(this.flags, flags);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"OptionDescription{\" + desc + \", values=\" + values + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/options/impl/OptionBuilder.java",
    "content": "package jadx.api.plugins.options.impl;\n\nimport java.util.List;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport jadx.api.plugins.options.OptionFlag;\nimport jadx.api.plugins.options.OptionType;\n\npublic interface OptionBuilder<T> {\n\n\t/**\n\t * Option description (required)\n\t */\n\tOptionBuilder<T> description(String desc);\n\n\tOptionBuilder<T> defaultValue(T defValue);\n\n\t/**\n\t * Function to parse input string into option value (required)\n\t */\n\tOptionBuilder<T> parser(Function<String, T> parser);\n\n\t/**\n\t * Function to format option value into string for build help (required)\n\t */\n\tOptionBuilder<T> formatter(Function<T, String> formatter);\n\n\t/**\n\t * Function to save/apply parsed option value (required)\n\t */\n\tOptionBuilder<T> setter(Consumer<T> setter);\n\n\t/**\n\t * Possible option values\n\t */\n\tOptionBuilder<T> values(List<T> values);\n\n\tOptionBuilder<T> type(OptionType optionType);\n\n\tOptionBuilder<T> flags(OptionFlag... flags);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/pass/JadxPass.java",
    "content": "package jadx.api.plugins.pass;\n\nimport jadx.api.plugins.pass.types.JadxPassType;\n\npublic interface JadxPass {\n\tJadxPassInfo getInfo();\n\n\tJadxPassType getPassType();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/pass/JadxPassInfo.java",
    "content": "package jadx.api.plugins.pass;\n\nimport java.util.List;\n\npublic interface JadxPassInfo {\n\n\t/**\n\t * Add this to 'run after' list to place pass before others\n\t */\n\tString START = \"start\";\n\n\t/**\n\t * Add this to 'run before' list to place pass at end\n\t */\n\tString END = \"end\";\n\n\t/**\n\t * Pass short id, should be unique.\n\t */\n\tString getName();\n\n\t/**\n\t * Pass description\n\t */\n\tString getDescription();\n\n\t/**\n\t * This pass will be executed after these passes.\n\t * Passes names list.\n\t */\n\tList<String> runAfter();\n\n\t/**\n\t * This pass will be executed before these passes.\n\t * Passes names list.\n\t */\n\tList<String> runBefore();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/pass/impl/OrderedJadxPassInfo.java",
    "content": "package jadx.api.plugins.pass.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.api.plugins.pass.JadxPassInfo;\n\npublic class OrderedJadxPassInfo implements JadxPassInfo {\n\n\tprivate final String name;\n\tprivate final String desc;\n\tprivate final List<String> runAfter;\n\tprivate final List<String> runBefore;\n\n\tpublic OrderedJadxPassInfo(String name, String desc) {\n\t\tthis(name, desc, new ArrayList<>(), new ArrayList<>());\n\t}\n\n\tpublic OrderedJadxPassInfo(String name, String desc, List<String> runAfter, List<String> runBefore) {\n\t\tthis.name = name;\n\t\tthis.desc = desc;\n\t\tthis.runAfter = runAfter;\n\t\tthis.runBefore = runBefore;\n\t}\n\n\tpublic OrderedJadxPassInfo after(String pass) {\n\t\trunAfter.add(pass);\n\t\treturn this;\n\t}\n\n\tpublic OrderedJadxPassInfo before(String pass) {\n\t\trunBefore.add(pass);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic String getDescription() {\n\t\treturn desc;\n\t}\n\n\t@Override\n\tpublic List<String> runAfter() {\n\t\treturn runAfter;\n\t}\n\n\t@Override\n\tpublic List<String> runBefore() {\n\t\treturn runBefore;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"PassInfo{'\" + name + '\\'' + \", desc='\" + desc + '\\'' + \", runAfter=\" + runAfter + \", runBefore=\" + runBefore + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/pass/impl/SimpleAfterLoadPass.java",
    "content": "package jadx.api.plugins.pass.impl;\n\nimport java.util.function.Consumer;\n\nimport jadx.api.JadxDecompiler;\nimport jadx.api.plugins.pass.JadxPassInfo;\nimport jadx.api.plugins.pass.types.JadxAfterLoadPass;\n\npublic class SimpleAfterLoadPass implements JadxAfterLoadPass {\n\n\tprivate final JadxPassInfo info;\n\tprivate final Consumer<JadxDecompiler> init;\n\n\tpublic SimpleAfterLoadPass(String name, Consumer<JadxDecompiler> init) {\n\t\tthis.info = new SimpleJadxPassInfo(name);\n\t\tthis.init = init;\n\t}\n\n\t@Override\n\tpublic JadxPassInfo getInfo() {\n\t\treturn info;\n\t}\n\n\t@Override\n\tpublic void init(JadxDecompiler decompiler) {\n\t\tinit.accept(decompiler);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/pass/impl/SimpleJadxPassInfo.java",
    "content": "package jadx.api.plugins.pass.impl;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.api.plugins.pass.JadxPassInfo;\n\npublic class SimpleJadxPassInfo implements JadxPassInfo {\n\n\tprivate final String name;\n\tprivate final String desc;\n\n\tpublic SimpleJadxPassInfo(String name) {\n\t\tthis(name, name);\n\t}\n\n\tpublic SimpleJadxPassInfo(String name, String desc) {\n\t\tthis.name = name;\n\t\tthis.desc = desc;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic String getDescription() {\n\t\treturn desc;\n\t}\n\n\t@Override\n\tpublic List<String> runAfter() {\n\t\treturn Collections.emptyList();\n\t}\n\n\t@Override\n\tpublic List<String> runBefore() {\n\t\treturn Collections.emptyList();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/pass/types/JadxAfterLoadPass.java",
    "content": "package jadx.api.plugins.pass.types;\n\nimport jadx.api.JadxDecompiler;\nimport jadx.api.plugins.pass.JadxPass;\n\npublic interface JadxAfterLoadPass extends JadxPass {\n\tJadxPassType TYPE = new JadxPassType(\"AfterLoadPass\");\n\n\tvoid init(JadxDecompiler decompiler);\n\n\t@Override\n\tdefault JadxPassType getPassType() {\n\t\treturn TYPE;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/pass/types/JadxDecompilePass.java",
    "content": "package jadx.api.plugins.pass.types;\n\nimport jadx.api.plugins.pass.JadxPass;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\n\npublic interface JadxDecompilePass extends JadxPass {\n\tJadxPassType TYPE = new JadxPassType(\"DecompilePass\");\n\n\tvoid init(RootNode root);\n\n\t/**\n\t * Visit class\n\t *\n\t * @return false for disable child methods and inner classes traversal\n\t */\n\tboolean visit(ClassNode cls);\n\n\t/**\n\t * Visit method\n\t */\n\tvoid visit(MethodNode mth);\n\n\t@Override\n\tdefault JadxPassType getPassType() {\n\t\treturn TYPE;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/pass/types/JadxPassType.java",
    "content": "package jadx.api.plugins.pass.types;\n\npublic class JadxPassType {\n\tprivate final String cls;\n\n\tpublic JadxPassType(String clsName) {\n\t\tthis.cls = clsName;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof JadxPassType)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn cls.equals(((JadxPassType) o).cls);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn cls.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn cls;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/pass/types/JadxPreparePass.java",
    "content": "package jadx.api.plugins.pass.types;\n\nimport jadx.api.plugins.pass.JadxPass;\nimport jadx.core.dex.nodes.RootNode;\n\npublic interface JadxPreparePass extends JadxPass {\n\tJadxPassType TYPE = new JadxPassType(\"PreparePass\");\n\n\tvoid init(RootNode root);\n\n\t@Override\n\tdefault JadxPassType getPassType() {\n\t\treturn TYPE;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/resources/IResContainerFactory.java",
    "content": "package jadx.api.plugins.resources;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ResourceFile;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.xmlgen.ResContainer;\n\n/**\n * Factory for {@link ResContainer}. Can be used in plugins via\n * {@code context.getResourcesLoader().addResContainerFactory()} to implement content parsing in\n * files with\n * different formats.\n */\npublic interface IResContainerFactory {\n\n\t/**\n\t * Optional init method\n\t */\n\tdefault void init(RootNode root) {\n\t}\n\n\t/**\n\t * Checks if resource file is of expected format and tries to parse its content.\n\t *\n\t * @return {@link ResContainer} if file is of expected format, {@code null} otherwise.\n\t */\n\t@Nullable\n\tResContainer create(ResourceFile resFile, InputStream inputStream) throws IOException;\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/resources/IResTableParserProvider.java",
    "content": "package jadx.api.plugins.resources;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ResourceFile;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.xmlgen.IResTableParser;\n\n/**\n * Provides the resource table parser instance for specific resource table file format. Can be used\n * in plugins via {@code context.getResourcesLoader().addResTableParserProvider()} to parse\n * resources from tables\n * in different formats.\n */\npublic interface IResTableParserProvider {\n\n\t/**\n\t * Optional init method\n\t */\n\tdefault void init(RootNode root) {\n\t}\n\n\t/**\n\t * Checks a file format and provides the instance if the format is expected.\n\t *\n\t * @return {@link IResTableParser} if resource table is of expected format, {@code null} otherwise.\n\t */\n\t@Nullable\n\tIResTableParser getParser(ResourceFile resFile);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/resources/IResourcesLoader.java",
    "content": "package jadx.api.plugins.resources;\n\npublic interface IResourcesLoader {\n\n\tvoid addResContainerFactory(IResContainerFactory resContainerFactory);\n\n\tvoid addResTableParserProvider(IResTableParserProvider resTableParserProvider);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/utils/CommonFileUtils.java",
    "content": "package jadx.api.plugins.utils;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class CommonFileUtils {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CommonFileUtils.class);\n\tpublic static final File CWD = getCWD();\n\tpublic static final Path CWD_PATH = CWD.toPath();\n\n\tprivate static File getCWD() {\n\t\ttry {\n\t\t\treturn new File(\".\").getCanonicalFile();\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(\"Failed to init current working dir constant\", e);\n\t\t}\n\t}\n\n\tpublic static Path saveToTempFile(InputStream in, String suffix) throws IOException {\n\t\treturn saveToTempFile(null, in, suffix);\n\t}\n\n\tpublic static Path saveToTempFile(byte[] dataPrefix, InputStream in, String suffix) throws IOException {\n\t\tPath tempFile = Files.createTempFile(\"jadx-temp-\", suffix);\n\t\ttry (OutputStream out = Files.newOutputStream(tempFile)) {\n\t\t\tif (dataPrefix != null) {\n\t\t\t\tout.write(dataPrefix);\n\t\t\t}\n\t\t\tcopyStream(in, out);\n\t\t} catch (Exception e) {\n\t\t\tthrow new IOException(\"Failed to save temp file\", e);\n\t\t}\n\t\treturn tempFile;\n\t}\n\n\tpublic static boolean safeDeleteFile(File file) {\n\t\ttry {\n\t\t\treturn file.delete();\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to delete file: {}\", file, e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic static byte[] loadBytes(InputStream input) throws IOException {\n\t\treturn loadBytes(null, input);\n\t}\n\n\tpublic static byte[] loadBytes(byte[] dataPrefix, InputStream in) throws IOException {\n\t\tint estimateSize = dataPrefix == null ? in.available() : dataPrefix.length + in.available();\n\t\ttry (ByteArrayOutputStream out = new ByteArrayOutputStream(estimateSize)) {\n\t\t\tif (dataPrefix != null) {\n\t\t\t\tout.write(dataPrefix);\n\t\t\t}\n\t\t\tcopyStream(in, out);\n\t\t\treturn out.toByteArray();\n\t\t} catch (Exception e) {\n\t\t\tthrow new IOException(\"Failed to read input stream to bytes array\", e);\n\t\t}\n\t}\n\n\tpublic static void copyStream(InputStream input, OutputStream output) throws IOException {\n\t\tbyte[] buffer = new byte[8192];\n\t\twhile (true) {\n\t\t\tint count = input.read(buffer);\n\t\t\tif (count == -1) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\toutput.write(buffer, 0, count);\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic static String getFileExtension(String fileName) {\n\t\tint dotIndex = fileName.lastIndexOf('.');\n\t\tif (dotIndex == -1) {\n\t\t\treturn null;\n\t\t}\n\t\treturn fileName.substring(dotIndex + 1);\n\t}\n\n\tpublic static String removeFileExtension(String fileName) {\n\t\tint dotIndex = fileName.lastIndexOf('.');\n\t\tif (dotIndex == -1) {\n\t\t\treturn fileName;\n\t\t}\n\t\treturn fileName.substring(0, dotIndex);\n\t}\n\n\tprivate static final Set<String> ZIP_FILE_EXTS = Utils.constSet(\"zip\", \"jar\", \"apk\");\n\n\tpublic static boolean isZipFileExt(String fileName) {\n\t\tString ext = getFileExtension(fileName);\n\t\tif (ext == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn ZIP_FILE_EXTS.contains(ext);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/utils/Utils.java",
    "content": "package jadx.api.plugins.utils;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic class Utils {\n\n\tpublic static <T> void addToList(Collection<T> list, @Nullable T item) {\n\t\tif (item != null) {\n\t\t\tlist.add(item);\n\t\t}\n\t}\n\n\tpublic static <T, I> void addToList(Collection<T> list, @Nullable I item, Function<I, T> map) {\n\t\tif (item != null) {\n\t\t\tT value = map.apply(item);\n\t\t\tif (value != null) {\n\t\t\t\tlist.add(value);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static <T> List<T> concat(List<T> a, List<T> b) {\n\t\tint aSize = a.size();\n\t\tint bSize = b.size();\n\t\tif (aSize == 0 && bSize == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tif (aSize == 0) {\n\t\t\treturn b;\n\t\t}\n\t\tif (bSize == 0) {\n\t\t\treturn a;\n\t\t}\n\t\tList<T> list = new ArrayList<>(aSize + bSize);\n\t\tlist.addAll(a);\n\t\tlist.addAll(b);\n\t\treturn list;\n\t}\n\n\tpublic static <T> List<T> concatDistinct(List<T> a, List<T> b) {\n\t\tint aSize = a.size();\n\t\tint bSize = b.size();\n\t\tif (aSize == 0 && bSize == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tif (aSize == 0) {\n\t\t\treturn b;\n\t\t}\n\t\tif (bSize == 0) {\n\t\t\treturn a;\n\t\t}\n\t\tSet<T> set = new LinkedHashSet<>(aSize + bSize);\n\t\tset.addAll(a);\n\t\tset.addAll(b);\n\t\treturn new ArrayList<>(set);\n\t}\n\n\tpublic static <T> String listToStr(List<T> list) {\n\t\tif (list == null) {\n\t\t\treturn \"null\";\n\t\t}\n\t\tif (list.isEmpty()) {\n\t\t\treturn \"\";\n\t\t}\n\t\tif (list.size() == 1) {\n\t\t\treturn Objects.toString(list.get(0));\n\t\t}\n\t\tStringBuilder sb = new StringBuilder();\n\t\tIterator<T> it = list.iterator();\n\t\tsb.append(it.next());\n\t\twhile (it.hasNext()) {\n\t\t\tsb.append(\", \").append(it.next());\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static String formatOffset(int offset) {\n\t\treturn String.format(\"0x%04x\", offset);\n\t}\n\n\t@SafeVarargs\n\tpublic static <T> Set<T> constSet(T... arr) {\n\t\treturn Collections.unmodifiableSet(new HashSet<>(Arrays.asList(arr)));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/plugins/utils/ZipSecurity.java",
    "content": "package jadx.api.plugins.utils;\n\nimport java.io.BufferedInputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.JadxDecompiler;\nimport jadx.api.plugins.JadxPluginContext;\nimport jadx.core.utils.Utils;\nimport jadx.zip.IZipEntry;\nimport jadx.zip.ZipReader;\nimport jadx.zip.io.LimitedInputStream;\nimport jadx.zip.security.DisabledZipSecurity;\nimport jadx.zip.security.IJadxZipSecurity;\nimport jadx.zip.security.JadxZipSecurity;\n\n/**\n * Deprecated, migrate to {@link ZipReader}. <br>\n * Prefer already configured instance from {@link JadxDecompiler#getZipReader()} or\n * {@link JadxPluginContext#getZipReader()}.\n */\n@Deprecated\npublic class ZipSecurity {\n\tprivate static final boolean DISABLE_CHECKS = Utils.getEnvVarBool(\"JADX_DISABLE_ZIP_SECURITY\", false);\n\n\tprivate static final int MAX_ENTRIES_COUNT = Utils.getEnvVarInt(\"JADX_ZIP_MAX_ENTRIES_COUNT\", 100_000);\n\n\tprivate static final IJadxZipSecurity ZIP_SECURITY = buildZipSecurity();\n\n\tprivate static final ZipReader ZIP_READER = new ZipReader(ZIP_SECURITY);\n\n\tprivate static IJadxZipSecurity buildZipSecurity() {\n\t\tif (DISABLE_CHECKS) {\n\t\t\treturn DisabledZipSecurity.INSTANCE;\n\t\t}\n\t\tJadxZipSecurity jadxZipSecurity = new JadxZipSecurity();\n\t\tjadxZipSecurity.setMaxEntriesCount(MAX_ENTRIES_COUNT);\n\t\treturn jadxZipSecurity;\n\t}\n\n\tprivate ZipSecurity() {\n\t}\n\n\tpublic static boolean isInSubDirectory(File baseDir, File file) {\n\t\treturn ZIP_SECURITY.isInSubDirectory(baseDir, file);\n\t}\n\n\t/**\n\t * Checks that entry name contains no any traversals and prevents cases like \"../classes.dex\",\n\t * to limit output only to the specified directory\n\t */\n\tpublic static boolean isValidZipEntryName(String entryName) {\n\t\treturn ZIP_SECURITY.isValidEntryName(entryName);\n\t}\n\n\tpublic static boolean isZipBomb(IZipEntry entry) {\n\t\treturn !ZIP_SECURITY.isValidEntry(entry);\n\t}\n\n\tpublic static boolean isValidZipEntry(IZipEntry entry) {\n\t\treturn ZIP_SECURITY.isValidEntry(entry);\n\t}\n\n\tpublic static InputStream getInputStreamForEntry(ZipFile zipFile, ZipEntry entry) throws IOException {\n\t\tif (DISABLE_CHECKS) {\n\t\t\treturn new BufferedInputStream(zipFile.getInputStream(entry));\n\t\t}\n\t\tInputStream in = zipFile.getInputStream(entry);\n\t\tLimitedInputStream limited = new LimitedInputStream(in, entry.getSize());\n\t\treturn new BufferedInputStream(limited);\n\t}\n\n\t/**\n\t * Visit valid entries in a zip file.\n\t * Return not null value from visitor to stop iteration.\n\t */\n\t@Nullable\n\tpublic static <R> R visitZipEntries(File file, Function<IZipEntry, R> visitor) {\n\t\treturn ZIP_READER.visitEntries(file, visitor);\n\t}\n\n\tpublic static void readZipEntries(File file, BiConsumer<IZipEntry, InputStream> visitor) {\n\t\tZIP_READER.readEntries(file, visitor);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/resources/ResourceContentType.java",
    "content": "package jadx.api.resources;\n\npublic enum ResourceContentType {\n\tCONTENT_TEXT,\n\tCONTENT_BINARY,\n\tCONTENT_NONE,\n\tCONTENT_UNKNOWN,\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/security/IJadxSecurity.java",
    "content": "package jadx.api.security;\n\nimport java.io.InputStream;\n\nimport org.w3c.dom.Document;\n\nimport jadx.zip.security.IJadxZipSecurity;\n\npublic interface IJadxSecurity extends IJadxZipSecurity {\n\n\t/**\n\t * Check if application package is safe\n\t *\n\t * @return normalized/sanitized string or same string if safe\n\t */\n\tString verifyAppPackage(String appPackage);\n\n\t/**\n\t * XML document parser\n\t */\n\tDocument parseXml(InputStream in);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/security/JadxSecurityFlag.java",
    "content": "package jadx.api.security;\n\nimport java.util.EnumSet;\nimport java.util.Set;\n\npublic enum JadxSecurityFlag {\n\n\tVERIFY_APP_PACKAGE,\n\tSECURE_XML_PARSER,\n\tSECURE_ZIP_READER;\n\n\tpublic static Set<JadxSecurityFlag> all() {\n\t\treturn EnumSet.allOf(JadxSecurityFlag.class);\n\t}\n\n\tpublic static Set<JadxSecurityFlag> none() {\n\t\treturn EnumSet.noneOf(JadxSecurityFlag.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/security/impl/JadxSecurity.java",
    "content": "package jadx.api.security.impl;\n\nimport java.io.File;\nimport java.io.InputStream;\nimport java.util.Set;\n\nimport javax.xml.parsers.DocumentBuilderFactory;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.w3c.dom.Document;\n\nimport jadx.api.security.IJadxSecurity;\nimport jadx.api.security.JadxSecurityFlag;\nimport jadx.core.deobf.NameMapper;\nimport jadx.zip.IZipEntry;\nimport jadx.zip.security.DisabledZipSecurity;\nimport jadx.zip.security.IJadxZipSecurity;\nimport jadx.zip.security.JadxZipSecurity;\n\nimport static jadx.api.security.JadxSecurityFlag.SECURE_ZIP_READER;\n\npublic class JadxSecurity implements IJadxSecurity {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxSecurity.class);\n\n\tprivate final Set<JadxSecurityFlag> flags;\n\tprivate final IJadxZipSecurity zipSecurity;\n\n\tpublic JadxSecurity(Set<JadxSecurityFlag> flags) {\n\t\tthis.flags = flags;\n\t\tthis.zipSecurity = flags.contains(SECURE_ZIP_READER) ? new JadxZipSecurity() : DisabledZipSecurity.INSTANCE;\n\t}\n\n\tpublic JadxSecurity(Set<JadxSecurityFlag> flags, IJadxZipSecurity zipSecurity) {\n\t\tthis.flags = flags;\n\t\tthis.zipSecurity = zipSecurity;\n\t}\n\n\t@Override\n\tpublic boolean isValidEntry(IZipEntry entry) {\n\t\treturn zipSecurity.isValidEntry(entry);\n\t}\n\n\t@Override\n\tpublic boolean isValidEntryName(String entryName) {\n\t\treturn zipSecurity.isValidEntryName(entryName);\n\t}\n\n\t@Override\n\tpublic boolean isInSubDirectory(File baseDir, File file) {\n\t\treturn zipSecurity.isInSubDirectory(baseDir, file);\n\t}\n\n\t@Override\n\tpublic boolean useLimitedDataStream() {\n\t\treturn zipSecurity.useLimitedDataStream();\n\t}\n\n\t@Override\n\tpublic int getMaxEntriesCount() {\n\t\treturn zipSecurity.getMaxEntriesCount();\n\t}\n\n\t@Override\n\tpublic String verifyAppPackage(String appPackage) {\n\t\tif (flags.contains(JadxSecurityFlag.VERIFY_APP_PACKAGE)\n\t\t\t\t&& !NameMapper.isValidFullIdentifier(appPackage)) {\n\t\t\tLOG.warn(\"App package '{}' has invalid format and will be ignored\", appPackage);\n\t\t\treturn \"INVALID_PACKAGE\";\n\t\t}\n\t\treturn appPackage;\n\t}\n\n\t@Override\n\tpublic Document parseXml(InputStream in) {\n\t\tDocumentBuilderFactory dbf;\n\t\tif (flags.contains(JadxSecurityFlag.SECURE_XML_PARSER)) {\n\t\t\tdbf = SecureDBFHolder.INSTANCE;\n\t\t} else {\n\t\t\tdbf = SimpleDBFHolder.INSTANCE;\n\t\t}\n\t\ttry {\n\t\t\treturn dbf.newDocumentBuilder().parse(in);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to parse xml\", e);\n\t\t}\n\t}\n\n\tprivate static final class SimpleDBFHolder {\n\t\tprivate static final DocumentBuilderFactory INSTANCE = DocumentBuilderFactory.newInstance();\n\t}\n\n\tprivate static final class SecureDBFHolder {\n\t\tprivate static final DocumentBuilderFactory INSTANCE = buildSecureDBF();\n\n\t\tprivate static DocumentBuilderFactory buildSecureDBF() {\n\t\t\ttry {\n\t\t\t\tDocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\n\t\t\t\tdbf.setFeature(\"http://apache.org/xml/features/disallow-doctype-decl\", true);\n\t\t\t\tdbf.setFeature(\"http://apache.org/xml/features/nonvalidating/load-external-dtd\", false);\n\t\t\t\tdbf.setFeature(\"http://xml.org/sax/features/external-general-entities\", false);\n\t\t\t\tdbf.setFeature(\"http://xml.org/sax/features/external-parameter-entities\", false);\n\t\t\t\tdbf.setFeature(\"http://apache.org/xml/features/dom/create-entity-ref-nodes\", false);\n\t\t\t\tdbf.setXIncludeAware(false);\n\t\t\t\tdbf.setExpandEntityReferences(false);\n\t\t\t\treturn dbf;\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new RuntimeException(\"Fail to build secure XML DocumentBuilderFactory\", e);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/usage/IUsageInfoCache.java",
    "content": "package jadx.api.usage;\n\nimport java.io.Closeable;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.nodes.RootNode;\n\npublic interface IUsageInfoCache extends Closeable {\n\n\t@Nullable\n\tIUsageInfoData get(RootNode root);\n\n\tvoid set(RootNode root, IUsageInfoData data);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/usage/IUsageInfoData.java",
    "content": "package jadx.api.usage;\n\nimport jadx.core.dex.nodes.ClassNode;\n\npublic interface IUsageInfoData {\n\n\tvoid apply();\n\n\tvoid applyForClass(ClassNode cls);\n\n\tvoid visitUsageData(IUsageInfoVisitor visitor);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/usage/IUsageInfoVisitor.java",
    "content": "package jadx.api.usage;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic interface IUsageInfoVisitor {\n\n\tvoid visitClassDeps(ClassNode cls, List<ClassNode> deps);\n\n\tvoid visitClassUsage(ClassNode cls, List<ClassNode> usage);\n\n\tvoid visitClassUseInMethods(ClassNode cls, List<MethodNode> methods);\n\n\tvoid visitFieldsUsage(FieldNode fld, List<MethodNode> methods);\n\n\tvoid visitMethodsUsage(MethodNode mth, List<MethodNode> methods);\n\n\tvoid visitMethodsUses(MethodNode mth, List<MethodNode> methods);\n\n\tvoid visitUnresolvedMethodsUsage(MethodNode mth, List<IMethodRef> methods);\n\n\tvoid visitIsSelfCall(MethodNode mth, boolean isSelfCall);\n\n\tvoid visitComplete();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/usage/impl/EmptyUsageInfoCache.java",
    "content": "package jadx.api.usage.impl;\n\nimport java.io.IOException;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.usage.IUsageInfoCache;\nimport jadx.api.usage.IUsageInfoData;\nimport jadx.core.dex.nodes.RootNode;\n\npublic class EmptyUsageInfoCache implements IUsageInfoCache {\n\t@Override\n\tpublic @Nullable IUsageInfoData get(RootNode root) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void set(RootNode root, IUsageInfoData data) {\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/usage/impl/InMemoryUsageInfoCache.java",
    "content": "package jadx.api.usage.impl;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.usage.IUsageInfoCache;\nimport jadx.api.usage.IUsageInfoData;\nimport jadx.core.dex.nodes.RootNode;\n\npublic class InMemoryUsageInfoCache implements IUsageInfoCache {\n\n\tprivate IUsageInfoData data;\n\n\t/**\n\t * `data` field tied to root node instance, keep hash to reset cache on change\n\t */\n\tprivate int rootNodeHash;\n\n\t@Override\n\tpublic @Nullable IUsageInfoData get(RootNode root) {\n\t\treturn rootNodeHash == root.hashCode() ? data : null;\n\t}\n\n\t@Override\n\tpublic void set(RootNode root, IUsageInfoData data) {\n\t\tthis.rootNodeHash = root.hashCode();\n\t\tthis.data = data;\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\tthis.rootNodeHash = 0;\n\t\tthis.data = null;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/utils/CodeUtils.java",
    "content": "package jadx.api.utils;\n\nimport java.util.function.BiFunction;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.metadata.annotations.NodeDeclareRef;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic class CodeUtils {\n\n\tpublic static String getLineForPos(String code, int pos) {\n\t\tint start = getLineStartForPos(code, pos);\n\t\tint end = getLineEndForPos(code, pos);\n\t\treturn code.substring(start, end);\n\t}\n\n\tpublic static int getLineStartForPos(String code, int pos) {\n\t\tint start = getNewLinePosBefore(code, pos);\n\t\treturn start == -1 ? 0 : start + 1;\n\t}\n\n\tpublic static int getLineEndForPos(String code, int pos) {\n\t\tint end = getNewLinePosAfter(code, pos);\n\t\treturn end == -1 ? code.length() : end;\n\t}\n\n\tpublic static int getNewLinePosAfter(String code, int startPos) {\n\t\tint pos = code.indexOf('\\n', startPos);\n\t\tif (pos != -1) {\n\t\t\t// check for '\\r\\n'\n\t\t\tint prev = pos - 1;\n\t\t\tif (code.charAt(prev) == '\\r') {\n\t\t\t\treturn prev;\n\t\t\t}\n\t\t}\n\t\treturn pos;\n\t}\n\n\tpublic static int getNewLinePosBefore(String code, int startPos) {\n\t\treturn code.lastIndexOf('\\n', startPos);\n\t}\n\n\tpublic static int getLineNumForPos(String code, int pos, String newLine) {\n\t\tint newLineLen = newLine.length();\n\t\tint line = 1;\n\t\tint prev = 0;\n\t\twhile (true) {\n\t\t\tint next = code.indexOf(newLine, prev);\n\t\t\tif (next >= pos) {\n\t\t\t\treturn line;\n\t\t\t}\n\t\t\tprev = next + newLineLen;\n\t\t\tline++;\n\t\t}\n\t}\n\n\t/**\n\t * Cut method code (including comments and annotations) from class code.\n\t *\n\t * @return method code or empty string if metadata is not available\n\t */\n\tpublic static String extractMethodCode(MethodNode mth, ICodeInfo codeInfo) {\n\t\tint end = getMethodEnd(mth, codeInfo);\n\t\tif (end == -1) {\n\t\t\treturn \"\";\n\t\t}\n\t\tint start = getMethodStart(mth, codeInfo);\n\t\tif (end < start) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn codeInfo.getCodeStr().substring(start, end);\n\t}\n\n\t/**\n\t * Search first empty line before method definition to include comments and annotations\n\t */\n\tprivate static int getMethodStart(MethodNode mth, ICodeInfo codeInfo) {\n\t\tint pos = mth.getDefPosition();\n\t\tString newLineStr = mth.root().getArgs().getCodeNewLineStr();\n\t\tString emptyLine = newLineStr + newLineStr;\n\t\tint emptyLinePos = codeInfo.getCodeStr().lastIndexOf(emptyLine, pos);\n\t\treturn emptyLinePos == -1 ? pos : emptyLinePos + emptyLine.length();\n\t}\n\n\t/**\n\t * Search method end position in provided class code info.\n\t *\n\t * @return end pos or -1 if metadata not available\n\t */\n\tpublic static int getMethodEnd(MethodNode mth, ICodeInfo codeInfo) {\n\t\tif (!codeInfo.hasMetadata()) {\n\t\t\treturn -1;\n\t\t}\n\t\t// skip nested nodes DEF/END until first unpaired END annotation (end of this method)\n\t\tInteger end = codeInfo.getCodeMetadata().searchDown(mth.getDefPosition() + 1, new BiFunction<>() {\n\t\t\tint nested = 0;\n\n\t\t\t@Override\n\t\t\tpublic Integer apply(Integer pos, ICodeAnnotation ann) {\n\t\t\t\tswitch (ann.getAnnType()) {\n\t\t\t\t\tcase DECLARATION:\n\t\t\t\t\t\tICodeNodeRef node = ((NodeDeclareRef) ann).getNode();\n\t\t\t\t\t\tswitch (node.getAnnType()) {\n\t\t\t\t\t\t\tcase CLASS:\n\t\t\t\t\t\t\tcase METHOD:\n\t\t\t\t\t\t\t\tnested++;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase END:\n\t\t\t\t\t\tif (nested == 0) {\n\t\t\t\t\t\t\treturn pos;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tnested--;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t});\n\t\treturn end == null ? -1 : end;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/api/utils/tasks/ITaskExecutor.java",
    "content": "package jadx.api.utils.tasks;\n\nimport java.util.List;\nimport java.util.concurrent.ExecutorService;\n\nimport org.jetbrains.annotations.Nullable;\n\n/**\n * Schedule and execute tasks combined into stages\n * with parallel or sequential execution (similar to the fork-join pattern).\n */\npublic interface ITaskExecutor {\n\n\t/**\n\t * Add parallel stage with provided tasks\n\t */\n\tvoid addParallelTasks(List<? extends Runnable> parallelTasks);\n\n\t/**\n\t * Add sequential stage with provided tasks\n\t */\n\tvoid addSequentialTasks(List<? extends Runnable> seqTasks);\n\n\t/**\n\t * Add sequential stage with a single task\n\t */\n\tvoid addSequentialTask(Runnable task);\n\n\t/**\n\t * Scheduled tasks count\n\t */\n\tint getTasksCount();\n\n\t/**\n\t * Set threads count for parallel stage.\n\t * Can be changed during execution.\n\t * Defaults to half of processors count.\n\t */\n\tvoid setThreadsCount(int threadsCount);\n\n\tint getThreadsCount();\n\n\t/**\n\t * Start tasks execution.\n\t */\n\tvoid execute();\n\n\tint getProgress();\n\n\t/**\n\t * Not started tasks will be not executed after this method invocation.\n\t */\n\tvoid terminate();\n\n\tboolean isTerminating();\n\n\tboolean isRunning();\n\n\t/**\n\t * Block until execution is finished\n\t */\n\tvoid awaitTermination();\n\n\t/**\n\t * Return internal executor service.\n\t */\n\t@Nullable\n\tExecutorService getInternalExecutor();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/Consts.java",
    "content": "package jadx.core;\n\npublic class Consts {\n\tpublic static final boolean DEBUG = false;\n\tpublic static final boolean DEBUG_WITH_ERRORS = false; // TODO: fix errors\n\tpublic static final boolean DEBUG_USAGE = false;\n\tpublic static final boolean DEBUG_TYPE_INFERENCE = false;\n\tpublic static final boolean DEBUG_OVERLOADED_CASTS = false;\n\tpublic static final boolean DEBUG_EXC_HANDLERS = false;\n\tpublic static final boolean DEBUG_FINALLY = false;\n\tpublic static final boolean DEBUG_ATTRIBUTES = false;\n\tpublic static final boolean DEBUG_RESTRUCTURE = false;\n\tpublic static final boolean DEBUG_EVENTS = Jadx.isDevVersion();\n\n\tpublic static final String CLASS_OBJECT = \"java.lang.Object\";\n\tpublic static final String CLASS_STRING = \"java.lang.String\";\n\tpublic static final String CLASS_CLASS = \"java.lang.Class\";\n\tpublic static final String CLASS_THROWABLE = \"java.lang.Throwable\";\n\tpublic static final String CLASS_ERROR = \"java.lang.Error\";\n\tpublic static final String CLASS_EXCEPTION = \"java.lang.Exception\";\n\tpublic static final String CLASS_RUNTIME_EXCEPTION = \"java.lang.RuntimeException\";\n\tpublic static final String CLASS_ENUM = \"java.lang.Enum\";\n\n\tpublic static final String CLASS_STRING_BUILDER = \"java.lang.StringBuilder\";\n\tpublic static final String OVERRIDE_ANNOTATION = \"Ljava/lang/Override;\";\n\n\tpublic static final String DEFAULT_PACKAGE_NAME = \"defpackage\";\n\tpublic static final String ANONYMOUS_CLASS_PREFIX = \"AnonymousClass\";\n\n\tpublic static final String MTH_TOSTRING_SIGNATURE = \"toString()Ljava/lang/String;\";\n\n\tprivate Consts() {\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/Jadx.java",
    "content": "package jadx.core;\n\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.jar.Manifest;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.CommentsLevel;\nimport jadx.api.JadxArgs;\nimport jadx.core.deobf.DeobfuscatorVisitor;\nimport jadx.core.deobf.SaveDeobfMapping;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.visitors.AdjustForIfMergeVisitor;\nimport jadx.core.dex.visitors.AnonymousClassVisitor;\nimport jadx.core.dex.visitors.ApplyVariableNames;\nimport jadx.core.dex.visitors.AttachCommentsVisitor;\nimport jadx.core.dex.visitors.AttachMethodDetails;\nimport jadx.core.dex.visitors.AttachTryCatchVisitor;\nimport jadx.core.dex.visitors.CheckCode;\nimport jadx.core.dex.visitors.ClassModifier;\nimport jadx.core.dex.visitors.ConstInlineVisitor;\nimport jadx.core.dex.visitors.ConstructorVisitor;\nimport jadx.core.dex.visitors.DeboxingVisitor;\nimport jadx.core.dex.visitors.DotGraphVisitor;\nimport jadx.core.dex.visitors.EnumVisitor;\nimport jadx.core.dex.visitors.ExtractFieldInit;\nimport jadx.core.dex.visitors.FallbackModeVisitor;\nimport jadx.core.dex.visitors.FixSwitchOverEnum;\nimport jadx.core.dex.visitors.GenericTypesVisitor;\nimport jadx.core.dex.visitors.IDexTreeVisitor;\nimport jadx.core.dex.visitors.InitCodeVariables;\nimport jadx.core.dex.visitors.InlineMethods;\nimport jadx.core.dex.visitors.MarkMethodsForInline;\nimport jadx.core.dex.visitors.MethodInvokeVisitor;\nimport jadx.core.dex.visitors.MethodThrowsVisitor;\nimport jadx.core.dex.visitors.MethodVisitor;\nimport jadx.core.dex.visitors.ModVisitor;\nimport jadx.core.dex.visitors.MoveInlineVisitor;\nimport jadx.core.dex.visitors.OverrideMethodVisitor;\nimport jadx.core.dex.visitors.PrepareForCodeGen;\nimport jadx.core.dex.visitors.ProcessAnonymous;\nimport jadx.core.dex.visitors.ProcessInstructionsVisitor;\nimport jadx.core.dex.visitors.ProcessMethodsForInline;\nimport jadx.core.dex.visitors.ReplaceNewArray;\nimport jadx.core.dex.visitors.ShadowFieldVisitor;\nimport jadx.core.dex.visitors.SignatureProcessor;\nimport jadx.core.dex.visitors.SimplifyVisitor;\nimport jadx.core.dex.visitors.blocks.BlockFinisher;\nimport jadx.core.dex.visitors.blocks.BlockProcessor;\nimport jadx.core.dex.visitors.blocks.BlockSplitter;\nimport jadx.core.dex.visitors.debuginfo.DebugInfoApplyVisitor;\nimport jadx.core.dex.visitors.debuginfo.DebugInfoAttachVisitor;\nimport jadx.core.dex.visitors.finaly.MarkFinallyVisitor;\nimport jadx.core.dex.visitors.fixaccessmodifiers.FixAccessModifiers;\nimport jadx.core.dex.visitors.gradle.NonFinalResIdsVisitor;\nimport jadx.core.dex.visitors.kotlin.ProcessKotlinInternals;\nimport jadx.core.dex.visitors.prepare.AddAndroidConstants;\nimport jadx.core.dex.visitors.prepare.CollectConstValues;\nimport jadx.core.dex.visitors.regions.CheckRegions;\nimport jadx.core.dex.visitors.regions.CleanRegions;\nimport jadx.core.dex.visitors.regions.IfRegionVisitor;\nimport jadx.core.dex.visitors.regions.LoopRegionVisitor;\nimport jadx.core.dex.visitors.regions.RegionMakerVisitor;\nimport jadx.core.dex.visitors.regions.ReturnVisitor;\nimport jadx.core.dex.visitors.regions.SwitchBreakVisitor;\nimport jadx.core.dex.visitors.regions.SwitchOverStringVisitor;\nimport jadx.core.dex.visitors.regions.variables.ProcessVariables;\nimport jadx.core.dex.visitors.rename.CodeRenameVisitor;\nimport jadx.core.dex.visitors.rename.RenameVisitor;\nimport jadx.core.dex.visitors.rename.SourceFileRename;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.dex.visitors.ssa.SSATransform;\nimport jadx.core.dex.visitors.typeinference.FinishTypeInference;\nimport jadx.core.dex.visitors.typeinference.FixTypesVisitor;\nimport jadx.core.dex.visitors.typeinference.TypeInferenceVisitor;\nimport jadx.core.dex.visitors.usage.UsageInfoVisitor;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class Jadx {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(Jadx.class);\n\n\tprivate Jadx() {\n\t}\n\n\tpublic static List<IDexTreeVisitor> getPassesList(JadxArgs args) {\n\t\tswitch (args.getDecompilationMode()) {\n\t\t\tcase AUTO:\n\t\t\tcase RESTRUCTURE:\n\t\t\t\treturn getRegionsModePasses(args);\n\t\t\tcase SIMPLE:\n\t\t\t\treturn getSimpleModePasses(args);\n\t\t\tcase FALLBACK:\n\t\t\t\treturn getFallbackPassesList();\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unknown decompilation mode: \" + args.getDecompilationMode());\n\t\t}\n\t}\n\n\tpublic static List<IDexTreeVisitor> getPreDecompilePassesList() {\n\t\tList<IDexTreeVisitor> passes = new ArrayList<>();\n\t\tpasses.add(new SignatureProcessor());\n\t\tpasses.add(new OverrideMethodVisitor());\n\t\tpasses.add(new AddAndroidConstants());\n\n\t\t// rename and deobfuscation\n\t\tpasses.add(new DeobfuscatorVisitor());\n\t\tpasses.add(new SourceFileRename());\n\t\tpasses.add(new RenameVisitor());\n\t\tpasses.add(new SaveDeobfMapping());\n\n\t\tpasses.add(new UsageInfoVisitor());\n\t\tpasses.add(new CollectConstValues());\n\t\tpasses.add(new ProcessAnonymous());\n\t\tpasses.add(new ProcessMethodsForInline());\n\t\treturn passes;\n\t}\n\n\tpublic static List<IDexTreeVisitor> getRegionsModePasses(JadxArgs args) {\n\t\tList<IDexTreeVisitor> passes = new ArrayList<>();\n\t\t// instructions IR\n\t\tpasses.add(new CheckCode());\n\t\tif (args.isDebugInfo()) {\n\t\t\tpasses.add(new DebugInfoAttachVisitor());\n\t\t}\n\t\tpasses.add(new AttachTryCatchVisitor());\n\t\tif (args.getCommentsLevel() != CommentsLevel.NONE) {\n\t\t\tpasses.add(new AttachCommentsVisitor());\n\t\t}\n\t\tpasses.add(new AttachMethodDetails());\n\t\tpasses.add(new ProcessInstructionsVisitor());\n\n\t\t// blocks IR\n\t\tpasses.add(new BlockSplitter());\n\t\tpasses.add(new BlockProcessor());\n\t\tpasses.add(new BlockFinisher());\n\t\tif (args.isRawCFGOutput()) {\n\t\t\tpasses.add(DotGraphVisitor.dumpRaw());\n\t\t}\n\n\t\tpasses.add(new SSATransform());\n\t\tpasses.add(new MoveInlineVisitor());\n\t\tpasses.add(new ConstructorVisitor());\n\t\tpasses.add(new InitCodeVariables());\n\t\tif (args.isExtractFinally()) {\n\t\t\tpasses.add(new MarkFinallyVisitor());\n\t\t}\n\t\tpasses.add(new ConstInlineVisitor());\n\t\tpasses.add(new TypeInferenceVisitor());\n\t\tif (args.isDebugInfo()) {\n\t\t\tpasses.add(new DebugInfoApplyVisitor());\n\t\t}\n\t\tpasses.add(new FixTypesVisitor());\n\t\tpasses.add(new FinishTypeInference());\n\n\t\tpasses.add(new AdjustForIfMergeVisitor());\n\n\t\tif (args.getUseKotlinMethodsForVarNames() != JadxArgs.UseKotlinMethodsForVarNames.DISABLE) {\n\t\t\tpasses.add(new ProcessKotlinInternals());\n\t\t}\n\t\tpasses.add(new CodeRenameVisitor());\n\t\tif (args.isInlineMethods()) {\n\t\t\tpasses.add(new InlineMethods());\n\t\t}\n\t\tpasses.add(new GenericTypesVisitor());\n\t\tpasses.add(new ShadowFieldVisitor());\n\t\tpasses.add(new DeboxingVisitor());\n\t\tpasses.add(new AnonymousClassVisitor());\n\t\tpasses.add(new ModVisitor());\n\t\tpasses.add(new CodeShrinkVisitor());\n\t\tpasses.add(new ReplaceNewArray());\n\t\tif (args.isCfgOutput()) {\n\t\t\tpasses.add(DotGraphVisitor.dump());\n\t\t}\n\n\t\t// regions IR\n\t\tpasses.add(new RegionMakerVisitor());\n\t\tpasses.add(new IfRegionVisitor());\n\t\tif (args.isRestoreSwitchOverString()) {\n\t\t\tpasses.add(new SwitchOverStringVisitor());\n\t\t}\n\t\tpasses.add(new ReturnVisitor());\n\t\tpasses.add(new CleanRegions());\n\n\t\tpasses.add(new MethodThrowsVisitor());\n\n\t\tpasses.add(new CodeShrinkVisitor());\n\t\tpasses.add(new MethodInvokeVisitor());\n\t\tpasses.add(new SimplifyVisitor());\n\t\tpasses.add(new CheckRegions());\n\n\t\tpasses.add(new EnumVisitor());\n\t\tpasses.add(new FixSwitchOverEnum());\n\t\tpasses.add(new NonFinalResIdsVisitor());\n\t\tpasses.add(new ExtractFieldInit());\n\t\tpasses.add(new FixAccessModifiers());\n\t\tpasses.add(new ClassModifier());\n\t\tpasses.add(new LoopRegionVisitor());\n\t\tpasses.add(new SwitchBreakVisitor());\n\n\t\tif (args.isInlineMethods()) {\n\t\t\tpasses.add(new MarkMethodsForInline());\n\t\t}\n\t\tpasses.add(new ProcessVariables());\n\t\tpasses.add(new ApplyVariableNames());\n\n\t\tpasses.add(new PrepareForCodeGen());\n\t\tif (args.isCfgOutput()) {\n\t\t\tpasses.add(DotGraphVisitor.dumpRegions());\n\t\t}\n\t\treturn passes;\n\t}\n\n\tpublic static List<IDexTreeVisitor> getSimpleModePasses(JadxArgs args) {\n\t\tList<IDexTreeVisitor> passes = new ArrayList<>();\n\t\tif (args.isDebugInfo()) {\n\t\t\tpasses.add(new DebugInfoAttachVisitor());\n\t\t}\n\t\tpasses.add(new AttachTryCatchVisitor());\n\t\tif (args.getCommentsLevel() != CommentsLevel.NONE) {\n\t\t\tpasses.add(new AttachCommentsVisitor());\n\t\t}\n\t\tpasses.add(new AttachMethodDetails());\n\t\tpasses.add(new ProcessInstructionsVisitor());\n\n\t\tpasses.add(new BlockSplitter());\n\t\tif (args.isRawCFGOutput()) {\n\t\t\tpasses.add(DotGraphVisitor.dumpRaw());\n\t\t}\n\t\tpasses.add(new MethodVisitor(\"DisableBlockLock\", mth -> mth.add(AFlag.DISABLE_BLOCKS_LOCK)));\n\t\tpasses.add(new BlockProcessor());\n\t\tpasses.add(new SSATransform());\n\t\tpasses.add(new MoveInlineVisitor());\n\t\tpasses.add(new ConstructorVisitor());\n\t\tpasses.add(new InitCodeVariables());\n\t\tpasses.add(new ConstInlineVisitor());\n\t\tpasses.add(new TypeInferenceVisitor());\n\t\tif (args.isDebugInfo()) {\n\t\t\tpasses.add(new DebugInfoApplyVisitor());\n\t\t}\n\t\tpasses.add(new FixTypesVisitor());\n\t\tpasses.add(new FinishTypeInference());\n\t\tpasses.add(new CodeRenameVisitor());\n\t\tpasses.add(new DeboxingVisitor());\n\t\tpasses.add(new ModVisitor());\n\t\tpasses.add(new CodeShrinkVisitor());\n\t\tpasses.add(new ReplaceNewArray());\n\t\tpasses.add(new SimplifyVisitor());\n\t\tpasses.add(new MethodVisitor(\"ForceGenerateAll\", mth -> mth.remove(AFlag.DONT_GENERATE)));\n\t\tif (args.isCfgOutput()) {\n\t\t\tpasses.add(DotGraphVisitor.dump());\n\t\t}\n\t\treturn passes;\n\t}\n\n\tpublic static List<IDexTreeVisitor> getFallbackPassesList() {\n\t\tList<IDexTreeVisitor> passes = new ArrayList<>();\n\t\tpasses.add(new AttachTryCatchVisitor());\n\t\tpasses.add(new AttachCommentsVisitor());\n\t\tpasses.add(new ProcessInstructionsVisitor());\n\t\tpasses.add(new FallbackModeVisitor());\n\t\treturn passes;\n\t}\n\n\tpublic static final String VERSION_DEV = \"dev\";\n\n\tprivate static String version;\n\n\tpublic static String getVersion() {\n\t\tif (version == null) {\n\t\t\tversion = searchJadxVersion();\n\t\t}\n\t\treturn version;\n\t}\n\n\tpublic static boolean isDevVersion() {\n\t\treturn getVersion().equals(VERSION_DEV);\n\t}\n\n\tprivate static String searchJadxVersion() {\n\t\ttry {\n\t\t\tClassLoader classLoader = Jadx.class.getClassLoader();\n\t\t\tif (classLoader != null) {\n\t\t\t\tEnumeration<URL> resources = classLoader.getResources(\"META-INF/MANIFEST.MF\");\n\t\t\t\twhile (resources.hasMoreElements()) {\n\t\t\t\t\ttry (InputStream is = resources.nextElement().openStream()) {\n\t\t\t\t\t\tManifest manifest = new Manifest(is);\n\t\t\t\t\t\tString ver = manifest.getMainAttributes().getValue(\"jadx-version\");\n\t\t\t\t\t\tif (ver != null) {\n\t\t\t\t\t\t\treturn ver;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Can't get manifest file\", e);\n\t\t}\n\t\treturn VERSION_DEV;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/ProcessClass.java",
    "content": "package jadx.core;\n\nimport java.util.EnumMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.DecompilationMode;\nimport jadx.api.ICodeInfo;\nimport jadx.api.JadxArgs;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.core.codegen.CodeGen;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.DecompileModeOverrideAttr;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.LoadStage;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.DepthTraversal;\nimport jadx.core.dex.visitors.IDexTreeVisitor;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.dex.nodes.ProcessState.GENERATED_AND_UNLOADED;\nimport static jadx.core.dex.nodes.ProcessState.LOADED;\nimport static jadx.core.dex.nodes.ProcessState.NOT_LOADED;\nimport static jadx.core.dex.nodes.ProcessState.PROCESS_COMPLETE;\nimport static jadx.core.dex.nodes.ProcessState.PROCESS_STARTED;\n\npublic class ProcessClass {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ProcessClass.class);\n\n\tprivate static final ICodeInfo NOT_GENERATED = new SimpleCodeInfo(\"\");\n\n\tprivate final List<IDexTreeVisitor> passes;\n\n\tpublic ProcessClass(List<IDexTreeVisitor> passesList) {\n\t\tthis.passes = passesList;\n\t}\n\n\t@Nullable\n\tprivate ICodeInfo process(ClassNode cls, boolean codegen) {\n\t\tif (!codegen && cls.getState() == PROCESS_COMPLETE) {\n\t\t\t// nothing to do\n\t\t\treturn null;\n\t\t}\n\t\tUtils.checkThreadInterrupt();\n\t\tsynchronized (cls.getClassInfo()) {\n\t\t\ttry {\n\t\t\t\tif (cls.contains(AFlag.CLASS_DEEP_RELOAD)) {\n\t\t\t\t\tcls.remove(AFlag.CLASS_DEEP_RELOAD);\n\t\t\t\t\tcls.deepUnload();\n\t\t\t\t\tcls.add(AFlag.CLASS_UNLOADED);\n\t\t\t\t}\n\t\t\t\tif (cls.contains(AFlag.CLASS_UNLOADED)) {\n\t\t\t\t\tcls.root().runPreDecompileStageForClass(cls);\n\t\t\t\t\tcls.remove(AFlag.CLASS_UNLOADED);\n\t\t\t\t}\n\t\t\t\tif (cls.getState() == GENERATED_AND_UNLOADED) {\n\t\t\t\t\t// force loading code again\n\t\t\t\t\tcls.setState(NOT_LOADED);\n\t\t\t\t}\n\t\t\t\tif (codegen) {\n\t\t\t\t\tcls.setLoadStage(LoadStage.CODEGEN_STAGE);\n\t\t\t\t\tif (cls.contains(AFlag.RELOAD_AT_CODEGEN_STAGE)) {\n\t\t\t\t\t\tcls.remove(AFlag.RELOAD_AT_CODEGEN_STAGE);\n\t\t\t\t\t\tcls.unload();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tcls.setLoadStage(LoadStage.PROCESS_STAGE);\n\t\t\t\t}\n\t\t\t\tif (cls.getState() == NOT_LOADED) {\n\t\t\t\t\tcls.load();\n\t\t\t\t}\n\t\t\t\tif (cls.getState() == LOADED) {\n\t\t\t\t\tcls.setState(PROCESS_STARTED);\n\t\t\t\t\tfor (IDexTreeVisitor visitor : passes) {\n\t\t\t\t\t\tDepthTraversal.visit(visitor, cls);\n\t\t\t\t\t}\n\t\t\t\t\tcls.setState(PROCESS_COMPLETE);\n\t\t\t\t}\n\t\t\t\tif (codegen) {\n\t\t\t\t\tUtils.checkThreadInterrupt();\n\t\t\t\t\tICodeInfo code = CodeGen.generate(cls);\n\t\t\t\t\tif (!cls.contains(AFlag.DONT_UNLOAD_CLASS)) {\n\t\t\t\t\t\tcls.unload();\n\t\t\t\t\t\tcls.setState(GENERATED_AND_UNLOADED);\n\t\t\t\t\t}\n\t\t\t\t\treturn code;\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t} catch (StackOverflowError | Exception e) {\n\t\t\t\tif (codegen) {\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t\tcls.addError(\"Class process error: \" + e.getClass().getSimpleName(), e);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\t@NotNull\n\tpublic ICodeInfo generateCode(ClassNode cls) {\n\t\tClassNode topParentClass = cls.getTopParentClass();\n\t\tif (topParentClass != cls) {\n\t\t\treturn generateCode(topParentClass);\n\t\t}\n\t\ttry {\n\t\t\tif (cls.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\tprocess(cls, false);\n\t\t\t\treturn NOT_GENERATED;\n\t\t\t}\n\t\t\tfor (ClassNode depCls : cls.getDependencies()) {\n\t\t\t\tprocess(depCls, false);\n\t\t\t}\n\t\t\tif (!cls.getCodegenDeps().isEmpty()) {\n\t\t\t\tprocess(cls, false);\n\t\t\t\tfor (ClassNode codegenDep : cls.getCodegenDeps()) {\n\t\t\t\t\tprocess(codegenDep, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\tICodeInfo code = process(cls, true);\n\t\t\tif (code == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Codegen failed\");\n\t\t\t}\n\t\t\treturn code;\n\t\t} catch (StackOverflowError | Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to generate code for class: \" + cls.getFullName(), e);\n\t\t}\n\t}\n\n\t/**\n\t * Load and process class without its deps\n\t */\n\tpublic void forceProcess(ClassNode cls) {\n\t\tClassNode topParentClass = cls.getTopParentClass();\n\t\tif (topParentClass != cls) {\n\t\t\tforceProcess(topParentClass);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tprocess(cls, false);\n\t\t} catch (StackOverflowError | Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to process class: \" + cls.getFullName(), e);\n\t\t}\n\t}\n\n\t/**\n\t * Generate code for class without processing its deps\n\t */\n\tpublic @Nullable ICodeInfo forceGenerateCode(ClassNode cls) {\n\t\ttry {\n\t\t\treturn process(cls, true);\n\t\t} catch (StackOverflowError | Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to generate code for class: \" + cls.getFullName(), e);\n\t\t}\n\t}\n\n\tprivate final Map<DecompilationMode, ProcessClass> modesMap = new EnumMap<>(DecompilationMode.class);\n\n\tpublic @Nullable ICodeInfo forceGenerateCodeForMode(ClassNode cls, DecompilationMode mode) {\n\t\tsynchronized (modesMap) {\n\t\t\tProcessClass prCls = modesMap.computeIfAbsent(mode, m -> {\n\t\t\t\tRootNode root = cls.root();\n\t\t\t\tProcessClass newPrCls = new ProcessClass(getPassesForMode(root.getArgs(), m));\n\t\t\t\tnewPrCls.initPasses(root);\n\t\t\t\treturn newPrCls;\n\t\t\t});\n\t\t\ttry {\n\t\t\t\tcls.addAttr(new DecompileModeOverrideAttr(mode));\n\t\t\t\treturn prCls.forceGenerateCode(cls);\n\t\t\t} finally {\n\t\t\t\tcls.remove(AType.DECOMPILE_MODE_OVERRIDE);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static List<IDexTreeVisitor> getPassesForMode(JadxArgs baseArgs, DecompilationMode mode) {\n\t\tswitch (mode) {\n\t\t\tcase FALLBACK:\n\t\t\t\treturn Jadx.getFallbackPassesList();\n\n\t\t\tcase SIMPLE:\n\t\t\t\t// copy properties into new args\n\t\t\t\t// keep in sync with properties usage in Jadx.getSimpleModePasses method\n\t\t\t\tJadxArgs args = new JadxArgs();\n\t\t\t\targs.setDebugInfo(baseArgs.isDebugInfo());\n\t\t\t\targs.setCommentsLevel(baseArgs.getCommentsLevel());\n\t\t\t\treturn Jadx.getSimpleModePasses(args);\n\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unexpected decompilation mode: \" + mode);\n\t\t}\n\t}\n\n\tpublic void initPasses(RootNode root) {\n\t\tfor (IDexTreeVisitor pass : passes) {\n\t\t\ttry {\n\t\t\t\tpass.init(root);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Visitor init failed: {}\", pass.getClass().getSimpleName(), e);\n\t\t\t}\n\t\t}\n\t}\n\n\t// TODO: make passes list private and not visible\n\tpublic List<IDexTreeVisitor> getPasses() {\n\t\treturn passes;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/clsp/ClsSet.java",
    "content": "package jadx.core.clsp;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.DataInputStream;\nimport java.io.DataOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.stream.Stream;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.exceptions.DecodeException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\n\n/**\n * Classes list for import into classpath graph\n */\npublic class ClsSet {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ClsSet.class);\n\n\tprivate static final String CLST_EXTENSION = \".jcst\";\n\tprivate static final String CLST_FILENAME = \"core\" + CLST_EXTENSION;\n\n\tprivate static final String CLST_PATH = \"/clst/\" + CLST_FILENAME;\n\n\tprivate static final String JADX_CLS_SET_HEADER = \"jadx-cst\";\n\tprivate static final int VERSION = 5;\n\n\tprivate static final String STRING_CHARSET = \"US-ASCII\";\n\n\tprivate static final ArgType[] EMPTY_ARGTYPE_ARRAY = new ArgType[0];\n\tprivate static final ArgType[] OBJECT_ARGTYPE_ARRAY = new ArgType[] { ArgType.OBJECT };\n\n\tprivate final RootNode root;\n\n\tprivate int androidApiLevel;\n\n\tpublic ClsSet(RootNode root) {\n\t\tthis.root = root;\n\t}\n\n\tprivate enum TypeEnum {\n\t\tWILDCARD,\n\t\tGENERIC,\n\t\tGENERIC_TYPE_VARIABLE,\n\t\tOUTER_GENERIC,\n\t\tOBJECT,\n\t\tARRAY,\n\t\tPRIMITIVE\n\t}\n\n\tprivate ClspClass[] classes;\n\n\tpublic void loadFromClstFile() throws IOException, DecodeException {\n\t\tlong startTime = System.currentTimeMillis();\n\t\ttry (InputStream input = ClsSet.class.getResourceAsStream(CLST_PATH)) {\n\t\t\tif (input == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Can't load classpath file: \" + CLST_PATH);\n\t\t\t}\n\t\t\tload(input);\n\t\t}\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tlong time = System.currentTimeMillis() - startTime;\n\t\t\tint methodsCount = Stream.of(classes).mapToInt(clspClass -> clspClass.getMethodsMap().size()).sum();\n\t\t\tLOG.debug(\"Clst file loaded in {}ms, android api: {}, classes: {}, methods: {}\",\n\t\t\t\t\ttime, androidApiLevel, classes.length, methodsCount);\n\t\t}\n\t}\n\n\tpublic void loadFrom(RootNode root) {\n\t\tList<ClassNode> list = root.getClasses(true);\n\t\tMap<String, ClspClass> names = new HashMap<>(list.size());\n\t\tint k = 0;\n\t\tfor (ClassNode cls : list) {\n\t\t\tArgType clsType = cls.getClassInfo().getType();\n\t\t\tString clsRawName = clsType.getObject();\n\t\t\tcls.load();\n\n\t\t\tClspClassSource source = getClspClassSource(cls);\n\t\t\tClspClass nClass = new ClspClass(clsType, k, cls.getAccessFlags().rawValue(), source);\n\t\t\tif (names.put(clsRawName, nClass) != null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Duplicate class: \" + clsRawName);\n\t\t\t}\n\t\t\tk++;\n\t\t\tnClass.setTypeParameters(cls.getGenericTypeParameters());\n\t\t\tnClass.setMethods(getMethodsDetails(cls));\n\t\t}\n\t\tclasses = new ClspClass[k];\n\t\tk = 0;\n\t\tfor (ClassNode cls : list) {\n\t\t\tClspClass nClass = getCls(cls, names);\n\t\t\tif (nClass == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Missing class: \" + cls);\n\t\t\t}\n\t\t\tnClass.setParents(makeParentsArray(cls));\n\t\t\tclasses[k] = nClass;\n\t\t\tk++;\n\t\t}\n\t}\n\n\tprivate static ClspClassSource getClspClassSource(ClassNode cls) {\n\t\tString inputFileName = cls.getClsData().getInputFileName();\n\t\tint idx = inputFileName.indexOf(':');\n\t\tString sourceFile = inputFileName.substring(0, idx);\n\t\tClspClassSource source = ClspClassSource.getClspClassSource(sourceFile);\n\t\tif (source == ClspClassSource.APP) {\n\t\t\tthrow new JadxRuntimeException(\"Unexpected input file: \" + inputFileName);\n\t\t}\n\t\treturn source;\n\t}\n\n\tprivate List<ClspMethod> getMethodsDetails(ClassNode cls) {\n\t\tList<MethodNode> methodsList = cls.getMethods();\n\t\tList<ClspMethod> methods = new ArrayList<>(methodsList.size());\n\t\tfor (MethodNode mth : methodsList) {\n\t\t\tprocessMethodDetails(mth, methods);\n\t\t}\n\t\treturn methods;\n\t}\n\n\tprivate void processMethodDetails(MethodNode mth, List<ClspMethod> methods) {\n\t\tAccessInfo accessFlags = mth.getAccessFlags();\n\t\tif (accessFlags.isPrivate() || accessFlags.isSynthetic() || accessFlags.isBridge()) {\n\t\t\treturn;\n\t\t}\n\t\tClspMethod clspMethod = new ClspMethod(mth.getMethodInfo(), mth.getArgTypes(),\n\t\t\t\tmth.getReturnType(), mth.getTypeParameters(),\n\t\t\t\tmth.getThrows(), accessFlags.rawValue());\n\t\tmethods.add(clspMethod);\n\t}\n\n\tpublic static ArgType[] makeParentsArray(ClassNode cls) {\n\t\tArgType superClass = cls.getSuperClass();\n\t\tif (superClass == null) {\n\t\t\t// cls is java.lang.Object\n\t\t\treturn EMPTY_ARGTYPE_ARRAY;\n\t\t}\n\t\tint interfacesCount = cls.getInterfaces().size();\n\t\tif (interfacesCount == 0 && superClass == ArgType.OBJECT) {\n\t\t\treturn OBJECT_ARGTYPE_ARRAY;\n\t\t}\n\t\tArgType[] parents = new ArgType[1 + interfacesCount];\n\t\tparents[0] = superClass;\n\t\tint k = 1;\n\t\tfor (ArgType iface : cls.getInterfaces()) {\n\t\t\tparents[k] = iface;\n\t\t\tk++;\n\t\t}\n\t\treturn parents;\n\t}\n\n\tprivate static ClspClass getCls(ClassNode cls, Map<String, ClspClass> names) {\n\t\treturn getCls(cls.getRawName(), names);\n\t}\n\n\tprivate static ClspClass getCls(ArgType clsType, Map<String, ClspClass> names) {\n\t\treturn getCls(clsType.getObject(), names);\n\t}\n\n\tprivate static ClspClass getCls(String fullName, Map<String, ClspClass> names) {\n\t\tClspClass cls = names.get(fullName);\n\t\tif (cls == null) {\n\t\t\tLOG.debug(\"Class not found: {}\", fullName);\n\t\t}\n\t\treturn cls;\n\t}\n\n\tpublic void save(Path path) throws IOException {\n\t\tFileUtils.makeDirsForFile(path);\n\t\tString outputName = path.getFileName().toString();\n\t\tif (outputName.endsWith(CLST_EXTENSION)) {\n\t\t\ttry (BufferedOutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(path))) {\n\t\t\t\tsave(outputStream);\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(\"Unknown file format: \" + outputName);\n\t\t}\n\t}\n\n\tprivate void save(OutputStream output) throws IOException {\n\t\tDataOutputStream out = new DataOutputStream(output);\n\t\tout.writeBytes(JADX_CLS_SET_HEADER);\n\t\tout.writeByte(VERSION);\n\t\tout.writeInt(androidApiLevel);\n\n\t\tMap<String, ClspClass> names = new HashMap<>(classes.length);\n\t\tout.writeInt(classes.length);\n\t\tfor (ClspClass cls : classes) {\n\t\t\tout.writeInt(cls.getAccFlags());\n\t\t\twriteUnsignedByte(out, cls.getSource().ordinal());\n\t\t\tString clsName = cls.getName();\n\t\t\twriteString(out, clsName);\n\t\t\tnames.put(clsName, cls);\n\t\t}\n\t\tfor (ClspClass cls : classes) {\n\t\t\twriteArgTypesArray(out, cls.getParents(), names);\n\t\t\twriteArgTypesList(out, cls.getTypeParameters(), names);\n\t\t\tList<ClspMethod> methods = cls.getSortedMethodsList();\n\t\t\tout.writeShort(methods.size());\n\t\t\tfor (ClspMethod method : methods) {\n\t\t\t\twriteMethod(out, method, names);\n\t\t\t}\n\t\t}\n\t\tint methodsCount = Stream.of(classes).mapToInt(c -> c.getMethodsMap().size()).sum();\n\t\tLOG.info(\"Classes: {}, methods: {}, file size: {} bytes\", classes.length, methodsCount, out.size());\n\t}\n\n\tprivate static void writeMethod(DataOutputStream out, ClspMethod method, Map<String, ClspClass> names) throws IOException {\n\t\tMethodInfo methodInfo = method.getMethodInfo();\n\t\twriteString(out, methodInfo.getName());\n\t\twriteArgTypesList(out, methodInfo.getArgumentsTypes(), names);\n\t\twriteArgType(out, methodInfo.getReturnType(), names);\n\n\t\twriteArgTypesList(out, method.containsGenericArgs() ? method.getArgTypes() : Collections.emptyList(), names);\n\t\twriteArgType(out, method.getReturnType(), names);\n\t\twriteArgTypesList(out, method.getTypeParameters(), names);\n\t\tout.writeInt(method.getRawAccessFlags());\n\t\twriteArgTypesList(out, method.getThrows(), names);\n\t}\n\n\tprivate static void writeArgTypesList(DataOutputStream out, List<ArgType> list, Map<String, ClspClass> names) throws IOException {\n\t\tint size = list.size();\n\t\twriteUnsignedByte(out, size);\n\t\tif (size != 0) {\n\t\t\tfor (ArgType type : list) {\n\t\t\t\twriteArgType(out, type, names);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void writeArgTypesArray(DataOutputStream out, @Nullable ArgType[] arr, Map<String, ClspClass> names) throws IOException {\n\t\tif (arr == null) {\n\t\t\tout.writeByte(-1);\n\t\t\treturn;\n\t\t}\n\t\tif (arr == OBJECT_ARGTYPE_ARRAY) {\n\t\t\tout.writeByte(-2);\n\t\t\treturn;\n\t\t}\n\t\tint size = arr.length;\n\t\tout.writeByte(size);\n\t\tif (size != 0) {\n\t\t\tfor (ArgType type : arr) {\n\t\t\t\twriteArgType(out, type, names);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void writeArgType(DataOutputStream out, ArgType argType, Map<String, ClspClass> names) throws IOException {\n\t\tif (argType == null) {\n\t\t\tout.writeByte(-1);\n\t\t\treturn;\n\t\t}\n\t\tif (argType.isPrimitive()) {\n\t\t\tout.writeByte(TypeEnum.PRIMITIVE.ordinal());\n\t\t\tout.writeByte(argType.getPrimitiveType().getShortName().charAt(0));\n\t\t} else if (argType.getOuterType() != null) {\n\t\t\tout.writeByte(TypeEnum.OUTER_GENERIC.ordinal());\n\t\t\twriteArgType(out, argType.getOuterType(), names);\n\t\t\twriteArgType(out, argType.getInnerType(), names);\n\t\t} else if (argType.getWildcardType() != null) {\n\t\t\tout.writeByte(TypeEnum.WILDCARD.ordinal());\n\t\t\tArgType.WildcardBound bound = argType.getWildcardBound();\n\t\t\tout.writeByte(bound.getNum());\n\t\t\tif (bound != ArgType.WildcardBound.UNBOUND) {\n\t\t\t\twriteArgType(out, argType.getWildcardType(), names);\n\t\t\t}\n\t\t} else if (argType.isGeneric()) {\n\t\t\tout.writeByte(TypeEnum.GENERIC.ordinal());\n\t\t\tout.writeInt(getCls(argType, names).getId());\n\t\t\twriteArgTypesList(out, argType.getGenericTypes(), names);\n\t\t} else if (argType.isGenericType()) {\n\t\t\tout.writeByte(TypeEnum.GENERIC_TYPE_VARIABLE.ordinal());\n\t\t\twriteString(out, argType.getObject());\n\t\t\twriteArgTypesList(out, argType.getExtendTypes(), names);\n\t\t} else if (argType.isObject()) {\n\t\t\tout.writeByte(TypeEnum.OBJECT.ordinal());\n\t\t\tout.writeInt(getCls(argType, names).getId());\n\t\t} else if (argType.isArray()) {\n\t\t\tout.writeByte(TypeEnum.ARRAY.ordinal());\n\t\t\twriteArgType(out, argType.getArrayElement(), names);\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(\"Cannot save type: \" + argType);\n\t\t}\n\t}\n\n\tprivate void load(InputStream input) throws IOException, DecodeException {\n\t\ttry (DataInputStream in = new DataInputStream(new BufferedInputStream(input))) {\n\t\t\tbyte[] header = new byte[JADX_CLS_SET_HEADER.length()];\n\t\t\tint readHeaderLength = in.read(header);\n\t\t\tif (readHeaderLength != JADX_CLS_SET_HEADER.length()\n\t\t\t\t\t|| !JADX_CLS_SET_HEADER.equals(new String(header, STRING_CHARSET))) {\n\t\t\t\tthrow new DecodeException(\"Wrong jadx class set header\");\n\t\t\t}\n\t\t\tint version = in.readByte();\n\t\t\tif (version != VERSION) {\n\t\t\t\tthrow new DecodeException(\"Wrong jadx class set version, got: \" + version + \", expect: \" + VERSION);\n\t\t\t}\n\t\t\tandroidApiLevel = in.readInt();\n\t\t\tint clsCount = in.readInt();\n\t\t\tclasses = new ClspClass[clsCount];\n\t\t\tfor (int i = 0; i < clsCount; i++) {\n\t\t\t\tint accFlags = in.readInt();\n\t\t\t\tClspClassSource clsSource = readClsSource(in);\n\t\t\t\tString name = readString(in);\n\t\t\t\tclasses[i] = new ClspClass(ArgType.object(name), i, accFlags, clsSource);\n\t\t\t}\n\t\t\tfor (int i = 0; i < clsCount; i++) {\n\t\t\t\tClspClass nClass = classes[i];\n\t\t\t\tClassInfo clsInfo = ClassInfo.fromType(root, nClass.getClsType());\n\t\t\t\tnClass.setParents(readArgTypesArray(in));\n\t\t\t\tnClass.setTypeParameters(readArgTypesList(in));\n\t\t\t\tnClass.setMethods(readClsMethods(in, clsInfo));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static ClspClassSource readClsSource(DataInputStream in) throws IOException, DecodeException {\n\t\tint source = readUnsignedByte(in);\n\t\tClspClassSource[] clspClassSources = ClspClassSource.values();\n\t\tif (source < 0 || source > clspClassSources.length) {\n\t\t\tthrow new DecodeException(\"Wrong jadx source identifier: \" + source);\n\t\t}\n\t\treturn clspClassSources[source];\n\t}\n\n\tprivate List<ClspMethod> readClsMethods(DataInputStream in, ClassInfo clsInfo) throws IOException {\n\t\tint mCount = in.readShort();\n\t\tList<ClspMethod> methods = new ArrayList<>(mCount);\n\t\tfor (int j = 0; j < mCount; j++) {\n\t\t\tmethods.add(readMethod(in, clsInfo));\n\t\t}\n\t\treturn methods;\n\t}\n\n\tprivate ClspMethod readMethod(DataInputStream in, ClassInfo clsInfo) throws IOException {\n\t\tString name = readString(in);\n\t\tList<ArgType> argTypes = readArgTypesList(in);\n\t\tArgType retType = readArgType(in);\n\t\tList<ArgType> genericArgTypes = readArgTypesList(in);\n\t\tif (genericArgTypes.isEmpty() || Objects.equals(genericArgTypes, argTypes)) {\n\t\t\tgenericArgTypes = argTypes;\n\t\t}\n\t\tArgType genericRetType = readArgType(in);\n\t\tif (Objects.equals(genericRetType, retType)) {\n\t\t\tgenericRetType = retType;\n\t\t}\n\t\tList<ArgType> typeParameters = readArgTypesList(in);\n\t\tint accFlags = in.readInt();\n\t\tList<ArgType> throwList = readArgTypesList(in);\n\t\tMethodInfo methodInfo = MethodInfo.fromDetails(root, clsInfo, name, argTypes, retType);\n\t\treturn new ClspMethod(methodInfo,\n\t\t\t\tgenericArgTypes, genericRetType,\n\t\t\t\ttypeParameters, throwList, accFlags);\n\t}\n\n\tprivate List<ArgType> readArgTypesList(DataInputStream in) throws IOException {\n\t\tint count = in.readByte();\n\t\tif (count == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<ArgType> list = new ArrayList<>(count);\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tlist.add(readArgType(in));\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Nullable\n\tprivate ArgType[] readArgTypesArray(DataInputStream in) throws IOException {\n\t\tint count = in.readByte();\n\t\tswitch (count) {\n\t\t\tcase -1:\n\t\t\t\treturn null;\n\t\t\tcase -2:\n\t\t\t\treturn OBJECT_ARGTYPE_ARRAY;\n\t\t\tcase 0:\n\t\t\t\treturn EMPTY_ARGTYPE_ARRAY;\n\t\t\tdefault:\n\t\t\t\tArgType[] arr = new ArgType[count];\n\t\t\t\tfor (int i = 0; i < count; i++) {\n\t\t\t\t\tarr[i] = readArgType(in);\n\t\t\t\t}\n\t\t\t\treturn arr;\n\t\t}\n\t}\n\n\tprivate ArgType readArgType(DataInputStream in) throws IOException {\n\t\tint ordinal = in.readByte();\n\t\tif (ordinal == -1) {\n\t\t\treturn null;\n\t\t}\n\t\tswitch (TypeEnum.values()[ordinal]) {\n\t\t\tcase WILDCARD:\n\t\t\t\tArgType.WildcardBound bound = ArgType.WildcardBound.getByNum(in.readByte());\n\t\t\t\tif (bound == ArgType.WildcardBound.UNBOUND) {\n\t\t\t\t\treturn ArgType.WILDCARD;\n\t\t\t\t}\n\t\t\t\tArgType objType = readArgType(in);\n\t\t\t\treturn ArgType.wildcard(objType, bound);\n\n\t\t\tcase OUTER_GENERIC:\n\t\t\t\tArgType outerType = readArgType(in);\n\t\t\t\tArgType innerType = readArgType(in);\n\t\t\t\treturn ArgType.outerGeneric(outerType, innerType);\n\n\t\t\tcase GENERIC:\n\t\t\t\tArgType clsType = classes[in.readInt()].getClsType();\n\t\t\t\treturn ArgType.generic(clsType, readArgTypesList(in));\n\n\t\t\tcase GENERIC_TYPE_VARIABLE:\n\t\t\t\tString typeVar = readString(in);\n\t\t\t\tList<ArgType> extendTypes = readArgTypesList(in);\n\t\t\t\treturn ArgType.genericType(typeVar, extendTypes);\n\n\t\t\tcase OBJECT:\n\t\t\t\treturn classes[in.readInt()].getClsType();\n\n\t\t\tcase ARRAY:\n\t\t\t\treturn ArgType.array(Objects.requireNonNull(readArgType(in)));\n\n\t\t\tcase PRIMITIVE:\n\t\t\t\tchar shortName = (char) in.readByte();\n\t\t\t\treturn ArgType.parse(shortName);\n\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unsupported Arg Type: \" + ordinal);\n\t\t}\n\t}\n\n\tprivate static void writeString(DataOutputStream out, String name) throws IOException {\n\t\tbyte[] bytes = name.getBytes(STRING_CHARSET);\n\t\tint len = bytes.length;\n\t\tif (len >= 0xFF) {\n\t\t\tthrow new JadxRuntimeException(\"String is too long: \" + name);\n\t\t}\n\t\twriteUnsignedByte(out, bytes.length);\n\t\tout.write(bytes);\n\t}\n\n\tprivate static String readString(DataInputStream in) throws IOException {\n\t\tint len = readUnsignedByte(in);\n\t\treturn readString(in, len);\n\t}\n\n\tprivate static String readString(DataInputStream in, int len) throws IOException {\n\t\tbyte[] bytes = new byte[len];\n\t\tint count = in.read(bytes);\n\t\twhile (count != len) {\n\t\t\tint res = in.read(bytes, count, len - count);\n\t\t\tif (res == -1) {\n\t\t\t\tthrow new IOException(\"String read error\");\n\t\t\t} else {\n\t\t\t\tcount += res;\n\t\t\t}\n\t\t}\n\t\treturn new String(bytes, STRING_CHARSET);\n\t}\n\n\tprivate static void writeUnsignedByte(DataOutputStream out, int value) throws IOException {\n\t\tif (value < 0 || value >= 0xFF) {\n\t\t\tthrow new JadxRuntimeException(\"Unsigned byte value is too big: \" + value);\n\t\t}\n\t\tout.writeByte(value);\n\t}\n\n\tprivate static int readUnsignedByte(DataInputStream in) throws IOException {\n\t\treturn ((int) in.readByte()) & 0xFF;\n\t}\n\n\tpublic int getClassesCount() {\n\t\treturn classes.length;\n\t}\n\n\tpublic void addToMap(Map<String, ClspClass> nameMap) {\n\t\tfor (ClspClass cls : classes) {\n\t\t\tnameMap.put(cls.getName(), cls);\n\t\t}\n\t}\n\n\tpublic int getAndroidApiLevel() {\n\t\treturn androidApiLevel;\n\t}\n\n\tpublic void setAndroidApiLevel(int androidApiLevel) {\n\t\tthis.androidApiLevel = androidApiLevel;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/clsp/ClspClass.java",
    "content": "package jadx.core.clsp;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.intellij.lang.annotations.MagicConstant;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.core.dex.instructions.args.ArgType;\n\n/**\n * Class node in classpath graph\n */\npublic class ClspClass {\n\n\tprivate final ArgType clsType;\n\tprivate final int id;\n\tprivate final int accFlags;\n\tprivate ArgType[] parents;\n\tprivate Map<String, ClspMethod> methodsMap = Collections.emptyMap();\n\tprivate List<ArgType> typeParameters = Collections.emptyList();\n\n\tprivate final ClspClassSource source;\n\n\tpublic ClspClass(ArgType clsType, int id, int accFlags, ClspClassSource source) {\n\t\tthis.clsType = clsType;\n\t\tthis.id = id;\n\t\tthis.accFlags = accFlags;\n\t\tthis.source = source;\n\t}\n\n\tpublic String getName() {\n\t\treturn clsType.getObject();\n\t}\n\n\tpublic ArgType getClsType() {\n\t\treturn clsType;\n\t}\n\n\tpublic int getId() {\n\t\treturn id;\n\t}\n\n\tpublic int getAccFlags() {\n\t\treturn accFlags;\n\t}\n\n\tpublic boolean isInterface() {\n\t\treturn AccessFlags.hasFlag(accFlags, AccessFlags.INTERFACE);\n\t}\n\n\tpublic boolean hasAccFlag(@MagicConstant(flagsFromClass = AccessFlags.class) int flags) {\n\t\treturn AccessFlags.hasFlag(accFlags, flags);\n\t}\n\n\tpublic ArgType[] getParents() {\n\t\treturn parents;\n\t}\n\n\tpublic void setParents(ArgType[] parents) {\n\t\tthis.parents = parents;\n\t}\n\n\tpublic Map<String, ClspMethod> getMethodsMap() {\n\t\treturn methodsMap;\n\t}\n\n\tpublic List<ClspMethod> getSortedMethodsList() {\n\t\tList<ClspMethod> list = new ArrayList<>(methodsMap.size());\n\t\tlist.addAll(methodsMap.values());\n\t\tCollections.sort(list);\n\t\treturn list;\n\t}\n\n\tpublic void setMethodsMap(Map<String, ClspMethod> methodsMap) {\n\t\tthis.methodsMap = Objects.requireNonNull(methodsMap);\n\t}\n\n\tpublic void setMethods(List<ClspMethod> methods) {\n\t\tMap<String, ClspMethod> map = new HashMap<>(methods.size());\n\t\tfor (ClspMethod mth : methods) {\n\t\t\tmap.put(mth.getMethodInfo().getShortId(), mth);\n\t\t}\n\t\tsetMethodsMap(map);\n\t}\n\n\tpublic List<ArgType> getTypeParameters() {\n\t\treturn typeParameters;\n\t}\n\n\tpublic void setTypeParameters(List<ArgType> typeParameters) {\n\t\tthis.typeParameters = typeParameters;\n\t}\n\n\tpublic ClspClassSource getSource() {\n\t\treturn this.source;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn clsType.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tClspClass nClass = (ClspClass) o;\n\t\treturn clsType.equals(nClass.clsType);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn clsType.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/clsp/ClspClassSource.java",
    "content": "package jadx.core.clsp;\n\npublic enum ClspClassSource {\n\tAPP(\"\"),\n\tCORE(\"android.jar\"),\n\tANDROID_CAR(\"android.car.jar\"),\n\tAPACHE_HTTP_LEGACY_CLIENT(\"org.apache.http.legacy.jar\");\n\n\tprivate final String jarFile;\n\n\tClspClassSource(String jarFile) {\n\t\tthis.jarFile = jarFile;\n\t}\n\n\tpublic String getJarFile() {\n\t\treturn jarFile;\n\t}\n\n\tpublic static ClspClassSource getClspClassSource(String jarFile) {\n\t\tfor (ClspClassSource classSource : ClspClassSource.values()) {\n\t\t\tif (classSource.getJarFile().equals(jarFile)) {\n\t\t\t\treturn classSource;\n\t\t\t}\n\t\t}\n\t\treturn APP;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java",
    "content": "package jadx.core.clsp;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.Consts;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.exceptions.DecodeException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n/**\n * Classes hierarchy graph with methods additional info\n */\npublic class ClspGraph {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ClspGraph.class);\n\n\tprivate final RootNode root;\n\tprivate Map<String, ClspClass> nameMap;\n\tprivate Map<String, Set<String>> superTypesCache;\n\tprivate Map<String, List<String>> implementsCache;\n\n\tprivate final Set<String> missingClasses = new HashSet<>();\n\n\tpublic ClspGraph(RootNode rootNode) {\n\t\tthis.root = rootNode;\n\t}\n\n\tpublic void loadClsSetFile() throws IOException, DecodeException {\n\t\tClsSet set = new ClsSet(root);\n\t\tset.loadFromClstFile();\n\t\taddClasspath(set);\n\t}\n\n\tpublic void addClasspath(ClsSet set) {\n\t\tif (nameMap == null) {\n\t\t\tnameMap = new HashMap<>(set.getClassesCount());\n\t\t\tset.addToMap(nameMap);\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(\"Classpath already loaded\");\n\t\t}\n\t}\n\n\tpublic void addApp(List<ClassNode> classes) {\n\t\tif (nameMap == null) {\n\t\t\tnameMap = new HashMap<>(classes.size());\n\t\t}\n\t\tfor (ClassNode cls : classes) {\n\t\t\taddClass(cls);\n\t\t}\n\t}\n\n\tpublic void initCache() {\n\t\tfillSuperTypesCache();\n\t\tfillImplementsCache();\n\t}\n\n\tpublic boolean isClsKnown(String fullName) {\n\t\treturn nameMap.containsKey(fullName);\n\t}\n\n\tpublic ClspClass getClsDetails(ArgType type) {\n\t\treturn nameMap.get(type.getObject());\n\t}\n\n\t@Nullable\n\tpublic IMethodDetails getMethodDetails(MethodInfo methodInfo) {\n\t\tClspClass cls = nameMap.get(methodInfo.getDeclClass().getRawName());\n\t\tif (cls == null) {\n\t\t\treturn null;\n\t\t}\n\t\tClspMethod clspMethod = getMethodFromClass(cls, methodInfo);\n\t\tif (clspMethod != null) {\n\t\t\treturn clspMethod;\n\t\t}\n\t\t// deep search\n\t\tfor (ArgType parent : cls.getParents()) {\n\t\t\tClspClass clspParent = getClspClass(parent);\n\t\t\tif (clspParent != null) {\n\t\t\t\tClspMethod methodFromParent = getMethodFromClass(clspParent, methodInfo);\n\t\t\t\tif (methodFromParent != null) {\n\t\t\t\t\treturn methodFromParent;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// unknown method\n\t\treturn new SimpleMethodDetails(methodInfo);\n\t}\n\n\tprivate ClspMethod getMethodFromClass(ClspClass cls, MethodInfo methodInfo) {\n\t\treturn cls.getMethodsMap().get(methodInfo.getShortId());\n\t}\n\n\tprivate void addClass(ClassNode cls) {\n\t\tArgType clsType = cls.getClassInfo().getType();\n\t\tString rawName = clsType.getObject();\n\t\tClspClass clspClass = new ClspClass(clsType, -1, cls.getAccessFlags().rawValue(), ClspClassSource.APP);\n\t\tclspClass.setParents(ClsSet.makeParentsArray(cls));\n\t\tnameMap.put(rawName, clspClass);\n\t}\n\n\t/**\n\t * @return {@code clsName} instanceof {@code implClsName}\n\t */\n\tpublic boolean isImplements(String clsName, String implClsName) {\n\t\tSet<String> anc = getSuperTypes(clsName);\n\t\treturn anc.contains(implClsName);\n\t}\n\n\tpublic List<String> getImplementations(String clsName) {\n\t\tList<String> list = implementsCache.get(clsName);\n\t\treturn list == null ? Collections.emptyList() : list;\n\t}\n\n\tprivate void fillImplementsCache() {\n\t\tMap<String, List<String>> map = new HashMap<>(nameMap.size());\n\t\tList<String> classes = new ArrayList<>(nameMap.keySet());\n\t\tCollections.sort(classes);\n\t\tfor (String cls : classes) {\n\t\t\tfor (String st : getSuperTypes(cls)) {\n\t\t\t\tmap.computeIfAbsent(st, v -> new ArrayList<>()).add(cls);\n\t\t\t}\n\t\t}\n\t\timplementsCache = map;\n\t}\n\n\tpublic String getCommonAncestor(String clsName, String implClsName) {\n\t\tif (clsName.equals(implClsName)) {\n\t\t\treturn clsName;\n\t\t}\n\t\tClspClass cls = nameMap.get(implClsName);\n\t\tif (cls == null) {\n\t\t\tmissingClasses.add(clsName);\n\t\t\treturn null;\n\t\t}\n\t\tif (isImplements(clsName, implClsName)) {\n\t\t\treturn implClsName;\n\t\t}\n\t\tSet<String> anc = getSuperTypes(clsName);\n\t\treturn searchCommonParent(anc, cls);\n\t}\n\n\tprivate String searchCommonParent(Set<String> anc, ClspClass cls) {\n\t\tfor (ArgType p : cls.getParents()) {\n\t\t\tString name = p.getObject();\n\t\t\tif (anc.contains(name)) {\n\t\t\t\treturn name;\n\t\t\t}\n\t\t\tClspClass nCls = getClspClass(p);\n\t\t\tif (nCls != null) {\n\t\t\t\tString r = searchCommonParent(anc, nCls);\n\t\t\t\tif (r != null) {\n\t\t\t\t\treturn r;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic Set<String> getSuperTypes(String clsName) {\n\t\tSet<String> result = superTypesCache.get(clsName);\n\t\treturn result == null ? Collections.emptySet() : result;\n\t}\n\n\tprivate static final Set<String> OBJECT_SINGLE_SET = Collections.singleton(Consts.CLASS_OBJECT);\n\n\tprivate void fillSuperTypesCache() {\n\t\tMap<String, Set<String>> map = new HashMap<>(nameMap.size());\n\t\tSet<String> tmpSet = new HashSet<>();\n\t\tfor (Map.Entry<String, ClspClass> entry : nameMap.entrySet()) {\n\t\t\tClspClass cls = entry.getValue();\n\t\t\ttmpSet.clear();\n\t\t\taddSuperTypes(cls, tmpSet);\n\t\t\tSet<String> result;\n\t\t\tint size = tmpSet.size();\n\t\t\tswitch (size) {\n\t\t\t\tcase 0: {\n\t\t\t\t\tresult = Collections.emptySet();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 1: {\n\t\t\t\t\tString supCls = tmpSet.iterator().next();\n\t\t\t\t\tif (supCls.equals(Consts.CLASS_OBJECT)) {\n\t\t\t\t\t\tresult = OBJECT_SINGLE_SET;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = Collections.singleton(supCls);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tresult = new HashSet<>(tmpSet);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tmap.put(cls.getName(), result);\n\t\t}\n\t\tsuperTypesCache = map;\n\t}\n\n\tprivate void addSuperTypes(ClspClass cls, Set<String> result) {\n\t\tfor (ArgType parentType : cls.getParents()) {\n\t\t\tif (parentType == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tClspClass parentCls = getClspClass(parentType);\n\t\t\tif (parentCls != null) {\n\t\t\t\tboolean isNew = result.add(parentCls.getName());\n\t\t\t\tif (isNew) {\n\t\t\t\t\taddSuperTypes(parentCls, result);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// parent type is unknown\n\t\t\t\tresult.add(parentType.getObject());\n\t\t\t}\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate ClspClass getClspClass(ArgType clsType) {\n\t\tClspClass clspClass = nameMap.get(clsType.getObject());\n\t\tif (clspClass == null) {\n\t\t\tmissingClasses.add(clsType.getObject());\n\t\t}\n\t\treturn clspClass;\n\t}\n\n\tpublic void printMissingClasses() {\n\t\tint count = missingClasses.size();\n\t\tif (count == 0) {\n\t\t\treturn;\n\t\t}\n\t\tLOG.warn(\"Found {} references to unknown classes\", count);\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tList<String> clsNames = new ArrayList<>(missingClasses);\n\t\t\tCollections.sort(clsNames);\n\t\t\tfor (String cls : clsNames) {\n\t\t\t\tLOG.debug(\"  {}\", cls);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/clsp/ClspMethod.java",
    "content": "package jadx.core.clsp;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.utils.Utils;\n\n/**\n * Method node in classpath graph.\n */\npublic class ClspMethod implements IMethodDetails, Comparable<ClspMethod> {\n\n\tprivate final MethodInfo methodInfo;\n\tprivate final List<ArgType> argTypes;\n\tprivate final ArgType returnType;\n\tprivate final List<ArgType> typeParameters;\n\tprivate final List<ArgType> throwList;\n\tprivate final int accFlags;\n\n\tpublic ClspMethod(MethodInfo methodInfo,\n\t\t\tList<ArgType> argTypes, ArgType returnType,\n\t\t\tList<ArgType> typeParameters, List<ArgType> throwList, int accFlags) {\n\t\tthis.methodInfo = methodInfo;\n\t\tthis.argTypes = argTypes;\n\t\tthis.returnType = returnType;\n\t\tthis.typeParameters = typeParameters;\n\t\tthis.throwList = throwList;\n\t\tthis.accFlags = accFlags;\n\t}\n\n\t@Override\n\tpublic MethodInfo getMethodInfo() {\n\t\treturn methodInfo;\n\t}\n\n\t@Override\n\tpublic ArgType getReturnType() {\n\t\treturn returnType;\n\t}\n\n\t@Override\n\tpublic List<ArgType> getArgTypes() {\n\t\treturn argTypes;\n\t}\n\n\tpublic boolean containsGenericArgs() {\n\t\treturn !Objects.equals(argTypes, methodInfo.getArgumentsTypes());\n\t}\n\n\tpublic int getArgsCount() {\n\t\treturn argTypes.size();\n\t}\n\n\t@Override\n\tpublic List<ArgType> getTypeParameters() {\n\t\treturn typeParameters;\n\t}\n\n\t@Override\n\tpublic List<ArgType> getThrows() {\n\t\treturn throwList;\n\t}\n\n\t@Override\n\tpublic boolean isVarArg() {\n\t\treturn (accFlags & AccessFlags.VARARGS) != 0;\n\t}\n\n\t@Override\n\tpublic int getRawAccessFlags() {\n\t\treturn accFlags;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof ClspMethod)) {\n\t\t\treturn false;\n\t\t}\n\t\tClspMethod other = (ClspMethod) o;\n\t\treturn methodInfo.equals(other.methodInfo);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn methodInfo.hashCode();\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull ClspMethod other) {\n\t\treturn this.methodInfo.compareTo(other.methodInfo);\n\t}\n\n\t@Override\n\tpublic String toAttrString() {\n\t\treturn IMethodDetails.super.toAttrString() + \" (c)\";\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"ClspMth{\");\n\t\tif (Utils.notEmpty(getTypeParameters())) {\n\t\t\tsb.append('<');\n\t\t\tsb.append(Utils.listToString(getTypeParameters()));\n\t\t\tsb.append(\"> \");\n\t\t}\n\t\tsb.append(getMethodInfo().getFullName());\n\t\tsb.append('(');\n\t\tsb.append(Utils.listToString(getArgTypes()));\n\t\tsb.append(\"):\");\n\t\tsb.append(getReturnType());\n\t\tif (isVarArg()) {\n\t\t\tsb.append(\" VARARG\");\n\t\t}\n\t\tList<ArgType> throwsList = getThrows();\n\t\tif (Utils.notEmpty(throwsList)) {\n\t\t\tsb.append(\" throws \").append(Utils.listToString(throwsList));\n\t\t}\n\t\tsb.append('}');\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/clsp/SimpleMethodDetails.java",
    "content": "package jadx.core.clsp;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.IMethodDetails;\n\n/**\n * Method details build from MethodInfo.\n * Note: some fields have unknown values.\n */\npublic class SimpleMethodDetails implements IMethodDetails {\n\n\tprivate final MethodInfo methodInfo;\n\n\tpublic SimpleMethodDetails(MethodInfo methodInfo) {\n\t\tthis.methodInfo = methodInfo;\n\t}\n\n\t@Override\n\tpublic MethodInfo getMethodInfo() {\n\t\treturn methodInfo;\n\t}\n\n\t@Override\n\tpublic ArgType getReturnType() {\n\t\treturn methodInfo.getReturnType();\n\t}\n\n\t@Override\n\tpublic List<ArgType> getArgTypes() {\n\t\treturn methodInfo.getArgumentsTypes();\n\t}\n\n\t@Override\n\tpublic List<ArgType> getTypeParameters() {\n\t\treturn Collections.emptyList();\n\t}\n\n\t@Override\n\tpublic List<ArgType> getThrows() {\n\t\treturn Collections.emptyList();\n\t}\n\n\t@Override\n\tpublic boolean isVarArg() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int getRawAccessFlags() {\n\t\treturn AccessFlags.PUBLIC;\n\t}\n\n\t@Override\n\tpublic String toAttrString() {\n\t\treturn IMethodDetails.super.toAttrString() + \" (s)\";\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"SimpleMethodDetails{\" + methodInfo + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/AnnotationGen.java",
    "content": "package jadx.core.codegen;\n\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ICodeWriter;\nimport jadx.api.plugins.input.data.IFieldRef;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationDefaultAttr;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationMethodParamsAttr;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationsAttr;\nimport jadx.core.Consts;\nimport jadx.core.dex.attributes.IAttributeNode;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class AnnotationGen {\n\n\tprivate final ClassNode cls;\n\tprivate final ClassGen classGen;\n\n\tpublic AnnotationGen(ClassNode cls, ClassGen classGen) {\n\t\tthis.cls = cls;\n\t\tthis.classGen = classGen;\n\t}\n\n\tpublic void addForClass(ICodeWriter code) {\n\t\tadd(cls, code);\n\t}\n\n\tpublic void addForMethod(ICodeWriter code, MethodNode mth) {\n\t\tadd(mth, code);\n\t}\n\n\tpublic void addForField(ICodeWriter code, FieldNode field) {\n\t\tadd(field, code);\n\t}\n\n\tpublic void addForParameter(ICodeWriter code, AnnotationMethodParamsAttr paramsAnnotations, int n) {\n\t\tList<AnnotationsAttr> paramList = paramsAnnotations.getParamList();\n\t\tif (n >= paramList.size()) {\n\t\t\treturn;\n\t\t}\n\t\tAnnotationsAttr aList = paramList.get(n);\n\t\tif (aList == null || aList.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (IAnnotation a : aList.getAll()) {\n\t\t\tformatAnnotation(code, a);\n\t\t\tcode.add(' ');\n\t\t}\n\t}\n\n\tprivate void add(IAttributeNode node, ICodeWriter code) {\n\t\tAnnotationsAttr aList = node.get(JadxAttrType.ANNOTATION_LIST);\n\t\tif (aList == null || aList.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (IAnnotation a : aList.getAll()) {\n\t\t\tString aCls = a.getAnnotationClass();\n\t\t\tif (!aCls.equals(Consts.OVERRIDE_ANNOTATION)) {\n\t\t\t\tcode.startLine();\n\t\t\t\tformatAnnotation(code, a);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void formatAnnotation(ICodeWriter code, IAnnotation a) {\n\t\tcode.add('@');\n\t\tClassNode annCls = cls.root().resolveClass(a.getAnnotationClass());\n\t\tif (annCls != null) {\n\t\t\tclassGen.useClass(code, annCls);\n\t\t} else {\n\t\t\tclassGen.useClass(code, a.getAnnotationClass());\n\t\t}\n\n\t\tMap<String, EncodedValue> vl = a.getValues();\n\t\tif (!vl.isEmpty()) {\n\t\t\tcode.add('(');\n\t\t\tfor (Iterator<Entry<String, EncodedValue>> it = vl.entrySet().iterator(); it.hasNext();) {\n\t\t\t\tEntry<String, EncodedValue> e = it.next();\n\t\t\t\tString paramName = getParamName(annCls, e.getKey());\n\t\t\t\tif (paramName.equals(\"value\") && vl.size() == 1) {\n\t\t\t\t\t// don't add \"value = \" if no other parameters\n\t\t\t\t} else {\n\t\t\t\t\tcode.add(paramName);\n\t\t\t\t\tcode.add(\" = \");\n\t\t\t\t}\n\t\t\t\tencodeValue(cls.root(), code, e.getValue());\n\t\t\t\tif (it.hasNext()) {\n\t\t\t\t\tcode.add(\", \");\n\t\t\t\t}\n\t\t\t}\n\t\t\tcode.add(')');\n\t\t}\n\t}\n\n\tprivate String getParamName(@Nullable ClassNode annCls, String paramName) {\n\t\tif (annCls != null) {\n\t\t\t// TODO: save value type and search using signature\n\t\t\tMethodNode mth = annCls.searchMethodByShortName(paramName);\n\t\t\tif (mth != null) {\n\t\t\t\treturn mth.getAlias();\n\t\t\t}\n\t\t}\n\t\treturn paramName;\n\t}\n\n\tpublic void addThrows(MethodNode mth, ICodeWriter code) {\n\t\tList<ArgType> throwList = mth.getThrows();\n\t\tif (!throwList.isEmpty()) {\n\t\t\tcode.add(\" throws \");\n\t\t\tfor (Iterator<ArgType> it = throwList.iterator(); it.hasNext();) {\n\t\t\t\tArgType ex = it.next();\n\t\t\t\tclassGen.useType(code, ex);\n\t\t\t\tif (it.hasNext()) {\n\t\t\t\t\tcode.add(\", \");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic EncodedValue getAnnotationDefaultValue(MethodNode mth) {\n\t\tAnnotationDefaultAttr defaultAttr = mth.get(JadxAttrType.ANNOTATION_DEFAULT);\n\t\tif (defaultAttr == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn defaultAttr.getValue();\n\t}\n\n\t// TODO: refactor this boilerplate code\n\tpublic void encodeValue(RootNode root, ICodeWriter code, EncodedValue encodedValue) {\n\t\tif (encodedValue == null) {\n\t\t\tcode.add(\"null\");\n\t\t\treturn;\n\t\t}\n\n\t\tStringUtils stringUtils = getStringUtils();\n\t\tObject value = encodedValue.getValue();\n\t\tswitch (encodedValue.getType()) {\n\t\t\tcase ENCODED_NULL:\n\t\t\t\tcode.add(\"null\");\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_BOOLEAN:\n\t\t\t\tcode.add(Boolean.TRUE.equals(value) ? \"true\" : \"false\");\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_BYTE:\n\t\t\t\tcode.add(stringUtils.formatByte((Byte) value, false));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_SHORT:\n\t\t\t\tcode.add(stringUtils.formatShort((Short) value, false));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_CHAR:\n\t\t\t\tcode.add(stringUtils.unescapeChar((Character) value));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_INT:\n\t\t\t\tcode.add(stringUtils.formatInteger((Integer) value, false));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_LONG:\n\t\t\t\tcode.add(stringUtils.formatLong((Long) value, false));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_FLOAT:\n\t\t\t\tcode.add(StringUtils.formatFloat((Float) value));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_DOUBLE:\n\t\t\t\tcode.add(StringUtils.formatDouble((Double) value));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_STRING:\n\t\t\t\tcode.add(stringUtils.unescapeString((String) value));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_TYPE:\n\t\t\t\tclassGen.useType(code, ArgType.parse((String) value));\n\t\t\t\tcode.add(\".class\");\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_ENUM:\n\t\t\tcase ENCODED_FIELD:\n\t\t\t\t// must be a static field\n\t\t\t\tif (value instanceof IFieldRef) {\n\t\t\t\t\tFieldInfo fieldInfo = FieldInfo.fromRef(root, (IFieldRef) value);\n\t\t\t\t\tInsnGen.makeStaticFieldAccess(code, fieldInfo, classGen);\n\t\t\t\t} else if (value instanceof FieldInfo) {\n\t\t\t\t\tInsnGen.makeStaticFieldAccess(code, (FieldInfo) value, classGen);\n\t\t\t\t} else {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Unexpected field type class: \" + value.getClass());\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_METHOD:\n\t\t\t\t// TODO\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_ARRAY:\n\t\t\t\tcode.add('{');\n\t\t\t\tIterator<?> it = ((Iterable<?>) value).iterator();\n\t\t\t\twhile (it.hasNext()) {\n\t\t\t\t\tEncodedValue v = (EncodedValue) it.next();\n\t\t\t\t\tencodeValue(cls.root(), code, v);\n\t\t\t\t\tif (it.hasNext()) {\n\t\t\t\t\t\tcode.add(\", \");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcode.add('}');\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_ANNOTATION:\n\t\t\t\tformatAnnotation(code, (IAnnotation) value);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Can't decode value: \" + encodedValue.getType() + \" (\" + encodedValue + ')');\n\t\t}\n\t}\n\n\tprivate StringUtils getStringUtils() {\n\t\treturn cls.root().getStringUtils();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/ClassGen.java",
    "content": "package jadx.core.codegen;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.CommentsLevel;\nimport jadx.api.ICodeInfo;\nimport jadx.api.ICodeWriter;\nimport jadx.api.JadxArgs;\nimport jadx.api.args.IntegerFormat;\nimport jadx.api.metadata.annotations.NodeEnd;\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.api.plugins.input.data.annotations.EncodedType;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.core.Consts;\nimport jadx.core.codegen.utils.CodeGenUtils;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.FieldInitInsnAttr;\nimport jadx.core.dex.attributes.nodes.EnumClassAttr;\nimport jadx.core.dex.attributes.nodes.EnumClassAttr.EnumField;\nimport jadx.core.dex.attributes.nodes.LineAttrNode;\nimport jadx.core.dex.attributes.nodes.MethodInlineAttr;\nimport jadx.core.dex.attributes.nodes.NotificationAttrNode;\nimport jadx.core.dex.attributes.nodes.SkipMethodArgsAttr;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.PrimitiveType;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.EncodedValueUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.android.AndroidResourcesUtils;\nimport jadx.core.utils.exceptions.CodegenException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class ClassGen {\n\n\tprivate final ClassNode cls;\n\tprivate final ClassGen parentGen;\n\tprivate final AnnotationGen annotationGen;\n\tprivate final boolean fallback;\n\tprivate final boolean useImports;\n\tprivate final boolean showInconsistentCode;\n\tprivate final IntegerFormat integerFormat;\n\n\tprivate final Set<ClassInfo> imports = new HashSet<>();\n\tprivate int clsDeclOffset;\n\n\tprivate boolean bodyGenStarted;\n\n\t@Nullable\n\tprivate NameGen outerNameGen;\n\n\tpublic ClassGen(ClassNode cls, JadxArgs jadxArgs) {\n\t\tthis(cls, null, jadxArgs.isUseImports(), jadxArgs.isFallbackMode(), jadxArgs.isShowInconsistentCode(), jadxArgs.getIntegerFormat());\n\t}\n\n\tpublic ClassGen(ClassNode cls, ClassGen parentClsGen) {\n\t\tthis(cls, parentClsGen, parentClsGen.useImports, parentClsGen.fallback, parentClsGen.showInconsistentCode,\n\t\t\t\tparentClsGen.integerFormat);\n\t}\n\n\tpublic ClassGen(ClassNode cls, ClassGen parentClsGen, boolean useImports, boolean fallback, boolean showBadCode,\n\t\t\tIntegerFormat integerFormat) {\n\t\tthis.cls = cls;\n\t\tthis.parentGen = parentClsGen;\n\t\tthis.fallback = fallback;\n\t\tthis.useImports = useImports;\n\t\tthis.showInconsistentCode = showBadCode;\n\t\tthis.integerFormat = integerFormat;\n\n\t\tthis.annotationGen = new AnnotationGen(cls, this);\n\t}\n\n\tpublic ClassNode getClassNode() {\n\t\treturn cls;\n\t}\n\n\tpublic ICodeInfo makeClass() throws CodegenException {\n\t\tif (cls.contains(AFlag.PACKAGE_INFO)) {\n\t\t\treturn makePackageInfo();\n\t\t}\n\t\tICodeWriter clsBody = cls.root().makeCodeWriter();\n\t\taddClassCode(clsBody);\n\n\t\tICodeWriter clsCode = cls.root().makeCodeWriter();\n\t\taddPackage(clsCode);\n\t\tclsCode.newLine();\n\t\taddImports(clsCode);\n\t\tclsCode.add(clsBody);\n\t\treturn clsCode.finish();\n\t}\n\n\tprivate void addPackage(ICodeWriter clsCode) {\n\t\tif (cls.getPackage().isEmpty()) {\n\t\t\tclsCode.add(\"// default package\");\n\t\t} else {\n\t\t\tclsCode.add(\"package \").add(cls.getPackage()).add(';');\n\t\t}\n\t}\n\n\tprivate void addImports(ICodeWriter clsCode) {\n\t\tint importsCount = imports.size();\n\t\tif (importsCount != 0) {\n\t\t\tList<ClassInfo> sortedImports = new ArrayList<>(imports);\n\t\t\tsortedImports.sort(Comparator.comparing(ClassInfo::getAliasFullName));\n\t\t\tsortedImports.forEach(classInfo -> {\n\t\t\t\tclsCode.startLine(\"import \");\n\t\t\t\tClassNode classNode = cls.root().resolveClass(classInfo);\n\t\t\t\tif (classNode != null) {\n\t\t\t\t\tclsCode.attachAnnotation(classNode);\n\t\t\t\t}\n\t\t\t\tclsCode.add(classInfo.getAliasFullName());\n\t\t\t\tclsCode.add(';');\n\t\t\t});\n\t\t\tclsCode.newLine();\n\t\t\timports.clear();\n\t\t}\n\t}\n\n\tprivate ICodeInfo makePackageInfo() {\n\t\tICodeWriter code = cls.root().makeCodeWriter();\n\t\tannotationGen.addForClass(code);\n\t\tcode.newLine();\n\t\tcode.attachDefinition(cls);\n\t\taddPackage(code);\n\t\tcode.newLine();\n\t\taddImports(code);\n\t\treturn code.finish();\n\t}\n\n\tpublic void addClassCode(ICodeWriter code) throws CodegenException {\n\t\tif (cls.contains(AFlag.DONT_GENERATE)) {\n\t\t\treturn;\n\t\t}\n\t\tif (Consts.DEBUG_USAGE) {\n\t\t\taddClassUsageInfo(code, cls);\n\t\t}\n\t\taddClassDeclaration(code);\n\t\taddClassBody(code);\n\t}\n\n\tpublic void addClassDeclaration(ICodeWriter clsCode) {\n\t\tAccessInfo af = cls.getAccessFlags();\n\t\tif (af.isInterface()) {\n\t\t\taf = af.remove(AccessFlags.ABSTRACT)\n\t\t\t\t\t.remove(AccessFlags.STATIC);\n\t\t}\n\t\t// 'static' and 'private' modifier not allowed for top classes (not inner)\n\t\tif (!cls.getClassInfo().isInner()) {\n\t\t\taf = af.remove(AccessFlags.STATIC).remove(AccessFlags.PRIVATE);\n\t\t}\n\n\t\tCodeGenUtils.addComments(clsCode, cls);\n\t\tCodeGenUtils.addClassRenamedComment(clsCode, cls);\n\t\tCodeGenUtils.addErrors(clsCode, cls);\n\t\tCodeGenUtils.addSourceFileInfo(clsCode, cls);\n\t\tCodeGenUtils.addInputFileInfo(clsCode, cls);\n\n\t\tannotationGen.addForClass(clsCode);\n\t\tclsCode.startLineWithNum(cls.getSourceLine()).add(af.makeString(cls.checkCommentsLevel(CommentsLevel.INFO)));\n\t\tif (af.isInterface()) {\n\t\t\tif (af.isAnnotation()) {\n\t\t\t\tclsCode.add('@');\n\t\t\t}\n\t\t\tclsCode.add(\"interface \");\n\t\t} else if (af.isEnum()) {\n\t\t\tclsCode.add(\"enum \");\n\t\t} else {\n\t\t\tclsCode.add(\"class \");\n\t\t}\n\t\tclsCode.attachDefinition(cls);\n\t\tclsCode.add(cls.getClassInfo().getAliasShortName());\n\n\t\taddGenericTypeParameters(clsCode, cls.getGenericTypeParameters(), true);\n\t\tclsCode.add(' ');\n\n\t\tArgType sup = cls.getSuperClass();\n\t\tif (sup != null\n\t\t\t\t&& !sup.equals(ArgType.OBJECT)\n\t\t\t\t&& !cls.contains(AFlag.REMOVE_SUPER_CLASS)) {\n\t\t\tclsCode.add(\"extends \");\n\t\t\tuseClass(clsCode, sup);\n\t\t\tclsCode.add(' ');\n\t\t}\n\n\t\tif (!cls.getInterfaces().isEmpty() && !af.isAnnotation()) {\n\t\t\tif (cls.getAccessFlags().isInterface()) {\n\t\t\t\tclsCode.add(\"extends \");\n\t\t\t} else {\n\t\t\t\tclsCode.add(\"implements \");\n\t\t\t}\n\t\t\tfor (Iterator<ArgType> it = cls.getInterfaces().iterator(); it.hasNext();) {\n\t\t\t\tArgType interf = it.next();\n\t\t\t\tuseClass(clsCode, interf);\n\t\t\t\tif (it.hasNext()) {\n\t\t\t\t\tclsCode.add(\", \");\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!cls.getInterfaces().isEmpty()) {\n\t\t\t\tclsCode.add(' ');\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic boolean addGenericTypeParameters(ICodeWriter code, List<ArgType> generics, boolean classDeclaration) {\n\t\tif (generics == null || generics.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tcode.add('<');\n\t\tint i = 0;\n\t\tfor (ArgType genericInfo : generics) {\n\t\t\tif (i != 0) {\n\t\t\t\tcode.add(\", \");\n\t\t\t}\n\t\t\tif (genericInfo.isGenericType()) {\n\t\t\t\tcode.add(genericInfo.getObject());\n\t\t\t} else {\n\t\t\t\tuseClass(code, genericInfo);\n\t\t\t}\n\t\t\tList<ArgType> list = genericInfo.getExtendTypes();\n\t\t\tif (list != null && !list.isEmpty()) {\n\t\t\t\tcode.add(\" extends \");\n\t\t\t\tfor (Iterator<ArgType> it = list.iterator(); it.hasNext();) {\n\t\t\t\t\tArgType g = it.next();\n\t\t\t\t\tif (g.isGenericType()) {\n\t\t\t\t\t\tcode.add(g.getObject());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tuseClass(code, g);\n\t\t\t\t\t\tif (classDeclaration\n\t\t\t\t\t\t\t\t&& !cls.getClassInfo().isInner()\n\t\t\t\t\t\t\t\t&& cls.root().getArgs().isUseImports()) {\n\t\t\t\t\t\t\taddImport(ClassInfo.fromType(cls.root(), g));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (it.hasNext()) {\n\t\t\t\t\t\tcode.add(\" & \");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\ti++;\n\t\t}\n\t\tcode.add('>');\n\t\treturn true;\n\t}\n\n\tpublic void addClassBody(ICodeWriter clsCode) throws CodegenException {\n\t\taddClassBody(clsCode, false);\n\t}\n\n\t/**\n\t * @param printClassName allows to print the original class name as comment (e.g. for inlined\n\t *                       classes)\n\t */\n\tpublic void addClassBody(ICodeWriter clsCode, boolean printClassName) throws CodegenException {\n\t\tclsCode.add('{');\n\t\tif (printClassName && cls.checkCommentsLevel(CommentsLevel.INFO)) {\n\t\t\tclsCode.add(\" // from class: \" + cls.getClassInfo().getFullName());\n\t\t}\n\t\tsetBodyGenStarted(true);\n\t\tclsDeclOffset = clsCode.getLength();\n\t\tclsCode.incIndent();\n\t\taddFields(clsCode);\n\t\taddInnerClsAndMethods(clsCode);\n\t\tclsCode.decIndent();\n\t\tclsCode.startLine('}');\n\t\tclsCode.attachAnnotation(NodeEnd.VALUE);\n\t}\n\n\tprivate void addInnerClsAndMethods(ICodeWriter clsCode) {\n\t\tStream.of(cls.getInnerClasses(), cls.getMethods())\n\t\t\t\t.flatMap(Collection::stream)\n\t\t\t\t.filter(node -> !skipNode(node))\n\t\t\t\t.sorted(Comparator.comparingInt(LineAttrNode::getSourceLine))\n\t\t\t\t.forEach(node -> {\n\t\t\t\t\tif (node instanceof ClassNode) {\n\t\t\t\t\t\taddInnerClass(clsCode, (ClassNode) node);\n\t\t\t\t\t} else {\n\t\t\t\t\t\taddMethod(clsCode, (MethodNode) node);\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\tprivate boolean skipNode(NotificationAttrNode node) {\n\t\tif (fallback) {\n\t\t\treturn false;\n\t\t}\n\t\tif (Consts.DEBUG_ATTRIBUTES) {\n\t\t\tif (node.contains(AType.JADX_COMMENTS)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn node.contains(AFlag.DONT_GENERATE);\n\t}\n\n\tprivate void addInnerClass(ICodeWriter code, ClassNode innerCls) {\n\t\ttry {\n\t\t\tClassGen inClGen = new ClassGen(innerCls, getParentGen());\n\t\t\tcode.newLine();\n\t\t\tinClGen.addClassCode(code);\n\t\t\timports.addAll(inClGen.getImports());\n\t\t} catch (Exception e) {\n\t\t\tinnerCls.addError(\"Inner class code generation error\", e);\n\t\t}\n\t}\n\n\tprivate boolean isInnerClassesPresents() {\n\t\tfor (ClassNode innerCls : cls.getInnerClasses()) {\n\t\t\tif (!innerCls.contains(AType.ANONYMOUS_CLASS)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate void addMethod(ICodeWriter code, MethodNode mth) {\n\t\tif (skipMethod(mth)) {\n\t\t\treturn;\n\t\t}\n\t\tif (code.getLength() != clsDeclOffset) {\n\t\t\tcode.newLine();\n\t\t}\n\t\tint savedIndent = code.getIndent();\n\t\ttry {\n\t\t\taddMethodCode(code, mth);\n\t\t} catch (Exception e) {\n\t\t\tif (mth.getParentClass().getTopParentClass().contains(AFlag.RESTART_CODEGEN)) {\n\t\t\t\tthrow new JadxRuntimeException(\"Method generation error\", e);\n\t\t\t}\n\t\t\tmth.addError(\"Method generation error\", e);\n\t\t\tCodeGenUtils.addErrors(code, mth);\n\t\t\tcode.setIndent(savedIndent);\n\t\t}\n\t}\n\n\t/**\n\t * Additional checks for inlined methods\n\t */\n\tprivate boolean skipMethod(MethodNode mth) {\n\t\tif (cls.root().getArgs().getDecompilationMode().isSpecial()) {\n\t\t\t// show all methods for special decompilation modes\n\t\t\treturn false;\n\t\t}\n\t\tMethodInlineAttr inlineAttr = mth.get(AType.METHOD_INLINE);\n\t\tif (inlineAttr == null || inlineAttr.notNeeded()) {\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tif (mth.getUseIn().isEmpty()) {\n\t\t\t\tmth.add(AFlag.DONT_GENERATE);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tList<MethodNode> useInCompleted = mth.getUseIn().stream()\n\t\t\t\t\t.filter(m -> m.getTopParentClass().getState().isProcessComplete())\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t\tif (useInCompleted.isEmpty()) {\n\t\t\t\tmth.add(AFlag.DONT_GENERATE);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tmth.addDebugComment(\"Method not inlined, still used in: \" + useInCompleted);\n\t\t\treturn false;\n\t\t} catch (Exception e) {\n\t\t\t// check failed => keep method\n\t\t\tmth.addWarnComment(\"Failed to check method usage\", e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate boolean isMethodsPresents() {\n\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\tif (!mth.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic void addMethodCode(ICodeWriter code, MethodNode mth) throws CodegenException {\n\t\tCodeGenUtils.addErrorsAndComments(code, mth);\n\t\tif (mth.isNoCode()) {\n\t\t\tMethodGen mthGen = new MethodGen(this, mth);\n\t\t\tmthGen.addDefinition(code);\n\t\t\tcode.add(';');\n\t\t} else {\n\t\t\tboolean badCode = mth.contains(AFlag.INCONSISTENT_CODE);\n\t\t\tif (badCode && showInconsistentCode) {\n\t\t\t\tbadCode = false;\n\t\t\t}\n\t\t\tMethodGen mthGen;\n\t\t\tif (badCode || fallback || mth.contains(AType.JADX_ERROR)) {\n\t\t\t\tmthGen = MethodGen.getFallbackMethodGen(mth);\n\t\t\t} else {\n\t\t\t\tmthGen = new MethodGen(this, mth);\n\t\t\t}\n\t\t\tif (mthGen.addDefinition(code)) {\n\t\t\t\tcode.add(' ');\n\t\t\t}\n\t\t\tcode.add('{');\n\t\t\tcode.incIndent();\n\t\t\tmthGen.addInstructions(code);\n\t\t\tcode.decIndent();\n\t\t\tcode.startLine('}');\n\t\t\tcode.attachAnnotation(NodeEnd.VALUE);\n\t\t}\n\t}\n\n\tprivate void addFields(ICodeWriter code) throws CodegenException {\n\t\taddEnumFields(code);\n\t\tfor (FieldNode f : cls.getFields()) {\n\t\t\taddField(code, f);\n\t\t}\n\t}\n\n\tpublic void addField(ICodeWriter code, FieldNode f) {\n\t\tif (f.contains(AFlag.DONT_GENERATE)) {\n\t\t\treturn;\n\t\t}\n\t\tif (f.contains(JadxAttrType.ANNOTATION_LIST)\n\t\t\t\t|| f.contains(AType.JADX_COMMENTS)\n\t\t\t\t|| f.contains(AType.CODE_COMMENTS)\n\t\t\t\t|| f.getFieldInfo().hasAlias()) {\n\t\t\tcode.newLine();\n\t\t}\n\t\tif (Consts.DEBUG_USAGE) {\n\t\t\taddFieldUsageInfo(code, f);\n\t\t}\n\t\tCodeGenUtils.addComments(code, f);\n\t\tif (f.getFieldInfo().hasAlias()) {\n\t\t\tCodeGenUtils.addRenamedComment(code, f, f.getName());\n\t\t}\n\t\tannotationGen.addForField(code, f);\n\n\t\tcode.startLine(f.getAccessFlags().makeString(f.checkCommentsLevel(CommentsLevel.INFO)));\n\t\tuseType(code, f.getType());\n\t\tcode.add(' ');\n\t\tcode.attachDefinition(f);\n\t\tcode.add(f.getAlias());\n\n\t\tFieldInitInsnAttr initInsnAttr = f.get(AType.FIELD_INIT_INSN);\n\t\tif (initInsnAttr != null) {\n\t\t\tInsnGen insnGen = makeInsnGen(initInsnAttr.getInsnMth());\n\t\t\tcode.add(\" = \");\n\t\t\taddInsnBody(insnGen, code, initInsnAttr.getInsn());\n\t\t} else {\n\t\t\tEncodedValue constVal = f.get(JadxAttrType.CONSTANT_VALUE);\n\t\t\tif (constVal != null) {\n\t\t\t\tcode.add(\" = \");\n\t\t\t\tif (constVal.getType() == EncodedType.ENCODED_NULL) {\n\t\t\t\t\tcode.add(TypeGen.literalToString(0, f.getType(), cls, fallback));\n\t\t\t\t} else {\n\t\t\t\t\tObject val = EncodedValueUtils.convertToConstValue(constVal);\n\t\t\t\t\tif (val instanceof LiteralArg) {\n\t\t\t\t\t\tlong lit = ((LiteralArg) val).getLiteral();\n\t\t\t\t\t\tcode.add(getIntegerString(lit, f.getType()));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tannotationGen.encodeValue(cls.root(), code, constVal);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcode.add(';');\n\t}\n\n\tprivate String getIntegerString(long lit, ArgType type) {\n\t\tif (integerFormat != IntegerFormat.DECIMAL && AndroidResourcesUtils.isResourceFieldValue(cls, type)) {\n\t\t\treturn String.format(\"0x%08x\", lit);\n\t\t}\n\t\t// force literal type to be same as field (java bytecode can use different type)\n\t\treturn TypeGen.literalToString(lit, type, cls, fallback);\n\t}\n\n\tprivate boolean isFieldsPresents() {\n\t\tfor (FieldNode field : cls.getFields()) {\n\t\t\tif (!field.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate void addEnumFields(ICodeWriter code) throws CodegenException {\n\t\tEnumClassAttr enumFields = cls.get(AType.ENUM_CLASS);\n\t\tif (enumFields == null) {\n\t\t\treturn;\n\t\t}\n\t\tInsnGen igen = null;\n\t\tfor (Iterator<EnumField> it = enumFields.getFields().iterator(); it.hasNext();) {\n\t\t\tEnumField f = it.next();\n\n\t\t\tCodeGenUtils.addComments(code, f.getField());\n\t\t\tcode.startLine(f.getField().getAlias());\n\t\t\tConstructorInsn constrInsn = f.getConstrInsn();\n\t\t\tMethodNode callMth = cls.root().resolveMethod(constrInsn.getCallMth());\n\t\t\tint skipCount = getEnumCtrSkipArgsCount(callMth);\n\t\t\tif (constrInsn.getArgsCount() > skipCount) {\n\t\t\t\tif (igen == null) {\n\t\t\t\t\tigen = makeInsnGen(enumFields.getStaticMethod());\n\t\t\t\t}\n\t\t\t\tigen.generateMethodArguments(code, constrInsn, 0, callMth);\n\t\t\t}\n\t\t\tif (f.getCls() != null) {\n\t\t\t\tcode.add(' ');\n\t\t\t\tnew ClassGen(f.getCls(), this).addClassBody(code, true);\n\t\t\t}\n\t\t\tif (it.hasNext()) {\n\t\t\t\tcode.add(',');\n\t\t\t}\n\t\t}\n\t\tif (isMethodsPresents() || isFieldsPresents() || isInnerClassesPresents()) {\n\t\t\tif (enumFields.getFields().isEmpty()) {\n\t\t\t\tcode.startLine();\n\t\t\t}\n\t\t\tcode.add(';');\n\t\t\tif (isFieldsPresents()) {\n\t\t\t\tcode.newLine();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate int getEnumCtrSkipArgsCount(@Nullable MethodNode callMth) {\n\t\tif (callMth != null) {\n\t\t\tSkipMethodArgsAttr skipArgsAttr = callMth.get(AType.SKIP_MTH_ARGS);\n\t\t\tif (skipArgsAttr != null) {\n\t\t\t\treturn skipArgsAttr.getSkipCount();\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\tprivate InsnGen makeInsnGen(MethodNode mth) {\n\t\tMethodGen mthGen = new MethodGen(this, mth);\n\t\treturn new InsnGen(mthGen, false);\n\t}\n\n\tprivate void addInsnBody(InsnGen insnGen, ICodeWriter code, InsnNode insn) {\n\t\ttry {\n\t\t\tinsnGen.makeInsn(insn, code, InsnGen.Flags.BODY_ONLY_NOWRAP);\n\t\t} catch (Exception e) {\n\t\t\tcls.addError(\"Failed to generate init code\", e);\n\t\t}\n\t}\n\n\tpublic void useType(ICodeWriter code, ArgType type) {\n\t\tPrimitiveType stype = type.getPrimitiveType();\n\t\tif (stype == null) {\n\t\t\tcode.add(type.toString());\n\t\t} else if (stype == PrimitiveType.OBJECT) {\n\t\t\tif (type.isGenericType()) {\n\t\t\t\tcode.add(type.getObject());\n\t\t\t} else {\n\t\t\t\tuseClass(code, type);\n\t\t\t}\n\t\t} else if (stype == PrimitiveType.ARRAY) {\n\t\t\tuseType(code, type.getArrayElement());\n\t\t\tcode.add(\"[]\");\n\t\t} else {\n\t\t\tcode.add(stype.getLongName());\n\t\t}\n\t}\n\n\tpublic void useClass(ICodeWriter code, String rawCls) {\n\t\tuseClass(code, ArgType.object(rawCls));\n\t}\n\n\tpublic void useClass(ICodeWriter code, ArgType type) {\n\t\tArgType outerType = type.getOuterType();\n\t\tif (outerType != null) {\n\t\t\tuseClass(code, outerType);\n\t\t\tcode.add('.');\n\t\t\taddInnerType(code, type);\n\t\t\treturn;\n\t\t}\n\t\tuseClass(code, ClassInfo.fromType(cls.root(), type));\n\t\taddGenerics(code, type);\n\t}\n\n\tprivate void addInnerType(ICodeWriter code, ArgType baseType) {\n\t\tArgType innerType = baseType.getInnerType();\n\t\tArgType outerType = innerType.getOuterType();\n\t\tif (outerType != null) {\n\t\t\tuseClassWithShortName(code, baseType, outerType);\n\t\t\tcode.add('.');\n\t\t\taddInnerType(code, innerType);\n\t\t\treturn;\n\t\t}\n\t\tuseClassWithShortName(code, baseType, innerType);\n\t}\n\n\tprivate void useClassWithShortName(ICodeWriter code, ArgType baseType, ArgType type) {\n\t\tString fullNameObj;\n\t\tif (type.getObject().contains(\".\")) {\n\t\t\tfullNameObj = type.getObject();\n\t\t} else {\n\t\t\tfullNameObj = baseType.getObject();\n\t\t}\n\t\tClassInfo classInfo = ClassInfo.fromName(cls.root(), fullNameObj);\n\t\tClassNode classNode = cls.root().resolveClass(classInfo);\n\t\tif (classNode != null) {\n\t\t\tcode.attachAnnotation(classNode);\n\t\t}\n\t\tcode.add(classInfo.getAliasShortName());\n\t\taddGenerics(code, type);\n\t}\n\n\tprivate void addGenerics(ICodeWriter code, ArgType type) {\n\t\tList<ArgType> generics = type.getGenericTypes();\n\t\tif (generics != null) {\n\t\t\tcode.add('<');\n\t\t\tint len = generics.size();\n\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\tif (i != 0) {\n\t\t\t\t\tcode.add(\", \");\n\t\t\t\t}\n\t\t\t\tArgType gt = generics.get(i);\n\t\t\t\tArgType wt = gt.getWildcardType();\n\t\t\t\tif (wt != null) {\n\t\t\t\t\tArgType.WildcardBound bound = gt.getWildcardBound();\n\t\t\t\t\tcode.add(bound.getStr());\n\t\t\t\t\tif (bound != ArgType.WildcardBound.UNBOUND) {\n\t\t\t\t\t\tuseType(code, wt);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tuseType(code, gt);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcode.add('>');\n\t\t}\n\t}\n\n\tpublic void useClass(ICodeWriter code, ClassInfo classInfo) {\n\t\tClassNode classNode = cls.root().resolveClass(classInfo);\n\t\tif (classNode != null) {\n\t\t\tuseClass(code, classNode);\n\t\t} else {\n\t\t\taddClsName(code, classInfo);\n\t\t}\n\t}\n\n\tpublic void useClass(ICodeWriter code, ClassNode classNode) {\n\t\tcode.attachAnnotation(classNode);\n\t\taddClsName(code, classNode.getClassInfo());\n\t}\n\n\tpublic void addClsName(ICodeWriter code, ClassInfo classInfo) {\n\t\tString clsName = useClassInternal(cls.getClassInfo(), classInfo);\n\t\tcode.add(clsName);\n\t}\n\n\tpublic void addClsShortNameForced(ICodeWriter code, ClassInfo classInfo) {\n\t\tcode.add(classInfo.getAliasShortName());\n\t\tif (!isBothClassesInOneTopClass(cls.getClassInfo(), classInfo)) {\n\t\t\taddImport(classInfo);\n\t\t}\n\t}\n\n\tprivate String useClassInternal(ClassInfo useCls, ClassInfo extClsInfo) {\n\t\tString fullName = extClsInfo.getAliasFullName();\n\t\tif (fallback || !useImports) {\n\t\t\treturn fullName;\n\t\t}\n\t\tString shortName = extClsInfo.getAliasShortName();\n\t\tif (useCls.equals(extClsInfo)) {\n\t\t\treturn shortName;\n\t\t}\n\t\tif (extClsInfo.getAliasPkg().isEmpty()) {\n\t\t\t// omit import for default package\n\t\t\treturn shortName;\n\t\t}\n\t\tif (isClassInnerFor(useCls, extClsInfo)) {\n\t\t\treturn shortName;\n\t\t}\n\t\tif (extClsInfo.isInner()) {\n\t\t\treturn expandInnerClassName(useCls, extClsInfo);\n\t\t}\n\t\tif (checkInnerCollision(cls.root(), useCls, extClsInfo)\n\t\t\t\t|| checkInPackageCollision(cls.root(), useCls, extClsInfo)) {\n\t\t\treturn fullName;\n\t\t}\n\t\tif (isBothClassesInOneTopClass(useCls, extClsInfo)) {\n\t\t\treturn shortName;\n\t\t}\n\t\t// don't add import for top classes from 'java.lang' package (subpackages excluded)\n\t\tif (extClsInfo.getPackage().equals(\"java.lang\") && extClsInfo.getParentClass() == null) {\n\t\t\treturn shortName;\n\t\t}\n\t\t// don't add import if this class from same package\n\t\tif (extClsInfo.getPackage().equals(useCls.getPackage()) && !extClsInfo.isInner()) {\n\t\t\treturn shortName;\n\t\t}\n\t\tif (extClsInfo.getAliasPkg().equals(useCls.getAliasPkg())) {\n\t\t\tfullName = extClsInfo.getAliasNameWithoutPackage();\n\t\t}\n\t\tfor (ClassInfo importCls : getImports()) {\n\t\t\tif (!importCls.equals(extClsInfo)\n\t\t\t\t\t&& importCls.getAliasShortName().equals(shortName)) {\n\t\t\t\tif (extClsInfo.isInner()) {\n\t\t\t\t\tString parent = useClassInternal(useCls, extClsInfo.getParentClass());\n\t\t\t\t\treturn parent + '.' + shortName;\n\t\t\t\t} else {\n\t\t\t\t\treturn fullName;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\taddImport(extClsInfo);\n\t\treturn shortName;\n\t}\n\n\tprivate String expandInnerClassName(ClassInfo useCls, ClassInfo extClsInfo) {\n\t\tList<ClassInfo> clsList = new ArrayList<>();\n\t\tclsList.add(extClsInfo);\n\t\tClassInfo parentCls = extClsInfo.getParentClass();\n\t\tboolean addImport = true;\n\t\twhile (parentCls != null) {\n\t\t\tif (parentCls == useCls || isClassInnerFor(useCls, parentCls)) {\n\t\t\t\taddImport = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tclsList.add(parentCls);\n\t\t\tparentCls = parentCls.getParentClass();\n\t\t}\n\t\tCollections.reverse(clsList);\n\t\tif (addImport) {\n\t\t\taddImport(clsList.get(0));\n\t\t}\n\t\treturn Utils.listToString(clsList, \".\", ClassInfo::getAliasShortName);\n\t}\n\n\tprivate void addImport(ClassInfo classInfo) {\n\t\tif (parentGen != null) {\n\t\t\tparentGen.addImport(classInfo);\n\t\t} else {\n\t\t\timports.add(classInfo);\n\t\t}\n\t}\n\n\tpublic Set<ClassInfo> getImports() {\n\t\tif (parentGen != null) {\n\t\t\treturn parentGen.getImports();\n\t\t} else {\n\t\t\treturn imports;\n\t\t}\n\t}\n\n\tprivate static boolean isBothClassesInOneTopClass(ClassInfo useCls, ClassInfo extClsInfo) {\n\t\tClassInfo a = useCls.getTopParentClass();\n\t\tClassInfo b = extClsInfo.getTopParentClass();\n\t\tif (a != null) {\n\t\t\treturn a.equals(b);\n\t\t}\n\t\t// useCls - is a top class\n\t\treturn useCls.equals(b);\n\t}\n\n\tprivate static boolean isClassInnerFor(ClassInfo inner, ClassInfo parent) {\n\t\tif (inner.isInner()) {\n\t\t\tClassInfo p = inner.getParentClass();\n\t\t\treturn Objects.equals(p, parent) || isClassInnerFor(p, parent);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean checkInnerCollision(RootNode root, @Nullable ClassInfo useCls, ClassInfo searchCls) {\n\t\tif (useCls == null) {\n\t\t\treturn false;\n\t\t}\n\t\tString shortName = searchCls.getAliasShortName();\n\t\tif (useCls.getAliasShortName().equals(shortName)) {\n\t\t\treturn true;\n\t\t}\n\t\tClassNode classNode = root.resolveClass(useCls);\n\t\tif (classNode != null) {\n\t\t\tfor (ClassNode inner : classNode.getInnerClasses()) {\n\t\t\t\tif (inner.getShortName().equals(shortName)\n\t\t\t\t\t\t&& !inner.getFullName().equals(searchCls.getAliasFullName())) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn checkInnerCollision(root, useCls.getParentClass(), searchCls);\n\t}\n\n\t/**\n\t * Check if class with same name exists in current package\n\t */\n\tprivate static boolean checkInPackageCollision(RootNode root, ClassInfo useCls, ClassInfo searchCls) {\n\t\tString currentPkg = useCls.getAliasPkg();\n\t\tif (currentPkg.equals(searchCls.getAliasPkg())) {\n\t\t\t// search class already from current package\n\t\t\treturn false;\n\t\t}\n\t\tString shortName = searchCls.getAliasShortName();\n\t\treturn root.getClsp().isClsKnown(currentPkg + '.' + shortName);\n\t}\n\n\tprivate static void addClassUsageInfo(ICodeWriter code, ClassNode cls) {\n\t\tList<ClassNode> deps = cls.getDependencies();\n\t\tcode.startLine(\"// deps - \").add(Integer.toString(deps.size()));\n\t\tfor (ClassNode depCls : deps) {\n\t\t\tcode.startLine(\"//  \").add(depCls.getClassInfo().getFullName());\n\t\t}\n\t\tList<ClassNode> useIn = cls.getUseIn();\n\t\tcode.startLine(\"// use in - \").add(Integer.toString(useIn.size()));\n\t\tfor (ClassNode useCls : useIn) {\n\t\t\tcode.startLine(\"//  \").add(useCls.getClassInfo().getFullName());\n\t\t}\n\t\tList<MethodNode> useInMths = cls.getUseInMth();\n\t\tcode.startLine(\"// use in methods - \").add(Integer.toString(useInMths.size()));\n\t\tfor (MethodNode useMth : useInMths) {\n\t\t\tcode.startLine(\"//  \").add(useMth.toString());\n\t\t}\n\t}\n\n\tstatic void addMthUsageInfo(ICodeWriter code, MethodNode mth) {\n\t\tList<MethodNode> useInMths = mth.getUseIn();\n\t\tcode.startLine(\"// use in methods - \").add(Integer.toString(useInMths.size()));\n\t\tfor (MethodNode useMth : useInMths) {\n\t\t\tcode.startLine(\"//  \").add(useMth.toString());\n\t\t}\n\t}\n\n\tprivate static void addFieldUsageInfo(ICodeWriter code, FieldNode fieldNode) {\n\t\tList<MethodNode> useInMths = fieldNode.getUseIn();\n\t\tcode.startLine(\"// use in methods - \").add(Integer.toString(useInMths.size()));\n\t\tfor (MethodNode useMth : useInMths) {\n\t\t\tcode.startLine(\"//  \").add(useMth.toString());\n\t\t}\n\t}\n\n\tpublic ClassGen getParentGen() {\n\t\treturn parentGen == null ? this : parentGen;\n\t}\n\n\tpublic AnnotationGen getAnnotationGen() {\n\t\treturn annotationGen;\n\t}\n\n\tpublic boolean isFallbackMode() {\n\t\treturn fallback;\n\t}\n\n\tpublic boolean isBodyGenStarted() {\n\t\treturn bodyGenStarted;\n\t}\n\n\tpublic void setBodyGenStarted(boolean bodyGenStarted) {\n\t\tthis.bodyGenStarted = bodyGenStarted;\n\t}\n\n\t@Nullable\n\tpublic NameGen getOuterNameGen() {\n\t\treturn outerNameGen;\n\t}\n\n\tpublic void setOuterNameGen(@NotNull NameGen outerNameGen) {\n\t\tthis.outerNameGen = outerNameGen;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/CodeGen.java",
    "content": "package jadx.core.codegen;\n\nimport java.util.concurrent.Callable;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.JadxArgs;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.core.codegen.json.JsonCodeGen;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class CodeGen {\n\n\tpublic static ICodeInfo generate(ClassNode cls) {\n\t\tif (cls.contains(AFlag.DONT_GENERATE)) {\n\t\t\treturn ICodeInfo.EMPTY;\n\t\t}\n\t\tJadxArgs args = cls.root().getArgs();\n\t\tswitch (args.getOutputFormat()) {\n\t\t\tcase JAVA:\n\t\t\t\treturn generateJavaCode(cls, args);\n\n\t\t\tcase JSON:\n\t\t\t\treturn generateJson(cls);\n\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unknown output format\");\n\t\t}\n\t}\n\n\tprivate static ICodeInfo generateJavaCode(ClassNode cls, JadxArgs args) {\n\t\tClassGen clsGen = new ClassGen(cls, args);\n\t\treturn wrapCodeGen(cls, clsGen::makeClass);\n\t}\n\n\tprivate static ICodeInfo generateJson(ClassNode cls) {\n\t\tJsonCodeGen codeGen = new JsonCodeGen(cls);\n\t\tString clsJson = wrapCodeGen(cls, codeGen::process);\n\t\treturn new SimpleCodeInfo(clsJson);\n\t}\n\n\tprivate static <R> R wrapCodeGen(ClassNode cls, Callable<R> codeGenFunc) {\n\t\ttry {\n\t\t\treturn codeGenFunc.call();\n\t\t} catch (Exception e) {\n\t\t\tif (cls.contains(AFlag.RESTART_CODEGEN)) {\n\t\t\t\tcls.remove(AFlag.RESTART_CODEGEN);\n\t\t\t\ttry {\n\t\t\t\t\treturn codeGenFunc.call();\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Code generation error after restart\", ex);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new JadxRuntimeException(\"Code generation error\", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate CodeGen() {\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/ConditionGen.java",
    "content": "package jadx.core.codegen;\n\nimport java.util.ArrayDeque;\nimport java.util.Iterator;\nimport java.util.Queue;\n\nimport jadx.api.ICodeWriter;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.ArithNode;\nimport jadx.core.dex.instructions.IfOp;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.regions.conditions.Compare;\nimport jadx.core.dex.regions.conditions.IfCondition;\nimport jadx.core.dex.regions.conditions.IfCondition.Mode;\nimport jadx.core.utils.exceptions.CodegenException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class ConditionGen extends InsnGen {\n\n\tprivate static class CondStack {\n\t\tprivate final Queue<IfCondition> stack = new ArrayDeque<>();\n\n\t\tpublic Queue<IfCondition> getStack() {\n\t\t\treturn stack;\n\t\t}\n\n\t\tpublic void push(IfCondition cond) {\n\t\t\tstack.add(cond);\n\t\t}\n\n\t\tpublic IfCondition pop() {\n\t\t\treturn stack.poll();\n\t\t}\n\t}\n\n\tpublic ConditionGen(InsnGen insnGen) {\n\t\tsuper(insnGen.mgen, insnGen.fallback);\n\t}\n\n\tpublic void add(ICodeWriter code, IfCondition condition) throws CodegenException {\n\t\tadd(code, new CondStack(), condition);\n\t}\n\n\tvoid wrap(ICodeWriter code, IfCondition condition) throws CodegenException {\n\t\twrap(code, new CondStack(), condition);\n\t}\n\n\tprivate void add(ICodeWriter code, CondStack stack, IfCondition condition) throws CodegenException {\n\t\tstack.push(condition);\n\t\tswitch (condition.getMode()) {\n\t\t\tcase COMPARE:\n\t\t\t\taddCompare(code, stack, condition.getCompare());\n\t\t\t\tbreak;\n\n\t\t\tcase TERNARY:\n\t\t\t\taddTernary(code, stack, condition);\n\t\t\t\tbreak;\n\n\t\t\tcase NOT:\n\t\t\t\taddNot(code, stack, condition);\n\t\t\t\tbreak;\n\n\t\t\tcase AND:\n\t\t\tcase OR:\n\t\t\t\taddAndOr(code, stack, condition);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unknown condition mode: \" + condition.getMode());\n\t\t}\n\t\tstack.pop();\n\t}\n\n\tprivate void wrap(ICodeWriter code, CondStack stack, IfCondition cond) throws CodegenException {\n\t\tboolean wrap = isWrapNeeded(cond);\n\t\tif (wrap) {\n\t\t\tcode.add('(');\n\t\t}\n\t\tadd(code, stack, cond);\n\t\tif (wrap) {\n\t\t\tcode.add(')');\n\t\t}\n\t}\n\n\tprivate void wrap(ICodeWriter code, InsnArg firstArg) throws CodegenException {\n\t\tboolean wrap = isArgWrapNeeded(firstArg);\n\t\tif (wrap) {\n\t\t\tcode.add('(');\n\t\t}\n\t\taddArg(code, firstArg, false);\n\t\tif (wrap) {\n\t\t\tcode.add(')');\n\t\t}\n\t}\n\n\tprivate void addCompare(ICodeWriter code, CondStack stack, Compare compare) throws CodegenException {\n\t\tIfOp op = compare.getOp();\n\t\tInsnArg firstArg = compare.getA();\n\t\tInsnArg secondArg = compare.getB();\n\t\tif (firstArg.getType().equals(ArgType.BOOLEAN)\n\t\t\t\t&& secondArg.isLiteral()\n\t\t\t\t&& secondArg.getType().equals(ArgType.BOOLEAN)) {\n\t\t\tLiteralArg lit = (LiteralArg) secondArg;\n\t\t\tif (lit.getLiteral() == 0) {\n\t\t\t\top = op.invert();\n\t\t\t}\n\t\t\tif (op == IfOp.EQ) {\n\t\t\t\t// == true\n\t\t\t\tif (stack.getStack().size() == 1) {\n\t\t\t\t\taddArg(code, firstArg, false);\n\t\t\t\t} else {\n\t\t\t\t\twrap(code, firstArg);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t} else if (op == IfOp.NE) {\n\t\t\t\t// != true\n\t\t\t\tcode.add('!');\n\t\t\t\twrap(code, firstArg);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmth.addWarn(\"Unsupported boolean condition \" + op.getSymbol());\n\t\t}\n\n\t\taddArg(code, firstArg, isArgWrapNeeded(firstArg));\n\t\tcode.add(' ').add(op.getSymbol()).add(' ');\n\t\taddArg(code, secondArg, isArgWrapNeeded(secondArg));\n\t}\n\n\tprivate void addTernary(ICodeWriter code, CondStack stack, IfCondition condition) throws CodegenException {\n\t\tadd(code, stack, condition.first());\n\t\tcode.add(\" ? \");\n\t\tadd(code, stack, condition.second());\n\t\tcode.add(\" : \");\n\t\tadd(code, stack, condition.third());\n\t}\n\n\tprivate void addNot(ICodeWriter code, CondStack stack, IfCondition condition) throws CodegenException {\n\t\tcode.add('!');\n\t\twrap(code, stack, condition.getArgs().get(0));\n\t}\n\n\tprivate void addAndOr(ICodeWriter code, CondStack stack, IfCondition condition) throws CodegenException {\n\t\tString mode = condition.getMode() == Mode.AND ? \" && \" : \" || \";\n\t\tIterator<IfCondition> it = condition.getArgs().iterator();\n\t\twhile (it.hasNext()) {\n\t\t\twrap(code, stack, it.next());\n\t\t\tif (it.hasNext()) {\n\t\t\t\tcode.add(mode);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate boolean isWrapNeeded(IfCondition condition) {\n\t\tif (condition.isCompare() || condition.contains(AFlag.DONT_WRAP)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn condition.getMode() != Mode.NOT;\n\t}\n\n\tprivate static boolean isArgWrapNeeded(InsnArg arg) {\n\t\tif (!arg.isInsnWrap()) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnNode insn = ((InsnWrapArg) arg).getWrapInsn();\n\t\tInsnType insnType = insn.getType();\n\t\tif (insnType == InsnType.ARITH) {\n\t\t\tswitch (((ArithNode) insn).getOp()) {\n\t\t\t\tcase ADD:\n\t\t\t\tcase SUB:\n\t\t\t\tcase MUL:\n\t\t\t\tcase DIV:\n\t\t\t\tcase REM:\n\t\t\t\t\treturn false;\n\n\t\t\t\tdefault:\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t} else {\n\t\t\tswitch (insnType) {\n\t\t\t\tcase INVOKE:\n\t\t\t\tcase SGET:\n\t\t\t\tcase IGET:\n\t\t\t\tcase AGET:\n\t\t\t\tcase CONST:\n\t\t\t\tcase ARRAY_LENGTH:\n\t\t\t\t\treturn false;\n\n\t\t\t\tdefault:\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/InsnGen.java",
    "content": "package jadx.core.codegen;\n\nimport java.util.EnumSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.CommentsLevel;\nimport jadx.api.ICodeWriter;\nimport jadx.api.metadata.annotations.InsnCodeOffset;\nimport jadx.api.metadata.annotations.VarNode;\nimport jadx.api.plugins.input.data.MethodHandleType;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.core.codegen.utils.CodeGenUtils;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.FieldInitInsnAttr;\nimport jadx.core.dex.attributes.nodes.FieldReplaceAttr;\nimport jadx.core.dex.attributes.nodes.GenericInfoAttr;\nimport jadx.core.dex.attributes.nodes.LoopLabelAttr;\nimport jadx.core.dex.attributes.nodes.MethodReplaceAttr;\nimport jadx.core.dex.attributes.nodes.SkipMethodArgsAttr;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.ArithNode;\nimport jadx.core.dex.instructions.ArithOp;\nimport jadx.core.dex.instructions.BaseInvokeNode;\nimport jadx.core.dex.instructions.ConstClassNode;\nimport jadx.core.dex.instructions.ConstStringNode;\nimport jadx.core.dex.instructions.FillArrayInsn;\nimport jadx.core.dex.instructions.FilledNewArrayNode;\nimport jadx.core.dex.instructions.GotoNode;\nimport jadx.core.dex.instructions.IfNode;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeCustomNode;\nimport jadx.core.dex.instructions.InvokeCustomRawNode;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.InvokeType;\nimport jadx.core.dex.instructions.NewArrayNode;\nimport jadx.core.dex.instructions.SwitchInsn;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.CodeVar;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.Named;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.instructions.java.JsrNode;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.instructions.mods.TernaryInsn;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.RegionUtils;\nimport jadx.core.utils.exceptions.CodegenException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.utils.android.AndroidResourcesUtils.handleAppResField;\n\npublic class InsnGen {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(InsnGen.class);\n\n\tprotected final MethodGen mgen;\n\tprotected final MethodNode mth;\n\tprotected final RootNode root;\n\tprotected final boolean fallback;\n\n\tprotected enum Flags {\n\t\tBODY_ONLY,\n\t\tBODY_ONLY_NOWRAP,\n\t\tINLINE\n\t}\n\n\tpublic InsnGen(MethodGen mgen, boolean fallback) {\n\t\tthis.mgen = mgen;\n\t\tthis.mth = mgen.getMethodNode();\n\t\tthis.root = mth.root();\n\t\tthis.fallback = fallback;\n\t}\n\n\tprivate boolean isFallback() {\n\t\treturn fallback;\n\t}\n\n\tpublic void addArgDot(ICodeWriter code, InsnArg arg) throws CodegenException {\n\t\tint len = code.getLength();\n\t\taddArg(code, arg, true);\n\t\tif (len != code.getLength()) {\n\t\t\tcode.add('.');\n\t\t}\n\t}\n\n\tpublic void addArg(ICodeWriter code, InsnArg arg) throws CodegenException {\n\t\taddArg(code, arg, true);\n\t}\n\n\tpublic void addArg(ICodeWriter code, InsnArg arg, boolean wrap) throws CodegenException {\n\t\taddArg(code, arg, wrap ? BODY_ONLY_FLAG : BODY_ONLY_NOWRAP_FLAGS);\n\t}\n\n\tpublic void addArg(ICodeWriter code, InsnArg arg, Set<Flags> flags) throws CodegenException {\n\t\tif (arg.isRegister()) {\n\t\t\tRegisterArg reg = (RegisterArg) arg;\n\t\t\tif (code.isMetadataSupported()) {\n\t\t\t\tcode.attachAnnotation(VarNode.getRef(mth, reg));\n\t\t\t}\n\t\t\tcode.add(mgen.getNameGen().useArg(reg));\n\t\t} else if (arg.isLiteral()) {\n\t\t\taddLiteralArg(code, (LiteralArg) arg, flags);\n\t\t} else if (arg.isInsnWrap()) {\n\t\t\taddWrappedArg(code, (InsnWrapArg) arg, flags);\n\t\t} else if (arg.isNamed()) {\n\t\t\tcode.add(((Named) arg).getName());\n\t\t} else {\n\t\t\tthrow new CodegenException(\"Unknown arg type \" + arg);\n\t\t}\n\t}\n\n\tprivate void addLiteralArg(ICodeWriter code, LiteralArg litArg, Set<Flags> flags) {\n\t\tString literalStr = lit(litArg);\n\t\tif (!flags.contains(Flags.BODY_ONLY_NOWRAP) && literalStr.startsWith(\"-\")) {\n\t\t\tcode.add('(').add(literalStr).add(')');\n\t\t} else {\n\t\t\tcode.add(literalStr);\n\t\t}\n\t}\n\n\tprivate void addWrappedArg(ICodeWriter code, InsnWrapArg arg, Set<Flags> flags) throws CodegenException {\n\t\tInsnNode wrapInsn = arg.getWrapInsn();\n\t\tif (wrapInsn.contains(AFlag.FORCE_ASSIGN_INLINE)) {\n\t\t\tcode.add('(');\n\t\t\tmakeInsn(wrapInsn, code, Flags.INLINE);\n\t\t\tcode.add(')');\n\t\t} else {\n\t\t\tmakeInsnBody(code, wrapInsn, flags);\n\t\t}\n\t}\n\n\tpublic void assignVar(ICodeWriter code, InsnNode insn) throws CodegenException {\n\t\tRegisterArg arg = insn.getResult();\n\t\tif (insn.contains(AFlag.DECLARE_VAR)) {\n\t\t\tdeclareVar(code, arg);\n\t\t} else {\n\t\t\taddArg(code, arg, false);\n\t\t}\n\t}\n\n\tpublic void declareVar(ICodeWriter code, RegisterArg arg) {\n\t\tdeclareVar(code, arg.getSVar().getCodeVar());\n\t}\n\n\tpublic void declareVar(ICodeWriter code, CodeVar codeVar) {\n\t\tif (codeVar.isFinal()) {\n\t\t\tcode.add(\"final \");\n\t\t}\n\t\tuseType(code, codeVar.getType());\n\t\tcode.add(' ');\n\t\tdefVar(code, codeVar);\n\t}\n\n\t/**\n\t * Variable definition without type, only var name\n\t */\n\tprivate void defVar(ICodeWriter code, CodeVar codeVar) {\n\t\tString varName = mgen.getNameGen().assignArg(codeVar);\n\t\tif (code.isMetadataSupported()) {\n\t\t\tcode.attachDefinition(VarNode.get(mth, codeVar));\n\t\t}\n\t\tcode.add(varName);\n\t}\n\n\tprivate String lit(LiteralArg arg) {\n\t\treturn TypeGen.literalToString(arg, mth, fallback);\n\t}\n\n\tprivate void instanceField(ICodeWriter code, FieldInfo field, InsnArg arg) throws CodegenException {\n\t\tClassNode pCls = mth.getParentClass();\n\t\tFieldNode fieldNode = pCls.root().resolveField(field);\n\t\tif (fieldNode != null) {\n\t\t\tFieldReplaceAttr replace = fieldNode.get(AType.FIELD_REPLACE);\n\t\t\tif (replace != null) {\n\t\t\t\tswitch (replace.getReplaceType()) {\n\t\t\t\t\tcase CLASS_INSTANCE:\n\t\t\t\t\t\tuseClass(code, replace.getClsRef());\n\t\t\t\t\t\tcode.add(\".this\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase VAR:\n\t\t\t\t\t\taddArg(code, replace.getVarRef());\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\taddArgDot(code, arg);\n\t\tif (fieldNode != null) {\n\t\t\tcode.attachAnnotation(fieldNode);\n\t\t}\n\t\tif (fieldNode == null) {\n\t\t\tcode.add(field.getAlias());\n\t\t} else {\n\t\t\tcode.add(fieldNode.getAlias());\n\t\t}\n\t}\n\n\tprotected void staticField(ICodeWriter code, FieldInfo field) throws CodegenException {\n\t\tFieldNode fieldNode = root.resolveField(field);\n\t\tif (fieldNode != null\n\t\t\t\t&& fieldNode.contains(AFlag.INLINE_INSTANCE_FIELD)\n\t\t\t\t&& fieldNode.getParentClass().contains(AType.ANONYMOUS_CLASS)) {\n\t\t\tFieldInitInsnAttr initInsnAttr = fieldNode.get(AType.FIELD_INIT_INSN);\n\t\t\tif (initInsnAttr != null) {\n\t\t\t\tInsnNode insn = initInsnAttr.getInsn();\n\t\t\t\tif (insn instanceof ConstructorInsn) {\n\t\t\t\t\tfieldNode.add(AFlag.DONT_GENERATE);\n\t\t\t\t\tinlineAnonymousConstructor(code, fieldNode.getParentClass(), (ConstructorInsn) insn);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tmakeStaticFieldAccess(code, field, fieldNode, mgen.getClassGen());\n\t}\n\n\tpublic static void makeStaticFieldAccess(ICodeWriter code, FieldInfo field, ClassGen clsGen) {\n\t\tFieldNode fieldNode = clsGen.getClassNode().root().resolveField(field);\n\t\tmakeStaticFieldAccess(code, field, fieldNode, clsGen);\n\t}\n\n\tprivate static void makeStaticFieldAccess(ICodeWriter code,\n\t\t\tFieldInfo field, @Nullable FieldNode fieldNode, ClassGen clsGen) {\n\t\tClassInfo declClass = field.getDeclClass();\n\t\t// TODO\n\t\tboolean fieldFromThisClass = clsGen.getClassNode().getClassInfo().equals(declClass);\n\t\tif (!fieldFromThisClass || !clsGen.isBodyGenStarted()) {\n\t\t\t// Android specific resources class handler\n\t\t\tif (!handleAppResField(code, clsGen, declClass)) {\n\t\t\t\tclsGen.useClass(code, declClass);\n\t\t\t}\n\t\t\tcode.add('.');\n\t\t}\n\t\tif (fieldNode != null) {\n\t\t\tcode.attachAnnotation(fieldNode);\n\t\t}\n\t\tif (fieldNode == null) {\n\t\t\tcode.add(field.getAlias());\n\t\t} else {\n\t\t\tcode.add(fieldNode.getAlias());\n\t\t}\n\t}\n\n\tpublic void useClass(ICodeWriter code, ArgType type) {\n\t\tmgen.getClassGen().useClass(code, type);\n\t}\n\n\tpublic void useClass(ICodeWriter code, ClassInfo cls) {\n\t\tmgen.getClassGen().useClass(code, cls);\n\t}\n\n\tprotected void useType(ICodeWriter code, ArgType type) {\n\t\tmgen.getClassGen().useType(code, type);\n\t}\n\n\tpublic void makeInsn(InsnNode insn, ICodeWriter code) throws CodegenException {\n\t\tmakeInsn(insn, code, null);\n\t}\n\n\tprivate static final Set<Flags> EMPTY_FLAGS = EnumSet.noneOf(Flags.class);\n\tprivate static final Set<Flags> BODY_ONLY_FLAG = EnumSet.of(Flags.BODY_ONLY);\n\tprivate static final Set<Flags> BODY_ONLY_NOWRAP_FLAGS = EnumSet.of(Flags.BODY_ONLY_NOWRAP);\n\n\tprotected void makeInsn(InsnNode insn, ICodeWriter code, Flags flag) throws CodegenException {\n\t\tif (insn.getType() == InsnType.REGION_ARG) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tif (flag == Flags.BODY_ONLY || flag == Flags.BODY_ONLY_NOWRAP) {\n\t\t\t\tmakeInsnBody(code, insn, flag == Flags.BODY_ONLY ? BODY_ONLY_FLAG : BODY_ONLY_NOWRAP_FLAGS);\n\t\t\t} else {\n\t\t\t\tif (flag != Flags.INLINE) {\n\t\t\t\t\tcode.startLineWithNum(insn.getSourceLine());\n\t\t\t\t\tInsnCodeOffset.attach(code, insn);\n\t\t\t\t\tif (insn.contains(AFlag.COMMENT_OUT)) {\n\t\t\t\t\t\tcode.add(\"// \");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tRegisterArg resArg = insn.getResult();\n\t\t\t\tif (resArg != null) {\n\t\t\t\t\tSSAVar var = resArg.getSVar();\n\t\t\t\t\tif (var == null || var.getUseCount() != 0 || insn.getType() != InsnType.CONSTRUCTOR) {\n\t\t\t\t\t\tassignVar(code, insn);\n\t\t\t\t\t\tcode.add(\" = \");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmakeInsnBody(code, insn, EMPTY_FLAGS);\n\t\t\t\tif (flag != Flags.INLINE) {\n\t\t\t\t\tcode.add(';');\n\t\t\t\t\tCodeGenUtils.addCodeComments(code, mth, insn);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new CodegenException(mth, \"Error generate insn: \" + insn, e);\n\t\t}\n\t}\n\n\tprivate void makeInsnBody(ICodeWriter code, InsnNode insn, Set<Flags> state) throws CodegenException {\n\t\tswitch (insn.getType()) {\n\t\t\tcase CONST_STR:\n\t\t\t\tString str = ((ConstStringNode) insn).getString();\n\t\t\t\tcode.add(mth.root().getStringUtils().unescapeString(str));\n\t\t\t\tbreak;\n\n\t\t\tcase CONST_CLASS:\n\t\t\t\tArgType clsType = ((ConstClassNode) insn).getClsType();\n\t\t\t\tuseType(code, clsType);\n\t\t\t\tcode.add(\".class\");\n\t\t\t\tbreak;\n\n\t\t\tcase CONST:\n\t\t\t\tLiteralArg arg = (LiteralArg) insn.getArg(0);\n\t\t\t\tcode.add(lit(arg));\n\t\t\t\tbreak;\n\n\t\t\tcase MOVE:\n\t\t\t\taddArg(code, insn.getArg(0), false);\n\t\t\t\tbreak;\n\n\t\t\tcase CHECK_CAST:\n\t\t\tcase CAST: {\n\t\t\t\tboolean wrap = state.contains(Flags.BODY_ONLY);\n\t\t\t\tif (wrap) {\n\t\t\t\t\tcode.add('(');\n\t\t\t\t}\n\t\t\t\tcode.add('(');\n\t\t\t\tuseType(code, (ArgType) ((IndexInsnNode) insn).getIndex());\n\t\t\t\tcode.add(\") \");\n\t\t\t\taddArg(code, insn.getArg(0), true);\n\t\t\t\tif (wrap) {\n\t\t\t\t\tcode.add(')');\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase ARITH:\n\t\t\t\tmakeArith((ArithNode) insn, code, state);\n\t\t\t\tbreak;\n\n\t\t\tcase NEG:\n\t\t\t\toneArgInsn(code, insn, state, '-');\n\t\t\t\tbreak;\n\n\t\t\tcase NOT:\n\t\t\t\tchar op = insn.getArg(0).getType() == ArgType.BOOLEAN ? '!' : '~';\n\t\t\t\toneArgInsn(code, insn, state, op);\n\t\t\t\tbreak;\n\n\t\t\tcase RETURN:\n\t\t\t\tif (insn.getArgsCount() != 0) {\n\t\t\t\t\tcode.add(\"return \");\n\t\t\t\t\taddArg(code, insn.getArg(0), false);\n\t\t\t\t} else {\n\t\t\t\t\tcode.add(\"return\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase BREAK:\n\t\t\t\tcode.add(\"break\");\n\t\t\t\tLoopLabelAttr labelAttr = insn.get(AType.LOOP_LABEL);\n\t\t\t\tif (labelAttr != null) {\n\t\t\t\t\tcode.add(' ').add(mgen.getNameGen().getLoopLabel(labelAttr));\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase CONTINUE:\n\t\t\t\tcode.add(\"continue\");\n\t\t\t\tbreak;\n\n\t\t\tcase THROW:\n\t\t\t\tcode.add(\"throw \");\n\t\t\t\taddArg(code, insn.getArg(0), true);\n\t\t\t\tbreak;\n\n\t\t\tcase CMP_L:\n\t\t\tcase CMP_G:\n\t\t\t\tcode.add('(');\n\t\t\t\taddArg(code, insn.getArg(0));\n\t\t\t\tcode.add(\" > \");\n\t\t\t\taddArg(code, insn.getArg(1));\n\t\t\t\tcode.add(\" ? 1 : (\");\n\t\t\t\taddArg(code, insn.getArg(0));\n\t\t\t\tcode.add(\" == \");\n\t\t\t\taddArg(code, insn.getArg(1));\n\t\t\t\tcode.add(\" ? 0 : -1))\");\n\t\t\t\tbreak;\n\n\t\t\tcase INSTANCE_OF: {\n\t\t\t\tboolean wrap = state.contains(Flags.BODY_ONLY);\n\t\t\t\tif (wrap) {\n\t\t\t\t\tcode.add('(');\n\t\t\t\t}\n\t\t\t\taddArg(code, insn.getArg(0));\n\t\t\t\tcode.add(\" instanceof \");\n\t\t\t\tuseType(code, (ArgType) ((IndexInsnNode) insn).getIndex());\n\t\t\t\tif (wrap) {\n\t\t\t\t\tcode.add(')');\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase CONSTRUCTOR:\n\t\t\t\tmakeConstructor((ConstructorInsn) insn, code);\n\t\t\t\tbreak;\n\n\t\t\tcase INVOKE:\n\t\t\t\tmakeInvoke((InvokeNode) insn, code);\n\t\t\t\tbreak;\n\n\t\t\tcase NEW_ARRAY: {\n\t\t\t\tArgType arrayType = ((NewArrayNode) insn).getArrayType();\n\t\t\t\tcode.add(\"new \");\n\t\t\t\tuseType(code, arrayType.getArrayRootElement());\n\t\t\t\tint k = 0;\n\t\t\t\tint argsCount = insn.getArgsCount();\n\t\t\t\tfor (; k < argsCount; k++) {\n\t\t\t\t\tcode.add('[');\n\t\t\t\t\taddArg(code, insn.getArg(k), false);\n\t\t\t\t\tcode.add(']');\n\t\t\t\t}\n\t\t\t\tint dim = arrayType.getArrayDimension();\n\t\t\t\tfor (; k < dim; k++) {\n\t\t\t\t\tcode.add(\"[]\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase ARRAY_LENGTH:\n\t\t\t\taddArg(code, insn.getArg(0));\n\t\t\t\tcode.add(\".length\");\n\t\t\t\tbreak;\n\n\t\t\tcase FILLED_NEW_ARRAY:\n\t\t\t\tfilledNewArray((FilledNewArrayNode) insn, code);\n\t\t\t\tbreak;\n\n\t\t\tcase FILL_ARRAY:\n\t\t\t\tFillArrayInsn arrayNode = (FillArrayInsn) insn;\n\t\t\t\tif (fallback) {\n\t\t\t\t\tString arrStr = arrayNode.dataToString();\n\t\t\t\t\taddArg(code, insn.getArg(0));\n\t\t\t\t\tcode.add(\" = {\").add(arrStr.substring(1, arrStr.length() - 1)).add(\"} // fill-array\");\n\t\t\t\t} else {\n\t\t\t\t\tfillArray(code, arrayNode);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase AGET:\n\t\t\t\taddArg(code, insn.getArg(0));\n\t\t\t\tcode.add('[');\n\t\t\t\taddArg(code, insn.getArg(1), false);\n\t\t\t\tcode.add(']');\n\t\t\t\tbreak;\n\n\t\t\tcase APUT:\n\t\t\t\taddArg(code, insn.getArg(0));\n\t\t\t\tcode.add('[');\n\t\t\t\taddArg(code, insn.getArg(1), false);\n\t\t\t\tcode.add(\"] = \");\n\t\t\t\taddArg(code, insn.getArg(2), false);\n\t\t\t\tbreak;\n\n\t\t\tcase IGET: {\n\t\t\t\tFieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) insn).getIndex();\n\t\t\t\tinstanceField(code, fieldInfo, insn.getArg(0));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase IPUT: {\n\t\t\t\tFieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) insn).getIndex();\n\t\t\t\tinstanceField(code, fieldInfo, insn.getArg(1));\n\t\t\t\tcode.add(\" = \");\n\t\t\t\taddArg(code, insn.getArg(0), false);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase SGET:\n\t\t\t\tstaticField(code, (FieldInfo) ((IndexInsnNode) insn).getIndex());\n\t\t\t\tbreak;\n\t\t\tcase SPUT:\n\t\t\t\tFieldInfo field = (FieldInfo) ((IndexInsnNode) insn).getIndex();\n\t\t\t\tstaticField(code, field);\n\t\t\t\tcode.add(\" = \");\n\t\t\t\taddArg(code, insn.getArg(0), false);\n\t\t\t\tbreak;\n\n\t\t\tcase STR_CONCAT:\n\t\t\t\tboolean wrap = state.contains(Flags.BODY_ONLY);\n\t\t\t\tif (wrap) {\n\t\t\t\t\tcode.add('(');\n\t\t\t\t}\n\t\t\t\tfor (Iterator<InsnArg> it = insn.getArguments().iterator(); it.hasNext();) {\n\t\t\t\t\taddArg(code, it.next());\n\t\t\t\t\tif (it.hasNext()) {\n\t\t\t\t\t\tcode.add(\" + \");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (wrap) {\n\t\t\t\t\tcode.add(')');\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase MONITOR_ENTER:\n\t\t\t\tif (isFallback()) {\n\t\t\t\t\tcode.add(\"monitor-enter(\");\n\t\t\t\t\taddArg(code, insn.getArg(0));\n\t\t\t\t\tcode.add(')');\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase MONITOR_EXIT:\n\t\t\t\tif (isFallback()) {\n\t\t\t\t\tcode.add(\"monitor-exit(\");\n\t\t\t\t\tif (insn.getArgsCount() == 1) {\n\t\t\t\t\t\taddArg(code, insn.getArg(0));\n\t\t\t\t\t}\n\t\t\t\t\tcode.add(')');\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase TERNARY:\n\t\t\t\tmakeTernary((TernaryInsn) insn, code, state);\n\t\t\t\tbreak;\n\n\t\t\tcase ONE_ARG:\n\t\t\t\taddArg(code, insn.getArg(0), state);\n\t\t\t\tbreak;\n\n\t\t\t/* fallback mode instructions */\n\t\t\tcase IF:\n\t\t\t\tfallbackOnlyInsn(insn);\n\t\t\t\tIfNode ifInsn = (IfNode) insn;\n\t\t\t\tcode.add(\"if (\");\n\t\t\t\taddArg(code, insn.getArg(0));\n\t\t\t\tcode.add(' ');\n\t\t\t\tcode.add(ifInsn.getOp().getSymbol()).add(' ');\n\t\t\t\taddArg(code, insn.getArg(1));\n\t\t\t\tcode.add(\") goto \").add(MethodGen.getLabelName(ifInsn));\n\t\t\t\tbreak;\n\n\t\t\tcase GOTO:\n\t\t\t\tfallbackOnlyInsn(insn);\n\t\t\t\tcode.add(\"goto \").add(MethodGen.getLabelName(((GotoNode) insn).getTarget()));\n\t\t\t\tbreak;\n\n\t\t\tcase MOVE_EXCEPTION:\n\t\t\t\tfallbackOnlyInsn(insn);\n\t\t\t\tcode.add(\"move-exception\");\n\t\t\t\tbreak;\n\n\t\t\tcase SWITCH:\n\t\t\t\tfallbackOnlyInsn(insn);\n\t\t\t\tSwitchInsn sw = (SwitchInsn) insn;\n\t\t\t\tcode.add(\"switch(\");\n\t\t\t\taddArg(code, insn.getArg(0));\n\t\t\t\tcode.add(\") {\");\n\t\t\t\tcode.incIndent();\n\t\t\t\tint[] keys = sw.getKeys();\n\t\t\t\tint size = keys.length;\n\t\t\t\tBlockNode[] targetBlocks = sw.getTargetBlocks();\n\t\t\t\tif (targetBlocks != null) {\n\t\t\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\t\t\tcode.startLine(\"case \").add(Integer.toString(keys[i])).add(\": goto \");\n\t\t\t\t\t\tcode.add(MethodGen.getLabelName(targetBlocks[i])).add(';');\n\t\t\t\t\t}\n\t\t\t\t\tcode.startLine(\"default: goto \");\n\t\t\t\t\tcode.add(MethodGen.getLabelName(sw.getDefTargetBlock())).add(';');\n\t\t\t\t} else {\n\t\t\t\t\tint[] targets = sw.getTargets();\n\t\t\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\t\t\tcode.startLine(\"case \").add(Integer.toString(keys[i])).add(\": goto \");\n\t\t\t\t\t\tcode.add(MethodGen.getLabelName(targets[i])).add(';');\n\t\t\t\t\t}\n\t\t\t\t\tcode.startLine(\"default: goto \");\n\t\t\t\t\tcode.add(MethodGen.getLabelName(sw.getDefaultCaseOffset())).add(';');\n\t\t\t\t}\n\t\t\t\tcode.decIndent();\n\t\t\t\tcode.startLine('}');\n\t\t\t\tbreak;\n\n\t\t\tcase NEW_INSTANCE:\n\t\t\t\t// only fallback - make new instance in constructor invoke\n\t\t\t\tfallbackOnlyInsn(insn);\n\t\t\t\tcode.add(\"new \").add(insn.getResult().getInitType().toString());\n\t\t\t\tbreak;\n\n\t\t\tcase PHI:\n\t\t\t\tfallbackOnlyInsn(insn);\n\t\t\t\tcode.add(insn.getType().toString()).add('(');\n\t\t\t\tfor (InsnArg insnArg : insn.getArguments()) {\n\t\t\t\t\taddArg(code, insnArg);\n\t\t\t\t\tcode.add(' ');\n\t\t\t\t}\n\t\t\t\tcode.add(')');\n\t\t\t\tbreak;\n\n\t\t\tcase MOVE_RESULT:\n\t\t\t\tfallbackOnlyInsn(insn);\n\t\t\t\tcode.add(\"move-result\");\n\t\t\t\tbreak;\n\n\t\t\tcase FILL_ARRAY_DATA:\n\t\t\t\tfallbackOnlyInsn(insn);\n\t\t\t\tcode.add(\"fill-array \" + insn);\n\t\t\t\tbreak;\n\n\t\t\tcase SWITCH_DATA:\n\t\t\t\tfallbackOnlyInsn(insn);\n\t\t\t\tcode.add(insn.toString());\n\t\t\t\tbreak;\n\n\t\t\tcase MOVE_MULTI:\n\t\t\t\tfallbackOnlyInsn(insn);\n\t\t\t\tint len = insn.getArgsCount();\n\t\t\t\tfor (int i = 0; i < len - 1; i += 2) {\n\t\t\t\t\taddArg(code, insn.getArg(i));\n\t\t\t\t\tcode.add(\" = \");\n\t\t\t\t\taddArg(code, insn.getArg(i + 1));\n\t\t\t\t\tcode.add(\"; \");\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase JAVA_JSR:\n\t\t\t\tfallbackOnlyInsn(insn);\n\t\t\t\tcode.add(\"jsr -> \").add(MethodGen.getLabelName(((JsrNode) insn).getTarget()));\n\t\t\t\tbreak;\n\n\t\t\tcase JAVA_RET:\n\t\t\t\tfallbackOnlyInsn(insn);\n\t\t\t\tcode.add(\"ret \");\n\t\t\t\taddArg(code, insn.getArg(0));\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthrow new CodegenException(mth, \"Unknown instruction: \" + insn.getType());\n\t\t}\n\t}\n\n\t/**\n\t * In most cases must be combined with new array instructions.\n\t * Use one by one array fill (can be replaced with System.arrayCopy)\n\t */\n\tprivate void fillArray(ICodeWriter code, FillArrayInsn arrayNode) throws CodegenException {\n\t\tif (mth.checkCommentsLevel(CommentsLevel.INFO)) {\n\t\t\tcode.add(\"// fill-array-data instruction\");\n\t\t}\n\t\tcode.startLine();\n\t\tInsnArg arrArg = arrayNode.getArg(0);\n\t\tArgType arrayType = arrArg.getType();\n\t\tArgType elemType;\n\t\tif (arrayType.isTypeKnown() && arrayType.isArray()) {\n\t\t\telemType = arrayType.getArrayElement();\n\t\t} else {\n\t\t\tArgType elementType = arrayNode.getElementType(); // unknown type\n\t\t\telemType = elementType.selectFirst();\n\t\t}\n\t\tList<LiteralArg> args = arrayNode.getLiteralArgs(elemType);\n\t\tint len = args.size();\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tif (i != 0) {\n\t\t\t\tcode.add(';');\n\t\t\t\tcode.startLine();\n\t\t\t}\n\t\t\taddArg(code, arrArg);\n\t\t\tcode.add('[').add(Integer.toString(i)).add(\"] = \").add(lit(args.get(i)));\n\t\t}\n\t}\n\n\tprivate void oneArgInsn(ICodeWriter code, InsnNode insn, Set<Flags> state, char op) throws CodegenException {\n\t\tboolean wrap = state.contains(Flags.BODY_ONLY);\n\t\tif (wrap) {\n\t\t\tcode.add('(');\n\t\t}\n\t\tcode.add(op);\n\t\taddArg(code, insn.getArg(0));\n\t\tif (wrap) {\n\t\t\tcode.add(')');\n\t\t}\n\t}\n\n\tprivate void fallbackOnlyInsn(InsnNode insn) throws CodegenException {\n\t\tif (!fallback) {\n\t\t\tString msg = insn.getType() + \" instruction can be used only in fallback mode\";\n\t\t\tCodegenException e = new CodegenException(msg);\n\t\t\tmth.addError(msg, e);\n\t\t\tmth.getParentClass().getTopParentClass().add(AFlag.RESTART_CODEGEN);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tprivate void filledNewArray(FilledNewArrayNode insn, ICodeWriter code) throws CodegenException {\n\t\tif (!insn.contains(AFlag.DECLARE_VAR)) {\n\t\t\tcode.add(\"new \");\n\t\t\tuseType(code, insn.getArrayType());\n\t\t}\n\t\tcode.add('{');\n\t\tint c = insn.getArgsCount();\n\t\tint wrap = 0;\n\t\tfor (int i = 0; i < c; i++) {\n\t\t\taddArg(code, insn.getArg(i), false);\n\t\t\tif (i + 1 < c) {\n\t\t\t\tcode.add(\", \");\n\t\t\t}\n\t\t\twrap++;\n\t\t\tif (wrap == 1000) {\n\t\t\t\tcode.startLine();\n\t\t\t\twrap = 0;\n\t\t\t}\n\t\t}\n\t\tcode.add('}');\n\t}\n\n\tprivate void makeConstructor(ConstructorInsn insn, ICodeWriter code) throws CodegenException {\n\t\tClassNode cls = mth.root().resolveClass(insn.getClassType());\n\t\tif (cls != null && cls.isAnonymous() && !fallback) {\n\t\t\tinlineAnonymousConstructor(code, cls, insn);\n\t\t\treturn;\n\t\t}\n\t\tif (insn.isSelf()) {\n\t\t\tthrow new JadxRuntimeException(\"Constructor 'self' invoke must be removed!\");\n\t\t}\n\t\tMethodNode callMth = mth.root().resolveMethod(insn.getCallMth());\n\t\tMethodNode refMth = callMth;\n\t\tif (callMth != null) {\n\t\t\tMethodReplaceAttr replaceAttr = callMth.get(AType.METHOD_REPLACE);\n\t\t\tif (replaceAttr != null) {\n\t\t\t\trefMth = replaceAttr.getReplaceMth();\n\t\t\t}\n\t\t}\n\n\t\tif (insn.isSuper()) {\n\t\t\tcode.attachAnnotation(refMth);\n\t\t\tcode.add(\"super\");\n\t\t} else if (insn.isThis()) {\n\t\t\tcode.attachAnnotation(refMth);\n\t\t\tcode.add(\"this\");\n\t\t} else {\n\t\t\tboolean forceShortName = addOuterClassInstance(insn, code, callMth);\n\t\t\tcode.add(\"new \");\n\t\t\tif (refMth == null || refMth.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\t// use class reference if constructor method is missing (default constructor)\n\t\t\t\tcode.attachAnnotation(mth.root().resolveClass(insn.getCallMth().getDeclClass()));\n\t\t\t} else {\n\t\t\t\tcode.attachAnnotation(refMth);\n\t\t\t}\n\t\t\tif (forceShortName) {\n\t\t\t\tmgen.getClassGen().addClsShortNameForced(code, insn.getClassType());\n\t\t\t} else {\n\t\t\t\tmgen.getClassGen().addClsName(code, insn.getClassType());\n\t\t\t}\n\t\t\tGenericInfoAttr genericInfoAttr = insn.get(AType.GENERIC_INFO);\n\t\t\tif (genericInfoAttr != null) {\n\t\t\t\tcode.add('<');\n\t\t\t\tif (genericInfoAttr.isExplicit()) {\n\t\t\t\t\tboolean first = true;\n\t\t\t\t\tfor (ArgType type : genericInfoAttr.getGenericTypes()) {\n\t\t\t\t\t\tif (!first) {\n\t\t\t\t\t\t\tcode.add(',');\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfirst = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmgen.getClassGen().useType(code, type);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcode.add('>');\n\t\t\t}\n\t\t}\n\t\tgenerateMethodArguments(code, insn, 0, callMth);\n\t}\n\n\tprivate boolean addOuterClassInstance(ConstructorInsn insn, ICodeWriter code, MethodNode callMth) throws CodegenException {\n\t\tif (callMth == null || !callMth.contains(AFlag.SKIP_FIRST_ARG)) {\n\t\t\treturn false;\n\t\t}\n\t\tClassNode ctrCls = callMth.getDeclaringClass();\n\t\tif (!ctrCls.isInner() || insn.getArgsCount() == 0) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnArg instArg = insn.getArg(0);\n\t\tif (instArg.isThis()) {\n\t\t\treturn false;\n\t\t}\n\t\t// instance arg should be of an outer class type\n\t\tif (!instArg.getType().equals(ctrCls.getDeclaringClass().getType())) {\n\t\t\treturn false;\n\t\t}\n\t\taddArgDot(code, instArg);\n\t\t// can't use another dot, force short name of class\n\t\treturn true;\n\t}\n\n\tprivate void inlineAnonymousConstructor(ICodeWriter code, ClassNode cls, ConstructorInsn insn) throws CodegenException {\n\t\tcls.ensureProcessed();\n\t\tif (this.mth.getParentClass() == cls) {\n\t\t\tcls.remove(AType.ANONYMOUS_CLASS);\n\t\t\tcls.remove(AFlag.DONT_GENERATE);\n\t\t\tmth.getParentClass().getTopParentClass().add(AFlag.RESTART_CODEGEN);\n\t\t\tthrow new CodegenException(\"Anonymous inner class unlimited recursion detected.\"\n\t\t\t\t\t+ \" Convert class to inner: \" + cls.getClassInfo().getFullName());\n\t\t}\n\t\tArgType parent = cls.get(AType.ANONYMOUS_CLASS).getBaseType();\n\t\t// hide empty anonymous constructors\n\t\tfor (MethodNode ctor : cls.getMethods()) {\n\t\t\tif (ctor.contains(AFlag.ANONYMOUS_CONSTRUCTOR)\n\t\t\t\t\t&& RegionUtils.isEmpty(ctor.getRegion())) {\n\t\t\t\tctor.add(AFlag.DONT_GENERATE);\n\t\t\t}\n\t\t}\n\t\tcode.attachDefinition(cls);\n\t\tcode.add(\"new \");\n\t\tuseClass(code, parent);\n\t\tMethodNode callMth = mth.root().resolveMethod(insn.getCallMth());\n\t\tif (callMth != null) {\n\t\t\t// copy var names\n\t\t\tList<RegisterArg> mthArgs = callMth.getArgRegs();\n\t\t\tint argsCount = Math.min(insn.getArgsCount(), mthArgs.size());\n\t\t\tfor (int i = 0; i < argsCount; i++) {\n\t\t\t\tInsnArg arg = insn.getArg(i);\n\t\t\t\tif (arg.isRegister()) {\n\t\t\t\t\tRegisterArg mthArg = mthArgs.get(i);\n\t\t\t\t\tRegisterArg insnArg = (RegisterArg) arg;\n\t\t\t\t\tmthArg.getSVar().setCodeVar(insnArg.getSVar().getCodeVar());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tgenerateMethodArguments(code, insn, 0, callMth);\n\t\tcode.add(' ');\n\n\t\tClassGen classGen = new ClassGen(cls, mgen.getClassGen().getParentGen());\n\t\tclassGen.setOuterNameGen(mgen.getNameGen());\n\t\tclassGen.addClassBody(code, true);\n\n\t\tmth.getParentClass().addInlinedClass(cls);\n\t}\n\n\tprivate void makeInvoke(InvokeNode insn, ICodeWriter code) throws CodegenException {\n\t\tInvokeType type = insn.getInvokeType();\n\t\tif (type == InvokeType.CUSTOM) {\n\t\t\tmakeInvokeLambda(code, (InvokeCustomNode) insn);\n\t\t\treturn;\n\t\t}\n\t\tMethodInfo callMth = insn.getCallMth();\n\t\tMethodNode callMthNode = mth.root().resolveMethod(callMth);\n\n\t\tif (type == InvokeType.CUSTOM_RAW) {\n\t\t\tmakeInvokeCustomRaw((InvokeCustomRawNode) insn, callMthNode, code);\n\t\t\treturn;\n\t\t}\n\t\tif (insn.isPolymorphicCall()) {\n\t\t\t// add missing cast\n\t\t\tcode.add('(');\n\t\t\tuseType(code, callMth.getReturnType());\n\t\t\tcode.add(\") \");\n\t\t}\n\n\t\tint k = 0;\n\t\tswitch (type) {\n\t\t\tcase DIRECT:\n\t\t\tcase VIRTUAL:\n\t\t\tcase INTERFACE:\n\t\t\tcase POLYMORPHIC:\n\t\t\t\tInsnArg arg = insn.getArg(0);\n\t\t\t\tif (needInvokeArg(arg)) {\n\t\t\t\t\taddArgDot(code, arg);\n\t\t\t\t}\n\t\t\t\tk++;\n\t\t\t\tbreak;\n\n\t\t\tcase SUPER:\n\t\t\t\tcallSuper(code, callMth);\n\t\t\t\tk++; // use 'super' instead 'this' in 0 arg\n\t\t\t\tcode.add('.');\n\t\t\t\tbreak;\n\n\t\t\tcase STATIC:\n\t\t\t\tClassInfo insnCls = mth.getParentClass().getClassInfo();\n\t\t\t\tClassInfo declClass = callMth.getDeclClass();\n\t\t\t\tif (!insnCls.equals(declClass)) {\n\t\t\t\t\tuseClass(code, declClass);\n\t\t\t\t\tcode.add('.');\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t\tif (callMthNode != null) {\n\t\t\tcode.attachAnnotation(callMthNode);\n\t\t}\n\t\tif (insn.contains(AFlag.FORCE_RAW_NAME)) {\n\t\t\tcode.add(callMth.getName());\n\t\t} else {\n\t\t\tif (callMthNode != null) {\n\t\t\t\tcode.add(callMthNode.getAlias());\n\t\t\t} else {\n\t\t\t\tcode.add(callMth.getAlias());\n\t\t\t}\n\t\t}\n\t\tgenerateMethodArguments(code, insn, k, callMthNode);\n\t}\n\n\tprivate void makeInvokeCustomRaw(InvokeCustomRawNode insn,\n\t\t\t@Nullable MethodNode callMthNode, ICodeWriter code) throws CodegenException {\n\t\tif (isFallback()) {\n\t\t\tcode.add(\"call_site(\");\n\t\t\tcode.incIndent();\n\t\t\tfor (EncodedValue value : insn.getCallSiteValues()) {\n\t\t\t\tcode.startLine(value.toString());\n\t\t\t}\n\t\t\tcode.decIndent();\n\t\t\tcode.startLine(\").invoke\");\n\t\t\tgenerateMethodArguments(code, insn, 0, callMthNode);\n\t\t} else {\n\t\t\tArgType returnType = insn.getCallMth().getReturnType();\n\t\t\tif (!returnType.isVoid()) {\n\t\t\t\tcode.add('(');\n\t\t\t\tuseType(code, returnType);\n\t\t\t\tcode.add(\") \");\n\t\t\t}\n\t\t\tmakeInvoke(insn.getResolveInvoke(), code);\n\t\t\tcode.add(\".dynamicInvoker().invoke\");\n\t\t\tgenerateMethodArguments(code, insn, 0, callMthNode);\n\t\t\tcode.add(\" /* invoke-custom */\");\n\t\t}\n\t}\n\n\t// FIXME: add 'this' for equals methods in scope\n\tprivate boolean needInvokeArg(InsnArg arg) {\n\t\tif (arg.isAnyThis()) {\n\t\t\tif (arg.isThis()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tClassNode clsNode = mth.root().resolveClass(arg.getType());\n\t\t\tif (clsNode != null && clsNode.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate void makeInvokeLambda(ICodeWriter code, InvokeCustomNode customNode) throws CodegenException {\n\t\tif (customNode.isUseRef()) {\n\t\t\tmakeRefLambda(code, customNode);\n\t\t\treturn;\n\t\t}\n\t\tif (fallback || !customNode.isInlineInsn()) {\n\t\t\tmakeSimpleLambda(code, customNode);\n\t\t\treturn;\n\t\t}\n\t\tMethodNode callMth = (MethodNode) customNode.getCallInsn().get(AType.METHOD_DETAILS);\n\t\tmakeInlinedLambdaMethod(code, customNode, callMth);\n\t}\n\n\tprivate void makeRefLambda(ICodeWriter code, InvokeCustomNode customNode) throws CodegenException {\n\t\tInsnNode callInsn = customNode.getCallInsn();\n\t\tif (callInsn instanceof ConstructorInsn) {\n\t\t\tMethodInfo callMth = ((ConstructorInsn) callInsn).getCallMth();\n\t\t\tuseClass(code, callMth.getDeclClass());\n\t\t\tcode.add(\"::new\");\n\t\t\treturn;\n\t\t}\n\t\tif (callInsn instanceof InvokeNode) {\n\t\t\tInvokeNode invokeInsn = (InvokeNode) callInsn;\n\t\t\tMethodInfo callMth = invokeInsn.getCallMth();\n\t\t\tif (customNode.getHandleType() == MethodHandleType.INVOKE_STATIC) {\n\t\t\t\tuseClass(code, callMth.getDeclClass());\n\t\t\t} else {\n\t\t\t\taddArg(code, customNode.getArg(0));\n\t\t\t}\n\t\t\tcode.add(\"::\").add(callMth.getAlias());\n\t\t}\n\t}\n\n\tprivate void makeSimpleLambda(ICodeWriter code, InvokeCustomNode customNode) {\n\t\ttry {\n\t\t\tInsnNode callInsn = customNode.getCallInsn();\n\t\t\tMethodInfo implMthInfo = customNode.getImplMthInfo();\n\t\t\tint implArgsCount = implMthInfo.getArgsCount();\n\t\t\tif (implArgsCount == 0) {\n\t\t\t\tcode.add(\"()\");\n\t\t\t} else {\n\t\t\t\tcode.add('(');\n\t\t\t\tint callArgsCount = callInsn.getArgsCount();\n\t\t\t\tint startArg = callArgsCount - implArgsCount;\n\t\t\t\tif (customNode.getHandleType() != MethodHandleType.INVOKE_STATIC\n\t\t\t\t\t\t&& customNode.getArgsCount() > 0\n\t\t\t\t\t\t&& customNode.getArg(0).isThis()) {\n\t\t\t\t\tcallInsn.getArg(0).add(AFlag.THIS);\n\t\t\t\t}\n\t\t\t\tif (startArg >= 0) {\n\t\t\t\t\tfor (int i = startArg; i < callArgsCount; i++) {\n\t\t\t\t\t\tif (i != startArg) {\n\t\t\t\t\t\t\tcode.add(\", \");\n\t\t\t\t\t\t}\n\t\t\t\t\t\taddArg(code, callInsn.getArg(i));\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tcode.add(\"/* ERROR: \" + startArg + \" */\");\n\t\t\t\t}\n\t\t\t\tcode.add(')');\n\t\t\t}\n\t\t\tcode.add(\" -> {\");\n\t\t\tif (fallback) {\n\t\t\t\tcode.add(\" // \").add(implMthInfo.toString());\n\t\t\t}\n\t\t\tcode.incIndent();\n\t\t\tcode.startLine();\n\t\t\tif (!implMthInfo.getReturnType().isVoid()) {\n\t\t\t\tcode.add(\"return \");\n\t\t\t}\n\t\t\tmakeInsn(callInsn, code, Flags.INLINE);\n\t\t\tcode.add(\";\");\n\n\t\t\tcode.decIndent();\n\t\t\tcode.startLine('}');\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to generate 'invoke-custom' instruction: \" + e.getMessage(), e);\n\t\t}\n\t}\n\n\tprivate void makeInlinedLambdaMethod(ICodeWriter code, InvokeCustomNode customNode, MethodNode callMth) throws CodegenException {\n\t\tMethodGen callMthGen = new MethodGen(mgen.getClassGen(), callMth);\n\t\tNameGen nameGen = callMthGen.getNameGen();\n\t\tnameGen.inheritUsedNames(this.mgen.getNameGen());\n\n\t\tList<ArgType> implArgs = customNode.getImplMthInfo().getArgumentsTypes();\n\t\tList<RegisterArg> callArgs = callMth.getArgRegs();\n\t\tif (implArgs.isEmpty()) {\n\t\t\tcode.add(\"()\");\n\t\t} else {\n\t\t\tint callArgsCount = callArgs.size();\n\t\t\tint startArg = callArgsCount - implArgs.size();\n\t\t\tif (callArgsCount - startArg > 1) {\n\t\t\t\tcode.add('(');\n\t\t\t}\n\t\t\tfor (int i = startArg; i < callArgsCount; i++) {\n\t\t\t\tif (i != startArg) {\n\t\t\t\t\tcode.add(\", \");\n\t\t\t\t}\n\t\t\t\tCodeVar argCodeVar = callArgs.get(i).getSVar().getCodeVar();\n\t\t\t\tdefVar(code, argCodeVar);\n\t\t\t}\n\t\t\tif (callArgsCount - startArg > 1) {\n\t\t\t\tcode.add(')');\n\t\t\t}\n\t\t}\n\t\t// force set external arg names into call method args\n\t\tint extArgsCount = customNode.getArgsCount();\n\t\tint startArg = customNode.getHandleType() == MethodHandleType.INVOKE_STATIC ? 0 : 1; // skip 'this' arg\n\t\tint callArg = 0;\n\t\tfor (int i = startArg; i < extArgsCount; i++) {\n\t\t\tInsnArg arg = customNode.getArg(i);\n\t\t\tif (arg.isRegister()) {\n\t\t\t\tRegisterArg extArg = (RegisterArg) arg;\n\t\t\t\tRegisterArg callRegArg = callArgs.get(callArg++);\n\t\t\t\tcallRegArg.getSVar().setCodeVar(extArg.getSVar().getCodeVar());\n\t\t\t} else {\n\t\t\t\tthrow new JadxRuntimeException(\"Unexpected argument type in lambda call: \" + arg.getClass().getSimpleName());\n\t\t\t}\n\t\t}\n\t\tcode.add(\" -> {\");\n\t\tcode.incIndent();\n\t\tcallMthGen.addInstructions(code);\n\n\t\tcode.decIndent();\n\t\tcode.startLine('}');\n\t}\n\n\tprivate void callSuper(ICodeWriter code, MethodInfo callMth) {\n\t\tClassInfo superCallCls = getClassForSuperCall(callMth);\n\t\tif (superCallCls == null) {\n\t\t\t// unknown class, add comment to keep that info\n\t\t\tcode.add(\"super/*\").add(callMth.getDeclClass().getFullName()).add(\"*/\");\n\t\t\treturn;\n\t\t}\n\t\tClassInfo curClass = mth.getParentClass().getClassInfo();\n\t\tif (superCallCls.equals(curClass)) {\n\t\t\tcode.add(\"super\");\n\t\t\treturn;\n\t\t}\n\t\t// use custom class\n\t\tuseClass(code, superCallCls);\n\t\tcode.add(\".super\");\n\t}\n\n\t/**\n\t * Search call class in super types of this\n\t * and all parent classes (needed for inlined synthetic calls)\n\t */\n\t@Nullable\n\tprivate ClassInfo getClassForSuperCall(MethodInfo callMth) {\n\t\tArgType declClsType = callMth.getDeclClass().getType();\n\t\tClassNode parentNode = mth.getParentClass();\n\t\twhile (true) {\n\t\t\tClassInfo parentCls = parentNode.getClassInfo();\n\t\t\tif (ArgType.isInstanceOf(root, parentCls.getType(), declClsType)) {\n\t\t\t\treturn parentCls;\n\t\t\t}\n\t\t\tClassNode nextParent = parentNode.getParentClass();\n\t\t\tif (nextParent == parentNode) {\n\t\t\t\t// no parent, class not found\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tparentNode = nextParent;\n\t\t}\n\t}\n\n\tvoid generateMethodArguments(ICodeWriter code, BaseInvokeNode insn, int startArgNum,\n\t\t\t@Nullable MethodNode mthNode) throws CodegenException {\n\t\tint k = startArgNum;\n\t\tif (mthNode != null && mthNode.contains(AFlag.SKIP_FIRST_ARG)) {\n\t\t\tk++;\n\t\t}\n\t\tint argsCount = insn.getArgsCount();\n\t\tcode.add('(');\n\t\tSkipMethodArgsAttr skipAttr = mthNode == null ? null : mthNode.get(AType.SKIP_MTH_ARGS);\n\t\tboolean firstArg = true;\n\t\tif (k < argsCount) {\n\t\t\tfor (int i = k; i < argsCount; i++) {\n\t\t\t\tInsnArg arg = insn.getArg(i);\n\t\t\t\tif (arg.contains(AFlag.SKIP_ARG) || (skipAttr != null && skipAttr.isSkip(i - startArgNum))) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (firstArg) {\n\t\t\t\t\tfirstArg = false;\n\t\t\t\t} else {\n\t\t\t\t\tcode.add(\", \");\n\t\t\t\t}\n\t\t\t\tif (i == argsCount - 1 && processVarArg(code, insn, arg)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\taddArg(code, arg, false);\n\t\t\t}\n\t\t}\n\t\tcode.add(')');\n\t}\n\n\t/**\n\t * Expand varArgs from filled array.\n\t */\n\tprivate boolean processVarArg(ICodeWriter code, BaseInvokeNode invokeInsn, InsnArg lastArg) throws CodegenException {\n\t\tif (!invokeInsn.contains(AFlag.VARARG_CALL)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!lastArg.getType().isArray() || !lastArg.isInsnWrap()) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnNode insn = ((InsnWrapArg) lastArg).getWrapInsn();\n\t\tif (insn.getType() != InsnType.FILLED_NEW_ARRAY) {\n\t\t\treturn false;\n\t\t}\n\t\tint count = insn.getArgsCount();\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tInsnArg elemArg = insn.getArg(i);\n\t\t\taddArg(code, elemArg, false);\n\t\t\tif (i < count - 1) {\n\t\t\t\tcode.add(\", \");\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate void makeTernary(TernaryInsn insn, ICodeWriter code, Set<Flags> state) throws CodegenException {\n\t\tboolean wrap = state.contains(Flags.BODY_ONLY);\n\t\tif (wrap) {\n\t\t\tcode.add('(');\n\t\t}\n\t\tInsnArg first = insn.getArg(0);\n\t\tInsnArg second = insn.getArg(1);\n\t\tConditionGen condGen = new ConditionGen(this);\n\t\tif (first.isTrue() && second.isFalse()) {\n\t\t\tcondGen.add(code, insn.getCondition());\n\t\t} else {\n\t\t\tcondGen.wrap(code, insn.getCondition());\n\t\t\tcode.add(\" ? \");\n\t\t\taddArg(code, first, false);\n\t\t\tcode.add(\" : \");\n\t\t\taddArg(code, second, false);\n\t\t}\n\t\tif (wrap) {\n\t\t\tcode.add(')');\n\t\t}\n\t}\n\n\tprivate void makeArith(ArithNode insn, ICodeWriter code, Set<Flags> state) throws CodegenException {\n\t\tif (insn.contains(AFlag.ARITH_ONEARG)) {\n\t\t\tmakeArithOneArg(insn, code);\n\t\t\treturn;\n\t\t}\n\t\t// wrap insn in brackets for save correct operation order\n\t\tboolean wrap = state.contains(Flags.BODY_ONLY) && !insn.contains(AFlag.DONT_WRAP);\n\t\tif (wrap) {\n\t\t\tcode.add('(');\n\t\t}\n\t\taddArg(code, insn.getArg(0));\n\t\tcode.add(' ');\n\t\tcode.add(insn.getOp().getSymbol());\n\t\tcode.add(' ');\n\t\taddArg(code, insn.getArg(1));\n\t\tif (wrap) {\n\t\t\tcode.add(')');\n\t\t}\n\t}\n\n\tprivate void makeArithOneArg(ArithNode insn, ICodeWriter code) throws CodegenException {\n\t\tArithOp op = insn.getOp();\n\t\tInsnArg resArg = insn.getArg(0);\n\t\tInsnArg arg = insn.getArg(1);\n\n\t\t// \"++\" or \"--\"\n\t\tif (arg.isLiteral() && (op == ArithOp.ADD || op == ArithOp.SUB)) {\n\t\t\tLiteralArg lit = (LiteralArg) arg;\n\t\t\tif (lit.getLiteral() == 1 && lit.isInteger()) {\n\t\t\t\taddArg(code, resArg, false);\n\t\t\t\tString opSymbol = op.getSymbol();\n\t\t\t\tcode.add(opSymbol).add(opSymbol);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// +=, -=, ...\n\t\taddArg(code, resArg, false);\n\t\tcode.add(' ').add(op.getSymbol()).add(\"= \");\n\t\taddArg(code, arg, false);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/MethodGen.java",
    "content": "package jadx.core.codegen;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.CommentsLevel;\nimport jadx.api.DecompilationMode;\nimport jadx.api.ICodeWriter;\nimport jadx.api.JadxArgs;\nimport jadx.api.args.IntegerFormat;\nimport jadx.api.metadata.annotations.InsnCodeOffset;\nimport jadx.api.metadata.annotations.VarNode;\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationMethodParamsAttr;\nimport jadx.core.Consts;\nimport jadx.core.Jadx;\nimport jadx.core.codegen.utils.CodeGenUtils;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.DecompileModeOverrideAttr;\nimport jadx.core.dex.attributes.nodes.JadxError;\nimport jadx.core.dex.attributes.nodes.JumpInfo;\nimport jadx.core.dex.attributes.nodes.MethodOverrideAttr;\nimport jadx.core.dex.attributes.nodes.MethodReplaceAttr;\nimport jadx.core.dex.attributes.nodes.SkipMethodArgsAttr;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.instructions.ConstStringNode;\nimport jadx.core.dex.instructions.IfNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.CodeVar;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.trycatch.CatchAttr;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.dex.visitors.DepthTraversal;\nimport jadx.core.dex.visitors.IDexTreeVisitor;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.CodegenException;\nimport jadx.core.utils.exceptions.JadxOverflowException;\n\nimport static jadx.core.codegen.MethodGen.FallbackOption.BLOCK_DUMP;\nimport static jadx.core.codegen.MethodGen.FallbackOption.COMMENTED_DUMP;\nimport static jadx.core.codegen.MethodGen.FallbackOption.FALLBACK_MODE;\n\npublic class MethodGen {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(MethodGen.class);\n\n\tprivate final MethodNode mth;\n\tprivate final ClassGen classGen;\n\tprivate final AnnotationGen annotationGen;\n\tprivate final NameGen nameGen;\n\n\tpublic MethodGen(ClassGen classGen, MethodNode mth) {\n\t\tthis.mth = mth;\n\t\tthis.classGen = classGen;\n\t\tthis.annotationGen = classGen.getAnnotationGen();\n\t\tthis.nameGen = new NameGen(mth, classGen);\n\t}\n\n\tpublic ClassGen getClassGen() {\n\t\treturn classGen;\n\t}\n\n\tpublic NameGen getNameGen() {\n\t\treturn nameGen;\n\t}\n\n\tpublic MethodNode getMethodNode() {\n\t\treturn mth;\n\t}\n\n\tpublic boolean addDefinition(ICodeWriter code) {\n\t\tif (mth.getMethodInfo().isClassInit()) {\n\t\t\tcode.startLine();\n\t\t\tcode.attachDefinition(mth);\n\t\t\tcode.add(\"static\");\n\t\t\treturn true;\n\t\t}\n\t\tif (mth.contains(AFlag.ANONYMOUS_CONSTRUCTOR)) {\n\t\t\t// don't add method name and arguments\n\t\t\tcode.startLine();\n\t\t\tcode.attachDefinition(mth);\n\t\t\treturn false;\n\t\t}\n\t\tif (Consts.DEBUG_USAGE) {\n\t\t\tClassGen.addMthUsageInfo(code, mth);\n\t\t}\n\t\taddOverrideAnnotation(code, mth);\n\t\tannotationGen.addForMethod(code, mth);\n\n\t\tAccessInfo clsAccFlags = mth.getParentClass().getAccessFlags();\n\t\tAccessInfo ai = mth.getAccessFlags();\n\t\t// don't add 'abstract' and 'public' to methods in interface\n\t\tif (clsAccFlags.isInterface()) {\n\t\t\tai = ai.remove(AccessFlags.ABSTRACT);\n\t\t\tai = ai.remove(AccessFlags.PUBLIC);\n\t\t}\n\t\t// don't add 'public' for annotations\n\t\tif (clsAccFlags.isAnnotation()) {\n\t\t\tai = ai.remove(AccessFlags.PUBLIC);\n\t\t}\n\t\tif (mth.getMethodInfo().hasAlias() && !ai.isConstructor()) {\n\t\t\tCodeGenUtils.addRenamedComment(code, mth, mth.getName());\n\t\t}\n\t\tif (mth.contains(AFlag.INCONSISTENT_CODE) && mth.checkCommentsLevel(CommentsLevel.ERROR)) {\n\t\t\tcode.startLine(\"/*\");\n\t\t\tcode.incIndent();\n\t\t\tcode.startLine(\"Code decompiled incorrectly, please refer to instructions dump.\");\n\t\t\tif (!mth.root().getArgs().isShowInconsistentCode()) {\n\t\t\t\tif (code.isMetadataSupported()) {\n\t\t\t\t\tcode.startLine(\"To view partially-correct code enable 'Show inconsistent code' option in preferences\");\n\t\t\t\t} else {\n\t\t\t\t\tcode.startLine(\"To view partially-correct add '--show-bad-code' argument\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tcode.decIndent();\n\t\t\tcode.startLine(\"*/\");\n\t\t}\n\n\t\tcode.startLineWithNum(mth.getSourceLine());\n\t\tcode.add(ai.makeString(mth.checkCommentsLevel(CommentsLevel.INFO)));\n\t\tif (clsAccFlags.isInterface() && !mth.isNoCode() && !mth.getAccessFlags().isStatic()) {\n\t\t\t// add 'default' for method with code in interface\n\t\t\tcode.add(\"default \");\n\t\t}\n\n\t\tif (classGen.addGenericTypeParameters(code, mth.getTypeParameters(), false)) {\n\t\t\tcode.add(' ');\n\t\t}\n\t\tif (ai.isConstructor()) {\n\t\t\tcode.attachDefinition(mth);\n\t\t\tcode.add(classGen.getClassNode().getShortName()); // constructor\n\t\t} else {\n\t\t\tclassGen.useType(code, mth.getReturnType());\n\t\t\tcode.add(' ');\n\t\t\tMethodNode defMth = getMethodForDefinition();\n\t\t\tcode.attachDefinition(defMth);\n\t\t\tcode.add(defMth.getAlias());\n\t\t}\n\t\tcode.add('(');\n\t\taddMethodArguments(code);\n\t\tcode.add(')');\n\n\t\tannotationGen.addThrows(mth, code);\n\n\t\t// add default value for annotation class\n\t\tif (mth.getParentClass().getAccessFlags().isAnnotation()) {\n\t\t\tEncodedValue def = annotationGen.getAnnotationDefaultValue(mth);\n\t\t\tif (def != null) {\n\t\t\t\tcode.add(\" default \");\n\t\t\t\tannotationGen.encodeValue(mth.root(), code, def);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate MethodNode getMethodForDefinition() {\n\t\tMethodReplaceAttr replaceAttr = mth.get(AType.METHOD_REPLACE);\n\t\tif (replaceAttr != null) {\n\t\t\treturn replaceAttr.getReplaceMth();\n\t\t}\n\t\treturn mth;\n\t}\n\n\tprivate void addOverrideAnnotation(ICodeWriter code, MethodNode mth) {\n\t\tMethodOverrideAttr overrideAttr = mth.get(AType.METHOD_OVERRIDE);\n\t\tif (overrideAttr == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (!overrideAttr.getBaseMethods().contains(mth)) {\n\t\t\tcode.startLine(\"@Override\");\n\t\t\tif (mth.checkCommentsLevel(CommentsLevel.INFO)) {\n\t\t\t\tcode.add(\" // \");\n\t\t\t\tcode.add(Utils.listToString(overrideAttr.getOverrideList(), \", \",\n\t\t\t\t\t\tmd -> md.getMethodInfo().getDeclClass().getAliasFullName()));\n\t\t\t}\n\t\t}\n\t\tif (Consts.DEBUG) {\n\t\t\tcode.startLine(\"// related by override: \");\n\t\t\tcode.add(Utils.listToString(overrideAttr.getRelatedMthNodes(), \", \", m -> m.getParentClass().getFullName()));\n\t\t}\n\t}\n\n\tprivate void addMethodArguments(ICodeWriter code) {\n\t\tList<RegisterArg> args = mth.getArgRegs();\n\t\tAnnotationMethodParamsAttr paramsAnnotation = mth.get(JadxAttrType.ANNOTATION_MTH_PARAMETERS);\n\t\tint argNum = -1;\n\t\tint lastArgNum = args.size() - 1;\n\t\tboolean first = true;\n\t\tfor (RegisterArg mthArg : args) {\n\t\t\targNum++;\n\t\t\tif (SkipMethodArgsAttr.isSkip(mth, argNum)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (first) {\n\t\t\t\tfirst = false;\n\t\t\t} else {\n\t\t\t\tcode.add(\", \");\n\t\t\t}\n\t\t\tSSAVar ssaVar = mthArg.getSVar();\n\t\t\tCodeVar var;\n\t\t\tif (ssaVar == null) {\n\t\t\t\t// abstract or interface methods\n\t\t\t\tvar = CodeVar.fromMthArg(mthArg, classGen.isFallbackMode());\n\t\t\t} else {\n\t\t\t\tvar = ssaVar.getCodeVar();\n\t\t\t}\n\n\t\t\t// add argument annotation\n\t\t\tif (paramsAnnotation != null) {\n\t\t\t\tannotationGen.addForParameter(code, paramsAnnotation, argNum);\n\t\t\t}\n\t\t\tif (var.isFinal()) {\n\t\t\t\tcode.add(\"final \");\n\t\t\t}\n\t\t\tArgType argType;\n\t\t\tArgType varType = var.getType();\n\t\t\tif (varType == null || varType == ArgType.UNKNOWN) {\n\t\t\t\t// occur on decompilation errors\n\t\t\t\targType = mthArg.getInitType();\n\t\t\t} else {\n\t\t\t\targType = varType;\n\t\t\t}\n\t\t\tif (argNum == lastArgNum && mth.getAccessFlags().isVarArgs()) {\n\t\t\t\t// change last array argument to varargs\n\t\t\t\tif (argType.isArray()) {\n\t\t\t\t\tArgType elType = argType.getArrayElement();\n\t\t\t\t\tclassGen.useType(code, elType);\n\t\t\t\t\tcode.add(\"...\");\n\t\t\t\t} else {\n\t\t\t\t\tmth.addWarnComment(\"Last argument in varargs method is not array: \" + var);\n\t\t\t\t\tclassGen.useType(code, argType);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tclassGen.useType(code, argType);\n\t\t\t}\n\t\t\tcode.add(' ');\n\t\t\tString varName = nameGen.assignArg(var);\n\t\t\tif (code.isMetadataSupported() && ssaVar != null /* for fallback mode */) {\n\t\t\t\tcode.attachDefinition(VarNode.get(mth, var));\n\t\t\t}\n\t\t\tcode.add(varName);\n\t\t}\n\t}\n\n\tpublic void addInstructions(ICodeWriter code) throws CodegenException {\n\t\tJadxArgs args = mth.root().getArgs();\n\t\tDecompileModeOverrideAttr modeOverrideAttr = mth.getTopParentClass().get(AType.DECOMPILE_MODE_OVERRIDE);\n\t\tDecompilationMode mode;\n\t\tif (modeOverrideAttr != null) {\n\t\t\tmode = modeOverrideAttr.getMode();\n\t\t} else {\n\t\t\tmode = args.getDecompilationMode();\n\t\t}\n\t\tswitch (mode) {\n\t\t\tcase AUTO:\n\t\t\t\tif (classGen.isFallbackMode() || mth.getRegion() == null) {\n\t\t\t\t\t// TODO: try simple mode first\n\t\t\t\t\tdumpInstructions(code);\n\t\t\t\t} else {\n\t\t\t\t\taddRegionInsns(code);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase RESTRUCTURE:\n\t\t\t\taddRegionInsns(code);\n\t\t\t\tbreak;\n\n\t\t\tcase SIMPLE:\n\t\t\t\taddSimpleMethodCode(code);\n\t\t\t\tbreak;\n\n\t\t\tcase FALLBACK:\n\t\t\t\taddFallbackMethodCode(code, FALLBACK_MODE);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tpublic void addRegionInsns(ICodeWriter code) throws CodegenException {\n\t\ttry {\n\t\t\tRegionGen regionGen = new RegionGen(this);\n\t\t\tregionGen.makeRegion(code, mth.getRegion());\n\t\t} catch (StackOverflowError | BootstrapMethodError e) {\n\t\t\tmth.addError(\"Method code generation error\", new JadxOverflowException(\"StackOverflow\"));\n\t\t\tCodeGenUtils.addErrors(code, mth);\n\t\t\tdumpInstructions(code);\n\t\t} catch (Exception e) {\n\t\t\tif (mth.getParentClass().getTopParentClass().contains(AFlag.RESTART_CODEGEN)) {\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t\tmth.addError(\"Method code generation error\", e);\n\t\t\tCodeGenUtils.addErrors(code, mth);\n\t\t\tdumpInstructions(code);\n\t\t}\n\t}\n\n\tprivate void addSimpleMethodCode(ICodeWriter code) {\n\t\tif (mth.getBasicBlocks() == null) {\n\t\t\tcode.startLine(\"// Blocks not ready for simple mode, using fallback\");\n\t\t\taddFallbackMethodCode(code, FALLBACK_MODE);\n\t\t\treturn;\n\t\t}\n\t\tJadxArgs args = mth.root().getArgs();\n\t\tICodeWriter tmpCode = args.getCodeWriterProvider().apply(args);\n\t\ttry {\n\t\t\ttmpCode.setIndent(code.getIndent());\n\t\t\tgenerateSimpleCode(tmpCode);\n\t\t\tcode.add(tmpCode);\n\t\t} catch (Exception e) {\n\t\t\tmth.addError(\"Simple mode code generation failed\", e);\n\t\t\tCodeGenUtils.addError(code, \"Simple mode code generation failed\", e);\n\t\t\tdumpInstructions(code);\n\t\t}\n\t}\n\n\tprivate void generateSimpleCode(ICodeWriter code) throws CodegenException {\n\t\tSimpleModeHelper helper = new SimpleModeHelper(mth);\n\t\tList<BlockNode> blocks = helper.prepareBlocks();\n\t\tInsnGen insnGen = new InsnGen(this, true);\n\t\tfor (BlockNode block : blocks) {\n\t\t\tif (block.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (helper.isNeedStartLabel(block)) {\n\t\t\t\tcode.decIndent();\n\t\t\t\tcode.startLine(getLabelName(block)).add(':');\n\t\t\t\tcode.incIndent();\n\t\t\t}\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tif (!insn.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\t\tif (insn.getResult() != null) {\n\t\t\t\t\t\tCodeVar codeVar = insn.getResult().getSVar().getCodeVar();\n\t\t\t\t\t\tif (!codeVar.isDeclared()) {\n\t\t\t\t\t\t\tinsn.add(AFlag.DECLARE_VAR);\n\t\t\t\t\t\t\tcodeVar.setDeclared(true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tInsnCodeOffset.attach(code, insn);\n\t\t\t\t\tinsnGen.makeInsn(insn, code);\n\t\t\t\t\taddCatchComment(code, insn, false);\n\t\t\t\t\tCodeGenUtils.addCodeComments(code, mth, insn);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (helper.isNeedEndGoto(block)) {\n\t\t\t\tcode.startLine(\"goto \").add(getLabelName(block.getSuccessors().get(0)));\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void dumpInstructions(ICodeWriter code) {\n\t\tif (mth.checkCommentsLevel(CommentsLevel.ERROR)) {\n\t\t\tcode.startLine(\"/*\");\n\t\t\taddFallbackMethodCode(code, COMMENTED_DUMP);\n\t\t\tcode.startLine(\"*/\");\n\t\t}\n\t\tcode.startLine(\"throw new UnsupportedOperationException(\\\"Method not decompiled: \")\n\t\t\t\t.add(mth.getParentClass().getClassInfo().getAliasFullName())\n\t\t\t\t.add('.')\n\t\t\t\t.add(mth.getAlias())\n\t\t\t\t.add('(')\n\t\t\t\t.add(Utils.listToString(mth.getMethodInfo().getArgumentsTypes()))\n\t\t\t\t.add(\"):\")\n\t\t\t\t.add(mth.getMethodInfo().getReturnType().toString())\n\t\t\t\t.add(\"\\\");\");\n\t}\n\n\tpublic void addFallbackMethodCode(ICodeWriter code, FallbackOption fallbackOption) {\n\t\tif (fallbackOption == COMMENTED_DUMP && mth.getCommentsLevel() != CommentsLevel.DEBUG) {\n\t\t\tlong insnCountEstimate = mth.getInsnsCount();\n\t\t\tif (insnCountEstimate > 200) {\n\t\t\t\tcode.incIndent();\n\t\t\t\tcode.startLine(\"Method dump skipped, instruction units count: \" + insnCountEstimate);\n\t\t\t\tif (code.isMetadataSupported()) {\n\t\t\t\t\tcode.startLine(\"To view this dump change 'Code comments level' option to 'DEBUG'\");\n\t\t\t\t} else {\n\t\t\t\t\tcode.startLine(\"To view this dump add '--comments-level debug' option\");\n\t\t\t\t}\n\t\t\t\tcode.decIndent();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (fallbackOption != FALLBACK_MODE) {\n\t\t\tList<JadxError> errors = mth.getAll(AType.JADX_ERROR); // preserve error before unload\n\t\t\ttry {\n\t\t\t\t// load original instructions\n\t\t\t\tmth.unload();\n\t\t\t\tmth.load();\n\t\t\t\tfor (IDexTreeVisitor visitor : Jadx.getFallbackPassesList()) {\n\t\t\t\t\tDepthTraversal.visit(visitor, mth);\n\t\t\t\t}\n\t\t\t\terrors.forEach(err -> mth.addAttr(AType.JADX_ERROR, err));\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Error reload instructions in fallback mode:\", e);\n\t\t\t\tcode.startLine(\"// Can't load method instructions: \" + e.getMessage());\n\t\t\t\treturn;\n\t\t\t} finally {\n\t\t\t\terrors.forEach(err -> mth.addAttr(AType.JADX_ERROR, err));\n\t\t\t}\n\t\t}\n\t\tInsnNode[] insnArr = mth.getInstructions();\n\t\tif (insnArr == null) {\n\t\t\tcode.startLine(\"// Can't load method instructions.\");\n\t\t\treturn;\n\t\t}\n\t\tcode.incIndent();\n\t\tif (mth.getThisArg() != null) {\n\t\t\tcode.startLine(nameGen.useArg(mth.getThisArg())).add(\" = this;\");\n\t\t}\n\t\taddFallbackInsns(code, mth, insnArr, fallbackOption);\n\t\tcode.decIndent();\n\t}\n\n\tpublic enum FallbackOption {\n\t\tFALLBACK_MODE,\n\t\tBLOCK_DUMP,\n\t\tCOMMENTED_DUMP\n\t}\n\n\tpublic static void addFallbackInsns(ICodeWriter code, MethodNode mth, InsnNode[] insnArr, FallbackOption option) {\n\t\tint startIndent = code.getIndent();\n\t\tMethodGen methodGen = getFallbackMethodGen(mth);\n\t\tInsnGen insnGen = new InsnGen(methodGen, true);\n\t\tInsnNode prevInsn = null;\n\t\tfor (InsnNode insn : insnArr) {\n\t\t\tif (insn == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmethodGen.dumpInsn(code, insnGen, option, startIndent, prevInsn, insn);\n\t\t\tprevInsn = insn;\n\t\t}\n\t}\n\n\tprivate boolean dumpInsn(ICodeWriter code, InsnGen insnGen, FallbackOption option, int startIndent,\n\t\t\t@Nullable InsnNode prevInsn, InsnNode insn) {\n\t\tif (insn.contains(AType.JADX_ERROR)) {\n\t\t\tfor (JadxError error : insn.getAll(AType.JADX_ERROR)) {\n\t\t\t\tcode.startLine(\"// \").add(error.getError());\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tif (option != BLOCK_DUMP && needLabel(insn, prevInsn)) {\n\t\t\tcode.decIndent();\n\t\t\tcode.startLine(getLabelName(insn.getOffset()) + ':');\n\t\t\tcode.incIndent();\n\t\t}\n\t\tif (insn.getType() == InsnType.NOP) {\n\t\t\treturn true;\n\t\t}\n\t\ttry {\n\t\t\tboolean escapeComment = isCommentEscapeNeeded(insn, option);\n\t\t\tif (escapeComment) {\n\t\t\t\tcode.decIndent();\n\t\t\t\tcode.startLine(\"*/\");\n\t\t\t\tcode.startLine(\"//  \");\n\t\t\t} else {\n\t\t\t\tcode.startLineWithNum(insn.getSourceLine());\n\t\t\t}\n\t\t\tInsnCodeOffset.attach(code, insn);\n\t\t\tRegisterArg resArg = insn.getResult();\n\t\t\tif (resArg != null) {\n\t\t\t\tArgType varType = resArg.getInitType();\n\t\t\t\tif (varType.isTypeKnown()) {\n\t\t\t\t\tcode.add(varType.toString()).add(' ');\n\t\t\t\t}\n\t\t\t}\n\t\t\tinsnGen.makeInsn(insn, code, InsnGen.Flags.INLINE);\n\t\t\tif (escapeComment) {\n\t\t\t\tcode.startLine(\"/*\");\n\t\t\t\tcode.incIndent();\n\t\t\t}\n\t\t\taddCatchComment(code, insn, true);\n\t\t\tCodeGenUtils.addCodeComments(code, mth, insn);\n\t\t} catch (Exception e) {\n\t\t\tLOG.debug(\"Error generate fallback instruction: \", e.getCause());\n\t\t\tcode.setIndent(startIndent);\n\t\t\tcode.startLine(\"// error: \" + insn);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate void addCatchComment(ICodeWriter code, InsnNode insn, boolean raw) {\n\t\tCatchAttr catchAttr = insn.get(AType.EXC_CATCH);\n\t\tif (catchAttr == null) {\n\t\t\treturn;\n\t\t}\n\t\tcode.add(\"     // Catch:\");\n\t\tfor (ExceptionHandler handler : catchAttr.getHandlers()) {\n\t\t\tcode.add(' ');\n\t\t\tclassGen.useClass(code, handler.getArgType());\n\t\t\tcode.add(\" -> \");\n\t\t\tif (raw) {\n\t\t\t\tcode.add(getLabelName(handler.getHandlerOffset()));\n\t\t\t} else {\n\t\t\t\tcode.add(getLabelName(handler.getHandlerBlock()));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean isCommentEscapeNeeded(InsnNode insn, FallbackOption option) {\n\t\tif (option == COMMENTED_DUMP) {\n\t\t\tif (insn.getType() == InsnType.CONST_STR) {\n\t\t\t\tString str = ((ConstStringNode) insn).getString();\n\t\t\t\treturn str.contains(\"*/\");\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean needLabel(InsnNode insn, InsnNode prevInsn) {\n\t\tif (insn.contains(AType.EXC_HANDLER)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (insn.contains(AType.JUMP)) {\n\t\t\t// don't add label for ifs else branch\n\t\t\tif (prevInsn != null && prevInsn.getType() == InsnType.IF) {\n\t\t\t\tList<JumpInfo> jumps = insn.getAll(AType.JUMP);\n\t\t\t\tif (jumps.size() == 1) {\n\t\t\t\t\tJumpInfo jump = jumps.get(0);\n\t\t\t\t\tif (jump.getSrc() == prevInsn.getOffset() && jump.getDest() == insn.getOffset()) {\n\t\t\t\t\t\tint target = ((IfNode) prevInsn).getTarget();\n\t\t\t\t\t\treturn insn.getOffset() == target;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Return fallback variant of method codegen\n\t */\n\tpublic static MethodGen getFallbackMethodGen(MethodNode mth) {\n\t\tClassGen clsGen = new ClassGen(mth.getParentClass(), null, false, true, true, IntegerFormat.AUTO);\n\t\treturn new MethodGen(clsGen, mth);\n\t}\n\n\tpublic static String getLabelName(BlockNode block) {\n\t\treturn String.format(\"L%d\", block.getCId());\n\t}\n\n\tpublic static String getLabelName(IfNode insn) {\n\t\tBlockNode thenBlock = insn.getThenBlock();\n\t\tif (thenBlock != null) {\n\t\t\treturn getLabelName(thenBlock);\n\t\t}\n\t\treturn getLabelName(insn.getTarget());\n\t}\n\n\tpublic static String getLabelName(int offset) {\n\t\tif (offset < 0) {\n\t\t\treturn String.format(\"LB_%x\", -offset);\n\t\t}\n\t\treturn String.format(\"L%x\", offset);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/NameGen.java",
    "content": "package jadx.core.codegen;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport jadx.core.deobf.NameMapper;\nimport jadx.core.dex.attributes.nodes.LoopLabelAttr;\nimport jadx.core.dex.instructions.args.CodeVar;\nimport jadx.core.dex.instructions.args.NamedArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic class NameGen {\n\tprivate final MethodNode mth;\n\tprivate final boolean fallback;\n\tprivate final Set<String> varNames = new HashSet<>();\n\n\tpublic NameGen(MethodNode mth, ClassGen classGen) {\n\t\tthis.mth = mth;\n\t\tthis.fallback = classGen.isFallbackMode();\n\t\tNameGen outerNameGen = classGen.getOuterNameGen();\n\t\tif (outerNameGen != null) {\n\t\t\tinheritUsedNames(outerNameGen);\n\t\t}\n\t\taddNamesUsedInClass();\n\t}\n\n\tpublic void inheritUsedNames(NameGen otherNameGen) {\n\t\tvarNames.addAll(otherNameGen.varNames);\n\t}\n\n\tprivate void addNamesUsedInClass() {\n\t\tClassNode parentClass = mth.getParentClass();\n\t\tfor (FieldNode field : parentClass.getFields()) {\n\t\t\tif (field.isStatic()) {\n\t\t\t\tvarNames.add(field.getAlias());\n\t\t\t}\n\t\t}\n\t\tfor (ClassNode innerClass : parentClass.getInnerClasses()) {\n\t\t\tvarNames.add(innerClass.getClassInfo().getAliasShortName());\n\t\t}\n\t\t// add all root package names to avoid collisions with full class names\n\t\tvarNames.addAll(mth.root().getCacheStorage().getRootPkgs());\n\t}\n\n\tpublic String assignArg(CodeVar var) {\n\t\tif (fallback) {\n\t\t\treturn getFallbackName(var);\n\t\t}\n\t\tif (var.isThis()) {\n\t\t\treturn RegisterArg.THIS_ARG_NAME;\n\t\t}\n\t\tString name = getUniqueVarName(makeArgName(var));\n\t\tvar.setName(name);\n\t\treturn name;\n\t}\n\n\tpublic String assignNamedArg(NamedArg arg) {\n\t\tString name = arg.getName();\n\t\tif (fallback) {\n\t\t\treturn name;\n\t\t}\n\t\tString uniqName = getUniqueVarName(name);\n\t\targ.setName(uniqName);\n\t\treturn uniqName;\n\t}\n\n\tpublic String useArg(RegisterArg arg) {\n\t\tString name = arg.getName();\n\t\tif (name == null || fallback) {\n\t\t\treturn getFallbackName(arg);\n\t\t}\n\t\treturn name;\n\t}\n\n\t// TODO: avoid name collision with variables names\n\tpublic String getLoopLabel(LoopLabelAttr attr) {\n\t\tString name = \"loop\" + attr.getLoop().getId();\n\t\tvarNames.add(name);\n\t\treturn name;\n\t}\n\n\tprivate String getUniqueVarName(String name) {\n\t\tString r = name;\n\t\tint i = 2;\n\t\twhile (varNames.contains(r)) {\n\t\t\tr = name + i;\n\t\t\ti++;\n\t\t}\n\t\tvarNames.add(r);\n\t\treturn r;\n\t}\n\n\tprivate String makeArgName(CodeVar var) {\n\t\tString name = var.getName();\n\t\tif (NameMapper.isValidAndPrintable(name)) {\n\t\t\treturn name;\n\t\t}\n\t\treturn getFallbackName(var);\n\t}\n\n\tprivate String getFallbackName(CodeVar var) {\n\t\tList<SSAVar> ssaVars = var.getSsaVars();\n\t\tif (ssaVars.isEmpty()) {\n\t\t\treturn \"v\";\n\t\t}\n\t\treturn getFallbackName(ssaVars.get(0).getAssign());\n\t}\n\n\tprivate String getFallbackName(RegisterArg arg) {\n\t\treturn \"r\" + arg.getRegNum();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/RegionGen.java",
    "content": "package jadx.core.codegen;\n\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.CommentsLevel;\nimport jadx.api.ICodeWriter;\nimport jadx.api.metadata.annotations.InsnCodeOffset;\nimport jadx.api.metadata.annotations.VarNode;\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.core.clsp.ClspClass;\nimport jadx.core.codegen.utils.CodeGenUtils;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.DeclareVariablesAttr;\nimport jadx.core.dex.attributes.nodes.ForceReturnAttr;\nimport jadx.core.dex.attributes.nodes.LoopLabelAttr;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.instructions.SwitchInsn;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.CodeVar;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.NamedArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.regions.SwitchRegion;\nimport jadx.core.dex.regions.SwitchRegion.CaseInfo;\nimport jadx.core.dex.regions.SynchronizedRegion;\nimport jadx.core.dex.regions.TryCatchRegion;\nimport jadx.core.dex.regions.conditions.IfCondition;\nimport jadx.core.dex.regions.conditions.IfRegion;\nimport jadx.core.dex.regions.loops.ForEachLoop;\nimport jadx.core.dex.regions.loops.ForLoop;\nimport jadx.core.dex.regions.loops.LoopRegion;\nimport jadx.core.dex.regions.loops.LoopType;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.RegionUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.CodegenException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class RegionGen extends InsnGen {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(RegionGen.class);\n\n\tpublic RegionGen(MethodGen mgen) {\n\t\tsuper(mgen, false);\n\t}\n\n\tpublic void makeRegion(ICodeWriter code, IContainer cont) throws CodegenException {\n\t\tdeclareVars(code, cont);\n\t\tcont.generate(this, code);\n\t}\n\n\tprivate void declareVars(ICodeWriter code, IContainer cont) {\n\t\tDeclareVariablesAttr declVars = cont.get(AType.DECLARE_VARIABLES);\n\t\tif (declVars != null) {\n\t\t\tfor (CodeVar v : declVars.getVars()) {\n\t\t\t\tcode.startLine();\n\t\t\t\tdeclareVar(code, v);\n\t\t\t\tcode.add(';');\n\t\t\t\tCodeGenUtils.addCodeComments(code, mth, v.getAnySsaVar().getAssign());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void makeRegionIndent(ICodeWriter code, IContainer region) throws CodegenException {\n\t\tcode.incIndent();\n\t\tmakeRegion(code, region);\n\t\tcode.decIndent();\n\t}\n\n\tpublic void makeSimpleBlock(IBlock block, ICodeWriter code) throws CodegenException {\n\t\tif (block.contains(AFlag.DONT_GENERATE)) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\tif (!insn.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\tmakeInsn(insn, code);\n\t\t\t}\n\t\t}\n\t\tForceReturnAttr retAttr = block.get(AType.FORCE_RETURN);\n\t\tif (retAttr != null) {\n\t\t\tmakeInsn(retAttr.getReturnInsn(), code);\n\t\t}\n\t}\n\n\tpublic void makeIf(IfRegion region, ICodeWriter code, boolean newLine) throws CodegenException {\n\t\tif (newLine) {\n\t\t\tcode.startLineWithNum(region.getSourceLine());\n\t\t} else {\n\t\t\tcode.attachSourceLine(region.getSourceLine());\n\t\t}\n\t\tboolean comment = region.contains(AFlag.COMMENT_OUT);\n\t\tif (comment) {\n\t\t\tcode.add(\"// \");\n\t\t}\n\n\t\tcode.add(\"if (\");\n\t\tnew ConditionGen(this).add(code, region.getCondition());\n\t\tcode.add(\") {\");\n\t\tif (code.isMetadataSupported()) {\n\t\t\tList<BlockNode> conditionBlocks = region.getConditionBlocks();\n\t\t\tif (!conditionBlocks.isEmpty()) {\n\t\t\t\tBlockNode blockNode = conditionBlocks.get(0);\n\t\t\t\tInsnNode lastInsn = BlockUtils.getLastInsn(blockNode);\n\t\t\t\tInsnCodeOffset.attach(code, lastInsn);\n\t\t\t\tCodeGenUtils.addCodeComments(code, mth, lastInsn);\n\t\t\t}\n\t\t}\n\t\tmakeRegionIndent(code, region.getThenRegion());\n\t\tif (comment) {\n\t\t\tcode.startLine(\"// }\");\n\t\t} else {\n\t\t\tcode.startLine('}');\n\t\t}\n\n\t\tIContainer els = region.getElseRegion();\n\t\tif (RegionUtils.notEmpty(els)) {\n\t\t\tcode.add(\" else \");\n\t\t\tif (connectElseIf(code, els)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcode.add('{');\n\t\t\tmakeRegionIndent(code, els);\n\t\t\tif (comment) {\n\t\t\t\tcode.startLine(\"// }\");\n\t\t\t} else {\n\t\t\t\tcode.startLine('}');\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Connect if-else-if block\n\t */\n\tprivate boolean connectElseIf(ICodeWriter code, IContainer els) throws CodegenException {\n\t\tif (els.contains(AFlag.ELSE_IF_CHAIN)) {\n\t\t\tIContainer elseBlock = RegionUtils.getSingleSubBlock(els);\n\t\t\tif (elseBlock instanceof IfRegion) {\n\t\t\t\tdeclareVars(code, elseBlock);\n\t\t\t\tmakeIf((IfRegion) elseBlock, code, false);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic void makeLoop(LoopRegion region, ICodeWriter code) throws CodegenException {\n\t\tcode.startLineWithNum(region.getSourceLine());\n\t\tLoopLabelAttr labelAttr = region.getInfo().getStart().get(AType.LOOP_LABEL);\n\t\tif (labelAttr != null) {\n\t\t\tcode.add(mgen.getNameGen().getLoopLabel(labelAttr)).add(\": \");\n\t\t}\n\n\t\tIfCondition condition = region.getCondition();\n\t\tif (condition == null) {\n\t\t\t// infinite loop\n\t\t\tcode.add(\"while (true) {\");\n\t\t\tmakeRegionIndent(code, region.getBody());\n\t\t\tcode.startLine('}');\n\t\t\treturn;\n\t\t}\n\t\tInsnNode condInsn = condition.getFirstInsn();\n\t\tInsnCodeOffset.attach(code, condInsn);\n\n\t\tConditionGen conditionGen = new ConditionGen(this);\n\t\tLoopType type = region.getType();\n\t\tif (type != null) {\n\t\t\tif (type instanceof ForLoop) {\n\t\t\t\tForLoop forLoop = (ForLoop) type;\n\t\t\t\tcode.add(\"for (\");\n\t\t\t\tmakeInsn(forLoop.getInitInsn(), code, Flags.INLINE);\n\t\t\t\tcode.add(\"; \");\n\t\t\t\tconditionGen.add(code, condition);\n\t\t\t\tcode.add(\"; \");\n\t\t\t\tmakeInsn(forLoop.getIncrInsn(), code, Flags.INLINE);\n\t\t\t\tcode.add(\") {\");\n\t\t\t\tCodeGenUtils.addCodeComments(code, mth, condInsn);\n\t\t\t\tmakeRegionIndent(code, region.getBody());\n\t\t\t\tcode.startLine('}');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (type instanceof ForEachLoop) {\n\t\t\t\tForEachLoop forEachLoop = (ForEachLoop) type;\n\t\t\t\tcode.add(\"for (\");\n\t\t\t\tdeclareVar(code, forEachLoop.getVarArg());\n\t\t\t\tcode.add(\" : \");\n\t\t\t\taddArg(code, forEachLoop.getIterableArg(), false);\n\t\t\t\tcode.add(\") {\");\n\t\t\t\tCodeGenUtils.addCodeComments(code, mth, condInsn);\n\t\t\t\tmakeRegionIndent(code, region.getBody());\n\t\t\t\tcode.startLine('}');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow new JadxRuntimeException(\"Unknown loop type: \" + type.getClass());\n\t\t}\n\t\tif (region.isConditionAtEnd()) {\n\t\t\tcode.add(\"do {\");\n\t\t\tCodeGenUtils.addCodeComments(code, mth, condInsn);\n\t\t\tmakeRegionIndent(code, region.getBody());\n\t\t\tcode.startLineWithNum(region.getSourceLine());\n\t\t\tcode.add(\"} while (\");\n\t\t\tconditionGen.add(code, condition);\n\t\t\tcode.add(\");\");\n\t\t} else {\n\t\t\tcode.add(\"while (\");\n\t\t\tconditionGen.add(code, condition);\n\t\t\tcode.add(\") {\");\n\t\t\tCodeGenUtils.addCodeComments(code, mth, condInsn);\n\t\t\tmakeRegionIndent(code, region.getBody());\n\t\t\tcode.startLine('}');\n\t\t}\n\t}\n\n\tpublic void makeSynchronizedRegion(SynchronizedRegion cont, ICodeWriter code) throws CodegenException {\n\t\tcode.startLine(\"synchronized (\");\n\t\tInsnNode monitorEnterInsn = cont.getEnterInsn();\n\t\taddArg(code, monitorEnterInsn.getArg(0));\n\t\tcode.add(\") {\");\n\n\t\tInsnCodeOffset.attach(code, monitorEnterInsn);\n\t\tCodeGenUtils.addCodeComments(code, mth, monitorEnterInsn);\n\n\t\tmakeRegionIndent(code, cont.getRegion());\n\t\tcode.startLine('}');\n\t}\n\n\tpublic void makeSwitch(SwitchRegion sw, ICodeWriter code) throws CodegenException {\n\t\tSwitchInsn insn = (SwitchInsn) BlockUtils.getLastInsn(sw.getHeader());\n\t\tObjects.requireNonNull(insn, \"Switch insn not found in header\");\n\t\tInsnArg arg = insn.getArg(0);\n\t\tcode.startLine(\"switch (\");\n\t\taddArg(code, arg, false);\n\t\tcode.add(\") {\");\n\t\tInsnCodeOffset.attach(code, insn);\n\t\tCodeGenUtils.addCodeComments(code, mth, insn);\n\t\tcode.incIndent();\n\n\t\tfor (CaseInfo caseInfo : sw.getCases()) {\n\t\t\tList<Object> keys = caseInfo.getKeys();\n\t\t\tIContainer c = caseInfo.getContainer();\n\t\t\tfor (Object k : keys) {\n\t\t\t\tif (k == SwitchRegion.DEFAULT_CASE_KEY) {\n\t\t\t\t\tcode.startLine(\"default:\");\n\t\t\t\t} else {\n\t\t\t\t\tcode.startLine(\"case \");\n\t\t\t\t\taddCaseKey(code, arg, k);\n\t\t\t\t\tcode.add(':');\n\t\t\t\t}\n\t\t\t}\n\t\t\tmakeRegionIndent(code, c);\n\t\t}\n\t\tcode.decIndent();\n\t\tcode.startLine('}');\n\t}\n\n\tprivate void addCaseKey(ICodeWriter code, InsnArg arg, Object k) throws CodegenException {\n\t\tif (k instanceof FieldNode) {\n\t\t\tFieldNode fld = (FieldNode) k;\n\t\t\tuseField(code, fld.getFieldInfo(), fld);\n\t\t} else if (k instanceof FieldInfo) {\n\t\t\tuseField(code, (FieldInfo) k, null);\n\t\t} else if (k instanceof Integer) {\n\t\t\tcode.add(TypeGen.literalToString((Integer) k, arg.getType(), mth, fallback));\n\t\t} else if (k instanceof String) {\n\t\t\tcode.add('\\\"').add((String) k).add('\\\"');\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(\"Unexpected key in switch: \" + (k != null ? k.getClass() : null));\n\t\t}\n\t}\n\n\tprivate void useField(ICodeWriter code, FieldInfo fldInfo, @Nullable FieldNode fld) throws CodegenException {\n\t\tboolean isEnum;\n\t\tif (fld != null) {\n\t\t\tisEnum = fld.getParentClass().isEnum();\n\t\t} else {\n\t\t\tClspClass clsDetails = root.getClsp().getClsDetails(fldInfo.getDeclClass().getType());\n\t\t\tisEnum = clsDetails != null && clsDetails.hasAccFlag(AccessFlags.ENUM);\n\t\t}\n\t\tif (isEnum) {\n\t\t\tif (fld != null) {\n\t\t\t\tcode.attachAnnotation(fld);\n\t\t\t}\n\t\t\tcode.add(fldInfo.getAlias());\n\t\t\treturn;\n\t\t}\n\t\tstaticField(code, fldInfo);\n\t\tif (fld != null && mth.checkCommentsLevel(CommentsLevel.INFO)) {\n\t\t\t// print original value, sometimes replaced with incorrect field\n\t\t\tEncodedValue constVal = fld.get(JadxAttrType.CONSTANT_VALUE);\n\t\t\tif (constVal != null && constVal.getValue() != null) {\n\t\t\t\tcode.add(\" /* \").add(constVal.getValue().toString()).add(\" */\");\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void makeTryCatch(TryCatchRegion region, ICodeWriter code) throws CodegenException {\n\t\tcode.startLine(\"try {\");\n\n\t\tInsnNode insn = BlockUtils.getFirstInsn(Utils.first(region.getTryCatchBlock().getBlocks()));\n\t\tInsnCodeOffset.attach(code, insn);\n\t\tCodeGenUtils.addCodeComments(code, mth, insn);\n\n\t\tmakeRegionIndent(code, region.getTryRegion());\n\t\t// TODO: move search of 'allHandler' to 'TryCatchRegion'\n\t\tExceptionHandler allHandler = null;\n\t\tfor (Map.Entry<ExceptionHandler, IContainer> entry : region.getCatchRegions().entrySet()) {\n\t\t\tExceptionHandler handler = entry.getKey();\n\t\t\tif (handler.isCatchAll()) {\n\t\t\t\tif (allHandler != null) {\n\t\t\t\t\tLOG.warn(\"Several 'all' handlers in try/catch block in {}\", mth);\n\t\t\t\t}\n\t\t\t\tallHandler = handler;\n\t\t\t} else {\n\t\t\t\tmakeCatchBlock(code, handler);\n\t\t\t}\n\t\t}\n\t\tif (allHandler != null) {\n\t\t\tmakeCatchBlock(code, allHandler);\n\t\t}\n\t\tIContainer finallyRegion = region.getFinallyRegion();\n\t\tif (finallyRegion != null) {\n\t\t\tcode.startLine(\"} finally {\");\n\t\t\tmakeRegionIndent(code, finallyRegion);\n\t\t}\n\t\tcode.startLine('}');\n\t}\n\n\tprivate void makeCatchBlock(ICodeWriter code, ExceptionHandler handler) throws CodegenException {\n\t\tIContainer region = handler.getHandlerRegion();\n\t\tif (region == null) {\n\t\t\treturn;\n\t\t}\n\t\tcode.startLine(\"} catch (\");\n\t\tif (handler.isCatchAll()) {\n\t\t\tuseClass(code, ArgType.THROWABLE);\n\t\t} else {\n\t\t\tIterator<ClassInfo> it = handler.getCatchTypes().iterator();\n\t\t\tif (it.hasNext()) {\n\t\t\t\tuseClass(code, it.next());\n\t\t\t}\n\t\t\twhile (it.hasNext()) {\n\t\t\t\tcode.add(\" | \");\n\t\t\t\tuseClass(code, it.next());\n\t\t\t}\n\t\t}\n\t\tcode.add(' ');\n\t\tInsnArg arg = handler.getArg();\n\t\tif (arg == null) {\n\t\t\tcode.add(\"unknown\"); // throwing exception is too late at this point\n\t\t} else if (arg instanceof RegisterArg) {\n\t\t\tSSAVar ssaVar = ((RegisterArg) arg).getSVar();\n\t\t\tif (code.isMetadataSupported()) {\n\t\t\t\tcode.attachDefinition(VarNode.get(mth, ssaVar));\n\t\t\t}\n\t\t\tcode.add(mgen.getNameGen().assignArg(ssaVar.getCodeVar()));\n\t\t} else if (arg instanceof NamedArg) {\n\t\t\tcode.add(mgen.getNameGen().assignNamedArg((NamedArg) arg));\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(\"Unexpected arg type in catch block: \" + arg + \", class: \" + arg.getClass().getSimpleName());\n\t\t}\n\t\tcode.add(\") {\");\n\n\t\tInsnCodeOffset.attach(code, handler.getHandlerOffset());\n\t\tCodeGenUtils.addCodeComments(code, mth, handler.getHandlerBlock());\n\n\t\tmakeRegionIndent(code, region);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/SimpleModeHelper.java",
    "content": "package jadx.core.codegen;\n\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.instructions.IfNode;\nimport jadx.core.dex.instructions.TargetInsnNode;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.dex.visitors.blocks.BlockProcessor;\nimport jadx.core.dex.visitors.blocks.BlockSplitter;\nimport jadx.core.utils.BlockUtils;\n\npublic class SimpleModeHelper {\n\n\tprivate final MethodNode mth;\n\n\tprivate final BitSet startLabel;\n\tprivate final BitSet endGoto;\n\n\tpublic SimpleModeHelper(MethodNode mth) {\n\t\tthis.mth = mth;\n\t\tthis.startLabel = BlockUtils.newBlocksBitSet(mth);\n\t\tthis.endGoto = BlockUtils.newBlocksBitSet(mth);\n\t}\n\n\tpublic List<BlockNode> prepareBlocks() {\n\t\tremoveEmptyBlocks();\n\t\tList<BlockNode> blocksList = getSortedBlocks();\n\t\tblocksList.removeIf(b -> b.equals(mth.getEnterBlock()) || b.equals(mth.getExitBlock()));\n\t\tunbindExceptionHandlers();\n\t\tif (blocksList.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\t@Nullable\n\t\tBlockNode prev = null;\n\t\tint blocksCount = blocksList.size();\n\t\tfor (int i = 0; i < blocksCount; i++) {\n\t\t\tBlockNode block = blocksList.get(i);\n\t\t\tBlockNode nextBlock = i + 1 == blocksCount ? null : blocksList.get(i + 1);\n\t\t\tList<BlockNode> preds = block.getPredecessors();\n\t\t\tint predsCount = preds.size();\n\t\t\tif (predsCount > 1) {\n\t\t\t\tstartLabel.set(block.getId());\n\t\t\t} else if (predsCount == 1 && prev != null) {\n\t\t\t\tif (!prev.equals(preds.get(0))) {\n\t\t\t\t\tif (!block.contains(AFlag.EXC_BOTTOM_SPLITTER)) {\n\t\t\t\t\t\tstartLabel.set(block.getId());\n\t\t\t\t\t}\n\t\t\t\t\tif (prev.getSuccessors().size() == 1 && !mth.isPreExitBlock(prev)) {\n\t\t\t\t\t\tendGoto.set(prev.getId());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tInsnNode lastInsn = BlockUtils.getLastInsn(block);\n\t\t\tif (lastInsn instanceof TargetInsnNode) {\n\t\t\t\tprocessTargetInsn(block, lastInsn, nextBlock);\n\t\t\t}\n\t\t\tif (block.contains(AType.EXC_HANDLER)) {\n\t\t\t\tstartLabel.set(block.getId());\n\t\t\t}\n\t\t\tif (nextBlock == null && !mth.isPreExitBlock(block)) {\n\t\t\t\tendGoto.set(block.getId());\n\t\t\t}\n\t\t\tprev = block;\n\t\t}\n\t\tif (mth.isVoidReturn()) {\n\t\t\tint last = blocksList.size() - 1;\n\t\t\tif (blocksList.get(last).contains(AFlag.RETURN)) {\n\t\t\t\t// remove trailing return\n\t\t\t\tblocksList.remove(last);\n\t\t\t}\n\t\t}\n\t\treturn blocksList;\n\t}\n\n\tprivate void removeEmptyBlocks() {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tif (block.getInstructions().isEmpty()\n\t\t\t\t\t&& block.getPredecessors().size() > 0\n\t\t\t\t\t&& block.getSuccessors().size() == 1) {\n\t\t\t\tBlockNode successor = block.getSuccessors().get(0);\n\t\t\t\tList<BlockNode> predecessors = block.getPredecessors();\n\t\t\t\tBlockSplitter.removeConnection(block, successor);\n\t\t\t\tif (predecessors.size() == 1) {\n\t\t\t\t\tBlockSplitter.replaceConnection(predecessors.get(0), block, successor);\n\t\t\t\t} else {\n\t\t\t\t\tfor (BlockNode pred : new ArrayList<>(predecessors)) {\n\t\t\t\t\t\tBlockSplitter.replaceConnection(pred, block, successor);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tblock.add(AFlag.REMOVE);\n\t\t\t}\n\t\t}\n\t\tBlockProcessor.removeMarkedBlocks(mth);\n\t}\n\n\tprivate void unbindExceptionHandlers() {\n\t\tif (mth.isNoExceptionHandlers()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (ExceptionHandler handler : mth.getExceptionHandlers()) {\n\t\t\tBlockNode handlerBlock = handler.getHandlerBlock();\n\t\t\tif (handlerBlock != null) {\n\t\t\t\tBlockSplitter.removePredecessors(handlerBlock);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void processTargetInsn(BlockNode block, InsnNode lastInsn, @Nullable BlockNode next) {\n\t\tif (lastInsn instanceof IfNode) {\n\t\t\tIfNode ifInsn = (IfNode) lastInsn;\n\t\t\tBlockNode thenBlock = ifInsn.getThenBlock();\n\t\t\tif (Objects.equals(next, thenBlock)) {\n\t\t\t\tifInsn.invertCondition();\n\t\t\t\tstartLabel.set(ifInsn.getThenBlock().getId());\n\t\t\t} else {\n\t\t\t\tstartLabel.set(thenBlock.getId());\n\t\t\t}\n\t\t\tifInsn.normalize();\n\t\t} else {\n\t\t\tfor (BlockNode successor : block.getSuccessors()) {\n\t\t\t\tstartLabel.set(successor.getId());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic boolean isNeedStartLabel(BlockNode block) {\n\t\treturn startLabel.get(block.getId());\n\t}\n\n\tpublic boolean isNeedEndGoto(BlockNode block) {\n\t\treturn endGoto.get(block.getId());\n\t}\n\n\t// DFS sort blocks to reduce goto count\n\tprivate List<BlockNode> getSortedBlocks() {\n\t\tList<BlockNode> list = new ArrayList<>(mth.getBasicBlocks().size());\n\t\tBlockUtils.visitDFS(mth, list::add);\n\t\treturn list;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/TypeGen.java",
    "content": "package jadx.core.codegen;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.PrimitiveType;\nimport jadx.core.dex.nodes.IDexNode;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class TypeGen {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(TypeGen.class);\n\n\tprivate TypeGen() {\n\t}\n\n\tpublic static String signature(ArgType type) {\n\t\tPrimitiveType stype = type.getPrimitiveType();\n\t\tif (stype == PrimitiveType.OBJECT) {\n\t\t\treturn Utils.makeQualifiedObjectName(type.getObject());\n\t\t}\n\t\tif (stype == PrimitiveType.ARRAY) {\n\t\t\treturn '[' + signature(type.getArrayElement());\n\t\t}\n\t\treturn stype.getShortName();\n\t}\n\n\t/**\n\t * Convert literal arg to string (preferred method)\n\t */\n\tpublic static String literalToString(LiteralArg arg, IDexNode dexNode, boolean fallback) {\n\t\treturn literalToString(arg.getLiteral(), arg.getType(),\n\t\t\t\tdexNode.root().getStringUtils(),\n\t\t\t\tfallback,\n\t\t\t\targ.contains(AFlag.EXPLICIT_PRIMITIVE_TYPE));\n\t}\n\n\t/**\n\t * Convert literal value to string according to value type\n\t *\n\t * @throws JadxRuntimeException for incorrect type or literal value\n\t */\n\tpublic static String literalToString(long lit, ArgType type, IDexNode dexNode, boolean fallback) {\n\t\treturn literalToString(lit, type, dexNode.root().getStringUtils(), fallback, false);\n\t}\n\n\tpublic static String literalToString(long lit, ArgType type, StringUtils stringUtils, boolean fallback, boolean cast) {\n\t\tif (type == null || !type.isTypeKnown()) {\n\t\t\tString n = Long.toString(lit);\n\t\t\tif (fallback && Math.abs(lit) > 100) {\n\t\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\t\tsb.append(n).append(\"(0x\").append(Long.toHexString(lit));\n\t\t\t\tif (type == null || type.contains(PrimitiveType.FLOAT)) {\n\t\t\t\t\tsb.append(\", float:\").append(Float.intBitsToFloat((int) lit));\n\t\t\t\t}\n\t\t\t\tif (type == null || type.contains(PrimitiveType.DOUBLE)) {\n\t\t\t\t\tsb.append(\", double:\").append(Double.longBitsToDouble(lit));\n\t\t\t\t}\n\t\t\t\tsb.append(')');\n\t\t\t\treturn sb.toString();\n\t\t\t}\n\t\t\treturn n;\n\t\t}\n\n\t\tswitch (type.getPrimitiveType()) {\n\t\t\tcase BOOLEAN:\n\t\t\t\treturn lit == 0 ? \"false\" : \"true\";\n\t\t\tcase CHAR:\n\t\t\t\treturn stringUtils.unescapeChar((char) lit, cast);\n\t\t\tcase BYTE:\n\t\t\t\treturn stringUtils.formatByte(lit, cast);\n\t\t\tcase SHORT:\n\t\t\t\treturn stringUtils.formatShort(lit, cast);\n\t\t\tcase INT:\n\t\t\t\treturn stringUtils.formatInteger(lit, cast);\n\t\t\tcase LONG:\n\t\t\t\treturn stringUtils.formatLong(lit, cast);\n\t\t\tcase FLOAT:\n\t\t\t\treturn StringUtils.formatFloat(Float.intBitsToFloat((int) lit));\n\t\t\tcase DOUBLE:\n\t\t\t\treturn StringUtils.formatDouble(Double.longBitsToDouble(lit));\n\n\t\t\tcase OBJECT:\n\t\t\tcase ARRAY:\n\t\t\t\tif (lit != 0) {\n\t\t\t\t\tLOG.warn(\"Wrong object literal: {} for type: {}\", lit, type);\n\t\t\t\t\treturn Long.toString(lit);\n\t\t\t\t}\n\t\t\t\treturn \"null\";\n\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unknown type in literalToString: \" + type);\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic static String literalToRawString(LiteralArg arg) {\n\t\tArgType type = arg.getType();\n\t\tif (type == null) {\n\t\t\treturn null;\n\t\t}\n\t\tlong lit = arg.getLiteral();\n\t\tswitch (type.getPrimitiveType()) {\n\t\t\tcase BOOLEAN:\n\t\t\t\treturn lit == 0 ? \"false\" : \"true\";\n\t\t\tcase CHAR:\n\t\t\t\treturn String.valueOf((char) lit);\n\n\t\t\tcase BYTE:\n\t\t\tcase SHORT:\n\t\t\tcase INT:\n\t\t\tcase LONG:\n\t\t\t\treturn Long.toString(lit);\n\n\t\t\tcase FLOAT:\n\t\t\t\treturn Float.toString(Float.intBitsToFloat((int) lit));\n\t\t\tcase DOUBLE:\n\t\t\t\treturn Double.toString(Double.longBitsToDouble(lit));\n\n\t\t\tcase OBJECT:\n\t\t\tcase ARRAY:\n\t\t\t\tif (lit != 0) {\n\t\t\t\t\tLOG.warn(\"Wrong object literal: {} for type: {}\", lit, type);\n\t\t\t\t\treturn Long.toString(lit);\n\t\t\t\t}\n\t\t\t\treturn \"null\";\n\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/json/JsonCodeGen.java",
    "content": "package jadx.core.codegen.json;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport com.google.gson.FieldNamingPolicy;\nimport com.google.gson.Gson;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ICodeWriter;\nimport jadx.api.JadxArgs;\nimport jadx.api.impl.AnnotatedCodeWriter;\nimport jadx.api.impl.SimpleCodeWriter;\nimport jadx.api.metadata.ICodeMetadata;\nimport jadx.api.metadata.annotations.InsnCodeOffset;\nimport jadx.core.codegen.ClassGen;\nimport jadx.core.codegen.MethodGen;\nimport jadx.core.codegen.json.cls.JsonClass;\nimport jadx.core.codegen.json.cls.JsonCodeLine;\nimport jadx.core.codegen.json.cls.JsonField;\nimport jadx.core.codegen.json.cls.JsonMethod;\nimport jadx.core.codegen.utils.CodeGenUtils;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.GsonUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class JsonCodeGen {\n\n\tprivate static final Gson GSON = GsonUtils.defaultGsonBuilder()\n\t\t\t.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES)\n\t\t\t.disableHtmlEscaping()\n\t\t\t.create();\n\n\tprivate final ClassNode cls;\n\tprivate final JadxArgs args;\n\tprivate final RootNode root;\n\n\tpublic JsonCodeGen(ClassNode cls) {\n\t\tthis.cls = cls;\n\t\tthis.root = cls.root();\n\t\tthis.args = root.getArgs();\n\t}\n\n\tpublic String process() {\n\t\tJsonClass jsonCls = processCls(cls, null);\n\t\treturn GSON.toJson(jsonCls);\n\t}\n\n\tprivate JsonClass processCls(ClassNode cls, @Nullable ClassGen parentCodeGen) {\n\t\tClassGen classGen;\n\t\tif (parentCodeGen == null) {\n\t\t\tclassGen = new ClassGen(cls, args);\n\t\t} else {\n\t\t\tclassGen = new ClassGen(cls, parentCodeGen);\n\t\t}\n\t\tClassInfo classInfo = cls.getClassInfo();\n\n\t\tJsonClass jsonCls = new JsonClass();\n\t\tjsonCls.setPkg(classInfo.getAliasPkg());\n\t\tjsonCls.setDex(cls.getInputFileName());\n\t\tjsonCls.setName(classInfo.getFullName());\n\t\tif (classInfo.hasAlias()) {\n\t\t\tjsonCls.setAlias(classInfo.getAliasFullName());\n\t\t}\n\t\tjsonCls.setType(getClassTypeStr(cls));\n\t\tjsonCls.setAccessFlags(cls.getAccessFlags().rawValue());\n\t\tArgType superClass = cls.getSuperClass();\n\t\tif (superClass != null\n\t\t\t\t&& !superClass.equals(ArgType.OBJECT)\n\t\t\t\t&& !cls.contains(AFlag.REMOVE_SUPER_CLASS)) {\n\t\t\tjsonCls.setSuperClass(getTypeAlias(classGen, superClass));\n\t\t}\n\t\tif (!cls.getInterfaces().isEmpty()) {\n\t\t\tjsonCls.setInterfaces(Utils.collectionMap(cls.getInterfaces(), clsType -> getTypeAlias(classGen, clsType)));\n\t\t}\n\n\t\tICodeWriter cw = new SimpleCodeWriter(args);\n\t\tCodeGenUtils.addErrorsAndComments(cw, cls);\n\t\tclassGen.addClassDeclaration(cw);\n\t\tjsonCls.setDeclaration(cw.getCodeStr());\n\n\t\taddFields(cls, jsonCls, classGen);\n\t\taddMethods(cls, jsonCls, classGen);\n\t\taddInnerClasses(cls, jsonCls, classGen);\n\n\t\tif (!cls.getClassInfo().isInner()) {\n\t\t\tList<String> imports = Utils.collectionMap(classGen.getImports(), ClassInfo::getAliasFullName);\n\t\t\tCollections.sort(imports);\n\t\t\tjsonCls.setImports(imports);\n\t\t}\n\t\treturn jsonCls;\n\t}\n\n\tprivate void addInnerClasses(ClassNode cls, JsonClass jsonCls, ClassGen classGen) {\n\t\tList<ClassNode> innerClasses = cls.getInnerClasses();\n\t\tif (innerClasses.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tjsonCls.setInnerClasses(new ArrayList<>(innerClasses.size()));\n\t\tfor (ClassNode innerCls : innerClasses) {\n\t\t\tif (innerCls.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tJsonClass innerJsonCls = processCls(innerCls, classGen);\n\t\t\tjsonCls.getInnerClasses().add(innerJsonCls);\n\t\t}\n\t}\n\n\tprivate void addFields(ClassNode cls, JsonClass jsonCls, ClassGen classGen) {\n\t\tjsonCls.setFields(new ArrayList<>());\n\t\tfor (FieldNode field : cls.getFields()) {\n\t\t\tif (field.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tJsonField jsonField = new JsonField();\n\t\t\tjsonField.setName(field.getName());\n\t\t\tif (field.getFieldInfo().hasAlias()) {\n\t\t\t\tjsonField.setAlias(field.getAlias());\n\t\t\t}\n\n\t\t\tICodeWriter cw = new SimpleCodeWriter(args);\n\t\t\tclassGen.addField(cw, field);\n\t\t\tjsonField.setDeclaration(cw.getCodeStr());\n\t\t\tjsonField.setAccessFlags(field.getAccessFlags().rawValue());\n\t\t\tjsonCls.getFields().add(jsonField);\n\t\t}\n\t}\n\n\tprivate void addMethods(ClassNode cls, JsonClass jsonCls, ClassGen classGen) {\n\t\tjsonCls.setMethods(new ArrayList<>());\n\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\tif (mth.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tJsonMethod jsonMth = new JsonMethod();\n\t\t\tjsonMth.setName(mth.getName());\n\t\t\tif (mth.getMethodInfo().hasAlias()) {\n\t\t\t\tjsonMth.setAlias(mth.getAlias());\n\t\t\t}\n\t\t\tjsonMth.setSignature(mth.getMethodInfo().getShortId());\n\t\t\tjsonMth.setReturnType(getTypeAlias(classGen, mth.getReturnType()));\n\t\t\tjsonMth.setArguments(Utils.collectionMap(mth.getMethodInfo().getArgumentsTypes(), clsType -> getTypeAlias(classGen, clsType)));\n\n\t\t\tMethodGen mthGen = new MethodGen(classGen, mth);\n\t\t\tICodeWriter cw = new AnnotatedCodeWriter(args);\n\t\t\tmthGen.addDefinition(cw);\n\t\t\tjsonMth.setDeclaration(cw.getCodeStr());\n\t\t\tjsonMth.setAccessFlags(mth.getAccessFlags().rawValue());\n\t\t\tjsonMth.setLines(fillMthCode(mth, mthGen));\n\t\t\tjsonMth.setOffset(\"0x\" + Long.toHexString(mth.getMethodCodeOffset()));\n\t\t\tjsonCls.getMethods().add(jsonMth);\n\t\t}\n\t}\n\n\tprivate List<JsonCodeLine> fillMthCode(MethodNode mth, MethodGen mthGen) {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\n\t\tICodeWriter cw = mth.root().makeCodeWriter();\n\t\ttry {\n\t\t\tmthGen.addInstructions(cw);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Method generation error\", e);\n\t\t}\n\t\tICodeInfo code = cw.finish();\n\t\tString codeStr = code.getCodeStr();\n\t\tif (codeStr.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\n\t\tString[] lines = codeStr.split(args.getCodeNewLineStr());\n\t\tMap<Integer, Integer> lineMapping = code.getCodeMetadata().getLineMapping();\n\t\tICodeMetadata metadata = code.getCodeMetadata();\n\t\tlong mthCodeOffset = mth.getMethodCodeOffset() + 16;\n\n\t\tint linesCount = lines.length;\n\t\tList<JsonCodeLine> codeLines = new ArrayList<>(linesCount);\n\t\tint lineStartPos = 0;\n\t\tint newLineLen = args.getCodeNewLineStr().length();\n\t\tfor (int i = 0; i < linesCount; i++) {\n\t\t\tString codeLine = lines[i];\n\t\t\tint line = i + 2;\n\t\t\tJsonCodeLine jsonCodeLine = new JsonCodeLine();\n\t\t\tjsonCodeLine.setCode(codeLine);\n\t\t\tjsonCodeLine.setSourceLine(lineMapping.get(line));\n\t\t\tObject obj = metadata.getAt(lineStartPos);\n\t\t\tif (obj instanceof InsnCodeOffset) {\n\t\t\t\tlong offset = ((InsnCodeOffset) obj).getOffset();\n\t\t\t\tjsonCodeLine.setOffset(\"0x\" + Long.toHexString(mthCodeOffset + offset * 2));\n\t\t\t}\n\t\t\tcodeLines.add(jsonCodeLine);\n\t\t\tlineStartPos += codeLine.length() + newLineLen;\n\t\t}\n\t\treturn codeLines;\n\t}\n\n\tprivate String getTypeAlias(ClassGen classGen, ArgType clsType) {\n\t\tICodeWriter code = new SimpleCodeWriter(args);\n\t\tclassGen.useType(code, clsType);\n\t\treturn code.getCodeStr();\n\t}\n\n\tprivate String getClassTypeStr(ClassNode cls) {\n\t\tif (cls.isEnum()) {\n\t\t\treturn \"enum\";\n\t\t}\n\t\tif (cls.getAccessFlags().isInterface()) {\n\t\t\treturn \"interface\";\n\t\t}\n\t\treturn \"class\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/json/JsonMappingGen.java",
    "content": "package jadx.core.codegen.json;\n\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.Writer;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.FieldNamingPolicy;\nimport com.google.gson.Gson;\n\nimport jadx.api.JadxArgs;\nimport jadx.core.codegen.json.mapping.JsonClsMapping;\nimport jadx.core.codegen.json.mapping.JsonFieldMapping;\nimport jadx.core.codegen.json.mapping.JsonMapping;\nimport jadx.core.codegen.json.mapping.JsonMthMapping;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.GsonUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\n\npublic class JsonMappingGen {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JsonMappingGen.class);\n\n\tprivate static final Gson GSON = GsonUtils.defaultGsonBuilder()\n\t\t\t.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES)\n\t\t\t.disableHtmlEscaping()\n\t\t\t.create();\n\n\tpublic static void dump(RootNode root) {\n\t\tJsonMapping mapping = new JsonMapping();\n\t\tfillMapping(mapping, root);\n\n\t\tJadxArgs args = root.getArgs();\n\t\tFile outDirSrc = args.getOutDirSrc().getAbsoluteFile();\n\t\tFile mappingFile = new File(outDirSrc, \"mapping.json\");\n\t\tFileUtils.makeDirsForFile(mappingFile);\n\t\ttry (Writer writer = new FileWriter(mappingFile)) {\n\t\t\tGSON.toJson(mapping, writer);\n\t\t\tLOG.info(\"Save mappings to {}\", mappingFile.getAbsolutePath());\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to save mapping json\", e);\n\t\t}\n\t}\n\n\tprivate static void fillMapping(JsonMapping mapping, RootNode root) {\n\t\tList<ClassNode> classes = root.getClasses(true);\n\t\tmapping.setClasses(new ArrayList<>(classes.size()));\n\t\tfor (ClassNode cls : classes) {\n\t\t\tClassInfo classInfo = cls.getClassInfo();\n\t\t\tJsonClsMapping jsonCls = new JsonClsMapping();\n\t\t\tjsonCls.setName(classInfo.getRawName());\n\t\t\tjsonCls.setAlias(classInfo.getAliasFullName());\n\t\t\tjsonCls.setInner(classInfo.isInner());\n\t\t\tjsonCls.setJson(cls.getTopParentClass().getClassInfo().getAliasFullPath() + \".json\");\n\t\t\tif (classInfo.isInner()) {\n\t\t\t\tjsonCls.setTopClass(cls.getTopParentClass().getClassInfo().getFullName());\n\t\t\t}\n\t\t\taddFields(cls, jsonCls);\n\t\t\taddMethods(cls, jsonCls);\n\t\t\tmapping.getClasses().add(jsonCls);\n\t\t}\n\t}\n\n\tprivate static void addMethods(ClassNode cls, JsonClsMapping jsonCls) {\n\t\tList<MethodNode> methods = cls.getMethods();\n\t\tif (methods.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tjsonCls.setMethods(new ArrayList<>(methods.size()));\n\t\tfor (MethodNode method : methods) {\n\t\t\tJsonMthMapping jsonMethod = new JsonMthMapping();\n\t\t\tMethodInfo methodInfo = method.getMethodInfo();\n\t\t\tjsonMethod.setSignature(methodInfo.getShortId());\n\t\t\tjsonMethod.setName(methodInfo.getName());\n\t\t\tjsonMethod.setAlias(methodInfo.getAlias());\n\t\t\tjsonMethod.setOffset(\"0x\" + Long.toHexString(method.getMethodCodeOffset()));\n\t\t\tjsonCls.getMethods().add(jsonMethod);\n\t\t}\n\t}\n\n\tprivate static void addFields(ClassNode cls, JsonClsMapping jsonCls) {\n\t\tList<FieldNode> fields = cls.getFields();\n\t\tif (fields.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tjsonCls.setFields(new ArrayList<>(fields.size()));\n\t\tfor (FieldNode field : fields) {\n\t\t\tJsonFieldMapping jsonField = new JsonFieldMapping();\n\t\t\tjsonField.setName(field.getName());\n\t\t\tjsonField.setAlias(field.getAlias());\n\t\t\tjsonCls.getFields().add(jsonField);\n\t\t}\n\t}\n\n\tprivate JsonMappingGen() {\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/json/cls/JsonClass.java",
    "content": "package jadx.core.codegen.json.cls;\n\nimport java.util.List;\n\nimport com.google.gson.annotations.SerializedName;\n\npublic class JsonClass extends JsonNode {\n\t@SerializedName(\"package\")\n\tprivate String pkg;\n\tprivate String type; // class, interface, enum\n\t@SerializedName(\"extends\")\n\tprivate String superClass;\n\t@SerializedName(\"implements\")\n\tprivate List<String> interfaces;\n\tprivate String dex;\n\n\tprivate List<JsonField> fields;\n\tprivate List<JsonMethod> methods;\n\tprivate List<JsonClass> innerClasses;\n\n\tprivate List<String> imports;\n\n\tpublic String getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setType(String type) {\n\t\tthis.type = type;\n\t}\n\n\tpublic String getSuperClass() {\n\t\treturn superClass;\n\t}\n\n\tpublic void setSuperClass(String superClass) {\n\t\tthis.superClass = superClass;\n\t}\n\n\tpublic List<String> getInterfaces() {\n\t\treturn interfaces;\n\t}\n\n\tpublic void setInterfaces(List<String> interfaces) {\n\t\tthis.interfaces = interfaces;\n\t}\n\n\tpublic List<JsonField> getFields() {\n\t\treturn fields;\n\t}\n\n\tpublic void setFields(List<JsonField> fields) {\n\t\tthis.fields = fields;\n\t}\n\n\tpublic List<JsonMethod> getMethods() {\n\t\treturn methods;\n\t}\n\n\tpublic void setMethods(List<JsonMethod> methods) {\n\t\tthis.methods = methods;\n\t}\n\n\tpublic List<JsonClass> getInnerClasses() {\n\t\treturn innerClasses;\n\t}\n\n\tpublic void setInnerClasses(List<JsonClass> innerClasses) {\n\t\tthis.innerClasses = innerClasses;\n\t}\n\n\tpublic String getPkg() {\n\t\treturn pkg;\n\t}\n\n\tpublic void setPkg(String pkg) {\n\t\tthis.pkg = pkg;\n\t}\n\n\tpublic String getDex() {\n\t\treturn dex;\n\t}\n\n\tpublic void setDex(String dex) {\n\t\tthis.dex = dex;\n\t}\n\n\tpublic List<String> getImports() {\n\t\treturn imports;\n\t}\n\n\tpublic void setImports(List<String> imports) {\n\t\tthis.imports = imports;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/json/cls/JsonCodeLine.java",
    "content": "package jadx.core.codegen.json.cls;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic class JsonCodeLine {\n\tprivate String code;\n\tprivate String offset;\n\tprivate Integer sourceLine;\n\n\tpublic String getCode() {\n\t\treturn code;\n\t}\n\n\tpublic void setCode(String code) {\n\t\tthis.code = code;\n\t}\n\n\tpublic String getOffset() {\n\t\treturn offset;\n\t}\n\n\tpublic void setOffset(String offset) {\n\t\tthis.offset = offset;\n\t}\n\n\tpublic Integer getSourceLine() {\n\t\treturn sourceLine;\n\t}\n\n\tpublic void setSourceLine(@Nullable Integer sourceLine) {\n\t\tthis.sourceLine = sourceLine;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/json/cls/JsonField.java",
    "content": "package jadx.core.codegen.json.cls;\n\npublic class JsonField extends JsonNode {\n\tString type;\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/json/cls/JsonMethod.java",
    "content": "package jadx.core.codegen.json.cls;\n\nimport java.util.List;\n\npublic class JsonMethod extends JsonNode {\n\tprivate String signature;\n\tprivate String returnType;\n\tprivate List<String> arguments;\n\tprivate List<JsonCodeLine> lines;\n\tprivate String offset;\n\n\tpublic String getSignature() {\n\t\treturn signature;\n\t}\n\n\tpublic void setSignature(String signature) {\n\t\tthis.signature = signature;\n\t}\n\n\tpublic String getReturnType() {\n\t\treturn returnType;\n\t}\n\n\tpublic void setReturnType(String returnType) {\n\t\tthis.returnType = returnType;\n\t}\n\n\tpublic List<String> getArguments() {\n\t\treturn arguments;\n\t}\n\n\tpublic void setArguments(List<String> arguments) {\n\t\tthis.arguments = arguments;\n\t}\n\n\tpublic List<JsonCodeLine> getLines() {\n\t\treturn lines;\n\t}\n\n\tpublic void setLines(List<JsonCodeLine> lines) {\n\t\tthis.lines = lines;\n\t}\n\n\tpublic String getOffset() {\n\t\treturn offset;\n\t}\n\n\tpublic void setOffset(String offset) {\n\t\tthis.offset = offset;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/json/cls/JsonNode.java",
    "content": "package jadx.core.codegen.json.cls;\n\npublic class JsonNode {\n\tprivate String name;\n\tprivate String alias;\n\tprivate String declaration;\n\tprivate int accessFlags;\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getAlias() {\n\t\treturn alias;\n\t}\n\n\tpublic void setAlias(String alias) {\n\t\tthis.alias = alias;\n\t}\n\n\tpublic String getDeclaration() {\n\t\treturn declaration;\n\t}\n\n\tpublic void setDeclaration(String declaration) {\n\t\tthis.declaration = declaration;\n\t}\n\n\tpublic int getAccessFlags() {\n\t\treturn accessFlags;\n\t}\n\n\tpublic void setAccessFlags(int accessFlags) {\n\t\tthis.accessFlags = accessFlags;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/json/mapping/JsonClsMapping.java",
    "content": "package jadx.core.codegen.json.mapping;\n\nimport java.util.List;\n\npublic class JsonClsMapping {\n\tprivate String name;\n\tprivate String alias;\n\n\tprivate String json;\n\tprivate boolean inner;\n\tprivate String topClass;\n\n\tprivate List<JsonFieldMapping> fields;\n\tprivate List<JsonMthMapping> methods;\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getAlias() {\n\t\treturn alias;\n\t}\n\n\tpublic void setAlias(String alias) {\n\t\tthis.alias = alias;\n\t}\n\n\tpublic String getJson() {\n\t\treturn json;\n\t}\n\n\tpublic void setJson(String json) {\n\t\tthis.json = json;\n\t}\n\n\tpublic boolean isInner() {\n\t\treturn inner;\n\t}\n\n\tpublic void setInner(boolean inner) {\n\t\tthis.inner = inner;\n\t}\n\n\tpublic String getTopClass() {\n\t\treturn topClass;\n\t}\n\n\tpublic void setTopClass(String topClass) {\n\t\tthis.topClass = topClass;\n\t}\n\n\tpublic List<JsonFieldMapping> getFields() {\n\t\treturn fields;\n\t}\n\n\tpublic void setFields(List<JsonFieldMapping> fields) {\n\t\tthis.fields = fields;\n\t}\n\n\tpublic List<JsonMthMapping> getMethods() {\n\t\treturn methods;\n\t}\n\n\tpublic void setMethods(List<JsonMthMapping> methods) {\n\t\tthis.methods = methods;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/json/mapping/JsonFieldMapping.java",
    "content": "package jadx.core.codegen.json.mapping;\n\npublic class JsonFieldMapping {\n\tprivate String name;\n\tprivate String alias;\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getAlias() {\n\t\treturn alias;\n\t}\n\n\tpublic void setAlias(String alias) {\n\t\tthis.alias = alias;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/json/mapping/JsonMapping.java",
    "content": "package jadx.core.codegen.json.mapping;\n\nimport java.util.List;\n\npublic class JsonMapping {\n\tprivate List<JsonClsMapping> classes;\n\n\tpublic List<JsonClsMapping> getClasses() {\n\t\treturn classes;\n\t}\n\n\tpublic void setClasses(List<JsonClsMapping> classes) {\n\t\tthis.classes = classes;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/json/mapping/JsonMthMapping.java",
    "content": "package jadx.core.codegen.json.mapping;\n\npublic class JsonMthMapping {\n\tprivate String signature;\n\tprivate String name;\n\tprivate String alias;\n\tprivate String offset;\n\n\tpublic String getSignature() {\n\t\treturn signature;\n\t}\n\n\tpublic void setSignature(String signature) {\n\t\tthis.signature = signature;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getAlias() {\n\t\treturn alias;\n\t}\n\n\tpublic void setAlias(String alias) {\n\t\tthis.alias = alias;\n\t}\n\n\tpublic String getOffset() {\n\t\treturn offset;\n\t}\n\n\tpublic void setOffset(String offset) {\n\t\tthis.offset = offset;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/utils/CodeComment.java",
    "content": "package jadx.core.codegen.utils;\n\nimport jadx.api.data.CommentStyle;\nimport jadx.api.data.ICodeComment;\n\npublic class CodeComment {\n\n\tprivate final String comment;\n\tprivate final CommentStyle style;\n\n\tpublic CodeComment(String comment, CommentStyle style) {\n\t\tthis.comment = comment;\n\t\tthis.style = style;\n\t}\n\n\tpublic CodeComment(ICodeComment comment) {\n\t\tthis(comment.getComment(), comment.getStyle());\n\t}\n\n\tpublic String getComment() {\n\t\treturn comment;\n\t}\n\n\tpublic CommentStyle getStyle() {\n\t\treturn style;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"CodeComment{\" + style + \": '\" + comment + \"'}\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/codegen/utils/CodeGenUtils.java",
    "content": "package jadx.core.codegen.utils;\n\nimport java.util.List;\nimport java.util.function.BiConsumer;\nimport java.util.regex.Pattern;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.CommentsLevel;\nimport jadx.api.ICodeWriter;\nimport jadx.api.data.CommentStyle;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.types.SourceFileAttr;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.IAttributeNode;\nimport jadx.core.dex.attributes.nodes.JadxCommentsAttr;\nimport jadx.core.dex.attributes.nodes.JadxError;\nimport jadx.core.dex.attributes.nodes.NotificationAttrNode;\nimport jadx.core.dex.attributes.nodes.RenameReasonAttr;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.instructions.args.CodeVar;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.ICodeNode;\nimport jadx.core.utils.Utils;\n\npublic class CodeGenUtils {\n\n\tpublic static void addErrorsAndComments(ICodeWriter code, NotificationAttrNode node) {\n\t\taddComments(code, node);\n\t\taddErrors(code, node);\n\t}\n\n\tpublic static void addErrors(ICodeWriter code, NotificationAttrNode node) {\n\t\tif (!node.checkCommentsLevel(CommentsLevel.ERROR)) {\n\t\t\treturn;\n\t\t}\n\t\tList<JadxError> errors = node.getAll(AType.JADX_ERROR);\n\t\tif (!errors.isEmpty()) {\n\t\t\terrors.stream().distinct().sorted().forEach(err -> {\n\t\t\t\taddError(code, err.getError(), err.getCause());\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic static void addError(ICodeWriter code, String errMsg, Throwable cause) {\n\t\tcode.startLine(\"/*  JADX ERROR: \").add(errMsg);\n\t\tif (cause != null) {\n\t\t\tcode.incIndent();\n\t\t\tUtils.appendStackTrace(code, cause);\n\t\t\tcode.decIndent();\n\t\t}\n\t\tcode.add(\"*/\");\n\t}\n\n\tpublic static void addComments(ICodeWriter code, NotificationAttrNode node) {\n\t\tJadxCommentsAttr commentsAttr = node.get(AType.JADX_COMMENTS);\n\t\tif (commentsAttr != null) {\n\t\t\tcommentsAttr.formatAndFilter(node.getCommentsLevel())\n\t\t\t\t\t.forEach(comment -> code.startLine(\"/* \").addMultiLine(comment).add(\" */\"));\n\t\t}\n\t\taddCodeComments(code, node, node);\n\t}\n\n\tpublic static void addCodeComments(ICodeWriter code, NotificationAttrNode parent, @Nullable IAttributeNode node) {\n\t\tif (node == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (parent.checkCommentsLevel(CommentsLevel.USER_ONLY)) {\n\t\t\taddCodeComments(code, node);\n\t\t}\n\t}\n\n\tprivate static void addCodeComments(ICodeWriter code, @Nullable IAttributeNode node) {\n\t\tif (node == null) {\n\t\t\treturn;\n\t\t}\n\t\tboolean startNewLine = node instanceof ICodeNode; // add on same line for instructions\n\t\tfor (CodeComment comment : node.getAll(AType.CODE_COMMENTS)) {\n\t\t\taddCodeComment(code, comment, startNewLine);\n\t\t}\n\t}\n\n\tprivate static void addCodeComment(ICodeWriter code, CodeComment comment, boolean startNewLine) {\n\t\tif (startNewLine) {\n\t\t\tcode.startLine();\n\t\t} else {\n\t\t\tcode.add(' ');\n\t\t}\n\t\taddCommentWithStyle(code, comment.getStyle(), comment.getComment());\n\t}\n\n\tpublic static void addJadxNodeComment(ICodeWriter code, NotificationAttrNode node,\n\t\t\tCommentsLevel level, BiConsumer<ICodeWriter, String> commentFunc) {\n\t\tif (node.checkCommentsLevel(level)) {\n\t\t\tcode.startLine();\n\t\t\taddCommentWithStyle(code, CommentStyle.BLOCK_CONDENSED, (commentCode, newLinePrefix) -> {\n\t\t\t\tcommentCode.add(\"JADX \").add(level.name()).add(\": \");\n\t\t\t\tcommentFunc.accept(commentCode, newLinePrefix);\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic static void addJadxComment(ICodeWriter code, CommentsLevel level, String commentStr) {\n\t\tcode.startLine();\n\t\taddCommentWithStyle(code, CommentStyle.BLOCK_CONDENSED, \"JADX \" + level.name() + \": \" + commentStr);\n\t}\n\n\tprivate static void addCommentWithStyle(ICodeWriter code, CommentStyle style, String commentStr) {\n\t\tappendMultiLineString(code, \"\", style.getStart());\n\t\tappendMultiLineString(code, style.getOnNewLine(), commentStr);\n\t\tappendMultiLineString(code, \"\", style.getEnd());\n\t}\n\n\t/**\n\t * Insert comment with function, use second arg as new line prefix\n\t */\n\tprivate static void addCommentWithStyle(ICodeWriter code, CommentStyle style, BiConsumer<ICodeWriter, String> commentFunc) {\n\t\tappendMultiLineString(code, \"\", style.getStart());\n\t\tcommentFunc.accept(code, style.getOnNewLine());\n\t\tappendMultiLineString(code, \"\", style.getEnd());\n\t}\n\n\tprivate static final Pattern NEW_LINE_PATTERN = Pattern.compile(\"\\\\R\");\n\n\tprivate static void appendMultiLineString(ICodeWriter code, String onNewLine, String str) {\n\t\tString[] lines = NEW_LINE_PATTERN.split(str);\n\t\tint linesCount = lines.length;\n\t\tif (linesCount == 0) {\n\t\t\treturn;\n\t\t}\n\t\tcode.add(lines[0]);\n\t\tfor (int i = 1; i < linesCount; i++) {\n\t\t\tcode.startLine(onNewLine);\n\t\t\tcode.add(lines[i]);\n\t\t}\n\t}\n\n\tpublic static void addClassRenamedComment(ICodeWriter code, ClassNode cls) {\n\t\tClassInfo classInfo = cls.getClassInfo();\n\t\tif (classInfo.hasAlias()) {\n\t\t\taddRenamedComment(code, cls, classInfo.getType().getObject());\n\t\t}\n\t}\n\n\tpublic static void addRenamedComment(ICodeWriter code, NotificationAttrNode node, String origName) {\n\t\taddJadxNodeComment(code, node, CommentsLevel.INFO, (commentCode, newLinePrefix) -> {\n\t\t\tcommentCode.add(\"renamed from: \").add(origName);\n\t\t\tRenameReasonAttr renameReasonAttr = node.get(AType.RENAME_REASON);\n\t\t\tif (renameReasonAttr != null) {\n\t\t\t\tcommentCode.add(\", reason: \").add(renameReasonAttr.getDescription());\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic static void addSourceFileInfo(ICodeWriter code, ClassNode node) {\n\t\tif (!node.checkCommentsLevel(CommentsLevel.INFO)) {\n\t\t\treturn;\n\t\t}\n\t\tSourceFileAttr sourceFileAttr = node.get(JadxAttrType.SOURCE_FILE);\n\t\tif (sourceFileAttr != null) {\n\t\t\tString fileName = sourceFileAttr.getFileName();\n\t\t\tString topClsName = node.getTopParentClass().getClassInfo().getShortName();\n\t\t\tif (topClsName.contains(fileName)) {\n\t\t\t\t// ignore similar name\n\t\t\t\treturn;\n\t\t\t}\n\t\t\taddJadxComment(code, CommentsLevel.INFO, \"compiled from: \" + fileName);\n\t\t}\n\t}\n\n\tpublic static void addInputFileInfo(ICodeWriter code, ClassNode cls) {\n\t\tif (cls.checkCommentsLevel(CommentsLevel.INFO) && cls.getClsData() != null) {\n\t\t\tString inputFileName = cls.getClsData().getInputFileName();\n\t\t\tif (inputFileName != null) {\n\t\t\t\tClassNode declCls = cls.getDeclaringClass();\n\t\t\t\tif (declCls != null\n\t\t\t\t\t\t&& declCls.getClsData() != null\n\t\t\t\t\t\t&& inputFileName.equals(declCls.getClsData().getInputFileName())) {\n\t\t\t\t\t// don't add same comment for inner classes\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\taddJadxComment(code, CommentsLevel.INFO, \"loaded from: \" + inputFileName);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static CodeVar getCodeVar(RegisterArg arg) {\n\t\tSSAVar svar = arg.getSVar();\n\t\tif (svar != null) {\n\t\t\treturn svar.getCodeVar();\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate CodeGenUtils() {\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/deobf/DeobfAliasProvider.java",
    "content": "package jadx.core.deobf;\n\nimport jadx.api.deobf.IAliasProvider;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.StringUtils;\n\npublic class DeobfAliasProvider implements IAliasProvider {\n\n\tprivate int pkgIndex = 0;\n\tprivate int clsIndex = 0;\n\tprivate int fldIndex = 0;\n\tprivate int mthIndex = 0;\n\n\tprivate int maxLength;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tthis.maxLength = root.getArgs().getDeobfuscationMaxLength();\n\t}\n\n\t@Override\n\tpublic void initIndexes(int pkg, int cls, int fld, int mth) {\n\t\tpkgIndex = pkg;\n\t\tclsIndex = cls;\n\t\tfldIndex = fld;\n\t\tmthIndex = mth;\n\t}\n\n\t@Override\n\tpublic String forPackage(PackageNode pkg) {\n\t\treturn String.format(\"p%03d%s\", pkgIndex++, prepareNamePart(pkg.getPkgInfo().getName()));\n\t}\n\n\t@Override\n\tpublic String forClass(ClassNode cls) {\n\t\tString prefix = makeClsPrefix(cls);\n\t\treturn String.format(\"%sC%04d%s\", prefix, clsIndex++, prepareNamePart(cls.getName()));\n\t}\n\n\t@Override\n\tpublic String forField(FieldNode fld) {\n\t\treturn String.format(\"f%d%s\", fldIndex++, prepareNamePart(fld.getName()));\n\t}\n\n\t@Override\n\tpublic String forMethod(MethodNode mth) {\n\t\tString prefix = mth.contains(AType.METHOD_OVERRIDE) ? \"mo\" : \"m\";\n\t\treturn String.format(\"%s%d%s\", prefix, mthIndex++, prepareNamePart(mth.getName()));\n\t}\n\n\tprivate String prepareNamePart(String name) {\n\t\tif (name.length() > maxLength) {\n\t\t\treturn 'x' + Integer.toHexString(name.hashCode());\n\t\t}\n\t\treturn NameMapper.removeInvalidCharsMiddle(name);\n\t}\n\n\t/**\n\t * Generate a prefix for a class name that bases on certain class properties, certain\n\t * extended superclasses or implemented interfaces.\n\t */\n\tprivate String makeClsPrefix(ClassNode cls) {\n\t\tif (cls.isEnum()) {\n\t\t\treturn \"Enum\";\n\t\t}\n\t\tStringBuilder result = new StringBuilder();\n\t\tif (cls.getAccessFlags().isInterface()) {\n\t\t\tresult.append(\"Interface\");\n\t\t} else if (cls.getAccessFlags().isAbstract()) {\n\t\t\tresult.append(\"Abstract\");\n\t\t}\n\t\tresult.append(getBaseName(cls));\n\t\treturn result.toString();\n\t}\n\n\t/**\n\t * Process current class and all super classes to get meaningful parent name\n\t */\n\tprivate static String getBaseName(ClassNode cls) {\n\t\tClassNode currentCls = cls;\n\t\twhile (currentCls != null) {\n\t\t\tArgType superCls = currentCls.getSuperClass();\n\t\t\tif (superCls != null) {\n\t\t\t\tString superClsName = superCls.getObject();\n\t\t\t\tif (superClsName.startsWith(\"android.app.\") // e.g. Activity or Fragment\n\t\t\t\t\t\t|| superClsName.startsWith(\"android.os.\") // e.g. AsyncTask\n\t\t\t\t) {\n\t\t\t\t\treturn getClsName(superClsName);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (ArgType interfaceType : cls.getInterfaces()) {\n\t\t\t\tString name = interfaceType.getObject();\n\t\t\t\tif (name.equals(\"java.lang.Runnable\")) {\n\t\t\t\t\treturn \"Runnable\";\n\t\t\t\t}\n\t\t\t\tif (name.startsWith(\"java.util.concurrent.\") // e.g. Callable\n\t\t\t\t\t\t|| name.startsWith(\"android.view.\") // e.g. View.OnClickListener\n\t\t\t\t\t\t|| name.startsWith(\"android.content.\") // e.g. DialogInterface.OnClickListener\n\t\t\t\t) {\n\t\t\t\t\treturn getClsName(name);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (superCls == null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcurrentCls = cls.root().resolveClass(superCls);\n\t\t}\n\t\treturn \"\";\n\t}\n\n\tprivate static String getClsName(String name) {\n\t\tint pgkEnd = name.lastIndexOf('.');\n\t\tString clsName = name.substring(pgkEnd + 1);\n\t\treturn StringUtils.removeChar(clsName, '$');\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java",
    "content": "package jadx.core.deobf;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.StandardOpenOption;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.args.GeneratedRenamesMappingFileMode;\nimport jadx.api.deobf.IAliasProvider;\nimport jadx.api.deobf.impl.AlwaysRename;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.files.FileUtils;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\n\npublic class DeobfPresets {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DeobfPresets.class);\n\n\tprivate static final Charset MAP_FILE_CHARSET = UTF_8;\n\n\tprivate final Path deobfMapFile;\n\n\tprivate final Map<String, String> pkgPresetMap = new HashMap<>();\n\tprivate final Map<String, String> clsPresetMap = new HashMap<>();\n\tprivate final Map<String, String> fldPresetMap = new HashMap<>();\n\tprivate final Map<String, String> mthPresetMap = new HashMap<>();\n\n\tpublic static DeobfPresets build(RootNode root) {\n\t\tPath deobfMapPath = getPathDeobfMapPath(root);\n\t\tif (root.getArgs().getGeneratedRenamesMappingFileMode() != GeneratedRenamesMappingFileMode.IGNORE) {\n\t\t\tLOG.debug(\"Deobfuscation map file set to: {}\", deobfMapPath);\n\t\t}\n\t\treturn new DeobfPresets(deobfMapPath);\n\t}\n\n\tprivate static Path getPathDeobfMapPath(RootNode root) {\n\t\tJadxArgs jadxArgs = root.getArgs();\n\t\tFile deobfMapFile = jadxArgs.getGeneratedRenamesMappingFile();\n\t\tif (deobfMapFile != null) {\n\t\t\treturn deobfMapFile.toPath();\n\t\t}\n\t\tPath inputFilePath = jadxArgs.getInputFiles().get(0).toPath().toAbsolutePath();\n\t\tString baseName = FileUtils.getPathBaseName(inputFilePath);\n\t\treturn inputFilePath.getParent().resolve(baseName + \".jobf\");\n\t}\n\n\tprivate DeobfPresets(Path deobfMapFile) {\n\t\tthis.deobfMapFile = deobfMapFile;\n\t}\n\n\t/**\n\t * Loads deobfuscator presets\n\t */\n\tpublic boolean load() {\n\t\tif (!Files.exists(deobfMapFile)) {\n\t\t\treturn false;\n\t\t}\n\t\tLOG.info(\"Loading obfuscation map from: {}\", deobfMapFile.toAbsolutePath());\n\t\ttry {\n\t\t\tList<String> lines = Files.readAllLines(deobfMapFile, MAP_FILE_CHARSET);\n\t\t\tfor (String l : lines) {\n\t\t\t\tl = l.trim();\n\t\t\t\tif (l.isEmpty() || l.startsWith(\"#\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tString[] va = splitAndTrim(l);\n\t\t\t\tif (va.length != 2) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tString origName = va[0];\n\t\t\t\tString alias = va[1];\n\t\t\t\tswitch (l.charAt(0)) {\n\t\t\t\t\tcase 'p':\n\t\t\t\t\t\tpkgPresetMap.put(origName, alias);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'c':\n\t\t\t\t\t\tclsPresetMap.put(origName, alias);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'f':\n\t\t\t\t\t\tfldPresetMap.put(origName, alias);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'm':\n\t\t\t\t\t\tmthPresetMap.put(origName, alias);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'v':\n\t\t\t\t\t\t// deprecated\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to load deobfuscation map file '{}'\", deobfMapFile.toAbsolutePath(), e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate static String[] splitAndTrim(String str) {\n\t\tString[] v = str.substring(2).split(\"=\");\n\t\tfor (int i = 0; i < v.length; i++) {\n\t\t\tv[i] = v[i].trim();\n\t\t}\n\t\treturn v;\n\t}\n\n\tpublic void save() throws IOException {\n\t\tList<String> list = new ArrayList<>();\n\t\tfor (Map.Entry<String, String> pkgEntry : pkgPresetMap.entrySet()) {\n\t\t\tlist.add(String.format(\"p %s = %s\", pkgEntry.getKey(), pkgEntry.getValue()));\n\t\t}\n\t\tfor (Map.Entry<String, String> clsEntry : clsPresetMap.entrySet()) {\n\t\t\tlist.add(String.format(\"c %s = %s\", clsEntry.getKey(), clsEntry.getValue()));\n\t\t}\n\t\tfor (Map.Entry<String, String> fldEntry : fldPresetMap.entrySet()) {\n\t\t\tlist.add(String.format(\"f %s = %s\", fldEntry.getKey(), fldEntry.getValue()));\n\t\t}\n\t\tfor (Map.Entry<String, String> mthEntry : mthPresetMap.entrySet()) {\n\t\t\tlist.add(String.format(\"m %s = %s\", mthEntry.getKey(), mthEntry.getValue()));\n\t\t}\n\t\tCollections.sort(list);\n\t\tif (list.isEmpty()) {\n\t\t\tif (LOG.isDebugEnabled()) {\n\t\t\t\tLOG.debug(\"Deobfuscation map is empty, not saving it\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tFiles.write(deobfMapFile, list, MAP_FILE_CHARSET,\n\t\t\t\tStandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);\n\t\tLOG.info(\"Deobfuscation map file saved as: {}\", deobfMapFile);\n\t}\n\n\tpublic void fill(RootNode root) {\n\t\tfor (PackageNode pkg : root.getPackages()) {\n\t\t\tif (pkg.isLeaf()) { // ignore middle packages\n\t\t\t\tif (pkg.hasParentAlias()) {\n\t\t\t\t\tpkgPresetMap.put(pkg.getPkgInfo().getFullName(), pkg.getAliasPkgInfo().getFullName());\n\t\t\t\t} else if (pkg.hasAlias()) {\n\t\t\t\t\tpkgPresetMap.put(pkg.getPkgInfo().getFullName(), pkg.getAliasPkgInfo().getName());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (ClassNode cls : root.getClasses()) {\n\t\t\tClassInfo classInfo = cls.getClassInfo();\n\t\t\tif (classInfo.hasAlias()) {\n\t\t\t\tclsPresetMap.put(classInfo.makeRawFullName(), classInfo.getAliasShortName());\n\t\t\t}\n\t\t\tfor (FieldNode fld : cls.getFields()) {\n\t\t\t\tFieldInfo fieldInfo = fld.getFieldInfo();\n\t\t\t\tif (fieldInfo.hasAlias()) {\n\t\t\t\t\tfldPresetMap.put(fieldInfo.getRawFullId(), fld.getAlias());\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\t\tMethodInfo methodInfo = mth.getMethodInfo();\n\t\t\t\tif (methodInfo.hasAlias()) {\n\t\t\t\t\tmthPresetMap.put(methodInfo.getRawFullId(), methodInfo.getAlias());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void apply(RootNode root) {\n\t\tDeobfuscatorVisitor.process(root,\n\t\t\t\tAlwaysRename.INSTANCE,\n\t\t\t\tnew IAliasProvider() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic String forPackage(PackageNode pkg) {\n\t\t\t\t\t\treturn pkgPresetMap.get(pkg.getPkgInfo().getFullName());\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic String forClass(ClassNode cls) {\n\t\t\t\t\t\treturn getForCls(cls.getClassInfo());\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic String forField(FieldNode fld) {\n\t\t\t\t\t\treturn getForFld(fld.getFieldInfo());\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic String forMethod(MethodNode mth) {\n\t\t\t\t\t\treturn getForMth(mth.getMethodInfo());\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\tpublic void initIndexes(IAliasProvider aliasProvider) {\n\t\taliasProvider.initIndexes(pkgPresetMap.size(), clsPresetMap.size(), fldPresetMap.size(), mthPresetMap.size());\n\t}\n\n\tpublic String getForCls(ClassInfo cls) {\n\t\tif (clsPresetMap.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn clsPresetMap.get(cls.makeRawFullName());\n\t}\n\n\tpublic String getForFld(FieldInfo fld) {\n\t\tif (fldPresetMap.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn fldPresetMap.get(fld.getRawFullId());\n\t}\n\n\tpublic String getForMth(MethodInfo mth) {\n\t\tif (mthPresetMap.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn mthPresetMap.get(mth.getRawFullId());\n\t}\n\n\tpublic void clear() {\n\t\tpkgPresetMap.clear();\n\t\tclsPresetMap.clear();\n\t\tfldPresetMap.clear();\n\t\tmthPresetMap.clear();\n\t}\n\n\tpublic Path getDeobfMapFile() {\n\t\treturn deobfMapFile;\n\t}\n\n\tpublic Map<String, String> getPkgPresetMap() {\n\t\treturn pkgPresetMap;\n\t}\n\n\tpublic Map<String, String> getClsPresetMap() {\n\t\treturn clsPresetMap;\n\t}\n\n\tpublic Map<String, String> getFldPresetMap() {\n\t\treturn fldPresetMap;\n\t}\n\n\tpublic Map<String, String> getMthPresetMap() {\n\t\treturn mthPresetMap;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/deobf/DeobfuscatorVisitor.java",
    "content": "package jadx.core.deobf;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.deobf.IAliasProvider;\nimport jadx.api.deobf.IRenameCondition;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.utils.exceptions.JadxException;\n\npublic class DeobfuscatorVisitor extends AbstractVisitor {\n\n\t@Override\n\tpublic void init(RootNode root) throws JadxException {\n\t\tJadxArgs args = root.getArgs();\n\t\tif (!args.isDeobfuscationOn()) {\n\t\t\treturn;\n\t\t}\n\t\tDeobfPresets mapping = DeobfPresets.build(root);\n\t\tif (args.getGeneratedRenamesMappingFileMode().shouldRead()) {\n\t\t\tif (mapping.load()) {\n\t\t\t\tmapping.apply(root);\n\t\t\t}\n\t\t}\n\t\tIAliasProvider aliasProvider = args.getAliasProvider();\n\t\tIRenameCondition renameCondition = args.getRenameCondition();\n\t\tmapping.initIndexes(aliasProvider);\n\t\tprocess(root, renameCondition, aliasProvider);\n\t}\n\n\tpublic static void process(RootNode root, IRenameCondition renameCondition, IAliasProvider aliasProvider) {\n\t\tboolean pkgUpdated = false;\n\t\tfor (PackageNode pkg : root.getPackages()) {\n\t\t\tif (renameCondition.shouldRename(pkg)) {\n\t\t\t\tString alias = aliasProvider.forPackage(pkg);\n\t\t\t\tif (alias != null) {\n\t\t\t\t\tpkg.rename(alias, false);\n\t\t\t\t\tpkgUpdated = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (pkgUpdated) {\n\t\t\troot.runPackagesUpdate();\n\t\t}\n\n\t\tfor (ClassNode cls : root.getClasses()) {\n\t\t\tif (renameCondition.shouldRename(cls)) {\n\t\t\t\tString clsAlias = aliasProvider.forClass(cls);\n\t\t\t\tif (clsAlias != null) {\n\t\t\t\t\tcls.rename(clsAlias);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (FieldNode fld : cls.getFields()) {\n\t\t\t\tif (renameCondition.shouldRename(fld)) {\n\t\t\t\t\tString fldAlias = aliasProvider.forField(fld);\n\t\t\t\t\tif (fldAlias != null) {\n\t\t\t\t\t\tfld.rename(fldAlias);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\t\tif (renameCondition.shouldRename(mth)) {\n\t\t\t\t\tString mthAlias = aliasProvider.forMethod(mth);\n\t\t\t\t\tif (mthAlias != null) {\n\t\t\t\t\t\tmth.rename(mthAlias);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"DeobfuscatorVisitor\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/deobf/FileTypeDetector.java",
    "content": "package jadx.core.deobf;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\n\nimport org.w3c.dom.Document;\n\nimport jadx.core.utils.FileSignature;\nimport jadx.core.utils.StringUtils;\n\npublic class FileTypeDetector {\n\tprivate static final Pattern DOCTYPE_PATTERN = Pattern.compile(\"\\\\s*<!doctype *(\\\\w+)[ >]\", Pattern.CASE_INSENSITIVE);\n\tprivate static final List<FileSignature> FILE_SIGNATURES = new ArrayList<>();\n\n\tstatic {\n\t\tregister(\"png\", \"89 50 4E 47\");\n\t\tregister(\"jpg\", \"FF D8 FF\");\n\t\tregister(\"gif\", \"47 49 46 38\");\n\t\tregister(\"webp\", \"52 49 46 46 ?? ?? ?? ?? 57 45 42 50 56 50 38\");\n\t\tregister(\"bmp\", \"42 4D\");\n\t\tregister(\"bmp\", \"42 41\");\n\t\tregister(\"bmp\", \"43 49\");\n\t\tregister(\"bmp\", \"43 50\");\n\t\tregister(\"bmp\", \"49 43\");\n\t\tregister(\"bmp\", \"50 54\");\n\t\tregister(\"mp4\", \"00 00 00 ?? 66 74 79 70 69 73 6F 36\");\n\t\tregister(\"mp4\", \"00 00 00 ?? 66 74 79 70 6D 70 34 32\");\n\t\tregister(\"m4a\", \"00 00 00 ?? 66 74 79 70 4D 34 41 20\");\n\t\tregister(\"mp3\", \"49 44 33\");\n\t\tregister(\"ogg\", \"4F 67 67 53\");\n\t\tregister(\"wav\", \"52 49 46 46 ?? ?? ?? ?? 57 41 56 45\");\n\t\tregister(\"ttf\", \"00 01 00 00\");\n\t\tregister(\"ttc\", \"74 74 63 66\");\n\t\tregister(\"otf\", \"4F 54 54 4F\");\n\t\tregister(\"xml\", \"03 00 08 00\");\n\t}\n\n\tpublic static void register(String fileType, String signature) {\n\t\tFILE_SIGNATURES.add(new FileSignature(fileType, signature));\n\t}\n\n\tprivate static String detectByHeaders(byte[] data) {\n\t\tfor (FileSignature sig : FILE_SIGNATURES) {\n\t\t\tif (FileSignature.matches(sig, data)) {\n\t\t\t\tif (sig.getFileType().equals(\"png\") && isNinePatch(data)) {\n\t\t\t\t\treturn \".9.png\";\n\t\t\t\t}\n\t\t\t\treturn \".\" + sig.getFileType();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static String detectFileExtension(byte[] data) {\n\t\t// detect ext by headers\n\t\tString extByHeaders = detectByHeaders(data);\n\t\tif (!StringUtils.isEmpty(extByHeaders)) {\n\t\t\treturn extByHeaders;\n\t\t}\n\n\t\t// detect ext by readable text\n\t\tString text = new String(data, StandardCharsets.UTF_8);\n\t\tif (text.startsWith(\"-----BEGIN CERTIFICATE-----\")) {\n\t\t\treturn \".cer\";\n\t\t}\n\t\tif (text.startsWith(\"-----BEGIN PRIVATE KEY-----\")) {\n\t\t\treturn \".key\";\n\t\t}\n\t\tif (text.contains(\"<html>\")) {\n\t\t\treturn \".html\";\n\t\t}\n\t\tMatcher m = DOCTYPE_PATTERN.matcher(text);\n\t\tif (m.lookingAt()) {\n\t\t\treturn \".\" + m.group(1).toLowerCase();\n\t\t}\n\n\t\ttry {\n\t\t\tDocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\n\t\t\tfactory.setNamespaceAware(true);\n\t\t\tfactory.setFeature(\"http://apache.org/xml/features/nonvalidating/load-external-dtd\", false);\n\t\t\tfactory.setFeature(\"http://xml.org/sax/features/external-general-entities\", false);\n\t\t\tfactory.setFeature(\"http://xml.org/sax/features/external-parameter-entities\", false);\n\n\t\t\tDocumentBuilder builder = factory.newDocumentBuilder();\n\t\t\tDocument doc = builder.parse(new java.io.ByteArrayInputStream(data));\n\t\t\tString rootTag = doc.getDocumentElement().getNodeName();\n\n\t\t\tif (\"svg\".equalsIgnoreCase(rootTag)) {\n\t\t\t\treturn \".svg\";\n\t\t\t}\n\t\t\tif (\"plist\".equalsIgnoreCase(rootTag)) {\n\t\t\t\treturn \".plist\";\n\t\t\t}\n\t\t\tif (\"kml\".equalsIgnoreCase(rootTag)) {\n\t\t\t\treturn \".kml\";\n\t\t\t}\n\t\t\treturn \".xml\";\n\t\t} catch (Exception ignored) {\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate static int readInt(byte[] data, int offset) {\n\t\treturn (data[offset] & 0xFF) << 24\n\t\t\t\t| (data[offset + 1] & 0xFF) << 16\n\t\t\t\t| (data[offset + 2] & 0xFF) << 8\n\t\t\t\t| (data[offset + 3] & 0xFF);\n\t}\n\n\tprivate static boolean isNinePatch(byte[] data) {\n\t\tint offset = 8;\n\t\twhile (offset + 8 < data.length) {\n\t\t\tint chunkLength = readInt(data, offset);\n\t\t\tint chunkType = readInt(data, offset + 4);\n\t\t\tif (chunkType == 0x6e705463) { // 'npTc'\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\toffset += 8 + chunkLength + 4; // chunk + data + CRC\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/deobf/NameMapper.java",
    "content": "package jadx.core.deobf;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.regex.Pattern;\n\nimport jadx.core.utils.StringUtils;\n\nimport static jadx.core.utils.StringUtils.notEmpty;\n\npublic class NameMapper {\n\n\tpublic static final Pattern VALID_JAVA_IDENTIFIER = Pattern.compile(\n\t\t\t\"\\\\p{javaJavaIdentifierStart}\\\\p{javaJavaIdentifierPart}*\");\n\n\tprivate static final Pattern VALID_JAVA_FULL_IDENTIFIER = Pattern.compile(\n\t\t\t\"(\" + VALID_JAVA_IDENTIFIER + \"\\\\.)*\" + VALID_JAVA_IDENTIFIER);\n\n\tprivate static final Set<String> RESERVED_NAMES = new HashSet<>(\n\t\t\tArrays.asList(\n\t\t\t\t\t\"_\",\n\t\t\t\t\t\"abstract\",\n\t\t\t\t\t\"assert\",\n\t\t\t\t\t\"boolean\",\n\t\t\t\t\t\"break\",\n\t\t\t\t\t\"byte\",\n\t\t\t\t\t\"case\",\n\t\t\t\t\t\"catch\",\n\t\t\t\t\t\"char\",\n\t\t\t\t\t\"class\",\n\t\t\t\t\t\"const\",\n\t\t\t\t\t\"continue\",\n\t\t\t\t\t\"default\",\n\t\t\t\t\t\"do\",\n\t\t\t\t\t\"double\",\n\t\t\t\t\t\"else\",\n\t\t\t\t\t\"enum\",\n\t\t\t\t\t\"extends\",\n\t\t\t\t\t\"false\",\n\t\t\t\t\t\"final\",\n\t\t\t\t\t\"finally\",\n\t\t\t\t\t\"float\",\n\t\t\t\t\t\"for\",\n\t\t\t\t\t\"goto\",\n\t\t\t\t\t\"if\",\n\t\t\t\t\t\"implements\",\n\t\t\t\t\t\"import\",\n\t\t\t\t\t\"instanceof\",\n\t\t\t\t\t\"int\",\n\t\t\t\t\t\"interface\",\n\t\t\t\t\t\"long\",\n\t\t\t\t\t\"native\",\n\t\t\t\t\t\"new\",\n\t\t\t\t\t\"null\",\n\t\t\t\t\t\"package\",\n\t\t\t\t\t\"private\",\n\t\t\t\t\t\"protected\",\n\t\t\t\t\t\"public\",\n\t\t\t\t\t\"return\",\n\t\t\t\t\t\"short\",\n\t\t\t\t\t\"static\",\n\t\t\t\t\t\"strictfp\",\n\t\t\t\t\t\"super\",\n\t\t\t\t\t\"switch\",\n\t\t\t\t\t\"synchronized\",\n\t\t\t\t\t\"this\",\n\t\t\t\t\t\"throw\",\n\t\t\t\t\t\"throws\",\n\t\t\t\t\t\"transient\",\n\t\t\t\t\t\"true\",\n\t\t\t\t\t\"try\",\n\t\t\t\t\t\"void\",\n\t\t\t\t\t\"volatile\",\n\t\t\t\t\t\"while\"));\n\n\tpublic static boolean isReserved(String str) {\n\t\treturn RESERVED_NAMES.contains(str);\n\t}\n\n\tpublic static boolean isValidIdentifier(String str) {\n\t\treturn notEmpty(str)\n\t\t\t\t&& !isReserved(str)\n\t\t\t\t&& VALID_JAVA_IDENTIFIER.matcher(str).matches();\n\t}\n\n\tpublic static boolean isValidFullIdentifier(String str) {\n\t\treturn notEmpty(str)\n\t\t\t\t&& !isReserved(str)\n\t\t\t\t&& VALID_JAVA_FULL_IDENTIFIER.matcher(str).matches();\n\t}\n\n\tpublic static boolean isValidAndPrintable(String str) {\n\t\treturn isValidIdentifier(str) && isAllCharsPrintable(str);\n\t}\n\n\tpublic static boolean isValidIdentifierStart(int codePoint) {\n\t\treturn Character.isJavaIdentifierStart(codePoint);\n\t}\n\n\tpublic static boolean isValidIdentifierPart(int codePoint) {\n\t\treturn Character.isJavaIdentifierPart(codePoint);\n\t}\n\n\tpublic static boolean isPrintableChar(char c) {\n\t\treturn 32 <= c && c <= 126;\n\t}\n\n\tpublic static boolean isPrintableAsciiCodePoint(int c) {\n\t\treturn 32 <= c && c <= 126;\n\t}\n\n\tpublic static boolean isPrintableCodePoint(int codePoint) {\n\t\tif (Character.isISOControl(codePoint)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (Character.isWhitespace(codePoint)) {\n\t\t\t// don't print whitespaces other than standard one\n\t\t\treturn codePoint == ' ';\n\t\t}\n\t\tswitch (Character.getType(codePoint)) {\n\t\t\tcase Character.CONTROL:\n\t\t\tcase Character.FORMAT:\n\t\t\tcase Character.PRIVATE_USE:\n\t\t\tcase Character.SURROGATE:\n\t\t\tcase Character.UNASSIGNED:\n\t\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static boolean isAllCharsPrintable(String str) {\n\t\tint len = str.length();\n\t\tint offset = 0;\n\t\twhile (offset < len) {\n\t\t\tint codePoint = str.codePointAt(offset);\n\t\t\tif (!isPrintableAsciiCodePoint(codePoint)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\toffset += Character.charCount(codePoint);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Return modified string with removed:\n\t * <ul>\n\t * <li>not printable chars (including unicode)\n\t * <li>chars not valid for java identifier part\n\t * </ul>\n\t * Note: this 'middle' method must be used with prefixed string:\n\t * <ul>\n\t * <li>can leave invalid chars for java identifier start (i.e numbers)\n\t * <li>result not checked for reserved words\n\t * </ul>\n\t */\n\tpublic static String removeInvalidCharsMiddle(String name) {\n\t\tif (isValidIdentifier(name) && isAllCharsPrintable(name)) {\n\t\t\treturn name;\n\t\t}\n\t\tint len = name.length();\n\t\tStringBuilder sb = new StringBuilder(len);\n\t\tStringUtils.visitCodePoints(name, codePoint -> {\n\t\t\tif (isPrintableAsciiCodePoint(codePoint) && isValidIdentifierPart(codePoint)) {\n\t\t\t\tsb.appendCodePoint(codePoint);\n\t\t\t}\n\t\t});\n\t\treturn sb.toString();\n\t}\n\n\t/**\n\t * Return string with removed invalid chars, see {@link #removeInvalidCharsMiddle}\n\t * <p>\n\t * Prepend prefix if first char is not valid as java identifier start char.\n\t */\n\tpublic static String removeInvalidChars(String name, String prefix) {\n\t\tString result = removeInvalidCharsMiddle(name);\n\t\tif (!result.isEmpty()) {\n\t\t\tint codePoint = result.codePointAt(0);\n\t\t\tif (!isValidIdentifierStart(codePoint)) {\n\t\t\t\treturn prefix + result;\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic static String removeNonPrintableCharacters(String name) {\n\t\tStringBuilder sb = new StringBuilder(name.length());\n\t\tStringUtils.visitCodePoints(name, codePoint -> {\n\t\t\tif (isPrintableAsciiCodePoint(codePoint)) {\n\t\t\t\tsb.appendCodePoint(codePoint);\n\t\t\t}\n\t\t});\n\t\treturn sb.toString();\n\t}\n\n\tprivate NameMapper() {\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/deobf/SaveDeobfMapping.java",
    "content": "package jadx.core.deobf;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.args.GeneratedRenamesMappingFileMode;\nimport jadx.core.codegen.json.JsonMappingGen;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.utils.exceptions.JadxException;\n\npublic class SaveDeobfMapping extends AbstractVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SaveDeobfMapping.class);\n\n\t@Override\n\tpublic void init(RootNode root) throws JadxException {\n\t\tJadxArgs args = root.getArgs();\n\t\tif (args.isDeobfuscationOn() || !args.isJsonOutput()) {\n\t\t\tsaveMappings(root);\n\t\t}\n\t\tif (args.isJsonOutput()) {\n\t\t\tJsonMappingGen.dump(root);\n\t\t}\n\t}\n\n\tprivate void saveMappings(RootNode root) {\n\t\tGeneratedRenamesMappingFileMode mode = root.getArgs().getGeneratedRenamesMappingFileMode();\n\t\tif (!mode.shouldWrite()) {\n\t\t\treturn;\n\t\t}\n\t\tDeobfPresets mapping = DeobfPresets.build(root);\n\t\tPath deobfMapFile = mapping.getDeobfMapFile();\n\t\tif (mode == GeneratedRenamesMappingFileMode.READ_OR_SAVE && Files.exists(deobfMapFile)) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tmapping.clear();\n\t\t\tmapping.fill(root);\n\t\t\tmapping.save();\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to save deobfuscation map file '{}'\", deobfMapFile.toAbsolutePath(), e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"SaveDeobfMapping\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/deobf/conditions/AbstractDeobfCondition.java",
    "content": "package jadx.core.deobf.conditions;\n\nimport jadx.api.deobf.IDeobfCondition;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.dex.nodes.RootNode;\n\npublic abstract class AbstractDeobfCondition implements IDeobfCondition {\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t}\n\n\t@Override\n\tpublic Action check(PackageNode pkg) {\n\t\treturn Action.NO_ACTION;\n\t}\n\n\t@Override\n\tpublic Action check(ClassNode cls) {\n\t\treturn Action.NO_ACTION;\n\t}\n\n\t@Override\n\tpublic Action check(FieldNode fld) {\n\t\treturn Action.NO_ACTION;\n\t}\n\n\t@Override\n\tpublic Action check(MethodNode mth) {\n\t\treturn Action.NO_ACTION;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/deobf/conditions/AvoidClsAndPkgNamesCollision.java",
    "content": "package jadx.core.deobf.conditions;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.dex.nodes.RootNode;\n\npublic class AvoidClsAndPkgNamesCollision extends AbstractDeobfCondition {\n\n\tprivate final Set<String> avoidClsNames = new HashSet<>();\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tavoidClsNames.clear();\n\t\tfor (PackageNode pkg : root.getPackages()) {\n\t\t\tavoidClsNames.add(pkg.getName());\n\t\t}\n\t}\n\n\t@Override\n\tpublic Action check(ClassNode cls) {\n\t\tif (avoidClsNames.contains(cls.getAlias())) {\n\t\t\treturn Action.FORCE_RENAME;\n\t\t}\n\t\treturn Action.NO_ACTION;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/deobf/conditions/BaseDeobfCondition.java",
    "content": "package jadx.core.deobf.conditions;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.PackageNode;\n\n/**\n * Disable deobfuscation for nodes:\n * - with 'DONT_RENAME' flag\n * - already renamed\n */\npublic class BaseDeobfCondition extends AbstractDeobfCondition {\n\n\t@Override\n\tpublic Action check(PackageNode pkg) {\n\t\tif (pkg.contains(AFlag.DONT_RENAME) || pkg.hasAlias()) {\n\t\t\treturn Action.FORBID_RENAME;\n\t\t}\n\t\treturn Action.NO_ACTION;\n\t}\n\n\t@Override\n\tpublic Action check(ClassNode cls) {\n\t\tif (cls.contains(AFlag.DONT_RENAME) || cls.getClassInfo().hasAlias()) {\n\t\t\treturn Action.FORBID_RENAME;\n\t\t}\n\t\treturn Action.NO_ACTION;\n\t}\n\n\t@Override\n\tpublic Action check(MethodNode mth) {\n\t\tif (mth.contains(AFlag.DONT_RENAME)\n\t\t\t\t|| mth.getMethodInfo().hasAlias()\n\t\t\t\t|| mth.isConstructor()) {\n\t\t\treturn Action.FORBID_RENAME;\n\t\t}\n\t\treturn Action.NO_ACTION;\n\t}\n\n\t@Override\n\tpublic Action check(FieldNode fld) {\n\t\tif (fld.contains(AFlag.DONT_RENAME) || fld.getFieldInfo().hasAlias()) {\n\t\t\treturn Action.FORBID_RENAME;\n\t\t}\n\t\treturn Action.NO_ACTION;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/deobf/conditions/DeobfLengthCondition.java",
    "content": "package jadx.core.deobf.conditions;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.deobf.IDeobfCondition;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.dex.nodes.RootNode;\n\npublic class DeobfLengthCondition implements IDeobfCondition {\n\n\tprivate int minLength;\n\tprivate int maxLength;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tJadxArgs args = root.getArgs();\n\t\tthis.minLength = args.getDeobfuscationMinLength();\n\t\tthis.maxLength = args.getDeobfuscationMaxLength();\n\t}\n\n\tprivate Action checkName(String s) {\n\t\tint len = s.length();\n\t\tif (len < minLength || len > maxLength) {\n\t\t\treturn Action.FORCE_RENAME;\n\t\t}\n\t\treturn Action.NO_ACTION;\n\t}\n\n\t@Override\n\tpublic Action check(PackageNode pkg) {\n\t\treturn checkName(pkg.getName());\n\t}\n\n\t@Override\n\tpublic Action check(ClassNode cls) {\n\t\treturn checkName(cls.getName());\n\t}\n\n\t@Override\n\tpublic Action check(FieldNode fld) {\n\t\treturn checkName(fld.getName());\n\t}\n\n\t@Override\n\tpublic Action check(MethodNode mth) {\n\t\treturn checkName(mth.getName());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/deobf/conditions/DeobfWhitelist.java",
    "content": "package jadx.core.deobf.conditions;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.Utils;\n\npublic class DeobfWhitelist extends AbstractDeobfCondition {\n\n\tpublic static final List<String> DEFAULT_LIST = Arrays.asList(\n\t\t\t\"android.support.v4.*\",\n\t\t\t\"android.support.v7.*\",\n\t\t\t\"android.support.v4.os.*\",\n\t\t\t\"android.support.annotation.Px\",\n\t\t\t\"androidx.core.os.*\",\n\t\t\t\"androidx.annotation.Px\");\n\n\tpublic static final String DEFAULT_STR = Utils.listToString(DEFAULT_LIST, \" \");\n\n\tprivate final Set<String> packages = new HashSet<>();\n\tprivate final Set<String> classes = new HashSet<>();\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tpackages.clear();\n\t\tclasses.clear();\n\t\tfor (String whitelistItem : root.getArgs().getDeobfuscationWhitelist()) {\n\t\t\tif (!whitelistItem.isEmpty()) {\n\t\t\t\tif (whitelistItem.endsWith(\".*\")) {\n\t\t\t\t\tpackages.add(whitelistItem.substring(0, whitelistItem.length() - 2));\n\t\t\t\t} else {\n\t\t\t\t\tclasses.add(whitelistItem);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic Action check(PackageNode pkg) {\n\t\tif (packages.contains(pkg.getPkgInfo().getFullName())) {\n\t\t\treturn Action.FORBID_RENAME;\n\t\t}\n\t\treturn Action.NO_ACTION;\n\t}\n\n\t@Override\n\tpublic Action check(ClassNode cls) {\n\t\tif (classes.contains(cls.getClassInfo().getFullName())) {\n\t\t\treturn Action.FORBID_RENAME;\n\t\t}\n\t\treturn Action.NO_ACTION;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/deobf/conditions/ExcludeAndroidRClass.java",
    "content": "package jadx.core.deobf.conditions;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic class ExcludeAndroidRClass extends AbstractDeobfCondition {\n\n\t@Override\n\tpublic Action check(ClassNode cls) {\n\t\tif (isR(cls.getTopParentClass())) {\n\t\t\treturn Action.FORBID_RENAME;\n\t\t}\n\t\treturn Action.NO_ACTION;\n\t}\n\n\tprivate static boolean isR(ClassNode cls) {\n\t\tif (cls.contains(AFlag.ANDROID_R_CLASS)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!cls.getClassInfo().getShortName().equals(\"R\")) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!cls.getMethods().isEmpty() || !cls.getFields().isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (ClassNode inner : cls.getInnerClasses()) {\n\t\t\tfor (MethodNode m : inner.getMethods()) {\n\t\t\t\tif (!m.getMethodInfo().isConstructor() && !m.getMethodInfo().isClassInit()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (FieldNode field : cls.getFields()) {\n\t\t\t\tArgType type = field.getType();\n\t\t\t\tif (type != ArgType.INT && (!type.isArray() || type.getArrayElement() != ArgType.INT)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcls.add(AFlag.ANDROID_R_CLASS);\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/deobf/conditions/ExcludePackageWithTLDNames.java",
    "content": "package jadx.core.deobf.conditions;\n\nimport java.io.BufferedReader;\nimport java.io.InputStreamReader;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n/**\n * Provides a list of all top level domains, so we can exclude them from deobfuscation.\n */\npublic class ExcludePackageWithTLDNames extends AbstractDeobfCondition {\n\n\t/**\n\t * Lazy load TLD set\n\t */\n\tprivate static class TldHolder {\n\t\tprivate static final Set<String> TLD_SET = loadTldSet();\n\t}\n\n\tprivate static Set<String> loadTldSet() {\n\t\ttry (BufferedReader reader = new BufferedReader(new InputStreamReader(TldHolder.class.getResourceAsStream(\"tlds.txt\")))) {\n\t\t\treturn reader.lines()\n\t\t\t\t\t.filter(line -> !line.startsWith(\"#\") && !line.isEmpty())\n\t\t\t\t\t.collect(Collectors.toSet());\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to load top level domain list file: tlds.txt\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Action check(PackageNode pkg) {\n\t\tif (pkg.isRoot() && TldHolder.TLD_SET.contains(pkg.getName())) {\n\t\t\treturn Action.FORBID_RENAME;\n\t\t}\n\t\treturn Action.NO_ACTION;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/deobf/conditions/JadxRenameConditions.java",
    "content": "package jadx.core.deobf.conditions;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.api.deobf.IDeobfCondition;\nimport jadx.api.deobf.IRenameCondition;\nimport jadx.api.deobf.impl.CombineDeobfConditions;\n\npublic class JadxRenameConditions {\n\n\t/**\n\t * This method provides a mutable list of default deobfuscation conditions used by jadx.\n\t * To build {@link IRenameCondition} use {@link CombineDeobfConditions#combine(List)} method.\n\t */\n\tpublic static List<IDeobfCondition> buildDefaultDeobfConditions() {\n\t\tList<IDeobfCondition> list = new ArrayList<>();\n\t\tlist.add(new BaseDeobfCondition());\n\t\tlist.add(new DeobfWhitelist());\n\t\tlist.add(new ExcludePackageWithTLDNames());\n\t\tlist.add(new ExcludeAndroidRClass());\n\t\tlist.add(new AvoidClsAndPkgNamesCollision());\n\t\tlist.add(new DeobfLengthCondition());\n\t\treturn list;\n\t}\n\n\tpublic static IRenameCondition buildDefault() {\n\t\treturn CombineDeobfConditions.combine(buildDefaultDeobfConditions());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java",
    "content": "package jadx.core.dex.attributes;\n\npublic enum AFlag {\n\tMTH_ENTER_BLOCK,\n\tMTH_EXIT_BLOCK,\n\n\tTRY_ENTER,\n\tTRY_LEAVE,\n\n\tLOOP_START,\n\tLOOP_END,\n\n\tSYNTHETIC,\n\n\tRETURN, // block contains only return instruction\n\tORIG_RETURN,\n\n\tDONT_WRAP,\n\tDONT_INLINE,\n\tDONT_INLINE_CONST,\n\tDONT_INVERT, // don't invert this if statement\n\tDONT_GENERATE, // process as usual, but don't output to generated code\n\tCOMMENT_OUT, // process as usual, but comment insn in generated code\n\tREMOVE, // can be completely removed\n\tREMOVE_SUPER_CLASS, // don't add super class\n\n\tHIDDEN, // instruction used inside other instruction but not listed in args\n\tCONVERTED_ENUM, // enum class successfully restored to original form\n\n\tDONT_RENAME, // do not rename during deobfuscation\n\tFORCE_RAW_NAME, // force use of raw name instead alias\n\n\tADDED_TO_REGION,\n\n\t// this loop condition has been merged or otherwise shouldn't be subject to the 1 instruction limit\n\tALLOW_MULTIPLE_INSNS_LOOP_COND,\n\n\tEXC_TOP_SPLITTER,\n\tEXC_BOTTOM_SPLITTER,\n\tFINALLY_INSNS,\n\tIGNORE_THROW_SPLIT,\n\n\tSKIP_FIRST_ARG,\n\tSKIP_ARG, // skip argument in invoke call\n\tNO_SKIP_ARGS,\n\n\tANONYMOUS_CONSTRUCTOR,\n\tINLINE_INSTANCE_FIELD,\n\n\tTHIS,\n\tSUPER,\n\n\tPACKAGE_INFO,\n\n\t/**\n\t * Mark Android resources class\n\t */\n\tANDROID_R_CLASS,\n\n\t/**\n\t * RegisterArg attribute for method arguments\n\t */\n\tMETHOD_ARGUMENT,\n\n\t/**\n\t * Type of RegisterArg or SSAVar can't be changed\n\t */\n\tIMMUTABLE_TYPE,\n\n\t/**\n\t * Force inline instruction with inline assign\n\t */\n\tFORCE_ASSIGN_INLINE,\n\n\t/**\n\t * A MOVE instruction has been inlined\n\t */\n\tMOVE_INLINED,\n\n\tCUSTOM_DECLARE, // variable for this register don't need declaration\n\tDECLARE_VAR,\n\n\tELSE_IF_CHAIN,\n\n\tWRAPPED,\n\tARITH_ONEARG,\n\n\tFALL_THROUGH,\n\n\tVARARG_CALL,\n\n\t/**\n\t * Use constants with explicit type: cast '(byte) 1' or type letter '7L'\n\t */\n\tEXPLICIT_PRIMITIVE_TYPE,\n\tEXPLICIT_CAST,\n\tSOFT_CAST, // synthetic cast to help type inference (allow unchecked casts for generics)\n\n\tINCONSISTENT_CODE, // warning about incorrect decompilation\n\n\tREQUEST_IF_REGION_OPTIMIZE, // run if region visitor again\n\tREQUEST_CODE_SHRINK,\n\n\tMETHOD_CANDIDATE_FOR_INLINE,\n\tUSE_LINES_HINTS, // source lines info in methods can be trusted\n\n\tDISABLE_BLOCKS_LOCK,\n\n\t// Class processing flags\n\tRESTART_CODEGEN, // codegen must be executed again\n\tRELOAD_AT_CODEGEN_STAGE, // class can't be analyzed at 'process' stage => unload before 'codegen' stage\n\tCLASS_DEEP_RELOAD, // perform deep class unload (reload) before process\n\tCLASS_UNLOADED, // class was completely unloaded\n\n\tDONT_UNLOAD_CLASS, // don't unload class after code generation (only for tests and debug!)\n\n\tRESOLVE_JAVA_JSR,\n\tCOMPUTE_POST_DOM,\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/AType.java",
    "content": "package jadx.core.dex.attributes;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.codegen.utils.CodeComment;\nimport jadx.core.dex.attributes.nodes.AnonymousClassAttr;\nimport jadx.core.dex.attributes.nodes.ClassTypeVarsAttr;\nimport jadx.core.dex.attributes.nodes.CodeFeaturesAttr;\nimport jadx.core.dex.attributes.nodes.DeclareVariablesAttr;\nimport jadx.core.dex.attributes.nodes.DecompileModeOverrideAttr;\nimport jadx.core.dex.attributes.nodes.EdgeInsnAttr;\nimport jadx.core.dex.attributes.nodes.EnumClassAttr;\nimport jadx.core.dex.attributes.nodes.EnumMapAttr;\nimport jadx.core.dex.attributes.nodes.ExcSplitCrossAttr;\nimport jadx.core.dex.attributes.nodes.FieldReplaceAttr;\nimport jadx.core.dex.attributes.nodes.ForceReturnAttr;\nimport jadx.core.dex.attributes.nodes.GenericInfoAttr;\nimport jadx.core.dex.attributes.nodes.InlinedAttr;\nimport jadx.core.dex.attributes.nodes.JadxCommentsAttr;\nimport jadx.core.dex.attributes.nodes.JadxError;\nimport jadx.core.dex.attributes.nodes.JumpInfo;\nimport jadx.core.dex.attributes.nodes.LocalVarsDebugInfoAttr;\nimport jadx.core.dex.attributes.nodes.LoopInfo;\nimport jadx.core.dex.attributes.nodes.LoopLabelAttr;\nimport jadx.core.dex.attributes.nodes.MethodBridgeAttr;\nimport jadx.core.dex.attributes.nodes.MethodInlineAttr;\nimport jadx.core.dex.attributes.nodes.MethodOverrideAttr;\nimport jadx.core.dex.attributes.nodes.MethodReplaceAttr;\nimport jadx.core.dex.attributes.nodes.MethodThrowsAttr;\nimport jadx.core.dex.attributes.nodes.MethodTypeVarsAttr;\nimport jadx.core.dex.attributes.nodes.PhiListAttr;\nimport jadx.core.dex.attributes.nodes.RegDebugInfoAttr;\nimport jadx.core.dex.attributes.nodes.RegionRefAttr;\nimport jadx.core.dex.attributes.nodes.RenameReasonAttr;\nimport jadx.core.dex.attributes.nodes.SkipMethodArgsAttr;\nimport jadx.core.dex.attributes.nodes.SpecialEdgeAttr;\nimport jadx.core.dex.attributes.nodes.TmpEdgeAttr;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.dex.trycatch.CatchAttr;\nimport jadx.core.dex.trycatch.ExcHandlerAttr;\nimport jadx.core.dex.trycatch.TryCatchBlockAttr;\n\n/**\n * Attribute types enumeration,\n * uses generic type for omit cast after 'AttributeStorage.get' method\n *\n * @param <T> attribute class implementation\n */\npublic final class AType<T extends IJadxAttribute> implements IJadxAttrType<T> {\n\n\t// class, method, field, insn\n\tpublic static final AType<AttrList<CodeComment>> CODE_COMMENTS = new AType<>();\n\n\t// class, method, field\n\tpublic static final AType<RenameReasonAttr> RENAME_REASON = new AType<>();\n\n\t// class, method\n\tpublic static final AType<AttrList<JadxError>> JADX_ERROR = new AType<>(); // code failed to decompile\n\tpublic static final AType<JadxCommentsAttr> JADX_COMMENTS = new AType<>(); // additional info about decompilation\n\n\t// class\n\tpublic static final AType<EnumClassAttr> ENUM_CLASS = new AType<>();\n\tpublic static final AType<EnumMapAttr> ENUM_MAP = new AType<>();\n\tpublic static final AType<ClassTypeVarsAttr> CLASS_TYPE_VARS = new AType<>();\n\tpublic static final AType<AnonymousClassAttr> ANONYMOUS_CLASS = new AType<>();\n\tpublic static final AType<InlinedAttr> INLINED = new AType<>();\n\tpublic static final AType<DecompileModeOverrideAttr> DECOMPILE_MODE_OVERRIDE = new AType<>();\n\n\t// field\n\tpublic static final AType<FieldInitInsnAttr> FIELD_INIT_INSN = new AType<>();\n\tpublic static final AType<FieldReplaceAttr> FIELD_REPLACE = new AType<>();\n\n\t// method\n\tpublic static final AType<LocalVarsDebugInfoAttr> LOCAL_VARS_DEBUG_INFO = new AType<>();\n\tpublic static final AType<MethodInlineAttr> METHOD_INLINE = new AType<>();\n\tpublic static final AType<MethodReplaceAttr> METHOD_REPLACE = new AType<>();\n\tpublic static final AType<MethodBridgeAttr> BRIDGED_BY = new AType<>();\n\tpublic static final AType<SkipMethodArgsAttr> SKIP_MTH_ARGS = new AType<>();\n\tpublic static final AType<MethodOverrideAttr> METHOD_OVERRIDE = new AType<>();\n\tpublic static final AType<MethodTypeVarsAttr> METHOD_TYPE_VARS = new AType<>();\n\tpublic static final AType<AttrList<TryCatchBlockAttr>> TRY_BLOCKS_LIST = new AType<>();\n\tpublic static final AType<CodeFeaturesAttr> METHOD_CODE_FEATURES = new AType<>();\n\tpublic static final AType<MethodThrowsAttr> METHOD_THROWS = new AType<>();\n\n\t// region\n\tpublic static final AType<DeclareVariablesAttr> DECLARE_VARIABLES = new AType<>();\n\n\t// block\n\tpublic static final AType<PhiListAttr> PHI_LIST = new AType<>();\n\tpublic static final AType<ForceReturnAttr> FORCE_RETURN = new AType<>();\n\tpublic static final AType<AttrList<LoopInfo>> LOOP = new AType<>();\n\tpublic static final AType<AttrList<EdgeInsnAttr>> EDGE_INSN = new AType<>();\n\tpublic static final AType<AttrList<SpecialEdgeAttr>> SPECIAL_EDGE = new AType<>();\n\tpublic static final AType<TmpEdgeAttr> TMP_EDGE = new AType<>();\n\tpublic static final AType<TryCatchBlockAttr> TRY_BLOCK = new AType<>();\n\tpublic static final AType<ExcSplitCrossAttr> EXC_SPLIT_CROSS = new AType<>();\n\n\t// block or insn\n\tpublic static final AType<ExcHandlerAttr> EXC_HANDLER = new AType<>();\n\tpublic static final AType<CatchAttr> EXC_CATCH = new AType<>();\n\n\t// instruction\n\tpublic static final AType<LoopLabelAttr> LOOP_LABEL = new AType<>();\n\tpublic static final AType<AttrList<JumpInfo>> JUMP = new AType<>();\n\tpublic static final AType<IMethodDetails> METHOD_DETAILS = new AType<>();\n\tpublic static final AType<GenericInfoAttr> GENERIC_INFO = new AType<>();\n\tpublic static final AType<RegionRefAttr> REGION_REF = new AType<>();\n\n\t// register\n\tpublic static final AType<RegDebugInfoAttr> REG_DEBUG_INFO = new AType<>();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/AttrList.java",
    "content": "package jadx.core.dex.attributes;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.utils.Utils;\n\npublic class AttrList<T> implements IJadxAttribute {\n\n\tprivate static final int MAX_ATTRLIST_LENGTH = 300;\n\n\tprivate final IJadxAttrType<AttrList<T>> type;\n\tprivate final List<T> list = new ArrayList<>();\n\n\tpublic AttrList(IJadxAttrType<AttrList<T>> type) {\n\t\tthis.type = type;\n\t}\n\n\tpublic List<T> getList() {\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<AttrList<T>> getAttrType() {\n\t\treturn type;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tString commaDelimited = Utils.listToString(list, \", \");\n\t\t// if the comma delimited list is too long, use newlines instead to maintain readability\n\t\tif (commaDelimited.length() > MAX_ATTRLIST_LENGTH) {\n\t\t\treturn Utils.listToString(list, \"\\n    \");\n\t\t}\n\t\treturn commaDelimited;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/AttrNode.java",
    "content": "package jadx.core.dex.attributes;\n\nimport java.util.List;\n\nimport jadx.api.CommentsLevel;\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.Consts;\nimport jadx.core.dex.attributes.nodes.JadxCommentsAttr;\nimport jadx.core.utils.Utils;\n\npublic abstract class AttrNode implements IAttributeNode {\n\n\tprivate static final AttributeStorage EMPTY_ATTR_STORAGE = EmptyAttrStorage.INSTANCE;\n\n\tprivate AttributeStorage storage = EMPTY_ATTR_STORAGE;\n\n\t@Override\n\tpublic void add(AFlag flag) {\n\t\tinitStorage().add(flag);\n\t\tif (Consts.DEBUG_ATTRIBUTES) {\n\t\t\taddDebugComment(\"Add flag \" + flag + \" at \" + Utils.currentStackTrace(2));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void addAttr(IJadxAttribute attr) {\n\t\tinitStorage().add(attr);\n\t\tif (Consts.DEBUG_ATTRIBUTES) {\n\t\t\taddDebugComment(\"Add attribute \" + attr.getClass().getSimpleName()\n\t\t\t\t\t+ \": \" + attr + \" at \" + Utils.currentStackTrace(2));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void addAttrs(List<IJadxAttribute> list) {\n\t\tif (list.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tinitStorage().add(list);\n\t}\n\n\t@Override\n\tpublic <T> void addAttr(IJadxAttrType<AttrList<T>> type, T obj) {\n\t\tinitStorage().add(type, obj);\n\t\tif (Consts.DEBUG_ATTRIBUTES) {\n\t\t\taddDebugComment(\"Add attribute \" + obj + \" at \" + Utils.currentStackTrace(2));\n\t\t}\n\t}\n\n\tpublic <T> void addAttr(IJadxAttrType<AttrList<T>> type, List<T> list) {\n\t\tAttributeStorage strg = initStorage();\n\t\tlist.forEach(attr -> strg.add(type, attr));\n\t}\n\n\t@Override\n\tpublic void copyAttributesFrom(AttrNode attrNode) {\n\t\tAttributeStorage copyFrom = attrNode.storage;\n\t\tif (!copyFrom.isEmpty()) {\n\t\t\tinitStorage().addAll(copyFrom);\n\t\t}\n\t}\n\n\t@Override\n\tpublic <T extends IJadxAttribute> void copyAttributeFrom(AttrNode attrNode, AType<T> attrType) {\n\t\tIJadxAttribute attr = attrNode.get(attrType);\n\t\tif (attr != null) {\n\t\t\tthis.addAttr(attr);\n\t\t}\n\t}\n\n\t/**\n\t * Remove attribute in this node, add copy from other if exists\n\t */\n\t@Override\n\tpublic <T extends IJadxAttribute> void rewriteAttributeFrom(AttrNode attrNode, AType<T> attrType) {\n\t\tremove(attrType);\n\t\tcopyAttributeFrom(attrNode, attrType);\n\t}\n\n\tprivate AttributeStorage initStorage() {\n\t\tAttributeStorage store = storage;\n\t\tif (store == EMPTY_ATTR_STORAGE) {\n\t\t\tstore = new AttributeStorage();\n\t\t\tstorage = store;\n\t\t}\n\t\treturn store;\n\t}\n\n\tprivate void unloadIfEmpty() {\n\t\tif (storage.isEmpty() && storage != EMPTY_ATTR_STORAGE) {\n\t\t\tstorage = EMPTY_ATTR_STORAGE;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean contains(AFlag flag) {\n\t\treturn storage.contains(flag);\n\t}\n\n\t@Override\n\tpublic <T extends IJadxAttribute> boolean contains(IJadxAttrType<T> type) {\n\t\treturn storage.contains(type);\n\t}\n\n\t@Override\n\tpublic <T extends IJadxAttribute> T get(IJadxAttrType<T> type) {\n\t\treturn storage.get(type);\n\t}\n\n\t@Override\n\tpublic IAnnotation getAnnotation(String cls) {\n\t\treturn storage.getAnnotation(cls);\n\t}\n\n\t@Override\n\tpublic <T> List<T> getAll(IJadxAttrType<AttrList<T>> type) {\n\t\treturn storage.getAll(type);\n\t}\n\n\t@Override\n\tpublic void remove(AFlag flag) {\n\t\tstorage.remove(flag);\n\t\tunloadIfEmpty();\n\t}\n\n\t@Override\n\tpublic <T extends IJadxAttribute> void remove(IJadxAttrType<T> type) {\n\t\tstorage.remove(type);\n\t\tunloadIfEmpty();\n\t}\n\n\t@Override\n\tpublic void removeAttr(IJadxAttribute attr) {\n\t\tstorage.remove(attr);\n\t\tunloadIfEmpty();\n\t}\n\n\t@Override\n\tpublic void clearAttributes() {\n\t\tstorage = EMPTY_ATTR_STORAGE;\n\t}\n\n\tpublic void unloadAttributes() {\n\t\tif (storage == EMPTY_ATTR_STORAGE) {\n\t\t\treturn;\n\t\t}\n\t\tstorage.unloadAttributes();\n\t\tstorage.clearFlags();\n\t\tunloadIfEmpty();\n\t}\n\n\t@Override\n\tpublic List<String> getAttributesStringsList() {\n\t\treturn storage.getAttributeStrings();\n\t}\n\n\t@Override\n\tpublic String getAttributesString() {\n\t\treturn storage.toString();\n\t}\n\n\t@Override\n\tpublic boolean isAttrStorageEmpty() {\n\t\treturn storage.isEmpty();\n\t}\n\n\tprivate void addDebugComment(String msg) {\n\t\tJadxCommentsAttr commentsAttr = get(AType.JADX_COMMENTS);\n\t\tif (commentsAttr == null) {\n\t\t\tcommentsAttr = new JadxCommentsAttr();\n\t\t\tinitStorage().add(commentsAttr);\n\t\t}\n\t\tcommentsAttr.add(CommentsLevel.DEBUG, msg);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/AttributeStorage.java",
    "content": "package jadx.core.dex.attributes;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.IdentityHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationsAttr;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n/**\n * Storage for different attribute types:<br>\n * 1. Flags - boolean attribute (set or not)<br>\n * 2. Attributes - class instance ({@link IJadxAttribute}) associated with an attribute type\n * ({@link IJadxAttrType})<br>\n */\npublic class AttributeStorage {\n\n\tpublic static AttributeStorage fromList(List<IJadxAttribute> list) {\n\t\tAttributeStorage storage = new AttributeStorage();\n\t\tstorage.add(list);\n\t\treturn storage;\n\t}\n\n\tstatic {\n\t\tint flagsCount = AFlag.values().length;\n\t\tif (flagsCount >= 64) {\n\t\t\tthrow new JadxRuntimeException(\"Try to reduce flags count to 64 for use one long in EnumSet, now \" + flagsCount);\n\t\t}\n\t}\n\n\tprivate static final Map<IJadxAttrType<?>, IJadxAttribute> EMPTY_ATTRIBUTES = Collections.emptyMap();\n\n\tprivate final Set<AFlag> flags;\n\tprivate Map<IJadxAttrType<?>, IJadxAttribute> attributes;\n\n\tpublic AttributeStorage() {\n\t\tflags = EnumSet.noneOf(AFlag.class);\n\t\tattributes = EMPTY_ATTRIBUTES;\n\t}\n\n\tpublic void add(AFlag flag) {\n\t\tflags.add(flag);\n\t}\n\n\tpublic void add(IJadxAttribute attr) {\n\t\twriteAttributes(map -> map.put(attr.getAttrType(), attr));\n\t}\n\n\tpublic void add(List<IJadxAttribute> list) {\n\t\twriteAttributes(map -> list.forEach(attr -> map.put(attr.getAttrType(), attr)));\n\t}\n\n\tpublic <T> void add(IJadxAttrType<AttrList<T>> type, T obj) {\n\t\tAttrList<T> list = get(type);\n\t\tif (list == null) {\n\t\t\tlist = new AttrList<>(type);\n\t\t\tadd(list);\n\t\t}\n\t\tlist.getList().add(obj);\n\t}\n\n\tpublic void addAll(AttributeStorage otherList) {\n\t\tflags.addAll(otherList.flags);\n\t\tif (!otherList.attributes.isEmpty()) {\n\t\t\twriteAttributes(m -> m.putAll(otherList.attributes));\n\t\t}\n\t}\n\n\tpublic boolean contains(AFlag flag) {\n\t\treturn flags.contains(flag);\n\t}\n\n\tpublic <T extends IJadxAttribute> boolean contains(IJadxAttrType<T> type) {\n\t\treturn attributes.containsKey(type);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T extends IJadxAttribute> T get(IJadxAttrType<T> type) {\n\t\treturn (T) attributes.get(type);\n\t}\n\n\tpublic IAnnotation getAnnotation(String cls) {\n\t\tAnnotationsAttr aList = get(JadxAttrType.ANNOTATION_LIST);\n\t\treturn aList == null ? null : aList.get(cls);\n\t}\n\n\tpublic <T> List<T> getAll(IJadxAttrType<AttrList<T>> type) {\n\t\tAttrList<T> attrList = get(type);\n\t\tif (attrList == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn Collections.unmodifiableList(attrList.getList());\n\t}\n\n\tpublic void remove(AFlag flag) {\n\t\tflags.remove(flag);\n\t}\n\n\tpublic void clearFlags() {\n\t\tflags.clear();\n\t}\n\n\tpublic <T extends IJadxAttribute> void remove(IJadxAttrType<T> type) {\n\t\tif (!attributes.isEmpty()) {\n\t\t\twriteAttributes(map -> map.remove(type));\n\t\t}\n\t}\n\n\tpublic void remove(IJadxAttribute attr) {\n\t\tif (!attributes.isEmpty()) {\n\t\t\twriteAttributes(map -> {\n\t\t\t\tIJadxAttrType<? extends IJadxAttribute> type = attr.getAttrType();\n\t\t\t\tIJadxAttribute a = map.get(type);\n\t\t\t\tif (a == attr) {\n\t\t\t\t\tmap.remove(type);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate void writeAttributes(Consumer<Map<IJadxAttrType<?>, IJadxAttribute>> mapConsumer) {\n\t\tsynchronized (this) {\n\t\t\tif (attributes == EMPTY_ATTRIBUTES) {\n\t\t\t\tattributes = new IdentityHashMap<>(2); // only 1 or 2 attributes added in most cases\n\t\t\t}\n\t\t\tmapConsumer.accept(attributes);\n\t\t\tif (attributes.isEmpty()) {\n\t\t\t\tattributes = EMPTY_ATTRIBUTES;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void unloadAttributes() {\n\t\tif (attributes.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\twriteAttributes(map -> map.entrySet().removeIf(entry -> !entry.getValue().keepLoaded()));\n\t}\n\n\tpublic List<String> getAttributeStrings() {\n\t\tint size = flags.size() + attributes.size();\n\t\tif (size == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<String> list = new ArrayList<>(size);\n\t\tfor (AFlag a : flags) {\n\t\t\tlist.add(a.toString());\n\t\t}\n\t\tfor (IJadxAttribute a : attributes.values()) {\n\t\t\tlist.add(a.toAttrString());\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic boolean isEmpty() {\n\t\treturn flags.isEmpty() && attributes.isEmpty();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tList<String> list = getAttributeStrings();\n\t\tif (list.isEmpty()) {\n\t\t\treturn \"\";\n\t\t}\n\t\tlist.sort(String::compareTo);\n\t\treturn \"A[\" + Utils.listToString(list) + ']';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/EmptyAttrStorage.java",
    "content": "package jadx.core.dex.attributes;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\n\npublic final class EmptyAttrStorage extends AttributeStorage {\n\n\tpublic static final AttributeStorage INSTANCE = new EmptyAttrStorage();\n\n\tprivate EmptyAttrStorage() {\n\t\t// singleton\n\t}\n\n\t@Override\n\tpublic boolean contains(AFlag flag) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic <T extends IJadxAttribute> boolean contains(IJadxAttrType<T> type) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic <T extends IJadxAttribute> T get(IJadxAttrType<T> type) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic IAnnotation getAnnotation(String cls) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic <T> List<T> getAll(IJadxAttrType<AttrList<T>> type) {\n\t\treturn Collections.emptyList();\n\t}\n\n\t@Override\n\tpublic void remove(AFlag flag) {\n\t\t// ignore\n\t}\n\n\t@Override\n\tpublic <T extends IJadxAttribute> void remove(IJadxAttrType<T> type) {\n\t\t// ignore\n\t}\n\n\t@Override\n\tpublic void remove(IJadxAttribute attr) {\n\t\t// ignore\n\t}\n\n\t@Override\n\tpublic List<String> getAttributeStrings() {\n\t\treturn Collections.emptyList();\n\t}\n\n\t@Override\n\tpublic boolean isEmpty() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/FieldInitInsnAttr.java",
    "content": "package jadx.core.dex.attributes;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\n\nimport static java.util.Objects.requireNonNull;\n\npublic final class FieldInitInsnAttr extends PinnedAttribute {\n\tprivate final MethodNode mth;\n\tprivate final InsnNode insn;\n\n\tpublic FieldInitInsnAttr(MethodNode mth, InsnNode insn) {\n\t\tthis.mth = requireNonNull(mth);\n\t\tthis.insn = requireNonNull(insn);\n\t}\n\n\tpublic InsnNode getInsn() {\n\t\treturn insn;\n\t}\n\n\tpublic MethodNode getInsnMth() {\n\t\treturn mth;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<? extends IJadxAttribute> getAttrType() {\n\t\treturn AType.FIELD_INIT_INSN;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"INIT{\" + insn + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/IAttributeNode.java",
    "content": "package jadx.core.dex.attributes;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\n\npublic interface IAttributeNode {\n\n\tvoid add(AFlag flag);\n\n\tvoid addAttr(IJadxAttribute attr);\n\n\tvoid addAttrs(List<IJadxAttribute> list);\n\n\t<T> void addAttr(IJadxAttrType<AttrList<T>> type, T obj);\n\n\tvoid copyAttributesFrom(AttrNode attrNode);\n\n\t<T extends IJadxAttribute> void copyAttributeFrom(AttrNode attrNode, AType<T> attrType);\n\n\t<T extends IJadxAttribute> void rewriteAttributeFrom(AttrNode attrNode, AType<T> attrType);\n\n\tboolean contains(AFlag flag);\n\n\t<T extends IJadxAttribute> boolean contains(IJadxAttrType<T> type);\n\n\t<T extends IJadxAttribute> T get(IJadxAttrType<T> type);\n\n\tIAnnotation getAnnotation(String cls);\n\n\t<T> List<T> getAll(IJadxAttrType<AttrList<T>> type);\n\n\tvoid remove(AFlag flag);\n\n\t<T extends IJadxAttribute> void remove(IJadxAttrType<T> type);\n\n\tvoid removeAttr(IJadxAttribute attr);\n\n\tvoid clearAttributes();\n\n\tList<String> getAttributesStringsList();\n\n\tString getAttributesString();\n\n\tboolean isAttrStorageEmpty();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/ILineAttributeNode.java",
    "content": "package jadx.core.dex.attributes;\n\npublic interface ILineAttributeNode {\n\tint getSourceLine();\n\n\tvoid setSourceLine(int sourceLine);\n\n\tint getDefPosition();\n\n\tvoid setDefPosition(int pos);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/AnonymousClassAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\n\npublic class AnonymousClassAttr extends PinnedAttribute {\n\n\tpublic enum InlineType {\n\t\tCONSTRUCTOR,\n\t\tINSTANCE_FIELD,\n\t}\n\n\tprivate final ClassNode outerCls;\n\tprivate final ArgType baseType;\n\tprivate final InlineType inlineType;\n\n\tpublic AnonymousClassAttr(ClassNode outerCls, ArgType baseType, InlineType inlineType) {\n\t\tthis.outerCls = outerCls;\n\t\tthis.baseType = baseType;\n\t\tthis.inlineType = inlineType;\n\t}\n\n\tpublic ClassNode getOuterCls() {\n\t\treturn outerCls;\n\t}\n\n\tpublic ArgType getBaseType() {\n\t\treturn baseType;\n\t}\n\n\tpublic InlineType getInlineType() {\n\t\treturn inlineType;\n\t}\n\n\t@Override\n\tpublic AType<AnonymousClassAttr> getAttrType() {\n\t\treturn AType.ANONYMOUS_CLASS;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"AnonymousClass{\" + outerCls + \", base: \" + baseType + \", inline type: \" + inlineType + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/ClassTypeVarsAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.instructions.args.ArgType;\n\npublic class ClassTypeVarsAttr implements IJadxAttribute {\n\tpublic static final ClassTypeVarsAttr EMPTY = new ClassTypeVarsAttr(Collections.emptyList(), Collections.emptyMap());\n\n\t/**\n\t * Type vars defined in current class\n\t */\n\tprivate final List<ArgType> typeVars;\n\n\t/**\n\t * Type vars mapping in current and super types:\n\t * TypeRawObj -> (TypeVarInSuperType -> TypeVarFromThisClass)\n\t */\n\tprivate final Map<String, Map<ArgType, ArgType>> superTypeMaps;\n\n\tpublic ClassTypeVarsAttr(List<ArgType> typeVars, Map<String, Map<ArgType, ArgType>> superTypeMaps) {\n\t\tthis.typeVars = typeVars;\n\t\tthis.superTypeMaps = superTypeMaps;\n\t}\n\n\tpublic List<ArgType> getTypeVars() {\n\t\treturn typeVars;\n\t}\n\n\tpublic Map<ArgType, ArgType> getTypeVarsMapFor(ArgType type) {\n\t\tMap<ArgType, ArgType> typeMap = superTypeMaps.get(type.getObject());\n\t\tif (typeMap == null) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\treturn typeMap;\n\t}\n\n\t@Override\n\tpublic AType<ClassTypeVarsAttr> getAttrType() {\n\t\treturn AType.CLASS_TYPE_VARS;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ClassTypeVarsAttr{\" + typeVars + \", super maps: \" + superTypeMaps + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/CodeFeaturesAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.EnumSet;\nimport java.util.Set;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic class CodeFeaturesAttr implements IJadxAttribute {\n\n\tpublic enum CodeFeature {\n\t\t/**\n\t\t * Code contains switch instruction\n\t\t */\n\t\tSWITCH,\n\n\t\t/**\n\t\t * Code contains new-array instruction\n\t\t */\n\t\tNEW_ARRAY,\n\t}\n\n\tpublic static boolean contains(MethodNode mth, CodeFeature feature) {\n\t\tCodeFeaturesAttr codeFeaturesAttr = mth.get(AType.METHOD_CODE_FEATURES);\n\t\tif (codeFeaturesAttr == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn codeFeaturesAttr.getCodeFeatures().contains(feature);\n\t}\n\n\tpublic static void add(MethodNode mth, CodeFeature feature) {\n\t\tCodeFeaturesAttr codeFeaturesAttr = mth.get(AType.METHOD_CODE_FEATURES);\n\t\tif (codeFeaturesAttr == null) {\n\t\t\tcodeFeaturesAttr = new CodeFeaturesAttr();\n\t\t\tmth.addAttr(codeFeaturesAttr);\n\t\t}\n\t\tcodeFeaturesAttr.getCodeFeatures().add(feature);\n\t}\n\n\tprivate final Set<CodeFeature> codeFeatures = EnumSet.noneOf(CodeFeature.class);\n\n\tpublic Set<CodeFeature> getCodeFeatures() {\n\t\treturn codeFeatures;\n\t}\n\n\t@Override\n\tpublic AType<CodeFeaturesAttr> getAttrType() {\n\t\treturn AType.METHOD_CODE_FEATURES;\n\t}\n\n\t@Override\n\tpublic String toAttrString() {\n\t\treturn \"CodeFeatures{\" + codeFeatures + '}';\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn toAttrString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/DeclareVariablesAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.instructions.args.CodeVar;\nimport jadx.core.utils.Utils;\n\n/**\n * List of variables to be declared at region start.\n */\npublic class DeclareVariablesAttr implements IJadxAttribute {\n\n\tprivate final List<CodeVar> vars = new ArrayList<>();\n\n\tpublic Iterable<CodeVar> getVars() {\n\t\treturn vars;\n\t}\n\n\tpublic void addVar(CodeVar arg) {\n\t\tvars.add(arg);\n\t}\n\n\t@Override\n\tpublic AType<DeclareVariablesAttr> getAttrType() {\n\t\treturn AType.DECLARE_VARIABLES;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"DECL_VAR: \" + Utils.listToString(vars);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/DecompileModeOverrideAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport jadx.api.DecompilationMode;\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\n\npublic class DecompileModeOverrideAttr implements IJadxAttribute {\n\n\tprivate final DecompilationMode mode;\n\n\tpublic DecompileModeOverrideAttr(DecompilationMode mode) {\n\t\tthis.mode = mode;\n\t}\n\n\tpublic DecompilationMode getMode() {\n\t\treturn mode;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<DecompileModeOverrideAttr> getAttrType() {\n\t\treturn AType.DECOMPILE_MODE_OVERRIDE;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"DECOMPILE_MODE_OVERRIDE: \" + mode;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/EdgeInsnAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.Objects;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.AttrList;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.Edge;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic class EdgeInsnAttr implements IJadxAttribute {\n\n\tprivate final BlockNode start;\n\tprivate final BlockNode end;\n\tprivate final InsnNode insn;\n\n\tpublic static void addEdgeInsn(Edge edge, InsnNode insn) {\n\t\taddEdgeInsn(edge.getSource(), edge.getTarget(), insn);\n\t}\n\n\tpublic static void addEdgeInsn(BlockNode start, BlockNode end, InsnNode insn) {\n\t\tEdgeInsnAttr edgeInsnAttr = new EdgeInsnAttr(start, end, insn);\n\t\tif (!start.getAll(AType.EDGE_INSN).contains(edgeInsnAttr)) {\n\t\t\tstart.addAttr(AType.EDGE_INSN, edgeInsnAttr);\n\t\t}\n\t\tif (!end.getAll(AType.EDGE_INSN).contains(edgeInsnAttr)) {\n\t\t\tend.addAttr(AType.EDGE_INSN, edgeInsnAttr);\n\t\t}\n\t}\n\n\tprivate EdgeInsnAttr(BlockNode start, BlockNode end, InsnNode insn) {\n\t\tthis.start = start;\n\t\tthis.end = end;\n\t\tthis.insn = insn;\n\t}\n\n\t@Override\n\tpublic AType<AttrList<EdgeInsnAttr>> getAttrType() {\n\t\treturn AType.EDGE_INSN;\n\t}\n\n\tpublic BlockNode getStart() {\n\t\treturn start;\n\t}\n\n\tpublic BlockNode getEnd() {\n\t\treturn end;\n\t}\n\n\tpublic InsnNode getInsn() {\n\t\treturn insn;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tEdgeInsnAttr that = (EdgeInsnAttr) o;\n\t\treturn start.equals(that.start)\n\t\t\t\t&& end.equals(that.end)\n\t\t\t\t&& insn.isDeepEquals(that.insn);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(start, end, insn);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"EDGE_INSN: \" + start + \"->\" + end + ' ' + insn;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/EnumClassAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic class EnumClassAttr implements IJadxAttribute {\n\n\tpublic static class EnumField {\n\t\tprivate final FieldNode field;\n\t\tprivate final ConstructorInsn constrInsn;\n\t\tprivate final @Nullable String nameStr;\n\t\tprivate ClassNode cls;\n\n\t\tpublic EnumField(FieldNode field, ConstructorInsn co, @Nullable String nameStr) {\n\t\t\tthis.field = field;\n\t\t\tthis.constrInsn = co;\n\t\t\tthis.nameStr = nameStr;\n\t\t}\n\n\t\tpublic FieldNode getField() {\n\t\t\treturn field;\n\t\t}\n\n\t\tpublic ConstructorInsn getConstrInsn() {\n\t\t\treturn constrInsn;\n\t\t}\n\n\t\tpublic ClassNode getCls() {\n\t\t\treturn cls;\n\t\t}\n\n\t\tpublic void setCls(ClassNode cls) {\n\t\t\tthis.cls = cls;\n\t\t}\n\n\t\tpublic @Nullable String getNameStr() {\n\t\t\treturn nameStr;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn field + \"(\" + constrInsn + \") \" + cls;\n\t\t}\n\t}\n\n\tprivate final List<EnumField> fields;\n\tprivate MethodNode staticMethod;\n\n\tpublic EnumClassAttr(List<EnumField> fields) {\n\t\tthis.fields = fields;\n\t}\n\n\tpublic List<EnumField> getFields() {\n\t\treturn fields;\n\t}\n\n\tpublic MethodNode getStaticMethod() {\n\t\treturn staticMethod;\n\t}\n\n\tpublic void setStaticMethod(MethodNode staticMethod) {\n\t\tthis.staticMethod = staticMethod;\n\t}\n\n\t@Override\n\tpublic AType<EnumClassAttr> getAttrType() {\n\t\treturn AType.ENUM_CLASS;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Enum fields: \" + fields;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/EnumMapAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.FieldNode;\n\npublic class EnumMapAttr implements IJadxAttribute {\n\n\tpublic static class KeyValueMap {\n\t\tprivate final Map<Object, Object> map = new HashMap<>();\n\n\t\tpublic Object get(Object key) {\n\t\t\treturn map.get(key);\n\t\t}\n\n\t\tvoid put(Object key, Object value) {\n\t\t\tmap.put(key, value);\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate Map<FieldNode, KeyValueMap> fieldsMap;\n\n\t@Nullable\n\tpublic KeyValueMap getMap(FieldNode field) {\n\t\tif (fieldsMap == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn fieldsMap.get(field);\n\t}\n\n\tpublic void add(FieldNode field, Object key, Object value) {\n\t\tKeyValueMap map = getMap(field);\n\t\tif (map == null) {\n\t\t\tmap = new KeyValueMap();\n\t\t\tif (fieldsMap == null) {\n\t\t\t\tfieldsMap = new HashMap<>();\n\t\t\t}\n\t\t\tfieldsMap.put(field, map);\n\t\t}\n\t\tmap.put(key, value);\n\t}\n\n\tpublic boolean isEmpty() {\n\t\treturn fieldsMap == null || fieldsMap.isEmpty();\n\t}\n\n\t@Override\n\tpublic AType<EnumMapAttr> getAttrType() {\n\t\treturn AType.ENUM_MAP;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Enum fields map: \" + fieldsMap;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/ExcSplitCrossAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.BlockNode;\n\n/**\n * This attribute is set on the new synthetic node that BlockExceptionHandler creates at the bottom\n * of certain try regions. It stores a reference to the original path cross of the bottom of the try\n * region, so that blocks can be restructured to not pass through it when that would create an\n * erroneous loop.\n */\npublic class ExcSplitCrossAttr implements IJadxAttribute {\n\n\tprivate final BlockNode originalPathCross;\n\n\tpublic ExcSplitCrossAttr(BlockNode originalPathCross) {\n\t\tthis.originalPathCross = originalPathCross;\n\t}\n\n\tpublic BlockNode getOriginalPathCross() {\n\t\treturn this.originalPathCross;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<? extends IJadxAttribute> getAttrType() {\n\t\treturn AType.EXC_SPLIT_CROSS;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ExcSplitCross -> \" + originalPathCross.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/FieldReplaceAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.instructions.args.InsnArg;\n\npublic class FieldReplaceAttr extends PinnedAttribute {\n\n\tpublic enum ReplaceWith {\n\t\tCLASS_INSTANCE,\n\t\tVAR\n\t}\n\n\tprivate final ReplaceWith replaceType;\n\tprivate final Object replaceObj;\n\n\tpublic FieldReplaceAttr(ClassInfo cls) {\n\t\tthis.replaceType = ReplaceWith.CLASS_INSTANCE;\n\t\tthis.replaceObj = cls;\n\t}\n\n\tpublic FieldReplaceAttr(InsnArg reg) {\n\t\tthis.replaceType = ReplaceWith.VAR;\n\t\tthis.replaceObj = reg;\n\t}\n\n\tpublic ReplaceWith getReplaceType() {\n\t\treturn replaceType;\n\t}\n\n\tpublic ClassInfo getClsRef() {\n\t\treturn (ClassInfo) replaceObj;\n\t}\n\n\tpublic InsnArg getVarRef() {\n\t\treturn (InsnArg) replaceObj;\n\t}\n\n\t@Override\n\tpublic AType<FieldReplaceAttr> getAttrType() {\n\t\treturn AType.FIELD_REPLACE;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"REPLACE: \" + replaceType + ' ' + replaceObj;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/ForceReturnAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.Utils;\n\npublic class ForceReturnAttr implements IJadxAttribute {\n\n\tprivate final InsnNode returnInsn;\n\n\tpublic ForceReturnAttr(InsnNode retInsn) {\n\t\tthis.returnInsn = retInsn;\n\t}\n\n\tpublic InsnNode getReturnInsn() {\n\t\treturn returnInsn;\n\t}\n\n\t@Override\n\tpublic AType<ForceReturnAttr> getAttrType() {\n\t\treturn AType.FORCE_RETURN;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"FORCE_RETURN \" + Utils.listToString(returnInsn.getArguments());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/GenericInfoAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.instructions.args.ArgType;\n\npublic class GenericInfoAttr implements IJadxAttribute {\n\tprivate final List<ArgType> genericTypes;\n\tprivate boolean explicit;\n\n\tpublic GenericInfoAttr(List<ArgType> genericTypes) {\n\t\tthis.genericTypes = genericTypes;\n\t}\n\n\tpublic List<ArgType> getGenericTypes() {\n\t\treturn genericTypes;\n\t}\n\n\tpublic boolean isExplicit() {\n\t\treturn explicit;\n\t}\n\n\tpublic void setExplicit(boolean explicit) {\n\t\tthis.explicit = explicit;\n\t}\n\n\t@Override\n\tpublic AType<GenericInfoAttr> getAttrType() {\n\t\treturn AType.GENERIC_INFO;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"GenericInfoAttr{\" + genericTypes + \", explicit=\" + explicit + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/InlinedAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.ClassNode;\n\npublic class InlinedAttr implements IJadxAttribute {\n\n\tprivate final ClassNode inlineCls;\n\n\tpublic InlinedAttr(ClassNode inlineCls) {\n\t\tthis.inlineCls = inlineCls;\n\t}\n\n\tpublic ClassNode getInlineCls() {\n\t\treturn inlineCls;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<InlinedAttr> getAttrType() {\n\t\treturn AType.INLINED;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"INLINED: \" + inlineCls;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/JadxCommentsAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.Collections;\nimport java.util.EnumMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport jadx.api.CommentsLevel;\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.IAttributeNode;\nimport jadx.core.utils.Utils;\n\npublic class JadxCommentsAttr implements IJadxAttribute {\n\n\tpublic static void add(IAttributeNode node, CommentsLevel level, String comment) {\n\t\tinitFor(node).add(level, comment);\n\t}\n\n\tprivate static JadxCommentsAttr initFor(IAttributeNode node) {\n\t\tJadxCommentsAttr currentAttr = node.get(AType.JADX_COMMENTS);\n\t\tif (currentAttr != null) {\n\t\t\treturn currentAttr;\n\t\t}\n\t\tJadxCommentsAttr newAttr = new JadxCommentsAttr();\n\t\tnode.addAttr(newAttr);\n\t\treturn newAttr;\n\t}\n\n\tprivate final Map<CommentsLevel, Set<String>> comments = new EnumMap<>(CommentsLevel.class);\n\n\tpublic void add(CommentsLevel level, String comment) {\n\t\tcomments.computeIfAbsent(level, l -> new HashSet<>()).add(comment);\n\t}\n\n\tpublic List<String> formatAndFilter(CommentsLevel level) {\n\t\tif (level == CommentsLevel.NONE || level == CommentsLevel.USER_ONLY) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn comments.entrySet().stream()\n\t\t\t\t.filter(e -> e.getKey().filter(level))\n\t\t\t\t.flatMap(e -> {\n\t\t\t\t\tString levelName = e.getKey().name();\n\t\t\t\t\treturn e.getValue().stream()\n\t\t\t\t\t\t\t.map(v -> \"JADX \" + levelName + \": \" + v);\n\t\t\t\t})\n\t\t\t\t.sorted()\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tpublic Map<CommentsLevel, Set<String>> getComments() {\n\t\treturn comments;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<JadxCommentsAttr> getAttrType() {\n\t\treturn AType.JADX_COMMENTS;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"JadxCommentsAttr{\\n \"\n\t\t\t\t+ Utils.listToString(comments.entrySet(), \"\\n \",\n\t\t\t\t\t\te -> e.getKey() + \": \\n -> \" + Utils.listToString(e.getValue(), \"\\n -> \"))\n\t\t\t\t+ '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/JadxError.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.core.utils.Utils;\n\npublic class JadxError implements Comparable<JadxError> {\n\n\tprivate final String error;\n\tprivate final Throwable cause;\n\n\tpublic JadxError(String error, Throwable cause) {\n\t\tthis.error = Objects.requireNonNull(error);\n\t\tthis.cause = cause;\n\t}\n\n\tpublic String getError() {\n\t\treturn error;\n\t}\n\n\tpublic Throwable getCause() {\n\t\treturn cause;\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull JadxError o) {\n\t\treturn this.error.compareTo(o.getError());\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tJadxError other = (JadxError) o;\n\t\treturn error.equals(other.error);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn error.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder str = new StringBuilder();\n\t\tstr.append(\"JadxError: \").append(error).append(' ');\n\t\tif (cause != null) {\n\t\t\tstr.append(cause.getClass());\n\t\t\tstr.append(':');\n\t\t\tstr.append(cause.getMessage());\n\t\t\tstr.append('\\n');\n\t\t\tstr.append(Utils.getStackTrace(cause));\n\t\t}\n\t\treturn str.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/JumpInfo.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport jadx.core.utils.InsnUtils;\n\npublic class JumpInfo {\n\n\tprivate final int src;\n\tprivate final int dest;\n\n\tpublic JumpInfo(int src, int dest) {\n\t\tthis.src = src;\n\t\tthis.dest = dest;\n\t}\n\n\tpublic int getSrc() {\n\t\treturn src;\n\t}\n\n\tpublic int getDest() {\n\t\treturn dest;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn 31 * dest + src;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tJumpInfo other = (JumpInfo) obj;\n\t\treturn dest == other.dest && src == other.src;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"JUMP: \" + InsnUtils.formatOffset(src) + \" -> \" + InsnUtils.formatOffset(dest);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/LineAttrNode.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport jadx.core.dex.attributes.AttrNode;\nimport jadx.core.dex.attributes.ILineAttributeNode;\n\npublic abstract class LineAttrNode extends AttrNode implements ILineAttributeNode {\n\n\tprivate int sourceLine;\n\n\t/**\n\t * Position where a node declared at in decompiled code\n\t */\n\tprivate int defPosition;\n\n\t@Override\n\tpublic int getSourceLine() {\n\t\treturn sourceLine;\n\t}\n\n\t@Override\n\tpublic void setSourceLine(int sourceLine) {\n\t\tthis.sourceLine = sourceLine;\n\t}\n\n\t@Override\n\tpublic int getDefPosition() {\n\t\treturn this.defPosition;\n\t}\n\n\t@Override\n\tpublic void setDefPosition(int defPosition) {\n\t\tthis.defPosition = defPosition;\n\t}\n\n\tpublic void addSourceLineFrom(LineAttrNode lineAttrNode) {\n\t\tif (this.getSourceLine() == 0) {\n\t\t\tthis.setSourceLine(lineAttrNode.getSourceLine());\n\t\t}\n\t}\n\n\tpublic void copyLines(LineAttrNode lineAttrNode) {\n\t\tsetSourceLine(lineAttrNode.getSourceLine());\n\t\tsetDefPosition(lineAttrNode.getDefPosition());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/LocalVarsDebugInfoAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.ILocalVar;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.utils.Utils;\n\npublic class LocalVarsDebugInfoAttr implements IJadxAttribute {\n\tprivate final List<ILocalVar> localVars;\n\n\tpublic LocalVarsDebugInfoAttr(List<ILocalVar> localVars) {\n\t\tthis.localVars = localVars;\n\t}\n\n\tpublic List<ILocalVar> getLocalVars() {\n\t\treturn localVars;\n\t}\n\n\t@Override\n\tpublic AType<LocalVarsDebugInfoAttr> getAttrType() {\n\t\treturn AType.LOCAL_VARS_DEBUG_INFO;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Debug Info:\\n  \" + Utils.listToString(localVars, \"\\n  \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/LoopInfo.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.Edge;\nimport jadx.core.utils.BlockUtils;\n\npublic class LoopInfo {\n\n\tprivate final BlockNode start;\n\tprivate final BlockNode end;\n\tprivate final Set<BlockNode> loopBlocks;\n\n\tprivate int id;\n\tprivate LoopInfo parentLoop;\n\n\tpublic LoopInfo(BlockNode start, BlockNode end, Set<BlockNode> loopBlocks) {\n\t\tthis.start = start;\n\t\tthis.end = end;\n\t\tthis.loopBlocks = loopBlocks;\n\t}\n\n\tpublic BlockNode getStart() {\n\t\treturn start;\n\t}\n\n\tpublic BlockNode getEnd() {\n\t\treturn end;\n\t}\n\n\tpublic Set<BlockNode> getLoopBlocks() {\n\t\treturn loopBlocks;\n\t}\n\n\t/**\n\t * Return source blocks of exit edges. <br>\n\t * Exit nodes belongs to loop (contains in {@code loopBlocks})\n\t */\n\tpublic Set<BlockNode> getExitNodes() {\n\t\tSet<BlockNode> nodes = new HashSet<>();\n\t\tSet<BlockNode> blocks = getLoopBlocks();\n\t\tfor (BlockNode block : blocks) {\n\t\t\t// exit: successor node not from this loop, (don't change to getCleanSuccessors)\n\t\t\tfor (BlockNode s : block.getSuccessors()) {\n\t\t\t\tif (!blocks.contains(s) && !s.contains(AType.EXC_HANDLER)) {\n\t\t\t\t\tnodes.add(block);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nodes;\n\t}\n\n\t/**\n\t * Return loop exit edges.\n\t */\n\tpublic List<Edge> getExitEdges() {\n\t\tList<Edge> edges = new ArrayList<>();\n\t\tSet<BlockNode> blocks = getLoopBlocks();\n\t\tfor (BlockNode block : blocks) {\n\t\t\tfor (BlockNode s : block.getSuccessors()) { // don't use clean successors to include loop back edges\n\t\t\t\tif (!blocks.contains(s) && !BlockUtils.isExceptionHandlerPath(s)) {\n\t\t\t\t\tedges.add(new Edge(block, s));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn edges;\n\t}\n\n\tpublic BlockNode getPreHeader() {\n\t\treturn BlockUtils.selectOther(end, start.getPredecessors());\n\t}\n\n\tpublic int getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(int id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic LoopInfo getParentLoop() {\n\t\treturn parentLoop;\n\t}\n\n\tpublic void setParentLoop(LoopInfo parentLoop) {\n\t\tthis.parentLoop = parentLoop;\n\t}\n\n\tpublic boolean hasParent(LoopInfo searchLoop) {\n\t\tLoopInfo parent = parentLoop;\n\t\twhile (true) {\n\t\t\tif (parent == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (parent == searchLoop) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tparent = parent.getParentLoop();\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"LOOP:\" + id + \": \" + start + \"->\" + end;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/LoopLabelAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\n\npublic class LoopLabelAttr implements IJadxAttribute {\n\n\tprivate final LoopInfo loop;\n\n\tpublic LoopLabelAttr(LoopInfo loop) {\n\t\tthis.loop = loop;\n\t}\n\n\tpublic LoopInfo getLoop() {\n\t\treturn loop;\n\t}\n\n\t@Override\n\tpublic AType<LoopLabelAttr> getAttrType() {\n\t\treturn AType.LOOP_LABEL;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"LOOP_LABEL: \" + loop;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/MethodBridgeAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic class MethodBridgeAttr extends PinnedAttribute {\n\n\tprivate final MethodNode bridgeMth;\n\n\tpublic MethodBridgeAttr(MethodNode bridgeMth) {\n\t\tthis.bridgeMth = bridgeMth;\n\t}\n\n\tpublic MethodNode getBridgeMth() {\n\t\treturn bridgeMth;\n\t}\n\n\t@Override\n\tpublic AType<MethodBridgeAttr> getAttrType() {\n\t\treturn AType.BRIDGED_BY;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"BRIDGED_BY: \" + bridgeMth;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/MethodInlineAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic class MethodInlineAttr extends PinnedAttribute {\n\n\tprivate static final MethodInlineAttr INLINE_NOT_NEEDED = new MethodInlineAttr(null, null);\n\n\tpublic static MethodInlineAttr markForInline(MethodNode mth, InsnNode replaceInsn) {\n\t\tObjects.requireNonNull(replaceInsn);\n\t\tList<RegisterArg> allArgRegs = mth.getAllArgRegs();\n\t\tint argsCount = allArgRegs.size();\n\t\tint[] regNums = new int[argsCount];\n\t\tfor (int i = 0; i < argsCount; i++) {\n\t\t\tRegisterArg reg = allArgRegs.get(i);\n\t\t\tregNums[i] = reg.getRegNum();\n\t\t}\n\t\tMethodInlineAttr mia = new MethodInlineAttr(replaceInsn, regNums);\n\t\tmth.addAttr(mia);\n\t\tmth.addDebugComment(\"Marked for inline\");\n\t\treturn mia;\n\t}\n\n\tpublic static MethodInlineAttr inlineNotNeeded(MethodNode mth) {\n\t\tmth.addAttr(INLINE_NOT_NEEDED);\n\t\treturn INLINE_NOT_NEEDED;\n\t}\n\n\tprivate final InsnNode insn;\n\n\t/**\n\t * Store method arguments register numbers to allow remap registers\n\t */\n\tprivate final int[] argsRegNums;\n\n\tprivate MethodInlineAttr(InsnNode insn, int[] argsRegNums) {\n\t\tthis.insn = insn;\n\t\tthis.argsRegNums = argsRegNums;\n\t}\n\n\tpublic boolean notNeeded() {\n\t\treturn insn == null;\n\t}\n\n\tpublic InsnNode getInsn() {\n\t\treturn insn;\n\t}\n\n\tpublic int[] getArgsRegNums() {\n\t\treturn argsRegNums;\n\t}\n\n\t@Override\n\tpublic AType<MethodInlineAttr> getAttrType() {\n\t\treturn AType.METHOD_INLINE;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tif (notNeeded()) {\n\t\t\treturn \"INLINE_NOT_NEEDED\";\n\t\t}\n\t\treturn \"INLINE: \" + insn;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/MethodOverrideAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.List;\nimport java.util.Set;\nimport java.util.SortedSet;\n\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic class MethodOverrideAttr extends PinnedAttribute {\n\n\t/**\n\t * All methods overridden by current method. Current method excluded, empty for base method.\n\t */\n\tprivate final List<IMethodDetails> overrideList;\n\n\t/**\n\t * All method nodes from override hierarchy. Current method included.\n\t */\n\tprivate SortedSet<MethodNode> relatedMthNodes;\n\n\tprivate final Set<IMethodDetails> baseMethods;\n\n\tpublic MethodOverrideAttr(List<IMethodDetails> overrideList, SortedSet<MethodNode> relatedMthNodes, Set<IMethodDetails> baseMethods) {\n\t\tthis.overrideList = overrideList;\n\t\tthis.relatedMthNodes = relatedMthNodes;\n\t\tthis.baseMethods = baseMethods;\n\t}\n\n\tpublic List<IMethodDetails> getOverrideList() {\n\t\treturn overrideList;\n\t}\n\n\tpublic SortedSet<MethodNode> getRelatedMthNodes() {\n\t\treturn relatedMthNodes;\n\t}\n\n\tpublic Set<IMethodDetails> getBaseMethods() {\n\t\treturn baseMethods;\n\t}\n\n\tpublic void setRelatedMthNodes(SortedSet<MethodNode> relatedMthNodes) {\n\t\tthis.relatedMthNodes = relatedMthNodes;\n\t}\n\n\t@Override\n\tpublic AType<MethodOverrideAttr> getAttrType() {\n\t\treturn AType.METHOD_OVERRIDE;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"METHOD_OVERRIDE: \" + getBaseMethods();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/MethodReplaceAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.MethodNode;\n\n/**\n * Calls of method should be replaced by provided method (used for synthetic methods redirect)\n */\npublic class MethodReplaceAttr extends PinnedAttribute {\n\n\tprivate final MethodNode replaceMth;\n\n\tpublic MethodReplaceAttr(MethodNode replaceMth) {\n\t\tthis.replaceMth = replaceMth;\n\t}\n\n\tpublic MethodNode getReplaceMth() {\n\t\treturn replaceMth;\n\t}\n\n\t@Override\n\tpublic AType<MethodReplaceAttr> getAttrType() {\n\t\treturn AType.METHOD_REPLACE;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"REPLACED_BY: \" + replaceMth;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/MethodThrowsAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.Set;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\nimport jadx.core.dex.attributes.AType;\n\npublic class MethodThrowsAttr extends PinnedAttribute {\n\tprivate final Set<String> list;\n\n\tprivate boolean visited;\n\n\tpublic MethodThrowsAttr(Set<String> list) {\n\t\tthis.list = list;\n\t}\n\n\tpublic boolean isVisited() {\n\t\treturn visited;\n\t}\n\n\tpublic void setVisited(boolean visited) {\n\t\tthis.visited = visited;\n\t}\n\n\tpublic Set<String> getList() {\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<MethodThrowsAttr> getAttrType() {\n\t\treturn AType.METHOD_THROWS;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"THROWS:\" + list;\n\t}\n\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/MethodTypeVarsAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.Collections;\nimport java.util.Set;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.instructions.args.ArgType;\n\nimport static jadx.core.utils.Utils.isEmpty;\n\n/**\n * Set of known type variables at current method\n */\npublic class MethodTypeVarsAttr implements IJadxAttribute {\n\tprivate static final MethodTypeVarsAttr EMPTY = new MethodTypeVarsAttr(Collections.emptySet());\n\n\tpublic static MethodTypeVarsAttr build(Set<ArgType> typeVars) {\n\t\tif (isEmpty(typeVars)) {\n\t\t\treturn EMPTY;\n\t\t}\n\t\treturn new MethodTypeVarsAttr(typeVars);\n\t}\n\n\tprivate final Set<ArgType> typeVars;\n\n\tprivate MethodTypeVarsAttr(Set<ArgType> typeVars) {\n\t\tthis.typeVars = typeVars;\n\t}\n\n\tpublic Set<ArgType> getTypeVars() {\n\t\treturn typeVars;\n\t}\n\n\t@Override\n\tpublic AType<MethodTypeVarsAttr> getAttrType() {\n\t\treturn AType.METHOD_TYPE_VARS;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tif (this == EMPTY) {\n\t\t\treturn \"TYPE_VARS: EMPTY\";\n\t\t}\n\t\treturn \"TYPE_VARS: \" + typeVars;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/NotificationAttrNode.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport jadx.api.CommentsLevel;\nimport jadx.api.data.CommentStyle;\nimport jadx.core.codegen.utils.CodeComment;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.ICodeNode;\nimport jadx.core.utils.ErrorsCounter;\nimport jadx.core.utils.Utils;\n\npublic abstract class NotificationAttrNode extends LineAttrNode implements ICodeNode {\n\n\tpublic boolean checkCommentsLevel(CommentsLevel required) {\n\t\treturn required.filter(this.root().getArgs().getCommentsLevel());\n\t}\n\n\tpublic void addError(String errStr, Throwable e) {\n\t\tErrorsCounter.error(this, errStr, e);\n\t}\n\n\tpublic void addWarn(String warn) {\n\t\tErrorsCounter.warning(this, warn);\n\t\tJadxCommentsAttr.add(this, CommentsLevel.WARN, warn);\n\t\tthis.add(AFlag.INCONSISTENT_CODE);\n\t}\n\n\tpublic void addCodeComment(String comment) {\n\t\taddAttr(AType.CODE_COMMENTS, new CodeComment(comment, CommentStyle.LINE));\n\t}\n\n\tpublic void addCodeComment(String comment, CommentStyle style) {\n\t\taddAttr(AType.CODE_COMMENTS, new CodeComment(comment, style));\n\t}\n\n\tpublic void addWarnComment(String warn) {\n\t\tJadxCommentsAttr.add(this, CommentsLevel.WARN, warn);\n\t}\n\n\tpublic void addWarnComment(String warn, Throwable exc) {\n\t\tString commentStr = warn + root().getArgs().getCodeNewLineStr() + Utils.getStackTrace(exc);\n\t\tJadxCommentsAttr.add(this, CommentsLevel.WARN, commentStr);\n\t}\n\n\tpublic void addInfoComment(String commentStr) {\n\t\tJadxCommentsAttr.add(this, CommentsLevel.INFO, commentStr);\n\t}\n\n\tpublic void addDebugComment(String commentStr) {\n\t\tJadxCommentsAttr.add(this, CommentsLevel.DEBUG, commentStr);\n\t}\n\n\tpublic CommentsLevel getCommentsLevel() {\n\t\treturn this.root().getArgs().getCommentsLevel();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/PhiListAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.instructions.PhiInsn;\nimport jadx.core.dex.instructions.args.RegisterArg;\n\npublic class PhiListAttr implements IJadxAttribute {\n\n\tprivate final List<PhiInsn> list = new ArrayList<>();\n\n\t@Override\n\tpublic AType<PhiListAttr> getAttrType() {\n\t\treturn AType.PHI_LIST;\n\t}\n\n\tpublic List<PhiInsn> getList() {\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"PHI:\");\n\t\tfor (PhiInsn phiInsn : list) {\n\t\t\tRegisterArg resArg = phiInsn.getResult();\n\t\t\tif (resArg != null) {\n\t\t\t\tsb.append(\" r\").append(resArg.getRegNum());\n\t\t\t}\n\t\t}\n\t\tfor (PhiInsn phiInsn : list) {\n\t\t\tsb.append('\\n').append(\"  \").append(phiInsn);\n\t\t}\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/RegDebugInfoAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.Objects;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.instructions.args.ArgType;\n\npublic class RegDebugInfoAttr implements IJadxAttribute {\n\n\tprivate final ArgType type;\n\tprivate final String name;\n\n\tpublic RegDebugInfoAttr(ArgType type, String name) {\n\t\tthis.type = type;\n\t\tthis.name = name;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic ArgType getRegType() {\n\t\treturn type;\n\t}\n\n\t@Override\n\tpublic AType<RegDebugInfoAttr> getAttrType() {\n\t\treturn AType.REG_DEBUG_INFO;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tRegDebugInfoAttr that = (RegDebugInfoAttr) o;\n\t\treturn Objects.equals(type, that.type) && Objects.equals(name, that.name);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(type, name);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"D('\" + name + \"' \" + type + ')';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/RegionRefAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.IRegion;\n\n/**\n * Region created based on parent instruction\n */\npublic class RegionRefAttr implements IJadxAttribute {\n\tprivate final IRegion region;\n\n\tpublic RegionRefAttr(IRegion region) {\n\t\tthis.region = region;\n\t}\n\n\tpublic IRegion getRegion() {\n\t\treturn region;\n\t}\n\n\t@Override\n\tpublic AType<RegionRefAttr> getAttrType() {\n\t\treturn AType.REGION_REF;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"RegionRef:\" + region.baseString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/RenameReasonAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.AttrNode;\n\npublic class RenameReasonAttr implements IJadxAttribute {\n\n\tpublic static RenameReasonAttr forNode(AttrNode node) {\n\t\tRenameReasonAttr renameReasonAttr = node.get(AType.RENAME_REASON);\n\t\tif (renameReasonAttr != null) {\n\t\t\treturn renameReasonAttr;\n\t\t}\n\t\tRenameReasonAttr newAttr = new RenameReasonAttr();\n\t\tnode.addAttr(newAttr);\n\t\treturn newAttr;\n\t}\n\n\tprivate String description;\n\n\tpublic RenameReasonAttr() {\n\t\tthis.description = \"\";\n\t}\n\n\tpublic RenameReasonAttr(String description) {\n\t\tthis.description = description;\n\t}\n\n\tpublic RenameReasonAttr(AttrNode node) {\n\t\tRenameReasonAttr renameReasonAttr = node.get(AType.RENAME_REASON);\n\t\tif (renameReasonAttr != null) {\n\t\t\tthis.description = renameReasonAttr.description;\n\t\t} else {\n\t\t\tthis.description = \"\";\n\t\t}\n\t}\n\n\tpublic RenameReasonAttr(AttrNode node, boolean notValid, boolean notPrintable) {\n\t\tthis(node);\n\t\tif (notValid) {\n\t\t\tnotValid();\n\t\t}\n\t\tif (notPrintable) {\n\t\t\tnotPrintable();\n\t\t}\n\t}\n\n\tpublic RenameReasonAttr notValid() {\n\t\treturn append(\"not valid java name\");\n\t}\n\n\tpublic RenameReasonAttr notPrintable() {\n\t\treturn append(\"contains not printable characters\");\n\t}\n\n\tpublic RenameReasonAttr append(String reason) {\n\t\tif (description.isEmpty()) {\n\t\t\tdescription += reason;\n\t\t} else {\n\t\t\tdescription += \" and \" + reason;\n\t\t}\n\t\treturn this;\n\t}\n\n\tpublic String getDescription() {\n\t\treturn description;\n\t}\n\n\t@Override\n\tpublic AType<RenameReasonAttr> getAttrType() {\n\t\treturn AType.RENAME_REASON;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"RENAME_REASON:\" + description;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/SkipMethodArgsAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport java.util.BitSet;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class SkipMethodArgsAttr extends PinnedAttribute {\n\n\tpublic static void skipArg(MethodNode mth, RegisterArg arg) {\n\t\tint argNum = Utils.indexInListByRef(mth.getArgRegs(), arg);\n\t\tif (argNum == -1) {\n\t\t\tthrow new JadxRuntimeException(\"Arg not found: \" + arg);\n\t\t}\n\t\tskipArg(mth, argNum);\n\t}\n\n\tpublic static void skipArg(MethodNode mth, int argNum) {\n\t\tSkipMethodArgsAttr attr = mth.get(AType.SKIP_MTH_ARGS);\n\t\tif (attr == null) {\n\t\t\tattr = new SkipMethodArgsAttr(mth);\n\t\t\tmth.addAttr(attr);\n\t\t}\n\t\tattr.skip(argNum);\n\t}\n\n\tpublic static boolean isSkip(@Nullable MethodNode mth, int argNum) {\n\t\tif (mth == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (argNum == 0 && mth.contains(AFlag.SKIP_FIRST_ARG)) {\n\t\t\treturn true;\n\t\t}\n\t\tSkipMethodArgsAttr attr = mth.get(AType.SKIP_MTH_ARGS);\n\t\tif (attr == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn attr.isSkip(argNum);\n\t}\n\n\tprivate final BitSet skipArgs;\n\n\tprivate SkipMethodArgsAttr(MethodNode mth) {\n\t\tthis.skipArgs = new BitSet(mth.getMethodInfo().getArgsCount());\n\t}\n\n\tpublic void skip(int argNum) {\n\t\tskipArgs.set(argNum);\n\t}\n\n\tpublic boolean isSkip(int argNum) {\n\t\treturn skipArgs.get(argNum);\n\t}\n\n\tpublic int getSkipCount() {\n\t\treturn skipArgs.cardinality();\n\t}\n\n\t@Override\n\tpublic AType<SkipMethodArgsAttr> getAttrType() {\n\t\treturn AType.SKIP_MTH_ARGS;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"SKIP_MTH_ARGS: \" + skipArgs;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/SpecialEdgeAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.AttrList;\nimport jadx.core.dex.nodes.BlockNode;\n\npublic class SpecialEdgeAttr implements IJadxAttribute {\n\n\tpublic enum SpecialEdgeType {\n\t\tBACK_EDGE,\n\t\tCROSS_EDGE\n\t}\n\n\tprivate final SpecialEdgeType type;\n\tprivate final BlockNode start;\n\tprivate final BlockNode end;\n\n\tpublic SpecialEdgeAttr(SpecialEdgeType type, BlockNode start, BlockNode end) {\n\t\tthis.type = type;\n\t\tthis.start = start;\n\t\tthis.end = end;\n\t}\n\n\tpublic SpecialEdgeType getType() {\n\t\treturn type;\n\t}\n\n\tpublic BlockNode getStart() {\n\t\treturn start;\n\t}\n\n\tpublic BlockNode getEnd() {\n\t\treturn end;\n\t}\n\n\t@Override\n\tpublic AType<AttrList<SpecialEdgeAttr>> getAttrType() {\n\t\treturn AType.SPECIAL_EDGE;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn type + \": \" + start + \" -> \" + end;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/attributes/nodes/TmpEdgeAttr.java",
    "content": "package jadx.core.dex.attributes.nodes;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.BlockNode;\n\npublic class TmpEdgeAttr implements IJadxAttribute {\n\n\tprivate final BlockNode block;\n\n\tpublic TmpEdgeAttr(BlockNode block) {\n\t\tthis.block = block;\n\t}\n\n\tpublic BlockNode getBlock() {\n\t\treturn block;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<? extends IJadxAttribute> getAttrType() {\n\t\treturn AType.TMP_EDGE;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"TMP_EDGE: \" + block;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/info/AccessInfo.java",
    "content": "package jadx.core.dex.info;\n\nimport org.intellij.lang.annotations.MagicConstant;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.core.Consts;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class AccessInfo {\n\n\tpublic static final int VISIBILITY_FLAGS = AccessFlags.PUBLIC | AccessFlags.PROTECTED | AccessFlags.PRIVATE;\n\tprivate final int accFlags;\n\n\tpublic enum AFType {\n\t\tCLASS, FIELD, METHOD\n\t}\n\n\tprivate final AFType type;\n\n\tpublic AccessInfo(int accessFlags, AFType type) {\n\t\tthis.accFlags = accessFlags;\n\t\tthis.type = type;\n\t}\n\n\t@MagicConstant(valuesFromClass = AccessFlags.class)\n\tpublic boolean containsFlag(int flag) {\n\t\treturn (accFlags & flag) != 0;\n\t}\n\n\t@MagicConstant(valuesFromClass = AccessFlags.class)\n\tpublic boolean containsFlags(int... flags) {\n\t\tfor (int flag : flags) {\n\t\t\tif ((accFlags & flag) == 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic AccessInfo remove(int flag) {\n\t\tif (containsFlag(flag)) {\n\t\t\treturn new AccessInfo(accFlags & ~flag, type);\n\t\t}\n\t\treturn this;\n\t}\n\n\tpublic AccessInfo add(int flag) {\n\t\tif (!containsFlag(flag)) {\n\t\t\treturn new AccessInfo(accFlags | flag, type);\n\t\t}\n\t\treturn this;\n\t}\n\n\tpublic AccessInfo changeVisibility(int flag) {\n\t\tint currentVisFlags = accFlags & VISIBILITY_FLAGS;\n\t\tif (currentVisFlags == flag) {\n\t\t\treturn this;\n\t\t}\n\t\tint unsetAllVisFlags = accFlags & ~VISIBILITY_FLAGS;\n\t\treturn new AccessInfo(unsetAllVisFlags | flag, type);\n\t}\n\n\tpublic AccessInfo getVisibility() {\n\t\treturn new AccessInfo(accFlags & VISIBILITY_FLAGS, type);\n\t}\n\n\tpublic boolean isVisibilityWeakerThan(AccessInfo otherAccInfo) {\n\t\tint thisVis = accFlags & VISIBILITY_FLAGS;\n\t\tint otherVis = otherAccInfo.accFlags & VISIBILITY_FLAGS;\n\t\tif (thisVis == otherVis) {\n\t\t\treturn false;\n\t\t}\n\t\treturn orderedVisibility(thisVis) < orderedVisibility(otherVis);\n\t}\n\n\tprivate static int orderedVisibility(int flag) {\n\t\tswitch (flag) {\n\t\t\tcase AccessFlags.PRIVATE:\n\t\t\t\treturn 1;\n\t\t\tcase 0: // package-private\n\t\t\t\treturn 2;\n\t\t\tcase AccessFlags.PROTECTED:\n\t\t\t\treturn 3;\n\t\t\tcase AccessFlags.PUBLIC:\n\t\t\t\treturn 4;\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unexpected visibility flag: \" + flag);\n\t\t}\n\t}\n\n\tpublic boolean isPublic() {\n\t\treturn (accFlags & AccessFlags.PUBLIC) != 0;\n\t}\n\n\tpublic boolean isProtected() {\n\t\treturn (accFlags & AccessFlags.PROTECTED) != 0;\n\t}\n\n\tpublic boolean isPrivate() {\n\t\treturn (accFlags & AccessFlags.PRIVATE) != 0;\n\t}\n\n\tpublic boolean isPackagePrivate() {\n\t\treturn (accFlags & VISIBILITY_FLAGS) == 0;\n\t}\n\n\tpublic boolean isAbstract() {\n\t\treturn (accFlags & AccessFlags.ABSTRACT) != 0;\n\t}\n\n\tpublic boolean isInterface() {\n\t\treturn (accFlags & AccessFlags.INTERFACE) != 0;\n\t}\n\n\tpublic boolean isAnnotation() {\n\t\treturn (accFlags & AccessFlags.ANNOTATION) != 0;\n\t}\n\n\tpublic boolean isNative() {\n\t\treturn (accFlags & AccessFlags.NATIVE) != 0;\n\t}\n\n\tpublic boolean isStatic() {\n\t\treturn (accFlags & AccessFlags.STATIC) != 0;\n\t}\n\n\tpublic boolean isFinal() {\n\t\treturn (accFlags & AccessFlags.FINAL) != 0;\n\t}\n\n\tpublic boolean isConstructor() {\n\t\treturn (accFlags & AccessFlags.CONSTRUCTOR) != 0;\n\t}\n\n\tpublic boolean isEnum() {\n\t\treturn (accFlags & AccessFlags.ENUM) != 0;\n\t}\n\n\tpublic boolean isSynthetic() {\n\t\treturn (accFlags & AccessFlags.SYNTHETIC) != 0;\n\t}\n\n\tpublic boolean isBridge() {\n\t\treturn (accFlags & AccessFlags.BRIDGE) != 0;\n\t}\n\n\tpublic boolean isVarArgs() {\n\t\treturn (accFlags & AccessFlags.VARARGS) != 0;\n\t}\n\n\tpublic boolean isSynchronized() {\n\t\treturn (accFlags & (AccessFlags.SYNCHRONIZED | AccessFlags.DECLARED_SYNCHRONIZED)) != 0;\n\t}\n\n\tpublic boolean isTransient() {\n\t\treturn (accFlags & AccessFlags.TRANSIENT) != 0;\n\t}\n\n\tpublic boolean isVolatile() {\n\t\treturn (accFlags & AccessFlags.VOLATILE) != 0;\n\t}\n\n\tpublic boolean isModuleInfo() {\n\t\treturn (accFlags & AccessFlags.MODULE) != 0;\n\t}\n\n\tpublic boolean isData() {\n\t\treturn (accFlags & AccessFlags.DATA) != 0;\n\t}\n\n\tpublic AFType getType() {\n\t\treturn type;\n\t}\n\n\tpublic String makeString(boolean showHidden) {\n\t\tStringBuilder code = new StringBuilder();\n\t\tif (isPublic()) {\n\t\t\tcode.append(\"public \");\n\t\t}\n\t\tif (isPrivate()) {\n\t\t\tcode.append(\"private \");\n\t\t}\n\t\tif (isProtected()) {\n\t\t\tcode.append(\"protected \");\n\t\t}\n\t\tif (isStatic()) {\n\t\t\tcode.append(\"static \");\n\t\t}\n\t\tif (isFinal()) {\n\t\t\tcode.append(\"final \");\n\t\t}\n\t\tif (isAbstract()) {\n\t\t\tcode.append(\"abstract \");\n\t\t}\n\t\tif (isNative()) {\n\t\t\tcode.append(\"native \");\n\t\t}\n\t\tswitch (type) {\n\t\t\tcase METHOD:\n\t\t\t\tif (isSynchronized()) {\n\t\t\t\t\tcode.append(\"synchronized \");\n\t\t\t\t}\n\t\t\t\tif (showHidden) {\n\t\t\t\t\tif (isBridge()) {\n\t\t\t\t\t\tcode.append(\"/* bridge */ \");\n\t\t\t\t\t}\n\t\t\t\t\tif (Consts.DEBUG && isVarArgs()) {\n\t\t\t\t\t\tcode.append(\"/* varargs */ \");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase FIELD:\n\t\t\t\tif (isVolatile()) {\n\t\t\t\t\tcode.append(\"volatile \");\n\t\t\t\t}\n\t\t\t\tif (isTransient()) {\n\t\t\t\t\tcode.append(\"transient \");\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase CLASS:\n\t\t\t\tif ((accFlags & AccessFlags.STRICT) != 0) {\n\t\t\t\t\tcode.append(\"strict \");\n\t\t\t\t}\n\t\t\t\tif (showHidden) {\n\t\t\t\t\tif (isData()) {\n\t\t\t\t\t\tcode.append(\"/* data */ \");\n\t\t\t\t\t}\n\t\t\t\t\tif (isModuleInfo()) {\n\t\t\t\t\t\tcode.append(\"/* module-info */ \");\n\t\t\t\t\t}\n\t\t\t\t\tif (Consts.DEBUG) {\n\t\t\t\t\t\tif ((accFlags & AccessFlags.SUPER) != 0) {\n\t\t\t\t\t\t\tcode.append(\"/* super */ \");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ((accFlags & AccessFlags.ENUM) != 0) {\n\t\t\t\t\t\t\tcode.append(\"/* enum */ \");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t\tif (isSynthetic() && showHidden) {\n\t\t\tcode.append(\"/* synthetic */ \");\n\t\t}\n\t\treturn code.toString();\n\t}\n\n\tpublic String visibilityName() {\n\t\tif (isPackagePrivate()) {\n\t\t\treturn \"package-private\";\n\t\t}\n\t\tif (isPublic()) {\n\t\t\treturn \"public\";\n\t\t}\n\t\tif (isPrivate()) {\n\t\t\treturn \"private\";\n\t\t}\n\t\tif (isProtected()) {\n\t\t\treturn \"protected\";\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Unknown visibility flags: \" + getVisibility());\n\t}\n\n\tpublic int rawValue() {\n\t\treturn accFlags;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"AccessInfo: \" + type + \" 0x\" + Integer.toHexString(accFlags) + \" (\" + makeString(true) + ')';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/info/ClassAliasInfo.java",
    "content": "package jadx.core.dex.info;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.utils.StringUtils;\n\nclass ClassAliasInfo {\n\tprivate final String shortName;\n\t@Nullable\n\tprivate final String pkg;\n\t@Nullable\n\tprivate String fullName;\n\n\tClassAliasInfo(@Nullable String pkg, String shortName) {\n\t\tif (StringUtils.isEmpty(shortName)) {\n\t\t\tthrow new IllegalArgumentException(\"Class alias can't be empty\");\n\t\t}\n\t\tthis.pkg = pkg;\n\t\tthis.shortName = shortName;\n\t}\n\n\t@Nullable\n\tpublic String getPkg() {\n\t\treturn pkg;\n\t}\n\n\tpublic String getShortName() {\n\t\treturn shortName;\n\t}\n\n\t@Nullable\n\tpublic String getFullName() {\n\t\treturn fullName;\n\t}\n\n\tpublic void setFullName(@Nullable String fullName) {\n\t\tthis.fullName = fullName;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Alias{\" + shortName + \", pkg=\" + pkg + \", fullName=\" + fullName + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java",
    "content": "package jadx.core.dex.info;\n\nimport java.io.File;\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic final class ClassInfo implements Comparable<ClassInfo> {\n\tprivate final ArgType type;\n\tprivate String name;\n\t@Nullable(\"for inner classes\")\n\tprivate String pkg;\n\tprivate String fullName;\n\t@Nullable\n\tprivate ClassInfo parentClass;\n\t@Nullable\n\tprivate ClassAliasInfo alias;\n\n\tprivate ClassInfo(RootNode root, ArgType type, boolean canBeInner) {\n\t\tthis.type = type;\n\t\tsplitAndApplyNames(root, type, canBeInner);\n\t}\n\n\tpublic static ClassInfo fromType(RootNode root, ArgType type) {\n\t\tArgType clsType = checkClassType(type);\n\t\tClassInfo cls = root.getInfoStorage().getCls(clsType);\n\t\tif (cls != null) {\n\t\t\treturn cls;\n\t\t}\n\t\tboolean canBeInner = root.getArgs().isMoveInnerClasses();\n\t\tClassInfo newClsInfo = new ClassInfo(root, clsType, canBeInner);\n\t\treturn root.getInfoStorage().putCls(newClsInfo);\n\t}\n\n\tpublic static ClassInfo fromName(RootNode root, String clsName) {\n\t\treturn fromType(root, ArgType.object(clsName));\n\t}\n\n\tpublic static ClassInfo fromNameWithoutCache(RootNode root, String fullClsName, boolean canBeInner) {\n\t\treturn new ClassInfo(root, ArgType.object(fullClsName), canBeInner);\n\t}\n\n\tprivate static ArgType checkClassType(ArgType type) {\n\t\tif (type == null) {\n\t\t\tthrow new JadxRuntimeException(\"Null class type\");\n\t\t}\n\t\tif (type.isArray()) {\n\t\t\t// TODO: check case with method declared in array class like ( clone in int[])\n\t\t\treturn ArgType.OBJECT;\n\t\t}\n\t\tif (!type.isObject() || type.isGenericType()) {\n\t\t\tthrow new JadxRuntimeException(\"Not class type: \" + type);\n\t\t}\n\t\tif (type.isGeneric()) {\n\t\t\treturn ArgType.object(type.getObject());\n\t\t}\n\t\treturn type;\n\t}\n\n\tpublic void changeShortName(String aliasName) {\n\t\tClassAliasInfo newAlias;\n\t\tString aliasPkg = getAliasPkg();\n\t\tif (Objects.equals(name, aliasName) || StringUtils.isEmpty(aliasName)) {\n\t\t\tif (Objects.equals(getPackage(), aliasPkg)) {\n\t\t\t\tnewAlias = null;\n\t\t\t} else {\n\t\t\t\tnewAlias = new ClassAliasInfo(aliasPkg, name);\n\t\t\t}\n\t\t} else {\n\t\t\tnewAlias = new ClassAliasInfo(aliasPkg, aliasName);\n\t\t}\n\t\tif (newAlias != null) {\n\t\t\tfillAliasFullName(newAlias);\n\t\t}\n\t\tthis.alias = newAlias;\n\t}\n\n\tpublic void changePkg(String aliasPkg) {\n\t\tif (isInner()) {\n\t\t\tthrow new JadxRuntimeException(\"Can't change package for inner class: \" + this);\n\t\t}\n\t\tif (!Objects.equals(getAliasPkg(), aliasPkg)) {\n\t\t\tClassAliasInfo newAlias = new ClassAliasInfo(aliasPkg, getAliasShortName());\n\t\t\tfillAliasFullName(newAlias);\n\t\t\tthis.alias = newAlias;\n\t\t}\n\t}\n\n\tpublic void changePkgAndName(String aliasPkg, String aliasShortName) {\n\t\tif (isInner()) {\n\t\t\tthrow new JadxRuntimeException(\"Can't change package for inner class\");\n\t\t}\n\t\tClassAliasInfo newAlias = new ClassAliasInfo(aliasPkg, aliasShortName);\n\t\tfillAliasFullName(newAlias);\n\t\tthis.alias = newAlias;\n\t}\n\n\tprivate void fillAliasFullName(ClassAliasInfo alias) {\n\t\tif (parentClass == null) {\n\t\t\talias.setFullName(makeFullClsName(alias.getPkg(), alias.getShortName(), null, true, false));\n\t\t}\n\t}\n\n\tpublic String getAliasPkg() {\n\t\tif (isInner()) {\n\t\t\treturn parentClass.getAliasPkg();\n\t\t}\n\t\treturn alias == null ? getPackage() : alias.getPkg();\n\t}\n\n\tpublic String getAliasShortName() {\n\t\treturn alias == null ? getShortName() : alias.getShortName();\n\t}\n\n\tpublic String getAliasFullName() {\n\t\tif (alias != null) {\n\t\t\tString aliasFullName = alias.getFullName();\n\t\t\tif (aliasFullName == null) {\n\t\t\t\treturn makeAliasFullName();\n\t\t\t}\n\t\t\treturn aliasFullName;\n\t\t}\n\t\tif (parentClass != null && parentClass.hasAlias()) {\n\t\t\treturn makeAliasFullName();\n\t\t}\n\t\treturn getFullName();\n\t}\n\n\tpublic boolean hasAlias() {\n\t\tif (alias != null && !alias.getShortName().equals(getShortName())) {\n\t\t\treturn true;\n\t\t}\n\t\treturn parentClass != null && parentClass.hasAlias();\n\t}\n\n\tpublic boolean hasAliasPkg() {\n\t\treturn !getPackage().equals(getAliasPkg());\n\t}\n\n\tpublic void removeAlias() {\n\t\tthis.alias = null;\n\t}\n\n\tprivate void splitAndApplyNames(RootNode root, ArgType type, boolean canBeInner) {\n\t\tString fullObjectName = type.getObject();\n\t\tString clsPkg;\n\t\tString clsName;\n\t\tint dot = fullObjectName.lastIndexOf('.');\n\t\tif (dot == -1) {\n\t\t\tclsPkg = \"\";\n\t\t\tclsName = fullObjectName;\n\t\t} else {\n\t\t\tclsPkg = fullObjectName.substring(0, dot);\n\t\t\tclsName = fullObjectName.substring(dot + 1);\n\t\t}\n\n\t\tboolean innerCls = false;\n\t\tif (canBeInner) {\n\t\t\tint sep = clsName.lastIndexOf('$');\n\t\t\tif (sep > 0 && sep != clsName.length() - 1) {\n\t\t\t\tString parClsName = clsPkg + '.' + clsName.substring(0, sep);\n\t\t\t\tif (clsPkg.isEmpty()) {\n\t\t\t\t\tparClsName = clsName.substring(0, sep);\n\t\t\t\t}\n\t\t\t\tpkg = null;\n\t\t\t\tparentClass = fromName(root, parClsName);\n\t\t\t\tclsName = clsName.substring(sep + 1);\n\t\t\t\tinnerCls = true;\n\t\t\t}\n\t\t}\n\t\tif (!innerCls) {\n\t\t\tpkg = clsPkg;\n\t\t\tparentClass = null;\n\t\t}\n\t\tthis.name = clsName;\n\t\tthis.fullName = makeFullName();\n\t}\n\n\tprivate static String makeFullClsName(String pkg, String shortName, ClassInfo parentClass, boolean alias, boolean raw) {\n\t\tif (parentClass != null) {\n\t\t\tString parentFullName;\n\t\t\tchar innerSep = raw ? '$' : '.';\n\t\t\tif (alias) {\n\t\t\t\tparentFullName = raw ? parentClass.makeAliasRawFullName() : parentClass.getAliasFullName();\n\t\t\t} else {\n\t\t\t\tparentFullName = raw ? parentClass.makeRawFullName() : parentClass.getFullName();\n\t\t\t}\n\t\t\treturn parentFullName + innerSep + shortName;\n\t\t}\n\t\treturn pkg.isEmpty() ? shortName : pkg + '.' + shortName;\n\t}\n\n\tprivate String makeFullName() {\n\t\treturn makeFullClsName(pkg, name, parentClass, false, false);\n\t}\n\n\tpublic String makeRawFullName() {\n\t\treturn makeFullClsName(pkg, name, parentClass, false, true);\n\t}\n\n\tpublic String makeAliasFullName() {\n\t\treturn makeFullClsName(getAliasPkg(), getAliasShortName(), parentClass, true, false);\n\t}\n\n\tpublic String makeAliasRawFullName() {\n\t\treturn makeFullClsName(getAliasPkg(), getAliasShortName(), parentClass, true, true);\n\t}\n\n\tpublic String getAliasFullPath() {\n\t\treturn getAliasPkg().replace('.', File.separatorChar)\n\t\t\t\t+ File.separatorChar\n\t\t\t\t+ getAliasNameWithoutPackage().replace('.', '_');\n\t}\n\n\tpublic String getFullName() {\n\t\treturn fullName;\n\t}\n\n\tpublic String getShortName() {\n\t\treturn name;\n\t}\n\n\t@NotNull\n\tpublic String getPackage() {\n\t\tif (parentClass != null) {\n\t\t\treturn parentClass.getPackage();\n\t\t}\n\t\tif (pkg == null) {\n\t\t\tthrow new JadxRuntimeException(\"Package is null for not inner class\");\n\t\t}\n\t\treturn pkg;\n\t}\n\n\tpublic boolean isDefaultPackage() {\n\t\treturn getPackage().isEmpty();\n\t}\n\n\tpublic String getRawName() {\n\t\treturn type.getObject();\n\t}\n\n\tpublic String getAliasNameWithoutPackage() {\n\t\tif (parentClass == null) {\n\t\t\treturn getAliasShortName();\n\t\t}\n\t\treturn parentClass.getAliasNameWithoutPackage() + '.' + getAliasShortName();\n\t}\n\n\t@Nullable\n\tpublic ClassInfo getParentClass() {\n\t\treturn parentClass;\n\t}\n\n\tpublic ClassInfo getTopParentClass() {\n\t\tif (parentClass != null) {\n\t\t\tClassInfo topCls = parentClass.getTopParentClass();\n\t\t\treturn topCls != null ? topCls : parentClass;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic boolean isInner() {\n\t\treturn parentClass != null;\n\t}\n\n\tpublic void notInner(RootNode root) {\n\t\tsplitAndApplyNames(root, type, false);\n\t\tthis.parentClass = null;\n\t}\n\n\tpublic void convertToInner(ClassNode parent) {\n\t\tsplitAndApplyNames(parent.root(), type, true);\n\t\tthis.parentClass = parent.getClassInfo();\n\t}\n\n\tpublic void updateNames(RootNode root) {\n\t\tsplitAndApplyNames(root, type, isInner());\n\t}\n\n\tpublic ArgType getType() {\n\t\treturn type;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getFullName();\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn type.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj instanceof ClassInfo) {\n\t\t\treturn type.equals(((ClassInfo) obj).type);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull ClassInfo other) {\n\t\treturn getRawName().compareTo(other.getRawName());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/info/ConstStorage.java",
    "content": "package jadx.core.dex.info;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.JadxArgs;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.PrimitiveType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.IFieldInfoRef;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.prepare.CollectConstValues;\n\npublic class ConstStorage {\n\n\tprivate static final class ValueStorage {\n\t\tprivate final Map<Object, IFieldInfoRef> values = new ConcurrentHashMap<>();\n\t\tprivate final Set<Object> duplicates = new HashSet<>();\n\n\t\tpublic Map<Object, IFieldInfoRef> getValues() {\n\t\t\treturn values;\n\t\t}\n\n\t\tpublic IFieldInfoRef get(Object key) {\n\t\t\treturn values.get(key);\n\t\t}\n\n\t\t/**\n\t\t * @return true if this value is duplicated\n\t\t */\n\t\tpublic boolean put(Object value, IFieldInfoRef fld) {\n\t\t\tif (duplicates.contains(value)) {\n\t\t\t\tvalues.remove(value);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tIFieldInfoRef prev = values.put(value, fld);\n\t\t\tif (prev != null) {\n\t\t\t\tvalues.remove(value);\n\t\t\t\tduplicates.add(value);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic boolean contains(Object value) {\n\t\t\treturn duplicates.contains(value) || values.containsKey(value);\n\t\t}\n\n\t\tvoid removeForCls(ClassNode cls) {\n\t\t\tvalues.entrySet().removeIf(entry -> {\n\t\t\t\tIFieldInfoRef field = entry.getValue();\n\t\t\t\tif (field instanceof FieldNode) {\n\t\t\t\t\treturn ((FieldNode) field).getParentClass().equals(cls);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate final boolean replaceEnabled;\n\tprivate final ValueStorage globalValues = new ValueStorage();\n\tprivate final Map<ClassNode, ValueStorage> classes = new HashMap<>();\n\n\tprivate Map<Integer, String> resourcesNames = new HashMap<>();\n\n\tpublic ConstStorage(JadxArgs args) {\n\t\tthis.replaceEnabled = args.isReplaceConsts();\n\t}\n\n\tpublic void addConstField(FieldNode fld, Object value, boolean isPublic) {\n\t\tif (isPublic) {\n\t\t\taddGlobalConstField(fld, value);\n\t\t} else {\n\t\t\tgetClsValues(fld.getParentClass()).put(value, fld);\n\t\t}\n\t}\n\n\tpublic void addGlobalConstField(IFieldInfoRef fld, Object value) {\n\t\tglobalValues.put(value, fld);\n\t}\n\n\t/**\n\t * Use method from CollectConstValues class\n\t */\n\t@Deprecated\n\tpublic static @Nullable Object getFieldConstValue(FieldNode fld) {\n\t\treturn CollectConstValues.getFieldConstValue(fld);\n\t}\n\n\tpublic void removeForClass(ClassNode cls) {\n\t\tclasses.remove(cls);\n\t\tglobalValues.removeForCls(cls);\n\t}\n\n\tprivate ValueStorage getClsValues(ClassNode cls) {\n\t\treturn classes.computeIfAbsent(cls, c -> new ValueStorage());\n\t}\n\n\tpublic @Nullable IFieldInfoRef getConstField(ClassNode cls, Object value, boolean searchGlobal) {\n\t\tif (!replaceEnabled) {\n\t\t\treturn null;\n\t\t}\n\t\tRootNode root = cls.root();\n\t\tif (value instanceof Integer) {\n\t\t\tFieldNode rField = getResourceField((Integer) value, root);\n\t\t\tif (rField != null) {\n\t\t\t\treturn rField;\n\t\t\t}\n\t\t}\n\t\tboolean foundInGlobal = globalValues.contains(value);\n\t\tif (foundInGlobal && !searchGlobal) {\n\t\t\treturn null;\n\t\t}\n\t\tClassNode current = cls;\n\t\twhile (current != null) {\n\t\t\tValueStorage classValues = classes.get(current);\n\t\t\tif (classValues != null) {\n\t\t\t\tIFieldInfoRef field = classValues.get(value);\n\t\t\t\tif (field != null) {\n\t\t\t\t\tif (foundInGlobal) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\treturn field;\n\t\t\t\t}\n\t\t\t}\n\t\t\tClassInfo parentClass = current.getClassInfo().getParentClass();\n\t\t\tif (parentClass == null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcurrent = root.resolveClass(parentClass);\n\t\t}\n\t\tif (searchGlobal) {\n\t\t\treturn globalValues.get(value);\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Nullable\n\tprivate FieldNode getResourceField(Integer value, RootNode root) {\n\t\tString str = resourcesNames.get(value);\n\t\tif (str == null) {\n\t\t\treturn null;\n\t\t}\n\t\tClassNode appResClass = root.getAppResClass();\n\t\tif (appResClass == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString[] parts = str.split(\"/\", 2);\n\t\tif (parts.length != 2) {\n\t\t\treturn null;\n\t\t}\n\t\tString typeName = parts[0];\n\t\tString fieldName = parts[1];\n\t\tfor (ClassNode innerClass : appResClass.getInnerClasses()) {\n\t\t\tif (innerClass.getClassInfo().getShortName().equals(typeName)) {\n\t\t\t\treturn innerClass.searchFieldByName(fieldName);\n\t\t\t}\n\t\t}\n\t\tappResClass.addWarn(\"Not found resource field with id: \" + value + \", name: \" + str.replace('/', '.'));\n\t\treturn null;\n\t}\n\n\tpublic @Nullable IFieldInfoRef getConstFieldByLiteralArg(ClassNode cls, LiteralArg arg) {\n\t\tif (!replaceEnabled) {\n\t\t\treturn null;\n\t\t}\n\t\tPrimitiveType type = arg.getType().getPrimitiveType();\n\t\tif (type == null) {\n\t\t\treturn null;\n\t\t}\n\t\tlong literal = arg.getLiteral();\n\t\tswitch (type) {\n\t\t\tcase BOOLEAN:\n\t\t\t\treturn getConstField(cls, literal == 1, false);\n\t\t\tcase CHAR:\n\t\t\t\treturn getConstField(cls, (char) literal, Math.abs(literal) > 10);\n\t\t\tcase BYTE:\n\t\t\t\treturn getConstField(cls, (byte) literal, Math.abs(literal) > 10);\n\t\t\tcase SHORT:\n\t\t\t\treturn getConstField(cls, (short) literal, Math.abs(literal) > 100);\n\t\t\tcase INT:\n\t\t\t\treturn getConstField(cls, (int) literal, Math.abs(literal) > 100);\n\t\t\tcase LONG:\n\t\t\t\treturn getConstField(cls, literal, Math.abs(literal) > 1000);\n\t\t\tcase FLOAT:\n\t\t\t\tfloat f = Float.intBitsToFloat((int) literal);\n\t\t\t\treturn getConstField(cls, f, Float.compare(f, 0) == 0);\n\t\t\tcase DOUBLE:\n\t\t\t\tdouble d = Double.longBitsToDouble(literal);\n\t\t\t\treturn getConstField(cls, d, Double.compare(d, 0) == 0);\n\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic void setResourcesNames(Map<Integer, String> resourcesNames) {\n\t\tthis.resourcesNames = resourcesNames;\n\t}\n\n\tpublic Map<Integer, String> getResourcesNames() {\n\t\treturn resourcesNames;\n\t}\n\n\tpublic Map<Object, IFieldInfoRef> getGlobalConstFields() {\n\t\treturn globalValues.getValues();\n\t}\n\n\tpublic boolean isReplaceEnabled() {\n\t\treturn replaceEnabled;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/info/FieldInfo.java",
    "content": "package jadx.core.dex.info;\n\nimport java.util.Objects;\n\nimport jadx.api.plugins.input.data.IFieldRef;\nimport jadx.core.codegen.TypeGen;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.IFieldInfoRef;\nimport jadx.core.dex.nodes.RootNode;\n\npublic final class FieldInfo implements IFieldInfoRef {\n\n\tprivate final ClassInfo declClass;\n\tprivate final String name;\n\tprivate final ArgType type;\n\tprivate String alias;\n\n\tprivate FieldInfo(ClassInfo declClass, String name, ArgType type) {\n\t\tthis.declClass = declClass;\n\t\tthis.name = name;\n\t\tthis.type = type;\n\t\tthis.alias = name;\n\t}\n\n\tpublic static FieldInfo from(RootNode root, ClassInfo declClass, String name, ArgType type) {\n\t\tFieldInfo field = new FieldInfo(declClass, name, type);\n\t\treturn root.getInfoStorage().getField(field);\n\t}\n\n\tpublic static FieldInfo fromRef(RootNode root, IFieldRef fieldRef) {\n\t\tClassInfo declClass = ClassInfo.fromName(root, fieldRef.getParentClassType());\n\t\tFieldInfo field = new FieldInfo(declClass, fieldRef.getName(), ArgType.parse(fieldRef.getType()));\n\t\treturn root.getInfoStorage().getField(field);\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic ArgType getType() {\n\t\treturn type;\n\t}\n\n\tpublic ClassInfo getDeclClass() {\n\t\treturn declClass;\n\t}\n\n\tpublic String getAlias() {\n\t\treturn alias;\n\t}\n\n\tpublic void setAlias(String alias) {\n\t\tthis.alias = alias;\n\t}\n\n\tpublic void removeAlias() {\n\t\tthis.alias = name;\n\t}\n\n\tpublic boolean hasAlias() {\n\t\treturn !Objects.equals(name, alias);\n\t}\n\n\tpublic String getFullId() {\n\t\treturn declClass.getFullName() + '.' + name + ':' + TypeGen.signature(type);\n\t}\n\n\tpublic String getShortId() {\n\t\treturn name + ':' + TypeGen.signature(type);\n\t}\n\n\tpublic String getRawFullId() {\n\t\treturn declClass.makeRawFullName() + '.' + name + ':' + TypeGen.signature(type);\n\t}\n\n\tpublic boolean equalsNameAndType(FieldInfo other) {\n\t\treturn name.equals(other.name) && type.equals(other.type);\n\t}\n\n\t@Override\n\tpublic FieldInfo getFieldInfo() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tFieldInfo fieldInfo = (FieldInfo) o;\n\t\treturn name.equals(fieldInfo.name)\n\t\t\t\t&& type.equals(fieldInfo.type)\n\t\t\t\t&& declClass.equals(fieldInfo.declClass);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = name.hashCode();\n\t\tresult = 31 * result + type.hashCode();\n\t\tresult = 31 * result + declClass.hashCode();\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn declClass + \".\" + name + ' ' + type;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/info/InfoStorage.java",
    "content": "package jadx.core.dex.info;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.instructions.args.ArgType;\n\npublic class InfoStorage {\n\n\tprivate final Map<ArgType, ClassInfo> classes = new HashMap<>();\n\tprivate final Map<FieldInfo, FieldInfo> fields = new HashMap<>();\n\t// use only one MethodInfo instance\n\tprivate final Map<MethodInfo, MethodInfo> uniqueMethods = new HashMap<>();\n\t// can contain same method with different ids (from different files)\n\tprivate final Map<Integer, MethodInfo> methods = new HashMap<>();\n\n\tprivate final Map<String, PackageInfo> packages = new HashMap<>();\n\n\tpublic ClassInfo getCls(ArgType type) {\n\t\treturn classes.get(type);\n\t}\n\n\tpublic ClassInfo putCls(ClassInfo cls) {\n\t\tsynchronized (classes) {\n\t\t\tClassInfo prev = classes.put(cls.getType(), cls);\n\t\t\treturn prev == null ? cls : prev;\n\t\t}\n\t}\n\n\tpublic MethodInfo getByUniqId(int id) {\n\t\tsynchronized (methods) {\n\t\t\treturn methods.get(id);\n\t\t}\n\t}\n\n\tpublic void putByUniqId(int id, MethodInfo mth) {\n\t\tsynchronized (methods) {\n\t\t\tmethods.put(id, mth);\n\t\t}\n\t}\n\n\tpublic MethodInfo putMethod(MethodInfo newMth) {\n\t\tsynchronized (uniqueMethods) {\n\t\t\tMethodInfo prev = uniqueMethods.get(newMth);\n\t\t\tif (prev != null) {\n\t\t\t\treturn prev;\n\t\t\t}\n\t\t\tuniqueMethods.put(newMth, newMth);\n\t\t\treturn newMth;\n\t\t}\n\t}\n\n\tpublic FieldInfo getField(FieldInfo field) {\n\t\tsynchronized (fields) {\n\t\t\tFieldInfo f = fields.get(field);\n\t\t\tif (f != null) {\n\t\t\t\treturn f;\n\t\t\t}\n\t\t\tfields.put(field, field);\n\t\t\treturn field;\n\t\t}\n\t}\n\n\tpublic @Nullable PackageInfo getPkg(String fullName) {\n\t\treturn packages.get(fullName);\n\t}\n\n\tpublic void putPkg(PackageInfo pkg) {\n\t\tpackages.put(pkg.getFullName(), pkg);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java",
    "content": "package jadx.core.dex.info;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.IMethodProto;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.core.codegen.TypeGen;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.Utils;\n\npublic final class MethodInfo implements Comparable<MethodInfo> {\n\n\tprivate final String name;\n\tprivate final ArgType retType;\n\tprivate final List<ArgType> argTypes;\n\tprivate final ClassInfo declClass;\n\tprivate final String shortId;\n\tprivate final String rawFullId;\n\tprivate final int hash;\n\n\tprivate String alias;\n\n\tprivate MethodInfo(ClassInfo declClass, String name, List<ArgType> args, ArgType retType) {\n\t\tthis.name = name;\n\t\tthis.alias = name;\n\t\tthis.declClass = declClass;\n\t\tthis.argTypes = args;\n\t\tthis.retType = retType;\n\t\tthis.shortId = makeShortId(name, argTypes, retType);\n\t\tthis.rawFullId = declClass.makeRawFullName() + '.' + shortId;\n\t\tthis.hash = calcHashCode();\n\t}\n\n\tpublic static MethodInfo fromRef(RootNode root, IMethodRef methodRef) {\n\t\tInfoStorage infoStorage = root.getInfoStorage();\n\t\tint uniqId = methodRef.getUniqId();\n\t\tif (uniqId != 0) {\n\t\t\tMethodInfo prevMth = infoStorage.getByUniqId(uniqId);\n\t\t\tif (prevMth != null) {\n\t\t\t\treturn prevMth;\n\t\t\t}\n\t\t}\n\t\tmethodRef.load();\n\t\tArgType parentClsType = ArgType.parse(methodRef.getParentClassType());\n\t\tClassInfo parentClass = ClassInfo.fromType(root, parentClsType);\n\t\tArgType returnType = ArgType.parse(methodRef.getReturnType());\n\t\tList<ArgType> args = Utils.collectionMap(methodRef.getArgTypes(), ArgType::parse);\n\t\tMethodInfo newMth = new MethodInfo(parentClass, methodRef.getName(), args, returnType);\n\t\tMethodInfo uniqMth = infoStorage.putMethod(newMth);\n\t\tif (uniqId != 0) {\n\t\t\tinfoStorage.putByUniqId(uniqId, uniqMth);\n\t\t}\n\t\treturn uniqMth;\n\t}\n\n\tpublic static MethodInfo fromDetails(RootNode root, ClassInfo declClass, String name, List<ArgType> args, ArgType retType) {\n\t\tMethodInfo newMth = new MethodInfo(declClass, name, args, retType);\n\t\treturn root.getInfoStorage().putMethod(newMth);\n\t}\n\n\tpublic static MethodInfo fromMethodProto(RootNode root, ClassInfo declClass, String name, IMethodProto proto) {\n\t\tList<ArgType> args = Utils.collectionMap(proto.getArgTypes(), ArgType::parse);\n\t\tArgType returnType = ArgType.parse(proto.getReturnType());\n\t\treturn fromDetails(root, declClass, name, args, returnType);\n\t}\n\n\tpublic String makeSignature(boolean includeRetType) {\n\t\treturn makeSignature(false, includeRetType);\n\t}\n\n\tpublic String makeSignature(boolean useAlias, boolean includeRetType) {\n\t\treturn makeShortId(useAlias ? alias : name,\n\t\t\t\targTypes,\n\t\t\t\tincludeRetType ? retType : null);\n\t}\n\n\tpublic static String makeShortId(String name, List<ArgType> argTypes, @Nullable ArgType retType) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(name);\n\t\tsb.append('(');\n\t\tfor (ArgType arg : argTypes) {\n\t\t\tsb.append(TypeGen.signature(arg));\n\t\t}\n\t\tsb.append(')');\n\t\tif (retType != null) {\n\t\t\tsb.append(TypeGen.signature(retType));\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic boolean isOverloadedBy(MethodInfo otherMthInfo) {\n\t\treturn argTypes.size() == otherMthInfo.argTypes.size()\n\t\t\t\t&& name.equals(otherMthInfo.name)\n\t\t\t\t&& !Objects.equals(this.shortId, otherMthInfo.shortId);\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic String getFullName() {\n\t\treturn declClass.getFullName() + '.' + name;\n\t}\n\n\tpublic String getAliasFullName() {\n\t\treturn declClass.getAliasFullName() + '.' + alias;\n\t}\n\n\tpublic String getFullId() {\n\t\treturn declClass.getFullName() + '.' + shortId;\n\t}\n\n\tpublic String getRawFullId() {\n\t\treturn rawFullId;\n\t}\n\n\t/**\n\t * Method name and signature\n\t */\n\tpublic String getShortId() {\n\t\treturn shortId;\n\t}\n\n\tpublic ClassInfo getDeclClass() {\n\t\treturn declClass;\n\t}\n\n\tpublic ArgType getReturnType() {\n\t\treturn retType;\n\t}\n\n\tpublic List<ArgType> getArgumentsTypes() {\n\t\treturn argTypes;\n\t}\n\n\tpublic int getArgsCount() {\n\t\treturn argTypes.size();\n\t}\n\n\tpublic boolean isConstructor() {\n\t\treturn name.equals(\"<init>\");\n\t}\n\n\tpublic boolean isClassInit() {\n\t\treturn name.equals(\"<clinit>\");\n\t}\n\n\tpublic String getAlias() {\n\t\treturn alias;\n\t}\n\n\tpublic void setAlias(String alias) {\n\t\tthis.alias = alias;\n\t}\n\n\tpublic void removeAlias() {\n\t\tthis.alias = name;\n\t}\n\n\tpublic boolean hasAlias() {\n\t\treturn !name.equals(alias);\n\t}\n\n\tpublic int calcHashCode() {\n\t\treturn shortId.hashCode() + 31 * declClass.hashCode();\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn hash;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof MethodInfo)) {\n\t\t\treturn false;\n\t\t}\n\t\tMethodInfo other = (MethodInfo) obj;\n\t\treturn shortId.equals(other.shortId)\n\t\t\t\t&& declClass.equals(other.declClass);\n\t}\n\n\t@Override\n\tpublic int compareTo(MethodInfo other) {\n\t\tint clsCmp = declClass.compareTo(other.declClass);\n\t\tif (clsCmp != 0) {\n\t\t\treturn clsCmp;\n\t\t}\n\t\treturn shortId.compareTo(other.shortId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn declClass.getFullName() + '.' + name\n\t\t\t\t+ '(' + Utils.listToString(argTypes) + \"):\" + retType;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/info/PackageInfo.java",
    "content": "package jadx.core.dex.info;\n\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.nodes.RootNode;\n\npublic class PackageInfo {\n\n\tprivate final @Nullable PackageInfo parentPkg;\n\tprivate final String fullName;\n\tprivate final String name;\n\n\tpublic static synchronized PackageInfo fromFullPkg(RootNode root, String fullPkg) {\n\t\tPackageInfo existPkg = root.getInfoStorage().getPkg(fullPkg);\n\t\tif (existPkg != null) {\n\t\t\treturn existPkg;\n\t\t}\n\t\tPackageInfo newPkg;\n\t\tint lastDot = fullPkg.lastIndexOf('.');\n\t\tif (lastDot == -1) {\n\t\t\t// unknown root pkg\n\t\t\tnewPkg = new PackageInfo(fullPkg, null, fullPkg);\n\t\t} else {\n\t\t\tPackageInfo parentPkg = fromFullPkg(root, fullPkg.substring(0, lastDot));\n\t\t\tnewPkg = new PackageInfo(fullPkg, parentPkg, fullPkg.substring(lastDot + 1));\n\t\t}\n\t\troot.getInfoStorage().putPkg(newPkg);\n\t\treturn newPkg;\n\t}\n\n\tpublic static synchronized PackageInfo fromShortName(RootNode root, @Nullable PackageInfo parent, String shortName) {\n\t\tString fullPkg = parent == null ? shortName : parent.getFullName() + '.' + shortName;\n\t\tPackageInfo existPkg = root.getInfoStorage().getPkg(fullPkg);\n\t\tif (existPkg != null) {\n\t\t\treturn existPkg;\n\t\t}\n\t\tPackageInfo newPkg = new PackageInfo(fullPkg, parent, shortName);\n\t\troot.getInfoStorage().putPkg(newPkg);\n\t\treturn newPkg;\n\t}\n\n\tprivate PackageInfo(String fullName, @Nullable PackageInfo parentPkg, String name) {\n\t\tthis.fullName = fullName;\n\t\tthis.parentPkg = parentPkg;\n\t\tthis.name = name;\n\t}\n\n\tpublic boolean isRoot() {\n\t\treturn parentPkg == null;\n\t}\n\n\tpublic boolean isDefaultPkg() {\n\t\treturn fullName.isEmpty();\n\t}\n\n\tpublic String getFullName() {\n\t\treturn fullName;\n\t}\n\n\tpublic @Nullable PackageInfo getParentPkg() {\n\t\treturn parentPkg;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof PackageInfo)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn Objects.equals(fullName, ((PackageInfo) o).getFullName());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn fullName.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn fullName;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/ArithNode.java",
    "content": "package jadx.core.dex.instructions;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class ArithNode extends InsnNode {\n\n\tpublic static ArithNode build(InsnData insn, ArithOp op, ArgType type) {\n\t\tRegisterArg resArg = InsnArg.reg(insn, 0, fixResultType(op, type));\n\t\tArgType argType = fixArgType(op, type);\n\t\tswitch (insn.getRegsCount()) {\n\t\t\tcase 2:\n\t\t\t\treturn new ArithNode(op, resArg, InsnArg.reg(insn, 0, argType), InsnArg.reg(insn, 1, argType));\n\t\t\tcase 3:\n\t\t\t\treturn new ArithNode(op, resArg, InsnArg.reg(insn, 1, argType), InsnArg.reg(insn, 2, argType));\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unexpected registers count in \" + insn);\n\t\t}\n\t}\n\n\tpublic static ArithNode buildLit(InsnData insn, ArithOp op, ArgType type) {\n\t\tRegisterArg resArg = InsnArg.reg(insn, 0, fixResultType(op, type));\n\t\tArgType argType = fixArgType(op, type);\n\t\tLiteralArg litArg = InsnArg.lit(insn, argType);\n\t\tswitch (insn.getRegsCount()) {\n\t\t\tcase 1:\n\t\t\t\treturn new ArithNode(op, resArg, InsnArg.reg(insn, 0, argType), litArg);\n\t\t\tcase 2:\n\t\t\t\treturn new ArithNode(op, resArg, InsnArg.reg(insn, 1, argType), litArg);\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unexpected registers count in \" + insn);\n\t\t}\n\t}\n\n\tprivate static ArgType fixResultType(ArithOp op, ArgType type) {\n\t\tif (type == ArgType.INT && op.isBitOp()) {\n\t\t\treturn ArgType.INT_BOOLEAN;\n\t\t}\n\t\treturn type;\n\t}\n\n\tprivate static ArgType fixArgType(ArithOp op, ArgType type) {\n\t\tif (type == ArgType.INT && op.isBitOp()) {\n\t\t\treturn ArgType.NARROW_NUMBERS_NO_FLOAT;\n\t\t}\n\t\treturn type;\n\t}\n\n\tprivate final ArithOp op;\n\n\tpublic ArithNode(ArithOp op, @Nullable RegisterArg res, InsnArg a, InsnArg b) {\n\t\tsuper(InsnType.ARITH, 2);\n\t\tthis.op = op;\n\t\tsetResult(res);\n\t\taddArg(a);\n\t\taddArg(b);\n\t}\n\n\t/**\n\t * Create one argument arithmetic instructions (a+=2).\n\t * Result is not set (null).\n\t *\n\t * @param res argument to change\n\t */\n\tpublic static ArithNode oneArgOp(ArithOp op, InsnArg res, InsnArg a) {\n\t\tArithNode insn = new ArithNode(op, null, res, a);\n\t\tinsn.add(AFlag.ARITH_ONEARG);\n\t\treturn insn;\n\t}\n\n\tpublic ArithOp getOp() {\n\t\treturn op;\n\t}\n\n\t@Override\n\tpublic boolean isSame(InsnNode obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof ArithNode) || !super.isSame(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tArithNode other = (ArithNode) obj;\n\t\treturn op == other.op && isSameLiteral(other);\n\t}\n\n\tprivate boolean isSameLiteral(ArithNode other) {\n\t\tInsnArg thisSecond = getArg(1);\n\t\tInsnArg otherSecond = other.getArg(1);\n\t\tif (thisSecond.isLiteral() != otherSecond.isLiteral()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!thisSecond.isLiteral()) {\n\t\t\t// both not literals\n\t\t\treturn true;\n\t\t}\n\t\t// both literals\n\t\tlong thisLit = ((LiteralArg) thisSecond).getLiteral();\n\t\tlong otherLit = ((LiteralArg) otherSecond).getLiteral();\n\t\treturn thisLit == otherLit;\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\tArithNode copy = new ArithNode(op, null, getArg(0).duplicate(), getArg(1).duplicate());\n\t\treturn copyCommonParams(copy);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(InsnUtils.formatOffset(offset));\n\t\tsb.append(\": ARITH \");\n\t\tif (contains(AFlag.ARITH_ONEARG)) {\n\t\t\tsb.append(getArg(0)).append(' ').append(op.getSymbol()).append(\"= \").append(getArg(1));\n\t\t} else {\n\t\t\tRegisterArg result = getResult();\n\t\t\tif (result != null) {\n\t\t\t\tsb.append(result).append(\" = \");\n\t\t\t}\n\t\t\tsb.append(getArg(0)).append(' ').append(op.getSymbol()).append(' ').append(getArg(1));\n\t\t}\n\n\t\tappendAttributes(sb);\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/ArithOp.java",
    "content": "package jadx.core.dex.instructions;\n\npublic enum ArithOp {\n\tADD(\"+\"),\n\tSUB(\"-\"),\n\tMUL(\"*\"),\n\tDIV(\"/\"),\n\tREM(\"%\"),\n\n\tAND(\"&\"),\n\tOR(\"|\"),\n\tXOR(\"^\"),\n\n\tSHL(\"<<\"),\n\tSHR(\">>\"),\n\tUSHR(\">>>\");\n\n\tprivate final String symbol;\n\n\tArithOp(String symbol) {\n\t\tthis.symbol = symbol;\n\t}\n\n\tpublic String getSymbol() {\n\t\treturn this.symbol;\n\t}\n\n\tpublic boolean isBitOp() {\n\t\tswitch (this) {\n\t\t\tcase AND:\n\t\t\tcase OR:\n\t\t\tcase XOR:\n\t\t\t\treturn true;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/BaseInvokeNode.java",
    "content": "package jadx.core.dex.instructions;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic abstract class BaseInvokeNode extends InsnNode {\n\tpublic BaseInvokeNode(InsnType type, int argsCount) {\n\t\tsuper(type, argsCount);\n\t}\n\n\tpublic abstract MethodInfo getCallMth();\n\n\t@Nullable\n\tpublic abstract InsnArg getInstanceArg();\n\n\tpublic abstract boolean isStaticCall();\n\n\t/**\n\t * Return offset to match method args from {@link #getCallMth()}\n\t */\n\tpublic abstract int getFirstArgOffset();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/ConstClassNode.java",
    "content": "package jadx.core.dex.instructions;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic final class ConstClassNode extends InsnNode {\n\n\tprivate final ArgType clsType;\n\n\tpublic ConstClassNode(ArgType clsType) {\n\t\tsuper(InsnType.CONST_CLASS, 0);\n\t\tthis.clsType = clsType;\n\t}\n\n\tpublic ArgType getClsType() {\n\t\treturn clsType;\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\treturn copyCommonParams(new ConstClassNode(clsType));\n\t}\n\n\t@Override\n\tpublic boolean isSame(InsnNode obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof ConstClassNode) || !super.isSame(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tConstClassNode other = (ConstClassNode) obj;\n\t\treturn clsType.equals(other.clsType);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn super.toString() + ' ' + clsType + \".class\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/ConstStringNode.java",
    "content": "package jadx.core.dex.instructions;\n\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.StringUtils;\n\npublic final class ConstStringNode extends InsnNode {\n\n\tprivate final String str;\n\n\tpublic ConstStringNode(String str) {\n\t\tsuper(InsnType.CONST_STR, 0);\n\t\tthis.str = str;\n\t}\n\n\tpublic String getString() {\n\t\treturn str;\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\treturn copyCommonParams(new ConstStringNode(str));\n\t}\n\n\t@Override\n\tpublic boolean isSame(InsnNode obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof ConstStringNode) || !super.isSame(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tConstStringNode other = (ConstStringNode) obj;\n\t\treturn str.equals(other.str);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn super.toString() + ' ' + StringUtils.getInstance().unescapeString(str);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/FillArrayData.java",
    "content": "package jadx.core.dex.instructions;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jadx.api.plugins.input.insns.custom.IArrayPayload;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.PrimitiveType;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic final class FillArrayData extends InsnNode {\n\n\tprivate static final ArgType ONE_BYTE_TYPE = ArgType.unknown(PrimitiveType.BYTE, PrimitiveType.BOOLEAN);\n\tprivate static final ArgType TWO_BYTES_TYPE = ArgType.unknown(PrimitiveType.SHORT, PrimitiveType.CHAR);\n\tprivate static final ArgType FOUR_BYTES_TYPE = ArgType.unknown(PrimitiveType.INT, PrimitiveType.FLOAT);\n\tprivate static final ArgType EIGHT_BYTES_TYPE = ArgType.unknown(PrimitiveType.LONG, PrimitiveType.DOUBLE);\n\n\tprivate final Object data;\n\tprivate final int size;\n\tprivate final int elemSize;\n\tprivate ArgType elemType;\n\n\tpublic FillArrayData(IArrayPayload payload) {\n\t\tthis(payload.getData(), payload.getSize(), payload.getElementSize());\n\t}\n\n\tprivate FillArrayData(Object data, int size, int elemSize) {\n\t\tsuper(InsnType.FILL_ARRAY_DATA, 0);\n\t\tthis.data = data;\n\t\tthis.size = size;\n\t\tthis.elemSize = elemSize;\n\t\tthis.elemType = getElementType(elemSize);\n\t}\n\n\tprivate static ArgType getElementType(int elementWidthUnit) {\n\t\tswitch (elementWidthUnit) {\n\t\t\tcase 1:\n\t\t\tcase 0:\n\t\t\t\treturn ONE_BYTE_TYPE;\n\t\t\tcase 2:\n\t\t\t\treturn TWO_BYTES_TYPE;\n\t\t\tcase 4:\n\t\t\t\treturn FOUR_BYTES_TYPE;\n\t\t\tcase 8:\n\t\t\t\treturn EIGHT_BYTES_TYPE;\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unknown array element width: \" + elementWidthUnit);\n\t\t}\n\t}\n\n\tpublic Object getData() {\n\t\treturn data;\n\t}\n\n\tpublic int getSize() {\n\t\treturn size;\n\t}\n\n\tpublic ArgType getElementType() {\n\t\treturn elemType;\n\t}\n\n\tpublic List<LiteralArg> getLiteralArgs(ArgType type) {\n\t\tList<LiteralArg> list = new ArrayList<>(size);\n\t\tObject array = data;\n\t\tswitch (elemSize) {\n\t\t\tcase 1:\n\t\t\t\tfor (byte b : (byte[]) array) {\n\t\t\t\t\tlist.add(InsnArg.lit(b, type));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tfor (short b : (short[]) array) {\n\t\t\t\t\tlist.add(InsnArg.lit(b, type));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 4:\n\t\t\t\tfor (int b : (int[]) array) {\n\t\t\t\t\tlist.add(InsnArg.lit(b, type));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 8:\n\t\t\t\tfor (long b : (long[]) array) {\n\t\t\t\t\tlist.add(InsnArg.lit(b, type));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unknown type: \" + data.getClass() + \", expected: \" + type);\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic boolean isSame(InsnNode obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof FillArrayData) || !super.isSame(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tFillArrayData other = (FillArrayData) obj;\n\t\treturn elemType.equals(other.elemType) && data == other.data;\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\tFillArrayData copy = new FillArrayData(data, size, elemSize);\n\t\tcopy.elemType = this.elemType;\n\t\treturn copyCommonParams(copy);\n\t}\n\n\tpublic String dataToString() {\n\t\tswitch (elemSize) {\n\t\t\tcase 1:\n\t\t\t\treturn Arrays.toString((byte[]) data);\n\t\t\tcase 2:\n\t\t\t\treturn Arrays.toString((short[]) data);\n\t\t\tcase 4:\n\t\t\t\treturn Arrays.toString((int[]) data);\n\t\t\tcase 8:\n\t\t\t\treturn Arrays.toString((long[]) data);\n\t\t\tdefault:\n\t\t\t\treturn \"?\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn super.toString() + \", data: \" + dataToString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/FillArrayInsn.java",
    "content": "package jadx.core.dex.instructions;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic final class FillArrayInsn extends InsnNode {\n\tprivate final int target;\n\tprivate FillArrayData arrayData;\n\n\tpublic FillArrayInsn(InsnArg arg, int target) {\n\t\tsuper(InsnType.FILL_ARRAY, 1);\n\t\tthis.target = target;\n\t\taddArg(arg);\n\t}\n\n\tpublic int getTarget() {\n\t\treturn target;\n\t}\n\n\tpublic void setArrayData(FillArrayData arrayData) {\n\t\tthis.arrayData = arrayData;\n\t}\n\n\t@Override\n\tpublic boolean isSame(InsnNode obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof FillArrayInsn) || !super.isSame(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tFillArrayInsn other = (FillArrayInsn) obj;\n\t\treturn Objects.equals(arrayData, other.arrayData);\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\tFillArrayInsn copy = new FillArrayInsn(getArg(0), target);\n\t\treturn copyCommonParams(copy);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn super.toString() + \", data: \" + arrayData;\n\t}\n\n\tpublic int getSize() {\n\t\treturn arrayData.getSize();\n\t}\n\n\tpublic ArgType getElementType() {\n\t\treturn arrayData.getElementType();\n\t}\n\n\tpublic List<LiteralArg> getLiteralArgs(ArgType elType) {\n\t\treturn arrayData.getLiteralArgs(elType);\n\t}\n\n\tpublic String dataToString() {\n\t\treturn Objects.toString(arrayData);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/FilledNewArrayNode.java",
    "content": "package jadx.core.dex.instructions;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic class FilledNewArrayNode extends InsnNode {\n\n\tprivate final ArgType elemType;\n\n\tpublic FilledNewArrayNode(@NotNull ArgType elemType, int size) {\n\t\tsuper(InsnType.FILLED_NEW_ARRAY, size);\n\t\tthis.elemType = elemType;\n\t}\n\n\tpublic ArgType getElemType() {\n\t\treturn elemType;\n\t}\n\n\tpublic ArgType getArrayType() {\n\t\treturn ArgType.array(elemType);\n\t}\n\n\t@Override\n\tpublic boolean isSame(InsnNode obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof FilledNewArrayNode) || !super.isSame(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tFilledNewArrayNode other = (FilledNewArrayNode) obj;\n\t\treturn elemType == other.elemType;\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\treturn copyCommonParams(new FilledNewArrayNode(elemType, getArgsCount()));\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn super.toString() + \" elemType: \" + elemType;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/GotoNode.java",
    "content": "package jadx.core.dex.instructions;\n\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.InsnUtils;\n\npublic class GotoNode extends TargetInsnNode {\n\n\tprotected final int target;\n\n\tpublic GotoNode(int target) {\n\t\tthis(InsnType.GOTO, target, 0);\n\t}\n\n\tprotected GotoNode(InsnType type, int target, int argsCount) {\n\t\tsuper(type, argsCount);\n\t\tthis.target = target;\n\t}\n\n\tpublic int getTarget() {\n\t\treturn target;\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\treturn copyCommonParams(new GotoNode(target));\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn super.toString() + \"-> \" + InsnUtils.formatOffset(target);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/IfNode.java",
    "content": "package jadx.core.dex.instructions;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.PrimitiveType;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.InsnUtils;\n\nimport static jadx.core.utils.BlockUtils.getBlockByOffset;\nimport static jadx.core.utils.BlockUtils.selectOther;\n\npublic class IfNode extends GotoNode {\n\n\tprotected IfOp op;\n\n\tprivate BlockNode thenBlock;\n\tprivate BlockNode elseBlock;\n\n\tpublic IfNode(InsnData insn, IfOp op) {\n\t\tsuper(InsnType.IF, insn.getTarget(), 2);\n\t\tthis.op = op;\n\t\tArgType argType = narrowTypeByOp(op);\n\t\taddArg(InsnArg.reg(insn, 0, argType));\n\t\tif (insn.getRegsCount() == 1) {\n\t\t\taddArg(InsnArg.lit(0, argType));\n\t\t} else {\n\t\t\taddArg(InsnArg.reg(insn, 1, argType));\n\t\t}\n\t}\n\n\tpublic IfNode(IfOp op, int targetOffset, InsnArg arg1, InsnArg arg2) {\n\t\tthis(op, targetOffset);\n\t\taddArg(arg1);\n\t\taddArg(arg2);\n\t}\n\n\tprivate IfNode(IfOp op, int targetOffset) {\n\t\tsuper(InsnType.IF, targetOffset, 2);\n\t\tthis.op = op;\n\t}\n\n\t// change default types priority\n\tprivate static final ArgType WIDE_TYPE = ArgType.unknown(\n\t\t\tPrimitiveType.INT, PrimitiveType.BOOLEAN,\n\t\t\tPrimitiveType.OBJECT, PrimitiveType.ARRAY,\n\t\t\tPrimitiveType.BYTE, PrimitiveType.SHORT, PrimitiveType.CHAR);\n\n\tprivate static final ArgType NUMBERS_TYPE = ArgType.unknown(\n\t\t\tPrimitiveType.INT, PrimitiveType.BYTE, PrimitiveType.SHORT, PrimitiveType.CHAR);\n\n\tprivate static ArgType narrowTypeByOp(IfOp op) {\n\t\tif (op == IfOp.EQ || op == IfOp.NE) {\n\t\t\treturn WIDE_TYPE;\n\t\t}\n\t\treturn NUMBERS_TYPE;\n\t}\n\n\tpublic IfOp getOp() {\n\t\treturn op;\n\t}\n\n\tpublic void invertCondition() {\n\t\top = op.invert();\n\t\tBlockNode tmp = thenBlock;\n\t\tthenBlock = elseBlock;\n\t\telseBlock = tmp;\n\t}\n\n\t/**\n\t * Change 'a != false' to 'a == true'\n\t */\n\tpublic void normalize() {\n\t\tif (getOp() == IfOp.NE && getArg(1).isFalse()) {\n\t\t\tchangeCondition(IfOp.EQ, getArg(0), LiteralArg.litTrue());\n\t\t}\n\t}\n\n\tpublic void changeCondition(IfOp op, InsnArg arg1, InsnArg arg2) {\n\t\tthis.op = op;\n\t\tsetArg(0, arg1);\n\t\tsetArg(1, arg2);\n\t}\n\n\t@Override\n\tpublic void initBlocks(BlockNode curBlock) {\n\t\tList<BlockNode> successors = curBlock.getSuccessors();\n\t\tthenBlock = getBlockByOffset(target, successors);\n\t\tif (successors.size() == 1) {\n\t\t\telseBlock = thenBlock;\n\t\t} else {\n\t\t\telseBlock = selectOther(thenBlock, successors);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean replaceTargetBlock(BlockNode origin, BlockNode replace) {\n\t\tboolean replaced = false;\n\t\tif (thenBlock == origin) {\n\t\t\tthenBlock = replace;\n\t\t\treplaced = true;\n\t\t}\n\t\tif (elseBlock == origin) {\n\t\t\telseBlock = replace;\n\t\t\treplaced = true;\n\t\t}\n\t\treturn replaced;\n\t}\n\n\tpublic BlockNode getThenBlock() {\n\t\treturn thenBlock;\n\t}\n\n\tpublic BlockNode getElseBlock() {\n\t\treturn elseBlock;\n\t}\n\n\t@Override\n\tpublic int getTarget() {\n\t\treturn thenBlock == null ? target : thenBlock.getStartOffset();\n\t}\n\n\t@Override\n\tpublic boolean isSame(InsnNode obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof IfNode) || !super.isSame(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tIfNode other = (IfNode) obj;\n\t\treturn op == other.op;\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\tIfNode copy = new IfNode(op, target);\n\t\tcopy.thenBlock = thenBlock;\n\t\tcopy.elseBlock = elseBlock;\n\t\treturn copyCommonParams(copy);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn InsnUtils.formatOffset(offset) + \": \"\n\t\t\t\t+ InsnUtils.insnTypeToString(insnType)\n\t\t\t\t+ getArg(0) + ' ' + op.getSymbol() + ' ' + getArg(1)\n\t\t\t\t+ \"  -> \" + (thenBlock != null ? thenBlock : InsnUtils.formatOffset(target))\n\t\t\t\t+ attributesString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/IfOp.java",
    "content": "package jadx.core.dex.instructions;\n\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic enum IfOp {\n\tEQ(\"==\"),\n\tNE(\"!=\"),\n\tLT(\"<\"),\n\tLE(\"<=\"),\n\tGT(\">\"),\n\tGE(\">=\");\n\n\tprivate final String symbol;\n\n\tIfOp(String symbol) {\n\t\tthis.symbol = symbol;\n\t}\n\n\tpublic String getSymbol() {\n\t\treturn symbol;\n\t}\n\n\tpublic IfOp invert() {\n\t\tswitch (this) {\n\t\t\tcase EQ:\n\t\t\t\treturn NE;\n\t\t\tcase NE:\n\t\t\t\treturn EQ;\n\n\t\t\tcase LT:\n\t\t\t\treturn GE;\n\t\t\tcase LE:\n\t\t\t\treturn GT;\n\n\t\t\tcase GT:\n\t\t\t\treturn LE;\n\t\t\tcase GE:\n\t\t\t\treturn LT;\n\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unknown if operations type: \" + this);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/IndexInsnNode.java",
    "content": "package jadx.core.dex.instructions;\n\nimport java.util.Objects;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.Utils;\n\npublic class IndexInsnNode extends InsnNode {\n\n\tprivate Object index;\n\n\tpublic IndexInsnNode(InsnType type, Object index, int argCount) {\n\t\tsuper(type, argCount);\n\t\tthis.index = index;\n\t}\n\n\tpublic Object getIndex() {\n\t\treturn index;\n\t}\n\n\tpublic void updateIndex(Object index) {\n\t\tthis.index = index;\n\t}\n\n\tpublic ArgType getIndexAsType() {\n\t\treturn (ArgType) index;\n\t}\n\n\t@Override\n\tpublic IndexInsnNode copy() {\n\t\treturn copyCommonParams(new IndexInsnNode(insnType, index, getArgsCount()));\n\t}\n\n\t@Override\n\tpublic boolean isSame(InsnNode obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof IndexInsnNode) || !super.isSame(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tIndexInsnNode other = (IndexInsnNode) obj;\n\t\treturn Objects.equals(index, other.index);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tswitch (insnType) {\n\t\t\tcase CAST:\n\t\t\tcase CHECK_CAST:\n\t\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\t\tsb.append(InsnUtils.formatOffset(offset)).append(\": \");\n\t\t\t\tsb.append(insnType).append(' ');\n\t\t\t\tif (getResult() != null) {\n\t\t\t\t\tsb.append(getResult()).append(\" = \");\n\t\t\t\t}\n\t\t\t\tsb.append('(').append(InsnUtils.indexToString(index)).append(\") \");\n\t\t\t\tsb.append(Utils.listToString(getArguments()));\n\t\t\t\treturn sb.toString();\n\n\t\t\tdefault:\n\t\t\t\treturn super.toString() + ' ' + InsnUtils.indexToString(index);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java",
    "content": "package jadx.core.dex.instructions;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport jadx.api.plugins.input.data.ICodeReader;\nimport jadx.api.plugins.input.data.IMethodProto;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.api.plugins.input.insns.custom.IArrayPayload;\nimport jadx.api.plugins.input.insns.custom.ICustomPayload;\nimport jadx.api.plugins.input.insns.custom.ISwitchPayload;\nimport jadx.core.Consts;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.CodeFeaturesAttr;\nimport jadx.core.dex.attributes.nodes.CodeFeaturesAttr.CodeFeature;\nimport jadx.core.dex.attributes.nodes.JadxError;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.java.JsrNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.DecodeException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.input.InsnDataUtils;\n\npublic class InsnDecoder {\n\tprivate final MethodNode method;\n\tprivate final RootNode root;\n\n\tpublic InsnDecoder(MethodNode mthNode) {\n\t\tthis.method = mthNode;\n\t\tthis.root = method.root();\n\t}\n\n\tpublic InsnNode[] process(ICodeReader codeReader) {\n\t\tInsnNode[] instructions = new InsnNode[codeReader.getUnitsCount()];\n\t\tcodeReader.visitInstructions(rawInsn -> {\n\t\t\tint offset = rawInsn.getOffset();\n\t\t\tInsnNode insn;\n\t\t\ttry {\n\t\t\t\trawInsn.decode();\n\t\t\t\tinsn = decode(rawInsn);\n\t\t\t} catch (Exception e) {\n\t\t\t\tboolean mthWithErrors = method.contains(AType.JADX_ERROR);\n\t\t\t\tmethod.addError(\"Failed to decode insn: \" + rawInsn, e);\n\t\t\t\tif (mthWithErrors) {\n\t\t\t\t\t// second error in this method => abort processing\n\t\t\t\t\tthrow new JadxRuntimeException(\"Failed to decode insn: \" + rawInsn, e);\n\t\t\t\t}\n\t\t\t\tinsn = new InsnNode(InsnType.NOP, 0);\n\t\t\t\tinsn.addAttr(AType.JADX_ERROR, new JadxError(\"decode failed: \" + e.getMessage(), e));\n\t\t\t}\n\t\t\tinsn.setOffset(offset);\n\t\t\tinstructions[offset] = insn;\n\t\t});\n\t\treturn instructions;\n\t}\n\n\tprotected InsnNode decode(InsnData insn) throws DecodeException {\n\t\tswitch (insn.getOpcode()) {\n\t\t\tcase NOP:\n\t\t\t\treturn new InsnNode(InsnType.NOP, 0);\n\n\t\t\t// move-result will be process in invoke and filled-new-array instructions\n\t\t\tcase MOVE_RESULT:\n\t\t\t\treturn insn(InsnType.MOVE_RESULT, InsnArg.reg(insn, 0, ArgType.UNKNOWN));\n\n\t\t\tcase CONST:\n\t\t\t\tLiteralArg narrowLitArg = InsnArg.lit(insn, ArgType.NARROW);\n\t\t\t\treturn insn(InsnType.CONST, InsnArg.reg(insn, 0, narrowLitArg.getType()), narrowLitArg);\n\n\t\t\tcase CONST_WIDE:\n\t\t\t\tLiteralArg wideLitArg = InsnArg.lit(insn, ArgType.WIDE);\n\t\t\t\treturn insn(InsnType.CONST, InsnArg.reg(insn, 0, wideLitArg.getType()), wideLitArg);\n\n\t\t\tcase CONST_STRING:\n\t\t\t\tInsnNode constStrInsn = new ConstStringNode(insn.getIndexAsString());\n\t\t\t\tconstStrInsn.setResult(InsnArg.reg(insn, 0, ArgType.STRING));\n\t\t\t\treturn constStrInsn;\n\n\t\t\tcase CONST_CLASS: {\n\t\t\t\tArgType clsType = ArgType.parse(insn.getIndexAsType());\n\t\t\t\tInsnNode constClsInsn = new ConstClassNode(clsType);\n\t\t\t\tconstClsInsn.setResult(InsnArg.reg(insn, 0, ArgType.generic(Consts.CLASS_CLASS, clsType)));\n\t\t\t\treturn constClsInsn;\n\t\t\t}\n\n\t\t\tcase MOVE:\n\t\t\t\treturn insn(InsnType.MOVE,\n\t\t\t\t\t\tInsnArg.reg(insn, 0, ArgType.NARROW),\n\t\t\t\t\t\tInsnArg.reg(insn, 1, ArgType.NARROW));\n\n\t\t\tcase MOVE_MULTI:\n\t\t\t\tint len = insn.getRegsCount();\n\t\t\t\tInsnNode mmv = new InsnNode(InsnType.MOVE_MULTI, len);\n\t\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\t\tmmv.addArg(InsnArg.reg(insn, i, ArgType.UNKNOWN));\n\t\t\t\t}\n\t\t\t\treturn mmv;\n\n\t\t\tcase MOVE_WIDE:\n\t\t\t\treturn insn(InsnType.MOVE,\n\t\t\t\t\t\tInsnArg.reg(insn, 0, ArgType.WIDE),\n\t\t\t\t\t\tInsnArg.reg(insn, 1, ArgType.WIDE));\n\n\t\t\tcase MOVE_OBJECT:\n\t\t\t\treturn insn(InsnType.MOVE,\n\t\t\t\t\t\tInsnArg.reg(insn, 0, ArgType.UNKNOWN_OBJECT),\n\t\t\t\t\t\tInsnArg.reg(insn, 1, ArgType.UNKNOWN_OBJECT));\n\n\t\t\tcase ADD_INT:\n\t\t\t\treturn arith(insn, ArithOp.ADD, ArgType.INT);\n\n\t\t\tcase ADD_DOUBLE:\n\t\t\t\treturn arith(insn, ArithOp.ADD, ArgType.DOUBLE);\n\n\t\t\tcase ADD_FLOAT:\n\t\t\t\treturn arith(insn, ArithOp.ADD, ArgType.FLOAT);\n\n\t\t\tcase ADD_LONG:\n\t\t\t\treturn arith(insn, ArithOp.ADD, ArgType.LONG);\n\n\t\t\tcase ADD_INT_LIT:\n\t\t\t\treturn arithLit(insn, ArithOp.ADD, ArgType.INT);\n\n\t\t\tcase SUB_INT:\n\t\t\t\treturn arith(insn, ArithOp.SUB, ArgType.INT);\n\n\t\t\tcase RSUB_INT:\n\t\t\t\treturn new ArithNode(ArithOp.SUB,\n\t\t\t\t\t\tInsnArg.reg(insn, 0, ArgType.INT),\n\t\t\t\t\t\tInsnArg.lit(insn, ArgType.INT),\n\t\t\t\t\t\tInsnArg.reg(insn, 1, ArgType.INT));\n\n\t\t\tcase SUB_LONG:\n\t\t\t\treturn arith(insn, ArithOp.SUB, ArgType.LONG);\n\n\t\t\tcase SUB_FLOAT:\n\t\t\t\treturn arith(insn, ArithOp.SUB, ArgType.FLOAT);\n\n\t\t\tcase SUB_DOUBLE:\n\t\t\t\treturn arith(insn, ArithOp.SUB, ArgType.DOUBLE);\n\n\t\t\tcase MUL_INT:\n\t\t\t\treturn arith(insn, ArithOp.MUL, ArgType.INT);\n\n\t\t\tcase MUL_DOUBLE:\n\t\t\t\treturn arith(insn, ArithOp.MUL, ArgType.DOUBLE);\n\n\t\t\tcase MUL_FLOAT:\n\t\t\t\treturn arith(insn, ArithOp.MUL, ArgType.FLOAT);\n\n\t\t\tcase MUL_LONG:\n\t\t\t\treturn arith(insn, ArithOp.MUL, ArgType.LONG);\n\n\t\t\tcase MUL_INT_LIT:\n\t\t\t\treturn arithLit(insn, ArithOp.MUL, ArgType.INT);\n\n\t\t\tcase DIV_INT:\n\t\t\t\treturn arith(insn, ArithOp.DIV, ArgType.INT);\n\n\t\t\tcase REM_INT:\n\t\t\t\treturn arith(insn, ArithOp.REM, ArgType.INT);\n\n\t\t\tcase REM_LONG:\n\t\t\t\treturn arith(insn, ArithOp.REM, ArgType.LONG);\n\n\t\t\tcase REM_FLOAT:\n\t\t\t\treturn arith(insn, ArithOp.REM, ArgType.FLOAT);\n\n\t\t\tcase REM_DOUBLE:\n\t\t\t\treturn arith(insn, ArithOp.REM, ArgType.DOUBLE);\n\n\t\t\tcase DIV_DOUBLE:\n\t\t\t\treturn arith(insn, ArithOp.DIV, ArgType.DOUBLE);\n\n\t\t\tcase DIV_FLOAT:\n\t\t\t\treturn arith(insn, ArithOp.DIV, ArgType.FLOAT);\n\n\t\t\tcase DIV_LONG:\n\t\t\t\treturn arith(insn, ArithOp.DIV, ArgType.LONG);\n\n\t\t\tcase DIV_INT_LIT:\n\t\t\t\treturn arithLit(insn, ArithOp.DIV, ArgType.INT);\n\n\t\t\tcase REM_INT_LIT:\n\t\t\t\treturn arithLit(insn, ArithOp.REM, ArgType.INT);\n\n\t\t\tcase AND_INT:\n\t\t\t\treturn arith(insn, ArithOp.AND, ArgType.INT);\n\n\t\t\tcase AND_INT_LIT:\n\t\t\t\treturn arithLit(insn, ArithOp.AND, ArgType.INT);\n\n\t\t\tcase XOR_INT_LIT:\n\t\t\t\treturn arithLit(insn, ArithOp.XOR, ArgType.INT);\n\n\t\t\tcase AND_LONG:\n\t\t\t\treturn arith(insn, ArithOp.AND, ArgType.LONG);\n\n\t\t\tcase OR_INT:\n\t\t\t\treturn arith(insn, ArithOp.OR, ArgType.INT);\n\n\t\t\tcase OR_INT_LIT:\n\t\t\t\treturn arithLit(insn, ArithOp.OR, ArgType.INT);\n\n\t\t\tcase XOR_INT:\n\t\t\t\treturn arith(insn, ArithOp.XOR, ArgType.INT);\n\n\t\t\tcase OR_LONG:\n\t\t\t\treturn arith(insn, ArithOp.OR, ArgType.LONG);\n\n\t\t\tcase XOR_LONG:\n\t\t\t\treturn arith(insn, ArithOp.XOR, ArgType.LONG);\n\n\t\t\tcase USHR_INT:\n\t\t\t\treturn arith(insn, ArithOp.USHR, ArgType.INT);\n\n\t\t\tcase USHR_LONG:\n\t\t\t\treturn arith(insn, ArithOp.USHR, ArgType.LONG);\n\n\t\t\tcase SHL_INT:\n\t\t\t\treturn arith(insn, ArithOp.SHL, ArgType.INT);\n\n\t\t\tcase SHL_LONG:\n\t\t\t\treturn arith(insn, ArithOp.SHL, ArgType.LONG);\n\n\t\t\tcase SHR_INT:\n\t\t\t\treturn arith(insn, ArithOp.SHR, ArgType.INT);\n\n\t\t\tcase SHR_LONG:\n\t\t\t\treturn arith(insn, ArithOp.SHR, ArgType.LONG);\n\n\t\t\tcase SHL_INT_LIT:\n\t\t\t\treturn arithLit(insn, ArithOp.SHL, ArgType.INT);\n\t\t\tcase SHR_INT_LIT:\n\t\t\t\treturn arithLit(insn, ArithOp.SHR, ArgType.INT);\n\t\t\tcase USHR_INT_LIT:\n\t\t\t\treturn arithLit(insn, ArithOp.USHR, ArgType.INT);\n\n\t\t\tcase NEG_INT:\n\t\t\t\treturn neg(insn, ArgType.INT);\n\t\t\tcase NEG_LONG:\n\t\t\t\treturn neg(insn, ArgType.LONG);\n\t\t\tcase NEG_FLOAT:\n\t\t\t\treturn neg(insn, ArgType.FLOAT);\n\t\t\tcase NEG_DOUBLE:\n\t\t\t\treturn neg(insn, ArgType.DOUBLE);\n\n\t\t\tcase NOT_INT:\n\t\t\t\treturn not(insn, ArgType.INT);\n\t\t\tcase NOT_LONG:\n\t\t\t\treturn not(insn, ArgType.LONG);\n\n\t\t\tcase INT_TO_BYTE:\n\t\t\t\treturn cast(insn, ArgType.INT, ArgType.BYTE);\n\t\t\tcase INT_TO_CHAR:\n\t\t\t\treturn cast(insn, ArgType.INT, ArgType.CHAR);\n\t\t\tcase INT_TO_SHORT:\n\t\t\t\treturn cast(insn, ArgType.INT, ArgType.SHORT);\n\t\t\tcase INT_TO_FLOAT:\n\t\t\t\treturn cast(insn, ArgType.INT, ArgType.FLOAT);\n\t\t\tcase INT_TO_DOUBLE:\n\t\t\t\treturn cast(insn, ArgType.INT, ArgType.DOUBLE);\n\t\t\tcase INT_TO_LONG:\n\t\t\t\treturn cast(insn, ArgType.INT, ArgType.LONG);\n\n\t\t\tcase FLOAT_TO_INT:\n\t\t\t\treturn cast(insn, ArgType.FLOAT, ArgType.INT);\n\t\t\tcase FLOAT_TO_DOUBLE:\n\t\t\t\treturn cast(insn, ArgType.FLOAT, ArgType.DOUBLE);\n\t\t\tcase FLOAT_TO_LONG:\n\t\t\t\treturn cast(insn, ArgType.FLOAT, ArgType.LONG);\n\n\t\t\tcase DOUBLE_TO_INT:\n\t\t\t\treturn cast(insn, ArgType.DOUBLE, ArgType.INT);\n\t\t\tcase DOUBLE_TO_FLOAT:\n\t\t\t\treturn cast(insn, ArgType.DOUBLE, ArgType.FLOAT);\n\t\t\tcase DOUBLE_TO_LONG:\n\t\t\t\treturn cast(insn, ArgType.DOUBLE, ArgType.LONG);\n\n\t\t\tcase LONG_TO_INT:\n\t\t\t\treturn cast(insn, ArgType.LONG, ArgType.INT);\n\t\t\tcase LONG_TO_FLOAT:\n\t\t\t\treturn cast(insn, ArgType.LONG, ArgType.FLOAT);\n\t\t\tcase LONG_TO_DOUBLE:\n\t\t\t\treturn cast(insn, ArgType.LONG, ArgType.DOUBLE);\n\n\t\t\tcase IF_EQ:\n\t\t\tcase IF_EQZ:\n\t\t\t\treturn new IfNode(insn, IfOp.EQ);\n\n\t\t\tcase IF_NE:\n\t\t\tcase IF_NEZ:\n\t\t\t\treturn new IfNode(insn, IfOp.NE);\n\n\t\t\tcase IF_GT:\n\t\t\tcase IF_GTZ:\n\t\t\t\treturn new IfNode(insn, IfOp.GT);\n\n\t\t\tcase IF_GE:\n\t\t\tcase IF_GEZ:\n\t\t\t\treturn new IfNode(insn, IfOp.GE);\n\n\t\t\tcase IF_LT:\n\t\t\tcase IF_LTZ:\n\t\t\t\treturn new IfNode(insn, IfOp.LT);\n\n\t\t\tcase IF_LE:\n\t\t\tcase IF_LEZ:\n\t\t\t\treturn new IfNode(insn, IfOp.LE);\n\n\t\t\tcase CMP_LONG:\n\t\t\t\treturn cmp(insn, InsnType.CMP_L, ArgType.LONG);\n\t\t\tcase CMPL_FLOAT:\n\t\t\t\treturn cmp(insn, InsnType.CMP_L, ArgType.FLOAT);\n\t\t\tcase CMPL_DOUBLE:\n\t\t\t\treturn cmp(insn, InsnType.CMP_L, ArgType.DOUBLE);\n\n\t\t\tcase CMPG_FLOAT:\n\t\t\t\treturn cmp(insn, InsnType.CMP_G, ArgType.FLOAT);\n\t\t\tcase CMPG_DOUBLE:\n\t\t\t\treturn cmp(insn, InsnType.CMP_G, ArgType.DOUBLE);\n\n\t\t\tcase GOTO:\n\t\t\t\treturn new GotoNode(insn.getTarget());\n\n\t\t\tcase JAVA_JSR:\n\t\t\t\tmethod.add(AFlag.RESOLVE_JAVA_JSR);\n\t\t\t\tJsrNode jsr = new JsrNode(insn.getTarget());\n\t\t\t\tjsr.setResult(InsnArg.reg(insn, 0, ArgType.UNKNOWN_INT));\n\t\t\t\treturn jsr;\n\n\t\t\tcase JAVA_RET:\n\t\t\t\tmethod.add(AFlag.RESOLVE_JAVA_JSR);\n\t\t\t\treturn insn(InsnType.JAVA_RET, null, InsnArg.reg(insn, 0, ArgType.UNKNOWN_INT));\n\n\t\t\tcase THROW:\n\t\t\t\treturn insn(InsnType.THROW, null, InsnArg.reg(insn, 0, ArgType.THROWABLE));\n\n\t\t\tcase MOVE_EXCEPTION:\n\t\t\t\tmethod.add(AFlag.COMPUTE_POST_DOM); // Post dominators required for try/catch block processing\n\t\t\t\treturn insn(InsnType.MOVE_EXCEPTION, InsnArg.reg(insn, 0, ArgType.UNKNOWN_OBJECT_NO_ARRAY));\n\n\t\t\tcase RETURN_VOID:\n\t\t\t\treturn new InsnNode(InsnType.RETURN, 0);\n\n\t\t\tcase RETURN:\n\t\t\t\treturn insn(InsnType.RETURN,\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\tInsnArg.reg(insn, 0, method.getReturnType()));\n\n\t\t\tcase INSTANCE_OF:\n\t\t\t\tInsnNode instInsn = new IndexInsnNode(InsnType.INSTANCE_OF, ArgType.parse(insn.getIndexAsType()), 1);\n\t\t\t\tinstInsn.setResult(InsnArg.reg(insn, 0, ArgType.BOOLEAN));\n\t\t\t\tinstInsn.addArg(InsnArg.reg(insn, 1, ArgType.UNKNOWN_OBJECT));\n\t\t\t\treturn instInsn;\n\n\t\t\tcase CHECK_CAST:\n\t\t\t\tArgType castType = ArgType.parse(insn.getIndexAsType());\n\t\t\t\tInsnNode checkCastInsn = new IndexInsnNode(InsnType.CHECK_CAST, castType, 1);\n\t\t\t\tcheckCastInsn.setResult(InsnArg.reg(insn, 0, castType));\n\t\t\t\tcheckCastInsn.addArg(InsnArg.reg(insn, insn.getRegsCount() == 2 ? 1 : 0, ArgType.UNKNOWN_OBJECT));\n\t\t\t\treturn checkCastInsn;\n\n\t\t\tcase IGET:\n\t\t\t\tFieldInfo igetFld = FieldInfo.fromRef(root, insn.getIndexAsField());\n\t\t\t\tInsnNode igetInsn = new IndexInsnNode(InsnType.IGET, igetFld, 1);\n\t\t\t\tigetInsn.setResult(InsnArg.reg(insn, 0, tryResolveFieldType(igetFld)));\n\t\t\t\tigetInsn.addArg(InsnArg.reg(insn, 1, igetFld.getDeclClass().getType()));\n\t\t\t\treturn igetInsn;\n\n\t\t\tcase IPUT:\n\t\t\t\tFieldInfo iputFld = FieldInfo.fromRef(root, insn.getIndexAsField());\n\t\t\t\tInsnNode iputInsn = new IndexInsnNode(InsnType.IPUT, iputFld, 2);\n\t\t\t\tiputInsn.addArg(InsnArg.reg(insn, 0, tryResolveFieldType(iputFld)));\n\t\t\t\tiputInsn.addArg(InsnArg.reg(insn, 1, iputFld.getDeclClass().getType()));\n\t\t\t\treturn iputInsn;\n\n\t\t\tcase SGET:\n\t\t\t\tFieldInfo sgetFld = FieldInfo.fromRef(root, insn.getIndexAsField());\n\t\t\t\tInsnNode sgetInsn = new IndexInsnNode(InsnType.SGET, sgetFld, 0);\n\t\t\t\tsgetInsn.setResult(InsnArg.reg(insn, 0, tryResolveFieldType(sgetFld)));\n\t\t\t\treturn sgetInsn;\n\n\t\t\tcase SPUT:\n\t\t\t\tFieldInfo sputFld = FieldInfo.fromRef(root, insn.getIndexAsField());\n\t\t\t\tInsnNode sputInsn = new IndexInsnNode(InsnType.SPUT, sputFld, 1);\n\t\t\t\tsputInsn.addArg(InsnArg.reg(insn, 0, tryResolveFieldType(sputFld)));\n\t\t\t\treturn sputInsn;\n\n\t\t\tcase ARRAY_LENGTH:\n\t\t\t\tInsnNode arrLenInsn = new InsnNode(InsnType.ARRAY_LENGTH, 1);\n\t\t\t\tarrLenInsn.setResult(InsnArg.reg(insn, 0, ArgType.INT));\n\t\t\t\tarrLenInsn.addArg(InsnArg.reg(insn, 1, ArgType.array(ArgType.UNKNOWN)));\n\t\t\t\treturn arrLenInsn;\n\n\t\t\tcase AGET:\n\t\t\t\treturn arrayGet(insn, ArgType.INT_FLOAT, ArgType.NARROW_NUMBERS_NO_BOOL);\n\t\t\tcase AGET_BOOLEAN:\n\t\t\t\treturn arrayGet(insn, ArgType.BOOLEAN);\n\t\t\tcase AGET_BYTE:\n\t\t\t\treturn arrayGet(insn, ArgType.BYTE, ArgType.NARROW_INTEGRAL);\n\t\t\tcase AGET_BYTE_BOOLEAN:\n\t\t\t\treturn arrayGet(insn, ArgType.BYTE_BOOLEAN);\n\t\t\tcase AGET_CHAR:\n\t\t\t\treturn arrayGet(insn, ArgType.CHAR);\n\t\t\tcase AGET_SHORT:\n\t\t\t\treturn arrayGet(insn, ArgType.SHORT);\n\t\t\tcase AGET_WIDE:\n\t\t\t\treturn arrayGet(insn, ArgType.WIDE);\n\t\t\tcase AGET_OBJECT:\n\t\t\t\treturn arrayGet(insn, ArgType.UNKNOWN_OBJECT);\n\n\t\t\tcase APUT:\n\t\t\t\treturn arrayPut(insn, ArgType.INT_FLOAT, ArgType.NARROW_NUMBERS_NO_BOOL);\n\t\t\tcase APUT_BOOLEAN:\n\t\t\t\treturn arrayPut(insn, ArgType.BOOLEAN);\n\t\t\tcase APUT_BYTE:\n\t\t\t\treturn arrayPut(insn, ArgType.BYTE);\n\t\t\tcase APUT_BYTE_BOOLEAN:\n\t\t\t\treturn arrayPut(insn, ArgType.BYTE_BOOLEAN);\n\t\t\tcase APUT_CHAR:\n\t\t\t\treturn arrayPut(insn, ArgType.CHAR);\n\t\t\tcase APUT_SHORT:\n\t\t\t\treturn arrayPut(insn, ArgType.SHORT);\n\t\t\tcase APUT_WIDE:\n\t\t\t\treturn arrayPut(insn, ArgType.WIDE);\n\t\t\tcase APUT_OBJECT:\n\t\t\t\treturn arrayPut(insn, ArgType.UNKNOWN_OBJECT);\n\n\t\t\tcase INVOKE_STATIC:\n\t\t\t\treturn invoke(insn, InvokeType.STATIC, false);\n\n\t\t\tcase INVOKE_STATIC_RANGE:\n\t\t\t\treturn invoke(insn, InvokeType.STATIC, true);\n\n\t\t\tcase INVOKE_DIRECT:\n\t\t\t\treturn invoke(insn, InvokeType.DIRECT, false);\n\t\t\tcase INVOKE_INTERFACE:\n\t\t\t\treturn invoke(insn, InvokeType.INTERFACE, false);\n\t\t\tcase INVOKE_SUPER:\n\t\t\t\treturn invoke(insn, InvokeType.SUPER, false);\n\t\t\tcase INVOKE_VIRTUAL:\n\t\t\t\treturn invoke(insn, InvokeType.VIRTUAL, false);\n\t\t\tcase INVOKE_CUSTOM:\n\t\t\t\treturn invokeCustom(insn, false);\n\t\t\tcase INVOKE_SPECIAL:\n\t\t\t\treturn invokeSpecial(insn);\n\t\t\tcase INVOKE_POLYMORPHIC:\n\t\t\t\treturn invokePolymorphic(insn, false);\n\n\t\t\tcase INVOKE_DIRECT_RANGE:\n\t\t\t\treturn invoke(insn, InvokeType.DIRECT, true);\n\t\t\tcase INVOKE_INTERFACE_RANGE:\n\t\t\t\treturn invoke(insn, InvokeType.INTERFACE, true);\n\t\t\tcase INVOKE_SUPER_RANGE:\n\t\t\t\treturn invoke(insn, InvokeType.SUPER, true);\n\t\t\tcase INVOKE_VIRTUAL_RANGE:\n\t\t\t\treturn invoke(insn, InvokeType.VIRTUAL, true);\n\t\t\tcase INVOKE_CUSTOM_RANGE:\n\t\t\t\treturn invokeCustom(insn, true);\n\t\t\tcase INVOKE_POLYMORPHIC_RANGE:\n\t\t\t\treturn invokePolymorphic(insn, true);\n\n\t\t\tcase NEW_INSTANCE:\n\t\t\t\tArgType clsType = ArgType.parse(insn.getIndexAsType());\n\t\t\t\tIndexInsnNode newInstInsn = new IndexInsnNode(InsnType.NEW_INSTANCE, clsType, 0);\n\t\t\t\tnewInstInsn.setResult(InsnArg.reg(insn, 0, clsType));\n\t\t\t\treturn newInstInsn;\n\n\t\t\tcase NEW_ARRAY:\n\t\t\t\treturn makeNewArray(insn);\n\n\t\t\tcase FILL_ARRAY_DATA:\n\t\t\t\treturn new FillArrayInsn(InsnArg.reg(insn, 0, ArgType.UNKNOWN_ARRAY), insn.getTarget());\n\t\t\tcase FILL_ARRAY_DATA_PAYLOAD:\n\t\t\t\treturn new FillArrayData((IArrayPayload) Objects.requireNonNull(insn.getPayload()));\n\n\t\t\tcase FILLED_NEW_ARRAY:\n\t\t\t\treturn filledNewArray(insn, false);\n\t\t\tcase FILLED_NEW_ARRAY_RANGE:\n\t\t\t\treturn filledNewArray(insn, true);\n\n\t\t\tcase PACKED_SWITCH:\n\t\t\t\treturn makeSwitch(insn, true);\n\t\t\tcase SPARSE_SWITCH:\n\t\t\t\treturn makeSwitch(insn, false);\n\n\t\t\tcase PACKED_SWITCH_PAYLOAD:\n\t\t\tcase SPARSE_SWITCH_PAYLOAD:\n\t\t\t\treturn new SwitchData((ISwitchPayload) insn.getPayload());\n\n\t\t\tcase MONITOR_ENTER:\n\t\t\t\treturn insn(InsnType.MONITOR_ENTER,\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\tInsnArg.reg(insn, 0, ArgType.UNKNOWN_OBJECT));\n\n\t\t\tcase MONITOR_EXIT:\n\t\t\t\treturn insn(InsnType.MONITOR_EXIT,\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\tInsnArg.reg(insn, 0, ArgType.UNKNOWN_OBJECT));\n\n\t\t\tdefault:\n\t\t\t\tthrow new DecodeException(\"Unknown instruction: '\" + insn + '\\'');\n\t\t}\n\t}\n\n\tprivate SwitchInsn makeSwitch(InsnData insn, boolean packed) {\n\t\tSwitchInsn swInsn = new SwitchInsn(InsnArg.reg(insn, 0, ArgType.NARROW_INTEGRAL), insn.getTarget(), packed);\n\t\tICustomPayload payload = insn.getPayload();\n\t\tif (payload != null) {\n\t\t\tswInsn.attachSwitchData(new SwitchData((ISwitchPayload) payload), insn.getTarget());\n\t\t}\n\t\tmethod.add(AFlag.COMPUTE_POST_DOM);\n\t\tCodeFeaturesAttr.add(method, CodeFeature.SWITCH);\n\t\treturn swInsn;\n\t}\n\n\tprivate InsnNode makeNewArray(InsnData insn) {\n\t\tArgType indexType = ArgType.parse(insn.getIndexAsType());\n\t\tint dim = (int) insn.getLiteral();\n\t\tArgType arrType;\n\t\tif (dim == 0) {\n\t\t\tarrType = indexType;\n\t\t} else {\n\t\t\tif (indexType.isArray()) {\n\t\t\t\t// java bytecode can pass array as a base type\n\t\t\t\tarrType = indexType;\n\t\t\t} else {\n\t\t\t\tarrType = ArgType.array(indexType, dim);\n\t\t\t}\n\t\t}\n\t\tint regsCount = insn.getRegsCount();\n\t\tNewArrayNode newArr = new NewArrayNode(arrType, regsCount - 1);\n\t\tnewArr.setResult(InsnArg.reg(insn, 0, arrType));\n\t\tfor (int i = 1; i < regsCount; i++) {\n\t\t\tnewArr.addArg(InsnArg.typeImmutableReg(insn, i, ArgType.INT));\n\t\t}\n\t\tCodeFeaturesAttr.add(method, CodeFeature.NEW_ARRAY);\n\t\treturn newArr;\n\t}\n\n\tprivate ArgType tryResolveFieldType(FieldInfo igetFld) {\n\t\tFieldNode fieldNode = root.resolveField(igetFld);\n\t\tif (fieldNode != null) {\n\t\t\treturn fieldNode.getType();\n\t\t}\n\t\treturn igetFld.getType();\n\t}\n\n\tprivate InsnNode filledNewArray(InsnData insn, boolean isRange) {\n\t\tArgType arrType = ArgType.parse(insn.getIndexAsType());\n\t\tArgType elType = arrType.getArrayElement();\n\t\tboolean typeImmutable = elType.isPrimitive();\n\t\tint regsCount = insn.getRegsCount();\n\t\tInsnArg[] regs = new InsnArg[regsCount];\n\t\tif (isRange) {\n\t\t\tint r = insn.getReg(0);\n\t\t\tfor (int i = 0; i < regsCount; i++) {\n\t\t\t\tregs[i] = InsnArg.reg(r, elType, typeImmutable);\n\t\t\t\tr++;\n\t\t\t}\n\t\t} else {\n\t\t\tfor (int i = 0; i < regsCount; i++) {\n\t\t\t\tint regNum = insn.getReg(i);\n\t\t\t\tregs[i] = InsnArg.reg(regNum, elType, typeImmutable);\n\t\t\t}\n\t\t}\n\t\tInsnNode node = new FilledNewArrayNode(elType, regs.length);\n\t\t// node.setResult(resReg == -1 ? null : InsnArg.reg(resReg, arrType));\n\t\tfor (InsnArg arg : regs) {\n\t\t\tnode.addArg(arg);\n\t\t}\n\t\treturn node;\n\t}\n\n\tprivate InsnNode cmp(InsnData insn, InsnType itype, ArgType argType) {\n\t\tInsnNode inode = new InsnNode(itype, 2);\n\t\tinode.setResult(InsnArg.reg(insn, 0, ArgType.INT));\n\t\tinode.addArg(InsnArg.reg(insn, 1, argType));\n\t\tinode.addArg(InsnArg.reg(insn, 2, argType));\n\t\treturn inode;\n\t}\n\n\tprivate InsnNode cast(InsnData insn, ArgType from, ArgType to) {\n\t\tInsnNode inode = new IndexInsnNode(InsnType.CAST, to, 1);\n\t\tinode.setResult(InsnArg.reg(insn, 0, to));\n\t\tinode.addArg(InsnArg.reg(insn, 1, from));\n\t\treturn inode;\n\t}\n\n\tprivate InsnNode invokeCustom(InsnData insn, boolean isRange) {\n\t\treturn InvokeCustomBuilder.build(method, insn, isRange);\n\t}\n\n\tprivate InsnNode invokePolymorphic(InsnData insn, boolean isRange) {\n\t\tIMethodRef mthRef = InsnDataUtils.getMethodRef(insn);\n\t\tif (mthRef == null) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to load method reference for insn: \" + insn);\n\t\t}\n\t\tMethodInfo callMth = MethodInfo.fromRef(root, mthRef);\n\t\tIMethodProto proto = insn.getIndexAsProto(insn.getTarget());\n\n\t\t// expand call args\n\t\tList<ArgType> args = Utils.collectionMap(proto.getArgTypes(), ArgType::parse);\n\t\tArgType returnType = ArgType.parse(proto.getReturnType());\n\t\tMethodInfo effectiveCallMth = MethodInfo.fromDetails(root, callMth.getDeclClass(),\n\t\t\t\tcallMth.getName(), args, returnType);\n\t\treturn new InvokePolymorphicNode(effectiveCallMth, insn, proto, callMth, isRange);\n\t}\n\n\tprivate InsnNode invokeSpecial(InsnData insn) {\n\t\tIMethodRef mthRef = InsnDataUtils.getMethodRef(insn);\n\t\tif (mthRef == null) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to load method reference for insn: \" + insn);\n\t\t}\n\t\tMethodInfo mthInfo = MethodInfo.fromRef(root, mthRef);\n\t\t// convert 'special' to 'direct/super' same as dx\n\t\tInvokeType type;\n\t\tif (mthInfo.isConstructor() || Objects.equals(mthInfo.getDeclClass(), method.getParentClass().getClassInfo())) {\n\t\t\ttype = InvokeType.DIRECT;\n\t\t} else {\n\t\t\ttype = InvokeType.SUPER;\n\t\t}\n\t\treturn new InvokeNode(mthInfo, insn, type, false);\n\t}\n\n\tprivate InsnNode invoke(InsnData insn, InvokeType type, boolean isRange) {\n\t\tIMethodRef mthRef = InsnDataUtils.getMethodRef(insn);\n\t\tif (mthRef == null) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to load method reference for insn: \" + insn);\n\t\t}\n\t\tMethodInfo mthInfo = MethodInfo.fromRef(root, mthRef);\n\t\treturn new InvokeNode(mthInfo, insn, type, isRange);\n\t}\n\n\tprivate InsnNode arrayGet(InsnData insn, ArgType argType) {\n\t\treturn arrayGet(insn, argType, argType);\n\t}\n\n\tprivate InsnNode arrayGet(InsnData insn, ArgType arrElemType, ArgType resType) {\n\t\tInsnNode inode = new InsnNode(InsnType.AGET, 2);\n\t\tinode.setResult(InsnArg.typeImmutableIfKnownReg(insn, 0, resType));\n\t\tinode.addArg(InsnArg.typeImmutableIfKnownReg(insn, 1, ArgType.array(arrElemType)));\n\t\tinode.addArg(InsnArg.reg(insn, 2, ArgType.NARROW_INTEGRAL));\n\t\treturn inode;\n\t}\n\n\tprivate InsnNode arrayPut(InsnData insn, ArgType argType) {\n\t\treturn arrayPut(insn, argType, argType);\n\t}\n\n\tprivate InsnNode arrayPut(InsnData insn, ArgType arrElemType, ArgType argType) {\n\t\tInsnNode inode = new InsnNode(InsnType.APUT, 3);\n\t\tinode.addArg(InsnArg.typeImmutableIfKnownReg(insn, 1, ArgType.array(arrElemType)));\n\t\tinode.addArg(InsnArg.reg(insn, 2, ArgType.NARROW_INTEGRAL));\n\t\tinode.addArg(InsnArg.typeImmutableIfKnownReg(insn, 0, argType));\n\t\treturn inode;\n\t}\n\n\tprivate InsnNode arith(InsnData insn, ArithOp op, ArgType type) {\n\t\treturn ArithNode.build(insn, op, type);\n\t}\n\n\tprivate InsnNode arithLit(InsnData insn, ArithOp op, ArgType type) {\n\t\treturn ArithNode.buildLit(insn, op, type);\n\t}\n\n\tprivate InsnNode neg(InsnData insn, ArgType type) {\n\t\tInsnNode inode = new InsnNode(InsnType.NEG, 1);\n\t\tinode.setResult(InsnArg.reg(insn, 0, type));\n\t\tinode.addArg(InsnArg.reg(insn, 1, type));\n\t\treturn inode;\n\t}\n\n\tprivate InsnNode not(InsnData insn, ArgType type) {\n\t\tInsnNode inode = new InsnNode(InsnType.NOT, 1);\n\t\tinode.setResult(InsnArg.reg(insn, 0, type));\n\t\tinode.addArg(InsnArg.reg(insn, 1, type));\n\t\treturn inode;\n\t}\n\n\tprivate InsnNode insn(InsnType type, RegisterArg res) {\n\t\tInsnNode node = new InsnNode(type, 0);\n\t\tnode.setResult(res);\n\t\treturn node;\n\t}\n\n\tprivate InsnNode insn(InsnType type, RegisterArg res, InsnArg arg) {\n\t\tInsnNode node = new InsnNode(type, 1);\n\t\tnode.setResult(res);\n\t\tnode.addArg(arg);\n\t\treturn node;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/InsnType.java",
    "content": "package jadx.core.dex.instructions;\n\npublic enum InsnType {\n\n\tCONST,\n\tCONST_STR,\n\tCONST_CLASS,\n\n\tARITH,\n\tNEG,\n\tNOT,\n\n\tMOVE,\n\tMOVE_MULTI,\n\tCAST,\n\n\tRETURN,\n\tGOTO,\n\n\tTHROW,\n\tMOVE_EXCEPTION,\n\n\tCMP_L,\n\tCMP_G,\n\tIF,\n\tSWITCH,\n\tSWITCH_DATA,\n\n\tMONITOR_ENTER,\n\tMONITOR_EXIT,\n\n\tCHECK_CAST,\n\tINSTANCE_OF,\n\n\tARRAY_LENGTH,\n\tFILL_ARRAY,\n\tFILL_ARRAY_DATA,\n\tFILLED_NEW_ARRAY,\n\n\tAGET,\n\tAPUT,\n\n\tNEW_ARRAY,\n\tNEW_INSTANCE,\n\n\tIGET,\n\tIPUT,\n\n\tSGET,\n\tSPUT,\n\n\tINVOKE,\n\tMOVE_RESULT,\n\n\t// *** Additional instructions ***\n\n\t// replacement for removed instructions\n\tNOP,\n\n\tTERNARY,\n\tCONSTRUCTOR,\n\n\tBREAK,\n\tCONTINUE,\n\n\t// strings concatenation\n\tSTR_CONCAT,\n\n\t// just generate one argument\n\tONE_ARG,\n\tPHI,\n\n\t// fake insn to keep arguments which will be used in regions codegen\n\tREGION_ARG,\n\n\t// Java specific dynamic jump instructions\n\tJAVA_JSR,\n\tJAVA_RET,\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomBuilder.java",
    "content": "package jadx.core.dex.instructions;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.ICallSite;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.JadxError;\nimport jadx.core.dex.instructions.invokedynamic.CustomLambdaCall;\nimport jadx.core.dex.instructions.invokedynamic.CustomRawCall;\nimport jadx.core.dex.instructions.invokedynamic.CustomStringConcat;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.input.InsnDataUtils;\n\npublic class InvokeCustomBuilder {\n\n\tpublic static InsnNode build(MethodNode mth, InsnData insn, boolean isRange) {\n\t\ttry {\n\t\t\tICallSite callSite = InsnDataUtils.getCallSite(insn);\n\t\t\tif (callSite == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Failed to get call site for insn: \" + insn);\n\t\t\t}\n\t\t\tcallSite.load();\n\t\t\tList<EncodedValue> values = callSite.getValues();\n\t\t\tif (CustomLambdaCall.isLambdaInvoke(values)) {\n\t\t\t\treturn CustomLambdaCall.buildLambdaMethodCall(mth, insn, isRange, values);\n\t\t\t}\n\t\t\tif (CustomStringConcat.isStringConcat(values)) {\n\t\t\t\treturn CustomStringConcat.buildStringConcat(insn, isRange, values);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn CustomRawCall.build(mth, insn, isRange, values);\n\t\t\t} catch (Exception e) {\n\t\t\t\tmth.addWarn(\"Failed to decode invoke-custom: \\n\" + Utils.listToString(values, \"\\n\")\n\t\t\t\t\t\t+ \",\\n exception: \" + Utils.getStackTrace(e));\n\t\t\t\tInsnNode nop = new InsnNode(InsnType.NOP, 0);\n\t\t\t\tnop.add(AFlag.SYNTHETIC);\n\t\t\t\tnop.addAttr(AType.JADX_ERROR, new JadxError(\"Failed to decode invoke-custom: \" + values, e));\n\t\t\t\treturn nop;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"'invoke-custom' instruction processing error: \" + e.getMessage(), e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomNode.java",
    "content": "package jadx.core.dex.instructions;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.MethodHandleType;\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.InsnUtils;\n\npublic class InvokeCustomNode extends InvokeNode {\n\tprivate MethodInfo implMthInfo;\n\tprivate MethodHandleType handleType;\n\tprivate InsnNode callInsn;\n\tprivate boolean inlineInsn;\n\tprivate boolean useRef;\n\n\tpublic InvokeCustomNode(MethodInfo lambdaInfo, InsnData insn, boolean instanceCall, boolean isRange) {\n\t\tsuper(lambdaInfo, insn, InvokeType.CUSTOM, instanceCall, isRange);\n\t}\n\n\tprivate InvokeCustomNode(MethodInfo mth, InvokeType invokeType, int argsCount) {\n\t\tsuper(mth, invokeType, argsCount);\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\tInvokeCustomNode copy = new InvokeCustomNode(getCallMth(), getInvokeType(), getArgsCount());\n\t\tcopyCommonParams(copy);\n\t\tcopy.setImplMthInfo(implMthInfo);\n\t\tcopy.setHandleType(handleType);\n\t\tcopy.setCallInsn(callInsn);\n\t\tcopy.setInlineInsn(inlineInsn);\n\t\tcopy.setUseRef(useRef);\n\t\treturn copy;\n\t}\n\n\t@Override\n\tpublic boolean isSame(InsnNode obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof InvokeCustomNode) || !super.isSame(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tInvokeCustomNode other = (InvokeCustomNode) obj;\n\t\treturn handleType == other.handleType\n\t\t\t\t&& implMthInfo.equals(other.implMthInfo)\n\t\t\t\t&& callInsn.isSame(other.callInsn)\n\t\t\t\t&& inlineInsn == other.inlineInsn\n\t\t\t\t&& useRef == other.useRef;\n\t}\n\n\tpublic MethodInfo getImplMthInfo() {\n\t\treturn implMthInfo;\n\t}\n\n\tpublic void setImplMthInfo(MethodInfo implMthInfo) {\n\t\tthis.implMthInfo = implMthInfo;\n\t}\n\n\tpublic MethodHandleType getHandleType() {\n\t\treturn handleType;\n\t}\n\n\tpublic void setHandleType(MethodHandleType handleType) {\n\t\tthis.handleType = handleType;\n\t}\n\n\tpublic InsnNode getCallInsn() {\n\t\treturn callInsn;\n\t}\n\n\tpublic void setCallInsn(InsnNode callInsn) {\n\t\tthis.callInsn = callInsn;\n\t}\n\n\tpublic boolean isInlineInsn() {\n\t\treturn inlineInsn;\n\t}\n\n\tpublic void setInlineInsn(boolean inlineInsn) {\n\t\tthis.inlineInsn = inlineInsn;\n\t}\n\n\tpublic boolean isUseRef() {\n\t\treturn useRef;\n\t}\n\n\tpublic void setUseRef(boolean useRef) {\n\t\tthis.useRef = useRef;\n\t}\n\n\t@Nullable\n\tpublic BaseInvokeNode getInvokeCall() {\n\t\tif (callInsn.getType() == InsnType.INVOKE) {\n\t\t\treturn (BaseInvokeNode) callInsn;\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic @Nullable InsnArg getInstanceArg() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isStaticCall() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int getFirstArgOffset() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(InsnUtils.formatOffset(offset)).append(\": INVOKE_CUSTOM \");\n\t\tif (getResult() != null) {\n\t\t\tsb.append(getResult()).append(\" = \");\n\t\t}\n\t\tappendArgs(sb);\n\t\tappendAttributes(sb);\n\t\tsb.append(\"\\n handle type: \").append(handleType);\n\t\tsb.append(\"\\n lambda: \").append(implMthInfo);\n\t\tsb.append(\"\\n call insn: \").append(callInsn);\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomRawNode.java",
    "content": "package jadx.core.dex.instructions;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.invokedynamic.CustomRawCall;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.Utils;\n\n/**\n * Information for raw invoke-custom instruction.<br>\n * Output will be formatted as polymorphic call with equivalent semantic\n * Contains two parts:\n * - resolve: treated as additional invoke insn (uses only constant args)\n * - invoke: call of resolved method (base for this invoke)\n * <br>\n * See {@link CustomRawCall} class for build details\n */\npublic class InvokeCustomRawNode extends InvokeNode {\n\tprivate final InvokeNode resolve;\n\tprivate List<EncodedValue> callSiteValues;\n\n\tpublic InvokeCustomRawNode(InvokeNode resolve, MethodInfo mthInfo, InsnData insn, boolean isRange) {\n\t\tsuper(mthInfo, insn, InvokeType.CUSTOM_RAW, false, isRange);\n\t\tthis.resolve = resolve;\n\t}\n\n\tpublic InvokeCustomRawNode(InvokeNode resolve, MethodInfo mthInfo, InvokeType invokeType, int argsCount) {\n\t\tsuper(mthInfo, invokeType, argsCount);\n\t\tthis.resolve = resolve;\n\t}\n\n\tpublic InvokeNode getResolveInvoke() {\n\t\treturn resolve;\n\t}\n\n\tpublic void setCallSiteValues(List<EncodedValue> callSiteValues) {\n\t\tthis.callSiteValues = callSiteValues;\n\t}\n\n\tpublic List<EncodedValue> getCallSiteValues() {\n\t\treturn callSiteValues;\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\tInvokeCustomRawNode copy = new InvokeCustomRawNode(resolve, getCallMth(), getInvokeType(), getArgsCount());\n\t\tcopyCommonParams(copy);\n\t\tcopy.setCallSiteValues(callSiteValues);\n\t\treturn copy;\n\t}\n\n\t@Override\n\tpublic boolean isStaticCall() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int getFirstArgOffset() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic @Nullable InsnArg getInstanceArg() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isSame(InsnNode obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj instanceof InvokeCustomRawNode) {\n\t\t\treturn super.isSame(obj) && resolve.isSame(((InvokeCustomRawNode) obj).resolve);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(InsnUtils.formatOffset(offset)).append(\": INVOKE_CUSTOM \");\n\t\tif (getResult() != null) {\n\t\t\tsb.append(getResult()).append(\" = \");\n\t\t}\n\t\tif (!appendArgs(sb)) {\n\t\t\tsb.append('\\n');\n\t\t}\n\t\tappendAttributes(sb);\n\t\tsb.append(\" call-site: \\n  \").append(Utils.listToString(callSiteValues, \"\\n  \")).append('\\n');\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java",
    "content": "package jadx.core.dex.instructions;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic class InvokeNode extends BaseInvokeNode {\n\n\tprivate final InvokeType type;\n\tprivate final MethodInfo mth;\n\n\tpublic InvokeNode(MethodInfo mthInfo, InsnData insn, InvokeType invokeType, boolean isRange) {\n\t\tthis(mthInfo, insn, invokeType, invokeType != InvokeType.STATIC, isRange);\n\t}\n\n\tpublic InvokeNode(MethodInfo mth, InsnData insn, InvokeType type, boolean instanceCall, boolean isRange) {\n\t\tsuper(InsnType.INVOKE, mth.getArgsCount() + (instanceCall ? 1 : 0));\n\t\tthis.mth = mth;\n\t\tthis.type = type;\n\n\t\tint k = isRange ? insn.getReg(0) : 0;\n\t\tif (instanceCall) {\n\t\t\tint r = isRange ? k : insn.getReg(k);\n\t\t\taddReg(r, mth.getDeclClass().getType());\n\t\t\tk++;\n\t\t}\n\t\tfor (ArgType arg : mth.getArgumentsTypes()) {\n\t\t\taddReg(isRange ? k : insn.getReg(k), arg);\n\t\t\tk += arg.getRegCount();\n\t\t}\n\t\tint resReg = insn.getResultReg();\n\t\tif (resReg != -1) {\n\t\t\tsetResult(InsnArg.reg(resReg, mth.getReturnType()));\n\t\t}\n\t}\n\n\tpublic InvokeNode(MethodInfo mth, InvokeType invokeType, int argsCount) {\n\t\tsuper(InsnType.INVOKE, argsCount);\n\t\tthis.mth = mth;\n\t\tthis.type = invokeType;\n\t}\n\n\tpublic InvokeType getInvokeType() {\n\t\treturn type;\n\t}\n\n\t@Override\n\tpublic MethodInfo getCallMth() {\n\t\treturn mth;\n\t}\n\n\t@Override\n\t@Nullable\n\tpublic InsnArg getInstanceArg() {\n\t\tif (type != InvokeType.STATIC && getArgsCount() > 0) {\n\t\t\treturn getArg(0);\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isStaticCall() {\n\t\treturn type == InvokeType.STATIC;\n\t}\n\n\tpublic boolean isPolymorphicCall() {\n\t\tif (type == InvokeType.POLYMORPHIC) {\n\t\t\treturn true;\n\t\t}\n\t\t// java bytecode uses virtual call with modified method info\n\t\tif (type == InvokeType.VIRTUAL\n\t\t\t\t&& mth.getDeclClass().getFullName().equals(\"java.lang.invoke.MethodHandle\")\n\t\t\t\t&& (mth.getName().equals(\"invoke\") || mth.getName().equals(\"invokeExact\"))) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic int getFirstArgOffset() {\n\t\treturn type == InvokeType.STATIC ? 0 : 1;\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\treturn copyCommonParams(new InvokeNode(mth, type, getArgsCount()));\n\t}\n\n\t@Override\n\tpublic boolean isSame(InsnNode obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof InvokeNode) || !super.isSame(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tInvokeNode other = (InvokeNode) obj;\n\t\treturn type == other.type && mth.equals(other.mth);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn baseString() + \" \" + type + \" call: \" + mth + attributesString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/InvokePolymorphicNode.java",
    "content": "package jadx.core.dex.instructions;\n\nimport jadx.api.plugins.input.data.IMethodProto;\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.InsnUtils;\n\npublic class InvokePolymorphicNode extends InvokeNode {\n\tprivate final IMethodProto proto;\n\tprivate final MethodInfo baseCallRef;\n\n\tpublic InvokePolymorphicNode(MethodInfo callMth, InsnData insn, IMethodProto proto, MethodInfo baseRef, boolean isRange) {\n\t\tsuper(callMth, insn, InvokeType.POLYMORPHIC, true, isRange);\n\t\tthis.proto = proto;\n\t\tthis.baseCallRef = baseRef;\n\t}\n\n\tpublic InvokePolymorphicNode(MethodInfo callMth, int argsCount, IMethodProto proto, MethodInfo baseRef) {\n\t\tsuper(callMth, InvokeType.POLYMORPHIC, argsCount);\n\t\tthis.proto = proto;\n\t\tthis.baseCallRef = baseRef;\n\t}\n\n\tpublic IMethodProto getProto() {\n\t\treturn proto;\n\t}\n\n\tpublic MethodInfo getBaseCallRef() {\n\t\treturn baseCallRef;\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\tInvokePolymorphicNode copy = new InvokePolymorphicNode(getCallMth(), getArgsCount(), proto, baseCallRef);\n\t\tcopyCommonParams(copy);\n\t\treturn copy;\n\t}\n\n\t@Override\n\tpublic boolean isSame(InsnNode obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof InvokePolymorphicNode) || !super.isSame(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tInvokePolymorphicNode other = (InvokePolymorphicNode) obj;\n\t\treturn proto.equals(other.proto);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(InsnUtils.formatOffset(offset)).append(\": INVOKE_POLYMORPHIC \");\n\t\tif (getResult() != null) {\n\t\t\tsb.append(getResult()).append(\" = \");\n\t\t}\n\t\tif (!appendArgs(sb)) {\n\t\t\tsb.append('\\n');\n\t\t}\n\t\tappendAttributes(sb);\n\t\tsb.append(\" base: \").append(baseCallRef).append('\\n');\n\t\tsb.append(\" proto: \").append(proto).append('\\n');\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/InvokeType.java",
    "content": "package jadx.core.dex.instructions;\n\npublic enum InvokeType {\n\tSTATIC,\n\tDIRECT,\n\tVIRTUAL,\n\tINTERFACE,\n\tSUPER,\n\tPOLYMORPHIC,\n\tCUSTOM,\n\tCUSTOM_RAW,\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/NewArrayNode.java",
    "content": "package jadx.core.dex.instructions;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic class NewArrayNode extends InsnNode {\n\n\tprivate final ArgType arrType;\n\n\tpublic NewArrayNode(ArgType arrType, int argsCount) {\n\t\tsuper(InsnType.NEW_ARRAY, argsCount);\n\t\tthis.arrType = arrType;\n\t}\n\n\tpublic ArgType getArrayType() {\n\t\treturn arrType;\n\t}\n\n\tpublic int getDimension() {\n\t\treturn arrType.getArrayDimension();\n\t}\n\n\t@Override\n\tpublic boolean isSame(InsnNode obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof NewArrayNode) || !super.isSame(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tNewArrayNode other = (NewArrayNode) obj;\n\t\treturn arrType == other.arrType;\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\treturn copyCommonParams(new NewArrayNode(arrType, getArgsCount()));\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn super.toString() + \" type: \" + arrType;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/PhiInsn.java",
    "content": "package jadx.core.dex.instructions;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic final class PhiInsn extends InsnNode {\n\n\t// map arguments to blocks (in same order as in arguments list)\n\tprivate final List<BlockNode> blockBinds;\n\n\tpublic PhiInsn(int regNum, int predecessors) {\n\t\tthis(predecessors);\n\t\tsetResult(InsnArg.reg(regNum, ArgType.UNKNOWN));\n\t\tadd(AFlag.DONT_INLINE);\n\t\tadd(AFlag.DONT_GENERATE);\n\t}\n\n\tprivate PhiInsn(int argsCount) {\n\t\tsuper(InsnType.PHI, argsCount);\n\t\tthis.blockBinds = new ArrayList<>(argsCount);\n\t}\n\n\tpublic RegisterArg bindArg(BlockNode pred) {\n\t\tRegisterArg arg = InsnArg.reg(getResult().getRegNum(), getResult().getInitType());\n\t\tbindArg(arg, pred);\n\t\treturn arg;\n\t}\n\n\tpublic void bindArg(RegisterArg arg, BlockNode pred) {\n\t\tif (blockBinds.contains(pred)) {\n\t\t\tthrow new JadxRuntimeException(\"Duplicate predecessors in PHI insn: \" + pred + \", \" + this);\n\t\t}\n\t\tif (pred == null) {\n\t\t\tthrow new JadxRuntimeException(\"Null bind block in PHI insn: \" + this);\n\t\t}\n\t\tsuper.addArg(arg);\n\t\tblockBinds.add(pred);\n\t}\n\n\t@Nullable\n\tpublic BlockNode getBlockByArg(RegisterArg arg) {\n\t\tint index = getArgIndex(arg);\n\t\tif (index == -1) {\n\t\t\treturn null;\n\t\t}\n\t\treturn blockBinds.get(index);\n\t}\n\n\tpublic BlockNode getBlockByArgIndex(int argIndex) {\n\t\treturn blockBinds.get(argIndex);\n\t}\n\n\t@Override\n\t@NotNull\n\tpublic RegisterArg getArg(int n) {\n\t\treturn (RegisterArg) super.getArg(n);\n\t}\n\n\tpublic @Nullable RegisterArg getArgByBlock(BlockNode block) {\n\t\tfor (int i = 0; i < blockBinds.size(); i++) {\n\t\t\tif (blockBinds.get(i) == block) {\n\t\t\t\treturn getArg(i);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean removeArg(InsnArg arg) {\n\t\tint index = getArgIndex(arg);\n\t\tif (index == -1) {\n\t\t\treturn false;\n\t\t}\n\t\tremoveArg(index);\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic RegisterArg removeArg(int index) {\n\t\tRegisterArg reg = (RegisterArg) super.removeArg(index);\n\t\tblockBinds.remove(index);\n\t\treg.getSVar().updateUsedInPhiList();\n\t\treturn reg;\n\t}\n\n\t@Nullable\n\tpublic RegisterArg getArgBySsaVar(SSAVar ssaVar) {\n\t\tif (getArgsCount() == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tfor (InsnArg insnArg : getArguments()) {\n\t\t\tRegisterArg reg = (RegisterArg) insnArg;\n\t\t\tif (reg.getSVar() == ssaVar) {\n\t\t\t\treturn reg;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Nullable\n\tpublic RegisterArg getArgByBlock(IBlock block) {\n\t\tif (getArgsCount() == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tint index = blockBinds.indexOf(block);\n\t\tif (index == -1) {\n\t\t\treturn null;\n\t\t}\n\t\treturn getArg(index);\n\t}\n\n\t@Override\n\tpublic boolean replaceArg(InsnArg from, InsnArg to) {\n\t\tif (!(from instanceof RegisterArg) || !(to instanceof RegisterArg)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tint argIndex = getArgIndex(from);\n\t\tif (argIndex == -1) {\n\t\t\treturn false;\n\t\t}\n\t\t((RegisterArg) to).getSVar().addUsedInPhi(this);\n\t\tsuper.setArg(argIndex, to);\n\n\t\tInsnRemover.unbindArgUsage(null, from);\n\t\t((RegisterArg) from).getSVar().updateUsedInPhiList();\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void addArg(InsnArg arg) {\n\t\tthrow new JadxRuntimeException(\"Direct addArg is forbidden for PHI insn, bindArg must be used\");\n\t}\n\n\t@Override\n\tpublic void setArg(int n, InsnArg arg) {\n\t\tthrow new JadxRuntimeException(\"Direct setArg is forbidden for PHI insn, bindArg must be used\");\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\treturn copyCommonParams(new PhiInsn(getArgsCount()));\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn baseString() + \" binds: \" + blockBinds + attributesString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/SwitchData.java",
    "content": "package jadx.core.dex.instructions;\n\nimport jadx.api.plugins.input.insns.custom.ISwitchPayload;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.InsnUtils;\n\npublic class SwitchData extends InsnNode {\n\tprivate final int size;\n\tprivate final int[] keys;\n\tprivate final int[] targets;\n\n\tpublic SwitchData(ISwitchPayload payload) {\n\t\tsuper(InsnType.SWITCH_DATA, 0);\n\t\tthis.size = payload.getSize();\n\t\tthis.keys = payload.getKeys();\n\t\tthis.targets = payload.getTargets();\n\t}\n\n\tpublic void fixTargets(int switchOffset) {\n\t\tint size = this.size;\n\t\tint[] targets = this.targets;\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\ttargets[i] += switchOffset;\n\t\t}\n\t}\n\n\tpublic int getSize() {\n\t\treturn size;\n\t}\n\n\tpublic int[] getKeys() {\n\t\treturn keys;\n\t}\n\n\tpublic int[] getTargets() {\n\t\treturn targets;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"switch-data {\");\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tsb.append(keys[i]).append(\"->\").append(InsnUtils.formatOffset(targets[i])).append(\", \");\n\t\t}\n\t\tsb.append('}');\n\t\tappendAttributes(sb);\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/SwitchInsn.java",
    "content": "package jadx.core.dex.instructions;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.utils.BlockUtils.getBlockByOffset;\n\npublic class SwitchInsn extends TargetInsnNode {\n\tprivate final int dataTarget;\n\tprivate final boolean packed; // type of switch insn, if true can contain filler keys\n\t@Nullable\n\tprivate SwitchData switchData;\n\n\tprivate int def; // next instruction\n\n\tprivate Object[] modifiedKeys;\n\tprivate BlockNode[] targetBlocks;\n\tprivate BlockNode defTargetBlock;\n\n\tpublic SwitchInsn(InsnArg arg, int dataTarget, boolean packed) {\n\t\tsuper(InsnType.SWITCH, 1);\n\t\taddArg(arg);\n\t\tthis.dataTarget = dataTarget;\n\t\tthis.packed = packed;\n\t}\n\n\tpublic boolean needData() {\n\t\treturn this.switchData == null;\n\t}\n\n\tpublic void attachSwitchData(SwitchData data, int def) {\n\t\tthis.switchData = data;\n\t\tthis.def = def;\n\t}\n\n\t@Override\n\tpublic void initBlocks(BlockNode curBlock) {\n\t\tif (switchData == null) {\n\t\t\tthrow new JadxRuntimeException(\"Switch data not yet attached\");\n\t\t}\n\t\tList<BlockNode> successors = curBlock.getSuccessors();\n\t\tint[] targets = switchData.getTargets();\n\t\tint len = targets.length;\n\t\ttargetBlocks = new BlockNode[len];\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\ttargetBlocks[i] = getBlockByOffset(targets[i], successors);\n\t\t}\n\t\tdefTargetBlock = getBlockByOffset(def, successors);\n\t}\n\n\t@Override\n\tpublic boolean replaceTargetBlock(BlockNode origin, BlockNode replace) {\n\t\tif (targetBlocks == null) {\n\t\t\treturn false;\n\t\t}\n\t\tint count = 0;\n\t\tint len = targetBlocks.length;\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tif (targetBlocks[i] == origin) {\n\t\t\t\ttargetBlocks[i] = replace;\n\t\t\t\tcount++;\n\t\t\t}\n\t\t}\n\t\tif (defTargetBlock == origin) {\n\t\t\tdefTargetBlock = replace;\n\t\t\tcount++;\n\t\t}\n\t\treturn count > 0;\n\t}\n\n\t@Override\n\tpublic boolean isSame(InsnNode obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof SwitchInsn) || !super.isSame(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tSwitchInsn other = (SwitchInsn) obj;\n\t\treturn dataTarget == other.dataTarget\n\t\t\t\t&& packed == other.packed;\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\tSwitchInsn copy = new SwitchInsn(getArg(0), dataTarget, packed);\n\t\tcopy.switchData = switchData;\n\t\tcopy.def = def;\n\t\tcopy.targetBlocks = targetBlocks;\n\t\tcopy.defTargetBlock = defTargetBlock;\n\t\treturn copyCommonParams(copy);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(baseString());\n\t\tif (switchData == null) {\n\t\t\tsb.append(\"no payload\");\n\t\t} else {\n\t\t\tint size = switchData.getSize();\n\t\t\tint[] keys = switchData.getKeys();\n\t\t\tif (targetBlocks != null) {\n\t\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\t\tsb.append('\\n');\n\t\t\t\t\tsb.append(\" case \").append(keys[i]).append(\": goto \").append(targetBlocks[i]);\n\t\t\t\t}\n\t\t\t\tif (def != -1) {\n\t\t\t\t\tsb.append('\\n').append(\" default: goto \").append(defTargetBlock);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tint[] targets = switchData.getTargets();\n\t\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\t\tsb.append('\\n');\n\t\t\t\t\tsb.append(\" case \").append(keys[i]).append(\": goto \").append(InsnUtils.formatOffset(targets[i]));\n\t\t\t\t}\n\t\t\t\tif (def != -1) {\n\t\t\t\t\tsb.append('\\n');\n\t\t\t\t\tsb.append(\" default: goto \").append(InsnUtils.formatOffset(def));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tappendAttributes(sb);\n\t\treturn sb.toString();\n\t}\n\n\tpublic int getDataTarget() {\n\t\treturn dataTarget;\n\t}\n\n\tpublic boolean isPacked() {\n\t\treturn packed;\n\t}\n\n\tpublic int getDefaultCaseOffset() {\n\t\treturn def;\n\t}\n\n\t@NotNull\n\tprivate SwitchData getSwitchData() {\n\t\tif (switchData == null) {\n\t\t\tthrow new JadxRuntimeException(\"Switch data not yet attached\");\n\t\t}\n\t\treturn switchData;\n\t}\n\n\tpublic int[] getTargets() {\n\t\treturn getSwitchData().getTargets();\n\t}\n\n\tpublic int[] getKeys() {\n\t\treturn getSwitchData().getKeys();\n\t}\n\n\tpublic Object getKey(int i) {\n\t\tif (modifiedKeys != null) {\n\t\t\treturn modifiedKeys[i];\n\t\t}\n\t\treturn getSwitchData().getKeys()[i];\n\t}\n\n\tpublic void modifyKey(int i, Object newKey) {\n\t\tif (modifiedKeys == null) {\n\t\t\tint[] keys = getKeys();\n\t\t\tint caseCount = keys.length;\n\t\t\tObject[] newKeys = new Object[caseCount];\n\t\t\tfor (int j = 0; j < caseCount; j++) {\n\t\t\t\tnewKeys[j] = keys[j];\n\t\t\t}\n\t\t\tmodifiedKeys = newKeys;\n\t\t}\n\t\tmodifiedKeys[i] = newKey;\n\t}\n\n\tpublic BlockNode[] getTargetBlocks() {\n\t\treturn targetBlocks;\n\t}\n\n\tpublic BlockNode getDefTargetBlock() {\n\t\treturn defTargetBlock;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/TargetInsnNode.java",
    "content": "package jadx.core.dex.instructions;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic abstract class TargetInsnNode extends InsnNode {\n\n\tpublic TargetInsnNode(InsnType type, int argsCount) {\n\t\tsuper(type, argsCount);\n\t}\n\n\tpublic void initBlocks(BlockNode curBlock) {\n\t}\n\n\tpublic boolean replaceTargetBlock(BlockNode origin, BlockNode replace) {\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java",
    "content": "package jadx.core.dex.instructions.args;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Function;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.jetbrains.annotations.TestOnly;\n\nimport jadx.core.Consts;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.typeinference.TypeCompareEnum;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic abstract class ArgType {\n\tpublic static final ArgType INT = primitive(PrimitiveType.INT);\n\tpublic static final ArgType BOOLEAN = primitive(PrimitiveType.BOOLEAN);\n\tpublic static final ArgType BYTE = primitive(PrimitiveType.BYTE);\n\tpublic static final ArgType SHORT = primitive(PrimitiveType.SHORT);\n\tpublic static final ArgType CHAR = primitive(PrimitiveType.CHAR);\n\tpublic static final ArgType FLOAT = primitive(PrimitiveType.FLOAT);\n\tpublic static final ArgType DOUBLE = primitive(PrimitiveType.DOUBLE);\n\tpublic static final ArgType LONG = primitive(PrimitiveType.LONG);\n\tpublic static final ArgType VOID = primitive(PrimitiveType.VOID);\n\n\tpublic static final ArgType OBJECT = objectNoCache(Consts.CLASS_OBJECT);\n\tpublic static final ArgType CLASS = objectNoCache(Consts.CLASS_CLASS);\n\tpublic static final ArgType STRING = objectNoCache(Consts.CLASS_STRING);\n\tpublic static final ArgType ENUM = objectNoCache(Consts.CLASS_ENUM);\n\tpublic static final ArgType THROWABLE = objectNoCache(Consts.CLASS_THROWABLE);\n\tpublic static final ArgType ERROR = objectNoCache(Consts.CLASS_ERROR);\n\tpublic static final ArgType EXCEPTION = objectNoCache(Consts.CLASS_EXCEPTION);\n\tpublic static final ArgType RUNTIME_EXCEPTION = objectNoCache(Consts.CLASS_RUNTIME_EXCEPTION);\n\tpublic static final ArgType OBJECT_ARRAY = array(OBJECT);\n\tpublic static final ArgType WILDCARD = wildcard();\n\n\tpublic static final ArgType UNKNOWN = unknown(PrimitiveType.values());\n\tpublic static final ArgType UNKNOWN_OBJECT = unknown(PrimitiveType.OBJECT, PrimitiveType.ARRAY);\n\tpublic static final ArgType UNKNOWN_OBJECT_NO_ARRAY = unknown(PrimitiveType.OBJECT);\n\tpublic static final ArgType UNKNOWN_ARRAY = array(UNKNOWN);\n\n\tpublic static final ArgType NARROW = unknown(\n\t\t\tPrimitiveType.INT, PrimitiveType.FLOAT,\n\t\t\tPrimitiveType.BOOLEAN, PrimitiveType.SHORT, PrimitiveType.BYTE, PrimitiveType.CHAR,\n\t\t\tPrimitiveType.OBJECT, PrimitiveType.ARRAY);\n\n\tpublic static final ArgType NARROW_NUMBERS = unknown(\n\t\t\tPrimitiveType.BOOLEAN, PrimitiveType.INT, PrimitiveType.FLOAT,\n\t\t\tPrimitiveType.SHORT, PrimitiveType.BYTE, PrimitiveType.CHAR);\n\n\tpublic static final ArgType NARROW_INTEGRAL = unknown(\n\t\t\tPrimitiveType.INT, PrimitiveType.SHORT, PrimitiveType.BYTE, PrimitiveType.CHAR);\n\n\tpublic static final ArgType NARROW_NUMBERS_NO_BOOL = unknown(\n\t\t\tPrimitiveType.INT, PrimitiveType.FLOAT,\n\t\t\tPrimitiveType.SHORT, PrimitiveType.BYTE, PrimitiveType.CHAR);\n\n\tpublic static final ArgType NARROW_NEG_NUMBERS = unknown(\n\t\t\tPrimitiveType.INT, PrimitiveType.SHORT, PrimitiveType.BYTE, PrimitiveType.FLOAT);\n\n\tpublic static final ArgType NARROW_NUMBERS_NO_FLOAT = unknown(\n\t\t\tPrimitiveType.INT, PrimitiveType.BOOLEAN,\n\t\t\tPrimitiveType.SHORT, PrimitiveType.BYTE, PrimitiveType.CHAR);\n\n\tpublic static final ArgType WIDE = unknown(PrimitiveType.LONG, PrimitiveType.DOUBLE);\n\n\tpublic static final ArgType INT_FLOAT = unknown(PrimitiveType.INT, PrimitiveType.FLOAT);\n\tpublic static final ArgType INT_BOOLEAN = unknown(PrimitiveType.INT, PrimitiveType.BOOLEAN);\n\tpublic static final ArgType BYTE_BOOLEAN = unknown(PrimitiveType.BYTE, PrimitiveType.BOOLEAN);\n\n\tpublic static final ArgType UNKNOWN_INT = unknown(PrimitiveType.INT);\n\n\tprotected int hash;\n\n\tprivate static ArgType primitive(PrimitiveType stype) {\n\t\treturn new PrimitiveArg(stype);\n\t}\n\n\tprivate static ArgType objectNoCache(String obj) {\n\t\treturn new ObjectType(obj);\n\t}\n\n\tpublic static ArgType object(String obj) {\n\t\t// TODO: add caching\n\t\tString cleanObjectName = Utils.cleanObjectName(obj);\n\t\tswitch (cleanObjectName) {\n\t\t\tcase Consts.CLASS_OBJECT:\n\t\t\t\treturn OBJECT;\n\t\t\tcase Consts.CLASS_STRING:\n\t\t\t\treturn STRING;\n\t\t\tcase Consts.CLASS_CLASS:\n\t\t\t\treturn CLASS;\n\t\t\tcase Consts.CLASS_THROWABLE:\n\t\t\t\treturn THROWABLE;\n\t\t\tcase Consts.CLASS_EXCEPTION:\n\t\t\t\treturn EXCEPTION;\n\t\t\tdefault:\n\t\t\t\treturn new ObjectType(cleanObjectName);\n\t\t}\n\t}\n\n\tpublic static ArgType genericType(String type) {\n\t\treturn new GenericType(type);\n\t}\n\n\tpublic static ArgType genericType(String type, ArgType extendType) {\n\t\treturn new GenericType(type, extendType);\n\t}\n\n\tpublic static ArgType genericType(String type, List<ArgType> extendTypes) {\n\t\treturn new GenericType(type, extendTypes);\n\t}\n\n\tpublic static ArgType wildcard() {\n\t\treturn new WildcardType(OBJECT, WildcardBound.UNBOUND);\n\t}\n\n\tpublic static ArgType wildcard(ArgType obj, WildcardBound bound) {\n\t\treturn new WildcardType(obj, bound);\n\t}\n\n\tpublic static ArgType generic(ArgType obj, List<ArgType> generics) {\n\t\tif (!obj.isObject()) {\n\t\t\tthrow new IllegalArgumentException(\"Expected Object as ArgType, got: \" + obj);\n\t\t}\n\t\treturn new GenericObject(obj.getObject(), generics);\n\t}\n\n\tpublic static ArgType generic(ArgType obj, ArgType... generics) {\n\t\treturn generic(obj, Arrays.asList(generics));\n\t}\n\n\tpublic static ArgType generic(String obj, List<ArgType> generics) {\n\t\treturn new GenericObject(Utils.cleanObjectName(obj), generics);\n\t}\n\n\tpublic static ArgType generic(String obj, ArgType generic) {\n\t\treturn generic(obj, Collections.singletonList(generic));\n\t}\n\n\t@TestOnly\n\tpublic static ArgType generic(String obj, ArgType... generics) {\n\t\treturn generic(obj, Arrays.asList(generics));\n\t}\n\n\tpublic static ArgType outerGeneric(ArgType genericOuterType, ArgType innerType) {\n\t\treturn new OuterGenericObject((ObjectType) genericOuterType, (ObjectType) innerType);\n\t}\n\n\tpublic static ArgType array(@NotNull ArgType vtype) {\n\t\treturn new ArrayArg(vtype);\n\t}\n\n\tpublic static ArgType array(@NotNull ArgType type, int dimension) {\n\t\tif (dimension == 1) {\n\t\t\treturn new ArrayArg(type);\n\t\t}\n\t\tArgType arrType = type;\n\t\tfor (int i = 0; i < dimension; i++) {\n\t\t\tarrType = new ArrayArg(arrType);\n\t\t}\n\t\treturn arrType;\n\t}\n\n\tpublic static ArgType unknown(PrimitiveType... types) {\n\t\treturn new UnknownArg(types);\n\t}\n\n\tprivate abstract static class KnownType extends ArgType {\n\n\t\tprivate static final PrimitiveType[] EMPTY_POSSIBLES = new PrimitiveType[0];\n\n\t\t@Override\n\t\tpublic boolean isTypeKnown() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean contains(PrimitiveType type) {\n\t\t\treturn getPrimitiveType() == type;\n\t\t}\n\n\t\t@Override\n\t\tpublic ArgType selectFirst() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic PrimitiveType[] getPossibleTypes() {\n\t\t\treturn EMPTY_POSSIBLES;\n\t\t}\n\t}\n\n\tprivate static final class PrimitiveArg extends KnownType {\n\t\tprivate final PrimitiveType type;\n\n\t\tpublic PrimitiveArg(PrimitiveType type) {\n\t\t\tthis.type = type;\n\t\t\tthis.hash = type.hashCode();\n\t\t}\n\n\t\t@Override\n\t\tpublic PrimitiveType getPrimitiveType() {\n\t\t\treturn type;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isPrimitive() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tboolean internalEquals(Object obj) {\n\t\t\treturn type == ((PrimitiveArg) obj).type;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn type.toString();\n\t\t}\n\t}\n\n\tprivate static class ObjectType extends KnownType {\n\t\tprotected final String objName;\n\n\t\tpublic ObjectType(String obj) {\n\t\t\tthis.objName = obj;\n\t\t\tthis.hash = objName.hashCode();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getObject() {\n\t\t\treturn objName;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isObject() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic PrimitiveType getPrimitiveType() {\n\t\t\treturn PrimitiveType.OBJECT;\n\t\t}\n\n\t\t@Override\n\t\tboolean internalEquals(Object obj) {\n\t\t\treturn objName.equals(((ObjectType) obj).objName);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn objName;\n\t\t}\n\t}\n\n\tprivate static final class GenericType extends ObjectType {\n\t\tprivate List<ArgType> extendTypes;\n\n\t\tpublic GenericType(String obj) {\n\t\t\tthis(obj, Collections.emptyList());\n\t\t}\n\n\t\tpublic GenericType(String obj, ArgType extendType) {\n\t\t\tthis(obj, Collections.singletonList(extendType));\n\t\t}\n\n\t\tpublic GenericType(String obj, List<ArgType> extendTypes) {\n\t\t\tsuper(obj);\n\t\t\tthis.extendTypes = extendTypes;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isGenericType() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic List<ArgType> getExtendTypes() {\n\t\t\treturn extendTypes;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setExtendTypes(List<ArgType> extendTypes) {\n\t\t\tthis.extendTypes = extendTypes;\n\t\t}\n\n\t\t@Override\n\t\tboolean internalEquals(Object obj) {\n\t\t\treturn super.internalEquals(obj)\n\t\t\t\t\t&& extendTypes.equals(((GenericType) obj).extendTypes);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tList<ArgType> extTypes = this.extendTypes;\n\t\t\tif (extTypes.isEmpty()) {\n\t\t\t\treturn objName;\n\t\t\t}\n\t\t\treturn objName + \" extends \" + Utils.listToString(extTypes, \" & \");\n\t\t}\n\t}\n\n\tpublic enum WildcardBound {\n\t\tEXTENDS(1, \"? extends \"), // upper bound (? extends A)\n\t\tUNBOUND(0, \"?\"), // no bounds (?)\n\t\tSUPER(-1, \"? super \"); // lower bound (? super A)\n\n\t\tprivate final int num;\n\t\tprivate final String str;\n\n\t\tWildcardBound(int val, String str) {\n\t\t\tthis.num = val;\n\t\t\tthis.str = str;\n\t\t}\n\n\t\tpublic int getNum() {\n\t\t\treturn num;\n\t\t}\n\n\t\tpublic String getStr() {\n\t\t\treturn str;\n\t\t}\n\n\t\tpublic static WildcardBound getByNum(int num) {\n\t\t\treturn num == 0 ? UNBOUND : (num == 1 ? EXTENDS : SUPER);\n\t\t}\n\t}\n\n\tprivate static final class WildcardType extends ObjectType {\n\t\tprivate final ArgType type;\n\t\tprivate final WildcardBound bound;\n\n\t\tpublic WildcardType(ArgType obj, WildcardBound bound) {\n\t\t\tsuper(OBJECT.getObject());\n\t\t\tthis.type = Objects.requireNonNull(obj);\n\t\t\tthis.bound = Objects.requireNonNull(bound);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isWildcard() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isGeneric() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic ArgType getWildcardType() {\n\t\t\treturn type;\n\t\t}\n\n\t\t@Override\n\t\tpublic WildcardBound getWildcardBound() {\n\t\t\treturn bound;\n\t\t}\n\n\t\t@Override\n\t\tboolean internalEquals(Object obj) {\n\t\t\treturn super.internalEquals(obj)\n\t\t\t\t\t&& bound == ((WildcardType) obj).bound\n\t\t\t\t\t&& type.equals(((WildcardType) obj).type);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tif (bound == WildcardBound.UNBOUND) {\n\t\t\t\treturn bound.getStr();\n\t\t\t}\n\t\t\treturn bound.getStr() + type;\n\t\t}\n\t}\n\n\tprivate static class GenericObject extends ObjectType {\n\t\tprivate final List<ArgType> generics;\n\n\t\tpublic GenericObject(String obj, List<ArgType> generics) {\n\t\t\tsuper(obj);\n\t\t\tthis.generics = Objects.requireNonNull(generics);\n\t\t\tthis.hash = calcHash();\n\t\t}\n\n\t\tprivate int calcHash() {\n\t\t\treturn objName.hashCode() + 31 * generics.hashCode();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isGeneric() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic List<ArgType> getGenericTypes() {\n\t\t\treturn generics;\n\t\t}\n\n\t\t@Override\n\t\tboolean internalEquals(Object obj) {\n\t\t\treturn super.internalEquals(obj)\n\t\t\t\t\t&& Objects.equals(generics, ((GenericObject) obj).generics);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn super.toString() + '<' + Utils.listToString(generics) + '>';\n\t\t}\n\t}\n\n\tprivate static class OuterGenericObject extends ObjectType {\n\t\tprivate final ObjectType outerType;\n\t\tprivate final ObjectType innerType;\n\n\t\tpublic OuterGenericObject(ObjectType outerType, ObjectType innerType) {\n\t\t\tsuper(outerType.getObject() + '$' + innerType.getObject());\n\t\t\tthis.outerType = outerType;\n\t\t\tthis.innerType = innerType;\n\t\t\tthis.hash = calcHash();\n\t\t}\n\n\t\tprivate int calcHash() {\n\t\t\treturn objName.hashCode() + 31 * (outerType.hashCode() + 31 * innerType.hashCode());\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isGeneric() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic List<ArgType> getGenericTypes() {\n\t\t\treturn innerType.getGenericTypes();\n\t\t}\n\n\t\t@Override\n\t\tpublic ArgType getOuterType() {\n\t\t\treturn outerType;\n\t\t}\n\n\t\t@Override\n\t\tpublic ArgType getInnerType() {\n\t\t\treturn innerType;\n\t\t}\n\n\t\t@Override\n\t\tboolean internalEquals(Object obj) {\n\t\t\treturn super.internalEquals(obj)\n\t\t\t\t\t&& Objects.equals(outerType, ((OuterGenericObject) obj).outerType)\n\t\t\t\t\t&& Objects.equals(innerType, ((OuterGenericObject) obj).innerType);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn outerType.toString() + '$' + innerType.toString();\n\t\t}\n\t}\n\n\tprivate static final class ArrayArg extends KnownType {\n\t\tprivate static final PrimitiveType[] ARRAY_POSSIBLES = new PrimitiveType[] { PrimitiveType.ARRAY };\n\t\tprivate final ArgType arrayElement;\n\n\t\tpublic ArrayArg(ArgType arrayElement) {\n\t\t\tthis.arrayElement = arrayElement;\n\t\t\tthis.hash = arrayElement.hashCode();\n\t\t}\n\n\t\t@Override\n\t\tpublic ArgType getArrayElement() {\n\t\t\treturn arrayElement;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isArray() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic PrimitiveType getPrimitiveType() {\n\t\t\treturn PrimitiveType.ARRAY;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isTypeKnown() {\n\t\t\treturn arrayElement.isTypeKnown();\n\t\t}\n\n\t\t@Override\n\t\tpublic ArgType selectFirst() {\n\t\t\treturn array(arrayElement.selectFirst());\n\t\t}\n\n\t\t@Override\n\t\tpublic PrimitiveType[] getPossibleTypes() {\n\t\t\treturn ARRAY_POSSIBLES;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getArrayDimension() {\n\t\t\treturn 1 + arrayElement.getArrayDimension();\n\t\t}\n\n\t\t@Override\n\t\tpublic ArgType getArrayRootElement() {\n\t\t\treturn arrayElement.getArrayRootElement();\n\t\t}\n\n\t\t@Override\n\t\tboolean internalEquals(Object other) {\n\t\t\tArrayArg otherArr = (ArrayArg) other;\n\t\t\treturn this.arrayElement.equals(otherArr.getArrayElement());\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn arrayElement + \"[]\";\n\t\t}\n\t}\n\n\tprivate static final class UnknownArg extends ArgType {\n\t\tprivate final PrimitiveType[] possibleTypes;\n\n\t\tpublic UnknownArg(PrimitiveType[] types) {\n\t\t\tthis.possibleTypes = types;\n\t\t\tthis.hash = Arrays.hashCode(possibleTypes);\n\t\t}\n\n\t\t@Override\n\t\tpublic PrimitiveType[] getPossibleTypes() {\n\t\t\treturn possibleTypes;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isTypeKnown() {\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean contains(PrimitiveType type) {\n\t\t\tfor (PrimitiveType t : possibleTypes) {\n\t\t\t\tif (t == type) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic ArgType selectFirst() {\n\t\t\tif (contains(PrimitiveType.OBJECT)) {\n\t\t\t\treturn OBJECT;\n\t\t\t}\n\t\t\tif (contains(PrimitiveType.ARRAY)) {\n\t\t\t\treturn array(OBJECT);\n\t\t\t}\n\t\t\treturn primitive(possibleTypes[0]);\n\t\t}\n\n\t\t@Override\n\t\tboolean internalEquals(Object obj) {\n\t\t\treturn Arrays.equals(possibleTypes, ((UnknownArg) obj).possibleTypes);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tif (possibleTypes.length == PrimitiveType.values().length) {\n\t\t\t\treturn \"??\";\n\t\t\t} else {\n\t\t\t\treturn \"??[\" + Utils.arrayToStr(possibleTypes) + ']';\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic boolean isTypeKnown() {\n\t\treturn false;\n\t}\n\n\tpublic PrimitiveType getPrimitiveType() {\n\t\treturn null;\n\t}\n\n\tpublic boolean isPrimitive() {\n\t\treturn false;\n\t}\n\n\tpublic String getObject() {\n\t\tthrow new UnsupportedOperationException(\"ArgType.getObject(), call class: \" + this.getClass());\n\t}\n\n\tpublic boolean isObject() {\n\t\treturn false;\n\t}\n\n\tpublic boolean isGeneric() {\n\t\treturn false;\n\t}\n\n\tpublic boolean isGenericType() {\n\t\treturn false;\n\t}\n\n\tpublic List<ArgType> getGenericTypes() {\n\t\treturn null;\n\t}\n\n\tpublic List<ArgType> getExtendTypes() {\n\t\treturn Collections.emptyList();\n\t}\n\n\tpublic void setExtendTypes(List<ArgType> extendTypes) {\n\t}\n\n\tpublic ArgType getWildcardType() {\n\t\treturn null;\n\t}\n\n\tpublic WildcardBound getWildcardBound() {\n\t\treturn null;\n\t}\n\n\tpublic boolean isWildcard() {\n\t\treturn false;\n\t}\n\n\tpublic ArgType getOuterType() {\n\t\treturn null;\n\t}\n\n\tpublic ArgType getInnerType() {\n\t\treturn null;\n\t}\n\n\tpublic boolean isArray() {\n\t\treturn false;\n\t}\n\n\tpublic int getArrayDimension() {\n\t\treturn 0;\n\t}\n\n\tpublic ArgType getArrayElement() {\n\t\treturn null;\n\t}\n\n\tpublic ArgType getArrayRootElement() {\n\t\treturn this;\n\t}\n\n\tpublic abstract boolean contains(PrimitiveType type);\n\n\tpublic abstract ArgType selectFirst();\n\n\tpublic abstract PrimitiveType[] getPossibleTypes();\n\n\tpublic static boolean isCastNeeded(RootNode root, ArgType from, ArgType to) {\n\t\tif (from.equals(to)) {\n\t\t\treturn false;\n\t\t}\n\t\tTypeCompareEnum result = root.getTypeCompare().compareTypes(from, to);\n\t\treturn !result.isNarrow();\n\t}\n\n\tpublic static boolean isInstanceOf(RootNode root, ArgType type, ArgType of) {\n\t\tif (type.equals(of)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!type.isObject() || !of.isObject()) {\n\t\t\treturn false;\n\t\t}\n\t\treturn root.getClsp().isImplements(type.getObject(), of.getObject());\n\t}\n\n\tpublic static boolean isClsKnown(RootNode root, ArgType cls) {\n\t\tif (cls.isObject()) {\n\t\t\treturn root.getClsp().isClsKnown(cls.getObject());\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean canBeObject() {\n\t\treturn isObject() || (!isTypeKnown() && contains(PrimitiveType.OBJECT));\n\t}\n\n\tpublic boolean canBeArray() {\n\t\treturn isArray() || (!isTypeKnown() && contains(PrimitiveType.ARRAY));\n\t}\n\n\tpublic boolean canBePrimitive(PrimitiveType primitiveType) {\n\t\treturn (isPrimitive() && getPrimitiveType() == primitiveType)\n\t\t\t\t|| (!isTypeKnown() && contains(primitiveType));\n\t}\n\n\tpublic boolean canBeAnyNumber() {\n\t\tif (isPrimitive()) {\n\t\t\treturn !getPrimitiveType().isObjectOrArray();\n\t\t}\n\t\tfor (PrimitiveType primitiveType : getPossibleTypes()) {\n\t\t\tif (!primitiveType.isObjectOrArray()) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static ArgType convertFromPrimitiveType(PrimitiveType primitiveType) {\n\t\tswitch (primitiveType) {\n\t\t\tcase BOOLEAN:\n\t\t\t\treturn BOOLEAN;\n\t\t\tcase CHAR:\n\t\t\t\treturn CHAR;\n\t\t\tcase BYTE:\n\t\t\t\treturn BYTE;\n\t\t\tcase SHORT:\n\t\t\t\treturn SHORT;\n\t\t\tcase INT:\n\t\t\t\treturn INT;\n\t\t\tcase FLOAT:\n\t\t\t\treturn FLOAT;\n\t\t\tcase LONG:\n\t\t\t\treturn LONG;\n\t\t\tcase DOUBLE:\n\t\t\t\treturn DOUBLE;\n\t\t\tcase OBJECT:\n\t\t\t\treturn OBJECT;\n\t\t\tcase ARRAY:\n\t\t\t\treturn OBJECT_ARRAY;\n\t\t\tcase VOID:\n\t\t\t\treturn ArgType.VOID;\n\t\t}\n\t\treturn OBJECT;\n\t}\n\n\tpublic static ArgType parse(String type) {\n\t\tif (type == null || type.isEmpty()) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to parse type string: \" + type);\n\t\t}\n\t\tchar f = type.charAt(0);\n\t\tswitch (f) {\n\t\t\tcase 'L':\n\t\t\t\treturn object(type);\n\t\t\tcase 'T':\n\t\t\t\treturn genericType(type.substring(1, type.length() - 1));\n\t\t\tcase '[':\n\t\t\t\treturn array(parse(type.substring(1)));\n\t\t\tdefault:\n\t\t\t\tif (type.length() != 1) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Unknown type string: \\\"\" + type + '\"');\n\t\t\t\t}\n\t\t\t\treturn parse(f);\n\t\t}\n\t}\n\n\tpublic static ArgType parse(char f) {\n\t\tswitch (f) {\n\t\t\tcase 'Z':\n\t\t\t\treturn BOOLEAN;\n\t\t\tcase 'B':\n\t\t\t\treturn BYTE;\n\t\t\tcase 'C':\n\t\t\t\treturn CHAR;\n\t\t\tcase 'S':\n\t\t\t\treturn SHORT;\n\t\t\tcase 'I':\n\t\t\t\treturn INT;\n\t\t\tcase 'J':\n\t\t\t\treturn LONG;\n\t\t\tcase 'F':\n\t\t\t\treturn FLOAT;\n\t\t\tcase 'D':\n\t\t\t\treturn DOUBLE;\n\t\t\tcase 'V':\n\t\t\t\treturn VOID;\n\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unknown type char: '\" + f + \"' (0x\" + Integer.toHexString(f) + ')');\n\t\t}\n\t}\n\n\tpublic int getRegCount() {\n\t\tif (isPrimitive()) {\n\t\t\tPrimitiveType type = getPrimitiveType();\n\t\t\tif (type == PrimitiveType.LONG || type == PrimitiveType.DOUBLE) {\n\t\t\t\treturn 2;\n\t\t\t} else {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t\tif (!isTypeKnown()) {\n\t\t\treturn 0;\n\t\t}\n\t\treturn 1;\n\t}\n\n\tpublic boolean containsGeneric() {\n\t\tif (isGeneric() || isGenericType()) {\n\t\t\treturn true;\n\t\t}\n\t\tif (isArray()) {\n\t\t\tArgType arrayElement = getArrayElement();\n\t\t\tif (arrayElement != null) {\n\t\t\t\treturn arrayElement.containsGeneric();\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean containsTypeVariable() {\n\t\tif (isGenericType()) {\n\t\t\treturn true;\n\t\t}\n\t\tArgType wildcardType = getWildcardType();\n\t\tif (wildcardType != null) {\n\t\t\treturn wildcardType.containsTypeVariable();\n\t\t}\n\t\tif (isGeneric()) {\n\t\t\tList<ArgType> genericTypes = getGenericTypes();\n\t\t\tif (genericTypes != null) {\n\t\t\t\tfor (ArgType genericType : genericTypes) {\n\t\t\t\t\tif (genericType.containsTypeVariable()) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tArgType outerType = getOuterType();\n\t\t\tif (outerType != null) {\n\t\t\t\treturn outerType.containsTypeVariable();\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tif (isArray()) {\n\t\t\tArgType arrayElement = getArrayElement();\n\t\t\tif (arrayElement != null) {\n\t\t\t\treturn arrayElement.containsTypeVariable();\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean isVoid() {\n\t\treturn isPrimitive() && getPrimitiveType() == PrimitiveType.VOID;\n\t}\n\n\t/**\n\t * Recursively visit all subtypes of this type.\n\t * To exit return non-null value.\n\t */\n\t@Nullable\n\tpublic <R> R visitTypes(Function<ArgType, R> visitor) {\n\t\tR r = visitor.apply(this);\n\t\tif (r != null) {\n\t\t\treturn r;\n\t\t}\n\t\tif (isArray()) {\n\t\t\tArgType arrayElement = getArrayElement();\n\t\t\tif (arrayElement != null) {\n\t\t\t\treturn arrayElement.visitTypes(visitor);\n\t\t\t}\n\t\t}\n\t\tArgType wildcardType = getWildcardType();\n\t\tif (wildcardType != null) {\n\t\t\tR res = wildcardType.visitTypes(visitor);\n\t\t\tif (res != null) {\n\t\t\t\treturn res;\n\t\t\t}\n\t\t}\n\t\tif (isGeneric()) {\n\t\t\tList<ArgType> genericTypes = getGenericTypes();\n\t\t\tif (genericTypes != null) {\n\t\t\t\tfor (ArgType genericType : genericTypes) {\n\t\t\t\t\tR res = genericType.visitTypes(visitor);\n\t\t\t\t\tif (res != null) {\n\t\t\t\t\t\treturn res;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static ArgType tryToResolveClassAlias(RootNode root, ArgType type) {\n\t\tif (type.isGenericType()) {\n\t\t\treturn type;\n\t\t}\n\t\tif (type.isArray()) {\n\t\t\tArgType rootType = type.getArrayRootElement();\n\t\t\tArgType aliasType = tryToResolveClassAlias(root, rootType);\n\t\t\tif (aliasType == rootType) {\n\t\t\t\treturn type;\n\t\t\t}\n\t\t\treturn ArgType.array(aliasType, type.getArrayDimension());\n\t\t}\n\t\tif (type.isObject()) {\n\t\t\tArgType wildcardType = type.getWildcardType();\n\t\t\tif (wildcardType != null) {\n\t\t\t\treturn new WildcardType(tryToResolveClassAlias(root, wildcardType), type.getWildcardBound());\n\t\t\t}\n\t\t\tClassInfo clsInfo = ClassInfo.fromName(root, type.getObject());\n\t\t\tArgType baseType = clsInfo.hasAlias() ? ArgType.object(clsInfo.getAliasFullName()) : type;\n\t\t\tif (!type.isGeneric()) {\n\t\t\t\treturn baseType;\n\t\t\t}\n\t\t\tList<ArgType> genericTypes = type.getGenericTypes();\n\t\t\tif (genericTypes != null) {\n\t\t\t\treturn new GenericObject(baseType.getObject(), tryToResolveClassAlias(root, genericTypes));\n\t\t\t}\n\t\t}\n\t\treturn type;\n\t}\n\n\tpublic static List<ArgType> tryToResolveClassAlias(RootNode root, List<ArgType> types) {\n\t\treturn ListUtils.map(types, t -> tryToResolveClassAlias(root, t));\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ARG_TYPE\";\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn hash;\n\t}\n\n\tabstract boolean internalEquals(Object obj);\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (hash != obj.hashCode()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\treturn internalEquals(obj);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/args/CodeVar.java",
    "content": "package jadx.core.dex.instructions.args;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.api.metadata.annotations.VarNode;\n\npublic class CodeVar {\n\tprivate String name;\n\tprivate ArgType type; // before type inference can be null and set only for immutable types\n\tprivate List<SSAVar> ssaVars = Collections.emptyList();\n\n\tprivate boolean isFinal;\n\tprivate boolean isThis;\n\tprivate boolean isDeclared;\n\n\tprivate VarNode cachedVarNode; // set and used at codegen stage\n\n\tpublic static CodeVar fromMthArg(RegisterArg mthArg, boolean linkRegister) {\n\t\tCodeVar var = new CodeVar();\n\t\tvar.setType(mthArg.getInitType());\n\t\tvar.setName(mthArg.getName());\n\t\tvar.setThis(mthArg.isThis());\n\t\tvar.setDeclared(true);\n\t\tvar.setThis(mthArg.isThis());\n\t\tif (linkRegister) {\n\t\t\tvar.setSsaVars(Collections.singletonList(new SSAVar(mthArg.getRegNum(), 0, mthArg)));\n\t\t}\n\t\treturn var;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic ArgType getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setType(ArgType type) {\n\t\tthis.type = type;\n\t}\n\n\tpublic List<SSAVar> getSsaVars() {\n\t\treturn ssaVars;\n\t}\n\n\tpublic void addSsaVar(SSAVar ssaVar) {\n\t\tif (ssaVars.isEmpty()) {\n\t\t\tssaVars = new ArrayList<>(3);\n\t\t}\n\t\tif (!ssaVars.contains(ssaVar)) {\n\t\t\tssaVars.add(ssaVar);\n\t\t}\n\t}\n\n\tpublic void setSsaVars(List<SSAVar> ssaVars) {\n\t\tthis.ssaVars = ssaVars;\n\t}\n\n\tpublic SSAVar getAnySsaVar() {\n\t\tif (ssaVars.isEmpty()) {\n\t\t\tthrow new IllegalStateException(\"CodeVar without SSA variables attached: \" + this);\n\t\t}\n\t\treturn ssaVars.get(0);\n\t}\n\n\tpublic boolean isFinal() {\n\t\treturn isFinal;\n\t}\n\n\tpublic void setFinal(boolean aFinal) {\n\t\tisFinal = aFinal;\n\t}\n\n\tpublic boolean isThis() {\n\t\treturn isThis;\n\t}\n\n\tpublic void setThis(boolean aThis) {\n\t\tisThis = aThis;\n\t}\n\n\tpublic boolean isDeclared() {\n\t\treturn isDeclared;\n\t}\n\n\tpublic void setDeclared(boolean declared) {\n\t\tisDeclared = declared;\n\t}\n\n\tpublic VarNode getCachedVarNode() {\n\t\treturn cachedVarNode;\n\t}\n\n\tpublic void setCachedVarNode(VarNode varNode) {\n\t\tthis.cachedVarNode = varNode;\n\t}\n\n\t/**\n\t * Merge flags with OR operator\n\t */\n\tpublic void mergeFlagsFrom(CodeVar other) {\n\t\tif (other.isDeclared()) {\n\t\t\tsetDeclared(true);\n\t\t}\n\t\tif (other.isThis()) {\n\t\t\tsetThis(true);\n\t\t}\n\t\tif (other.isFinal()) {\n\t\t\tsetFinal(true);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn (isFinal ? \"final \" : \"\") + type + ' ' + name;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java",
    "content": "package jadx.core.dex.instructions.args;\n\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n/**\n * Instruction argument.\n * Can be: register, literal, instruction or name\n */\npublic abstract class InsnArg extends Typed {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(InsnArg.class);\n\n\t@Nullable(\"Null for method arguments\")\n\tprotected InsnNode parentInsn;\n\n\tpublic static RegisterArg reg(int regNum, ArgType type) {\n\t\treturn new RegisterArg(regNum, type);\n\t}\n\n\tpublic static RegisterArg reg(InsnData insn, int argNum, ArgType type) {\n\t\treturn reg(insn.getReg(argNum), type);\n\t}\n\n\tpublic static RegisterArg typeImmutableIfKnownReg(InsnData insn, int argNum, ArgType type) {\n\t\tif (type.isTypeKnown()) {\n\t\t\treturn typeImmutableReg(insn.getReg(argNum), type);\n\t\t}\n\t\treturn reg(insn.getReg(argNum), type);\n\t}\n\n\tpublic static RegisterArg typeImmutableReg(InsnData insn, int argNum, ArgType type) {\n\t\treturn typeImmutableReg(insn.getReg(argNum), type);\n\t}\n\n\tpublic static RegisterArg typeImmutableReg(int regNum, ArgType type) {\n\t\treturn reg(regNum, type, true);\n\t}\n\n\tpublic static RegisterArg reg(int regNum, ArgType type, boolean typeImmutable) {\n\t\tRegisterArg reg = new RegisterArg(regNum, type);\n\t\tif (typeImmutable) {\n\t\t\treg.add(AFlag.IMMUTABLE_TYPE);\n\t\t}\n\t\treturn reg;\n\t}\n\n\tpublic static LiteralArg lit(long literal, ArgType type) {\n\t\treturn LiteralArg.makeWithFixedType(literal, type);\n\t}\n\n\tpublic static LiteralArg lit(InsnData insn, ArgType type) {\n\t\treturn lit(insn.getLiteral(), type);\n\t}\n\n\tprivate static InsnWrapArg wrap(InsnNode insn) {\n\t\tinsn.add(AFlag.WRAPPED);\n\t\treturn new InsnWrapArg(insn);\n\t}\n\n\tpublic boolean isRegister() {\n\t\treturn false;\n\t}\n\n\tpublic boolean isLiteral() {\n\t\treturn false;\n\t}\n\n\tpublic boolean isInsnWrap() {\n\t\treturn false;\n\t}\n\n\tpublic boolean isNamed() {\n\t\treturn false;\n\t}\n\n\t@Nullable\n\tpublic InsnNode getParentInsn() {\n\t\treturn parentInsn;\n\t}\n\n\tpublic void setParentInsn(@Nullable InsnNode parentInsn) {\n\t\tthis.parentInsn = parentInsn;\n\t}\n\n\t@Nullable(\"if wrap failed\")\n\tpublic InsnArg wrapInstruction(MethodNode mth, InsnNode insn) {\n\t\treturn wrapInstruction(mth, insn, true);\n\t}\n\n\t@Nullable(\"if wrap failed\")\n\tpublic InsnArg wrapInstruction(MethodNode mth, InsnNode insn, boolean unbind) {\n\t\tInsnNode parent = parentInsn;\n\t\tif (parent == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (parent == insn) {\n\t\t\tLOG.debug(\"Can't wrap instruction info itself: {}\", insn);\n\t\t\treturn null;\n\t\t}\n\t\tint i = getArgIndex(parent, this);\n\t\tif (i == -1) {\n\t\t\treturn null;\n\t\t}\n\t\tif (insn.getType() == InsnType.MOVE && this.isRegister()) {\n\t\t\t// preserve variable name for move insn (needed in `for-each` loop for iteration variable)\n\t\t\tString name = ((RegisterArg) this).getName();\n\t\t\tif (name != null) {\n\t\t\t\tInsnArg arg = insn.getArg(0);\n\t\t\t\tif (arg.isRegister()) {\n\t\t\t\t\t((RegisterArg) arg).setNameIfUnknown(name);\n\t\t\t\t} else if (arg.isInsnWrap()) {\n\t\t\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\t\tRegisterArg registerArg = wrapInsn.getResult();\n\t\t\t\t\tif (registerArg != null) {\n\t\t\t\t\t\tregisterArg.setNameIfUnknown(name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tInsnArg arg = wrapInsnIntoArg(insn);\n\t\tInsnArg oldArg = parent.getArg(i);\n\t\tif (arg.getType() == ArgType.UNKNOWN) {\n\t\t\t// restore arg type if wrapped insn missing result\n\t\t\targ.setType(oldArg.getType());\n\t\t}\n\t\tparent.setArg(i, arg);\n\t\tInsnRemover.unbindArgUsage(mth, oldArg);\n\t\tif (unbind) {\n\t\t\tInsnRemover.unbindArgUsage(mth, this);\n\t\t\t// result not needed in wrapped insn\n\t\t\tInsnRemover.unbindResult(mth, insn);\n\t\t\tinsn.setResult(null);\n\t\t}\n\t\treturn arg;\n\t}\n\n\tprivate static int getArgIndex(InsnNode parent, InsnArg arg) {\n\t\tint count = parent.getArgsCount();\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tif (parent.getArg(i) == arg) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\t@NotNull\n\tpublic static InsnArg wrapInsnIntoArg(InsnNode insn) {\n\t\tInsnType type = insn.getType();\n\t\tif (type == InsnType.CONST || type == InsnType.MOVE) {\n\t\t\tif (insn.contains(AFlag.FORCE_ASSIGN_INLINE)) {\n\t\t\t\tRegisterArg resArg = insn.getResult();\n\t\t\t\tInsnArg arg = wrap(insn);\n\t\t\t\tif (resArg != null) {\n\t\t\t\t\targ.setType(resArg.getType());\n\t\t\t\t}\n\t\t\t\treturn arg;\n\t\t\t} else {\n\t\t\t\tInsnArg arg = insn.getArg(0);\n\t\t\t\tinsn.add(AFlag.DONT_GENERATE);\n\t\t\t\treturn arg;\n\t\t\t}\n\t\t}\n\t\treturn wrapArg(insn);\n\t}\n\n\t/**\n\t * Prefer {@link InsnArg#wrapInsnIntoArg(InsnNode)}.\n\t * <p>\n\t * This method don't support MOVE and CONST insns!\n\t */\n\tpublic static InsnArg wrapArg(InsnNode insn) {\n\t\tRegisterArg resArg = insn.getResult();\n\t\tInsnArg arg = wrap(insn);\n\t\tswitch (insn.getType()) {\n\t\t\tcase CONST:\n\t\t\tcase MOVE:\n\t\t\t\tthrow new JadxRuntimeException(\"Don't wrap MOVE or CONST insns: \" + insn);\n\n\t\t\tcase CONST_STR:\n\t\t\t\targ.setType(ArgType.STRING);\n\t\t\t\tif (resArg != null) {\n\t\t\t\t\tresArg.setType(ArgType.STRING);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase CONST_CLASS:\n\t\t\t\targ.setType(ArgType.CLASS);\n\t\t\t\tif (resArg != null) {\n\t\t\t\t\tresArg.setType(ArgType.CLASS);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tif (resArg != null) {\n\t\t\t\t\targ.setType(resArg.getType());\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t\treturn arg;\n\t}\n\n\tpublic boolean isZeroLiteral() {\n\t\treturn false;\n\t}\n\n\tpublic boolean isZeroConst() {\n\t\tif (isZeroLiteral()) {\n\t\t\treturn true;\n\t\t}\n\t\tif (isInsnWrap()) {\n\t\t\tInsnNode wrapInsn = ((InsnWrapArg) this).getWrapInsn();\n\t\t\tif (wrapInsn.getType() == InsnType.CONST) {\n\t\t\t\treturn wrapInsn.getArg(0).isZeroLiteral();\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean isFalse() {\n\t\tif (isLiteral()) {\n\t\t\tLiteralArg litArg = (LiteralArg) this;\n\t\t\treturn litArg.getLiteral() == 0 && Objects.equals(litArg.getType(), ArgType.BOOLEAN);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean isTrue() {\n\t\tif (isLiteral()) {\n\t\t\tLiteralArg litArg = (LiteralArg) this;\n\t\t\treturn litArg.getLiteral() == 1 && Objects.equals(litArg.getType(), ArgType.BOOLEAN);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean isThis() {\n\t\treturn contains(AFlag.THIS);\n\t}\n\n\t/**\n\t * Return true for 'this' from other classes (often occur in anonymous classes)\n\t */\n\tpublic boolean isAnyThis() {\n\t\tif (contains(AFlag.THIS)) {\n\t\t\treturn true;\n\t\t}\n\t\tInsnNode wrappedInsn = unwrap();\n\t\tif (wrappedInsn != null && wrappedInsn.getType() == InsnType.IGET) {\n\t\t\treturn wrappedInsn.getArg(0).isAnyThis();\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic InsnNode unwrap() {\n\t\tif (isInsnWrap()) {\n\t\t\treturn ((InsnWrapArg) this).getWrapInsn();\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic boolean isConst() {\n\t\treturn isLiteral() || (isInsnWrap() && ((InsnWrapArg) this).getWrapInsn().isConstInsn());\n\t}\n\n\tpublic boolean isSameConst(InsnArg other) {\n\t\tif (isConst() && other.isConst()) {\n\t\t\treturn this.equals(other);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean isSameVar(RegisterArg arg) {\n\t\tif (arg == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (isRegister()) {\n\t\t\treturn ((RegisterArg) this).sameRegAndSVar(arg);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean isSameVar(SSAVar ssaVar) {\n\t\tif (ssaVar == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (isRegister()) {\n\t\t\tSSAVar thisSsaVar = ((RegisterArg) this).getSVar();\n\t\t\treturn Objects.equals(thisSsaVar, ssaVar);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean isSameCodeVar(RegisterArg arg) {\n\t\tif (arg == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (isRegister()) {\n\t\t\treturn ((RegisterArg) this).sameCodeVar(arg);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean isUseVar(RegisterArg arg) {\n\t\treturn InsnUtils.containsVar(this, arg);\n\t}\n\n\tprotected final <T extends InsnArg> T copyCommonParams(T copy) {\n\t\tcopy.copyAttributesFrom(this);\n\t\tcopy.setParentInsn(parentInsn);\n\t\treturn copy;\n\t}\n\n\tpublic InsnArg duplicate() {\n\t\treturn this;\n\t}\n\n\tpublic String toShortString() {\n\t\treturn this.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnWrapArg.java",
    "content": "package jadx.core.dex.instructions.args;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.ConstStringNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic final class InsnWrapArg extends InsnArg {\n\n\tprivate final InsnNode wrappedInsn;\n\n\t/**\n\t * Use {@link InsnArg#wrapInsnIntoArg(InsnNode)} instead this constructor\n\t */\n\tInsnWrapArg(@NotNull InsnNode insn) {\n\t\tRegisterArg result = insn.getResult();\n\t\tthis.type = result != null ? result.getType() : ArgType.UNKNOWN;\n\t\tthis.wrappedInsn = insn;\n\t}\n\n\tpublic InsnNode getWrapInsn() {\n\t\treturn wrappedInsn;\n\t}\n\n\tpublic InsnNode unWrapWithCopy() {\n\t\tInsnNode copy = wrappedInsn.copyWithoutResult();\n\t\tcopy.remove(AFlag.WRAPPED);\n\t\treturn copy;\n\t}\n\n\t@Override\n\tpublic void setParentInsn(InsnNode parentInsn) {\n\t\tif (parentInsn == wrappedInsn) {\n\t\t\tthrow new JadxRuntimeException(\"Can't wrap instruction info itself: \" + parentInsn);\n\t\t}\n\t\tthis.parentInsn = parentInsn;\n\t}\n\n\t@Override\n\tpublic InsnArg duplicate() {\n\t\tInsnWrapArg copy = new InsnWrapArg(wrappedInsn.copyWithoutResult());\n\t\tcopy.setType(type);\n\t\treturn copyCommonParams(copy);\n\t}\n\n\t@Override\n\tpublic boolean isInsnWrap() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn wrappedInsn.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof InsnWrapArg)) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnWrapArg that = (InsnWrapArg) o;\n\t\tInsnNode thisInsn = wrappedInsn;\n\t\tInsnNode thatInsn = that.wrappedInsn;\n\t\tif (!thisInsn.isSame(thatInsn)) {\n\t\t\treturn false;\n\t\t}\n\t\tint count = thisInsn.getArgsCount();\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tif (!thisInsn.getArg(i).equals(thatInsn.getArg(i))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String toShortString() {\n\t\tif (wrappedInsn.getType() == InsnType.CONST_STR) {\n\t\t\treturn \"(\\\"\" + ((ConstStringNode) wrappedInsn).getString() + \"\\\")\";\n\t\t}\n\t\treturn \"(wrap:\" + type + \":\" + wrappedInsn.getType() + ')';\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tif (wrappedInsn.getType() == InsnType.CONST_STR) {\n\t\t\treturn \"(\\\"\" + ((ConstStringNode) wrappedInsn).getString() + \"\\\")\";\n\t\t}\n\t\treturn \"(wrap:\" + type + \":\" + wrappedInsn + ')';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/args/LiteralArg.java",
    "content": "package jadx.core.dex.instructions.args;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.codegen.TypeGen;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic final class LiteralArg extends InsnArg {\n\n\tpublic static LiteralArg make(long value, ArgType type) {\n\t\treturn new LiteralArg(value, type);\n\t}\n\n\tpublic static LiteralArg makeWithFixedType(long value, ArgType type) {\n\t\treturn new LiteralArg(value, fixLiteralType(value, type));\n\t}\n\n\tprivate static ArgType fixLiteralType(long value, ArgType type) {\n\t\tif (value == 0 || type.isTypeKnown() || type.contains(PrimitiveType.LONG) || type.contains(PrimitiveType.DOUBLE)) {\n\t\t\treturn type;\n\t\t}\n\t\tif (value == 1) {\n\t\t\treturn ArgType.NARROW_NUMBERS;\n\t\t}\n\t\tif (value < 0) {\n\t\t\treturn ArgType.NARROW_NEG_NUMBERS;\n\t\t}\n\t\treturn ArgType.NARROW_NUMBERS_NO_BOOL;\n\t}\n\n\tpublic static LiteralArg litFalse() {\n\t\treturn new LiteralArg(0, ArgType.BOOLEAN);\n\t}\n\n\tpublic static LiteralArg litTrue() {\n\t\treturn new LiteralArg(1, ArgType.BOOLEAN);\n\t}\n\n\tprivate final long literal;\n\n\tprivate LiteralArg(long value, ArgType type) {\n\t\tif (value != 0 && type.isObject()) {\n\t\t\tthrow new JadxRuntimeException(\"Wrong literal type: \" + type + \" for value: \" + value);\n\t\t}\n\t\tthis.literal = value;\n\t\tthis.type = type;\n\t}\n\n\tpublic long getLiteral() {\n\t\treturn literal;\n\t}\n\n\t@Override\n\tpublic void setType(ArgType type) {\n\t\tsuper.setType(type);\n\t}\n\n\t@Override\n\tpublic boolean isLiteral() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isZeroLiteral() {\n\t\treturn literal == 0;\n\t}\n\n\tpublic boolean isInteger() {\n\t\tswitch (type.getPrimitiveType()) {\n\t\t\tcase INT:\n\t\t\tcase BYTE:\n\t\t\tcase CHAR:\n\t\t\tcase SHORT:\n\t\t\tcase LONG:\n\t\t\t\treturn true;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic boolean isNegative() {\n\t\tif (isInteger()) {\n\t\t\treturn literal < 0;\n\t\t}\n\t\tif (type == ArgType.FLOAT) {\n\t\t\tfloat val = Float.intBitsToFloat(((int) literal));\n\t\t\treturn val < 0 && Float.isFinite(val);\n\t\t}\n\t\tif (type == ArgType.DOUBLE) {\n\t\t\tdouble val = Double.longBitsToDouble(literal);\n\t\t\treturn val < 0 && Double.isFinite(val);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Nullable\n\tpublic LiteralArg negate() {\n\t\tlong neg;\n\t\tif (isInteger()) {\n\t\t\tneg = -literal;\n\t\t} else if (type == ArgType.FLOAT) {\n\t\t\tfloat val = Float.intBitsToFloat(((int) literal));\n\t\t\tneg = Float.floatToIntBits(-val);\n\t\t} else if (type == ArgType.DOUBLE) {\n\t\t\tdouble val = Double.longBitsToDouble(literal);\n\t\t\tneg = Double.doubleToLongBits(-val);\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t\treturn new LiteralArg(neg, type);\n\t}\n\n\t@Override\n\tpublic InsnArg duplicate() {\n\t\treturn copyCommonParams(new LiteralArg(literal, type));\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn (int) (literal ^ literal >>> 32) + 31 * getType().hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tLiteralArg that = (LiteralArg) o;\n\t\treturn literal == that.literal && getType().equals(that.getType());\n\t}\n\n\t@Override\n\tpublic String toShortString() {\n\t\treturn Long.toString(literal);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\ttry {\n\t\t\tString value = TypeGen.literalToString(literal, getType(), StringUtils.getInstance(), true, false);\n\t\t\tif (getType().equals(ArgType.BOOLEAN) && (value.equals(\"true\") || value.equals(\"false\"))) {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t\treturn '(' + value + ' ' + type + ')';\n\t\t} catch (JadxRuntimeException ex) {\n\t\t\t// can't convert literal to string\n\t\t\treturn \"(\" + literal + ' ' + type + ')';\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/args/Named.java",
    "content": "package jadx.core.dex.instructions.args;\n\npublic interface Named {\n\n\tString getName();\n\n\tvoid setName(String name);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/args/NamedArg.java",
    "content": "package jadx.core.dex.instructions.args;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic final class NamedArg extends InsnArg implements Named {\n\n\t@NotNull\n\tprivate String name;\n\n\tpublic NamedArg(@NotNull String name, @NotNull ArgType type) {\n\t\tthis.name = name;\n\t\tthis.type = type;\n\t}\n\n\t@NotNull\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic boolean isNamed() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void setName(@NotNull String name) {\n\t\tthis.name = name;\n\t}\n\n\t@Override\n\tpublic InsnArg duplicate() {\n\t\treturn copyCommonParams(new NamedArg(name, type));\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn name.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof NamedArg)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn name.equals(((NamedArg) o).name);\n\t}\n\n\t@Override\n\tpublic String toShortString() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn '(' + name + ' ' + type + ')';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/args/PrimitiveType.java",
    "content": "package jadx.core.dex.instructions.args;\n\npublic enum PrimitiveType {\n\tBOOLEAN(\"Z\", \"boolean\", ArgType.object(\"java.lang.Boolean\")),\n\tCHAR(\"C\", \"char\", ArgType.object(\"java.lang.Character\")),\n\tBYTE(\"B\", \"byte\", ArgType.object(\"java.lang.Byte\")),\n\tSHORT(\"S\", \"short\", ArgType.object(\"java.lang.Short\")),\n\tINT(\"I\", \"int\", ArgType.object(\"java.lang.Integer\")),\n\tFLOAT(\"F\", \"float\", ArgType.object(\"java.lang.Float\")),\n\tLONG(\"J\", \"long\", ArgType.object(\"java.lang.Long\")),\n\tDOUBLE(\"D\", \"double\", ArgType.object(\"java.lang.Double\")),\n\tOBJECT(\"L\", \"OBJECT\", ArgType.OBJECT),\n\tARRAY(\"[\", \"ARRAY\", ArgType.OBJECT_ARRAY),\n\tVOID(\"V\", \"void\", ArgType.object(\"java.lang.Void\"));\n\n\tprivate final String shortName;\n\tprivate final String longName;\n\tprivate final ArgType boxType;\n\n\tPrimitiveType(String shortName, String longName, ArgType boxType) {\n\t\tthis.shortName = shortName;\n\t\tthis.longName = longName;\n\t\tthis.boxType = boxType;\n\t}\n\n\tpublic String getShortName() {\n\t\treturn shortName;\n\t}\n\n\tpublic String getLongName() {\n\t\treturn longName;\n\t}\n\n\tpublic ArgType getBoxType() {\n\t\treturn boxType;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn longName;\n\t}\n\n\tpublic boolean isObjectOrArray() {\n\t\treturn this == OBJECT || this == ARRAY;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java",
    "content": "package jadx.core.dex.instructions.args;\n\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class RegisterArg extends InsnArg implements Named {\n\tpublic static final String THIS_ARG_NAME = \"this\";\n\tpublic static final String SUPER_ARG_NAME = \"super\";\n\n\tprotected final int regNum;\n\t// not null after SSATransform pass\n\tprivate SSAVar sVar;\n\n\tpublic RegisterArg(int rn, ArgType type) {\n\t\tthis.type = type; // initial type, not changing, can be unknown\n\t\tthis.regNum = rn;\n\t}\n\n\tpublic int getRegNum() {\n\t\treturn regNum;\n\t}\n\n\t@Override\n\tpublic boolean isRegister() {\n\t\treturn true;\n\t}\n\n\tpublic ArgType getInitType() {\n\t\treturn type;\n\t}\n\n\t@Override\n\tpublic ArgType getType() {\n\t\tif (sVar != null) {\n\t\t\treturn sVar.getTypeInfo().getType();\n\t\t}\n\t\treturn ArgType.UNKNOWN;\n\t}\n\n\t@Override\n\tpublic void setType(ArgType newType) {\n\t\tif (sVar == null) {\n\t\t\tthrow new JadxRuntimeException(\"Can't change type for register without SSA variable: \" + this);\n\t\t}\n\t\tsVar.setType(newType);\n\t}\n\n\tpublic void forceSetInitType(ArgType type) {\n\t\tthis.type = type;\n\t}\n\n\t@Nullable\n\tpublic ArgType getImmutableType() {\n\t\tif (sVar != null) {\n\t\t\treturn sVar.getImmutableType();\n\t\t}\n\t\tif (contains(AFlag.IMMUTABLE_TYPE)) {\n\t\t\treturn type;\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isTypeImmutable() {\n\t\tif (sVar != null) {\n\t\t\treturn sVar.isTypeImmutable();\n\t\t}\n\t\treturn contains(AFlag.IMMUTABLE_TYPE);\n\t}\n\n\tpublic SSAVar getSVar() {\n\t\treturn sVar;\n\t}\n\n\tvoid setSVar(@NotNull SSAVar sVar) {\n\t\tthis.sVar = sVar;\n\t}\n\n\tpublic void resetSSAVar() {\n\t\tthis.sVar = null;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\tif (isSuper()) {\n\t\t\treturn SUPER_ARG_NAME;\n\t\t}\n\t\tif (isThis()) {\n\t\t\treturn THIS_ARG_NAME;\n\t\t}\n\t\tif (sVar == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn sVar.getName();\n\t}\n\n\tprivate boolean isSuper() {\n\t\treturn contains(AFlag.SUPER);\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\tif (sVar != null && name != null) {\n\t\t\tsVar.setName(name);\n\t\t}\n\t}\n\n\tpublic void setNameIfUnknown(String name) {\n\t\tif (getName() == null) {\n\t\t\tsetName(name);\n\t\t}\n\t}\n\n\tpublic boolean isNameEquals(InsnArg arg) {\n\t\tString n = getName();\n\t\tif (n == null || !(arg instanceof Named)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn n.equals(((Named) arg).getName());\n\t}\n\n\t@Override\n\tpublic RegisterArg duplicate() {\n\t\treturn duplicate(getRegNum(), getInitType(), sVar);\n\t}\n\n\tpublic RegisterArg duplicate(ArgType initType) {\n\t\treturn duplicate(getRegNum(), initType, sVar);\n\t}\n\n\tpublic RegisterArg duplicateWithNewSSAVar(MethodNode mth) {\n\t\tRegisterArg duplicate = duplicate(regNum, getInitType(), null);\n\t\tmth.makeNewSVar(duplicate);\n\t\treturn duplicate;\n\t}\n\n\tpublic RegisterArg duplicate(int regNum, @Nullable SSAVar sVar) {\n\t\treturn duplicate(regNum, getInitType(), sVar);\n\t}\n\n\tpublic RegisterArg duplicate(int regNum, ArgType initType, @Nullable SSAVar sVar) {\n\t\tRegisterArg dup = new RegisterArg(regNum, initType);\n\t\tif (sVar != null) {\n\t\t\t// only 'set' here, 'assign' or 'use' will binds later\n\t\t\tdup.setSVar(sVar);\n\t\t}\n\t\treturn copyCommonParams(dup);\n\t}\n\n\t@Nullable\n\tpublic InsnNode getAssignInsn() {\n\t\tif (sVar == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn sVar.getAssign().getParentInsn();\n\t}\n\n\tpublic boolean equalRegisterAndType(RegisterArg arg) {\n\t\treturn regNum == arg.regNum && type.equals(arg.type);\n\t}\n\n\tpublic boolean sameRegAndSVar(InsnArg arg) {\n\t\tif (this == arg) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!arg.isRegister()) {\n\t\t\treturn false;\n\t\t}\n\t\tRegisterArg reg = (RegisterArg) arg;\n\t\treturn regNum == reg.getRegNum()\n\t\t\t\t&& Objects.equals(sVar, reg.getSVar());\n\t}\n\n\tpublic boolean sameReg(InsnArg arg) {\n\t\tif (!arg.isRegister()) {\n\t\t\treturn false;\n\t\t}\n\t\treturn regNum == ((RegisterArg) arg).getRegNum();\n\t}\n\n\tpublic boolean sameType(InsnArg arg) {\n\t\treturn this.getType().equals(arg.getType());\n\t}\n\n\tpublic boolean sameCodeVar(RegisterArg arg) {\n\t\treturn this.getSVar().getCodeVar() == arg.getSVar().getCodeVar();\n\t}\n\n\tpublic boolean isLinkedToOtherSsaVars() {\n\t\treturn getSVar().getCodeVar().getSsaVars().size() > 1;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn regNum;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof RegisterArg)) {\n\t\t\treturn false;\n\t\t}\n\t\tRegisterArg other = (RegisterArg) obj;\n\t\treturn regNum == other.regNum\n\t\t\t\t&& Objects.equals(sVar, other.getSVar());\n\t}\n\n\t@Override\n\tpublic String toShortString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"r\").append(regNum);\n\t\tif (sVar != null) {\n\t\t\tsb.append('v').append(sVar.getVersion());\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"(r\").append(regNum);\n\t\tif (sVar != null) {\n\t\t\tsb.append('v').append(sVar.getVersion());\n\t\t}\n\t\tif (getName() != null) {\n\t\t\tsb.append(\" '\").append(getName()).append('\\'');\n\t\t}\n\t\tArgType type = sVar != null ? getType() : null;\n\t\tif (type != null) {\n\t\t\tsb.append(' ').append(type);\n\t\t}\n\t\tArgType initType = getInitType();\n\t\tif (type == null || (!type.equals(initType) && !type.isTypeKnown())) {\n\t\t\tsb.append(\" I:\").append(initType);\n\t\t}\n\t\tif (!isAttrStorageEmpty()) {\n\t\t\tsb.append(' ').append(getAttributesString());\n\t\t}\n\t\tsb.append(')');\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/args/SSAVar.java",
    "content": "package jadx.core.dex.instructions.args;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.Consts;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.RegDebugInfoAttr;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.PhiInsn;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.typeinference.TypeInfo;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class SSAVar implements Comparable<SSAVar> {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SSAVar.class);\n\n\tprivate static final Comparator<SSAVar> SSA_VAR_COMPARATOR =\n\t\t\tComparator.comparingInt(SSAVar::getRegNum).thenComparingInt(SSAVar::getVersion);\n\n\tprivate final int regNum;\n\tprivate final int version;\n\n\tprivate RegisterArg assign;\n\tprivate final List<RegisterArg> useList = new ArrayList<>(2);\n\tprivate List<PhiInsn> usedInPhi = null;\n\n\tprivate final TypeInfo typeInfo = new TypeInfo();\n\n\t@Nullable(\"Set in InitCodeVariables pass\")\n\tprivate CodeVar codeVar;\n\n\tpublic SSAVar(int regNum, int v, @NotNull RegisterArg assign) {\n\t\tthis.regNum = regNum;\n\t\tthis.version = v;\n\t\tthis.assign = assign;\n\n\t\tassign.setSVar(this);\n\t}\n\n\tpublic int getRegNum() {\n\t\treturn regNum;\n\t}\n\n\tpublic int getVersion() {\n\t\treturn version;\n\t}\n\n\tpublic @NotNull RegisterArg getAssign() {\n\t\treturn assign;\n\t}\n\n\tpublic @Nullable InsnNode getAssignInsn() {\n\t\treturn assign.getParentInsn();\n\t}\n\n\tpublic void setAssign(@NotNull RegisterArg assign) {\n\t\tRegisterArg oldAssign = this.assign;\n\t\tif (oldAssign == null) {\n\t\t\tthis.assign = assign;\n\t\t} else if (oldAssign != assign) {\n\t\t\toldAssign.resetSSAVar();\n\t\t\tthis.assign = assign;\n\t\t}\n\t}\n\n\tpublic List<RegisterArg> getUseList() {\n\t\treturn useList;\n\t}\n\n\tpublic int getUseCount() {\n\t\treturn useList.size();\n\t}\n\n\t@Nullable\n\tpublic ArgType getImmutableType() {\n\t\tif (isTypeImmutable()) {\n\t\t\treturn assign.getInitType();\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic boolean isTypeImmutable() {\n\t\treturn assign.contains(AFlag.IMMUTABLE_TYPE);\n\t}\n\n\tpublic void markAsImmutable(ArgType type) {\n\t\tassign.add(AFlag.IMMUTABLE_TYPE);\n\t\tArgType initType = assign.getInitType();\n\t\tif (!initType.equals(type)) {\n\t\t\tassign.forceSetInitType(type);\n\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\tLOG.debug(\"Update immutable type at var {} assign with type: {} previous type: {}\", this.toShortString(), type, initType);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void setType(ArgType type) {\n\t\tArgType imType = getImmutableType();\n\t\tif (imType != null && !imType.equals(type)) {\n\t\t\tthrow new JadxRuntimeException(\"Can't change immutable type \" + imType + \" to \" + type + \" for \" + this);\n\t\t}\n\t\tupdateType(type);\n\t}\n\n\tpublic void forceSetType(ArgType type) {\n\t\tupdateType(type);\n\t}\n\n\tprivate void updateType(ArgType type) {\n\t\ttypeInfo.setType(type);\n\t\tif (codeVar != null) {\n\t\t\tcodeVar.setType(type);\n\t\t}\n\t}\n\n\tpublic void use(RegisterArg arg) {\n\t\tif (arg.getSVar() != null) {\n\t\t\targ.getSVar().removeUse(arg);\n\t\t}\n\t\targ.setSVar(this);\n\t\tuseList.add(arg);\n\t}\n\n\tpublic void removeUse(RegisterArg arg) {\n\t\tuseList.removeIf(registerArg -> registerArg == arg);\n\t}\n\n\tpublic void addUsedInPhi(PhiInsn phiInsn) {\n\t\tif (usedInPhi == null) {\n\t\t\tusedInPhi = new ArrayList<>(1);\n\t\t}\n\t\tusedInPhi.add(phiInsn);\n\t}\n\n\tpublic void removeUsedInPhi(PhiInsn phiInsn) {\n\t\tif (usedInPhi != null) {\n\t\t\tusedInPhi.removeIf(insn -> insn == phiInsn);\n\t\t\tif (usedInPhi.isEmpty()) {\n\t\t\t\tusedInPhi = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void updateUsedInPhiList() {\n\t\tthis.usedInPhi = null;\n\t\tfor (RegisterArg reg : useList) {\n\t\t\tInsnNode parentInsn = reg.getParentInsn();\n\t\t\tif (parentInsn != null && parentInsn.getType() == InsnType.PHI) {\n\t\t\t\taddUsedInPhi((PhiInsn) parentInsn);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic PhiInsn getOnlyOneUseInPhi() {\n\t\tif (usedInPhi != null && usedInPhi.size() == 1) {\n\t\t\treturn usedInPhi.get(0);\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic List<PhiInsn> getUsedInPhi() {\n\t\tif (usedInPhi == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn usedInPhi;\n\t}\n\n\t/**\n\t * Concat assign PHI insn and usedInPhi\n\t */\n\tpublic List<PhiInsn> getPhiList() {\n\t\tInsnNode assignInsn = getAssign().getParentInsn();\n\t\tif (assignInsn != null && assignInsn.getType() == InsnType.PHI) {\n\t\t\tPhiInsn assignPhi = (PhiInsn) assignInsn;\n\t\t\tif (usedInPhi == null) {\n\t\t\t\treturn Collections.singletonList(assignPhi);\n\t\t\t}\n\t\t\tList<PhiInsn> list = new ArrayList<>(1 + usedInPhi.size());\n\t\t\tlist.add(assignPhi);\n\t\t\tlist.addAll(usedInPhi);\n\t\t\treturn list;\n\t\t}\n\t\tif (usedInPhi == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn usedInPhi;\n\t}\n\n\tpublic boolean isAssignInPhi() {\n\t\tInsnNode assignInsn = getAssignInsn();\n\t\treturn assignInsn != null && assignInsn.getType() == InsnType.PHI;\n\t}\n\n\tpublic boolean isUsedInPhi() {\n\t\treturn usedInPhi != null && !usedInPhi.isEmpty();\n\t}\n\n\tpublic void setName(String name) {\n\t\tif (name != null) {\n\t\t\tif (codeVar == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"CodeVar not initialized for name set in SSAVar: \" + this);\n\t\t\t}\n\t\t\tcodeVar.setName(name);\n\t\t}\n\t}\n\n\tpublic String getName() {\n\t\tif (codeVar == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn codeVar.getName();\n\t}\n\n\tpublic TypeInfo getTypeInfo() {\n\t\treturn typeInfo;\n\t}\n\n\t@NotNull\n\tpublic CodeVar getCodeVar() {\n\t\tif (codeVar == null) {\n\t\t\tthrow new JadxRuntimeException(\"Code variable not set in \" + this);\n\t\t}\n\t\treturn codeVar;\n\t}\n\n\tpublic void setCodeVar(@NotNull CodeVar codeVar) {\n\t\tthis.codeVar = codeVar;\n\t\tcodeVar.addSsaVar(this);\n\t\tArgType imType = getImmutableType();\n\t\tif (imType != null) {\n\t\t\tcodeVar.setType(imType);\n\t\t}\n\t}\n\n\tpublic void resetTypeAndCodeVar() {\n\t\tif (!isTypeImmutable()) {\n\t\t\tupdateType(ArgType.UNKNOWN);\n\t\t}\n\t\tthis.typeInfo.getBounds().clear();\n\t\tthis.codeVar = null;\n\t}\n\n\tpublic boolean isCodeVarSet() {\n\t\treturn codeVar != null;\n\t}\n\n\tpublic String getDetailedVarInfo(MethodNode mth) {\n\t\tSet<ArgType> types = new HashSet<>();\n\t\tSet<String> names = Collections.emptySet();\n\n\t\tList<RegisterArg> useArgs = new ArrayList<>(1 + useList.size());\n\t\tuseArgs.add(assign);\n\t\tuseArgs.addAll(useList);\n\n\t\tif (mth.contains(AType.LOCAL_VARS_DEBUG_INFO)) {\n\t\t\tnames = new HashSet<>();\n\t\t\tfor (RegisterArg arg : useArgs) {\n\t\t\t\tRegDebugInfoAttr debugInfoAttr = arg.get(AType.REG_DEBUG_INFO);\n\t\t\t\tif (debugInfoAttr != null) {\n\t\t\t\t\tnames.add(debugInfoAttr.getName());\n\t\t\t\t\ttypes.add(debugInfoAttr.getRegType());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (RegisterArg arg : useArgs) {\n\t\t\tArgType initType = arg.getInitType();\n\t\t\tif (initType.isTypeKnown()) {\n\t\t\t\ttypes.add(initType);\n\t\t\t}\n\t\t\tArgType type = arg.getType();\n\t\t\tif (type.isTypeKnown()) {\n\t\t\t\ttypes.add(type);\n\t\t\t}\n\t\t}\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append('r').append(regNum).append('v').append(version);\n\t\tif (!names.isEmpty()) {\n\t\t\tString orderedNames = names.stream()\n\t\t\t\t\t.sorted()\n\t\t\t\t\t.collect(Collectors.joining(\", \", \"[\", \"]\"));\n\n\t\t\tsb.append(\", names: \").append(orderedNames);\n\t\t}\n\t\tif (!types.isEmpty()) {\n\t\t\tString orderedTypes = types.stream()\n\t\t\t\t\t.map(String::valueOf)\n\t\t\t\t\t.sorted()\n\t\t\t\t\t.collect(Collectors.joining(\", \", \"[\", \"]\"));\n\n\t\t\tsb.append(\", types: \").append(orderedTypes);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof SSAVar)) {\n\t\t\treturn false;\n\t\t}\n\t\tSSAVar ssaVar = (SSAVar) o;\n\t\treturn regNum == ssaVar.regNum && version == ssaVar.version;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn 31 * regNum + version;\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull SSAVar o) {\n\t\treturn SSA_VAR_COMPARATOR.compare(this, o);\n\t}\n\n\tpublic String toShortString() {\n\t\treturn \"r\" + regNum + 'v' + version;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn toShortString()\n\t\t\t\t+ (StringUtils.notEmpty(getName()) ? \" '\" + getName() + \"' \" : \"\")\n\t\t\t\t+ ' ' + typeInfo.getType();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/args/Typed.java",
    "content": "package jadx.core.dex.instructions.args;\n\nimport jadx.core.dex.attributes.AttrNode;\n\npublic abstract class Typed extends AttrNode {\n\n\tprotected ArgType type;\n\n\tpublic ArgType getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setType(ArgType type) {\n\t\tthis.type = type;\n\t}\n\n\tpublic boolean isTypeImmutable() {\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/args/VarName.java",
    "content": "package jadx.core.dex.instructions.args;\n\npublic class VarName {\n\tprivate String name;\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn name;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/invokedynamic/CustomLambdaCall.java",
    "content": "package jadx.core.dex.instructions.invokedynamic;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.api.plugins.input.data.IMethodHandle;\nimport jadx.api.plugins.input.data.IMethodProto;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.input.data.MethodHandleType;\nimport jadx.api.plugins.input.data.annotations.EncodedType;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.InvokeCustomNode;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.InvokeType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.NamedArg;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class CustomLambdaCall {\n\n\t/**\n\t * Expect LambdaMetafactory.metafactory method\n\t */\n\tpublic static boolean isLambdaInvoke(List<EncodedValue> values) {\n\t\tif (values.size() < 6) {\n\t\t\treturn false;\n\t\t}\n\t\tEncodedValue mthRef = values.get(0);\n\t\tif (mthRef.getType() != EncodedType.ENCODED_METHOD_HANDLE) {\n\t\t\treturn false;\n\t\t}\n\t\tIMethodHandle methodHandle = (IMethodHandle) mthRef.getValue();\n\t\tif (methodHandle.getType() != MethodHandleType.INVOKE_STATIC) {\n\t\t\treturn false;\n\t\t}\n\t\tIMethodRef methodRef = methodHandle.getMethodRef();\n\t\tif (!methodRef.getParentClassType().equals(\"Ljava/lang/invoke/LambdaMetafactory;\")) {\n\t\t\treturn false;\n\t\t}\n\t\tString mthName = methodRef.getName();\n\t\treturn mthName.equals(\"metafactory\") || mthName.equals(\"altMetafactory\");\n\t}\n\n\tpublic static InvokeCustomNode buildLambdaMethodCall(MethodNode mth, InsnData insn, boolean isRange, List<EncodedValue> values) {\n\t\tIMethodHandle callMthHandle = (IMethodHandle) values.get(4).getValue();\n\t\tif (callMthHandle.getType().isField()) {\n\t\t\tthrow new JadxRuntimeException(\"Not yet supported\");\n\t\t}\n\t\tInvokeCustomNode resNode = buildMethodCall(mth, insn, isRange, values, callMthHandle);\n\t\tint resReg = insn.getResultReg();\n\t\tif (resReg != -1) {\n\t\t\tresNode.setResult(InsnArg.reg(resReg, mth.getReturnType()));\n\t\t}\n\t\treturn resNode;\n\t}\n\n\t@NotNull\n\tprivate static InvokeCustomNode buildMethodCall(MethodNode mth, InsnData insn, boolean isRange,\n\t\t\tList<EncodedValue> values, IMethodHandle callMthHandle) {\n\t\tRootNode root = mth.root();\n\t\tIMethodProto lambdaProto = (IMethodProto) values.get(2).getValue();\n\t\tMethodInfo lambdaInfo = MethodInfo.fromMethodProto(root, mth.getParentClass().getClassInfo(), \"\", lambdaProto);\n\n\t\tMethodHandleType methodHandleType = callMthHandle.getType();\n\t\tInvokeCustomNode invokeCustomNode = new InvokeCustomNode(lambdaInfo, insn, false, isRange);\n\t\tinvokeCustomNode.setHandleType(methodHandleType);\n\n\t\tClassInfo implCls = ClassInfo.fromType(root, lambdaInfo.getReturnType());\n\t\tString implName = (String) values.get(1).getValue();\n\t\tIMethodProto implProto = (IMethodProto) values.get(3).getValue();\n\t\tMethodInfo implMthInfo = MethodInfo.fromMethodProto(root, implCls, implName, implProto);\n\t\tinvokeCustomNode.setImplMthInfo(implMthInfo);\n\n\t\tMethodInfo callMthInfo = MethodInfo.fromRef(root, callMthHandle.getMethodRef());\n\t\tInvokeNode invokeNode = buildInvokeNode(methodHandleType, invokeCustomNode, callMthInfo);\n\n\t\tif (methodHandleType == MethodHandleType.INVOKE_CONSTRUCTOR) {\n\t\t\tConstructorInsn ctrInsn = new ConstructorInsn(mth, invokeNode);\n\t\t\tinvokeCustomNode.setCallInsn(ctrInsn);\n\t\t} else {\n\t\t\tinvokeCustomNode.setCallInsn(invokeNode);\n\t\t}\n\n\t\tMethodNode callMth = root.resolveMethod(callMthInfo);\n\t\tif (callMth != null) {\n\t\t\tinvokeCustomNode.getCallInsn().addAttr(callMth);\n\t\t\tif (callMth.getAccessFlags().isSynthetic()\n\t\t\t\t\t&& callMth.getParentClass().equals(mth.getParentClass())) {\n\t\t\t\t// inline only synthetic methods from same class\n\t\t\t\tcallMth.add(AFlag.DONT_GENERATE);\n\t\t\t\tinvokeCustomNode.setInlineInsn(true);\n\t\t\t}\n\t\t}\n\t\tif (!invokeCustomNode.isInlineInsn()) {\n\t\t\tIMethodProto effectiveMthProto = (IMethodProto) values.get(5).getValue();\n\t\t\tList<ArgType> args = Utils.collectionMap(effectiveMthProto.getArgTypes(), ArgType::parse);\n\t\t\tboolean sameArgs = args.equals(callMthInfo.getArgumentsTypes());\n\t\t\tinvokeCustomNode.setUseRef(sameArgs);\n\t\t}\n\n\t\t// prevent args inlining into not generated invoke custom node\n\t\tfor (InsnArg arg : invokeCustomNode.getArguments()) {\n\t\t\targ.add(AFlag.DONT_INLINE);\n\t\t}\n\t\treturn invokeCustomNode;\n\t}\n\n\t@NotNull\n\tprivate static InvokeNode buildInvokeNode(MethodHandleType methodHandleType, InvokeCustomNode invokeCustomNode,\n\t\t\tMethodInfo callMthInfo) {\n\t\tInvokeType invokeType = InvokeCustomUtils.convertInvokeType(methodHandleType);\n\t\tint callArgsCount = callMthInfo.getArgsCount();\n\t\tboolean instanceCall = invokeType != InvokeType.STATIC;\n\t\tif (instanceCall) {\n\t\t\tcallArgsCount++;\n\t\t}\n\t\tInvokeNode invokeNode = new InvokeNode(callMthInfo, invokeType, callArgsCount);\n\n\t\t// copy insn args\n\t\tint argsCount = invokeCustomNode.getArgsCount();\n\t\tfor (int i = 0; i < argsCount; i++) {\n\t\t\tInsnArg arg = invokeCustomNode.getArg(i);\n\t\t\tinvokeNode.addArg(arg.duplicate());\n\t\t}\n\t\tif (callArgsCount > argsCount) {\n\t\t\t// fill remaining args with NamedArg\n\t\t\tint callArgNum = argsCount;\n\t\t\tif (instanceCall) {\n\t\t\t\tcallArgNum--; // start from instance type\n\t\t\t}\n\t\t\tList<ArgType> callArgTypes = callMthInfo.getArgumentsTypes();\n\t\t\tfor (int i = argsCount; i < callArgsCount; i++) {\n\t\t\t\tArgType argType;\n\t\t\t\tif (callArgNum < 0) {\n\t\t\t\t\t// instance arg type\n\t\t\t\t\targType = callMthInfo.getDeclClass().getType();\n\t\t\t\t} else {\n\t\t\t\t\targType = callArgTypes.get(callArgNum++);\n\t\t\t\t}\n\t\t\t\tinvokeNode.addArg(new NamedArg(\"v\" + i, argType));\n\t\t\t}\n\t\t}\n\t\treturn invokeNode;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/invokedynamic/CustomRawCall.java",
    "content": "package jadx.core.dex.instructions.invokedynamic;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.IMethodHandle;\nimport jadx.api.plugins.input.data.IMethodProto;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.ConstStringNode;\nimport jadx.core.dex.instructions.InvokeCustomRawNode;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.InvokeType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.utils.EncodedValueUtils.buildLookupArg;\nimport static jadx.core.utils.EncodedValueUtils.convertToInsnArg;\n\n/**\n * Show `invoke-custom` similar to polymorphic call\n */\npublic class CustomRawCall {\n\n\tpublic static InsnNode build(MethodNode mth, InsnData insn, boolean isRange, List<EncodedValue> values) {\n\t\tIMethodHandle resolveHandle = (IMethodHandle) values.get(0).getValue();\n\t\tString invokeName = (String) values.get(1).getValue();\n\t\tIMethodProto invokeProto = (IMethodProto) values.get(2).getValue();\n\t\tList<InsnArg> resolveArgs = buildArgs(mth, values);\n\n\t\tif (resolveHandle.getType().isField()) {\n\t\t\tthrow new JadxRuntimeException(\"Field handle not yet supported\");\n\t\t}\n\n\t\tRootNode root = mth.root();\n\t\tMethodInfo resolveMth = MethodInfo.fromRef(root, resolveHandle.getMethodRef());\n\t\tInvokeType resolveInvokeType = InvokeCustomUtils.convertInvokeType(resolveHandle.getType());\n\t\tInvokeNode resolve = new InvokeNode(resolveMth, resolveInvokeType, resolveArgs.size());\n\t\tresolveArgs.forEach(resolve::addArg);\n\n\t\tClassInfo invokeCls = ClassInfo.fromType(root, ArgType.OBJECT); // type will be known at runtime\n\t\tMethodInfo invokeMth = MethodInfo.fromMethodProto(root, invokeCls, invokeName, invokeProto);\n\t\tInvokeCustomRawNode customRawNode = new InvokeCustomRawNode(resolve, invokeMth, insn, isRange);\n\t\tcustomRawNode.setCallSiteValues(values);\n\t\treturn customRawNode;\n\t}\n\n\tprivate static List<InsnArg> buildArgs(MethodNode mth, List<EncodedValue> values) {\n\t\tint valuesCount = values.size();\n\t\tList<InsnArg> list = new ArrayList<>(valuesCount);\n\t\tRootNode root = mth.root();\n\t\tlist.add(buildLookupArg(root)); // use `java.lang.invoke.MethodHandles.lookup()` as first arg\n\t\tfor (int i = 1; i < valuesCount; i++) {\n\t\t\tEncodedValue value = values.get(i);\n\t\t\ttry {\n\t\t\t\tlist.add(convertToInsnArg(root, value));\n\t\t\t} catch (Exception e) {\n\t\t\t\tmth.addWarnComment(\"Failed to build arg in invoke-custom insn: \" + value, e);\n\t\t\t\tlist.add(InsnArg.wrapArg(new ConstStringNode(value.toString())));\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/invokedynamic/CustomStringConcat.java",
    "content": "package jadx.core.dex.instructions.invokedynamic;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport jadx.api.plugins.input.data.IMethodHandle;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.input.data.MethodHandleType;\nimport jadx.api.plugins.input.data.annotations.EncodedType;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.JadxError;\nimport jadx.core.dex.instructions.ConstClassNode;\nimport jadx.core.dex.instructions.ConstStringNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.EncodedValueUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class CustomStringConcat {\n\n\tpublic static boolean isStringConcat(List<EncodedValue> values) {\n\t\tif (values.size() < 4) {\n\t\t\treturn false;\n\t\t}\n\t\tIMethodHandle methodHandle = (IMethodHandle) values.get(0).getValue();\n\t\tif (methodHandle.getType() != MethodHandleType.INVOKE_STATIC) {\n\t\t\treturn false;\n\t\t}\n\t\tIMethodRef methodRef = methodHandle.getMethodRef();\n\t\tif (!methodRef.getName().equals(\"makeConcatWithConstants\")) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!methodRef.getParentClassType().equals(\"Ljava/lang/invoke/StringConcatFactory;\")) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!Objects.equals(values.get(1).getValue(), \"makeConcatWithConstants\")) {\n\t\t\treturn false;\n\t\t}\n\t\tif (values.get(3).getType() != EncodedType.ENCODED_STRING) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static InsnNode buildStringConcat(InsnData insn, boolean isRange, List<EncodedValue> values) {\n\t\ttry {\n\t\t\tint argsCount = values.size() - 3 + insn.getRegsCount();\n\t\t\tInsnNode concat = new InsnNode(InsnType.STR_CONCAT, argsCount);\n\t\t\tString recipe = (String) values.get(3).getValue();\n\t\t\tprocessRecipe(recipe, concat, values, insn);\n\t\t\tint resReg = insn.getResultReg();\n\t\t\tif (resReg != -1) {\n\t\t\t\tconcat.setResult(InsnArg.reg(resReg, ArgType.STRING));\n\t\t\t}\n\t\t\treturn concat;\n\t\t} catch (Exception e) {\n\t\t\tInsnNode nop = new InsnNode(InsnType.NOP, 0);\n\t\t\tnop.add(AFlag.SYNTHETIC);\n\t\t\tnop.addAttr(AType.JADX_ERROR, new JadxError(\"Failed to process dynamic string concat: \" + e.getMessage(), e));\n\t\t\treturn nop;\n\t\t}\n\t}\n\n\tprivate static void processRecipe(String recipe, InsnNode concat, List<EncodedValue> values, InsnData insn) {\n\t\tint len = recipe.length();\n\t\tint offset = 0;\n\t\tint argNum = 0;\n\t\tint constNum = 4;\n\t\tStringBuilder sb = new StringBuilder(len);\n\t\twhile (offset < len) {\n\t\t\tint cp = recipe.codePointAt(offset);\n\t\t\toffset += Character.charCount(cp);\n\t\t\tboolean argTag = cp == 1;\n\t\t\tboolean constTag = cp == 2;\n\t\t\tif (argTag || constTag) {\n\t\t\t\tif (sb.length() != 0) {\n\t\t\t\t\tconcat.addArg(InsnArg.wrapArg(new ConstStringNode(sb.toString())));\n\t\t\t\t\tsb.setLength(0);\n\t\t\t\t}\n\t\t\t\tif (argTag) {\n\t\t\t\t\tconcat.addArg(InsnArg.reg(insn, argNum++, ArgType.UNKNOWN));\n\t\t\t\t} else {\n\t\t\t\t\tInsnArg constArg = buildInsnArgFromEncodedValue(values.get(constNum++));\n\t\t\t\t\tconcat.addArg(constArg);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsb.appendCodePoint(cp);\n\t\t\t}\n\t\t}\n\t\tif (sb.length() != 0) {\n\t\t\tconcat.addArg(InsnArg.wrapArg(new ConstStringNode(sb.toString())));\n\t\t}\n\t}\n\n\tprivate static InsnArg buildInsnArgFromEncodedValue(EncodedValue encodedValue) {\n\t\tObject value = EncodedValueUtils.convertToConstValue(encodedValue);\n\t\tif (value == null) {\n\t\t\treturn InsnArg.lit(0, ArgType.UNKNOWN);\n\t\t}\n\t\tif (value instanceof LiteralArg) {\n\t\t\treturn ((LiteralArg) value);\n\t\t}\n\t\tif (value instanceof ArgType) {\n\t\t\treturn InsnArg.wrapArg(new ConstClassNode((ArgType) value));\n\t\t}\n\t\tif (value instanceof String) {\n\t\t\treturn InsnArg.wrapArg(new ConstStringNode(((String) value)));\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Can't build insn arg from encoded value: \" + encodedValue);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/invokedynamic/InvokeCustomUtils.java",
    "content": "package jadx.core.dex.instructions.invokedynamic;\n\nimport jadx.api.plugins.input.data.MethodHandleType;\nimport jadx.core.dex.instructions.InvokeType;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class InvokeCustomUtils {\n\n\tpublic static InvokeType convertInvokeType(MethodHandleType type) {\n\t\tswitch (type) {\n\t\t\tcase INVOKE_STATIC:\n\t\t\t\treturn InvokeType.STATIC;\n\t\t\tcase INVOKE_INSTANCE:\n\t\t\t\treturn InvokeType.VIRTUAL;\n\t\t\tcase INVOKE_DIRECT:\n\t\t\tcase INVOKE_CONSTRUCTOR:\n\t\t\t\treturn InvokeType.DIRECT;\n\t\t\tcase INVOKE_INTERFACE:\n\t\t\t\treturn InvokeType.INTERFACE;\n\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unsupported method handle type: \" + type);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/java/JsrNode.java",
    "content": "package jadx.core.dex.instructions.java;\n\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.TargetInsnNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.InsnUtils;\n\npublic class JsrNode extends TargetInsnNode {\n\n\tprotected final int target;\n\n\tpublic JsrNode(int target) {\n\t\tthis(InsnType.JAVA_JSR, target, 0);\n\t}\n\n\tprotected JsrNode(InsnType type, int target, int argsCount) {\n\t\tsuper(type, argsCount);\n\t\tthis.target = target;\n\t}\n\n\tpublic int getTarget() {\n\t\treturn target;\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\treturn copyCommonParams(new JsrNode(target));\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn baseString() + \" -> \" + InsnUtils.formatOffset(target) + attributesString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java",
    "content": "package jadx.core.dex.instructions.mods;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.BaseInvokeNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic final class ConstructorInsn extends BaseInvokeNode {\n\n\tprivate final MethodInfo callMth;\n\tprivate final CallType callType;\n\n\tpublic enum CallType {\n\t\tCONSTRUCTOR, // just new instance\n\t\tSUPER, // super call\n\t\tTHIS, // call constructor from other constructor\n\t\tSELF // call itself\n\t}\n\n\tpublic ConstructorInsn(MethodNode mth, InvokeNode invoke) {\n\t\tthis(mth, invoke, invoke.getCallMth());\n\t}\n\n\tpublic ConstructorInsn(MethodNode mth, InvokeNode invoke, MethodInfo callMth) {\n\t\tsuper(InsnType.CONSTRUCTOR, invoke.getArgsCount() - 1);\n\t\tthis.callMth = callMth;\n\t\tthis.callType = getCallType(mth, callMth.getDeclClass(), invoke.getArg(0));\n\t\tint argsCount = invoke.getArgsCount();\n\t\tfor (int i = 1; i < argsCount; i++) {\n\t\t\taddArg(invoke.getArg(i));\n\t\t}\n\t}\n\n\tprivate CallType getCallType(MethodNode mth, ClassInfo classType, InsnArg instanceArg) {\n\t\tif (!instanceArg.isThis()) {\n\t\t\treturn CallType.CONSTRUCTOR;\n\t\t}\n\t\tif (!classType.equals(mth.getParentClass().getClassInfo())) {\n\t\t\treturn CallType.SUPER;\n\t\t}\n\t\tif (callMth.getShortId().equals(mth.getMethodInfo().getShortId())) {\n\t\t\t// self constructor\n\t\t\treturn CallType.SELF;\n\t\t}\n\t\treturn CallType.THIS;\n\t}\n\n\tpublic ConstructorInsn(MethodInfo callMth, CallType callType) {\n\t\tsuper(InsnType.CONSTRUCTOR, callMth.getArgsCount());\n\t\tthis.callMth = callMth;\n\t\tthis.callType = callType;\n\t}\n\n\t@Override\n\tpublic MethodInfo getCallMth() {\n\t\treturn callMth;\n\t}\n\n\t@Override\n\t@Nullable\n\tpublic RegisterArg getInstanceArg() {\n\t\treturn null;\n\t}\n\n\tpublic ClassInfo getClassType() {\n\t\treturn callMth.getDeclClass();\n\t}\n\n\tpublic CallType getCallType() {\n\t\treturn callType;\n\t}\n\n\tpublic boolean isNewInstance() {\n\t\treturn callType == CallType.CONSTRUCTOR;\n\t}\n\n\tpublic boolean isSuper() {\n\t\treturn callType == CallType.SUPER;\n\t}\n\n\tpublic boolean isThis() {\n\t\treturn callType == CallType.THIS;\n\t}\n\n\tpublic boolean isSelf() {\n\t\treturn callType == CallType.SELF;\n\t}\n\n\t@Override\n\tpublic boolean isStaticCall() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int getFirstArgOffset() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean isSame(InsnNode obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof ConstructorInsn) || !super.isSame(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tConstructorInsn other = (ConstructorInsn) obj;\n\t\treturn callMth.equals(other.callMth)\n\t\t\t\t&& callType == other.callType;\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\treturn copyCommonParams(new ConstructorInsn(callMth, callType));\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn super.toString() + \" call: \" + callMth + \" type: \" + callType;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/instructions/mods/TernaryInsn.java",
    "content": "package jadx.core.dex.instructions.mods;\n\nimport java.util.Collection;\nimport java.util.function.Consumer;\n\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.regions.conditions.IfCondition;\nimport jadx.core.utils.InsnUtils;\n\npublic final class TernaryInsn extends InsnNode {\n\n\tprivate IfCondition condition;\n\n\tpublic TernaryInsn(IfCondition condition, RegisterArg result, InsnArg th, InsnArg els) {\n\t\tthis();\n\t\tsetResult(result);\n\n\t\tif (th.isFalse() && els.isTrue()) {\n\t\t\t// inverted\n\t\t\tthis.condition = IfCondition.invert(condition);\n\t\t\taddArg(els);\n\t\t\taddArg(th);\n\t\t} else {\n\t\t\tthis.condition = condition;\n\t\t\taddArg(th);\n\t\t\taddArg(els);\n\t\t}\n\t\tvisitInsns(this::inheritMetadata);\n\t}\n\n\tprivate TernaryInsn() {\n\t\tsuper(InsnType.TERNARY, 2);\n\t}\n\n\tpublic IfCondition getCondition() {\n\t\treturn condition;\n\t}\n\n\tpublic void simplifyCondition() {\n\t\tcondition = IfCondition.simplify(condition);\n\t\tif (condition.getMode() == IfCondition.Mode.NOT) {\n\t\t\tinvert();\n\t\t}\n\t}\n\n\tprivate void invert() {\n\t\tcondition = IfCondition.invert(condition);\n\t\tInsnArg tmp = getArg(0);\n\t\tsetArg(0, getArg(1));\n\t\tsetArg(1, tmp);\n\t}\n\n\t@Override\n\tpublic void getRegisterArgs(Collection<RegisterArg> list) {\n\t\tsuper.getRegisterArgs(list);\n\t\tlist.addAll(condition.getRegisterArgs());\n\t}\n\n\t@Override\n\tpublic boolean replaceArg(InsnArg from, InsnArg to) {\n\t\tif (super.replaceArg(from, to)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn condition.replaceArg(from, to);\n\t}\n\n\tpublic void visitInsns(Consumer<InsnNode> visitor) {\n\t\tsuper.visitInsns(visitor);\n\t\tcondition.visitInsns(visitor);\n\t}\n\n\t@Override\n\tpublic boolean isSame(InsnNode obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof TernaryInsn) || !super.isSame(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tTernaryInsn that = (TernaryInsn) obj;\n\t\treturn condition.equals(that.condition);\n\t}\n\n\t@Override\n\tpublic InsnNode copy() {\n\t\tTernaryInsn copy = new TernaryInsn();\n\t\tcopy.condition = condition;\n\t\treturn copyCommonParams(copy);\n\t}\n\n\t@Override\n\tpublic void rebindArgs() {\n\t\tsuper.rebindArgs();\n\t\tfor (RegisterArg reg : condition.getRegisterArgs()) {\n\t\t\tInsnNode parentInsn = reg.getParentInsn();\n\t\t\tif (parentInsn != null) {\n\t\t\t\tparentInsn.rebindArgs();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn InsnUtils.formatOffset(offset) + \": TERNARY \"\n\t\t\t\t+ getResult() + \" = (\" + condition + \") ? \" + getArg(0) + \" : \" + getArg(1);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java",
    "content": "package jadx.core.dex.nodes;\n\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.List;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.AttrNode;\nimport jadx.core.dex.attributes.nodes.LoopInfo;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.EmptyBitSet;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.utils.Utils.lockList;\n\npublic final class BlockNode extends AttrNode implements IBlock, Comparable<BlockNode> {\n\n\t/**\n\t * Const ID\n\t */\n\tprivate final int cid;\n\n\t/**\n\t * Position in blocks list (easier to use BitSet)\n\t */\n\tprivate int pos;\n\n\t/**\n\t * Offset in methods bytecode\n\t */\n\tprivate final int startOffset;\n\n\tprivate final List<InsnNode> instructions = new ArrayList<>(2);\n\n\tprivate List<BlockNode> predecessors = new ArrayList<>(1);\n\tprivate List<BlockNode> successors = new ArrayList<>(1);\n\tprivate List<BlockNode> cleanSuccessors;\n\n\t/**\n\t * All dominators, excluding self\n\t */\n\tprivate BitSet doms = EmptyBitSet.EMPTY;\n\n\t/**\n\t * Post dominators, excluding self\n\t */\n\tprivate BitSet postDoms = EmptyBitSet.EMPTY;\n\n\t/**\n\t * Dominance frontier\n\t */\n\tprivate BitSet domFrontier;\n\n\t/**\n\t * Immediate dominator\n\t */\n\tprivate BlockNode idom;\n\n\t/**\n\t * Immediate post dominator\n\t */\n\tprivate BlockNode iPostDom;\n\n\t/**\n\t * Blocks on which dominates this block\n\t */\n\tprivate List<BlockNode> dominatesOn = new ArrayList<>(3);\n\n\tpublic BlockNode(int cid, int pos, int offset) {\n\t\tthis.cid = cid;\n\t\tthis.pos = pos;\n\t\tthis.startOffset = offset;\n\t}\n\n\tpublic int getCId() {\n\t\treturn cid;\n\t}\n\n\tvoid setPos(int id) {\n\t\tthis.pos = id;\n\t}\n\n\t/**\n\t * Deprecated. Use {@link #getPos()}.\n\t */\n\t@Deprecated\n\tpublic int getId() {\n\t\treturn pos;\n\t}\n\n\tpublic int getPos() {\n\t\treturn pos;\n\t}\n\n\tpublic List<BlockNode> getPredecessors() {\n\t\treturn predecessors;\n\t}\n\n\tpublic List<BlockNode> getSuccessors() {\n\t\treturn successors;\n\t}\n\n\tpublic List<BlockNode> getCleanSuccessors() {\n\t\treturn this.cleanSuccessors;\n\t}\n\n\tpublic void updateCleanSuccessors() {\n\t\tcleanSuccessors = cleanSuccessors(this);\n\t}\n\n\tpublic static void updateBlockPositions(List<BlockNode> blocks) {\n\t\tint count = blocks.size();\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tblocks.get(i).setPos(i);\n\t\t}\n\t}\n\n\tpublic void lock() {\n\t\ttry {\n\t\t\tList<BlockNode> successorsList = successors;\n\t\t\tsuccessors = lockList(successorsList);\n\t\t\tcleanSuccessors = successorsList == cleanSuccessors ? this.successors : lockList(cleanSuccessors);\n\t\t\tpredecessors = lockList(predecessors);\n\t\t\tdominatesOn = lockList(dominatesOn);\n\t\t\tif (domFrontier == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Dominance frontier not set for block: \" + this);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to lock block: \" + this, e);\n\t\t}\n\t}\n\n\t/**\n\t * Return all successor which are not exception handler or followed by loop back edge\n\t */\n\tprivate static List<BlockNode> cleanSuccessors(BlockNode block) {\n\t\tList<BlockNode> sucList = block.getSuccessors();\n\t\tif (sucList.isEmpty()) {\n\t\t\treturn sucList;\n\t\t}\n\t\tList<BlockNode> toRemove = new ArrayList<>(sucList.size());\n\t\tfor (BlockNode b : sucList) {\n\t\t\tif (BlockUtils.isExceptionHandlerPath(b)) {\n\t\t\t\ttoRemove.add(b);\n\t\t\t}\n\t\t}\n\t\tif (block.contains(AFlag.LOOP_END)) {\n\t\t\tList<LoopInfo> loops = block.getAll(AType.LOOP);\n\t\t\tfor (LoopInfo loop : loops) {\n\t\t\t\ttoRemove.add(loop.getStart());\n\t\t\t}\n\t\t}\n\t\tif (toRemove.isEmpty()) {\n\t\t\treturn sucList;\n\t\t}\n\t\tList<BlockNode> result = new ArrayList<>(sucList);\n\t\tresult.removeAll(toRemove);\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic List<InsnNode> getInstructions() {\n\t\treturn instructions;\n\t}\n\n\tpublic int getStartOffset() {\n\t\treturn startOffset;\n\t}\n\n\t/**\n\t * Check if 'block' dominated on this node\n\t */\n\tpublic boolean isDominator(BlockNode block) {\n\t\treturn doms.get(block.getPos());\n\t}\n\n\t/**\n\t * Dominators of this node (exclude itself)\n\t */\n\tpublic BitSet getDoms() {\n\t\treturn doms;\n\t}\n\n\tpublic void setDoms(BitSet doms) {\n\t\tthis.doms = doms;\n\t}\n\n\tpublic BitSet getPostDoms() {\n\t\treturn postDoms;\n\t}\n\n\tpublic void setPostDoms(BitSet postDoms) {\n\t\tthis.postDoms = postDoms;\n\t}\n\n\tpublic BitSet getDomFrontier() {\n\t\treturn domFrontier;\n\t}\n\n\tpublic void setDomFrontier(BitSet domFrontier) {\n\t\tthis.domFrontier = domFrontier;\n\t}\n\n\t/**\n\t * Immediate dominator\n\t */\n\tpublic BlockNode getIDom() {\n\t\treturn idom;\n\t}\n\n\tpublic void setIDom(BlockNode idom) {\n\t\tthis.idom = idom;\n\t}\n\n\tpublic BlockNode getIPostDom() {\n\t\treturn iPostDom;\n\t}\n\n\tpublic void setIPostDom(BlockNode iPostDom) {\n\t\tthis.iPostDom = iPostDom;\n\t}\n\n\tpublic List<BlockNode> getDominatesOn() {\n\t\treturn dominatesOn;\n\t}\n\n\tpublic void addDominatesOn(BlockNode block) {\n\t\tdominatesOn.add(block);\n\t}\n\n\tpublic boolean isSynthetic() {\n\t\treturn contains(AFlag.SYNTHETIC);\n\t}\n\n\tpublic boolean isReturnBlock() {\n\t\treturn contains(AFlag.RETURN);\n\t}\n\n\tpublic boolean isMthExitBlock() {\n\t\treturn contains(AFlag.MTH_EXIT_BLOCK);\n\t}\n\n\tpublic boolean isEmpty() {\n\t\treturn instructions.isEmpty();\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn cid;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof BlockNode)) {\n\t\t\treturn false;\n\t\t}\n\t\tBlockNode other = (BlockNode) obj;\n\t\treturn cid == other.cid;\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull BlockNode o) {\n\t\treturn Integer.compare(cid, o.cid);\n\t}\n\n\t@Override\n\tpublic String baseString() {\n\t\treturn Integer.toString(cid);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"B:\" + cid + ':' + InsnUtils.formatOffset(startOffset);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java",
    "content": "package jadx.core.dex.nodes;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.BiConsumer;\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.DecompilationMode;\nimport jadx.api.ICodeCache;\nimport jadx.api.ICodeInfo;\nimport jadx.api.JavaClass;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.api.impl.SimpleCodeWriter;\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.annotations.NodeDeclareRef;\nimport jadx.api.metadata.annotations.VarRef;\nimport jadx.api.plugins.input.data.IClassData;\nimport jadx.api.plugins.input.data.IFieldData;\nimport jadx.api.plugins.input.data.IMethodData;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationDefaultAttr;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationDefaultClassAttr;\nimport jadx.api.plugins.input.data.attributes.types.InnerClassesAttr;\nimport jadx.api.plugins.input.data.attributes.types.InnerClsInfo;\nimport jadx.api.plugins.input.data.attributes.types.SourceFileAttr;\nimport jadx.api.plugins.input.data.impl.ListConsumer;\nimport jadx.api.usage.IUsageInfoData;\nimport jadx.core.Consts;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.InlinedAttr;\nimport jadx.core.dex.attributes.nodes.NotificationAttrNode;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.info.AccessInfo.AFType;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.nodes.utils.TypeUtils;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.dex.nodes.ProcessState.LOADED;\nimport static jadx.core.dex.nodes.ProcessState.NOT_LOADED;\n\npublic class ClassNode extends NotificationAttrNode\n\t\timplements ILoadable, ICodeNode, IPackageUpdate, Comparable<ClassNode> {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ClassNode.class);\n\n\tprivate final RootNode root;\n\tprivate final IClassData clsData;\n\n\tprivate final ClassInfo clsInfo;\n\tprivate PackageNode packageNode;\n\tprivate AccessInfo accessFlags;\n\tprivate ArgType superClass;\n\tprivate List<ArgType> interfaces;\n\tprivate List<ArgType> generics = Collections.emptyList();\n\tprivate String inputFileName;\n\n\tprivate List<MethodNode> methods;\n\tprivate List<FieldNode> fields;\n\tprivate List<ClassNode> innerClasses = Collections.emptyList();\n\n\tprivate List<ClassNode> inlinedClasses = Collections.emptyList();\n\n\t// store smali\n\tprivate String smali;\n\t// store parent for inner classes or 'this' otherwise\n\tprivate ClassNode parentClass = this;\n\n\tprivate volatile ProcessState state = ProcessState.NOT_LOADED;\n\tprivate LoadStage loadStage = LoadStage.NONE;\n\n\t/**\n\t * Top level classes used in this class (only for top level classes, empty for inners)\n\t */\n\tprivate List<ClassNode> dependencies = Collections.emptyList();\n\t/**\n\t * Top level classes needed for code generation stage\n\t */\n\tprivate List<ClassNode> codegenDeps = Collections.emptyList();\n\t/**\n\t * Classes which uses this class\n\t */\n\tprivate List<ClassNode> useIn = Collections.emptyList();\n\t/**\n\t * Methods which uses this class (by instructions only, definition is excluded)\n\t */\n\tprivate List<MethodNode> useInMth = Collections.emptyList();\n\n\t// cache maps\n\tprivate Map<MethodInfo, MethodNode> mthInfoMap = Collections.emptyMap();\n\n\tprivate JavaClass javaNode;\n\n\tpublic ClassNode(RootNode root, IClassData cls) {\n\t\tthis.root = root;\n\t\tthis.clsInfo = ClassInfo.fromType(root, ArgType.object(cls.getType()));\n\t\tthis.packageNode = PackageNode.getForClass(root, clsInfo.getPackage(), this);\n\t\tthis.clsData = cls.copy();\n\t\tload(clsData, false);\n\t}\n\n\tprivate void load(IClassData cls, boolean reloading) {\n\t\ttry {\n\t\t\taddAttrs(cls.getAttributes());\n\t\t\tthis.accessFlags = new AccessInfo(getAccessFlags(cls), AFType.CLASS);\n\t\t\tthis.superClass = checkSuperType(cls);\n\t\t\tthis.interfaces = Utils.collectionMap(cls.getInterfacesTypes(), ArgType::object);\n\t\t\tsetInputFileName(cls.getInputFileName());\n\n\t\t\tListConsumer<IFieldData, FieldNode> fieldsConsumer = new ListConsumer<>(fld -> FieldNode.build(this, fld));\n\t\t\tListConsumer<IMethodData, MethodNode> methodsConsumer = new ListConsumer<>(mth -> MethodNode.build(this, mth));\n\t\t\tcls.visitFieldsAndMethods(fieldsConsumer, methodsConsumer);\n\t\t\tthis.fields = fieldsConsumer.getResult();\n\t\t\tthis.methods = methodsConsumer.getResult();\n\t\t\tif (reloading) {\n\t\t\t\trestoreUsageData();\n\t\t\t}\n\t\t\tinitStaticValues(fields);\n\t\t\tprocessAttributes(this);\n\t\t\tprocessSpecialClasses(this);\n\t\t\tbuildCache();\n\n\t\t\t// TODO: implement module attribute parsing\n\t\t\tif (this.accessFlags.isModuleInfo()) {\n\t\t\t\tthis.addWarnComment(\"Modules not supported yet\");\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Error decode class: \" + clsInfo, e);\n\t\t}\n\t}\n\n\tprivate void restoreUsageData() {\n\t\tIUsageInfoData usageInfoData = root.getArgs().getUsageInfoCache().get(root);\n\t\tif (usageInfoData != null) {\n\t\t\tusageInfoData.applyForClass(this);\n\t\t} else {\n\t\t\tLOG.warn(\"Can't restore usage data for class: {}\", this);\n\t\t}\n\t}\n\n\tprivate ArgType checkSuperType(IClassData cls) {\n\t\tString superType = cls.getSuperType();\n\t\tif (superType == null) {\n\t\t\tif (clsInfo.getType().getObject().equals(Consts.CLASS_OBJECT)) {\n\t\t\t\t// java.lang.Object don't have super class\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (this.accessFlags.isModuleInfo()) {\n\t\t\t\t// module-info also don't have super class\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tthrow new JadxRuntimeException(\"No super class in \" + clsInfo.getType());\n\t\t}\n\t\treturn ArgType.object(superType);\n\t}\n\n\tpublic void updateGenericClsData(List<ArgType> generics, ArgType superClass, List<ArgType> interfaces) {\n\t\tthis.generics = generics;\n\t\tthis.superClass = superClass;\n\t\tthis.interfaces = interfaces;\n\t}\n\n\tprivate static void processSpecialClasses(ClassNode cls) {\n\t\tif (cls.getName().equals(\"package-info\") && cls.getFields().isEmpty() && cls.getMethods().isEmpty()) {\n\t\t\tcls.add(AFlag.PACKAGE_INFO);\n\t\t\tcls.add(AFlag.DONT_RENAME);\n\t\t}\n\t}\n\n\tprivate static void processAttributes(ClassNode cls) {\n\t\t// move AnnotationDefault from cls to methods (dex specific)\n\t\tAnnotationDefaultClassAttr defAttr = cls.get(JadxAttrType.ANNOTATION_DEFAULT_CLASS);\n\t\tif (defAttr != null) {\n\t\t\tcls.remove(JadxAttrType.ANNOTATION_DEFAULT_CLASS);\n\t\t\tfor (Map.Entry<String, EncodedValue> entry : defAttr.getValues().entrySet()) {\n\t\t\t\tMethodNode mth = cls.searchMethodByShortName(entry.getKey());\n\t\t\t\tif (mth != null) {\n\t\t\t\t\tmth.addAttr(new AnnotationDefaultAttr(entry.getValue()));\n\t\t\t\t} else {\n\t\t\t\t\tcls.addWarnComment(\"Method from annotation default annotation not found: \" + entry.getKey());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// check source file attribute\n\t\tif (!cls.checkSourceFilenameAttr()) {\n\t\t\tcls.remove(JadxAttrType.SOURCE_FILE);\n\t\t}\n\t}\n\n\tprivate int getAccessFlags(IClassData cls) {\n\t\tInnerClassesAttr innerClassesAttr = get(JadxAttrType.INNER_CLASSES);\n\t\tif (innerClassesAttr != null) {\n\t\t\tInnerClsInfo innerClsInfo = innerClassesAttr.getMap().get(cls.getType());\n\t\t\tif (innerClsInfo != null) {\n\t\t\t\treturn innerClsInfo.getAccessFlags();\n\t\t\t}\n\t\t}\n\t\treturn cls.getAccessFlags();\n\t}\n\n\tpublic static ClassNode addSyntheticClass(RootNode root, String name, int accessFlags) {\n\t\tClassInfo clsInfo = ClassInfo.fromName(root, name);\n\t\tClassNode existCls = root.resolveClass(clsInfo);\n\t\tif (existCls != null) {\n\t\t\tthrow new JadxRuntimeException(\"Class already exist: \" + name);\n\t\t}\n\t\treturn addSyntheticClass(root, clsInfo, accessFlags);\n\t}\n\n\tpublic static ClassNode addSyntheticClass(RootNode root, ClassInfo clsInfo, int accessFlags) {\n\t\tClassNode cls = new ClassNode(root, clsInfo, accessFlags);\n\t\tcls.add(AFlag.SYNTHETIC);\n\t\tcls.setInputFileName(\"synthetic\");\n\t\tcls.setState(ProcessState.PROCESS_COMPLETE);\n\t\troot.addClassNode(cls);\n\t\treturn cls;\n\t}\n\n\t// Create empty class\n\tprivate ClassNode(RootNode root, ClassInfo clsInfo, int accessFlags) {\n\t\tthis.root = root;\n\t\tthis.clsData = null;\n\t\tthis.clsInfo = clsInfo;\n\t\tthis.interfaces = new ArrayList<>();\n\t\tthis.methods = new ArrayList<>();\n\t\tthis.fields = new ArrayList<>();\n\t\tthis.accessFlags = new AccessInfo(accessFlags, AFType.CLASS);\n\t\tthis.packageNode = PackageNode.getForClass(root, clsInfo.getPackage(), this);\n\t}\n\n\tprivate void initStaticValues(List<FieldNode> fields) {\n\t\tif (fields.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\t// bytecode can omit field initialization to 0 (of any type)\n\t\t// add explicit init to all static final fields\n\t\t// incorrect initializations will be removed if assign found in class init\n\t\tfor (FieldNode fld : fields) {\n\t\t\tAccessInfo accFlags = fld.getAccessFlags();\n\t\t\tif (accFlags.isStatic() && accFlags.isFinal() && fld.get(JadxAttrType.CONSTANT_VALUE) == null) {\n\t\t\t\tfld.addAttr(EncodedValue.NULL);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate boolean checkSourceFilenameAttr() {\n\t\tSourceFileAttr sourceFileAttr = get(JadxAttrType.SOURCE_FILE);\n\t\tif (sourceFileAttr == null) {\n\t\t\treturn true;\n\t\t}\n\t\tString fileName = sourceFileAttr.getFileName();\n\t\tif (fileName.endsWith(\".java\")) {\n\t\t\tfileName = fileName.substring(0, fileName.length() - 5);\n\t\t}\n\t\tif (fileName.isEmpty() || fileName.equals(\"SourceFile\")) {\n\t\t\treturn false;\n\t\t}\n\t\tif (clsInfo != null) {\n\t\t\tString name = clsInfo.getShortName();\n\t\t\tif (fileName.equals(name)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tClassInfo parentCls = clsInfo.getParentClass();\n\t\t\twhile (parentCls != null) {\n\t\t\t\tString parentName = parentCls.getShortName();\n\t\t\t\tif (parentName.equals(fileName) || parentName.startsWith(fileName + '$')) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tparentCls = parentCls.getParentClass();\n\t\t\t}\n\t\t\tif (fileName.contains(\"$\") && fileName.endsWith('$' + name)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (name.contains(\"$\") && name.startsWith(fileName)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic boolean checkProcessed() {\n\t\treturn getTopParentClass().getState().isProcessComplete();\n\t}\n\n\tpublic void ensureProcessed() {\n\t\tif (!checkProcessed()) {\n\t\t\tClassNode topParentClass = getTopParentClass();\n\t\t\tthrow new JadxRuntimeException(\"Expected class to be processed at this point,\"\n\t\t\t\t\t+ \" class: \" + topParentClass + \", state: \" + topParentClass.getState());\n\t\t}\n\t}\n\n\tpublic ICodeInfo decompile() {\n\t\treturn decompile(true);\n\t}\n\n\tprivate static final Object DECOMPILE_WITH_MODE_SYNC = new Object();\n\n\t/**\n\t * WARNING: Slow operation! Use with caution!\n\t */\n\tpublic ICodeInfo decompileWithMode(DecompilationMode mode) {\n\t\tswitch (mode) {\n\t\t\tcase AUTO:\n\t\t\tcase RESTRUCTURE:\n\t\t\t\treturn decompile(true);\n\n\t\t\tcase SIMPLE:\n\t\t\tcase FALLBACK:\n\t\t\t\tsynchronized (DECOMPILE_WITH_MODE_SYNC) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tunload();\n\t\t\t\t\t\tICodeInfo code = root.getProcessClasses().forceGenerateCodeForMode(this, mode);\n\t\t\t\t\t\treturn Utils.getOrElse(code, ICodeInfo.EMPTY);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tunload();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unknown mode: \" + mode);\n\t\t}\n\t}\n\n\tpublic ICodeInfo getCode() {\n\t\treturn decompile(true);\n\t}\n\n\tpublic ICodeInfo reloadCode() {\n\t\tadd(AFlag.CLASS_DEEP_RELOAD);\n\t\treturn decompile(false);\n\t}\n\n\tpublic void unloadCode() {\n\t\tif (state == NOT_LOADED) {\n\t\t\treturn;\n\t\t}\n\t\tadd(AFlag.CLASS_UNLOADED);\n\t\tunloadFromCache();\n\t\tdeepUnload();\n\t}\n\n\tpublic void deepUnload() {\n\t\tif (clsData == null) {\n\t\t\t// manually added class\n\t\t\treturn;\n\t\t}\n\t\tclearAttributes();\n\t\tunload();\n\t\troot().getConstValues().removeForClass(this);\n\t\tload(clsData, true);\n\n\t\tinnerClasses.forEach(ClassNode::deepUnload);\n\t}\n\n\tpublic void unloadFromCache() {\n\t\tif (isInner()) {\n\t\t\treturn;\n\t\t}\n\t\tICodeCache codeCache = root().getCodeCache();\n\t\tcodeCache.remove(getRawName());\n\t}\n\n\tprivate synchronized ICodeInfo decompile(boolean searchInCache) {\n\t\tif (isInner()) {\n\t\t\treturn ICodeInfo.EMPTY;\n\t\t}\n\t\tICodeCache codeCache = root().getCodeCache();\n\t\tString clsRawName = getRawName();\n\t\tif (searchInCache) {\n\t\t\tICodeInfo code = codeCache.get(clsRawName);\n\t\t\tif (code != ICodeInfo.EMPTY) {\n\t\t\t\treturn code;\n\t\t\t}\n\t\t}\n\t\tICodeInfo codeInfo = generateClassCode();\n\t\tif (codeInfo != ICodeInfo.EMPTY) {\n\t\t\tcodeCache.add(clsRawName, codeInfo);\n\t\t}\n\t\treturn codeInfo;\n\t}\n\n\tprivate ICodeInfo generateClassCode() {\n\t\ttry {\n\t\t\tif (Consts.DEBUG) {\n\t\t\t\tLOG.debug(\"Decompiling class: {}\", this);\n\t\t\t}\n\t\t\tICodeInfo codeInfo = root.getProcessClasses().generateCode(this);\n\t\t\tprocessDefinitionAnnotations(codeInfo);\n\t\t\treturn codeInfo;\n\t\t} catch (StackOverflowError | Exception e) {\n\t\t\taddError(\"Code generation failed\", e);\n\t\t\treturn new SimpleCodeInfo(Utils.getStackTrace(e));\n\t\t}\n\t}\n\n\t/**\n\t * Save node definition positions found in code\n\t */\n\tprivate static void processDefinitionAnnotations(ICodeInfo codeInfo) {\n\t\tMap<Integer, ICodeAnnotation> annotations = codeInfo.getCodeMetadata().getAsMap();\n\t\tif (annotations.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (Map.Entry<Integer, ICodeAnnotation> entry : annotations.entrySet()) {\n\t\t\tICodeAnnotation ann = entry.getValue();\n\t\t\tif (ann.getAnnType() == AnnType.DECLARATION) {\n\t\t\t\tNodeDeclareRef declareRef = (NodeDeclareRef) ann;\n\t\t\t\tint pos = entry.getKey();\n\t\t\t\tdeclareRef.setDefPos(pos);\n\t\t\t\tdeclareRef.getNode().setDefPosition(pos);\n\t\t\t}\n\t\t}\n\t\t// validate var refs\n\t\tannotations.values().removeIf(v -> {\n\t\t\tif (v.getAnnType() == ICodeAnnotation.AnnType.VAR_REF) {\n\t\t\t\tVarRef varRef = (VarRef) v;\n\t\t\t\tif (varRef.getRefPos() == 0) {\n\t\t\t\t\tif (LOG.isDebugEnabled()) {\n\t\t\t\t\t\tLOG.debug(\"Var reference '{}' incorrect (ref pos is zero) and was removed from metadata\", varRef);\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn false;\n\t\t});\n\t}\n\n\t@Nullable\n\tpublic ICodeInfo getCodeFromCache() {\n\t\tICodeCache codeCache = root().getCodeCache();\n\t\tString clsRawName = getRawName();\n\t\tICodeInfo codeInfo = codeCache.get(clsRawName);\n\t\tif (codeInfo == ICodeInfo.EMPTY) {\n\t\t\treturn null;\n\t\t}\n\t\treturn codeInfo;\n\t}\n\n\t@Override\n\tpublic void load() {\n\t\tfor (MethodNode mth : getMethods()) {\n\t\t\ttry {\n\t\t\t\tmth.load();\n\t\t\t} catch (Exception e) {\n\t\t\t\tmth.addError(\"Method load error\", e);\n\t\t\t}\n\t\t}\n\t\tfor (ClassNode innerCls : getInnerClasses()) {\n\t\t\tinnerCls.load();\n\t\t}\n\t\tsetState(LOADED);\n\t}\n\n\t@Override\n\tpublic void unload() {\n\t\tif (state == NOT_LOADED) {\n\t\t\treturn;\n\t\t}\n\t\tsynchronized (clsInfo) { // decompilation sync\n\t\t\tmethods.forEach(MethodNode::unload);\n\t\t\tinnerClasses.forEach(ClassNode::unload);\n\t\t\tfields.forEach(FieldNode::unload);\n\t\t\tunloadAttributes();\n\t\t\tsetState(NOT_LOADED);\n\t\t\tthis.loadStage = LoadStage.NONE;\n\t\t\tthis.smali = null;\n\t\t}\n\t}\n\n\tprivate void buildCache() {\n\t\tmthInfoMap = new HashMap<>(methods.size());\n\t\tfor (MethodNode mth : methods) {\n\t\t\tmthInfoMap.put(mth.getMethodInfo(), mth);\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic ArgType getSuperClass() {\n\t\treturn superClass;\n\t}\n\n\tpublic List<ArgType> getInterfaces() {\n\t\treturn interfaces;\n\t}\n\n\tpublic List<ArgType> getGenericTypeParameters() {\n\t\treturn generics;\n\t}\n\n\tpublic ArgType getType() {\n\t\tArgType clsType = clsInfo.getType();\n\t\tif (Utils.notEmpty(generics)) {\n\t\t\treturn ArgType.generic(clsType, generics);\n\t\t}\n\t\treturn clsType;\n\t}\n\n\tpublic List<MethodNode> getMethods() {\n\t\treturn methods;\n\t}\n\n\tpublic List<FieldNode> getFields() {\n\t\treturn fields;\n\t}\n\n\tpublic void addField(FieldNode fld) {\n\t\tif (fields == null || fields.isEmpty()) {\n\t\t\tfields = new ArrayList<>(1);\n\t\t}\n\t\tfields.add(fld);\n\t}\n\n\tpublic @Nullable IFieldInfoRef getConstField(Object obj) {\n\t\treturn getConstField(obj, true);\n\t}\n\n\tpublic @Nullable IFieldInfoRef getConstField(Object obj, boolean searchGlobal) {\n\t\treturn root().getConstValues().getConstField(this, obj, searchGlobal);\n\t}\n\n\tpublic @Nullable IFieldInfoRef getConstFieldByLiteralArg(LiteralArg arg) {\n\t\treturn root().getConstValues().getConstFieldByLiteralArg(this, arg);\n\t}\n\n\tpublic FieldNode searchField(FieldInfo field) {\n\t\tfor (FieldNode f : fields) {\n\t\t\tif (f.getFieldInfo().equals(field)) {\n\t\t\t\treturn f;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic FieldNode searchFieldByNameAndType(FieldInfo field) {\n\t\tfor (FieldNode f : fields) {\n\t\t\tif (f.getFieldInfo().equalsNameAndType(field)) {\n\t\t\t\treturn f;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic FieldNode searchFieldByName(String name) {\n\t\tfor (FieldNode f : fields) {\n\t\t\tif (f.getName().equals(name)) {\n\t\t\t\treturn f;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic FieldNode searchFieldByShortId(String shortId) {\n\t\tfor (FieldNode f : fields) {\n\t\t\tif (f.getFieldInfo().getShortId().equals(shortId)) {\n\t\t\t\treturn f;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic MethodNode searchMethod(MethodInfo mth) {\n\t\treturn mthInfoMap.get(mth);\n\t}\n\n\tpublic MethodNode searchMethodByShortId(String shortId) {\n\t\tfor (MethodNode m : methods) {\n\t\t\tif (m.getMethodInfo().getShortId().equals(shortId)) {\n\t\t\t\treturn m;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Return first method by original short name\n\t * Note: methods are not unique by name (class can have several methods with same name but different\n\t * signature)\n\t */\n\t@Nullable\n\tpublic MethodNode searchMethodByShortName(String name) {\n\t\tfor (MethodNode m : methods) {\n\t\t\tif (m.getMethodInfo().getName().equals(name)) {\n\t\t\t\treturn m;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic ClassNode getDeclaringClass() {\n\t\treturn isInner() ? parentClass : null;\n\t}\n\n\tpublic ClassNode getParentClass() {\n\t\treturn parentClass;\n\t}\n\n\tpublic void notInner() {\n\t\tthis.clsInfo.notInner(root);\n\t\tthis.parentClass = this;\n\t}\n\n\t/**\n\t * Change class name and package (if full name provided)\n\t * Leading dot can be used to move to default package.\n\t * Package for inner classes can't be changed.\n\t */\n\t@Override\n\tpublic void rename(String newName) {\n\t\tif (newName.indexOf('.') == -1) {\n\t\t\tclsInfo.changeShortName(newName);\n\t\t\treturn;\n\t\t}\n\t\t// full name provided\n\t\tClassInfo newClsInfo = ClassInfo.fromNameWithoutCache(root, newName, clsInfo.isInner());\n\t\t// change class package\n\t\tString newPkg = newClsInfo.getPackage();\n\t\tString newShortName = newClsInfo.getShortName();\n\t\tif (clsInfo.isInner()) {\n\t\t\tif (!newPkg.equals(clsInfo.getPackage())) {\n\t\t\t\taddWarn(\"Can't change package for inner class: \" + this + \" to \" + newName);\n\t\t\t}\n\t\t\tclsInfo.changeShortName(newShortName);\n\t\t} else {\n\t\t\tif (changeClassNodePackage(newPkg)) {\n\t\t\t\tclsInfo.changePkgAndName(newPkg, newShortName);\n\t\t\t} else {\n\t\t\t\tclsInfo.changeShortName(newShortName);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate boolean changeClassNodePackage(String fullPkg) {\n\t\tif (fullPkg.equals(clsInfo.getAliasPkg())) {\n\t\t\treturn false;\n\t\t}\n\t\tif (clsInfo.isInner()) {\n\t\t\tthrow new JadxRuntimeException(\"Can't change package for inner class: \" + clsInfo);\n\t\t}\n\t\troot.removeClsFromPackage(packageNode, this);\n\t\tpackageNode = PackageNode.getForClass(root, fullPkg, this);\n\t\troot.sortPackages();\n\t\treturn true;\n\t}\n\n\tpublic void removeAlias() {\n\t\tif (!clsInfo.isInner()) {\n\t\t\tchangeClassNodePackage(clsInfo.getPackage());\n\t\t}\n\t\tclsInfo.removeAlias();\n\t}\n\n\t@Override\n\tpublic void onParentPackageUpdate(PackageNode updatedPkg) {\n\t\tif (clsInfo.isInner()) {\n\t\t\treturn;\n\t\t}\n\t\tclsInfo.changePkg(packageNode.getAliasPkgInfo().getFullName());\n\t}\n\n\tpublic PackageNode getPackageNode() {\n\t\treturn packageNode;\n\t}\n\n\tpublic ClassNode getTopParentClass() {\n\t\tClassNode parent = getParentClass();\n\t\treturn parent == this ? this : parent.getTopParentClass();\n\t}\n\n\tpublic void visitParentClasses(Consumer<ClassNode> consumer) {\n\t\tClassNode currentCls = this;\n\t\tClassNode parentCls = currentCls.getParentClass();\n\t\twhile (parentCls != currentCls) {\n\t\t\tconsumer.accept(parentCls);\n\t\t\tcurrentCls = parentCls;\n\t\t\tparentCls = currentCls.getParentClass();\n\t\t}\n\t}\n\n\tpublic void visitSuperTypes(BiConsumer<ArgType, ArgType> consumer) {\n\t\tTypeUtils typeUtils = root.getTypeUtils();\n\t\tArgType thisType = this.getType();\n\t\tif (!superClass.equals(ArgType.OBJECT)) {\n\t\t\tconsumer.accept(thisType, superClass);\n\t\t\ttypeUtils.visitSuperTypes(superClass, consumer);\n\t\t}\n\t\tfor (ArgType iface : interfaces) {\n\t\t\tconsumer.accept(thisType, iface);\n\t\t\ttypeUtils.visitSuperTypes(iface, consumer);\n\t\t}\n\t}\n\n\tpublic boolean hasNotGeneratedParent() {\n\t\tif (contains(AFlag.DONT_GENERATE)) {\n\t\t\treturn true;\n\t\t}\n\t\tClassNode parent = getParentClass();\n\t\tif (parent == this) {\n\t\t\treturn false;\n\t\t}\n\t\treturn parent.hasNotGeneratedParent();\n\t}\n\n\tpublic List<ClassNode> getInnerClasses() {\n\t\treturn innerClasses;\n\t}\n\n\tpublic List<ClassNode> getInlinedClasses() {\n\t\treturn inlinedClasses;\n\t}\n\n\t/**\n\t * Get all inner and inlined classes recursively\n\t *\n\t * @param resultClassesSet all identified inner and inlined classes are added to this set\n\t */\n\tpublic void getInnerAndInlinedClassesRecursive(Set<ClassNode> resultClassesSet) {\n\t\tfor (ClassNode innerCls : innerClasses) {\n\t\t\tif (resultClassesSet.add(innerCls)) {\n\t\t\t\tinnerCls.getInnerAndInlinedClassesRecursive(resultClassesSet);\n\t\t\t}\n\t\t}\n\t\tfor (ClassNode inlinedCls : inlinedClasses) {\n\t\t\tif (resultClassesSet.add(inlinedCls)) {\n\t\t\t\tinlinedCls.getInnerAndInlinedClassesRecursive(resultClassesSet);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void addInnerClass(ClassNode cls) {\n\t\tif (innerClasses.isEmpty()) {\n\t\t\tinnerClasses = new ArrayList<>(5);\n\t\t}\n\t\tinnerClasses.add(cls);\n\t\tcls.parentClass = this;\n\t}\n\n\tpublic void addInlinedClass(ClassNode cls) {\n\t\tif (inlinedClasses.isEmpty()) {\n\t\t\tinlinedClasses = new ArrayList<>(5);\n\t\t}\n\t\tcls.addAttr(new InlinedAttr(this));\n\t\tinlinedClasses.add(cls);\n\t}\n\n\tpublic boolean isEnum() {\n\t\treturn getAccessFlags().isEnum()\n\t\t\t\t&& getSuperClass() != null\n\t\t\t\t&& getSuperClass().getObject().equals(ArgType.ENUM.getObject());\n\t}\n\n\tpublic boolean isAnonymous() {\n\t\treturn contains(AType.ANONYMOUS_CLASS);\n\t}\n\n\tpublic boolean isSynthetic() {\n\t\treturn contains(AFlag.SYNTHETIC);\n\t}\n\n\tpublic boolean isInner() {\n\t\treturn parentClass != this;\n\t}\n\n\tpublic boolean isTopClass() {\n\t\treturn parentClass == this;\n\t}\n\n\t@Nullable\n\tpublic MethodNode getClassInitMth() {\n\t\treturn searchMethodByShortId(\"<clinit>()V\");\n\t}\n\n\t@Nullable\n\tpublic MethodNode getDefaultConstructor() {\n\t\tfor (MethodNode mth : methods) {\n\t\t\tif (mth.isDefaultConstructor()) {\n\t\t\t\treturn mth;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic AccessInfo getAccessFlags() {\n\t\treturn accessFlags;\n\t}\n\n\t@Override\n\tpublic void setAccessFlags(AccessInfo accessFlags) {\n\t\tthis.accessFlags = accessFlags;\n\t}\n\n\t@Override\n\tpublic RootNode root() {\n\t\treturn root;\n\t}\n\n\t@Override\n\tpublic String typeName() {\n\t\treturn \"class\";\n\t}\n\n\tpublic String getRawName() {\n\t\treturn clsInfo.getRawName();\n\t}\n\n\t/**\n\t * Internal class info (don't use in code generation and external api).\n\t */\n\tpublic ClassInfo getClassInfo() {\n\t\treturn clsInfo;\n\t}\n\n\tpublic String getName() {\n\t\treturn clsInfo.getShortName();\n\t}\n\n\tpublic String getAlias() {\n\t\treturn clsInfo.getAliasShortName();\n\t}\n\n\t/**\n\t * Deprecated. Use {@link #getAlias()}\n\t */\n\t@Deprecated\n\tpublic String getShortName() {\n\t\treturn clsInfo.getAliasShortName();\n\t}\n\n\tpublic String getFullName() {\n\t\treturn clsInfo.getAliasFullName();\n\t}\n\n\tpublic String getPackage() {\n\t\treturn clsInfo.getAliasPkg();\n\t}\n\n\tpublic String getDisassembledCode() {\n\t\tif (smali == null) {\n\t\t\tSimpleCodeWriter code = new SimpleCodeWriter(root.getArgs());\n\t\t\tgetDisassembledCode(code);\n\t\t\tSet<ClassNode> allInlinedClasses = new LinkedHashSet<>();\n\t\t\tgetInnerAndInlinedClassesRecursive(allInlinedClasses);\n\t\t\tfor (ClassNode innerClass : allInlinedClasses) {\n\t\t\t\tinnerClass.getDisassembledCode(code);\n\t\t\t}\n\t\t\tsmali = code.finish().getCodeStr();\n\t\t}\n\t\treturn smali;\n\t}\n\n\tprotected void getDisassembledCode(SimpleCodeWriter code) {\n\t\tif (clsData == null) {\n\t\t\tcode.startLine(String.format(\"###### Class %s is created by jadx\", getFullName()));\n\t\t\treturn;\n\t\t}\n\t\tcode.startLine(String.format(\"###### Class %s (%s)\", getFullName(), getRawName()));\n\t\ttry {\n\t\t\tcode.startLine(clsData.getDisassembledCode());\n\t\t} catch (Exception e) {\n\t\t\tcode.startLine(\"Failed to disassemble class:\");\n\t\t\tcode.startLine(Utils.getStackTrace(e));\n\t\t}\n\t}\n\n\t/**\n\t * Low level class data access.\n\t *\n\t * @return null for classes generated by jadx\n\t */\n\tpublic @Nullable IClassData getClsData() {\n\t\treturn clsData;\n\t}\n\n\tpublic ProcessState getState() {\n\t\treturn state;\n\t}\n\n\tpublic void setState(ProcessState state) {\n\t\tthis.state = state;\n\t}\n\n\tpublic LoadStage getLoadStage() {\n\t\treturn loadStage;\n\t}\n\n\tpublic void setLoadStage(LoadStage loadStage) {\n\t\tthis.loadStage = loadStage;\n\t}\n\n\tpublic void reloadAtCodegenStage() {\n\t\tClassNode topCls = this.getTopParentClass();\n\t\tif (topCls.getLoadStage() == LoadStage.CODEGEN_STAGE) {\n\t\t\tthrow new JadxRuntimeException(\"Class not yet loaded at codegen stage: \" + topCls);\n\t\t}\n\t\ttopCls.add(AFlag.RELOAD_AT_CODEGEN_STAGE);\n\t}\n\n\tpublic List<ClassNode> getDependencies() {\n\t\treturn dependencies;\n\t}\n\n\tpublic void setDependencies(List<ClassNode> dependencies) {\n\t\tthis.dependencies = dependencies;\n\t}\n\n\tpublic void removeDependency(ClassNode dep) {\n\t\tthis.dependencies = ListUtils.safeRemoveAndTrim(this.dependencies, dep);\n\t}\n\n\tpublic List<ClassNode> getCodegenDeps() {\n\t\treturn codegenDeps;\n\t}\n\n\tpublic void setCodegenDeps(List<ClassNode> codegenDeps) {\n\t\tthis.codegenDeps = codegenDeps;\n\t}\n\n\tpublic void addCodegenDep(ClassNode dep) {\n\t\tif (!codegenDeps.contains(dep)) {\n\t\t\tthis.codegenDeps = ListUtils.safeAdd(this.codegenDeps, dep);\n\t\t}\n\t}\n\n\tpublic int getTotalDepsCount() {\n\t\treturn dependencies.size() + codegenDeps.size();\n\t}\n\n\tpublic List<ClassNode> getUseIn() {\n\t\treturn useIn;\n\t}\n\n\tpublic void setUseIn(List<ClassNode> useIn) {\n\t\tthis.useIn = useIn;\n\t}\n\n\tpublic List<MethodNode> getUseInMth() {\n\t\treturn useInMth;\n\t}\n\n\tpublic void setUseInMth(List<MethodNode> useInMth) {\n\t\tthis.useInMth = useInMth;\n\t}\n\n\t@Override\n\tpublic String getInputFileName() {\n\t\treturn inputFileName;\n\t}\n\n\tpublic void setInputFileName(String inputFileName) {\n\t\tthis.inputFileName = inputFileName;\n\t}\n\n\tpublic JavaClass getJavaNode() {\n\t\treturn javaNode;\n\t}\n\n\tpublic void setJavaNode(JavaClass javaNode) {\n\t\tthis.javaNode = javaNode;\n\t}\n\n\t@Override\n\tpublic AnnType getAnnType() {\n\t\treturn AnnType.CLASS;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn clsInfo.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o instanceof ClassNode) {\n\t\t\tClassNode other = (ClassNode) o;\n\t\t\treturn clsInfo.equals(other.clsInfo);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull ClassNode o) {\n\t\treturn this.clsInfo.compareTo(o.clsInfo);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn clsInfo.getFullName();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/Edge.java",
    "content": "package jadx.core.dex.nodes;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AttrNode;\n\npublic class Edge extends AttrNode {\n\tprivate final BlockNode source;\n\tprivate final BlockNode target;\n\n\tpublic Edge(BlockNode source, BlockNode target) {\n\t\tthis(source, target, false);\n\t}\n\n\tpublic Edge(BlockNode source, BlockNode target, boolean isSynthetic) {\n\t\tif (isSynthetic) {\n\t\t\tthis.add(AFlag.SYNTHETIC);\n\t\t}\n\n\t\tthis.source = source;\n\t\tthis.target = target;\n\n\t}\n\n\tpublic BlockNode getSource() {\n\t\treturn source;\n\t}\n\n\tpublic BlockNode getTarget() {\n\t\treturn target;\n\t}\n\n\tpublic boolean isSynthetic() {\n\t\treturn this.contains(AFlag.SYNTHETIC);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tEdge edge = (Edge) o;\n\t\treturn source.equals(edge.source) && target.equals(edge.target);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn source.hashCode() + 31 * target.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Edge: \" + source + \" -> \" + target;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/FieldNode.java",
    "content": "package jadx.core.dex.nodes;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.api.JavaField;\nimport jadx.api.plugins.input.data.IFieldData;\nimport jadx.core.dex.attributes.nodes.NotificationAttrNode;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.info.AccessInfo.AFType;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.utils.ListUtils;\n\npublic class FieldNode extends NotificationAttrNode implements ICodeNode, IFieldInfoRef {\n\n\tprivate final ClassNode parentClass;\n\tprivate final FieldInfo fieldInfo;\n\tprivate AccessInfo accFlags;\n\n\tprivate ArgType type;\n\n\tprivate List<MethodNode> useIn = Collections.emptyList();\n\n\tprivate JavaField javaNode;\n\n\tpublic static FieldNode build(ClassNode cls, IFieldData fieldData) {\n\t\tFieldInfo fieldInfo = FieldInfo.fromRef(cls.root(), fieldData);\n\t\tFieldNode fieldNode = new FieldNode(cls, fieldInfo, fieldData.getAccessFlags());\n\t\tfieldNode.addAttrs(fieldData.getAttributes());\n\t\treturn fieldNode;\n\t}\n\n\tpublic FieldNode(ClassNode cls, FieldInfo fieldInfo, int accessFlags) {\n\t\tthis.parentClass = cls;\n\t\tthis.fieldInfo = fieldInfo;\n\t\tthis.type = fieldInfo.getType();\n\t\tthis.accFlags = new AccessInfo(accessFlags, AFType.FIELD);\n\t}\n\n\tpublic void unload() {\n\t\tunloadAttributes();\n\t}\n\n\tpublic void updateType(ArgType type) {\n\t\tthis.type = type;\n\t}\n\n\t@Override\n\tpublic FieldInfo getFieldInfo() {\n\t\treturn fieldInfo;\n\t}\n\n\t@Override\n\tpublic AccessInfo getAccessFlags() {\n\t\treturn accFlags;\n\t}\n\n\t@Override\n\tpublic void setAccessFlags(AccessInfo accFlags) {\n\t\tthis.accFlags = accFlags;\n\t}\n\n\tpublic boolean isStatic() {\n\t\treturn accFlags.isStatic();\n\t}\n\n\tpublic boolean isInstance() {\n\t\treturn !accFlags.isStatic();\n\t}\n\n\tpublic String getName() {\n\t\treturn fieldInfo.getName();\n\t}\n\n\tpublic String getAlias() {\n\t\treturn fieldInfo.getAlias();\n\t}\n\n\t@Override\n\tpublic void rename(String alias) {\n\t\tfieldInfo.setAlias(alias);\n\t}\n\n\tpublic ArgType getType() {\n\t\treturn type;\n\t}\n\n\t@Override\n\tpublic ClassNode getDeclaringClass() {\n\t\treturn parentClass;\n\t}\n\n\tpublic ClassNode getParentClass() {\n\t\treturn parentClass;\n\t}\n\n\tpublic ClassNode getTopParentClass() {\n\t\treturn parentClass.getTopParentClass();\n\t}\n\n\tpublic List<MethodNode> getUseIn() {\n\t\treturn useIn;\n\t}\n\n\tpublic void setUseIn(List<MethodNode> useIn) {\n\t\tthis.useIn = useIn;\n\t}\n\n\tpublic synchronized void addUseIn(MethodNode mth) {\n\t\tuseIn = ListUtils.safeAdd(useIn, mth);\n\t}\n\n\t@Override\n\tpublic String typeName() {\n\t\treturn \"field\";\n\t}\n\n\t@Override\n\tpublic String getInputFileName() {\n\t\treturn parentClass.getInputFileName();\n\t}\n\n\t@Override\n\tpublic RootNode root() {\n\t\treturn parentClass.root();\n\t}\n\n\tpublic JavaField getJavaNode() {\n\t\treturn javaNode;\n\t}\n\n\tpublic void setJavaNode(JavaField javaNode) {\n\t\tthis.javaNode = javaNode;\n\t}\n\n\t@Override\n\tpublic AnnType getAnnType() {\n\t\treturn AnnType.FIELD;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn fieldInfo.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tFieldNode other = (FieldNode) obj;\n\t\treturn fieldInfo.equals(other.fieldInfo);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn fieldInfo.getDeclClass() + \".\" + fieldInfo.getName() + \" :\" + type;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/IBlock.java",
    "content": "package jadx.core.dex.nodes;\n\nimport java.util.List;\n\nimport jadx.api.ICodeWriter;\nimport jadx.core.codegen.RegionGen;\nimport jadx.core.utils.exceptions.CodegenException;\n\npublic interface IBlock extends IContainer {\n\n\tList<InsnNode> getInstructions();\n\n\t@Override\n\tdefault void generate(RegionGen regionGen, ICodeWriter code) throws CodegenException {\n\t\tregionGen.makeSimpleBlock(this, code);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/IBranchRegion.java",
    "content": "package jadx.core.dex.nodes;\n\nimport java.util.List;\n\npublic interface IBranchRegion extends IRegion {\n\n\t/**\n\t * Return list of branches in this region.\n\t * NOTE: Contains 'null' elements for indicate empty branches.\n\t */\n\tList<IContainer> getBranches();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/ICodeDataUpdateListener.java",
    "content": "package jadx.core.dex.nodes;\n\nimport jadx.api.data.ICodeData;\n\npublic interface ICodeDataUpdateListener {\n\n\tvoid updated(ICodeData codeData);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/ICodeNode.java",
    "content": "package jadx.core.dex.nodes;\n\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.core.dex.attributes.IAttributeNode;\nimport jadx.core.dex.info.AccessInfo;\n\npublic interface ICodeNode extends IDexNode, IAttributeNode, IUsageInfoNode, ICodeNodeRef {\n\n\tClassNode getDeclaringClass();\n\n\tAccessInfo getAccessFlags();\n\n\tvoid setAccessFlags(AccessInfo newAccessFlags);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/IConditionRegion.java",
    "content": "package jadx.core.dex.nodes;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.regions.conditions.IfCondition;\n\npublic interface IConditionRegion extends IRegion {\n\n\t@Nullable\n\tIfCondition getCondition();\n\n\t/**\n\t * Blocks merged into condition\n\t * Needed for backtracking\n\t * TODO: merge into condition object ???\n\t */\n\tList<BlockNode> getConditionBlocks();\n\n\tvoid invertCondition();\n\n\tboolean simplifyCondition();\n\n\tint getConditionSourceLine();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/IContainer.java",
    "content": "package jadx.core.dex.nodes;\n\nimport jadx.api.ICodeWriter;\nimport jadx.core.codegen.RegionGen;\nimport jadx.core.dex.attributes.IAttributeNode;\nimport jadx.core.utils.exceptions.CodegenException;\n\npublic interface IContainer extends IAttributeNode {\n\n\t/**\n\t * Unique id for use in 'toString()' method\n\t */\n\tString baseString();\n\n\t/**\n\t * Dispatch to needed generate method in RegionGen\n\t */\n\tdefault void generate(RegionGen regionGen, ICodeWriter code) throws CodegenException {\n\t\tthrow new CodegenException(\"Code generate not implemented for container: \" + getClass().getSimpleName());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/IDexNode.java",
    "content": "package jadx.core.dex.nodes;\n\nimport jadx.api.data.IRenameNode;\n\npublic interface IDexNode extends IRenameNode {\n\n\tString typeName();\n\n\tRootNode root();\n\n\tString getInputFileName();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/IFieldInfoRef.java",
    "content": "package jadx.core.dex.nodes;\n\nimport jadx.core.dex.info.FieldInfo;\n\n/**\n * Common interface for FieldInfo and FieldNode\n */\npublic interface IFieldInfoRef {\n\tFieldInfo getFieldInfo();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/ILoadable.java",
    "content": "package jadx.core.dex.nodes;\n\nimport jadx.core.utils.exceptions.DecodeException;\n\npublic interface ILoadable {\n\n\t/**\n\t * On demand loading\n\t */\n\tvoid load() throws DecodeException;\n\n\t/**\n\t * Free resources\n\t */\n\tvoid unload();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/IMethodDetails.java",
    "content": "package jadx.core.dex.nodes;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.utils.Utils;\n\npublic interface IMethodDetails extends IJadxAttribute {\n\n\tMethodInfo getMethodInfo();\n\n\tArgType getReturnType();\n\n\tList<ArgType> getArgTypes();\n\n\tList<ArgType> getTypeParameters();\n\n\tList<ArgType> getThrows();\n\n\tboolean isVarArg();\n\n\tint getRawAccessFlags();\n\n\t@Override\n\tdefault AType<IMethodDetails> getAttrType() {\n\t\treturn AType.METHOD_DETAILS;\n\t}\n\n\t@Override\n\tdefault String toAttrString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"MD:\");\n\t\tif (Utils.notEmpty(getTypeParameters())) {\n\t\t\tsb.append('<');\n\t\t\tsb.append(Utils.listToString(getTypeParameters()));\n\t\t\tsb.append(\">:\");\n\t\t}\n\t\tsb.append('(');\n\t\tsb.append(Utils.listToString(getArgTypes()));\n\t\tsb.append(\"):\");\n\t\tsb.append(getReturnType());\n\t\tif (isVarArg()) {\n\t\t\tsb.append(\" VARARG\");\n\t\t}\n\t\tList<ArgType> throwsList = getThrows();\n\t\tif (Utils.notEmpty(throwsList)) {\n\t\t\tsb.append(\" throws \").append(Utils.listToString(throwsList));\n\t\t}\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/IPackageUpdate.java",
    "content": "package jadx.core.dex.nodes;\n\npublic interface IPackageUpdate {\n\n\tvoid onParentPackageUpdate(PackageNode updatedPkg);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/IRegion.java",
    "content": "package jadx.core.dex.nodes;\n\nimport java.util.List;\n\npublic interface IRegion extends IContainer {\n\n\tIRegion getParent();\n\n\tvoid setParent(IRegion parent);\n\n\tList<IContainer> getSubBlocks();\n\n\tboolean replaceSubBlock(IContainer oldBlock, IContainer newBlock);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/IUsageInfoNode.java",
    "content": "package jadx.core.dex.nodes;\n\nimport java.util.List;\n\npublic interface IUsageInfoNode {\n\tList<? extends ICodeNode> getUseIn();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/InsnContainer.java",
    "content": "package jadx.core.dex.nodes;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.core.dex.attributes.AttrNode;\n\n/**\n * Lightweight replacement for BlockNode in regions.\n * Use with caution! Some passes still expect BlockNode in method blocks list (mth.getBlockNodes())\n */\npublic final class InsnContainer extends AttrNode implements IBlock {\n\n\tprivate final List<InsnNode> insns;\n\n\tpublic InsnContainer(InsnNode insn) {\n\t\tList<InsnNode> list = new ArrayList<>(1);\n\t\tlist.add(insn);\n\t\tthis.insns = list;\n\t}\n\n\tpublic InsnContainer(List<InsnNode> insns) {\n\t\tthis.insns = insns;\n\t}\n\n\t@Override\n\tpublic List<InsnNode> getInstructions() {\n\t\treturn insns;\n\t}\n\n\t@Override\n\tpublic String baseString() {\n\t\treturn \"IC\";\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"InsnContainer\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java",
    "content": "package jadx.core.dex.nodes;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.LineAttrNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class InsnNode extends LineAttrNode {\n\tprotected final InsnType insnType;\n\n\tprivate RegisterArg result;\n\tprivate final List<InsnArg> arguments;\n\tprotected int offset;\n\n\tpublic InsnNode(InsnType type, int argsCount) {\n\t\tthis(type, argsCount == 0 ? Collections.emptyList() : new ArrayList<>(argsCount));\n\t}\n\n\tpublic InsnNode(InsnType type, List<InsnArg> args) {\n\t\tthis.insnType = type;\n\t\tthis.arguments = args;\n\t\tthis.offset = -1;\n\t\tfor (InsnArg arg : args) {\n\t\t\tattachArg(arg);\n\t\t}\n\t}\n\n\tpublic static InsnNode wrapArg(InsnArg arg) {\n\t\tInsnNode insn = new InsnNode(InsnType.ONE_ARG, 1);\n\t\tinsn.addArg(arg);\n\t\treturn insn;\n\t}\n\n\tpublic void setResult(@Nullable RegisterArg res) {\n\t\tthis.result = res;\n\t\tif (res != null) {\n\t\t\tres.setParentInsn(this);\n\t\t\tSSAVar ssaVar = res.getSVar();\n\t\t\tif (ssaVar != null) {\n\t\t\t\tssaVar.setAssign(res);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void addArg(InsnArg arg) {\n\t\targuments.add(arg);\n\t\tattachArg(arg);\n\t}\n\n\tpublic void setArg(int n, InsnArg arg) {\n\t\targuments.set(n, arg);\n\t\tattachArg(arg);\n\t}\n\n\tprotected void attachArg(InsnArg arg) {\n\t\targ.setParentInsn(this);\n\t\tif (arg.isRegister()) {\n\t\t\tRegisterArg reg = (RegisterArg) arg;\n\t\t\tSSAVar ssaVar = reg.getSVar();\n\t\t\tif (ssaVar != null) {\n\t\t\t\tssaVar.use(reg);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic InsnType getType() {\n\t\treturn insnType;\n\t}\n\n\tpublic RegisterArg getResult() {\n\t\treturn result;\n\t}\n\n\tpublic Iterable<InsnArg> getArguments() {\n\t\treturn arguments;\n\t}\n\n\tpublic List<InsnArg> getArgList() {\n\t\treturn arguments;\n\t}\n\n\tpublic int getArgsCount() {\n\t\treturn arguments.size();\n\t}\n\n\tpublic InsnArg getArg(int n) {\n\t\treturn arguments.get(n);\n\t}\n\n\tpublic boolean containsArg(InsnArg arg) {\n\t\tif (getArgsCount() == 0) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (InsnArg a : arguments) {\n\t\t\tif (a == arg) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean containsVar(RegisterArg arg) {\n\t\tif (getArgsCount() == 0) {\n\t\t\treturn false;\n\t\t}\n\t\treturn InsnUtils.containsVar(arguments, arg);\n\t}\n\n\t/**\n\t * Replace instruction arg with another using recursive search.\n\t */\n\tpublic boolean replaceArg(InsnArg from, InsnArg to) {\n\t\tint count = getArgsCount();\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tInsnArg arg = arguments.get(i);\n\t\t\tif (arg == from) {\n\t\t\t\tInsnRemover.unbindArgUsage(null, arg);\n\t\t\t\tsetArg(i, to);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (arg.isInsnWrap() && ((InsnWrapArg) arg).getWrapInsn().replaceArg(from, to)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprotected boolean removeArg(InsnArg arg) {\n\t\tint index = getArgIndex(arg);\n\t\tif (index == -1) {\n\t\t\treturn false;\n\t\t}\n\t\tremoveArg(index);\n\t\treturn true;\n\t}\n\n\tpublic InsnArg removeArg(int index) {\n\t\tInsnArg arg = arguments.get(index);\n\t\targuments.remove(index);\n\t\tInsnRemover.unbindArgUsage(null, arg);\n\t\treturn arg;\n\t}\n\n\tpublic int getArgIndex(InsnArg arg) {\n\t\tint count = getArgsCount();\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tif (arg == arguments.get(i)) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\tprotected void addReg(InsnData insn, int i, ArgType type) {\n\t\taddArg(InsnArg.reg(insn, i, type));\n\t}\n\n\tprotected void addReg(int regNum, ArgType type) {\n\t\taddArg(InsnArg.reg(regNum, type));\n\t}\n\n\tprotected void addLit(long literal, ArgType type) {\n\t\taddArg(InsnArg.lit(literal, type));\n\t}\n\n\tprotected void addLit(InsnData insn, ArgType type) {\n\t\taddArg(InsnArg.lit(insn, type));\n\t}\n\n\tpublic int getOffset() {\n\t\treturn offset;\n\t}\n\n\tpublic void setOffset(int offset) {\n\t\tthis.offset = offset;\n\t}\n\n\tpublic void getRegisterArgs(Collection<RegisterArg> collection) {\n\t\tfor (InsnArg arg : this.getArguments()) {\n\t\t\tif (arg.isRegister()) {\n\t\t\t\tcollection.add((RegisterArg) arg);\n\t\t\t} else if (arg.isInsnWrap()) {\n\t\t\t\t((InsnWrapArg) arg).getWrapInsn().getRegisterArgs(collection);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic boolean isConstInsn() {\n\t\tswitch (getType()) {\n\t\t\tcase CONST:\n\t\t\tcase CONST_STR:\n\t\t\tcase CONST_CLASS:\n\t\t\t\treturn true;\n\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic boolean isExitEdgeInsn() {\n\t\tswitch (getType()) {\n\t\t\tcase RETURN:\n\t\t\tcase THROW:\n\t\t\tcase CONTINUE:\n\t\t\tcase BREAK:\n\t\t\t\treturn true;\n\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic boolean canRemoveResult() {\n\t\tswitch (getType()) {\n\t\t\tcase INVOKE:\n\t\t\tcase CONSTRUCTOR:\n\t\t\t\treturn true;\n\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic boolean canReorder() {\n\t\tif (contains(AFlag.DONT_GENERATE)) {\n\t\t\tif (getType() == InsnType.MONITOR_EXIT) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tfor (InsnArg arg : getArguments()) {\n\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\tif (!wrapInsn.canReorder()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tswitch (getType()) {\n\t\t\tcase CONST:\n\t\t\tcase CONST_STR:\n\t\t\tcase CONST_CLASS:\n\t\t\tcase CAST:\n\t\t\tcase MOVE:\n\t\t\tcase ARITH:\n\t\t\tcase NEG:\n\t\t\tcase CMP_L:\n\t\t\tcase CMP_G:\n\t\t\tcase CHECK_CAST:\n\t\t\tcase INSTANCE_OF:\n\t\t\tcase FILL_ARRAY:\n\t\t\tcase FILLED_NEW_ARRAY:\n\t\t\tcase NEW_ARRAY:\n\t\t\tcase STR_CONCAT:\n\t\t\t\treturn true;\n\n\t\t\tcase SGET:\n\t\t\tcase IGET:\n\t\t\t\t// TODO: allow to move final fields\n\t\t\t\treturn false;\n\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic boolean containsWrappedInsn() {\n\t\tfor (InsnArg arg : this.getArguments()) {\n\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Visit this instruction and all inner (wrapped) instructions\n\t */\n\tpublic void visitInsns(Consumer<InsnNode> visitor) {\n\t\tvisitor.accept(this);\n\t\tfor (InsnArg arg : this.getArguments()) {\n\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\t((InsnWrapArg) arg).getWrapInsn().visitInsns(visitor);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Visit this instruction and all inner (wrapped) instructions\n\t * To terminate visiting return non-null value\n\t */\n\t@Nullable\n\tpublic <R> R visitInsns(Function<InsnNode, R> visitor) {\n\t\tR result = visitor.apply(this);\n\t\tif (result != null) {\n\t\t\treturn result;\n\t\t}\n\t\tfor (InsnArg arg : this.getArguments()) {\n\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\tInsnNode innerInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\tR res = innerInsn.visitInsns(visitor);\n\t\t\t\tif (res != null) {\n\t\t\t\t\treturn res;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Visit all args recursively (including inner instructions), but excluding wrapped args\n\t */\n\tpublic void visitArgs(Consumer<InsnArg> visitor) {\n\t\tfor (InsnArg arg : getArguments()) {\n\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\t((InsnWrapArg) arg).getWrapInsn().visitArgs(visitor);\n\t\t\t} else {\n\t\t\t\tvisitor.accept(arg);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Visit all args recursively (including inner instructions), but excluding wrapped args.\n\t * To terminate visiting return non-null value\n\t */\n\t@Nullable\n\tpublic <R> R visitArgs(Function<InsnArg, R> visitor) {\n\t\tfor (InsnArg arg : getArguments()) {\n\t\t\tR result;\n\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\tresult = wrapInsn.visitArgs(visitor);\n\t\t\t} else {\n\t\t\t\tresult = visitor.apply(arg);\n\t\t\t}\n\t\t\tif (result != null) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * 'Soft' equals, don't compare arguments, only instruction specific parameters.\n\t */\n\tpublic boolean isSame(InsnNode other) {\n\t\tif (this == other) {\n\t\t\treturn true;\n\t\t}\n\t\tif (insnType != other.insnType) {\n\t\t\treturn false;\n\t\t}\n\t\tint size = arguments.size();\n\t\tif (size != other.arguments.size()) {\n\t\t\treturn false;\n\t\t}\n\t\t// check wrapped instructions\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tInsnArg arg = arguments.get(i);\n\t\t\tInsnArg otherArg = other.arguments.get(i);\n\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\tif (!otherArg.isInsnWrap()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\tInsnNode otherWrapInsn = ((InsnWrapArg) otherArg).getWrapInsn();\n\t\t\t\tif (!wrapInsn.isSame(otherWrapInsn)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * 'Hard' equals, compare all arguments\n\t */\n\tpublic boolean isDeepEquals(InsnNode other) {\n\t\tif (this == other) {\n\t\t\treturn true;\n\t\t}\n\t\treturn isSame(other)\n\t\t\t\t&& Objects.equals(result, other.result)\n\t\t\t\t&& Objects.equals(arguments, other.arguments);\n\t}\n\n\tprotected final <T extends InsnNode> T copyCommonParams(T copy) {\n\t\tif (copy.getArgsCount() == 0) {\n\t\t\tfor (InsnArg arg : this.getArguments()) {\n\t\t\t\tcopy.addArg(arg.duplicate());\n\t\t\t}\n\t\t}\n\t\tcopy.copyAttributesFrom(this);\n\t\tcopy.copyLines(this);\n\t\tcopy.setOffset(this.getOffset());\n\t\treturn copy;\n\t}\n\n\tpublic void copyAttributesFrom(InsnNode attrNode) {\n\t\tsuper.copyAttributesFrom(attrNode);\n\t\tthis.addSourceLineFrom(attrNode);\n\t}\n\n\t/**\n\t * Make copy of InsnNode object.\n\t * <br>\n\t * NOTE: can't copy instruction with result argument\n\t * (SSA variable can't be used in two different assigns).\n\t * <br>\n\t * Prefer use next methods:\n\t * <ul>\n\t * <li>{@link #copyWithoutResult()} to explicitly state that result not needed\n\t * <li>{@link #copy(RegisterArg)} to provide new result arg\n\t * <li>{@link #copyWithNewSsaVar(MethodNode)} to make new SSA variable for result arg\n\t * </ul>\n\t */\n\tpublic InsnNode copy() {\n\t\tif (this.getClass() != InsnNode.class) {\n\t\t\tthrow new JadxRuntimeException(\"Copy method not implemented in insn class \" + this.getClass().getSimpleName());\n\t\t}\n\t\treturn copyCommonParams(new InsnNode(insnType, getArgsCount()));\n\t}\n\n\t/**\n\t * See {@link #copy()}\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T extends InsnNode> T copyWithoutResult() {\n\t\treturn (T) copy();\n\t}\n\n\tpublic InsnNode copyWithoutSsa() {\n\t\tInsnNode copy = copyWithoutResult();\n\t\tif (result != null) {\n\t\t\tif (result.getSVar() == null) {\n\t\t\t\tcopy.setResult(result.duplicate());\n\t\t\t} else {\n\t\t\t\tthrow new JadxRuntimeException(\"Can't copy if SSA var is set\");\n\t\t\t}\n\t\t}\n\t\treturn copy;\n\t}\n\n\t/**\n\t * See {@link #copy()}\n\t */\n\tpublic InsnNode copy(RegisterArg newReturnArg) {\n\t\tInsnNode copy = copy();\n\t\tcopy.setResult(newReturnArg);\n\t\treturn copy;\n\t}\n\n\t/**\n\t * See {@link #copy()}\n\t */\n\tpublic InsnNode copyWithNewSsaVar(MethodNode mth) {\n\t\tRegisterArg result = getResult();\n\t\tif (result == null) {\n\t\t\tthrow new JadxRuntimeException(\"Result in null\");\n\t\t}\n\t\tint regNum = result.getRegNum();\n\t\tRegisterArg resDupArg = result.duplicate(regNum, null);\n\t\tmth.makeNewSVar(resDupArg);\n\t\treturn copy(resDupArg);\n\t}\n\n\t/**\n\t * Fix SSAVar info in register arguments.\n\t * Must be used after altering instructions.\n\t */\n\tpublic void rebindArgs() {\n\t\tRegisterArg resArg = getResult();\n\t\tif (resArg != null) {\n\t\t\tSSAVar ssaVar = resArg.getSVar();\n\t\t\tif (ssaVar == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"No SSA var for result arg: \" + resArg + \" from \" + resArg.getParentInsn());\n\t\t\t}\n\t\t\tssaVar.setAssign(resArg);\n\t\t}\n\t\tfor (InsnArg arg : getArguments()) {\n\t\t\tif (arg instanceof RegisterArg) {\n\t\t\t\tRegisterArg reg = (RegisterArg) arg;\n\t\t\t\tSSAVar ssaVar = reg.getSVar();\n\t\t\t\tssaVar.use(reg);\n\t\t\t\tssaVar.updateUsedInPhiList();\n\t\t\t} else if (arg instanceof InsnWrapArg) {\n\t\t\t\t((InsnWrapArg) arg).getWrapInsn().rebindArgs();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic boolean canThrowException() {\n\t\tswitch (getType()) {\n\t\t\tcase RETURN:\n\t\t\tcase IF:\n\t\t\tcase GOTO:\n\t\t\tcase MOVE:\n\t\t\tcase MOVE_EXCEPTION:\n\t\t\tcase NEG:\n\t\t\tcase CONST:\n\t\t\tcase CONST_STR:\n\t\t\tcase CONST_CLASS:\n\t\t\tcase CMP_L:\n\t\t\tcase CMP_G:\n\t\t\tcase NOP:\n\t\t\t\treturn false;\n\n\t\t\tdefault:\n\t\t\t\treturn true;\n\t\t}\n\t}\n\n\tpublic void inheritMetadata(InsnNode sourceInsn) {\n\t\tif (insnType == InsnType.RETURN) {\n\t\t\tthis.copyLines(sourceInsn);\n\t\t\tif (this.contains(AFlag.SYNTHETIC)) {\n\t\t\t\tthis.setOffset(sourceInsn.getOffset());\n\t\t\t\tthis.rewriteAttributeFrom(sourceInsn, AType.CODE_COMMENTS);\n\t\t\t} else {\n\t\t\t\tthis.copyAttributeFrom(sourceInsn, AType.CODE_COMMENTS);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.copyAttributeFrom(sourceInsn, AType.CODE_COMMENTS);\n\t\t\tthis.addSourceLineFrom(sourceInsn);\n\t\t}\n\t}\n\n\t/**\n\t * Compare instruction only by identity.\n\t */\n\t@SuppressWarnings(\"EmptyMethod\")\n\t@Override\n\tpublic final int hashCode() {\n\t\treturn super.hashCode();\n\t}\n\n\t/**\n\t * Compare instruction only by identity.\n\t */\n\t@Override\n\tpublic final boolean equals(Object obj) {\n\t\treturn super.equals(obj);\n\t}\n\n\t/**\n\t * Append arguments type, wrap line if too long\n\t *\n\t * @return true if args wrapped\n\t */\n\tprotected boolean appendArgs(StringBuilder sb) {\n\t\tif (arguments.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tString argsStr = Utils.listToString(arguments);\n\t\tif (argsStr.length() < 120) {\n\t\t\tsb.append(argsStr);\n\t\t\treturn false;\n\t\t}\n\t\t// wrap args\n\t\tString separator = \"\\n  \";\n\t\tsb.append(separator).append(Utils.listToString(arguments, separator));\n\t\tsb.append('\\n');\n\t\treturn true;\n\t}\n\n\tprotected String attributesString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tappendAttributes(sb);\n\t\treturn sb.toString();\n\t}\n\n\tprotected void appendAttributes(StringBuilder sb) {\n\t\tif (!isAttrStorageEmpty()) {\n\t\t\tsb.append(' ').append(getAttributesString());\n\t\t}\n\t\tif (getSourceLine() != 0) {\n\t\t\tsb.append(\" (LINE:\").append(getSourceLine()).append(')');\n\t\t}\n\t}\n\n\tprotected String baseString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tif (offset != -1) {\n\t\t\tsb.append(InsnUtils.formatOffset(offset)).append(\": \");\n\t\t}\n\t\tsb.append(insnType).append(' ');\n\t\tif (result != null) {\n\t\t\tsb.append(result).append(\" = \");\n\t\t}\n\t\tappendArgs(sb);\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn baseString() + attributesString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/LoadStage.java",
    "content": "package jadx.core.dex.nodes;\n\npublic enum LoadStage {\n\tNONE,\n\tPROCESS_STAGE, // dependencies not yet loaded\n\tCODEGEN_STAGE, // all dependencies loaded\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java",
    "content": "package jadx.core.dex.nodes;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.ApiStatus;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.JavaMethod;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.metadata.annotations.NodeDeclareRef;\nimport jadx.api.metadata.annotations.VarNode;\nimport jadx.api.plugins.input.data.ICodeReader;\nimport jadx.api.plugins.input.data.IDebugInfo;\nimport jadx.api.plugins.input.data.IMethodData;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.types.ExceptionsAttr;\nimport jadx.api.utils.CodeUtils;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.LoopInfo;\nimport jadx.core.dex.attributes.nodes.MethodOverrideAttr;\nimport jadx.core.dex.attributes.nodes.MethodThrowsAttr;\nimport jadx.core.dex.attributes.nodes.NotificationAttrNode;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.info.AccessInfo.AFType;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.InsnDecoder;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.utils.TypeUtils;\nimport jadx.core.dex.regions.Region;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.dex.visitors.InitCodeVariables;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.DecodeException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.utils.Utils.lockList;\n\npublic class MethodNode extends NotificationAttrNode implements IMethodDetails, ILoadable, ICodeNode, Comparable<MethodNode> {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(MethodNode.class);\n\tprivate static final InsnNode[] EMPTY_INSN_ARRAY = new InsnNode[0];\n\n\tprivate final MethodInfo mthInfo;\n\tprivate final ClassNode parentClass;\n\tprivate AccessInfo accFlags;\n\n\tprivate final ICodeReader codeReader;\n\tprivate final int insnsCount;\n\n\tprivate boolean noCode;\n\tprivate int regsCount;\n\tprivate int argsStartReg;\n\n\tprivate boolean loaded;\n\n\t// additional info available after load, keep on unload\n\tprivate ArgType retType;\n\tprivate List<ArgType> argTypes;\n\tprivate List<ArgType> typeParameters;\n\n\t// decompilation data, reset on unload\n\tprivate RegisterArg thisArg;\n\tprivate List<RegisterArg> argsList;\n\tprivate InsnNode[] instructions;\n\tprivate List<BlockNode> blocks;\n\tprivate int blocksMaxCId;\n\tprivate BlockNode enterBlock;\n\tprivate BlockNode exitBlock;\n\tprivate List<SSAVar> sVars;\n\tprivate List<ExceptionHandler> exceptionHandlers;\n\tprivate List<LoopInfo> loops;\n\tprivate Region region;\n\n\t// Methods that use this method\n\tprivate List<MethodNode> useIn = Collections.emptyList();\n\t// Unresolved methods that use this method\n\tprivate List<IMethodRef> unresolvedUsed = Collections.emptyList();\n\t// Methods that this method uses\n\tprivate Set<MethodNode> methodsUsed = new HashSet<>();\n\t// True if this method contains a self call\n\tprivate boolean callsSelf = false;\n\n\tprivate JavaMethod javaNode;\n\n\tpublic static MethodNode build(ClassNode classNode, IMethodData methodData) {\n\t\tMethodNode methodNode = new MethodNode(classNode, methodData);\n\t\tmethodNode.addAttrs(methodData.getAttributes());\n\t\treturn methodNode;\n\t}\n\n\tprivate MethodNode(ClassNode classNode, IMethodData mthData) {\n\t\tthis.mthInfo = MethodInfo.fromRef(classNode.root(), mthData.getMethodRef());\n\t\tthis.parentClass = classNode;\n\t\tthis.accFlags = new AccessInfo(mthData.getAccessFlags(), AFType.METHOD);\n\t\tICodeReader codeReader = mthData.getCodeReader();\n\t\tthis.noCode = codeReader == null;\n\t\tif (noCode) {\n\t\t\tthis.codeReader = null;\n\t\t\tthis.insnsCount = 0;\n\t\t} else {\n\t\t\tthis.codeReader = codeReader.copy();\n\t\t\tthis.insnsCount = codeReader.getUnitsCount();\n\t\t}\n\n\t\tthis.retType = mthInfo.getReturnType();\n\t\tthis.argTypes = mthInfo.getArgumentsTypes();\n\t\tthis.typeParameters = Collections.emptyList();\n\t\tunload();\n\t}\n\n\t@Override\n\tpublic void unload() {\n\t\tloaded = false;\n\t\t// don't unload retType, argTypes, typeParameters\n\t\tthisArg = null;\n\t\targsList = null;\n\t\tsVars = Collections.emptyList();\n\t\tinstructions = null;\n\t\tblocks = null;\n\t\tenterBlock = null;\n\t\texitBlock = null;\n\t\tregion = null;\n\t\texceptionHandlers = Collections.emptyList();\n\t\tloops = Collections.emptyList();\n\t\tunloadAttributes();\n\t}\n\n\tpublic void updateTypes(List<ArgType> argTypes, ArgType retType) {\n\t\tthis.argTypes = argTypes;\n\t\tthis.retType = retType;\n\t}\n\n\tpublic void updateTypeParameters(List<ArgType> typeParameters) {\n\t\tthis.typeParameters = typeParameters;\n\t}\n\n\t@Override\n\tpublic void load() throws DecodeException {\n\t\tif (loaded) {\n\t\t\t// method already loaded\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tloaded = true;\n\t\t\tif (noCode) {\n\t\t\t\tregsCount = 0;\n\t\t\t\t// TODO: registers not needed without code\n\t\t\t\tinitArguments(this.argTypes);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.regsCount = codeReader.getRegistersCount();\n\t\t\tthis.argsStartReg = codeReader.getArgsStartReg();\n\t\t\tinitArguments(this.argTypes);\n\n\t\t\tif (contains(AType.JADX_ERROR)) {\n\t\t\t\t// don't load instructions for method with errors\n\t\t\t\tthis.instructions = EMPTY_INSN_ARRAY;\n\t\t\t} else {\n\t\t\t\tInsnDecoder decoder = new InsnDecoder(this);\n\t\t\t\tthis.instructions = decoder.process(codeReader);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tif (!noCode) {\n\t\t\t\tunload();\n\t\t\t\tnoCode = true;\n\t\t\t\t// load without code\n\t\t\t\tload();\n\t\t\t\tnoCode = false;\n\t\t\t}\n\t\t\tthrow new DecodeException(this, \"Load method exception: \"\n\t\t\t\t\t+ e.getClass().getSimpleName() + \": \" + e.getMessage(), e);\n\t\t}\n\t}\n\n\tpublic void reload() {\n\t\tunload();\n\t\ttry {\n\t\t\tload();\n\t\t} catch (DecodeException e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to reload method \" + getClass().getName() + \".\" + getName());\n\t\t}\n\t}\n\n\tprivate void initArguments(List<ArgType> args) {\n\t\tint pos = getArgsStartPos(args);\n\t\tTypeUtils typeUtils = root().getTypeUtils();\n\t\tif (accFlags.isStatic()) {\n\t\t\tthisArg = null;\n\t\t} else {\n\t\t\tArgType thisClsType = typeUtils.expandTypeVariables(this, parentClass.getType());\n\t\t\tRegisterArg arg = InsnArg.reg(pos++, thisClsType);\n\t\t\targ.add(AFlag.THIS);\n\t\t\targ.add(AFlag.IMMUTABLE_TYPE);\n\t\t\tthisArg = arg;\n\t\t}\n\t\tif (args.isEmpty()) {\n\t\t\targsList = Collections.emptyList();\n\t\t\treturn;\n\t\t}\n\t\targsList = new ArrayList<>(args.size());\n\t\tfor (ArgType argType : args) {\n\t\t\tArgType expandedType = typeUtils.expandTypeVariables(this, argType);\n\t\t\tRegisterArg regArg = InsnArg.reg(pos, expandedType);\n\t\t\tregArg.add(AFlag.METHOD_ARGUMENT);\n\t\t\tregArg.add(AFlag.IMMUTABLE_TYPE);\n\t\t\targsList.add(regArg);\n\t\t\tpos += argType.getRegCount();\n\t\t}\n\t}\n\n\tprivate int getArgsStartPos(List<ArgType> args) {\n\t\tif (noCode) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (argsStartReg != -1) {\n\t\t\treturn argsStartReg;\n\t\t}\n\t\tint pos = regsCount;\n\t\tfor (ArgType arg : args) {\n\t\t\tpos -= arg.getRegCount();\n\t\t}\n\t\tif (!accFlags.isStatic()) {\n\t\t\tpos--;\n\t\t}\n\t\treturn pos;\n\t}\n\n\t@Override\n\t@NotNull\n\tpublic List<ArgType> getArgTypes() {\n\t\tif (argTypes == null) {\n\t\t\tthrow new JadxRuntimeException(\"Method generic types not initialized: \" + this);\n\t\t}\n\t\treturn argTypes;\n\t}\n\n\tpublic void updateArgTypes(List<ArgType> newArgTypes, String comment) {\n\t\tthis.addDebugComment(comment + \", original types: \" + getArgTypes());\n\t\tthis.argTypes = Collections.unmodifiableList(newArgTypes);\n\t\tinitArguments(newArgTypes);\n\t}\n\n\tpublic boolean containsGenericArgs() {\n\t\treturn !Objects.equals(mthInfo.getArgumentsTypes(), getArgTypes());\n\t}\n\n\t@Override\n\t@NotNull\n\tpublic ArgType getReturnType() {\n\t\treturn retType;\n\t}\n\n\tpublic void updateReturnType(ArgType type) {\n\t\tthis.retType = type;\n\t}\n\n\tpublic boolean isVoidReturn() {\n\t\treturn mthInfo.getReturnType().equals(ArgType.VOID);\n\t}\n\n\tpublic List<VarNode> collectArgNodes() {\n\t\tICodeInfo codeInfo = getTopParentClass().getCode();\n\t\tint mthDefPos = getDefPosition();\n\t\tint lineEndPos = CodeUtils.getLineEndForPos(codeInfo.getCodeStr(), mthDefPos);\n\t\tint argsCount = mthInfo.getArgsCount();\n\t\tList<VarNode> args = new ArrayList<>(argsCount);\n\t\tcodeInfo.getCodeMetadata().searchDown(mthDefPos, (pos, ann) -> {\n\t\t\tif (pos > lineEndPos) {\n\t\t\t\t// Stop at line end\n\t\t\t\treturn Boolean.TRUE;\n\t\t\t}\n\t\t\tif (ann instanceof NodeDeclareRef) {\n\t\t\t\tICodeNodeRef declRef = ((NodeDeclareRef) ann).getNode();\n\t\t\t\tif (declRef instanceof VarNode) {\n\t\t\t\t\tVarNode varNode = (VarNode) declRef;\n\t\t\t\t\tif (!varNode.getMth().equals(this)) {\n\t\t\t\t\t\t// Stop if we've gone too far and have entered a different method\n\t\t\t\t\t\treturn Boolean.TRUE;\n\t\t\t\t\t}\n\t\t\t\t\targs.add(varNode);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t\tif (args.size() != argsCount) {\n\t\t\tLOG.warn(\"Incorrect args count, expected: {}, got: {}\", argsCount, args.size());\n\t\t}\n\t\treturn args;\n\t}\n\n\tpublic List<RegisterArg> getArgRegs() {\n\t\tif (argsList == null) {\n\t\t\tthrow new JadxRuntimeException(\"Method arg registers not loaded: \" + this\n\t\t\t\t\t+ \", class status: \" + parentClass.getTopParentClass().getState());\n\t\t}\n\t\treturn argsList;\n\t}\n\n\tpublic List<RegisterArg> getAllArgRegs() {\n\t\tList<RegisterArg> argRegs = getArgRegs();\n\t\tif (thisArg != null) {\n\t\t\tList<RegisterArg> list = new ArrayList<>(argRegs.size() + 1);\n\t\t\tlist.add(thisArg);\n\t\t\tlist.addAll(argRegs);\n\t\t\treturn list;\n\t\t}\n\t\treturn argRegs;\n\t}\n\n\t@Nullable\n\tpublic RegisterArg getThisArg() {\n\t\treturn thisArg;\n\t}\n\n\tpublic void skipFirstArgument() {\n\t\tthis.add(AFlag.SKIP_FIRST_ARG);\n\t}\n\n\t@Override\n\tpublic List<ArgType> getTypeParameters() {\n\t\treturn typeParameters;\n\t}\n\n\tpublic String getName() {\n\t\treturn mthInfo.getName();\n\t}\n\n\tpublic String getAlias() {\n\t\treturn mthInfo.getAlias();\n\t}\n\n\t@Override\n\tpublic ClassNode getDeclaringClass() {\n\t\treturn parentClass;\n\t}\n\n\tpublic ClassNode getParentClass() {\n\t\treturn parentClass;\n\t}\n\n\tpublic ClassNode getTopParentClass() {\n\t\treturn parentClass.getTopParentClass();\n\t}\n\n\tpublic boolean isNoCode() {\n\t\treturn noCode;\n\t}\n\n\tpublic InsnNode[] getInstructions() {\n\t\treturn instructions;\n\t}\n\n\tpublic void unloadInsnArr() {\n\t\tthis.instructions = null;\n\t}\n\n\tpublic void initBasicBlocks() {\n\t\tblocks = new ArrayList<>();\n\t}\n\n\tpublic void finishBasicBlocks() {\n\t\tblocks = lockList(blocks);\n\t\tloops = lockList(loops);\n\t\tblocks.forEach(BlockNode::lock);\n\t}\n\n\tpublic List<BlockNode> getBasicBlocks() {\n\t\treturn blocks;\n\t}\n\n\tpublic void setBasicBlocks(List<BlockNode> blocks) {\n\t\tthis.blocks = blocks;\n\t\tupdateBlockPositions();\n\t}\n\n\tpublic void updateBlockPositions() {\n\t\tBlockNode.updateBlockPositions(blocks);\n\t}\n\n\tpublic int getNextBlockCId() {\n\t\treturn blocksMaxCId++;\n\t}\n\n\tpublic BlockNode getEnterBlock() {\n\t\treturn enterBlock;\n\t}\n\n\tpublic void setEnterBlock(BlockNode enterBlock) {\n\t\tthis.enterBlock = enterBlock;\n\t}\n\n\tpublic BlockNode getExitBlock() {\n\t\treturn exitBlock;\n\t}\n\n\tpublic void setExitBlock(BlockNode exitBlock) {\n\t\tthis.exitBlock = exitBlock;\n\t}\n\n\tpublic List<BlockNode> getPreExitBlocks() {\n\t\treturn exitBlock.getPredecessors();\n\t}\n\n\tpublic boolean isPreExitBlock(BlockNode block) {\n\t\tList<BlockNode> successors = block.getSuccessors();\n\t\tif (successors.size() == 1) {\n\t\t\treturn successors.get(0).equals(exitBlock);\n\t\t}\n\t\treturn exitBlock.getPredecessors().contains(block);\n\t}\n\n\tpublic void resetLoops() {\n\t\tthis.loops = new ArrayList<>();\n\t}\n\n\tpublic void registerLoop(LoopInfo loop) {\n\t\tif (loops.isEmpty()) {\n\t\t\tloops = new ArrayList<>(5);\n\t\t}\n\t\tloop.setId(loops.size());\n\t\tloops.add(loop);\n\t}\n\n\t@Nullable\n\tpublic LoopInfo getLoopForBlock(BlockNode block) {\n\t\tif (loops.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tfor (LoopInfo loop : loops) {\n\t\t\tif (loop.getLoopBlocks().contains(block)) {\n\t\t\t\treturn loop;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic List<LoopInfo> getAllLoopsForBlock(BlockNode block) {\n\t\tif (loops.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<LoopInfo> list = new ArrayList<>(loops.size());\n\t\tfor (LoopInfo loop : loops) {\n\t\t\tif (loop.getLoopBlocks().contains(block)) {\n\t\t\t\tlist.add(loop);\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic int getLoopsCount() {\n\t\treturn loops.size();\n\t}\n\n\tpublic Iterable<LoopInfo> getLoops() {\n\t\treturn loops;\n\t}\n\n\tpublic ExceptionHandler addExceptionHandler(ExceptionHandler handler) {\n\t\tif (exceptionHandlers.isEmpty()) {\n\t\t\texceptionHandlers = new ArrayList<>(2);\n\t\t}\n\t\texceptionHandlers.add(handler);\n\t\treturn handler;\n\t}\n\n\tpublic boolean clearExceptionHandlers() {\n\t\treturn exceptionHandlers.removeIf(ExceptionHandler::isRemoved);\n\t}\n\n\tpublic Iterable<ExceptionHandler> getExceptionHandlers() {\n\t\treturn exceptionHandlers;\n\t}\n\n\tpublic boolean isNoExceptionHandlers() {\n\t\treturn exceptionHandlers.isEmpty();\n\t}\n\n\tpublic int getExceptionHandlersCount() {\n\t\treturn exceptionHandlers.size();\n\t}\n\n\t@Override\n\tpublic List<ArgType> getThrows() {\n\t\tMethodThrowsAttr throwsAttr = get(AType.METHOD_THROWS);\n\t\tif (throwsAttr != null) {\n\t\t\treturn Utils.collectionMap(throwsAttr.getList(), ArgType::object);\n\t\t}\n\t\tExceptionsAttr exceptionsAttr = get(JadxAttrType.EXCEPTIONS);\n\t\tif (exceptionsAttr != null) {\n\t\t\treturn Utils.collectionMap(exceptionsAttr.getList(), ArgType::object);\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\t/**\n\t * Return true if exists method with same name and arguments count\n\t */\n\tpublic boolean isArgsOverloaded() {\n\t\tMethodInfo thisMthInfo = this.mthInfo;\n\t\t// quick check in current class\n\t\tfor (MethodNode method : parentClass.getMethods()) {\n\t\t\tif (method == this) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (method.getMethodInfo().isOverloadedBy(thisMthInfo)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn root().getMethodUtils().isMethodArgsOverloaded(parentClass.getClassInfo().getType(), thisMthInfo);\n\t}\n\n\tpublic boolean isConstructor() {\n\t\treturn accFlags.isConstructor() && mthInfo.isConstructor();\n\t}\n\n\tpublic boolean isDefaultConstructor() {\n\t\tif (isConstructor()) {\n\t\t\tint defaultArgCount = 0;\n\t\t\t// workaround for non-static inner class constructor, that has synthetic argument\n\t\t\tif (parentClass.getClassInfo().isInner()\n\t\t\t\t\t&& !parentClass.getAccessFlags().isStatic()) {\n\t\t\t\tClassNode outerCls = parentClass.getParentClass();\n\t\t\t\tif (argsList != null && !argsList.isEmpty()\n\t\t\t\t\t\t&& argsList.get(0).getInitType().equals(outerCls.getClassInfo().getType())) {\n\t\t\t\t\tdefaultArgCount = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn argsList == null || argsList.size() == defaultArgCount;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic int getRegsCount() {\n\t\treturn regsCount;\n\t}\n\n\tpublic int getArgsStartReg() {\n\t\treturn argsStartReg;\n\t}\n\n\t/**\n\t * Create new fake register arg.\n\t */\n\tpublic RegisterArg makeSyntheticRegArg(ArgType type) {\n\t\tRegisterArg arg = InsnArg.reg(0, type);\n\t\targ.add(AFlag.SYNTHETIC);\n\t\tSSAVar ssaVar = makeNewSVar(arg);\n\t\tInitCodeVariables.initCodeVar(ssaVar);\n\t\tssaVar.setType(type);\n\t\treturn arg;\n\t}\n\n\tpublic RegisterArg makeSyntheticRegArg(ArgType type, String name) {\n\t\tRegisterArg arg = makeSyntheticRegArg(type);\n\t\targ.setName(name);\n\t\treturn arg;\n\t}\n\n\tpublic SSAVar makeNewSVar(@NotNull RegisterArg assignArg) {\n\t\tint regNum = assignArg.getRegNum();\n\t\treturn makeNewSVar(regNum, getNextSVarVersion(regNum), assignArg);\n\t}\n\n\tpublic SSAVar makeNewSVar(int regNum, int version, @NotNull RegisterArg assignArg) {\n\t\tSSAVar var = new SSAVar(regNum, version, assignArg);\n\t\tif (sVars.isEmpty()) {\n\t\t\tsVars = new ArrayList<>();\n\t\t}\n\t\tsVars.add(var);\n\t\treturn var;\n\t}\n\n\tprivate int getNextSVarVersion(int regNum) {\n\t\tint v = -1;\n\t\tfor (SSAVar sVar : sVars) {\n\t\t\tif (sVar.getRegNum() == regNum) {\n\t\t\t\tv = Math.max(v, sVar.getVersion());\n\t\t\t}\n\t\t}\n\t\tv++;\n\t\treturn v;\n\t}\n\n\tpublic void removeSVar(SSAVar var) {\n\t\tsVars.remove(var);\n\t}\n\n\tpublic List<SSAVar> getSVars() {\n\t\treturn sVars;\n\t}\n\n\t@Override\n\tpublic int getRawAccessFlags() {\n\t\treturn accFlags.rawValue();\n\t}\n\n\t@Override\n\tpublic AccessInfo getAccessFlags() {\n\t\treturn accFlags;\n\t}\n\n\t@Override\n\tpublic void setAccessFlags(AccessInfo newAccessFlags) {\n\t\tthis.accFlags = newAccessFlags;\n\t}\n\n\tpublic Region getRegion() {\n\t\treturn region;\n\t}\n\n\tpublic void setRegion(Region region) {\n\t\tthis.region = region;\n\t}\n\n\t@Override\n\tpublic RootNode root() {\n\t\treturn parentClass.root();\n\t}\n\n\t@Override\n\tpublic String typeName() {\n\t\treturn \"method\";\n\t}\n\n\t@Override\n\tpublic String getInputFileName() {\n\t\treturn parentClass.getInputFileName();\n\t}\n\n\t@Override\n\tpublic MethodInfo getMethodInfo() {\n\t\treturn mthInfo;\n\t}\n\n\tpublic long getMethodCodeOffset() {\n\t\treturn noCode ? 0 : codeReader.getCodeOffset();\n\t}\n\n\t@Nullable\n\tpublic IDebugInfo getDebugInfo() {\n\t\treturn noCode ? null : codeReader.getDebugInfo();\n\t}\n\n\tpublic void ignoreMethod() {\n\t\tadd(AFlag.DONT_GENERATE);\n\t\tnoCode = true;\n\t}\n\n\t@Override\n\tpublic void rename(String newName) {\n\t\tMethodOverrideAttr overrideAttr = get(AType.METHOD_OVERRIDE);\n\t\tif (overrideAttr != null) {\n\t\t\tfor (MethodNode relatedMth : overrideAttr.getRelatedMthNodes()) {\n\t\t\t\trelatedMth.getMethodInfo().setAlias(newName);\n\t\t\t}\n\t\t} else {\n\t\t\tmthInfo.setAlias(newName);\n\t\t}\n\t}\n\n\t/**\n\t * Calculate instructions count at current stage\n\t */\n\tpublic long countInsns() {\n\t\tif (instructions != null) {\n\t\t\treturn instructions.length;\n\t\t}\n\t\tif (blocks != null) {\n\t\t\treturn blocks.stream().mapToLong(block -> block.getInstructions().size()).sum();\n\t\t}\n\t\treturn -1;\n\t}\n\n\t/**\n\t * Raw instructions count in method bytecode\n\t */\n\tpublic int getInsnsCount() {\n\t\treturn insnsCount;\n\t}\n\n\t/**\n\t * Returns method code with comments and annotations\n\t */\n\tpublic String getCodeStr() {\n\t\treturn CodeUtils.extractMethodCode(this, getTopParentClass().getCode());\n\t}\n\n\t@Override\n\tpublic boolean isVarArg() {\n\t\treturn accFlags.isVarArgs();\n\t}\n\n\tpublic boolean isLoaded() {\n\t\treturn loaded;\n\t}\n\n\tpublic @Nullable ICodeReader getCodeReader() {\n\t\treturn codeReader;\n\t}\n\n\t// Cannot modify through get, use setUseIn\n\tpublic List<MethodNode> getUseIn() {\n\t\treturn Collections.unmodifiableList(useIn);\n\t}\n\n\t// Do not modify passed list after setting\n\tpublic void setUseIn(List<MethodNode> useIn) {\n\t\tthis.useIn = useIn;\n\n\t\t// Notify all methods (callers) this method (calee) is used in\n\t\tfor (MethodNode methodUsedIn : useIn) {\n\t\t\tmethodUsedIn.addUsed(this);\n\t\t}\n\t}\n\n\tpublic void addUsed(MethodNode used) {\n\t\tif (used != null) {\n\t\t\tthis.methodsUsed.add(used);\n\t\t}\n\t}\n\n\tpublic void setUsed(List<MethodNode> methodsUsed) {\n\t\tthis.methodsUsed = new HashSet<>(methodsUsed);\n\t}\n\n\tpublic Set<MethodNode> getUsed() {\n\t\tthis.removeInavlidMethodsUsed();\n\t\treturn methodsUsed;\n\t}\n\n\tpublic List<IMethodRef> getUnresolvedUsed() {\n\t\treturn unresolvedUsed;\n\t}\n\n\tpublic void setUnresolvedUsed(List<IMethodRef> unresolvedUsed) {\n\t\tthis.unresolvedUsed = unresolvedUsed;\n\t}\n\n\tpublic void setCallsSelf(boolean callsSelf) {\n\t\tthis.callsSelf = callsSelf;\n\t}\n\n\tpublic boolean callsSelf() {\n\t\treturn this.callsSelf;\n\t}\n\n\t// Remove any methods from the list of used methods (calees) if this method (caller) has been\n\t// removed from the calee's list of callers\n\tprivate void removeInavlidMethodsUsed() {\n\t\tfor (MethodNode methodUsed : new ArrayList<>(methodsUsed)) {\n\t\t\tif (!methodUsed.getUseIn().contains(this)) {\n\t\t\t\tmethodsUsed.remove(methodUsed);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic JavaMethod getJavaNode() {\n\t\treturn javaNode;\n\t}\n\n\t@ApiStatus.Internal\n\tpublic void setJavaNode(JavaMethod javaNode) {\n\t\tthis.javaNode = javaNode;\n\t}\n\n\t@Override\n\tpublic AnnType getAnnType() {\n\t\treturn AnnType.METHOD;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn mthInfo.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tMethodNode other = (MethodNode) obj;\n\t\treturn mthInfo.equals(other.mthInfo);\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull MethodNode o) {\n\t\treturn mthInfo.compareTo(o.mthInfo);\n\t}\n\n\t@Override\n\tpublic String toAttrString() {\n\t\treturn IMethodDetails.super.toAttrString() + \" (m)\";\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn parentClass + \".\" + mthInfo.getName()\n\t\t\t\t+ '(' + Utils.listToString(argTypes) + \"):\"\n\t\t\t\t+ retType;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/PackageNode.java",
    "content": "package jadx.core.dex.nodes;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.JavaPackage;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.core.dex.attributes.nodes.LineAttrNode;\nimport jadx.core.dex.info.PackageInfo;\n\nimport static jadx.core.utils.StringUtils.containsChar;\n\npublic class PackageNode extends LineAttrNode\n\t\timplements IPackageUpdate, IDexNode, ICodeNodeRef, Comparable<PackageNode> {\n\n\tprivate final RootNode root;\n\tprivate final PackageInfo pkgInfo;\n\tprivate final @Nullable PackageNode parentPkg;\n\tprivate final List<PackageNode> subPackages = new ArrayList<>();\n\tprivate final List<ClassNode> classes = new ArrayList<>();\n\n\tprivate PackageInfo aliasPkgInfo;\n\n\tprivate JavaPackage javaNode;\n\n\tpublic static PackageNode getForClass(RootNode root, String fullPkg, ClassNode cls) {\n\t\tPackageNode pkg = getOrBuild(root, fullPkg);\n\t\tpkg.getClasses().add(cls);\n\t\treturn pkg;\n\t}\n\n\tpublic static PackageNode getOrBuild(RootNode root, String fullPkg) {\n\t\tPackageNode existPkg = root.resolvePackage(fullPkg);\n\t\tif (existPkg != null) {\n\t\t\treturn existPkg;\n\t\t}\n\t\tPackageInfo pgkInfo = PackageInfo.fromFullPkg(root, fullPkg);\n\t\tPackageNode parentPkg = getParentPkg(root, pgkInfo);\n\t\tPackageNode pkgNode = new PackageNode(root, parentPkg, pgkInfo);\n\t\tif (parentPkg != null) {\n\t\t\tparentPkg.getSubPackages().add(pkgNode);\n\t\t}\n\t\troot.addPackage(pkgNode);\n\t\treturn pkgNode;\n\t}\n\n\tprivate static @Nullable PackageNode getParentPkg(RootNode root, PackageInfo pgkInfo) {\n\t\tPackageInfo parentPkg = pgkInfo.getParentPkg();\n\t\tif (parentPkg == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn getOrBuild(root, parentPkg.getFullName());\n\t}\n\n\tprivate PackageNode(RootNode root, @Nullable PackageNode parentPkg, PackageInfo pkgInfo) {\n\t\tthis.root = root;\n\t\tthis.parentPkg = parentPkg;\n\t\tthis.pkgInfo = pkgInfo;\n\t\tthis.aliasPkgInfo = pkgInfo;\n\t}\n\n\t@Override\n\tpublic void rename(String newName) {\n\t\trename(newName, true);\n\t}\n\n\tpublic void rename(String newName, boolean runUpdates) {\n\t\tString alias;\n\t\tboolean isFullAlias;\n\t\tif (containsChar(newName, '/')) {\n\t\t\talias = newName.replace('/', '.');\n\t\t\tisFullAlias = true;\n\t\t} else if (newName.startsWith(\".\")) {\n\t\t\t// treat as full pkg, remove start dot\n\t\t\talias = newName.substring(1);\n\t\t\tisFullAlias = true;\n\t\t} else {\n\t\t\talias = newName;\n\t\t\tisFullAlias = containsChar(newName, '.');\n\t\t}\n\t\tif (isFullAlias) {\n\t\t\tsetFullAlias(alias, runUpdates);\n\t\t} else {\n\t\t\tsetLeafAlias(alias, runUpdates);\n\t\t}\n\t}\n\n\tpublic void setLeafAlias(String alias, boolean runUpdates) {\n\t\tif (pkgInfo.getName().equals(alias)) {\n\t\t\taliasPkgInfo = pkgInfo;\n\t\t} else {\n\t\t\taliasPkgInfo = PackageInfo.fromShortName(root, getParentAliasPkgInfo(), alias);\n\t\t}\n\t\tif (runUpdates) {\n\t\t\tupdatePackages(this);\n\t\t}\n\t}\n\n\tpublic void setFullAlias(String fullAlias, boolean runUpdates) {\n\t\tif (pkgInfo.getFullName().equals(fullAlias)) {\n\t\t\taliasPkgInfo = pkgInfo;\n\t\t} else {\n\t\t\taliasPkgInfo = PackageInfo.fromFullPkg(root, fullAlias);\n\t\t}\n\t\tif (runUpdates) {\n\t\t\tupdatePackages(this);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onParentPackageUpdate(PackageNode updatedPkg) {\n\t\taliasPkgInfo = PackageInfo.fromShortName(root, getParentAliasPkgInfo(), aliasPkgInfo.getName());\n\t\tupdatePackages(updatedPkg);\n\t}\n\n\tpublic void updatePackages() {\n\t\tupdatePackages(this);\n\t}\n\n\tprivate void updatePackages(PackageNode updatedPkg) {\n\t\tfor (PackageNode subPackage : subPackages) {\n\t\t\tsubPackage.onParentPackageUpdate(updatedPkg);\n\t\t}\n\t\tfor (ClassNode cls : classes) {\n\t\t\tcls.onParentPackageUpdate(updatedPkg);\n\t\t}\n\t}\n\n\tpublic String getName() {\n\t\treturn pkgInfo.getName();\n\t}\n\n\tpublic String getFullName() {\n\t\treturn pkgInfo.getFullName();\n\t}\n\n\tpublic PackageInfo getPkgInfo() {\n\t\treturn pkgInfo;\n\t}\n\n\tpublic PackageInfo getAliasPkgInfo() {\n\t\treturn aliasPkgInfo;\n\t}\n\n\tpublic boolean hasAlias() {\n\t\tif (pkgInfo == aliasPkgInfo) {\n\t\t\treturn false;\n\t\t}\n\t\treturn !pkgInfo.getName().equals(aliasPkgInfo.getName());\n\t}\n\n\tpublic boolean hasParentAlias() {\n\t\tif (pkgInfo == aliasPkgInfo) {\n\t\t\treturn false;\n\t\t}\n\t\treturn !Objects.equals(pkgInfo.getParentPkg(), aliasPkgInfo.getParentPkg());\n\t}\n\n\tpublic void removeAlias() {\n\t\taliasPkgInfo = pkgInfo;\n\t}\n\n\tpublic @Nullable PackageNode getParentPkg() {\n\t\treturn parentPkg;\n\t}\n\n\tpublic @Nullable PackageInfo getParentAliasPkgInfo() {\n\t\treturn parentPkg == null ? null : parentPkg.aliasPkgInfo;\n\t}\n\n\tpublic boolean isRoot() {\n\t\treturn parentPkg == null;\n\t}\n\n\tpublic boolean isLeaf() {\n\t\treturn subPackages.isEmpty();\n\t}\n\n\tpublic List<PackageNode> getSubPackages() {\n\t\treturn subPackages;\n\t}\n\n\tpublic List<ClassNode> getClasses() {\n\t\treturn classes;\n\t}\n\n\tpublic List<ClassNode> getClassesNoDup() {\n\t\treturn classes.stream()\n\t\t\t\t.map(ClassNode::getClassInfo)\n\t\t\t\t.collect(Collectors.toSet())\n\t\t\t\t.stream()\n\t\t\t\t.map(e -> root.resolveClass(e)).collect(Collectors.toList());\n\t}\n\n\tpublic JavaPackage getJavaNode() {\n\t\treturn javaNode;\n\t}\n\n\tpublic void setJavaNode(JavaPackage javaNode) {\n\t\tthis.javaNode = javaNode;\n\t}\n\n\tpublic boolean isEmpty() {\n\t\treturn classes.isEmpty() && subPackages.isEmpty();\n\t}\n\n\t@Override\n\tpublic String typeName() {\n\t\treturn \"package\";\n\t}\n\n\t@Override\n\tpublic AnnType getAnnType() {\n\t\treturn AnnType.PKG;\n\t}\n\n\t@Override\n\tpublic RootNode root() {\n\t\treturn root;\n\t}\n\n\t@Override\n\tpublic String getInputFileName() {\n\t\treturn \"\";\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof PackageNode)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn pkgInfo.equals(((PackageNode) o).pkgInfo);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn pkgInfo.hashCode();\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull PackageNode other) {\n\t\treturn getPkgInfo().getFullName().compareTo(other.getPkgInfo().getFullName());\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getPkgInfo().getFullName();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/ProcessState.java",
    "content": "package jadx.core.dex.nodes;\n\npublic enum ProcessState {\n\tNOT_LOADED,\n\tLOADED,\n\tPROCESS_STARTED,\n\tPROCESS_COMPLETE,\n\tGENERATED_AND_UNLOADED;\n\n\tpublic boolean isProcessComplete() {\n\t\treturn this == PROCESS_COMPLETE || this == GENERATED_AND_UNLOADED;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java",
    "content": "package jadx.core.dex.nodes;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.DecompilationMode;\nimport jadx.api.ICodeCache;\nimport jadx.api.ICodeWriter;\nimport jadx.api.JadxArgs;\nimport jadx.api.JadxDecompiler;\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourceType;\nimport jadx.api.ResourcesLoader;\nimport jadx.api.data.ICodeData;\nimport jadx.api.impl.passes.DecompilePassWrapper;\nimport jadx.api.impl.passes.PreparePassWrapper;\nimport jadx.api.plugins.input.ICodeLoader;\nimport jadx.api.plugins.input.data.IClassData;\nimport jadx.api.plugins.pass.JadxPass;\nimport jadx.api.plugins.pass.types.JadxDecompilePass;\nimport jadx.api.plugins.pass.types.JadxPassType;\nimport jadx.api.plugins.pass.types.JadxPreparePass;\nimport jadx.core.Jadx;\nimport jadx.core.ProcessClass;\nimport jadx.core.clsp.ClspGraph;\nimport jadx.core.dex.attributes.AttributeStorage;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.ConstStorage;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.info.InfoStorage;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.info.PackageInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.utils.MethodUtils;\nimport jadx.core.dex.nodes.utils.SelectFromDuplicates;\nimport jadx.core.dex.nodes.utils.TypeUtils;\nimport jadx.core.dex.visitors.DepthTraversal;\nimport jadx.core.dex.visitors.IDexTreeVisitor;\nimport jadx.core.dex.visitors.typeinference.TypeCompare;\nimport jadx.core.dex.visitors.typeinference.TypeUpdate;\nimport jadx.core.export.GradleInfoStorage;\nimport jadx.core.utils.CacheStorage;\nimport jadx.core.utils.DebugChecks;\nimport jadx.core.utils.ErrorsCounter;\nimport jadx.core.utils.PassMerge;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.android.AndroidResourcesUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.xmlgen.IResTableParser;\nimport jadx.core.xmlgen.ManifestAttributes;\nimport jadx.core.xmlgen.ResourceStorage;\nimport jadx.core.xmlgen.entry.ResourceEntry;\nimport jadx.core.xmlgen.entry.ValuesParser;\n\npublic class RootNode {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(RootNode.class);\n\n\tprivate final JadxArgs args;\n\tprivate final ErrorsCounter errorsCounter = new ErrorsCounter();\n\tprivate final StringUtils stringUtils;\n\tprivate final ConstStorage constValues;\n\tprivate final InfoStorage infoStorage = new InfoStorage();\n\tprivate final CacheStorage cacheStorage = new CacheStorage();\n\tprivate final TypeUpdate typeUpdate;\n\tprivate final MethodUtils methodUtils;\n\tprivate final TypeUtils typeUtils;\n\tprivate final AttributeStorage attributes = new AttributeStorage();\n\n\tprivate final List<ICodeDataUpdateListener> codeDataUpdateListeners = new ArrayList<>();\n\tprivate final GradleInfoStorage gradleInfoStorage = new GradleInfoStorage();\n\n\tprivate final Map<ClassInfo, ClassNode> clsMap = new HashMap<>();\n\tprivate final Map<String, ClassNode> rawClsMap = new HashMap<>();\n\tprivate List<ClassNode> classes = new ArrayList<>();\n\n\tprivate final Map<String, PackageNode> pkgMap = new HashMap<>();\n\tprivate final List<PackageNode> packages = new ArrayList<>();\n\n\tprivate List<IDexTreeVisitor> preDecompilePasses;\n\tprivate ProcessClass processClasses;\n\n\tprivate ClspGraph clsp;\n\tprivate @Nullable String appPackage;\n\tprivate @Nullable ClassNode appResClass;\n\n\t/**\n\t * Optional decompiler reference\n\t */\n\tprivate @Nullable JadxDecompiler decompiler;\n\n\tprivate @Nullable ManifestAttributes manifestAttributes;\n\n\tpublic RootNode(JadxDecompiler decompiler) {\n\t\tthis(decompiler, decompiler.getArgs());\n\t}\n\n\t/**\n\t * Deprecated. Prefer {@link #RootNode(JadxDecompiler)}\n\t */\n\t@Deprecated\n\tpublic RootNode(JadxArgs args) {\n\t\tthis(null, args);\n\t}\n\n\tprivate RootNode(@Nullable JadxDecompiler decompiler, JadxArgs args) {\n\t\tthis.decompiler = decompiler;\n\t\tthis.args = args;\n\t\tthis.preDecompilePasses = Jadx.getPreDecompilePassesList();\n\t\tthis.processClasses = new ProcessClass(Jadx.getPassesList(args));\n\t\tthis.stringUtils = new StringUtils(args);\n\t\tthis.constValues = new ConstStorage(args);\n\t\tthis.typeUpdate = new TypeUpdate(this);\n\t\tthis.methodUtils = new MethodUtils(this);\n\t\tthis.typeUtils = new TypeUtils(this);\n\t}\n\n\tpublic void init() {\n\t\tif (args.isDeobfuscationOn() || !args.getRenameFlags().isEmpty()) {\n\t\t\targs.getAliasProvider().init(this);\n\t\t}\n\t\tif (args.isDeobfuscationOn()) {\n\t\t\targs.getRenameCondition().init(this);\n\t\t}\n\t}\n\n\tpublic void loadClasses(List<ICodeLoader> loadedInputs) {\n\t\tfor (ICodeLoader codeLoader : loadedInputs) {\n\t\t\tcodeLoader.visitClasses(cls -> {\n\t\t\t\ttry {\n\t\t\t\t\taddClassNode(new ClassNode(RootNode.this, cls));\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\taddDummyClass(cls, e);\n\t\t\t\t}\n\t\t\t\tUtils.checkThreadInterrupt();\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic void finishClassLoad() {\n\t\tif (classes.size() != clsMap.size()) {\n\t\t\t// class name duplication detected\n\t\t\tfixDuplicatedClasses();\n\t\t}\n\t\tclasses = new ArrayList<>(clsMap.values());\n\n\t\t// print stats for loaded classes\n\t\tint mthCount = classes.stream().mapToInt(c -> c.getMethods().size()).sum();\n\t\tint insnsCount = classes.stream().flatMap(c -> c.getMethods().stream()).mapToInt(MethodNode::getInsnsCount).sum();\n\t\tLOG.info(\"Loaded classes: {}, methods: {}, instructions: {}\", classes.size(), mthCount, insnsCount);\n\n\t\t// sort classes by name, expect top classes before inner\n\t\tclasses.sort(Comparator.comparing(ClassNode::getRawName));\n\n\t\tif (args.isMoveInnerClasses()) {\n\t\t\t// detect and move inner classes\n\t\t\tinitInnerClasses();\n\t\t}\n\t\t// sort packages\n\t\tCollections.sort(packages);\n\t}\n\n\tprivate void addDummyClass(IClassData classData, Exception exc) {\n\t\ttry {\n\t\t\tString typeStr = classData.getType();\n\t\t\tString name = null;\n\t\t\ttry {\n\t\t\t\tClassInfo clsInfo = ClassInfo.fromName(this, typeStr);\n\t\t\t\tif (clsInfo != null) {\n\t\t\t\t\tname = clsInfo.getShortName();\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to get name for class with type {}\", typeStr, e);\n\t\t\t}\n\t\t\tif (name == null || name.isEmpty()) {\n\t\t\t\tname = \"CLASS_\" + typeStr;\n\t\t\t}\n\t\t\tClassNode clsNode = ClassNode.addSyntheticClass(this, name, classData.getAccessFlags());\n\t\t\tErrorsCounter.error(clsNode, \"Load error\", exc);\n\t\t} catch (Exception innerExc) {\n\t\t\tLOG.error(\"Failed to load class from file: {}\", classData.getInputFileName(), exc);\n\t\t}\n\t}\n\n\tprivate void fixDuplicatedClasses() {\n\t\tclasses.stream()\n\t\t\t\t.collect(Collectors.groupingBy(ClassNode::getClassInfo))\n\t\t\t\t.entrySet()\n\t\t\t\t.stream()\n\t\t\t\t.filter(entry -> entry.getValue().size() > 1)\n\t\t\t\t.forEach(entry -> {\n\t\t\t\t\tClassInfo clsInfo = entry.getKey();\n\t\t\t\t\tList<ClassNode> dupClsList = entry.getValue();\n\t\t\t\t\tClassNode selectedCls = SelectFromDuplicates.process(dupClsList);\n\n\t\t\t\t\t// keep only selected class in classes maps\n\t\t\t\t\tclsMap.put(clsInfo, selectedCls);\n\t\t\t\t\trawClsMap.put(selectedCls.getRawName(), selectedCls);\n\n\t\t\t\t\tString selectedSource = selectedCls.getInputFileName();\n\t\t\t\t\tString sources = dupClsList.stream()\n\t\t\t\t\t\t\t.map(ClassNode::getInputFileName)\n\t\t\t\t\t\t\t.sorted()\n\t\t\t\t\t\t\t.collect(Collectors.joining(\"\\n  \"));\n\t\t\t\t\tLOG.warn(\"Found duplicated class: {}, count: {}, sources:\"\n\t\t\t\t\t\t\t+ \"\\n  {}\\n Keep class with source: {}, others will be removed.\",\n\t\t\t\t\t\t\tclsInfo, dupClsList.size(), sources, selectedSource);\n\t\t\t\t\tselectedCls.addWarnComment(\"Classes with same name are omitted, all sources:\\n  \" + sources + '\\n');\n\t\t\t\t});\n\t}\n\n\tpublic void addClassNode(ClassNode clsNode) {\n\t\tclasses.add(clsNode);\n\t\tclsMap.put(clsNode.getClassInfo(), clsNode);\n\t\trawClsMap.put(clsNode.getRawName(), clsNode);\n\t}\n\n\tpublic void loadResources(ResourcesLoader resLoader, List<ResourceFile> resources) {\n\t\tResourceFile arsc = getResourceFile(resources);\n\t\tif (arsc == null) {\n\t\t\tLOG.debug(\"'resources.arsc' or 'resources.pb' file not found\");\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tIResTableParser parser = ResourcesLoader.decodeStream(arsc, (size, is) -> resLoader.decodeTable(arsc, is));\n\t\t\tif (parser != null) {\n\t\t\t\tprocessResources(parser.getResStorage());\n\t\t\t\tupdateObfuscatedFiles(parser, resources);\n\t\t\t\tinitManifestAttributes().updateAttributes(parser);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to parse 'resources.pb'/'.arsc' file\", e);\n\t\t}\n\t}\n\n\tprivate @Nullable ResourceFile getResourceFile(List<ResourceFile> resources) {\n\t\tfor (ResourceFile rf : resources) {\n\t\t\tif (rf.getType() == ResourceType.ARSC) {\n\t\t\t\treturn rf;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic void processResources(ResourceStorage resStorage) {\n\t\tconstValues.setResourcesNames(resStorage.getResourcesNames());\n\t\tappPackage = resStorage.getAppPackage();\n\t\tappResClass = AndroidResourcesUtils.searchAppResClass(this, resStorage);\n\t}\n\n\tpublic void initClassPath() {\n\t\ttry {\n\t\t\tif (this.clsp == null) {\n\t\t\t\tClspGraph newClsp = new ClspGraph(this);\n\t\t\t\tif (args.isLoadJadxClsSetFile()) {\n\t\t\t\t\tnewClsp.loadClsSetFile();\n\t\t\t\t}\n\t\t\t\tnewClsp.addApp(classes);\n\t\t\t\tnewClsp.initCache();\n\t\t\t\tthis.clsp = newClsp;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Error loading jadx class set\", e);\n\t\t}\n\t}\n\n\tprivate void updateObfuscatedFiles(IResTableParser parser, List<ResourceFile> resources) {\n\t\tif (args.isSkipResources()) {\n\t\t\treturn;\n\t\t}\n\t\tboolean useHeaders = args.isUseHeadersForDetectResourceExtensions();\n\t\tlong start = System.currentTimeMillis();\n\t\tint renamedCount = 0;\n\t\tResourceStorage resStorage = parser.getResStorage();\n\t\tValuesParser valuesParser = new ValuesParser(parser.getStrings(), resStorage.getResourcesNames());\n\t\tMap<String, ResourceEntry> entryNames = new HashMap<>();\n\t\tfor (ResourceEntry resEntry : resStorage.getResources()) {\n\t\t\tString val = valuesParser.getSimpleValueString(resEntry);\n\t\t\tif (val != null) {\n\t\t\t\tentryNames.put(val, resEntry);\n\t\t\t}\n\t\t}\n\t\tfor (ResourceFile resource : resources) {\n\t\t\tResourceEntry resEntry = entryNames.get(resource.getOriginalName());\n\t\t\tif (resEntry != null) {\n\t\t\t\tif (resource.setAlias(resEntry, useHeaders)) {\n\t\t\t\t\trenamedCount++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tLOG.debug(\"Renamed obfuscated resources: {}, duration: {}ms\", renamedCount, System.currentTimeMillis() - start);\n\t\t}\n\t}\n\n\tprivate void initInnerClasses() {\n\t\t// move inner classes\n\t\tList<ClassNode> inner = new ArrayList<>();\n\t\tfor (ClassNode cls : classes) {\n\t\t\tif (cls.getClassInfo().isInner()) {\n\t\t\t\tinner.add(cls);\n\t\t\t}\n\t\t}\n\t\tList<ClassNode> updated = new ArrayList<>();\n\t\tfor (ClassNode cls : inner) {\n\t\t\tClassInfo clsInfo = cls.getClassInfo();\n\t\t\tClassNode parent = resolveParentClass(clsInfo);\n\t\t\tif (parent == null) {\n\t\t\t\tupdated.add(cls);\n\t\t\t\tcls.notInner();\n\t\t\t} else {\n\t\t\t\tparent.addInnerClass(cls);\n\t\t\t}\n\t\t}\n\t\t// reload names for inner classes of updated parents\n\t\tfor (ClassNode updCls : updated) {\n\t\t\tfor (ClassNode innerCls : updCls.getInnerClasses()) {\n\t\t\t\tinnerCls.getClassInfo().updateNames(this);\n\t\t\t}\n\t\t}\n\t\tfor (PackageNode pkg : packages) {\n\t\t\tpkg.getClasses().removeIf(cls -> cls.getClassInfo().isInner());\n\t\t}\n\t}\n\n\tpublic void mergePasses(Map<JadxPassType, List<JadxPass>> customPasses) {\n\t\tDecompilationMode mode = args.getDecompilationMode();\n\t\tif (mode == DecompilationMode.FALLBACK || mode == DecompilationMode.SIMPLE) {\n\t\t\t// for predefined modes ignore custom (and plugin) passes\n\t\t\treturn;\n\t\t}\n\n\t\tnew PassMerge(preDecompilePasses)\n\t\t\t\t.merge(customPasses.get(JadxPreparePass.TYPE), p -> new PreparePassWrapper((JadxPreparePass) p));\n\t\tnew PassMerge(processClasses.getPasses())\n\t\t\t\t.merge(customPasses.get(JadxDecompilePass.TYPE), p -> new DecompilePassWrapper((JadxDecompilePass) p));\n\n\t\tif (args.isRunDebugChecks()) {\n\t\t\tpreDecompilePasses = DebugChecks.insertPasses(preDecompilePasses);\n\t\t\tprocessClasses = new ProcessClass(DebugChecks.insertPasses(processClasses.getPasses()));\n\t\t}\n\t\tList<String> disabledPasses = args.getDisabledPasses();\n\t\tif (!disabledPasses.isEmpty()) {\n\t\t\tSet<String> disabledSet = new HashSet<>(disabledPasses);\n\t\t\tPredicate<IDexTreeVisitor> filter = p -> {\n\t\t\t\tif (disabledSet.contains(p.getName())) {\n\t\t\t\t\tLOG.debug(\"Disable pass: {}\", p.getName());\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t};\n\t\t\tpreDecompilePasses.removeIf(filter);\n\t\t\tprocessClasses.getPasses().removeIf(filter);\n\t\t}\n\t}\n\n\tpublic void runPreDecompileStage() {\n\t\tboolean debugEnabled = LOG.isDebugEnabled();\n\t\tfor (IDexTreeVisitor pass : preDecompilePasses) {\n\t\t\tUtils.checkThreadInterrupt();\n\t\t\tlong start = debugEnabled ? System.currentTimeMillis() : 0;\n\t\t\ttry {\n\t\t\t\tpass.init(this);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Visitor init failed: {}\", pass.getClass().getSimpleName(), e);\n\t\t\t}\n\t\t\tfor (ClassNode cls : classes) {\n\t\t\t\tif (cls.isInner()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tDepthTraversal.visit(pass, cls);\n\t\t\t}\n\t\t\tif (debugEnabled) {\n\t\t\t\tLOG.debug(\"Prepare pass: '{}' - {}ms\", pass, System.currentTimeMillis() - start);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void runPreDecompileStageForClass(ClassNode cls) {\n\t\tfor (IDexTreeVisitor pass : preDecompilePasses) {\n\t\t\tDepthTraversal.visit(pass, cls);\n\t\t}\n\t}\n\n\t// TODO: make better API for reload passes lists\n\tpublic void resetPasses() {\n\t\tpreDecompilePasses.clear();\n\t\tpreDecompilePasses.addAll(Jadx.getPreDecompilePassesList());\n\n\t\tprocessClasses.getPasses().clear();\n\t\tprocessClasses.getPasses().addAll(Jadx.getPassesList(args));\n\t}\n\n\tpublic void restartVisitors() {\n\t\tfor (ClassNode cls : classes) {\n\t\t\tcls.unload();\n\t\t\tcls.clearAttributes();\n\t\t\tcls.unloadFromCache();\n\t\t}\n\t\trunPreDecompileStage();\n\t}\n\n\tpublic List<ClassNode> getClasses() {\n\t\treturn classes;\n\t}\n\n\tpublic List<ClassNode> getClassesWithoutInner() {\n\t\treturn getClasses(false);\n\t}\n\n\tpublic List<ClassNode> getClasses(boolean includeInner) {\n\t\tif (includeInner) {\n\t\t\treturn classes;\n\t\t}\n\t\tList<ClassNode> notInnerClasses = new ArrayList<>();\n\t\tfor (ClassNode cls : classes) {\n\t\t\tif (!cls.getClassInfo().isInner()) {\n\t\t\t\tnotInnerClasses.add(cls);\n\t\t\t}\n\t\t}\n\t\treturn notInnerClasses;\n\t}\n\n\tpublic List<PackageNode> getPackages() {\n\t\treturn packages;\n\t}\n\n\tpublic @Nullable PackageNode resolvePackage(String fullPkg) {\n\t\treturn pkgMap.get(fullPkg);\n\t}\n\n\tpublic @Nullable PackageNode resolvePackage(@Nullable PackageInfo pkgInfo) {\n\t\treturn pkgInfo == null ? null : pkgMap.get(pkgInfo.getFullName());\n\t}\n\n\tpublic void addPackage(PackageNode pkg) {\n\t\tpkgMap.put(pkg.getPkgInfo().getFullName(), pkg);\n\t\tpackages.add(pkg);\n\t}\n\n\tpublic void removePackage(PackageNode pkg) {\n\t\tif (pkgMap.remove(pkg.getPkgInfo().getFullName()) != null) {\n\t\t\tpackages.remove(pkg);\n\t\t\tPackageNode parentPkg = pkg.getParentPkg();\n\t\t\tif (parentPkg != null) {\n\t\t\t\tparentPkg.getSubPackages().remove(pkg);\n\t\t\t\tif (parentPkg.isEmpty()) {\n\t\t\t\t\tremovePackage(parentPkg);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (PackageNode subPkg : pkg.getSubPackages()) {\n\t\t\t\tremovePackage(subPkg);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void sortPackages() {\n\t\tCollections.sort(packages);\n\t}\n\n\tpublic void removeClsFromPackage(PackageNode pkg, ClassNode cls) {\n\t\tboolean removed = pkg.getClasses().remove(cls);\n\t\tif (removed && pkg.isEmpty()) {\n\t\t\tremovePackage(pkg);\n\t\t}\n\t}\n\n\t/**\n\t * Update sub packages\n\t */\n\tpublic void runPackagesUpdate() {\n\t\tfor (PackageNode pkg : getPackages()) {\n\t\t\tif (pkg.isRoot()) {\n\t\t\t\tpkg.updatePackages();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic ClassNode resolveClass(ClassInfo clsInfo) {\n\t\treturn clsMap.get(clsInfo);\n\t}\n\n\t@Nullable\n\tpublic ClassNode resolveClass(ArgType clsType) {\n\t\tif (!clsType.isTypeKnown() || clsType.isGenericType()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (clsType.getWildcardBound() == ArgType.WildcardBound.UNBOUND) {\n\t\t\treturn null;\n\t\t}\n\t\tif (clsType.isGeneric()) {\n\t\t\tclsType = ArgType.object(clsType.getObject());\n\t\t}\n\t\treturn resolveClass(ClassInfo.fromType(this, clsType));\n\t}\n\n\t@Nullable\n\tpublic ClassNode resolveClass(String fullName) {\n\t\tClassInfo clsInfo = ClassInfo.fromName(this, fullName);\n\t\treturn resolveClass(clsInfo);\n\t}\n\n\t@Nullable\n\tpublic ClassNode resolveRawClass(String rawFullName) {\n\t\treturn rawClsMap.get(rawFullName);\n\t}\n\n\t/**\n\t * Find and correct the parent of an inner class.\n\t * <br>\n\t * Sometimes inner ClassInfo generated wrong parent info.\n\t * e.g. inner is `Cls$mth$1`, current parent = `Cls$mth`, real parent = `Cls`\n\t */\n\t@Nullable\n\tpublic ClassNode resolveParentClass(ClassInfo clsInfo) {\n\t\tClassInfo parentInfo = clsInfo.getParentClass();\n\t\tClassNode parentNode = resolveClass(parentInfo);\n\t\tif (parentNode == null && parentInfo != null) {\n\t\t\tString parClsName = parentInfo.getFullName();\n\t\t\t// strip last part as method name\n\t\t\tint sep = parClsName.lastIndexOf('.');\n\t\t\tif (sep > 0 && sep != parClsName.length() - 1) {\n\t\t\t\tString mthName = parClsName.substring(sep + 1);\n\t\t\t\tString upperParClsName = parClsName.substring(0, sep);\n\t\t\t\tClassNode tmpParent = resolveClass(upperParClsName);\n\t\t\t\tif (tmpParent != null && tmpParent.searchMethodByShortName(mthName) != null) {\n\t\t\t\t\tparentNode = tmpParent;\n\t\t\t\t\tclsInfo.convertToInner(parentNode);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn parentNode;\n\t}\n\n\t/**\n\t * Searches for ClassNode by its full name (original or alias name)\n\t * <br>\n\t * Warning: This method has a runtime of O(n) (n = number of classes).\n\t * If you need to call it more than once consider {@link #buildFullAliasClassCache()} instead\n\t */\n\t@Nullable\n\tpublic ClassNode searchClassByFullAlias(String fullName) {\n\t\tfor (ClassNode cls : classes) {\n\t\t\tClassInfo classInfo = cls.getClassInfo();\n\t\t\tif (classInfo.getFullName().equals(fullName)\n\t\t\t\t\t|| classInfo.getAliasFullName().equals(fullName)) {\n\t\t\t\treturn cls;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic Map<String, ClassNode> buildFullAliasClassCache() {\n\t\tMap<String, ClassNode> classNameCache = new HashMap<>(classes.size());\n\t\tfor (ClassNode cls : classes) {\n\t\t\tClassInfo classInfo = cls.getClassInfo();\n\t\t\tString fullName = classInfo.getFullName();\n\t\t\tString alias = classInfo.getAliasFullName();\n\t\t\tclassNameCache.put(fullName, cls);\n\t\t\tif (alias != null && !fullName.equals(alias)) {\n\t\t\t\tclassNameCache.put(alias, cls);\n\t\t\t}\n\t\t}\n\t\treturn classNameCache;\n\t}\n\n\tpublic List<ClassNode> searchClassByShortName(String shortName) {\n\t\tList<ClassNode> list = new ArrayList<>();\n\t\tfor (ClassNode cls : classes) {\n\t\t\tif (cls.getClassInfo().getShortName().equals(shortName)) {\n\t\t\t\tlist.add(cls);\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Nullable\n\tpublic MethodNode resolveMethod(@NotNull MethodInfo mth) {\n\t\tClassNode cls = resolveClass(mth.getDeclClass());\n\t\tif (cls == null) {\n\t\t\treturn null;\n\t\t}\n\t\tMethodNode methodNode = cls.searchMethod(mth);\n\t\tif (methodNode != null) {\n\t\t\treturn methodNode;\n\t\t}\n\t\treturn deepResolveMethod(cls, mth.makeSignature(false));\n\t}\n\n\tpublic @NotNull MethodNode resolveDirectMethod(String rawClsName, String mthShortId) {\n\t\tClassNode clsNode = resolveRawClass(rawClsName);\n\t\tif (clsNode == null) {\n\t\t\tthrow new RuntimeException(\"Class not found: \" + rawClsName);\n\t\t}\n\t\tMethodNode methodNode = clsNode.searchMethodByShortId(mthShortId);\n\t\tif (methodNode == null) {\n\t\t\tthrow new RuntimeException(\"Method not found: \" + rawClsName + \".\" + mthShortId);\n\t\t}\n\t\treturn methodNode;\n\t}\n\n\t@Nullable\n\tprivate MethodNode deepResolveMethod(@NotNull ClassNode cls, String signature) {\n\t\tfor (MethodNode m : cls.getMethods()) {\n\t\t\tif (m.getMethodInfo().getShortId().startsWith(signature)) {\n\t\t\t\treturn m;\n\t\t\t}\n\t\t}\n\t\tMethodNode found;\n\t\tArgType superClass = cls.getSuperClass();\n\t\tif (superClass != null) {\n\t\t\tClassNode superNode = resolveClass(superClass);\n\t\t\tif (superNode != null) {\n\t\t\t\tfound = deepResolveMethod(superNode, signature);\n\t\t\t\tif (found != null) {\n\t\t\t\t\treturn found;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (ArgType iFaceType : cls.getInterfaces()) {\n\t\t\tClassNode iFaceNode = resolveClass(iFaceType);\n\t\t\tif (iFaceNode != null) {\n\t\t\t\tfound = deepResolveMethod(iFaceNode, signature);\n\t\t\t\tif (found != null) {\n\t\t\t\t\treturn found;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Nullable\n\tpublic FieldNode resolveField(FieldInfo field) {\n\t\tClassNode cls = resolveClass(field.getDeclClass());\n\t\tif (cls == null) {\n\t\t\treturn null;\n\t\t}\n\t\tFieldNode fieldNode = cls.searchField(field);\n\t\tif (fieldNode != null) {\n\t\t\treturn fieldNode;\n\t\t}\n\t\treturn deepResolveField(cls, field);\n\t}\n\n\t@Nullable\n\tprivate FieldNode deepResolveField(@NotNull ClassNode cls, FieldInfo fieldInfo) {\n\t\tFieldNode field = cls.searchFieldByNameAndType(fieldInfo);\n\t\tif (field != null) {\n\t\t\treturn field;\n\t\t}\n\t\tArgType superClass = cls.getSuperClass();\n\t\tif (superClass != null) {\n\t\t\tClassNode superNode = resolveClass(superClass);\n\t\t\tif (superNode != null) {\n\t\t\t\tFieldNode found = deepResolveField(superNode, fieldInfo);\n\t\t\t\tif (found != null) {\n\t\t\t\t\treturn found;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (ArgType iFaceType : cls.getInterfaces()) {\n\t\t\tClassNode iFaceNode = resolveClass(iFaceType);\n\t\t\tif (iFaceNode != null) {\n\t\t\t\tFieldNode found = deepResolveField(iFaceNode, fieldInfo);\n\t\t\t\tif (found != null) {\n\t\t\t\t\treturn found;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic ProcessClass getProcessClasses() {\n\t\treturn processClasses;\n\t}\n\n\tpublic List<IDexTreeVisitor> getPasses() {\n\t\treturn processClasses.getPasses();\n\t}\n\n\tpublic List<IDexTreeVisitor> getPreDecompilePasses() {\n\t\treturn preDecompilePasses;\n\t}\n\n\tpublic void initPasses() {\n\t\tprocessClasses.initPasses(this);\n\t}\n\n\tpublic ICodeWriter makeCodeWriter() {\n\t\tJadxArgs jadxArgs = this.args;\n\t\treturn jadxArgs.getCodeWriterProvider().apply(jadxArgs);\n\t}\n\n\tpublic void registerCodeDataUpdateListener(ICodeDataUpdateListener listener) {\n\t\tthis.codeDataUpdateListeners.add(listener);\n\t}\n\n\tpublic void notifyCodeDataListeners() {\n\t\tICodeData codeData = args.getCodeData();\n\t\tcodeDataUpdateListeners.forEach(l -> l.updated(codeData));\n\t}\n\n\tpublic ClspGraph getClsp() {\n\t\treturn clsp;\n\t}\n\n\tpublic ErrorsCounter getErrorsCounter() {\n\t\treturn errorsCounter;\n\t}\n\n\t@Nullable\n\tpublic String getAppPackage() {\n\t\treturn appPackage;\n\t}\n\n\t@Nullable\n\tpublic ClassNode getAppResClass() {\n\t\treturn appResClass;\n\t}\n\n\tpublic StringUtils getStringUtils() {\n\t\treturn stringUtils;\n\t}\n\n\tpublic ConstStorage getConstValues() {\n\t\treturn constValues;\n\t}\n\n\tpublic InfoStorage getInfoStorage() {\n\t\treturn infoStorage;\n\t}\n\n\tpublic CacheStorage getCacheStorage() {\n\t\treturn cacheStorage;\n\t}\n\n\tpublic JadxArgs getArgs() {\n\t\treturn args;\n\t}\n\n\tpublic @Nullable JadxDecompiler getDecompiler() {\n\t\treturn decompiler;\n\t}\n\n\tpublic TypeUpdate getTypeUpdate() {\n\t\treturn typeUpdate;\n\t}\n\n\tpublic TypeCompare getTypeCompare() {\n\t\treturn typeUpdate.getTypeCompare();\n\t}\n\n\tpublic ICodeCache getCodeCache() {\n\t\treturn args.getCodeCache();\n\t}\n\n\tpublic MethodUtils getMethodUtils() {\n\t\treturn methodUtils;\n\t}\n\n\tpublic TypeUtils getTypeUtils() {\n\t\treturn typeUtils;\n\t}\n\n\tpublic AttributeStorage getAttributes() {\n\t\treturn attributes;\n\t}\n\n\tpublic GradleInfoStorage getGradleInfoStorage() {\n\t\treturn gradleInfoStorage;\n\t}\n\n\tpublic synchronized ManifestAttributes initManifestAttributes() {\n\t\tManifestAttributes attrs = manifestAttributes;\n\t\tif (attrs == null) {\n\t\t\tattrs = new ManifestAttributes(args.getSecurity());\n\t\t\tmanifestAttributes = attrs;\n\t\t}\n\t\treturn attrs;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java",
    "content": "package jadx.core.dex.nodes.parser;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.types.SignatureAttr;\nimport jadx.core.dex.attributes.IAttributeNode;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class SignatureParser {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SignatureParser.class);\n\n\tprivate static final char STOP_CHAR = 0;\n\n\tprivate final String sign;\n\tprivate final int end;\n\tprivate int pos;\n\tprivate int mark;\n\n\tpublic SignatureParser(String signature) {\n\t\tsign = signature;\n\t\tend = sign.length();\n\t\tpos = -1;\n\t\tmark = 0;\n\t}\n\n\t@Nullable\n\tpublic static SignatureParser fromNode(IAttributeNode node) {\n\t\tString signature = getSignature(node);\n\t\tif (signature == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new SignatureParser(signature);\n\t}\n\n\t@Nullable\n\tpublic static String getSignature(IAttributeNode node) {\n\t\tSignatureAttr attr = node.get(JadxAttrType.SIGNATURE);\n\t\tif (attr == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn attr.getSignature();\n\t}\n\n\tprivate char next() {\n\t\tpos++;\n\t\tif (pos >= end) {\n\t\t\treturn STOP_CHAR;\n\t\t}\n\t\treturn sign.charAt(pos);\n\t}\n\n\tprivate boolean lookAhead(char ch) {\n\t\tint next = pos + 1;\n\t\treturn next < end && sign.charAt(next) == ch;\n\t}\n\n\tprivate void mark() {\n\t\tmark = pos;\n\t}\n\n\t/**\n\t * Exclusive slice.\n\t *\n\t * @return string from 'mark' to current position (not including current character)\n\t */\n\tprivate String slice() {\n\t\tint start = mark == -1 ? 0 : mark;\n\t\tif (start >= pos) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn sign.substring(start, pos);\n\t}\n\n\t/**\n\t * Inclusive slice (includes current character)\n\t */\n\tprivate String inclusiveSlice() {\n\t\tint start = mark;\n\t\tif (start == -1) {\n\t\t\tstart = 0;\n\t\t}\n\t\tint last = pos + 1;\n\t\tif (start >= last) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn sign.substring(start, last);\n\t}\n\n\tprivate boolean skipUntil(char untilChar) {\n\t\tint startPos = pos;\n\t\twhile (true) {\n\t\t\tif (lookAhead(untilChar)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tchar ch = next();\n\t\t\tif (ch == STOP_CHAR) {\n\t\t\t\tpos = startPos;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void consume(char exp) {\n\t\tchar c = next();\n\t\tif (exp != c) {\n\t\t\tthrow new JadxRuntimeException(\"Consume wrong char: '\" + c + \"' != '\" + exp\n\t\t\t\t\t+ \"', sign: \" + debugString());\n\t\t}\n\t}\n\n\tprivate boolean tryConsume(char exp) {\n\t\tif (lookAhead(exp)) {\n\t\t\tnext();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Nullable\n\tpublic String consumeUntil(char lastChar) {\n\t\tmark();\n\t\treturn skipUntil(lastChar) ? inclusiveSlice() : null;\n\t}\n\n\tpublic ArgType consumeType() {\n\t\tchar ch = next();\n\t\tswitch (ch) {\n\t\t\tcase 'L':\n\t\t\t\tArgType obj = consumeObjectType(false);\n\t\t\t\tif (obj != null) {\n\t\t\t\t\treturn obj;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'T':\n\t\t\t\tnext();\n\t\t\t\tmark();\n\t\t\t\tString typeVarName = consumeUntil(';');\n\t\t\t\tif (typeVarName != null) {\n\t\t\t\t\tconsume(';');\n\t\t\t\t\tif (typeVarName.contains(\")\")) {\n\t\t\t\t\t\tthrow new JadxRuntimeException(\"Bad name for type variable: \" + typeVarName);\n\t\t\t\t\t}\n\t\t\t\t\treturn ArgType.genericType(typeVarName);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase '[':\n\t\t\t\treturn ArgType.array(consumeType());\n\n\t\t\tcase STOP_CHAR:\n\t\t\t\treturn null;\n\n\t\t\tdefault:\n\t\t\t\t// primitive type (one char)\n\t\t\t\tArgType type = ArgType.parse(ch);\n\t\t\t\tif (type != null) {\n\t\t\t\t\treturn type;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Can't parse type: \" + debugString() + \", unexpected: \" + ch);\n\t}\n\n\tpublic List<ArgType> consumeTypeList() {\n\t\tList<ArgType> list = null;\n\t\twhile (true) {\n\t\t\tArgType type = consumeType();\n\t\t\tif (type == null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (list == null) {\n\t\t\t\tlist = new ArrayList<>();\n\t\t\t}\n\t\t\tlist.add(type);\n\t\t}\n\t\tif (list == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn list;\n\t}\n\n\tprivate ArgType consumeObjectType(boolean innerType) {\n\t\tmark();\n\t\tint ch;\n\t\tdo {\n\t\t\tif (innerType && lookAhead('.')) {\n\t\t\t\t// stop before next nested inner class\n\t\t\t\treturn ArgType.object(inclusiveSlice());\n\t\t\t}\n\t\t\tch = next();\n\t\t\tif (ch == STOP_CHAR) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t} while (ch != '<' && ch != ';');\n\n\t\tif (ch == ';') {\n\t\t\tString obj;\n\t\t\tif (innerType) {\n\t\t\t\tobj = slice().replace('/', '.');\n\t\t\t} else {\n\t\t\t\tobj = inclusiveSlice();\n\t\t\t}\n\t\t\treturn ArgType.object(obj);\n\t\t}\n\t\t// generic type start ('<')\n\t\tString obj = slice();\n\t\tif (!innerType) {\n\t\t\tobj += ';';\n\t\t} else {\n\t\t\tobj = obj.replace('/', '.');\n\t\t}\n\t\tList<ArgType> typeVars = consumeGenericArgs();\n\t\tconsume('>');\n\n\t\tArgType genericType = ArgType.generic(obj, typeVars);\n\t\tif (!lookAhead('.')) {\n\t\t\tconsume(';');\n\t\t\treturn genericType;\n\t\t}\n\t\tconsume('.');\n\t\tnext();\n\t\t// type parsing not completed, proceed to inner class\n\t\tArgType inner = consumeObjectType(true);\n\t\tif (inner == null) {\n\t\t\tthrow new JadxRuntimeException(\"No inner type found: \" + debugString());\n\t\t}\n\t\t// for every nested inner type create nested type object\n\t\twhile (lookAhead('.')) {\n\t\t\tgenericType = ArgType.outerGeneric(genericType, inner);\n\t\t\tconsume('.');\n\t\t\tnext();\n\t\t\tinner = consumeObjectType(true);\n\t\t\tif (inner == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Unexpected inner type found: \" + debugString());\n\t\t\t}\n\t\t}\n\t\treturn ArgType.outerGeneric(genericType, inner);\n\t}\n\n\tprivate List<ArgType> consumeGenericArgs() {\n\t\tList<ArgType> list = new ArrayList<>();\n\t\tArgType type;\n\t\tdo {\n\t\t\tif (lookAhead('*')) {\n\t\t\t\tnext();\n\t\t\t\ttype = ArgType.wildcard();\n\t\t\t} else if (lookAhead('+')) {\n\t\t\t\tnext();\n\t\t\t\ttype = ArgType.wildcard(consumeType(), ArgType.WildcardBound.EXTENDS);\n\t\t\t} else if (lookAhead('-')) {\n\t\t\t\tnext();\n\t\t\t\ttype = ArgType.wildcard(consumeType(), ArgType.WildcardBound.SUPER);\n\t\t\t} else {\n\t\t\t\ttype = consumeType();\n\t\t\t}\n\t\t\tif (type != null) {\n\t\t\t\tlist.add(type);\n\t\t\t}\n\t\t} while (type != null && !lookAhead('>'));\n\t\treturn list;\n\t}\n\n\t/**\n\t * Map of generic types names to extends classes.\n\t * <p>\n\t * Example: \"&lt;T:Ljava/lang/Exception;:Ljava/lang/Object;&gt;\"\n\t */\n\t@SuppressWarnings(\"ConditionalBreakInInfiniteLoop\")\n\tpublic List<ArgType> consumeGenericTypeParameters() {\n\t\tif (!lookAhead('<')) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<ArgType> list = new ArrayList<>();\n\t\tconsume('<');\n\t\twhile (true) {\n\t\t\tif (lookAhead('>') || next() == STOP_CHAR) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tString id = consumeUntil(':');\n\t\t\tif (id == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Failed to parse generic types map\");\n\t\t\t}\n\t\t\tconsume(':');\n\t\t\ttryConsume(':');\n\t\t\tList<ArgType> types = consumeExtendsTypesList();\n\t\t\tlist.add(ArgType.genericType(id, types));\n\t\t}\n\t\tconsume('>');\n\t\treturn list;\n\t}\n\n\t/**\n\t * List of types separated by ':' last type is 'java.lang.Object'.\n\t * <p/>\n\t * Example: \"Ljava/lang/Exception;:Ljava/lang/Object;\"\n\t */\n\tprivate List<ArgType> consumeExtendsTypesList() {\n\t\tList<ArgType> types = Collections.emptyList();\n\t\tboolean next;\n\t\tdo {\n\t\t\tArgType argType = consumeType();\n\t\t\tif (argType == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Unexpected end of signature\");\n\t\t\t}\n\t\t\tif (!argType.equals(ArgType.OBJECT)) {\n\t\t\t\tif (types.isEmpty()) {\n\t\t\t\t\ttypes = new ArrayList<>();\n\t\t\t\t}\n\t\t\t\ttypes.add(argType);\n\t\t\t}\n\t\t\tnext = lookAhead(':');\n\t\t\tif (next) {\n\t\t\t\tconsume(':');\n\t\t\t}\n\t\t} while (next);\n\t\treturn types;\n\t}\n\n\tpublic List<ArgType> consumeMethodArgs(int argsCount) {\n\t\tconsume('(');\n\t\tif (lookAhead(')')) {\n\t\t\tconsume(')');\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<ArgType> args = new ArrayList<>(argsCount);\n\t\tint limit = argsCount + 10; // just prevent endless loop, args count can be different for synthetic methods\n\t\tdo {\n\t\t\tArgType type = consumeType();\n\t\t\tif (type == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Unexpected end of signature\");\n\t\t\t}\n\t\t\targs.add(type);\n\t\t\tif (args.size() > limit) {\n\t\t\t\tthrow new JadxRuntimeException(\"Arguments count limit reached: \" + args.size());\n\t\t\t}\n\t\t} while (!lookAhead(')'));\n\t\tconsume(')');\n\t\treturn args;\n\t}\n\n\tprivate static String mergeSignature(List<String> list) {\n\t\tif (list.size() == 1) {\n\t\t\treturn list.get(0);\n\t\t}\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (String s : list) {\n\t\t\tsb.append(s);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic String getSignature() {\n\t\treturn sign;\n\t}\n\n\tprivate String debugString() {\n\t\tif (pos >= sign.length()) {\n\t\t\treturn sign;\n\t\t}\n\t\treturn sign + \" at position \" + pos + \" ('\" + sign.charAt(pos) + \"')\";\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tif (pos == -1) {\n\t\t\treturn sign;\n\t\t}\n\t\treturn sign.substring(0, mark) + '{' + sign.substring(mark, pos) + '}' + sign.substring(pos);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/utils/MethodUtils.java",
    "content": "package jadx.core.dex.nodes.utils;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.clsp.ClspClass;\nimport jadx.core.clsp.ClspMethod;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.MethodBridgeAttr;\nimport jadx.core.dex.attributes.nodes.MethodOverrideAttr;\nimport jadx.core.dex.attributes.nodes.SkipMethodArgsAttr;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.BaseInvokeNode;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.Utils;\n\npublic class MethodUtils {\n\tprivate final RootNode root;\n\n\tpublic MethodUtils(RootNode rootNode) {\n\t\tthis.root = rootNode;\n\t}\n\n\t@Nullable\n\tpublic IMethodDetails getMethodDetails(BaseInvokeNode invokeNode) {\n\t\tIMethodDetails methodDetails = invokeNode.get(AType.METHOD_DETAILS);\n\t\tif (methodDetails != null) {\n\t\t\treturn methodDetails;\n\t\t}\n\t\treturn getMethodDetails(invokeNode.getCallMth());\n\t}\n\n\t@Nullable\n\tpublic IMethodDetails getMethodDetails(MethodInfo callMth) {\n\t\tMethodNode mthNode = root.resolveMethod(callMth);\n\t\tif (mthNode != null) {\n\t\t\treturn mthNode;\n\t\t}\n\t\treturn root.getClsp().getMethodDetails(callMth);\n\t}\n\n\t@Nullable\n\tpublic MethodNode resolveMethod(BaseInvokeNode invokeNode) {\n\t\tIMethodDetails methodDetails = getMethodDetails(invokeNode);\n\t\tif (methodDetails instanceof MethodNode) {\n\t\t\treturn (MethodNode) methodDetails;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic boolean isSkipArg(BaseInvokeNode invokeNode, InsnArg arg) {\n\t\tMethodNode mth = resolveMethod(invokeNode);\n\t\tif (mth == null) {\n\t\t\treturn false;\n\t\t}\n\t\tSkipMethodArgsAttr skipArgsAttr = mth.get(AType.SKIP_MTH_ARGS);\n\t\tif (skipArgsAttr == null) {\n\t\t\treturn false;\n\t\t}\n\t\tint argIndex = invokeNode.getArgIndex(arg);\n\t\treturn skipArgsAttr.isSkip(argIndex);\n\t}\n\n\t/**\n\t * Search methods with same name and args count in class hierarchy starting from {@code startCls}\n\t * Beware {@code startCls} can be different from {@code mthInfo.getDeclClass()}\n\t */\n\tpublic boolean isMethodArgsOverloaded(ArgType startCls, MethodInfo mthInfo) {\n\t\treturn processMethodArgsOverloaded(startCls, mthInfo, null);\n\t}\n\n\tpublic List<IMethodDetails> collectOverloadedMethods(ArgType startCls, MethodInfo mthInfo) {\n\t\tList<IMethodDetails> list = new ArrayList<>();\n\t\tprocessMethodArgsOverloaded(startCls, mthInfo, list);\n\t\treturn list;\n\t}\n\n\t@Nullable\n\tpublic ArgType getMethodGenericReturnType(BaseInvokeNode invokeNode) {\n\t\tIMethodDetails methodDetails = getMethodDetails(invokeNode);\n\t\tif (methodDetails != null) {\n\t\t\tArgType returnType = methodDetails.getReturnType();\n\t\t\tif (returnType != null && returnType.containsGeneric()) {\n\t\t\t\treturn returnType;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate boolean processMethodArgsOverloaded(ArgType startCls, MethodInfo mthInfo, @Nullable List<IMethodDetails> collectedMths) {\n\t\tif (startCls == null || !startCls.isObject()) {\n\t\t\treturn false;\n\t\t}\n\t\tboolean isMthConstructor = mthInfo.isConstructor() || mthInfo.isClassInit();\n\t\tClassNode classNode = root.resolveClass(startCls);\n\t\tif (classNode != null) {\n\t\t\tfor (MethodNode mth : classNode.getMethods()) {\n\t\t\t\tif (mthInfo.isOverloadedBy(mth.getMethodInfo())) {\n\t\t\t\t\tif (collectedMths == null) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tcollectedMths.add(mth);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!isMthConstructor) {\n\t\t\t\tif (processMethodArgsOverloaded(classNode.getSuperClass(), mthInfo, collectedMths)) {\n\t\t\t\t\tif (collectedMths == null) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (ArgType parentInterface : classNode.getInterfaces()) {\n\t\t\t\t\tif (processMethodArgsOverloaded(parentInterface, mthInfo, collectedMths)) {\n\t\t\t\t\t\tif (collectedMths == null) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tClspClass clsDetails = root.getClsp().getClsDetails(startCls);\n\t\t\tif (clsDetails == null) {\n\t\t\t\t// class info not available\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfor (ClspMethod clspMth : clsDetails.getMethodsMap().values()) {\n\t\t\t\tif (mthInfo.isOverloadedBy(clspMth.getMethodInfo())) {\n\t\t\t\t\tif (collectedMths == null) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tcollectedMths.add(clspMth);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!isMthConstructor) {\n\t\t\t\tfor (ArgType parent : clsDetails.getParents()) {\n\t\t\t\t\tif (processMethodArgsOverloaded(parent, mthInfo, collectedMths)) {\n\t\t\t\t\t\tif (collectedMths == null) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Nullable\n\tpublic IMethodDetails getOverrideBaseMth(MethodNode mth) {\n\t\tMethodOverrideAttr overrideAttr = mth.get(AType.METHOD_OVERRIDE);\n\t\tif (overrideAttr == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn Utils.getOne(overrideAttr.getBaseMethods());\n\t}\n\n\tpublic ClassInfo getMethodOriginDeclClass(MethodNode mth) {\n\t\tIMethodDetails baseMth = getOverrideBaseMth(mth);\n\t\tif (baseMth != null) {\n\t\t\treturn baseMth.getMethodInfo().getDeclClass();\n\t\t}\n\t\tMethodBridgeAttr bridgeAttr = mth.get(AType.BRIDGED_BY);\n\t\tif (bridgeAttr != null) {\n\t\t\treturn getMethodOriginDeclClass(bridgeAttr.getBridgeMth());\n\t\t}\n\t\treturn mth.getMethodInfo().getDeclClass();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/utils/SelectFromDuplicates.java",
    "content": "package jadx.core.dex.nodes.utils;\n\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.nodes.ClassNode;\n\n/**\n * Select best class from list of classes with same full name\n * Current implementation: use class with source file as 'classesN.dex' where N is minimal\n */\npublic class SelectFromDuplicates {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SelectFromDuplicates.class);\n\n\tprivate static final Pattern CLASSES_DEX_PATTERN = Pattern.compile(\"classes([1-9]\\\\d*)\\\\.dex\");\n\n\tpublic static ClassNode process(List<ClassNode> dupClsList) {\n\t\tClassNode bestCls = null;\n\t\tint bestClsIndex = -1;\n\t\tfor (ClassNode clsNode : dupClsList) {\n\t\t\tboolean selectCurrent = false;\n\t\t\tif (bestCls == null) {\n\t\t\t\tselectCurrent = true;\n\t\t\t} else {\n\t\t\t\tint clsIndex = getClassesIndex(clsNode.getInputFileName());\n\t\t\t\tif (clsIndex != -1) {\n\t\t\t\t\tif (bestClsIndex != -1) {\n\t\t\t\t\t\t// if both are valid, the lower index has precedence\n\t\t\t\t\t\tif (clsIndex < bestClsIndex) {\n\t\t\t\t\t\t\tselectCurrent = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// valid dex names have precedence\n\t\t\t\t\t\tselectCurrent = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (selectCurrent) {\n\t\t\t\tbestCls = clsNode;\n\t\t\t\tbestClsIndex = getClassesIndex(clsNode.getInputFileName());\n\t\t\t}\n\t\t}\n\t\treturn bestCls;\n\t}\n\n\t/**\n\t * Get N from classesN.dex\n\t *\n\t * @return -1 if source is not valid dex name\n\t */\n\tprivate static int getClassesIndex(String source) {\n\t\tif (\"classes.dex\".equals(source)) {\n\t\t\treturn 1;\n\t\t}\n\t\ttry {\n\t\t\tMatcher matcher = CLASSES_DEX_PATTERN.matcher(source);\n\t\t\tif (!matcher.matches()) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tString num = matcher.group(1);\n\t\t\tif (num.equals(\"1\")) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\treturn Integer.parseInt(num);\n\t\t} catch (Exception e) {\n\t\t\tLOG.debug(\"Failed to parse source classes index\", e);\n\t\t\treturn -1;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/nodes/utils/TypeUtils.java",
    "content": "package jadx.core.dex.nodes.utils;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.BiConsumer;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.clsp.ClspClass;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.ClassTypeVarsAttr;\nimport jadx.core.dex.attributes.nodes.MethodTypeVarsAttr;\nimport jadx.core.dex.attributes.nodes.NotificationAttrNode;\nimport jadx.core.dex.instructions.BaseInvokeNode;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.Utils;\n\nimport static jadx.core.utils.Utils.isEmpty;\nimport static jadx.core.utils.Utils.notEmpty;\n\npublic class TypeUtils {\n\tprivate final RootNode root;\n\n\tpublic TypeUtils(RootNode rootNode) {\n\t\tthis.root = rootNode;\n\t}\n\n\tpublic List<ArgType> getClassGenerics(ArgType type) {\n\t\tClassNode classNode = root.resolveClass(type);\n\t\tif (classNode != null) {\n\t\t\treturn classNode.getGenericTypeParameters();\n\t\t}\n\t\tClspClass clsDetails = root.getClsp().getClsDetails(type);\n\t\tif (clsDetails == null || clsDetails.getTypeParameters().isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<ArgType> generics = clsDetails.getTypeParameters();\n\t\treturn generics == null ? Collections.emptyList() : generics;\n\t}\n\n\t@Nullable\n\tpublic ClassTypeVarsAttr getClassTypeVars(ArgType type) {\n\t\tClassNode classNode = root.resolveClass(type);\n\t\tif (classNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tClassTypeVarsAttr typeVarsAttr = classNode.get(AType.CLASS_TYPE_VARS);\n\t\tif (typeVarsAttr != null) {\n\t\t\treturn typeVarsAttr;\n\t\t}\n\t\treturn buildClassTypeVarsAttr(classNode);\n\t}\n\n\tpublic ArgType expandTypeVariables(ClassNode cls, ArgType type) {\n\t\tif (type.containsTypeVariable()) {\n\t\t\texpandTypeVar(cls, type, getKnownTypeVarsAtClass(cls));\n\t\t}\n\t\treturn type;\n\t}\n\n\tpublic ArgType expandTypeVariables(MethodNode mth, ArgType type) {\n\t\tif (type.containsTypeVariable()) {\n\t\t\texpandTypeVar(mth, type, getKnownTypeVarsAtMethod(mth));\n\t\t}\n\t\treturn type;\n\t}\n\n\tprivate void expandTypeVar(NotificationAttrNode node, ArgType type, Collection<ArgType> typeVars) {\n\t\tif (typeVars.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tboolean allExtendsEmpty = true;\n\t\tfor (ArgType argType : typeVars) {\n\t\t\tif (notEmpty(argType.getExtendTypes())) {\n\t\t\t\tallExtendsEmpty = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (allExtendsEmpty) {\n\t\t\treturn;\n\t\t}\n\t\ttype.visitTypes(t -> {\n\t\t\tif (t.isGenericType()) {\n\t\t\t\tString typeVarName = t.getObject();\n\t\t\t\tfor (ArgType typeVar : typeVars) {\n\t\t\t\t\tif (typeVar.getObject().equals(typeVarName)) {\n\t\t\t\t\t\tt.setExtendTypes(typeVar.getExtendTypes());\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tnode.addWarnComment(\"Unknown type variable: \" + typeVarName + \" in type: \" + type);\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t}\n\n\tpublic Set<ArgType> getKnownTypeVarsAtMethod(MethodNode mth) {\n\t\tMethodTypeVarsAttr typeVarsAttr = mth.get(AType.METHOD_TYPE_VARS);\n\t\tif (typeVarsAttr != null) {\n\t\t\treturn typeVarsAttr.getTypeVars();\n\t\t}\n\t\tSet<ArgType> typeVars = collectKnownTypeVarsAtMethod(mth);\n\t\tMethodTypeVarsAttr varsAttr = MethodTypeVarsAttr.build(typeVars);\n\t\tmth.addAttr(varsAttr);\n\t\treturn varsAttr.getTypeVars();\n\t}\n\n\tprivate static Collection<ArgType> getKnownTypeVarsAtClass(ClassNode cls) {\n\t\tif (cls.isInner()) {\n\t\t\tSet<ArgType> typeVars = new HashSet<>(cls.getGenericTypeParameters());\n\t\t\tcls.visitParentClasses(parent -> typeVars.addAll(parent.getGenericTypeParameters()));\n\t\t\treturn typeVars;\n\t\t}\n\t\treturn cls.getGenericTypeParameters();\n\t}\n\n\tprivate static Set<ArgType> collectKnownTypeVarsAtMethod(MethodNode mth) {\n\t\tSet<ArgType> typeVars = new HashSet<>();\n\t\ttypeVars.addAll(getKnownTypeVarsAtClass(mth.getParentClass()));\n\t\ttypeVars.addAll(mth.getTypeParameters());\n\t\treturn typeVars.isEmpty() ? Collections.emptySet() : typeVars;\n\t}\n\n\t/**\n\t * Search for unknown type vars at current method. Return only first.\n\t *\n\t * @return unknown type var, null if not found\n\t */\n\t@Nullable\n\tpublic ArgType checkForUnknownTypeVars(MethodNode mth, ArgType checkType) {\n\t\tSet<ArgType> knownTypeVars = getKnownTypeVarsAtMethod(mth);\n\t\treturn checkType.visitTypes(type -> {\n\t\t\tif (type.isGenericType() && !knownTypeVars.contains(type)) {\n\t\t\t\treturn type;\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t}\n\n\tpublic boolean containsUnknownTypeVar(MethodNode mth, ArgType type) {\n\t\treturn checkForUnknownTypeVars(mth, type) != null;\n\t}\n\n\t/**\n\t * Replace generic types in {@code typeWithGeneric} using instance types\n\t * <br>\n\t * Example:\n\t * <ul>\n\t * <li>{@code instanceType: Set<String>}\n\t * <li>{@code typeWithGeneric: Iterator<E>}\n\t * <li>{@code return: Iterator<String>}\n\t * </ul>\n\t */\n\t@Nullable\n\tpublic ArgType replaceClassGenerics(ArgType instanceType, ArgType typeWithGeneric) {\n\t\treturn replaceClassGenerics(instanceType, instanceType, typeWithGeneric);\n\t}\n\n\t@Nullable\n\tpublic ArgType replaceClassGenerics(ArgType instanceType, ArgType genericSourceType, ArgType typeWithGeneric) {\n\t\tif (typeWithGeneric == null || genericSourceType == null) {\n\t\t\treturn null;\n\t\t}\n\t\tMap<ArgType, ArgType> typeVarsMap = Collections.emptyMap();\n\t\tClassTypeVarsAttr typeVars = getClassTypeVars(instanceType);\n\t\tif (typeVars != null) {\n\t\t\ttypeVarsMap = mergeTypeMaps(typeVarsMap, typeVars.getTypeVarsMapFor(genericSourceType));\n\t\t}\n\t\ttypeVarsMap = mergeTypeMaps(typeVarsMap, getTypeVariablesMapping(instanceType));\n\t\tArgType outerType = instanceType.getOuterType();\n\t\twhile (outerType != null) {\n\t\t\ttypeVarsMap = mergeTypeMaps(typeVarsMap, getTypeVariablesMapping(outerType));\n\t\t\touterType = outerType.getOuterType();\n\t\t}\n\t\treturn replaceTypeVariablesUsingMap(typeWithGeneric, typeVarsMap);\n\t}\n\n\tprivate static Map<ArgType, ArgType> mergeTypeMaps(Map<ArgType, ArgType> base, Map<ArgType, ArgType> addition) {\n\t\tif (base.isEmpty()) {\n\t\t\treturn addition;\n\t\t}\n\t\tif (addition.isEmpty()) {\n\t\t\treturn base;\n\t\t}\n\t\tMap<ArgType, ArgType> map = new HashMap<>(base.size() + addition.size());\n\t\tfor (Map.Entry<ArgType, ArgType> entry : base.entrySet()) {\n\t\t\tArgType value = entry.getValue();\n\t\t\tArgType type = addition.remove(value);\n\t\t\tif (type != null) {\n\t\t\t\tmap.put(entry.getKey(), type);\n\t\t\t} else {\n\t\t\t\tmap.put(entry.getKey(), entry.getValue());\n\t\t\t}\n\t\t}\n\t\tmap.putAll(addition);\n\t\treturn map;\n\t}\n\n\tpublic Map<ArgType, ArgType> getTypeVariablesMapping(ArgType clsType) {\n\t\tif (!clsType.isGeneric()) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tList<ArgType> typeParameters = root.getTypeUtils().getClassGenerics(clsType);\n\t\tif (typeParameters.isEmpty()) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tList<ArgType> actualTypes = clsType.getGenericTypes();\n\t\tif (isEmpty(actualTypes)) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tint genericParamsCount = actualTypes.size();\n\t\tif (genericParamsCount != typeParameters.size()) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tMap<ArgType, ArgType> replaceMap = new HashMap<>(genericParamsCount);\n\t\tfor (int i = 0; i < genericParamsCount; i++) {\n\t\t\tArgType actualType = actualTypes.get(i);\n\t\t\tArgType typeVar = typeParameters.get(i);\n\t\t\tif (typeVar.getExtendTypes() != null) {\n\t\t\t\t// force short form (only type var name)\n\t\t\t\ttypeVar = ArgType.genericType(typeVar.getObject());\n\t\t\t}\n\t\t\treplaceMap.put(typeVar, actualType);\n\t\t}\n\t\treturn replaceMap;\n\t}\n\n\tpublic Map<ArgType, ArgType> getTypeVarMappingForInvoke(BaseInvokeNode invokeInsn) {\n\t\tIMethodDetails mthDetails = root.getMethodUtils().getMethodDetails(invokeInsn);\n\t\tif (mthDetails == null) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tMap<ArgType, ArgType> map = new HashMap<>(1 + invokeInsn.getArgsCount());\n\t\taddTypeVarMapping(map, mthDetails.getReturnType(), invokeInsn.getResult());\n\t\tint argCount = Math.min(mthDetails.getArgTypes().size(), invokeInsn.getArgsCount() - invokeInsn.getFirstArgOffset());\n\t\tfor (int i = 0; i < argCount; i++) {\n\t\t\taddTypeVarMapping(map, mthDetails.getArgTypes().get(i), invokeInsn.getArg(i + invokeInsn.getFirstArgOffset()));\n\t\t}\n\t\treturn map;\n\t}\n\n\tprivate static void addTypeVarMapping(Map<ArgType, ArgType> map, ArgType typeVar, InsnArg arg) {\n\t\tif (arg == null || typeVar == null || !typeVar.isTypeKnown()) {\n\t\t\treturn;\n\t\t}\n\t\tif (typeVar.isGenericType()) {\n\t\t\tmap.put(typeVar, arg.getType());\n\t\t}\n\t\t// TODO: resolve inner type vars: 'List<T> -> List<String>' to 'T -> String'\n\t}\n\n\t@Nullable\n\tpublic ArgType replaceMethodGenerics(BaseInvokeNode invokeInsn, IMethodDetails details, ArgType typeWithGeneric) {\n\t\tif (typeWithGeneric == null) {\n\t\t\treturn null;\n\t\t}\n\t\tList<ArgType> methodArgTypes = details.getArgTypes();\n\t\tif (methodArgTypes.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tint firstArgOffset = invokeInsn.getFirstArgOffset();\n\t\tint argsCount = methodArgTypes.size();\n\t\tfor (int i = 0; i < argsCount; i++) {\n\t\t\tArgType methodArgType = methodArgTypes.get(i);\n\t\t\tInsnArg insnArg = invokeInsn.getArg(i + firstArgOffset);\n\t\t\tArgType insnType = insnArg.getType();\n\t\t\tif (methodArgType.equals(typeWithGeneric)) {\n\t\t\t\treturn insnType;\n\t\t\t}\n\t\t}\n\t\t// TODO build complete map for type variables\n\t\treturn null;\n\t}\n\n\t@Nullable\n\tpublic ArgType replaceTypeVariablesUsingMap(ArgType replaceType, Map<ArgType, ArgType> replaceMap) {\n\t\tif (replaceMap.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (replaceType.isGenericType()) {\n\t\t\treturn replaceMap.get(replaceType);\n\t\t}\n\t\tif (replaceType.isArray()) {\n\t\t\tArgType replaced = replaceTypeVariablesUsingMap(replaceType.getArrayElement(), replaceMap);\n\t\t\tif (replaced == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn ArgType.array(replaced);\n\t\t}\n\n\t\tArgType wildcardType = replaceType.getWildcardType();\n\t\tif (wildcardType != null && wildcardType.containsTypeVariable()) {\n\t\t\tArgType newWildcardType = replaceTypeVariablesUsingMap(wildcardType, replaceMap);\n\t\t\tif (newWildcardType == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn ArgType.wildcard(newWildcardType, replaceType.getWildcardBound());\n\t\t}\n\n\t\tif (replaceType.isGeneric()) {\n\t\t\tArgType outerType = replaceType.getOuterType();\n\t\t\tif (outerType != null) {\n\t\t\t\tArgType replacedOuter = replaceTypeVariablesUsingMap(outerType, replaceMap);\n\t\t\t\tif (replacedOuter == null) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tArgType innerType = replaceType.getInnerType();\n\t\t\t\tArgType replacedInner = replaceTypeVariablesUsingMap(innerType, replaceMap);\n\t\t\t\treturn ArgType.outerGeneric(replacedOuter, replacedInner == null ? innerType : replacedInner);\n\t\t\t}\n\t\t\tList<ArgType> genericTypes = replaceType.getGenericTypes();\n\t\t\tif (notEmpty(genericTypes)) {\n\t\t\t\tList<ArgType> newTypes = Utils.collectionMap(genericTypes, t -> {\n\t\t\t\t\tArgType type = replaceTypeVariablesUsingMap(t, replaceMap);\n\t\t\t\t\treturn type == null ? t : type;\n\t\t\t\t});\n\t\t\t\treturn ArgType.generic(replaceType, newTypes);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate ClassTypeVarsAttr buildClassTypeVarsAttr(ClassNode cls) {\n\t\tMap<String, Map<ArgType, ArgType>> map = new HashMap<>();\n\t\tArgType currentClsType = cls.getClassInfo().getType();\n\t\tmap.put(currentClsType.getObject(), getTypeVariablesMapping(currentClsType));\n\n\t\tcls.visitSuperTypes((parent, type) -> {\n\t\t\tList<ArgType> currentVars = type.getGenericTypes();\n\t\t\tif (Utils.isEmpty(currentVars)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tint varsCount = currentVars.size();\n\t\t\tList<ArgType> sourceTypeVars = getClassGenerics(type);\n\t\t\tif (varsCount == sourceTypeVars.size()) {\n\t\t\t\tMap<ArgType, ArgType> parentTypeMap = map.get(parent.getObject());\n\t\t\t\tMap<ArgType, ArgType> varsMap = new HashMap<>(varsCount);\n\t\t\t\tfor (int i = 0; i < varsCount; i++) {\n\t\t\t\t\tArgType currentTypeVar = currentVars.get(i);\n\t\t\t\t\tArgType resultType = parentTypeMap != null ? parentTypeMap.get(currentTypeVar) : null;\n\t\t\t\t\tvarsMap.put(sourceTypeVars.get(i), resultType != null ? resultType : currentTypeVar);\n\t\t\t\t}\n\t\t\t\tmap.put(type.getObject(), varsMap);\n\t\t\t}\n\t\t});\n\t\tList<ArgType> currentTypeVars = cls.getGenericTypeParameters();\n\t\tClassTypeVarsAttr typeVarsAttr = new ClassTypeVarsAttr(currentTypeVars, map);\n\t\tcls.addAttr(typeVarsAttr);\n\t\treturn typeVarsAttr;\n\t}\n\n\tpublic void visitSuperTypes(ArgType type, BiConsumer<ArgType, ArgType> consumer) {\n\t\tClassNode cls = root.resolveClass(type);\n\t\tif (cls != null) {\n\t\t\tcls.visitSuperTypes(consumer);\n\t\t} else {\n\t\t\tClspClass clspClass = root.getClsp().getClsDetails(type);\n\t\t\tif (clspClass != null) {\n\t\t\t\tfor (ArgType superType : clspClass.getParents()) {\n\t\t\t\t\tif (!superType.equals(ArgType.OBJECT)) {\n\t\t\t\t\t\tconsumer.accept(type, superType);\n\t\t\t\t\t\tvisitSuperTypes(superType, consumer);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/regions/AbstractRegion.java",
    "content": "package jadx.core.dex.regions;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.attributes.AttrNode;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\n\npublic abstract class AbstractRegion extends AttrNode implements IRegion {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(AbstractRegion.class);\n\n\tprivate IRegion parent;\n\n\tpublic AbstractRegion(IRegion parent) {\n\t\tthis.parent = parent;\n\t}\n\n\t@Override\n\tpublic IRegion getParent() {\n\t\treturn parent;\n\t}\n\n\t@Override\n\tpublic void setParent(IRegion parent) {\n\t\tthis.parent = parent;\n\t}\n\n\t@Override\n\tpublic boolean replaceSubBlock(IContainer oldBlock, IContainer newBlock) {\n\t\tLOG.warn(\"Replace sub block not supported for class \\\"{}\\\"\", this.getClass());\n\t\treturn false;\n\t}\n\n\tpublic void updateParent(IContainer container, IRegion newParent) {\n\t\tif (container instanceof IRegion) {\n\t\t\t((IRegion) container).setParent(newParent);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/regions/Region.java",
    "content": "package jadx.core.dex.regions;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.api.ICodeWriter;\nimport jadx.core.codegen.RegionGen;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.CodegenException;\n\npublic final class Region extends AbstractRegion {\n\n\tprivate final List<IContainer> blocks;\n\n\tpublic Region(IRegion parent) {\n\t\tsuper(parent);\n\t\tthis.blocks = new ArrayList<>(1);\n\t}\n\n\t@Override\n\tpublic List<IContainer> getSubBlocks() {\n\t\treturn blocks;\n\t}\n\n\tpublic void add(IContainer region) {\n\t\tupdateParent(region, this);\n\t\tblocks.add(region);\n\t}\n\n\t@Override\n\tpublic void generate(RegionGen regionGen, ICodeWriter code) throws CodegenException {\n\t\tfor (IContainer c : blocks) {\n\t\t\tregionGen.makeRegion(code, c);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean replaceSubBlock(IContainer oldBlock, IContainer newBlock) {\n\t\tint i = blocks.indexOf(oldBlock);\n\t\tif (i != -1) {\n\t\t\tblocks.set(i, newBlock);\n\t\t\tupdateParent(newBlock, this);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String baseString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tint size = blocks.size();\n\t\tsb.append('(');\n\t\tsb.append(size);\n\t\tif (size > 0) {\n\t\t\tsb.append(':');\n\t\t\tUtils.listToString(sb, blocks, \"|\", IContainer::baseString);\n\t\t}\n\t\tsb.append(')');\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn 'R' + baseString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/regions/SwitchRegion.java",
    "content": "package jadx.core.dex.regions;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.api.ICodeWriter;\nimport jadx.core.codegen.RegionGen;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IBranchRegion;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.CodegenException;\n\npublic final class SwitchRegion extends AbstractRegion implements IBranchRegion {\n\n\tpublic static final Object DEFAULT_CASE_KEY = new Object() {\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"default\";\n\t\t}\n\t};\n\n\tprivate final BlockNode header;\n\n\tprivate final List<CaseInfo> cases;\n\n\tpublic SwitchRegion(IRegion parent, BlockNode header) {\n\t\tsuper(parent);\n\t\tthis.header = header;\n\t\tthis.cases = new ArrayList<>();\n\t}\n\n\tpublic static final class CaseInfo {\n\t\tprivate final List<Object> keys;\n\t\tprivate final IContainer container;\n\n\t\tpublic CaseInfo(List<Object> keys, IContainer container) {\n\t\t\tthis.keys = keys;\n\t\t\tthis.container = container;\n\t\t}\n\n\t\tpublic boolean isDefaultCase() {\n\t\t\treturn keys.size() == 1 && keys.get(0) == DEFAULT_CASE_KEY;\n\t\t}\n\n\t\tpublic List<Object> getKeys() {\n\t\t\treturn keys;\n\t\t}\n\n\t\tpublic IContainer getContainer() {\n\t\t\treturn container;\n\t\t}\n\t}\n\n\tpublic BlockNode getHeader() {\n\t\treturn header;\n\t}\n\n\tpublic void addCase(List<Object> keysList, IContainer c) {\n\t\tcases.add(new CaseInfo(keysList, c));\n\t}\n\n\tpublic List<CaseInfo> getCases() {\n\t\treturn cases;\n\t}\n\n\tpublic List<IContainer> getCaseContainers() {\n\t\treturn Utils.collectionMap(cases, caseInfo -> caseInfo.container);\n\t}\n\n\t@Override\n\tpublic List<IContainer> getSubBlocks() {\n\t\tList<IContainer> all = new ArrayList<>(cases.size() + 1);\n\t\tall.add(header);\n\t\tall.addAll(getCaseContainers());\n\t\treturn Collections.unmodifiableList(all);\n\t}\n\n\t@Override\n\tpublic List<IContainer> getBranches() {\n\t\treturn Collections.unmodifiableList(getCaseContainers());\n\t}\n\n\t@Override\n\tpublic void generate(RegionGen regionGen, ICodeWriter code) throws CodegenException {\n\t\tregionGen.makeSwitch(this, code);\n\t}\n\n\t@Override\n\tpublic String baseString() {\n\t\treturn \"SW:\" + header.baseString();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"Switch: \").append(header.baseString());\n\t\tfor (CaseInfo caseInfo : cases) {\n\t\t\tList<String> keyStrings = Utils.collectionMap(caseInfo.getKeys(),\n\t\t\t\t\tk -> k == DEFAULT_CASE_KEY ? \"default\" : k.toString());\n\t\t\tsb.append(\"\\n case \")\n\t\t\t\t\t.append(Utils.listToString(keyStrings))\n\t\t\t\t\t.append(\" -> \").append(caseInfo.getContainer());\n\t\t}\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/regions/SynchronizedRegion.java",
    "content": "package jadx.core.dex.regions;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.api.ICodeWriter;\nimport jadx.core.codegen.RegionGen;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.exceptions.CodegenException;\n\npublic final class SynchronizedRegion extends AbstractRegion {\n\n\tprivate final InsnNode enterInsn;\n\tprivate final List<InsnNode> exitInsns = new ArrayList<>();\n\tprivate final Region region;\n\n\tpublic SynchronizedRegion(IRegion parent, InsnNode insn) {\n\t\tsuper(parent);\n\t\tthis.enterInsn = insn;\n\t\tthis.region = new Region(this);\n\t}\n\n\tpublic InsnNode getEnterInsn() {\n\t\treturn enterInsn;\n\t}\n\n\tpublic List<InsnNode> getExitInsns() {\n\t\treturn exitInsns;\n\t}\n\n\tpublic Region getRegion() {\n\t\treturn region;\n\t}\n\n\t@Override\n\tpublic List<IContainer> getSubBlocks() {\n\t\treturn region.getSubBlocks();\n\t}\n\n\t@Override\n\tpublic void generate(RegionGen regionGen, ICodeWriter code) throws CodegenException {\n\t\tregionGen.makeSynchronizedRegion(this, code);\n\t}\n\n\t@Override\n\tpublic String baseString() {\n\t\treturn Integer.toHexString(enterInsn.getOffset());\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Synchronized:\" + region;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/regions/TryCatchRegion.java",
    "content": "package jadx.core.dex.regions;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport jadx.api.ICodeWriter;\nimport jadx.core.codegen.RegionGen;\nimport jadx.core.dex.nodes.IBranchRegion;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.dex.trycatch.TryCatchBlockAttr;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.CodegenException;\n\npublic final class TryCatchRegion extends AbstractRegion implements IBranchRegion {\n\n\tprivate final IContainer tryRegion;\n\tprivate Map<ExceptionHandler, IContainer> catchRegions = Collections.emptyMap();\n\tprivate IContainer finallyRegion;\n\tprivate TryCatchBlockAttr tryCatchBlock;\n\n\tpublic TryCatchRegion(IRegion parent, IContainer tryRegion) {\n\t\tsuper(parent);\n\t\tthis.tryRegion = tryRegion;\n\t}\n\n\tpublic void setTryCatchBlock(TryCatchBlockAttr tryCatchBlock) {\n\t\tthis.tryCatchBlock = tryCatchBlock;\n\t\tint count = tryCatchBlock.getHandlersCount();\n\t\tthis.catchRegions = new LinkedHashMap<>(count);\n\t\tfor (ExceptionHandler handler : tryCatchBlock.getHandlers()) {\n\t\t\tIContainer handlerRegion = handler.getHandlerRegion();\n\t\t\tif (handlerRegion != null) {\n\t\t\t\tif (handler.isFinally()) {\n\t\t\t\t\tfinallyRegion = handlerRegion;\n\t\t\t\t} else {\n\t\t\t\t\tcatchRegions.put(handler, handlerRegion);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic IContainer getTryRegion() {\n\t\treturn tryRegion;\n\t}\n\n\tpublic Map<ExceptionHandler, IContainer> getCatchRegions() {\n\t\treturn catchRegions;\n\t}\n\n\tpublic TryCatchBlockAttr getTryCatchBlock() {\n\t\treturn tryCatchBlock;\n\t}\n\n\tpublic IContainer getFinallyRegion() {\n\t\treturn finallyRegion;\n\t}\n\n\tpublic void setFinallyRegion(IContainer finallyRegion) {\n\t\tthis.finallyRegion = finallyRegion;\n\t}\n\n\t@Override\n\tpublic List<IContainer> getSubBlocks() {\n\t\tList<IContainer> all = new ArrayList<>(2 + catchRegions.size());\n\t\tall.add(tryRegion);\n\t\tall.addAll(catchRegions.values());\n\t\tif (finallyRegion != null) {\n\t\t\tall.add(finallyRegion);\n\t\t}\n\t\treturn Collections.unmodifiableList(all);\n\t}\n\n\t@Override\n\tpublic List<IContainer> getBranches() {\n\t\treturn getSubBlocks();\n\t}\n\n\t@Override\n\tpublic void generate(RegionGen regionGen, ICodeWriter code) throws CodegenException {\n\t\tregionGen.makeTryCatch(this, code);\n\t}\n\n\t@Override\n\tpublic String baseString() {\n\t\treturn tryRegion.baseString();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"Try: \").append(tryRegion);\n\t\tif (!catchRegions.isEmpty()) {\n\t\t\tsb.append(\" catches: \").append(Utils.listToString(catchRegions.values()));\n\t\t}\n\t\tif (finallyRegion != null) {\n\t\t\tsb.append(\" finally: \").append(finallyRegion);\n\t\t}\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/regions/conditions/Compare.java",
    "content": "package jadx.core.dex.regions.conditions;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.IfNode;\nimport jadx.core.dex.instructions.IfOp;\nimport jadx.core.dex.instructions.args.InsnArg;\n\npublic final class Compare {\n\tprivate final IfNode insn;\n\n\tpublic Compare(IfNode insn) {\n\t\tinsn.add(AFlag.HIDDEN);\n\t\tthis.insn = insn;\n\t}\n\n\tpublic IfOp getOp() {\n\t\treturn insn.getOp();\n\t}\n\n\tpublic InsnArg getA() {\n\t\treturn insn.getArg(0);\n\t}\n\n\tpublic InsnArg getB() {\n\t\treturn insn.getArg(1);\n\t}\n\n\tpublic IfNode getInsn() {\n\t\treturn insn;\n\t}\n\n\tpublic Compare invert() {\n\t\tinsn.invertCondition();\n\t\treturn this;\n\t}\n\n\tpublic void normalize() {\n\t\tinsn.normalize();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getA() + \" \" + getOp().getSymbol() + ' ' + getB();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/regions/conditions/ConditionRegion.java",
    "content": "package jadx.core.dex.regions.conditions;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IConditionRegion;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.regions.AbstractRegion;\nimport jadx.core.utils.BlockUtils;\n\npublic abstract class ConditionRegion extends AbstractRegion implements IConditionRegion {\n\n\t@Nullable\n\tprivate IfCondition condition;\n\tprivate List<BlockNode> conditionBlocks = Collections.emptyList();\n\n\tpublic ConditionRegion(IRegion parent) {\n\t\tsuper(parent);\n\t}\n\n\t@Override\n\t@Nullable\n\tpublic IfCondition getCondition() {\n\t\treturn condition;\n\t}\n\n\t@Override\n\tpublic List<BlockNode> getConditionBlocks() {\n\t\treturn conditionBlocks;\n\t}\n\n\t@Override\n\tpublic void invertCondition() {\n\t\tif (condition != null) {\n\t\t\tcondition = IfCondition.invert(condition);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean simplifyCondition() {\n\t\tif (condition == null) {\n\t\t\treturn false;\n\t\t}\n\t\tIfCondition updated = IfCondition.simplify(condition);\n\t\tif (updated != condition) {\n\t\t\tcondition = updated;\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int getConditionSourceLine() {\n\t\tfor (BlockNode block : conditionBlocks) {\n\t\t\tInsnNode lastInsn = BlockUtils.getLastInsn(block);\n\t\t\tif (lastInsn != null) {\n\t\t\t\tint sourceLine = lastInsn.getSourceLine();\n\t\t\t\tif (sourceLine != 0) {\n\t\t\t\t\treturn sourceLine;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Preferred way to update condition info\n\t */\n\tpublic void updateCondition(IfInfo info) {\n\t\tthis.condition = info.getCondition();\n\t\tthis.conditionBlocks = info.getMergedBlocks().toList();\n\t}\n\n\tpublic void updateCondition(IfCondition condition, List<BlockNode> conditionBlocks) {\n\t\tthis.condition = condition;\n\t\tthis.conditionBlocks = conditionBlocks;\n\t}\n\n\tpublic void updateCondition(BlockNode block) {\n\t\tthis.condition = IfCondition.fromIfBlock(block);\n\t\tthis.conditionBlocks = Collections.singletonList(block);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfCondition.java",
    "content": "package jadx.core.dex.regions.conditions;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.attributes.AttrNode;\nimport jadx.core.dex.instructions.ArithNode;\nimport jadx.core.dex.instructions.ArithOp;\nimport jadx.core.dex.instructions.IfNode;\nimport jadx.core.dex.instructions.IfOp;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic final class IfCondition extends AttrNode {\n\n\tpublic enum Mode {\n\t\tCOMPARE,\n\t\tTERNARY,\n\t\tNOT,\n\t\tAND,\n\t\tOR\n\t}\n\n\tprivate final Mode mode;\n\tprivate final List<IfCondition> args;\n\tprivate final Compare compare;\n\n\tprivate IfCondition(Compare compare) {\n\t\tthis.mode = Mode.COMPARE;\n\t\tthis.compare = compare;\n\t\tthis.args = Collections.emptyList();\n\t}\n\n\tprivate IfCondition(Mode mode, List<IfCondition> args) {\n\t\tthis.mode = mode;\n\t\tthis.args = args;\n\t\tthis.compare = null;\n\t}\n\n\tprivate IfCondition(IfCondition c) {\n\t\tthis.mode = c.mode;\n\t\tthis.compare = c.compare;\n\t\tif (c.mode == Mode.COMPARE) {\n\t\t\tthis.args = Collections.emptyList();\n\t\t} else {\n\t\t\tthis.args = new ArrayList<>(c.args);\n\t\t}\n\t}\n\n\tpublic static IfCondition fromIfBlock(BlockNode header) {\n\t\tInsnNode lastInsn = BlockUtils.getLastInsn(header);\n\t\tif (lastInsn == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn fromIfNode((IfNode) lastInsn);\n\t}\n\n\tpublic static IfCondition fromIfNode(IfNode insn) {\n\t\treturn new IfCondition(new Compare(insn));\n\t}\n\n\tpublic static IfCondition ternary(IfCondition a, IfCondition b, IfCondition c) {\n\t\treturn new IfCondition(Mode.TERNARY, Arrays.asList(a, b, c));\n\t}\n\n\tpublic static IfCondition merge(Mode mode, IfCondition a, IfCondition b) {\n\t\tif (a.getMode() == mode) {\n\t\t\tIfCondition n = new IfCondition(a);\n\t\t\tn.addArg(b);\n\t\t\treturn n;\n\t\t}\n\t\treturn new IfCondition(mode, Arrays.asList(a, b));\n\t}\n\n\tpublic Mode getMode() {\n\t\treturn mode;\n\t}\n\n\tpublic List<IfCondition> getArgs() {\n\t\treturn args;\n\t}\n\n\tpublic IfCondition first() {\n\t\treturn args.get(0);\n\t}\n\n\tpublic IfCondition second() {\n\t\treturn args.get(1);\n\t}\n\n\tpublic IfCondition third() {\n\t\treturn args.get(2);\n\t}\n\n\tpublic void addArg(IfCondition c) {\n\t\targs.add(c);\n\t}\n\n\tpublic boolean isCompare() {\n\t\treturn mode == Mode.COMPARE;\n\t}\n\n\tpublic Compare getCompare() {\n\t\treturn compare;\n\t}\n\n\tpublic static IfCondition invert(IfCondition cond) {\n\t\tMode mode = cond.getMode();\n\t\tswitch (mode) {\n\t\t\tcase COMPARE:\n\t\t\t\treturn new IfCondition(cond.getCompare().invert());\n\t\t\tcase TERNARY:\n\t\t\t\treturn ternary(cond.first(), not(cond.second()), not(cond.third()));\n\t\t\tcase NOT:\n\t\t\t\treturn cond.first();\n\t\t\tcase AND:\n\t\t\tcase OR:\n\t\t\t\tList<IfCondition> args = cond.getArgs();\n\t\t\t\tList<IfCondition> newArgs = new ArrayList<>(args.size());\n\t\t\t\tfor (IfCondition arg : args) {\n\t\t\t\t\tnewArgs.add(invert(arg));\n\t\t\t\t}\n\t\t\t\treturn new IfCondition(mode == Mode.AND ? Mode.OR : Mode.AND, newArgs);\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Unknown mode for invert: \" + mode);\n\t}\n\n\tpublic static IfCondition not(IfCondition cond) {\n\t\tif (cond.getMode() == Mode.NOT) {\n\t\t\treturn cond.first();\n\t\t}\n\t\tif (cond.getCompare() != null) {\n\t\t\treturn new IfCondition(cond.compare.invert());\n\t\t}\n\t\treturn new IfCondition(Mode.NOT, Collections.singletonList(cond));\n\t}\n\n\tpublic static IfCondition simplify(IfCondition cond) {\n\t\tif (cond.isCompare()) {\n\t\t\tCompare c = cond.getCompare();\n\t\t\tIfCondition i = simplifyCmpOp(c);\n\t\t\tif (i != null) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t\tif (c.getOp() == IfOp.EQ && c.getB().isFalse()) {\n\t\t\t\tcond = new IfCondition(Mode.NOT, Collections.singletonList(new IfCondition(c.invert())));\n\t\t\t} else {\n\t\t\t\tc.normalize();\n\t\t\t}\n\t\t}\n\t\tList<IfCondition> args = null;\n\t\tfor (int i = 0; i < cond.getArgs().size(); i++) {\n\t\t\tIfCondition arg = cond.getArgs().get(i);\n\t\t\tIfCondition simpl = simplify(arg);\n\t\t\tif (simpl != arg) {\n\t\t\t\tif (args == null) {\n\t\t\t\t\targs = new ArrayList<>(cond.getArgs());\n\t\t\t\t}\n\t\t\t\targs.set(i, simpl);\n\t\t\t}\n\t\t}\n\t\tif (args != null) {\n\t\t\t// arguments was changed\n\t\t\tcond = new IfCondition(cond.getMode(), args);\n\t\t}\n\t\tif (cond.getMode() == Mode.NOT && cond.first().getMode() == Mode.NOT) {\n\t\t\tcond = invert(cond.first());\n\t\t}\n\t\tif (cond.getMode() == Mode.TERNARY && cond.first().getMode() == Mode.NOT) {\n\t\t\tcond = invert(cond);\n\t\t}\n\n\t\t// for condition with a lot of negations => make invert\n\t\tif (cond.getMode() == Mode.OR || cond.getMode() == Mode.AND) {\n\t\t\tint count = cond.getArgs().size();\n\t\t\tif (count > 1) {\n\t\t\t\tint negCount = 0;\n\t\t\t\tfor (IfCondition arg : cond.getArgs()) {\n\t\t\t\t\tif (arg.getMode() == Mode.NOT\n\t\t\t\t\t\t\t|| (arg.isCompare() && arg.getCompare().getOp() == IfOp.NE)) {\n\t\t\t\t\t\tnegCount++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (negCount > count / 2) {\n\t\t\t\t\treturn not(invert(cond));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn cond;\n\t}\n\n\tprivate static IfCondition simplifyCmpOp(Compare c) {\n\t\tif (!c.getA().isInsnWrap()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!c.getB().isLiteral()) {\n\t\t\treturn null;\n\t\t}\n\t\tlong lit = ((LiteralArg) c.getB()).getLiteral();\n\t\tif (lit != 0 && lit != 1) {\n\t\t\treturn null;\n\t\t}\n\n\t\tInsnNode wrapInsn = ((InsnWrapArg) c.getA()).getWrapInsn();\n\t\tswitch (wrapInsn.getType()) {\n\t\t\tcase CMP_L:\n\t\t\tcase CMP_G:\n\t\t\t\tif (lit == 0) {\n\t\t\t\t\tIfNode insn = c.getInsn();\n\t\t\t\t\tinsn.changeCondition(insn.getOp(), wrapInsn.getArg(0), wrapInsn.getArg(1));\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase ARITH:\n\t\t\t\tif (c.getB().getType() == ArgType.BOOLEAN) {\n\t\t\t\t\tArithOp arithOp = ((ArithNode) wrapInsn).getOp();\n\t\t\t\t\tif (arithOp == ArithOp.OR || arithOp == ArithOp.AND) {\n\t\t\t\t\t\tIfOp ifOp = c.getInsn().getOp();\n\t\t\t\t\t\tboolean isTrue = ifOp == IfOp.NE && lit == 0\n\t\t\t\t\t\t\t\t|| ifOp == IfOp.EQ && lit == 1;\n\n\t\t\t\t\t\tIfOp op = isTrue ? IfOp.NE : IfOp.EQ;\n\t\t\t\t\t\tMode mode = isTrue && arithOp == ArithOp.OR\n\t\t\t\t\t\t\t\t|| !isTrue && arithOp == ArithOp.AND ? Mode.OR : Mode.AND;\n\n\t\t\t\t\t\tIfNode if1 = new IfNode(op, -1, wrapInsn.getArg(0), LiteralArg.litFalse());\n\t\t\t\t\t\tIfNode if2 = new IfNode(op, -1, wrapInsn.getArg(1), LiteralArg.litFalse());\n\t\t\t\t\t\treturn new IfCondition(mode,\n\t\t\t\t\t\t\t\tArrays.asList(new IfCondition(new Compare(if1)),\n\t\t\t\t\t\t\t\t\t\tnew IfCondition(new Compare(if2))));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic List<RegisterArg> getRegisterArgs() {\n\t\tList<RegisterArg> list = new ArrayList<>();\n\t\tif (mode == Mode.COMPARE) {\n\t\t\tcompare.getInsn().getRegisterArgs(list);\n\t\t} else {\n\t\t\tfor (IfCondition arg : args) {\n\t\t\t\tlist.addAll(arg.getRegisterArgs());\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic boolean replaceArg(InsnArg from, InsnArg to) {\n\t\tif (mode == Mode.COMPARE) {\n\t\t\treturn compare.getInsn().replaceArg(from, to);\n\t\t}\n\t\tfor (IfCondition arg : args) {\n\t\t\tif (arg.replaceArg(from, to)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic void visitInsns(Consumer<InsnNode> visitor) {\n\t\tif (mode == Mode.COMPARE) {\n\t\t\tcompare.getInsn().visitInsns(visitor);\n\t\t} else {\n\t\t\targs.forEach(arg -> arg.visitInsns(visitor));\n\t\t}\n\t}\n\n\tpublic List<InsnNode> collectInsns() {\n\t\tList<InsnNode> list = new ArrayList<>();\n\t\tvisitInsns(list::add);\n\t\treturn list;\n\t}\n\n\tpublic int getSourceLine() {\n\t\tfor (InsnNode insn : collectInsns()) {\n\t\t\tint line = insn.getSourceLine();\n\t\t\tif (line != 0) {\n\t\t\t\treturn line;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\t@Nullable\n\tpublic InsnNode getFirstInsn() {\n\t\tif (mode == Mode.COMPARE) {\n\t\t\treturn compare.getInsn();\n\t\t}\n\t\treturn args.get(0).getFirstInsn();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tswitch (mode) {\n\t\t\tcase COMPARE:\n\t\t\t\treturn compare.toString();\n\t\t\tcase TERNARY:\n\t\t\t\treturn first() + \" ? \" + second() + \" : \" + third();\n\t\t\tcase NOT:\n\t\t\t\treturn \"!(\" + first() + ')';\n\t\t\tcase AND:\n\t\t\tcase OR:\n\t\t\t\tString op = mode == Mode.OR ? \" || \" : \" && \";\n\t\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\t\tsb.append('(');\n\t\t\t\tfor (Iterator<IfCondition> it = args.iterator(); it.hasNext();) {\n\t\t\t\t\tIfCondition arg = it.next();\n\t\t\t\t\tsb.append(arg);\n\t\t\t\t\tif (it.hasNext()) {\n\t\t\t\t\t\tsb.append(op);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tsb.append(')');\n\t\t\t\treturn sb.toString();\n\t\t}\n\t\treturn \"??\";\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof IfCondition)) {\n\t\t\treturn false;\n\t\t}\n\t\tIfCondition other = (IfCondition) obj;\n\t\tif (mode != other.mode) {\n\t\t\treturn false;\n\t\t}\n\t\treturn Objects.equals(args, other.args)\n\t\t\t\t&& Objects.equals(compare, other.compare);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = super.hashCode();\n\t\tresult = 31 * result + mode.hashCode();\n\t\tresult = 31 * result + args.hashCode();\n\t\tresult = 31 * result + (compare != null ? compare.hashCode() : 0);\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfInfo.java",
    "content": "package jadx.core.dex.regions.conditions;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.blocks.BlockSet;\n\npublic final class IfInfo {\n\tprivate final MethodNode mth;\n\tprivate final IfCondition condition;\n\tprivate final BlockSet mergedBlocks;\n\tprivate final BlockNode thenBlock;\n\tprivate final BlockNode elseBlock;\n\tprivate final Set<BlockNode> skipBlocks;\n\tprivate final List<InsnNode> forceInlineInsns;\n\tprivate BlockNode outBlock;\n\n\tpublic IfInfo(MethodNode mth, IfCondition condition, BlockNode thenBlock, BlockNode elseBlock) {\n\t\tthis(mth, condition, thenBlock, elseBlock, BlockSet.empty(mth), new HashSet<>(), new ArrayList<>());\n\t}\n\n\tpublic IfInfo(IfInfo info, BlockNode thenBlock, BlockNode elseBlock) {\n\t\tthis(info.getMth(), info.getCondition(), thenBlock, elseBlock,\n\t\t\t\tinfo.getMergedBlocks(), info.getSkipBlocks(), info.getForceInlineInsns());\n\t}\n\n\tprivate IfInfo(MethodNode mth, IfCondition condition, BlockNode thenBlock, BlockNode elseBlock,\n\t\t\tBlockSet mergedBlocks, Set<BlockNode> skipBlocks, List<InsnNode> forceInlineInsns) {\n\t\tthis.mth = mth;\n\t\tthis.condition = condition;\n\t\tthis.thenBlock = thenBlock;\n\t\tthis.elseBlock = elseBlock;\n\t\tthis.mergedBlocks = mergedBlocks;\n\t\tthis.skipBlocks = skipBlocks;\n\t\tthis.forceInlineInsns = forceInlineInsns;\n\t}\n\n\tpublic static IfInfo invert(IfInfo info) {\n\t\treturn new IfInfo(info.getMth(),\n\t\t\t\tIfCondition.invert(info.getCondition()),\n\t\t\t\tinfo.getElseBlock(), info.getThenBlock(),\n\t\t\t\tinfo.getMergedBlocks(), info.getSkipBlocks(), info.getForceInlineInsns());\n\t}\n\n\tpublic void merge(IfInfo... arr) {\n\t\tfor (IfInfo info : arr) {\n\t\t\tmergedBlocks.addAll(info.getMergedBlocks());\n\t\t\tskipBlocks.addAll(info.getSkipBlocks());\n\t\t\taddInsnsForForcedInline(info.getForceInlineInsns());\n\t\t}\n\t}\n\n\t@Deprecated\n\tpublic BlockNode getFirstIfBlock() {\n\t\treturn mergedBlocks.getFirst();\n\t}\n\n\tpublic BlockSet getMergedBlocks() {\n\t\treturn mergedBlocks;\n\t}\n\n\tpublic MethodNode getMth() {\n\t\treturn mth;\n\t}\n\n\tpublic IfCondition getCondition() {\n\t\treturn condition;\n\t}\n\n\tpublic Set<BlockNode> getSkipBlocks() {\n\t\treturn skipBlocks;\n\t}\n\n\tpublic BlockNode getThenBlock() {\n\t\treturn thenBlock;\n\t}\n\n\tpublic BlockNode getElseBlock() {\n\t\treturn elseBlock;\n\t}\n\n\tpublic BlockNode getOutBlock() {\n\t\treturn outBlock;\n\t}\n\n\tpublic void setOutBlock(BlockNode outBlock) {\n\t\tthis.outBlock = outBlock;\n\t}\n\n\tpublic List<InsnNode> getForceInlineInsns() {\n\t\treturn forceInlineInsns;\n\t}\n\n\tpublic void resetForceInlineInsns() {\n\t\tforceInlineInsns.clear();\n\t}\n\n\tpublic void addInsnsForForcedInline(List<InsnNode> insns) {\n\t\tforceInlineInsns.addAll(insns);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"IfInfo: then: \" + thenBlock + \", else: \" + elseBlock;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfRegion.java",
    "content": "package jadx.core.dex.regions.conditions;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.api.ICodeWriter;\nimport jadx.core.codegen.RegionGen;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IBranchRegion;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.utils.exceptions.CodegenException;\n\npublic final class IfRegion extends ConditionRegion implements IBranchRegion {\n\tprivate IContainer thenRegion;\n\tprivate IContainer elseRegion;\n\n\tpublic IfRegion(IRegion parent) {\n\t\tsuper(parent);\n\t}\n\n\tpublic IContainer getThenRegion() {\n\t\treturn thenRegion;\n\t}\n\n\tpublic void setThenRegion(IContainer thenRegion) {\n\t\tthis.thenRegion = thenRegion;\n\t}\n\n\tpublic IContainer getElseRegion() {\n\t\treturn elseRegion;\n\t}\n\n\tpublic void setElseRegion(IContainer elseRegion) {\n\t\tthis.elseRegion = elseRegion;\n\t}\n\n\tpublic void invert() {\n\t\tinvertCondition();\n\t\t// swap regions\n\t\tIContainer tmp = thenRegion;\n\t\tthenRegion = elseRegion;\n\t\telseRegion = tmp;\n\t}\n\n\tpublic int getSourceLine() {\n\t\treturn getConditionSourceLine();\n\t}\n\n\t@Override\n\tpublic List<IContainer> getSubBlocks() {\n\t\tList<BlockNode> conditionBlocks = getConditionBlocks();\n\t\tList<IContainer> all = new ArrayList<>(conditionBlocks.size() + 2);\n\t\tall.addAll(conditionBlocks);\n\t\tif (thenRegion != null) {\n\t\t\tall.add(thenRegion);\n\t\t}\n\t\tif (elseRegion != null) {\n\t\t\tall.add(elseRegion);\n\t\t}\n\t\treturn Collections.unmodifiableList(all);\n\t}\n\n\t@Override\n\tpublic List<IContainer> getBranches() {\n\t\tList<IContainer> branches = new ArrayList<>(2);\n\t\tbranches.add(thenRegion);\n\t\tbranches.add(elseRegion);\n\t\treturn Collections.unmodifiableList(branches);\n\t}\n\n\t@Override\n\tpublic boolean replaceSubBlock(IContainer oldBlock, IContainer newBlock) {\n\t\tif (oldBlock == thenRegion) {\n\t\t\tthenRegion = newBlock;\n\t\t\tupdateParent(thenRegion, this);\n\t\t\treturn true;\n\t\t}\n\t\tif (oldBlock == elseRegion) {\n\t\t\telseRegion = newBlock;\n\t\t\tupdateParent(elseRegion, this);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void generate(RegionGen regionGen, ICodeWriter code) throws CodegenException {\n\t\tregionGen.makeIf(this, code, true);\n\t}\n\n\t@Override\n\tpublic String baseString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tif (thenRegion != null) {\n\t\t\tsb.append(thenRegion.baseString());\n\t\t}\n\t\tif (elseRegion != null) {\n\t\t\tsb.append(elseRegion.baseString());\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"IF \" + getConditionBlocks() + \" THEN: \" + thenRegion + \" ELSE: \" + elseRegion;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/regions/loops/ForEachLoop.java",
    "content": "package jadx.core.dex.regions.loops;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic final class ForEachLoop extends LoopType {\n\tprivate final InsnNode varArgInsn;\n\tprivate final InsnNode iterableArgInsn;\n\n\tpublic ForEachLoop(RegisterArg varArg, InsnArg iterableArg) {\n\t\t// store for-each args in fake instructions to\n\t\t// save code semantics and allow args manipulations like args inlining\n\t\tvarArgInsn = new InsnNode(InsnType.REGION_ARG, 0);\n\t\tvarArgInsn.add(AFlag.DONT_INLINE);\n\t\tvarArgInsn.setResult(varArg.duplicate());\n\n\t\titerableArgInsn = new InsnNode(InsnType.REGION_ARG, 1);\n\t\titerableArgInsn.add(AFlag.DONT_INLINE);\n\t\titerableArgInsn.addArg(iterableArg.duplicate());\n\n\t\t// will be declared at codegen\n\t\tgetVarArg().getSVar().getCodeVar().setDeclared(true);\n\t}\n\n\tpublic void injectFakeInsns(LoopRegion loopRegion) {\n\t\tloopRegion.getInfo().getPreHeader().getInstructions().add(iterableArgInsn);\n\t\tloopRegion.getHeader().getInstructions().add(0, varArgInsn);\n\t}\n\n\tpublic RegisterArg getVarArg() {\n\t\treturn varArgInsn.getResult();\n\t}\n\n\tpublic InsnArg getIterableArg() {\n\t\treturn iterableArgInsn.getArg(0);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/regions/loops/ForLoop.java",
    "content": "package jadx.core.dex.regions.loops;\n\nimport jadx.core.dex.nodes.InsnNode;\n\npublic final class ForLoop extends LoopType {\n\n\tprivate final InsnNode initInsn;\n\tprivate final InsnNode incrInsn;\n\n\tpublic ForLoop(InsnNode initInsn, InsnNode incrInsn) {\n\t\tthis.initInsn = initInsn;\n\t\tthis.incrInsn = incrInsn;\n\t}\n\n\tpublic InsnNode getInitInsn() {\n\t\treturn initInsn;\n\t}\n\n\tpublic InsnNode getIncrInsn() {\n\t\treturn incrInsn;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/regions/loops/LoopRegion.java",
    "content": "package jadx.core.dex.regions.loops;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ICodeWriter;\nimport jadx.core.codegen.RegionGen;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.nodes.LoopInfo;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.regions.conditions.ConditionRegion;\nimport jadx.core.dex.regions.conditions.IfCondition;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.exceptions.CodegenException;\n\npublic final class LoopRegion extends ConditionRegion {\n\n\tprivate final LoopInfo info;\n\tprivate final boolean conditionAtEnd;\n\tprivate final @Nullable BlockNode header;\n\t// instruction which must be executed before condition in every loop\n\tprivate @Nullable BlockNode preCondition;\n\n\tprivate IRegion body;\n\tprivate LoopType type;\n\n\tpublic LoopRegion(IRegion parent, LoopInfo info, @Nullable BlockNode header, boolean reversed) {\n\t\tsuper(parent);\n\t\tthis.info = info;\n\t\tthis.header = header;\n\t\tthis.conditionAtEnd = reversed;\n\t\tif (header != null) {\n\t\t\tupdateCondition(header);\n\t\t}\n\t}\n\n\tpublic LoopInfo getInfo() {\n\t\treturn info;\n\t}\n\n\t@Nullable\n\tpublic BlockNode getHeader() {\n\t\treturn header;\n\t}\n\n\tpublic boolean isEndless() {\n\t\treturn header == null;\n\t}\n\n\tpublic IRegion getBody() {\n\t\treturn body;\n\t}\n\n\tpublic void setBody(IRegion body) {\n\t\tthis.body = body;\n\t}\n\n\tpublic boolean isConditionAtEnd() {\n\t\treturn conditionAtEnd;\n\t}\n\n\t/**\n\t * Set instructions which must be executed before condition in every loop\n\t */\n\tpublic void setPreCondition(BlockNode preCondition) {\n\t\tthis.preCondition = preCondition;\n\t}\n\n\t/**\n\t * Check if pre-conditions can be inlined into loop condition\n\t */\n\tpublic boolean checkPreCondition() {\n\t\tList<InsnNode> insns = preCondition.getInstructions();\n\t\tif (insns.isEmpty()) {\n\t\t\treturn true;\n\t\t}\n\t\tIfCondition condition = getCondition();\n\t\tif (condition == null) {\n\t\t\treturn false;\n\t\t}\n\t\tList<RegisterArg> conditionArgs = condition.getRegisterArgs();\n\t\tif (conditionArgs.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tint size = insns.size();\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tInsnNode insn = insns.get(i);\n\t\t\tif (insn.getResult() == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tRegisterArg res = insn.getResult();\n\t\t\tif (res.getSVar().getUseCount() > 1) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tboolean found = false;\n\t\t\t// search result arg in other insns\n\t\t\tfor (int j = i + 1; j < size; j++) {\n\t\t\t\tif (insns.get(i).containsVar(res)) {\n\t\t\t\t\tfound = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// or in if insn\n\t\t\tif (!found && InsnUtils.containsVar(conditionArgs, res)) {\n\t\t\t\tfound = true;\n\t\t\t}\n\t\t\tif (!found) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Move all preCondition block instructions before conditionBlock instructions\n\t */\n\tpublic void mergePreCondition() {\n\t\tif (preCondition != null && header != null) {\n\t\t\tList<InsnNode> condInsns = header.getInstructions();\n\t\t\tList<InsnNode> preCondInsns = preCondition.getInstructions();\n\t\t\tpreCondInsns.addAll(condInsns);\n\t\t\tcondInsns.clear();\n\t\t\tcondInsns.addAll(preCondInsns);\n\t\t\theader.add(AFlag.ALLOW_MULTIPLE_INSNS_LOOP_COND);\n\t\t\tpreCondInsns.clear();\n\t\t\tpreCondition = null;\n\t\t}\n\t}\n\n\tpublic int getSourceLine() {\n\t\tInsnNode lastInsn = BlockUtils.getLastInsn(header);\n\t\tint headerLine = lastInsn == null ? 0 : lastInsn.getSourceLine();\n\t\tif (headerLine != 0) {\n\t\t\treturn headerLine;\n\t\t}\n\t\treturn getConditionSourceLine();\n\t}\n\n\tpublic LoopType getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setType(LoopType type) {\n\t\tthis.type = type;\n\t}\n\n\t@Override\n\tpublic List<IContainer> getSubBlocks() {\n\t\tList<IContainer> all = new ArrayList<>(2 + getConditionBlocks().size());\n\t\tif (preCondition != null) {\n\t\t\tall.add(preCondition);\n\t\t}\n\t\tall.addAll(getConditionBlocks());\n\t\tif (body != null) {\n\t\t\tall.add(body);\n\t\t}\n\t\treturn all;\n\t}\n\n\t@Override\n\tpublic boolean replaceSubBlock(IContainer oldBlock, IContainer newBlock) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void generate(RegionGen regionGen, ICodeWriter code) throws CodegenException {\n\t\tregionGen.makeLoop(this, code);\n\t}\n\n\t@Override\n\tpublic String baseString() {\n\t\treturn body == null ? \"-\" : body.baseString();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"LOOP:\" + info.getId() + \": \" + baseString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/regions/loops/LoopType.java",
    "content": "package jadx.core.dex.regions.loops;\n\npublic abstract class LoopType {\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/trycatch/CatchAttr.java",
    "content": "package jadx.core.dex.trycatch;\n\nimport java.util.Comparator;\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.utils.Utils;\n\npublic class CatchAttr implements IJadxAttribute {\n\n\tpublic static CatchAttr build(List<ExceptionHandler> handlers) {\n\t\thandlers.sort(Comparator.comparingInt(ExceptionHandler::getHandlerOffset));\n\t\treturn new CatchAttr(handlers);\n\t}\n\n\tprivate final List<ExceptionHandler> handlers;\n\n\tprivate CatchAttr(List<ExceptionHandler> handlers) {\n\t\tthis.handlers = handlers;\n\t}\n\n\tpublic List<ExceptionHandler> getHandlers() {\n\t\treturn handlers;\n\t}\n\n\t@Override\n\tpublic AType<CatchAttr> getAttrType() {\n\t\treturn AType.EXC_CATCH;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof CatchAttr)) {\n\t\t\treturn false;\n\t\t}\n\t\tCatchAttr catchAttr = (CatchAttr) o;\n\t\treturn getHandlers().equals(catchAttr.getHandlers());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn getHandlers().hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Catch: \" + Utils.listToString(getHandlers());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/trycatch/ExcHandlerAttr.java",
    "content": "package jadx.core.dex.trycatch;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\n\npublic class ExcHandlerAttr implements IJadxAttribute {\n\n\tprivate final ExceptionHandler handler;\n\n\tpublic ExcHandlerAttr(ExceptionHandler handler) {\n\t\tthis.handler = handler;\n\t}\n\n\t@Override\n\tpublic AType<ExcHandlerAttr> getAttrType() {\n\t\treturn AType.EXC_HANDLER;\n\t}\n\n\tpublic TryCatchBlockAttr getTryBlock() {\n\t\treturn handler.getTryBlock();\n\t}\n\n\tpublic ExceptionHandler getHandler() {\n\t\treturn handler;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ExcHandler: \" + handler;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/trycatch/ExceptionHandler.java",
    "content": "package jadx.core.dex.trycatch;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.Consts;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.Utils;\n\npublic class ExceptionHandler {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ExceptionHandler.class);\n\n\tprivate final List<ClassInfo> catchTypes = new ArrayList<>(1);\n\tprivate final int handlerOffset;\n\n\tprivate BlockNode handlerBlock;\n\tprivate final List<BlockNode> blocks = new ArrayList<>();\n\tprivate IContainer handlerRegion;\n\tprivate InsnArg arg;\n\n\tprivate TryCatchBlockAttr tryBlock;\n\tprivate boolean isFinally;\n\n\tprivate boolean removed = false;\n\n\tpublic static ExceptionHandler build(MethodNode mth, int addr, @Nullable ClassInfo type) {\n\t\tExceptionHandler eh = new ExceptionHandler(addr);\n\t\teh.addCatchType(mth, type);\n\t\treturn eh;\n\t}\n\n\tprivate ExceptionHandler(int addr) {\n\t\tthis.handlerOffset = addr;\n\t}\n\n\t/**\n\t * Add exception type to catch block\n\t *\n\t * @param type - null for 'all' or 'Throwable' handler\n\t */\n\tpublic boolean addCatchType(MethodNode mth, @Nullable ClassInfo type) {\n\t\tif (type != null) {\n\t\t\tif (catchTypes.contains(type)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn catchTypes.add(type);\n\t\t}\n\t\tif (!this.catchTypes.isEmpty()) {\n\t\t\tmth.addDebugComment(\"Throwable added to exception handler: '\" + catchTypeStr() + \"', keep only Throwable\");\n\t\t\tcatchTypes.clear();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic void addCatchTypes(MethodNode mth, Collection<ClassInfo> types) {\n\t\tfor (ClassInfo type : types) {\n\t\t\taddCatchType(mth, type);\n\t\t}\n\t}\n\n\tpublic List<ClassInfo> getCatchTypes() {\n\t\treturn catchTypes;\n\t}\n\n\tpublic ArgType getArgType() {\n\t\tif (isCatchAll()) {\n\t\t\treturn ArgType.THROWABLE;\n\t\t}\n\t\tList<ClassInfo> types = getCatchTypes();\n\t\tif (types.size() == 1) {\n\t\t\treturn types.iterator().next().getType();\n\t\t} else {\n\t\t\treturn ArgType.THROWABLE;\n\t\t}\n\t}\n\n\tpublic boolean isCatchAll() {\n\t\tif (catchTypes.isEmpty()) {\n\t\t\treturn true;\n\t\t}\n\t\tfor (ClassInfo classInfo : catchTypes) {\n\t\t\tif (classInfo.getFullName().equals(Consts.CLASS_THROWABLE)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic int getHandlerOffset() {\n\t\treturn handlerOffset;\n\t}\n\n\tpublic BlockNode getHandlerBlock() {\n\t\treturn handlerBlock;\n\t}\n\n\tpublic void setHandlerBlock(BlockNode handlerBlock) {\n\t\tthis.handlerBlock = handlerBlock;\n\t}\n\n\tpublic List<BlockNode> getBlocks() {\n\t\treturn blocks;\n\t}\n\n\tpublic void addBlock(BlockNode node) {\n\t\tblocks.add(node);\n\t}\n\n\tpublic IContainer getHandlerRegion() {\n\t\treturn handlerRegion;\n\t}\n\n\tpublic void setHandlerRegion(IContainer handlerRegion) {\n\t\tthis.handlerRegion = handlerRegion;\n\t}\n\n\tpublic InsnArg getArg() {\n\t\treturn arg;\n\t}\n\n\tpublic void setArg(InsnArg arg) {\n\t\tthis.arg = arg;\n\t}\n\n\tpublic void setTryBlock(TryCatchBlockAttr tryBlock) {\n\t\tthis.tryBlock = tryBlock;\n\t}\n\n\tpublic TryCatchBlockAttr getTryBlock() {\n\t\treturn tryBlock;\n\t}\n\n\tpublic boolean isFinally() {\n\t\treturn isFinally;\n\t}\n\n\tpublic void setFinally(boolean isFinally) {\n\t\tthis.isFinally = isFinally;\n\t}\n\n\tpublic boolean isRemoved() {\n\t\treturn removed;\n\t}\n\n\t@Nullable\n\tpublic BlockNode getBottomSplitter() {\n\t\tTryCatchBlockAttr handlerTryBlock = getTryBlock();\n\t\t// TODO: Implement support for finding bottom splitter of catch with inner tries\n\t\tif (handlerTryBlock.getInnerTryBlocks().size() > 1) {\n\t\t\tLOG.warn(\"No support yet for finding bottom block of try body with multipe inner trys\");\n\t\t\treturn null;\n\t\t}\n\t\tfinal TryCatchBlockAttr searchForTryBody;\n\t\tif (handlerTryBlock.getInnerTryBlocks().isEmpty()) {\n\t\t\tsearchForTryBody = handlerTryBlock;\n\t\t} else {\n\t\t\tsearchForTryBody = Utils.getOne(handlerTryBlock.getInnerTryBlocks());\n\t\t}\n\n\t\tBlockNode splitter = null;\n\t\tfor (BlockNode handlerPredecessor : getHandlerBlock().getPredecessors()) {\n\t\t\tif (!handlerPredecessor.contains(AFlag.EXC_BOTTOM_SPLITTER)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (BlockNode splitterPredecessor : handlerPredecessor.getPredecessors()) {\n\t\t\t\tTryCatchBlockAttr tryBody = splitterPredecessor.get(AType.TRY_BLOCK);\n\t\t\t\tif (tryBody == searchForTryBody) {\n\t\t\t\t\tsplitter = handlerPredecessor;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (splitter != null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn splitter;\n\t}\n\n\tpublic void markForRemove() {\n\t\tthis.removed = true;\n\t\tthis.blocks.forEach(b -> b.add(AFlag.REMOVE));\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tExceptionHandler that = (ExceptionHandler) o;\n\t\treturn handlerOffset == that.handlerOffset\n\t\t\t\t&& catchTypes.equals(that.catchTypes)\n\t\t\t\t&& Objects.equals(tryBlock, that.tryBlock);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(catchTypes, handlerOffset /* , tryBlock */);\n\t}\n\n\tpublic String catchTypeStr() {\n\t\treturn catchTypes.isEmpty() ? \"all\" : Utils.listToString(catchTypes, \" | \", ClassInfo::getShortName);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn catchTypeStr() + \" -> \" + InsnUtils.formatOffset(handlerOffset);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlockAttr.java",
    "content": "package jadx.core.dex.trycatch;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.LoopInfo;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.Edge;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class TryCatchBlockAttr implements IJadxAttribute {\n\n\tpublic static boolean isImplicitOrMerged(TryCatchBlockAttr tryBlock) {\n\t\treturn tryBlock.isMerged() || tryBlock.getHandlers().isEmpty();\n\t}\n\n\tprivate final int id;\n\tprivate final List<ExceptionHandler> handlers;\n\tprivate List<BlockNode> blocks;\n\n\tprivate TryCatchBlockAttr outerTryBlock;\n\tprivate List<TryCatchBlockAttr> innerTryBlocks = Collections.emptyList();\n\tprivate boolean merged = false;\n\n\tprivate BlockNode topSplitter;\n\n\tpublic TryCatchBlockAttr(int id, List<ExceptionHandler> handlers, List<BlockNode> blocks) {\n\t\tthis.id = id;\n\t\tthis.handlers = handlers;\n\t\tthis.blocks = blocks;\n\n\t\thandlers.forEach(h -> h.setTryBlock(this));\n\t}\n\n\tpublic boolean isAllHandler() {\n\t\treturn handlers.size() == 1 && handlers.get(0).isCatchAll();\n\t}\n\n\tpublic boolean isThrowOnly() {\n\t\tboolean throwFound = false;\n\t\tfor (BlockNode block : blocks) {\n\t\t\tList<InsnNode> insns = block.getInstructions();\n\t\t\tif (insns.size() != 1) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tInsnNode insn = insns.get(0);\n\t\t\tswitch (insn.getType()) {\n\t\t\t\tcase MOVE_EXCEPTION:\n\t\t\t\tcase MONITOR_EXIT:\n\t\t\t\t\t// allowed instructions\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase THROW:\n\t\t\t\t\tthrowFound = true;\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn throwFound;\n\t}\n\n\tpublic int getId() {\n\t\treturn id;\n\t}\n\n\tpublic List<ExceptionHandler> getHandlers() {\n\t\treturn handlers;\n\t}\n\n\tpublic int getHandlersCount() {\n\t\treturn handlers.size();\n\t}\n\n\tpublic List<BlockNode> getBlocks() {\n\t\treturn blocks;\n\t}\n\n\tpublic void setBlocks(List<BlockNode> blocks) {\n\t\tthis.blocks = blocks;\n\t}\n\n\tpublic void clear() {\n\t\tblocks.clear();\n\t\thandlers.forEach(ExceptionHandler::markForRemove);\n\t\thandlers.clear();\n\t}\n\n\tpublic void removeBlock(BlockNode block) {\n\t\tblocks.remove(block);\n\t}\n\n\tpublic void removeHandler(ExceptionHandler handler) {\n\t\thandlers.remove(handler);\n\t\thandler.markForRemove();\n\t}\n\n\tpublic List<TryCatchBlockAttr> getInnerTryBlocks() {\n\t\treturn innerTryBlocks;\n\t}\n\n\tpublic void addInnerTryBlock(TryCatchBlockAttr inner) {\n\t\tif (this.innerTryBlocks.isEmpty()) {\n\t\t\tthis.innerTryBlocks = new ArrayList<>();\n\t\t}\n\t\tthis.innerTryBlocks.add(inner);\n\t}\n\n\tpublic TryCatchBlockAttr getOuterTryBlock() {\n\t\treturn outerTryBlock;\n\t}\n\n\tpublic void setOuterTryBlock(TryCatchBlockAttr outerTryBlock) {\n\t\tthis.outerTryBlock = outerTryBlock;\n\t}\n\n\tpublic BlockNode getTopSplitter() {\n\t\treturn topSplitter;\n\t}\n\n\tpublic void setTopSplitter(BlockNode topSplitter) {\n\t\tthis.topSplitter = topSplitter;\n\t}\n\n\tpublic boolean isMerged() {\n\t\treturn merged;\n\t}\n\n\tpublic void setMerged(boolean merged) {\n\t\tthis.merged = merged;\n\t}\n\n\tpublic int id() {\n\t\treturn id;\n\t}\n\n\tpublic List<TryEdge> getHandlerTryEdges() {\n\t\tList<ExceptionHandler> mergedHandlers = getMergedHandlers();\n\t\tList<TryEdge> edges = new ArrayList<>(mergedHandlers.size());\n\t\tfor (ExceptionHandler handler : mergedHandlers) {\n\t\t\tBlockNode handlerBlock = handler.getHandlerBlock();\n\t\t\tBlockNode handlerSplitter = handler.getBottomSplitter();\n\t\t\tif (handlerSplitter == null) {\n\t\t\t\t// If we cannot find a bottom splitter, there might be none. In this case, assume that the top\n\t\t\t\t// splitter of this try catch is the source of the exit.\n\t\t\t\tList<BlockNode> allChildren = ListUtils.filter(handlerBlock.getPredecessors(), blk -> getBlocks().contains(blk));\n\t\t\t\thandlerSplitter = BlockUtils.getBottomBlock(allChildren);\n\t\t\t\tif (handlerSplitter == null) {\n\t\t\t\t\thandlerSplitter = getTopSplitter();\n\t\t\t\t}\n\t\t\t}\n\t\t\tTryEdge edge = new TryEdge(handlerSplitter, handlerBlock, handler);\n\t\t\tedges.add(edge);\n\t\t}\n\t\treturn edges;\n\t}\n\n\tpublic List<TryEdge> getFallthroughTryEdges() {\n\t\tList<TryEdge> edges = new LinkedList<>();\n\t\tList<BlockNode> exploredBlocks = new ArrayList<>();\n\t\tList<TryCatchBlockAttr> exploredTrys = new LinkedList<>();\n\n\t\tgetFallthroughTryEdges(edges, exploredBlocks, exploredTrys);\n\t\treturn edges;\n\t}\n\n\tpublic void getFallthroughTryEdges(List<TryEdge> edges, List<BlockNode> exploredBlocks, List<TryCatchBlockAttr> exploredTrys) {\n\t\tList<ExceptionHandler> mergedHandlers = getMergedHandlers();\n\t\tSet<BlockNode> searchBlocks = new HashSet<>();\n\t\tsearchBlocks.addAll(getBlocks());\n\t\tfor (ExceptionHandler handler : mergedHandlers) {\n\t\t\tsearchBlocks.removeAll(handler.getBlocks());\n\t\t}\n\n\t\tBlockNode sourceBlock = BlockUtils.getTopBlock(new ArrayList<>(searchBlocks));\n\n\t\texploredTrys.add(this);\n\n\t\texploreTryPath(edges, sourceBlock, searchBlocks, exploredBlocks, exploredTrys);\n\t}\n\n\tpublic List<TryEdge> getTryEdges() {\n\t\tList<TryEdge> handlerEdges = getHandlerTryEdges();\n\t\tList<TryEdge> fallthroughEdges = getFallthroughTryEdges();\n\t\tList<TryEdge> edges = new ArrayList<>(handlerEdges.size() + fallthroughEdges.size());\n\t\tedges.addAll(handlerEdges);\n\t\tedges.addAll(fallthroughEdges);\n\t\treturn Collections.unmodifiableList(edges);\n\t}\n\n\tprivate void exploreTryPath(List<TryEdge> edges, BlockNode blk, Set<BlockNode> searchBlocks, List<BlockNode> exploredBlocks,\n\t\t\tList<TryCatchBlockAttr> exploredTrys) {\n\t\tfor (BlockNode successor : blk.getSuccessors()) {\n\t\t\t// If a separate branch has already explored this block, we don't need to recalculate its exits.\n\t\t\tif (exploredBlocks.contains(successor)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// If this is a bottom splitter, ignore - we only care about non-handler edges.\n\t\t\tif (successor.contains(AFlag.EXC_BOTTOM_SPLITTER)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\texploredBlocks.add(successor);\n\n\t\t\tif (successor.contains(AFlag.LOOP_END)) {\n\t\t\t\tfinal var loopsAttrList = successor.get(AType.LOOP);\n\t\t\t\tfinal List<LoopInfo> loops = loopsAttrList.getList();\n\t\t\t\tfinal List<BlockNode> loopStartBlocks = new LinkedList<>();\n\t\t\t\tfor (final LoopInfo loop : loops) {\n\t\t\t\t\tloopStartBlocks.add(loop.getStart());\n\t\t\t\t\tfinal List<Edge> loopEdges = loop.getExitEdges();\n\t\t\t\t\tfor (final Edge loopEdge : loopEdges) {\n\t\t\t\t\t\tif (loopEdge.getTarget() == successor) {\n\t\t\t\t\t\t\tloopStartBlocks.add(loopEdge.getSource());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfinal boolean includesAllLoopStart = ListUtils.allMatch(loopStartBlocks, exploredBlocks::contains);\n\t\t\t\tif (!includesAllLoopStart) {\n\t\t\t\t\tedges.add(new TryEdge(blk, successor, TryEdgeType.LOOP_EXIT));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tboolean isPathToAnySearchBlock = false;\n\t\t\tfor (final BlockNode searchBlock : searchBlocks) {\n\t\t\t\tif (BlockUtils.isPathExists(successor, searchBlock)) {\n\t\t\t\t\tisPathToAnySearchBlock = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!searchBlocks.contains(successor) && !isPathToAnySearchBlock) {\n\t\t\t\t// This block is not contained within this try's block list. This can either be since it is an exit\n\t\t\t\t// to the try or it is a block which leads to an exit (for example, an exception handler).\n\n\t\t\t\t// If this block (successor) leads to an exit, then the \"bottom block\" of all try blocks and this\n\t\t\t\t// block will be\n\t\t\t\t// equal to the bottom block of all try blocks. If this block is an exit, then either:\n\t\t\t\t// - a path does not exist from all try blocks to this block, thus making the bottom block null.\n\t\t\t\t// - a path does exist from all try blocks to this block but no more try blocks follow, thus making\n\t\t\t\t// the bottom block this block.\n\t\t\t\tList<BlockNode> allBlocksWithCurrent = new ArrayList<>(getBlocks().size() + 1);\n\t\t\t\tallBlocksWithCurrent.addAll(getBlocks());\n\t\t\t\tallBlocksWithCurrent.add(successor);\n\t\t\t\tBlockNode bottomBlock = BlockUtils.getBottomBlock(allBlocksWithCurrent);\n\n\t\t\t\tif (!(bottomBlock == null || bottomBlock == successor)) {\n\t\t\t\t\t// This block leads to an exit.\n\t\t\t\t\texploreTryPath(edges, successor, searchBlocks, exploredBlocks, exploredTrys);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tBlockNode emptyPathEndOfSuccessor = BlockUtils.followEmptyPath(successor, false, false);\n\n\t\t\t\tif (emptyPathEndOfSuccessor.contains(AFlag.EXC_TOP_SPLITTER)) {\n\t\t\t\t\t// This block is an exit which enters another try catch. In this case, the next try catch is within\n\t\t\t\t\t// the same scope. Thus, we will take all of the edges out of that try and add them to the list of\n\t\t\t\t\t// edges of this try.\n\t\t\t\t\tSet<TryCatchBlockAttr> nestedTrys = new HashSet<>();\n\t\t\t\t\tList<BlockNode> allSuccessorsOnTryBody = ListUtils.filter(emptyPathEndOfSuccessor.getSuccessors(),\n\t\t\t\t\t\t\tpotentialTryBlock -> potentialTryBlock.contains(AFlag.TRY_ENTER));\n\t\t\t\t\tfor (BlockNode tryBodyEnter : allSuccessorsOnTryBody) {\n\t\t\t\t\t\tTryCatchBlockAttr nestedTry = tryBodyEnter.get(AType.TRY_BLOCK);\n\t\t\t\t\t\tif (nestedTry == null) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If we have already added a try's edges, skip over it to avoid infinite recursion.\n\t\t\t\t\t\tif (exploredTrys.contains(nestedTry)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Unsure of why these top splitters have to be the same for them to be \"nested\" trys, but this\n\t\t\t\t\t\t// seems to work (?)\n\t\t\t\t\t\tif (nestedTry.getTopSplitter() != getTopSplitter()) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tnestedTrys.add(nestedTry);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only will we attempt to add nested inners if there exists any. If none exist, perform normal\n\t\t\t\t\t// handling of the edge.\n\t\t\t\t\tif (!nestedTrys.isEmpty()) {\n\t\t\t\t\t\tfor (TryCatchBlockAttr nestedTry : nestedTrys) {\n\t\t\t\t\t\t\tnestedTry.getFallthroughTryEdges(edges, exploredBlocks, exploredTrys);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (bottomBlock == null) {\n\t\t\t\t\t// This block is an exit which occurs before all try blocks are logically executed.\n\t\t\t\t\tedges.add(new TryEdge(blk, successor, TryEdgeType.PREMATURE_EXIT));\n\t\t\t\t} else if (bottomBlock == successor) {\n\t\t\t\t\t// This block is an exit which occurs after all try blocks are logically executed.\n\t\t\t\t\tedges.add(new TryEdge(blk, successor, TryEdgeType.TRUE_FALLTHROUGH));\n\t\t\t\t} else {\n\t\t\t\t\t// All possible cases should have been caught by the above if / else and the preceeding if.\n\t\t\t\t\t// If this is hit, any changes made to this algorithm must aptly handle all possible code paths\n\t\t\t\t\t// before executing this.\n\t\t\t\t\tthrow new JadxRuntimeException(\n\t\t\t\t\t\t\t\"Unexpected code execution branch taken during try edge resolution: blk=\"\n\t\t\t\t\t\t\t\t\t+ blk + \",successor=\" + successor);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\texploreTryPath(edges, successor, searchBlocks, exploredBlocks, exploredTrys);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic List<ExceptionHandler> getMergedHandlers() {\n\t\tboolean hasInnerBlocks = !getInnerTryBlocks().isEmpty();\n\t\tfinal List<ExceptionHandler> mergedHandlers;\n\t\tif (hasInnerBlocks) {\n\t\t\t// collect handlers from this and all inner blocks\n\t\t\t// (intentionally not using recursive collect for now)\n\t\t\tmergedHandlers = new ArrayList<>(getHandlers());\n\t\t\tfor (TryCatchBlockAttr innerTryBlock : getInnerTryBlocks()) {\n\t\t\t\tmergedHandlers.addAll(innerTryBlock.getHandlers());\n\t\t\t}\n\t\t} else {\n\t\t\tmergedHandlers = getHandlers();\n\t\t}\n\t\treturn Collections.unmodifiableList(mergedHandlers);\n\t}\n\n\tpublic Map<TryEdge, BlockNode> getEdgeBlockMap(MethodNode mth) {\n\t\tList<TryEdge> edges = getTryEdges();\n\t\tMap<TryEdge, BlockNode> blockMap = new HashMap<>();\n\t\tfor (TryEdge edge : edges) {\n\t\t\tblockMap.put(edge, edge.getTarget());\n\t\t}\n\t\treturn blockMap;\n\t}\n\n\tpublic TryEdgeScopeGroupMap getExecutionScopeGroups(MethodNode mth) {\n\t\tMap<TryEdge, BlockNode> handlerBlocks = getEdgeBlockMap(mth);\n\t\tTryEdgeScopeGroupMap scopeGroups = new TryEdgeScopeGroupMap(mth, this, handlerBlocks.size());\n\t\tscopeGroups.populateFromEdges(handlerBlocks);\n\n\t\treturn scopeGroups;\n\t}\n\n\tpublic Map<BlockNode, List<TryEdge>> getHandlerFallthroughGroups(MethodNode mth, TryEdgeScopeGroupMap scopeGroups) {\n\t\treturn scopeGroups.getScopeEnds(mth);\n\t}\n\n\tpublic List<BlockNode> getSearchBlocksFromFallthroughGroups(MethodNode mth, ExceptionHandler finallyHandler,\n\t\t\tMap<BlockNode, List<TryEdge>> fallthroughGroups) {\n\n\t\tList<BlockNode> searchBlocks = new LinkedList<>();\n\t\tfor (Map.Entry<BlockNode, List<TryEdge>> entry : fallthroughGroups.entrySet()) {\n\t\t\tBlockNode scopeEndBlock = entry.getKey();\n\t\t\tList<TryEdge> sourceHandlers = entry.getValue();\n\n\t\t\tfor (BlockNode scopeEndPredecessor : scopeEndBlock.getPredecessors()) {\n\t\t\t\t// Add all predecessors to the scope end which are connected to some handler's scope start\n\t\t\t\ttry (Stream<TryEdge> stream = sourceHandlers.stream()) {\n\t\t\t\t\tObject[] matchedHandlerPaths =\n\t\t\t\t\t\t\tstream.filter(handler -> !(handler.isHandlerExit() && handler.getExceptionHandler() == finallyHandler))\n\t\t\t\t\t\t\t\t\t.map(handler -> handler.getTarget())\n\t\t\t\t\t\t\t\t\t.filter(scopeStart -> BlockUtils.isPathExists(scopeStart, scopeEndPredecessor))\n\t\t\t\t\t\t\t\t\t.toArray();\n\t\t\t\t\tif (matchedHandlerPaths.length != 0) {\n\t\t\t\t\t\tsearchBlocks.add(scopeEndPredecessor);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn searchBlocks;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<? extends IJadxAttribute> getAttrType() {\n\t\treturn AType.TRY_BLOCK;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn handlers.hashCode() + 31 * blocks.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tTryCatchBlockAttr other = (TryCatchBlockAttr) obj;\n\t\treturn id == other.id\n\t\t\t\t&& handlers.equals(other.handlers)\n\t\t\t\t&& blocks.equals(other.blocks);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tif (merged) {\n\t\t\treturn \"Merged into \" + outerTryBlock;\n\t\t}\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"TryCatch #\").append(id).append(\" {\").append(Utils.listToString(handlers));\n\t\tsb.append(\", blocks: (\").append(Utils.listToString(blocks)).append(')');\n\t\tif (topSplitter != null) {\n\t\t\tsb.append(\", top: \").append(topSplitter);\n\t\t}\n\t\tif (outerTryBlock != null) {\n\t\t\tsb.append(\", outer: #\").append(outerTryBlock.id);\n\t\t}\n\t\tif (!innerTryBlocks.isEmpty()) {\n\t\t\tsb.append(\", inners: \").append(Utils.listToString(innerTryBlocks, inner -> \"#\" + inner.id));\n\t\t}\n\t\tsb.append(\" }\");\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/trycatch/TryEdge.java",
    "content": "package jadx.core.dex.trycatch;\n\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n/**\n * Represents an edge between two blocks representing an exit out of a try body.\n * The source block will be within the try body.\n */\npublic final class TryEdge {\n\n\tprivate final BlockNode source;\n\tprivate final BlockNode target;\n\tprivate final Optional<ExceptionHandler> handler;\n\tprivate final TryEdgeType type;\n\n\tpublic TryEdge(final BlockNode source, final BlockNode target, final TryEdgeType type) {\n\t\tthis(source, target, type, Optional.empty());\n\t}\n\n\tpublic TryEdge(final BlockNode source, final BlockNode target, final @NotNull ExceptionHandler handler) {\n\t\tthis(source, target, TryEdgeType.HANDLER, Optional.of(handler));\n\t}\n\n\tpublic TryEdge(final BlockNode source, final BlockNode target, final TryEdgeType type, final Optional<ExceptionHandler> handler) {\n\t\tthis.source = source;\n\t\tthis.target = target;\n\t\tthis.handler = handler;\n\t\tthis.type = type;\n\n\t\tif (isHandlerExit() && handler.isEmpty()) {\n\t\t\tthrow new JadxRuntimeException(\"Attempted to add a null exception handler as an edge of \\\"\" + type.toString() + \"\\\" type\");\n\t\t} else if (isNotHandlerExit() && handler.isPresent()) {\n\t\t\tthrow new JadxRuntimeException(\"Attempted to add an exception handler as an edge of \\\"\" + type.toString() + \"\\\" type\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic final String toString() {\n\t\tStringBuilder sb = new StringBuilder(\"TryEdge: [\");\n\t\tsb.append(type);\n\t\tsb.append(' ');\n\t\tsb.append(source.toString());\n\t\tsb.append(\" -> \");\n\t\tsb.append(target.toString());\n\t\tsb.append(\"] - Handler: \");\n\t\tif (handler.isEmpty()) {\n\t\t\tsb.append(\"None\");\n\t\t} else {\n\t\t\tsb.append(handler.get().toString());\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic final boolean equals(Object obj) {\n\t\tif (!(obj instanceof TryEdge)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal TryEdge other = (TryEdge) obj;\n\n\t\treturn source.equals(other.source)\n\t\t\t\t&& target.equals(other.target)\n\t\t\t\t&& handler.equals(other.handler)\n\t\t\t\t&& type.equals(other.type);\n\t}\n\n\t@Override\n\tpublic final int hashCode() {\n\t\treturn Objects.hash(source, target, type, handler);\n\t}\n\n\tpublic final BlockNode getSource() {\n\t\treturn source;\n\t}\n\n\tpublic final BlockNode getTarget() {\n\t\treturn target;\n\t}\n\n\tpublic final TryEdgeType getType() {\n\t\treturn type;\n\t}\n\n\tpublic final boolean isHandlerExit() {\n\t\treturn type == TryEdgeType.HANDLER;\n\t}\n\n\tpublic final boolean isNotHandlerExit() {\n\t\treturn !isHandlerExit();\n\t}\n\n\tpublic final ExceptionHandler getExceptionHandler() {\n\t\tif (!isHandlerExit()) {\n\t\t\tthrow new JadxRuntimeException(\"Attempted to get the exception handler of a non-handler edge type\");\n\t\t}\n\n\t\tif (handler.isEmpty()) {\n\t\t\tthrow new JadxRuntimeException(\"Attempted to get the exception handler of a handler edge type, however none was present\");\n\t\t}\n\n\t\treturn handler.get();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/trycatch/TryEdgeScopeGroupMap.java",
    "content": "package jadx.core.dex.trycatch;\n\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.Pair;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n/**\n * A map which stores the information of how try edges correlate with each other.\n * K is a try edge and V contains all other try edges whose who share the same logical scope.\n */\npublic final class TryEdgeScopeGroupMap implements Map<TryEdge, Map<TryEdge, BlockNode>> {\n\n\tprivate static final class TryEdgeScope {\n\n\t\tprivate final TryEdge edge;\n\t\tprivate final BlockNode block;\n\n\t\tpublic TryEdgeScope(final TryEdge edge, final BlockNode block) {\n\t\t\tthis.edge = edge;\n\t\t\tthis.block = block;\n\t\t}\n\t}\n\n\tprivate final List<Pair<TryEdge>> mergedEdges = new ArrayList<>();\n\tprivate final TryCatchBlockAttr tryCatch;\n\tprivate final Map<TryEdge, Map<TryEdge, BlockNode>> underlyingMap;\n\n\tpublic TryEdgeScopeGroupMap(final MethodNode mth, final TryCatchBlockAttr tryCatch, final int initialCapacity) {\n\t\tthis.tryCatch = tryCatch;\n\t\tunderlyingMap = new HashMap<>(initialCapacity);\n\t}\n\n\t@Override\n\tpublic final void clear() {\n\t\tunderlyingMap.clear();\n\t}\n\n\t@Override\n\tpublic final boolean containsKey(Object key) {\n\t\treturn underlyingMap.containsKey(key);\n\t}\n\n\t@Override\n\tpublic final boolean containsValue(Object value) {\n\t\tif (!(value instanceof TryEdge)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal TryEdge edge = (TryEdge) value;\n\t\treturn underlyingMap.containsKey(edge);\n\t}\n\n\t@Override\n\tpublic final Set<Entry<TryEdge, Map<TryEdge, BlockNode>>> entrySet() {\n\t\treturn underlyingMap.entrySet();\n\t}\n\n\t@Override\n\tpublic final Map<TryEdge, BlockNode> get(Object key) {\n\t\treturn underlyingMap.get(key);\n\t}\n\n\t@Override\n\tpublic final boolean isEmpty() {\n\t\treturn underlyingMap.isEmpty();\n\t}\n\n\t@Override\n\tpublic final Set<TryEdge> keySet() {\n\t\treturn underlyingMap.keySet();\n\t}\n\n\t@Override\n\tpublic final Map<TryEdge, BlockNode> put(TryEdge key, Map<TryEdge, BlockNode> value) {\n\t\treturn underlyingMap.put(key, value);\n\t}\n\n\t@Override\n\tpublic final void putAll(Map<? extends TryEdge, ? extends Map<TryEdge, BlockNode>> otherMap) {\n\t\tunderlyingMap.putAll(otherMap);\n\t}\n\n\t@Override\n\tpublic final Map<TryEdge, BlockNode> remove(Object key) {\n\t\treturn underlyingMap.remove(key);\n\t}\n\n\t@Override\n\tpublic final int size() {\n\t\treturn underlyingMap.size();\n\t}\n\n\t@Override\n\tpublic final Collection<Map<TryEdge, BlockNode>> values() {\n\t\treturn underlyingMap.values();\n\t}\n\n\tpublic final boolean hasMergedEdges() {\n\t\treturn !mergedEdges.isEmpty();\n\t}\n\n\tpublic final List<Pair<TryEdge>> getMergedScopes() {\n\t\treturn mergedEdges;\n\t}\n\n\tpublic final void populateFromEdges(final Map<TryEdge, BlockNode> edges) {\n\t\tmergeSameScopes(edges);\n\n\t\tfor (final TryEdge edge : edges.keySet()) {\n\t\t\tfinal BlockNode edgeBlock = edges.get(edge);\n\n\t\t\tfinal Map<TryEdge, BlockNode> handlerFallthroughMap = createEdgeTerminusMap(edges, edge, edgeBlock);\n\t\t\tput(edge, handlerFallthroughMap);\n\t\t}\n\t}\n\n\t/**\n\t * Returns a map of all points where edges meet with each other, dictating the end of that\n\t * edge's scope.\n\t *\n\t * @param mth\n\t * @return\n\t */\n\tpublic Map<BlockNode, List<TryEdge>> getScopeEnds(final MethodNode mth) {\n\t\tfinal Map<BlockNode, List<TryEdge>> groups = new HashMap<>();\n\n\t\t// A list containing pairs of edges where there are no shared common clean successors between the\n\t\t// two handlers. This usually indicates that these edge pairs must be processed differently.\n\t\tfinal List<TryEdge> isolatedEdgePairs = new LinkedList<>();\n\n\t\tfor (final TryEdge mergeEdgeA : keySet()) {\n\t\t\tfinal Pair<TryEdge> edgeMergedPair = getMergedNodeFromEdge(mergeEdgeA);\n\n\t\t\tif (edgeMergedPair != null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfinal Map<TryEdge, BlockNode> handlerRelations = get(mergeEdgeA);\n\n\t\t\tfinal List<BlockNode> scopeEnds = new ArrayList<>(handlerRelations.size());\n\t\t\tfor (final TryEdge mergeEdgeB : handlerRelations.keySet()) {\n\t\t\t\tfinal Pair<TryEdge> mergedPairFromRelation = getMergedNodeFromEdge(mergeEdgeB);\n\t\t\t\tif (mergedPairFromRelation != null && mergedPairFromRelation.getFirst() == mergeEdgeA) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfinal BlockNode sharedTerminator = handlerRelations.get(mergeEdgeB);\n\n\t\t\t\tif (sharedTerminator == null) {\n\t\t\t\t\t// There are no common clean succesors between the two handlers.\n\t\t\t\t\tisolatedEdgePairs.add(mergeEdgeB);\n\t\t\t\t} else {\n\t\t\t\t\tscopeEnds.add(sharedTerminator);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (scopeEnds.isEmpty()) {\n\t\t\t\t// Isolated edge pairs found - we will deal with them later\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfinal BlockNode topGrouping = BlockUtils.getTopBlock(scopeEnds);\n\n\t\t\tif (groups.containsKey(topGrouping)) {\n\t\t\t\tgroups.get(topGrouping).add(mergeEdgeA);\n\t\t\t} else {\n\t\t\t\tfinal List<TryEdge> groupingHandlers = new LinkedList<>();\n\t\t\t\tgroupingHandlers.add(mergeEdgeA);\n\t\t\t\tgroups.put(topGrouping, groupingHandlers);\n\t\t\t}\n\t\t}\n\n\t\tfor (final TryEdge isolatedEdge : isolatedEdgePairs) {\n\t\t\tboolean isInList = false;\n\t\t\tfor (final List<TryEdge> foundEdges : groups.values()) {\n\t\t\t\tif (foundEdges.contains(isolatedEdge)) {\n\t\t\t\t\tisInList = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isInList) {\n\t\t\t\t// The isolated edge is not isolated with another handler - we can ignore this edge.\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// If an isolated edge has not been added to the groupings, we will add it now.\n\t\t\t// This will be added by locating the point where the search for a common successor stops.\n\t\t\t// Since a common successor of all blocks which do have some clean path can be found in the method\n\t\t\t// exit node, the mentioned point will be the farthest successor of the edge target which has no\n\t\t\t// clean successors.\n\n\t\t\tfinal BlockNode target = isolatedEdge.getTarget();\n\t\t\tfinal List<BlockNode> successorBlocks = BlockUtils.collectAllSuccessors(mth, target, true);\n\t\t\tfinal BlockNode cleanSuccessorEnd = BlockUtils.getBottomBlock(successorBlocks);\n\t\t\tif (cleanSuccessorEnd == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Could not find bottom clean successor for isolated try edge\");\n\t\t\t}\n\n\t\t\tfinal List<TryEdge> scopeTerminusList;\n\t\t\tif (groups.containsKey(cleanSuccessorEnd)) {\n\t\t\t\tscopeTerminusList = groups.get(cleanSuccessorEnd);\n\t\t\t} else {\n\t\t\t\tscopeTerminusList = new LinkedList<>();\n\t\t\t\tgroups.put(cleanSuccessorEnd, scopeTerminusList);\n\t\t\t}\n\t\t\tscopeTerminusList.add(isolatedEdge);\n\t\t}\n\n\t\tif (groups.size() == 1) {\n\t\t\tfor (final Pair<TryEdge> pair : mergedEdges) {\n\t\t\t\tfinal TryEdge keptEdge = pair.getFirst();\n\t\t\t\tfinal TryEdge removedEdge = pair.getSecond();\n\n\t\t\t\tif (keptEdge.isHandlerExit() && !tryCatch.getHandlers().contains(keptEdge.getExceptionHandler())) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (removedEdge.isHandlerExit() && !tryCatch.getHandlers().contains(removedEdge.getExceptionHandler())) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// If both handlers are not handler exits, we can assume that the code paths merge at some Phi node\n\t\t\t\t// which begins the finally duplicated code.\n\t\t\t\tif (keptEdge.isNotHandlerExit() && removedEdge.isNotHandlerExit()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfor (final List<TryEdge> edgesWithTerminus : groups.values()) {\n\t\t\t\t\tif (edgesWithTerminus.contains(keptEdge)) {\n\t\t\t\t\t\tedgesWithTerminus.remove(keptEdge);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfinal BlockNode terminus = get(keptEdge).get(removedEdge);\n\t\t\t\tfinal List<TryEdge> terminusEdges;\n\t\t\t\tif (!groups.containsKey(terminus)) {\n\t\t\t\t\tterminusEdges = new LinkedList<>();\n\t\t\t\t\tterminusEdges.add(keptEdge);\n\t\t\t\t\tgroups.put(terminus, terminusEdges);\n\t\t\t\t} else {\n\t\t\t\t\tterminusEdges = groups.get(terminus);\n\t\t\t\t}\n\t\t\t\tterminusEdges.add(removedEdge);\n\t\t\t}\n\t\t}\n\n\t\treturn groups;\n\t}\n\n\t@Nullable\n\tprivate Pair<TryEdge> getMergedNodeFromEdge(final TryEdge edge) {\n\t\tfor (Pair<TryEdge> pair : mergedEdges) {\n\t\t\tif (pair.getSecond() == edge) {\n\t\t\t\treturn pair;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate Map<TryEdge, BlockNode> createEdgeTerminusMap(final Map<TryEdge, BlockNode> edgeStartMap, final TryEdge edge,\n\t\t\tfinal BlockNode edgeStart) {\n\t\tfinal Map<TryEdge, BlockNode> scopeRelations = new HashMap<>(edgeStartMap.size() - 1);\n\t\tfor (final TryEdge otherEdge : edgeStartMap.keySet()) {\n\t\t\tif (edge == otherEdge) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfinal BlockNode otherEdgeStart = edgeStartMap.get(otherEdge);\n\n\t\t\tfinal boolean eitherEdgeIsHandler = edge.isHandlerExit() || otherEdge.isHandlerExit();\n\t\t\tif (otherEdgeStart == edgeStart && eitherEdgeIsHandler) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (otherEdgeStart.isMthExitBlock()) {\n\t\t\t\tscopeRelations.put(otherEdge, otherEdgeStart);\n\t\t\t\t// Everything leads to the exit node so merged edges are no longer needed\n\t\t\t\tmergedEdges.clear();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (edgeStart.isMthExitBlock()) {\n\t\t\t\tscopeRelations.put(otherEdge, edgeStart);\n\t\t\t\t// Everything leads to the exit node so merged edges are no longer needed\n\t\t\t\tmergedEdges.clear();\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfinal BitSet sharedPostDominators = (BitSet) edgeStart.getPostDoms().clone();\n\t\t\tfinal BitSet otherPostDoms = otherEdgeStart.getPostDoms();\n\t\t\tif (sharedPostDominators.isEmpty() || otherPostDoms.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tsharedPostDominators.and(otherPostDoms);\n\n\t\t\tfinal List<BlockNode> postDomHandler = new LinkedList<>();\n\t\t\tBlockNode currentBlock = edgeStart;\n\t\t\twhile (currentBlock != null) {\n\t\t\t\tpostDomHandler.add(currentBlock);\n\t\t\t\tcurrentBlock = currentBlock.getIPostDom();\n\t\t\t}\n\n\t\t\tBlockNode commonPostDom = null;\n\t\t\tcurrentBlock = otherEdgeStart;\n\t\t\twhile (currentBlock != null) {\n\t\t\t\tif (postDomHandler.contains(currentBlock)) {\n\t\t\t\t\tcommonPostDom = currentBlock;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcurrentBlock = currentBlock.getIPostDom();\n\t\t\t}\n\n\t\t\tfinal BlockNode scopeEnd = commonPostDom;\n\t\t\tscopeRelations.put(otherEdge, scopeEnd);\n\t\t}\n\t\treturn scopeRelations;\n\t}\n\n\t/**\n\t * If two scopes ever merge, as in if one edge leads to the same execution point as the target of\n\t * another edge, this function will record it.\n\t *\n\t * @param handlers\n\t * @return\n\t */\n\tprivate Map<TryEdge, BlockNode> mergeSameScopes(final Map<TryEdge, BlockNode> handlers) {\n\t\tfinal List<Entry<TryEdge, BlockNode>> exceptionHandlers = new ArrayList<>(handlers.entrySet());\n\n\t\tfinal List<Pair<TryEdgeScope>> handlerPairs = new LinkedList<>();\n\t\tfor (int i = 0; i < exceptionHandlers.size(); i++) {\n\t\t\tfor (int j = i + 1; j < exceptionHandlers.size(); j++) {\n\t\t\t\tTryEdgeScope a = new TryEdgeScope(exceptionHandlers.get(i).getKey(), exceptionHandlers.get(i).getValue());\n\t\t\t\tTryEdgeScope b = new TryEdgeScope(exceptionHandlers.get(j).getKey(), exceptionHandlers.get(j).getValue());\n\t\t\t\thandlerPairs.add(new Pair<>(a, b));\n\t\t\t}\n\t\t}\n\n\t\tfinal Map<TryEdge, BlockNode> simplifiedScopes = new HashMap<>(handlers);\n\n\t\tint i = 0;\n\t\twhile (i < handlerPairs.size()) {\n\t\t\tfinal Pair<TryEdgeScope> handlerPair = handlerPairs.get(i);\n\n\t\t\tfinal TryEdgeScope edgeScopeA = handlerPair.getFirst();\n\t\t\tfinal TryEdgeScope edgeScopeB = handlerPair.getSecond();\n\t\t\tfinal BlockNode edgeBlockA = edgeScopeA.block;\n\t\t\tfinal BlockNode edgeBlockB = edgeScopeB.block;\n\t\t\tfinal boolean pathExists = BlockUtils.isPathExists(edgeBlockA, edgeBlockB) || BlockUtils.isPathExists(edgeBlockB, edgeBlockA);\n\t\t\tif (pathExists) {\n\t\t\t\tBlockNode bottomBlock = BlockUtils.getBottomBlock(List.of(edgeBlockA, edgeBlockB));\n\t\t\t\t// The two blocks are within the same scope - remove these from the matrix\n\t\t\t\tfinal TryEdge removeHandler = edgeBlockA != bottomBlock ? edgeScopeA.edge : edgeScopeB.edge;\n\t\t\t\tfinal TryEdge keepHandler = edgeBlockA == bottomBlock ? edgeScopeA.edge : edgeScopeB.edge;\n\t\t\t\tsimplifiedScopes.remove(removeHandler);\n\t\t\t\thandlerPairs.remove(i);\n\n\t\t\t\tmergedEdges.add(new Pair<>(keepHandler, removeHandler));\n\t\t\t} else {\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\n\t\treturn simplifiedScopes;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/trycatch/TryEdgeType.java",
    "content": "package jadx.core.dex.trycatch;\n\npublic enum TryEdgeType {\n\tTRUE_FALLTHROUGH,\n\tPREMATURE_EXIT,\n\tLOOP_EXIT,\n\tHANDLER\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/AbstractVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.exceptions.JadxException;\n\npublic abstract class AbstractVisitor implements IDexTreeVisitor {\n\n\t@Override\n\tpublic void init(RootNode root) throws JadxException {\n\t\t// no op implementation\n\t}\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) throws JadxException {\n\t\t// no op implementation\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\t// no op implementation\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn this.getClass().getSimpleName();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getName();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/AdjustForIfMergeVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.SpecialEdgeAttr;\nimport jadx.core.dex.attributes.nodes.SpecialEdgeAttr.SpecialEdgeType;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.regions.RegionMakerVisitor;\nimport jadx.core.dex.visitors.typeinference.FinishTypeInference;\nimport jadx.core.utils.BlockUtils;\n\n@JadxVisitor(\n\t\tname = \"AdjustForIfMergeVisitor\",\n\t\tdesc = \"Move instructions between if blocks that can't be inlined but are safe to push through the if to allow the ifs to merge\",\n\t\trunBefore = { RegionMakerVisitor.class },\n\t\trunAfter = { FinishTypeInference.class }\n)\npublic class AdjustForIfMergeVisitor extends AbstractVisitor {\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tif (mth.isNoCode() || mth.getBasicBlocks() == null) {\n\t\t\treturn;\n\t\t}\n\t\t// Find candidates for adjustment by selecting blocks between two if statements\n\t\tList<BlockNode> blocks = mth.getBasicBlocks();\n\n\t\tfor (BlockNode blk : blocks) {\n\t\t\tif (areSurroundingsCorrectShape(blk)) {\n\t\t\t\tBlockNode pred = blk.getPredecessors().get(0);\n\t\t\t\tBlockNode succ = blk.getCleanSuccessors().get(0);\n\n\t\t\t\tif (isSimpleIf(pred) && isSimpleIf(succ)) {\n\t\t\t\t\tList<InsnNode> movableInstructions = getMovableInstructions(blk, succ);\n\n\t\t\t\t\tif (!movableInstructions.isEmpty() && couldMerge(mth, pred, blk, succ)) {\n\t\t\t\t\t\tdoMove(mth, blk, succ, movableInstructions);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprivate boolean areSurroundingsCorrectShape(BlockNode blk) {\n\t\treturn (blk.getPredecessors().size() == 1 && blk.getCleanSuccessors().size() == 1);\n\t}\n\n\tprivate boolean isSimpleIf(BlockNode blk) {\n\t\treturn blk.getInstructions().size() == 1 && blk.getInstructions().get(0).getType() == InsnType.IF;\n\t}\n\n\tprivate boolean couldMerge(MethodNode mth, BlockNode pred, BlockNode blk, BlockNode succ) {\n\t\t// we cannot merge if the edge from blk to succ is a back edge\n\t\t// there's a function in BlockUtils that purports to check if something is a back edge but it\n\t\t// doesn't so do it by hand here\n\n\t\tList<SpecialEdgeAttr> specialEdges = mth.getAll(AType.SPECIAL_EDGE);\n\t\tfor (SpecialEdgeAttr edge : specialEdges) {\n\t\t\tif (edge.getStart() == blk && edge.getEnd() == succ && edge.getType() == SpecialEdgeType.BACK_EDGE) {\n\t\t\t\tmth.addDebugComment(\"Refusing to push insns through at block \" + blk.toString() + \" : edge to successor is a back edge.\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate List<InsnNode> getMovableInstructions(BlockNode blk, BlockNode succ) {\n\t\t// A 'movable instruction' is one that does not impact either codegen or the semantics of the\n\t\t// following block, so it can be pushed through into the new synthetics.\n\n\t\t// For now, we just look for nop moves along the same register such that the target variable is not\n\t\t// used in the succ block.\n\n\t\tList<InsnNode> movableInstructions = new ArrayList<>();\n\t\tfor (InsnNode insn : blk.getInstructions()) {\n\t\t\tif (insn.getType() == InsnType.MOVE) {\n\t\t\t\tif (!(insn.getArg(0) instanceof RegisterArg)) {\n\t\t\t\t\t// could be a LiteralArg\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tRegisterArg source = (RegisterArg) insn.getArg(0);\n\t\t\t\tRegisterArg target = insn.getResult();\n\n\t\t\t\tList<RegisterArg> uses = target.getSVar().getUseList();\n\t\t\t\tfor (RegisterArg use : uses) {\n\t\t\t\t\tif (BlockUtils.blockContains(succ, use.getParentInsn())) {\n\t\t\t\t\t\t// the target is used inside the successor, so we can't cleanly do the assignment afterwards\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// we don't want to just push everything through, e.g.\n\t\t\t\t// if (condition) { return; }\n\t\t\t\t// x = 123456\n\t\t\t\t// if (condition) { return; }\n\t\t\t\t// would be a less clean result if the assignment was pushed into the block of the 2nd if.\n\n\t\t\t\tif (source.getRegNum() == target.getRegNum()) {\n\t\t\t\t\tmovableInstructions.add(insn);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn movableInstructions;\n\t}\n\n\tprivate void doMove(MethodNode mth, BlockNode target, BlockNode bottomIf, List<InsnNode> movableInstructions) {\n\t\t// Move instructions from the list out of blk and into new synthetics on each edge out of succ\n\n\t\t// preserving instruction ordering, although it's unlikely that it would ever matter here\n\t\tCollections.reverse(movableInstructions);\n\t\tfor (InsnNode insn : movableInstructions) {\n\t\t\ttarget.getInstructions().remove(insn);\n\t\t\tfor (BlockNode succ : bottomIf.getCleanSuccessors()) {\n\t\t\t\tsucc.getInstructions().add(0, insn); // add at start\n\n\t\t\t\tif (succ.contains(AFlag.LOOP_START)) {\n\t\t\t\t\t// if we're merging into a loop condition, silence the warning when there's more than one\n\t\t\t\t\t// instruction in the loop header\n\t\t\t\t\tsucc.add(AFlag.ALLOW_MULTIPLE_INSNS_LOOP_COND);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/AnonymousClassVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.FieldReplaceAttr;\nimport jadx.core.dex.attributes.nodes.SkipMethodArgsAttr;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"AnonymousClassVisitor\",\n\t\tdesc = \"Prepare anonymous class for inline\",\n\t\trunBefore = {\n\t\t\t\tModVisitor.class,\n\t\t\t\tCodeShrinkVisitor.class\n\t\t}\n)\npublic class AnonymousClassVisitor extends AbstractVisitor {\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) throws JadxException {\n\t\tif (cls.contains(AType.ANONYMOUS_CLASS)) {\n\t\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\t\tif (mth.contains(AFlag.ANONYMOUS_CONSTRUCTOR)) {\n\t\t\t\t\tprocessAnonymousConstructor(mth);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static void processAnonymousConstructor(MethodNode mth) {\n\t\tList<InsnNode> usedInsns = new ArrayList<>();\n\t\tMap<InsnArg, FieldNode> argsMap = getArgsToFieldsMapping(mth, usedInsns);\n\t\tif (argsMap.isEmpty()) {\n\t\t\tmth.add(AFlag.NO_SKIP_ARGS);\n\t\t} else {\n\t\t\tfor (Map.Entry<InsnArg, FieldNode> entry : argsMap.entrySet()) {\n\t\t\t\tFieldNode field = entry.getValue();\n\t\t\t\tif (field == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tInsnArg arg = entry.getKey();\n\t\t\t\tfield.addAttr(new FieldReplaceAttr(arg));\n\t\t\t\tfield.add(AFlag.DONT_GENERATE);\n\t\t\t\tif (arg.isRegister()) {\n\t\t\t\t\targ.add(AFlag.SKIP_ARG);\n\t\t\t\t\tSkipMethodArgsAttr.skipArg(mth, ((RegisterArg) arg));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (InsnNode usedInsn : usedInsns) {\n\t\t\tusedInsn.add(AFlag.DONT_GENERATE);\n\t\t}\n\t}\n\n\tprivate static Map<InsnArg, FieldNode> getArgsToFieldsMapping(MethodNode mth, List<InsnNode> usedInsns) {\n\t\tMethodInfo callMth = mth.getMethodInfo();\n\t\tClassNode cls = mth.getParentClass();\n\t\tList<RegisterArg> argList = mth.getArgRegs();\n\t\tClassNode outerCls = mth.getUseIn().get(0).getParentClass();\n\t\tint startArg = 0;\n\t\tif (callMth.getArgsCount() != 0 && callMth.getArgumentsTypes().get(0).equals(outerCls.getClassInfo().getType())) {\n\t\t\tstartArg = 1;\n\t\t}\n\t\tMap<InsnArg, FieldNode> map = new LinkedHashMap<>();\n\t\tint argsCount = argList.size();\n\t\tfor (int i = startArg; i < argsCount; i++) {\n\t\t\tRegisterArg arg = argList.get(i);\n\t\t\tInsnNode useInsn = getParentInsnSkipMove(arg);\n\t\t\tif (useInsn == null) {\n\t\t\t\treturn Collections.emptyMap();\n\t\t\t}\n\t\t\tswitch (useInsn.getType()) {\n\t\t\t\tcase IPUT:\n\t\t\t\t\tFieldNode fieldNode = cls.searchField((FieldInfo) ((IndexInsnNode) useInsn).getIndex());\n\t\t\t\t\tif (fieldNode == null || !fieldNode.getAccessFlags().isSynthetic()) {\n\t\t\t\t\t\treturn Collections.emptyMap();\n\t\t\t\t\t}\n\t\t\t\t\tmap.put(arg, fieldNode);\n\t\t\t\t\tusedInsns.add(useInsn);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase CONSTRUCTOR:\n\t\t\t\t\tConstructorInsn superConstr = (ConstructorInsn) useInsn;\n\t\t\t\t\tif (!superConstr.isSuper()) {\n\t\t\t\t\t\treturn Collections.emptyMap();\n\t\t\t\t\t}\n\t\t\t\t\tusedInsns.add(useInsn);\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\treturn Collections.emptyMap();\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t}\n\n\tprivate static InsnNode getParentInsnSkipMove(RegisterArg arg) {\n\t\tSSAVar sVar = arg.getSVar();\n\t\tif (sVar.getUseCount() != 1) {\n\t\t\treturn null;\n\t\t}\n\t\tRegisterArg useArg = sVar.getUseList().get(0);\n\t\tInsnNode parentInsn = useArg.getParentInsn();\n\t\tif (parentInsn == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (parentInsn.getType() == InsnType.MOVE) {\n\t\t\treturn getParentInsnSkipMove(parentInsn.getResult());\n\t\t}\n\t\treturn parentInsn;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/ApplyVariableNames.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.Consts;\nimport jadx.core.deobf.NameMapper;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.CodeVar;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.regions.variables.ProcessVariables;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"ApplyVariableNames\",\n\t\tdesc = \"Try to guess variable name from usage\",\n\t\trunAfter = {\n\t\t\t\tProcessVariables.class\n\t\t}\n)\npublic class ApplyVariableNames extends AbstractVisitor {\n\n\tprivate static final Map<String, String> OBJ_ALIAS = Utils.newConstStringMap(\n\t\t\tConsts.CLASS_STRING, \"str\",\n\t\t\tConsts.CLASS_CLASS, \"cls\",\n\t\t\tConsts.CLASS_THROWABLE, \"th\",\n\t\t\tConsts.CLASS_OBJECT, \"obj\",\n\t\t\t\"java.util.Iterator\", \"it\",\n\t\t\t\"java.util.HashMap\", \"map\",\n\t\t\t\"java.lang.Boolean\", \"bool\",\n\t\t\t\"java.lang.Short\", \"sh\",\n\t\t\t\"java.lang.Integer\", \"num\",\n\t\t\t\"java.lang.Character\", \"ch\",\n\t\t\t\"java.lang.Byte\", \"b\",\n\t\t\t\"java.lang.Float\", \"f\",\n\t\t\t\"java.lang.Long\", \"l\",\n\t\t\t\"java.lang.Double\", \"d\",\n\t\t\t\"java.lang.StringBuilder\", \"sb\",\n\t\t\t\"java.lang.Exception\", \"exc\");\n\n\tprivate static final Set<String> GOOD_VAR_NAMES = Set.of(\n\t\t\t\"size\", \"length\", \"list\", \"map\", \"next\");\n\tprivate static final List<String> INVOKE_PREFIXES = List.of(\n\t\t\t\"get\", \"set\", \"to\", \"parse\", \"read\", \"format\");\n\n\tprivate RootNode root;\n\n\t@Override\n\tpublic void init(RootNode root) throws JadxException {\n\t\tthis.root = root;\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tfor (SSAVar ssaVar : mth.getSVars()) {\n\t\t\tCodeVar codeVar = ssaVar.getCodeVar();\n\t\t\tString newName = guessName(codeVar);\n\t\t\tif (newName != null) {\n\t\t\t\tcodeVar.setName(newName);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate @Nullable String guessName(CodeVar var) {\n\t\tif (var.isThis()) {\n\t\t\treturn RegisterArg.THIS_ARG_NAME;\n\t\t}\n\t\tif (!var.isDeclared()) {\n\t\t\t// name is not used in code\n\t\t\treturn null;\n\t\t}\n\t\tif (NameMapper.isValidAndPrintable(var.getName())) {\n\t\t\t// the current name is valid, keep it\n\t\t\treturn null;\n\t\t}\n\t\tList<SSAVar> ssaVars = var.getSsaVars();\n\t\tif (Utils.notEmpty(ssaVars)) {\n\t\t\tboolean mthArg = ssaVars.stream().anyMatch(ssaVar -> ssaVar.getAssign().contains(AFlag.METHOD_ARGUMENT));\n\t\t\tif (mthArg) {\n\t\t\t\t// for method args use defined type and ignore usage\n\t\t\t\treturn makeNameForType(var.getType());\n\t\t\t}\n\t\t\tfor (SSAVar ssaVar : ssaVars) {\n\t\t\t\tString name = makeNameForSSAVar(ssaVar);\n\t\t\t\tif (name != null) {\n\t\t\t\t\treturn name;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn makeNameForType(var.getType());\n\t}\n\n\tprivate @Nullable String makeNameForSSAVar(SSAVar ssaVar) {\n\t\tString ssaVarName = ssaVar.getName();\n\t\tif (ssaVarName != null) {\n\t\t\treturn ssaVarName;\n\t\t}\n\t\tInsnNode assignInsn = ssaVar.getAssignInsn();\n\t\tif (assignInsn != null) {\n\t\t\tString name = makeNameFromInsn(ssaVar, assignInsn);\n\t\t\tif (NameMapper.isValidAndPrintable(name)) {\n\t\t\t\treturn name;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String makeNameFromInsn(SSAVar ssaVar, InsnNode insn) {\n\t\tswitch (insn.getType()) {\n\t\t\tcase INVOKE:\n\t\t\t\treturn makeNameFromInvoke(ssaVar, (InvokeNode) insn);\n\n\t\t\tcase CONSTRUCTOR:\n\t\t\t\tConstructorInsn co = (ConstructorInsn) insn;\n\t\t\t\tMethodNode callMth = root.getMethodUtils().resolveMethod(co);\n\t\t\t\tif (callMth != null && callMth.contains(AFlag.ANONYMOUS_CONSTRUCTOR)) {\n\t\t\t\t\t// don't use name of anonymous class\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\treturn makeNameForClass(co.getClassType());\n\n\t\t\tcase ARRAY_LENGTH:\n\t\t\t\treturn \"length\";\n\n\t\t\tcase ARITH:\n\t\t\tcase TERNARY:\n\t\t\tcase CAST:\n\t\t\t\tfor (InsnArg arg : insn.getArguments()) {\n\t\t\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\t\t\tString wName = makeNameFromInsn(ssaVar, wrapInsn);\n\t\t\t\t\t\tif (wName != null) {\n\t\t\t\t\t\t\treturn wName;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String makeNameForType(ArgType type) {\n\t\tif (type.isPrimitive()) {\n\t\t\treturn type.getPrimitiveType().getShortName().toLowerCase();\n\t\t}\n\t\tif (type.isArray()) {\n\t\t\treturn makeNameForType(type.getArrayRootElement()) + \"Arr\";\n\t\t}\n\t\treturn makeNameForObject(type);\n\t}\n\n\tprivate String makeNameForObject(ArgType type) {\n\t\tif (type.isGenericType()) {\n\t\t\treturn StringUtils.escape(type.getObject().toLowerCase());\n\t\t}\n\t\tif (type.isObject()) {\n\t\t\tString alias = getAliasForObject(type.getObject());\n\t\t\tif (alias != null) {\n\t\t\t\treturn alias;\n\t\t\t}\n\t\t\treturn makeNameForCheckedClass(ClassInfo.fromType(root, type));\n\t\t}\n\t\treturn StringUtils.escape(type.toString());\n\t}\n\n\tprivate String makeNameForCheckedClass(ClassInfo classInfo) {\n\t\tString shortName = classInfo.getAliasShortName();\n\t\tString vName = fromName(shortName);\n\t\tif (vName != null) {\n\t\t\treturn vName;\n\t\t}\n\t\tString lower = StringUtils.escape(shortName.toLowerCase());\n\t\tif (shortName.equals(lower)) {\n\t\t\treturn lower + \"Var\";\n\t\t}\n\t\treturn lower;\n\t}\n\n\tprivate String makeNameForClass(ClassInfo classInfo) {\n\t\tString alias = getAliasForObject(classInfo.getFullName());\n\t\tif (alias != null) {\n\t\t\treturn alias;\n\t\t}\n\t\treturn makeNameForCheckedClass(classInfo);\n\t}\n\n\tprivate static String fromName(String name) {\n\t\tif (name == null || name.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (name.toUpperCase().equals(name)) {\n\t\t\t// all characters are upper case\n\t\t\treturn name.toLowerCase();\n\t\t}\n\t\tString v1 = Character.toLowerCase(name.charAt(0)) + name.substring(1);\n\t\tif (!v1.equals(name)) {\n\t\t\treturn v1;\n\t\t}\n\t\tif (name.length() < 3) {\n\t\t\treturn name + \"Var\";\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static String getAliasForObject(String name) {\n\t\treturn OBJ_ALIAS.get(name);\n\t}\n\n\tprivate String makeNameFromInvoke(SSAVar ssaVar, InvokeNode inv) {\n\t\tMethodInfo callMth = inv.getCallMth();\n\t\tString name = callMth.getAlias();\n\t\tClassInfo declClass = callMth.getDeclClass();\n\t\tif (\"getInstance\".equals(name)) {\n\t\t\t// e.g. Cipher.getInstance\n\t\t\treturn makeNameForClass(declClass);\n\t\t}\n\t\tString shortName = cutPrefix(name);\n\t\tif (shortName != null) {\n\t\t\treturn fromName(shortName);\n\t\t}\n\t\tif (\"iterator\".equals(name)) {\n\t\t\treturn \"it\";\n\t\t}\n\t\tif (\"toString\".equals(name)) {\n\t\t\treturn makeNameForClass(declClass);\n\t\t}\n\t\tif (\"forName\".equals(name) && declClass.getType().equals(ArgType.CLASS)) {\n\t\t\treturn OBJ_ALIAS.get(Consts.CLASS_CLASS);\n\t\t}\n\t\t// use method name as a variable name not the best idea in most cases\n\t\tif (!GOOD_VAR_NAMES.contains(name)) {\n\t\t\tString typeName = makeNameForType(ssaVar.getCodeVar().getType());\n\t\t\tif (!typeName.equalsIgnoreCase(name)) {\n\t\t\t\treturn typeName + StringUtils.capitalizeFirstChar(name);\n\t\t\t}\n\t\t}\n\t\treturn name;\n\t}\n\n\tprivate @Nullable String cutPrefix(String name) {\n\t\tfor (String prefix : INVOKE_PREFIXES) {\n\t\t\tif (name.startsWith(prefix)) {\n\t\t\t\treturn name.substring(prefix.length());\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"ApplyVariableNames\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/AttachCommentsVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.data.CodeRefType;\nimport jadx.api.data.ICodeComment;\nimport jadx.api.data.ICodeData;\nimport jadx.api.data.IJavaCodeRef;\nimport jadx.api.data.IJavaNodeRef;\nimport jadx.core.codegen.utils.CodeComment;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.IAttributeNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.exceptions.JadxException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n@JadxVisitor(\n\t\tname = \"AttachComments\",\n\t\tdesc = \"Attach user code comments\",\n\t\trunBefore = {\n\t\t\t\tProcessInstructionsVisitor.class\n\t\t}\n)\npublic class AttachCommentsVisitor extends AbstractVisitor {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(AttachCommentsVisitor.class);\n\n\tprivate Map<String, List<ICodeComment>> clsCommentsMap;\n\n\t@Override\n\tpublic void init(RootNode root) throws JadxException {\n\t\tupdateCommentsData(root.getArgs().getCodeData());\n\t\troot.registerCodeDataUpdateListener(this::updateCommentsData);\n\t}\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) {\n\t\tList<ICodeComment> clsComments = getCommentsData(cls);\n\t\tif (!clsComments.isEmpty()) {\n\t\t\tapplyComments(cls, clsComments);\n\t\t}\n\t\tcls.getInnerClasses().forEach(this::visit);\n\t\treturn false;\n\t}\n\n\tprivate static void applyComments(ClassNode cls, List<ICodeComment> clsComments) {\n\t\tfor (ICodeComment comment : clsComments) {\n\t\t\tIJavaNodeRef nodeRef = comment.getNodeRef();\n\t\t\tswitch (nodeRef.getType()) {\n\t\t\t\tcase CLASS:\n\t\t\t\t\taddComment(cls, comment);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase FIELD:\n\t\t\t\t\tFieldNode fieldNode = cls.searchFieldByShortId(nodeRef.getShortId());\n\t\t\t\t\tif (fieldNode == null) {\n\t\t\t\t\t\tLOG.warn(\"Field reference not found: {}\", nodeRef);\n\t\t\t\t\t} else {\n\t\t\t\t\t\taddComment(fieldNode, comment);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase METHOD:\n\t\t\t\t\tMethodNode methodNode = cls.searchMethodByShortId(nodeRef.getShortId());\n\t\t\t\t\tif (methodNode == null) {\n\t\t\t\t\t\tLOG.warn(\"Method reference not found: {}\", nodeRef);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tIJavaCodeRef codeRef = comment.getCodeRef();\n\t\t\t\t\t\tif (codeRef == null) {\n\t\t\t\t\t\t\taddComment(methodNode, comment);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tprocessCustomAttach(methodNode, codeRef, comment);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static InsnNode getInsnByOffset(MethodNode mth, int offset) {\n\t\ttry {\n\t\t\treturn mth.getInstructions()[offset];\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Insn reference not found in: {} with offset: {}\", mth, offset);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static void processCustomAttach(MethodNode mth, IJavaCodeRef codeRef, ICodeComment comment) {\n\t\tCodeRefType attachType = codeRef.getAttachType();\n\t\tswitch (attachType) {\n\t\t\tcase INSN: {\n\t\t\t\tInsnNode insn = getInsnByOffset(mth, codeRef.getIndex());\n\t\t\t\taddComment(insn, comment);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unexpected attach type: \" + attachType);\n\t\t}\n\t}\n\n\tprivate static void addComment(@Nullable IAttributeNode node, ICodeComment comment) {\n\t\tif (node == null) {\n\t\t\treturn;\n\t\t}\n\t\tnode.addAttr(AType.CODE_COMMENTS, new CodeComment(comment));\n\t}\n\n\tprivate List<ICodeComment> getCommentsData(ClassNode cls) {\n\t\tif (clsCommentsMap == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<ICodeComment> clsComments = clsCommentsMap.get(cls.getClassInfo().getRawName());\n\t\tif (clsComments == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn clsComments;\n\t}\n\n\tprivate void updateCommentsData(@Nullable ICodeData data) {\n\t\tif (data == null) {\n\t\t\tthis.clsCommentsMap = Collections.emptyMap();\n\t\t} else {\n\t\t\tthis.clsCommentsMap = data.getComments().stream()\n\t\t\t\t\t.collect(Collectors.groupingBy(c -> c.getNodeRef().getDeclaringClass()));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/AttachMethodDetails.java",
    "content": "package jadx.core.dex.visitors;\n\nimport jadx.core.dex.instructions.BaseInvokeNode;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.nodes.utils.MethodUtils;\nimport jadx.core.dex.visitors.blocks.BlockSplitter;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"Attach Method Details\",\n\t\tdesc = \"Attach method details for invoke instructions\",\n\t\trunBefore = {\n\t\t\t\tBlockSplitter.class,\n\t\t\t\tMethodInvokeVisitor.class\n\t\t}\n)\npublic class AttachMethodDetails extends AbstractVisitor {\n\n\tprivate MethodUtils methodUtils;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tmethodUtils = root.getMethodUtils();\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (InsnNode insn : mth.getInstructions()) {\n\t\t\tif (insn instanceof BaseInvokeNode) {\n\t\t\t\tattachMethodDetails((BaseInvokeNode) insn);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void attachMethodDetails(BaseInvokeNode insn) {\n\t\tIMethodDetails methodDetails = methodUtils.getMethodDetails(insn.getCallMth());\n\t\tif (methodDetails != null) {\n\t\t\tinsn.addAttr(methodDetails);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/AttachTryCatchVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.input.data.ICatch;\nimport jadx.api.plugins.input.data.ITry;\nimport jadx.api.plugins.utils.Utils;\nimport jadx.core.Consts;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.trycatch.CatchAttr;\nimport jadx.core.dex.trycatch.ExcHandlerAttr;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.utils.exceptions.JadxException;\n\nimport static jadx.core.dex.visitors.ProcessInstructionsVisitor.getNextInsnOffset;\n\n@JadxVisitor(\n\t\tname = \"Attach Try/Catch Visitor\",\n\t\tdesc = \"Attach try/catch info to instructions\",\n\t\trunBefore = {\n\t\t\t\tProcessInstructionsVisitor.class\n\t\t}\n)\npublic class AttachTryCatchVisitor extends AbstractVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(AttachTryCatchVisitor.class);\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tinitTryCatches(mth, mth.getInstructions(), mth.getCodeReader().getTries());\n\t}\n\n\tprivate static void initTryCatches(MethodNode mth, InsnNode[] insnByOffset, List<ITry> tries) {\n\t\tif (tries.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tif (Consts.DEBUG_EXC_HANDLERS) {\n\t\t\tLOG.debug(\"Raw try blocks in {}\", mth);\n\t\t\ttries.forEach(tryData -> LOG.debug(\" - {}\", tryData));\n\t\t}\n\t\tfor (ITry tryData : tries) {\n\t\t\tList<ExceptionHandler> handlers = convertToHandlers(mth, tryData.getCatch(), insnByOffset);\n\t\t\tif (handlers.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmarkTryBounds(insnByOffset, tryData, CatchAttr.build(handlers));\n\t\t}\n\t}\n\n\tprivate static void markTryBounds(InsnNode[] insnByOffset, ITry aTry, CatchAttr catchAttr) {\n\t\tint offset = aTry.getStartOffset();\n\t\tint end = aTry.getEndOffset();\n\n\t\tboolean tryBlockStarted = false;\n\t\tInsnNode insn = null;\n\t\twhile (offset <= end) {\n\t\t\tInsnNode insnAtOffset = insnByOffset[offset];\n\t\t\tif (insnAtOffset != null) {\n\t\t\t\tinsn = insnAtOffset;\n\t\t\t\tattachCatchAttr(catchAttr, insn);\n\t\t\t\tif (!tryBlockStarted) {\n\t\t\t\t\tinsn.add(AFlag.TRY_ENTER);\n\t\t\t\t\ttryBlockStarted = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\toffset = getNextInsnOffset(insnByOffset, offset);\n\t\t\tif (offset == -1) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (tryBlockStarted) {\n\t\t\tinsn.add(AFlag.TRY_LEAVE);\n\t\t} else {\n\t\t\t// no instructions found in range -> add nop at start offset\n\t\t\tInsnNode nop = insertNOP(insnByOffset, aTry.getStartOffset());\n\t\t\tnop.add(AFlag.TRY_ENTER);\n\t\t\tnop.add(AFlag.TRY_LEAVE);\n\t\t\tnop.addAttr(catchAttr);\n\t\t}\n\t}\n\n\tprivate static void attachCatchAttr(CatchAttr catchAttr, InsnNode insn) {\n\t\tCatchAttr existAttr = insn.get(AType.EXC_CATCH);\n\t\tif (existAttr != null) {\n\t\t\t// merge handlers\n\t\t\tList<ExceptionHandler> handlers = Utils.concat(existAttr.getHandlers(), catchAttr.getHandlers());\n\t\t\tinsn.addAttr(CatchAttr.build(handlers));\n\t\t} else {\n\t\t\tinsn.addAttr(catchAttr);\n\t\t}\n\t}\n\n\tprivate static List<ExceptionHandler> convertToHandlers(MethodNode mth, ICatch catchBlock, InsnNode[] insnByOffset) {\n\t\tint[] handlerOffsetArr = catchBlock.getHandlers();\n\t\tString[] handlerTypes = catchBlock.getTypes();\n\n\t\tint handlersCount = handlerOffsetArr.length;\n\t\tList<ExceptionHandler> list = new ArrayList<>(handlersCount);\n\t\tfor (int i = 0; i < handlersCount; i++) {\n\t\t\tint handlerOffset = handlerOffsetArr[i];\n\t\t\tClassInfo type = ClassInfo.fromName(mth.root(), handlerTypes[i]);\n\t\t\tUtils.addToList(list, createHandler(mth, insnByOffset, handlerOffset, type));\n\t\t}\n\t\tint allHandlerOffset = catchBlock.getCatchAllHandler();\n\t\tif (allHandlerOffset >= 0) {\n\t\t\tUtils.addToList(list, createHandler(mth, insnByOffset, allHandlerOffset, null));\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Nullable\n\tprivate static ExceptionHandler createHandler(MethodNode mth, InsnNode[] insnByOffset, int handlerOffset, @Nullable ClassInfo type) {\n\t\tInsnNode insn = insnByOffset[handlerOffset];\n\t\tif (insn != null) {\n\t\t\tExcHandlerAttr excHandlerAttr = insn.get(AType.EXC_HANDLER);\n\t\t\tif (excHandlerAttr != null) {\n\t\t\t\tExceptionHandler handler = excHandlerAttr.getHandler();\n\t\t\t\tif (handler.addCatchType(mth, type)) {\n\t\t\t\t\t// exist handler updated (assume from same try block) - don't add again\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\t// same handler (can be used in different try blocks)\n\t\t\t\treturn handler;\n\t\t\t}\n\t\t} else {\n\t\t\tinsn = insertNOP(insnByOffset, handlerOffset);\n\t\t}\n\t\tExceptionHandler handler = ExceptionHandler.build(mth, handlerOffset, type);\n\t\tmth.addExceptionHandler(handler);\n\t\tinsn.addAttr(new ExcHandlerAttr(handler));\n\t\treturn handler;\n\t}\n\n\tprivate static InsnNode insertNOP(InsnNode[] insnByOffset, int offset) {\n\t\tInsnNode nop = new InsnNode(InsnType.NOP, 0);\n\t\tnop.setOffset(offset);\n\t\tnop.add(AFlag.SYNTHETIC);\n\t\tinsnByOffset[offset] = nop;\n\t\treturn nop;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/CheckCode.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.exceptions.JadxException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.utils.Utils.isEmpty;\n\n@JadxVisitor(\n\t\tname = \"CheckCode\",\n\t\tdesc = \"Check and remove bad or incorrect code\"\n)\npublic class CheckCode extends AbstractVisitor {\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tMethodInfo mthInfo = mth.getMethodInfo();\n\t\tif (mthInfo.getArgumentsTypes().size() > 255) {\n\t\t\t// java spec don't allow more than 255 args\n\t\t\tif (canRemoveMethod(mth)) {\n\t\t\t\tmth.ignoreMethod();\n\t\t\t} else {\n\t\t\t\t// TODO: convert args to array\n\t\t\t}\n\t\t}\n\t\tcheckInstructions(mth);\n\t}\n\n\tprivate boolean canRemoveMethod(MethodNode mth) {\n\t\tif (mth.getUseIn().isEmpty()) {\n\t\t\treturn true;\n\t\t}\n\t\tInsnNode[] insns = mth.getInstructions();\n\t\tif (insns.length == 0) {\n\t\t\treturn true;\n\t\t}\n\t\tfor (InsnNode insn : insns) {\n\t\t\tif (insn != null && insn.getType() != InsnType.NOP) {\n\t\t\t\tif (insn.getType() == InsnType.RETURN && insn.getArgsCount() == 0) {\n\t\t\t\t\t// ignore void return\n\t\t\t\t} else {\n\t\t\t\t\t// found useful instruction\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic void checkInstructions(MethodNode mth) {\n\t\tif (isEmpty(mth.getInstructions())) {\n\t\t\treturn;\n\t\t}\n\t\tint regsCount = mth.getRegsCount();\n\t\tList<RegisterArg> list = new ArrayList<>();\n\t\tfor (InsnNode insnNode : mth.getInstructions()) {\n\t\t\tif (insnNode == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlist.clear();\n\t\t\tRegisterArg resultArg = insnNode.getResult();\n\t\t\tif (resultArg != null) {\n\t\t\t\tlist.add(resultArg);\n\t\t\t}\n\t\t\tinsnNode.getRegisterArgs(list);\n\t\t\tfor (RegisterArg arg : list) {\n\t\t\t\tint regNum = arg.getRegNum();\n\t\t\t\tif (regNum < 0) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Incorrect negative register number in instruction: \" + insnNode);\n\t\t\t\t}\n\t\t\t\tif (regNum >= regsCount) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Incorrect register number in instruction: \" + insnNode\n\t\t\t\t\t\t\t+ \", expected to be less than \" + regsCount);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.core.Consts;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.FieldReplaceAttr;\nimport jadx.core.dex.attributes.nodes.MethodReplaceAttr;\nimport jadx.core.dex.attributes.nodes.RenameReasonAttr;\nimport jadx.core.dex.attributes.nodes.SkipMethodArgsAttr;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.InvokeType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.fixaccessmodifiers.FixAccessModifiers;\nimport jadx.core.dex.visitors.usage.UsageInfoVisitor;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"ClassModifier\",\n\t\tdesc = \"Remove synthetic classes, methods and fields\",\n\t\trunAfter = {\n\t\t\t\tModVisitor.class,\n\t\t\t\tFixAccessModifiers.class,\n\t\t\t\tProcessAnonymous.class,\n\t\t\t\tExtractFieldInit.class\n\t\t}\n)\npublic class ClassModifier extends AbstractVisitor {\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) throws JadxException {\n\t\tif (cls.contains(AFlag.PACKAGE_INFO)) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (ClassNode inner : cls.getInnerClasses()) {\n\t\t\tvisit(inner);\n\t\t}\n\t\tif (isEmptySyntheticClass(cls)) {\n\t\t\tcls.add(AFlag.DONT_GENERATE);\n\t\t\treturn false;\n\t\t}\n\t\tremoveSyntheticFields(cls);\n\t\tcls.getMethods().forEach(ClassModifier::removeSyntheticMethods);\n\t\tcls.getMethods().forEach(ClassModifier::removeEmptyMethods);\n\t\treturn false;\n\t}\n\n\tprivate static boolean isEmptySyntheticClass(ClassNode cls) {\n\t\treturn cls.getAccessFlags().isSynthetic()\n\t\t\t\t&& cls.getFields().isEmpty()\n\t\t\t\t&& cls.getMethods().isEmpty()\n\t\t\t\t&& cls.getInnerClasses().isEmpty();\n\t}\n\n\t/**\n\t * Remove synthetic fields if type is outer class or class will be inlined (anonymous)\n\t */\n\tprivate static void removeSyntheticFields(ClassNode cls) {\n\t\tboolean inline = cls.isAnonymous();\n\t\tif (inline || cls.getClassInfo().isInner()) {\n\t\t\tfor (FieldNode field : cls.getFields()) {\n\t\t\t\tArgType fldType = field.getType();\n\t\t\t\tif (field.getAccessFlags().isSynthetic() && fldType.isObject() && !fldType.isGenericType()) {\n\t\t\t\t\tClassInfo clsInfo = ClassInfo.fromType(cls.root(), fldType);\n\t\t\t\t\tClassNode fieldsCls = cls.root().resolveClass(clsInfo);\n\t\t\t\t\tClassInfo parentClass = cls.getClassInfo().getParentClass();\n\t\t\t\t\tif (fieldsCls == null) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tboolean isParentInst = Objects.equals(parentClass, fieldsCls.getClassInfo());\n\t\t\t\t\tif (inline || isParentInst) {\n\t\t\t\t\t\tint found = 0;\n\t\t\t\t\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\t\t\t\t\tif (removeFieldUsageFromConstructor(mth, field, fieldsCls)) {\n\t\t\t\t\t\t\t\tfound++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (found != 0) {\n\t\t\t\t\t\t\tif (isParentInst) {\n\t\t\t\t\t\t\t\tfield.addAttr(new FieldReplaceAttr(fieldsCls.getClassInfo()));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfield.add(AFlag.DONT_GENERATE);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean removeFieldUsageFromConstructor(MethodNode mth, FieldNode field, ClassNode fieldsCls) {\n\t\tif (mth.isNoCode() || !mth.getAccessFlags().isConstructor()) {\n\t\t\treturn false;\n\t\t}\n\t\tList<RegisterArg> args = mth.getArgRegs();\n\t\tif (args.isEmpty() || mth.contains(AFlag.SKIP_FIRST_ARG)) {\n\t\t\treturn false;\n\t\t}\n\t\tRegisterArg arg = args.get(0);\n\t\tif (!arg.getType().equals(fieldsCls.getClassInfo().getType())) {\n\t\t\treturn false;\n\t\t}\n\t\tBlockNode block = mth.getEnterBlock().getCleanSuccessors().get(0);\n\t\tList<InsnNode> instructions = block.getInstructions();\n\t\tif (instructions.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnNode insn = instructions.get(0);\n\t\tif (insn.getType() != InsnType.IPUT) {\n\t\t\treturn false;\n\t\t}\n\t\tIndexInsnNode putInsn = (IndexInsnNode) insn;\n\t\tFieldInfo fieldInfo = (FieldInfo) putInsn.getIndex();\n\t\tif (!fieldInfo.equals(field.getFieldInfo()) || !putInsn.getArg(0).equals(arg)) {\n\t\t\treturn false;\n\t\t}\n\t\tmth.skipFirstArgument();\n\t\tInsnRemover.remove(mth, block, insn);\n\t\t// other arg usage -> wrap with IGET insn\n\t\tif (arg.getSVar().getUseCount() != 0) {\n\t\t\tInsnNode iget = new IndexInsnNode(InsnType.IGET, fieldInfo, 1);\n\t\t\tiget.addArg(insn.getArg(1));\n\t\t\tfor (InsnArg insnArg : new ArrayList<>(arg.getSVar().getUseList())) {\n\t\t\t\tinsnArg.wrapInstruction(mth, iget);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static void removeSyntheticMethods(MethodNode mth) {\n\t\tif (mth.isNoCode() || mth.contains(AFlag.DONT_GENERATE)) {\n\t\t\treturn;\n\t\t}\n\t\tAccessInfo af = mth.getAccessFlags();\n\t\tif (!af.isSynthetic()) {\n\t\t\treturn;\n\t\t}\n\t\tClassNode cls = mth.getParentClass();\n\t\tif (removeBridgeMethod(cls, mth)) {\n\t\t\tif (Consts.DEBUG) {\n\t\t\t\tmth.addDebugComment(\"Removed as synthetic bridge method\");\n\t\t\t} else {\n\t\t\t\tmth.add(AFlag.DONT_GENERATE);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// remove synthetic constructor for inner classes\n\t\tif (mth.isConstructor()\n\t\t\t\t&& (mth.contains(AFlag.METHOD_CANDIDATE_FOR_INLINE) || mth.contains(AFlag.ANONYMOUS_CONSTRUCTOR))) {\n\t\t\tInsnNode insn = BlockUtils.getOnlyOneInsnFromMth(mth);\n\t\t\tif (insn != null) {\n\t\t\t\tList<RegisterArg> args = mth.getArgRegs();\n\t\t\t\tif (isRemovedClassInArgs(cls, args)) {\n\t\t\t\t\tmodifySyntheticMethod(cls, mth, insn, args);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean isRemovedClassInArgs(ClassNode cls, List<RegisterArg> mthArgs) {\n\t\tboolean removedFound = false;\n\t\tfor (RegisterArg arg : mthArgs) {\n\t\t\tArgType argType = arg.getType();\n\t\t\tif (!argType.isObject()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tboolean remove = false;\n\t\t\tClassNode argCls = cls.root().resolveClass(argType);\n\t\t\tif (argCls == null) {\n\t\t\t\t// check if missing class from current top class\n\t\t\t\tClassInfo argClsInfo = ClassInfo.fromType(cls.root(), argType);\n\t\t\t\tif (argClsInfo.getParentClass() != null\n\t\t\t\t\t\t&& cls.getFullName().startsWith(argClsInfo.getParentClass().getFullName())) {\n\t\t\t\t\tremove = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (argCls.contains(AFlag.DONT_GENERATE) || isEmptySyntheticClass(argCls)) {\n\t\t\t\t\tremove = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (remove) {\n\t\t\t\targ.add(AFlag.REMOVE);\n\t\t\t\tremovedFound = true;\n\t\t\t}\n\t\t}\n\t\treturn removedFound;\n\t}\n\n\t/**\n\t * Remove synthetic constructor and redirect calls to existing constructor\n\t */\n\tprivate static void modifySyntheticMethod(ClassNode cls, MethodNode mth, InsnNode insn, List<RegisterArg> args) {\n\t\tif (insn.getType() == InsnType.CONSTRUCTOR) {\n\t\t\tConstructorInsn constr = (ConstructorInsn) insn;\n\t\t\tif (constr.isThis() && !args.isEmpty()) {\n\t\t\t\t// remove first arg for non-static class (references to outer class)\n\t\t\t\tRegisterArg firstArg = args.get(0);\n\t\t\t\tif (firstArg.getType().equals(cls.getParentClass().getClassInfo().getType())) {\n\t\t\t\t\tSkipMethodArgsAttr.skipArg(mth, 0);\n\t\t\t\t}\n\t\t\t\t// remove unused args\n\t\t\t\tint argsCount = args.size();\n\t\t\t\tfor (int i = 0; i < argsCount; i++) {\n\t\t\t\t\tRegisterArg arg = args.get(i);\n\t\t\t\t\tSSAVar sVar = arg.getSVar();\n\t\t\t\t\tif (sVar != null && sVar.getUseCount() == 0) {\n\t\t\t\t\t\tSkipMethodArgsAttr.skipArg(mth, i);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tMethodInfo callMth = constr.getCallMth();\n\t\t\t\tMethodNode callMthNode = cls.root().resolveMethod(callMth);\n\t\t\t\tif (callMthNode != null) {\n\t\t\t\t\tmth.addAttr(new MethodReplaceAttr(callMthNode));\n\t\t\t\t\tmth.add(AFlag.DONT_GENERATE);\n\t\t\t\t\t// code generation order should be already fixed for marked methods\n\t\t\t\t\tUsageInfoVisitor.replaceMethodUsage(callMthNode, mth);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean removeBridgeMethod(ClassNode cls, MethodNode mth) {\n\t\tif (cls.root().getArgs().isInlineMethods()) { // simple wrapper remove is same as inline\n\t\t\tList<InsnNode> allInsns = BlockUtils.collectAllInsns(mth.getBasicBlocks());\n\t\t\tif (allInsns.size() == 1) {\n\t\t\t\tInsnNode wrappedInsn = allInsns.get(0);\n\t\t\t\tif (wrappedInsn.getType() == InsnType.RETURN) {\n\t\t\t\t\tInsnArg arg = wrappedInsn.getArg(0);\n\t\t\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\t\t\twrappedInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn checkSyntheticWrapper(mth, wrappedInsn);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean checkSyntheticWrapper(MethodNode mth, InsnNode insn) {\n\t\tInsnType insnType = insn.getType();\n\t\tif (insnType != InsnType.INVOKE) {\n\t\t\treturn false;\n\t\t}\n\t\tInvokeNode invokeInsn = (InvokeNode) insn;\n\t\tif (invokeInsn.getInvokeType() == InvokeType.SUPER) {\n\t\t\treturn false;\n\t\t}\n\t\tMethodInfo callMth = invokeInsn.getCallMth();\n\t\tMethodNode wrappedMth = mth.root().resolveMethod(callMth);\n\t\tif (wrappedMth == null) {\n\t\t\treturn false;\n\t\t}\n\t\tAccessInfo wrappedAccFlags = wrappedMth.getAccessFlags();\n\t\tif (wrappedAccFlags.isStatic()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (callMth.getArgsCount() != mth.getMethodInfo().getArgsCount()) {\n\t\t\treturn false;\n\t\t}\n\t\t// rename method only from current class\n\t\tif (!mth.getParentClass().equals(wrappedMth.getParentClass())) {\n\t\t\treturn false;\n\t\t}\n\t\t// all args must be registers passed from method args (allow only casts insns)\n\t\tfor (InsnArg arg : insn.getArguments()) {\n\t\t\tif (!registersAndCastsOnly(arg)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\t// remove confirmed, change visibility and name if needed\n\t\tif (!wrappedAccFlags.isPublic() && !mth.root().getArgs().isRespectBytecodeAccModifiers()) {\n\t\t\t// must be public\n\t\t\tFixAccessModifiers.changeVisibility(wrappedMth, AccessFlags.PUBLIC);\n\t\t}\n\t\tString alias = mth.getAlias();\n\t\tif (!Objects.equals(wrappedMth.getAlias(), alias)) {\n\t\t\twrappedMth.rename(alias);\n\t\t\tRenameReasonAttr.forNode(wrappedMth).append(\"merged with bridge method [inline-methods]\");\n\t\t}\n\t\twrappedMth.addAttr(new MethodReplaceAttr(mth));\n\t\twrappedMth.copyAttributeFrom(mth, AType.METHOD_OVERRIDE);\n\t\twrappedMth.addDebugComment(\"Method merged with bridge method: \" + mth.getMethodInfo().getShortId());\n\t\treturn true;\n\t}\n\n\tprivate static boolean registersAndCastsOnly(InsnArg arg) {\n\t\tif (arg.isRegister()) {\n\t\t\treturn true;\n\t\t}\n\t\tif (arg.isInsnWrap()) {\n\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\tif (wrapInsn.getType() == InsnType.CHECK_CAST) {\n\t\t\t\treturn registersAndCastsOnly(wrapInsn.getArg(0));\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Remove public empty constructors (static or default)\n\t */\n\tprivate static void removeEmptyMethods(MethodNode mth) {\n\t\tif (!mth.getArgRegs().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tAccessInfo af = mth.getAccessFlags();\n\t\tboolean publicConstructor = mth.isConstructor() && af.isPublic();\n\t\tboolean enumDefConstructor = mth.isConstructor() && mth.getParentClass().contains(AFlag.CONVERTED_ENUM);\n\t\tboolean clsInit = mth.getMethodInfo().isClassInit() && af.isStatic();\n\t\tif (publicConstructor || enumDefConstructor || clsInit) {\n\t\t\tif (!BlockUtils.isAllBlocksEmpty(mth.getBasicBlocks())) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (clsInit) {\n\t\t\t\tmth.add(AFlag.DONT_GENERATE);\n\t\t\t} else {\n\t\t\t\t// don't remove default constructor if other constructors exists or constructor has annotations\n\t\t\t\tif (mth.isDefaultConstructor()\n\t\t\t\t\t\t&& !isNonDefaultConstructorExists(mth)\n\t\t\t\t\t\t&& !mth.contains(JadxAttrType.ANNOTATION_LIST)) {\n\t\t\t\t\tmth.add(AFlag.DONT_GENERATE);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean isNonDefaultConstructorExists(MethodNode defCtor) {\n\t\tClassNode parentClass = defCtor.getParentClass();\n\t\tfor (MethodNode mth : parentClass.getMethods()) {\n\t\t\tif (mth != defCtor\n\t\t\t\t\t&& mth.isConstructor()\n\t\t\t\t\t&& !mth.isDefaultConstructor()) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlineVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.BaseInvokeNode;\nimport jadx.core.dex.instructions.ConstStringNode;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.PrimitiveType;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IFieldInfoRef;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.finaly.MarkFinallyVisitor;\nimport jadx.core.dex.visitors.ssa.SSATransform;\nimport jadx.core.dex.visitors.typeinference.TypeInferenceVisitor;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.exceptions.JadxException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n@JadxVisitor(\n\t\tname = \"Constants Inline\",\n\t\tdesc = \"Inline constant registers into instructions\",\n\t\trunAfter = {\n\t\t\t\tSSATransform.class,\n\t\t\t\tMarkFinallyVisitor.class\n\t\t},\n\t\trunBefore = TypeInferenceVisitor.class\n)\npublic class ConstInlineVisitor extends AbstractVisitor {\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tprocess(mth);\n\t}\n\n\tpublic static void process(MethodNode mth) {\n\t\tList<InsnNode> toRemove = new ArrayList<>();\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\ttoRemove.clear();\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tcheckInsn(mth, insn, toRemove);\n\t\t\t}\n\t\t\tInsnRemover.removeAllAndUnbind(mth, block, toRemove);\n\t\t}\n\t}\n\n\tprivate static void checkInsn(MethodNode mth, InsnNode insn, List<InsnNode> toRemove) {\n\t\tif (insn.contains(AFlag.DONT_INLINE)\n\t\t\t\t|| insn.contains(AFlag.DONT_GENERATE)\n\t\t\t\t|| insn.getResult() == null) {\n\t\t\treturn;\n\t\t}\n\t\tSSAVar sVar = insn.getResult().getSVar();\n\t\tInsnArg constArg;\n\t\tRunnable onSuccess = null;\n\t\tswitch (insn.getType()) {\n\t\t\tcase CONST:\n\t\t\tcase MOVE: {\n\t\t\t\tconstArg = insn.getArg(0);\n\t\t\t\tif (!constArg.isLiteral()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (constArg.isZeroLiteral() && forbidNullInlines(sVar)) {\n\t\t\t\t\t// all usages forbids inlining\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase CONST_STR: {\n\t\t\t\tString s = ((ConstStringNode) insn).getString();\n\t\t\t\tIFieldInfoRef f = mth.getParentClass().getConstField(s);\n\t\t\t\tif (f == null) {\n\t\t\t\t\tInsnNode copy = insn.copyWithoutResult();\n\t\t\t\t\tconstArg = InsnArg.wrapArg(copy);\n\t\t\t\t} else {\n\t\t\t\t\tInsnNode constGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);\n\t\t\t\t\tconstArg = InsnArg.wrapArg(constGet);\n\t\t\t\t\tconstArg.setType(ArgType.STRING);\n\t\t\t\t\tonSuccess = () -> ModVisitor.addFieldUsage(f, mth);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase CONST_CLASS: {\n\t\t\t\tif (sVar.isUsedInPhi()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconstArg = InsnArg.wrapArg(insn.copyWithoutResult());\n\t\t\t\tconstArg.setType(ArgType.CLASS);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn;\n\t\t}\n\n\t\t// all check passed, run replace\n\t\tif (replaceConst(mth, insn, constArg)) {\n\t\t\ttoRemove.add(insn);\n\t\t\tif (onSuccess != null) {\n\t\t\t\tonSuccess.run();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Don't inline null object\n\t */\n\tprivate static boolean forbidNullInlines(SSAVar sVar) {\n\t\tList<RegisterArg> useList = sVar.getUseList();\n\t\tif (useList.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tint k = 0;\n\t\tfor (RegisterArg useArg : useList) {\n\t\t\tInsnNode insn = useArg.getParentInsn();\n\t\t\tif (insn != null && forbidNullArgInline(insn, useArg)) {\n\t\t\t\tk++;\n\t\t\t}\n\t\t}\n\t\treturn k == useList.size();\n\t}\n\n\tprivate static boolean forbidNullArgInline(InsnNode insn, RegisterArg useArg) {\n\t\tif (insn.getType() == InsnType.MOVE) {\n\t\t\t// result is null, chain checks\n\t\t\treturn forbidNullInlines(insn.getResult().getSVar());\n\t\t}\n\t\tif (!canUseNull(insn, useArg)) {\n\t\t\tuseArg.add(AFlag.DONT_INLINE_CONST);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean canUseNull(InsnNode insn, RegisterArg useArg) {\n\t\tswitch (insn.getType()) {\n\t\t\tcase INVOKE:\n\t\t\t\treturn ((InvokeNode) insn).getInstanceArg() != useArg;\n\n\t\t\tcase ARRAY_LENGTH:\n\t\t\tcase AGET:\n\t\t\tcase APUT:\n\t\t\tcase IGET:\n\t\t\tcase SWITCH:\n\t\t\tcase MONITOR_ENTER:\n\t\t\tcase MONITOR_EXIT:\n\t\t\tcase INSTANCE_OF:\n\t\t\t\treturn insn.getArg(0) != useArg;\n\n\t\t\tcase IPUT:\n\t\t\t\treturn insn.getArg(1) != useArg;\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean replaceConst(MethodNode mth, InsnNode constInsn, InsnArg constArg) {\n\t\tSSAVar ssaVar = constInsn.getResult().getSVar();\n\t\tif (ssaVar.getUseCount() == 0) {\n\t\t\treturn true;\n\t\t}\n\t\tList<RegisterArg> useList = new ArrayList<>(ssaVar.getUseList());\n\t\tint replaceCount = 0;\n\t\tfor (RegisterArg arg : useList) {\n\t\t\tif (canInline(mth, arg) && replaceArg(mth, arg, constArg, constInsn)) {\n\t\t\t\treplaceCount++;\n\t\t\t}\n\t\t}\n\t\tif (replaceCount == useList.size()) {\n\t\t\treturn true;\n\t\t}\n\t\t// hide insn if used only in not generated insns\n\t\tif (ssaVar.getUseList().stream().allMatch(ConstInlineVisitor::canIgnoreInsn)) {\n\t\t\tconstInsn.add(AFlag.DONT_GENERATE);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean canIgnoreInsn(RegisterArg reg) {\n\t\tInsnNode parentInsn = reg.getParentInsn();\n\t\tif (parentInsn == null || parentInsn.getType() == InsnType.PHI) {\n\t\t\treturn false;\n\t\t}\n\t\tif (reg.isLinkedToOtherSsaVars()) {\n\t\t\treturn false;\n\t\t}\n\t\treturn parentInsn.contains(AFlag.DONT_GENERATE);\n\t}\n\n\t@SuppressWarnings(\"RedundantIfStatement\")\n\tprivate static boolean canInline(MethodNode mth, RegisterArg arg) {\n\t\tif (arg.contains(AFlag.DONT_INLINE_CONST) || arg.contains(AFlag.DONT_INLINE)) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnNode parentInsn = arg.getParentInsn();\n\t\tif (parentInsn == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (parentInsn.contains(AFlag.DONT_GENERATE)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (arg.isLinkedToOtherSsaVars() && !arg.getSVar().isUsedInPhi()) {\n\t\t\t// don't inline vars used in finally block\n\t\t\treturn false;\n\t\t}\n\t\tif (parentInsn.getType() == InsnType.CONSTRUCTOR) {\n\t\t\t// don't inline into anonymous call if it can be inlined later\n\t\t\tMethodNode ctrMth = mth.root().getMethodUtils().resolveMethod((ConstructorInsn) parentInsn);\n\t\t\tif (ctrMth != null\n\t\t\t\t\t&& (ctrMth.contains(AFlag.METHOD_CANDIDATE_FOR_INLINE) || ctrMth.contains(AFlag.ANONYMOUS_CONSTRUCTOR))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean replaceArg(MethodNode mth, RegisterArg arg, InsnArg constArg, InsnNode constInsn) {\n\t\tInsnNode useInsn = arg.getParentInsn();\n\t\tif (useInsn == null) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnType insnType = useInsn.getType();\n\t\tif (insnType == InsnType.PHI) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (constArg.isLiteral()) {\n\t\t\tlong literal = ((LiteralArg) constArg).getLiteral();\n\t\t\tArgType argType = arg.getType();\n\t\t\tif (argType == ArgType.UNKNOWN) {\n\t\t\t\targType = arg.getInitType();\n\t\t\t}\n\t\t\tif (argType.isObject() && literal != 0) {\n\t\t\t\targType = ArgType.NARROW_NUMBERS;\n\t\t\t}\n\t\t\tLiteralArg litArg = InsnArg.lit(literal, argType);\n\t\t\tlitArg.copyAttributesFrom(constArg);\n\t\t\tif (!useInsn.replaceArg(arg, litArg)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// arg replaced, made some optimizations\n\t\t\tIFieldInfoRef fieldNode = null;\n\t\t\tArgType litArgType = litArg.getType();\n\t\t\tif (litArgType.isTypeKnown()) {\n\t\t\t\tfieldNode = mth.getParentClass().getConstFieldByLiteralArg(litArg);\n\t\t\t} else if (litArgType.contains(PrimitiveType.INT)) {\n\t\t\t\tfieldNode = mth.getParentClass().getConstField((int) literal, false);\n\t\t\t}\n\t\t\tif (fieldNode != null) {\n\t\t\t\tIndexInsnNode sgetInsn = new IndexInsnNode(InsnType.SGET, fieldNode.getFieldInfo(), 0);\n\t\t\t\tif (litArg.wrapInstruction(mth, sgetInsn) != null) {\n\t\t\t\t\tModVisitor.addFieldUsage(fieldNode, mth);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\taddExplicitCast(useInsn, litArg);\n\t\t\t}\n\t\t} else {\n\t\t\tif (!useInsn.replaceArg(arg, constArg.duplicate())) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tuseInsn.inheritMetadata(constInsn);\n\t\treturn true;\n\t}\n\n\tprivate static void addExplicitCast(InsnNode insn, LiteralArg arg) {\n\t\tif (insn instanceof BaseInvokeNode) {\n\t\t\tBaseInvokeNode callInsn = (BaseInvokeNode) insn;\n\t\t\tMethodInfo callMth = callInsn.getCallMth();\n\t\t\tif (callInsn.getInstanceArg() == arg) {\n\t\t\t\t// instance arg is null, force cast\n\t\t\t\tif (!arg.isZeroLiteral()) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Unexpected instance arg in invoke\");\n\t\t\t\t}\n\t\t\t\tArgType castType = callMth.getDeclClass().getType();\n\t\t\t\tInsnNode castInsn = new IndexInsnNode(InsnType.CAST, castType, 1);\n\t\t\t\tcastInsn.addArg(arg);\n\t\t\t\tcastInsn.add(AFlag.EXPLICIT_CAST);\n\t\t\t\tInsnArg wrapCast = InsnArg.wrapArg(castInsn);\n\t\t\t\twrapCast.setType(castType);\n\t\t\t\tinsn.replaceArg(arg, wrapCast);\n\t\t\t} else {\n\t\t\t\tint offset = callInsn.getFirstArgOffset();\n\t\t\t\tint argIndex = insn.getArgIndex(arg);\n\t\t\t\tArgType argType = callMth.getArgumentsTypes().get(argIndex - offset);\n\t\t\t\tif (argType.isPrimitive()) {\n\t\t\t\t\targ.setType(argType);\n\t\t\t\t\tif (argType.equals(ArgType.BYTE)) {\n\t\t\t\t\t\targ.add(AFlag.EXPLICIT_PRIMITIVE_TYPE);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.codegen.TypeGen;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.PhiInsn;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.ssa.SSATransform;\nimport jadx.core.dex.visitors.typeinference.TypeInferenceVisitor;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n@JadxVisitor(\n\t\tname = \"ConstructorVisitor\",\n\t\tdesc = \"Replace invoke with constructor call\",\n\t\trunAfter = { SSATransform.class, MoveInlineVisitor.class },\n\t\trunBefore = TypeInferenceVisitor.class\n)\npublic class ConstructorVisitor extends AbstractVisitor {\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tif (replaceInvoke(mth)) {\n\t\t\tMoveInlineVisitor.moveInline(mth);\n\t\t}\n\t}\n\n\tprivate static boolean replaceInvoke(MethodNode mth) {\n\t\tboolean replaced = false;\n\t\tInsnRemover remover = new InsnRemover(mth);\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tremover.setBlock(block);\n\t\t\tint size = block.getInstructions().size();\n\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\tInsnNode insn = block.getInstructions().get(i);\n\t\t\t\tif (insn.getType() == InsnType.INVOKE) {\n\t\t\t\t\treplaced |= processInvoke(mth, block, i, remover);\n\t\t\t\t}\n\t\t\t}\n\t\t\tremover.perform();\n\t\t}\n\t\treturn replaced;\n\t}\n\n\tprivate static boolean processInvoke(MethodNode mth, BlockNode block, int indexInBlock, InsnRemover remover) {\n\t\tInvokeNode inv = (InvokeNode) block.getInstructions().get(indexInBlock);\n\t\tMethodInfo callMth = inv.getCallMth();\n\t\tif (!callMth.isConstructor()) {\n\t\t\treturn false;\n\t\t}\n\t\tArgType instType = searchInstanceType(inv);\n\t\tif (instType != null && !instType.equals(callMth.getDeclClass().getType())) {\n\t\t\tClassInfo instCls = ClassInfo.fromType(mth.root(), instType);\n\t\t\tcallMth = MethodInfo.fromDetails(mth.root(), instCls, callMth.getName(),\n\t\t\t\t\tcallMth.getArgumentsTypes(), callMth.getReturnType());\n\t\t}\n\t\tConstructorInsn co = new ConstructorInsn(mth, inv, callMth);\n\t\tif (canRemoveConstructor(mth, co)) {\n\t\t\tremover.addAndUnbind(inv);\n\t\t\treturn false;\n\t\t}\n\t\tco.inheritMetadata(inv);\n\n\t\tRegisterArg instanceArg = (RegisterArg) inv.getArg(0);\n\t\tinstanceArg.getSVar().removeUse(instanceArg);\n\t\tif (co.isNewInstance()) {\n\t\t\tInsnNode assignInsn = instanceArg.getAssignInsn();\n\t\t\tif (assignInsn != null) {\n\t\t\t\tif (assignInsn.getType() == InsnType.CONSTRUCTOR) {\n\t\t\t\t\t// arg already used in another constructor instruction\n\t\t\t\t\t// insert new PHI insn to merge these branched constructors results\n\t\t\t\t\tinstanceArg = insertPhiInsn(mth, block, instanceArg, ((ConstructorInsn) assignInsn));\n\t\t\t\t} else {\n\t\t\t\t\tInsnNode newInstInsn = removeAssignChain(mth, assignInsn, remover, InsnType.NEW_INSTANCE);\n\t\t\t\t\tif (newInstInsn != null) {\n\t\t\t\t\t\tco.inheritMetadata(newInstInsn);\n\t\t\t\t\t\tnewInstInsn.add(AFlag.REMOVE);\n\t\t\t\t\t\tremover.addWithoutUnbind(newInstInsn);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// convert instance arg from 'use' to 'assign'\n\t\t\tco.setResult(instanceArg.duplicate());\n\t\t}\n\t\tco.rebindArgs();\n\t\tConstructorInsn replace = processConstructor(mth, co);\n\t\tif (replace != null) {\n\t\t\tremover.addAndUnbind(co);\n\t\t\tBlockUtils.replaceInsn(mth, block, indexInBlock, replace);\n\t\t} else {\n\t\t\tBlockUtils.replaceInsn(mth, block, indexInBlock, co);\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static @Nullable ArgType searchInstanceType(InvokeNode inv) {\n\t\tInsnArg instanceArg = inv.getInstanceArg();\n\t\tif (instanceArg == null || !instanceArg.isRegister()) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnNode assignInsn = ((RegisterArg) instanceArg).getSVar().getAssignInsn();\n\t\tif (assignInsn == null || assignInsn.getType() != InsnType.NEW_INSTANCE) {\n\t\t\treturn null;\n\t\t}\n\t\treturn ((IndexInsnNode) assignInsn).getIndexAsType();\n\t}\n\n\tprivate static RegisterArg insertPhiInsn(MethodNode mth, BlockNode curBlock,\n\t\t\tRegisterArg instArg, ConstructorInsn otherCtr) {\n\t\tBlockNode otherBlock = BlockUtils.getBlockByInsn(mth, otherCtr);\n\t\tif (otherBlock == null) {\n\t\t\tthrow new JadxRuntimeException(\"Block not found by insn: \" + otherCtr);\n\t\t}\n\t\tBlockNode crossBlock = BlockUtils.getPathCross(mth, curBlock, otherBlock);\n\t\tif (crossBlock == null) {\n\t\t\t// no path cross => PHI insn not needed\n\t\t\t// use new SSA var on usage from current path\n\t\t\tRegisterArg newResArg = instArg.duplicateWithNewSSAVar(mth);\n\t\t\tList<BlockNode> pathBlocks = BlockUtils.collectAllSuccessors(mth, curBlock, true);\n\t\t\tfor (RegisterArg useReg : instArg.getSVar().getUseList()) {\n\t\t\t\tInsnNode parentInsn = useReg.getParentInsn();\n\t\t\t\tif (parentInsn != null) {\n\t\t\t\t\tBlockNode useBlock = BlockUtils.getBlockByInsn(mth, parentInsn, pathBlocks);\n\t\t\t\t\tif (useBlock != null) {\n\t\t\t\t\t\tparentInsn.replaceArg(useReg, newResArg.duplicate());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn newResArg;\n\t\t}\n\t\tRegisterArg newResArg = instArg.duplicateWithNewSSAVar(mth);\n\t\tRegisterArg useArg = otherCtr.getResult();\n\t\tRegisterArg otherResArg = useArg.duplicateWithNewSSAVar(mth);\n\n\t\tPhiInsn phiInsn = SSATransform.addPhi(mth, crossBlock, useArg.getRegNum());\n\t\tphiInsn.setResult(useArg.duplicate());\n\t\tphiInsn.bindArg(newResArg.duplicate(), BlockUtils.getPrevBlockOnPath(mth, crossBlock, curBlock));\n\t\tphiInsn.bindArg(otherResArg.duplicate(), BlockUtils.getPrevBlockOnPath(mth, crossBlock, otherBlock));\n\t\tphiInsn.rebindArgs();\n\n\t\totherCtr.setResult(otherResArg.duplicate());\n\t\totherCtr.rebindArgs();\n\t\treturn newResArg;\n\t}\n\n\tprivate static boolean canRemoveConstructor(MethodNode mth, ConstructorInsn co) {\n\t\tClassNode parentClass = mth.getParentClass();\n\t\tif (co.isSuper() && co.getArgsCount() == 0) {\n\t\t\treturn true;\n\t\t}\n\t\tif (co.isThis() && co.getArgsCount() == 0) {\n\t\t\tMethodNode defCo = parentClass.searchMethodByShortId(co.getCallMth().getShortId());\n\t\t\tif (defCo == null || defCo.isNoCode()) {\n\t\t\t\t// default constructor not implemented\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\t// remove super() call in instance initializer\n\t\treturn parentClass.isAnonymous() && mth.isDefaultConstructor() && co.isSuper();\n\t}\n\n\t/**\n\t * Replace call of synthetic constructor with all 'null' args\n\t * to a non-synthetic or default constructor if possible.\n\t *\n\t * @return insn for replacement or null if replace not needed or not possible.\n\t */\n\t@Nullable\n\tprivate static ConstructorInsn processConstructor(MethodNode mth, ConstructorInsn co) {\n\t\tMethodNode callMth = mth.root().resolveMethod(co.getCallMth());\n\t\tif (callMth == null\n\t\t\t\t|| !callMth.getAccessFlags().isSynthetic()\n\t\t\t\t|| !allArgsNull(co)) {\n\t\t\treturn null;\n\t\t}\n\t\tClassNode classNode = mth.root().resolveClass(callMth.getParentClass().getClassInfo());\n\t\tif (classNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tRegisterArg instanceArg = co.getResult();\n\t\tif (instanceArg == null) {\n\t\t\treturn null;\n\t\t}\n\t\tboolean passThis = instanceArg.isThis();\n\t\tString ctrId = \"<init>(\" + (passThis ? TypeGen.signature(instanceArg.getInitType()) : \"\") + \")V\";\n\t\tMethodNode defCtr = classNode.searchMethodByShortId(ctrId);\n\t\tif (defCtr == null || defCtr.equals(callMth) || defCtr.getAccessFlags().isSynthetic()) {\n\t\t\treturn null;\n\t\t}\n\t\tConstructorInsn newInsn = new ConstructorInsn(defCtr.getMethodInfo(), co.getCallType());\n\t\tnewInsn.setResult(co.getResult().duplicate());\n\t\tnewInsn.inheritMetadata(co);\n\t\treturn newInsn;\n\t}\n\n\tprivate static boolean allArgsNull(ConstructorInsn insn) {\n\t\tfor (InsnArg insnArg : insn.getArguments()) {\n\t\t\tif (insnArg.isLiteral()) {\n\t\t\t\tLiteralArg lit = (LiteralArg) insnArg;\n\t\t\t\tif (lit.getLiteral() != 0) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Remove instructions on 'move' chain until instruction with type 'insnType'\n\t */\n\tprivate static InsnNode removeAssignChain(MethodNode mth, InsnNode insn, InsnRemover remover, InsnType insnType) {\n\t\tif (insn == null) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnType type = insn.getType();\n\t\tif (type == insnType) {\n\t\t\treturn insn;\n\t\t}\n\t\tif (insn.isAttrStorageEmpty()) {\n\t\t\tremover.addWithoutUnbind(insn);\n\t\t} else {\n\t\t\tBlockUtils.replaceInsn(mth, insn, new InsnNode(InsnType.NOP, 0));\n\t\t}\n\t\tif (type == InsnType.MOVE) {\n\t\t\tRegisterArg arg = (RegisterArg) insn.getArg(0);\n\t\t\treturn removeAssignChain(mth, arg.getAssignInsn(), remover, insnType);\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/DeboxingVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.InvokeType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.regions.variables.ProcessVariables;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.exceptions.JadxException;\n\n/**\n * Remove primitives boxing\n * i.e convert 'Integer.valueOf(1)' to '1'\n */\n@JadxVisitor(\n\t\tname = \"DeboxingVisitor\",\n\t\tdesc = \"Remove primitives boxing\",\n\t\trunBefore = {\n\t\t\t\tCodeShrinkVisitor.class,\n\t\t\t\tProcessVariables.class\n\t\t}\n)\npublic class DeboxingVisitor extends AbstractVisitor {\n\n\tprivate Set<MethodInfo> valueOfMths;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tvalueOfMths = new HashSet<>();\n\t\tvalueOfMths.add(valueOfMth(root, ArgType.INT, \"java.lang.Integer\"));\n\t\tvalueOfMths.add(valueOfMth(root, ArgType.BOOLEAN, \"java.lang.Boolean\"));\n\t\tvalueOfMths.add(valueOfMth(root, ArgType.BYTE, \"java.lang.Byte\"));\n\t\tvalueOfMths.add(valueOfMth(root, ArgType.SHORT, \"java.lang.Short\"));\n\t\tvalueOfMths.add(valueOfMth(root, ArgType.CHAR, \"java.lang.Character\"));\n\t\tvalueOfMths.add(valueOfMth(root, ArgType.LONG, \"java.lang.Long\"));\n\t}\n\n\tprivate static MethodInfo valueOfMth(RootNode root, ArgType argType, String clsName) {\n\t\tArgType boxType = ArgType.object(clsName);\n\t\tClassInfo boxCls = ClassInfo.fromType(root, boxType);\n\t\treturn MethodInfo.fromDetails(root, boxCls, \"valueOf\", Collections.singletonList(argType), boxType);\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tboolean replaced = false;\n\t\tfor (BlockNode blockNode : mth.getBasicBlocks()) {\n\t\t\tList<InsnNode> insnList = blockNode.getInstructions();\n\t\t\tint count = insnList.size();\n\t\t\tfor (int i = 0; i < count; i++) {\n\t\t\t\tInsnNode insnNode = insnList.get(i);\n\t\t\t\tif (insnNode.getType() == InsnType.INVOKE) {\n\t\t\t\t\tInsnNode replaceInsn = checkForReplace(((InvokeNode) insnNode));\n\t\t\t\t\tif (replaceInsn != null) {\n\t\t\t\t\t\tBlockUtils.replaceInsn(mth, blockNode, i, replaceInsn);\n\t\t\t\t\t\treplaced = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (replaced) {\n\t\t\tConstInlineVisitor.process(mth);\n\t\t}\n\t}\n\n\tprivate InsnNode checkForReplace(InvokeNode insnNode) {\n\t\tif (insnNode.getInvokeType() != InvokeType.STATIC\n\t\t\t\t|| insnNode.getResult() == null) {\n\t\t\treturn null;\n\t\t}\n\t\tMethodInfo callMth = insnNode.getCallMth();\n\t\tif (valueOfMths.contains(callMth)) {\n\t\t\tRegisterArg resArg = insnNode.getResult();\n\t\t\tInsnArg arg = insnNode.getArg(0);\n\t\t\tif (arg.isLiteral()) {\n\t\t\t\tArgType primitiveType = callMth.getArgumentsTypes().get(0);\n\t\t\t\tArgType boxType = callMth.getReturnType();\n\t\t\t\tif (isNeedExplicitCast(resArg, primitiveType, boxType)) {\n\t\t\t\t\targ.add(AFlag.EXPLICIT_PRIMITIVE_TYPE);\n\t\t\t\t}\n\t\t\t\targ.setType(primitiveType);\n\t\t\t\tboolean forbidInline;\n\t\t\t\tif (canChangeTypeToPrimitive(resArg, boxType)) {\n\t\t\t\t\tresArg.setType(primitiveType);\n\t\t\t\t\tforbidInline = false;\n\t\t\t\t} else {\n\t\t\t\t\tforbidInline = true;\n\t\t\t\t}\n\n\t\t\t\tInsnNode constInsn = new InsnNode(InsnType.CONST, 1);\n\t\t\t\tconstInsn.addArg(arg);\n\t\t\t\tconstInsn.setResult(resArg);\n\t\t\t\tif (forbidInline) {\n\t\t\t\t\tconstInsn.add(AFlag.DONT_INLINE);\n\t\t\t\t}\n\t\t\t\treturn constInsn;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate boolean isNeedExplicitCast(RegisterArg resArg, ArgType primitiveType, ArgType boxType) {\n\t\tif (primitiveType == ArgType.LONG) {\n\t\t\treturn true;\n\t\t}\n\t\tif (primitiveType != ArgType.INT) {\n\t\t\tSet<ArgType> useTypes = collectUseTypes(resArg);\n\t\t\tuseTypes.add(resArg.getType());\n\t\t\tuseTypes.remove(boxType);\n\t\t\tuseTypes.remove(primitiveType);\n\t\t\treturn !useTypes.isEmpty();\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate boolean canChangeTypeToPrimitive(RegisterArg arg, ArgType boxType) {\n\t\tfor (SSAVar ssaVar : arg.getSVar().getCodeVar().getSsaVars()) {\n\t\t\tif (ssaVar.isTypeImmutable()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tInsnNode assignInsn = ssaVar.getAssignInsn();\n\t\t\tif (assignInsn == null) {\n\t\t\t\t// method arg\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tInsnType assignInsnType = assignInsn.getType();\n\t\t\tif (assignInsnType == InsnType.CONST || assignInsnType == InsnType.MOVE) {\n\t\t\t\tif (assignInsn.getArg(0).getType().isObject()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tArgType initType = assignInsn.getResult().getInitType();\n\t\t\tif (initType.isObject() && !initType.equals(boxType)) {\n\t\t\t\t// some of related vars have another object type\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tfor (RegisterArg useArg : ssaVar.getUseList()) {\n\t\t\t\tInsnNode parentInsn = useArg.getParentInsn();\n\t\t\t\tif (parentInsn == null) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (parentInsn.getType() == InsnType.INVOKE) {\n\t\t\t\t\tInvokeNode invokeNode = (InvokeNode) parentInsn;\n\t\t\t\t\tif (useArg.equals(invokeNode.getInstanceArg())) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate Set<ArgType> collectUseTypes(RegisterArg arg) {\n\t\tSet<ArgType> types = new HashSet<>();\n\t\tfor (RegisterArg useArg : arg.getSVar().getUseList()) {\n\t\t\ttypes.add(useArg.getType());\n\t\t\ttypes.add(useArg.getInitType());\n\t\t}\n\t\treturn types;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/DepthTraversal.java",
    "content": "package jadx.core.dex.visitors;\n\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic class DepthTraversal {\n\n\tpublic static void visit(IDexTreeVisitor visitor, ClassNode cls) {\n\t\ttry {\n\t\t\tif (visitor.visit(cls)) {\n\t\t\t\tcls.getInnerClasses().forEach(inCls -> visit(visitor, inCls));\n\t\t\t\tcls.getMethods().forEach(mth -> visit(visitor, mth));\n\t\t\t}\n\t\t} catch (StackOverflowError | BootstrapMethodError | Exception e) {\n\t\t\tcls.addError(e.getClass().getSimpleName() + \" in pass: \" + visitor.getClass().getSimpleName(), e);\n\t\t}\n\t}\n\n\tpublic static void visit(IDexTreeVisitor visitor, MethodNode mth) {\n\t\ttry {\n\t\t\tif (mth.contains(AType.JADX_ERROR)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvisitor.visit(mth);\n\t\t} catch (StackOverflowError | BootstrapMethodError | Exception e) {\n\t\t\tmth.addError(e.getClass().getSimpleName() + \" in pass: \" + visitor.getClass().getSimpleName(), e);\n\t\t}\n\t}\n\n\tprivate DepthTraversal() {\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.io.File;\nimport java.util.Optional;\nimport java.util.regex.Matcher;\n\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.DotGraphUtils;\n\npublic class DotGraphVisitor extends AbstractVisitor {\n\n\tprivate static final String NL = \"\\\\l\";\n\tprivate static final String NLQR = Matcher.quoteReplacement(NL);\n\tprivate static final boolean PRINT_DOMINATORS = false;\n\tprivate static final boolean PRINT_DOMINATORS_INFO = false;\n\n\tprivate final boolean useRegions;\n\tprivate final boolean rawInsn;\n\n\t// if present, this region and it's children will still be drawn when not in regions mode.\n\tprivate Optional<IRegion> highlightRegion;\n\n\tpublic static DotGraphVisitor dump() {\n\t\treturn new DotGraphVisitor(false, false);\n\t}\n\n\tpublic static DotGraphVisitor dumpRaw() {\n\t\treturn new DotGraphVisitor(false, true);\n\t}\n\n\tpublic static DotGraphVisitor dumpRegions() {\n\t\treturn new DotGraphVisitor(true, false);\n\t}\n\n\tpublic static DotGraphVisitor dumpRawRegions() {\n\t\treturn new DotGraphVisitor(true, true);\n\t}\n\n\t/**\n\t * Helper function to generate a cfg at a given point showing only one of the regions in the graph.\n\t * Intended to be called during a debugging session to produce a CFG with only a region of interest,\n\t * with DotGraphVisitor.debugDumpWithRegionHiglight(region).visit(mth);\n\t *\n\t * @param region the region to show\n\t * @return the visitor, to be invoked with `.visit(mth);`\n\t */\n\tpublic static DotGraphVisitor debugDumpWithRegionHighlight(IRegion region) {\n\t\treturn new DotGraphVisitor(false, false, Optional.of(region));\n\t}\n\n\tprivate DotGraphVisitor(boolean useRegions, boolean rawInsn) {\n\t\tthis(useRegions, rawInsn, Optional.empty());\n\t}\n\n\tprivate DotGraphVisitor(boolean useRegions, boolean rawInsn, Optional<IRegion> highlightRegion) {\n\t\tthis.useRegions = useRegions;\n\t\tthis.rawInsn = rawInsn;\n\t\tthis.highlightRegion = highlightRegion;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"DotGraphVisitor\";\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tnew DotGraphUtils(useRegions, rawInsn, highlightRegion).dumpToFile(mth);\n\t}\n\n\tpublic void save(File dir, MethodNode mth) {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tnew DotGraphUtils(useRegions, rawInsn, highlightRegion).dumpToFile(mth, dir);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/EnumVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.core.codegen.TypeGen;\nimport jadx.core.deobf.NameMapper;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.EnumClassAttr;\nimport jadx.core.dex.attributes.nodes.EnumClassAttr.EnumField;\nimport jadx.core.dex.attributes.nodes.RenameReasonAttr;\nimport jadx.core.dex.attributes.nodes.SkipMethodArgsAttr;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.InvokeType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.regions.Region;\nimport jadx.core.dex.visitors.regions.CheckRegions;\nimport jadx.core.dex.visitors.regions.IfRegionVisitor;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.utils.BlockInsnPair;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.utils.InsnUtils.checkInsnType;\nimport static jadx.core.utils.InsnUtils.getSingleArg;\nimport static jadx.core.utils.InsnUtils.getWrappedInsn;\n\n@JadxVisitor(\n\t\tname = \"EnumVisitor\",\n\t\tdesc = \"Restore enum classes\",\n\t\trunAfter = {\n\t\t\t\tCodeShrinkVisitor.class, // all possible instructions already inlined\n\t\t\t\tModVisitor.class,\n\t\t\t\tReplaceNewArray.class, // values array normalized\n\t\t\t\tIfRegionVisitor.class, // ternary operator inlined\n\t\t\t\tCheckRegions.class // regions processing finished\n\t\t},\n\t\trunBefore = {\n\t\t\t\tExtractFieldInit.class\n\t\t}\n)\npublic class EnumVisitor extends AbstractVisitor {\n\tprivate static final String ENUM_SUPER_CONSTRUCTOR_ID = \"java.lang.Enum.<init>(Ljava/lang/String;I)V\";\n\n\tprivate MethodInfo enumValueOfMth;\n\tprivate MethodInfo cloneMth;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tenumValueOfMth = MethodInfo.fromDetails(\n\t\t\t\troot,\n\t\t\t\tClassInfo.fromType(root, ArgType.ENUM),\n\t\t\t\t\"valueOf\",\n\t\t\t\tArrays.asList(ArgType.CLASS, ArgType.STRING),\n\t\t\t\tArgType.ENUM);\n\n\t\tcloneMth = MethodInfo.fromDetails(root,\n\t\t\t\tClassInfo.fromType(root, ArgType.OBJECT),\n\t\t\t\t\"clone\",\n\t\t\t\tCollections.emptyList(),\n\t\t\t\tArgType.OBJECT);\n\t}\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) throws JadxException {\n\t\tif (cls.isEnum()) {\n\t\t\tboolean converted;\n\t\t\ttry {\n\t\t\t\tconverted = convertToEnum(cls);\n\t\t\t} catch (Exception e) {\n\t\t\t\tcls.addWarnComment(\"Enum visitor error\", e);\n\t\t\t\tconverted = false;\n\t\t\t}\n\t\t\tif (!converted) {\n\t\t\t\tAccessInfo accessFlags = cls.getAccessFlags();\n\t\t\t\tif (accessFlags.isEnum()) {\n\t\t\t\t\tcls.setAccessFlags(accessFlags.remove(AccessFlags.ENUM));\n\t\t\t\t\tcls.addWarnComment(\"Failed to restore enum class, 'enum' modifier and super class removed\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate boolean convertToEnum(ClassNode cls) {\n\t\tArgType superType = cls.getSuperClass();\n\t\tif (superType != null && superType.getObject().equals(ArgType.ENUM.getObject())) {\n\t\t\tcls.add(AFlag.REMOVE_SUPER_CLASS);\n\t\t}\n\t\tMethodNode classInitMth = cls.getClassInitMth();\n\t\tif (classInitMth == null) {\n\t\t\tcls.addWarnComment(\"Enum class init method not found\");\n\t\t\treturn false;\n\t\t}\n\t\tRegion staticRegion = classInitMth.getRegion();\n\t\tif (staticRegion == null || classInitMth.getBasicBlocks().isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\t// collect blocks on linear part of static method (ignore branching on method end)\n\t\tList<BlockNode> staticBlocks = new ArrayList<>();\n\t\tfor (IContainer subBlock : staticRegion.getSubBlocks()) {\n\t\t\tif (subBlock instanceof BlockNode) {\n\t\t\t\tstaticBlocks.add((BlockNode) subBlock);\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (staticBlocks.isEmpty()) {\n\t\t\tcls.addWarnComment(\"Unexpected branching in enum static init block\");\n\t\t\treturn false;\n\t\t}\n\t\tEnumData data = new EnumData(cls, classInitMth, staticBlocks);\n\t\tif (!searchValuesField(data)) {\n\t\t\treturn false;\n\t\t}\n\t\tList<EnumField> enumFields = null;\n\t\tInsnArg arrArg = data.valuesInitInsn.getArg(0);\n\t\tif (arrArg.isInsnWrap()) {\n\t\t\tInsnNode wrappedInsn = ((InsnWrapArg) arrArg).getWrapInsn();\n\t\t\tenumFields = extractEnumFieldsFromInsn(data, wrappedInsn);\n\t\t} else if (arrArg.isRegister()) {\n\t\t\t// Kotlin 1.9+ $ENTRIES pattern: array register has multiple uses,\n\t\t\t// preventing CodeShrinkVisitor from inlining into the SPUT\n\t\t\tRegisterArg regArg = (RegisterArg) arrArg;\n\t\t\tInsnNode assignInsn = regArg.getAssignInsn();\n\t\t\tif (assignInsn != null) {\n\t\t\t\tenumFields = extractEnumFieldsFromInsn(data, assignInsn);\n\t\t\t}\n\t\t}\n\t\tif (enumFields == null) {\n\t\t\tcls.addWarnComment(\"Unknown enum class pattern. Please report as an issue!\");\n\t\t\treturn false;\n\t\t}\n\t\tdata.toRemove.add(data.valuesInitInsn);\n\n\t\t// all checks complete, perform transform\n\t\tEnumClassAttr attr = new EnumClassAttr(enumFields);\n\t\tattr.setStaticMethod(classInitMth);\n\t\tcls.addAttr(attr);\n\n\t\tfor (EnumField enumField : attr.getFields()) {\n\t\t\tFieldNode fieldNode = enumField.getField();\n\t\t\tString name = enumField.getNameStr();\n\t\t\tif (name != null\n\t\t\t\t\t&& !fieldNode.getAlias().equals(name)\n\t\t\t\t\t&& NameMapper.isValidAndPrintable(name)\n\t\t\t\t\t&& cls.root().getArgs().isRenameValid()) {\n\t\t\t\tfieldNode.getFieldInfo().setAlias(name);\n\t\t\t}\n\t\t\tfieldNode.add(AFlag.DONT_GENERATE);\n\t\t\tprocessConstructorInsn(data, enumField, classInitMth);\n\t\t}\n\t\tdata.valuesField.add(AFlag.DONT_GENERATE);\n\t\tInsnRemover.removeAllAndUnbind(classInitMth, data.toRemove);\n\t\tif (classInitMth.countInsns() == 0) {\n\t\t\tclassInitMth.add(AFlag.DONT_GENERATE);\n\t\t} else if (!data.toRemove.isEmpty()) {\n\t\t\tCodeShrinkVisitor.shrinkMethod(classInitMth);\n\t\t}\n\t\tremoveEnumMethods(cls, data.valuesField);\n\t\tfixAccessFlags(cls);\n\t\tcls.add(AFlag.CONVERTED_ENUM);\n\t\treturn true;\n\t}\n\n\tprivate static void fixAccessFlags(ClassNode cls) {\n\t\t// remove invalid access flags\n\t\tcls.setAccessFlags(cls.getAccessFlags()\n\t\t\t\t.remove(AccessFlags.FINAL)\n\t\t\t\t.remove(AccessFlags.ABSTRACT)\n\t\t\t\t.remove(AccessFlags.STATIC));\n\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\tif (mth.getMethodInfo().isConstructor()) {\n\t\t\t\tmth.setAccessFlags(mth.getAccessFlags().remove(AccessInfo.VISIBILITY_FLAGS));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Search \"$VALUES\" field (holds all enum values)\n\t */\n\tprivate boolean searchValuesField(EnumData data) {\n\t\tArgType clsType = data.cls.getClassInfo().getType();\n\t\tList<FieldNode> valuesCandidates = data.cls.getFields().stream()\n\t\t\t\t.filter(f -> f.getAccessFlags().isStatic())\n\t\t\t\t.filter(f -> f.getType().isArray())\n\t\t\t\t.filter(f -> Objects.equals(f.getType().getArrayRootElement(), clsType))\n\t\t\t\t.collect(Collectors.toList());\n\n\t\tif (valuesCandidates.isEmpty()) {\n\t\t\tdata.cls.addWarnComment(\"$VALUES field not found\");\n\t\t\treturn false;\n\t\t}\n\t\tif (valuesCandidates.size() > 1) {\n\t\t\tvaluesCandidates.removeIf(f -> !f.getAccessFlags().isSynthetic());\n\t\t}\n\t\tif (valuesCandidates.size() > 1) {\n\t\t\tOptional<FieldNode> valuesOpt = valuesCandidates.stream().filter(f -> f.getName().equals(\"$VALUES\")).findAny();\n\t\t\tif (valuesOpt.isPresent()) {\n\t\t\t\tvaluesCandidates.clear();\n\t\t\t\tvaluesCandidates.add(valuesOpt.get());\n\t\t\t}\n\t\t}\n\t\tif (valuesCandidates.size() != 1) {\n\t\t\tdata.cls.addWarnComment(\"Found several \\\"values\\\" enum fields: \" + valuesCandidates);\n\t\t\treturn false;\n\t\t}\n\t\tdata.valuesField = valuesCandidates.get(0);\n\n\t\t// search \"$VALUES\" array init and collect enum fields\n\t\tBlockInsnPair valuesInitPair = getValuesInitInsn(data);\n\t\tif (valuesInitPair == null) {\n\t\t\treturn false;\n\t\t}\n\t\tdata.valuesInitInsn = valuesInitPair.getInsn();\n\t\treturn true;\n\t}\n\n\tprivate void processConstructorInsn(EnumData data, EnumField enumField, MethodNode classInitMth) {\n\t\tConstructorInsn co = enumField.getConstrInsn();\n\t\tClassInfo enumClsInfo = co.getClassType();\n\t\tif (!enumClsInfo.equals(data.cls.getClassInfo())) {\n\t\t\tClassNode enumCls = data.cls.root().resolveClass(enumClsInfo);\n\t\t\tif (enumCls != null) {\n\t\t\t\tprocessEnumCls(data.cls, enumField, enumCls);\n\t\t\t}\n\t\t}\n\t\tMethodNode ctrMth = data.cls.root().resolveMethod(co.getCallMth());\n\t\tif (ctrMth != null) {\n\t\t\tmarkArgsForSkip(ctrMth);\n\t\t}\n\t\tRegisterArg coResArg = co.getResult();\n\t\tif (coResArg == null || coResArg.getSVar().getUseList().size() <= 2) {\n\t\t\tdata.toRemove.add(co);\n\t\t} else {\n\t\t\tboolean varUseFound = coResArg.getSVar().getUseList().stream()\n\t\t\t\t\t.anyMatch(useArg -> !data.toRemove.contains(useArg.getParentInsn()));\n\t\t\tif (varUseFound) {\n\t\t\t\t// constructor result used in other places -> replace constructor with enum field get (SGET)\n\t\t\t\tIndexInsnNode enumGet = new IndexInsnNode(InsnType.SGET, enumField.getField().getFieldInfo(), 0);\n\t\t\t\tenumGet.setResult(coResArg.duplicate());\n\t\t\t\tBlockUtils.replaceInsn(classInitMth, co, enumGet);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate List<EnumField> extractEnumFieldsFromInsn(EnumData enumData, InsnNode wrappedInsn) {\n\t\tswitch (wrappedInsn.getType()) {\n\t\t\tcase FILLED_NEW_ARRAY:\n\t\t\t\treturn extractEnumFieldsFromFilledArray(enumData, wrappedInsn);\n\n\t\t\tcase INVOKE:\n\t\t\t\t// handle redirection of values array fill (added in java 15)\n\t\t\t\treturn extractEnumFieldsFromInvoke(enumData, (InvokeNode) wrappedInsn);\n\n\t\t\tcase NEW_ARRAY:\n\t\t\t\tInsnArg arg = wrappedInsn.getArg(0);\n\t\t\t\tif (arg.isZeroLiteral()) {\n\t\t\t\t\t// empty enum\n\t\t\t\t\treturn Collections.emptyList();\n\t\t\t\t}\n\t\t\t\treturn null;\n\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate List<EnumField> extractEnumFieldsFromInvoke(EnumData enumData, InvokeNode invokeNode) {\n\t\tMethodInfo callMth = invokeNode.getCallMth();\n\t\tMethodNode valuesMth = enumData.cls.root().resolveMethod(callMth);\n\t\tif (valuesMth == null || valuesMth.isVoidReturn()) {\n\t\t\treturn null;\n\t\t}\n\t\tBlockNode returnBlock = Utils.getOne(valuesMth.getPreExitBlocks());\n\t\tInsnNode returnInsn = BlockUtils.getLastInsn(returnBlock);\n\t\tInsnNode wrappedInsn = getWrappedInsn(getSingleArg(returnInsn));\n\t\tif (wrappedInsn == null) {\n\t\t\treturn null;\n\t\t}\n\t\tList<EnumField> enumFields = extractEnumFieldsFromInsn(enumData, wrappedInsn);\n\t\tif (enumFields != null && ListUtils.isSingleElement(valuesMth.getUseIn(), enumData.classInitMth)) {\n\t\t\tvaluesMth.add(AFlag.DONT_GENERATE);\n\t\t\tif (valuesMth.getName().equals(\"$values\")) {\n\t\t\t\t// Kotlin synthetic method used for init values\n\t\t\t\t// rename to actual values method to use in $ENTRIES init code\n\t\t\t\tvaluesMth.getMethodInfo().setAlias(\"values\");\n\t\t\t}\n\t\t}\n\t\treturn enumFields;\n\t}\n\n\tprivate BlockInsnPair getValuesInitInsn(EnumData data) {\n\t\tFieldInfo searchField = data.valuesField.getFieldInfo();\n\t\tfor (BlockNode blockNode : data.staticBlocks) {\n\t\t\tfor (InsnNode insn : blockNode.getInstructions()) {\n\t\t\t\tif (insn.getType() == InsnType.SPUT) {\n\t\t\t\t\tIndexInsnNode indexInsnNode = (IndexInsnNode) insn;\n\t\t\t\t\tFieldInfo f = (FieldInfo) indexInsnNode.getIndex();\n\t\t\t\t\tif (f.equals(searchField)) {\n\t\t\t\t\t\treturn new BlockInsnPair(blockNode, indexInsnNode);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate List<EnumField> extractEnumFieldsFromFilledArray(EnumData enumData, InsnNode arrFillInsn) {\n\t\tList<EnumField> enumFields = new ArrayList<>();\n\t\tfor (InsnArg arg : arrFillInsn.getArguments()) {\n\t\t\tEnumField field = null;\n\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\tInsnNode wrappedInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\tfield = processEnumFieldByWrappedInsn(enumData, wrappedInsn);\n\t\t\t} else if (arg.isRegister()) {\n\t\t\t\tfield = processEnumFieldByRegister(enumData, (RegisterArg) arg);\n\t\t\t}\n\t\t\tif (field == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tenumFields.add(field);\n\t\t}\n\t\tenumData.toRemove.add(arrFillInsn);\n\t\treturn enumFields;\n\t}\n\n\tprivate EnumField processEnumFieldByWrappedInsn(EnumData data, InsnNode wrappedInsn) {\n\t\tif (wrappedInsn.getType() == InsnType.SGET) {\n\t\t\treturn processEnumFieldByField(data, wrappedInsn);\n\t\t}\n\t\tConstructorInsn constructorInsn = castConstructorInsn(wrappedInsn);\n\t\tif (constructorInsn != null) {\n\t\t\tFieldNode enumFieldNode = createFakeField(data.cls, \"EF\" + constructorInsn.getOffset());\n\t\t\tdata.cls.addField(enumFieldNode);\n\t\t\treturn createEnumFieldByConstructor(data, enumFieldNode, constructorInsn);\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Nullable\n\tprivate EnumField processEnumFieldByField(EnumData data, InsnNode sgetInsn) {\n\t\tif (sgetInsn.getType() != InsnType.SGET) {\n\t\t\treturn null;\n\t\t}\n\t\tFieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) sgetInsn).getIndex();\n\t\tFieldNode enumFieldNode = data.cls.searchField(fieldInfo);\n\t\tif (enumFieldNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnNode sputInsn = searchFieldPutInsn(data, enumFieldNode);\n\t\tif (sputInsn == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tConstructorInsn co = getConstructorInsn(sputInsn);\n\t\tif (co == null) {\n\t\t\treturn null;\n\t\t}\n\t\tRegisterArg sgetResult = sgetInsn.getResult();\n\t\tif (sgetResult == null || sgetResult.getSVar().getUseCount() == 1) {\n\t\t\tdata.toRemove.add(sgetInsn);\n\t\t}\n\t\tdata.toRemove.add(sputInsn);\n\t\treturn createEnumFieldByConstructor(data, enumFieldNode, co);\n\t}\n\n\t@Nullable\n\tprivate EnumField processEnumFieldByRegister(EnumData data, RegisterArg arg) {\n\t\tInsnNode assignInsn = arg.getAssignInsn();\n\t\tif (assignInsn != null && assignInsn.getType() == InsnType.SGET) {\n\t\t\treturn processEnumFieldByField(data, assignInsn);\n\t\t}\n\n\t\tSSAVar ssaVar = arg.getSVar();\n\t\tif (ssaVar.getUseCount() == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnNode constrInsn = ssaVar.getAssign().getParentInsn();\n\t\tif (constrInsn == null || constrInsn.getType() != InsnType.CONSTRUCTOR) {\n\t\t\treturn null;\n\t\t}\n\t\tFieldNode enumFieldNode = searchEnumField(data, ssaVar);\n\t\tif (enumFieldNode == null) {\n\t\t\tenumFieldNode = createFakeField(data.cls, \"EF\" + arg.getRegNum());\n\t\t\tdata.cls.addField(enumFieldNode);\n\t\t}\n\t\treturn createEnumFieldByConstructor(data, enumFieldNode, (ConstructorInsn) constrInsn);\n\t}\n\n\tprivate FieldNode createFakeField(ClassNode cls, String name) {\n\t\tFieldNode enumFieldNode;\n\t\tFieldInfo fldInfo = FieldInfo.from(cls.root(), cls.getClassInfo(), name, cls.getType());\n\t\tenumFieldNode = new FieldNode(cls, fldInfo, 0);\n\t\tenumFieldNode.add(AFlag.SYNTHETIC);\n\t\tenumFieldNode.addInfoComment(\"Fake field, exist only in values array\");\n\t\treturn enumFieldNode;\n\t}\n\n\t@Nullable\n\tprivate FieldNode searchEnumField(EnumData data, SSAVar ssaVar) {\n\t\tInsnNode sputInsn = ssaVar.getUseList().get(0).getParentInsn();\n\t\tif (sputInsn == null || sputInsn.getType() != InsnType.SPUT) {\n\t\t\treturn null;\n\t\t}\n\t\tFieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) sputInsn).getIndex();\n\t\tFieldNode enumFieldNode = data.cls.searchField(fieldInfo);\n\t\tif (enumFieldNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tdata.toRemove.add(sputInsn);\n\t\treturn enumFieldNode;\n\t}\n\n\tprivate EnumField createEnumFieldByConstructor(EnumData data, FieldNode enumFieldNode, ConstructorInsn co) {\n\t\t// usually constructor signature is '<init>(Ljava/lang/String;I)V', sometimes one or both args can\n\t\t// be omitted\n\t\tClassNode cls = data.cls;\n\t\tClassInfo clsInfo = co.getClassType();\n\t\tClassNode constrCls = cls.root().resolveClass(clsInfo);\n\t\tif (constrCls == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (constrCls.equals(cls)) {\n\t\t\t// allow same class\n\t\t} else if (constrCls.contains(AType.ANONYMOUS_CLASS)) {\n\t\t\t// allow external class already marked as anonymous\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t\tMethodNode ctrMth = cls.root().resolveMethod(co.getCallMth());\n\t\tif (ctrMth == null) {\n\t\t\treturn null;\n\t\t}\n\t\t// usually constructor signature is '<init>(Ljava/lang/String;I)V'\n\t\t// sometimes one or both args can be inlined or omitted\n\t\tString nameStr = null;\n\t\tif (co.getArgsCount() == 0) {\n\t\t\tConstructorInsn ctrInsn = searchEnumSuperCtrInsn(ctrMth);\n\t\t\tif (ctrInsn != null && ctrInsn.getArgsCount() != 0) {\n\t\t\t\tnameStr = getConstString(ctrMth.root(), ctrInsn.getArg(0));\n\t\t\t}\n\t\t} else {\n\t\t\tnameStr = getConstString(cls.root(), co.getArg(0));\n\t\t\t// verify and try to inline additional constructor args\n\t\t\tList<RegisterArg> regs = new ArrayList<>();\n\t\t\tco.getRegisterArgs(regs);\n\t\t\tif (!regs.isEmpty()) {\n\t\t\t\tConstructorInsn replacedCo = inlineExternalRegs(data, co);\n\t\t\t\tif (replacedCo == null) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Init of enum field '\" + enumFieldNode.getName() + \"' uses external variables\");\n\t\t\t\t}\n\t\t\t\tdata.toRemove.add(co);\n\t\t\t\tco = replacedCo;\n\t\t\t}\n\t\t}\n\t\treturn new EnumField(enumFieldNode, co, nameStr);\n\t}\n\n\tprivate @Nullable ConstructorInsn searchEnumSuperCtrInsn(MethodNode ctrMth) {\n\t\tfor (BlockNode block : ctrMth.getBasicBlocks()) {\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tif (insn.getType() == InsnType.CONSTRUCTOR) {\n\t\t\t\t\tConstructorInsn ctrCall = (ConstructorInsn) insn;\n\t\t\t\t\tif (ctrCall.isSuper()\n\t\t\t\t\t\t\t&& ctrCall.getArgsCount() != 0\n\t\t\t\t\t\t\t&& ctrCall.getCallMth().getRawFullId().equals(ENUM_SUPER_CONSTRUCTOR_ID)) {\n\t\t\t\t\t\treturn ctrCall;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate ConstructorInsn inlineExternalRegs(EnumData data, ConstructorInsn co) {\n\t\tConstructorInsn resCo = co.copyWithoutResult();\n\t\tList<RegisterArg> regs = new ArrayList<>();\n\t\tresCo.getRegisterArgs(regs);\n\t\tfor (RegisterArg reg : regs) {\n\t\t\tFieldInfo enumField = checkExternalRegUsage(data, reg);\n\t\t\tif (enumField == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tInsnNode enumUse = new IndexInsnNode(InsnType.SGET, enumField, 0);\n\t\t\tboolean replaced = resCo.replaceArg(reg, InsnArg.wrapArg(enumUse));\n\t\t\tif (!replaced) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\treturn resCo;\n\t}\n\n\tprivate static FieldInfo checkExternalRegUsage(EnumData data, RegisterArg reg) {\n\t\tClassNode cls = data.cls;\n\t\tSSAVar ssaVar = reg.getSVar();\n\t\tInsnNode assignInsn = checkInsnType(ssaVar.getAssignInsn(), InsnType.CONSTRUCTOR);\n\t\tif (assignInsn == null || !((ConstructorInsn) assignInsn).getClassType().equals(cls.getClassInfo())) {\n\t\t\treturn null;\n\t\t}\n\t\tFieldInfo enumField = null;\n\t\tfor (RegisterArg useArg : ssaVar.getUseList()) {\n\t\t\tInsnNode useInsn = useArg.getParentInsn();\n\t\t\tif (useInsn == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tswitch (useInsn.getType()) {\n\t\t\t\tcase SPUT: {\n\t\t\t\t\tFieldInfo field = (FieldInfo) ((IndexInsnNode) useInsn).getIndex();\n\t\t\t\t\tif (!field.getDeclClass().equals(cls.getClassInfo())\n\t\t\t\t\t\t\t|| !field.getType().equals(cls.getType())) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tenumField = field;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase CONSTRUCTOR: {\n\t\t\t\t\tConstructorInsn useCo = (ConstructorInsn) useInsn;\n\t\t\t\t\tif (!useCo.getClassType().equals(cls.getClassInfo())) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase FILLED_NEW_ARRAY: {\n\t\t\t\t\t// allow usage in values init instruction\n\t\t\t\t\tInsnArg valuesArg = data.valuesInitInsn.getArg(0);\n\t\t\t\t\tInsnNode unwrapped = valuesArg.unwrap();\n\t\t\t\t\tif (unwrapped != null) {\n\t\t\t\t\t\tif (unwrapped != useInsn) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (valuesArg.isRegister()) {\n\t\t\t\t\t\tInsnNode valuesAssign = ((RegisterArg) valuesArg).getAssignInsn();\n\t\t\t\t\t\tif (valuesAssign != useInsn) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tif (enumField != null) {\n\t\t\tdata.toRemove.add(assignInsn);\n\t\t}\n\t\treturn enumField;\n\t}\n\n\t@Nullable\n\tprivate InsnNode searchFieldPutInsn(EnumData data, FieldNode enumFieldNode) {\n\t\tfor (BlockNode block : data.staticBlocks) {\n\t\t\tfor (InsnNode sputInsn : block.getInstructions()) {\n\t\t\t\tif (sputInsn != null && sputInsn.getType() == InsnType.SPUT) {\n\t\t\t\t\tFieldInfo f = (FieldInfo) ((IndexInsnNode) sputInsn).getIndex();\n\t\t\t\t\tFieldNode fieldNode = data.cls.searchField(f);\n\t\t\t\t\tif (Objects.equals(fieldNode, enumFieldNode)) {\n\t\t\t\t\t\treturn sputInsn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate void removeEnumMethods(ClassNode cls, FieldNode valuesField) {\n\t\tArgType clsType = cls.getClassInfo().getType();\n\t\tString valuesMethodShortId = \"values()\" + TypeGen.signature(ArgType.array(clsType));\n\t\tMethodNode valuesMethod = null;\n\t\t// remove compiler generated methods\n\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\tMethodInfo mi = mth.getMethodInfo();\n\t\t\tif (mi.isClassInit() || mth.isNoCode()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString shortId = mi.getShortId();\n\t\t\tif (mi.isConstructor()) {\n\t\t\t\tmarkArgsForSkip(mth);\n\t\t\t\t// remove super constructor call\n\t\t\t\tConstructorInsn superCtrInsn = searchEnumSuperCtrInsn(mth);\n\t\t\t\tif (superCtrInsn != null) {\n\t\t\t\t\tsuperCtrInsn.add(AFlag.DONT_GENERATE);\n\t\t\t\t\tInsnRemover.remove(mth, superCtrInsn);\n\t\t\t\t}\n\t\t\t\tif (isDefaultConstructor(mth, shortId)) {\n\t\t\t\t\tmth.add(AFlag.DONT_GENERATE);\n\t\t\t\t}\n\t\t\t} else if (mi.getShortId().equals(valuesMethodShortId)) {\n\t\t\t\tif (isValuesMethod(mth, clsType)) {\n\t\t\t\t\tvaluesMethod = mth;\n\t\t\t\t\tmth.add(AFlag.DONT_GENERATE);\n\t\t\t\t} else {\n\t\t\t\t\t// custom values method => rename to resolve conflict with enum method\n\t\t\t\t\tmth.getMethodInfo().setAlias(\"valuesCustom\");\n\t\t\t\t\tmth.addAttr(new RenameReasonAttr(mth).append(\"to resolve conflict with enum method\"));\n\t\t\t\t}\n\t\t\t} else if (isValuesMethod(mth, clsType)) {\n\t\t\t\tif (!mth.getMethodInfo().getAlias().equals(\"values\") && !mth.getUseIn().isEmpty()) {\n\t\t\t\t\t// rename to use default values method\n\t\t\t\t\tmth.getMethodInfo().setAlias(\"values\");\n\t\t\t\t\tmth.addAttr(new RenameReasonAttr(mth).append(\"to match enum method name\"));\n\t\t\t\t\tmth.add(AFlag.DONT_RENAME);\n\t\t\t\t}\n\t\t\t\tvaluesMethod = mth;\n\t\t\t\tmth.add(AFlag.DONT_GENERATE);\n\t\t\t} else if (simpleValueOfMth(mth, clsType)) {\n\t\t\t\tmth.add(AFlag.DONT_GENERATE);\n\t\t\t}\n\t\t}\n\t\tFieldInfo valuesFieldInfo = valuesField.getFieldInfo();\n\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\t// fix access to 'values' field and 'values()' method\n\t\t\tfixValuesAccess(mth, valuesFieldInfo, clsType, valuesMethod);\n\t\t}\n\t}\n\n\tprivate void markArgsForSkip(MethodNode mth) {\n\t\t// skip first and second args\n\t\tSkipMethodArgsAttr.skipArg(mth, 0);\n\t\tif (mth.getMethodInfo().getArgsCount() > 1) {\n\t\t\tSkipMethodArgsAttr.skipArg(mth, 1);\n\t\t}\n\t}\n\n\tprivate boolean isDefaultConstructor(MethodNode mth, String shortId) {\n\t\tboolean defaultId = shortId.equals(\"<init>(Ljava/lang/String;I)V\")\n\t\t\t\t|| shortId.equals(\"<init>(Ljava/lang/String;)V\");\n\t\tif (defaultId) {\n\t\t\t// check content\n\t\t\treturn mth.countInsns() == 0;\n\t\t}\n\t\treturn false;\n\t}\n\n\t// TODO: support other method patterns ???\n\tprivate boolean isValuesMethod(MethodNode mth, ArgType clsType) {\n\t\tArgType retType = mth.getReturnType();\n\t\tif (!retType.isArray() || !retType.getArrayElement().equals(clsType)) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnNode returnInsn = BlockUtils.getOnlyOneInsnFromMth(mth);\n\t\tif (returnInsn == null || returnInsn.getType() != InsnType.RETURN || returnInsn.getArgsCount() != 1) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnNode wrappedInsn = getWrappedInsn(getSingleArg(returnInsn));\n\t\tIndexInsnNode castInsn = (IndexInsnNode) checkInsnType(wrappedInsn, InsnType.CHECK_CAST);\n\t\tif (castInsn != null && Objects.equals(castInsn.getIndex(), ArgType.array(clsType))) {\n\t\t\tInvokeNode invokeInsn = (InvokeNode) checkInsnType(getWrappedInsn(getSingleArg(castInsn)), InsnType.INVOKE);\n\t\t\treturn invokeInsn != null && invokeInsn.getCallMth().equals(cloneMth);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate boolean simpleValueOfMth(MethodNode mth, ArgType clsType) {\n\t\tInsnNode returnInsn = InsnUtils.searchSingleReturnInsn(mth, insn -> insn.getArgsCount() == 1);\n\t\tif (returnInsn == null) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnNode wrappedInsn = getWrappedInsn(getSingleArg(returnInsn));\n\t\tIndexInsnNode castInsn = (IndexInsnNode) checkInsnType(wrappedInsn, InsnType.CHECK_CAST);\n\t\tif (castInsn != null && Objects.equals(castInsn.getIndex(), clsType)) {\n\t\t\tInvokeNode invokeInsn = (InvokeNode) checkInsnType(getWrappedInsn(getSingleArg(castInsn)), InsnType.INVOKE);\n\t\t\treturn invokeInsn != null && invokeInsn.getCallMth().equals(enumValueOfMth);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate void fixValuesAccess(MethodNode mth, FieldInfo valuesFieldInfo, ArgType clsType, @Nullable MethodNode valuesMethod) {\n\t\tMethodInfo mi = mth.getMethodInfo();\n\t\tif (mi.isConstructor() || mi.isClassInit() || mth.isNoCode() || mth == valuesMethod) {\n\t\t\treturn;\n\t\t}\n\t\t// search value field usage\n\t\tPredicate<InsnNode> insnTest = insn -> Objects.equals(((IndexInsnNode) insn).getIndex(), valuesFieldInfo);\n\t\tInsnNode useInsn = InsnUtils.searchInsn(mth, InsnType.SGET, insnTest);\n\t\tif (useInsn == null) {\n\t\t\treturn;\n\t\t}\n\t\t// replace 'values' field with 'values()' method\n\t\tInsnUtils.replaceInsns(mth, insn -> {\n\t\t\tif (insn.getType() == InsnType.SGET && insnTest.test(insn)) {\n\t\t\t\tMethodInfo valueMth = valuesMethod == null\n\t\t\t\t\t\t? getValueMthInfo(mth.root(), clsType)\n\t\t\t\t\t\t: valuesMethod.getMethodInfo();\n\t\t\t\tInvokeNode invokeNode = new InvokeNode(valueMth, InvokeType.STATIC, 0);\n\t\t\t\tinvokeNode.setResult(insn.getResult());\n\t\t\t\tif (valuesMethod == null) {\n\t\t\t\t\t// forcing enum method (can overlap and get renamed by custom method)\n\t\t\t\t\tinvokeNode.add(AFlag.FORCE_RAW_NAME);\n\t\t\t\t}\n\t\t\t\tmth.addDebugComment(\"Replace access to removed values field (\" + valuesFieldInfo.getName() + \") with 'values()' method\");\n\t\t\t\treturn invokeNode;\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t}\n\n\tprivate MethodInfo getValueMthInfo(RootNode root, ArgType clsType) {\n\t\treturn MethodInfo.fromDetails(root,\n\t\t\t\tClassInfo.fromType(root, clsType),\n\t\t\t\t\"values\",\n\t\t\t\tCollections.emptyList(), ArgType.array(clsType));\n\t}\n\n\tprivate static void processEnumCls(ClassNode cls, EnumField field, ClassNode innerCls) {\n\t\t// remove constructor, because it is anonymous class\n\t\tfor (MethodNode innerMth : innerCls.getMethods()) {\n\t\t\tif (innerMth.getAccessFlags().isConstructor()) {\n\t\t\t\tinnerMth.add(AFlag.DONT_GENERATE);\n\t\t\t}\n\t\t}\n\t\tfield.setCls(innerCls);\n\t\tif (!innerCls.getParentClass().equals(cls)) {\n\t\t\t// not inner\n\t\t\tcls.addInlinedClass(innerCls);\n\t\t\tinnerCls.add(AFlag.DONT_GENERATE);\n\t\t}\n\t}\n\n\tprivate ConstructorInsn getConstructorInsn(InsnNode insn) {\n\t\tif (insn.getArgsCount() != 1) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnArg arg = insn.getArg(0);\n\t\tif (arg.isInsnWrap()) {\n\t\t\treturn castConstructorInsn(((InsnWrapArg) arg).getWrapInsn());\n\t\t}\n\t\tif (arg.isRegister()) {\n\t\t\treturn castConstructorInsn(((RegisterArg) arg).getAssignInsn());\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Nullable\n\tprivate ConstructorInsn castConstructorInsn(InsnNode coCandidate) {\n\t\tif (coCandidate != null && coCandidate.getType() == InsnType.CONSTRUCTOR) {\n\t\t\treturn (ConstructorInsn) coCandidate;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String getConstString(RootNode root, InsnArg arg) {\n\t\tif (arg.isInsnWrap()) {\n\t\t\tInsnNode constInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\tObject constValue = InsnUtils.getConstValueByInsn(root, constInsn);\n\t\t\tif (constValue instanceof String) {\n\t\t\t\treturn (String) constValue;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static class EnumData {\n\t\tfinal ClassNode cls;\n\t\tfinal MethodNode classInitMth;\n\t\tfinal List<BlockNode> staticBlocks;\n\t\tfinal List<InsnNode> toRemove = new ArrayList<>();\n\t\tFieldNode valuesField;\n\t\tInsnNode valuesInitInsn;\n\n\t\tpublic EnumData(ClassNode cls, MethodNode classInitMth, List<BlockNode> staticBlocks) {\n\t\t\tthis.cls = cls;\n\t\t\tthis.classInitMth = classInitMth;\n\t\t\tthis.staticBlocks = staticBlocks;\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"EnumVisitor\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/ExtractFieldInit.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.FieldInitInsnAttr;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"ExtractFieldInit\",\n\t\tdesc = \"Move duplicated field initialization from constructors\",\n\t\trunAfter = ModVisitor.class,\n\t\trunBefore = ClassModifier.class\n)\npublic class ExtractFieldInit extends AbstractVisitor {\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) throws JadxException {\n\t\tfor (ClassNode inner : cls.getInnerClasses()) {\n\t\t\tvisit(inner);\n\t\t}\n\t\tif (!cls.getFields().isEmpty()) {\n\t\t\tmoveStaticFieldsInit(cls);\n\t\t\tmoveCommonFieldsInit(cls);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static final class FieldInitInfo {\n\t\tfinal FieldNode fieldNode;\n\t\tfinal IndexInsnNode putInsn;\n\t\tfinal boolean canMove;\n\n\t\tpublic FieldInitInfo(FieldNode fieldNode, IndexInsnNode putInsn, boolean canMove) {\n\t\t\tthis.fieldNode = fieldNode;\n\t\t\tthis.putInsn = putInsn;\n\t\t\tthis.canMove = canMove;\n\t\t}\n\t}\n\n\tprivate static final class ConstructorInitInfo {\n\t\tfinal MethodNode constructorMth;\n\t\tfinal List<FieldInitInfo> fieldInits;\n\n\t\tprivate ConstructorInitInfo(MethodNode constructorMth, List<FieldInitInfo> fieldInits) {\n\t\t\tthis.constructorMth = constructorMth;\n\t\t\tthis.fieldInits = fieldInits;\n\t\t}\n\t}\n\n\tprivate static void moveStaticFieldsInit(ClassNode cls) {\n\t\tMethodNode classInitMth = cls.getClassInitMth();\n\t\tif (classInitMth == null\n\t\t\t\t|| !classInitMth.getAccessFlags().isStatic()\n\t\t\t\t|| classInitMth.isNoCode()\n\t\t\t\t|| classInitMth.getBasicBlocks() == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (ListUtils.noneMatch(cls.getFields(), FieldNode::isStatic)) {\n\t\t\treturn;\n\t\t}\n\t\twhile (processStaticFields(cls, classInitMth)) {\n\t\t\t// sometimes instructions moved to field init prevent from vars inline -> inline and try again\n\t\t\tCodeShrinkVisitor.shrinkMethod(classInitMth);\n\t\t}\n\t}\n\n\tprivate static boolean processStaticFields(ClassNode cls, MethodNode classInitMth) {\n\t\tList<FieldInitInfo> inits = collectFieldsInit(cls, classInitMth, InsnType.SPUT);\n\t\tif (inits.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\t// ignore field init constant if field initialized in class init method\n\t\tfor (FieldInitInfo fieldInit : inits) {\n\t\t\tFieldNode field = fieldInit.fieldNode;\n\t\t\tif (field.getAccessFlags().isFinal()) {\n\t\t\t\tfield.remove(JadxAttrType.CONSTANT_VALUE);\n\t\t\t}\n\t\t}\n\t\tfilterFieldsInit(inits);\n\t\tif (inits.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (FieldInitInfo fieldInit : inits) {\n\t\t\tIndexInsnNode insn = fieldInit.putInsn;\n\t\t\tInsnArg arg = insn.getArg(0);\n\t\t\tif (arg instanceof InsnWrapArg) {\n\t\t\t\t((InsnWrapArg) arg).getWrapInsn().add(AFlag.DECLARE_VAR);\n\t\t\t}\n\t\t\tInsnRemover.remove(classInitMth, insn);\n\t\t\taddFieldInitAttr(classInitMth, fieldInit.fieldNode, insn);\n\t\t}\n\t\tfixFieldsOrder(cls, inits);\n\t\treturn true;\n\t}\n\n\tprivate static void moveCommonFieldsInit(ClassNode cls) {\n\t\tif (ListUtils.noneMatch(cls.getFields(), FieldNode::isInstance)) {\n\t\t\treturn;\n\t\t}\n\t\tList<MethodNode> constructors = getConstructorsList(cls);\n\t\tif (constructors.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tList<ConstructorInitInfo> infoList = new ArrayList<>(constructors.size());\n\t\tfor (MethodNode constructorMth : constructors) {\n\t\t\tList<FieldInitInfo> inits = collectFieldsInit(cls, constructorMth, InsnType.IPUT);\n\t\t\tfilterFieldsInit(inits);\n\t\t\tif (inits.isEmpty()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tinfoList.add(new ConstructorInitInfo(constructorMth, inits));\n\t\t}\n\t\t// compare collected instructions\n\t\tConstructorInitInfo common = null;\n\t\tfor (ConstructorInitInfo info : infoList) {\n\t\t\tif (common == null) {\n\t\t\t\tcommon = info;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!compareFieldInits(common.fieldInits, info.fieldInits)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (common == null) {\n\t\t\treturn;\n\t\t}\n\t\t// all checks passed\n\t\tfor (ConstructorInitInfo info : infoList) {\n\t\t\tfor (FieldInitInfo fieldInit : info.fieldInits) {\n\t\t\t\tIndexInsnNode putInsn = fieldInit.putInsn;\n\t\t\t\tInsnArg arg = putInsn.getArg(0);\n\t\t\t\tif (arg instanceof InsnWrapArg) {\n\t\t\t\t\t((InsnWrapArg) arg).getWrapInsn().add(AFlag.DECLARE_VAR);\n\t\t\t\t}\n\t\t\t\tInsnRemover.remove(info.constructorMth, putInsn);\n\t\t\t}\n\t\t}\n\t\tfor (FieldInitInfo fieldInit : common.fieldInits) {\n\t\t\taddFieldInitAttr(common.constructorMth, fieldInit.fieldNode, fieldInit.putInsn);\n\t\t}\n\t\tfixFieldsOrder(cls, common.fieldInits);\n\t}\n\n\tprivate static List<FieldInitInfo> collectFieldsInit(ClassNode cls, MethodNode mth, InsnType putType) {\n\t\tList<FieldInitInfo> fieldsInit = new ArrayList<>();\n\t\tSet<BlockNode> singlePathBlocks = new HashSet<>();\n\t\tBlockUtils.visitSinglePath(mth.getEnterBlock(), singlePathBlocks::add);\n\n\t\tboolean canReorder = true;\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tboolean fieldInsn = false;\n\t\t\t\tif (insn.getType() == putType) {\n\t\t\t\t\tIndexInsnNode putInsn = (IndexInsnNode) insn;\n\t\t\t\t\tFieldInfo field = (FieldInfo) putInsn.getIndex();\n\t\t\t\t\tif (field.getDeclClass().equals(cls.getClassInfo())) {\n\t\t\t\t\t\tFieldNode fn = cls.searchField(field);\n\t\t\t\t\t\tif (fn != null) {\n\t\t\t\t\t\t\tboolean canMove = canReorder && singlePathBlocks.contains(block);\n\t\t\t\t\t\t\tfieldsInit.add(new FieldInitInfo(fn, putInsn, canMove));\n\t\t\t\t\t\t\tfieldInsn = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!fieldInsn && canReorder && !insn.canReorder()) {\n\t\t\t\t\tcanReorder = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn fieldsInit;\n\t}\n\n\tprivate static void filterFieldsInit(List<FieldInitInfo> inits) {\n\t\t// exclude fields initialized several times\n\t\tSet<FieldInfo> excludedFields = inits\n\t\t\t\t.stream()\n\t\t\t\t.collect(Collectors.toMap(fi -> fi.fieldNode, fi -> 1, Integer::sum))\n\t\t\t\t.entrySet()\n\t\t\t\t.stream()\n\t\t\t\t.filter(v -> v.getValue() > 1)\n\t\t\t\t.map(v -> v.getKey().getFieldInfo())\n\t\t\t\t.collect(Collectors.toSet());\n\n\t\tfor (FieldInitInfo initInfo : inits) {\n\t\t\tif (!checkInsn(initInfo)) {\n\t\t\t\texcludedFields.add(initInfo.fieldNode.getFieldInfo());\n\t\t\t}\n\t\t}\n\t\tif (!excludedFields.isEmpty()) {\n\t\t\tboolean changed;\n\t\t\tdo {\n\t\t\t\tchanged = false;\n\t\t\t\tfor (FieldInitInfo initInfo : inits) {\n\t\t\t\t\tFieldInfo fieldInfo = initInfo.fieldNode.getFieldInfo();\n\t\t\t\t\tif (excludedFields.contains(fieldInfo)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (insnUseExcludedField(initInfo, excludedFields)) {\n\t\t\t\t\t\texcludedFields.add(fieldInfo);\n\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} while (changed);\n\t\t}\n\n\t\t// apply\n\t\tif (!excludedFields.isEmpty()) {\n\t\t\tinits.removeIf(fi -> excludedFields.contains(fi.fieldNode.getFieldInfo()));\n\t\t}\n\t}\n\n\tprivate static boolean checkInsn(FieldInitInfo initInfo) {\n\t\tif (!initInfo.canMove) {\n\t\t\treturn false;\n\t\t}\n\t\tIndexInsnNode insn = initInfo.putInsn;\n\t\tInsnArg arg = insn.getArg(0);\n\t\tif (arg.isInsnWrap()) {\n\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\tif (!wrapInsn.canReorder() && insn.contains(AType.EXC_CATCH)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else {\n\t\t\treturn arg.isLiteral() || arg.isThis();\n\t\t}\n\t\tSet<RegisterArg> regs = new HashSet<>();\n\t\tinsn.getRegisterArgs(regs);\n\t\tif (!regs.isEmpty()) {\n\t\t\tfor (RegisterArg reg : regs) {\n\t\t\t\tif (!reg.isThis()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean insnUseExcludedField(FieldInitInfo initInfo, Set<FieldInfo> excludedFields) {\n\t\tif (excludedFields.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tIndexInsnNode insn = initInfo.putInsn;\n\t\tboolean staticField = insn.getType() == InsnType.SPUT;\n\t\tInsnType useType = staticField ? InsnType.SGET : InsnType.IGET;\n\t\t// exclude if init code use any excluded field\n\t\tBoolean exclude = insn.visitInsns(innerInsn -> {\n\t\t\tif (innerInsn.getType() == useType) {\n\t\t\t\tFieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) innerInsn).getIndex();\n\t\t\t\tif (excludedFields.contains(fieldInfo)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t\treturn Objects.equals(exclude, Boolean.TRUE);\n\t}\n\n\tprivate static void fixFieldsOrder(ClassNode cls, List<FieldInitInfo> inits) {\n\t\tList<FieldNode> orderedFields = processFieldsDependencies(cls, inits);\n\t\tapplyFieldsOrder(cls, orderedFields);\n\t}\n\n\tprivate static List<FieldNode> processFieldsDependencies(ClassNode cls, List<FieldInitInfo> inits) {\n\t\tList<FieldNode> orderedFields = Utils.collectionMap(inits, v -> v.fieldNode);\n\t\t// collect dependant fields\n\t\tMap<FieldNode, List<FieldNode>> deps = new HashMap<>(inits.size());\n\t\tfor (FieldInitInfo initInfo : inits) {\n\t\t\tIndexInsnNode insn = initInfo.putInsn;\n\t\t\tboolean staticField = insn.getType() == InsnType.SPUT;\n\t\t\tInsnType useType = staticField ? InsnType.SGET : InsnType.IGET;\n\t\t\tinsn.visitInsns(subInsn -> {\n\t\t\t\tif (subInsn.getType() == useType) {\n\t\t\t\t\tFieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) subInsn).getIndex();\n\t\t\t\t\tif (fieldInfo.getDeclClass().equals(cls.getClassInfo())) {\n\t\t\t\t\t\tFieldNode depField = cls.searchField(fieldInfo);\n\t\t\t\t\t\tif (depField != null) {\n\t\t\t\t\t\t\tdeps.computeIfAbsent(initInfo.fieldNode, k -> new ArrayList<>())\n\t\t\t\t\t\t\t\t\t.add(depField);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tif (deps.isEmpty()) {\n\t\t\treturn orderedFields;\n\t\t}\n\t\t// build new list with deps fields before usage field\n\t\tList<FieldNode> result = new ArrayList<>();\n\t\tfor (FieldNode field : orderedFields) {\n\t\t\tint idx = result.indexOf(field);\n\t\t\tList<FieldNode> fieldDeps = deps.get(field);\n\t\t\tif (fieldDeps == null) {\n\t\t\t\tif (idx == -1) {\n\t\t\t\t\tresult.add(field);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (idx == -1) {\n\t\t\t\tfor (FieldNode depField : fieldDeps) {\n\t\t\t\t\tif (!result.contains(depField)) {\n\t\t\t\t\t\tresult.add(depField);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tresult.add(field);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (FieldNode depField : fieldDeps) {\n\t\t\t\tint depIdx = result.indexOf(depField);\n\t\t\t\tif (depIdx == -1) {\n\t\t\t\t\tresult.add(idx, depField);\n\t\t\t\t} else if (depIdx > idx) {\n\t\t\t\t\tresult.remove(depIdx);\n\t\t\t\t\tresult.add(idx, depField);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate static void applyFieldsOrder(ClassNode cls, List<FieldNode> orderedFields) {\n\t\tList<FieldNode> clsFields = cls.getFields();\n\t\t// check if already ordered\n\t\tboolean ordered = Collections.indexOfSubList(clsFields, orderedFields) != -1;\n\t\tif (!ordered) {\n\t\t\tclsFields.removeAll(orderedFields);\n\t\t\tclsFields.addAll(orderedFields);\n\t\t}\n\t}\n\n\tprivate static boolean compareFieldInits(List<FieldInitInfo> base, List<FieldInitInfo> other) {\n\t\tif (base.size() != other.size()) {\n\t\t\treturn false;\n\t\t}\n\t\tint count = base.size();\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tInsnNode baseInsn = base.get(i).putInsn;\n\t\t\tInsnNode otherInsn = other.get(i).putInsn;\n\t\t\tif (!baseInsn.isSame(otherInsn)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static List<MethodNode> getConstructorsList(ClassNode cls) {\n\t\tList<MethodNode> list = new ArrayList<>();\n\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\tAccessInfo accFlags = mth.getAccessFlags();\n\t\t\tif (!accFlags.isStatic() && accFlags.isConstructor()) {\n\t\t\t\tlist.add(mth);\n\t\t\t\tif (mth.isNoCode() || BlockUtils.isAllBlocksEmpty(mth.getBasicBlocks())) {\n\t\t\t\t\treturn Collections.emptyList();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n\tprivate static void addFieldInitAttr(MethodNode mth, FieldNode field, IndexInsnNode putInsn) {\n\t\tInsnNode assignInsn;\n\t\tInsnArg fldArg = putInsn.getArg(0);\n\t\tif (fldArg.isInsnWrap()) {\n\t\t\tassignInsn = ((InsnWrapArg) fldArg).getWrapInsn();\n\t\t} else {\n\t\t\tassignInsn = InsnNode.wrapArg(fldArg);\n\t\t}\n\t\tfield.addAttr(new FieldInitInsnAttr(mth, assignInsn));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/FallbackModeVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport jadx.core.codegen.json.JsonMappingGen;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.trycatch.CatchAttr;\nimport jadx.core.utils.exceptions.JadxException;\n\npublic class FallbackModeVisitor extends AbstractVisitor {\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tif (root.getArgs().isJsonOutput()) {\n\t\t\tJsonMappingGen.dump(root);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (InsnNode insn : mth.getInstructions()) {\n\t\t\tif (insn == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// remove 'exception catch' for instruction which don't throw any exceptions\n\t\t\tCatchAttr catchAttr = insn.get(AType.EXC_CATCH);\n\t\t\tif (catchAttr != null) {\n\t\t\t\tswitch (insn.getType()) {\n\t\t\t\t\tcase RETURN:\n\t\t\t\t\tcase IF:\n\t\t\t\t\tcase GOTO:\n\t\t\t\t\tcase JAVA_JSR:\n\t\t\t\t\tcase MOVE:\n\t\t\t\t\tcase MOVE_EXCEPTION:\n\t\t\t\t\tcase ARITH: // ??\n\t\t\t\t\tcase NEG:\n\t\t\t\t\tcase CONST:\n\t\t\t\t\tcase CONST_STR:\n\t\t\t\t\tcase CONST_CLASS:\n\t\t\t\t\tcase CMP_L:\n\t\t\t\t\tcase CMP_G:\n\t\t\t\t\t\tinsn.remove(AType.EXC_CATCH);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/FixSwitchOverEnum.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.IntFunction;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.EnumClassAttr;\nimport jadx.core.dex.attributes.nodes.EnumMapAttr;\nimport jadx.core.dex.attributes.nodes.RegionRefAttr;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.SwitchInsn;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.regions.SwitchRegion;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"FixSwitchOverEnum\",\n\t\tdesc = \"Simplify synthetic code in switch over enum\",\n\t\trunAfter = {\n\t\t\t\tCodeShrinkVisitor.class,\n\t\t\t\tEnumVisitor.class\n\t\t}\n)\npublic class FixSwitchOverEnum extends AbstractVisitor {\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) throws JadxException {\n\t\tinitClsEnumMap(cls);\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tboolean changed = false;\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tif (insn.getType() == InsnType.SWITCH && !insn.contains(AFlag.REMOVE)) {\n\t\t\t\t\tchanged |= processEnumSwitch(mth, (SwitchInsn) insn);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (changed) {\n\t\t\tCodeShrinkVisitor.shrinkMethod(mth);\n\t\t}\n\t}\n\n\tprivate static boolean processEnumSwitch(MethodNode mth, SwitchInsn insn) {\n\t\tInsnArg arg = insn.getArg(0);\n\t\tif (!arg.isInsnWrap()) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\tswitch (wrapInsn.getType()) {\n\t\t\tcase AGET:\n\t\t\t\treturn processRemappedEnumSwitch(mth, insn, wrapInsn, arg);\n\t\t\tcase INVOKE:\n\t\t\t\treturn processDirectEnumSwitch(mth, insn, (InvokeNode) wrapInsn, arg);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean executeReplace(SwitchInsn swInsn, InsnArg arg, InsnArg invVar, IntFunction<Object> caseReplace) {\n\t\tRegionRefAttr regionRefAttr = swInsn.get(AType.REGION_REF);\n\t\tif (regionRefAttr == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!swInsn.replaceArg(arg, invVar)) {\n\t\t\treturn false;\n\t\t}\n\t\tMap<Object, Object> replaceMap = new HashMap<>();\n\t\tint caseCount = swInsn.getKeys().length;\n\t\tfor (int i = 0; i < caseCount; i++) {\n\t\t\tObject key = swInsn.getKey(i);\n\t\t\tObject replaceObj = caseReplace.apply(i);\n\t\t\tswInsn.modifyKey(i, replaceObj);\n\t\t\treplaceMap.put(key, replaceObj);\n\t\t}\n\t\tSwitchRegion region = (SwitchRegion) regionRefAttr.getRegion();\n\t\tfor (SwitchRegion.CaseInfo caseInfo : region.getCases()) {\n\t\t\tcaseInfo.getKeys().replaceAll(key -> Utils.getOrElse(replaceMap.get(key), key));\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean processDirectEnumSwitch(MethodNode mth, SwitchInsn swInsn, InvokeNode invInsn, InsnArg arg) {\n\t\tMethodInfo callMth = invInsn.getCallMth();\n\t\tif (!callMth.getShortId().equals(\"ordinal()I\")) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnArg invVar = invInsn.getArg(0);\n\t\tClassNode enumCls = mth.root().resolveClass(invVar.getType());\n\t\tif (enumCls == null) {\n\t\t\treturn false;\n\t\t}\n\t\tEnumClassAttr enumClassAttr = enumCls.get(AType.ENUM_CLASS);\n\t\tif (enumClassAttr == null) {\n\t\t\treturn false;\n\t\t}\n\t\tFieldNode[] casesReplaceArr = mapToCases(swInsn, enumClassAttr.getFields());\n\t\tif (casesReplaceArr == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn executeReplace(swInsn, arg, invVar, i -> casesReplaceArr[i]);\n\t}\n\n\tprivate static @Nullable FieldNode[] mapToCases(SwitchInsn swInsn, List<EnumClassAttr.EnumField> fields) {\n\t\tint caseCount = swInsn.getKeys().length;\n\t\tif (fields.size() < caseCount) {\n\t\t\treturn null;\n\t\t}\n\t\tFieldNode[] casesMap = new FieldNode[caseCount];\n\t\tfor (int i = 0; i < caseCount; i++) {\n\t\t\tObject key = swInsn.getKey(i);\n\t\t\tif (key instanceof Integer) {\n\t\t\t\tint ordinal = (Integer) key;\n\t\t\t\ttry {\n\t\t\t\t\tcasesMap[ordinal] = fields.get(ordinal).getField();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\treturn casesMap;\n\t}\n\n\tprivate static boolean processRemappedEnumSwitch(MethodNode mth, SwitchInsn insn, InsnNode wrapInsn, InsnArg arg) {\n\t\tEnumMapInfo enumMapInfo = checkEnumMapAccess(mth.root(), wrapInsn);\n\t\tif (enumMapInfo == null) {\n\t\t\treturn false;\n\t\t}\n\t\tFieldNode enumMapField = enumMapInfo.getMapField();\n\t\tInsnArg invArg = enumMapInfo.getArg();\n\n\t\tEnumMapAttr.KeyValueMap valueMap = getEnumMap(enumMapField);\n\t\tif (valueMap == null) {\n\t\t\treturn false;\n\t\t}\n\t\tint caseCount = insn.getKeys().length;\n\t\tfor (int i = 0; i < caseCount; i++) {\n\t\t\tObject key = insn.getKey(i);\n\t\t\tObject newKey = valueMap.get(key);\n\t\t\tif (newKey == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tif (executeReplace(insn, arg, invArg, i -> valueMap.get(insn.getKey(i)))) {\n\t\t\tenumMapField.add(AFlag.DONT_GENERATE);\n\t\t\tcheckAndHideClass(enumMapField.getParentClass());\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static void initClsEnumMap(ClassNode enumCls) {\n\t\tMethodNode clsInitMth = enumCls.getClassInitMth();\n\t\tif (clsInitMth == null || clsInitMth.isNoCode() || clsInitMth.getBasicBlocks() == null) {\n\t\t\treturn;\n\t\t}\n\t\tEnumMapAttr mapAttr = new EnumMapAttr();\n\t\tfor (BlockNode block : clsInitMth.getBasicBlocks()) {\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tif (insn.getType() == InsnType.APUT) {\n\t\t\t\t\taddToEnumMap(enumCls.root(), mapAttr, insn);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!mapAttr.isEmpty()) {\n\t\t\tenumCls.addAttr(mapAttr);\n\t\t}\n\t}\n\n\tprivate static @Nullable EnumMapAttr.KeyValueMap getEnumMap(FieldNode field) {\n\t\tClassNode syntheticClass = field.getParentClass();\n\t\tEnumMapAttr mapAttr = syntheticClass.get(AType.ENUM_MAP);\n\t\tif (mapAttr == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn mapAttr.getMap(field);\n\t}\n\n\tprivate static void addToEnumMap(RootNode root, EnumMapAttr mapAttr, InsnNode aputInsn) {\n\t\tInsnArg litArg = aputInsn.getArg(2);\n\t\tif (!litArg.isLiteral()) {\n\t\t\treturn;\n\t\t}\n\t\tEnumMapInfo mapInfo = checkEnumMapAccess(root, aputInsn);\n\t\tif (mapInfo == null) {\n\t\t\treturn;\n\t\t}\n\t\tInsnArg enumArg = mapInfo.getArg();\n\t\tFieldNode field = mapInfo.getMapField();\n\t\tif (field == null || !enumArg.isInsnWrap()) {\n\t\t\treturn;\n\t\t}\n\t\tInsnNode sget = ((InsnWrapArg) enumArg).getWrapInsn();\n\t\tif (!(sget instanceof IndexInsnNode)) {\n\t\t\treturn;\n\t\t}\n\t\tObject index = ((IndexInsnNode) sget).getIndex();\n\t\tif (!(index instanceof FieldInfo)) {\n\t\t\treturn;\n\t\t}\n\t\tFieldNode fieldNode = root.resolveField((FieldInfo) index);\n\t\tif (fieldNode == null) {\n\t\t\treturn;\n\t\t}\n\t\tint literal = (int) ((LiteralArg) litArg).getLiteral();\n\t\tmapAttr.add(field, literal, fieldNode);\n\t}\n\n\tprivate static @Nullable EnumMapInfo checkEnumMapAccess(RootNode root, InsnNode checkInsn) {\n\t\tInsnArg sgetArg = checkInsn.getArg(0);\n\t\tInsnArg invArg = checkInsn.getArg(1);\n\t\tif (!sgetArg.isInsnWrap() || !invArg.isInsnWrap()) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnNode invInsn = ((InsnWrapArg) invArg).getWrapInsn();\n\t\tInsnNode sgetInsn = ((InsnWrapArg) sgetArg).getWrapInsn();\n\t\tif (invInsn.getType() != InsnType.INVOKE || sgetInsn.getType() != InsnType.SGET) {\n\t\t\treturn null;\n\t\t}\n\t\tInvokeNode inv = (InvokeNode) invInsn;\n\t\tif (!inv.getCallMth().getShortId().equals(\"ordinal()I\")) {\n\t\t\treturn null;\n\t\t}\n\t\tClassNode enumCls = root.resolveClass(inv.getCallMth().getDeclClass());\n\t\tif (enumCls == null || !enumCls.isEnum()) {\n\t\t\treturn null;\n\t\t}\n\t\tObject index = ((IndexInsnNode) sgetInsn).getIndex();\n\t\tif (!(index instanceof FieldInfo)) {\n\t\t\treturn null;\n\t\t}\n\t\tFieldNode enumMapField = root.resolveField((FieldInfo) index);\n\t\tif (enumMapField == null || !enumMapField.getAccessFlags().isSynthetic()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new EnumMapInfo(inv.getArg(0), enumMapField);\n\t}\n\n\t/**\n\t * If all static final synthetic fields have DONT_GENERATE => hide whole class\n\t */\n\tprivate static void checkAndHideClass(ClassNode cls) {\n\t\tfor (FieldNode field : cls.getFields()) {\n\t\t\tAccessInfo af = field.getAccessFlags();\n\t\t\tif (af.isSynthetic() && af.isStatic() && af.isFinal()\n\t\t\t\t\t&& !field.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tcls.add(AFlag.DONT_GENERATE);\n\t}\n\n\tprivate static class EnumMapInfo {\n\t\tprivate final InsnArg arg;\n\t\tprivate final FieldNode mapField;\n\n\t\tpublic EnumMapInfo(InsnArg arg, FieldNode mapField) {\n\t\t\tthis.arg = arg;\n\t\t\tthis.mapField = mapField;\n\t\t}\n\n\t\tpublic InsnArg getArg() {\n\t\t\treturn arg;\n\t\t}\n\n\t\tpublic FieldNode getMapField() {\n\t\t\treturn mapField;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/GenericTypesVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.attributes.nodes.GenericInfoAttr;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.dex.visitors.typeinference.TypeInferenceVisitor;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"GenericTypesVisitor\",\n\t\tdesc = \"Fix and apply generic type info\",\n\t\trunAfter = TypeInferenceVisitor.class,\n\t\trunBefore = { CodeShrinkVisitor.class, MethodInvokeVisitor.class }\n)\npublic class GenericTypesVisitor extends AbstractVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(GenericTypesVisitor.class);\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tif (insn.getType() == InsnType.CONSTRUCTOR) {\n\t\t\t\t\tattachGenericTypesInfo(mth, (ConstructorInsn) insn);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void attachGenericTypesInfo(MethodNode mth, ConstructorInsn insn) {\n\t\ttry {\n\t\t\tRegisterArg resultArg = insn.getResult();\n\t\t\tif (resultArg == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tArgType argType = resultArg.getSVar().getCodeVar().getType();\n\t\t\tif (argType == null || argType.getGenericTypes() == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tClassNode cls = mth.root().resolveClass(insn.getClassType());\n\t\t\tif (cls != null && cls.getGenericTypeParameters().isEmpty()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tinsn.addAttr(new GenericInfoAttr(argType.getGenericTypes()));\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to attach constructor generic info\", e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/IDexTreeVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.exceptions.JadxException;\n\n/**\n * Visitor interface for traverse dex tree\n */\npublic interface IDexTreeVisitor {\n\n\t/**\n\t * Visitor short id\n\t */\n\tString getName();\n\n\t/**\n\t * Called after loading dex tree, but before visitor traversal.\n\t */\n\tvoid init(RootNode root) throws JadxException;\n\n\t/**\n\t * Visit class\n\t *\n\t * @return false for disable child methods and inner classes traversal\n\t */\n\tboolean visit(ClassNode cls) throws JadxException;\n\n\t/**\n\t * Visit method\n\t */\n\tvoid visit(MethodNode mth) throws JadxException;\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/InitCodeVariables.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.PhiInsn;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.CodeVar;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.ssa.SSATransform;\nimport jadx.core.utils.exceptions.JadxException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n@JadxVisitor(\n\t\tname = \"InitCodeVariables\",\n\t\tdesc = \"Initialize code variables\",\n\t\trunAfter = SSATransform.class\n)\npublic class InitCodeVariables extends AbstractVisitor {\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tinitCodeVars(mth);\n\t}\n\n\tpublic static void rerun(MethodNode mth) {\n\t\tfor (SSAVar sVar : mth.getSVars()) {\n\t\t\tsVar.resetTypeAndCodeVar();\n\t\t}\n\t\tinitCodeVars(mth);\n\t}\n\n\tprivate static void initCodeVars(MethodNode mth) {\n\t\tRegisterArg thisArg = mth.getThisArg();\n\t\tif (thisArg != null) {\n\t\t\tinitCodeVar(mth, thisArg);\n\t\t}\n\t\tfor (RegisterArg mthArg : mth.getArgRegs()) {\n\t\t\tinitCodeVar(mth, mthArg);\n\t\t}\n\t\tfor (SSAVar ssaVar : mth.getSVars()) {\n\t\t\tinitCodeVar(ssaVar);\n\t\t}\n\t}\n\n\tpublic static void initCodeVar(MethodNode mth, RegisterArg regArg) {\n\t\tSSAVar ssaVar = regArg.getSVar();\n\t\tif (ssaVar == null) {\n\t\t\tssaVar = mth.makeNewSVar(regArg);\n\t\t}\n\t\tinitCodeVar(ssaVar);\n\t}\n\n\tpublic static void initCodeVar(SSAVar ssaVar) {\n\t\tif (ssaVar.isCodeVarSet()) {\n\t\t\treturn;\n\t\t}\n\t\tCodeVar codeVar = new CodeVar();\n\t\tRegisterArg assignArg = ssaVar.getAssign();\n\t\tif (assignArg.contains(AFlag.THIS)) {\n\t\t\tcodeVar.setName(RegisterArg.THIS_ARG_NAME);\n\t\t\tcodeVar.setThis(true);\n\t\t}\n\t\tif (assignArg.contains(AFlag.METHOD_ARGUMENT) || assignArg.contains(AFlag.CUSTOM_DECLARE)) {\n\t\t\tcodeVar.setDeclared(true);\n\t\t}\n\n\t\tsetCodeVar(ssaVar, codeVar);\n\t}\n\n\tprivate static void setCodeVar(SSAVar ssaVar, CodeVar codeVar) {\n\t\tList<PhiInsn> phiList = ssaVar.getPhiList();\n\t\tif (!phiList.isEmpty()) {\n\t\t\tSet<SSAVar> vars = new LinkedHashSet<>();\n\t\t\tvars.add(ssaVar);\n\t\t\tcollectConnectedVars(phiList, vars);\n\t\t\tsetCodeVarType(codeVar, vars);\n\t\t\tvars.forEach(var -> {\n\t\t\t\tif (var.isCodeVarSet()) {\n\t\t\t\t\tcodeVar.mergeFlagsFrom(var.getCodeVar());\n\t\t\t\t}\n\t\t\t\tvar.setCodeVar(codeVar);\n\t\t\t});\n\t\t} else {\n\t\t\tssaVar.setCodeVar(codeVar);\n\t\t}\n\t}\n\n\tprivate static void setCodeVarType(CodeVar codeVar, Set<SSAVar> vars) {\n\t\tif (vars.size() > 1) {\n\t\t\tList<ArgType> imTypes = vars.stream()\n\t\t\t\t\t.map(SSAVar::getImmutableType)\n\t\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t\t.filter(ArgType::isTypeKnown)\n\t\t\t\t\t.distinct()\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t\tint imCount = imTypes.size();\n\t\t\tif (imCount == 1) {\n\t\t\t\tcodeVar.setType(imTypes.get(0));\n\t\t\t} else if (imCount > 1) {\n\t\t\t\tthrow new JadxRuntimeException(\"Several immutable types in one variable: \" + imTypes + \", vars: \" + vars);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void collectConnectedVars(List<PhiInsn> phiInsnList, Set<SSAVar> vars) {\n\t\tif (phiInsnList.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (PhiInsn phiInsn : phiInsnList) {\n\t\t\tSSAVar resultVar = phiInsn.getResult().getSVar();\n\t\t\tif (vars.add(resultVar)) {\n\t\t\t\tcollectConnectedVars(resultVar.getPhiList(), vars);\n\t\t\t}\n\t\t\tphiInsn.getArguments().forEach(arg -> {\n\t\t\t\tSSAVar sVar = ((RegisterArg) arg).getSVar();\n\t\t\t\tif (vars.add(sVar)) {\n\t\t\t\t\tcollectConnectedVars(sVar.getPhiList(), vars);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/InlineMethods.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.MethodInlineAttr;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.BaseInvokeNode;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.typeinference.TypeInferenceVisitor;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.exceptions.JadxException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n@JadxVisitor(\n\t\tname = \"InlineMethods\",\n\t\tdesc = \"Inline methods (previously marked in MarkMethodsForInline)\",\n\t\trunAfter = TypeInferenceVisitor.class,\n\t\trunBefore = ModVisitor.class\n)\npublic class InlineMethods extends AbstractVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(InlineMethods.class);\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tif (insn.getType() == InsnType.INVOKE) {\n\t\t\t\t\tprocessInvokeInsn(mth, block, (InvokeNode) insn);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void processInvokeInsn(MethodNode mth, BlockNode block, InvokeNode insn) {\n\t\tIMethodDetails callMthDetails = insn.get(AType.METHOD_DETAILS);\n\t\tif (!(callMthDetails instanceof MethodNode)) {\n\t\t\treturn;\n\t\t}\n\t\tMethodNode callMth = (MethodNode) callMthDetails;\n\t\ttry {\n\t\t\tMethodInlineAttr mia = MarkMethodsForInline.process(callMth);\n\t\t\tif (mia == null) {\n\t\t\t\t// method is not yet loaded => force process\n\t\t\t\tmth.addDebugComment(\"Class process forced to load method for inline: \" + callMth);\n\t\t\t\tmth.root().getProcessClasses().forceProcess(callMth.getParentClass());\n\t\t\t\t// run check again\n\t\t\t\tmia = MarkMethodsForInline.process(callMth);\n\t\t\t\tif (mia == null) {\n\t\t\t\t\tmth.addWarnComment(\"Failed to check method for inline after forced process\" + callMth);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (mia.notNeeded()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tinlineMethod(mth, callMth, mia, block, insn);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to process method for inline: \" + callMth, e);\n\t\t}\n\t}\n\n\tprivate void inlineMethod(MethodNode mth, MethodNode callMth, MethodInlineAttr mia, BlockNode block, InvokeNode insn) {\n\t\tInsnNode inlCopy = mia.getInsn().copyWithoutResult();\n\t\tif (replaceRegs(mth, callMth, mia, insn, inlCopy)) {\n\t\t\tIMethodDetails methodDetailsAttr = inlCopy.get(AType.METHOD_DETAILS);\n\t\t\t// replaceInsn replaces the attributes as well, make sure to preserve METHOD_DETAILS\n\t\t\tif (BlockUtils.replaceInsn(mth, block, insn, inlCopy)) {\n\t\t\t\tif (methodDetailsAttr != null) {\n\t\t\t\t\tinlCopy.addAttr(methodDetailsAttr);\n\t\t\t\t}\n\t\t\t\tupdateUsageInfo(mth, callMth, mia.getInsn());\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tmth.addWarnComment(\"Failed to inline method: \" + callMth);\n\t\t// undo changes to insn\n\t\tInsnRemover.unbindInsn(mth, inlCopy);\n\t\tinsn.rebindArgs();\n\t}\n\n\tprivate boolean replaceRegs(MethodNode mth, MethodNode callMth, MethodInlineAttr mia, InvokeNode insn, InsnNode inlCopy) {\n\t\ttry {\n\t\t\tif (!callMth.getMethodInfo().getArgumentsTypes().isEmpty()) {\n\t\t\t\t// remap args\n\t\t\t\tInsnArg[] regs = new InsnArg[callMth.getRegsCount()];\n\t\t\t\tint[] regNums = mia.getArgsRegNums();\n\t\t\t\tfor (int i = 0; i < regNums.length; i++) {\n\t\t\t\t\tInsnArg arg = insn.getArg(i);\n\t\t\t\t\tregs[regNums[i]] = arg;\n\t\t\t\t}\n\t\t\t\t// replace args\n\t\t\t\tList<RegisterArg> inlArgs = new ArrayList<>();\n\t\t\t\tinlCopy.getRegisterArgs(inlArgs);\n\t\t\t\tfor (RegisterArg r : inlArgs) {\n\t\t\t\t\tint regNum = r.getRegNum();\n\t\t\t\t\tif (regNum >= regs.length) {\n\t\t\t\t\t\tmth.addWarnComment(\"Unknown register number '\" + r + \"' in method call: \" + callMth);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tInsnArg repl = regs[regNum];\n\t\t\t\t\tif (repl == null) {\n\t\t\t\t\t\tmth.addWarnComment(\"Not passed register '\" + r + \"' in method call: \" + callMth);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (!inlCopy.replaceArg(r, repl.duplicate())) {\n\t\t\t\t\t\tmth.addWarnComment(\"Failed to replace arg \" + r + \" for method inline: \" + callMth);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tRegisterArg resultArg = insn.getResult();\n\t\t\tif (resultArg != null) {\n\t\t\t\tinlCopy.setResult(resultArg.duplicate());\n\t\t\t} else if (isAssignNeeded(mia.getInsn(), insn, callMth)) {\n\t\t\t\t// add a fake result to make correct java expression (see test TestGetterInlineNegative)\n\t\t\t\tinlCopy.setResult(mth.makeSyntheticRegArg(callMth.getReturnType(), \"unused\"));\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Method inline failed with exception\", e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate boolean isAssignNeeded(InsnNode inlineInsn, InvokeNode parentInsn, MethodNode callMthNode) {\n\t\tif (parentInsn.getResult() != null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (parentInsn.contains(AFlag.WRAPPED)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (inlineInsn.getType() == InsnType.IPUT) {\n\t\t\treturn false;\n\t\t}\n\t\treturn !callMthNode.isVoidReturn();\n\t}\n\n\tprivate void updateUsageInfo(MethodNode mth, MethodNode inlinedMth, InsnNode insn) {\n\t\tList<MethodNode> newUseIn = new ArrayList<>(inlinedMth.getUseIn());\n\t\tnewUseIn.remove(mth);\n\t\tinlinedMth.setUseIn(newUseIn);\n\t\tinsn.visitInsns(innerInsn -> {\n\t\t\t// TODO: share code with UsageInfoVisitor\n\t\t\tswitch (innerInsn.getType()) {\n\t\t\t\tcase INVOKE:\n\t\t\t\tcase CONSTRUCTOR:\n\t\t\t\t\tMethodInfo callMth = ((BaseInvokeNode) innerInsn).getCallMth();\n\t\t\t\t\tMethodNode callMthNode = mth.root().resolveMethod(callMth);\n\t\t\t\t\tif (callMthNode != null) {\n\t\t\t\t\t\tcallMthNode.setUseIn(ListUtils.safeReplace(new ArrayList<>(callMthNode.getUseIn()), inlinedMth, mth));\n\t\t\t\t\t\treplaceClsUsage(mth, inlinedMth, callMthNode.getParentClass());\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase IGET:\n\t\t\t\tcase IPUT:\n\t\t\t\tcase SPUT:\n\t\t\t\tcase SGET:\n\t\t\t\t\tFieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) innerInsn).getIndex();\n\t\t\t\t\tFieldNode fieldNode = mth.root().resolveField(fieldInfo);\n\t\t\t\t\tif (fieldNode != null) {\n\t\t\t\t\t\tfieldNode.setUseIn(ListUtils.safeReplace(new ArrayList<>(fieldNode.getUseIn()), inlinedMth, mth));\n\t\t\t\t\t\treplaceClsUsage(mth, inlinedMth, fieldNode.getParentClass());\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void replaceClsUsage(MethodNode mth, MethodNode inlinedMth, ClassNode parentClass) {\n\t\tparentClass.setUseInMth(ListUtils.safeReplace(parentClass.getUseInMth(), inlinedMth, mth));\n\t\tparentClass.setUseIn(ListUtils.safeReplace(parentClass.getUseIn(), inlinedMth.getParentClass(), mth.getParentClass()));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/JadxVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Annotation for describe dependencies of jadx visitors\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\npublic @interface JadxVisitor {\n\t/**\n\t * Visitor short name (identifier)\n\t */\n\tString name();\n\n\t/**\n\t * Detailed visitor description\n\t */\n\tString desc() default \"\";\n\n\t/**\n\t * This visitor must be run <b>after</b> listed visitors\n\t */\n\tClass<? extends IDexTreeVisitor>[] runAfter() default {};\n\n\t/**\n\t * This visitor must be run <b>before</b> listed visitors\n\t */\n\tClass<? extends IDexTreeVisitor>[] runBefore() default {};\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/MarkMethodsForInline.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.MethodInlineAttr;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.fixaccessmodifiers.FixAccessModifiers;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"MarkMethodsForInline\",\n\t\tdesc = \"Mark synthetic static methods for inline\",\n\t\trunAfter = {\n\t\t\t\tFixAccessModifiers.class,\n\t\t\t\tClassModifier.class\n\t\t}\n)\npublic class MarkMethodsForInline extends AbstractVisitor {\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tprocess(mth);\n\t}\n\n\t/**\n\t * @return null if method can't be analyzed (not loaded)\n\t */\n\t@Nullable\n\tpublic static MethodInlineAttr process(MethodNode mth) {\n\t\ttry {\n\t\t\tMethodInlineAttr mia = mth.get(AType.METHOD_INLINE);\n\t\t\tif (mia != null) {\n\t\t\t\treturn mia;\n\t\t\t}\n\t\t\tif (mth.contains(AFlag.METHOD_CANDIDATE_FOR_INLINE)) {\n\t\t\t\tif (mth.getBasicBlocks() == null) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tMethodInlineAttr inlined = inlineMth(mth);\n\t\t\t\tif (inlined != null) {\n\t\t\t\t\treturn inlined;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Method inline analysis failed\", e);\n\t\t}\n\t\treturn MethodInlineAttr.inlineNotNeeded(mth);\n\t}\n\n\t@Nullable\n\tprivate static MethodInlineAttr inlineMth(MethodNode mth) {\n\t\tList<InsnNode> insns = BlockUtils.collectInsnsWithLimit(mth.getBasicBlocks(), 2);\n\t\tint insnsCount = insns.size();\n\t\tif (insnsCount == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tif (insnsCount == 1) {\n\t\t\tInsnNode insn = insns.get(0);\n\t\t\tif (insn.getType() == InsnType.RETURN && insn.getArgsCount() == 1) {\n\t\t\t\t// synthetic field getter\n\t\t\t\t// set arg from 'return' instruction\n\t\t\t\tInsnArg arg = insn.getArg(0);\n\t\t\t\tif (!arg.isInsnWrap()) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\treturn addInlineAttr(mth, ((InsnWrapArg) arg).unWrapWithCopy(), true);\n\t\t\t}\n\t\t\t// method invoke\n\t\t\treturn addInlineAttr(mth, insn, false);\n\t\t}\n\t\tif (insnsCount == 2 && insns.get(1).getType() == InsnType.RETURN) {\n\t\t\tInsnNode firstInsn = insns.get(0);\n\t\t\tInsnNode retInsn = insns.get(1);\n\t\t\tif (retInsn.getArgsCount() == 0\n\t\t\t\t\t|| isSyntheticAccessPattern(mth, firstInsn, retInsn)) {\n\t\t\t\treturn addInlineAttr(mth, firstInsn, false);\n\t\t\t}\n\t\t}\n\t\t// TODO: inline field arithmetics. Disabled tests: TestAnonymousClass3a and TestAnonymousClass5\n\t\treturn null;\n\t}\n\n\tprivate static boolean isSyntheticAccessPattern(MethodNode mth, InsnNode firstInsn, InsnNode retInsn) {\n\t\tList<RegisterArg> mthRegs = mth.getArgRegs();\n\t\tswitch (firstInsn.getType()) {\n\t\t\tcase IGET:\n\t\t\t\treturn mthRegs.size() == 1\n\t\t\t\t\t\t&& retInsn.getArg(0).isSameVar(firstInsn.getResult())\n\t\t\t\t\t\t&& firstInsn.getArg(0).isSameVar(mthRegs.get(0));\n\t\t\tcase SGET:\n\t\t\t\treturn mthRegs.isEmpty()\n\t\t\t\t\t\t&& retInsn.getArg(0).isSameVar(firstInsn.getResult());\n\n\t\t\tcase IPUT:\n\t\t\t\treturn mthRegs.size() == 2\n\t\t\t\t\t\t&& retInsn.getArg(0).isSameVar(mthRegs.get(1))\n\t\t\t\t\t\t&& firstInsn.getArg(0).isSameVar(mthRegs.get(1))\n\t\t\t\t\t\t&& firstInsn.getArg(1).isSameVar(mthRegs.get(0));\n\t\t\tcase SPUT:\n\t\t\t\treturn mthRegs.size() == 1\n\t\t\t\t\t\t&& retInsn.getArg(0).isSameVar(mthRegs.get(0))\n\t\t\t\t\t\t&& firstInsn.getArg(0).isSameVar(mthRegs.get(0));\n\n\t\t\tcase INVOKE:\n\t\t\t\tif (!retInsn.getArg(0).isSameVar(firstInsn.getResult())) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn ListUtils.orderedEquals(\n\t\t\t\t\t\tmth.getArgRegs(), firstInsn.getArgList(),\n\t\t\t\t\t\t(mthArg, insnArg) -> insnArg.isSameVar(mthArg));\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate static @Nullable MethodInlineAttr addInlineAttr(MethodNode mth, InsnNode insn, boolean isCopy) {\n\t\tif (!fixVisibilityOfInlineCode(mth, insn)) {\n\t\t\tif (isCopy) {\n\t\t\t\tunbindSsaVars(insn);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tInsnNode inlInsn = isCopy ? insn : insn.copyWithoutResult();\n\t\tunbindSsaVars(inlInsn);\n\t\treturn MethodInlineAttr.markForInline(mth, inlInsn);\n\t}\n\n\tprivate static void unbindSsaVars(InsnNode insn) {\n\t\tinsn.visitArgs(arg -> {\n\t\t\tif (arg.isRegister()) {\n\t\t\t\tRegisterArg reg = (RegisterArg) arg;\n\t\t\t\tSSAVar ssaVar = reg.getSVar();\n\t\t\t\tif (ssaVar != null) {\n\t\t\t\t\tssaVar.removeUse(reg);\n\t\t\t\t\treg.resetSSAVar();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate static boolean fixVisibilityOfInlineCode(MethodNode mth, InsnNode insn) {\n\t\tint newVisFlag = AccessFlags.PUBLIC; // TODO: calculate more precisely\n\t\tInsnType insnType = insn.getType();\n\t\tif (insnType == InsnType.INVOKE) {\n\t\t\tInvokeNode invoke = (InvokeNode) insn;\n\t\t\tMethodNode callMthNode = mth.root().resolveMethod(invoke.getCallMth());\n\t\t\tif (callMthNode != null && !callMthNode.root().getArgs().isRespectBytecodeAccModifiers()) {\n\t\t\t\tFixAccessModifiers.changeVisibility(callMthNode, newVisFlag);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tif (insnType == InsnType.ONE_ARG) {\n\t\t\tInsnArg arg = insn.getArg(0);\n\t\t\tif (!arg.isInsnWrap()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn fixVisibilityOfInlineCode(mth, ((InsnWrapArg) arg).getWrapInsn());\n\t\t}\n\t\tif (insn instanceof IndexInsnNode) {\n\t\t\tObject indexObj = ((IndexInsnNode) insn).getIndex();\n\t\t\tif (indexObj instanceof FieldInfo) {\n\t\t\t\t// field access must be already fixed in ModVisitor.fixFieldUsage method\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tmth.addDebugComment(\"Can't inline method, not implemented redirect type for insn: \" + insn);\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/MethodInvokeVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.Consts;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.BaseInvokeNode;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.nodes.utils.TypeUtils;\nimport jadx.core.dex.visitors.methods.MutableMethodDetails;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.dex.visitors.typeinference.TypeCompare;\nimport jadx.core.dex.visitors.typeinference.TypeCompareEnum;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n@JadxVisitor(\n\t\tname = \"MethodInvokeVisitor\",\n\t\tdesc = \"Process additional info for method invocation (overload, vararg)\",\n\t\trunAfter = {\n\t\t\t\tCodeShrinkVisitor.class,\n\t\t\t\tModVisitor.class\n\t\t},\n\t\trunBefore = {\n\t\t\t\tSimplifyVisitor.class // run before cast remove and StringBuilder replace\n\t\t}\n)\npublic class MethodInvokeVisitor extends AbstractVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(MethodInvokeVisitor.class);\n\n\tprivate RootNode root;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tthis.root = root;\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tif (block.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tif (insn.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tinsn.visitInsns(in -> {\n\t\t\t\t\tif (in instanceof BaseInvokeNode) {\n\t\t\t\t\t\tprocessInvoke(mth, ((BaseInvokeNode) in));\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void processInvoke(MethodNode parentMth, BaseInvokeNode invokeInsn) {\n\t\tMethodInfo callMth = invokeInsn.getCallMth();\n\t\tif (callMth.getArgsCount() == 0) {\n\t\t\treturn;\n\t\t}\n\t\tIMethodDetails mthDetails = root.getMethodUtils().getMethodDetails(invokeInsn);\n\t\tif (mthDetails == null) {\n\t\t\tif (Consts.DEBUG) {\n\t\t\t\tparentMth.addDebugComment(\"Method info not found: \" + callMth);\n\t\t\t}\n\t\t\tprocessUnknown(invokeInsn);\n\t\t} else {\n\t\t\tif (mthDetails.isVarArg()) {\n\t\t\t\tArgType last = Utils.last(mthDetails.getArgTypes());\n\t\t\t\tif (last != null && last.isArray()) {\n\t\t\t\t\tinvokeInsn.add(AFlag.VARARG_CALL);\n\t\t\t\t}\n\t\t\t}\n\t\t\tprocessOverloaded(parentMth, invokeInsn, mthDetails);\n\t\t}\n\t}\n\n\tprivate void processOverloaded(MethodNode parentMth, BaseInvokeNode invokeInsn, IMethodDetails mthDetails) {\n\t\tMethodInfo callMth = invokeInsn.getCallMth();\n\t\tArgType callCls = getCallClassFromInvoke(parentMth, invokeInsn, callMth);\n\t\tList<IMethodDetails> overloadMethods = root.getMethodUtils().collectOverloadedMethods(callCls, callMth);\n\t\tif (overloadMethods.isEmpty()) {\n\t\t\t// not overloaded\n\t\t\treturn;\n\t\t}\n\n\t\t// resolve generic type variables\n\t\tMap<ArgType, ArgType> typeVarsMapping = getTypeVarsMapping(invokeInsn);\n\t\tIMethodDetails effectiveMthDetails = resolveTypeVars(mthDetails, typeVarsMapping);\n\t\tList<IMethodDetails> effectiveOverloadMethods = new ArrayList<>(overloadMethods.size() + 1);\n\t\tfor (IMethodDetails overloadMethod : overloadMethods) {\n\t\t\teffectiveOverloadMethods.add(resolveTypeVars(overloadMethod, typeVarsMapping));\n\t\t}\n\t\teffectiveOverloadMethods.add(effectiveMthDetails);\n\n\t\t// search cast types to resolve overloading\n\t\tint argsOffset = invokeInsn.getFirstArgOffset();\n\t\tList<ArgType> compilerVarTypes = collectCompilerVarTypes(invokeInsn, argsOffset);\n\t\tList<ArgType> castTypes = searchCastTypes(parentMth, effectiveMthDetails, effectiveOverloadMethods, compilerVarTypes);\n\t\tList<ArgType> resultCastTypes = expandTypes(parentMth, effectiveMthDetails, castTypes);\n\t\tapplyArgsCast(invokeInsn, argsOffset, compilerVarTypes, resultCastTypes);\n\t}\n\n\t/**\n\t * Method details not found => add cast for 'null' args\n\t */\n\tprivate void processUnknown(BaseInvokeNode invokeInsn) {\n\t\tint argsOffset = invokeInsn.getFirstArgOffset();\n\t\tList<ArgType> compilerVarTypes = collectCompilerVarTypes(invokeInsn, argsOffset);\n\t\tList<ArgType> castTypes = new ArrayList<>(compilerVarTypes);\n\t\tif (replaceUnknownTypes(castTypes, invokeInsn.getCallMth().getArgumentsTypes())) {\n\t\t\tapplyArgsCast(invokeInsn, argsOffset, compilerVarTypes, castTypes);\n\t\t}\n\t}\n\n\tprivate ArgType getCallClassFromInvoke(MethodNode parentMth, BaseInvokeNode invokeInsn, MethodInfo callMth) {\n\t\tif (invokeInsn instanceof ConstructorInsn) {\n\t\t\tConstructorInsn constrInsn = (ConstructorInsn) invokeInsn;\n\t\t\tif (constrInsn.isSuper()) {\n\t\t\t\treturn parentMth.getParentClass().getSuperClass();\n\t\t\t}\n\t\t}\n\t\tInsnArg instanceArg = invokeInsn.getInstanceArg();\n\t\tif (instanceArg != null) {\n\t\t\treturn instanceArg.getType();\n\t\t}\n\t\t// static call\n\t\treturn callMth.getDeclClass().getType();\n\t}\n\n\tprivate Map<ArgType, ArgType> getTypeVarsMapping(BaseInvokeNode invokeInsn) {\n\t\tMethodInfo callMthInfo = invokeInsn.getCallMth();\n\t\tArgType declClsType = callMthInfo.getDeclClass().getType();\n\t\tArgType callClsType = getClsCallType(invokeInsn, declClsType);\n\n\t\tTypeUtils typeUtils = root.getTypeUtils();\n\t\tMap<ArgType, ArgType> clsTypeVars = typeUtils.getTypeVariablesMapping(callClsType);\n\t\tMap<ArgType, ArgType> mthTypeVars = typeUtils.getTypeVarMappingForInvoke(invokeInsn);\n\t\treturn Utils.mergeMaps(clsTypeVars, mthTypeVars);\n\t}\n\n\tprivate ArgType getClsCallType(BaseInvokeNode invokeInsn, ArgType declClsType) {\n\t\tInsnArg instanceArg = invokeInsn.getInstanceArg();\n\t\tif (instanceArg != null) {\n\t\t\treturn instanceArg.getType();\n\t\t}\n\t\tif (invokeInsn.getType() == InsnType.CONSTRUCTOR && invokeInsn.getResult() != null) {\n\t\t\treturn invokeInsn.getResult().getType();\n\t\t}\n\t\treturn declClsType;\n\t}\n\n\tprivate void applyArgsCast(BaseInvokeNode invokeInsn, int argsOffset, List<ArgType> compilerVarTypes, List<ArgType> castTypes) {\n\t\tint argsCount = invokeInsn.getArgsCount();\n\t\tfor (int i = argsOffset; i < argsCount; i++) {\n\t\t\tInsnArg arg = invokeInsn.getArg(i);\n\t\t\tint origPos = i - argsOffset;\n\t\t\tArgType compilerType = compilerVarTypes.get(origPos);\n\t\t\tArgType castType = castTypes.get(origPos);\n\t\t\tif (castType != null) {\n\t\t\t\tif (!castType.equals(compilerType)) {\n\t\t\t\t\tif (arg.isLiteral() && compilerType.isPrimitive() && castType.isPrimitive()) {\n\t\t\t\t\t\targ.setType(castType);\n\t\t\t\t\t\targ.add(AFlag.EXPLICIT_PRIMITIVE_TYPE);\n\t\t\t\t\t} else if (InsnUtils.isWrapped(arg, InsnType.CHECK_CAST)) {\n\t\t\t\t\t\tIndexInsnNode wrapInsn = (IndexInsnNode) ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\t\t\twrapInsn.updateIndex(castType);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\t\t\t\tLOG.info(\"Insert cast for invoke insn arg: {}, insn: {}\", arg, invokeInsn);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tInsnNode castInsn = new IndexInsnNode(InsnType.CAST, castType, 1);\n\t\t\t\t\t\tcastInsn.addArg(arg);\n\t\t\t\t\t\tcastInsn.add(AFlag.EXPLICIT_CAST);\n\t\t\t\t\t\tInsnArg wrapCast = InsnArg.wrapArg(castInsn);\n\t\t\t\t\t\twrapCast.setType(castType);\n\t\t\t\t\t\tinvokeInsn.setArg(i, wrapCast);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// protect already existed cast\n\t\t\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\t\t\tif (wrapInsn.getType() == InsnType.CHECK_CAST) {\n\t\t\t\t\t\t\twrapInsn.add(AFlag.EXPLICIT_CAST);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate IMethodDetails resolveTypeVars(IMethodDetails mthDetails, Map<ArgType, ArgType> typeVarsMapping) {\n\t\tList<ArgType> argTypes = mthDetails.getArgTypes();\n\t\tint argsCount = argTypes.size();\n\t\tboolean fixed = false;\n\t\tList<ArgType> fixedArgTypes = new ArrayList<>(argsCount);\n\t\tfor (int argNum = 0; argNum < argsCount; argNum++) {\n\t\t\tArgType argType = argTypes.get(argNum);\n\t\t\tif (argType == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Null arg type in \" + mthDetails + \" at: \" + argNum + \" in: \" + argTypes);\n\t\t\t}\n\t\t\tif (argType.containsTypeVariable()) {\n\t\t\t\tArgType resolvedType = root.getTypeUtils().replaceTypeVariablesUsingMap(argType, typeVarsMapping);\n\t\t\t\tif (resolvedType == null || resolvedType.equals(argType)) {\n\t\t\t\t\t// type variables erased from method info by compiler\n\t\t\t\t\tresolvedType = mthDetails.getMethodInfo().getArgumentsTypes().get(argNum);\n\t\t\t\t}\n\t\t\t\tfixedArgTypes.add(resolvedType);\n\t\t\t\tfixed = true;\n\t\t\t} else {\n\t\t\t\tfixedArgTypes.add(argType);\n\t\t\t}\n\t\t}\n\t\tArgType returnType = mthDetails.getReturnType();\n\t\tif (returnType.containsTypeVariable()) {\n\t\t\tArgType resolvedType = root.getTypeUtils().replaceTypeVariablesUsingMap(returnType, typeVarsMapping);\n\t\t\tif (resolvedType == null || resolvedType.containsTypeVariable()) {\n\t\t\t\treturnType = mthDetails.getMethodInfo().getReturnType();\n\t\t\t\tfixed = true;\n\t\t\t}\n\t\t}\n\n\t\tif (!fixed) {\n\t\t\treturn mthDetails;\n\t\t}\n\t\tMutableMethodDetails mutableMethodDetails = new MutableMethodDetails(mthDetails);\n\t\tmutableMethodDetails.setArgTypes(fixedArgTypes);\n\t\tmutableMethodDetails.setRetType(returnType);\n\t\treturn mutableMethodDetails;\n\t}\n\n\tprivate List<ArgType> searchCastTypes(MethodNode parentMth, IMethodDetails mthDetails, List<IMethodDetails> overloadedMethods,\n\t\t\tList<ArgType> compilerVarTypes) {\n\t\t// try compiler types\n\t\tif (isOverloadResolved(mthDetails, overloadedMethods, compilerVarTypes)) {\n\t\t\treturn compilerVarTypes;\n\t\t}\n\t\tint argsCount = compilerVarTypes.size();\n\t\tList<ArgType> castTypes = new ArrayList<>(compilerVarTypes);\n\n\t\t// replace unknown types\n\t\tboolean changed = replaceUnknownTypes(castTypes, mthDetails.getArgTypes());\n\t\tif (changed && isOverloadResolved(mthDetails, overloadedMethods, castTypes)) {\n\t\t\treturn castTypes;\n\t\t}\n\n\t\t// replace generic types\n\t\tchanged = false;\n\t\tfor (int i = 0; i < argsCount; i++) {\n\t\t\tArgType castType = castTypes.get(i);\n\t\t\tArgType mthType = mthDetails.getArgTypes().get(i);\n\t\t\tif (!castType.isGeneric() && mthType.isGeneric()) {\n\t\t\t\tcastTypes.set(i, mthType);\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t\tif (changed && isOverloadResolved(mthDetails, overloadedMethods, castTypes)) {\n\t\t\treturn castTypes;\n\t\t}\n\n\t\t// if just one arg => cast will resolve\n\t\tif (argsCount == 1) {\n\t\t\treturn mthDetails.getArgTypes();\n\t\t}\n\t\tif (Consts.DEBUG_OVERLOADED_CASTS) {\n\t\t\t// TODO: try to minimize casts count\n\t\t\tparentMth.addDebugComment(\"Failed to find minimal casts for resolve overloaded methods, cast all args instead\"\n\t\t\t\t\t+ \"\\n method: \" + mthDetails\n\t\t\t\t\t+ \"\\n arg types: \" + compilerVarTypes\n\t\t\t\t\t+ \"\\n candidates:\"\n\t\t\t\t\t+ \"\\n  \" + Utils.listToString(overloadedMethods, \"\\n  \"));\n\t\t}\n\t\t// not resolved -> cast all args\n\t\treturn mthDetails.getArgTypes();\n\t}\n\n\tprivate boolean replaceUnknownTypes(List<ArgType> castTypes, List<ArgType> mthArgTypes) {\n\t\tint argsCount = castTypes.size();\n\t\tboolean changed = false;\n\t\tfor (int i = 0; i < argsCount; i++) {\n\t\t\tArgType castType = castTypes.get(i);\n\t\t\tif (!castType.isTypeKnown()) {\n\t\t\t\tArgType mthType = mthArgTypes.get(i);\n\t\t\t\tcastTypes.set(i, mthType);\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t\treturn changed;\n\t}\n\n\t/**\n\t * Use generified types if available\n\t */\n\tprivate List<ArgType> expandTypes(MethodNode parentMth, IMethodDetails methodDetails, List<ArgType> castTypes) {\n\t\tTypeCompare typeCompare = parentMth.root().getTypeCompare();\n\t\tList<ArgType> mthArgTypes = methodDetails.getArgTypes();\n\t\tint argsCount = castTypes.size();\n\t\tList<ArgType> list = new ArrayList<>(argsCount);\n\t\tfor (int i = 0; i < argsCount; i++) {\n\t\t\tArgType mthType = mthArgTypes.get(i);\n\t\t\tArgType castType = castTypes.get(i);\n\t\t\tTypeCompareEnum result = typeCompare.compareTypes(mthType, castType);\n\t\t\tif (result == TypeCompareEnum.NARROW_BY_GENERIC) {\n\t\t\t\tlist.add(mthType);\n\t\t\t} else {\n\t\t\t\tlist.add(castType);\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n\tprivate boolean isOverloadResolved(IMethodDetails expectedMthDetails, List<IMethodDetails> overloadedMethods, List<ArgType> castTypes) {\n\t\tif (overloadedMethods.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\t// TODO: search closest method, instead filtering\n\t\tList<IMethodDetails> strictMethods = filterApplicableMethods(overloadedMethods, castTypes, MethodInvokeVisitor::isStrictTypes);\n\t\tif (strictMethods.size() == 1) {\n\t\t\treturn strictMethods.get(0).equals(expectedMthDetails);\n\t\t}\n\t\tList<IMethodDetails> resolvedMethods = filterApplicableMethods(overloadedMethods, castTypes, MethodInvokeVisitor::isTypeApplicable);\n\t\tif (resolvedMethods.size() == 1) {\n\t\t\treturn resolvedMethods.get(0).equals(expectedMthDetails);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean isStrictTypes(TypeCompareEnum result) {\n\t\treturn result.isEqual();\n\t}\n\n\tprivate static boolean isTypeApplicable(TypeCompareEnum result) {\n\t\treturn result.isNarrowOrEqual() || result == TypeCompareEnum.WIDER_BY_GENERIC;\n\t}\n\n\tprivate List<IMethodDetails> filterApplicableMethods(List<IMethodDetails> methods, List<ArgType> types,\n\t\t\tFunction<TypeCompareEnum, Boolean> acceptFunction) {\n\t\tList<IMethodDetails> list = new ArrayList<>(methods.size());\n\t\tfor (IMethodDetails m : methods) {\n\t\t\tif (isMethodAcceptable(m, types, acceptFunction)) {\n\t\t\t\tlist.add(m);\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n\tprivate boolean isMethodAcceptable(IMethodDetails methodDetails, List<ArgType> types,\n\t\t\tFunction<TypeCompareEnum, Boolean> acceptFunction) {\n\t\tList<ArgType> mthTypes = methodDetails.getArgTypes();\n\t\tint argCount = mthTypes.size();\n\t\tif (argCount != types.size()) {\n\t\t\treturn false;\n\t\t}\n\t\tTypeCompare typeCompare = root.getTypeUpdate().getTypeCompare();\n\t\tfor (int i = 0; i < argCount; i++) {\n\t\t\tArgType mthType = mthTypes.get(i);\n\t\t\tArgType argType = types.get(i);\n\t\t\tTypeCompareEnum result = typeCompare.compareTypes(argType, mthType);\n\t\t\tif (!acceptFunction.apply(result)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate List<ArgType> collectCompilerVarTypes(BaseInvokeNode insn, int argOffset) {\n\t\tint argsCount = insn.getArgsCount();\n\t\tList<ArgType> result = new ArrayList<>(argsCount);\n\t\tfor (int i = argOffset; i < argsCount; i++) {\n\t\t\tInsnArg arg = insn.getArg(i);\n\t\t\tresult.add(getCompilerVarType(arg));\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Return type as seen by compiler\n\t */\n\tprivate ArgType getCompilerVarType(InsnArg arg) {\n\t\tif (arg instanceof LiteralArg) {\n\t\t\tLiteralArg literalArg = (LiteralArg) arg;\n\t\t\tArgType type = literalArg.getType();\n\t\t\tif (literalArg.getLiteral() == 0) {\n\t\t\t\tif (type.isObject() || type.isArray()) {\n\t\t\t\t\t// null\n\t\t\t\t\treturn ArgType.UNKNOWN_OBJECT;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (type.isPrimitive() && !arg.contains(AFlag.EXPLICIT_PRIMITIVE_TYPE)) {\n\t\t\t\treturn ArgType.INT;\n\t\t\t}\n\t\t\treturn arg.getType();\n\t\t}\n\t\tif (arg instanceof RegisterArg) {\n\t\t\treturn arg.getType();\n\t\t}\n\t\tif (arg instanceof InsnWrapArg) {\n\t\t\tInsnWrapArg wrapArg = (InsnWrapArg) arg;\n\t\t\treturn getInsnCompilerType(arg, wrapArg.getWrapInsn());\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Unknown var type for: \" + arg);\n\t}\n\n\tprivate static ArgType getInsnCompilerType(InsnArg arg, InsnNode insn) {\n\t\tswitch (insn.getType()) {\n\t\t\tcase CAST:\n\t\t\tcase CHECK_CAST:\n\t\t\t\treturn ((IndexInsnNode) insn).getIndexAsType();\n\n\t\t\tdefault:\n\t\t\t\tif (insn.getResult() != null) {\n\t\t\t\t\treturn insn.getResult().getType();\n\t\t\t\t}\n\t\t\t\treturn arg.getType();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/MethodThrowsVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.types.ExceptionsAttr;\nimport jadx.core.Consts;\nimport jadx.core.clsp.ClspClass;\nimport jadx.core.clsp.ClspMethod;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.MethodThrowsAttr;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.trycatch.CatchAttr;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.dex.visitors.regions.RegionMakerVisitor;\nimport jadx.core.dex.visitors.typeinference.TypeCompare;\nimport jadx.core.dex.visitors.typeinference.TypeCompareEnum;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"MethodThrowsVisitor\",\n\t\tdesc = \"Scan methods to collect thrown exceptions\",\n\t\trunAfter = {\n\t\t\t\tRegionMakerVisitor.class // Run after RegionMakerVisitor to ignore throw instructions of synchronized regions\n\t\t}\n)\npublic class MethodThrowsVisitor extends AbstractVisitor {\n\n\tprivate enum ExceptionType {\n\t\tTHROWS_REQUIRED, RUNTIME, UNKNOWN_TYPE, NO_EXCEPTION\n\t}\n\n\tprivate RootNode root;\n\n\t@Override\n\tpublic void init(RootNode root) throws JadxException {\n\t\tthis.root = root;\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tMethodThrowsAttr attr = mth.get(AType.METHOD_THROWS);\n\t\tif (attr == null) {\n\t\t\tattr = new MethodThrowsAttr(new HashSet<>());\n\t\t\tmth.addAttr(attr);\n\t\t}\n\t\tif (!attr.isVisited()) {\n\t\t\tattr.setVisited(true);\n\t\t\tprocessInstructions(mth);\n\t\t}\n\n\t\tList<ArgType> invalid = new ArrayList<>();\n\t\tExceptionsAttr exceptions = mth.get(JadxAttrType.EXCEPTIONS);\n\t\tif (exceptions != null && !exceptions.getList().isEmpty()) {\n\t\t\tfor (String throwsTypeStr : exceptions.getList()) {\n\t\t\t\tArgType excType = ArgType.object(throwsTypeStr);\n\t\t\t\tif (validateException(excType) == ExceptionType.NO_EXCEPTION) {\n\t\t\t\t\tinvalid.add(excType);\n\t\t\t\t} else {\n\t\t\t\t\tattr.getList().add(excType.getObject());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!invalid.isEmpty()) {\n\t\t\tmth.addWarnComment(\"Byte code manipulation detected: skipped illegal throws declarations: \" + invalid);\n\t\t}\n\t\tmergeExceptions(attr.getList());\n\t}\n\n\tprivate void mergeExceptions(Set<String> excSet) {\n\t\tif (excSet.contains(Consts.CLASS_EXCEPTION)) {\n\t\t\texcSet.removeIf(e -> !e.equals(Consts.CLASS_EXCEPTION));\n\t\t\treturn;\n\t\t}\n\t\tif (excSet.contains(Consts.CLASS_THROWABLE)) {\n\t\t\texcSet.removeIf(e -> !e.equals(Consts.CLASS_THROWABLE));\n\t\t\treturn;\n\t\t}\n\t\tList<String> toRemove = new ArrayList<>();\n\t\tfor (String ex1 : excSet) {\n\t\t\tfor (String ex2 : excSet) {\n\t\t\t\tif (ex1.equals(ex2)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (isBaseException(ex1, ex2)) {\n\t\t\t\t\ttoRemove.add(ex1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttoRemove.forEach(excSet::remove);\n\t}\n\n\tprivate void processInstructions(MethodNode mth) {\n\t\tif (mth.isNoCode() || mth.getBasicBlocks() == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tblocks: for (final BlockNode block : mth.getBasicBlocks()) {\n\t\t\t\t// Skip e.g. throw instructions of synchronized regions\n\t\t\t\tboolean skipExceptions = block.contains(AFlag.REMOVE) || block.contains(AFlag.DONT_GENERATE);\n\t\t\t\tSet<String> excludedExceptions = new HashSet<>();\n\t\t\t\tCatchAttr catchAttr = block.get(AType.EXC_CATCH);\n\t\t\t\tif (catchAttr != null) {\n\t\t\t\t\tfor (ExceptionHandler handler : catchAttr.getHandlers()) {\n\t\t\t\t\t\tif (handler.isCatchAll()) {\n\t\t\t\t\t\t\tcontinue blocks;\n\t\t\t\t\t\t}\n\t\t\t\t\t\texcludedExceptions.add(handler.getArgType().toString());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (final InsnNode insn : block.getInstructions()) {\n\t\t\t\t\tcheckInsn(mth, insn, excludedExceptions, skipExceptions);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Failed to analyze thrown exceptions\", e);\n\t\t}\n\t}\n\n\tprivate void checkInsn(MethodNode mth, InsnNode insn, Set<String> excludedExceptions, boolean skipExceptions) throws JadxException {\n\t\tif (!skipExceptions && insn.getType() == InsnType.THROW && !insn.contains(AFlag.DONT_GENERATE)) {\n\t\t\tInsnArg throwArg = insn.getArg(0);\n\t\t\tif (throwArg instanceof RegisterArg) {\n\t\t\t\tRegisterArg regArg = (RegisterArg) throwArg;\n\t\t\t\tArgType exceptionType = regArg.getType();\n\t\t\t\tif (exceptionType.equals(ArgType.THROWABLE)) {\n\n\t\t\t\t\tInsnNode assignInsn = regArg.getAssignInsn();\n\t\t\t\t\tif (assignInsn != null\n\t\t\t\t\t\t\t&& assignInsn.getType() == InsnType.MOVE_EXCEPTION\n\t\t\t\t\t\t\t&& assignInsn.getResult().contains(AFlag.CUSTOM_DECLARE)) {\n\t\t\t\t\t\t// arg variable is from catch statement, ignore Throwable rethrow\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tvisitThrows(mth, exceptionType, excludedExceptions);\n\t\t\t} else {\n\t\t\t\tif (throwArg instanceof InsnWrapArg) {\n\t\t\t\t\tInsnWrapArg insnWrapArg = (InsnWrapArg) throwArg;\n\t\t\t\t\tArgType exceptionType = insnWrapArg.getType();\n\t\t\t\t\tvisitThrows(mth, exceptionType, excludedExceptions);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (insn.getType() == InsnType.INVOKE) {\n\t\t\tInvokeNode invokeNode = (InvokeNode) insn;\n\t\t\tMethodInfo callMth = invokeNode.getCallMth();\n\t\t\tString signature = callMth.makeSignature(true);\n\t\t\tClassInfo classInfo = callMth.getDeclClass();\n\n\t\t\tClassNode classNode = root.resolveClass(classInfo);\n\t\t\tif (classNode != null) {\n\t\t\t\tMethodNode cMth = searchOverriddenMethod(classNode, callMth, signature);\n\t\t\t\tif (cMth == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tvisit(cMth);\n\t\t\t\tMethodThrowsAttr cAttr = cMth.get(AType.METHOD_THROWS);\n\t\t\t\tMethodThrowsAttr attr = mth.get(AType.METHOD_THROWS);\n\t\t\t\tif (attr != null && cAttr != null && !cAttr.getList().isEmpty()) {\n\t\t\t\t\tfor (String argTypeStr : cAttr.getList()) {\n\t\t\t\t\t\tvisitThrows(mth, ArgType.object(argTypeStr), excludedExceptions);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tClspClass clsDetails = root.getClsp().getClsDetails(classInfo.getType());\n\t\t\t\tif (clsDetails != null) {\n\t\t\t\t\tClspMethod cMth = searchOverriddenMethod(clsDetails, signature);\n\t\t\t\t\tif (cMth != null && cMth.getThrows() != null && !cMth.getThrows().isEmpty()) {\n\t\t\t\t\t\tMethodThrowsAttr attr = mth.get(AType.METHOD_THROWS);\n\t\t\t\t\t\tif (attr != null) {\n\t\t\t\t\t\t\tfor (ArgType argType : cMth.getThrows()) {\n\t\t\t\t\t\t\t\tvisitThrows(mth, argType, excludedExceptions);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void visitThrows(MethodNode mth, ArgType excType, Set<String> excludedExceptions) {\n\t\tif (excType.isTypeKnown() && isThrowsRequired(mth, excType)) {\n\t\t\tfor (String excludedException : excludedExceptions) {\n\t\t\t\tif (isBaseException(excType.getObject(), excludedException)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmth.get(AType.METHOD_THROWS).getList().add(excType.getObject());\n\t\t}\n\t}\n\n\tprivate boolean isThrowsRequired(MethodNode mth, ArgType type) {\n\t\tExceptionType result = validateException(type);\n\t\tif (result == ExceptionType.UNKNOWN_TYPE) {\n\t\t\tmth.addInfoComment(\"Thrown type has an unknown type hierarchy: \" + type);\n\t\t\treturn true; // assume an exception\n\t\t}\n\t\treturn result == ExceptionType.THROWS_REQUIRED;\n\t}\n\n\tprivate ExceptionType validateException(ArgType clsType) {\n\t\tif (clsType == null || clsType.equals(ArgType.OBJECT)) {\n\t\t\treturn ExceptionType.NO_EXCEPTION;\n\t\t}\n\t\tif (!clsType.isTypeKnown() || !root.getClsp().isClsKnown(clsType.getObject())) {\n\t\t\treturn ExceptionType.UNKNOWN_TYPE;\n\t\t}\n\t\tif (isImplements(clsType, ArgType.RUNTIME_EXCEPTION) || isImplements(clsType, ArgType.ERROR)) {\n\t\t\treturn ExceptionType.RUNTIME;\n\t\t}\n\t\tif (isImplements(clsType, ArgType.THROWABLE) || isImplements(clsType, ArgType.EXCEPTION)) {\n\t\t\treturn ExceptionType.THROWS_REQUIRED;\n\t\t}\n\t\treturn ExceptionType.NO_EXCEPTION;\n\t}\n\n\t/**\n\t * @return is 'possibleParent' an exception class of 'exception'\n\t */\n\tprivate boolean isBaseException(String exception, String possibleParent) {\n\t\tif (exception.equals(possibleParent)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn root.getClsp().isImplements(exception, possibleParent);\n\t}\n\n\tprivate boolean isImplements(ArgType type, ArgType baseType) {\n\t\tif (type.equals(baseType)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn root.getClsp().isImplements(type.getObject(), baseType.getObject());\n\t}\n\n\tprivate @Nullable MethodNode searchOverriddenMethod(ClassNode cls, MethodInfo mth, String signature) {\n\t\t// search by exact full signature (with return value) to fight obfuscation (see test\n\t\t// 'TestOverrideWithSameName')\n\t\tString shortId = mth.getShortId();\n\t\tfor (MethodNode supMth : cls.getMethods()) {\n\t\t\tif (supMth.getMethodInfo().getShortId().equals(shortId)) {\n\t\t\t\treturn supMth;\n\t\t\t}\n\t\t}\n\t\t// search by signature without return value and check if return value is wider type\n\t\tfor (MethodNode supMth : cls.getMethods()) {\n\t\t\tif (supMth.getMethodInfo().getShortId().startsWith(signature) && !supMth.getAccessFlags().isStatic()) {\n\t\t\t\tTypeCompare typeCompare = cls.root().getTypeCompare();\n\t\t\t\tArgType supRetType = supMth.getMethodInfo().getReturnType();\n\t\t\t\tArgType mthRetType = mth.getReturnType();\n\t\t\t\tTypeCompareEnum res = typeCompare.compareTypes(supRetType, mthRetType);\n\t\t\t\tif (res.isWider()) {\n\t\t\t\t\treturn supMth;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate ClspMethod searchOverriddenMethod(ClspClass clsDetails, String signature) {\n\t\tMap<String, ClspMethod> methodsMap = clsDetails.getMethodsMap();\n\t\tfor (Map.Entry<String, ClspMethod> entry : methodsMap.entrySet()) {\n\t\t\tString mthShortId = entry.getKey();\n\t\t\t// do not check full signature, classpath methods can be trusted\n\t\t\t// i.e. doesn't contain methods with same signature in one class\n\t\t\tif (mthShortId.startsWith(signature)) {\n\t\t\t\treturn entry.getValue();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/MethodVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.function.Consumer;\n\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.exceptions.JadxException;\n\npublic class MethodVisitor extends AbstractVisitor {\n\n\tprivate final String name;\n\tprivate final Consumer<MethodNode> visitor;\n\n\tpublic MethodVisitor(String name, Consumer<MethodNode> visitor) {\n\t\tthis.name = name;\n\t\tthis.visitor = visitor;\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tvisitor.accept(mth);\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.input.data.annotations.AnnotationVisibility;\nimport jadx.api.plugins.input.data.annotations.EncodedType;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationMethodParamsAttr;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationsAttr;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.AttrNode;\nimport jadx.core.dex.attributes.nodes.SkipMethodArgsAttr;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.instructions.ArithNode;\nimport jadx.core.dex.instructions.ConstClassNode;\nimport jadx.core.dex.instructions.ConstStringNode;\nimport jadx.core.dex.instructions.FillArrayInsn;\nimport jadx.core.dex.instructions.FilledNewArrayNode;\nimport jadx.core.dex.instructions.IfNode;\nimport jadx.core.dex.instructions.IfOp;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.NewArrayNode;\nimport jadx.core.dex.instructions.SwitchInsn;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.NamedArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.instructions.mods.TernaryInsn;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.IFieldInfoRef;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.conditions.IfCondition;\nimport jadx.core.dex.trycatch.ExcHandlerAttr;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.dex.visitors.regions.variables.ProcessVariables;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.dex.visitors.typeinference.TypeCompareEnum;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.exceptions.JadxException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.utils.BlockUtils.replaceInsn;\nimport static jadx.core.utils.ListUtils.allMatch;\n\n/**\n * Visitor for modify method instructions\n * (remove, replace, process exception handlers)\n */\n@JadxVisitor(\n\t\tname = \"ModVisitor\",\n\t\tdesc = \"Modify method instructions\",\n\t\trunBefore = {\n\t\t\t\tCodeShrinkVisitor.class,\n\t\t\t\tProcessVariables.class\n\t\t}\n)\npublic class ModVisitor extends AbstractVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ModVisitor.class);\n\n\tprivate static final long DOUBLE_TO_BITS = Double.doubleToLongBits(1);\n\tprivate static final long FLOAT_TO_BITS = Float.floatToIntBits(1);\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) throws JadxException {\n\t\treplaceConstInAnnotations(cls);\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tInsnRemover remover = new InsnRemover(mth);\n\t\treplaceStep(mth, remover);\n\t\tremoveStep(mth, remover);\n\t\titerativeRemoveStep(mth);\n\t}\n\n\tprivate static void replaceStep(MethodNode mth, InsnRemover remover) {\n\t\tClassNode parentClass = mth.getParentClass();\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tremover.setBlock(block);\n\t\t\tList<InsnNode> insnsList = block.getInstructions();\n\t\t\tint size = insnsList.size();\n\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\tInsnNode insn = insnsList.get(i);\n\t\t\t\tswitch (insn.getType()) {\n\t\t\t\t\tcase CONSTRUCTOR:\n\t\t\t\t\t\tprocessAnonymousConstructor(mth, ((ConstructorInsn) insn));\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase CONST:\n\t\t\t\t\tcase CONST_STR:\n\t\t\t\t\tcase CONST_CLASS:\n\t\t\t\t\t\treplaceConst(mth, parentClass, block, i, insn);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase SWITCH:\n\t\t\t\t\t\treplaceConstKeys(mth, parentClass, (SwitchInsn) insn);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase NEW_ARRAY:\n\t\t\t\t\t\t// replace with filled array if 'fill-array' is next instruction\n\t\t\t\t\t\tNewArrayNode newArrInsn = (NewArrayNode) insn;\n\t\t\t\t\t\tInsnNode nextInsn = getFirstUseSkipMove(insn.getResult());\n\t\t\t\t\t\tif (nextInsn != null && nextInsn.getType() == InsnType.FILL_ARRAY) {\n\t\t\t\t\t\t\tFillArrayInsn fillArrInsn = (FillArrayInsn) nextInsn;\n\t\t\t\t\t\t\tif (checkArrSizes(mth, newArrInsn, fillArrInsn)) {\n\t\t\t\t\t\t\t\tInsnNode filledArr = makeFilledArrayInsn(mth, newArrInsn, fillArrInsn);\n\t\t\t\t\t\t\t\treplaceInsn(mth, block, i, filledArr);\n\t\t\t\t\t\t\t\tremover.addAndUnbind(nextInsn);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase MOVE_EXCEPTION:\n\t\t\t\t\t\tprocessMoveException(mth, block, insn, remover);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase ARITH:\n\t\t\t\t\t\tprocessArith(mth, parentClass, (ArithNode) insn);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase CMP_L:\n\t\t\t\t\tcase CMP_G:\n\t\t\t\t\t\tinlineCMPInsns(mth, block, i, insn, remover);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase CHECK_CAST:\n\t\t\t\t\t\tremoveCheckCast(mth, block, i, (IndexInsnNode) insn);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase CAST:\n\t\t\t\t\t\tfixPrimitiveCast(mth, block, i, insn);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase IPUT:\n\t\t\t\t\tcase IGET:\n\t\t\t\t\t\tfixFieldUsage(mth, (IndexInsnNode) insn);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tremover.perform();\n\t\t}\n\t}\n\n\t/**\n\t * If field is not visible from use site => cast to origin class\n\t */\n\tprivate static void fixFieldUsage(MethodNode mth, IndexInsnNode insn) {\n\t\tInsnArg instanceArg = insn.getArg(insn.getType() == InsnType.IGET ? 0 : 1);\n\t\tif (instanceArg.contains(AFlag.SUPER)) {\n\t\t\treturn;\n\t\t}\n\t\tif (instanceArg.isInsnWrap() && ((InsnWrapArg) instanceArg).getWrapInsn().getType() == InsnType.CAST) {\n\t\t\treturn;\n\t\t}\n\t\tFieldInfo fieldInfo = (FieldInfo) insn.getIndex();\n\t\tArgType clsType = fieldInfo.getDeclClass().getType();\n\t\tArgType instanceType = instanceArg.getType();\n\t\tif (Objects.equals(clsType, instanceType)) {\n\t\t\t// cast not needed\n\t\t\treturn;\n\t\t}\n\n\t\tFieldNode fieldNode = mth.root().resolveField(fieldInfo);\n\t\tif (fieldNode == null) {\n\t\t\t// unknown field\n\t\t\tTypeCompareEnum result = mth.root().getTypeCompare().compareTypes(instanceType, clsType);\n\t\t\tif (result.isEqual() || (result == TypeCompareEnum.NARROW_BY_GENERIC && !instanceType.isGenericType())) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (isFieldVisibleInMethod(fieldNode, mth)) {\n\t\t\treturn;\n\t\t}\n\t\t// insert cast\n\t\tIndexInsnNode castInsn = new IndexInsnNode(InsnType.CAST, clsType, 1);\n\t\tcastInsn.addArg(instanceArg.duplicate());\n\t\tcastInsn.add(AFlag.SYNTHETIC);\n\t\tcastInsn.add(AFlag.EXPLICIT_CAST);\n\n\t\tInsnArg castArg = InsnArg.wrapInsnIntoArg(castInsn);\n\t\tcastArg.setType(clsType);\n\t\tinsn.replaceArg(instanceArg, castArg);\n\t\tInsnRemover.unbindArgUsage(mth, instanceArg);\n\t}\n\n\tprivate static boolean isFieldVisibleInMethod(FieldNode field, MethodNode mth) {\n\t\tAccessInfo accessFlags = field.getAccessFlags();\n\t\tif (accessFlags.isPublic()) {\n\t\t\treturn true;\n\t\t}\n\t\tClassNode useCls = mth.getParentClass();\n\t\tClassNode fieldCls = field.getParentClass();\n\t\tboolean sameScope = Objects.equals(useCls, fieldCls) && !mth.getAccessFlags().isStatic();\n\t\tif (sameScope) {\n\t\t\treturn true;\n\t\t}\n\t\tif (accessFlags.isPrivate()) {\n\t\t\treturn false;\n\t\t}\n\t\t// package-private or protected\n\t\tif (Objects.equals(useCls.getClassInfo().getPackage(), fieldCls.getClassInfo().getPackage())) {\n\t\t\t// same package\n\t\t\treturn true;\n\t\t}\n\t\tif (accessFlags.isPackagePrivate()) {\n\t\t\treturn false;\n\t\t}\n\t\t// protected\n\t\tTypeCompareEnum result = mth.root().getTypeCompare().compareTypes(useCls, fieldCls);\n\t\treturn result == TypeCompareEnum.NARROW; // true if use class is subclass of field class\n\t}\n\n\tprivate static void replaceConstKeys(MethodNode mth, ClassNode parentClass, SwitchInsn insn) {\n\t\tint[] keys = insn.getKeys();\n\t\tint len = keys.length;\n\t\tfor (int k = 0; k < len; k++) {\n\t\t\tIFieldInfoRef f = parentClass.getConstField(keys[k]);\n\t\t\tif (f != null) {\n\t\t\t\tinsn.modifyKey(k, f);\n\t\t\t\taddFieldUsage(f, mth);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void fixPrimitiveCast(MethodNode mth, BlockNode block, int i, InsnNode insn) {\n\t\t// replace boolean to (byte/char/short/long/double/float) cast with ternary\n\t\tInsnArg castArg = insn.getArg(0);\n\t\tif (castArg.getType() == ArgType.BOOLEAN) {\n\t\t\tArgType type = insn.getResult().getType();\n\t\t\tif (type.isPrimitive()) {\n\t\t\t\tTernaryInsn ternary = makeBooleanConvertInsn(insn.getResult(), castArg, type);\n\t\t\t\treplaceInsn(mth, block, i, ternary);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static TernaryInsn makeBooleanConvertInsn(RegisterArg result, InsnArg castArg, ArgType type) {\n\t\tInsnArg zero = LiteralArg.make(0, type);\n\t\tlong litVal = 1;\n\t\tif (type == ArgType.DOUBLE) {\n\t\t\tlitVal = DOUBLE_TO_BITS;\n\t\t} else if (type == ArgType.FLOAT) {\n\t\t\tlitVal = FLOAT_TO_BITS;\n\t\t}\n\t\tInsnArg one = LiteralArg.make(litVal, type);\n\n\t\tIfNode ifNode = new IfNode(IfOp.EQ, -1, castArg, LiteralArg.litTrue());\n\t\tIfCondition condition = IfCondition.fromIfNode(ifNode);\n\t\treturn new TernaryInsn(condition, result, one, zero);\n\t}\n\n\tprivate void replaceConstInAnnotations(ClassNode cls) {\n\t\tif (cls.root().getArgs().isReplaceConsts()) {\n\t\t\treplaceConstsInAnnotationForAttrNode(cls, cls);\n\t\t\tcls.getFields().forEach(f -> replaceConstsInAnnotationForAttrNode(cls, f));\n\t\t\tcls.getMethods().forEach((m) -> {\n\t\t\t\treplaceConstsInAnnotationForAttrNode(cls, m);\n\t\t\t\treplaceConstsInAnnotationForMethodParamsAttr(cls, m);\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate void replaceConstsInAnnotationForMethodParamsAttr(ClassNode cls, MethodNode m) {\n\t\tAnnotationMethodParamsAttr paramsAnnotation = m.get(JadxAttrType.ANNOTATION_MTH_PARAMETERS);\n\t\tif (paramsAnnotation == null) {\n\t\t\treturn;\n\t\t}\n\t\tparamsAnnotation.getParamList().forEach(annotationsList -> replaceConstsInAnnotationsAttr(cls, annotationsList));\n\t}\n\n\tprivate void replaceConstsInAnnotationForAttrNode(ClassNode parentCls, AttrNode attrNode) {\n\t\tAnnotationsAttr annotationsList = attrNode.get(JadxAttrType.ANNOTATION_LIST);\n\t\treplaceConstsInAnnotationsAttr(parentCls, annotationsList);\n\t}\n\n\tprivate void replaceConstsInAnnotationsAttr(ClassNode parentCls, AnnotationsAttr annotationsList) {\n\t\tif (annotationsList == null) {\n\t\t\treturn;\n\t\t}\n\t\tfor (IAnnotation annotation : annotationsList.getAll()) {\n\t\t\tif (annotation.getVisibility() == AnnotationVisibility.SYSTEM) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (Map.Entry<String, EncodedValue> entry : annotation.getValues().entrySet()) {\n\t\t\t\tentry.setValue(replaceConstValue(parentCls, entry.getValue()));\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate EncodedValue replaceConstValue(ClassNode parentCls, EncodedValue encodedValue) {\n\t\tif (encodedValue.getType() == EncodedType.ENCODED_ANNOTATION) {\n\t\t\tIAnnotation annotation = (IAnnotation) encodedValue.getValue();\n\t\t\tfor (Map.Entry<String, EncodedValue> entry : annotation.getValues().entrySet()) {\n\t\t\t\tentry.setValue(replaceConstValue(parentCls, entry.getValue()));\n\t\t\t}\n\t\t\treturn encodedValue;\n\t\t}\n\t\tif (encodedValue.getType() == EncodedType.ENCODED_ARRAY) {\n\t\t\tList<EncodedValue> listVal = (List<EncodedValue>) encodedValue.getValue();\n\t\t\tif (!listVal.isEmpty()) {\n\t\t\t\tlistVal.replaceAll(v -> replaceConstValue(parentCls, v));\n\t\t\t}\n\t\t\treturn new EncodedValue(EncodedType.ENCODED_ARRAY, listVal);\n\t\t}\n\t\tIFieldInfoRef constField = parentCls.getConstField(encodedValue.getValue());\n\t\tif (constField != null) {\n\t\t\treturn new EncodedValue(EncodedType.ENCODED_FIELD, constField.getFieldInfo());\n\t\t}\n\t\treturn encodedValue;\n\t}\n\n\tprivate static void replaceConst(MethodNode mth, ClassNode parentClass, BlockNode block, int i, InsnNode insn) {\n\t\tIFieldInfoRef f;\n\t\tif (insn.getType() == InsnType.CONST_STR) {\n\t\t\tString s = ((ConstStringNode) insn).getString();\n\t\t\tf = parentClass.getConstField(s);\n\t\t} else if (insn.getType() == InsnType.CONST_CLASS) {\n\t\t\tArgType t = ((ConstClassNode) insn).getClsType();\n\t\t\tf = parentClass.getConstField(t);\n\t\t} else {\n\t\t\tf = parentClass.getConstFieldByLiteralArg((LiteralArg) insn.getArg(0));\n\t\t}\n\t\tif (f != null) {\n\t\t\tInsnNode inode = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);\n\t\t\tinode.setResult(insn.getResult());\n\t\t\treplaceInsn(mth, block, i, inode);\n\t\t\taddFieldUsage(f, mth);\n\t\t}\n\t}\n\n\tprivate static void processArith(MethodNode mth, ClassNode parentClass, ArithNode arithNode) {\n\t\tif (arithNode.getArgsCount() != 2) {\n\t\t\tthrow new JadxRuntimeException(\"Invalid args count in insn: \" + arithNode);\n\t\t}\n\t\tInsnArg litArg = arithNode.getArg(1);\n\t\tif (litArg.isLiteral()) {\n\t\t\tIFieldInfoRef f = parentClass.getConstFieldByLiteralArg((LiteralArg) litArg);\n\t\t\tif (f != null) {\n\t\t\t\tInsnNode fGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);\n\t\t\t\tif (arithNode.replaceArg(litArg, InsnArg.wrapArg(fGet))) {\n\t\t\t\t\taddFieldUsage(f, mth);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Inline CMP instructions into 'if' to help conditions merging\n\t */\n\tprivate static void inlineCMPInsns(MethodNode mth, BlockNode block, int i, InsnNode insn, InsnRemover remover) {\n\t\tRegisterArg resArg = insn.getResult();\n\t\tList<RegisterArg> useList = resArg.getSVar().getUseList();\n\t\tif (allMatch(useList, use -> InsnUtils.isInsnType(use.getParentInsn(), InsnType.IF))) {\n\t\t\tfor (RegisterArg useArg : new ArrayList<>(useList)) {\n\t\t\t\tInsnNode useInsn = useArg.getParentInsn();\n\t\t\t\tif (useInsn != null) {\n\t\t\t\t\tInsnArg wrapArg = InsnArg.wrapInsnIntoArg(insn.copyWithoutResult());\n\t\t\t\t\tif (!useInsn.replaceArg(useArg, wrapArg)) {\n\t\t\t\t\t\tmth.addWarnComment(\"Failed to inline CMP insn: \" + insn + \" into \" + useInsn);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tremover.addAndUnbind(insn);\n\t\t}\n\t}\n\n\tprivate static boolean checkArrSizes(MethodNode mth, NewArrayNode newArrInsn, FillArrayInsn fillArrInsn) {\n\t\tint dataSize = fillArrInsn.getSize();\n\t\tInsnArg arrSizeArg = newArrInsn.getArg(0);\n\t\tObject value = InsnUtils.getConstValueByArg(mth.root(), arrSizeArg);\n\t\tif (value instanceof LiteralArg) {\n\t\t\tlong literal = ((LiteralArg) value).getLiteral();\n\t\t\treturn dataSize == (int) literal;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static void removeCheckCast(MethodNode mth, BlockNode block, int i, IndexInsnNode insn) {\n\t\tInsnArg castArg = insn.getArg(0);\n\t\tif (castArg.isZeroLiteral()) {\n\t\t\t// always keep cast for 'null'\n\t\t\tinsn.add(AFlag.EXPLICIT_CAST);\n\t\t\treturn;\n\t\t}\n\t\tArgType castType = (ArgType) insn.getIndex();\n\t\tif (!ArgType.isCastNeeded(mth.root(), castArg.getType(), castType)) {\n\t\t\tRegisterArg result = insn.getResult();\n\t\t\tresult.setType(castArg.getType());\n\n\t\t\tInsnNode move = new InsnNode(InsnType.MOVE, 1);\n\t\t\tmove.setResult(result);\n\t\t\tmove.addArg(castArg);\n\t\t\treplaceInsn(mth, block, i, move);\n\t\t\treturn;\n\t\t}\n\t\tInsnNode prevCast = isCastDuplicate(insn);\n\t\tif (prevCast != null) {\n\t\t\t// replace previous cast with move\n\t\t\tInsnNode move = new InsnNode(InsnType.MOVE, 1);\n\t\t\tmove.setResult(prevCast.getResult());\n\t\t\tmove.addArg(prevCast.getArg(0));\n\t\t\treplaceInsn(mth, block, prevCast, move);\n\t\t}\n\t}\n\n\tprivate static @Nullable InsnNode isCastDuplicate(IndexInsnNode castInsn) {\n\t\tInsnArg arg = castInsn.getArg(0);\n\t\tif (arg.isRegister()) {\n\t\t\tSSAVar sVar = ((RegisterArg) arg).getSVar();\n\t\t\tif (sVar != null && sVar.getUseCount() == 1 && !sVar.isUsedInPhi()) {\n\t\t\t\tInsnNode assignInsn = sVar.getAssign().getParentInsn();\n\t\t\t\tif (assignInsn != null && assignInsn.getType() == InsnType.CHECK_CAST) {\n\t\t\t\t\tArgType assignCastType = (ArgType) ((IndexInsnNode) assignInsn).getIndex();\n\t\t\t\t\tif (assignCastType.equals(castInsn.getIndex())) {\n\t\t\t\t\t\treturn assignInsn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Remove unnecessary instructions\n\t */\n\tprivate static void removeStep(MethodNode mth, InsnRemover remover) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tremover.setBlock(block);\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tswitch (insn.getType()) {\n\t\t\t\t\tcase NOP:\n\t\t\t\t\tcase GOTO:\n\t\t\t\t\tcase NEW_INSTANCE:\n\t\t\t\t\t\tremover.addAndUnbind(insn);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif (insn.contains(AFlag.REMOVE)) {\n\t\t\t\t\t\t\tremover.addAndUnbind(insn);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tremover.perform();\n\t\t}\n\t}\n\n\tprivate static void iterativeRemoveStep(MethodNode mth) {\n\t\tboolean changed;\n\t\tdo {\n\t\t\tchanged = false;\n\t\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\t\tif (insn.getType() == InsnType.MOVE\n\t\t\t\t\t\t\t&& insn.isAttrStorageEmpty()\n\t\t\t\t\t\t\t&& isResultArgNotUsed(insn)) {\n\t\t\t\t\t\tInsnRemover.remove(mth, block, insn);\n\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} while (changed);\n\t}\n\n\tprivate static boolean isResultArgNotUsed(InsnNode insn) {\n\t\tRegisterArg result = insn.getResult();\n\t\tif (result != null) {\n\t\t\tSSAVar ssaVar = result.getSVar();\n\t\t\treturn ssaVar.getUseCount() == 0;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * For args in anonymous constructor invoke apply:\n\t * - forbid inline into constructor call\n\t * - make variables final (compiler require this implicitly)\n\t */\n\tprivate static void processAnonymousConstructor(MethodNode mth, ConstructorInsn co) {\n\t\tIMethodDetails callMthDetails = mth.root().getMethodUtils().getMethodDetails(co);\n\t\tif (!(callMthDetails instanceof MethodNode)) {\n\t\t\treturn;\n\t\t}\n\t\tMethodNode callMth = (MethodNode) callMthDetails;\n\t\tif (!callMth.contains(AFlag.ANONYMOUS_CONSTRUCTOR) || callMth.contains(AFlag.NO_SKIP_ARGS)) {\n\t\t\treturn;\n\t\t}\n\t\tSkipMethodArgsAttr attr = callMth.get(AType.SKIP_MTH_ARGS);\n\t\tif (attr != null) {\n\t\t\tint argsCount = Math.min(callMth.getMethodInfo().getArgsCount(), co.getArgsCount());\n\t\t\tfor (int i = 0; i < argsCount; i++) {\n\t\t\t\tif (attr.isSkip(i)) {\n\t\t\t\t\tanonymousCallArgMod(co.getArg(i));\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// additional info not available apply mods to all args (the safest solution)\n\t\t\tco.getArguments().forEach(ModVisitor::anonymousCallArgMod);\n\t\t}\n\t}\n\n\tprivate static void anonymousCallArgMod(InsnArg arg) {\n\t\targ.add(AFlag.DONT_INLINE);\n\t\tif (arg.isRegister()) {\n\t\t\t((RegisterArg) arg).getSVar().getCodeVar().setFinal(true);\n\t\t}\n\t}\n\n\t/**\n\t * Return first usage instruction for arg.\n\t * If used only once try to follow move chain\n\t */\n\t@Nullable\n\tprivate static InsnNode getFirstUseSkipMove(RegisterArg arg) {\n\t\tSSAVar sVar = arg.getSVar();\n\t\tint useCount = sVar.getUseCount();\n\t\tif (useCount == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tRegisterArg useArg = sVar.getUseList().get(0);\n\t\tInsnNode parentInsn = useArg.getParentInsn();\n\t\tif (parentInsn == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (useCount == 1 && parentInsn.getType() == InsnType.MOVE) {\n\t\t\treturn getFirstUseSkipMove(parentInsn.getResult());\n\t\t}\n\t\treturn parentInsn;\n\t}\n\n\tprivate static InsnNode makeFilledArrayInsn(MethodNode mth, NewArrayNode newArrayNode, FillArrayInsn insn) {\n\t\tArgType insnArrayType = newArrayNode.getArrayType();\n\t\tArgType insnElementType = insnArrayType.getArrayElement();\n\t\tArgType elType = insn.getElementType();\n\t\tif (!elType.isTypeKnown()\n\t\t\t\t&& insnElementType.isPrimitive()\n\t\t\t\t&& elType.contains(insnElementType.getPrimitiveType())) {\n\t\t\telType = insnElementType;\n\t\t}\n\t\tif (!elType.equals(insnElementType) && !insnArrayType.equals(ArgType.OBJECT)) {\n\t\t\tmth.addWarn(\"Incorrect type for fill-array insn \" + InsnUtils.formatOffset(insn.getOffset())\n\t\t\t\t\t+ \", element type: \" + elType + \", insn element type: \" + insnElementType);\n\t\t}\n\t\tif (!elType.isTypeKnown()) {\n\t\t\tLOG.warn(\"Unknown array element type: {} in mth: {}\", elType, mth);\n\t\t\telType = insnElementType.isTypeKnown() ? insnElementType : elType.selectFirst();\n\t\t\tif (elType == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Null array element type\");\n\t\t\t}\n\t\t}\n\n\t\tList<LiteralArg> list = insn.getLiteralArgs(elType);\n\t\tInsnNode filledArr = new FilledNewArrayNode(elType, list.size());\n\t\tfilledArr.setResult(newArrayNode.getResult().duplicate());\n\t\tfor (LiteralArg arg : list) {\n\t\t\tIFieldInfoRef f = mth.getParentClass().getConstFieldByLiteralArg(arg);\n\t\t\tif (f != null) {\n\t\t\t\tInsnNode fGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);\n\t\t\t\tfilledArr.addArg(InsnArg.wrapArg(fGet));\n\t\t\t\taddFieldUsage(f, mth);\n\t\t\t} else {\n\t\t\t\tfilledArr.addArg(arg.duplicate());\n\t\t\t}\n\t\t}\n\t\treturn filledArr;\n\t}\n\n\tprivate static void processMoveException(MethodNode mth, BlockNode block, InsnNode insn, InsnRemover remover) {\n\t\tExcHandlerAttr excHandlerAttr = block.get(AType.EXC_HANDLER);\n\t\tif (excHandlerAttr == null) {\n\t\t\treturn;\n\t\t}\n\t\tExceptionHandler excHandler = excHandlerAttr.getHandler();\n\n\t\t// result arg used both in this insn and exception handler,\n\t\tRegisterArg resArg = insn.getResult();\n\t\tArgType type = excHandler.getArgType();\n\t\tString name = excHandler.isCatchAll() ? \"th\" : \"e\";\n\t\tif (resArg.getName() == null) {\n\t\t\tresArg.setName(name);\n\t\t}\n\t\tSSAVar sVar = insn.getResult().getSVar();\n\t\tif (sVar.getUseCount() == 0) {\n\t\t\texcHandler.setArg(new NamedArg(name, type));\n\t\t\tremover.addAndUnbind(insn);\n\t\t} else if (sVar.isUsedInPhi()) {\n\t\t\t// exception var moved to external variable => replace with 'move' insn\n\t\t\tInsnNode moveInsn = new InsnNode(InsnType.MOVE, 1);\n\t\t\tmoveInsn.setResult(insn.getResult());\n\t\t\tNamedArg namedArg = new NamedArg(name, type);\n\t\t\tmoveInsn.addArg(namedArg);\n\t\t\texcHandler.setArg(namedArg);\n\t\t\treplaceInsn(mth, block, 0, moveInsn);\n\t\t}\n\t\tblock.copyAttributeFrom(insn, AType.CODE_COMMENTS); // save comment\n\t}\n\n\tpublic static void addFieldUsage(IFieldInfoRef fieldData, MethodNode mth) {\n\t\tif (fieldData instanceof FieldNode) {\n\t\t\t((FieldNode) fieldData).addUseIn(mth);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/MoveInlineVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.RegDebugInfoAttr;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.dex.visitors.ssa.SSATransform;\nimport jadx.core.utils.InsnRemover;\n\n@JadxVisitor(\n\t\tname = \"MoveInlineVisitor\",\n\t\tdesc = \"Inline redundant move instructions\",\n\t\trunAfter = SSATransform.class,\n\t\trunBefore = CodeShrinkVisitor.class\n)\npublic class MoveInlineVisitor extends AbstractVisitor {\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tmoveInline(mth);\n\t}\n\n\tpublic static void moveInline(MethodNode mth) {\n\t\tInsnRemover remover = new InsnRemover(mth);\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tremover.setBlock(block);\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tif (insn.getType() != InsnType.MOVE) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (processMove(mth, insn)) {\n\t\t\t\t\tblock.add(AFlag.MOVE_INLINED);\n\t\t\t\t\tremover.addAndUnbind(insn);\n\t\t\t\t}\n\t\t\t}\n\t\t\tremover.perform();\n\t\t}\n\t}\n\n\tprivate static boolean processMove(MethodNode mth, InsnNode move) {\n\t\tRegisterArg resultArg = move.getResult();\n\t\tInsnArg moveArg = move.getArg(0);\n\t\tif (resultArg.sameRegAndSVar(moveArg)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (moveArg.isRegister()) {\n\t\t\tRegisterArg moveReg = (RegisterArg) moveArg;\n\t\t\tif (moveReg.getSVar().isAssignInPhi()) {\n\t\t\t\t// don't mix already merged variables\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tSSAVar ssaVar = resultArg.getSVar();\n\t\tif (ssaVar.getUseList().isEmpty()) {\n\t\t\t// unused result\n\t\t\treturn true;\n\t\t}\n\n\t\tif (ssaVar.isUsedInPhi()) {\n\t\t\treturn false;\n\t\t\t// TODO: review conditions of 'up' move inline (test TestMoveInline)\n\t\t\t// return deleteMove(mth, move);\n\t\t}\n\t\tRegDebugInfoAttr debugInfo = moveArg.get(AType.REG_DEBUG_INFO);\n\t\tfor (RegisterArg useArg : ssaVar.getUseList()) {\n\t\t\tInsnNode useInsn = useArg.getParentInsn();\n\t\t\tif (useInsn == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (debugInfo == null) {\n\t\t\t\tRegDebugInfoAttr debugInfoAttr = useArg.get(AType.REG_DEBUG_INFO);\n\t\t\t\tif (debugInfoAttr != null) {\n\t\t\t\t\tdebugInfo = debugInfoAttr;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// all checks passed, execute inline\n\t\tfor (RegisterArg useArg : new ArrayList<>(ssaVar.getUseList())) {\n\t\t\tInsnNode useInsn = useArg.getParentInsn();\n\t\t\tif (useInsn == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tInsnArg replaceArg;\n\t\t\tif (moveArg.isRegister()) {\n\t\t\t\treplaceArg = ((RegisterArg) moveArg).duplicate(useArg.getInitType());\n\t\t\t} else {\n\t\t\t\treplaceArg = moveArg.duplicate();\n\t\t\t}\n\t\t\tuseInsn.inheritMetadata(move);\n\t\t\treplaceArg.copyAttributesFrom(useArg);\n\t\t\tif (debugInfo != null) {\n\t\t\t\treplaceArg.addAttr(debugInfo);\n\t\t\t}\n\t\t\tif (!useInsn.replaceArg(useArg, replaceArg)) {\n\t\t\t\tmth.addWarnComment(\"Failed to replace arg in insn: \" + useInsn);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean deleteMove(MethodNode mth, InsnNode move) {\n\t\tInsnArg moveArg = move.getArg(0);\n\t\tif (!moveArg.isRegister()) {\n\t\t\treturn false;\n\t\t}\n\t\tRegisterArg moveReg = (RegisterArg) moveArg;\n\t\tSSAVar ssaVar = moveReg.getSVar();\n\t\tif (ssaVar.getUseCount() != 1 || ssaVar.isUsedInPhi()) {\n\t\t\treturn false;\n\t\t}\n\t\tRegisterArg assignArg = ssaVar.getAssign();\n\t\tInsnNode parentInsn = assignArg.getParentInsn();\n\t\tif (parentInsn == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (parentInsn.getSourceLine() != move.getSourceLine()\n\t\t\t\t|| moveArg.contains(AType.REG_DEBUG_INFO)) {\n\t\t\t// preserve debug info\n\t\t\treturn false;\n\t\t}\n\t\t// set move result into parent insn result\n\t\tInsnRemover.unbindAllArgs(mth, move);\n\t\tInsnRemover.unbindResult(mth, parentInsn);\n\n\t\tRegisterArg resArg = parentInsn.getResult();\n\t\tRegisterArg newResArg = move.getResult().duplicate(resArg.getInitType());\n\t\tnewResArg.copyAttributesFrom(resArg);\n\t\tparentInsn.setResult(newResArg);\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.clsp.ClspClass;\nimport jadx.core.clsp.ClspMethod;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.MethodBridgeAttr;\nimport jadx.core.dex.attributes.nodes.MethodOverrideAttr;\nimport jadx.core.dex.attributes.nodes.RenameReasonAttr;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.rename.RenameVisitor;\nimport jadx.core.dex.visitors.typeinference.TypeCompare;\nimport jadx.core.dex.visitors.typeinference.TypeCompareEnum;\nimport jadx.core.dex.visitors.typeinference.TypeInferenceVisitor;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n@JadxVisitor(\n\t\tname = \"OverrideMethodVisitor\",\n\t\tdesc = \"Mark override methods and revert type erasure\",\n\t\trunBefore = {\n\t\t\t\tTypeInferenceVisitor.class,\n\t\t\t\tRenameVisitor.class\n\t\t}\n)\npublic class OverrideMethodVisitor extends AbstractVisitor {\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) throws JadxException {\n\t\tSuperTypesData superData = collectSuperTypes(cls);\n\t\tif (superData != null) {\n\t\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\t\tprocessMth(mth, superData);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate void processMth(MethodNode mth, SuperTypesData superData) {\n\t\tif (mth.isConstructor() || mth.getAccessFlags().isStatic() || mth.getAccessFlags().isPrivate()) {\n\t\t\treturn;\n\t\t}\n\t\tMethodOverrideAttr attr = processOverrideMethods(mth, superData);\n\t\tif (attr != null) {\n\t\t\tif (attr.getBaseMethods().isEmpty()) {\n\t\t\t\tthrow new JadxRuntimeException(\"No base methods for override attribute: \" + attr.getOverrideList());\n\t\t\t}\n\t\t\tmth.addAttr(attr);\n\t\t\tIMethodDetails baseMth = Utils.getOne(attr.getBaseMethods());\n\t\t\tif (baseMth != null) {\n\t\t\t\tboolean updated = fixMethodReturnType(mth, baseMth, superData);\n\t\t\t\tupdated |= fixMethodArgTypes(mth, baseMth, superData);\n\t\t\t\tif (updated) {\n\t\t\t\t\t// check if new signature cause method collisions\n\t\t\t\t\tcheckMethodSignatureCollisions(mth, mth.root().getArgs().isRenameValid());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate MethodOverrideAttr processOverrideMethods(MethodNode mth, SuperTypesData superData) {\n\t\tMethodOverrideAttr result = mth.get(AType.METHOD_OVERRIDE);\n\t\tif (result != null) {\n\t\t\treturn result;\n\t\t}\n\t\tClassNode cls = mth.getParentClass();\n\t\tString signature = mth.getMethodInfo().makeSignature(false);\n\t\tList<IMethodDetails> overrideList = new ArrayList<>();\n\t\tSet<IMethodDetails> baseMethods = new HashSet<>();\n\t\tfor (ArgType superType : superData.getSuperTypes()) {\n\t\t\tClassNode classNode = mth.root().resolveClass(superType);\n\t\t\tif (classNode != null) {\n\t\t\t\tMethodNode ovrdMth = searchOverriddenMethod(classNode, mth, signature);\n\t\t\t\tif (ovrdMth != null) {\n\t\t\t\t\tif (isMethodVisibleInCls(ovrdMth, cls)) {\n\t\t\t\t\t\toverrideList.add(ovrdMth);\n\t\t\t\t\t\tMethodOverrideAttr attr = ovrdMth.get(AType.METHOD_OVERRIDE);\n\t\t\t\t\t\tif (attr != null) {\n\t\t\t\t\t\t\taddBaseMethod(superData, overrideList, baseMethods, superType);\n\t\t\t\t\t\t\treturn buildOverrideAttr(mth, overrideList, baseMethods, attr);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tClspClass clsDetails = mth.root().getClsp().getClsDetails(superType);\n\t\t\t\tif (clsDetails != null) {\n\t\t\t\t\tMap<String, ClspMethod> methodsMap = clsDetails.getMethodsMap();\n\t\t\t\t\tfor (Map.Entry<String, ClspMethod> entry : methodsMap.entrySet()) {\n\t\t\t\t\t\tString mthShortId = entry.getKey();\n\t\t\t\t\t\t// do not check full signature, classpath methods can be trusted\n\t\t\t\t\t\t// i.e. doesn't contain methods with same signature in one class\n\t\t\t\t\t\tif (mthShortId.startsWith(signature)) {\n\t\t\t\t\t\t\toverrideList.add(entry.getValue());\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\taddBaseMethod(superData, overrideList, baseMethods, superType);\n\t\t}\n\t\treturn buildOverrideAttr(mth, overrideList, baseMethods, null);\n\t}\n\n\tprivate void addBaseMethod(SuperTypesData superData, List<IMethodDetails> overrideList, Set<IMethodDetails> baseMethods,\n\t\t\tArgType superType) {\n\t\tif (superData.getEndTypes().contains(superType.getObject())) {\n\t\t\tIMethodDetails last = Utils.last(overrideList);\n\t\t\tif (last != null) {\n\t\t\t\tbaseMethods.add(last);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate MethodNode searchOverriddenMethod(ClassNode cls, MethodNode mth, String signature) {\n\t\t// search by exact full signature (with return value) to fight obfuscation (see test\n\t\t// 'TestOverrideWithSameName')\n\t\tString shortId = mth.getMethodInfo().getShortId();\n\t\tfor (MethodNode supMth : cls.getMethods()) {\n\t\t\tif (supMth.getMethodInfo().getShortId().equals(shortId) && !supMth.getAccessFlags().isStatic()) {\n\t\t\t\treturn supMth;\n\t\t\t}\n\t\t}\n\t\t// search by signature without return value and check if return value is wider type\n\t\tfor (MethodNode supMth : cls.getMethods()) {\n\t\t\tif (supMth.getMethodInfo().getShortId().startsWith(signature) && !supMth.getAccessFlags().isStatic()) {\n\t\t\t\tTypeCompare typeCompare = cls.root().getTypeCompare();\n\t\t\t\tArgType supRetType = supMth.getMethodInfo().getReturnType();\n\t\t\t\tArgType mthRetType = mth.getMethodInfo().getReturnType();\n\t\t\t\tTypeCompareEnum res = typeCompare.compareTypes(supRetType, mthRetType);\n\t\t\t\tif (res.isWider()) {\n\t\t\t\t\treturn supMth;\n\t\t\t\t}\n\t\t\t\tif (res == TypeCompareEnum.UNKNOWN || res == TypeCompareEnum.CONFLICT) {\n\t\t\t\t\tmth.addDebugComment(\"Possible override for method \" + supMth.getMethodInfo().getFullId());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Nullable\n\tprivate MethodOverrideAttr buildOverrideAttr(MethodNode mth, List<IMethodDetails> overrideList,\n\t\t\tSet<IMethodDetails> baseMethods, @Nullable MethodOverrideAttr attr) {\n\t\tif (overrideList.isEmpty() && attr == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (attr == null) {\n\t\t\t// traced to base method\n\t\t\tList<IMethodDetails> cleanOverrideList = overrideList.stream().distinct().collect(Collectors.toList());\n\t\t\treturn applyOverrideAttr(mth, cleanOverrideList, baseMethods, false);\n\t\t}\n\t\t// trace stopped at already processed method -> start merging\n\t\tList<IMethodDetails> mergedOverrideList = Utils.mergeLists(overrideList, attr.getOverrideList());\n\t\tList<IMethodDetails> cleanOverrideList = mergedOverrideList.stream().distinct().collect(Collectors.toList());\n\t\tSet<IMethodDetails> mergedBaseMethods = Utils.mergeSets(baseMethods, attr.getBaseMethods());\n\t\treturn applyOverrideAttr(mth, cleanOverrideList, mergedBaseMethods, true);\n\t}\n\n\tprivate MethodOverrideAttr applyOverrideAttr(MethodNode mth, List<IMethodDetails> overrideList,\n\t\t\tSet<IMethodDetails> baseMethods, boolean update) {\n\t\t// don't rename method if override list contains not resolved method\n\t\tboolean dontRename = overrideList.stream().anyMatch(m -> !(m instanceof MethodNode));\n\t\tSortedSet<MethodNode> relatedMethods = null;\n\t\tList<MethodNode> mthNodes = getMethodNodes(mth, overrideList);\n\t\tif (update) {\n\t\t\t// merge related methods from all override attributes\n\t\t\tfor (MethodNode mthNode : mthNodes) {\n\t\t\t\tMethodOverrideAttr ovrdAttr = mthNode.get(AType.METHOD_OVERRIDE);\n\t\t\t\tif (ovrdAttr != null) {\n\t\t\t\t\t// use one of already allocated sets\n\t\t\t\t\trelatedMethods = ovrdAttr.getRelatedMthNodes();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (relatedMethods != null) {\n\t\t\t\trelatedMethods.addAll(mthNodes);\n\t\t\t} else {\n\t\t\t\trelatedMethods = new TreeSet<>(mthNodes);\n\t\t\t}\n\t\t\tfor (MethodNode mthNode : mthNodes) {\n\t\t\t\tMethodOverrideAttr ovrdAttr = mthNode.get(AType.METHOD_OVERRIDE);\n\t\t\t\tif (ovrdAttr != null) {\n\t\t\t\t\tSortedSet<MethodNode> set = ovrdAttr.getRelatedMthNodes();\n\t\t\t\t\tif (relatedMethods != set) {\n\t\t\t\t\t\trelatedMethods.addAll(set);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\trelatedMethods = new TreeSet<>(mthNodes);\n\t\t}\n\n\t\tint depth = 0;\n\t\tfor (MethodNode mthNode : mthNodes) {\n\t\t\tif (dontRename) {\n\t\t\t\tmthNode.add(AFlag.DONT_RENAME);\n\t\t\t}\n\t\t\tif (depth == 0) {\n\t\t\t\t// skip current (first) method\n\t\t\t\tdepth = 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (update) {\n\t\t\t\tMethodOverrideAttr ovrdAttr = mthNode.get(AType.METHOD_OVERRIDE);\n\t\t\t\tif (ovrdAttr != null) {\n\t\t\t\t\tovrdAttr.setRelatedMthNodes(relatedMethods);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tmthNode.addAttr(new MethodOverrideAttr(Utils.listTail(overrideList, depth), relatedMethods, baseMethods));\n\t\t\tdepth++;\n\t\t}\n\t\treturn new MethodOverrideAttr(overrideList, relatedMethods, baseMethods);\n\t}\n\n\t@NotNull\n\tprivate List<MethodNode> getMethodNodes(MethodNode mth, List<IMethodDetails> overrideList) {\n\t\tList<MethodNode> list = new ArrayList<>(1 + overrideList.size());\n\t\tlist.add(mth);\n\t\tfor (IMethodDetails md : overrideList) {\n\t\t\tif (md instanceof MethodNode) {\n\t\t\t\tlist.add((MethodNode) md);\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n\t/**\n\t * NOTE: Simplified version of method from ModVisitor.isFieldVisibleInMethod\n\t */\n\tprivate boolean isMethodVisibleInCls(MethodNode superMth, ClassNode cls) {\n\t\tAccessInfo accessFlags = superMth.getAccessFlags();\n\t\tif (accessFlags.isPrivate()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (accessFlags.isPublic() || accessFlags.isProtected()) {\n\t\t\treturn true;\n\t\t}\n\t\t// package-private\n\t\treturn Objects.equals(superMth.getParentClass().getPackage(), cls.getPackage());\n\t}\n\n\tprivate static final class SuperTypesData {\n\t\tprivate final List<ArgType> superTypes;\n\t\tprivate final Set<String> endTypes;\n\n\t\tprivate SuperTypesData(List<ArgType> superTypes, Set<String> endTypes) {\n\t\t\tthis.superTypes = superTypes;\n\t\t\tthis.endTypes = endTypes;\n\t\t}\n\n\t\tpublic List<ArgType> getSuperTypes() {\n\t\t\treturn superTypes;\n\t\t}\n\n\t\tpublic Set<String> getEndTypes() {\n\t\t\treturn endTypes;\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate SuperTypesData collectSuperTypes(ClassNode cls) {\n\t\tSet<ArgType> superTypes = new LinkedHashSet<>();\n\t\tSet<String> endTypes = new HashSet<>();\n\t\tcollectSuperTypes(cls, superTypes, endTypes);\n\t\tif (superTypes.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (endTypes.isEmpty()) {\n\t\t\tthrow new JadxRuntimeException(\"No end types in class hierarchy: \" + cls);\n\t\t}\n\t\treturn new SuperTypesData(new ArrayList<>(superTypes), endTypes);\n\t}\n\n\tprivate void collectSuperTypes(ClassNode cls, Set<ArgType> superTypes, Set<String> endTypes) {\n\t\tRootNode root = cls.root();\n\t\tint k = 0;\n\t\tArgType superClass = cls.getSuperClass();\n\t\tif (superClass != null) {\n\t\t\tk += addSuperType(root, superTypes, endTypes, superClass);\n\t\t}\n\t\tfor (ArgType iface : cls.getInterfaces()) {\n\t\t\tk += addSuperType(root, superTypes, endTypes, iface);\n\t\t}\n\t\tif (k == 0) {\n\t\t\tendTypes.add(cls.getType().getObject());\n\t\t}\n\t}\n\n\tprivate int addSuperType(RootNode root, Set<ArgType> superTypes, Set<String> endTypes, ArgType superType) {\n\t\tif (Objects.equals(superType, ArgType.OBJECT)) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (!superTypes.add(superType)) {\n\t\t\t// found 'super' loop, stop processing\n\t\t\treturn 0;\n\t\t}\n\t\tClassNode classNode = root.resolveClass(superType);\n\t\tif (classNode != null) {\n\t\t\tcollectSuperTypes(classNode, superTypes, endTypes);\n\t\t\treturn 1;\n\t\t}\n\t\tClspClass clsDetails = root.getClsp().getClsDetails(superType);\n\t\tif (clsDetails != null) {\n\t\t\tint k = 0;\n\t\t\tfor (ArgType parentType : clsDetails.getParents()) {\n\t\t\t\tk += addSuperType(root, superTypes, endTypes, parentType);\n\t\t\t}\n\t\t\tif (k == 0) {\n\t\t\t\tendTypes.add(superType.getObject());\n\t\t\t}\n\t\t\treturn 1;\n\t\t}\n\t\t// no info found => treat as hierarchy end\n\t\tendTypes.add(superType.getObject());\n\t\treturn 1;\n\t}\n\n\tprivate boolean fixMethodReturnType(MethodNode mth, IMethodDetails baseMth, SuperTypesData superData) {\n\t\tArgType returnType = mth.getReturnType();\n\t\tif (returnType == ArgType.VOID) {\n\t\t\treturn false;\n\t\t}\n\t\tboolean updated = updateReturnType(mth, baseMth, superData);\n\t\tif (updated) {\n\t\t\tmth.addDebugComment(\"Return type fixed from '\" + returnType + \"' to match base method\");\n\t\t}\n\t\treturn updated;\n\t}\n\n\tprivate boolean updateReturnType(MethodNode mth, IMethodDetails baseMth, SuperTypesData superData) {\n\t\tArgType baseReturnType = baseMth.getReturnType();\n\t\tif (mth.getReturnType().equals(baseReturnType)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!baseReturnType.containsTypeVariable()) {\n\t\t\treturn false;\n\t\t}\n\t\tTypeCompare typeCompare = mth.root().getTypeUpdate().getTypeCompare();\n\t\tArgType baseCls = baseMth.getMethodInfo().getDeclClass().getType();\n\t\tfor (ArgType superType : superData.getSuperTypes()) {\n\t\t\tTypeCompareEnum compareResult = typeCompare.compareTypes(superType, baseCls);\n\t\t\tif (compareResult == TypeCompareEnum.NARROW_BY_GENERIC) {\n\t\t\t\tArgType targetRetType = mth.root().getTypeUtils().replaceClassGenerics(superType, baseReturnType);\n\t\t\t\tif (targetRetType != null\n\t\t\t\t\t\t&& !targetRetType.containsTypeVariable()\n\t\t\t\t\t\t&& !targetRetType.equals(mth.getReturnType())) {\n\t\t\t\t\tmth.updateReturnType(targetRetType);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate boolean fixMethodArgTypes(MethodNode mth, IMethodDetails baseMth, SuperTypesData superData) {\n\t\tList<ArgType> mthArgTypes = mth.getArgTypes();\n\t\tList<ArgType> baseArgTypes = baseMth.getArgTypes();\n\t\tif (mthArgTypes.equals(baseArgTypes)) {\n\t\t\treturn false;\n\t\t}\n\t\tint argCount = mthArgTypes.size();\n\t\tif (argCount != baseArgTypes.size()) {\n\t\t\treturn false;\n\t\t}\n\t\tboolean changed = false;\n\t\tList<ArgType> newArgTypes = new ArrayList<>(argCount);\n\t\tfor (int argNum = 0; argNum < argCount; argNum++) {\n\t\t\tArgType newType = updateArgType(mth, baseMth, superData, argNum);\n\t\t\tif (newType != null) {\n\t\t\t\tchanged = true;\n\t\t\t\tnewArgTypes.add(newType);\n\t\t\t} else {\n\t\t\t\tnewArgTypes.add(mthArgTypes.get(argNum));\n\t\t\t}\n\t\t}\n\t\tif (changed) {\n\t\t\tmth.updateArgTypes(newArgTypes, \"Method arguments types fixed to match base method\");\n\t\t}\n\t\treturn changed;\n\t}\n\n\tprivate ArgType updateArgType(MethodNode mth, IMethodDetails baseMth, SuperTypesData superData, int argNum) {\n\t\tArgType arg = mth.getArgTypes().get(argNum);\n\t\tArgType baseArg = baseMth.getArgTypes().get(argNum);\n\t\tif (arg.equals(baseArg)) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!baseArg.containsTypeVariable()) {\n\t\t\treturn null;\n\t\t}\n\t\tTypeCompare typeCompare = mth.root().getTypeUpdate().getTypeCompare();\n\t\tArgType baseCls = baseMth.getMethodInfo().getDeclClass().getType();\n\t\tfor (ArgType superType : superData.getSuperTypes()) {\n\t\t\tTypeCompareEnum compareResult = typeCompare.compareTypes(superType, baseCls);\n\t\t\tif (compareResult == TypeCompareEnum.NARROW_BY_GENERIC) {\n\t\t\t\tArgType targetArgType = mth.root().getTypeUtils().replaceClassGenerics(superType, baseArg);\n\t\t\t\tif (targetArgType != null\n\t\t\t\t\t\t&& !targetArgType.containsTypeVariable()\n\t\t\t\t\t\t&& !targetArgType.equals(arg)) {\n\t\t\t\t\treturn targetArgType;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate void checkMethodSignatureCollisions(MethodNode mth, boolean rename) {\n\t\tString mthName = mth.getMethodInfo().getAlias();\n\t\tString newSignature = MethodInfo.makeShortId(mthName, mth.getArgTypes(), null);\n\t\tfor (MethodNode otherMth : mth.getParentClass().getMethods()) {\n\t\t\tString otherMthName = otherMth.getAlias();\n\t\t\tif (otherMthName.equals(mthName) && otherMth != mth) {\n\t\t\t\tString otherSignature = otherMth.getMethodInfo().makeSignature(true, false);\n\t\t\t\tif (otherSignature.equals(newSignature)) {\n\t\t\t\t\tif (rename) {\n\t\t\t\t\t\tif (otherMth.contains(AFlag.DONT_RENAME) || otherMth.contains(AType.METHOD_OVERRIDE)) {\n\t\t\t\t\t\t\totherMth.addWarnComment(\"Can't rename method to resolve collision\");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\totherMth.getMethodInfo().setAlias(makeNewAlias(otherMth));\n\t\t\t\t\t\t\totherMth.addAttr(new RenameReasonAttr(\"avoid collision after fix types in other method\"));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\totherMth.addAttr(new MethodBridgeAttr(mth));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// TODO: at this point deobfuscator is not available and map file already saved\n\tprivate static String makeNewAlias(MethodNode mth) {\n\t\tClassNode cls = mth.getParentClass();\n\t\tString baseName = mth.getAlias();\n\t\tint k = 2;\n\t\twhile (true) {\n\t\t\tString alias = baseName + k;\n\t\t\tMethodNode methodNode = cls.searchMethodByShortName(alias);\n\t\t\tif (methodNode == null) {\n\t\t\t\treturn alias;\n\t\t\t}\n\t\t\tk++;\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"OverrideMethodVisitor\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.IFieldRef;\nimport jadx.api.plugins.input.data.annotations.AnnotationVisibility;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationsAttr;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.AttrNode;\nimport jadx.core.dex.attributes.nodes.DeclareVariablesAttr;\nimport jadx.core.dex.attributes.nodes.LineAttrNode;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.instructions.ArithNode;\nimport jadx.core.dex.instructions.ArithOp;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.instructions.mods.TernaryInsn;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.InsnContainer;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.conditions.IfCondition;\nimport jadx.core.dex.regions.conditions.IfCondition.Mode;\nimport jadx.core.dex.visitors.regions.variables.ProcessVariables;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.InsnList;\nimport jadx.core.utils.exceptions.JadxException;\n\n/**\n * Prepare instructions for code generation pass,\n * most of this modification breaks register dependencies,\n * so this pass must be just before CodeGen.\n */\n@JadxVisitor(\n\t\tname = \"PrepareForCodeGen\",\n\t\tdesc = \"Prepare instructions for code generation pass\",\n\t\trunAfter = { CodeShrinkVisitor.class, ClassModifier.class, ProcessVariables.class }\n)\npublic class PrepareForCodeGen extends AbstractVisitor {\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"PrepareForCodeGen\";\n\t}\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) throws JadxException {\n\t\tif (cls.root().getArgs().isDebugInfo()) {\n\t\t\tsetClassSourceLine(cls);\n\t\t}\n\t\tcollectFieldsUsageInAnnotations(cls);\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tif (block.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tremoveInstructions(block);\n\t\t\tcheckInline(block);\n\t\t\tremoveParenthesis(block);\n\t\t\tmodifyArith(block);\n\t\t\tcheckConstUsage(block);\n\t\t\taddNullCasts(mth, block);\n\t\t}\n\t\tmoveConstructorInConstructor(mth);\n\t\tcollectFieldsUsageInAnnotations(mth, mth);\n\t}\n\n\tprivate static void removeInstructions(BlockNode block) {\n\t\tIterator<InsnNode> it = block.getInstructions().iterator();\n\t\twhile (it.hasNext()) {\n\t\t\tInsnNode insn = it.next();\n\t\t\tswitch (insn.getType()) {\n\t\t\t\tcase NOP:\n\t\t\t\tcase MONITOR_ENTER:\n\t\t\t\tcase MONITOR_EXIT:\n\t\t\t\tcase MOVE_EXCEPTION:\n\t\t\t\t\tit.remove();\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase CONSTRUCTOR:\n\t\t\t\t\tConstructorInsn co = (ConstructorInsn) insn;\n\t\t\t\t\tif (co.isSelf()) {\n\t\t\t\t\t\tit.remove();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase MOVE:\n\t\t\t\t\t// remove redundant moves: unused result and same args names (a = a;)\n\t\t\t\t\tRegisterArg result = insn.getResult();\n\t\t\t\t\tif (result.getSVar().getUseCount() == 0\n\t\t\t\t\t\t\t&& result.isNameEquals(insn.getArg(0))) {\n\t\t\t\t\t\tit.remove();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void checkInline(BlockNode block) {\n\t\tList<InsnNode> list = block.getInstructions();\n\t\tfor (int i = 0; i < list.size(); i++) {\n\t\t\tInsnNode insn = list.get(i);\n\t\t\t// replace 'move' with inner wrapped instruction\n\t\t\tif (insn.getType() == InsnType.MOVE\n\t\t\t\t\t&& insn.getArg(0).isInsnWrap()) {\n\t\t\t\tInsnNode wrapInsn = ((InsnWrapArg) insn.getArg(0)).getWrapInsn();\n\t\t\t\twrapInsn.setResult(insn.getResult());\n\t\t\t\twrapInsn.copyAttributesFrom(insn);\n\t\t\t\tlist.set(i, wrapInsn);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Add explicit type for non int constants\n\t */\n\tprivate static void checkConstUsage(BlockNode block) {\n\t\tfor (InsnNode blockInsn : block.getInstructions()) {\n\t\t\tblockInsn.visitInsns(insn -> {\n\t\t\t\tif (forbidExplicitType(insn.getType())) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tfor (InsnArg arg : insn.getArguments()) {\n\t\t\t\t\tif (arg.isLiteral() && arg.getType() != ArgType.INT) {\n\t\t\t\t\t\targ.add(AFlag.EXPLICIT_PRIMITIVE_TYPE);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate static boolean forbidExplicitType(InsnType type) {\n\t\tswitch (type) {\n\t\t\tcase CONST:\n\t\t\tcase CAST:\n\t\t\tcase IF:\n\t\t\tcase FILLED_NEW_ARRAY:\n\t\t\tcase APUT:\n\t\t\tcase ARITH:\n\t\t\t\treturn true;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate static void removeParenthesis(BlockNode block) {\n\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\tremoveParenthesis(insn);\n\t\t}\n\t}\n\n\t/**\n\t * Remove parenthesis for wrapped insn in arith '+' or '-'\n\t * ('(a + b) +c' => 'a + b + c')\n\t */\n\tprivate static void removeParenthesis(InsnNode insn) {\n\t\tif (insn.getType() == InsnType.ARITH) {\n\t\t\tArithNode arith = (ArithNode) insn;\n\t\t\tArithOp op = arith.getOp();\n\t\t\tif (op == ArithOp.ADD || op == ArithOp.MUL || op == ArithOp.AND || op == ArithOp.OR) {\n\t\t\t\tfor (int i = 0; i < 2; i++) {\n\t\t\t\t\tInsnArg arg = arith.getArg(i);\n\t\t\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\t\t\tif (wrapInsn.getType() == InsnType.ARITH && ((ArithNode) wrapInsn).getOp() == op) {\n\t\t\t\t\t\t\twrapInsn.add(AFlag.DONT_WRAP);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tremoveParenthesis(wrapInsn);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (insn.getType() == InsnType.TERNARY) {\n\t\t\t\tremoveParenthesis(((TernaryInsn) insn).getCondition());\n\t\t\t}\n\t\t\tfor (InsnArg arg : insn.getArguments()) {\n\t\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\t\tremoveParenthesis(wrapInsn);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void removeParenthesis(IfCondition cond) {\n\t\tMode mode = cond.getMode();\n\t\tfor (IfCondition c : cond.getArgs()) {\n\t\t\tif (c.getMode() == mode) {\n\t\t\t\tc.add(AFlag.DONT_WRAP);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Replace arithmetic operation with short form\n\t * ('a = a + 2' => 'a += 2')\n\t */\n\tprivate static void modifyArith(BlockNode block) {\n\t\tList<InsnNode> list = block.getInstructions();\n\t\tfor (InsnNode insn : list) {\n\t\t\tif (insn.getType() == InsnType.ARITH\n\t\t\t\t\t&& !insn.contains(AFlag.ARITH_ONEARG)\n\t\t\t\t\t&& !insn.contains(AFlag.DECLARE_VAR)) {\n\t\t\t\tRegisterArg res = insn.getResult();\n\t\t\t\tInsnArg arg = insn.getArg(0);\n\t\t\t\tboolean replace = false;\n\t\t\t\tif (res.equals(arg)) {\n\t\t\t\t\treplace = true;\n\t\t\t\t} else if (arg.isRegister()) {\n\t\t\t\t\tRegisterArg regArg = (RegisterArg) arg;\n\t\t\t\t\treplace = res.sameCodeVar(regArg);\n\t\t\t\t}\n\t\t\t\tif (replace) {\n\t\t\t\t\tinsn.setResult(null);\n\t\t\t\t\tinsn.add(AFlag.ARITH_ONEARG);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Check that 'super' or 'this' call in constructor is a first instruction.\n\t * Otherwise, move to the top and add a warning.\n\t */\n\tprivate void moveConstructorInConstructor(MethodNode mth) {\n\t\tif (!mth.isConstructor()) {\n\t\t\treturn;\n\t\t}\n\t\tConstructorInsn ctrInsn = searchConstructorCall(mth);\n\t\tif (ctrInsn == null || ctrInsn.contains(AFlag.DONT_GENERATE)) {\n\t\t\treturn;\n\t\t}\n\t\tboolean firstInsn = BlockUtils.isFirstInsn(mth, ctrInsn);\n\t\tDeclareVariablesAttr declVarsAttr = mth.getRegion().get(AType.DECLARE_VARIABLES);\n\t\tif (firstInsn && declVarsAttr == null) {\n\t\t\t// move not needed\n\t\t\treturn;\n\t\t}\n\t\tString callType = ctrInsn.getCallType().toString().toLowerCase();\n\t\tBlockNode blockByInsn = BlockUtils.getBlockByInsn(mth, ctrInsn);\n\t\tif (blockByInsn == null) {\n\t\t\tmth.addWarn(\"Failed to move \" + callType + \" instruction to top\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (!firstInsn) {\n\t\t\tSet<RegisterArg> regArgs = new HashSet<>();\n\t\t\tctrInsn.getRegisterArgs(regArgs);\n\t\t\tregArgs.remove(mth.getThisArg());\n\t\t\tmth.getArgRegs().forEach(regArgs::remove);\n\t\t\tif (!regArgs.isEmpty()) {\n\t\t\t\tmth.addWarnComment(\"Illegal instructions before constructor call\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmth.addWarnComment(\"'\" + callType + \"' call moved to the top of the method (can break code semantics)\");\n\t\t}\n\n\t\t// move confirmed\n\t\tInsnList.remove(blockByInsn, ctrInsn);\n\t\tmth.getRegion().getSubBlocks().add(0, new InsnContainer(ctrInsn));\n\t}\n\n\tprivate @Nullable ConstructorInsn searchConstructorCall(MethodNode mth) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tif (insn.getType() == InsnType.CONSTRUCTOR) {\n\t\t\t\t\tConstructorInsn ctrInsn = (ConstructorInsn) insn;\n\t\t\t\t\tif (ctrInsn.isSuper() || ctrInsn.isThis()) {\n\t\t\t\t\t\treturn ctrInsn;\n\t\t\t\t\t}\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Use source line from top method\n\t */\n\tprivate void setClassSourceLine(ClassNode cls) {\n\t\tfor (ClassNode innerClass : cls.getInnerClasses()) {\n\t\t\tsetClassSourceLine(innerClass);\n\t\t}\n\t\tint minLine = Stream.of(cls.getMethods(), cls.getInnerClasses(), cls.getFields())\n\t\t\t\t.flatMap(Collection::stream)\n\t\t\t\t.filter(mth -> !mth.contains(AFlag.DONT_GENERATE))\n\t\t\t\t.filter(mth -> mth.getSourceLine() != 0)\n\t\t\t\t.mapToInt(LineAttrNode::getSourceLine)\n\t\t\t\t.min()\n\t\t\t\t.orElse(0);\n\t\tif (minLine != 0) {\n\t\t\tcls.setSourceLine(minLine - 1);\n\t\t}\n\t}\n\n\tprivate void collectFieldsUsageInAnnotations(ClassNode cls) {\n\t\tMethodNode useMth = cls.getDefaultConstructor();\n\t\tif (useMth == null && !cls.getMethods().isEmpty()) {\n\t\t\tuseMth = cls.getMethods().get(0);\n\t\t}\n\t\tif (useMth == null) {\n\t\t\treturn;\n\t\t}\n\t\tcollectFieldsUsageInAnnotations(useMth, cls);\n\t\tMethodNode finalUseMth = useMth;\n\t\tcls.getFields().forEach(f -> collectFieldsUsageInAnnotations(finalUseMth, f));\n\t}\n\n\tprivate void collectFieldsUsageInAnnotations(MethodNode mth, AttrNode attrNode) {\n\t\tAnnotationsAttr annotationsList = attrNode.get(JadxAttrType.ANNOTATION_LIST);\n\t\tif (annotationsList == null) {\n\t\t\treturn;\n\t\t}\n\t\tfor (IAnnotation annotation : annotationsList.getAll()) {\n\t\t\tif (annotation.getVisibility() == AnnotationVisibility.SYSTEM) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (Map.Entry<String, EncodedValue> entry : annotation.getValues().entrySet()) {\n\t\t\t\tcheckEncodedValue(mth, entry.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate void checkEncodedValue(MethodNode mth, EncodedValue encodedValue) {\n\t\tswitch (encodedValue.getType()) {\n\t\t\tcase ENCODED_FIELD:\n\t\t\t\tObject fieldData = encodedValue.getValue();\n\t\t\t\tFieldInfo fieldInfo;\n\t\t\t\tif (fieldData instanceof IFieldRef) {\n\t\t\t\t\tfieldInfo = FieldInfo.fromRef(mth.root(), (IFieldRef) fieldData);\n\t\t\t\t} else {\n\t\t\t\t\tfieldInfo = (FieldInfo) fieldData;\n\t\t\t\t}\n\t\t\t\tFieldNode fieldNode = mth.root().resolveField(fieldInfo);\n\t\t\t\tif (fieldNode != null) {\n\t\t\t\t\tfieldNode.addUseIn(mth);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase ENCODED_ANNOTATION:\n\t\t\t\tIAnnotation annotation = (IAnnotation) encodedValue.getValue();\n\t\t\t\tannotation.getValues().forEach((k, v) -> checkEncodedValue(mth, v));\n\t\t\t\tbreak;\n\n\t\t\tcase ENCODED_ARRAY:\n\t\t\t\tList<EncodedValue> valueList = (List<EncodedValue>) encodedValue.getValue();\n\t\t\t\tvalueList.forEach(v -> checkEncodedValue(mth, v));\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate void addNullCasts(MethodNode mth, BlockNode block) {\n\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\tswitch (insn.getType()) {\n\t\t\t\tcase INVOKE:\n\t\t\t\t\tverifyNullCast(mth, ((InvokeNode) insn).getInstanceArg());\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase ARRAY_LENGTH:\n\t\t\t\t\tverifyNullCast(mth, insn.getArg(0));\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void verifyNullCast(MethodNode mth, InsnArg arg) {\n\t\tif (arg != null && arg.isZeroConst()) {\n\t\t\tArgType castType = arg.getType();\n\t\t\tIndexInsnNode castInsn = new IndexInsnNode(InsnType.CAST, castType, 1);\n\t\t\tcastInsn.addArg(InsnArg.lit(0, castType));\n\t\t\targ.wrapInstruction(mth, castInsn);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/ProcessAnonymous.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.AnonymousClassAttr;\nimport jadx.core.dex.attributes.nodes.AnonymousClassAttr.InlineType;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.usage.UsageInfoVisitor;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"ProcessAnonymous\",\n\t\tdesc = \"Mark anonymous and lambda classes (for future inline)\",\n\t\trunAfter = {\n\t\t\t\tUsageInfoVisitor.class\n\t\t}\n)\n@SuppressWarnings(\"BooleanMethodIsAlwaysInverted\")\npublic class ProcessAnonymous extends AbstractVisitor {\n\n\tprivate boolean inlineAnonymousClasses;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tinlineAnonymousClasses = root.getArgs().isInlineAnonymousClasses();\n\t\tif (!inlineAnonymousClasses) {\n\t\t\treturn;\n\t\t}\n\t\troot.getClasses().forEach(ProcessAnonymous::processClass);\n\t\tmergeAnonymousDeps(root);\n\t}\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) throws JadxException {\n\t\tif (inlineAnonymousClasses && cls.contains(AFlag.CLASS_UNLOADED)) {\n\t\t\t// enter only on class reload\n\t\t\tvisitClassAndInners(cls);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate void visitClassAndInners(ClassNode cls) {\n\t\tprocessClass(cls);\n\t\tcls.getInnerClasses().forEach(this::visitClassAndInners);\n\t}\n\n\tprivate static void processClass(ClassNode cls) {\n\t\ttry {\n\t\t\tmarkAnonymousClass(cls);\n\t\t} catch (StackOverflowError | Exception e) {\n\t\t\tcls.addError(\"Anonymous visitor error\", e);\n\t\t}\n\t}\n\n\tprivate static void markAnonymousClass(ClassNode cls) {\n\t\tif (!canBeAnonymous(cls)) {\n\t\t\treturn;\n\t\t}\n\t\tMethodNode anonymousConstructor = ListUtils.filterOnlyOne(cls.getMethods(), MethodNode::isConstructor);\n\t\tif (anonymousConstructor == null) {\n\t\t\treturn;\n\t\t}\n\t\tInlineType inlineType = checkUsage(cls, anonymousConstructor);\n\t\tif (inlineType == null) {\n\t\t\treturn;\n\t\t}\n\t\tArgType baseType = getBaseType(cls);\n\t\tif (baseType == null) {\n\t\t\treturn;\n\t\t}\n\t\tClassNode outerCls;\n\t\tif (inlineType == InlineType.INSTANCE_FIELD) {\n\t\t\touterCls = cls.getUseInMth().get(0).getParentClass();\n\t\t} else {\n\t\t\touterCls = anonymousConstructor.getUseIn().get(0).getParentClass();\n\t\t}\n\t\touterCls.addInlinedClass(cls);\n\t\tcls.addAttr(new AnonymousClassAttr(outerCls, baseType, inlineType));\n\t\tcls.add(AFlag.DONT_GENERATE);\n\t\tanonymousConstructor.add(AFlag.ANONYMOUS_CONSTRUCTOR);\n\n\t\t// force anonymous class to be processed before outer class,\n\t\t// actual usage of outer class will be removed at anonymous class process,\n\t\t// see ModVisitor.processAnonymousConstructor method\n\t\tClassNode topOuterCls = outerCls.getTopParentClass();\n\t\tcls.removeDependency(topOuterCls);\n\t\tListUtils.safeRemove(outerCls.getUseIn(), cls);\n\n\t\t// move dependency to codegen stage\n\t\tif (cls.isTopClass()) {\n\t\t\ttopOuterCls.removeDependency(cls);\n\t\t\ttopOuterCls.addCodegenDep(cls);\n\t\t}\n\t}\n\n\tprivate static void undoAnonymousMark(ClassNode cls) {\n\t\tAnonymousClassAttr attr = cls.get(AType.ANONYMOUS_CLASS);\n\t\tClassNode outerCls = attr.getOuterCls();\n\t\tcls.setDependencies(ListUtils.safeAdd(cls.getDependencies(), outerCls.getTopParentClass()));\n\t\touterCls.setUseIn(ListUtils.safeAdd(outerCls.getUseIn(), cls));\n\n\t\tcls.remove(AType.ANONYMOUS_CLASS);\n\t\tcls.remove(AFlag.DONT_GENERATE);\n\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\tif (mth.isConstructor()) {\n\t\t\t\tmth.remove(AFlag.ANONYMOUS_CONSTRUCTOR);\n\t\t\t}\n\t\t}\n\t\tcls.addDebugComment(\"Anonymous mark cleared\");\n\t}\n\n\tprivate void mergeAnonymousDeps(RootNode root) {\n\t\t// Collect edges to build bidirectional tree:\n\t\t// inline edge: anonymous -> outer (one-to-one)\n\t\t// use edges: outer -> *anonymous (one-to-many)\n\t\tMap<ClassNode, ClassNode> inlineMap = new HashMap<>();\n\t\tMap<ClassNode, List<ClassNode>> useMap = new HashMap<>();\n\t\tfor (ClassNode anonymousCls : root.getClasses()) {\n\t\t\tAnonymousClassAttr attr = anonymousCls.get(AType.ANONYMOUS_CLASS);\n\t\t\tif (attr != null) {\n\t\t\t\tClassNode outerCls = attr.getOuterCls();\n\t\t\t\tList<ClassNode> list = useMap.get(outerCls);\n\t\t\t\tif (list == null || list.isEmpty()) {\n\t\t\t\t\tlist = new ArrayList<>(2);\n\t\t\t\t\tuseMap.put(outerCls, list);\n\t\t\t\t}\n\t\t\t\tlist.add(anonymousCls);\n\t\t\t\tuseMap.putIfAbsent(anonymousCls, Collections.emptyList()); // put leaf explicitly\n\t\t\t\tinlineMap.put(anonymousCls, outerCls);\n\t\t\t}\n\t\t}\n\t\tif (inlineMap.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\t// starting from leaf process deps in nodes up to root\n\t\tSet<ClassNode> added = new HashSet<>();\n\t\tuseMap.forEach((key, list) -> {\n\t\t\tif (list.isEmpty()) {\n\t\t\t\tadded.clear();\n\t\t\t\tupdateDeps(key, inlineMap, added);\n\t\t\t}\n\t\t});\n\t\tfor (ClassNode cls : root.getClasses()) {\n\t\t\tList<ClassNode> deps = cls.getCodegenDeps();\n\t\t\tif (deps.size() > 1) {\n\t\t\t\t// distinct sorted dep, reusing collections to reduce memory allocations :)\n\t\t\t\tadded.clear();\n\t\t\t\tadded.addAll(deps);\n\t\t\t\tdeps.clear();\n\t\t\t\tdeps.addAll(added);\n\t\t\t\tCollections.sort(deps);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void updateDeps(ClassNode leafCls, Map<ClassNode, ClassNode> inlineMap, Set<ClassNode> added) {\n\t\tClassNode topNode;\n\t\tClassNode current = leafCls;\n\t\twhile (true) {\n\t\t\tif (!added.add(current)) {\n\t\t\t\tcurrent.addWarnComment(\"Loop in anonymous inline: \" + current + \", path: \" + added);\n\t\t\t\tadded.forEach(ProcessAnonymous::undoAnonymousMark);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tClassNode next = inlineMap.get(current);\n\t\t\tif (next == null) {\n\t\t\t\ttopNode = current.getTopParentClass();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcurrent = next;\n\t\t}\n\t\tif (added.size() <= 2) {\n\t\t\t// first level deps already processed\n\t\t\treturn;\n\t\t}\n\t\tList<ClassNode> deps = topNode.getCodegenDeps();\n\t\tif (deps.isEmpty()) {\n\t\t\tdeps = new ArrayList<>(added.size());\n\t\t\ttopNode.setCodegenDeps(deps);\n\t\t}\n\t\tfor (ClassNode add : added) {\n\t\t\tdeps.add(add.getTopParentClass());\n\t\t}\n\t}\n\n\tprivate static boolean canBeAnonymous(ClassNode cls) {\n\t\tif (cls.getAccessFlags().isSynthetic()) {\n\t\t\treturn true;\n\t\t}\n\t\tString shortName = cls.getClassInfo().getShortName();\n\t\tif (shortName.contains(\"$\") || Character.isDigit(shortName.charAt(0))) {\n\t\t\treturn true;\n\t\t}\n\t\tif (cls.getUseIn().size() == 1 && cls.getUseInMth().size() == 1) {\n\t\t\tMethodNode useMth = cls.getUseInMth().get(0);\n\t\t\t// allow use in enum class init\n\t\t\treturn useMth.getMethodInfo().isClassInit() && useMth.getParentClass().isEnum();\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Checks:\n\t * - class have only one constructor which used only once (allow common code for field init)\n\t * - methods or fields not used outside (allow only nested inner classes with synthetic usage)\n\t * - if constructor used only in class init check if possible inline by instance field\n\t *\n\t * @return decided inline type\n\t */\n\tprivate static InlineType checkUsage(ClassNode cls, MethodNode ctr) {\n\t\tif (ctr.getUseIn().size() != 1) {\n\t\t\t// check if used in common field init in all constructors\n\t\t\tif (!checkForCommonFieldInit(ctr)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tMethodNode ctrUseMth = ctr.getUseIn().get(0);\n\t\tClassNode ctrUseCls = ctrUseMth.getParentClass();\n\t\tif (ctrUseCls.equals(cls)) {\n\t\t\tif (checkForInstanceFieldUsage(cls, ctr)) {\n\t\t\t\treturn InlineType.INSTANCE_FIELD;\n\t\t\t}\n\t\t\t// exclude self usage\n\t\t\treturn null;\n\t\t}\n\t\tif (ctrUseCls.getTopParentClass().equals(cls)) {\n\t\t\t// exclude usage inside inner classes\n\t\t\treturn null;\n\t\t}\n\t\tif (!checkMethodsUsage(cls, ctr, ctrUseMth)) {\n\t\t\treturn null;\n\t\t}\n\t\tfor (FieldNode field : cls.getFields()) {\n\t\t\tfor (MethodNode useMth : field.getUseIn()) {\n\t\t\t\tif (badMethodUsage(cls, useMth, field.getAccessFlags())) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn InlineType.CONSTRUCTOR;\n\t}\n\n\tprivate static boolean checkMethodsUsage(ClassNode cls, MethodNode ctr, MethodNode ctrUseMth) {\n\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\tif (mth == ctr) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (MethodNode useMth : mth.getUseIn()) {\n\t\t\t\tif (useMth.equals(ctrUseMth)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (badMethodUsage(cls, useMth, mth.getAccessFlags())) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean checkForInstanceFieldUsage(ClassNode cls, MethodNode ctr) {\n\t\tMethodNode ctrUseMth = ctr.getUseIn().get(0);\n\t\tif (!ctrUseMth.getMethodInfo().isClassInit()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (cls.getUseInMth().isEmpty()) {\n\t\t\t// no outside usage, inline not needed\n\t\t\treturn false;\n\t\t}\n\t\tFieldNode instFld = ListUtils.filterOnlyOne(cls.getFields(),\n\t\t\t\tf -> f.getAccessFlags().containsFlags(AccessFlags.PUBLIC, AccessFlags.STATIC, AccessFlags.FINAL)\n\t\t\t\t\t\t&& f.getFieldInfo().getType().equals(cls.getClassInfo().getType()));\n\t\tif (instFld == null) {\n\t\t\treturn false;\n\t\t}\n\t\tList<MethodNode> instFldUseIn = instFld.getUseIn();\n\t\tif (instFldUseIn.size() != 2\n\t\t\t\t|| !instFldUseIn.contains(ctrUseMth) // initialized in class init\n\t\t\t\t|| !instFldUseIn.containsAll(cls.getUseInMth()) // class used only with this field\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!checkMethodsUsage(cls, ctr, ctrUseMth)) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (FieldNode field : cls.getFields()) {\n\t\t\tif (field == instFld) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (MethodNode useMth : field.getUseIn()) {\n\t\t\t\tif (badMethodUsage(cls, useMth, field.getAccessFlags())) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tinstFld.add(AFlag.INLINE_INSTANCE_FIELD);\n\t\treturn true;\n\t}\n\n\tprivate static boolean badMethodUsage(ClassNode cls, MethodNode useMth, AccessInfo accessFlags) {\n\t\tClassNode useCls = useMth.getParentClass();\n\t\tif (useCls.equals(cls)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (accessFlags.isSynthetic()) {\n\t\t\t// allow synthetic usage in inner class\n\t\t\treturn !useCls.getParentClass().equals(cls);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Checks:\n\t * + all in constructors\n\t * + all usage in one class\n\t * - same field put (ignored: methods not loaded yet)\n\t */\n\tprivate static boolean checkForCommonFieldInit(MethodNode ctrMth) {\n\t\tList<MethodNode> ctrUse = ctrMth.getUseIn();\n\t\tif (ctrUse.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tClassNode firstUseCls = ctrUse.get(0).getParentClass();\n\t\treturn ListUtils.allMatch(ctrUse, m -> m.isConstructor() && m.getParentClass().equals(firstUseCls));\n\t}\n\n\t@Nullable\n\tprivate static ArgType getBaseType(ClassNode cls) {\n\t\tint interfacesCount = cls.getInterfaces().size();\n\t\tif (interfacesCount > 1) {\n\t\t\treturn null;\n\t\t}\n\t\tArgType superCls = cls.getSuperClass();\n\t\tif (superCls == null || superCls.equals(ArgType.OBJECT)) {\n\t\t\tif (interfacesCount == 1) {\n\t\t\t\treturn cls.getInterfaces().get(0);\n\t\t\t}\n\t\t\treturn ArgType.OBJECT;\n\t\t}\n\t\tif (interfacesCount == 0) {\n\t\t\treturn superCls;\n\t\t}\n\t\t// check if super class already implement that interface (weird case)\n\t\tArgType interfaceType = cls.getInterfaces().get(0);\n\t\tif (cls.root().getClsp().isImplements(superCls.getObject(), interfaceType.getObject())) {\n\t\t\treturn superCls;\n\t\t}\n\t\tif (cls.root().getArgs().isAllowInlineKotlinLambda()) {\n\t\t\tif (superCls.getObject().equals(\"kotlin.jvm.internal.Lambda\")) {\n\t\t\t\t// Inline such class with have different semantic: missing 'arity' property.\n\t\t\t\t// For now, it is unclear how it may affect code execution.\n\t\t\t\treturn interfaceType;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"ProcessAnonymous\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/ProcessInstructionsVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.JumpInfo;\nimport jadx.core.dex.instructions.BaseInvokeNode;\nimport jadx.core.dex.instructions.FillArrayData;\nimport jadx.core.dex.instructions.FillArrayInsn;\nimport jadx.core.dex.instructions.FilledNewArrayNode;\nimport jadx.core.dex.instructions.GotoNode;\nimport jadx.core.dex.instructions.IfNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.SwitchData;\nimport jadx.core.dex.instructions.SwitchInsn;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.java.JsrNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.blocks.BlockSplitter;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.exceptions.JadxException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n@JadxVisitor(\n\t\tname = \"Process Instructions Visitor\",\n\t\tdesc = \"Init instructions info\",\n\t\trunBefore = {\n\t\t\t\tBlockSplitter.class\n\t\t}\n)\npublic class ProcessInstructionsVisitor extends AbstractVisitor {\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\n\t\tinitJumps(mth, mth.getInstructions());\n\t}\n\n\tprivate static void initJumps(MethodNode mth, InsnNode[] insnByOffset) {\n\t\tfor (int offset = 0; offset < insnByOffset.length; offset++) {\n\t\t\tInsnNode insn = insnByOffset[offset];\n\t\t\tif (insn == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tswitch (insn.getType()) {\n\t\t\t\tcase SWITCH:\n\t\t\t\t\tSwitchInsn sw = (SwitchInsn) insn;\n\t\t\t\t\tif (sw.needData()) {\n\t\t\t\t\t\tattachSwitchData(insnByOffset, offset, sw);\n\t\t\t\t\t}\n\t\t\t\t\tint defCaseOffset = sw.getDefaultCaseOffset();\n\t\t\t\t\tif (defCaseOffset != -1) {\n\t\t\t\t\t\taddJump(mth, insnByOffset, offset, defCaseOffset);\n\t\t\t\t\t}\n\t\t\t\t\tfor (int target : sw.getTargets()) {\n\t\t\t\t\t\taddJump(mth, insnByOffset, offset, target);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase IF:\n\t\t\t\t\tint next = getNextInsnOffset(insnByOffset, offset);\n\t\t\t\t\tif (next != -1) {\n\t\t\t\t\t\taddJump(mth, insnByOffset, offset, next);\n\t\t\t\t\t}\n\t\t\t\t\taddJump(mth, insnByOffset, offset, ((IfNode) insn).getTarget());\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase GOTO:\n\t\t\t\t\taddJump(mth, insnByOffset, offset, ((GotoNode) insn).getTarget());\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase JAVA_JSR:\n\t\t\t\t\taddJump(mth, insnByOffset, offset, ((JsrNode) insn).getTarget());\n\t\t\t\t\tint onRet = getNextInsnOffset(insnByOffset, offset);\n\t\t\t\t\tif (onRet != -1) {\n\t\t\t\t\t\taddJump(mth, insnByOffset, offset, onRet);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase INVOKE:\n\t\t\t\t\tif (insn.getResult() == null) {\n\t\t\t\t\t\tArgType retType = ((BaseInvokeNode) insn).getCallMth().getReturnType();\n\t\t\t\t\t\tmergeMoveResult(insnByOffset, offset, insn, retType);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase STR_CONCAT:\n\t\t\t\t\t// invoke-custom with string concatenation translated directly to STR_CONCAT, merge next move-result\n\t\t\t\t\tif (insn.getResult() == null) {\n\t\t\t\t\t\tmergeMoveResult(insnByOffset, offset, insn, ArgType.STRING);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase FILLED_NEW_ARRAY:\n\t\t\t\t\tArgType arrType = ((FilledNewArrayNode) insn).getArrayType();\n\t\t\t\t\tmergeMoveResult(insnByOffset, offset, insn, arrType);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase FILL_ARRAY:\n\t\t\t\t\tFillArrayInsn fillArrayInsn = (FillArrayInsn) insn;\n\t\t\t\t\tint target = fillArrayInsn.getTarget();\n\t\t\t\t\tInsnNode arrDataInsn = getInsnAtOffset(insnByOffset, target);\n\t\t\t\t\tif (arrDataInsn != null && arrDataInsn.getType() == InsnType.FILL_ARRAY_DATA) {\n\t\t\t\t\t\tfillArrayInsn.setArrayData((FillArrayData) arrDataInsn);\n\t\t\t\t\t\tremoveInsn(insnByOffset, arrDataInsn);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new JadxRuntimeException(\"Payload for fill-array not found at \" + InsnUtils.formatOffset(target));\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void attachSwitchData(InsnNode[] insnByOffset, int offset, SwitchInsn sw) {\n\t\tint nextInsnOffset = getNextInsnOffset(insnByOffset, offset);\n\t\tint dataTarget = sw.getDataTarget();\n\t\tInsnNode switchDataInsn = getInsnAtOffset(insnByOffset, dataTarget);\n\t\tif (switchDataInsn != null && switchDataInsn.getType() == InsnType.SWITCH_DATA) {\n\t\t\tSwitchData data = (SwitchData) switchDataInsn;\n\t\t\tdata.fixTargets(offset);\n\t\t\tsw.attachSwitchData(data, nextInsnOffset);\n\t\t\tremoveInsn(insnByOffset, switchDataInsn);\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(\"Payload for switch not found at \" + InsnUtils.formatOffset(dataTarget));\n\t\t}\n\t}\n\n\tprivate static void mergeMoveResult(InsnNode[] insnByOffset, int offset, InsnNode insn, ArgType resType) {\n\t\tint nextInsnOffset = getNextInsnOffset(insnByOffset, offset);\n\t\tif (nextInsnOffset == -1) {\n\t\t\treturn;\n\t\t}\n\t\tInsnNode nextInsn = insnByOffset[nextInsnOffset];\n\t\tif (nextInsn.getType() != InsnType.MOVE_RESULT) {\n\t\t\treturn;\n\t\t}\n\t\tRegisterArg moveRes = nextInsn.getResult();\n\t\tinsn.setResult(moveRes.duplicate(resType));\n\t\tinsn.copyAttributesFrom(nextInsn);\n\t\tremoveInsn(insnByOffset, nextInsn);\n\t}\n\n\tprivate static void addJump(MethodNode mth, InsnNode[] insnByOffset, int offset, int target) {\n\t\ttry {\n\t\t\tinsnByOffset[target].addAttr(AType.JUMP, new JumpInfo(offset, target));\n\t\t} catch (Exception e) {\n\t\t\tmth.addError(\"Failed to set jump: \" + InsnUtils.formatOffset(offset) + \" -> \" + InsnUtils.formatOffset(target), e);\n\t\t}\n\t}\n\n\tpublic static int getNextInsnOffset(InsnNode[] insnByOffset, int offset) {\n\t\tint len = insnByOffset.length;\n\t\tfor (int i = offset + 1; i < len; i++) {\n\t\t\tInsnNode insnNode = insnByOffset[i];\n\t\t\tif (insnNode != null && insnNode.getType() != InsnType.NOP) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\t@Nullable\n\tprivate static InsnNode getInsnAtOffset(InsnNode[] insnByOffset, int offset) {\n\t\tint len = insnByOffset.length;\n\t\tfor (int i = offset; i < len; i++) {\n\t\t\tInsnNode insnNode = insnByOffset[i];\n\t\t\tif (insnNode != null && insnNode.getType() != InsnType.NOP) {\n\t\t\t\treturn insnNode;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static void removeInsn(InsnNode[] insnByOffset, InsnNode insn) {\n\t\tinsnByOffset[insn.getOffset()] = null;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/ProcessMethodsForInline.java",
    "content": "package jadx.core.dex.visitors;\n\nimport jadx.core.Consts;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.usage.UsageInfoVisitor;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"ProcessMethodsForInline\",\n\t\tdesc = \"Mark methods for future inline\",\n\t\trunAfter = {\n\t\t\t\tUsageInfoVisitor.class\n\t\t}\n)\npublic class ProcessMethodsForInline extends AbstractVisitor {\n\n\tprivate boolean inlineMethods;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tinlineMethods = root.getArgs().isInlineMethods();\n\t}\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) throws JadxException {\n\t\tif (!inlineMethods) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\tif (canInline(mth)) {\n\t\t\t\tmth.add(AFlag.METHOD_CANDIDATE_FOR_INLINE);\n\t\t\t\tfixClassDependencies(mth);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean canInline(MethodNode mth) {\n\t\tif (mth.isNoCode() || mth.contains(AFlag.DONT_GENERATE)) {\n\t\t\treturn false;\n\t\t}\n\t\tAccessInfo accessFlags = mth.getAccessFlags();\n\t\tboolean isSynthetic = accessFlags.isSynthetic() || mth.getName().contains(\"$\");\n\t\treturn isSynthetic && canInlineMethod(mth, accessFlags);\n\t}\n\n\tprivate static boolean canInlineMethod(MethodNode mth, AccessInfo accessFlags) {\n\t\tif (accessFlags.isStatic()) {\n\t\t\treturn true;\n\t\t}\n\t\treturn mth.isConstructor() && mth.root().getArgs().isInlineAnonymousClasses();\n\t}\n\n\tprivate static void fixClassDependencies(MethodNode mth) {\n\t\tClassNode parentClass = mth.getTopParentClass();\n\t\tfor (MethodNode useInMth : mth.getUseIn()) {\n\t\t\t// remove possible cross dependency\n\t\t\t// to force class with inline method to be processed before its usage\n\t\t\tClassNode useTopCls = useInMth.getTopParentClass();\n\t\t\tif (useTopCls != parentClass) {\n\t\t\t\tparentClass.removeDependency(useTopCls);\n\t\t\t\tuseTopCls.addCodegenDep(parentClass);\n\t\t\t\tif (Consts.DEBUG_USAGE) {\n\t\t\t\t\tparentClass.addDebugComment(\"Remove dependency: \" + useTopCls + \" to inline \" + mth);\n\t\t\t\t\tuseTopCls.addDebugComment(\"Add dependency: \" + parentClass + \" to inline \" + mth);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"ProcessMethodsForInline\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/ReplaceNewArray.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.IdentityHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.SortedMap;\nimport java.util.TreeMap;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.nodes.CodeFeaturesAttr;\nimport jadx.core.dex.attributes.nodes.CodeFeaturesAttr.CodeFeature;\nimport jadx.core.dex.instructions.FilledNewArrayNode;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.NewArrayNode;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IFieldInfoRef;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.utils.InsnList;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"ReplaceNewArray\",\n\t\tdesc = \"Replace new-array and sequence of array-put to new filled-array instruction\",\n\t\trunAfter = CodeShrinkVisitor.class\n)\npublic class ReplaceNewArray extends AbstractVisitor {\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (!CodeFeaturesAttr.contains(mth, CodeFeature.NEW_ARRAY)) {\n\t\t\treturn;\n\t\t}\n\t\tInsnRemover remover = new InsnRemover(mth);\n\t\tint k = 0;\n\t\twhile (true) {\n\t\t\tboolean changed = false;\n\t\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\t\tList<InsnNode> insnList = block.getInstructions();\n\t\t\t\tint size = insnList.size();\n\t\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\t\tchanged |= processInsn(mth, insnList, i, remover);\n\t\t\t\t}\n\t\t\t\tremover.performForBlock(block);\n\t\t\t}\n\t\t\tif (changed) {\n\t\t\t\tCodeShrinkVisitor.shrinkMethod(mth);\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (k++ > 100) {\n\t\t\t\tmth.addWarnComment(\"Reached limit for ReplaceNewArray iterations\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean processInsn(MethodNode mth, List<InsnNode> instructions, int i, InsnRemover remover) {\n\t\tInsnNode insn = instructions.get(i);\n\t\tif (insn.getType() == InsnType.NEW_ARRAY && !insn.contains(AFlag.REMOVE)) {\n\t\t\treturn processNewArray(mth, (NewArrayNode) insn, instructions, remover);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean processNewArray(MethodNode mth,\n\t\t\tNewArrayNode newArrayInsn, List<InsnNode> instructions, InsnRemover remover) {\n\t\tObject arrayLenConst = InsnUtils.getConstValueByArg(mth.root(), newArrayInsn.getArg(0));\n\t\tif (!(arrayLenConst instanceof LiteralArg)) {\n\t\t\treturn false;\n\t\t}\n\t\tint len = (int) ((LiteralArg) arrayLenConst).getLiteral();\n\t\tif (len == 0) {\n\t\t\treturn false;\n\t\t}\n\t\tArgType arrType = newArrayInsn.getArrayType();\n\t\tArgType elemType = arrType.getArrayElement();\n\t\tboolean allowMissingKeys = arrType.getArrayDimension() == 1 && elemType.isPrimitive();\n\t\tint minLen = allowMissingKeys ? len / 2 : len;\n\n\t\tRegisterArg arrArg = newArrayInsn.getResult();\n\t\tList<RegisterArg> useList = arrArg.getSVar().getUseList();\n\t\tif (useList.size() < minLen) {\n\t\t\treturn false;\n\t\t}\n\t\t// quick check if APUT is used\n\t\tboolean foundPut = false;\n\t\tfor (RegisterArg registerArg : useList) {\n\t\t\tInsnNode parentInsn = registerArg.getParentInsn();\n\t\t\tif (parentInsn != null && parentInsn.getType() == InsnType.APUT) {\n\t\t\t\tfoundPut = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!foundPut) {\n\t\t\treturn false;\n\t\t}\n\t\t// collect put instructions sorted by array index\n\t\tSortedMap<Long, InsnNode> arrPuts = new TreeMap<>();\n\t\tInsnNode firstNotAPutUsage = null;\n\t\tfor (RegisterArg registerArg : useList) {\n\t\t\tInsnNode parentInsn = registerArg.getParentInsn();\n\t\t\tif (parentInsn == null\n\t\t\t\t\t|| parentInsn.getType() != InsnType.APUT\n\t\t\t\t\t|| !arrArg.sameRegAndSVar(parentInsn.getArg(0))) {\n\t\t\t\tif (firstNotAPutUsage == null) {\n\t\t\t\t\tfirstNotAPutUsage = parentInsn;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tObject constVal = InsnUtils.getConstValueByArg(mth.root(), parentInsn.getArg(1));\n\t\t\tif (!(constVal instanceof LiteralArg)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tlong index = ((LiteralArg) constVal).getLiteral();\n\t\t\tif (index >= len) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (arrPuts.containsKey(index)) {\n\t\t\t\t// stop on index rewrite\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tarrPuts.put(index, parentInsn);\n\t\t}\n\t\tif (arrPuts.size() < minLen) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!verifyPutInsns(arrArg, instructions, arrPuts)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// checks complete, apply\n\t\tInsnNode filledArr = new FilledNewArrayNode(elemType, len);\n\t\tfilledArr.setResult(arrArg.duplicate());\n\t\tfilledArr.copyAttributesFrom(newArrayInsn);\n\t\tfilledArr.inheritMetadata(newArrayInsn);\n\t\tfilledArr.setOffset(newArrayInsn.getOffset());\n\n\t\tlong prevIndex = -1;\n\t\tfor (Map.Entry<Long, InsnNode> entry : arrPuts.entrySet()) {\n\t\t\tlong index = entry.getKey();\n\t\t\tif (index != prevIndex) {\n\t\t\t\t// use zero for missing keys\n\t\t\t\tfor (long i = prevIndex + 1; i < index; i++) {\n\t\t\t\t\tfilledArr.addArg(InsnArg.lit(0, elemType));\n\t\t\t\t}\n\t\t\t}\n\t\t\tInsnNode put = entry.getValue();\n\t\t\tfilledArr.addArg(replaceConstInArg(mth, put.getArg(2)));\n\t\t\tremover.addAndUnbind(put);\n\t\t\tprevIndex = index;\n\t\t}\n\t\t// add missing trailing zeros\n\t\tfor (long i = prevIndex + 1; i < len; i++) {\n\t\t\tfilledArr.addArg(InsnArg.lit(0, elemType));\n\t\t}\n\t\tremover.addAndUnbind(newArrayInsn);\n\n\t\t// place new insn at last array put or before first usage\n\t\tInsnNode lastPut = arrPuts.get(arrPuts.lastKey());\n\t\tint newInsnPos = InsnList.getIndex(instructions, lastPut);\n\t\tif (firstNotAPutUsage != null) {\n\t\t\tint idx = InsnList.getIndex(instructions, firstNotAPutUsage);\n\t\t\tif (idx != -1) {\n\t\t\t\t// TODO: check that all args already assigned\n\t\t\t\tnewInsnPos = Math.min(idx, newInsnPos);\n\t\t\t}\n\t\t}\n\t\tinstructions.add(newInsnPos, filledArr);\n\t\treturn true;\n\t}\n\n\tprivate static boolean verifyPutInsns(RegisterArg arrReg, List<InsnNode> insnList, SortedMap<Long, InsnNode> arrPuts) {\n\t\tList<InsnNode> puts = new ArrayList<>(arrPuts.values());\n\t\tint putsCount = puts.size();\n\t\t// expect all puts to be in the same block\n\t\tif (insnList.size() < putsCount) {\n\t\t\treturn false;\n\t\t}\n\t\tSet<InsnNode> insnSet = Collections.newSetFromMap(new IdentityHashMap<>());\n\t\tinsnSet.addAll(insnList);\n\t\tif (!insnSet.containsAll(puts)) {\n\t\t\treturn false;\n\t\t}\n\t\t// array arg shouldn't be used in puts insns\n\t\tfor (InsnNode put : puts) {\n\t\t\tInsnArg putArg = put.getArg(2);\n\t\t\tif (putArg.isUseVar(arrReg)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static InsnArg replaceConstInArg(MethodNode mth, InsnArg valueArg) {\n\t\tif (valueArg.isLiteral()) {\n\t\t\tIFieldInfoRef f = mth.getParentClass().getConstFieldByLiteralArg((LiteralArg) valueArg);\n\t\t\tif (f != null) {\n\t\t\t\tInsnNode fGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);\n\t\t\t\tInsnArg arg = InsnArg.wrapArg(fGet);\n\t\t\t\tModVisitor.addFieldUsage(f, mth);\n\t\t\t\treturn arg;\n\t\t\t}\n\t\t}\n\t\treturn valueArg.duplicate();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.io.File;\nimport java.io.PrintWriter;\nimport java.nio.charset.StandardCharsets;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.JadxArgs;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\n\npublic class SaveCode {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SaveCode.class);\n\n\tprivate SaveCode() {\n\t}\n\n\tpublic static void save(File dir, ClassNode cls, ICodeInfo code) {\n\t\tif (cls.contains(AFlag.DONT_GENERATE)) {\n\t\t\treturn;\n\t\t}\n\t\tif (code == null) {\n\t\t\tthrow new JadxRuntimeException(\"Code not generated for class \" + cls.getFullName());\n\t\t}\n\t\tif (code == ICodeInfo.EMPTY) {\n\t\t\treturn;\n\t\t}\n\t\tString codeStr = code.getCodeStr();\n\t\tif (codeStr.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tJadxArgs args = cls.root().getArgs();\n\t\tif (args.isSkipFilesSave()) {\n\t\t\treturn;\n\t\t}\n\t\tString fileName = cls.getClassInfo().getAliasFullPath() + getFileExtension(cls.root());\n\t\tif (!args.getSecurity().isValidEntryName(fileName)) {\n\t\t\treturn;\n\t\t}\n\t\tsave(codeStr, new File(dir, fileName));\n\t}\n\n\tpublic static void save(ICodeInfo codeInfo, File file) {\n\t\tsave(codeInfo.getCodeStr(), file);\n\t}\n\n\tpublic static void save(String code, File file) {\n\t\tFile outFile = FileUtils.prepareFile(file);\n\t\ttry (PrintWriter out = new PrintWriter(outFile, StandardCharsets.UTF_8)) {\n\t\t\tout.println(code);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Save file error\", e);\n\t\t}\n\t}\n\n\tpublic static String getFileExtension(RootNode root) {\n\t\tJadxArgs.OutputFormatEnum outputFormat = root.getArgs().getOutputFormat();\n\t\tswitch (outputFormat) {\n\t\t\tcase JAVA:\n\t\t\t\treturn \".java\";\n\n\t\t\tcase JSON:\n\t\t\t\treturn \".json\";\n\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unknown output format: \" + outputFormat);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/ShadowFieldVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.dex.visitors.typeinference.TypeInferenceVisitor;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"ShadowFieldVisitor\",\n\t\tdesc = \"Fix shadowed field access\",\n\t\trunAfter = TypeInferenceVisitor.class,\n\t\trunBefore = CodeShrinkVisitor.class\n)\npublic class ShadowFieldVisitor extends AbstractVisitor {\n\tprivate Map<String, FieldFixInfo> fixInfoMap;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tMap<String, FieldFixInfo> map = new HashMap<>();\n\t\tfor (ClassNode cls : root.getClasses(true)) {\n\t\t\tMap<FieldInfo, FieldFixType> fieldFixMap = searchShadowedFields(cls);\n\t\t\tif (!fieldFixMap.isEmpty()) {\n\t\t\t\tFieldFixInfo fixInfo = new FieldFixInfo();\n\t\t\t\tfixInfo.fieldFixMap = fieldFixMap;\n\t\t\t\tmap.put(cls.getRawName(), fixInfo);\n\t\t\t}\n\t\t}\n\t\tthis.fixInfoMap = map;\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tfixShadowFieldAccess(mth, fixInfoMap);\n\t}\n\n\tprivate static class FieldFixInfo {\n\t\tMap<FieldInfo, FieldFixType> fieldFixMap;\n\t}\n\n\tprivate enum FieldFixType {\n\t\tSUPER,\n\t\tCAST\n\t}\n\n\tprivate static Map<FieldInfo, FieldFixType> searchShadowedFields(ClassNode thisCls) {\n\t\tList<FieldNode> allFields = collectAllInstanceFields(thisCls);\n\t\tif (allFields.isEmpty()) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tMap<String, List<FieldNode>> mapByName = groupByName(allFields);\n\t\tmapByName.entrySet().removeIf(entry -> entry.getValue().size() == 1);\n\t\tif (mapByName.isEmpty()) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tMap<FieldInfo, FieldFixType> fixMap = new HashMap<>();\n\t\tfor (List<FieldNode> fields : mapByName.values()) {\n\t\t\tboolean fromThisCls = fields.get(0).getParentClass() == thisCls;\n\t\t\tif (fromThisCls && fields.size() == 2) {\n\t\t\t\t// only one super class contains same field => can use super\n\t\t\t\tFieldNode otherField = fields.get(1);\n\t\t\t\tif (otherField.getParentClass() != thisCls) {\n\t\t\t\t\tfixMap.put(otherField.getFieldInfo(), FieldFixType.SUPER);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// several super classes contains same field => can't use super, need cast to exact class\n\t\t\t\tfor (FieldNode field : fields) {\n\t\t\t\t\tif (field.getParentClass() != thisCls) {\n\t\t\t\t\t\tfixMap.put(field.getFieldInfo(), FieldFixType.CAST);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn fixMap;\n\t}\n\n\tprivate static Map<String, List<FieldNode>> groupByName(List<FieldNode> allFields) {\n\t\tMap<String, List<FieldNode>> groupByName = new HashMap<>(allFields.size());\n\t\tfor (FieldNode field : allFields) {\n\t\t\tgroupByName\n\t\t\t\t\t.computeIfAbsent(field.getName(), k -> new ArrayList<>())\n\t\t\t\t\t.add(field);\n\t\t}\n\t\treturn groupByName;\n\t}\n\n\tprivate static List<FieldNode> collectAllInstanceFields(ClassNode cls) {\n\t\tList<FieldNode> fieldsList = new ArrayList<>();\n\t\tSet<ClassNode> visited = new HashSet<>();\n\t\tClassNode currentClass = cls;\n\t\twhile (currentClass != null) {\n\t\t\tif (!visited.add(currentClass)) {\n\t\t\t\tString msg = \"Found 'super' loop in classes: \" + visited;\n\t\t\t\tvisited.forEach(c -> c.addWarnComment(msg));\n\t\t\t\treturn fieldsList;\n\t\t\t}\n\t\t\tfor (FieldNode field : currentClass.getFields()) {\n\t\t\t\tif (!field.getAccessFlags().isStatic()) {\n\t\t\t\t\tfieldsList.add(field);\n\t\t\t\t}\n\t\t\t}\n\t\t\tArgType superClass = currentClass.getSuperClass();\n\t\t\tif (superClass == null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcurrentClass = cls.root().resolveClass(superClass);\n\t\t}\n\t\treturn fieldsList;\n\t}\n\n\tprivate static void fixShadowFieldAccess(MethodNode mth, Map<String, FieldFixInfo> fixInfoMap) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tprocessInsn(mth, insn, fixInfoMap);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void processInsn(MethodNode mth, InsnNode insn, Map<String, FieldFixInfo> fixInfoMap) {\n\t\tFieldInfo fieldInfo = getFieldInfo(insn);\n\t\tif (fieldInfo == null) {\n\t\t\treturn;\n\t\t}\n\t\tInsnArg arg = insn.getArg(insn.getArgsCount() - 1);\n\t\tArgType type = arg.getType();\n\t\tif (!type.isTypeKnown() || !type.isObject()) {\n\t\t\treturn;\n\t\t}\n\t\tFieldFixInfo fieldFixInfo = fixInfoMap.get(type.getObject());\n\t\tif (fieldFixInfo == null) {\n\t\t\treturn;\n\t\t}\n\t\tFieldFixType fieldFixType = fieldFixInfo.fieldFixMap.get(fieldInfo);\n\t\tif (fieldFixType == null) {\n\t\t\treturn;\n\t\t}\n\t\tfixFieldAccess(mth, fieldInfo, fieldFixType, arg);\n\t}\n\n\t@Nullable\n\tprivate static FieldInfo getFieldInfo(InsnNode insn) {\n\t\tswitch (insn.getType()) {\n\t\t\tcase IPUT:\n\t\t\tcase IGET:\n\t\t\t\treturn (FieldInfo) ((IndexInsnNode) insn).getIndex();\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static void fixFieldAccess(MethodNode mth, FieldInfo fieldInfo, FieldFixType fieldFixType, InsnArg arg) {\n\t\tif (fieldFixType == FieldFixType.SUPER) {\n\t\t\tif (arg.isThis()) {\n\t\t\t\t// convert 'this' to 'super'\n\t\t\t\targ.add(AFlag.SUPER);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t// apply cast\n\t\tInsnNode castInsn = new IndexInsnNode(InsnType.CAST, fieldInfo.getDeclClass().getType(), 1);\n\t\tcastInsn.addArg(arg.duplicate());\n\t\tcastInsn.add(AFlag.SYNTHETIC);\n\t\tcastInsn.add(AFlag.EXPLICIT_CAST);\n\t\targ.wrapInstruction(mth, castInsn, false);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/SignatureProcessor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.deobf.NameMapper;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.nodes.parser.SignatureParser;\nimport jadx.core.dex.nodes.utils.TypeUtils;\nimport jadx.core.dex.visitors.typeinference.TypeCompareEnum;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxException;\n\npublic class SignatureProcessor extends AbstractVisitor {\n\tprivate RootNode root;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tthis.root = root;\n\t}\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) throws JadxException {\n\t\tparseClassSignature(cls);\n\t\tfor (FieldNode field : cls.getFields()) {\n\t\t\tparseFieldSignature(field);\n\t\t}\n\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\tparseMethodSignature(mth);\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate void parseClassSignature(ClassNode cls) {\n\t\tSignatureParser sp = SignatureParser.fromNode(cls);\n\t\tif (sp == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tList<ArgType> generics = sp.consumeGenericTypeParameters();\n\t\t\tArgType superClass = processSuperType(cls, sp.consumeType());\n\t\t\tList<ArgType> interfaces = processInterfaces(cls, sp.consumeTypeList());\n\t\t\tList<ArgType> resultGenerics = fixTypeParamDeclarations(cls, generics, superClass, interfaces);\n\t\t\tcls.updateGenericClsData(resultGenerics, superClass, interfaces);\n\t\t} catch (Exception e) {\n\t\t\tcls.addWarnComment(\"Failed to parse class signature: \" + sp.getSignature(), e);\n\t\t}\n\t}\n\n\tprivate ArgType processSuperType(ClassNode cls, ArgType parsedType) {\n\t\tArgType superType = cls.getSuperClass();\n\t\tif (Objects.equals(parsedType.getObject(), cls.getClassInfo().getType().getObject())) {\n\t\t\tcls.addWarnComment(\"Incorrect class signature: super class is equals to this class\");\n\t\t\treturn superType;\n\t\t}\n\t\treturn bestClsType(cls, parsedType, superType);\n\t}\n\n\t/**\n\t * Parse, validate and update class interfaces types.\n\t */\n\tprivate List<ArgType> processInterfaces(ClassNode cls, List<ArgType> parsedTypes) {\n\t\tList<ArgType> interfaces = cls.getInterfaces();\n\t\tif (parsedTypes.isEmpty()) {\n\t\t\treturn interfaces;\n\t\t}\n\t\tint parsedCount = parsedTypes.size();\n\t\tint interfacesCount = interfaces.size();\n\t\tList<ArgType> result = new ArrayList<>(interfacesCount);\n\t\tint count = Math.min(interfacesCount, parsedCount);\n\t\tfor (int i = 0; i < interfacesCount; i++) {\n\t\t\tif (i < count) {\n\t\t\t\tresult.add(bestClsType(cls, parsedTypes.get(i), interfaces.get(i)));\n\t\t\t} else {\n\t\t\t\tresult.add(interfaces.get(i));\n\t\t\t}\n\t\t}\n\t\tif (interfacesCount < parsedCount) {\n\t\t\tcls.addWarnComment(\"Unexpected interfaces in signature: \" + parsedTypes.subList(interfacesCount, parsedCount));\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Add missing type parameters from super type and interfaces to make code compilable\n\t */\n\tprivate static List<ArgType> fixTypeParamDeclarations(ClassNode cls,\n\t\t\tList<ArgType> generics, ArgType superClass, List<ArgType> interfaces) {\n\t\tif (interfaces.isEmpty() && superClass.equals(ArgType.OBJECT)) {\n\t\t\treturn generics;\n\t\t}\n\t\tSet<String> typeParams = new HashSet<>();\n\t\tsuperClass.visitTypes(t -> addGenericType(typeParams, t));\n\t\tinterfaces.forEach(i -> i.visitTypes(t -> addGenericType(typeParams, t)));\n\t\tif (typeParams.isEmpty()) {\n\t\t\treturn generics;\n\t\t}\n\t\tList<ArgType> knownTypeParams;\n\t\tif (cls.isInner()) {\n\t\t\tknownTypeParams = new ArrayList<>(generics);\n\t\t\tcls.visitParentClasses(p -> knownTypeParams.addAll(p.getGenericTypeParameters()));\n\t\t} else {\n\t\t\tknownTypeParams = generics;\n\t\t}\n\t\tfor (ArgType declTypeParam : knownTypeParams) {\n\t\t\ttypeParams.remove(declTypeParam.getObject());\n\t\t}\n\t\tif (typeParams.isEmpty()) {\n\t\t\treturn generics;\n\t\t}\n\t\tcls.addInfoComment(\"Add missing generic type declarations: \" + typeParams);\n\t\tList<ArgType> fixedGenerics = new ArrayList<>(generics.size() + typeParams.size());\n\t\tfixedGenerics.addAll(generics);\n\t\ttypeParams.stream().sorted().map(ArgType::genericType).forEach(fixedGenerics::add);\n\t\treturn fixedGenerics;\n\t}\n\n\tprivate static @Nullable Object addGenericType(Set<String> usedTypeParameters, ArgType t) {\n\t\tif (t.isGenericType()) {\n\t\t\tusedTypeParameters.add(t.getObject());\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate ArgType bestClsType(ClassNode cls, ArgType candidateType, ArgType currentType) {\n\t\tif (validateClsType(cls, candidateType)) {\n\t\t\treturn candidateType;\n\t\t}\n\t\treturn currentType;\n\t}\n\n\tprivate boolean validateClsType(ClassNode cls, ArgType candidateType) {\n\t\tif (candidateType == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!candidateType.isObject()) {\n\t\t\tcls.addWarnComment(\"Incorrect class signature, class is not an object: \" + candidateType);\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate void parseFieldSignature(FieldNode field) {\n\t\tSignatureParser sp = SignatureParser.fromNode(field);\n\t\tif (sp == null) {\n\t\t\treturn;\n\t\t}\n\t\tClassNode cls = field.getParentClass();\n\t\ttry {\n\t\t\tArgType signatureType = sp.consumeType();\n\t\t\tif (signatureType == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!validateInnerType(signatureType)) {\n\t\t\t\tfield.addWarnComment(\"Incorrect inner types in field signature: \" + sp.getSignature());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tArgType type = root.getTypeUtils().expandTypeVariables(cls, signatureType);\n\t\t\tif (!validateParsedType(type, field.getType())) {\n\t\t\t\tfield.addInfoComment(\"Incorrect field signature: \" + sp.getSignature());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfield.updateType(type);\n\t\t} catch (Exception e) {\n\t\t\tcls.addWarnComment(\"Field signature parse error: \" + field.getName(), e);\n\t\t}\n\t}\n\n\tprivate void parseMethodSignature(MethodNode mth) {\n\t\tSignatureParser sp = SignatureParser.fromNode(mth);\n\t\tif (sp == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tList<ArgType> typeParameters = sp.consumeGenericTypeParameters();\n\t\t\tList<ArgType> parsedArgTypes = sp.consumeMethodArgs(mth.getMethodInfo().getArgsCount());\n\t\t\tArgType parsedRetType = sp.consumeType();\n\n\t\t\tif (!validateInnerType(parsedRetType) || !validateInnerType(parsedArgTypes)) {\n\t\t\t\tmth.addWarnComment(\"Incorrect inner types in method signature: \" + sp.getSignature());\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tmth.updateTypeParameters(typeParameters); // apply before expand args\n\t\t\tTypeUtils typeUtils = root.getTypeUtils();\n\t\t\tArgType retType = typeUtils.expandTypeVariables(mth, parsedRetType);\n\t\t\tList<ArgType> argTypes = Utils.collectionMap(parsedArgTypes, t -> typeUtils.expandTypeVariables(mth, t));\n\n\t\t\tif (!validateAndApplyTypes(mth, sp, retType, argTypes)) {\n\t\t\t\t// bad types -> reset typed parameters\n\t\t\t\tmth.updateTypeParameters(Collections.emptyList());\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Failed to parse method signature: \" + sp.getSignature(), e);\n\t\t}\n\t}\n\n\tprivate boolean validateAndApplyTypes(MethodNode mth, SignatureParser sp, ArgType retType, List<ArgType> argTypes) {\n\t\ttry {\n\t\t\tif (!validateParsedType(retType, mth.getMethodInfo().getReturnType())) {\n\t\t\t\tmth.addWarnComment(\"Incorrect return type in method signature: \" + sp.getSignature());\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tList<ArgType> checkedArgTypes = checkArgTypes(mth, sp, argTypes);\n\t\t\tif (checkedArgTypes == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tmth.updateTypes(Collections.unmodifiableList(checkedArgTypes), retType);\n\t\t\treturn true;\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Type validation failed for signature: \" + sp.getSignature(), e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate List<ArgType> checkArgTypes(MethodNode mth, SignatureParser sp, List<ArgType> parsedArgTypes) {\n\t\tMethodInfo mthInfo = mth.getMethodInfo();\n\t\tList<ArgType> mthArgTypes = mthInfo.getArgumentsTypes();\n\t\tint len = parsedArgTypes.size();\n\t\tif (len != mthArgTypes.size()) {\n\t\t\tif (mth.getParentClass().getAccessFlags().isEnum()) {\n\t\t\t\t// ignore for enums\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (mthInfo.isConstructor() && !mthArgTypes.isEmpty() && !parsedArgTypes.isEmpty()) {\n\t\t\t\t// add synthetic arg for outer class (see test TestGeneric8)\n\t\t\t\tList<ArgType> newArgTypes = new ArrayList<>(parsedArgTypes);\n\t\t\t\tnewArgTypes.add(0, mthArgTypes.get(0));\n\t\t\t\tif (newArgTypes.size() == mthArgTypes.size()) {\n\t\t\t\t\treturn newArgTypes;\n\t\t\t\t}\n\t\t\t}\n\t\t\tmth.addDebugComment(\"Incorrect args count in method signature: \" + sp.getSignature());\n\t\t\treturn null;\n\t\t}\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tArgType parsedType = parsedArgTypes.get(i);\n\t\t\tArgType mthArgType = mthArgTypes.get(i);\n\t\t\tif (!validateParsedType(parsedType, mthArgType)) {\n\t\t\t\tmth.addWarnComment(\"Incorrect types in method signature: \" + sp.getSignature());\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\treturn parsedArgTypes;\n\t}\n\n\tprivate boolean validateParsedType(ArgType parsedType, ArgType currentType) {\n\t\tTypeCompareEnum result = root.getTypeCompare().compareTypes(parsedType, currentType);\n\t\tif (result == TypeCompareEnum.UNKNOWN\n\t\t\t\t&& parsedType.isObject()\n\t\t\t\t&& !validateFullClsName(parsedType.getObject())) {\n\t\t\t// ignore external invalid class names: may be a reserved words or garbage\n\t\t\treturn false;\n\t\t}\n\t\treturn result != TypeCompareEnum.CONFLICT;\n\t}\n\n\tprivate boolean validateFullClsName(String fullClsName) {\n\t\tif (!NameMapper.isValidFullIdentifier(fullClsName)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (fullClsName.indexOf('.') > 0) {\n\t\t\tfor (String namePart : fullClsName.split(\"\\\\.\")) {\n\t\t\t\tif (!NameMapper.isValidIdentifier(namePart)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate boolean validateInnerType(List<ArgType> types) {\n\t\tfor (ArgType type : types) {\n\t\t\tif (!validateInnerType(type)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate boolean validateInnerType(ArgType type) {\n\t\tArgType innerType = type.getInnerType();\n\t\tif (innerType == null) {\n\t\t\treturn true;\n\t\t}\n\t\t// check in outer type has inner type as inner class\n\t\tArgType outerType = type.getOuterType();\n\t\tClassNode outerCls = root.resolveClass(outerType);\n\t\tif (outerCls == null) {\n\t\t\t// can't check class not found\n\t\t\treturn true;\n\t\t}\n\t\tString innerObj;\n\t\tif (innerType.getOuterType() != null) {\n\t\t\tinnerObj = innerType.getOuterType().getObject();\n\t\t\t// \"next\" inner type will be processed at end of method\n\t\t} else {\n\t\t\tinnerObj = innerType.getObject();\n\t\t}\n\t\tif (!innerObj.contains(\".\")) {\n\t\t\t// short reference\n\t\t\tfor (ClassNode innerClass : outerCls.getInnerClasses()) {\n\t\t\t\tif (innerClass.getShortName().equals(innerObj)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t// full name\n\t\tClassNode innerCls = root.resolveClass(innerObj);\n\t\tif (innerCls == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!innerCls.getParentClass().equals(outerCls)) {\n\t\t\t// not inner => fixing\n\t\t\touterCls.addInnerClass(innerCls);\n\t\t\tinnerCls.getClassInfo().convertToInner(outerCls);\n\t\t}\n\t\treturn validateInnerType(innerType);\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"SignatureProcessor\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java",
    "content": "package jadx.core.dex.visitors;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.Consts;\nimport jadx.core.codegen.TypeGen;\nimport jadx.core.deobf.NameMapper;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.ArithNode;\nimport jadx.core.dex.instructions.ArithOp;\nimport jadx.core.dex.instructions.ConstStringNode;\nimport jadx.core.dex.instructions.FilledNewArrayNode;\nimport jadx.core.dex.instructions.IfNode;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.InvokeType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.instructions.mods.TernaryInsn;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.regions.conditions.IfCondition;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.dex.visitors.typeinference.TypeCompareEnum;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.InsnList;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class SimplifyVisitor extends AbstractVisitor {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SimplifyVisitor.class);\n\n\tprivate MethodInfo stringGetBytesMth;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tstringGetBytesMth = MethodInfo.fromDetails(\n\t\t\t\troot,\n\t\t\t\tClassInfo.fromType(root, ArgType.STRING),\n\t\t\t\t\"getBytes\",\n\t\t\t\tCollections.emptyList(),\n\t\t\t\tArgType.array(ArgType.BYTE));\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tboolean changed = false;\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tif (simplifyBlock(mth, block)) {\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t\tif (changed || mth.contains(AFlag.REQUEST_CODE_SHRINK)) {\n\t\t\tCodeShrinkVisitor.shrinkMethod(mth);\n\t\t}\n\t}\n\n\tprivate boolean simplifyBlock(MethodNode mth, BlockNode block) {\n\t\tboolean changed = false;\n\t\tList<InsnNode> list = block.getInstructions();\n\t\tfor (int i = 0; i < list.size(); i++) {\n\t\t\tInsnNode insn = list.get(i);\n\t\t\tint insnCount = list.size();\n\t\t\tInsnNode modInsn = simplifyInsn(mth, insn, null);\n\t\t\tif (modInsn != null) {\n\t\t\t\tmodInsn.rebindArgs();\n\t\t\t\tif (i < list.size() && list.get(i) == insn) {\n\t\t\t\t\tlist.set(i, modInsn);\n\t\t\t\t} else {\n\t\t\t\t\tint idx = InsnList.getIndex(list, insn);\n\t\t\t\t\tif (idx == -1) {\n\t\t\t\t\t\tthrow new JadxRuntimeException(\"Failed to replace insn\");\n\t\t\t\t\t}\n\t\t\t\t\tlist.set(idx, modInsn);\n\t\t\t\t}\n\t\t\t\tif (list.size() < insnCount) {\n\t\t\t\t\t// some insns removed => restart block processing\n\t\t\t\t\tsimplifyBlock(mth, block);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t\treturn changed;\n\t}\n\n\tprivate void simplifyArgs(MethodNode mth, InsnNode insn) {\n\t\tboolean changed = false;\n\t\tfor (InsnArg arg : insn.getArguments()) {\n\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\tInsnNode replaceInsn = simplifyInsn(mth, wrapInsn, insn);\n\t\t\t\tif (replaceInsn != null) {\n\t\t\t\t\targ.wrapInstruction(mth, replaceInsn, false);\n\t\t\t\t\tInsnRemover.unbindInsn(mth, wrapInsn);\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (changed) {\n\t\t\tinsn.rebindArgs();\n\t\t\tmth.add(AFlag.REQUEST_CODE_SHRINK);\n\t\t}\n\t}\n\n\tprivate InsnNode simplifyInsn(MethodNode mth, InsnNode insn, @Nullable InsnNode parentInsn) {\n\t\tif (insn.contains(AFlag.DONT_GENERATE)) {\n\t\t\treturn null;\n\t\t}\n\t\tsimplifyArgs(mth, insn);\n\t\tswitch (insn.getType()) {\n\t\t\tcase ARITH:\n\t\t\t\treturn simplifyArith((ArithNode) insn);\n\n\t\t\tcase IF:\n\t\t\t\tsimplifyIf(mth, (IfNode) insn);\n\t\t\t\tbreak;\n\t\t\tcase TERNARY:\n\t\t\t\tsimplifyTernary(mth, (TernaryInsn) insn);\n\t\t\t\tbreak;\n\n\t\t\tcase INVOKE:\n\t\t\t\treturn convertInvoke(mth, (InvokeNode) insn);\n\n\t\t\tcase IPUT:\n\t\t\tcase SPUT:\n\t\t\t\treturn convertFieldArith(mth, insn);\n\n\t\t\tcase CAST:\n\t\t\tcase CHECK_CAST:\n\t\t\t\treturn processCast(mth, (IndexInsnNode) insn, parentInsn);\n\n\t\t\tcase MOVE:\n\t\t\t\tInsnArg firstArg = insn.getArg(0);\n\t\t\t\tif (firstArg.isLiteral()) {\n\t\t\t\t\tInsnNode constInsn = new InsnNode(InsnType.CONST, 1);\n\t\t\t\t\tconstInsn.setResult(insn.getResult());\n\t\t\t\t\tconstInsn.addArg(firstArg);\n\t\t\t\t\tconstInsn.copyAttributesFrom(insn);\n\t\t\t\t\treturn constInsn;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase CONSTRUCTOR:\n\t\t\t\treturn simplifyStringConstructor(mth, (ConstructorInsn) insn);\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate InsnNode simplifyStringConstructor(MethodNode mth, ConstructorInsn insn) {\n\t\tif (insn.getCallMth().getDeclClass().getType().equals(ArgType.STRING)\n\t\t\t\t&& insn.getArgsCount() != 0\n\t\t\t\t&& insn.getArg(0).isInsnWrap()) {\n\t\t\tInsnNode arrInsn = ((InsnWrapArg) insn.getArg(0)).getWrapInsn();\n\t\t\tif (arrInsn.getType() == InsnType.FILLED_NEW_ARRAY\n\t\t\t\t\t&& arrInsn.getArgsCount() != 0) {\n\t\t\t\tArgType elemType = ((FilledNewArrayNode) arrInsn).getElemType();\n\t\t\t\tif (elemType == ArgType.BYTE || elemType == ArgType.CHAR) {\n\t\t\t\t\tint printable = 0;\n\t\t\t\t\tbyte[] arr = new byte[arrInsn.getArgsCount()];\n\t\t\t\t\tfor (int i = 0; i < arr.length; i++) {\n\t\t\t\t\t\tInsnArg arrArg = arrInsn.getArg(i);\n\t\t\t\t\t\tif (!arrArg.isLiteral()) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tarr[i] = (byte) ((LiteralArg) arrArg).getLiteral();\n\t\t\t\t\t\tif (NameMapper.isPrintableChar((char) arr[i])) {\n\t\t\t\t\t\t\tprintable++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (printable >= arr.length - printable) {\n\t\t\t\t\t\tInsnNode constStr = new ConstStringNode(new String(arr));\n\t\t\t\t\t\tif (insn.getArgsCount() == 1) {\n\t\t\t\t\t\t\tconstStr.setResult(insn.getResult());\n\t\t\t\t\t\t\tconstStr.copyAttributesFrom(insn);\n\t\t\t\t\t\t\tInsnRemover.unbindArgUsage(mth, insn.getArg(0));\n\t\t\t\t\t\t\treturn constStr;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tInvokeNode in = new InvokeNode(stringGetBytesMth, InvokeType.VIRTUAL, 1);\n\t\t\t\t\t\t\tin.addArg(InsnArg.wrapArg(constStr));\n\t\t\t\t\t\t\tInsnArg bytesArg = InsnArg.wrapArg(in);\n\t\t\t\t\t\t\tbytesArg.setType(stringGetBytesMth.getReturnType());\n\t\t\t\t\t\t\tinsn.setArg(0, bytesArg);\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static InsnNode processCast(MethodNode mth, IndexInsnNode castInsn, @Nullable InsnNode parentInsn) {\n\t\tif (castInsn.contains(AFlag.EXPLICIT_CAST)) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnArg castArg = castInsn.getArg(0);\n\t\tArgType argType = castArg.getType();\n\n\t\t// Don't removes CHECK_CAST for wrapped INVOKE if invoked method returns different type\n\t\tif (castArg.isInsnWrap()) {\n\t\t\tInsnNode wrapInsn = ((InsnWrapArg) castArg).getWrapInsn();\n\t\t\tif (wrapInsn.getType() == InsnType.INVOKE) {\n\t\t\t\targType = ((InvokeNode) wrapInsn).getCallMth().getReturnType();\n\t\t\t}\n\t\t}\n\n\t\tArgType castToType = (ArgType) castInsn.getIndex();\n\t\tif (isArithWideUpCast(parentInsn, argType, castToType)) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!ArgType.isCastNeeded(mth.root(), argType, castToType)\n\t\t\t\t|| isCastDuplicate(castInsn)\n\t\t\t\t|| shadowedByOuterCast(mth.root(), castToType, parentInsn)) {\n\t\t\tInsnNode insnNode = new InsnNode(InsnType.MOVE, 1);\n\t\t\tinsnNode.setOffset(castInsn.getOffset());\n\t\t\tinsnNode.setResult(castInsn.getResult());\n\t\t\tinsnNode.addArg(castArg);\n\t\t\treturn insnNode;\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Keep cast to wide types in arith instructions,\n\t * because arguments type determine instruction used in result bytecode.\n\t * Example: (long) i << 32 - without 'long' cast will be used 'int shift' instruction and result\n\t * will be incorrect\n\t */\n\tprivate static boolean isArithWideUpCast(@Nullable InsnNode parentInsn, ArgType argType, ArgType castToType) {\n\t\tif (parentInsn != null\n\t\t\t\t&& parentInsn.getType() == InsnType.ARITH\n\t\t\t\t&& argType.isPrimitive() && castToType.isPrimitive()) {\n\t\t\treturn castToType.getRegCount() > argType.getRegCount();\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean isCastDuplicate(IndexInsnNode castInsn) {\n\t\tInsnArg arg = castInsn.getArg(0);\n\t\tif (arg.isRegister()) {\n\t\t\tSSAVar sVar = ((RegisterArg) arg).getSVar();\n\t\t\tif (sVar != null && sVar.getUseCount() == 1 && !sVar.isUsedInPhi()) {\n\t\t\t\tInsnNode assignInsn = sVar.getAssign().getParentInsn();\n\t\t\t\tif (assignInsn != null && assignInsn.getType() == InsnType.CHECK_CAST) {\n\t\t\t\t\tArgType assignCastType = (ArgType) ((IndexInsnNode) assignInsn).getIndex();\n\t\t\t\t\treturn assignCastType.equals(castInsn.getIndex());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean shadowedByOuterCast(RootNode root, ArgType castType, @Nullable InsnNode parentInsn) {\n\t\tif (parentInsn != null && parentInsn.getType() == InsnType.CAST) {\n\t\t\tArgType parentCastType = (ArgType) ((IndexInsnNode) parentInsn).getIndex();\n\t\t\tTypeCompareEnum result = root.getTypeCompare().compareTypes(parentCastType, castType);\n\t\t\treturn result.isNarrow();\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Simplify 'cmp' instruction in if condition\n\t */\n\tprivate static void simplifyIf(MethodNode mth, IfNode insn) {\n\t\tInsnArg f = insn.getArg(0);\n\t\tif (f.isInsnWrap()) {\n\t\t\tInsnNode wi = ((InsnWrapArg) f).getWrapInsn();\n\t\t\tif (wi.getType() == InsnType.CMP_L || wi.getType() == InsnType.CMP_G) {\n\t\t\t\tif (insn.getArg(1).isZeroLiteral()) {\n\t\t\t\t\tinsn.changeCondition(insn.getOp(), wi.getArg(0).duplicate(), wi.getArg(1).duplicate());\n\t\t\t\t\tInsnRemover.unbindInsn(mth, wi);\n\t\t\t\t} else {\n\t\t\t\t\tLOG.warn(\"TODO: cmp {}\", insn);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Simplify condition in ternary operation\n\t */\n\tprivate static void simplifyTernary(MethodNode mth, TernaryInsn insn) {\n\t\tIfCondition condition = insn.getCondition();\n\t\tif (condition.isCompare()) {\n\t\t\tsimplifyIf(mth, condition.getCompare().getInsn());\n\t\t} else {\n\t\t\tinsn.simplifyCondition();\n\t\t}\n\t}\n\n\t/**\n\t * Simplify chains of calls to StringBuilder#append() plus constructor of StringBuilder.\n\t * Those chains are usually automatically generated by the Java compiler when you create String\n\t * concatenations like <code>\"text \" + 1 + \" text\"</code>.\n\t */\n\tprivate static InsnNode convertInvoke(MethodNode mth, InvokeNode insn) {\n\t\tMethodInfo callMth = insn.getCallMth();\n\n\t\tif (callMth.getDeclClass().getFullName().equals(Consts.CLASS_STRING_BUILDER)\n\t\t\t\t&& callMth.getShortId().equals(Consts.MTH_TOSTRING_SIGNATURE)) {\n\t\t\tInsnArg instanceArg = insn.getArg(0);\n\t\t\tif (instanceArg.isInsnWrap()) {\n\t\t\t\t// Convert 'new StringBuilder(xxx).append(yyy).append(zzz).toString() to STRING_CONCAT insn\n\t\t\t\tList<InsnNode> callChain = flattenInsnChainUntil(insn, InsnType.CONSTRUCTOR);\n\t\t\t\treturn convertStringBuilderChain(mth, insn, callChain);\n\t\t\t}\n\t\t\tif (instanceArg.isRegister()) {\n\t\t\t\t// Convert 'StringBuilder sb = new StringBuilder(xxx); sb.append(yyy); String str = sb.toString();'\n\t\t\t\tList<InsnNode> useChain = collectUseChain(mth, insn, (RegisterArg) instanceArg);\n\t\t\t\treturn convertStringBuilderChain(mth, insn, useChain);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static List<InsnNode> collectUseChain(MethodNode mth, InvokeNode insn, RegisterArg instanceArg) {\n\t\tSSAVar sVar = instanceArg.getSVar();\n\t\tif (sVar.isUsedInPhi() || sVar.getUseCount() == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<InsnNode> useChain = new ArrayList<>(sVar.getUseCount() + 1);\n\t\tInsnNode assignInsn = sVar.getAssign().getParentInsn();\n\t\tif (assignInsn == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tuseChain.add(assignInsn);\n\t\tfor (RegisterArg reg : sVar.getUseList()) {\n\t\t\tInsnNode parentInsn = reg.getParentInsn();\n\t\t\tif (parentInsn == null) {\n\t\t\t\treturn Collections.emptyList();\n\t\t\t}\n\t\t\tuseChain.add(parentInsn);\n\t\t}\n\t\tint toStrIdx = InsnList.getIndex(useChain, insn);\n\t\tif (useChain.size() - 1 != toStrIdx) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tuseChain.remove(toStrIdx);\n\n\t\t// all insns must be in one block and sequential\n\t\tBlockNode assignBlock = BlockUtils.getBlockByInsn(mth, assignInsn);\n\t\tif (assignBlock == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<InsnNode> blockInsns = assignBlock.getInstructions();\n\t\tint assignIdx = InsnList.getIndex(blockInsns, assignInsn);\n\t\tint chainSize = useChain.size();\n\t\tint lastInsn = blockInsns.size() - assignIdx;\n\t\tif (lastInsn < chainSize) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tfor (int i = 1; i < chainSize; i++) {\n\t\t\tif (blockInsns.get(assignIdx + i) != useChain.get(i)) {\n\t\t\t\treturn Collections.emptyList();\n\t\t\t}\n\t\t}\n\t\treturn useChain;\n\t}\n\n\tprivate static InsnNode convertStringBuilderChain(MethodNode mth, InvokeNode toStrInsn, List<InsnNode> chain) {\n\t\ttry {\n\t\t\tint chainSize = chain.size();\n\t\t\tif (chainSize < 2) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tList<InsnArg> args = new ArrayList<>(chainSize);\n\t\t\tInsnNode firstInsn = chain.get(0);\n\t\t\tif (firstInsn.getType() != InsnType.CONSTRUCTOR) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tConstructorInsn constrInsn = (ConstructorInsn) firstInsn;\n\t\t\tif (constrInsn.getArgsCount() == 1) {\n\t\t\t\tArgType argType = constrInsn.getCallMth().getArgumentsTypes().get(0);\n\t\t\t\tif (!argType.isObject()) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\targs.add(constrInsn.getArg(0));\n\t\t\t}\n\t\t\tfor (int i = 1; i < chainSize; i++) {\n\t\t\t\tInsnNode chainInsn = chain.get(i);\n\t\t\t\tInsnArg arg = getArgFromAppend(chainInsn);\n\t\t\t\tif (arg == null) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\targs.add(arg);\n\t\t\t}\n\n\t\t\tboolean stringArgFound = false;\n\t\t\tfor (InsnArg arg : args) {\n\t\t\t\tif (arg.getType().equals(ArgType.STRING)) {\n\t\t\t\t\tstringArgFound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!stringArgFound) {\n\t\t\t\tString argStr = Utils.listToString(args, InsnArg::toShortString);\n\t\t\t\tmth.addDebugComment(\"TODO: convert one arg to string using `String.valueOf()`, args: \" + argStr);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// all check passed\n\t\t\tList<InsnArg> dupArgs = Utils.collectionMap(args, InsnArg::duplicate);\n\t\t\tList<InsnArg> simplifiedArgs = concatConstArgs(dupArgs);\n\t\t\tInsnNode concatInsn = new InsnNode(InsnType.STR_CONCAT, simplifiedArgs);\n\t\t\tconcatInsn.add(AFlag.SYNTHETIC);\n\t\t\tif (toStrInsn.getResult() == null && !toStrInsn.contains(AFlag.WRAPPED)) {\n\t\t\t\t// string concat without assign to variable will cause compilation error\n\t\t\t\tconcatInsn.setResult(mth.makeSyntheticRegArg(ArgType.STRING));\n\t\t\t} else {\n\t\t\t\tconcatInsn.setResult(toStrInsn.getResult());\n\t\t\t}\n\t\t\tconcatInsn.copyAttributesFrom(toStrInsn);\n\t\t\tremoveStringBuilderInsns(mth, toStrInsn, chain);\n\t\t\treturn concatInsn;\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"String concatenation convert failed\", e);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static boolean isConstConcatNeeded(List<InsnArg> args) {\n\t\tboolean prevConst = false;\n\t\tfor (InsnArg arg : args) {\n\t\t\tboolean curConst = arg.isConst();\n\t\t\tif (curConst && prevConst) {\n\t\t\t\t// found 2 consecutive constants\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tprevConst = curConst;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static List<InsnArg> concatConstArgs(List<InsnArg> args) {\n\t\tif (!isConstConcatNeeded(args)) {\n\t\t\treturn args;\n\t\t}\n\t\tint size = args.size();\n\t\tList<InsnArg> newArgs = new ArrayList<>(size);\n\t\tList<String> concatList = new ArrayList<>(size);\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tInsnArg arg = args.get(i);\n\t\t\tString constStr = getConstString(arg);\n\t\t\tif (constStr != null) {\n\t\t\t\tconcatList.add(constStr);\n\t\t\t} else {\n\t\t\t\tif (!concatList.isEmpty()) {\n\t\t\t\t\tnewArgs.add(getConcatArg(concatList, args, i));\n\t\t\t\t\tconcatList.clear();\n\t\t\t\t}\n\t\t\t\tnewArgs.add(arg);\n\t\t\t}\n\t\t}\n\t\tif (!concatList.isEmpty()) {\n\t\t\tnewArgs.add(getConcatArg(concatList, args, size));\n\t\t}\n\t\treturn newArgs;\n\t}\n\n\tprivate static InsnArg getConcatArg(List<String> concatList, List<InsnArg> args, int idx) {\n\t\tif (concatList.size() == 1) {\n\t\t\treturn args.get(idx - 1);\n\t\t}\n\t\tString str = Utils.concatStrings(concatList);\n\t\treturn InsnArg.wrapArg(new ConstStringNode(str));\n\t}\n\n\t@Nullable\n\tprivate static String getConstString(InsnArg arg) {\n\t\tif (arg.isLiteral()) {\n\t\t\treturn TypeGen.literalToRawString((LiteralArg) arg);\n\t\t}\n\t\tif (arg.isInsnWrap()) {\n\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\tif (wrapInsn instanceof ConstStringNode) {\n\t\t\t\treturn ((ConstStringNode) wrapInsn).getString();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Remove and unbind all instructions with StringBuilder\n\t */\n\tprivate static void removeStringBuilderInsns(MethodNode mth, InvokeNode toStrInsn, List<InsnNode> chain) {\n\t\tInsnRemover.unbindAllArgs(mth, toStrInsn);\n\t\tfor (InsnNode insnNode : chain) {\n\t\t\tInsnRemover.unbindAllArgs(mth, insnNode);\n\t\t}\n\t\tInsnRemover insnRemover = new InsnRemover(mth);\n\t\tfor (InsnNode insnNode : chain) {\n\t\t\tif (insnNode != toStrInsn) {\n\t\t\t\tinsnRemover.addAndUnbind(insnNode);\n\t\t\t}\n\t\t}\n\t\tinsnRemover.perform();\n\t}\n\n\tprivate static List<InsnNode> flattenInsnChainUntil(InsnNode insn, InsnType insnType) {\n\t\tList<InsnNode> chain = new ArrayList<>();\n\t\tInsnArg arg = insn.getArg(0);\n\t\twhile (arg.isInsnWrap()) {\n\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\tchain.add(wrapInsn);\n\t\t\tif (wrapInsn.getType() == insnType\n\t\t\t\t\t|| wrapInsn.getArgsCount() == 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\targ = wrapInsn.getArg(0);\n\t\t}\n\t\tCollections.reverse(chain);\n\t\treturn chain;\n\t}\n\n\tprivate static InsnArg getArgFromAppend(InsnNode chainInsn) {\n\t\tif (chainInsn.getType() == InsnType.INVOKE && chainInsn.getArgsCount() == 2) {\n\t\t\tMethodInfo callMth = ((InvokeNode) chainInsn).getCallMth();\n\t\t\tif (callMth.getDeclClass().getFullName().equals(Consts.CLASS_STRING_BUILDER)\n\t\t\t\t\t&& callMth.getName().equals(\"append\")) {\n\t\t\t\treturn chainInsn.getArg(1);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static InsnNode simplifyArith(ArithNode arith) {\n\t\tif (arith.getArgsCount() != 2) {\n\t\t\treturn null;\n\t\t}\n\t\tLiteralArg litArg = null;\n\t\tInsnArg secondArg = arith.getArg(1);\n\t\tif (secondArg.isInsnWrap()) {\n\t\t\tInsnNode wr = ((InsnWrapArg) secondArg).getWrapInsn();\n\t\t\tif (wr.getType() == InsnType.CONST) {\n\t\t\t\tInsnArg arg = wr.getArg(0);\n\t\t\t\tif (arg.isLiteral()) {\n\t\t\t\t\tlitArg = (LiteralArg) arg;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (secondArg.isLiteral()) {\n\t\t\tlitArg = (LiteralArg) secondArg;\n\t\t}\n\t\tif (litArg == null) {\n\t\t\treturn null;\n\t\t}\n\t\tswitch (arith.getOp()) {\n\t\t\tcase ADD:\n\t\t\t\t// fix 'c + (-1)' to 'c - (1)'\n\t\t\t\tif (litArg.isNegative()) {\n\t\t\t\t\tLiteralArg negLitArg = litArg.negate();\n\t\t\t\t\tif (negLitArg != null) {\n\t\t\t\t\t\treturn new ArithNode(ArithOp.SUB, arith.getResult(), arith.getArg(0), negLitArg);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase XOR:\n\t\t\t\t// simplify xor on boolean\n\t\t\t\tInsnArg firstArg = arith.getArg(0);\n\t\t\t\tlong lit = litArg.getLiteral();\n\t\t\t\tif (firstArg.getType() == ArgType.BOOLEAN && (lit == 0 || lit == 1)) {\n\t\t\t\t\tInsnNode node = new InsnNode(lit == 0 ? InsnType.MOVE : InsnType.NOT, 1);\n\t\t\t\t\tnode.setResult(arith.getResult());\n\t\t\t\t\tnode.addArg(firstArg);\n\t\t\t\t\treturn node;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Convert field arith operation to arith instruction\n\t * (IPUT (ARITH (IGET, lit)) -> ARITH ((IGET)) <op>= lit))\n\t */\n\tprivate static ArithNode convertFieldArith(MethodNode mth, InsnNode insn) {\n\t\tInsnArg arg = insn.getArg(0);\n\t\tif (!arg.isInsnWrap()) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnNode wrap = ((InsnWrapArg) arg).getWrapInsn();\n\t\tInsnType wrapType = wrap.getType();\n\t\tif (wrapType != InsnType.ARITH && wrapType != InsnType.STR_CONCAT\n\t\t\t\t|| !wrap.getArg(0).isInsnWrap()) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnArg getWrap = wrap.getArg(0);\n\t\tInsnNode get = ((InsnWrapArg) getWrap).getWrapInsn();\n\t\tInsnType getType = get.getType();\n\t\tif (getType != InsnType.IGET && getType != InsnType.SGET) {\n\t\t\treturn null;\n\t\t}\n\t\tFieldInfo field = (FieldInfo) ((IndexInsnNode) insn).getIndex();\n\t\tFieldInfo innerField = (FieldInfo) ((IndexInsnNode) get).getIndex();\n\t\tif (!field.equals(innerField)) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\tif (getType == InsnType.IGET && insn.getType() == InsnType.IPUT) {\n\t\t\t\tInsnArg reg = get.getArg(0);\n\t\t\t\tInsnArg putReg = insn.getArg(1);\n\t\t\t\tif (!reg.equals(putReg)) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tInsnArg fArg = getWrap.duplicate();\n\t\t\tInsnRemover.unbindInsn(mth, get);\n\t\t\tif (insn.getType() == InsnType.IPUT) {\n\t\t\t\tInsnRemover.unbindArgUsage(mth, insn.getArg(1));\n\t\t\t}\n\t\t\tif (wrapType == InsnType.ARITH) {\n\t\t\t\tArithNode ar = (ArithNode) wrap;\n\t\t\t\treturn ArithNode.oneArgOp(ar.getOp(), fArg, ar.getArg(1));\n\t\t\t}\n\t\t\tint argsCount = wrap.getArgsCount();\n\t\t\tInsnNode concat = new InsnNode(InsnType.STR_CONCAT, argsCount - 1);\n\t\t\tfor (int i = 1; i < argsCount; i++) {\n\t\t\t\tconcat.addArg(wrap.getArg(i));\n\t\t\t}\n\t\t\tInsnArg concatArg = InsnArg.wrapArg(concat);\n\t\t\tconcatArg.setType(ArgType.STRING);\n\t\t\treturn ArithNode.oneArgOp(ArithOp.ADD, fArg, concatArg);\n\t\t} catch (Exception e) {\n\t\t\tLOG.debug(\"Can't convert field arith insn: {}, mth: {}\", insn, mth, e);\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockExceptionHandler.java",
    "content": "package jadx.core.dex.visitors.blocks;\n\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Deque;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.utils.Utils;\nimport jadx.core.Consts;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.ExcSplitCrossAttr;\nimport jadx.core.dex.attributes.nodes.TmpEdgeAttr;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.NamedArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.trycatch.CatchAttr;\nimport jadx.core.dex.trycatch.ExcHandlerAttr;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.dex.trycatch.TryCatchBlockAttr;\nimport jadx.core.dex.visitors.typeinference.TypeCompare;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.blocks.BlockSet;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class BlockExceptionHandler {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(BlockExceptionHandler.class);\n\n\tpublic static boolean process(MethodNode mth) {\n\t\tif (mth.isNoExceptionHandlers()) {\n\t\t\treturn false;\n\t\t}\n\t\tBlockProcessor.updateCleanSuccessors(mth);\n\t\tDominatorTree.computeDominanceFrontier(mth);\n\n\t\tprocessCatchAttr(mth);\n\t\tinitExcHandlers(mth);\n\n\t\tList<TryCatchBlockAttr> tryBlocks = prepareTryBlocks(mth);\n\t\tconnectExcHandlers(mth, tryBlocks);\n\t\tmth.addAttr(AType.TRY_BLOCKS_LIST, tryBlocks);\n\t\tmth.getBasicBlocks().forEach(BlockNode::updateCleanSuccessors);\n\n\t\tfor (ExceptionHandler eh : mth.getExceptionHandlers()) {\n\t\t\tremoveMonitorExitFromExcHandler(mth, eh);\n\t\t}\n\t\tBlockProcessor.removeMarkedBlocks(mth);\n\n\t\tBlockSet sorted = new BlockSet(mth);\n\t\tBlockUtils.visitDFS(mth, sorted::add);\n\t\tremoveUnusedExcHandlers(mth, tryBlocks, sorted);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Wrap try blocks with top/bottom splitter and connect them to handler block.\n\t * Sometimes try block can be handler block itself and should be connected before wrapping.\n\t * Use queue for postpone try blocks not ready for wrap.\n\t */\n\tprivate static void connectExcHandlers(MethodNode mth, List<TryCatchBlockAttr> tryBlocks) {\n\t\tif (tryBlocks.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tint limit = tryBlocks.size() * 3;\n\t\tint count = 0;\n\t\tDeque<TryCatchBlockAttr> queue = new ArrayDeque<>(tryBlocks);\n\t\twhile (!queue.isEmpty()) {\n\t\t\tTryCatchBlockAttr tryBlock = queue.removeFirst();\n\t\t\tboolean complete = wrapBlocksWithTryCatch(mth, tryBlock);\n\t\t\tif (!complete) {\n\t\t\t\tqueue.addLast(tryBlock); // return to queue at the end\n\t\t\t}\n\t\t\tif (count++ > limit) {\n\t\t\t\tthrow new JadxRuntimeException(\"Try blocks wrapping queue limit reached! Please report as an issue!\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void processCatchAttr(MethodNode mth) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tif (insn.contains(AType.EXC_CATCH) && !insn.canThrowException()) {\n\t\t\t\t\tinsn.remove(AType.EXC_CATCH);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// if all instructions in block have same 'catch' attribute -> add this attribute for whole block.\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tCatchAttr commonCatchAttr = getCommonCatchAttr(block);\n\t\t\tif (commonCatchAttr != null) {\n\t\t\t\tblock.addAttr(commonCatchAttr);\n\t\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\t\tif (insn.contains(AFlag.TRY_ENTER)) {\n\t\t\t\t\t\tblock.add(AFlag.TRY_ENTER);\n\t\t\t\t\t}\n\t\t\t\t\tif (insn.contains(AFlag.TRY_LEAVE)) {\n\t\t\t\t\t\tblock.add(AFlag.TRY_LEAVE);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate static CatchAttr getCommonCatchAttr(BlockNode block) {\n\t\tCatchAttr commonCatchAttr = null;\n\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\tCatchAttr catchAttr = insn.get(AType.EXC_CATCH);\n\t\t\tif (catchAttr != null) {\n\t\t\t\tif (commonCatchAttr == null) {\n\t\t\t\t\tcommonCatchAttr = catchAttr;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!commonCatchAttr.equals(catchAttr)) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn commonCatchAttr;\n\t}\n\n\t@SuppressWarnings(\"ForLoopReplaceableByForEach\")\n\tprivate static void initExcHandlers(MethodNode mth) {\n\t\tList<BlockNode> blocks = mth.getBasicBlocks();\n\t\tint blocksCount = blocks.size();\n\t\tfor (int i = 0; i < blocksCount; i++) { // will add new blocks to list end\n\t\t\tBlockNode block = blocks.get(i);\n\t\t\tInsnNode firstInsn = BlockUtils.getFirstInsn(block);\n\t\t\tif (firstInsn == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tExcHandlerAttr excHandlerAttr = firstInsn.get(AType.EXC_HANDLER);\n\t\t\tif (excHandlerAttr == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfirstInsn.remove(AType.EXC_HANDLER);\n\t\t\tremoveTmpConnection(block);\n\n\t\t\tExceptionHandler excHandler = excHandlerAttr.getHandler();\n\t\t\tif (block.getPredecessors().isEmpty()) {\n\t\t\t\texcHandler.setHandlerBlock(block);\n\t\t\t\tblock.addAttr(excHandlerAttr);\n\t\t\t\texcHandler.addBlock(block);\n\t\t\t\tBlockUtils.collectBlocksDominatedByWithExcHandlers(mth, block, block)\n\t\t\t\t\t\t.forEach(excHandler::addBlock);\n\t\t\t} else {\n\t\t\t\t// ignore already connected handlers -> make catch empty\n\t\t\t\tBlockNode emptyHandlerBlock = BlockSplitter.startNewBlock(mth, block.getStartOffset());\n\t\t\t\temptyHandlerBlock.add(AFlag.SYNTHETIC);\n\t\t\t\temptyHandlerBlock.addAttr(excHandlerAttr);\n\t\t\t\tBlockSplitter.connect(emptyHandlerBlock, block);\n\t\t\t\texcHandler.setHandlerBlock(emptyHandlerBlock);\n\t\t\t\texcHandler.addBlock(emptyHandlerBlock);\n\t\t\t}\n\t\t\tfixMoveExceptionInsn(block, excHandlerAttr);\n\t\t}\n\t}\n\n\tprivate static void removeTmpConnection(BlockNode block) {\n\t\tTmpEdgeAttr tmpEdgeAttr = block.get(AType.TMP_EDGE);\n\t\tif (tmpEdgeAttr != null) {\n\t\t\t// remove temp connection\n\t\t\tBlockSplitter.removeConnection(tmpEdgeAttr.getBlock(), block);\n\t\t\tblock.remove(AType.TMP_EDGE);\n\t\t}\n\t}\n\n\tprivate static List<TryCatchBlockAttr> prepareTryBlocks(MethodNode mth) {\n\t\tMap<ExceptionHandler, List<BlockNode>> blocksByHandler = new HashMap<>();\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tCatchAttr catchAttr = block.get(AType.EXC_CATCH);\n\t\t\tif (catchAttr != null) {\n\t\t\t\tfor (ExceptionHandler eh : catchAttr.getHandlers()) {\n\t\t\t\t\tblocksByHandler\n\t\t\t\t\t\t\t.computeIfAbsent(eh, c -> new ArrayList<>())\n\t\t\t\t\t\t\t.add(block);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (Consts.DEBUG_EXC_HANDLERS) {\n\t\t\tLOG.debug(\"Input exception handlers:\");\n\t\t\tblocksByHandler.forEach((eh, blocks) -> LOG.debug(\" {}, throw blocks: {}, handler blocks: {}\", eh, blocks, eh.getBlocks()));\n\t\t}\n\t\tif (blocksByHandler.isEmpty()) {\n\t\t\t// no catch blocks -> remove all handlers\n\t\t\tmth.getExceptionHandlers().forEach(eh -> removeExcHandler(mth, eh));\n\t\t} else {\n\t\t\t// remove handlers without blocks in catch attribute\n\t\t\tblocksByHandler.forEach((eh, blocks) -> {\n\t\t\t\tif (blocks.isEmpty()) {\n\t\t\t\t\tremoveExcHandler(mth, eh);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tBlockSplitter.detachMarkedBlocks(mth);\n\t\tmth.clearExceptionHandlers();\n\t\tif (mth.isNoExceptionHandlers()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\n\t\tblocksByHandler.forEach((eh, blocks) -> {\n\t\t\t// remove catches from same handler\n\t\t\tblocks.removeAll(eh.getBlocks());\n\t\t});\n\n\t\tList<TryCatchBlockAttr> tryBlocks = new ArrayList<>();\n\t\tblocksByHandler.forEach((eh, blocks) -> {\n\t\t\tList<ExceptionHandler> handlers = new ArrayList<>(1);\n\t\t\thandlers.add(eh);\n\t\t\ttryBlocks.add(new TryCatchBlockAttr(tryBlocks.size(), handlers, blocks));\n\t\t});\n\t\tif (tryBlocks.size() > 1) {\n\t\t\t// merge or mark as outer/inner\n\t\t\twhile (true) {\n\t\t\t\tboolean restart = combineTryCatchBlocks(tryBlocks);\n\t\t\t\tif (!restart) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcheckForMultiCatch(mth, tryBlocks);\n\t\tclearTryBlocks(mth, tryBlocks);\n\t\tsortHandlers(mth, tryBlocks);\n\n\t\tif (Consts.DEBUG_EXC_HANDLERS) {\n\t\t\tLOG.debug(\"Result try-catch blocks:\");\n\t\t\ttryBlocks.forEach(tryBlock -> LOG.debug(\" {}\", tryBlock));\n\t\t}\n\t\treturn tryBlocks;\n\t}\n\n\tprivate static void clearTryBlocks(MethodNode mth, List<TryCatchBlockAttr> tryBlocks) {\n\t\ttryBlocks.forEach(tc -> tc.getBlocks().removeIf(b -> b.contains(AFlag.REMOVE)));\n\t\ttryBlocks.removeIf(tb -> tb.getBlocks().isEmpty() || tb.getHandlers().isEmpty());\n\t\tmth.clearExceptionHandlers();\n\t\tBlockSplitter.detachMarkedBlocks(mth);\n\t}\n\n\tprivate static boolean combineTryCatchBlocks(List<TryCatchBlockAttr> tryBlocks) {\n\t\tfor (TryCatchBlockAttr outerTryBlock : tryBlocks) {\n\t\t\tfor (TryCatchBlockAttr innerTryBlock : tryBlocks) {\n\t\t\t\tif (outerTryBlock == innerTryBlock || innerTryBlock.getOuterTryBlock() != null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (checkTryCatchRelation(tryBlocks, outerTryBlock, innerTryBlock)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean checkTryCatchRelation(List<TryCatchBlockAttr> tryBlocks,\n\t\t\tTryCatchBlockAttr outerTryBlock, TryCatchBlockAttr innerTryBlock) {\n\t\tif (outerTryBlock.getBlocks().equals(innerTryBlock.getBlocks())) {\n\t\t\t// same try blocks -> merge handlers\n\t\t\tList<ExceptionHandler> handlers = Utils.concatDistinct(outerTryBlock.getHandlers(), innerTryBlock.getHandlers());\n\t\t\ttryBlocks.add(new TryCatchBlockAttr(tryBlocks.size(), handlers, outerTryBlock.getBlocks()));\n\t\t\ttryBlocks.remove(outerTryBlock);\n\t\t\ttryBlocks.remove(innerTryBlock);\n\t\t\treturn true;\n\t\t}\n\n\t\tSet<BlockNode> handlerBlocks = innerTryBlock.getHandlers().stream()\n\t\t\t\t.flatMap(eh -> eh.getBlocks().stream())\n\t\t\t\t.collect(Collectors.toSet());\n\t\tboolean catchInHandler = handlerBlocks.stream().anyMatch(isHandlersIntersects(outerTryBlock));\n\t\tboolean catchInTry = innerTryBlock.getBlocks().stream().anyMatch(isHandlersIntersects(outerTryBlock));\n\t\tboolean blocksOutsideHandler = outerTryBlock.getBlocks().stream().anyMatch(b -> !handlerBlocks.contains(b));\n\n\t\tif (catchInHandler && (catchInTry || blocksOutsideHandler)) {\n\t\t\t// convert to inner\n\t\t\tList<BlockNode> mergedBlocks = Utils.concatDistinct(outerTryBlock.getBlocks(), innerTryBlock.getBlocks());\n\t\t\tinnerTryBlock.getHandlers().removeAll(outerTryBlock.getHandlers());\n\t\t\tinnerTryBlock.setOuterTryBlock(outerTryBlock);\n\t\t\touterTryBlock.addInnerTryBlock(innerTryBlock);\n\t\t\touterTryBlock.setBlocks(mergedBlocks);\n\t\t\treturn false;\n\t\t}\n\t\tSet<ExceptionHandler> innerHandlerSet = new HashSet<>(innerTryBlock.getHandlers());\n\t\tif (innerHandlerSet.containsAll(outerTryBlock.getHandlers())) {\n\t\t\t// merge\n\t\t\tList<BlockNode> mergedBlocks = Utils.concatDistinct(outerTryBlock.getBlocks(), innerTryBlock.getBlocks());\n\t\t\tList<ExceptionHandler> handlers = Utils.concatDistinct(outerTryBlock.getHandlers(), innerTryBlock.getHandlers());\n\t\t\ttryBlocks.add(new TryCatchBlockAttr(tryBlocks.size(), handlers, mergedBlocks));\n\t\t\ttryBlocks.remove(outerTryBlock);\n\t\t\ttryBlocks.remove(innerTryBlock);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@NotNull\n\tprivate static Predicate<BlockNode> isHandlersIntersects(TryCatchBlockAttr outerTryBlock) {\n\t\treturn block -> {\n\t\t\tCatchAttr catchAttr = block.get(AType.EXC_CATCH);\n\t\t\treturn catchAttr != null && Objects.equals(catchAttr.getHandlers(), outerTryBlock.getHandlers());\n\t\t};\n\t}\n\n\tprivate static void removeExcHandler(MethodNode mth, ExceptionHandler excHandler) {\n\t\texcHandler.markForRemove();\n\t\tBlockSplitter.removeConnection(mth.getEnterBlock(), excHandler.getHandlerBlock());\n\t}\n\n\tprivate static boolean wrapBlocksWithTryCatch(MethodNode mth, TryCatchBlockAttr tryCatchBlock) {\n\t\tList<BlockNode> blocks = tryCatchBlock.getBlocks();\n\t\tBlockNode top = searchTopBlock(mth, blocks);\n\t\tif (top.getPredecessors().isEmpty() && top != mth.getEnterBlock()) {\n\t\t\treturn false;\n\t\t}\n\t\tBlockNode bottom = searchBottomBlock(mth, blocks);\n\t\tBlockNode splitReturn;\n\t\tif (bottom != null && bottom.isReturnBlock()) {\n\t\t\tif (Consts.DEBUG_EXC_HANDLERS) {\n\t\t\t\tLOG.debug(\"TryCatch #{} bottom block ({}) is return, split\", tryCatchBlock.id(), bottom);\n\t\t\t}\n\t\t\tsplitReturn = bottom;\n\t\t\tbottom = BlockSplitter.blockSplitTop(mth, bottom);\n\t\t\tbottom.add(AFlag.SYNTHETIC);\n\t\t} else {\n\t\t\tsplitReturn = null;\n\t\t}\n\t\tif (Consts.DEBUG_EXC_HANDLERS) {\n\t\t\tLOG.debug(\"TryCatch #{} split: top {}, bottom: {}\", tryCatchBlock.id(), top, bottom);\n\t\t}\n\t\tBlockNode topSplitterBlock = getTopSplitterBlock(mth, top);\n\t\ttopSplitterBlock.add(AFlag.EXC_TOP_SPLITTER);\n\t\ttopSplitterBlock.add(AFlag.SYNTHETIC);\n\n\t\tint totalHandlerBlocks = tryCatchBlock.getHandlers().stream().mapToInt(eh -> eh.getBlocks().size()).sum();\n\n\t\tBlockNode bottomSplitterBlock;\n\t\tif (bottom == null || totalHandlerBlocks == 0) {\n\t\t\tbottomSplitterBlock = null;\n\t\t} else {\n\t\t\tBlockNode existBottomSplitter = BlockUtils.getBlockWithFlag(bottom.getSuccessors(), AFlag.EXC_BOTTOM_SPLITTER);\n\t\t\tbottomSplitterBlock = existBottomSplitter != null ? existBottomSplitter : BlockSplitter.startNewBlock(mth, -1);\n\t\t\tbottomSplitterBlock.add(AFlag.EXC_BOTTOM_SPLITTER);\n\t\t\tbottomSplitterBlock.add(AFlag.SYNTHETIC);\n\t\t\tBlockSplitter.connect(bottom, bottomSplitterBlock);\n\t\t\tif (splitReturn != null) {\n\t\t\t\t// redirect handler to return block instead synthetic split block to avoid self-loop\n\t\t\t\tBlockSet bottomPreds = BlockSet.from(mth, bottom.getPredecessors());\n\t\t\t\tfor (ExceptionHandler handler : tryCatchBlock.getHandlers()) {\n\t\t\t\t\tif (bottomPreds.intersects(handler.getBlocks())) {\n\t\t\t\t\t\tBlockNode lastBlock = bottomPreds.intersect(handler.getBlocks()).getOne();\n\t\t\t\t\t\tif (lastBlock != null) {\n\t\t\t\t\t\t\tBlockSplitter.replaceConnection(lastBlock, bottom, splitReturn);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (Consts.DEBUG_EXC_HANDLERS) {\n\t\t\tLOG.debug(\"TryCatch #{} result splitters: top {}, bottom: {}\",\n\t\t\t\t\ttryCatchBlock.id(), topSplitterBlock, bottomSplitterBlock);\n\t\t}\n\t\tconnectSplittersAndHandlers(tryCatchBlock, topSplitterBlock, bottomSplitterBlock);\n\n\t\t// At this point, it's possible that a cross edge to the original bottom has been turned into a back\n\t\t// edge by the insertion of the new bottom. This causes problems because back edges usually signifiy\n\t\t// loops, but this is not a loop. To fix this, predecessors of the bottom that also have a path from\n\t\t// the bottom are rewritten to point to the original path crossing point (before synthetic blocks).\n\t\tif (bottom != null && bottom.contains(AType.EXC_SPLIT_CROSS)) {\n\t\t\tList<BlockNode> convertBlocks = new ArrayList<>();\n\t\t\tfor (BlockNode b : bottom.getPredecessors()) {\n\t\t\t\tif (BlockUtils.isAnyPathExists(bottom, b)) {\n\t\t\t\t\tconvertBlocks.add(b);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (BlockNode b : convertBlocks) {\n\t\t\t\t// The connection can't be replaced during the first loop because it would modify the preds list.\n\t\t\t\tBlockSplitter.replaceConnection(b, bottom, bottom.get(AType.EXC_SPLIT_CROSS).getOriginalPathCross());\n\t\t\t}\n\t\t}\n\n\t\tfor (BlockNode block : blocks) {\n\t\t\tTryCatchBlockAttr currentTCBAttr = block.get(AType.TRY_BLOCK);\n\t\t\tif (currentTCBAttr == null || currentTCBAttr.getInnerTryBlocks().contains(tryCatchBlock)) {\n\t\t\t\tblock.addAttr(tryCatchBlock);\n\t\t\t}\n\t\t}\n\t\ttryCatchBlock.setTopSplitter(topSplitterBlock);\n\n\t\ttopSplitterBlock.updateCleanSuccessors();\n\t\tif (bottomSplitterBlock != null) {\n\t\t\tbottomSplitterBlock.updateCleanSuccessors();\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static BlockNode getTopSplitterBlock(MethodNode mth, BlockNode top) {\n\t\tif (top == mth.getEnterBlock()) {\n\t\t\tBlockNode fixedTop = mth.getEnterBlock().getSuccessors().get(0);\n\t\t\treturn BlockSplitter.blockSplitTop(mth, fixedTop);\n\t\t}\n\t\tBlockNode existPredTopSplitter = BlockUtils.getBlockWithFlag(top.getPredecessors(), AFlag.EXC_TOP_SPLITTER);\n\t\tif (existPredTopSplitter != null) {\n\t\t\treturn existPredTopSplitter;\n\t\t}\n\t\t// try to reuse exists splitter on empty simple path below top block\n\t\tif (top.getCleanSuccessors().size() == 1 && top.getInstructions().isEmpty()) {\n\t\t\tBlockNode otherTopSplitter = BlockUtils.getBlockWithFlag(top.getCleanSuccessors(), AFlag.EXC_TOP_SPLITTER);\n\t\t\tif (otherTopSplitter != null && otherTopSplitter.getPredecessors().size() == 1) {\n\t\t\t\treturn otherTopSplitter;\n\t\t\t}\n\t\t}\n\t\treturn BlockSplitter.blockSplitTop(mth, top);\n\t}\n\n\tprivate static BlockNode searchTopBlock(MethodNode mth, List<BlockNode> blocks) {\n\t\tBlockNode top = BlockUtils.getTopBlock(blocks);\n\t\tif (top != null) {\n\t\t\treturn adjustTopBlock(top);\n\t\t}\n\t\tBlockNode topDom = BlockUtils.getCommonDominator(mth, blocks);\n\t\tif (topDom != null) {\n\t\t\t// dominator always return one up block if blocks already contains dominator, use successor instead\n\t\t\tif (topDom.getSuccessors().size() == 1) {\n\t\t\t\tBlockNode upBlock = topDom.getSuccessors().get(0);\n\t\t\t\tif (blocks.contains(upBlock)) {\n\t\t\t\t\treturn upBlock;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn adjustTopBlock(topDom);\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Failed to find top block for try-catch from: \" + blocks);\n\t}\n\n\tprivate static BlockNode adjustTopBlock(BlockNode topBlock) {\n\t\tif (topBlock.getSuccessors().size() == 1 && !topBlock.contains(AType.EXC_CATCH)) {\n\t\t\t// top block can be lifted by other exception handlers included in blocks list, trying to undo that\n\t\t\treturn topBlock.getSuccessors().get(0);\n\t\t}\n\t\treturn topBlock;\n\t}\n\n\t@Nullable\n\tprivate static BlockNode searchBottomBlock(MethodNode mth, List<BlockNode> blocks) {\n\t\t// search common post-dominator block inside input set\n\t\tBlockNode bottom = BlockUtils.getBottomBlock(blocks);\n\t\tif (bottom != null) {\n\t\t\treturn bottom;\n\t\t}\n\t\t// not found -> blocks don't have same dominator\n\t\t// try to search common cross block outside input set\n\t\t// NOTE: bottom block not needed for exit nodes (no data flow from them)\n\t\tBlockNode pathCross = BlockUtils.getPathCross(mth, blocks);\n\t\tif (pathCross == null) {\n\t\t\treturn null;\n\t\t}\n\t\tList<BlockNode> preds = new ArrayList<>(pathCross.getPredecessors());\n\t\tpreds.removeAll(blocks);\n\t\tList<BlockNode> outsidePredecessors = preds.stream()\n\t\t\t\t.filter(p -> !BlockUtils.atLeastOnePathExists(blocks, p))\n\t\t\t\t.collect(Collectors.toList());\n\t\t// if we have no predecessors or every predecessor is outside (which would mean that inserting the\n\t\t// new synthetic block does nothing), just return the existing path cross instead.\n\t\tif (outsidePredecessors.isEmpty() || outsidePredecessors.size() == pathCross.getPredecessors().size()) {\n\t\t\treturn pathCross;\n\t\t}\n\t\t// some predecessors outside of input set paths -> split block only for input set\n\t\tBlockNode splitCross = BlockSplitter.blockSplitTop(mth, pathCross);\n\t\tsplitCross.add(AFlag.SYNTHETIC);\n\t\tsplitCross.addAttr(new ExcSplitCrossAttr(pathCross));\n\t\tfor (BlockNode outsidePredecessor : outsidePredecessors) {\n\t\t\t// return predecessors to split bottom block (original)\n\t\t\tBlockSplitter.replaceConnection(outsidePredecessor, splitCross, pathCross);\n\t\t}\n\t\treturn splitCross;\n\t}\n\n\tprivate static void connectSplittersAndHandlers(TryCatchBlockAttr tryCatchBlock, BlockNode topSplitterBlock,\n\t\t\t@Nullable BlockNode bottomSplitterBlock) {\n\t\tfor (ExceptionHandler handler : tryCatchBlock.getHandlers()) {\n\t\t\tBlockNode handlerBlock = handler.getHandlerBlock();\n\t\t\tBlockSplitter.connect(topSplitterBlock, handlerBlock);\n\t\t\tif (bottomSplitterBlock != null) {\n\t\t\t\tBlockSplitter.connect(bottomSplitterBlock, handlerBlock);\n\t\t\t}\n\t\t}\n\t\tTryCatchBlockAttr outerTryBlock = tryCatchBlock.getOuterTryBlock();\n\t\tif (outerTryBlock != null) {\n\t\t\tconnectSplittersAndHandlers(outerTryBlock, topSplitterBlock, bottomSplitterBlock);\n\t\t}\n\t}\n\n\tprivate static void fixMoveExceptionInsn(BlockNode block, ExcHandlerAttr excHandlerAttr) {\n\t\tExceptionHandler excHandler = excHandlerAttr.getHandler();\n\t\tArgType argType = excHandler.getArgType();\n\t\tInsnNode me = BlockUtils.getLastInsn(block);\n\t\tif (me != null && me.getType() == InsnType.MOVE_EXCEPTION) {\n\t\t\t// set correct type for 'move-exception' operation\n\t\t\tRegisterArg resArg = InsnArg.reg(me.getResult().getRegNum(), argType);\n\t\t\tresArg.copyAttributesFrom(me);\n\t\t\tme.setResult(resArg);\n\t\t\tme.add(AFlag.DONT_INLINE);\n\t\t\tresArg.add(AFlag.CUSTOM_DECLARE);\n\t\t\texcHandler.setArg(resArg);\n\t\t\tme.addAttr(excHandlerAttr);\n\t\t\treturn;\n\t\t}\n\t\t// handler arguments not used\n\t\texcHandler.setArg(new NamedArg(\"unused\", argType));\n\t}\n\n\tprivate static void removeMonitorExitFromExcHandler(MethodNode mth, ExceptionHandler excHandler) {\n\t\tfor (BlockNode excBlock : excHandler.getBlocks()) {\n\t\t\tInsnRemover remover = new InsnRemover(mth, excBlock);\n\t\t\tfor (InsnNode insn : excBlock.getInstructions()) {\n\t\t\t\tif (insn.getType() == InsnType.MONITOR_ENTER) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (insn.getType() == InsnType.MONITOR_EXIT) {\n\t\t\t\t\tremover.addAndUnbind(insn);\n\t\t\t\t}\n\t\t\t}\n\t\t\tremover.perform();\n\t\t}\n\t}\n\n\tprivate static void checkForMultiCatch(MethodNode mth, List<TryCatchBlockAttr> tryBlocks) {\n\t\tboolean merged = false;\n\t\tfor (TryCatchBlockAttr tryBlock : tryBlocks) {\n\t\t\tif (mergeMultiCatch(mth, tryBlock)) {\n\t\t\t\tmerged = true;\n\t\t\t}\n\t\t}\n\t\tif (merged) {\n\t\t\tBlockSplitter.detachMarkedBlocks(mth);\n\t\t\tmth.clearExceptionHandlers();\n\t\t}\n\t}\n\n\tprivate static boolean mergeMultiCatch(MethodNode mth, TryCatchBlockAttr tryCatch) {\n\t\tif (tryCatch.getHandlers().size() < 2) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (ExceptionHandler handler : tryCatch.getHandlers()) {\n\t\t\tif (handler.getBlocks().size() != 1) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tBlockNode block = handler.getHandlerBlock();\n\t\t\tif (block.getInstructions().size() != 1\n\t\t\t\t\t|| !BlockUtils.checkLastInsnType(block, InsnType.MOVE_EXCEPTION)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tList<BlockNode> handlerBlocks = ListUtils.map(tryCatch.getHandlers(), ExceptionHandler::getHandlerBlock);\n\t\tList<BlockNode> successorBlocks = handlerBlocks.stream()\n\t\t\t\t.flatMap(h -> h.getSuccessors().stream())\n\t\t\t\t.distinct()\n\t\t\t\t.collect(Collectors.toList());\n\t\tif (successorBlocks.size() != 1) {\n\t\t\treturn false;\n\t\t}\n\t\tBlockNode successorBlock = successorBlocks.get(0);\n\t\tif (!ListUtils.unorderedEquals(successorBlock.getPredecessors(), handlerBlocks)) {\n\t\t\treturn false;\n\t\t}\n\t\tList<RegisterArg> regs = tryCatch.getHandlers().stream()\n\t\t\t\t.map(h -> Objects.requireNonNull(BlockUtils.getLastInsn(h.getHandlerBlock())).getResult())\n\t\t\t\t.distinct()\n\t\t\t\t.collect(Collectors.toList());\n\t\tif (regs.size() != 1) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// merge confirm, leave only first handler, remove others\n\t\tExceptionHandler resultHandler = tryCatch.getHandlers().get(0);\n\t\ttryCatch.getHandlers().removeIf(handler -> {\n\t\t\tif (handler == resultHandler) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tresultHandler.addCatchTypes(mth, handler.getCatchTypes());\n\t\t\thandler.markForRemove();\n\t\t\treturn true;\n\t\t});\n\t\treturn true;\n\t}\n\n\tprivate static void sortHandlers(MethodNode mth, List<TryCatchBlockAttr> tryBlocks) {\n\t\tTypeCompare typeCompare = mth.root().getTypeCompare();\n\t\tComparator<ArgType> comparator = typeCompare.getReversedComparator();\n\t\tfor (TryCatchBlockAttr tryBlock : tryBlocks) {\n\t\t\tfor (ExceptionHandler handler : tryBlock.getHandlers()) {\n\t\t\t\thandler.getCatchTypes().sort((first, second) -> compareByTypeAndName(comparator, first, second));\n\t\t\t}\n\t\t\ttryBlock.getHandlers().sort((first, second) -> {\n\t\t\t\tif (first.equals(second)) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Same handlers in try block: \" + tryBlock);\n\t\t\t\t}\n\t\t\t\tif (first.isCatchAll()) {\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tif (second.isCatchAll()) {\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\treturn compareByTypeAndName(comparator,\n\t\t\t\t\t\tListUtils.first(first.getCatchTypes()), ListUtils.first(second.getCatchTypes()));\n\t\t\t});\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"ComparatorResultComparison\")\n\tprivate static int compareByTypeAndName(Comparator<ArgType> comparator, ClassInfo first, ClassInfo second) {\n\t\tint r = comparator.compare(first.getType(), second.getType());\n\t\tif (r == -2) {\n\t\t\t// on conflict sort by name\n\t\t\treturn first.compareTo(second);\n\t\t}\n\t\treturn r;\n\t}\n\n\t/**\n\t * Remove excHandlers that were not used when connecting.\n\t * Check first if the blocks are unreachable.\n\t */\n\tprivate static void removeUnusedExcHandlers(MethodNode mth, List<TryCatchBlockAttr> tryBlocks, BlockSet blocks) {\n\t\tfor (ExceptionHandler eh : mth.getExceptionHandlers()) {\n\t\t\tboolean notProcessed = true;\n\t\t\tBlockNode handlerBlock = eh.getHandlerBlock();\n\t\t\tif (handlerBlock == null || blocks.contains(handlerBlock)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (TryCatchBlockAttr tcb : tryBlocks) {\n\t\t\t\tif (tcb.getHandlers().contains(eh)) {\n\t\t\t\t\tnotProcessed = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (notProcessed) {\n\t\t\t\tBlockProcessor.removeUnreachableBlock(handlerBlock, mth);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockFinisher.java",
    "content": "package jadx.core.dex.visitors.blocks;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\n\npublic class BlockFinisher extends AbstractVisitor {\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tif (mth.isNoCode() || mth.getBasicBlocks().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tif (!mth.contains(AFlag.DISABLE_BLOCKS_LOCK)) {\n\t\t\tmth.finishBasicBlocks();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockProcessor.java",
    "content": "package jadx.core.dex.visitors.blocks;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.CodeFeaturesAttr;\nimport jadx.core.dex.attributes.nodes.LoopInfo;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.Edge;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.trycatch.ExcHandlerAttr;\nimport jadx.core.dex.trycatch.TryCatchBlockAttr;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.dex.visitors.blocks.BlockSplitter.connect;\n\npublic class BlockProcessor extends AbstractVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(BlockProcessor.class);\n\n\tprivate static final boolean DEBUG_MODS = false;\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tif (mth.isNoCode() || mth.getBasicBlocks().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tprocessBlocksTree(mth);\n\t}\n\n\tprivate static void processBlocksTree(MethodNode mth) {\n\t\tremoveUnreachableBlocks(mth);\n\n\t\tcomputeDominators(mth);\n\t\tif (independentBlockTreeMod(mth)) {\n\t\t\tcheckForUnreachableBlocks(mth);\n\t\t\tcomputeDominators(mth);\n\t\t}\n\t\tif (FixMultiEntryLoops.process(mth)) {\n\t\t\tcomputeDominators(mth);\n\t\t}\n\t\tupdateCleanSuccessors(mth);\n\n\t\tint blocksCount = mth.getBasicBlocks().size();\n\t\tint modLimit = Math.max(100, blocksCount);\n\t\tif (DEBUG_MODS) {\n\t\t\tmth.addAttr(new DebugModAttr());\n\t\t}\n\t\tint i = 0;\n\t\twhile (modifyBlocksTree(mth)) {\n\t\t\tcomputeDominators(mth);\n\t\t\tif (i++ > modLimit) {\n\t\t\t\tmth.addWarn(\"CFG modification limit reached, blocks count: \" + blocksCount);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (DEBUG_MODS && i != 0) {\n\t\t\tString stats = \"CFG modifications count: \" + i\n\t\t\t\t\t+ \", blocks count: \" + blocksCount + '\\n'\n\t\t\t\t\t+ mth.get(DebugModAttr.TYPE).formatStats() + '\\n';\n\t\t\tmth.addDebugComment(stats);\n\t\t\tLOG.debug(\"Method: {}\\n{}\", mth, stats);\n\t\t\tmth.remove(DebugModAttr.TYPE);\n\t\t}\n\t\tcheckForUnreachableBlocks(mth);\n\n\t\tDominatorTree.computeDominanceFrontier(mth);\n\t\tregisterLoops(mth);\n\t\tprocessNestedLoops(mth);\n\n\t\tPostDominatorTree.compute(mth);\n\n\t\tupdateCleanSuccessors(mth);\n\t}\n\n\t/**\n\t * Recalculate all additional info attached to blocks:\n\t *\n\t * <pre>\n\t * - dominators\n\t * - dominance frontier\n\t * - post dominators (only if {@link AFlag#COMPUTE_POST_DOM} added to method)\n\t * - loops and nested loop info\n\t * </pre>\n\t * <p>\n\t * This method should be called after changing a block tree in custom passes added before\n\t * {@link BlockFinisher}.\n\t */\n\tpublic static void updateBlocksData(MethodNode mth) {\n\t\tclearBlocksState(mth);\n\t\tDominatorTree.compute(mth);\n\t\tmarkLoops(mth);\n\n\t\tDominatorTree.computeDominanceFrontier(mth);\n\t\tregisterLoops(mth);\n\t\tprocessNestedLoops(mth);\n\n\t\tPostDominatorTree.compute(mth);\n\n\t\tupdateCleanSuccessors(mth);\n\t}\n\n\tstatic void updateCleanSuccessors(MethodNode mth) {\n\t\tmth.getBasicBlocks().forEach(BlockNode::updateCleanSuccessors);\n\t}\n\n\tprivate static void checkForUnreachableBlocks(MethodNode mth) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tif (block.getPredecessors().isEmpty() && block != mth.getEnterBlock()) {\n\t\t\t\t// Sometimes a split cross block will have all it's predecessors moved elsewhere after it's been\n\t\t\t\t// created. This is usually detected at the time of it's creation, but in certain edge cases it\n\t\t\t\t// is difficult to do so. In those cases it will be cleanly removed here, along with the associated\n\t\t\t\t// bottom splitter.\n\t\t\t\tif (block.contains(AType.EXC_SPLIT_CROSS) && fixUnreachableSplitCross(mth, block)) {\n\t\t\t\t\tmth.addInfoComment(\"Removed unreachable split cross block \" + block.toString());\n\t\t\t\t} else {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Unreachable block: \" + block);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Attempts to remove an unreachable synthetic split cross block that has been added previously,\n\t * along with the associated bottom splitter.\n\t *\n\t * @param mth        the method containing the unreachable block\n\t * @param splitCross the unreachable block\n\t * @return true if the operation was successful, false if a precondition was not satisfied and no\n\t *         changes were made.\n\t */\n\tprivate static boolean fixUnreachableSplitCross(MethodNode mth, BlockNode splitCross) {\n\t\tBlockNode bottomSplitter = null;\n\t\tfor (BlockNode succ : splitCross.getSuccessors()) {\n\t\t\tif (succ.contains(AFlag.EXC_BOTTOM_SPLITTER)) {\n\t\t\t\tbottomSplitter = succ;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (bottomSplitter == null || bottomSplitter.getPredecessors().size() != 1) {\n\t\t\treturn false;\n\t\t}\n\t\tSet<BlockNode> removeSet = new HashSet<>();\n\t\tremoveSet.add(bottomSplitter);\n\t\tremoveSet.add(splitCross);\n\t\tremoveFromMethod(removeSet, mth);\n\n\t\treturn true;\n\t}\n\n\tprivate static boolean deduplicateBlockInsns(MethodNode mth, BlockNode block) {\n\t\tif (block.contains(AFlag.LOOP_START) || block.contains(AFlag.LOOP_END)) {\n\t\t\t// search for same instruction at end of all predecessors blocks\n\t\t\tList<BlockNode> predecessors = block.getPredecessors();\n\t\t\tint predsCount = predecessors.size();\n\t\t\tif (predsCount > 1) {\n\t\t\t\tInsnNode lastInsn = BlockUtils.getLastInsn(block);\n\t\t\t\tif (lastInsn != null && lastInsn.getType() == InsnType.IF) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (BlockUtils.checkFirstInsn(block, insn -> insn.contains(AType.EXC_HANDLER))) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t// TODO: implement insn extraction into separate block for partial predecessors\n\t\t\t\tint sameInsnCount = getSameLastInsnCount(predecessors);\n\t\t\t\tif (sameInsnCount > 0) {\n\t\t\t\t\tList<InsnNode> insns = getLastInsns(predecessors.get(0), sameInsnCount);\n\t\t\t\t\tinsertAtStart(block, insns);\n\t\t\t\t\tpredecessors.forEach(pred -> getLastInsns(pred, sameInsnCount).clear());\n\t\t\t\t\tmth.addDebugComment(\"Move duplicate insns, count: \" + sameInsnCount + \" to block \" + block);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static List<InsnNode> getLastInsns(BlockNode blockNode, int sameInsnCount) {\n\t\tList<InsnNode> instructions = blockNode.getInstructions();\n\t\tint size = instructions.size();\n\t\treturn instructions.subList(size - sameInsnCount, size);\n\t}\n\n\tprivate static void insertAtStart(BlockNode block, List<InsnNode> insns) {\n\t\tList<InsnNode> blockInsns = block.getInstructions();\n\n\t\tList<InsnNode> newInsnList = new ArrayList<>(insns.size() + blockInsns.size());\n\t\tnewInsnList.addAll(insns);\n\t\tnewInsnList.addAll(blockInsns);\n\n\t\tblockInsns.clear();\n\t\tblockInsns.addAll(newInsnList);\n\t}\n\n\tprivate static int getSameLastInsnCount(List<BlockNode> predecessors) {\n\t\tint sameInsnCount = 0;\n\t\twhile (true) {\n\t\t\tInsnNode insn = null;\n\t\t\tfor (BlockNode pred : predecessors) {\n\t\t\t\tInsnNode curInsn = getInsnsFromEnd(pred, sameInsnCount);\n\t\t\t\tif (curInsn == null) {\n\t\t\t\t\treturn sameInsnCount;\n\t\t\t\t}\n\t\t\t\tif (insn == null) {\n\t\t\t\t\tinsn = curInsn;\n\t\t\t\t} else {\n\t\t\t\t\tif (!isSame(insn, curInsn)) {\n\t\t\t\t\t\treturn sameInsnCount;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tsameInsnCount++;\n\t\t}\n\t}\n\n\tprivate static boolean isSame(InsnNode insn, InsnNode curInsn) {\n\t\treturn isInsnsEquals(insn, curInsn) && insn.canReorder();\n\t}\n\n\tprivate static boolean isInsnsEquals(InsnNode insn, InsnNode otherInsn) {\n\t\tif (insn == otherInsn) {\n\t\t\treturn true;\n\t\t}\n\t\tif (insn.isSame(otherInsn)\n\t\t\t\t&& sameArgs(insn.getResult(), otherInsn.getResult())) {\n\t\t\tint argsCount = insn.getArgsCount();\n\t\t\tfor (int i = 0; i < argsCount; i++) {\n\t\t\t\tif (!sameArgs(insn.getArg(i), otherInsn.getArg(i))) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean sameArgs(@Nullable InsnArg arg, @Nullable InsnArg otherArg) {\n\t\tif (arg == otherArg) {\n\t\t\treturn true;\n\t\t}\n\t\tif (arg == null || otherArg == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (arg.getClass().equals(otherArg.getClass())) {\n\t\t\tif (arg.isRegister()) {\n\t\t\t\treturn ((RegisterArg) arg).getRegNum() == ((RegisterArg) otherArg).getRegNum();\n\t\t\t}\n\t\t\tif (arg.isLiteral()) {\n\t\t\t\treturn ((LiteralArg) arg).getLiteral() == ((LiteralArg) otherArg).getLiteral();\n\t\t\t}\n\t\t\tthrow new JadxRuntimeException(\"Unexpected InsnArg types: \" + arg + \" and \" + otherArg);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static InsnNode getInsnsFromEnd(BlockNode block, int number) {\n\t\tList<InsnNode> instructions = block.getInstructions();\n\t\tint insnCount = instructions.size();\n\t\tif (insnCount <= number) {\n\t\t\treturn null;\n\t\t}\n\t\treturn instructions.get(insnCount - number - 1);\n\t}\n\n\tprivate static void computeDominators(MethodNode mth) {\n\t\tclearBlocksState(mth);\n\t\tDominatorTree.compute(mth);\n\t\tmarkLoops(mth);\n\t}\n\n\tprivate static void markLoops(MethodNode mth) {\n\t\tmth.getBasicBlocks().forEach(block -> {\n\t\t\t// Every successor that dominates its predecessor is a header of a loop,\n\t\t\t// block -> successor is a back edge.\n\t\t\tblock.getSuccessors().forEach(successor -> {\n\t\t\t\tif (block.getDoms().get(successor.getId()) || block == successor) {\n\t\t\t\t\tsuccessor.add(AFlag.LOOP_START);\n\t\t\t\t\tblock.add(AFlag.LOOP_END);\n\n\t\t\t\t\tSet<BlockNode> loopBlocks = BlockUtils.getAllPathsBlocks(successor, block);\n\t\t\t\t\tLoopInfo loop = new LoopInfo(successor, block, loopBlocks);\n\t\t\t\t\tsuccessor.addAttr(AType.LOOP, loop);\n\t\t\t\t\tblock.addAttr(AType.LOOP, loop);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate static void registerLoops(MethodNode mth) {\n\t\tmth.resetLoops();\n\t\tmth.getBasicBlocks().forEach(block -> {\n\t\t\tif (block.contains(AFlag.LOOP_START)) {\n\t\t\t\tblock.getAll(AType.LOOP).forEach(mth::registerLoop);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate static void processNestedLoops(MethodNode mth) {\n\t\tif (mth.getLoopsCount() == 0) {\n\t\t\treturn;\n\t\t}\n\t\tfor (LoopInfo outLoop : mth.getLoops()) {\n\t\t\tfor (LoopInfo innerLoop : mth.getLoops()) {\n\t\t\t\tif (outLoop == innerLoop) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (outLoop.getLoopBlocks().containsAll(innerLoop.getLoopBlocks())) {\n\t\t\t\t\tLoopInfo parentLoop = innerLoop.getParentLoop();\n\t\t\t\t\tif (parentLoop != null) {\n\t\t\t\t\t\tif (parentLoop.getLoopBlocks().containsAll(outLoop.getLoopBlocks())) {\n\t\t\t\t\t\t\toutLoop.setParentLoop(parentLoop);\n\t\t\t\t\t\t\tinnerLoop.setParentLoop(outLoop);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tparentLoop.setParentLoop(outLoop);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tinnerLoop.setParentLoop(outLoop);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean modifyBlocksTree(MethodNode mth) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tif (checkLoops(mth, block)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tif (mergeConstReturn(mth)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (CodeFeaturesAttr.contains(mth, CodeFeaturesAttr.CodeFeature.SWITCH)) {\n\t\t\tfor (BlockNode basicBlock : mth.getBasicBlocks()) {\n\t\t\t\tif (duplicateSimpleMoveBlock(mth, basicBlock)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn splitExitBlocks(mth);\n\t}\n\n\tprivate static boolean mergeConstReturn(MethodNode mth) {\n\t\tif (mth.isVoidReturn()) {\n\t\t\treturn false;\n\t\t}\n\t\tboolean changed = false;\n\t\tfor (BlockNode retBlock : new ArrayList<>(mth.getPreExitBlocks())) {\n\t\t\tBlockNode pred = Utils.getOne(retBlock.getPredecessors());\n\t\t\tif (pred != null) {\n\t\t\t\tInsnNode constInsn = Utils.getOne(pred.getInstructions());\n\t\t\t\tif (constInsn != null && constInsn.isConstInsn()) {\n\t\t\t\t\tRegisterArg constArg = constInsn.getResult();\n\t\t\t\t\tInsnNode returnInsn = BlockUtils.getLastInsn(retBlock);\n\t\t\t\t\tif (returnInsn != null && returnInsn.getType() == InsnType.RETURN) {\n\t\t\t\t\t\tInsnArg retArg = returnInsn.getArg(0);\n\t\t\t\t\t\tif (constArg.sameReg(retArg)) {\n\t\t\t\t\t\t\tmergeConstAndReturnBlocks(mth, retBlock, pred);\n\t\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (changed) {\n\t\t\tremoveMarkedBlocks(mth);\n\t\t\tif (DEBUG_MODS) {\n\t\t\t\tmth.get(DebugModAttr.TYPE).addEvent(\"Merge const return\");\n\t\t\t}\n\t\t}\n\t\treturn changed;\n\t}\n\n\tprivate static void mergeConstAndReturnBlocks(MethodNode mth, BlockNode retBlock, BlockNode pred) {\n\t\tpred.getInstructions().addAll(retBlock.getInstructions());\n\t\tpred.copyAttributesFrom(retBlock);\n\t\tBlockSplitter.removeConnection(pred, retBlock);\n\t\tretBlock.getInstructions().clear();\n\t\tretBlock.add(AFlag.REMOVE);\n\t\tBlockNode exitBlock = mth.getExitBlock();\n\t\tBlockSplitter.removeConnection(retBlock, exitBlock);\n\t\tBlockSplitter.connect(pred, exitBlock);\n\t\tpred.updateCleanSuccessors();\n\t}\n\n\tprivate static boolean independentBlockTreeMod(MethodNode mth) {\n\t\tboolean changed = false;\n\t\tList<BlockNode> basicBlocks = mth.getBasicBlocks();\n\t\tfor (BlockNode basicBlock : basicBlocks) {\n\t\t\tif (deduplicateBlockInsns(mth, basicBlock)) {\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t\tif (BlockExceptionHandler.process(mth)) {\n\t\t\tchanged = true;\n\t\t}\n\t\tfor (BlockNode basicBlock : basicBlocks) {\n\t\t\tif (BlockSplitter.removeEmptyBlock(basicBlock)) {\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t\tif (BlockSplitter.removeEmptyDetachedBlocks(mth)) {\n\t\t\tchanged = true;\n\t\t}\n\t\treturn changed;\n\t}\n\n\t/**\n\t * Duplicate block if it contains only one 'move' insn and all predecessors are 'switch' and 'if'.\n\t * This will help to resolve switch cases order and fallthrough detection\n\t * because such move blocks can be deduplicated by compiler.\n\t */\n\tprivate static boolean duplicateSimpleMoveBlock(MethodNode mth, BlockNode block) {\n\t\tList<InsnNode> insns = block.getInstructions();\n\t\tif (insns.size() == 1 && block.getSuccessors().size() == 1) {\n\t\t\tInsnNode insn = insns.get(0);\n\t\t\tif (insn.getType() == InsnType.MOVE) {\n\t\t\t\tList<BlockNode> preds = block.getPredecessors();\n\t\t\t\tint predSize = preds.size();\n\t\t\t\tif (predSize >= 3 && onlySwitchAndIfInLastInsns(preds)) {\n\t\t\t\t\t// confirmed, duplicate block\n\t\t\t\t\tBlockNode successor = block.getSuccessors().get(0);\n\t\t\t\t\tList<BlockNode> predsCopy = new ArrayList<>(preds);\n\t\t\t\t\tfor (int i = 1; i < predSize; i++) {\n\t\t\t\t\t\tBlockNode pred = predsCopy.get(i);\n\t\t\t\t\t\tBlockNode newBlock = BlockSplitter.startNewBlock(mth, -1);\n\t\t\t\t\t\tnewBlock.add(AFlag.SYNTHETIC);\n\t\t\t\t\t\tfor (InsnNode oldInsn : block.getInstructions()) {\n\t\t\t\t\t\t\tInsnNode copyInsn = oldInsn.copyWithoutSsa();\n\t\t\t\t\t\t\tcopyInsn.add(AFlag.SYNTHETIC);\n\t\t\t\t\t\t\tnewBlock.getInstructions().add(copyInsn);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tnewBlock.copyAttributesFrom(block);\n\t\t\t\t\t\tBlockSplitter.replaceConnection(pred, block, newBlock);\n\t\t\t\t\t\tBlockSplitter.connect(newBlock, successor);\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean onlySwitchAndIfInLastInsns(List<BlockNode> preds) {\n\t\tboolean hasSwitch = false;\n\t\tboolean hasIf = false;\n\t\tfor (BlockNode pred : preds) {\n\t\t\tInsnNode lastInsn = BlockUtils.getLastInsn(pred);\n\t\t\tif (lastInsn == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tInsnType insnType = lastInsn.getType();\n\t\t\tswitch (insnType) {\n\t\t\t\tcase SWITCH:\n\t\t\t\t\thasSwitch = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase IF:\n\t\t\t\t\thasIf = true;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn hasSwitch && hasIf;\n\t}\n\n\tprivate static boolean simplifyLoopEnd(MethodNode mth, LoopInfo loop) {\n\t\tBlockNode loopEnd = loop.getEnd();\n\t\tif (loopEnd.getSuccessors().size() <= 1) {\n\t\t\treturn false;\n\t\t}\n\t\t// make loop end a simple path block\n\t\tBlockNode newLoopEnd = BlockSplitter.startNewBlock(mth, -1);\n\t\tnewLoopEnd.add(AFlag.SYNTHETIC);\n\t\tnewLoopEnd.add(AFlag.LOOP_END);\n\t\tBlockNode loopStart = loop.getStart();\n\t\tBlockSplitter.replaceConnection(loopEnd, loopStart, newLoopEnd);\n\t\tBlockSplitter.connect(newLoopEnd, loopStart);\n\t\tif (DEBUG_MODS) {\n\t\t\tmth.get(DebugModAttr.TYPE).addEvent(\"Simplify loop end\");\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean checkLoops(MethodNode mth, BlockNode block) {\n\t\tif (!block.contains(AFlag.LOOP_START)) {\n\t\t\treturn false;\n\t\t}\n\t\tList<LoopInfo> loops = block.getAll(AType.LOOP);\n\t\tint loopsCount = loops.size();\n\t\tif (loopsCount == 0) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (LoopInfo loop : loops) {\n\t\t\tif (insertBlocksForBreak(mth, loop)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tif (loopsCount > 1 && splitLoops(mth, block, loops)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (loopsCount == 1) {\n\t\t\tLoopInfo loop = loops.get(0);\n\t\t\treturn insertBlocksForContinue(mth, loop)\n\t\t\t\t\t|| insertPreHeader(mth, loop)\n\t\t\t\t\t|| simplifyLoopEnd(mth, loop);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Insert simple path block before loop header\n\t */\n\tprivate static boolean insertPreHeader(MethodNode mth, LoopInfo loop) {\n\t\tBlockNode start = loop.getStart();\n\t\tList<BlockNode> preds = start.getPredecessors();\n\t\tint predsCount = preds.size() - 1; // don't count back edge\n\t\tif (predsCount == 1) {\n\t\t\treturn false;\n\t\t}\n\t\tif (predsCount == 0) {\n\t\t\tif (!start.contains(AFlag.MTH_ENTER_BLOCK)) {\n\t\t\t\tmth.addWarnComment(\"Unexpected block without predecessors: \" + start);\n\t\t\t}\n\t\t\tBlockNode newEnterBlock = BlockSplitter.startNewBlock(mth, -1);\n\t\t\tnewEnterBlock.add(AFlag.SYNTHETIC);\n\t\t\tnewEnterBlock.add(AFlag.MTH_ENTER_BLOCK);\n\t\t\tmth.setEnterBlock(newEnterBlock);\n\t\t\tstart.remove(AFlag.MTH_ENTER_BLOCK);\n\t\t\tBlockSplitter.connect(newEnterBlock, start);\n\t\t} else {\n\t\t\t// multiple predecessors\n\t\t\tBlockNode preHeader = BlockSplitter.startNewBlock(mth, -1);\n\t\t\tpreHeader.add(AFlag.SYNTHETIC);\n\t\t\tBlockNode loopEnd = loop.getEnd();\n\t\t\tfor (BlockNode pred : new ArrayList<>(preds)) {\n\t\t\t\tif (pred != loopEnd) {\n\t\t\t\t\tBlockSplitter.replaceConnection(pred, start, preHeader);\n\t\t\t\t}\n\t\t\t}\n\t\t\tBlockSplitter.connect(preHeader, start);\n\t\t}\n\t\tif (DEBUG_MODS) {\n\t\t\tmth.get(DebugModAttr.TYPE).addEvent(\"Insert loop pre header\");\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Insert additional blocks for possible 'break' insertion\n\t */\n\tprivate static boolean insertBlocksForBreak(MethodNode mth, LoopInfo loop) {\n\t\tboolean change = false;\n\t\tList<Edge> edges = loop.getExitEdges();\n\t\tif (!edges.isEmpty()) {\n\t\t\tfor (Edge edge : edges) {\n\t\t\t\tBlockNode target = edge.getTarget();\n\t\t\t\tBlockNode source = edge.getSource();\n\t\t\t\tif (!target.contains(AFlag.SYNTHETIC) && !source.contains(AFlag.SYNTHETIC)) {\n\t\t\t\t\tBlockSplitter.insertBlockBetween(mth, source, target);\n\t\t\t\t\tchange = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (DEBUG_MODS && change) {\n\t\t\tmth.get(DebugModAttr.TYPE).addEvent(\"Insert loop break blocks\");\n\t\t}\n\t\treturn change;\n\t}\n\n\t/**\n\t * Insert additional blocks for possible 'continue' insertion\n\t */\n\tprivate static boolean insertBlocksForContinue(MethodNode mth, LoopInfo loop) {\n\t\tBlockNode loopEnd = loop.getEnd();\n\t\tboolean change = false;\n\t\tList<BlockNode> preds = loopEnd.getPredecessors();\n\t\tif (preds.size() > 1) {\n\t\t\tfor (BlockNode pred : new ArrayList<>(preds)) {\n\t\t\t\tif (!pred.contains(AFlag.SYNTHETIC)) {\n\t\t\t\t\tBlockSplitter.insertBlockBetween(mth, pred, loopEnd);\n\t\t\t\t\tchange = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (DEBUG_MODS && change) {\n\t\t\tmth.get(DebugModAttr.TYPE).addEvent(\"Insert loop continue block\");\n\t\t}\n\t\treturn change;\n\t}\n\n\tprivate static boolean splitLoops(MethodNode mth, BlockNode block, List<LoopInfo> loops) {\n\t\tboolean oneHeader = true;\n\t\tfor (LoopInfo loop : loops) {\n\t\t\tif (loop.getStart() != block) {\n\t\t\t\toneHeader = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!oneHeader) {\n\t\t\treturn false;\n\t\t}\n\t\t// several back edges connected to one loop header => make additional block\n\t\tBlockNode newLoopEnd = BlockSplitter.startNewBlock(mth, block.getStartOffset());\n\t\tnewLoopEnd.add(AFlag.SYNTHETIC);\n\t\tconnect(newLoopEnd, block);\n\t\tfor (LoopInfo la : loops) {\n\t\t\tBlockSplitter.replaceConnection(la.getEnd(), block, newLoopEnd);\n\t\t}\n\t\tif (DEBUG_MODS) {\n\t\t\tmth.get(DebugModAttr.TYPE).addEvent(\"Split loops\");\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean splitExitBlocks(MethodNode mth) {\n\t\tboolean changed = false;\n\t\tfor (BlockNode preExitBlock : mth.getPreExitBlocks()) {\n\t\t\tif (splitReturn(mth, preExitBlock)) {\n\t\t\t\tchanged = true;\n\t\t\t} else if (splitThrow(mth, preExitBlock)) {\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t\tif (changed) {\n\t\t\tupdateExitBlockConnections(mth);\n\t\t\tif (DEBUG_MODS) {\n\t\t\t\tmth.get(DebugModAttr.TYPE).addEvent(\"Split exit block\");\n\t\t\t}\n\t\t}\n\t\treturn changed;\n\t}\n\n\tprivate static void updateExitBlockConnections(MethodNode mth) {\n\t\tBlockNode exitBlock = mth.getExitBlock();\n\t\tBlockSplitter.removePredecessors(exitBlock);\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tif (block != exitBlock\n\t\t\t\t\t&& block.getSuccessors().isEmpty()\n\t\t\t\t\t&& !block.contains(AFlag.REMOVE)) {\n\t\t\t\tBlockSplitter.connect(block, exitBlock);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Splice return block if several predecessors presents\n\t */\n\tprivate static boolean splitReturn(MethodNode mth, BlockNode returnBlock) {\n\t\tif (returnBlock.contains(AFlag.SYNTHETIC)\n\t\t\t\t|| returnBlock.contains(AFlag.ORIG_RETURN)\n\t\t\t\t|| returnBlock.contains(AType.EXC_HANDLER)) {\n\t\t\treturn false;\n\t\t}\n\t\tList<BlockNode> preds = returnBlock.getPredecessors();\n\t\tif (preds.size() < 2) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnNode returnInsn = BlockUtils.getLastInsn(returnBlock);\n\t\tif (returnInsn == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (returnInsn.getArgsCount() == 1\n\t\t\t\t&& returnBlock.getInstructions().size() == 1\n\t\t\t\t&& !isArgAssignInPred(preds, returnInsn.getArg(0))) {\n\t\t\treturn false;\n\t\t}\n\n\t\tboolean first = true;\n\t\tfor (BlockNode pred : new ArrayList<>(preds)) {\n\t\t\tif (first) {\n\t\t\t\treturnBlock.add(AFlag.ORIG_RETURN);\n\t\t\t\tfirst = false;\n\t\t\t} else {\n\t\t\t\tBlockNode newRetBlock = BlockSplitter.startNewBlock(mth, -1);\n\t\t\t\tnewRetBlock.add(AFlag.SYNTHETIC);\n\t\t\t\tnewRetBlock.add(AFlag.RETURN);\n\t\t\t\tfor (InsnNode oldInsn : returnBlock.getInstructions()) {\n\t\t\t\t\tInsnNode copyInsn = oldInsn.copyWithoutSsa();\n\t\t\t\t\tcopyInsn.add(AFlag.SYNTHETIC);\n\t\t\t\t\tnewRetBlock.getInstructions().add(copyInsn);\n\t\t\t\t}\n\t\t\t\tBlockSplitter.replaceConnection(pred, returnBlock, newRetBlock);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean splitThrow(MethodNode mth, BlockNode exitBlock) {\n\t\tif (exitBlock.contains(AFlag.IGNORE_THROW_SPLIT)) {\n\t\t\treturn false;\n\t\t}\n\t\tList<BlockNode> preds = exitBlock.getPredecessors();\n\t\tif (preds.size() < 2) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnNode throwInsn = BlockUtils.getLastInsn(exitBlock);\n\t\tif (throwInsn == null || throwInsn.getType() != InsnType.THROW) {\n\t\t\treturn false;\n\t\t}\n\t\t// split only for several exception handlers\n\t\t// traverse predecessors to exception handler\n\t\tMap<BlockNode, ExcHandlerAttr> handlersMap = new HashMap<>(preds.size());\n\t\tSet<BlockNode> handlers = new HashSet<>(preds.size());\n\t\tfor (BlockNode pred : preds) {\n\t\t\tBlockUtils.visitPredecessorsUntil(mth, pred, block -> {\n\t\t\t\tExcHandlerAttr excHandlerAttr = block.get(AType.EXC_HANDLER);\n\t\t\t\tif (excHandlerAttr == null) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tboolean correctHandler = excHandlerAttr.getHandler().getBlocks().contains(block);\n\t\t\t\tif (correctHandler && isArgAssignInPred(Collections.singletonList(block), throwInsn.getArg(0))) {\n\t\t\t\t\thandlersMap.put(pred, excHandlerAttr);\n\t\t\t\t\thandlers.add(block);\n\t\t\t\t}\n\t\t\t\treturn correctHandler;\n\t\t\t});\n\t\t}\n\t\tif (handlers.size() == 1) {\n\t\t\texitBlock.add(AFlag.IGNORE_THROW_SPLIT);\n\t\t\treturn false;\n\t\t}\n\n\t\tboolean first = true;\n\t\tfor (BlockNode pred : new ArrayList<>(preds)) {\n\t\t\tif (first) {\n\t\t\t\tfirst = false;\n\t\t\t} else {\n\t\t\t\tBlockNode newThrowBlock = BlockSplitter.startNewBlock(mth, -1);\n\t\t\t\tnewThrowBlock.add(AFlag.SYNTHETIC);\n\t\t\t\tfor (InsnNode oldInsn : exitBlock.getInstructions()) {\n\t\t\t\t\tInsnNode copyInsn = oldInsn.copyWithoutSsa();\n\t\t\t\t\tcopyInsn.add(AFlag.SYNTHETIC);\n\t\t\t\t\tnewThrowBlock.getInstructions().add(copyInsn);\n\t\t\t\t}\n\t\t\t\tnewThrowBlock.copyAttributesFrom(exitBlock);\n\t\t\t\tExcHandlerAttr excHandlerAttr = handlersMap.get(pred);\n\t\t\t\tif (excHandlerAttr != null) {\n\t\t\t\t\texcHandlerAttr.getHandler().addBlock(newThrowBlock);\n\t\t\t\t}\n\t\t\t\tBlockSplitter.replaceConnection(pred, exitBlock, newThrowBlock);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean isArgAssignInPred(List<BlockNode> preds, InsnArg arg) {\n\t\tif (arg.isRegister()) {\n\t\t\tint regNum = ((RegisterArg) arg).getRegNum();\n\t\t\tfor (BlockNode pred : preds) {\n\t\t\t\tfor (InsnNode insnNode : pred.getInstructions()) {\n\t\t\t\t\tRegisterArg result = insnNode.getResult();\n\t\t\t\t\tif (result != null && result.getRegNum() == regNum) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static void removeMarkedBlocks(MethodNode mth) {\n\t\tboolean removed = mth.getBasicBlocks().removeIf(block -> {\n\t\t\tif (block.contains(AFlag.REMOVE)) {\n\t\t\t\tif (!block.getPredecessors().isEmpty() || !block.getSuccessors().isEmpty()) {\n\t\t\t\t\tLOG.warn(\"Block {} not deleted, method: {}\", block, mth);\n\t\t\t\t} else {\n\t\t\t\t\tTryCatchBlockAttr tryBlockAttr = block.get(AType.TRY_BLOCK);\n\t\t\t\t\tif (tryBlockAttr != null) {\n\t\t\t\t\t\ttryBlockAttr.removeBlock(block);\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t});\n\t\tif (removed) {\n\t\t\tmth.updateBlockPositions();\n\t\t}\n\t}\n\n\tprivate static void removeUnreachableBlocks(MethodNode mth) {\n\t\tSet<BlockNode> toRemove = new LinkedHashSet<>();\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tcomputeUnreachableFromBlock(toRemove, block, mth);\n\t\t}\n\t\tremoveFromMethod(toRemove, mth);\n\t}\n\n\tpublic static void removeUnreachableBlock(BlockNode blockToRemove, MethodNode mth) {\n\t\tSet<BlockNode> toRemove = new LinkedHashSet<>();\n\t\tcomputeUnreachableFromBlock(toRemove, blockToRemove, mth);\n\t\tremoveFromMethod(toRemove, mth);\n\t}\n\n\tprivate static void computeUnreachableFromBlock(Set<BlockNode> toRemove, BlockNode block, MethodNode mth) {\n\t\tif (block.getPredecessors().isEmpty() && block != mth.getEnterBlock()) {\n\t\t\tBlockSplitter.collectSuccessors(block, mth.getEnterBlock(), toRemove);\n\t\t}\n\t}\n\n\tprivate static void removeFromMethod(Set<BlockNode> toRemove, MethodNode mth) {\n\t\tif (toRemove.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\n\t\tlong notEmptyBlocks = toRemove.stream().filter(block -> !block.getInstructions().isEmpty()).count();\n\t\tif (notEmptyBlocks != 0) {\n\t\t\tint insnsCount = toRemove.stream().mapToInt(block -> block.getInstructions().size()).sum();\n\t\t\tmth.addWarnComment(\"Unreachable blocks removed: \" + notEmptyBlocks + \", instructions: \" + insnsCount);\n\t\t}\n\n\t\ttoRemove.forEach(BlockSplitter::detachBlock);\n\t\tmth.getBasicBlocks().removeAll(toRemove);\n\t\tmth.updateBlockPositions();\n\t}\n\n\tprivate static void clearBlocksState(MethodNode mth) {\n\t\tmth.getBasicBlocks().forEach(block -> {\n\t\t\tblock.remove(AType.LOOP);\n\t\t\tblock.remove(AFlag.LOOP_START);\n\t\t\tblock.remove(AFlag.LOOP_END);\n\t\t\tblock.setDoms(null);\n\t\t\tblock.setIDom(null);\n\t\t\tblock.setDomFrontier(null);\n\t\t\tblock.getDominatesOn().clear();\n\t\t});\n\t}\n\n\tprivate static final class DebugModAttr implements IJadxAttribute {\n\t\tstatic final IJadxAttrType<DebugModAttr> TYPE = IJadxAttrType.create(\"DebugModAttr\");\n\n\t\tprivate final Map<String, Integer> statMap = new HashMap<>();\n\n\t\tpublic void addEvent(String name) {\n\t\t\tstatMap.merge(name, 1, Integer::sum);\n\t\t}\n\n\t\tpublic String formatStats() {\n\t\t\treturn statMap.entrySet().stream()\n\t\t\t\t\t.map(entry -> \" \" + entry.getKey() + \": \" + entry.getValue())\n\t\t\t\t\t.collect(Collectors.joining(\"\\n\"));\n\t\t}\n\n\t\t@Override\n\t\tpublic IJadxAttrType<DebugModAttr> getAttrType() {\n\t\t\treturn TYPE;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockSplitter.java",
    "content": "package jadx.core.dex.visitors.blocks;\n\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.Deque;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.JumpInfo;\nimport jadx.core.dex.attributes.nodes.TmpEdgeAttr;\nimport jadx.core.dex.instructions.IfNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.TargetInsnNode;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.trycatch.CatchAttr;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class BlockSplitter extends AbstractVisitor {\n\n\t/**\n\t * Leave these instructions alone in the block node\n\t */\n\tprivate static final Set<InsnType> SEPARATE_INSNS = EnumSet.of(\n\t\t\tInsnType.RETURN,\n\t\t\tInsnType.IF,\n\t\t\tInsnType.SWITCH,\n\t\t\tInsnType.MONITOR_ENTER,\n\t\t\tInsnType.MONITOR_EXIT,\n\t\t\tInsnType.THROW,\n\t\t\tInsnType.MOVE_EXCEPTION);\n\n\tpublic static boolean isSeparate(InsnType insnType) {\n\t\treturn SEPARATE_INSNS.contains(insnType);\n\t}\n\n\t/**\n\t * Split without connecting to the next block\n\t */\n\tprivate static final Set<InsnType> SPLIT_WITHOUT_CONNECT = EnumSet.of(\n\t\t\tInsnType.RETURN,\n\t\t\tInsnType.THROW,\n\t\t\tInsnType.GOTO,\n\t\t\tInsnType.IF,\n\t\t\tInsnType.SWITCH,\n\t\t\tInsnType.JAVA_JSR,\n\t\t\tInsnType.JAVA_RET);\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tmth.initBasicBlocks();\n\t\tMap<Integer, BlockNode> blocksMap = splitBasicBlocks(mth);\n\t\tsetupConnectionsFromJumps(mth, blocksMap);\n\t\tinitBlocksInTargetNodes(mth);\n\n\t\texpandMoveMulti(mth);\n\t\tif (mth.contains(AFlag.RESOLVE_JAVA_JSR)) {\n\t\t\tResolveJavaJSR.process(mth);\n\t\t}\n\n\t\tremoveJumpAttr(mth);\n\t\tremoveInsns(mth);\n\t\tremoveEmptyDetachedBlocks(mth);\n\t\tmth.getBasicBlocks().removeIf(BlockSplitter::removeEmptyBlock);\n\n\t\taddTempConnectionsForExcHandlers(mth, blocksMap);\n\t\tsetupExitConnections(mth);\n\n\t\tmth.updateBlockPositions();\n\t\tmth.unloadInsnArr();\n\t}\n\n\tprivate static Map<Integer, BlockNode> splitBasicBlocks(MethodNode mth) {\n\t\tBlockNode enterBlock = startNewBlock(mth, -1);\n\t\tenterBlock.add(AFlag.MTH_ENTER_BLOCK);\n\t\tmth.setEnterBlock(enterBlock);\n\n\t\tBlockNode exitBlock = startNewBlock(mth, -1);\n\t\texitBlock.add(AFlag.MTH_EXIT_BLOCK);\n\t\tmth.setExitBlock(exitBlock);\n\n\t\tMap<Integer, BlockNode> blocksMap = new HashMap<>();\n\t\tBlockNode curBlock = enterBlock;\n\t\tInsnNode prevInsn = null;\n\t\tfor (InsnNode insn : mth.getInstructions()) {\n\t\t\tif (insn == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (insn.getType() == InsnType.NOP && insn.isAttrStorageEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tint insnOffset = insn.getOffset();\n\t\t\tif (prevInsn == null) {\n\t\t\t\t// first block after method enter block\n\t\t\t\tcurBlock = connectNewBlock(mth, curBlock, insnOffset);\n\t\t\t} else {\n\t\t\t\tInsnType prevType = prevInsn.getType();\n\t\t\t\tif (SPLIT_WITHOUT_CONNECT.contains(prevType)) {\n\t\t\t\t\tcurBlock = startNewBlock(mth, insnOffset);\n\t\t\t\t} else if (isSeparate(prevType)\n\t\t\t\t\t\t|| isSeparate(insn.getType())\n\t\t\t\t\t\t|| insn.contains(AFlag.TRY_ENTER)\n\t\t\t\t\t\t|| prevInsn.contains(AFlag.TRY_LEAVE)\n\t\t\t\t\t\t|| insn.contains(AType.EXC_HANDLER)\n\t\t\t\t\t\t|| isSplitByJump(prevInsn, insn)\n\t\t\t\t\t\t|| isDoWhile(blocksMap, curBlock, insn)) {\n\t\t\t\t\tcurBlock = connectNewBlock(mth, curBlock, insnOffset);\n\t\t\t\t}\n\t\t\t}\n\t\t\tblocksMap.put(insnOffset, curBlock);\n\t\t\tcurBlock.getInstructions().add(insn);\n\t\t\tprevInsn = insn;\n\t\t}\n\t\treturn blocksMap;\n\t}\n\n\t/**\n\t * Init 'then' and 'else' blocks for 'if' instruction.\n\t */\n\tprivate static void initBlocksInTargetNodes(MethodNode mth) {\n\t\tmth.getBasicBlocks().forEach(block -> {\n\t\t\tInsnNode lastInsn = BlockUtils.getLastInsn(block);\n\t\t\tif (lastInsn instanceof TargetInsnNode) {\n\t\t\t\t((TargetInsnNode) lastInsn).initBlocks(block);\n\t\t\t}\n\t\t});\n\t}\n\n\tstatic BlockNode connectNewBlock(MethodNode mth, BlockNode block, int offset) {\n\t\tBlockNode newBlock = startNewBlock(mth, offset);\n\t\tconnect(block, newBlock);\n\t\treturn newBlock;\n\t}\n\n\tstatic BlockNode startNewBlock(MethodNode mth, int offset) {\n\t\tList<BlockNode> blocks = mth.getBasicBlocks();\n\t\tBlockNode block = new BlockNode(mth.getNextBlockCId(), blocks.size(), offset);\n\t\tblocks.add(block);\n\t\treturn block;\n\t}\n\n\tpublic static void connect(BlockNode from, BlockNode to) {\n\t\tif (!from.getSuccessors().contains(to)) {\n\t\t\tfrom.getSuccessors().add(to);\n\t\t}\n\t\tif (!to.getPredecessors().contains(from)) {\n\t\t\tto.getPredecessors().add(from);\n\t\t}\n\t}\n\n\tpublic static void removeConnection(BlockNode from, BlockNode to) {\n\t\tfrom.getSuccessors().remove(to);\n\t\tto.getPredecessors().remove(from);\n\t}\n\n\tpublic static void removePredecessors(BlockNode block) {\n\t\tfor (BlockNode pred : block.getPredecessors()) {\n\t\t\tpred.getSuccessors().remove(block);\n\t\t}\n\t\tblock.getPredecessors().clear();\n\t}\n\n\tpublic static void replaceConnection(BlockNode source, BlockNode oldDest, BlockNode newDest) {\n\t\tremoveConnection(source, oldDest);\n\t\tconnect(source, newDest);\n\t\treplaceTarget(source, oldDest, newDest);\n\t}\n\n\tpublic static BlockNode insertBlockBetween(MethodNode mth, BlockNode source, BlockNode target) {\n\t\tBlockNode newBlock = startNewBlock(mth, target.getStartOffset());\n\t\tnewBlock.add(AFlag.SYNTHETIC);\n\t\tremoveConnection(source, target);\n\t\tconnect(source, newBlock);\n\t\tconnect(newBlock, target);\n\t\treplaceTarget(source, target, newBlock);\n\t\tsource.updateCleanSuccessors();\n\t\tnewBlock.updateCleanSuccessors();\n\t\treturn newBlock;\n\t}\n\n\tstatic BlockNode blockSplitTop(MethodNode mth, BlockNode block) {\n\t\tBlockNode newBlock = startNewBlock(mth, block.getStartOffset());\n\t\tfor (BlockNode pred : new ArrayList<>(block.getPredecessors())) {\n\t\t\treplaceConnection(pred, block, newBlock);\n\t\t\tpred.updateCleanSuccessors();\n\t\t}\n\t\tconnect(newBlock, block);\n\t\tnewBlock.updateCleanSuccessors();\n\t\treturn newBlock;\n\t}\n\n\tstatic void copyBlockData(BlockNode from, BlockNode to) {\n\t\tList<InsnNode> toInsns = to.getInstructions();\n\t\tfor (InsnNode insn : from.getInstructions()) {\n\t\t\ttoInsns.add(insn.copyWithoutSsa());\n\t\t}\n\t\tto.copyAttributesFrom(from);\n\t}\n\n\tstatic List<BlockNode> copyBlocksTree(MethodNode mth, List<BlockNode> blocks) {\n\t\tList<BlockNode> copyBlocks = new ArrayList<>(blocks.size());\n\t\tMap<BlockNode, BlockNode> map = new HashMap<>();\n\t\tfor (BlockNode block : blocks) {\n\t\t\tBlockNode newBlock = startNewBlock(mth, block.getStartOffset());\n\t\t\tcopyBlockData(block, newBlock);\n\t\t\tcopyBlocks.add(newBlock);\n\t\t\tmap.put(block, newBlock);\n\t\t}\n\t\tfor (BlockNode block : blocks) {\n\t\t\tBlockNode newBlock = getNewBlock(block, map);\n\t\t\tfor (BlockNode successor : block.getSuccessors()) {\n\t\t\t\tBlockNode newSuccessor = getNewBlock(successor, map);\n\t\t\t\tBlockSplitter.connect(newBlock, newSuccessor);\n\t\t\t}\n\t\t}\n\t\treturn copyBlocks;\n\t}\n\n\tprivate static BlockNode getNewBlock(BlockNode block, Map<BlockNode, BlockNode> map) {\n\t\tBlockNode newBlock = map.get(block);\n\t\tif (newBlock == null) {\n\t\t\tthrow new JadxRuntimeException(\"Copy blocks tree failed. Missing block for connection: \" + block);\n\t\t}\n\t\treturn newBlock;\n\t}\n\n\tstatic void replaceTarget(BlockNode source, BlockNode oldTarget, BlockNode newTarget) {\n\t\tInsnNode lastInsn = BlockUtils.getLastInsn(source);\n\t\tif (lastInsn instanceof TargetInsnNode) {\n\t\t\t((TargetInsnNode) lastInsn).replaceTargetBlock(oldTarget, newTarget);\n\t\t}\n\t}\n\n\tprivate static void setupConnectionsFromJumps(MethodNode mth, Map<Integer, BlockNode> blocksMap) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tList<JumpInfo> jumps = insn.getAll(AType.JUMP);\n\t\t\t\tfor (JumpInfo jump : jumps) {\n\t\t\t\t\tBlockNode srcBlock = getBlock(jump.getSrc(), blocksMap);\n\t\t\t\t\tBlockNode thisBlock = getBlock(jump.getDest(), blocksMap);\n\t\t\t\t\tconnect(srcBlock, thisBlock);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Connect exception handlers to the throw block.\n\t * This temporary connection is necessary to build close to a final dominator tree.\n\t * Will be used and removed in {@code jadx.core.dex.visitors.blocks.BlockExceptionHandler}\n\t */\n\tprivate static void addTempConnectionsForExcHandlers(MethodNode mth, Map<Integer, BlockNode> blocksMap) {\n\t\tif (mth.isNoExceptionHandlers()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tCatchAttr catchAttr = insn.get(AType.EXC_CATCH);\n\t\t\t\tif (catchAttr == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tfor (ExceptionHandler handler : catchAttr.getHandlers()) {\n\t\t\t\t\tBlockNode handlerBlock = getBlock(handler.getHandlerOffset(), blocksMap);\n\t\t\t\t\tif (!handlerBlock.contains(AType.TMP_EDGE)) {\n\t\t\t\t\t\tList<BlockNode> preds = block.getPredecessors();\n\t\t\t\t\t\tif (preds.isEmpty()) {\n\t\t\t\t\t\t\tthrow new JadxRuntimeException(\"Unexpected missing predecessor for block: \" + block);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tBlockNode start = preds.size() == 1 ? preds.get(0) : block;\n\t\t\t\t\t\tif (!start.getSuccessors().contains(handlerBlock)) {\n\t\t\t\t\t\t\tconnect(start, handlerBlock);\n\t\t\t\t\t\t\thandlerBlock.addAttr(new TmpEdgeAttr(start));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void setupExitConnections(MethodNode mth) {\n\t\tBlockNode exitBlock = mth.getExitBlock();\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tif (block.getSuccessors().isEmpty() && block != exitBlock) {\n\t\t\t\tconnect(block, exitBlock);\n\t\t\t\tif (BlockUtils.checkLastInsnType(block, InsnType.RETURN)) {\n\t\t\t\t\tblock.add(AFlag.RETURN);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean isSplitByJump(InsnNode prevInsn, InsnNode currentInsn) {\n\t\tList<JumpInfo> pJumps = prevInsn.getAll(AType.JUMP);\n\t\tfor (JumpInfo jump : pJumps) {\n\t\t\tif (jump.getSrc() == prevInsn.getOffset()) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tList<JumpInfo> cJumps = currentInsn.getAll(AType.JUMP);\n\t\tfor (JumpInfo jump : cJumps) {\n\t\t\tif (jump.getDest() == currentInsn.getOffset()) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean isDoWhile(Map<Integer, BlockNode> blocksMap, BlockNode curBlock, InsnNode insn) {\n\t\t// split 'do-while' block (last instruction: 'if', target this block)\n\t\tif (insn.getType() != InsnType.IF) {\n\t\t\treturn false;\n\t\t}\n\t\tIfNode ifs = (IfNode) insn;\n\t\tBlockNode targetBlock = blocksMap.get(ifs.getTarget());\n\t\treturn targetBlock == curBlock;\n\t}\n\n\tprivate static BlockNode getBlock(int offset, Map<Integer, BlockNode> blocksMap) {\n\t\tBlockNode block = blocksMap.get(offset);\n\t\tif (block == null) {\n\t\t\tthrow new JadxRuntimeException(\"Missing block: \" + offset);\n\t\t}\n\t\treturn block;\n\t}\n\n\tprivate static void expandMoveMulti(MethodNode mth) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tList<InsnNode> insnsList = block.getInstructions();\n\t\t\tint len = insnsList.size();\n\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\tInsnNode insn = insnsList.get(i);\n\t\t\t\tif (insn.getType() == InsnType.MOVE_MULTI) {\n\t\t\t\t\tint mvCount = insn.getArgsCount() / 2;\n\t\t\t\t\tfor (int j = 0; j < mvCount; j++) {\n\t\t\t\t\t\tInsnNode mv = new InsnNode(InsnType.MOVE, 1);\n\t\t\t\t\t\tint startArg = j * 2;\n\t\t\t\t\t\tmv.setResult((RegisterArg) insn.getArg(startArg));\n\t\t\t\t\t\tmv.addArg(insn.getArg(startArg + 1));\n\t\t\t\t\t\tmv.copyAttributesFrom(insn);\n\t\t\t\t\t\tif (j == 0) {\n\t\t\t\t\t\t\tmv.setOffset(insn.getOffset());\n\t\t\t\t\t\t\tinsnsList.set(i, mv);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tinsnsList.add(i + j, mv);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\ti += mvCount - 1;\n\t\t\t\t\tlen = insnsList.size();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void removeJumpAttr(MethodNode mth) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tinsn.remove(AType.JUMP);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void removeInsns(MethodNode mth) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tblock.getInstructions().removeIf(insn -> {\n\t\t\t\tif (!insn.isAttrStorageEmpty()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tInsnType insnType = insn.getType();\n\t\t\t\treturn insnType == InsnType.GOTO || insnType == InsnType.NOP;\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic static void detachMarkedBlocks(MethodNode mth) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tif (block.contains(AFlag.REMOVE)) {\n\t\t\t\tdetachBlock(block);\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic boolean removeEmptyDetachedBlocks(MethodNode mth) {\n\t\treturn mth.getBasicBlocks().removeIf(block -> block.getInstructions().isEmpty()\n\t\t\t\t&& block.getPredecessors().isEmpty()\n\t\t\t\t&& block.getSuccessors().isEmpty()\n\t\t\t\t&& !block.contains(AFlag.MTH_ENTER_BLOCK)\n\t\t\t\t&& !block.contains(AFlag.MTH_EXIT_BLOCK));\n\t}\n\n\tstatic boolean removeEmptyBlock(BlockNode block) {\n\t\tif (canRemoveBlock(block)) {\n\t\t\tif (block.getSuccessors().size() == 1) {\n\t\t\t\tBlockNode successor = block.getSuccessors().get(0);\n\t\t\t\tblock.getPredecessors().forEach(pred -> {\n\t\t\t\t\tpred.getSuccessors().remove(block);\n\t\t\t\t\tBlockSplitter.connect(pred, successor);\n\t\t\t\t\tBlockSplitter.replaceTarget(pred, block, successor);\n\t\t\t\t\tpred.updateCleanSuccessors();\n\t\t\t\t});\n\t\t\t\tBlockSplitter.removeConnection(block, successor);\n\t\t\t} else {\n\t\t\t\tblock.getPredecessors().forEach(pred -> {\n\t\t\t\t\tpred.getSuccessors().remove(block);\n\t\t\t\t\tpred.updateCleanSuccessors();\n\t\t\t\t});\n\t\t\t}\n\t\t\tblock.add(AFlag.REMOVE);\n\t\t\tblock.getSuccessors().clear();\n\t\t\tblock.getPredecessors().clear();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean canRemoveBlock(BlockNode block) {\n\t\treturn block.getInstructions().isEmpty()\n\t\t\t\t&& block.isAttrStorageEmpty()\n\t\t\t\t&& block.getSuccessors().size() <= 1\n\t\t\t\t&& !block.getPredecessors().isEmpty()\n\t\t\t\t&& !block.contains(AFlag.MTH_ENTER_BLOCK)\n\t\t\t\t&& !block.contains(AFlag.MTH_EXIT_BLOCK)\n\t\t\t\t&& !block.getSuccessors().contains(block); // no self loop\n\t}\n\n\tstatic void collectSuccessors(BlockNode startBlock, BlockNode methodEnterBlock, Set<BlockNode> toRemove) {\n\t\tDeque<BlockNode> stack = new ArrayDeque<>();\n\t\tstack.add(startBlock);\n\t\twhile (!stack.isEmpty()) {\n\t\t\tBlockNode block = stack.pop();\n\t\t\tif (!toRemove.contains(block)) {\n\t\t\t\ttoRemove.add(block);\n\t\t\t\tfor (BlockNode successor : block.getSuccessors()) {\n\t\t\t\t\tif (successor != methodEnterBlock && toRemove.containsAll(successor.getPredecessors())) {\n\t\t\t\t\t\tstack.push(successor);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic void detachBlock(BlockNode block) {\n\t\tfor (BlockNode pred : block.getPredecessors()) {\n\t\t\tpred.getSuccessors().remove(block);\n\t\t\tpred.updateCleanSuccessors();\n\t\t}\n\t\tfor (BlockNode successor : block.getSuccessors()) {\n\t\t\tsuccessor.getPredecessors().remove(block);\n\t\t}\n\t\tblock.add(AFlag.REMOVE);\n\t\tblock.getPredecessors().clear();\n\t\tblock.getSuccessors().clear();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/blocks/DominatorTree.java",
    "content": "package jadx.core.dex.visitors.blocks;\n\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.EmptyBitSet;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n/**\n * Build dominator tree based on the algorithm described in paper:\n * Cooper, Keith D.; Harvey, Timothy J; Kennedy, Ken (2001).\n * \"A Simple, Fast Dominance Algorithm\"\n * http://www.hipersoft.rice.edu/grads/publications/dom14.pdf\n */\n@SuppressWarnings(\"JavadocLinkAsPlainText\")\npublic class DominatorTree {\n\n\tpublic static void compute(MethodNode mth) {\n\t\tList<BlockNode> sorted = sortBlocks(mth);\n\t\tBlockNode[] doms = build(sorted, BlockNode::getPredecessors);\n\t\tapply(sorted, doms);\n\t}\n\n\tprivate static List<BlockNode> sortBlocks(MethodNode mth) {\n\t\tint blocksCount = mth.getBasicBlocks().size();\n\t\tList<BlockNode> sorted = new ArrayList<>(blocksCount);\n\t\tBlockUtils.visitDFS(mth, sorted::add);\n\t\tif (sorted.size() != blocksCount) {\n\t\t\tthrow new JadxRuntimeException(\"Found unreachable blocks\");\n\t\t}\n\t\tmth.setBasicBlocks(sorted);\n\t\treturn sorted;\n\t}\n\n\tstatic BlockNode[] build(List<BlockNode> sorted, Function<BlockNode, List<BlockNode>> predFunc) {\n\t\tint blocksCount = sorted.size();\n\t\tBlockNode[] doms = new BlockNode[blocksCount];\n\t\tdoms[0] = sorted.get(0);\n\t\tboolean changed = true;\n\t\twhile (changed) {\n\t\t\tchanged = false;\n\t\t\tfor (int blockId = 1; blockId < blocksCount; blockId++) {\n\t\t\t\tBlockNode b = sorted.get(blockId);\n\t\t\t\tList<BlockNode> preds = predFunc.apply(b);\n\t\t\t\tint pickedPred = -1;\n\t\t\t\tBlockNode newIDom = null;\n\t\t\t\tfor (BlockNode pred : preds) {\n\t\t\t\t\tint id = pred.getId();\n\t\t\t\t\tif (doms[id] != null) {\n\t\t\t\t\t\tnewIDom = pred;\n\t\t\t\t\t\tpickedPred = id;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (newIDom == null) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"No immediate dominator for block: \" + b);\n\t\t\t\t}\n\t\t\t\tfor (BlockNode predBlock : preds) {\n\t\t\t\t\tint predId = predBlock.getId();\n\t\t\t\t\tif (predId == pickedPred) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (doms[predId] != null) {\n\t\t\t\t\t\tnewIDom = intersect(sorted, doms, predBlock, newIDom);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (doms[blockId] != newIDom) {\n\t\t\t\t\tdoms[blockId] = newIDom;\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn doms;\n\t}\n\n\tprivate static BlockNode intersect(List<BlockNode> sorted, BlockNode[] doms, BlockNode b1, BlockNode b2) {\n\t\tint f1 = b1.getId();\n\t\tint f2 = b2.getId();\n\t\twhile (f1 != f2) {\n\t\t\twhile (f1 > f2) {\n\t\t\t\tf1 = doms[f1].getId();\n\t\t\t}\n\t\t\twhile (f2 > f1) {\n\t\t\t\tf2 = doms[f2].getId();\n\t\t\t}\n\t\t}\n\t\treturn sorted.get(f1);\n\t}\n\n\tprivate static void apply(List<BlockNode> sorted, BlockNode[] doms) {\n\t\tBlockNode enterBlock = sorted.get(0);\n\t\tenterBlock.setDoms(EmptyBitSet.EMPTY);\n\t\tenterBlock.setIDom(null);\n\t\tint blocksCount = sorted.size();\n\t\tfor (int i = 1; i < blocksCount; i++) {\n\t\t\tBlockNode block = sorted.get(i);\n\t\t\tBlockNode idom = doms[i];\n\t\t\tblock.setIDom(idom);\n\t\t\tidom.addDominatesOn(block);\n\t\t\tBitSet domBS = collectDoms(doms, idom);\n\t\t\tdomBS.clear(i);\n\t\t\tblock.setDoms(domBS);\n\t\t}\n\t}\n\n\tstatic BitSet collectDoms(BlockNode[] doms, BlockNode idom) {\n\t\tBitSet domBS = new BitSet(doms.length);\n\t\tBlockNode nextIDom = idom;\n\t\twhile (true) {\n\t\t\tint id = nextIDom.getId();\n\t\t\tif (domBS.get(id)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdomBS.set(id);\n\t\t\tBitSet curDoms = nextIDom.getDoms();\n\t\t\tif (curDoms != null) {\n\t\t\t\t// use already collected set\n\t\t\t\tdomBS.or(curDoms);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tnextIDom = doms[id];\n\t\t}\n\t\treturn domBS;\n\t}\n\n\tpublic static void computeDominanceFrontier(MethodNode mth) {\n\t\tList<BlockNode> blocks = mth.getBasicBlocks();\n\t\tfor (BlockNode block : blocks) {\n\t\t\tblock.setDomFrontier(null);\n\t\t}\n\t\tint blocksCount = blocks.size();\n\t\tfor (BlockNode block : blocks) {\n\t\t\tList<BlockNode> preds = block.getPredecessors();\n\t\t\tif (preds.size() >= 2) {\n\t\t\t\tBlockNode idom = block.getIDom();\n\t\t\t\tfor (BlockNode pred : preds) {\n\t\t\t\t\tBlockNode runner = pred;\n\t\t\t\t\twhile (runner != idom) {\n\t\t\t\t\t\taddToDF(runner, block, blocksCount);\n\t\t\t\t\t\trunner = runner.getIDom();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (BlockNode block : blocks) {\n\t\t\tBitSet df = block.getDomFrontier();\n\t\t\tif (df == null || df.isEmpty()) {\n\t\t\t\tblock.setDomFrontier(EmptyBitSet.EMPTY);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void addToDF(BlockNode block, BlockNode dfBlock, int blocksCount) {\n\t\tBitSet df = block.getDomFrontier();\n\t\tif (df == null) {\n\t\t\tdf = new BitSet(blocksCount);\n\t\t\tblock.setDomFrontier(df);\n\t\t}\n\t\tdf.set(dfBlock.getId());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/blocks/FixMultiEntryLoops.java",
    "content": "package jadx.core.dex.visitors.blocks;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.SpecialEdgeAttr;\nimport jadx.core.dex.attributes.nodes.SpecialEdgeAttr.SpecialEdgeType;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.ListUtils;\n\npublic class FixMultiEntryLoops {\n\n\tpublic static boolean process(MethodNode mth) {\n\t\ttry {\n\t\t\tdetectSpecialEdges(mth);\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Failed to detect multi-entry loops\", e);\n\t\t\treturn false;\n\t\t}\n\t\tList<SpecialEdgeAttr> specialEdges = mth.getAll(AType.SPECIAL_EDGE);\n\t\tList<SpecialEdgeAttr> multiEntryLoops = specialEdges.stream()\n\t\t\t\t.filter(e -> e.getType() == SpecialEdgeType.BACK_EDGE && !isSingleEntryLoop(e))\n\t\t\t\t.collect(Collectors.toList());\n\t\tif (multiEntryLoops.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tList<SpecialEdgeAttr> crossEdges = ListUtils.filter(specialEdges, e -> e.getType() == SpecialEdgeType.CROSS_EDGE);\n\t\t\tboolean changed = false;\n\t\t\tfor (SpecialEdgeAttr backEdge : multiEntryLoops) {\n\t\t\t\tchanged |= fixLoop(mth, backEdge, crossEdges);\n\t\t\t}\n\t\t\treturn changed;\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Failed to fix multi-entry loops\", e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate static boolean fixLoop(MethodNode mth, SpecialEdgeAttr backEdge, List<SpecialEdgeAttr> crossEdges) {\n\t\tif (isHeaderSuccessorEntry(mth, backEdge, crossEdges)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (isEndBlockEntry(mth, backEdge, crossEdges)) {\n\t\t\treturn true;\n\t\t}\n\t\tmth.addWarnComment(\"Unsupported multi-entry loop pattern (\" + backEdge + \"). Please report as a decompilation issue!!!\");\n\t\treturn false;\n\t}\n\n\tprivate static boolean isHeaderSuccessorEntry(MethodNode mth, SpecialEdgeAttr backEdge, List<SpecialEdgeAttr> crossEdges) {\n\t\tBlockNode header = backEdge.getEnd();\n\t\tBlockNode headerIDom = header.getIDom();\n\t\tSpecialEdgeAttr subEntry = ListUtils.filterOnlyOne(crossEdges, e -> e.getStart() == headerIDom);\n\t\tif (subEntry == null || !ListUtils.isSingleElement(header.getSuccessors(), subEntry.getEnd())) {\n\t\t\treturn false;\n\t\t}\n\t\tBlockNode loopEnd = backEdge.getStart();\n\t\tBlockNode subEntryBlock = subEntry.getEnd();\n\t\tBlockNode copyHeader = BlockSplitter.insertBlockBetween(mth, loopEnd, header);\n\t\tBlockSplitter.copyBlockData(header, copyHeader);\n\t\tBlockSplitter.replaceConnection(copyHeader, header, subEntryBlock);\n\t\tmth.addDebugComment(\"Duplicate block (\" + header + \") to fix multi-entry loop: \" + backEdge);\n\t\treturn true;\n\t}\n\n\tprivate static boolean isEndBlockEntry(MethodNode mth, SpecialEdgeAttr backEdge, List<SpecialEdgeAttr> crossEdges) {\n\t\tBlockNode loopEnd = backEdge.getStart();\n\t\tSpecialEdgeAttr subEntry = ListUtils.filterOnlyOne(crossEdges, e -> e.getEnd() == loopEnd);\n\t\tif (subEntry == null) {\n\t\t\treturn false;\n\t\t}\n\t\tdupPath(mth, subEntry.getStart(), loopEnd, backEdge.getEnd());\n\t\tmth.addDebugComment(\"Duplicate block (\" + loopEnd + \") to fix multi-entry loop: \" + backEdge);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Duplicate 'center' block on path from 'start' to 'end'\n\t */\n\tprivate static void dupPath(MethodNode mth, BlockNode start, BlockNode center, BlockNode end) {\n\t\tBlockNode copyCenter = BlockSplitter.insertBlockBetween(mth, start, end);\n\t\tBlockSplitter.copyBlockData(center, copyCenter);\n\t\tBlockSplitter.removeConnection(start, center);\n\t}\n\n\tprivate static boolean isSingleEntryLoop(SpecialEdgeAttr e) {\n\t\tBlockNode header = e.getEnd();\n\t\tBlockNode loopEnd = e.getStart();\n\t\treturn header == loopEnd\n\t\t\t\t|| loopEnd.getDoms().get(header.getId()); // header dominates loop end\n\t}\n\n\tprivate enum BlockColor {\n\t\tWHITE, GRAY, BLACK\n\t}\n\n\tprivate static void detectSpecialEdges(MethodNode mth) {\n\t\tBlockColor[] colors = new BlockColor[mth.getBasicBlocks().size()];\n\t\tArrays.fill(colors, BlockColor.WHITE);\n\t\tcolorDFS(mth, colors, mth.getEnterBlock());\n\t}\n\n\t// TODO: transform to non-recursive form\n\tprivate static void colorDFS(MethodNode mth, BlockColor[] colors, BlockNode block) {\n\t\tcolors[block.getId()] = BlockColor.GRAY;\n\t\tfor (BlockNode v : block.getSuccessors()) {\n\t\t\tswitch (colors[v.getId()]) {\n\t\t\t\tcase WHITE:\n\t\t\t\t\tcolorDFS(mth, colors, v);\n\t\t\t\t\tbreak;\n\t\t\t\tcase GRAY:\n\t\t\t\t\tmth.addAttr(AType.SPECIAL_EDGE, new SpecialEdgeAttr(SpecialEdgeType.BACK_EDGE, block, v));\n\t\t\t\t\tbreak;\n\t\t\t\tcase BLACK:\n\t\t\t\t\tmth.addAttr(AType.SPECIAL_EDGE, new SpecialEdgeAttr(SpecialEdgeType.CROSS_EDGE, block, v));\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tcolors[block.getId()] = BlockColor.BLACK;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/blocks/PostDominatorTree.java",
    "content": "package jadx.core.dex.visitors.blocks;\n\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.List;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.EmptyBitSet;\n\npublic class PostDominatorTree {\n\n\tpublic static void compute(MethodNode mth) {\n\t\tif (!mth.contains(AFlag.COMPUTE_POST_DOM)) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tint mthBlocksCount = mth.getBasicBlocks().size();\n\t\t\tList<BlockNode> sorted = new ArrayList<>(mthBlocksCount);\n\t\t\tBlockUtils.visitReverseDFS(mth, sorted::add);\n\t\t\t// temporary set block positions to match reverse sorted order\n\t\t\t// save old positions for later remapping\n\t\t\tint blocksCount = sorted.size();\n\t\t\tint[] posMapping = new int[mthBlocksCount];\n\t\t\tfor (int i = 0; i < blocksCount; i++) {\n\t\t\t\tposMapping[i] = sorted.get(i).getPos();\n\t\t\t}\n\t\t\tBlockNode.updateBlockPositions(sorted);\n\n\t\t\tBlockNode[] postDoms = DominatorTree.build(sorted, BlockNode::getSuccessors);\n\t\t\tBlockNode firstBlock = sorted.get(0);\n\t\t\tfirstBlock.setPostDoms(EmptyBitSet.EMPTY);\n\t\t\tfirstBlock.setIPostDom(null);\n\t\t\tfor (int i = 1; i < blocksCount; i++) {\n\t\t\t\tBlockNode block = sorted.get(i);\n\t\t\t\tBlockNode iPostDom = postDoms[i];\n\t\t\t\tblock.setIPostDom(iPostDom);\n\t\t\t\tBitSet postDomBS = DominatorTree.collectDoms(postDoms, iPostDom);\n\t\t\t\tblock.setPostDoms(postDomBS);\n\t\t\t}\n\t\t\tfor (int i = 1; i < blocksCount; i++) {\n\t\t\t\tBlockNode block = sorted.get(i);\n\t\t\t\tBitSet bs = new BitSet(blocksCount);\n\t\t\t\tblock.getPostDoms().stream().forEach(n -> bs.set(posMapping[n]));\n\t\t\t\tbs.clear(posMapping[i]);\n\t\t\t\tblock.setPostDoms(bs);\n\t\t\t}\n\t\t\t// check for missing blocks in 'sorted' list\n\t\t\t// can be caused by infinite loops\n\t\t\tint blocksDelta = mthBlocksCount - blocksCount;\n\t\t\tif (blocksDelta != 0) {\n\t\t\t\tint insnsCount = 0;\n\t\t\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\t\t\tif (block.getPostDoms() == null) {\n\t\t\t\t\t\tblock.setPostDoms(EmptyBitSet.EMPTY);\n\t\t\t\t\t\tblock.setIPostDom(null);\n\t\t\t\t\t\tinsnsCount += block.getInstructions().size();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmth.addInfoComment(\"Infinite loop detected, blocks: \" + blocksDelta + \", insns: \" + insnsCount);\n\t\t\t}\n\t\t} catch (StackOverflowError | Exception e) {\n\t\t\t// show error as a warning because this info not always used\n\t\t\tmth.addWarnComment(\"Failed to build post-dominance tree\", e);\n\t\t} finally {\n\t\t\t// revert block positions change\n\t\t\tmth.updateBlockPositions();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/blocks/ResolveJavaJSR.java",
    "content": "package jadx.core.dex.visitors.blocks;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n/**\n * Duplicate code to resolve java jsr/ret.\n * JSR (jump subroutine) allows executing the same code from different places.\n * Used mostly for 'finally' blocks, deprecated in Java 7.\n */\npublic class ResolveJavaJSR {\n\n\tpublic static void process(MethodNode mth) {\n\t\tint blocksCount = mth.getBasicBlocks().size();\n\t\tint k = 0;\n\t\twhile (true) {\n\t\t\tboolean changed = resolve(mth);\n\t\t\tif (!changed) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (k++ > blocksCount) {\n\t\t\t\tthrow new JadxRuntimeException(\"Fail to resolve jsr instructions\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean resolve(MethodNode mth) {\n\t\tList<BlockNode> blocks = mth.getBasicBlocks();\n\t\tint blocksCount = blocks.size();\n\t\tfor (BlockNode block : blocks) {\n\t\t\tif (BlockUtils.checkLastInsnType(block, InsnType.JAVA_RET)) {\n\t\t\t\tresolveForRetBlock(mth, block);\n\t\t\t\tif (blocksCount != mth.getBasicBlocks().size()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static void resolveForRetBlock(MethodNode mth, BlockNode retBlock) {\n\t\tBlockUtils.visitPredecessorsUntil(mth, retBlock, startBlock -> {\n\t\t\tList<BlockNode> preds = startBlock.getPredecessors();\n\t\t\tif (preds.size() > 1\n\t\t\t\t\t&& preds.stream().allMatch(p -> BlockUtils.checkLastInsnType(p, InsnType.JAVA_JSR))) {\n\t\t\t\tList<BlockNode> jsrBlocks = new ArrayList<>(preds);\n\t\t\t\tList<BlockNode> dupBlocks = BlockUtils.collectAllSuccessors(mth, startBlock, false);\n\t\t\t\tremoveInsns(retBlock, startBlock, jsrBlocks);\n\t\t\t\tprocessBlocks(mth, retBlock, startBlock, jsrBlocks, dupBlocks);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t});\n\t}\n\n\tprivate static void removeInsns(BlockNode retBlock, BlockNode startBlock, List<BlockNode> jsrBlocks) {\n\t\tInsnNode retInsn = ListUtils.removeLast(retBlock.getInstructions());\n\t\tif (retInsn != null && retInsn.getType() == InsnType.JAVA_RET) {\n\t\t\tInsnArg retArg = retInsn.getArg(0);\n\t\t\tif (retArg.isRegister()) {\n\t\t\t\tint regNum = ((RegisterArg) retArg).getRegNum();\n\t\t\t\tInsnNode startInsn = BlockUtils.getFirstInsn(startBlock);\n\t\t\t\tif (startInsn != null\n\t\t\t\t\t\t&& startInsn.getType() == InsnType.MOVE\n\t\t\t\t\t\t&& startInsn.getResult().getRegNum() == regNum) {\n\t\t\t\t\tstartBlock.getInstructions().remove(0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tjsrBlocks.forEach(p -> ListUtils.removeLast(p.getInstructions()));\n\t}\n\n\tprivate static void processBlocks(MethodNode mth, BlockNode retBlock, BlockNode startBlock,\n\t\t\tList<BlockNode> jsrBlocks, List<BlockNode> dupBlocks) {\n\t\tBlockNode first = null;\n\t\tfor (BlockNode jsrBlock : jsrBlocks) {\n\t\t\tif (first == null) {\n\t\t\t\tfirst = jsrBlock;\n\t\t\t} else {\n\t\t\t\tBlockNode pathBlock = BlockUtils.selectOther(startBlock, jsrBlock.getSuccessors());\n\t\t\t\tBlockSplitter.removeConnection(jsrBlock, startBlock);\n\t\t\t\tBlockSplitter.removeConnection(jsrBlock, pathBlock);\n\t\t\t\tList<BlockNode> newBlocks = BlockSplitter.copyBlocksTree(mth, dupBlocks);\n\t\t\t\tBlockNode newStart = newBlocks.get(dupBlocks.indexOf(startBlock));\n\t\t\t\tBlockNode newRetBlock = newBlocks.get(dupBlocks.indexOf(retBlock));\n\t\t\t\tBlockSplitter.connect(jsrBlock, newStart);\n\t\t\t\tBlockSplitter.connect(newRetBlock, pathBlock);\n\t\t\t}\n\t\t}\n\t\tif (first != null) {\n\t\t\tBlockNode pathBlock = BlockUtils.selectOther(startBlock, first.getSuccessors());\n\t\t\tBlockSplitter.removeConnection(first, pathBlock);\n\t\t\tBlockSplitter.connect(retBlock, pathBlock);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/DebugInfoApplyVisitor.java",
    "content": "package jadx.core.dex.visitors.debuginfo;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.OptionalInt;\nimport java.util.Set;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.api.plugins.input.data.ILocalVar;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.types.MethodParametersAttr;\nimport jadx.core.Consts;\nimport jadx.core.deobf.NameMapper;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.LocalVarsDebugInfoAttr;\nimport jadx.core.dex.attributes.nodes.RegDebugInfoAttr;\nimport jadx.core.dex.instructions.PhiInsn;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.CodeVar;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.Named;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.dex.visitors.ssa.SSATransform;\nimport jadx.core.dex.visitors.typeinference.TypeInferenceVisitor;\nimport jadx.core.dex.visitors.typeinference.TypeUpdateResult;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"Debug Info Apply\",\n\t\tdesc = \"Apply debug info to registers (type and names)\",\n\t\trunAfter = {\n\t\t\t\tSSATransform.class,\n\t\t\t\tTypeInferenceVisitor.class\n\t\t}\n)\npublic class DebugInfoApplyVisitor extends AbstractVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DebugInfoApplyVisitor.class);\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\ttry {\n\t\t\tif (mth.contains(AType.LOCAL_VARS_DEBUG_INFO)) {\n\t\t\t\tapplyDebugInfo(mth);\n\t\t\t\tmth.remove(AType.LOCAL_VARS_DEBUG_INFO);\n\t\t\t}\n\t\t\tprocessMethodParametersAttribute(mth);\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Failed to apply debug info\", e);\n\t\t}\n\t}\n\n\tprivate static void applyDebugInfo(MethodNode mth) {\n\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\tLOG.info(\"Apply debug info for method: {}\", mth);\n\t\t}\n\t\tmth.getSVars().forEach(ssaVar -> searchAndApplyVarDebugInfo(mth, ssaVar));\n\n\t\tfixLinesForReturn(mth);\n\t\tfixNamesForPhiInsns(mth);\n\t}\n\n\tprivate static void searchAndApplyVarDebugInfo(MethodNode mth, SSAVar ssaVar) {\n\t\tif (applyDebugInfo(mth, ssaVar, ssaVar.getAssign())) {\n\t\t\treturn;\n\t\t}\n\t\tfor (RegisterArg useArg : ssaVar.getUseList()) {\n\t\t\tif (applyDebugInfo(mth, ssaVar, useArg)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tsearchDebugInfoByOffset(mth, ssaVar);\n\t}\n\n\tprivate static void searchDebugInfoByOffset(MethodNode mth, SSAVar ssaVar) {\n\t\tLocalVarsDebugInfoAttr debugInfoAttr = mth.get(AType.LOCAL_VARS_DEBUG_INFO);\n\t\tif (debugInfoAttr == null) {\n\t\t\treturn;\n\t\t}\n\t\tOptionalInt max = ssaVar.getUseList().stream().mapToInt(DebugInfoApplyVisitor::getInsnOffsetByArg).max();\n\t\tif (max.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tint startOffset = getInsnOffsetByArg(ssaVar.getAssign());\n\t\tint endOffset = max.getAsInt();\n\t\tint regNum = ssaVar.getRegNum();\n\t\tfor (ILocalVar localVar : debugInfoAttr.getLocalVars()) {\n\t\t\tif (localVar.getRegNum() == regNum) {\n\t\t\t\tint startAddr = localVar.getStartOffset();\n\t\t\t\tint endAddr = localVar.getEndOffset();\n\t\t\t\tif (isInside(startOffset, startAddr, endAddr) || isInside(endOffset, startAddr, endAddr)) {\n\t\t\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\t\t\tLOG.debug(\"Apply debug info by offset for: {} to {}\", ssaVar, localVar);\n\t\t\t\t\t}\n\t\t\t\t\tArgType type = DebugInfoAttachVisitor.getVarType(mth, localVar);\n\t\t\t\t\tapplyDebugInfo(mth, ssaVar, type, localVar.getName());\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean isInside(int var, int start, int end) {\n\t\treturn start <= var && var <= end;\n\t}\n\n\tprivate static int getInsnOffsetByArg(InsnArg arg) {\n\t\tif (arg != null) {\n\t\t\tInsnNode insn = arg.getParentInsn();\n\t\t\tif (insn != null) {\n\t\t\t\treturn insn.getOffset();\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\tpublic static boolean applyDebugInfo(MethodNode mth, SSAVar ssaVar, RegisterArg arg) {\n\t\tRegDebugInfoAttr debugInfoAttr = arg.get(AType.REG_DEBUG_INFO);\n\t\tif (debugInfoAttr == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn applyDebugInfo(mth, ssaVar, debugInfoAttr.getRegType(), debugInfoAttr.getName());\n\t}\n\n\tpublic static boolean applyDebugInfo(MethodNode mth, SSAVar ssaVar, ArgType type, String varName) {\n\t\tTypeUpdateResult result = mth.root().getTypeUpdate().applyDebugInfo(mth, ssaVar, type);\n\t\tif (result == TypeUpdateResult.REJECT) {\n\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\tLOG.debug(\"Reject debug info of type: {} and name: '{}' for {}, mth: {}\", type, varName, ssaVar, mth);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tif (NameMapper.isValidAndPrintable(varName)) {\n\t\t\tssaVar.setName(varName);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Fix debug info for splitter 'return' instructions\n\t */\n\tprivate static void fixLinesForReturn(MethodNode mth) {\n\t\tif (mth.isVoidReturn()) {\n\t\t\treturn;\n\t\t}\n\t\tInsnNode origReturn = null;\n\t\tList<InsnNode> newReturns = new ArrayList<>(mth.getPreExitBlocks().size());\n\t\tfor (BlockNode exit : mth.getPreExitBlocks()) {\n\t\t\tInsnNode ret = BlockUtils.getLastInsn(exit);\n\t\t\tif (ret != null) {\n\t\t\t\tif (ret.contains(AFlag.ORIG_RETURN)) {\n\t\t\t\t\torigReturn = ret;\n\t\t\t\t} else {\n\t\t\t\t\tnewReturns.add(ret);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (origReturn != null) {\n\t\t\tfor (InsnNode ret : newReturns) {\n\t\t\t\tInsnArg oldArg = origReturn.getArg(0);\n\t\t\t\tInsnArg newArg = ret.getArg(0);\n\t\t\t\tif (oldArg.isRegister() && newArg.isRegister()) {\n\t\t\t\t\tRegisterArg oldArgReg = (RegisterArg) oldArg;\n\t\t\t\t\tRegisterArg newArgReg = (RegisterArg) newArg;\n\t\t\t\t\tapplyDebugInfo(mth, newArgReg.getSVar(), oldArgReg.getType(), oldArgReg.getName());\n\t\t\t\t}\n\t\t\t\tret.setSourceLine(origReturn.getSourceLine());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void fixNamesForPhiInsns(MethodNode mth) {\n\t\tmth.getSVars().forEach(ssaVar -> {\n\t\t\tfor (PhiInsn phiInsn : ssaVar.getUsedInPhi()) {\n\t\t\t\tSet<String> names = new HashSet<>(1 + phiInsn.getArgsCount());\n\t\t\t\taddArgName(phiInsn.getResult(), names);\n\t\t\t\tphiInsn.getArguments().forEach(arg -> addArgName(arg, names));\n\t\t\t\tif (names.size() == 1) {\n\t\t\t\t\tsetNameForInsn(phiInsn, names.iterator().next());\n\t\t\t\t} else if (names.size() > 1) {\n\t\t\t\t\tmth.addDebugComment(\"Different variable names in phi insn: \" + names + \", use first\");\n\t\t\t\t\tsetNameForInsn(phiInsn, names.iterator().next());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate static void addArgName(InsnArg arg, Set<String> names) {\n\t\tif (arg instanceof Named) {\n\t\t\tString name = ((Named) arg).getName();\n\t\t\tif (name != null) {\n\t\t\t\tnames.add(name);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void setNameForInsn(PhiInsn phiInsn, String name) {\n\t\tphiInsn.getResult().setName(name);\n\t\tphiInsn.getArguments().forEach(arg -> {\n\t\t\tif (arg instanceof Named) {\n\t\t\t\t((Named) arg).setName(name);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void processMethodParametersAttribute(MethodNode mth) {\n\t\tMethodParametersAttr parametersAttr = mth.get(JadxAttrType.METHOD_PARAMETERS);\n\t\tif (parametersAttr == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tList<MethodParametersAttr.Info> params = parametersAttr.getList();\n\t\t\tif (params.size() != mth.getMethodInfo().getArgsCount()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tint i = 0;\n\t\t\tfor (RegisterArg mthArg : mth.getArgRegs()) {\n\t\t\t\tMethodParametersAttr.Info paramInfo = params.get(i++);\n\t\t\t\tString name = paramInfo.getName();\n\t\t\t\tif (NameMapper.isValidAndPrintable(name)) {\n\t\t\t\t\tCodeVar codeVar = mthArg.getSVar().getCodeVar();\n\t\t\t\t\tcodeVar.setName(name);\n\t\t\t\t\tif (AccessFlags.hasFlag(paramInfo.getAccFlags(), AccessFlags.FINAL)) {\n\t\t\t\t\t\tcodeVar.setFinal(true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Failed to process method parameters attribute: \" + parametersAttr.getList(), e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/DebugInfoAttachVisitor.java",
    "content": "package jadx.core.dex.visitors.debuginfo;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport jadx.api.plugins.input.data.IDebugInfo;\nimport jadx.api.plugins.input.data.ILocalVar;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.nodes.LocalVarsDebugInfoAttr;\nimport jadx.core.dex.attributes.nodes.RegDebugInfoAttr;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.parser.SignatureParser;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.dex.visitors.blocks.BlockSplitter;\nimport jadx.core.dex.visitors.ssa.SSATransform;\nimport jadx.core.utils.exceptions.InvalidDataException;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"Debug Info Parser\",\n\t\tdesc = \"Attach debug information (variable names and types, instruction lines)\",\n\t\trunBefore = {\n\t\t\t\tBlockSplitter.class,\n\t\t\t\tSSATransform.class\n\t\t}\n)\npublic class DebugInfoAttachVisitor extends AbstractVisitor {\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\ttry {\n\t\t\tIDebugInfo debugInfo = mth.getDebugInfo();\n\t\t\tif (debugInfo != null) {\n\t\t\t\tprocessDebugInfo(mth, debugInfo);\n\t\t\t}\n\t\t} catch (InvalidDataException e) {\n\t\t\tmth.addWarnComment(e.getMessage());\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Failed to parse debug info\", e);\n\t\t}\n\t}\n\n\tprivate void processDebugInfo(MethodNode mth, IDebugInfo debugInfo) {\n\t\tInsnNode[] insnArr = mth.getInstructions();\n\t\tattachSourceLines(mth, debugInfo.getSourceLineMapping(), insnArr);\n\t\tattachDebugInfo(mth, debugInfo.getLocalVars(), insnArr);\n\t\tsetMethodSourceLine(mth, insnArr);\n\t}\n\n\tprivate void attachSourceLines(MethodNode mth, Map<Integer, Integer> lineMapping, InsnNode[] insnArr) {\n\t\tif (lineMapping.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (Map.Entry<Integer, Integer> entry : lineMapping.entrySet()) {\n\t\t\ttry {\n\t\t\t\tInsnNode insn = insnArr[entry.getKey()];\n\t\t\t\tif (insn != null) {\n\t\t\t\t\tinsn.setSourceLine(entry.getValue());\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tmth.addWarnComment(\"Error attach source line\", e);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tString ignoreReason = verifyDebugLines(lineMapping);\n\t\tif (ignoreReason != null) {\n\t\t\tmth.addDebugComment(\"Don't trust debug lines info. \" + ignoreReason);\n\t\t} else {\n\t\t\tmth.add(AFlag.USE_LINES_HINTS);\n\t\t}\n\t}\n\n\tprivate String verifyDebugLines(Map<Integer, Integer> lineMapping) {\n\t\t// search min line in method\n\t\tint minLine = lineMapping.values().stream().mapToInt(v -> v).min().orElse(Integer.MAX_VALUE);\n\t\tif (minLine < 3) {\n\t\t\treturn \"Lines numbers was adjusted: min line is \" + minLine;\n\t\t}\n\n\t\t// count repeating lines\n\t\t// 3 here is allowed maximum for line repeat count\n\t\t// can occur in indexed 'for' loops (3 instructions with the same line)\n\t\tvar repeatingLines = lineMapping.values().stream()\n\t\t\t\t.collect(Collectors.toMap(l -> l, l -> 1, Integer::sum))\n\t\t\t\t.entrySet().stream()\n\t\t\t\t.filter(p -> p.getValue() > 3)\n\t\t\t\t.collect(Collectors.toList());\n\t\tif (!repeatingLines.isEmpty()) {\n\t\t\treturn \"Repeating lines: \" + repeatingLines;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate void attachDebugInfo(MethodNode mth, List<ILocalVar> localVars, InsnNode[] insnArr) {\n\t\tif (localVars.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (ILocalVar var : localVars) {\n\t\t\tint regNum = var.getRegNum();\n\t\t\tint start = var.getStartOffset();\n\t\t\tint end = var.getEndOffset();\n\n\t\t\tArgType type = getVarType(mth, var);\n\t\t\tRegDebugInfoAttr debugInfoAttr = new RegDebugInfoAttr(type, var.getName());\n\t\t\tif (start <= 0) {\n\t\t\t\t// attach to method arguments\n\t\t\t\tRegisterArg thisArg = mth.getThisArg();\n\t\t\t\tif (thisArg != null) {\n\t\t\t\t\tattachDebugInfo(thisArg, debugInfoAttr, regNum);\n\t\t\t\t}\n\t\t\t\tfor (RegisterArg arg : mth.getArgRegs()) {\n\t\t\t\t\tattachDebugInfo(arg, debugInfoAttr, regNum);\n\t\t\t\t}\n\t\t\t\tstart = 0;\n\t\t\t}\n\t\t\tfor (int i = start; i <= end; i++) {\n\t\t\t\tInsnNode insn = insnArr[i];\n\t\t\t\tif (insn == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tint count = 0;\n\t\t\t\tfor (InsnArg arg : insn.getArguments()) {\n\t\t\t\t\tcount += attachDebugInfo(arg, debugInfoAttr, regNum);\n\t\t\t\t}\n\t\t\t\tif (count != 0) {\n\t\t\t\t\t// don't apply same info for result if applied to args\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tattachDebugInfo(insn.getResult(), debugInfoAttr, regNum);\n\t\t\t}\n\t\t}\n\n\t\tmth.addAttr(new LocalVarsDebugInfoAttr(localVars));\n\t}\n\n\tprivate int attachDebugInfo(InsnArg arg, RegDebugInfoAttr debugInfoAttr, int regNum) {\n\t\tif (arg instanceof RegisterArg) {\n\t\t\tRegisterArg reg = (RegisterArg) arg;\n\t\t\tif (regNum == reg.getRegNum()) {\n\t\t\t\treg.addAttr(debugInfoAttr);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\tpublic static ArgType getVarType(MethodNode mth, ILocalVar var) {\n\t\tArgType type = ArgType.parse(var.getType());\n\t\tString sign = var.getSignature();\n\t\tif (sign == null) {\n\t\t\treturn type;\n\t\t}\n\t\ttry {\n\t\t\tArgType gType = new SignatureParser(sign).consumeType();\n\t\t\tArgType expandedType = mth.root().getTypeUtils().expandTypeVariables(mth, gType);\n\t\t\tif (checkSignature(mth, type, expandedType)) {\n\t\t\t\treturn expandedType;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Can't parse signature for local variable: \" + sign, e);\n\t\t}\n\t\treturn type;\n\t}\n\n\tprivate static boolean checkSignature(MethodNode mth, ArgType type, ArgType gType) {\n\t\tboolean apply;\n\t\tArgType el = gType.getArrayRootElement();\n\t\tif (el.isGeneric()) {\n\t\t\tif (!type.getArrayRootElement().getObject().equals(el.getObject())) {\n\t\t\t\tmth.addWarnComment(\"Generic types in debug info not equals: \" + type + \" != \" + gType);\n\t\t\t}\n\t\t\tapply = true;\n\t\t} else {\n\t\t\tapply = el.isGenericType();\n\t\t}\n\t\treturn apply;\n\t}\n\n\t/**\n\t * Set method source line from first instruction\n\t */\n\tprivate void setMethodSourceLine(MethodNode mth, InsnNode[] insnArr) {\n\t\tfor (InsnNode insn : insnArr) {\n\t\t\tif (insn != null) {\n\t\t\t\tint line = insn.getSourceLine();\n\t\t\t\tif (line != 0) {\n\t\t\t\t\tmth.setSourceLine(line - 1);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/CentralityState.java",
    "content": "package jadx.core.dex.visitors.finaly;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Set;\n\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.InsnNode;\n\n/**\n * A centrality state is an object which helps track how instructions can be skipped.\n * When looking for a finally, one of the things we have to do is make sure that instructions\n * are not part of the return and are actually part of the \"finally\" block.\n * This object helps keep track of registers, instructions etc to see if instructions can be\n * skipped.\n */\npublic final class CentralityState {\n\n\tprivate final Set<RegisterArg> allowableOutputArguments = new HashSet<>();\n\tprivate final SameInstructionsStrategy sameInstructionsStrategy;\n\tprivate boolean allowsCentral = true;\n\tprivate boolean allowsNonStartingNode;\n\n\tpublic CentralityState(final SameInstructionsStrategy sameInstructionsStrategy, final boolean allowsNonStartingNode) {\n\t\tthis.sameInstructionsStrategy = sameInstructionsStrategy;\n\t\tthis.allowsNonStartingNode = allowsNonStartingNode;\n\t}\n\n\t@Override\n\tpublic final String toString() {\n\t\tfinal StringBuilder sb = new StringBuilder(\"CentralityState - \");\n\t\tif (allowsCentral) {\n\t\t\tsb.append(\"allows central\");\n\t\t} else {\n\t\t\tsb.append(\"disallows central\");\n\t\t}\n\t\tsb.append(\" | \");\n\t\tfor (RegisterArg registerArg : allowableOutputArguments) {\n\t\t\tsb.append(registerArg.getName());\n\t\t\tsb.append(\" \");\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic final SameInstructionsStrategy getSameInstructionsStrategy() {\n\t\treturn sameInstructionsStrategy;\n\t}\n\n\tpublic final boolean getAllowsCentral() {\n\t\treturn allowsCentral;\n\t}\n\n\tpublic final void setAllowsCentral(final boolean allowsCentral) {\n\t\tthis.allowsCentral = allowsCentral;\n\t}\n\n\tpublic final boolean getAllowsNonStartingNode() {\n\t\treturn allowsNonStartingNode;\n\t}\n\n\tpublic final void setAllowsNonStartingNode(final boolean allowsNonStartingNode) {\n\t\tthis.allowsNonStartingNode = allowsNonStartingNode;\n\t}\n\n\tpublic final void addAllowableOutput(final RegisterArg allowableOutput) {\n\t\tallowableOutputArguments.add(allowableOutput);\n\t}\n\n\tpublic final void addAllowableOutputs(final Collection<RegisterArg> allowableOutputs) {\n\t\tallowableOutputArguments.addAll(allowableOutputs);\n\t}\n\n\t/**\n\t * Adds all inputs register arguments from an instruction as allowable output arguments.\n\t *\n\t * @param allowableOutputInsn The instruction to retrieve the list of inputs from.\n\t */\n\tpublic final void addAllowableOutputs(final InsnNode allowableOutputInsn) {\n\t\tfinal List<RegisterArg> registerArgs = new LinkedList<>();\n\t\tfor (final InsnArg arg : allowableOutputInsn.getArgList()) {\n\t\t\tif (!(arg instanceof RegisterArg)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tregisterArgs.add((RegisterArg) arg);\n\t\t}\n\n\t\tregisterArgs.forEach(this::addAllowableOutput);\n\t}\n\n\tpublic final boolean hasAllowableOutput(final InsnNode insn) {\n\t\tif (allowableOutputArguments.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal RegisterArg registerArg;\n\t\tif (insn.getResult() != null) {\n\t\t\tregisterArg = insn.getResult();\n\t\t} else {\n\t\t\tregisterArg = null;\n\t\t}\n\n\t\tif (registerArg == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (final RegisterArg allowableOutput : allowableOutputArguments) {\n\t\t\tif (allowableOutput.equals(registerArg)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tpublic final boolean hasAllowableInputs(final InsnNode insn) {\n\t\tif (allowableOutputArguments.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal List<RegisterArg> registerArgs = new ArrayList<>();\n\n\t\tfor (final InsnArg arg : insn.getArgList()) {\n\t\t\tif (arg instanceof RegisterArg) {\n\t\t\t\tregisterArgs.add((RegisterArg) arg);\n\t\t\t}\n\t\t}\n\n\t\tif (registerArgs.isEmpty() || allowableOutputArguments.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (final RegisterArg regArg : registerArgs) {\n\t\t\tboolean foundMatch = false;\n\t\t\tfor (final RegisterArg allowableOutput : allowableOutputArguments) {\n\t\t\t\tif (regArg.equals(allowableOutput)) {\n\t\t\t\t\tfoundMatch = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!foundMatch) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic final CentralityState duplicate() {\n\t\tfinal CentralityState state = new CentralityState(sameInstructionsStrategy, allowsNonStartingNode);\n\t\tstate.allowsCentral = allowsCentral;\n\t\tstate.allowableOutputArguments.addAll(allowableOutputArguments);\n\t\treturn state;\n\t}\n\n\tpublic final Set<RegisterArg> getAllowableOutputArguments() {\n\t\treturn allowableOutputArguments;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/FinallyExtractInfo.java",
    "content": "package jadx.core.dex.visitors.finaly;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.utils.Utils;\n\npublic class FinallyExtractInfo {\n\tprivate final MethodNode mth;\n\tprivate final ExceptionHandler finallyHandler;\n\tprivate final List<BlockNode> allHandlerBlocks;\n\tprivate final List<InsnsSlice> duplicateSlices = new ArrayList<>();\n\tprivate final Set<BlockNode> checkedBlocks = new HashSet<>();\n\tprivate final InsnsSlice finallyInsnsSlice = new InsnsSlice();\n\tprivate final BlockNode startBlock;\n\n\tprivate InsnsSlice curDupSlice;\n\tprivate List<InsnNode> curDupInsns;\n\tprivate int curDupInsnsOffset;\n\n\tpublic FinallyExtractInfo(MethodNode mth, ExceptionHandler finallyHandler, BlockNode startBlock, List<BlockNode> allHandlerBlocks) {\n\t\tthis.mth = mth;\n\t\tthis.finallyHandler = finallyHandler;\n\t\tthis.startBlock = startBlock;\n\t\tthis.allHandlerBlocks = allHandlerBlocks;\n\t}\n\n\tpublic MethodNode getMth() {\n\t\treturn mth;\n\t}\n\n\tpublic ExceptionHandler getFinallyHandler() {\n\t\treturn finallyHandler;\n\t}\n\n\tpublic List<BlockNode> getAllHandlerBlocks() {\n\t\treturn allHandlerBlocks;\n\t}\n\n\tpublic InsnsSlice getFinallyInsnsSlice() {\n\t\treturn finallyInsnsSlice;\n\t}\n\n\tpublic List<InsnsSlice> getDuplicateSlices() {\n\t\treturn duplicateSlices;\n\t}\n\n\tpublic Set<BlockNode> getCheckedBlocks() {\n\t\treturn checkedBlocks;\n\t}\n\n\tpublic BlockNode getStartBlock() {\n\t\treturn startBlock;\n\t}\n\n\tpublic InsnsSlice getCurDupSlice() {\n\t\treturn curDupSlice;\n\t}\n\n\tpublic void setCurDupSlice(InsnsSlice curDupSlice) {\n\t\tthis.curDupSlice = curDupSlice;\n\t}\n\n\tpublic List<InsnNode> getCurDupInsns() {\n\t\treturn curDupInsns;\n\t}\n\n\tpublic int getCurDupInsnsOffset() {\n\t\treturn curDupInsnsOffset;\n\t}\n\n\tpublic void setCurDupInsns(List<InsnNode> insns, int offset) {\n\t\tthis.curDupInsns = insns;\n\t\tthis.curDupInsnsOffset = offset;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"FinallyExtractInfo{\"\n\t\t\t\t+ \"\\n finally:\\n  \" + finallyInsnsSlice\n\t\t\t\t+ \"\\n dups:\\n  \" + Utils.listToString(duplicateSlices, \"\\n  \")\n\t\t\t\t+ \"\\n}\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/InsnsSlice.java",
    "content": "package jadx.core.dex.visitors.finaly;\n\nimport java.util.ArrayList;\nimport java.util.IdentityHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic class InsnsSlice {\n\tprivate final List<InsnNode> insnsList = new ArrayList<>();\n\tprivate final Map<InsnNode, BlockNode> insnMap = new IdentityHashMap<>();\n\tprivate boolean complete;\n\n\tpublic void addInsn(InsnNode insn, BlockNode block) {\n\t\tinsnsList.add(insn);\n\t\tinsnMap.put(insn, block);\n\t}\n\n\tpublic void addBlock(BlockNode block) {\n\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\taddInsn(insn, block);\n\t\t}\n\t}\n\n\tpublic void addInsns(BlockNode block, int startIndex, int endIndex) {\n\t\tList<InsnNode> insns = block.getInstructions();\n\t\tfor (int i = startIndex; i < endIndex; i++) {\n\t\t\taddInsn(insns.get(i), block);\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic BlockNode getBlock(InsnNode insn) {\n\t\treturn insnMap.get(insn);\n\t}\n\n\tpublic List<InsnNode> getInsnsList() {\n\t\treturn insnsList;\n\t}\n\n\tpublic Set<BlockNode> getBlocks() {\n\t\tSet<BlockNode> set = new LinkedHashSet<>();\n\t\tfor (InsnNode insn : insnsList) {\n\t\t\tset.add(insnMap.get(insn));\n\t\t}\n\t\treturn set;\n\t}\n\n\tpublic void resetIncomplete() {\n\t\tif (!complete) {\n\t\t\tinsnsList.clear();\n\t\t\tinsnMap.clear();\n\t\t}\n\t}\n\n\tpublic boolean isComplete() {\n\t\treturn complete;\n\t}\n\n\tpublic void setComplete(boolean complete) {\n\t\tthis.complete = complete;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"{[\"\n\t\t\t\t+ insnsList.stream().map(insn -> insn.getType().toString()).collect(Collectors.joining(\", \"))\n\t\t\t\t+ ']'\n\t\t\t\t+ (complete ? \" complete\" : \"\")\n\t\t\t\t+ '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/MarkFinallyVisitor.java",
    "content": "package jadx.core.dex.visitors.finaly;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.dex.trycatch.TryCatchBlockAttr;\nimport jadx.core.dex.trycatch.TryEdge;\nimport jadx.core.dex.trycatch.TryEdgeScopeGroupMap;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.ConstInlineVisitor;\nimport jadx.core.dex.visitors.DepthTraversal;\nimport jadx.core.dex.visitors.IDexTreeVisitor;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.dex.visitors.finaly.traverser.TraverserController;\nimport jadx.core.dex.visitors.finaly.traverser.TraverserException;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserActivePathState;\nimport jadx.core.dex.visitors.ssa.SSATransform;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.Pair;\nimport jadx.core.utils.exceptions.DecodeException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n/**\n * This visitor is responsible for extracting finally blocks from duplicated instructions located\n * within the ends of each branch leading to the terminating point of all code paths.\n *\n * To do this, the terminating point of each handler / exit from try body is found relative to every\n * other handler / exit from try body. This, in effect, is used to identify the \"scopes\" of each\n * possible path within the try block and thus can be used to find a common series of included\n * blocks\n * within the \"scope\" of each handler and a block to start searching from in reverse to find common\n * instructions between that and the \"nominated finally\" handler. These groups are described by the\n * {@link TryEdgeScopeGroupMap} object.\n *\n * After this, the {@link TraverserController} is responsible for traversing the block graphs from\n * each \"scope terminus\" along the blocks contained with each handlers \"scope\", comparing them\n * against\n * the \"nominated finally\" block. If the control flow and instructions of each block match, then\n * they\n * are added as duplicate instructions. At the end, the visitor will mark the identified duplicated\n * instructions and identified finally instructions with the respective {@link AFlag} to be handled\n * during regioning of the block graph.\n */\n@JadxVisitor(\n\t\tname = \"MarkFinallyVisitor\",\n\t\tdesc = \"Search and mark duplicate code generated for finally block\",\n\t\trunAfter = SSATransform.class,\n\t\trunBefore = ConstInlineVisitor.class\n)\npublic class MarkFinallyVisitor extends AbstractVisitor {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(MarkFinallyVisitor.class);\n\n\tprivate static final class TryExtractInfo {\n\n\t\tprivate final TryCatchBlockAttr tryBlock;\n\t\tprivate final TryEdgeScopeGroupMap scopeGroups;\n\t\tprivate final ExceptionHandler finallyHandler;\n\t\tprivate final Map<BlockNode, List<TryEdge>> scopeTerminusGroups;\n\t\tprivate final TryCatchEdgeBlockMap handlerScopes;\n\t\tprivate final Set<BlockNode> allHandlerBlocks;\n\t\tprivate final Set<BlockNode> rethrowBlocks;\n\n\t\tprivate Set<BlockNode> completeFinallyBlocks = null;\n\t\tprivate Set<BlockNode> completeCandidateBlocks = null;\n\n\t\tprivate TryExtractInfo(final TryCatchBlockAttr tryBlock, final TryEdgeScopeGroupMap scopeGroups,\n\t\t\t\tfinal ExceptionHandler finallyHandler, final Map<BlockNode, List<TryEdge>> fallthroughGroups,\n\t\t\t\tfinal TryCatchEdgeBlockMap handlerScopes) {\n\t\t\tthis.tryBlock = tryBlock;\n\t\t\tthis.scopeGroups = scopeGroups;\n\t\t\tthis.finallyHandler = finallyHandler;\n\t\t\tthis.scopeTerminusGroups = fallthroughGroups;\n\t\t\tthis.handlerScopes = handlerScopes;\n\t\t\tthis.allHandlerBlocks = new HashSet<>();\n\t\t\tthis.rethrowBlocks = new HashSet<>();\n\n\t\t\tfor (final List<BlockNode> handlerBlocks : handlerScopes.values()) {\n\t\t\t\tallHandlerBlocks.addAll(handlerBlocks);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void visit(final MethodNode mth) {\n\t\tif (mth.isNoCode() || mth.isNoExceptionHandlers()) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tboolean implicitHandlerRemoved = false;\n\t\t\tfinal List<TryCatchBlockAttr> tryBlocks = mth.getAll(AType.TRY_BLOCKS_LIST);\n\n\t\t\tfinal List<TryCatchBlockAttr> processRequiredTryBlocks = new ArrayList<>();\n\n\t\t\t// Search through all exception handlers and:\n\t\t\t// - Remove implicit handlers\n\t\t\t// - Mark non-implicit handlers to be searched for a finally block\n\t\t\tfor (final TryCatchBlockAttr tryBlock : tryBlocks) {\n\t\t\t\tfinal TryExtractInfo tryInfo = getTryBlockData(mth, tryBlock);\n\t\t\t\tif (tryInfo == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tfinal List<BlockNode> cutHandlerBlocks = cutHandlerBlocks(tryInfo, tryInfo.finallyHandler);\n\t\t\t\tif (cutHandlerBlocks == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (attemptRemoveImplicitHandlers(cutHandlerBlocks, tryInfo)) {\n\t\t\t\t\timplicitHandlerRemoved = true;\n\t\t\t\t} else {\n\t\t\t\t\tprocessRequiredTryBlocks.add(tryBlock);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// If any implicit handlers have been found, remove them\n\t\t\tif (implicitHandlerRemoved) {\n\t\t\t\tresetTryBlocks(mth, tryBlocks);\n\t\t\t}\n\n\t\t\t// Search through all non-implicit handlers and search for a finally block.\n\t\t\tboolean finallyExtracted = false;\n\t\t\tfor (final TryCatchBlockAttr tryBlock : processRequiredTryBlocks) {\n\t\t\t\t// Refresh scope groups now due to implicit handlers\n\t\t\t\tfinal TryExtractInfo tryInfo = getTryBlockData(mth, tryBlock);\n\n\t\t\t\tif (tryInfo == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tcutHandlerBlocks(tryInfo, tryInfo.finallyHandler);\n\n\t\t\t\tfinallyExtracted |= processTryBlock(mth, tryInfo);\n\t\t\t}\n\t\t\t// If any handlers have been merged, remove them\n\t\t\tif (finallyExtracted) {\n\t\t\t\tresetTryBlocks(mth, tryBlocks);\n\t\t\t}\n\t\t} catch (final Exception e) {\n\t\t\tLOG.error(e.getMessage());\n\t\t\tundoFinallyVisitor(mth);\n\t\t\tmth.addWarnComment(\"Undo finally extract visitor\", e);\n\t\t}\n\t}\n\n\tprivate static void resetTryBlocks(final MethodNode mth, final List<TryCatchBlockAttr> tryBlocks) {\n\t\tmth.clearExceptionHandlers();\n\t\t// remove merged or empty try blocks from list in method attribute\n\t\tfinal List<TryCatchBlockAttr> clearedTryBlocks = new ArrayList<>(tryBlocks);\n\t\tif (clearedTryBlocks.removeIf(TryCatchBlockAttr::isImplicitOrMerged)) {\n\t\t\tmth.remove(AType.TRY_BLOCKS_LIST);\n\t\t\tmth.addAttr(AType.TRY_BLOCKS_LIST, clearedTryBlocks);\n\t\t}\n\t}\n\n\t/**\n\t * For a given try block, attempts to calculate try block data. This includes the handler blocks for\n\t * each try branch, data regarding the scope of each try branch relative to every other branch, and\n\t * the blocks logically contained within each try branch. This information is stored via internal\n\t * class members and is not returned by the function.\n\t *\n\t * @param mth      The method containing the try block.\n\t * @param tryBlock The try block to determine the scope information of.\n\t * @return The handler identified as the \"all\" handler.\n\t */\n\t@Nullable\n\tprivate static TryExtractInfo getTryBlockData(final MethodNode mth, final TryCatchBlockAttr tryBlock) {\n\t\tif (tryBlock.isMerged()) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Find the all handler\n\t\tExceptionHandler allHandler = null;\n\t\tfor (final ExceptionHandler excHandler : tryBlock.getHandlers()) {\n\t\t\tif (excHandler.isCatchAll()) {\n\t\t\t\tallHandler = excHandler;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (allHandler == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tfinal TryEdgeScopeGroupMap scopeGroups = tryBlock.getExecutionScopeGroups(mth);\n\t\tfinal var fallthroughGroups = tryBlock.getHandlerFallthroughGroups(mth, scopeGroups);\n\t\tfinal var handlerScopes = TryCatchEdgeBlockMap.getAllInScope(mth, tryBlock, scopeGroups, allHandler, fallthroughGroups);\n\t\treturn new TryExtractInfo(tryBlock, scopeGroups, allHandler, fallthroughGroups, handlerScopes);\n\t}\n\n\t/**\n\t * Processes a try block, attempting to extract a finally by locating common instruction patterns\n\t * between all\n\t * try branches.\n\t *\n\t * @param mth     The method containing the try block.\n\t * @param tryInfo The try block information.\n\t * @return Whether a finally block has been successfully extracted.\n\t */\n\tprivate static boolean processTryBlock(final MethodNode mth, final TryExtractInfo tryInfo) {\n\t\tif (tryInfo.rethrowBlocks.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (extractFinally(mth, tryInfo)) {\n\t\t\tfor (final BlockNode rethrowBlock : tryInfo.rethrowBlocks) {\n\t\t\t\tfinal InsnNode lastInsn = BlockUtils.getLastInsn(rethrowBlock);\n\t\t\t\tif (lastInsn == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlastInsn.add(AFlag.DONT_GENERATE);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Nullable\n\tprivate static List<BlockNode> cutHandlerBlocks(final TryExtractInfo tryInfo, final ExceptionHandler handler) {\n\t\tfinal BlockNode handlerBlock = handler.getHandlerBlock();\n\t\tfinal List<BlockNode> handlerBlocks = tryInfo.handlerScopes.getBlocksForHandler(handler);\n\t\tif (handlerBlocks == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tfinal InsnNode handlerFinalInsn = BlockUtils.getFirstInsn(handlerBlock);\n\t\tif (handlerFinalInsn != null && handlerFinalInsn.getType() == InsnType.MOVE_EXCEPTION) {\n\t\t\thandlerBlocks.remove(handlerBlock); // exclude block with 'move-exception'\n\t\t}\n\n\t\tfinal BlockNode bottomBlock = BlockUtils.getBottomBlock(handlerBlocks);\n\t\tfinal List<BlockNode> pathExits = BlockUtils.followEmptyUpPathWithinSet(bottomBlock, handlerBlocks);\n\t\tif (pathExits.isEmpty()) {\n\t\t\treturn handlerBlocks;\n\t\t}\n\t\tfor (final BlockNode pathExit : pathExits) {\n\t\t\t// For this to be able to extract a finally, we must ensure that all paths into the handler's logic\n\t\t\t// end with a THROW equal to the output of the move-exception instruction located at the start of\n\t\t\t// this handler, if any.\n\t\t\tfinal InsnNode bottomBlockLastInsn = BlockUtils.getLastInsn(pathExit);\n\t\t\tfinal boolean isValidPathExit = bottomBlockLastInsn != null\n\t\t\t\t\t&& handlerFinalInsn != null\n\t\t\t\t\t&& bottomBlockLastInsn.getType() == InsnType.THROW\n\t\t\t\t\t&& bottomBlockLastInsn.getArgsCount() > 0\n\t\t\t\t\t&& bottomBlockLastInsn.getArg(0).equals(handlerFinalInsn.getResult());\n\t\t\tif (!isValidPathExit) {\n\t\t\t\treturn handlerBlocks;\n\t\t\t}\n\t\t}\n\t\tfinal List<BlockNode> cutHandlerBlocks = new ArrayList<>(handlerBlocks);\n\t\tfor (final BlockNode pathExit : pathExits) {\n\t\t\tcutHandlerBlocks.remove(pathExit);\n\t\t\tremoveEmptyUpPath(cutHandlerBlocks, pathExit);\n\t\t\ttryInfo.rethrowBlocks.add(pathExit);\n\t\t}\n\t\treturn cutHandlerBlocks;\n\t}\n\n\t/**\n\t * Attempts to identify and remove an implicit try catch block.\n\t *\n\t * @param cutHandlerBlocks The cut handler blocks of the all handler.\n\t * @return Whether the try block is implicit and has been removed.\n\t */\n\tprivate static boolean attemptRemoveImplicitHandlers(final List<BlockNode> cutHandlerBlocks, final TryExtractInfo tryInfo) {\n\t\tif (!(cutHandlerBlocks.isEmpty() || BlockUtils.isAllBlocksEmpty(cutHandlerBlocks))) {\n\t\t\treturn false;\n\t\t}\n\t\t// remove empty catch\n\t\ttryInfo.finallyHandler.getTryBlock().removeHandler(tryInfo.finallyHandler);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Search and mark common code from 'try' block and 'handlers'.\n\t */\n\tprivate static boolean extractFinally(final MethodNode mth, final TryExtractInfo tryInfo) {\n\t\t// Get all handlers from this and inner try blocks.\n\t\tfinal boolean hasInnerBlocks = !tryInfo.tryBlock.getInnerTryBlocks().isEmpty();\n\t\tfinal List<ExceptionHandler> handlers = getHandlersForTryCatch(tryInfo.tryBlock);\n\t\tif (handlers.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal Map<InsnNode, List<InsnNode>> insns = findCommonInsns(mth, tryInfo);\n\t\tif (insns == null || insns.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal Set<InsnNode> ignoredFinallyInsns = new HashSet<>();\n\t\tfinal Set<InsnNode> ignoredCandidateInsns = new HashSet<>();\n\t\tfinal Map<InsnNode, List<InsnNode>> insnMap = new HashMap<>();\n\t\tfor (final InsnNode finallyInsn : insns.keySet()) {\n\t\t\tfinal List<InsnNode> candidateInsns = insns.get(finallyInsn);\n\n\t\t\t// For an instruction to have matched, the number of times it has been found must be\n\t\t\t// equal to the number of edges that the exception handler has which aren't the\n\t\t\t// finally handler.\n\t\t\tif (candidateInsns.size() != tryInfo.handlerScopes.size() - 1) {\n\t\t\t\tignoredFinallyInsns.add(finallyInsn);\n\t\t\t\tignoredCandidateInsns.addAll(candidateInsns);\n\t\t\t\t// TODO: Add support for partial `catch (Throwable)` finally clauses.\n\t\t\t\t// continue;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tinsnMap.put(finallyInsn, candidateInsns);\n\t\t}\n\n\t\tfor (final InsnNode finallyInsn : insnMap.keySet()) {\n\t\t\tfinallyInsn.add(AFlag.FINALLY_INSNS);\n\t\t\tfinal List<InsnNode> candidateInsns = insnMap.get(finallyInsn);\n\t\t\tfor (final InsnNode candidateInsn : candidateInsns) {\n\t\t\t\tcopyCodeVars(finallyInsn, candidateInsn);\n\t\t\t\tcandidateInsn.add(AFlag.DONT_GENERATE);\n\t\t\t}\n\t\t}\n\n\t\tfor (final BlockNode finallyBlock : tryInfo.completeFinallyBlocks) {\n\t\t\tif (ListUtils.anyMatch(finallyBlock.getInstructions(), ignoredFinallyInsns::contains)) {\n\t\t\t\t// If this block contains an instruction which was not found in all try edges,\n\t\t\t\t// don't mark it as a finally block.\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfinallyBlock.add(AFlag.FINALLY_INSNS);\n\t\t}\n\t\tfor (final BlockNode candidateBlock : tryInfo.completeCandidateBlocks) {\n\t\t\tif (ListUtils.anyMatch(candidateBlock.getInstructions(), ignoredCandidateInsns::contains)) {\n\t\t\t\t// If this block contains an instruction which was found to \"duplicate\" a finally\n\t\t\t\t// instruction which was not found in all try edges, don't mark it as a duplicated\n\t\t\t\t// block.\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcandidateBlock.add(AFlag.DONT_GENERATE);\n\t\t}\n\n\t\t// If any scope has been merged with the fallthrough case of the try catch, don't merge inner trys.\n\t\t// Otherwise, merge inner trys.\n\t\tfinal boolean mergedFallthroughScope =\n\t\t\t\tListUtils.anyMatch(tryInfo.scopeGroups.getMergedScopes(), scopePair -> scopePair.getFirst().isNotHandlerExit());\n\t\tfinal boolean mergeInnerTryBlocks = hasInnerBlocks && !mergedFallthroughScope;\n\n\t\ttryInfo.finallyHandler.setFinally(true);\n\n\t\tif (mergeInnerTryBlocks) {\n\t\t\tfinal List<TryCatchBlockAttr> innerTryBlocks = tryInfo.tryBlock.getInnerTryBlocks();\n\t\t\tfor (final TryCatchBlockAttr innerTryBlock : innerTryBlocks) {\n\t\t\t\ttryInfo.tryBlock.getHandlers().addAll(innerTryBlock.getHandlers());\n\t\t\t\ttryInfo.tryBlock.getBlocks().addAll(innerTryBlock.getBlocks());\n\t\t\t\tinnerTryBlock.setMerged(true);\n\t\t\t}\n\t\t\ttryInfo.tryBlock.setBlocks(ListUtils.distinctList(tryInfo.tryBlock.getBlocks()));\n\t\t\tinnerTryBlocks.clear();\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Gets a list of every exception handler attached to this try block, including handlers of inner\n\t * try blocks.\n\t *\n\t * @param tryBlock The source try block to get the list of exception handlers for\n\t * @return The list of exception handlers.\n\t */\n\tprivate static List<ExceptionHandler> getHandlersForTryCatch(final TryCatchBlockAttr tryBlock) {\n\t\tfinal boolean hasInnerBlocks = !tryBlock.getInnerTryBlocks().isEmpty();\n\t\tfinal List<ExceptionHandler> handlers;\n\t\tif (hasInnerBlocks) {\n\t\t\t// collect handlers from this and all inner blocks\n\t\t\thandlers = new ArrayList<>(tryBlock.getHandlers());\n\t\t\tfor (final TryCatchBlockAttr innerTryBlock : tryBlock.getInnerTryBlocks()) {\n\t\t\t\thandlers.addAll(getHandlersForTryCatch(innerTryBlock));\n\t\t\t}\n\t\t} else {\n\t\t\thandlers = tryBlock.getHandlers();\n\t\t}\n\t\treturn handlers;\n\t}\n\n\t@Nullable\n\tprivate static Map<InsnNode, List<InsnNode>> findCommonInsns(final MethodNode mth, final TryExtractInfo tryInfo) {\n\t\tfinal List<BlockNode> allHandlerBlocks = tryInfo.handlerScopes.getBlocksForHandler(tryInfo.finallyHandler);\n\t\tfinal BlockNode finallyScopeTerminus = getTerminusForHandler(tryInfo.finallyHandler, tryInfo);\n\n\t\tfinal Map<InsnNode, List<InsnNode>> matchingInsns = new HashMap<>();\n\t\tfor (final TryEdge edge : tryInfo.handlerScopes.keySet()) {\n\t\t\tif (edge.isHandlerExit() && edge.getExceptionHandler() == tryInfo.finallyHandler) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfinal List<BlockNode> handlerBlocks = tryInfo.handlerScopes.get(edge);\n\t\t\tBlockNode scopeTerminus = null;\n\t\t\tfor (final BlockNode edgeTerminusBlock : tryInfo.scopeTerminusGroups.keySet()) {\n\t\t\t\tfinal List<TryEdge> edgesWithTerminus = tryInfo.scopeTerminusGroups.get(edgeTerminusBlock);\n\t\t\t\tif (edgesWithTerminus.contains(edge)) {\n\t\t\t\t\tscopeTerminus = edgeTerminusBlock;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (scopeTerminus == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Expected to find fallthrough terminus for handler \" + edge);\n\t\t\t}\n\n\t\t\tfinal TraverserActivePathState comparatorState =\n\t\t\t\t\tnew TraverserActivePathState(mth, new SameInstructionsStrategyImpl(), finallyScopeTerminus,\n\t\t\t\t\t\t\tscopeTerminus, allHandlerBlocks, handlerBlocks);\n\t\t\tfinal TraverserController controller = new TraverserController();\n\t\t\tfinal List<TraverserActivePathState> pathResults;\n\t\t\ttry {\n\t\t\t\tpathResults = controller.process(comparatorState);\n\t\t\t} catch (final TraverserException e) {\n\t\t\t\tLOG.error(\"Could not search for finally duplicate instructions in path\", e);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tfinal Set<BlockNode> completeFinally = new HashSet<>();\n\t\t\tfinal Set<BlockNode> completeCandidate = new HashSet<>();\n\t\t\tfor (final TraverserActivePathState pathResult : pathResults) {\n\t\t\t\tfor (final Pair<InsnNode> matchingInsnPair : pathResult.getMatchedInsns()) {\n\t\t\t\t\tfinal InsnNode finallyInsn = matchingInsnPair.getFirst();\n\t\t\t\t\tfinal InsnNode candidateInsn = matchingInsnPair.getSecond();\n\t\t\t\t\tfinal List<InsnNode> candidateInsnsList;\n\t\t\t\t\tif (!matchingInsns.containsKey(finallyInsn)) {\n\t\t\t\t\t\tcandidateInsnsList = new LinkedList<>();\n\t\t\t\t\t\tmatchingInsns.put(finallyInsn, candidateInsnsList);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcandidateInsnsList = matchingInsns.get(finallyInsn);\n\t\t\t\t\t}\n\t\t\t\t\tcandidateInsnsList.add(candidateInsn);\n\t\t\t\t}\n\n\t\t\t\tcompleteFinally.addAll(pathResult.getAllFullyMatchedFinallyBlocks());\n\t\t\t\tcompleteCandidate.addAll(pathResult.getAllFullyMatchedCandidateBlocks());\n\t\t\t}\n\n\t\t\tif (tryInfo.completeFinallyBlocks == null) {\n\t\t\t\ttryInfo.completeFinallyBlocks = completeFinally;\n\t\t\t} else {\n\t\t\t\ttryInfo.completeFinallyBlocks.retainAll(completeFinally);\n\t\t\t}\n\n\t\t\tif (tryInfo.completeCandidateBlocks == null) {\n\t\t\t\ttryInfo.completeCandidateBlocks = completeCandidate;\n\t\t\t} else {\n\t\t\t\ttryInfo.completeCandidateBlocks.addAll(completeCandidate);\n\t\t\t}\n\t\t}\n\n\t\treturn matchingInsns;\n\t}\n\n\tprivate static void removeEmptyUpPath(final List<BlockNode> handlerBlocks, final BlockNode startBlock) {\n\t\tfor (final BlockNode pred : startBlock.getPredecessors()) {\n\t\t\tif (pred.isEmpty()) {\n\t\t\t\tif (handlerBlocks.remove(pred) && !BlockUtils.isBackEdge(pred, startBlock)) {\n\t\t\t\t\tremoveEmptyUpPath(handlerBlocks, pred);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void copyCodeVars(final InsnNode fromInsn, final InsnNode toInsn) {\n\t\tcopyCodeVars(fromInsn.getResult(), toInsn.getResult());\n\t\tfinal int argsCount = fromInsn.getArgsCount();\n\t\tfor (int i = 0; i < argsCount; i++) {\n\t\t\tcopyCodeVars(fromInsn.getArg(i), toInsn.getArg(i));\n\t\t}\n\t}\n\n\tprivate static void copyCodeVars(final InsnArg fromArg, final InsnArg toArg) {\n\t\tif (fromArg == null || toArg == null\n\t\t\t\t|| !fromArg.isRegister() || !toArg.isRegister()) {\n\t\t\treturn;\n\t\t}\n\t\tfinal SSAVar fromSsaVar = ((RegisterArg) fromArg).getSVar();\n\t\tfinal SSAVar toSsaVar = ((RegisterArg) toArg).getSVar();\n\t\ttoSsaVar.setCodeVar(fromSsaVar.getCodeVar());\n\t}\n\n\t/**\n\t * Reload method without applying this visitor\n\t */\n\tprivate static void undoFinallyVisitor(final MethodNode mth) {\n\t\ttry {\n\t\t\t// TODO: make more common and less hacky\n\t\t\tmth.unload();\n\t\t\tmth.load();\n\t\t\tfor (IDexTreeVisitor visitor : mth.root().getPasses()) {\n\t\t\t\tif (visitor instanceof MarkFinallyVisitor) {\n\t\t\t\t\tbreak;\n\t\t\t\t\t// All visitors after MarkFinally will be invoked as usual after this because\n\t\t\t\t\t// the original decompilation request will proceed.\n\t\t\t\t}\n\t\t\t\tDepthTraversal.visit(visitor, mth);\n\t\t\t}\n\t\t} catch (final DecodeException e) {\n\t\t\tmth.addError(\"Undo finally extract failed\", e);\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate static BlockNode getTerminusForHandler(final ExceptionHandler handler, final TryExtractInfo tryInfo) {\n\t\tfor (final BlockNode terminus : tryInfo.scopeTerminusGroups.keySet()) {\n\t\t\tfinal List<TryEdge> edgesWithTerminus = tryInfo.scopeTerminusGroups.get(terminus);\n\t\t\tfor (final TryEdge edge : edgesWithTerminus) {\n\t\t\t\tif (edge.isNotHandlerExit()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (edge.getExceptionHandler().equals(handler)) {\n\t\t\t\t\treturn terminus;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/SameInstructionsStrategy.java",
    "content": "package jadx.core.dex.visitors.finaly;\n\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic abstract class SameInstructionsStrategy {\n\n\tpublic abstract boolean sameInsns(InsnNode dupInsn, InsnNode fInsn);\n\n\tpublic abstract boolean isSameArgs(InsnArg dupArg, InsnArg fArg);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/SameInstructionsStrategyImpl.java",
    "content": "package jadx.core.dex.visitors.finaly;\n\nimport java.util.Objects;\n\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.RegDebugInfoAttr;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic final class SameInstructionsStrategyImpl extends SameInstructionsStrategy {\n\n\tprivate static boolean sameDebugInfo(final RegisterArg dupReg, final RegisterArg fReg) {\n\t\tfinal RegDebugInfoAttr fDbgInfo = fReg.get(AType.REG_DEBUG_INFO);\n\t\tfinal RegDebugInfoAttr dupDbgInfo = dupReg.get(AType.REG_DEBUG_INFO);\n\t\tif (fDbgInfo == null || dupDbgInfo == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn dupDbgInfo.equals(fDbgInfo);\n\t}\n\n\tprivate static boolean assignInsnDifferent(final RegisterArg dupReg, final RegisterArg fReg) {\n\t\tfinal InsnNode assignInsn = fReg.getAssignInsn();\n\t\tfinal InsnNode dupAssign = dupReg.getAssignInsn();\n\t\tif (assignInsn == null || dupAssign == null) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!assignInsn.isSame(dupAssign)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (assignInsn.isConstInsn() && dupAssign.isConstInsn()) {\n\t\t\t// Do this and not deep equals since we already know that the result is the same and that the insn\n\t\t\t// type is the same\n\t\t\treturn !(Objects.equals(assignInsn.getArguments(), assignInsn.getArguments()));\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic final boolean sameInsns(final InsnNode dupInsn, final InsnNode fInsn) {\n\t\tif (!dupInsn.isSame(fInsn)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (int i = 0; i < dupInsn.getArgsCount(); i++) {\n\t\t\tfinal InsnArg dupArg = dupInsn.getArg(i);\n\t\t\tfinal InsnArg fArg = fInsn.getArg(i);\n\t\t\tif (!isSameArgs(dupArg, fArg)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic final boolean isSameArgs(final InsnArg dupArg, final InsnArg fArg) {\n\t\tif (dupArg == null) {\n\t\t\treturn false;\n\t\t}\n\t\tfinal boolean isReg = dupArg.isRegister();\n\t\tif (isReg != fArg.isRegister()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (isReg) {\n\t\t\tfinal RegisterArg dupReg = (RegisterArg) dupArg;\n\t\t\tfinal RegisterArg fReg = (RegisterArg) fArg;\n\n\t\t\tif (!dupReg.sameCodeVar(fReg)\n\t\t\t\t\t&& !sameDebugInfo(dupReg, fReg)\n\t\t\t\t\t&& assignInsnDifferent(dupReg, fReg)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tfinal boolean remConst = dupArg.isConst();\n\t\tif (remConst != fArg.isConst()) {\n\t\t\treturn false;\n\t\t}\n\t\treturn !(remConst && !dupArg.isSameConst(fArg));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/TryCatchEdgeBlockMap.java",
    "content": "package jadx.core.dex.visitors.finaly;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.dex.trycatch.TryCatchBlockAttr;\nimport jadx.core.dex.trycatch.TryEdge;\nimport jadx.core.dex.trycatch.TryEdgeScopeGroupMap;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.ListUtils;\n\n/**\n * A map containing all edges within a try catch block as the key and all blocks that the\n * respective edge can traverse to within the \"scope\" of that edge (relative to the\n * entire try / catch).\n */\npublic final class TryCatchEdgeBlockMap implements Map<TryEdge, List<BlockNode>> {\n\n\tpublic static boolean anyBlockHasNonImplicitTry(final List<BlockNode> blocks) {\n\t\tfinal List<BlockNode> blocksWithTries = ListUtils.filter(blocks, blk -> blk.contains(AFlag.EXC_TOP_SPLITTER));\n\t\tif (blocksWithTries.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (final BlockNode topSplitter : blocksWithTries) {\n\t\t\tTryCatchBlockAttr block = null;\n\t\t\tfor (final BlockNode topSplitterSuccessor : topSplitter.getCleanSuccessors()) {\n\t\t\t\tif (topSplitterSuccessor.contains(AType.TRY_BLOCK)) {\n\t\t\t\t\tblock = topSplitterSuccessor.get(AType.TRY_BLOCK);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (block == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!TryCatchBlockAttr.isImplicitOrMerged(block)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static TryCatchEdgeBlockMap getAllInScope(final MethodNode mth, final TryCatchBlockAttr tryCatch,\n\t\t\tfinal TryEdgeScopeGroupMap scopeGroups, final ExceptionHandler finallyHandler,\n\t\t\tfinal Map<BlockNode, List<TryEdge>> scopeTerminusGroups) {\n\t\tfinal Map<TryEdge, BlockNode> edgeBlocks = tryCatch.getEdgeBlockMap(mth);\n\n\t\tfinal TryCatchEdgeBlockMap result = new TryCatchEdgeBlockMap();\n\t\tfor (final BlockNode scopeTerminus : scopeTerminusGroups.keySet()) {\n\t\t\tfinal List<TryEdge> sourceEdges = scopeTerminusGroups.get(scopeTerminus);\n\t\t\tfor (final TryEdge sourceEdge : sourceEdges) {\n\t\t\t\tfinal BlockNode edgeBlock = edgeBlocks.get(sourceEdge);\n\n\t\t\t\tfinal boolean useClean = !(sourceEdge.isNotHandlerExit()\n\t\t\t\t\t\t&& ListUtils.anyMatch(scopeGroups.getMergedScopes(), pair -> pair.getSecond().isNotHandlerExit()));\n\t\t\t\tList<BlockNode> allBlocks =\n\t\t\t\t\t\tBlockUtils.collectAllSuccessorsUntil(mth, edgeBlock, useClean, (block) -> block == scopeTerminus);\n\t\t\t\tfinal boolean anyBlockHasTry = anyBlockHasNonImplicitTry(allBlocks);\n\n\t\t\t\tif (anyBlockHasTry && useClean) {\n\t\t\t\t\t// If there's a try edge in the found blocks, collect all successors, not just clean successors.\n\t\t\t\t\tallBlocks = BlockUtils.collectAllSuccessorsUntil(mth, edgeBlock, false, (block) -> block == scopeTerminus);\n\t\t\t\t}\n\t\t\t\tif (sourceEdge.isNotHandlerExit()) {\n\t\t\t\t\t// If source edge is a fallthrough case, add the try body.\n\t\t\t\t\tallBlocks = new ArrayList<>(allBlocks);\n\t\t\t\t\tallBlocks.addAll(tryCatch.getBlocks());\n\t\t\t\t}\n\n\t\t\t\tresult.put(sourceEdge, allBlocks);\n\t\t\t}\n\t\t}\n\n\t\tfinal List<BlockNode> finallyBlocks = result.getBlocksForHandler(finallyHandler);\n\t\tfor (final TryEdge edge : result.keySet()) {\n\t\t\tif (edge.isHandlerExit() && edge.getExceptionHandler() == finallyHandler) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfinal List<BlockNode> blocks = result.get(edge);\n\t\t\tblocks.removeAll(finallyBlocks);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate final Map<TryEdge, List<BlockNode>> underlying;\n\n\tpublic TryCatchEdgeBlockMap() {\n\t\tunderlying = new HashMap<>();\n\t}\n\n\t@Override\n\tpublic final void clear() {\n\t\tunderlying.clear();\n\t}\n\n\t@Override\n\tpublic final boolean containsKey(Object key) {\n\t\treturn underlying.containsKey(key);\n\t}\n\n\t@Override\n\tpublic final boolean containsValue(Object value) {\n\t\tif (!(value instanceof TryEdge)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal TryEdge edge = (TryEdge) value;\n\t\treturn underlying.containsKey(edge);\n\t}\n\n\t@Override\n\tpublic final Set<Entry<TryEdge, List<BlockNode>>> entrySet() {\n\t\treturn underlying.entrySet();\n\t}\n\n\t@Override\n\tpublic final List<BlockNode> get(Object key) {\n\t\treturn underlying.get(key);\n\t}\n\n\t@Override\n\tpublic final boolean isEmpty() {\n\t\treturn underlying.isEmpty();\n\t}\n\n\t@Override\n\tpublic final Set<TryEdge> keySet() {\n\t\treturn underlying.keySet();\n\t}\n\n\t@Override\n\tpublic final List<BlockNode> put(TryEdge key, List<BlockNode> value) {\n\t\treturn underlying.put(key, value);\n\t}\n\n\t@Override\n\tpublic final void putAll(Map<? extends TryEdge, ? extends List<BlockNode>> otherMap) {\n\t\tunderlying.putAll(otherMap);\n\t}\n\n\t@Override\n\tpublic final List<BlockNode> remove(Object key) {\n\t\treturn underlying.remove(key);\n\t}\n\n\t@Override\n\tpublic final int size() {\n\t\treturn underlying.size();\n\t}\n\n\t@Override\n\tpublic final Collection<List<BlockNode>> values() {\n\t\treturn underlying.values();\n\t}\n\n\t@Nullable\n\tpublic final List<BlockNode> getBlocksForHandler(final ExceptionHandler handler) {\n\t\tTryEdge edgeWithHandler = null;\n\t\tfor (final TryEdge edge : keySet()) {\n\t\t\tif (edge.isNotHandlerExit()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!edge.getExceptionHandler().equals(handler)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tedgeWithHandler = edge;\n\t\t\tbreak;\n\t\t}\n\t\tif (edgeWithHandler == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn get(edgeWithHandler);\n\t}\n\n\tpublic final List<BlockNode> getBlocksForAllFallthroughs() {\n\t\tfinal List<BlockNode> blks = new ArrayList<>();\n\t\tfor (final TryEdge edge : keySet()) {\n\t\t\tif (edge.isHandlerExit()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tblks.addAll(get(edge));\n\t\t}\n\t\treturn blks;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/GlobalTraverserSourceState.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser;\n\nimport java.util.Set;\n\nimport jadx.core.dex.nodes.BlockNode;\n\n/**\n * A state used by the traverser controller for storing information regarding an entire path to\n * take during traversal. This should be static amongst all states following the same path.\n */\npublic final class GlobalTraverserSourceState {\n\n\tprivate final Set<BlockNode> containedBlocks;\n\n\tpublic GlobalTraverserSourceState(final Set<BlockNode> containedBlocks) {\n\t\tthis.containedBlocks = containedBlocks;\n\t}\n\n\tpublic final boolean isBlockContained(final BlockNode block) {\n\t\treturn containedBlocks.contains(block);\n\t}\n\n\tpublic final Set<BlockNode> getContainedBlocks() {\n\t\treturn containedBlocks;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/TraverserController.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.function.Function;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.visitors.finaly.traverser.factory.TraverserStateFactory;\nimport jadx.core.dex.visitors.finaly.traverser.handlers.AbstractActivePathTraverserHandler;\nimport jadx.core.dex.visitors.finaly.traverser.handlers.AbstractBlockPathTraverserHandler;\nimport jadx.core.dex.visitors.finaly.traverser.handlers.AbstractBlockTraverserHandler;\nimport jadx.core.dex.visitors.finaly.traverser.state.RecoveredFromCacheTraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TerminalTraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserActivePathState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserBlockInfo;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserGlobalCommonState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserState;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n/**\n * Responsible for determining if two distinct subgraphs are the same within a graph by comparing\n * all blocks and their instructions.\n * This is used for identifying duplicated instructions for extracting finally blocks.\n *\n * The terms \"finally\" and \"candidate\" are used to represent the two distinct subgraphs explored\n * by this controller; the \"finally\" subgraph, which is the subgraph which is what is being used\n * as a finally block, and the \"candidate\" subgraph, which is the subgraph which is being\n * compared to the \"finally\" subgraph to see if they are the same. There is only ever one\n * \"finally\" subgraph, however it is run against multiple different \"candidate\" subgraphs depending\n * on the complexity of the try catch block that this is being run for.\n */\npublic final class TraverserController {\n\n\tprivate static List<TraverserActivePathState> processHandlerImplementations(final TraverserActivePathState state,\n\t\t\tfinal AbstractBlockTraverserHandler handler) throws TraverserException {\n\t\tif (handler instanceof AbstractBlockPathTraverserHandler) {\n\t\t\t((AbstractBlockPathTraverserHandler) handler).process();\n\t\t\treturn List.of(state);\n\t\t} else if (handler instanceof AbstractActivePathTraverserHandler) {\n\t\t\treturn ((AbstractActivePathTraverserHandler) handler).process();\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(\n\t\t\t\t\t\"A sealed class, \" + AbstractBlockPathTraverserHandler.class.getSimpleName() + \", has an unknown implementation\");\n\t\t}\n\t}\n\n\tprivate final @Nullable Function<TraverserState, Boolean> stateAbortCondition;\n\n\tpublic TraverserController() {\n\t\tthis(null);\n\t}\n\n\tpublic TraverserController(final @Nullable Function<TraverserState, Boolean> stateAbortCondition) {\n\t\tthis.stateAbortCondition = stateAbortCondition;\n\t}\n\n\t/**\n\t * Processes a traverser path state using from a {@link TraverserActivePathState}. This\n\t * function will continue evaluating an active path until either:\n\t * <ul>\n\t * <li>The state abort condition is met by both \"finally\" and \"candidate\" path, if there is\n\t * one.</li>\n\t * <li>The path state of either the \"finally\" or \"candidate\" path has terminated.</li>\n\t * <li>The path has began a comparison of two blocks which have already been compared.</li>\n\t * <li>The \"finally\" and \"candidate\" states, on two different executions of\n\t * {@link TraverserController#advance}, did not change.\n\t * </ul>\n\t * This function will return a list of all of the different paths taken at the point of\n\t * termination of each individual branch.\n\t *\n\t * @param state\n\t * @return\n\t */\n\tpublic final List<TraverserActivePathState> process(final TraverserActivePathState state) throws TraverserException {\n\t\tTraverserActivePathState nextState = state;\n\t\tfinal AtomicReference<TraverserState> previousFinallyState = new AtomicReference<>(null);\n\t\tfinal AtomicReference<TraverserState> previousCandidateState = new AtomicReference<>(null);\n\t\twhile (true) {\n\t\t\tfinal List<TraverserActivePathState> advancedStates = advance(nextState, previousFinallyState, previousCandidateState);\n\t\t\tif (advancedStates == null || advancedStates.isEmpty()) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (advancedStates.size() != 1) {\n\t\t\t\tfinal TraverserController nextController = new TraverserController(stateAbortCondition);\n\t\t\t\tfinal List<TraverserActivePathState> returnStates = new ArrayList<>();\n\t\t\t\tfor (final TraverserActivePathState advancedState : advancedStates) {\n\t\t\t\t\tfinal List<TraverserActivePathState> childStates = nextController.process(advancedState);\n\t\t\t\t\treturnStates.addAll(childStates);\n\t\t\t\t}\n\t\t\t\treturn returnStates;\n\t\t\t}\n\n\t\t\tnextState = advancedStates.get(0);\n\t\t}\n\t\treturn List.of(nextState);\n\t}\n\n\t/**\n\t * Processes a singular traverser state once.\n\t *\n\t * @param state\n\t * @param previousFinallyState\n\t * @param previousCandidateState\n\t * @return\n\t */\n\tpublic final List<TraverserActivePathState> advance(final TraverserActivePathState state,\n\t\t\tfinal AtomicReference<TraverserState> previousFinallyState,\n\t\t\tfinal AtomicReference<TraverserState> previousCandidateState) throws TraverserException {\n\t\tfinal TraverserGlobalCommonState commonState = state.getGlobalCommonState();\n\t\tfinal TraverserState finallyState = state.getFinallyState();\n\t\tfinal TraverserState candidateState = state.getCandidateState();\n\n\t\tif (previousFinallyState.get() == finallyState && previousCandidateState.get() == candidateState) {\n\t\t\tfinal TraverserStateFactory<TerminalTraverserState> finallyStateProducer =\n\t\t\t\t\tTerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.UNRESOLVABLE_STATES);\n\t\t\tfinal TraverserStateFactory<TerminalTraverserState> candidateStateProducer =\n\t\t\t\t\tTerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.UNRESOLVABLE_STATES);\n\t\t\treturn List.of(TraverserActivePathState.produceFromFactories(state, finallyStateProducer, candidateStateProducer));\n\t\t}\n\n\t\tpreviousFinallyState.set(finallyState);\n\t\tpreviousCandidateState.set(candidateState);\n\n\t\tif (finallyState.isTerminal() || candidateState.isTerminal()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (finallyState.getClass().equals(candidateState.getClass())\n\t\t\t\t&& finallyState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE\n\t\t\t\t&& candidateState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE) {\n\n\t\t\tfinal BlockNode finallyBlock;\n\t\t\tfinal BlockNode candidateBlock;\n\t\t\tfinal TraverserBlockInfo finallyBlockInfo = finallyState.getBlockInsnInfo();\n\t\t\tfinal TraverserBlockInfo candidateBlockInfo = candidateState.getBlockInsnInfo();\n\t\t\tif (finallyBlockInfo != null && candidateBlockInfo != null) {\n\t\t\t\tfinallyBlock = finallyBlockInfo.getBlock();\n\t\t\t\tcandidateBlock = candidateBlockInfo.getBlock();\n\t\t\t} else {\n\t\t\t\tfinallyBlock = null;\n\t\t\t\tcandidateBlock = null;\n\t\t\t}\n\n\t\t\tfinal boolean isCached;\n\t\t\tif (finallyBlock != null && candidateBlock != null) {\n\t\t\t\tisCached = commonState.hasBlocksBeenCached(finallyBlock, candidateBlock);\n\t\t\t} else {\n\t\t\t\tisCached = false;\n\t\t\t}\n\n\t\t\tif (isCached) {\n\t\t\t\tfinal List<TraverserActivePathState> dupStates = commonState.getCachedStateFor(finallyBlock, candidateBlock);\n\t\t\t\tfinal List<TraverserActivePathState> recoveredFromCacheStates = new ArrayList<>(dupStates.size());\n\t\t\t\tfor (final TraverserActivePathState dupState : dupStates) {\n\t\t\t\t\tfinal TraverserState reusedFinallyState = dupState.getFinallyState();\n\t\t\t\t\tfinal TraverserState reusedCandidateState = dupState.getCandidateState();\n\t\t\t\t\tfinal TraverserStateFactory<?> finallyStateProducer = RecoveredFromCacheTraverserState.getFactory(reusedFinallyState);\n\t\t\t\t\tfinal TraverserStateFactory<?> candidateStateProducer =\n\t\t\t\t\t\t\tRecoveredFromCacheTraverserState.getFactory(reusedCandidateState);\n\t\t\t\t\tfinal TraverserActivePathState recoveredFromCacheState =\n\t\t\t\t\t\t\tTraverserActivePathState.produceFromFactories(state, finallyStateProducer, candidateStateProducer);\n\t\t\t\t\trecoveredFromCacheState.mergeWith(dupStates);\n\t\t\t\t\trecoveredFromCacheStates.add(recoveredFromCacheState);\n\t\t\t\t}\n\t\t\t\treturn recoveredFromCacheStates;\n\t\t\t}\n\n\t\t\tfinal AbstractBlockTraverserHandler handler = candidateState.getNextHandler();\n\t\t\tfinal List<TraverserActivePathState> resultingStates = processHandlerImplementations(state, handler);\n\t\t\treturn resultingStates;\n\t\t}\n\n\t\tfinal boolean hasReadyToCompare = finallyState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE\n\t\t\t\t|| candidateState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE;\n\n\t\tfinal boolean finallyStateAborted = advanceSingleState(state, finallyState, hasReadyToCompare);\n\t\tfinal boolean candidateStateAborted = advanceSingleState(state, candidateState, hasReadyToCompare);\n\n\t\tif (finallyStateAborted && candidateStateAborted) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn List.of(state);\n\t}\n\n\t/**\n\t * Advances a singular state once.\n\t *\n\t * @return Whether this state has been aborted by the state abort function.\n\t */\n\tprivate boolean advanceSingleState(final TraverserActivePathState activePathState, final TraverserState singleState,\n\t\t\tfinal boolean hasReadyToCompare) throws TraverserException {\n\t\tfinal boolean stateAborted = stateAbortCondition != null && stateAbortCondition.apply(singleState);\n\t\tif (stateAbortCondition == null || !stateAborted) {\n\t\t\tif (singleState.getCompareState() == TraverserState.ComparisonState.NOT_READY\n\t\t\t\t\t|| (singleState.getCompareState() == TraverserState.ComparisonState.AWAITING_OPTIONAL_PREDECESSOR_MERGE\n\t\t\t\t\t\t\t&& hasReadyToCompare)) {\n\t\t\t\tfinal AbstractBlockTraverserHandler handler = singleState.getNextHandler();\n\t\t\t\tfinal List<TraverserActivePathState> results = processHandlerImplementations(activePathState, handler);\n\t\t\t\tif (results.size() != 1 || results.get(0) != activePathState) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"A traverser handler which was not expected to change path states actually did\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn stateAborted;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/TraverserException.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser;\n\npublic class TraverserException extends Exception {\n\n\tpublic TraverserException(final String msg) {\n\t\tsuper(msg);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/factory/DuplicatedTraverserStateFactory.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.factory;\n\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserActivePathState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserState;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic final class DuplicatedTraverserStateFactory<T extends TraverserState> extends TraverserStateFactory<T> {\n\n\tprivate final T baseState;\n\n\tpublic DuplicatedTraverserStateFactory(final T baseState) {\n\t\tthis.baseState = baseState;\n\t}\n\n\t@Override\n\tpublic final T generateInternalState(final TraverserActivePathState state) {\n\t\tfinal Class<? extends T> baseStateClass = (Class<? extends T>) baseState.getClass();\n\t\tfinal TraverserState duplicated = baseState.duplicate(state);\n\t\tif (!baseStateClass.isInstance(duplicated)) {\n\t\t\tthrow new JadxRuntimeException(\n\t\t\t\t\t\"A state of class \" + baseState.getClass() + \" has duplicated to produce a class of \" + duplicated.getClass());\n\t\t}\n\t\treturn baseStateClass.cast(duplicated);\n\t}\n\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/factory/TraverserStateFactory.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.factory;\n\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserActivePathState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserState;\n\npublic abstract class TraverserStateFactory<T extends TraverserState> {\n\n\tprotected abstract T generateInternalState(final TraverserActivePathState state);\n\n\tpublic final T generateState(final TraverserActivePathState state) {\n\t\tfinal T generatedState = generateInternalState(state);\n\t\treturn generatedState;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/handlers/AbstractActivePathTraverserHandler.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.handlers;\n\nimport java.util.List;\n\nimport jadx.core.dex.visitors.finaly.traverser.TraverserException;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserActivePathState;\n\npublic abstract class AbstractActivePathTraverserHandler extends AbstractBlockTraverserHandler {\n\n\tprivate final TraverserActivePathState comparatorState;\n\n\tpublic AbstractActivePathTraverserHandler(final TraverserActivePathState comparatorState) {\n\t\tthis.comparatorState = comparatorState;\n\t}\n\n\tprotected abstract List<TraverserActivePathState> handle() throws TraverserException;\n\n\tpublic final List<TraverserActivePathState> process() throws TraverserException {\n\t\treturn handle();\n\t}\n\n\tpublic final TraverserActivePathState getComparator() {\n\t\treturn comparatorState;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/handlers/AbstractBlockPathTraverserHandler.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.handlers;\n\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport jadx.core.dex.visitors.finaly.traverser.TraverserException;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserState;\n\n/**\n * Traverser handlers are responsible for deducing how blocks should be searched within a path\n * whilst\n * searching for duplicate 'finally' instructions.\n */\npublic abstract class AbstractBlockPathTraverserHandler extends AbstractBlockTraverserHandler {\n\n\tprivate final AtomicReference<? extends TraverserState> stateRef;\n\n\tpublic AbstractBlockPathTraverserHandler(final TraverserState initialState) {\n\t\tthis.stateRef = new AtomicReference<>(initialState);\n\t}\n\n\tpublic AbstractBlockPathTraverserHandler(final AtomicReference<? extends TraverserState> initialStateRef) {\n\t\tthis.stateRef = initialStateRef;\n\t}\n\n\tprotected abstract void handle() throws TraverserException;\n\n\tpublic final void process() throws TraverserException {\n\t\thandle();\n\t}\n\n\tpublic final TraverserState getState() {\n\t\treturn stateRef.get();\n\t}\n\n\tpublic final AtomicReference<? extends TraverserState> getStateReference() {\n\t\treturn stateRef;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/handlers/AbstractBlockTraverserHandler.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.handlers;\n\n/**\n * Sealed class\n */\npublic abstract class AbstractBlockTraverserHandler {\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/handlers/BaseBlockTraverserHandler.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.handlers;\n\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserActivePathState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserBlockInfo;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.visitors.ImplicitInsnBlockTraverserVisitor;\nimport jadx.core.dex.visitors.finaly.traverser.visitors.PathEndBlockTraverserVisitor;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class BaseBlockTraverserHandler extends AbstractBlockPathTraverserHandler {\n\n\tpublic BaseBlockTraverserHandler(final TraverserState initialState) {\n\t\tsuper(initialState);\n\t}\n\n\tpublic BaseBlockTraverserHandler(final AtomicReference<TraverserState> initialStateRef) {\n\t\tsuper(initialStateRef);\n\t}\n\n\t@Override\n\tprotected void handle() {\n\t\tfinal TraverserBlockInfo blockInsnInfo = getState().getBlockInsnInfo();\n\t\tif (blockInsnInfo == null) {\n\t\t\tthrow new JadxRuntimeException(\"Expected to find block info within \" + getClass().getSimpleName());\n\t\t}\n\n\t\tfinal TraverserActivePathState comparator = getState().getComparatorState();\n\t\tfinal AtomicReference<TraverserState> stateRef = comparator.getReferenceForState(getState());\n\n\t\tif (stateRef == null) {\n\t\t\tthrow new JadxRuntimeException(\"Orphaned traverser state\");\n\t\t}\n\n\t\tfinal BlockNode block = blockInsnInfo.getBlock();\n\n\t\tfinal ImplicitInsnBlockTraverserVisitor implicitVisitor = new ImplicitInsnBlockTraverserVisitor(getState());\n\t\tfinal TraverserState stateAfterImplicit = implicitVisitor.visit(block);\n\t\tfinal PathEndBlockTraverserVisitor pathEndVisitor = new PathEndBlockTraverserVisitor(stateAfterImplicit);\n\t\tfinal TraverserState nextState = pathEndVisitor.visit(block);\n\n\t\tstateRef.set(nextState);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/handlers/InstructionActivePathTraverserHandler.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.handlers;\n\nimport java.util.List;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.visitors.finaly.traverser.TraverserException;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserActivePathState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserBlockInfo;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserGlobalCommonState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.visitors.comparator.InstructionBlockComparatorTraverserVisitor;\n\npublic final class InstructionActivePathTraverserHandler extends AbstractActivePathTraverserHandler {\n\n\tpublic static final class UnresolvableBlockException extends TraverserException {\n\t\tpublic UnresolvableBlockException(final BlockNode block, final String reason) {\n\t\t\tsuper(\"A block, \" + block.toString() + \", could not have instructions compared.\\n\\t\" + reason);\n\t\t}\n\t}\n\n\tpublic InstructionActivePathTraverserHandler(final TraverserActivePathState state) {\n\t\tsuper(state);\n\t}\n\n\t@Override\n\tprotected List<TraverserActivePathState> handle() throws TraverserException {\n\t\tfinal TraverserActivePathState comparator = getComparator();\n\t\tfinal TraverserGlobalCommonState commonState = comparator.getGlobalCommonState();\n\n\t\tfinal TraverserState finallyState = comparator.getFinallyState();\n\t\tfinal TraverserState candidateState = comparator.getCandidateState();\n\n\t\tfinal TraverserBlockInfo finallyBlockInfo = finallyState.getBlockInsnInfo();\n\t\tfinal TraverserBlockInfo candidateBlockInfo = candidateState.getBlockInsnInfo();\n\t\tfinal BlockNode finallyBlock = finallyBlockInfo.getBlock();\n\t\tfinal BlockNode candidateBlock = candidateBlockInfo.getBlock();\n\n\t\tfinal InstructionBlockComparatorTraverserVisitor visitor = new InstructionBlockComparatorTraverserVisitor();\n\t\tfinal TraverserActivePathState newState = visitor.visit(comparator);\n\n\t\tif (finallyBlock != null && candidateBlock != null) {\n\t\t\tcommonState.addCachedStateFor(finallyBlock, candidateBlock, List.of(newState));\n\t\t}\n\n\t\treturn List.of(newState);\n\t}\n\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/handlers/MergePathActivePathTraverserHandler.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.handlers;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.Stack;\nimport java.util.function.Function;\n\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.visitors.finaly.CentralityState;\nimport jadx.core.dex.visitors.finaly.traverser.GlobalTraverserSourceState;\nimport jadx.core.dex.visitors.finaly.traverser.TraverserController;\nimport jadx.core.dex.visitors.finaly.traverser.TraverserException;\nimport jadx.core.dex.visitors.finaly.traverser.factory.TraverserStateFactory;\nimport jadx.core.dex.visitors.finaly.traverser.state.IdentifiedScopeWithTerminatorTraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.NewBlockTraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.RecoveredFromCacheTraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TerminalTraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserActivePathState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserBlockInfo;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserGlobalCommonState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserState;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic final class MergePathActivePathTraverserHandler extends AbstractActivePathTraverserHandler {\n\n\tprivate static TraverserActivePathState createNonMatchingTerminator(final TraverserActivePathState state) {\n\t\tfinal TraverserStateFactory<TerminalTraverserState> finallyStateFactory =\n\t\t\t\tTerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.NON_MATCHING_PATHS);\n\t\tfinal TraverserStateFactory<TerminalTraverserState> candidateStateFactory =\n\t\t\t\tTerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.NON_MATCHING_PATHS);\n\n\t\treturn TraverserActivePathState.produceFromFactories(state, finallyStateFactory, candidateStateFactory);\n\t}\n\n\tprivate static boolean isStateOnTerminus(final TraverserState state, final BlockNode terminus) {\n\t\tfinal TraverserBlockInfo blockInfo = state.getBlockInsnInfo();\n\t\tif (blockInfo == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn blockInfo.getBlock() == terminus;\n\t}\n\n\tprivate static Function<TraverserState, Boolean> getStateAbortOnTerminusFunction(\n\t\t\tfinal IdentifiedScopeWithTerminatorTraverserState finallyState,\n\t\t\tfinal IdentifiedScopeWithTerminatorTraverserState candidateState) {\n\t\tfinal BlockNode finallyTerminus = finallyState.getTerminus();\n\t\tfinal BlockNode candidateTerminus = candidateState.getTerminus();\n\t\tfinal GlobalTraverserSourceState finallyGlobalState = finallyState.getGlobalState();\n\t\tfinal GlobalTraverserSourceState candidateGlobalState = candidateState.getGlobalState();\n\n\t\treturn (final TraverserState state) -> {\n\t\t\tif (state.getGlobalState() == finallyGlobalState) {\n\t\t\t\treturn isStateOnTerminus(state, finallyTerminus);\n\t\t\t} else if (state.getGlobalState() == candidateGlobalState) {\n\t\t\t\treturn isStateOnTerminus(state, candidateTerminus);\n\t\t\t} else {\n\t\t\t\tthrow new JadxRuntimeException(\"Unknown global traverser state. Has a global state been duplicated?\");\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate static PostMergeStatus getScopeSplitPostMergeStatus(final List<TraverserActivePathState> pathsTaken) {\n\t\t// If the scope split is the same, all branches must not end in a terminator.\n\n\t\tfinal PostMergeStatus status = new PostMergeStatus();\n\t\tfor (final TraverserActivePathState path : pathsTaken) {\n\t\t\tfinal TraverserState finallyState;\n\t\t\tfinal TraverserState candidateState;\n\t\t\tif (path.getFinallyState().isTerminal() || path.getCandidateState().isTerminal()) {\n\t\t\t\tfinal TraverserState rawFinallyState = path.getFinallyState();\n\t\t\t\tfinal TraverserState rawCandidateState = path.getCandidateState();\n\t\t\t\tfinal boolean finallyIsCached = rawFinallyState instanceof RecoveredFromCacheTraverserState;\n\t\t\t\tfinal boolean candidateIsCached = rawCandidateState instanceof RecoveredFromCacheTraverserState;\n\t\t\t\tif (!(finallyIsCached && candidateIsCached)) {\n\t\t\t\t\tstatus.perfectMatch = false;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfinal RecoveredFromCacheTraverserState finallyCachedState = (RecoveredFromCacheTraverserState) rawFinallyState;\n\t\t\t\tfinal RecoveredFromCacheTraverserState candidateCachedState = (RecoveredFromCacheTraverserState) rawCandidateState;\n\t\t\t\tif (finallyCachedState.canContinue() || candidateCachedState.canContinue()) {\n\t\t\t\t\tstatus.perfectMatch = false;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tfinallyState = finallyCachedState.getUnderlying();\n\t\t\t\tcandidateState = candidateCachedState.getUnderlying();\n\t\t\t} else {\n\t\t\t\tfinallyState = path.getFinallyState();\n\t\t\t\tcandidateState = path.getCandidateState();\n\t\t\t}\n\n\t\t\tfinal CentralityState finallyCentralityState = finallyState.getCentralityState();\n\t\t\tfinal CentralityState candidateCentralityState = candidateState.getCentralityState();\n\t\t\tstatus.finallyAllowsCentral &= finallyCentralityState.getAllowsCentral();\n\t\t\tstatus.candidateAllowsCentral &= candidateCentralityState.getAllowsCentral();\n\t\t\tstatus.finallyAllowableOutputs.addAll(finallyCentralityState.getAllowableOutputArguments());\n\t\t\tstatus.candidateAllowableOutputs.addAll(candidateCentralityState.getAllowableOutputArguments());\n\t\t}\n\n\t\treturn status;\n\t}\n\n\tprivate static final class PostMergeStatus {\n\n\t\tpublic final Set<RegisterArg> finallyAllowableOutputs = new HashSet<>();\n\t\tpublic final Set<RegisterArg> candidateAllowableOutputs = new HashSet<>();\n\t\tpublic boolean finallyAllowsCentral;\n\t\tpublic boolean candidateAllowsCentral;\n\t\tpublic boolean perfectMatch = true;\n\t}\n\n\tpublic MergePathActivePathTraverserHandler(final TraverserActivePathState comparatorState) {\n\t\tsuper(comparatorState);\n\t}\n\n\t@Override\n\tprotected final List<TraverserActivePathState> handle() {\n\t\tfinal TraverserActivePathState comparator = getComparator().duplicate();\n\t\tfinal TraverserGlobalCommonState commonState = comparator.getGlobalCommonState();\n\t\tfinal IdentifiedScopeWithTerminatorTraverserState finallyState =\n\t\t\t\t(IdentifiedScopeWithTerminatorTraverserState) comparator.getFinallyState();\n\t\tfinal IdentifiedScopeWithTerminatorTraverserState candidateState =\n\t\t\t\t(IdentifiedScopeWithTerminatorTraverserState) comparator.getCandidateState();\n\n\t\tfinal BlockNode finallyTerminus = finallyState.getTerminus();\n\t\tfinal BlockNode candidateTerminus = candidateState.getTerminus();\n\n\t\tfinal Function<TraverserState, Boolean> abortFunction = getStateAbortOnTerminusFunction(finallyState, candidateState);\n\n\t\tfinal List<BlockNode[]> allPermutationsPaths = getAllPermutationsOfCollection(candidateState.getRoots());\n\t\tList<TraverserActivePathState> paths = null;\n\t\tPostMergeStatus postMerge = null;\n\t\tfor (final BlockNode[] candidateRootsPermutation : allPermutationsPaths) {\n\t\t\tfinal List<TraverserActivePathState> traversalPaths = new ArrayList<>();\n\t\t\tfor (int i = 0; i < finallyState.getRoots().size(); i++) {\n\t\t\t\tfinal var finallyRoot = finallyState.getRoots().get(i);\n\t\t\t\tfinal var candidateRoot = candidateRootsPermutation[i];\n\n\t\t\t\tfinal var finallyCentrality = finallyState.getCentralityState().duplicate();\n\t\t\t\tfinal var candidateCentrality = candidateState.getCentralityState().duplicate();\n\n\t\t\t\tfinal var finallyBlockInfo = new TraverserBlockInfo(finallyRoot);\n\t\t\t\tfinal var candidateBlockInfo = new TraverserBlockInfo(candidateRoot);\n\n\t\t\t\tfinal var finallyStateFactory = NewBlockTraverserState.getFactory(finallyCentrality, finallyBlockInfo);\n\t\t\t\tfinal var candidateStateFactory = NewBlockTraverserState.getFactory(candidateCentrality, candidateBlockInfo);\n\n\t\t\t\tfinal var newState = TraverserActivePathState.produceFromFactories(comparator, finallyStateFactory, candidateStateFactory);\n\t\t\t\ttraversalPaths.add(newState);\n\t\t\t}\n\n\t\t\tfinal List<TraverserActivePathState> currentPaths = new ArrayList<>();\n\t\t\tboolean errorOccurred = false;\n\t\t\tfor (final TraverserActivePathState pathState : traversalPaths) {\n\t\t\t\tfinal TraverserController branchController = new TraverserController(abortFunction);\n\t\t\t\tfinal List<TraverserActivePathState> out;\n\t\t\t\ttry {\n\t\t\t\t\tout = branchController.process(pathState);\n\t\t\t\t} catch (final TraverserException e) {\n\t\t\t\t\terrorOccurred = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcurrentPaths.addAll(out);\n\t\t\t}\n\n\t\t\tif (errorOccurred) {\n\t\t\t\t// If an error occurred, this path was not successful.\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// If the finally terminus and candidate terminus have been cached at this stage, it means that a\n\t\t\t// path that we searched evaluated the two termini. At this point, we can ignore a non-perfect\n\t\t\t// match if the path could continue from the point of the termini.\n\t\t\tfinal boolean hasTerminusBeenEvaluatedInPaths = commonState.hasBlocksBeenCached(finallyTerminus, candidateTerminus);\n\t\t\tfinal PostMergeStatus currentPostMerge = getScopeSplitPostMergeStatus(currentPaths);\n\t\t\tif (!currentPostMerge.perfectMatch && !hasTerminusBeenEvaluatedInPaths) {\n\t\t\t\t// No match\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tpaths = currentPaths;\n\t\t\tpostMerge = currentPostMerge;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (paths == null || postMerge == null) {\n\t\t\tfinal TraverserActivePathState nonMatchingState = createNonMatchingTerminator(comparator);\n\t\t\treturn List.of(nonMatchingState);\n\t\t}\n\n\t\tfinal CentralityState newFinallyCentralityState = finallyState.getCentralityState().duplicate();\n\t\tnewFinallyCentralityState.setAllowsCentral(postMerge.finallyAllowsCentral);\n\t\tnewFinallyCentralityState.addAllowableOutputs(postMerge.finallyAllowableOutputs);\n\t\tfinal CentralityState newCandidateCentralityState = candidateState.getCentralityState().duplicate();\n\t\tnewCandidateCentralityState.setAllowsCentral(postMerge.candidateAllowsCentral);\n\t\tnewCandidateCentralityState.addAllowableOutputs(postMerge.candidateAllowableOutputs);\n\n\t\tfinal TraverserBlockInfo finallyTerminusBlockInfo = new TraverserBlockInfo(finallyState.getTerminus());\n\t\tfinal TraverserBlockInfo candidateTerminusBlockInfo = new TraverserBlockInfo(candidateState.getTerminus());\n\n\t\tfinal TraverserStateFactory<NewBlockTraverserState> finallyStateFactory =\n\t\t\t\tNewBlockTraverserState.getFactory(newFinallyCentralityState, finallyTerminusBlockInfo);\n\t\tfinal TraverserStateFactory<NewBlockTraverserState> candidateStateFactory =\n\t\t\t\tNewBlockTraverserState.getFactory(newCandidateCentralityState, candidateTerminusBlockInfo);\n\n\t\tfinal TraverserActivePathState nextState =\n\t\t\t\tTraverserActivePathState.produceFromFactories(comparator, finallyStateFactory, candidateStateFactory);\n\t\tnextState.mergeWith(paths);\n\t\treturn List.of(nextState);\n\t}\n\n\tpublic static List<BlockNode[]> getAllPermutationsOfCollection(final Collection<BlockNode> elements) {\n\t\tfinal Stack<BlockNode> permutationStack = new Stack<>();\n\t\tfinal List<BlockNode[]> permutations = new ArrayList<>();\n\t\tpermutations(permutations, elements, permutationStack, elements.size());\n\t\treturn permutations;\n\t}\n\n\tpublic static void permutations(final List<BlockNode[]> permutations, final Collection<BlockNode> elements,\n\t\t\tfinal Stack<BlockNode> permutationStack, final int size) {\n\t\tif (permutationStack.size() == size) {\n\t\t\tpermutations.add(permutationStack.toArray(BlockNode[]::new));\n\t\t}\n\n\t\tBlockNode[] availableItems = elements.toArray(BlockNode[]::new);\n\t\tfor (BlockNode i : availableItems) {\n\t\t\tpermutationStack.push(i);\n\t\t\telements.remove(i);\n\t\t\tpermutations(permutations, elements, permutationStack, size);\n\t\t\telements.add(permutationStack.pop());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/handlers/PredecessorBlockPathTraverserHandler.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.handlers;\n\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.visitors.finaly.traverser.state.ISourceBlockState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserActivePathState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.visitors.AbstractBlockTraverserVisitor;\nimport jadx.core.dex.visitors.finaly.traverser.visitors.PredecessorBlockTraverserVisitor;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic final class PredecessorBlockPathTraverserHandler<T extends TraverserState & ISourceBlockState>\n\t\textends AbstractBlockPathTraverserHandler {\n\n\tprivate final ISourceBlockState sourceBlockState;\n\n\tpublic PredecessorBlockPathTraverserHandler(final T initialState) {\n\t\tsuper(initialState);\n\n\t\tthis.sourceBlockState = initialState;\n\t}\n\n\tpublic PredecessorBlockPathTraverserHandler(final AtomicReference<T> initialStateRef) {\n\t\tsuper(initialStateRef);\n\n\t\tthis.sourceBlockState = initialStateRef.get();\n\t}\n\n\t@Override\n\tprotected final void handle() {\n\t\tfinal TraverserState baseState = getState();\n\t\tfinal TraverserActivePathState comparator = baseState.getComparatorState();\n\t\tfinal AtomicReference<TraverserState> stateRef = comparator.getReferenceForState(baseState);\n\n\t\tif (stateRef == null) {\n\t\t\tthrow new JadxRuntimeException(\"Orphaned traverser state\");\n\t\t}\n\n\t\tfinal BlockNode sourceBlock = sourceBlockState.getSourceBlock();\n\t\tfinal AbstractBlockTraverserVisitor visitor = new PredecessorBlockTraverserVisitor(baseState);\n\t\tfinal TraverserState nextState = visitor.visit(sourceBlock);\n\n\t\tstateRef.set(nextState);\n\t}\n\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/handlers/PredecessorMergeActivePathTraverserHandler.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.handlers;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.finaly.CentralityState;\nimport jadx.core.dex.visitors.finaly.traverser.GlobalTraverserSourceState;\nimport jadx.core.dex.visitors.finaly.traverser.TraverserException;\nimport jadx.core.dex.visitors.finaly.traverser.factory.DuplicatedTraverserStateFactory;\nimport jadx.core.dex.visitors.finaly.traverser.factory.TraverserStateFactory;\nimport jadx.core.dex.visitors.finaly.traverser.state.IdentifiedScopeWithTerminatorTraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.NewBlockTraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TerminalTraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserActivePathState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserBlockInfo;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.UnknownAdvanceStrategyTraverserState;\nimport jadx.core.utils.BlockUtils;\n\npublic final class PredecessorMergeActivePathTraverserHandler extends AbstractActivePathTraverserHandler {\n\n\tprivate static List<BlockNode> orderBlocks(final List<BlockNode> blocks) {\n\t\tfinal List<BlockNode> dup = new ArrayList<>(blocks);\n\n\t\t// Collections.sort(dup, (blk1, blk2) -> Integer.compare(blk1.getCId(), blk2.getCId()));\n\n\t\treturn dup;\n\t}\n\n\tpublic PredecessorMergeActivePathTraverserHandler(TraverserActivePathState initialState) {\n\t\tsuper(initialState);\n\t}\n\n\t@Override\n\tprotected final List<TraverserActivePathState> handle() throws TraverserException {\n\t\t// At this point, we expect the handler to contain the block state of the path which is\n\t\t// requesting a predecessor merge. If the other handler also requests a predecessor merge,\n\t\t// we can merge the two. If not, we'll split the active handler to support the multiple\n\t\t// paths.\n\n\t\tfinal TraverserActivePathState comparator = getComparator();\n\t\tfinal TraverserState finallyState = comparator.getFinallyState();\n\t\tfinal TraverserState candidateState = comparator.getCandidateState();\n\n\t\tfinal boolean finallyNeedsDuplicate = finallyState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE;\n\t\tfinal boolean candidateNeedsDuplicate = candidateState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE;\n\t\tfinal boolean shouldMerge = finallyNeedsDuplicate && candidateNeedsDuplicate;\n\n\t\tif (shouldMerge) {\n\t\t\treturn mergeScopes((UnknownAdvanceStrategyTraverserState) finallyState, (UnknownAdvanceStrategyTraverserState) candidateState);\n\t\t} else {\n\t\t\tfinal UnknownAdvanceStrategyTraverserState advancingState;\n\t\t\tfinal TraverserState otherState;\n\t\t\tif (finallyNeedsDuplicate) {\n\t\t\t\tadvancingState = (UnknownAdvanceStrategyTraverserState) finallyState;\n\t\t\t\totherState = candidateState;\n\t\t\t} else {\n\t\t\t\tadvancingState = (UnknownAdvanceStrategyTraverserState) candidateState;\n\t\t\t\totherState = finallyState;\n\t\t\t}\n\t\t\treturn duplicateForPaths(comparator, advancingState, otherState, finallyNeedsDuplicate);\n\t\t}\n\t}\n\n\tprivate List<TraverserActivePathState> mergeScopes(final UnknownAdvanceStrategyTraverserState finallyState,\n\t\t\tfinal UnknownAdvanceStrategyTraverserState candidateState) throws TraverserException {\n\t\tfinal List<BlockNode> finallyBlocks = finallyState.getNextBlocks();\n\t\tfinal List<BlockNode> candidateBlocks = candidateState.getNextBlocks();\n\n\t\tfinal int finallyBlocksSize = finallyBlocks.size();\n\t\tfinal int candidateBlocksSize = candidateBlocks.size();\n\n\t\tfinal List<TraverserActivePathState> states;\n\t\tif (candidateBlocksSize % finallyBlocksSize == 0 && candidateBlocksSize == finallyBlocksSize) {\n\t\t\tfinal List<BlockNode> finallyBlocksOrdered = orderBlocks(finallyBlocks);\n\t\t\tfinal List<BlockNode> candidateBlocksOrdered = orderBlocks(candidateBlocks);\n\n\t\t\tfinal int duplicationCount = candidateBlocksSize / finallyBlocksSize;\n\n\t\t\tstates = new ArrayList<>(duplicationCount);\n\t\t\tfor (int i = 0; i < duplicationCount; i++) {\n\t\t\t\tfinal List<BlockNode> candidateBlocksSubset = new ArrayList<>(finallyBlocksSize);\n\t\t\t\tfor (int j = 0; j < finallyBlocksSize; j++) {\n\t\t\t\t\tcandidateBlocksSubset.add(candidateBlocksOrdered.get(i * finallyBlocksSize + j));\n\t\t\t\t}\n\n\t\t\t\tfinal TraverserActivePathState comparatorState = getScopeForBlocks(finallyBlocksOrdered, candidateBlocksSubset);\n\t\t\t\tstates.add(comparatorState);\n\t\t\t}\n\t\t} else {\n\t\t\tfinal TraverserStateFactory<TerminalTraverserState> finallyStateFactory =\n\t\t\t\t\tTerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.UNMERGEABLE_STATE);\n\t\t\tfinal TraverserStateFactory<TerminalTraverserState> candidateStateFactory =\n\t\t\t\t\tTerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.UNMERGEABLE_STATE);\n\t\t\tfinal TraverserActivePathState newState =\n\t\t\t\t\tTraverserActivePathState.produceFromFactories(getComparator(), finallyStateFactory, candidateStateFactory);\n\t\t\tstates = List.of(newState);\n\t\t}\n\t\treturn states;\n\t}\n\n\tprivate List<TraverserActivePathState> duplicateForPaths(final TraverserActivePathState comparator,\n\t\t\tfinal UnknownAdvanceStrategyTraverserState advancingState, final TraverserState otherState,\n\t\t\tfinal boolean duplicateIsFromFinally) {\n\t\tfinal List<BlockNode> nextPredecessors = advancingState.getNextBlocks();\n\t\tfinal List<TraverserActivePathState> newPaths = new ArrayList<>(nextPredecessors.size());\n\t\tfor (final BlockNode predecessor : nextPredecessors) {\n\t\t\tfinal CentralityState centralityState = advancingState.getCentralityState();\n\t\t\tfinal TraverserBlockInfo duplicatePathBlockInfo = new TraverserBlockInfo(predecessor);\n\t\t\tfinal TraverserStateFactory<NewBlockTraverserState> duplicatePathStateFactory =\n\t\t\t\t\tNewBlockTraverserState.getFactory(centralityState, duplicatePathBlockInfo);\n\t\t\tfinal TraverserStateFactory<?> otherStateFactory = new DuplicatedTraverserStateFactory<>(otherState);\n\n\t\t\tfinal TraverserActivePathState comparatorDuplicated = comparator.duplicate();\n\t\t\tfinal TraverserActivePathState newPathState;\n\t\t\tif (duplicateIsFromFinally) {\n\t\t\t\tnewPathState =\n\t\t\t\t\t\tTraverserActivePathState.produceFromFactories(comparatorDuplicated, duplicatePathStateFactory, otherStateFactory);\n\t\t\t} else {\n\t\t\t\tnewPathState =\n\t\t\t\t\t\tTraverserActivePathState.produceFromFactories(comparatorDuplicated, otherStateFactory, duplicatePathStateFactory);\n\t\t\t}\n\t\t\tnewPaths.add(newPathState);\n\t\t}\n\n\t\treturn newPaths;\n\t}\n\n\tprivate TraverserActivePathState getScopeForBlocks(final List<BlockNode> finallyBlocks, final List<BlockNode> candidateBlocks) {\n\t\tfinal TraverserActivePathState comparator = getComparator();\n\t\tfinal MethodNode mth = getComparator().getGlobalCommonState().getMethodNode();\n\n\t\tfinal TraverserState finallyState = comparator.getFinallyState();\n\t\tfinal TraverserState candidateState = comparator.getCandidateState();\n\n\t\tfinal GlobalTraverserSourceState finallyGlobalState = comparator.getGlobalStateFor(finallyState);\n\t\tfinal CentralityState finallyCentralityState = finallyState.getCentralityState();\n\t\tfinal BlockNode finallyTerminator =\n\t\t\t\tBlockUtils.getBottomCommonPredecessor(mth, finallyBlocks, finallyGlobalState.getContainedBlocks());\n\t\tfinal TraverserStateFactory<IdentifiedScopeWithTerminatorTraverserState> finallyStateFactory =\n\t\t\t\tIdentifiedScopeWithTerminatorTraverserState.getFactory(finallyCentralityState, finallyBlocks, finallyTerminator);\n\n\t\tfinal GlobalTraverserSourceState candidateGlobalState = comparator.getGlobalStateFor(candidateState);\n\t\tfinal CentralityState candidateCentralityState = candidateState.getCentralityState();\n\t\tfinal BlockNode candidateTerminator =\n\t\t\t\tBlockUtils.getBottomCommonPredecessor(mth, candidateBlocks, candidateGlobalState.getContainedBlocks());\n\t\tfinal TraverserStateFactory<IdentifiedScopeWithTerminatorTraverserState> candidateStateFactory =\n\t\t\t\tIdentifiedScopeWithTerminatorTraverserState.getFactory(candidateCentralityState, candidateBlocks, candidateTerminator);\n\n\t\treturn TraverserActivePathState.produceFromFactories(comparator, finallyStateFactory, candidateStateFactory);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/state/AwaitingInsnCompareTraverserState.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.state;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.visitors.finaly.CentralityState;\nimport jadx.core.dex.visitors.finaly.traverser.handlers.AbstractBlockTraverserHandler;\nimport jadx.core.dex.visitors.finaly.traverser.handlers.InstructionActivePathTraverserHandler;\n\npublic final class AwaitingInsnCompareTraverserState extends TraverserState {\n\n\tprivate final CentralityState centralityState;\n\tprivate final @Nullable TraverserBlockInfo blockInsnInfo;\n\n\tpublic AwaitingInsnCompareTraverserState(final TraverserActivePathState state, final CentralityState centralityState,\n\t\t\tfinal TraverserBlockInfo blockInsnInfo) {\n\t\tsuper(state);\n\n\t\tthis.centralityState = centralityState;\n\t\tthis.blockInsnInfo = blockInsnInfo;\n\t}\n\n\t@Override\n\tpublic final @Nullable AbstractBlockTraverserHandler getNextHandler() {\n\t\treturn new InstructionActivePathTraverserHandler(getComparatorState());\n\t}\n\n\t@Override\n\tpublic final ComparisonState getCompareState() {\n\t\treturn ComparisonState.READY_TO_COMPARE;\n\t}\n\n\t@Override\n\tpublic final boolean isTerminal() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tprotected final @Nullable CentralityState getUnderlyingCentralityState() {\n\t\treturn centralityState;\n\t}\n\n\t@Override\n\tprotected final @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() {\n\t\treturn blockInsnInfo;\n\t}\n\n\t@Override\n\tprotected final TraverserState duplicateInternalState(final TraverserActivePathState comparatorState) {\n\t\tfinal CentralityState dCentralityState = centralityState.duplicate();\n\t\tfinal TraverserBlockInfo dBlockInsnInfo = blockInsnInfo.duplicate();\n\n\t\tfinal TraverserState duplicated = new AwaitingInsnCompareTraverserState(comparatorState, dCentralityState, dBlockInsnInfo);\n\n\t\treturn duplicated;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/state/ISourceBlockState.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.state;\n\nimport jadx.core.dex.nodes.BlockNode;\n\npublic interface ISourceBlockState {\n\n\tpublic abstract BlockNode getSourceBlock();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/state/IdentifiedScopeWithTerminatorTraverserState.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.state;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.visitors.finaly.CentralityState;\nimport jadx.core.dex.visitors.finaly.traverser.factory.TraverserStateFactory;\nimport jadx.core.dex.visitors.finaly.traverser.handlers.AbstractBlockTraverserHandler;\nimport jadx.core.dex.visitors.finaly.traverser.handlers.MergePathActivePathTraverserHandler;\n\npublic final class IdentifiedScopeWithTerminatorTraverserState extends TraverserState {\n\n\tpublic static TraverserStateFactory<IdentifiedScopeWithTerminatorTraverserState> getFactory(final CentralityState centralityState,\n\t\t\tfinal List<BlockNode> roots, final BlockNode scopeTerminator) {\n\t\treturn new IdentifiedScopeWithTerminatorStateFactory(centralityState, roots, scopeTerminator);\n\t}\n\n\tprivate static final class IdentifiedScopeWithTerminatorStateFactory\n\t\t\textends TraverserStateFactory<IdentifiedScopeWithTerminatorTraverserState> {\n\n\t\tprivate final CentralityState centralityState;\n\t\tprivate final List<BlockNode> roots;\n\t\tprivate final BlockNode scopeTerminator;\n\n\t\tpublic IdentifiedScopeWithTerminatorStateFactory(final CentralityState centralityState, final List<BlockNode> roots,\n\t\t\t\tfinal BlockNode scopeTerminator) {\n\t\t\tthis.centralityState = centralityState;\n\t\t\tthis.roots = roots;\n\t\t\tthis.scopeTerminator = scopeTerminator;\n\t\t}\n\n\t\t@Override\n\t\tpublic final IdentifiedScopeWithTerminatorTraverserState generateInternalState(final TraverserActivePathState state) {\n\t\t\treturn new IdentifiedScopeWithTerminatorTraverserState(state, centralityState, roots, scopeTerminator);\n\t\t}\n\t}\n\n\tprivate final CentralityState centralityState;\n\tprivate final List<BlockNode> roots;\n\tprivate final BlockNode scopeTerminator;\n\n\tpublic IdentifiedScopeWithTerminatorTraverserState(final TraverserActivePathState state, final CentralityState centralityState,\n\t\t\tfinal List<BlockNode> roots, final BlockNode scopeTerminator) {\n\t\tsuper(state);\n\t\tthis.roots = roots;\n\t\tthis.scopeTerminator = scopeTerminator;\n\t\tthis.centralityState = centralityState;\n\t}\n\n\t@Override\n\tpublic final @Nullable AbstractBlockTraverserHandler getNextHandler() {\n\t\treturn new MergePathActivePathTraverserHandler(getComparatorState());\n\t}\n\n\t@Override\n\tpublic final ComparisonState getCompareState() {\n\t\treturn ComparisonState.READY_TO_COMPARE;\n\t}\n\n\t@Override\n\tpublic final boolean isTerminal() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tprotected final @Nullable CentralityState getUnderlyingCentralityState() {\n\t\treturn centralityState;\n\t}\n\n\t@Override\n\tprotected final @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tprotected final TraverserState duplicateInternalState(final TraverserActivePathState comparatorState) {\n\t\treturn new IdentifiedScopeWithTerminatorTraverserState(comparatorState, centralityState, roots, scopeTerminator);\n\t}\n\n\tpublic final BlockNode getTerminus() {\n\t\treturn scopeTerminator;\n\t}\n\n\tpublic final List<BlockNode> getRoots() {\n\t\treturn roots;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/state/NewBlockTraverserState.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.state;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.visitors.finaly.CentralityState;\nimport jadx.core.dex.visitors.finaly.traverser.factory.TraverserStateFactory;\nimport jadx.core.dex.visitors.finaly.traverser.handlers.AbstractBlockPathTraverserHandler;\nimport jadx.core.dex.visitors.finaly.traverser.handlers.BaseBlockTraverserHandler;\n\npublic final class NewBlockTraverserState extends TraverserState {\n\n\tpublic static final TraverserStateFactory<NewBlockTraverserState> getFactory(final CentralityState centralityState,\n\t\t\tfinal TraverserBlockInfo blockInsnInfo) {\n\t\treturn new NewBlockStateFactory(centralityState, blockInsnInfo);\n\t}\n\n\tprivate static class NewBlockStateFactory extends TraverserStateFactory<NewBlockTraverserState> {\n\n\t\tprivate final CentralityState centralityState;\n\t\tprivate final TraverserBlockInfo blockInsnInfo;\n\n\t\tpublic NewBlockStateFactory(final CentralityState centralityState, final TraverserBlockInfo blockInsnInfo) {\n\t\t\tthis.centralityState = centralityState;\n\t\t\tthis.blockInsnInfo = blockInsnInfo;\n\t\t}\n\n\t\t@Override\n\t\tpublic NewBlockTraverserState generateInternalState(final TraverserActivePathState state) {\n\t\t\treturn new NewBlockTraverserState(state, centralityState, blockInsnInfo);\n\t\t}\n\t}\n\n\tprivate final CentralityState centralityState;\n\tprivate final @Nullable TraverserBlockInfo blockInsnInfo;\n\n\tpublic NewBlockTraverserState(final TraverserActivePathState state, final CentralityState centralityState,\n\t\t\tfinal TraverserBlockInfo blockInsnInfo) {\n\t\tsuper(state);\n\t\tthis.centralityState = centralityState;\n\t\tthis.blockInsnInfo = blockInsnInfo;\n\t}\n\n\t@Override\n\tpublic final ComparisonState getCompareState() {\n\t\treturn ComparisonState.NOT_READY;\n\t}\n\n\t@Override\n\tpublic final boolean isTerminal() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic @Nullable AbstractBlockPathTraverserHandler getNextHandler() {\n\t\t// We have no data on this block, so we'll give it the base block handler to gain\n\t\t// information about it to gather more state information regarding the block.\n\t\treturn new BaseBlockTraverserHandler(this);\n\t}\n\n\t@Override\n\tprotected @Nullable CentralityState getUnderlyingCentralityState() {\n\t\treturn centralityState;\n\t}\n\n\t@Override\n\tprotected @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() {\n\t\treturn blockInsnInfo;\n\t}\n\n\t@Override\n\tprotected final TraverserState duplicateInternalState(final TraverserActivePathState comparatorState) {\n\t\tfinal CentralityState dCentralityState = centralityState.duplicate();\n\t\tfinal TraverserBlockInfo dBlockInsnInfo = blockInsnInfo.duplicate();\n\n\t\tfinal TraverserState duplicated = new NewBlockTraverserState(comparatorState, dCentralityState, dBlockInsnInfo);\n\n\t\treturn duplicated;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/state/NoBlockTraverserState.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.state;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.visitors.finaly.CentralityState;\nimport jadx.core.dex.visitors.finaly.traverser.factory.TraverserStateFactory;\nimport jadx.core.dex.visitors.finaly.traverser.handlers.AbstractBlockPathTraverserHandler;\nimport jadx.core.dex.visitors.finaly.traverser.handlers.PredecessorBlockPathTraverserHandler;\n\npublic final class NoBlockTraverserState extends TraverserState implements ISourceBlockState {\n\n\tpublic static TraverserStateFactory<NoBlockTraverserState> getFactory(final CentralityState centralityState,\n\t\t\tfinal BlockNode sourceBlock) {\n\t\treturn new NoBlockStateFactory(centralityState, sourceBlock);\n\t}\n\n\tprivate static class NoBlockStateFactory extends TraverserStateFactory<NoBlockTraverserState> {\n\n\t\tprivate final CentralityState centralityState;\n\t\tprivate final BlockNode sourceBlock;\n\n\t\tpublic NoBlockStateFactory(final CentralityState centralityState, final BlockNode sourceBlock) {\n\t\t\tthis.centralityState = centralityState;\n\t\t\tthis.sourceBlock = sourceBlock;\n\t\t}\n\n\t\t@Override\n\t\tpublic NoBlockTraverserState generateInternalState(final TraverserActivePathState state) {\n\t\t\treturn new NoBlockTraverserState(state, centralityState, sourceBlock);\n\t\t}\n\t}\n\n\tprivate final BlockNode sourceBlock;\n\tprivate final CentralityState centralityState;\n\n\tpublic NoBlockTraverserState(final TraverserActivePathState state, final CentralityState centralityState, final BlockNode sourceBlock) {\n\t\tsuper(state);\n\t\tthis.sourceBlock = sourceBlock;\n\t\tthis.centralityState = centralityState;\n\t}\n\n\t@Override\n\tpublic final @Nullable AbstractBlockPathTraverserHandler getNextHandler() {\n\t\treturn new PredecessorBlockPathTraverserHandler<>(this);\n\t}\n\n\t@Override\n\tpublic final ComparisonState getCompareState() {\n\t\treturn ComparisonState.NOT_READY;\n\t}\n\n\t@Override\n\tpublic final boolean isTerminal() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tprotected final @Nullable CentralityState getUnderlyingCentralityState() {\n\t\treturn centralityState;\n\t}\n\n\t@Override\n\tprotected final @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic final BlockNode getSourceBlock() {\n\t\treturn sourceBlock;\n\t}\n\n\t@Override\n\tprotected final TraverserState duplicateInternalState(final TraverserActivePathState comparatorState) {\n\t\tfinal CentralityState dCentralityState = centralityState.duplicate();\n\t\treturn new NoBlockTraverserState(comparatorState, dCentralityState, sourceBlock);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/state/RecoveredFromCacheTraverserState.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.state;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.visitors.finaly.CentralityState;\nimport jadx.core.dex.visitors.finaly.traverser.factory.TraverserStateFactory;\nimport jadx.core.dex.visitors.finaly.traverser.handlers.AbstractBlockTraverserHandler;\n\npublic final class RecoveredFromCacheTraverserState extends TraverserState {\n\n\tpublic static TraverserStateFactory<RecoveredFromCacheTraverserState> getFactory(final TraverserState underlying) {\n\t\treturn new RecoveredFromCacheStateFactory(underlying);\n\t}\n\n\tprivate static final class RecoveredFromCacheStateFactory extends TraverserStateFactory<RecoveredFromCacheTraverserState> {\n\n\t\tprivate final TraverserState underlying;\n\n\t\tprivate RecoveredFromCacheStateFactory(final TraverserState underlying) {\n\t\t\tthis.underlying = underlying;\n\t\t}\n\n\t\t@Override\n\t\tprotected final RecoveredFromCacheTraverserState generateInternalState(final TraverserActivePathState state) {\n\t\t\treturn new RecoveredFromCacheTraverserState(underlying);\n\t\t}\n\n\t}\n\n\tprivate final TraverserState underlying;\n\n\tpublic RecoveredFromCacheTraverserState(final TraverserState underlying) {\n\t\tsuper(underlying.getComparatorState());\n\n\t\tthis.underlying = underlying;\n\t}\n\n\t@Override\n\tpublic final @Nullable AbstractBlockTraverserHandler getNextHandler() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic final ComparisonState getCompareState() {\n\t\treturn ComparisonState.NOT_READY;\n\t}\n\n\t@Override\n\tpublic final boolean isTerminal() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tprotected final @Nullable CentralityState getUnderlyingCentralityState() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tprotected final @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tprotected final TraverserState duplicateInternalState(final TraverserActivePathState comparatorState) {\n\t\treturn new RecoveredFromCacheTraverserState(underlying);\n\t}\n\n\tpublic final TraverserState getUnderlying() {\n\t\treturn underlying;\n\t}\n\n\tpublic final boolean canContinue() {\n\t\treturn underlying.isTerminal();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/state/TerminalTraverserState.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.state;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.visitors.finaly.CentralityState;\nimport jadx.core.dex.visitors.finaly.traverser.factory.TraverserStateFactory;\nimport jadx.core.dex.visitors.finaly.traverser.handlers.AbstractBlockPathTraverserHandler;\n\npublic final class TerminalTraverserState extends TraverserState {\n\n\tpublic static TraverserStateFactory<TerminalTraverserState> getFactory(final TerminationReason terminationReason) {\n\t\treturn new TerminalStateFactory(terminationReason);\n\t}\n\n\tpublic static enum TerminationReason {\n\t\t/**\n\t\t * When comparing instructions within a finally and candidate block, non-matching\n\t\t * instructions were found calling for the termination of the Traverser.\n\t\t */\n\t\tNON_MATCHING_INSTRUCTIONS,\n\n\t\tNON_MATCHING_PATHS,\n\n\t\t/**\n\t\t * When a handler was requested to find the predecessors of a block, no predecessors within\n\t\t * the scope existed.\n\t\t */\n\t\tEND_OF_PATH,\n\t\t/**\n\t\t * When a handler was requested to process a block, a cached result for that handler\n\t\t * already existed.\n\t\t */\n\t\tUSING_CACHED_RESULTS,\n\n\t\tUNMERGEABLE_STATE,\n\n\t\tUNRESOLVABLE_STATES,\n\t}\n\n\tprivate static class TerminalStateFactory extends TraverserStateFactory<TerminalTraverserState> {\n\n\t\tprivate final TerminationReason terminationReason;\n\n\t\tpublic TerminalStateFactory(final TerminationReason terminationReason) {\n\t\t\tthis.terminationReason = terminationReason;\n\t\t}\n\n\t\t@Override\n\t\tpublic TerminalTraverserState generateInternalState(TraverserActivePathState state) {\n\t\t\treturn new TerminalTraverserState(state, terminationReason);\n\t\t}\n\t}\n\n\tprivate final TerminationReason terminationReason;\n\n\tpublic TerminalTraverserState(final TraverserActivePathState state, final TerminationReason terminationReason) {\n\t\tsuper(state);\n\t\tthis.terminationReason = terminationReason;\n\t}\n\n\t@Override\n\tpublic final boolean isTerminal() {\n\t\treturn true;\n\t}\n\n\t@Override\n\t@Nullable\n\tpublic final AbstractBlockPathTraverserHandler getNextHandler() {\n\t\treturn null;\n\t}\n\n\tpublic final TerminationReason getTerminationReason() {\n\t\treturn terminationReason;\n\t}\n\n\t@Override\n\tpublic final ComparisonState getCompareState() {\n\t\treturn ComparisonState.NOT_READY;\n\t}\n\n\t@Override\n\tprotected final @Nullable CentralityState getUnderlyingCentralityState() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tprotected final @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tprotected final TraverserState duplicateInternalState(final TraverserActivePathState comparatorState) {\n\t\tfinal TraverserState duplicated = new TerminalTraverserState(comparatorState, terminationReason);\n\n\t\treturn duplicated;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/state/TraverserActivePathState.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.state;\n\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.finaly.CentralityState;\nimport jadx.core.dex.visitors.finaly.SameInstructionsStrategy;\nimport jadx.core.dex.visitors.finaly.traverser.GlobalTraverserSourceState;\nimport jadx.core.dex.visitors.finaly.traverser.factory.TraverserStateFactory;\nimport jadx.core.utils.Pair;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n/**\n * A state used by the traverser controller. For two given branches, the \"finally\" branch and the\n * \"candidate\" branch whilst determining similar instructions and blocks, the active path state\n * contains information regarding the current matched instructions and blocks, as well as the\n * current state of both the finally and candidate path being explored.\n */\npublic class TraverserActivePathState {\n\n\t/**\n\t * Produces a shallow clone of the {@link TraverserActivePathState}. Since this is a shallow clone,\n\t * it should only be used for a singular branch. If a branch is used and this needs to be\n\t * duplicated, be sure to use the deep clone duplication on the previous\n\t * {@link TraverserActivePathState} before invoking this method on it.\n\t *\n\t * @param previousTraverserState The previous active path state to create a shallow clone of.\n\t * @param finallyStateProducer   The factory responsible for producing the new finally state to be\n\t *                               held by the resulting active path state.\n\t * @param candidateStateProducer The factory responsible for producing the new candidate state to\n\t *                               be held by the resulting active path state.\n\t * @return The cloned active path state.\n\t */\n\tpublic static TraverserActivePathState produceFromFactories(final TraverserActivePathState previousTraverserState,\n\t\t\tfinal TraverserStateFactory<?> finallyStateProducer, final TraverserStateFactory<?> candidateStateProducer) {\n\t\tfinal TraverserActivePathState dState =\n\t\t\t\tnew TraverserActivePathState(previousTraverserState.matchedInsns, previousTraverserState.finallyCompletionMonitor,\n\t\t\t\t\t\tpreviousTraverserState.candidateCompletionMonitor, previousTraverserState.commonGlobalState,\n\t\t\t\t\t\tpreviousTraverserState.finallyGlobalState,\n\t\t\t\t\t\tpreviousTraverserState.candidateGlobalState);\n\n\t\tfinal TraverserState dFinallyState = finallyStateProducer.generateState(dState);\n\t\tfinal TraverserState dCandidateState = candidateStateProducer.generateState(dState);\n\n\t\tdState.candidateStateRef.set(dCandidateState);\n\t\tdState.finallyStateRef.set(dFinallyState);\n\n\t\treturn dState;\n\t}\n\n\t/**\n\t * Tracks the comparison state of a given block.\n\t * i.e. how many of the instructions have been compared in the Traversal.\n\t */\n\tprivate static final class BlockCompletionMonitor {\n\n\t\tprivate final BlockNode block;\n\t\tprivate final Set<Integer> matchedIndices;\n\t\tprivate final int insnCount;\n\n\t\tprivate BlockCompletionMonitor(final BlockNode block) {\n\t\t\tthis.block = block;\n\t\t\tthis.insnCount = block.getInstructions().size();\n\t\t\tthis.matchedIndices = new HashSet<>(insnCount);\n\t\t\tfor (int i = 0; i < insnCount; i++) {\n\t\t\t\tmatchedIndices.add(i);\n\t\t\t}\n\t\t}\n\n\t\tprivate void registerWithBlockInfo(final TraverserBlockInfo info, final int numberMatched) {\n\t\t\tif (info.getBlock() != block) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfinal int botPointer = info.getBottomOffset();\n\t\t\tfor (int i = 0; i < numberMatched; i++) {\n\t\t\t\tfinal int indexMatched = botPointer + i;\n\t\t\t\tmatchedIndices.remove(indexMatched);\n\t\t\t}\n\n\t\t\tfinal int bottomImplicitCount = info.getBottomImplicitCount();\n\t\t\tfinal boolean noPathEndInsns = botPointer - bottomImplicitCount == 0;\n\t\t\tif (noPathEndInsns) {\n\t\t\t\tfor (int i = 0; i < bottomImplicitCount; i++) {\n\t\t\t\t\tmatchedIndices.remove(i);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate BlockCompletionMonitor duplicate() {\n\t\t\tfinal BlockCompletionMonitor dup = new BlockCompletionMonitor(block);\n\t\t\tdup.matchedIndices.retainAll(matchedIndices);\n\t\t\treturn dup;\n\t\t}\n\n\t\tprivate void mergeWith(final BlockCompletionMonitor other) {\n\t\t\tif (other.block != block) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tmatchedIndices.retainAll(other.matchedIndices);\n\t\t}\n\n\t\tprivate boolean isEntireBlock() {\n\t\t\treturn matchedIndices.isEmpty();\n\t\t}\n\t}\n\n\tprivate static final class BlockCompletionMonitorMap implements Map<BlockNode, BlockCompletionMonitor> {\n\n\t\tprivate final Map<BlockNode, BlockCompletionMonitor> underlying;\n\n\t\tpublic BlockCompletionMonitorMap() {\n\t\t\tunderlying = new HashMap<>();\n\t\t}\n\n\t\t@Override\n\t\tpublic final void clear() {\n\t\t\tunderlying.clear();\n\t\t}\n\n\t\t@Override\n\t\tpublic final boolean containsKey(Object key) {\n\t\t\treturn underlying.containsKey(key);\n\t\t}\n\n\t\t@Override\n\t\tpublic final boolean containsValue(Object value) {\n\t\t\tif (!(value instanceof BlockNode)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tfinal BlockNode edge = (BlockNode) value;\n\t\t\treturn underlying.containsKey(edge);\n\t\t}\n\n\t\t@Override\n\t\tpublic final Set<Entry<BlockNode, BlockCompletionMonitor>> entrySet() {\n\t\t\treturn underlying.entrySet();\n\t\t}\n\n\t\t@Override\n\t\tpublic final BlockCompletionMonitor get(Object key) {\n\t\t\treturn underlying.get(key);\n\t\t}\n\n\t\t@Override\n\t\tpublic final boolean isEmpty() {\n\t\t\treturn underlying.isEmpty();\n\t\t}\n\n\t\t@Override\n\t\tpublic final Set<BlockNode> keySet() {\n\t\t\treturn underlying.keySet();\n\t\t}\n\n\t\t@Override\n\t\tpublic final BlockCompletionMonitor put(BlockNode key, BlockCompletionMonitor value) {\n\t\t\treturn underlying.put(key, value);\n\t\t}\n\n\t\t@Override\n\t\tpublic final void putAll(Map<? extends BlockNode, ? extends BlockCompletionMonitor> otherMap) {\n\t\t\tunderlying.putAll(otherMap);\n\t\t}\n\n\t\t@Override\n\t\tpublic final BlockCompletionMonitor remove(Object key) {\n\t\t\treturn underlying.remove(key);\n\t\t}\n\n\t\t@Override\n\t\tpublic final int size() {\n\t\t\treturn underlying.size();\n\t\t}\n\n\t\t@Override\n\t\tpublic final Collection<BlockCompletionMonitor> values() {\n\t\t\treturn underlying.values();\n\t\t}\n\n\t\tprivate void registerWithBlockInfo(final TraverserBlockInfo info, final int numberMatched) {\n\t\t\tfinal BlockNode block = info.getBlock();\n\t\t\tif (containsKey(block)) {\n\t\t\t\tget(block).registerWithBlockInfo(info, numberMatched);\n\t\t\t} else {\n\t\t\t\tfinal BlockCompletionMonitor monitor = new BlockCompletionMonitor(block);\n\t\t\t\tmonitor.registerWithBlockInfo(info, numberMatched);\n\t\t\t\tput(block, monitor);\n\t\t\t}\n\t\t}\n\n\t\tprivate void mergeEntry(final BlockCompletionMonitor other) {\n\t\t\tfinal BlockNode block = other.block;\n\t\t\tif (containsKey(block)) {\n\t\t\t\tget(block).mergeWith(other);\n\t\t\t} else {\n\t\t\t\tfinal BlockCompletionMonitor monitor = other.duplicate();\n\t\t\t\tput(block, monitor);\n\t\t\t}\n\t\t}\n\n\t\tprivate void mergeMap(final BlockCompletionMonitorMap other) {\n\t\t\tfor (final BlockCompletionMonitor monitor : other.values()) {\n\t\t\t\tmergeEntry(monitor);\n\t\t\t}\n\t\t}\n\n\t\tprivate BlockCompletionMonitorMap duplicate() {\n\t\t\tfinal BlockCompletionMonitorMap dup = new BlockCompletionMonitorMap();\n\t\t\tfor (final BlockNode sourceBlock : keySet()) {\n\t\t\t\tfinal BlockCompletionMonitor monitor = get(sourceBlock);\n\t\t\t\tdup.put(sourceBlock, monitor.duplicate());\n\t\t\t}\n\t\t\treturn dup;\n\t\t}\n\t}\n\n\tprivate final AtomicReference<TraverserState> finallyStateRef;\n\tprivate final AtomicReference<TraverserState> candidateStateRef;\n\tprivate final GlobalTraverserSourceState finallyGlobalState;\n\tprivate final GlobalTraverserSourceState candidateGlobalState;\n\tprivate final TraverserGlobalCommonState commonGlobalState;\n\n\tprivate final Set<Pair<InsnNode>> matchedInsns;\n\tprivate final BlockCompletionMonitorMap finallyCompletionMonitor;\n\tprivate final BlockCompletionMonitorMap candidateCompletionMonitor;\n\n\t/**\n\t * Creates a new instance of a traversal active path. This constructor is used to create a new\n\t * path to be used by the traverser controller to begin a new traversal.\n\t *\n\t * @param mth\n\t * @param sameInstructionsStrategy\n\t * @param finallyBlockTerminus\n\t * @param candidateBlockTerminus\n\t * @param finallyBlocks\n\t * @param candidateBlocks\n\t */\n\tpublic TraverserActivePathState(final MethodNode mth, final SameInstructionsStrategy sameInstructionsStrategy,\n\t\t\tfinal BlockNode finallyBlockTerminus, final BlockNode candidateBlockTerminus, final List<BlockNode> finallyBlocks,\n\t\t\tfinal List<BlockNode> candidateBlocks) {\n\t\tfinal boolean shouldFinallyAllowFirstBlockSkip = !finallyBlockTerminus.getInstructions().isEmpty();\n\t\tfinal boolean shouldCandidateAllowFirstBlockSkip = !candidateBlockTerminus.getInstructions().isEmpty();\n\t\tfinal CentralityState finallyCentralityState = new CentralityState(sameInstructionsStrategy, shouldFinallyAllowFirstBlockSkip);\n\t\tfinal CentralityState candidateCentralityState = new CentralityState(sameInstructionsStrategy, shouldCandidateAllowFirstBlockSkip);\n\n\t\tfinal TraverserBlockInfo finallyBlockInfo = new TraverserBlockInfo(finallyBlockTerminus);\n\t\tfinal TraverserBlockInfo candidateBlockInfo = new TraverserBlockInfo(candidateBlockTerminus);\n\n\t\tfinal TraverserState finallyState = new NewBlockTraverserState(this, finallyCentralityState, finallyBlockInfo);\n\t\tfinal TraverserState candidateState = new NewBlockTraverserState(this, candidateCentralityState, candidateBlockInfo);\n\n\t\tthis.finallyGlobalState = new GlobalTraverserSourceState(new HashSet<>(finallyBlocks));\n\t\tthis.candidateGlobalState = new GlobalTraverserSourceState(new HashSet<>(candidateBlocks));\n\t\tthis.commonGlobalState = new TraverserGlobalCommonState(mth);\n\n\t\tthis.finallyStateRef = new AtomicReference<>(finallyState);\n\t\tthis.candidateStateRef = new AtomicReference<>(candidateState);\n\t\tthis.matchedInsns = new HashSet<>();\n\t\tthis.finallyCompletionMonitor = new BlockCompletionMonitorMap();\n\t\tthis.candidateCompletionMonitor = new BlockCompletionMonitorMap();\n\t}\n\n\t/**\n\t * Creates a new instance of a traversal active path. This constructor is used to duplicate a\n\t * state between a previous traverser controller and is a liaison for initialising non-null\n\t * final fields for the {@link TraverserActivePathState#produceFromFactories} function.\n\t *\n\t * @param matchedInsns\n\t * @param finallyCompletionMonitor\n\t * @param candidateCompletionMonitor\n\t * @param commonGlobalState\n\t * @param finallyGlobalState\n\t * @param candidateGlobalState\n\t */\n\tprivate TraverserActivePathState(final Set<Pair<InsnNode>> matchedInsns, final BlockCompletionMonitorMap finallyCompletionMonitor,\n\t\t\tfinal BlockCompletionMonitorMap candidateCompletionMonitor, final TraverserGlobalCommonState commonGlobalState,\n\t\t\tfinal GlobalTraverserSourceState finallyGlobalState, final GlobalTraverserSourceState candidateGlobalState) {\n\t\tthis.finallyStateRef = new AtomicReference<>();\n\t\tthis.candidateStateRef = new AtomicReference<>();\n\t\tthis.matchedInsns = matchedInsns;\n\t\tthis.finallyGlobalState = finallyGlobalState;\n\t\tthis.candidateGlobalState = candidateGlobalState;\n\t\tthis.commonGlobalState = commonGlobalState;\n\t\tthis.finallyCompletionMonitor = finallyCompletionMonitor;\n\t\tthis.candidateCompletionMonitor = candidateCompletionMonitor;\n\t}\n\n\tpublic final TraverserActivePathState duplicate() {\n\t\tfinal Set<Pair<InsnNode>> dMatchedInsns = new HashSet<>(matchedInsns);\n\t\tfinal BlockCompletionMonitorMap dFinallyCompletionMonitor = finallyCompletionMonitor.duplicate();\n\t\tfinal BlockCompletionMonitorMap dCandidateCompletionMonitor = candidateCompletionMonitor.duplicate();\n\t\tfinal TraverserActivePathState dState =\n\t\t\t\tnew TraverserActivePathState(dMatchedInsns, dFinallyCompletionMonitor, dCandidateCompletionMonitor,\n\t\t\t\t\t\tcommonGlobalState, finallyGlobalState, candidateGlobalState);\n\n\t\tfinal TraverserState dFinallyState = getFinallyState().duplicate(dState);\n\t\tfinal TraverserState dCandidateState = getCandidateState().duplicate(dState);\n\n\t\tdState.candidateStateRef.set(dCandidateState);\n\t\tdState.finallyStateRef.set(dFinallyState);\n\n\t\treturn dState;\n\t}\n\n\tpublic final TraverserState getFinallyState() {\n\t\treturn finallyStateRef.get();\n\t}\n\n\tpublic final TraverserState getCandidateState() {\n\t\treturn candidateStateRef.get();\n\t}\n\n\tpublic final AtomicReference<TraverserState> getFinallyStateRef() {\n\t\treturn finallyStateRef;\n\t}\n\n\tpublic final AtomicReference<TraverserState> getCandidateStateRef() {\n\t\treturn candidateStateRef;\n\t}\n\n\tpublic final Set<Pair<InsnNode>> getMatchedInsns() {\n\t\treturn matchedInsns;\n\t}\n\n\t@Nullable\n\tpublic final AtomicReference<TraverserState> getReferenceForState(final TraverserState state) {\n\t\tif (finallyStateRef.get() == state) {\n\t\t\treturn finallyStateRef;\n\t\t} else if (candidateStateRef.get() == state) {\n\t\t\treturn candidateStateRef;\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic final GlobalTraverserSourceState getGlobalStateFor(final TraverserState state) {\n\t\tif (finallyStateRef.get() == state) {\n\t\t\treturn finallyGlobalState;\n\t\t} else if (candidateStateRef.get() == state) {\n\t\t\treturn candidateGlobalState;\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(\"Orphaned TraverserState node\");\n\t\t}\n\t}\n\n\tpublic final GlobalTraverserSourceState getFinallyGlobalState() {\n\t\treturn finallyGlobalState;\n\t}\n\n\tpublic final GlobalTraverserSourceState getCandidateGlobalState() {\n\t\treturn candidateGlobalState;\n\t}\n\n\tpublic final TraverserGlobalCommonState getGlobalCommonState() {\n\t\treturn commonGlobalState;\n\t}\n\n\tpublic final void mergeWith(final List<TraverserActivePathState> otherStates) {\n\t\tfor (final TraverserActivePathState otherState : otherStates) {\n\t\t\tmatchedInsns.addAll(otherState.getMatchedInsns());\n\n\t\t\tfinallyCompletionMonitor.mergeMap(otherState.finallyCompletionMonitor);\n\t\t\tcandidateCompletionMonitor.mergeMap(otherState.candidateCompletionMonitor);\n\t\t}\n\t}\n\n\tpublic final void registerWithBlockInfo(final TraverserBlockInfo info, final int numberMatched) {\n\t\tfinal BlockNode block = info.getBlock();\n\t\tfinal boolean isFinallyBlock = finallyGlobalState.isBlockContained(block);\n\t\tfinal BlockCompletionMonitorMap monitorMap;\n\t\tif (isFinallyBlock) {\n\t\t\tmonitorMap = finallyCompletionMonitor;\n\t\t} else {\n\t\t\tmonitorMap = candidateCompletionMonitor;\n\t\t}\n\t\tmonitorMap.registerWithBlockInfo(info, numberMatched);\n\t}\n\n\tpublic final Set<BlockNode> getAllFullyMatchedFinallyBlocks() {\n\t\treturn getAllFullyMatchedBlocks(finallyCompletionMonitor);\n\t}\n\n\tpublic final Set<BlockNode> getAllFullyMatchedCandidateBlocks() {\n\t\treturn getAllFullyMatchedBlocks(candidateCompletionMonitor);\n\t}\n\n\tprivate Set<BlockNode> getAllFullyMatchedBlocks(final BlockCompletionMonitorMap monitorMap) {\n\t\tfinal Set<BlockNode> matches = new HashSet<>();\n\n\t\tfor (final BlockCompletionMonitor monitor : monitorMap.values()) {\n\t\t\tif (!monitor.isEntireBlock()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tmatches.add(monitor.block);\n\t\t}\n\n\t\treturn matches;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/state/TraverserBlockInfo.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.state;\n\nimport java.util.List;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic final class TraverserBlockInfo {\n\n\tprivate final BlockNode block;\n\n\t// These offsets are the instruction indices NOT an instruction size.\n\tprivate int bottomOffset;\n\tprivate int topOffset;\n\tprivate int bottomImplicitCount;\n\n\tpublic TraverserBlockInfo(final BlockNode block) {\n\t\tthis(block, 0, 0, 0);\n\t}\n\n\tpublic TraverserBlockInfo(final BlockNode block, final int bottomOffset, final int topOffset, final int bottomImplicitCount) {\n\t\tthis.bottomOffset = bottomOffset;\n\t\tthis.topOffset = topOffset;\n\t\tthis.block = block;\n\t\tthis.bottomImplicitCount = bottomImplicitCount;\n\t}\n\n\t@Override\n\tpublic final String toString() {\n\t\treturn toString(\"\");\n\t}\n\n\tpublic final String toString(final String indent) {\n\t\tfinal StringBuilder sb = new StringBuilder(\"BlockInsnInfo - \");\n\n\t\tsb.append(block.toString());\n\t\tsb.append(\" [↑ \");\n\t\tsb.append(bottomOffset);\n\t\tsb.append(\"] [↓ \");\n\t\tsb.append(topOffset);\n\t\tsb.append(\"] \");\n\n\t\treturn sb.toString();\n\t}\n\n\tpublic final TraverserBlockInfo duplicate() {\n\t\treturn new TraverserBlockInfo(block, bottomOffset, topOffset, bottomImplicitCount);\n\t}\n\n\tpublic final BlockNode getBlock() {\n\t\treturn block;\n\t}\n\n\tpublic final int getTopOffset() {\n\t\treturn topOffset;\n\t}\n\n\tpublic final void setTopOffset(final int topOffset) {\n\t\tthis.topOffset = topOffset;\n\t}\n\n\tpublic final int getBottomOffset() {\n\t\treturn bottomOffset;\n\t}\n\n\tpublic final void setBottomOffset(final int bottomOffset) {\n\t\tthis.bottomOffset = bottomOffset;\n\t}\n\n\tpublic final int getBottomImplicitCount() {\n\t\treturn bottomImplicitCount;\n\t}\n\n\tpublic final void setBottomImplicitOffset(final int bottomImplicitCount) {\n\t\tthis.bottomImplicitCount = bottomImplicitCount;\n\t}\n\n\tpublic final List<InsnNode> getInsnsSlice() {\n\t\tfinal List<InsnNode> insns = block.getInstructions();\n\n\t\tfinal int totalSkippedCount = bottomOffset + topOffset;\n\t\tif (totalSkippedCount > insns.size()) {\n\t\t\tthrow new IndexOutOfBoundsException(\"Attempted to get instructions slice of block \" + block.toString() + \" with \"\n\t\t\t\t\t+ totalSkippedCount + \" skipped instructions whilst only having \" + insns.size() + \" instructions in block.\");\n\t\t}\n\n\t\tfinal int startIndex = topOffset;\n\t\tfinal int endIndex = insns.size() - bottomOffset;\n\n\t\treturn insns.subList(startIndex, endIndex);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/state/TraverserGlobalCommonState.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.state;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.Pair;\n\npublic final class TraverserGlobalCommonState {\n\n\tprivate final MethodNode mth;\n\tprivate final Map<Pair<BlockNode>, List<TraverserActivePathState>> searchedStates;\n\n\tpublic TraverserGlobalCommonState(final MethodNode mth) {\n\t\tthis.mth = mth;\n\t\tthis.searchedStates = new HashMap<>();\n\t}\n\n\tpublic final void addCachedStateFor(final BlockNode finallyBlock, final BlockNode candidateBlock,\n\t\t\tfinal List<TraverserActivePathState> state) {\n\t\tfinal Pair<BlockNode> blocks = new Pair<>(finallyBlock, candidateBlock);\n\t\tsearchedStates.put(blocks, state);\n\t}\n\n\t@Nullable\n\tpublic final List<TraverserActivePathState> getCachedStateFor(final BlockNode finallyBlock, final BlockNode candidateBlock) {\n\t\tfinal Pair<BlockNode> blocks = new Pair<>(finallyBlock, candidateBlock);\n\t\treturn searchedStates.get(blocks);\n\t}\n\n\tpublic final boolean hasBlocksBeenCached(final BlockNode finallyBlock, final BlockNode candidateBlock) {\n\t\tfinal Pair<BlockNode> blocks = new Pair<>(finallyBlock, candidateBlock);\n\t\treturn searchedStates.containsKey(blocks);\n\t}\n\n\tpublic final MethodNode getMethodNode() {\n\t\treturn mth;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/state/TraverserState.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.state;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.visitors.finaly.CentralityState;\nimport jadx.core.dex.visitors.finaly.traverser.GlobalTraverserSourceState;\nimport jadx.core.dex.visitors.finaly.traverser.handlers.AbstractBlockTraverserHandler;\n\npublic abstract class TraverserState {\n\n\tpublic static enum ComparisonState {\n\t\tNOT_READY,\n\t\tAWAITING_OPTIONAL_PREDECESSOR_MERGE,\n\t\tREADY_TO_COMPARE\n\t}\n\n\tprivate final TraverserActivePathState comparatorState;\n\n\tpublic TraverserState(final TraverserActivePathState comparatorState) {\n\t\tthis.comparatorState = comparatorState;\n\t}\n\n\t@Nullable\n\tpublic abstract AbstractBlockTraverserHandler getNextHandler();\n\n\tpublic abstract ComparisonState getCompareState();\n\n\tpublic abstract boolean isTerminal();\n\n\tprotected abstract @Nullable CentralityState getUnderlyingCentralityState();\n\n\tprotected abstract @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo();\n\n\t/**\n\t * Performs a deep clone of this Traverser state.\n\t *\n\t * @return The deep cloned duplication of this Traverser state.\n\t */\n\tprotected abstract TraverserState duplicateInternalState(final TraverserActivePathState comparatorState);\n\n\t@Override\n\tpublic final String toString() {\n\t\treturn toString(0);\n\t}\n\n\tpublic final TraverserState duplicate(final TraverserActivePathState comparatorState) {\n\t\tfinal TraverserState duplicatedState = duplicateInternalState(comparatorState);\n\t\treturn duplicatedState;\n\t}\n\n\tpublic final TraverserActivePathState getComparatorState() {\n\t\treturn comparatorState;\n\t}\n\n\tpublic final String toString(final int indentAmount) {\n\t\tfinal String baseIndent = \" \".repeat(indentAmount);\n\t\tfinal String secondIndent = \" \".repeat(indentAmount + 2);\n\n\t\tfinal StringBuilder sb = new StringBuilder(baseIndent);\n\t\tsb.append(getClass().getSimpleName());\n\t\tsb.append(' ');\n\n\t\tif (isTerminal()) {\n\t\t\tsb.append(\"TERMINAL \");\n\t\t}\n\n\t\tsb.append(\" {\");\n\t\tsb.append(System.lineSeparator());\n\n\t\tsb.append(secondIndent);\n\t\tsb.append(\"centrality: \");\n\t\tfinal CentralityState centralityState = getUnderlyingCentralityState();\n\t\tif (centralityState == null) {\n\t\t\tsb.append(\"none\");\n\t\t} else {\n\t\t\tsb.append(getCentralityState());\n\t\t}\n\t\tsb.append(System.lineSeparator());\n\n\t\tsb.append(secondIndent);\n\t\tsb.append(getCompareState());\n\t\tsb.append(System.lineSeparator());\n\n\t\tsb.append(secondIndent);\n\t\tfinal TraverserBlockInfo blockInsnInfo = getBlockInsnInfo();\n\t\tif (blockInsnInfo != null) {\n\t\t\tsb.append(blockInsnInfo.toString(secondIndent));\n\t\t} else {\n\t\t\tsb.append(\"NO ACTIVE BLOCK\");\n\t\t}\n\t\tsb.append(System.lineSeparator());\n\n\t\tsb.append(baseIndent);\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\tpublic final CentralityState getCentralityState() {\n\t\tfinal CentralityState underlying = getUnderlyingCentralityState();\n\t\tif (underlying == null) {\n\t\t\tthrow new UnsupportedOperationException(\"Centrality state is not supported for \" + getClass().getName());\n\t\t}\n\t\treturn underlying;\n\t}\n\n\tpublic final @Nullable TraverserBlockInfo getBlockInsnInfo() {\n\t\treturn getUnderlyingBlockInsnInfo();\n\t}\n\n\tpublic final GlobalTraverserSourceState getGlobalState() {\n\t\treturn getComparatorState().getGlobalStateFor(this);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/state/UnknownAdvanceStrategyTraverserState.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.state;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.visitors.finaly.CentralityState;\nimport jadx.core.dex.visitors.finaly.traverser.handlers.AbstractActivePathTraverserHandler;\nimport jadx.core.dex.visitors.finaly.traverser.handlers.PredecessorMergeActivePathTraverserHandler;\n\npublic final class UnknownAdvanceStrategyTraverserState extends TraverserState {\n\n\tprivate final CentralityState centralityState;\n\tprivate final List<BlockNode> nextBlocks;\n\n\tpublic UnknownAdvanceStrategyTraverserState(final TraverserActivePathState state, final CentralityState centralityState,\n\t\t\tfinal List<BlockNode> nextBlocks) {\n\t\tsuper(state);\n\n\t\tthis.centralityState = centralityState;\n\t\tthis.nextBlocks = nextBlocks;\n\t}\n\n\t@Override\n\tpublic final @Nullable AbstractActivePathTraverserHandler getNextHandler() {\n\t\treturn new PredecessorMergeActivePathTraverserHandler(getComparatorState());\n\t}\n\n\t@Override\n\tpublic final ComparisonState getCompareState() {\n\t\treturn ComparisonState.READY_TO_COMPARE;\n\t}\n\n\t@Override\n\tpublic final boolean isTerminal() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tprotected final @Nullable CentralityState getUnderlyingCentralityState() {\n\t\treturn centralityState;\n\t}\n\n\t@Override\n\tprotected final @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tprotected final TraverserState duplicateInternalState(final TraverserActivePathState comparatorState) {\n\t\tfinal CentralityState dCentralityState = centralityState.duplicate();\n\t\tfinal List<BlockNode> dNextBlocks = new ArrayList<>(nextBlocks);\n\n\t\tfinal TraverserState duplicated = new UnknownAdvanceStrategyTraverserState(comparatorState, dCentralityState, dNextBlocks);\n\n\t\treturn duplicated;\n\t}\n\n\tpublic final List<BlockNode> getNextBlocks() {\n\t\treturn nextBlocks;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/visitors/AbstractBlockTraverserVisitor.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.visitors;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserActivePathState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserState;\n\npublic abstract class AbstractBlockTraverserVisitor {\n\n\tprivate final TraverserState state;\n\n\tpublic AbstractBlockTraverserVisitor(TraverserState state) {\n\t\tthis.state = state;\n\t}\n\n\tpublic abstract TraverserState visit(BlockNode block);\n\n\tpublic TraverserState getState() {\n\t\treturn state;\n\t}\n\n\tpublic TraverserActivePathState getComparator() {\n\t\treturn state.getComparatorState();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/visitors/ImplicitInsnBlockTraverserVisitor.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.visitors;\n\nimport java.util.List;\nimport java.util.ListIterator;\n\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserBlockInfo;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserState;\n\npublic final class ImplicitInsnBlockTraverserVisitor extends AbstractBlockTraverserVisitor {\n\n\tpublic static boolean isInstructionImplicit(final InsnNode node) {\n\t\t// An instruction is implicit if it can be safely skipped for comparison when traversing in reverse\n\t\t// order.\n\t\t// The presence of a GOTO should be reflected in the structure of a block graph.\n\t\t// i.e. a GOTO should be the last instruction of a block with a single successor.\n\t\t// Another example might be NOP.\n\t\tfinal InsnType type = node.getType();\n\t\tswitch (type) {\n\t\t\tcase GOTO:\n\t\t\t\treturn true;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic ImplicitInsnBlockTraverserVisitor(final TraverserState state) {\n\t\tsuper(state);\n\t}\n\n\t@Override\n\tpublic final TraverserState visit(final BlockNode block) {\n\t\tfinal TraverserBlockInfo insnInfo = getState().getBlockInsnInfo();\n\n\t\tfinal List<InsnNode> insns = insnInfo.getInsnsSlice();\n\t\tfinal ListIterator<InsnNode> insnsIterator = insns.listIterator(insns.size());\n\n\t\t/**\n\t\t * The number of instructions that have been identified as \"implicit\" instructions.\n\t\t */\n\t\tint bottomDelta = 0;\n\t\twhile (insnsIterator.hasPrevious()) {\n\t\t\tfinal InsnNode insn = insnsIterator.previous();\n\t\t\tif (!isInstructionImplicit(insn)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbottomDelta++;\n\t\t}\n\n\t\tinsnInfo.setBottomOffset(insnInfo.getBottomOffset() + bottomDelta);\n\t\tinsnInfo.setBottomImplicitOffset(insnInfo.getBottomImplicitCount() + bottomDelta);\n\t\treturn getState();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/visitors/PathEndBlockTraverserVisitor.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.visitors;\n\nimport java.util.List;\nimport java.util.ListIterator;\n\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.visitors.finaly.CentralityState;\nimport jadx.core.dex.visitors.finaly.traverser.state.AwaitingInsnCompareTraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.NoBlockTraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserBlockInfo;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserState;\n\npublic final class PathEndBlockTraverserVisitor extends AbstractBlockTraverserVisitor {\n\n\tpublic static boolean isInstructionPathEnd(final InsnNode insn) {\n\t\tfinal InsnType type = insn.getType();\n\n\t\tswitch (type) {\n\t\t\tcase RETURN:\n\t\t\tcase THROW:\n\t\t\t\treturn true;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic PathEndBlockTraverserVisitor(final TraverserState state) {\n\t\tsuper(state);\n\t}\n\n\t@Override\n\tpublic final TraverserState visit(final BlockNode block) {\n\t\tfinal CentralityState centralityState = getState().getCentralityState();\n\n\t\tfinal TraverserBlockInfo insnInfo = getState().getBlockInsnInfo();\n\n\t\tif (!centralityState.getAllowsCentral()) {\n\t\t\treturn new AwaitingInsnCompareTraverserState(getComparator(), centralityState, insnInfo);\n\t\t}\n\n\t\tfinal List<InsnNode> insns = insnInfo.getInsnsSlice();\n\t\tfinal ListIterator<InsnNode> insnsIterator = insns.listIterator(insns.size());\n\n\t\t/**\n\t\t * The number of instructions that have been identified as \"path end\" instructions.\n\t\t */\n\t\tint bottomDelta = 0;\n\t\twhile (insnsIterator.hasPrevious()) {\n\t\t\tfinal InsnNode insn = insnsIterator.previous();\n\n\t\t\t// Check if we should ignore the instruction due to it being a \"path end\" instruction.\n\t\t\tif (isInstructionPathEnd(insn)) {\n\t\t\t\t// This instruction is a path end instruction - this instruction causes the handler to exit. Here,\n\t\t\t\t// we will check the argument\n\t\t\t\t// of the path end instruction. If the instruction is a THROW or RETURN, this will indicate the\n\t\t\t\t// argument, so long as it exists\n\t\t\t\t// and is a register argument, which is operated upon before exiting this scope. Thus, we will mark\n\t\t\t\t// this argument as an allowable\n\t\t\t\t// path end instruction so long as an instruction returns this argument.\n\t\t\t\t//\n\t\t\t\t// Example:\n\t\t\t\t// CONST_STR r2 = \"return this string\" <-- A path end instruction since it sets an arg which is used\n\t\t\t\t// by path end insn\n\t\t\t\t// RETURN r2 <-- A path end instruction\n\n\t\t\t\tif (insn.getArgsCount() != 0) {\n\t\t\t\t\tfinal InsnArg handlerExitArg = insn.getArg(0);\n\t\t\t\t\t// Returned values from instructions can only be register args so we check that the input to the\n\t\t\t\t\t// path end insn is a register arg\n\t\t\t\t\tif (handlerExitArg instanceof RegisterArg) {\n\t\t\t\t\t\tcentralityState.addAllowableOutput((RegisterArg) handlerExitArg);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbottomDelta++;\n\t\t\t\t// If this instruction is not a path end instruction, check if it sets or invokes a value which is\n\t\t\t\t// used by a path end instruction.\n\t\t\t} else if (centralityState.hasAllowableOutput(insn)) {\n\t\t\t\tbottomDelta++;\n\t\t\t\tcentralityState.addAllowableOutputs(insn);\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tinsnInfo.setBottomOffset(insnInfo.getBottomOffset() + bottomDelta);\n\n\t\tfinal BlockNode sourceBlock = insnInfo.getBlock();\n\t\tfinal boolean noInstructionsLeft = insnInfo.getBottomOffset() >= sourceBlock.getInstructions().size();\n\t\tif (noInstructionsLeft) {\n\t\t\t// Mark the state to request finding predecessors to search for duplicate instructions for\n\t\t\treturn new NoBlockTraverserState(getComparator(), centralityState, sourceBlock);\n\t\t} else {\n\t\t\t// Mark the current state to await comparing of instructions\n\t\t\treturn new AwaitingInsnCompareTraverserState(getComparator(), centralityState, insnInfo);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/visitors/PredecessorBlockTraverserVisitor.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.visitors;\n\nimport java.util.List;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.visitors.finaly.CentralityState;\nimport jadx.core.dex.visitors.finaly.traverser.GlobalTraverserSourceState;\nimport jadx.core.dex.visitors.finaly.traverser.state.NewBlockTraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TerminalTraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserBlockInfo;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.UnknownAdvanceStrategyTraverserState;\nimport jadx.core.utils.ListUtils;\n\npublic final class PredecessorBlockTraverserVisitor extends AbstractBlockTraverserVisitor {\n\n\tpublic PredecessorBlockTraverserVisitor(final TraverserState state) {\n\t\tsuper(state);\n\t}\n\n\t@Override\n\tpublic final TraverserState visit(final BlockNode block) {\n\n\t\tfinal TraverserState currentState = getState();\n\t\tfinal CentralityState centralityState = currentState.getCentralityState();\n\t\tfinal GlobalTraverserSourceState globalState = currentState.getGlobalState();\n\n\t\tfinal List<BlockNode> predecessors = block.getPredecessors();\n\t\tfinal List<BlockNode> containedPredecessors = ListUtils.filter(predecessors, globalState::isBlockContained);\n\t\tfinal int predecessorsCount = containedPredecessors.size();\n\n\t\tswitch (predecessorsCount) {\n\t\t\tcase 0:\n\t\t\t\treturn new TerminalTraverserState(getComparator(), TerminalTraverserState.TerminationReason.END_OF_PATH);\n\t\t\tcase 1:\n\t\t\t\tfinal BlockNode nextBlock = containedPredecessors.get(0);\n\t\t\t\tfinal TraverserBlockInfo blockInfo = new TraverserBlockInfo(nextBlock);\n\t\t\t\treturn new NewBlockTraverserState(getComparator(), centralityState, blockInfo);\n\t\t\tdefault:\n\t\t\t\treturn new UnknownAdvanceStrategyTraverserState(getComparator(), centralityState, containedPredecessors);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/visitors/comparator/AbstractTraverserComparatorVisitor.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.visitors.comparator;\n\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserActivePathState;\n\npublic abstract class AbstractTraverserComparatorVisitor {\n\n\tpublic abstract TraverserActivePathState visit(final TraverserActivePathState state);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/visitors/comparator/InstructionBlockComparatorTraverserVisitor.java",
    "content": "package jadx.core.dex.visitors.finaly.traverser.visitors.comparator;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.visitors.finaly.CentralityState;\nimport jadx.core.dex.visitors.finaly.SameInstructionsStrategy;\nimport jadx.core.dex.visitors.finaly.SameInstructionsStrategyImpl;\nimport jadx.core.dex.visitors.finaly.traverser.factory.DuplicatedTraverserStateFactory;\nimport jadx.core.dex.visitors.finaly.traverser.factory.TraverserStateFactory;\nimport jadx.core.dex.visitors.finaly.traverser.state.NoBlockTraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TerminalTraverserState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserActivePathState;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserBlockInfo;\nimport jadx.core.dex.visitors.finaly.traverser.state.TraverserState;\nimport jadx.core.utils.Pair;\n\npublic final class InstructionBlockComparatorTraverserVisitor extends AbstractTraverserComparatorVisitor {\n\n\tprivate static TraverserActivePathState createStateForPerfectMatch(final TraverserActivePathState previousState,\n\t\t\tfinal BlockNode finallyBlock,\n\t\t\tfinal BlockNode candidateBlock) {\n\t\tfinal CentralityState finallyCentralityState = previousState.getFinallyState().getCentralityState().duplicate();\n\t\tfinal CentralityState candidateCentralityState = previousState.getCandidateState().getCentralityState().duplicate();\n\n\t\tfinallyCentralityState.setAllowsCentral(false);\n\t\tcandidateCentralityState.setAllowsCentral(false);\n\t\tfinallyCentralityState.setAllowsNonStartingNode(false);\n\t\tcandidateCentralityState.setAllowsNonStartingNode(false);\n\n\t\tfinal TraverserStateFactory<NoBlockTraverserState> finallyStateProducer =\n\t\t\t\tNoBlockTraverserState.getFactory(finallyCentralityState, finallyBlock);\n\t\tfinal TraverserStateFactory<NoBlockTraverserState> candidateStateProducer =\n\t\t\t\tNoBlockTraverserState.getFactory(candidateCentralityState, candidateBlock);\n\n\t\treturn TraverserActivePathState.produceFromFactories(previousState, finallyStateProducer, candidateStateProducer);\n\t}\n\n\tprivate static TraverserActivePathState createStateForUnevenMatch(final TraverserActivePathState previousState,\n\t\t\tfinal TraverserState finallyState,\n\t\t\tfinal TraverserState candidateState, final BlockNode finallyBlock, final BlockNode candidateBlock, final int finallyInsnsSize,\n\t\t\tfinal int candidateInsnsSize) {\n\t\tfinal int maxIterateCount = Math.max(finallyInsnsSize, candidateInsnsSize);\n\t\tfinal boolean finallyOverruns = finallyInsnsSize > candidateInsnsSize;\n\n\t\tfinal int insnsDelta;\n\t\tfinal TraverserStateFactory<?> newFinallyStateProducer;\n\t\tfinal TraverserStateFactory<?> newCandidateStateProducer;\n\t\tfinal TraverserBlockInfo adjustedBlockInfo;\n\t\tif (finallyOverruns) {\n\t\t\t// More finally instructions than candidate instructions\n\t\t\tfinal CentralityState candidateCentralityState = candidateState.getCentralityState().duplicate();\n\t\t\tcandidateCentralityState.setAllowsCentral(false);\n\t\t\tcandidateCentralityState.setAllowsNonStartingNode(false);\n\t\t\tfinal CentralityState finallyCentralityState = finallyState.getCentralityState();\n\t\t\tfinallyCentralityState.setAllowsCentral(false);\n\t\t\tfinallyCentralityState.setAllowsNonStartingNode(false);\n\n\t\t\tinsnsDelta = finallyInsnsSize - maxIterateCount;\n\t\t\tnewFinallyStateProducer = new DuplicatedTraverserStateFactory<>(finallyState);\n\t\t\tadjustedBlockInfo = finallyState.getBlockInsnInfo();\n\t\t\tnewCandidateStateProducer = NoBlockTraverserState.getFactory(candidateCentralityState, candidateBlock);\n\t\t} else {\n\t\t\t// More candidate instructions than finally instructions\n\t\t\tfinal CentralityState finallyCentralityState = finallyState.getCentralityState().duplicate();\n\t\t\tfinallyCentralityState.setAllowsCentral(false);\n\t\t\tfinallyCentralityState.setAllowsNonStartingNode(false);\n\t\t\tfinal CentralityState candidateCentralityState = candidateState.getCentralityState();\n\t\t\tcandidateCentralityState.setAllowsCentral(false);\n\t\t\tcandidateCentralityState.setAllowsNonStartingNode(false);\n\n\t\t\tinsnsDelta = candidateInsnsSize - maxIterateCount;\n\t\t\tcandidateState.getCentralityState().setAllowsCentral(false);\n\t\t\tnewCandidateStateProducer = new DuplicatedTraverserStateFactory<>(candidateState);\n\t\t\tadjustedBlockInfo = candidateState.getBlockInsnInfo();\n\t\t\tnewFinallyStateProducer = NoBlockTraverserState.getFactory(finallyCentralityState, finallyBlock);\n\t\t}\n\t\tadjustedBlockInfo.setBottomOffset(adjustedBlockInfo.getBottomOffset() + insnsDelta);\n\n\t\treturn TraverserActivePathState.produceFromFactories(previousState, newFinallyStateProducer, newCandidateStateProducer);\n\t}\n\n\tprivate static TraverserActivePathState createStateForBlockSkip(final TraverserActivePathState previousState,\n\t\t\tfinal TraverserState finallyState,\n\t\t\tfinal TraverserState candidateState, final BlockNode finallyBlock, final BlockNode candidateBlock) {\n\t\tfinal CentralityState finallyCentralityState = finallyState.getCentralityState();\n\t\tfinal CentralityState candidateCentralityState = candidateState.getCentralityState();\n\n\t\t// TODO: Maybe replace this with controller logic so that we can determine if we need to use these\n\t\t// as path ends and then merge above path?\n\n\t\t// Fix up finally path first. If this continues to fail, check if candidate can be fixed up in a\n\t\t// later iteration.\n\t\tif (finallyCentralityState.getAllowsNonStartingNode()) {\n\t\t\tfinallyCentralityState.setAllowsNonStartingNode(false);\n\t\t\tfinal TraverserStateFactory<NoBlockTraverserState> newFinallyStateProducer =\n\t\t\t\t\tNoBlockTraverserState.getFactory(finallyCentralityState, finallyBlock);\n\t\t\tfinal TraverserStateFactory<?> newCandidateStateProducer = new DuplicatedTraverserStateFactory<>(candidateState);\n\t\t\treturn TraverserActivePathState.produceFromFactories(previousState, newFinallyStateProducer, newCandidateStateProducer);\n\t\t} else {\n\t\t\tcandidateCentralityState.setAllowsNonStartingNode(false);\n\t\t\tfinal TraverserStateFactory<NoBlockTraverserState> newCandidateStateProducer =\n\t\t\t\t\tNoBlockTraverserState.getFactory(candidateCentralityState, candidateBlock);\n\t\t\tfinal TraverserStateFactory<?> newFinallyStateProducer = new DuplicatedTraverserStateFactory<>(finallyState);\n\t\t\treturn TraverserActivePathState.produceFromFactories(previousState, newFinallyStateProducer, newCandidateStateProducer);\n\t\t}\n\t}\n\n\tprivate static TraverserActivePathState createStateForTerminatorState(final TraverserActivePathState previousState) {\n\t\tfinal TraverserStateFactory<TerminalTraverserState> finallyStateProducer =\n\t\t\t\tTerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.NON_MATCHING_INSTRUCTIONS);\n\t\tfinal TraverserStateFactory<TerminalTraverserState> candidateStateProducer =\n\t\t\t\tTerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.NON_MATCHING_INSTRUCTIONS);\n\n\t\treturn TraverserActivePathState.produceFromFactories(previousState, finallyStateProducer, candidateStateProducer);\n\t}\n\n\tprivate final SameInstructionsStrategy sameInstructionsStrategy = new SameInstructionsStrategyImpl();\n\n\t@Override\n\tpublic final TraverserActivePathState visit(final TraverserActivePathState state) {\n\t\tfinal TraverserState finallyState = state.getFinallyState();\n\t\tfinal TraverserState candidateState = state.getCandidateState();\n\n\t\tfinal TraverserBlockInfo finallyBlockInfo = finallyState.getBlockInsnInfo();\n\t\tfinal TraverserBlockInfo candidateBlockInfo = candidateState.getBlockInsnInfo();\n\n\t\tif (finallyBlockInfo == null || candidateBlockInfo == null) {\n\t\t\tthrow new UnsupportedOperationException(\n\t\t\t\t\t\"The instruction comparator handler has received a state which does not support block insn info\");\n\t\t}\n\n\t\tfinal BlockNode finallyBlock = finallyBlockInfo.getBlock();\n\t\tfinal BlockNode candidateBlock = candidateBlockInfo.getBlock();\n\n\t\tfinal List<InsnNode> finallyInsns = finallyBlockInfo.getInsnsSlice();\n\t\tfinal List<InsnNode> candidateInsns = candidateBlockInfo.getInsnsSlice();\n\t\tfinal int finallyInsnsSize = finallyInsns.size();\n\t\tfinal int candidateInsnsSize = candidateInsns.size();\n\n\t\tfinal int maxIterateCount = Math.min(finallyInsnsSize, candidateInsnsSize);\n\n\t\tfinal List<Pair<InsnNode>> matchingInsns = new ArrayList<>(maxIterateCount);\n\n\t\t// Search through each instruction in reverse and see how many match\n\t\tfor (int i = 0; i < maxIterateCount; i++) {\n\t\t\tfinal InsnNode candidateInsn = candidateInsns.get(candidateInsnsSize - i - 1);\n\t\t\tfinal InsnNode finallyInsn = finallyInsns.get(finallyInsnsSize - i - 1);\n\n\t\t\tif (!sameInstructionsStrategy.sameInsns(candidateInsn, finallyInsn)) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tfinal Pair<InsnNode> match = new Pair<>(finallyInsn, candidateInsn);\n\t\t\tmatchingInsns.add(match);\n\t\t}\n\n\t\tfinal int matchedInsnsCount = matchingInsns.size();\n\n\t\tstate.registerWithBlockInfo(finallyBlockInfo, matchedInsnsCount);\n\t\tstate.registerWithBlockInfo(candidateBlockInfo, matchedInsnsCount);\n\n\t\tfinal boolean finallyOverruns = finallyInsnsSize > candidateInsnsSize;\n\t\tfinal boolean candidateOverruns = finallyInsnsSize < candidateInsnsSize;\n\t\tfinal boolean sameSizedSlices = !finallyOverruns && !candidateOverruns;\n\t\tfinal boolean allMatched = matchedInsnsCount == maxIterateCount;\n\t\tfinal boolean noneMatched = matchedInsnsCount == 0;\n\n\t\tstate.getMatchedInsns().addAll(matchingInsns);\n\n\t\tfinal TraverserActivePathState newState;\n\t\tif (allMatched) {\n\t\t\tif (sameSizedSlices) {\n\t\t\t\t// All instructions matched and there are no more instructions to match in either\n\t\t\t\t// block. Continue to the next set of blocks.\n\t\t\t\tnewState = createStateForPerfectMatch(state, finallyBlock, candidateBlock);\n\t\t\t} else {\n\t\t\t\t// All instructions matched, however one block contained more instructions than the\n\t\t\t\t// other. Continue to next set of blocks for the handler whose instructions list was\n\t\t\t\t// fully searched.\n\t\t\t\tnewState = createStateForUnevenMatch(state, finallyState, candidateState, finallyBlock, candidateBlock, finallyInsnsSize,\n\t\t\t\t\t\tcandidateInsnsSize);\n\t\t\t}\n\t\t} else if (noneMatched && eitherStateAllowsBlockSkip(finallyState, candidateState)) {\n\t\t\tnewState = createStateForBlockSkip(state, finallyState, candidateState, finallyBlock, candidateBlock);\n\t\t} else {\n\t\t\t// If any didn't match, this means that the first instructions of the block don't\n\t\t\t// match. This therefore means that no future blocks should be marked as duplicate\n\t\t\t// instructions and thus we should return a terminator state to stop the search.\n\t\t\tnewState = createStateForTerminatorState(state);\n\t\t}\n\n\t\treturn newState;\n\t}\n\n\tprivate boolean eitherStateAllowsBlockSkip(final TraverserState finallyState, final TraverserState candidateState) {\n\t\tfinal CentralityState finallyCentralityState = finallyState.getCentralityState();\n\t\tfinal CentralityState candidateCentralityState = candidateState.getCentralityState();\n\n\t\treturn finallyCentralityState.getAllowsNonStartingNode() || candidateCentralityState.getAllowsNonStartingNode();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/fixaccessmodifiers/FixAccessModifiers.java",
    "content": "package jadx.core.dex.visitors.fixaccessmodifiers;\n\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.MethodInlineAttr;\nimport jadx.core.dex.attributes.nodes.MethodOverrideAttr;\nimport jadx.core.dex.attributes.nodes.NotificationAttrNode;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.dex.visitors.ModVisitor;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"FixAccessModifiers\",\n\t\tdesc = \"Change class and method access modifiers if needed\",\n\t\trunAfter = ModVisitor.class\n)\npublic class FixAccessModifiers extends AbstractVisitor {\n\n\tprivate VisibilityUtils visibilityUtils;\n\n\tprivate boolean respectAccessModifiers;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tthis.visibilityUtils = new VisibilityUtils(root);\n\t\tthis.respectAccessModifiers = root.getArgs().isRespectBytecodeAccModifiers();\n\t}\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) throws JadxException {\n\t\tif (respectAccessModifiers) {\n\t\t\treturn true;\n\t\t}\n\n\t\tfixClassVisibility(cls);\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tif (respectAccessModifiers || mth.contains(AFlag.DONT_GENERATE)) {\n\t\t\treturn;\n\t\t}\n\n\t\tfixMethodVisibility(mth);\n\t}\n\n\tprivate void fixClassVisibility(ClassNode cls) {\n\t\tAccessInfo accessFlags = cls.getAccessFlags();\n\t\tif (cls.isTopClass() && accessFlags.isPublic()) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (cls.isTopClass() && (accessFlags.isPrivate() || accessFlags.isProtected())) {\n\t\t\tchangeVisibility(cls, AccessFlags.PUBLIC);\n\t\t\treturn;\n\t\t}\n\n\t\tfor (ClassNode useCls : cls.getUseIn()) {\n\t\t\tvisibilityUtils.checkVisibility(cls, useCls, (node, visFlag) -> {\n\t\t\t\tchangeVisibility((NotificationAttrNode) node, visFlag);\n\t\t\t});\n\t\t}\n\n\t\tfor (MethodNode useMth : cls.getUseInMth()) {\n\t\t\tMethodInlineAttr inlineAttr = useMth.get(AType.METHOD_INLINE);\n\t\t\tboolean isInline = inlineAttr != null && !inlineAttr.notNeeded();\n\t\t\tboolean isCandidateForInline = useMth.contains(AFlag.METHOD_CANDIDATE_FOR_INLINE);\n\n\t\t\tif (isInline || isCandidateForInline) {\n\t\t\t\tSet<ClassNode> usedInClss = useMth.getUseIn().stream()\n\t\t\t\t\t\t.map(MethodNode::getParentClass)\n\t\t\t\t\t\t.collect(Collectors.toSet());\n\n\t\t\t\tfor (ClassNode useCls : usedInClss) {\n\t\t\t\t\tvisibilityUtils.checkVisibility(cls, useCls, (node, visFlag) -> {\n\t\t\t\t\t\tchangeVisibility((NotificationAttrNode) node, visFlag);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void fixMethodVisibility(MethodNode mth) {\n\t\tAccessInfo accessFlags = mth.getAccessFlags();\n\t\tMethodOverrideAttr overrideAttr = mth.get(AType.METHOD_OVERRIDE);\n\t\tif (overrideAttr != null && !overrideAttr.getOverrideList().isEmpty()) {\n\t\t\t// visibility can't be weaker\n\t\t\tIMethodDetails parentMD = overrideAttr.getOverrideList().get(0);\n\t\t\tAccessInfo parentAccInfo = new AccessInfo(parentMD.getRawAccessFlags(), AccessInfo.AFType.METHOD);\n\t\t\tif (accessFlags.isVisibilityWeakerThan(parentAccInfo)) {\n\t\t\t\tchangeVisibility(mth, parentAccInfo.getVisibility().rawValue());\n\t\t\t}\n\t\t}\n\n\t\tfor (MethodNode useMth : mth.getUseIn()) {\n\t\t\tvisibilityUtils.checkVisibility(mth, useMth, (node, visFlag) -> {\n\t\t\t\tchangeVisibility((NotificationAttrNode) node, visFlag);\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic static void changeVisibility(NotificationAttrNode node, int newVisFlag) {\n\t\tAccessInfo accessFlags = node.getAccessFlags();\n\t\tAccessInfo newAccFlags = accessFlags.changeVisibility(newVisFlag);\n\t\tif (newAccFlags != accessFlags) {\n\t\t\tnode.setAccessFlags(newAccFlags);\n\t\t\tnode.addInfoComment(\"Access modifiers changed from: \" + accessFlags.visibilityName());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/fixaccessmodifiers/VisibilityUtils.java",
    "content": "package jadx.core.dex.visitors.fixaccessmodifiers;\n\nimport java.util.function.Consumer;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.ICodeNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nclass VisibilityUtils {\n\n\tprivate final RootNode root;\n\n\tVisibilityUtils(RootNode rootNode) {\n\t\tthis.root = rootNode;\n\t}\n\n\tvoid checkVisibility(ICodeNode targetNode, ICodeNode callerNode, OnBadVisibilityCallback callback) {\n\t\tClassNode targetCls = targetNode instanceof ClassNode\n\t\t\t\t? (ClassNode) targetNode\n\t\t\t\t: targetNode.getDeclaringClass();\n\n\t\tClassNode callerCls = callerNode instanceof ClassNode\n\t\t\t\t? (ClassNode) callerNode\n\t\t\t\t: callerNode.getDeclaringClass();\n\n\t\tif (targetCls.equals(callerCls) || inSameTopClass(targetCls, callerCls)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (inSamePkg(targetCls, callerCls)) {\n\t\t\tvisitDeclaringNodes(targetNode, node -> {\n\t\t\t\tif (node.getAccessFlags().isPrivate()) {\n\t\t\t\t\tcallback.onBadVisibility(node, 0); // PACKAGE_PRIVATE\n\t\t\t\t}\n\t\t\t});\n\t\t} else {\n\t\t\tvisitDeclaringNodes(targetNode, node -> {\n\t\t\t\tAccessInfo nodeVisFlags = node.getAccessFlags().getVisibility();\n\t\t\t\tif (nodeVisFlags.isPublic()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (nodeVisFlags.isPrivate() || nodeVisFlags.isPackagePrivate()) {\n\t\t\t\t\tClassNode nodeDeclaringCls = node.getDeclaringClass();\n\t\t\t\t\tint expectedVisFlag = nodeDeclaringCls != null && isSuperType(callerCls, nodeDeclaringCls)\n\t\t\t\t\t\t\t? AccessFlags.PROTECTED\n\t\t\t\t\t\t\t: AccessFlags.PUBLIC;\n\n\t\t\t\t\tcallback.onBadVisibility(node, expectedVisFlag);\n\t\t\t\t} else if (nodeVisFlags.isProtected()) {\n\t\t\t\t\tClassNode nodeDeclaringCls = node.getDeclaringClass();\n\t\t\t\t\tif (nodeDeclaringCls == null || !isSuperType(callerCls, nodeDeclaringCls)) {\n\t\t\t\t\t\tcallback.onBadVisibility(node, AccessFlags.PUBLIC);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthrow new JadxRuntimeException(nodeVisFlags + \" is not supported\");\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate static void visitDeclaringNodes(ICodeNode targetNode, Consumer<ICodeNode> action) {\n\t\tICodeNode currentNode = targetNode;\n\t\tdo {\n\t\t\taction.accept(currentNode);\n\t\t\tcurrentNode = currentNode.getDeclaringClass();\n\t\t} while (currentNode != null);\n\t}\n\n\tprivate static boolean inSamePkg(ClassNode cls1, ClassNode cls2) {\n\t\treturn cls1.getPackageNode().equals(cls2.getPackageNode());\n\t}\n\n\tprivate static boolean inSameTopClass(ClassNode cls1, ClassNode cls2) {\n\t\treturn cls1.getTopParentClass().equals(cls2.getTopParentClass());\n\t}\n\n\tprivate boolean isSuperType(ClassNode cls, ClassNode superCls) {\n\t\treturn root.getClsp().getSuperTypes(cls.getRawName()).stream()\n\t\t\t\t.anyMatch(x -> x.equals(superCls.getRawName()));\n\t}\n\n\tinterface OnBadVisibilityCallback {\n\t\tvoid onBadVisibility(ICodeNode node, int expectedVisFlag);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/gradle/NonFinalResIdsVisitor.java",
    "content": "package jadx.core.dex.visitors.gradle;\n\nimport java.util.Map;\n\nimport jadx.api.plugins.input.data.annotations.AnnotationVisibility;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationsAttr;\nimport jadx.core.dex.attributes.nodes.CodeFeaturesAttr;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.IFieldInfoRef;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.regions.SwitchRegion;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.FixSwitchOverEnum;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.dex.visitors.regions.DepthRegionTraversal;\nimport jadx.core.dex.visitors.regions.IRegionIterativeVisitor;\nimport jadx.core.export.GradleInfoStorage;\nimport jadx.core.utils.android.AndroidResourcesUtils;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"NonFinalResIdsVisitor\",\n\t\tdesc = \"Detect usage of android resource constants in cases where constant expressions are required.\",\n\t\trunAfter = FixSwitchOverEnum.class\n)\npublic class NonFinalResIdsVisitor extends AbstractVisitor implements IRegionIterativeVisitor {\n\n\tprivate boolean nonFinalResIdsFlagRequired = false;\n\n\tprivate GradleInfoStorage gradleInfoStorage;\n\n\tpublic void init(RootNode root) throws JadxException {\n\t\tgradleInfoStorage = root.getGradleInfoStorage();\n\t}\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) throws JadxException {\n\t\tif (nonFinalResIdsFlagRequired) {\n\t\t\treturn false;\n\t\t}\n\t\tAnnotationsAttr annotationsList = cls.get(JadxAttrType.ANNOTATION_LIST);\n\t\tif (visitAnnotationList(annotationsList)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn super.visit(cls);\n\t}\n\n\tprivate static boolean isCustomResourceClass(ClassInfo cls) {\n\t\tClassInfo parentClass = cls.getParentClass();\n\t\treturn parentClass != null && parentClass.getShortName().equals(\"R\") && !parentClass.getFullName().equals(\"android.R\");\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tAnnotationsAttr annotationsList = mth.get(JadxAttrType.ANNOTATION_LIST);\n\t\tif (visitAnnotationList(annotationsList)) {\n\t\t\tnonFinalResIdsFlagRequired = true;\n\t\t\treturn;\n\t\t}\n\n\t\tif (nonFinalResIdsFlagRequired || !CodeFeaturesAttr.contains(mth, CodeFeaturesAttr.CodeFeature.SWITCH)) {\n\t\t\treturn;\n\t\t}\n\t\tDepthRegionTraversal.traverseIterative(mth, this);\n\t}\n\n\tprivate boolean visitAnnotationList(AnnotationsAttr annotationsList) {\n\t\tif (annotationsList != null) {\n\t\t\tfor (IAnnotation annotation : annotationsList.getAll()) {\n\t\t\t\tif (annotation.getVisibility() == AnnotationVisibility.SYSTEM) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tfor (Map.Entry<String, EncodedValue> entry : annotation.getValues().entrySet()) {\n\t\t\t\t\tObject value = entry.getValue().getValue();\n\t\t\t\t\tif (value instanceof IFieldInfoRef && isCustomResourceClass(((IFieldInfoRef) value).getFieldInfo().getDeclClass())) {\n\t\t\t\t\t\tgradleInfoStorage.setNonFinalResIds(true);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean visitRegion(MethodNode mth, IRegion region) {\n\t\tif (nonFinalResIdsFlagRequired) {\n\t\t\treturn false;\n\t\t}\n\t\tif (region instanceof SwitchRegion) {\n\t\t\treturn detectSwitchOverResIds((SwitchRegion) region);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate boolean detectSwitchOverResIds(SwitchRegion switchRegion) {\n\t\tfor (SwitchRegion.CaseInfo caseInfo : switchRegion.getCases()) {\n\t\t\tfor (Object key : caseInfo.getKeys()) {\n\t\t\t\tif (key instanceof FieldNode) {\n\t\t\t\t\tClassNode topParentClass = ((FieldNode) key).getTopParentClass();\n\t\t\t\t\tif (AndroidResourcesUtils.isResourceClass(topParentClass) && !\"android.R\".equals(topParentClass.getFullName())) {\n\t\t\t\t\t\tthis.nonFinalResIdsFlagRequired = true;\n\t\t\t\t\t\tgradleInfoStorage.setNonFinalResIds(true);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/kotlin/ProcessKotlinInternals.java",
    "content": "package jadx.core.dex.visitors.kotlin;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxArgs.UseKotlinMethodsForVarNames;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.core.deobf.NameMapper;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.ConstStringNode;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.InitCodeVariables;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.dex.visitors.debuginfo.DebugInfoApplyVisitor;\nimport jadx.core.dex.visitors.rename.CodeRenameVisitor;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"ProcessKotlinInternals\",\n\t\tdesc = \"Use variable names from Kotlin intrinsic1 methods\",\n\t\trunAfter = {\n\t\t\t\tInitCodeVariables.class,\n\t\t\t\tDebugInfoApplyVisitor.class\n\t\t},\n\t\trunBefore = {\n\t\t\t\tCodeRenameVisitor.class\n\t\t}\n)\npublic class ProcessKotlinInternals extends AbstractVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ProcessKotlinInternals.class);\n\n\tprivate static final String KOTLIN_INTERNAL_PKG = \"kotlin.jvm.internal.\";\n\tprivate static final String KOTLIN_INTRINSICS_CLS_SHORT_NAME = \"Intrinsics\";\n\tprivate static final String KOTLIN_INTRINSICS_CLS = KOTLIN_INTERNAL_PKG + KOTLIN_INTRINSICS_CLS_SHORT_NAME;\n\tprivate static final String KOTLIN_VARNAME_SOURCE_MTH1 = \"(Ljava/lang/Object;Ljava/lang/String;)V\";\n\tprivate static final String KOTLIN_VARNAME_SOURCE_MTH2 = \"(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V\";\n\n\tprivate @Nullable ClassInfo kotlinIntrinsicsCls;\n\tprivate Set<MethodInfo> kotlinVarNameSourceMethods;\n\tprivate boolean hideInsns;\n\n\t@Override\n\tpublic void init(RootNode root) throws JadxException {\n\t\tClassNode kotlinCls = searchKotlinIntrinsicsClass(root);\n\t\tif (kotlinCls != null) {\n\t\t\tkotlinIntrinsicsCls = kotlinCls.getClassInfo();\n\t\t\tkotlinVarNameSourceMethods = collectMethods(kotlinCls);\n\t\t\tLOG.debug(\"Kotlin Intrinsics class: {}, methods: {}\", kotlinCls, kotlinVarNameSourceMethods.size());\n\t\t} else {\n\t\t\tkotlinIntrinsicsCls = null;\n\t\t\tLOG.debug(\"Kotlin Intrinsics class not found\");\n\t\t}\n\t\thideInsns = root.getArgs().getUseKotlinMethodsForVarNames() == UseKotlinMethodsForVarNames.APPLY_AND_HIDE;\n\t}\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) {\n\t\tif (kotlinIntrinsicsCls == null) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\tprocessMth(mth);\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate void processMth(MethodNode mth) {\n\t\tif (mth.isNoCode() || mth.contains(AType.JADX_ERROR)) {\n\t\t\treturn;\n\t\t}\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tif (insn.getType() == InsnType.INVOKE) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tprocessInvoke(mth, insn);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tmth.addWarnComment(\"Failed to extract var names\", e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void processInvoke(MethodNode mth, InsnNode insn) {\n\t\tint argsCount = insn.getArgsCount();\n\t\tif (argsCount < 2) {\n\t\t\treturn;\n\t\t}\n\t\tMethodInfo invokeMth = ((InvokeNode) insn).getCallMth();\n\t\tif (!kotlinVarNameSourceMethods.contains(invokeMth)) {\n\t\t\treturn;\n\t\t}\n\t\tInsnArg firstArg = insn.getArg(0);\n\t\tif (!firstArg.isRegister()) {\n\t\t\treturn;\n\t\t}\n\t\tRegisterArg varArg = (RegisterArg) firstArg;\n\t\tboolean renamed = false;\n\t\tif (argsCount == 2) {\n\t\t\tString str = getConstString(mth, insn, 1);\n\t\t\tif (str != null) {\n\t\t\t\trenamed = checkAndRename(varArg, str);\n\t\t\t}\n\t\t} else if (argsCount == 3) {\n\t\t\t// TODO: use second arg for rename class\n\t\t\tString str = getConstString(mth, insn, 2);\n\t\t\tif (str != null) {\n\t\t\t\trenamed = checkAndRename(varArg, str);\n\t\t\t}\n\t\t}\n\t\tif (renamed && hideInsns) {\n\t\t\tinsn.add(AFlag.DONT_GENERATE);\n\t\t}\n\t}\n\n\tprivate boolean checkAndRename(RegisterArg arg, String str) {\n\t\tString name = trimName(str);\n\t\tif (NameMapper.isValidAndPrintable(name)) {\n\t\t\targ.getSVar().getCodeVar().setName(name);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Nullable\n\tprivate String getConstString(MethodNode mth, InsnNode insn, int arg) {\n\t\tInsnArg strArg = insn.getArg(arg);\n\t\tif (!strArg.isInsnWrap()) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnNode constInsn = ((InsnWrapArg) strArg).getWrapInsn();\n\t\tInsnType insnType = constInsn.getType();\n\t\tif (insnType == InsnType.CONST_STR) {\n\t\t\treturn ((ConstStringNode) constInsn).getString();\n\t\t}\n\t\tif (insnType == InsnType.SGET) {\n\t\t\t// revert const field inline :(\n\t\t\tFieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) constInsn).getIndex();\n\t\t\tFieldNode fieldNode = mth.root().resolveField(fieldInfo);\n\t\t\tif (fieldNode != null) {\n\t\t\t\tString str = (String) fieldNode.get(JadxAttrType.CONSTANT_VALUE).getValue();\n\t\t\t\tInsnArg newArg = InsnArg.wrapArg(new ConstStringNode(str));\n\t\t\t\tinsn.replaceArg(strArg, newArg);\n\t\t\t\treturn str;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String trimName(String str) {\n\t\tif (str.startsWith(\"$this$\")) {\n\t\t\treturn str.substring(6);\n\t\t}\n\t\tif (str.startsWith(\"$\")) {\n\t\t\treturn str.substring(1);\n\t\t}\n\t\treturn str;\n\t}\n\n\t@Nullable\n\tprivate static ClassNode searchKotlinIntrinsicsClass(RootNode root) {\n\t\tClassNode kotlinCls = root.resolveClass(KOTLIN_INTRINSICS_CLS);\n\t\tif (kotlinCls != null) {\n\t\t\treturn kotlinCls;\n\t\t}\n\t\tList<ClassNode> candidates = new ArrayList<>();\n\t\tfor (ClassNode cls : root.getClasses()) {\n\t\t\tif (isKotlinIntrinsicsClass(cls)) {\n\t\t\t\tcandidates.add(cls);\n\t\t\t}\n\t\t}\n\t\treturn Utils.getOne(candidates);\n\t}\n\n\tprivate static boolean isKotlinIntrinsicsClass(ClassNode cls) {\n\t\tClassInfo clsInfo = cls.getClassInfo();\n\t\tif (clsInfo.getAliasShortName().equals(KOTLIN_INTRINSICS_CLS_SHORT_NAME)\n\t\t\t\t&& clsInfo.getAliasFullName().equals(KOTLIN_INTRINSICS_CLS)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!clsInfo.getFullName().startsWith(KOTLIN_INTERNAL_PKG)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (cls.getMethods().size() < 5) {\n\t\t\treturn false;\n\t\t}\n\t\tint mthCount = 0;\n\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\tif (mth.getAccessFlags().isStatic()\n\t\t\t\t\t&& mth.getMethodInfo().getShortId().endsWith(KOTLIN_VARNAME_SOURCE_MTH1)) {\n\t\t\t\tmthCount++;\n\t\t\t}\n\t\t}\n\t\treturn mthCount > 2;\n\t}\n\n\tprivate Set<MethodInfo> collectMethods(ClassNode kotlinCls) {\n\t\tSet<MethodInfo> set = new HashSet<>();\n\t\tfor (MethodNode mth : kotlinCls.getMethods()) {\n\t\t\tif (!mth.getAccessFlags().isStatic()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString shortId = mth.getMethodInfo().getShortId();\n\t\t\tif (shortId.endsWith(KOTLIN_VARNAME_SOURCE_MTH1) || shortId.endsWith(KOTLIN_VARNAME_SOURCE_MTH2)) {\n\t\t\t\tset.add(mth.getMethodInfo());\n\t\t\t}\n\t\t}\n\t\treturn set;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/methods/MutableMethodDetails.java",
    "content": "package jadx.core.dex.visitors.methods;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.IMethodDetails;\n\npublic class MutableMethodDetails implements IMethodDetails {\n\n\tprivate final MethodInfo mthInfo;\n\tprivate ArgType retType;\n\tprivate List<ArgType> argTypes;\n\tprivate List<ArgType> typeParams;\n\tprivate List<ArgType> throwTypes;\n\tprivate boolean varArg;\n\tprivate int accFlags;\n\n\tpublic MutableMethodDetails(IMethodDetails base) {\n\t\tthis.mthInfo = base.getMethodInfo();\n\t\tthis.retType = base.getReturnType();\n\t\tthis.argTypes = Collections.unmodifiableList(base.getArgTypes());\n\t\tthis.typeParams = Collections.unmodifiableList(base.getTypeParameters());\n\t\tthis.throwTypes = Collections.unmodifiableList(base.getThrows());\n\t\tthis.varArg = base.isVarArg();\n\t\tthis.accFlags = base.getRawAccessFlags();\n\t}\n\n\t@Override\n\tpublic MethodInfo getMethodInfo() {\n\t\treturn mthInfo;\n\t}\n\n\t@Override\n\tpublic ArgType getReturnType() {\n\t\treturn retType;\n\t}\n\n\t@Override\n\tpublic List<ArgType> getArgTypes() {\n\t\treturn argTypes;\n\t}\n\n\t@Override\n\tpublic List<ArgType> getTypeParameters() {\n\t\treturn typeParams;\n\t}\n\n\t@Override\n\tpublic List<ArgType> getThrows() {\n\t\treturn throwTypes;\n\t}\n\n\t@Override\n\tpublic boolean isVarArg() {\n\t\treturn varArg;\n\t}\n\n\tpublic void setRetType(ArgType retType) {\n\t\tthis.retType = retType;\n\t}\n\n\tpublic void setArgTypes(List<ArgType> argTypes) {\n\t\tthis.argTypes = argTypes;\n\t}\n\n\tpublic void setTypeParams(List<ArgType> typeParams) {\n\t\tthis.typeParams = typeParams;\n\t}\n\n\tpublic void setThrowTypes(List<ArgType> throwTypes) {\n\t\tthis.throwTypes = throwTypes;\n\t}\n\n\tpublic void setVarArg(boolean varArg) {\n\t\tthis.varArg = varArg;\n\t}\n\n\t@Override\n\tpublic int getRawAccessFlags() {\n\t\treturn accFlags;\n\t}\n\n\tpublic void setRawAccessFlags(int accFlags) {\n\t\tthis.accFlags = accFlags;\n\t}\n\n\t@Override\n\tpublic String toAttrString() {\n\t\treturn IMethodDetails.super.toAttrString() + \" (mut)\";\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Mutable\" + toAttrString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/prepare/AddAndroidConstants.java",
    "content": "package jadx.core.dex.visitors.prepare;\n\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.ConstStorage;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.utils.android.AndroidResourcesMap;\nimport jadx.core.utils.exceptions.JadxException;\n\n// TODO: move this pass to separate \"Android plugin\"\n@JadxVisitor(\n\t\tname = \"AddAndroidConstants\",\n\t\tdesc = \"Insert Android constants from resource mapping file\",\n\t\trunBefore = {\n\t\t\t\tCollectConstValues.class\n\t\t}\n)\npublic class AddAndroidConstants extends AbstractVisitor {\n\n\tprivate static final String R_CLS = \"android.R\";\n\tprivate static final String R_INNER_CLS = R_CLS + '$';\n\n\t@Override\n\tpublic void init(RootNode root) throws JadxException {\n\t\tif (!root.getArgs().isReplaceConsts()) {\n\t\t\treturn;\n\t\t}\n\t\tif (root.resolveClass(R_CLS) != null) {\n\t\t\t// Android R class already loaded\n\t\t\treturn;\n\t\t}\n\t\tConstStorage constStorage = root.getConstValues();\n\t\tAndroidResourcesMap.getMap().forEach((resId, path) -> {\n\t\t\tint sep = path.indexOf('/');\n\t\t\tString clsName = R_INNER_CLS + path.substring(0, sep);\n\t\t\tString resName = path.substring(sep + 1);\n\t\t\tClassInfo cls = ClassInfo.fromName(root, clsName);\n\t\t\tFieldInfo field = FieldInfo.from(root, cls, resName, ArgType.INT);\n\t\t\tconstStorage.addGlobalConstField(field, resId);\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/prepare/CollectConstValues.java",
    "content": "package jadx.core.dex.visitors.prepare;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.info.ConstStorage;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.dex.visitors.usage.UsageInfoVisitor;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"CollectConstValues\",\n\t\tdesc = \"Collect and store values from static final fields\",\n\t\trunAfter = {\n\t\t\t\tUsageInfoVisitor.class // check field usage (do not restore if used somewhere)\n\t\t}\n)\npublic class CollectConstValues extends AbstractVisitor {\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) throws JadxException {\n\t\tRootNode root = cls.root();\n\t\tif (!root.getArgs().isReplaceConsts()) {\n\t\t\treturn true;\n\t\t}\n\t\tif (cls.getFields().isEmpty()) {\n\t\t\treturn true;\n\t\t}\n\t\tConstStorage constStorage = root.getConstValues();\n\t\tfor (FieldNode fld : cls.getFields()) {\n\t\t\ttry {\n\t\t\t\tObject value = getFieldConstValue(fld);\n\t\t\t\tif (value != null) {\n\t\t\t\t\tconstStorage.addConstField(fld, value, fld.getAccessFlags().isPublic());\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tcls.addWarnComment(\"Failed to process value of field: \" + fld, e);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static @Nullable Object getFieldConstValue(FieldNode fld) {\n\t\tAccessInfo accFlags = fld.getAccessFlags();\n\t\tif (!accFlags.isStatic() || !accFlags.isFinal()) {\n\t\t\treturn null;\n\t\t}\n\t\tEncodedValue constVal = fld.get(JadxAttrType.CONSTANT_VALUE);\n\t\tif (constVal == null || constVal == EncodedValue.NULL) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!fld.getUseIn().isEmpty()) {\n\t\t\t// field still used somewhere and not inlined by compiler, so we don't need to restore it\n\t\t\treturn null;\n\t\t}\n\t\treturn constVal.getValue();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/AbstractRegionVisitor.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic abstract class AbstractRegionVisitor implements IRegionVisitor {\n\n\t@Override\n\tpublic boolean enterRegion(MethodNode mth, IRegion region) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void processBlock(MethodNode mth, IBlock block) {\n\t}\n\n\t@Override\n\tpublic void leaveRegion(MethodNode mth, IRegion region) {\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/CheckRegions.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeWriter;\nimport jadx.api.impl.SimpleCodeWriter;\nimport jadx.core.Consts;\nimport jadx.core.codegen.InsnGen;\nimport jadx.core.codegen.MethodGen;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.loops.LoopRegion;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.utils.exceptions.CodegenException;\nimport jadx.core.utils.exceptions.JadxException;\n\npublic class CheckRegions extends AbstractVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CheckRegions.class);\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (mth.isNoCode()\n\t\t\t\t|| mth.getRegion() == null\n\t\t\t\t|| mth.getBasicBlocks().isEmpty()\n\t\t\t\t|| mth.contains(AType.JADX_ERROR)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// check if all blocks included in regions\n\t\tSet<BlockNode> blocksInRegions = new HashSet<>();\n\t\tDepthRegionTraversal.traverse(mth, new AbstractRegionVisitor() {\n\t\t\t@Override\n\t\t\tpublic void processBlock(MethodNode mth, IBlock container) {\n\t\t\t\tif (!(container instanceof BlockNode)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tBlockNode block = (BlockNode) container;\n\t\t\t\tif (blocksInRegions.add(block)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (Consts.DEBUG_RESTRUCTURE\n\t\t\t\t\t\t&& LOG.isDebugEnabled()\n\t\t\t\t\t\t&& !block.contains(AFlag.RETURN)\n\t\t\t\t\t\t&& !block.contains(AFlag.REMOVE)\n\t\t\t\t\t\t&& !block.contains(AFlag.SYNTHETIC)\n\t\t\t\t\t\t&& !block.getInstructions().isEmpty()) {\n\t\t\t\t\tLOG.debug(\"Duplicated block: {} - {}\", mth, block);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tif (mth.getBasicBlocks().size() != blocksInRegions.size()) {\n\t\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\t\tif (!blocksInRegions.contains(block)\n\t\t\t\t\t\t&& !block.getInstructions().isEmpty()\n\t\t\t\t\t\t&& !block.contains(AFlag.ADDED_TO_REGION)\n\t\t\t\t\t\t&& !block.contains(AFlag.DONT_GENERATE)\n\t\t\t\t\t\t&& !block.contains(AFlag.REMOVE)) {\n\t\t\t\t\tString blockCode = getBlockInsnStr(mth, block).replace(\"*/\", \"*\\\\/\");\n\t\t\t\t\tmth.addWarn(\"Code restructure failed: missing block: \" + block + \", code lost:\" + blockCode);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tDepthRegionTraversal.traverse(mth, new AbstractRegionVisitor() {\n\t\t\t@Override\n\t\t\tpublic boolean enterRegion(MethodNode mth, IRegion region) {\n\t\t\t\tif (region instanceof LoopRegion) {\n\t\t\t\t\t// check loop conditions\n\t\t\t\t\tBlockNode loopHeader = ((LoopRegion) region).getHeader();\n\t\t\t\t\tif (loopHeader != null && !loopHeader.contains(AFlag.ALLOW_MULTIPLE_INSNS_LOOP_COND)\n\t\t\t\t\t\t\t&& loopHeader.getInstructions().size() != 1) {\n\t\t\t\t\t\tmth.addWarn(\"Incorrect condition in loop: \" + loopHeader);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate static String getBlockInsnStr(MethodNode mth, IBlock block) {\n\t\tICodeWriter code = new SimpleCodeWriter();\n\t\tcode.incIndent();\n\t\tcode.newLine();\n\t\tMethodGen mg = MethodGen.getFallbackMethodGen(mth);\n\t\tInsnGen ig = new InsnGen(mg, true);\n\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\ttry {\n\t\t\t\tig.makeInsn(insn, code);\n\t\t\t} catch (CodegenException e) {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t}\n\t\tcode.newLine();\n\t\treturn code.getCodeStr();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/CleanRegions.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport java.util.List;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.Region;\nimport jadx.core.dex.regions.loops.LoopRegion;\nimport jadx.core.dex.visitors.AbstractVisitor;\n\npublic class CleanRegions extends AbstractVisitor {\n\tprivate static final IRegionVisitor REMOVE_REGION_VISITOR = new RemoveRegionVisitor();\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tprocess(mth);\n\t}\n\n\tpublic static void process(MethodNode mth) {\n\t\tif (mth.isNoCode() || mth.getBasicBlocks().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tDepthRegionTraversal.traverse(mth, REMOVE_REGION_VISITOR);\n\t}\n\n\tprivate static class RemoveRegionVisitor extends AbstractRegionVisitor {\n\t\t@Override\n\t\tpublic boolean enterRegion(MethodNode mth, IRegion region) {\n\t\t\tif (region instanceof Region) {\n\t\t\t\tregion.getSubBlocks().removeIf(RemoveRegionVisitor::canRemoveRegion);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate static boolean canRemoveRegion(IContainer container) {\n\t\t\tif (container.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (container instanceof BlockNode) {\n\t\t\t\tBlockNode block = (BlockNode) container;\n\t\t\t\treturn block.getInstructions().isEmpty();\n\t\t\t}\n\t\t\tif (container instanceof LoopRegion) {\n\t\t\t\tLoopRegion loopRegion = (LoopRegion) container;\n\t\t\t\tif (loopRegion.isEndless()) {\n\t\t\t\t\t// keep empty endless loops\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (container instanceof IRegion) {\n\t\t\t\tList<IContainer> subBlocks = ((IRegion) container).getSubBlocks();\n\t\t\t\tfor (IContainer subBlock : subBlocks) {\n\t\t\t\t\tif (!canRemoveRegion(subBlock)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/DebugRegionCounter.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\n\npublic class DebugRegionCounter extends AbstractVisitor {\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tRegionCounterVisitor visitor = new RegionCounterVisitor();\n\t\tDepthRegionTraversal.traverse(mth, visitor);\n\t\tList<BlockDepthEntry> sortedBlocks = visitor.getSortedEntries();\n\t\tfor (BlockDepthEntry x : sortedBlocks) {\n\t\t\tSystem.out.println(x.depth + \" : \" + x.block.toString() + \" // \" + x.block.getInstructions().toString());\n\t\t}\n\n\t\tSystem.out.println(\"nregions :: \" + visitor.getNRegions());\n\t}\n\n\tprivate static class RegionCounterVisitor extends AbstractRegionVisitor {\n\t\tprivate int depth = 0;\n\t\tprivate int nregions = 0;\n\t\tprivate List<BlockDepthEntry> blockDepths = new ArrayList<>();\n\n\t\t@Override\n\t\tpublic boolean enterRegion(MethodNode mth, IRegion region) {\n\t\t\tdepth += 1;\n\t\t\tnregions += 1;\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic void processBlock(MethodNode mth, IBlock container) {\n\t\t\tif (container instanceof BlockNode) {\n\t\t\t\tBlockNode b = (BlockNode) container;\n\t\t\t\tblockDepths.add(new BlockDepthEntry(depth, b));\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void leaveRegion(MethodNode mth, IRegion region) {\n\t\t\tdepth -= 1;\n\t\t}\n\n\t\tpublic List<BlockDepthEntry> getSortedEntries() {\n\t\t\tblockDepths.sort(Comparator.comparingInt(x -> x.depth));\n\t\t\treturn blockDepths;\n\t\t}\n\n\t\tpublic int getNRegions() {\n\t\t\treturn nregions;\n\t\t}\n\n\t}\n\n\tprivate static class BlockDepthEntry {\n\t\tpublic int depth;\n\t\tpublic BlockNode block;\n\n\t\tpublic BlockDepthEntry(int depth, BlockNode block) {\n\t\t\tthis.depth = depth;\n\t\t\tthis.block = block;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/DepthRegionTraversal.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.utils.exceptions.JadxOverflowException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class DepthRegionTraversal {\n\n\tprivate static final int ITERATIVE_LIMIT_MULTIPLIER = 5;\n\n\tprivate DepthRegionTraversal() {\n\t}\n\n\tpublic static void traverse(MethodNode mth, IRegionVisitor visitor) {\n\t\ttraverseInternal(mth, visitor, mth.getRegion());\n\t}\n\n\tpublic static void traverse(MethodNode mth, IContainer container, IRegionVisitor visitor) {\n\t\ttraverseInternal(mth, visitor, container);\n\t}\n\n\tpublic static void traverseIterative(MethodNode mth, IRegionIterativeVisitor visitor) {\n\t\tboolean repeat;\n\t\tint k = 0;\n\t\tint limit = ITERATIVE_LIMIT_MULTIPLIER * mth.getBasicBlocks().size();\n\t\tdo {\n\t\t\trepeat = traverseIterativeStepInternal(mth, visitor, mth.getRegion());\n\t\t\tif (k++ > limit) {\n\t\t\t\tthrow new JadxRuntimeException(\"Iterative traversal limit reached: \"\n\t\t\t\t\t\t+ \"limit: \" + limit + \", visitor: \" + visitor.getClass().getName()\n\t\t\t\t\t\t+ \", blocks count: \" + mth.getBasicBlocks().size());\n\t\t\t}\n\t\t} while (repeat);\n\t}\n\n\tpublic static void traverseIncludingExcHandlers(MethodNode mth, IRegionIterativeVisitor visitor) {\n\t\tboolean repeat;\n\t\tint k = 0;\n\t\tint limit = ITERATIVE_LIMIT_MULTIPLIER * mth.getBasicBlocks().size();\n\t\tdo {\n\t\t\trepeat = traverseIterativeStepInternal(mth, visitor, mth.getRegion());\n\t\t\tif (!repeat) {\n\t\t\t\tfor (ExceptionHandler h : mth.getExceptionHandlers()) {\n\t\t\t\t\trepeat = traverseIterativeStepInternal(mth, visitor, h.getHandlerRegion());\n\t\t\t\t\tif (repeat) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (k++ > limit) {\n\t\t\t\tthrow new JadxRuntimeException(\"Iterative traversal limit reached: \"\n\t\t\t\t\t\t+ \"limit: \" + limit + \", visitor: \" + visitor.getClass().getName()\n\t\t\t\t\t\t+ \", blocks count: \" + mth.getBasicBlocks().size());\n\t\t\t}\n\t\t} while (repeat);\n\t}\n\n\tprivate static void traverseInternal(MethodNode mth, IRegionVisitor visitor, IContainer container) {\n\t\tif (container instanceof IBlock) {\n\t\t\tvisitor.processBlock(mth, (IBlock) container);\n\t\t} else if (container instanceof IRegion) {\n\t\t\tIRegion region = (IRegion) container;\n\t\t\tif (visitor.enterRegion(mth, region)) {\n\t\t\t\tregion.getSubBlocks().forEach(subCont -> traverseInternal(mth, visitor, subCont));\n\t\t\t}\n\t\t\tvisitor.leaveRegion(mth, region);\n\t\t}\n\t}\n\n\tprivate static boolean traverseIterativeStepInternal(MethodNode mth, IRegionIterativeVisitor visitor, IContainer container) {\n\t\tif (container instanceof IRegion) {\n\t\t\tIRegion region = (IRegion) container;\n\t\t\tif (visitor.visitRegion(mth, region)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tfor (IContainer subCont : region.getSubBlocks()) {\n\t\t\t\ttry {\n\t\t\t\t\tif (traverseIterativeStepInternal(mth, visitor, subCont)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t} catch (StackOverflowError overflow) {\n\t\t\t\t\tthrow new JadxOverflowException(\"Region traversal failed: Recursive call in traverseIterativeStepInternal method\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/IRegionIterativeVisitor.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic interface IRegionIterativeVisitor {\n\n\t/**\n\t * If return 'true' traversal will be restarted.\n\t */\n\tboolean visitRegion(MethodNode mth, IRegion region);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/IRegionVisitor.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic interface IRegionVisitor {\n\n\tvoid processBlock(MethodNode mth, IBlock container);\n\n\t/**\n\t * @return true for traverse sub-blocks, false otherwise.\n\t */\n\tboolean enterRegion(MethodNode mth, IRegion region);\n\n\tvoid leaveRegion(MethodNode mth, IRegion region);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfRegionVisitor.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport java.util.List;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.Region;\nimport jadx.core.dex.regions.conditions.IfCondition;\nimport jadx.core.dex.regions.conditions.IfCondition.Mode;\nimport jadx.core.dex.regions.conditions.IfRegion;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.RegionUtils;\n\nimport static jadx.core.utils.RegionUtils.insnsCount;\n\npublic class IfRegionVisitor extends AbstractVisitor {\n\tprivate static final ProcessIfRegionVisitor PROCESS_IF_REGION_VISITOR = new ProcessIfRegionVisitor();\n\tprivate static final RemoveRedundantElseVisitor REMOVE_REDUNDANT_ELSE_VISITOR = new RemoveRedundantElseVisitor();\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tprocess(mth);\n\t}\n\n\tpublic static void processIfRequested(MethodNode mth) {\n\t\tif (mth.contains(AFlag.REQUEST_IF_REGION_OPTIMIZE)) {\n\t\t\ttry {\n\t\t\t\tprocess(mth);\n\t\t\t} finally {\n\t\t\t\tmth.remove(AFlag.REQUEST_IF_REGION_OPTIMIZE);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void process(MethodNode mth) {\n\t\tTernaryMod.process(mth);\n\t\tDepthRegionTraversal.traverse(mth, PROCESS_IF_REGION_VISITOR);\n\t\tDepthRegionTraversal.traverseIterative(mth, REMOVE_REDUNDANT_ELSE_VISITOR);\n\t}\n\n\tprivate static class ProcessIfRegionVisitor extends AbstractRegionVisitor {\n\t\t@Override\n\t\tpublic boolean enterRegion(MethodNode mth, IRegion region) {\n\t\t\tif (region instanceof IfRegion) {\n\t\t\t\tIfRegion ifRegion = (IfRegion) region;\n\t\t\t\torderBranches(mth, ifRegion);\n\t\t\t\tmarkElseIfChains(mth, ifRegion);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t@SuppressWarnings({ \"UnnecessaryReturnStatement\" })\n\tprivate static void orderBranches(MethodNode mth, IfRegion ifRegion) {\n\t\tif (RegionUtils.isEmpty(ifRegion.getElseRegion())) {\n\t\t\treturn;\n\t\t}\n\t\tif (RegionUtils.isEmpty(ifRegion.getThenRegion())) {\n\t\t\tinvertIfRegion(ifRegion);\n\t\t\treturn;\n\t\t}\n\t\tif (mth.contains(AFlag.USE_LINES_HINTS)) {\n\t\t\tint thenLine = RegionUtils.getFirstSourceLine(ifRegion.getThenRegion());\n\t\t\tint elseLine = RegionUtils.getFirstSourceLine(ifRegion.getElseRegion());\n\t\t\tif (thenLine != 0 && elseLine != 0) {\n\t\t\t\tif (thenLine > elseLine) {\n\t\t\t\t\tinvertIfRegion(ifRegion);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (ifRegion.simplifyCondition()) {\n\t\t\tIfCondition condition = ifRegion.getCondition();\n\t\t\tif (condition != null && condition.getMode() == Mode.NOT) {\n\t\t\t\tinvertIfRegion(ifRegion);\n\t\t\t}\n\t\t}\n\t\tint thenSize = insnsCount(ifRegion.getThenRegion());\n\t\tint elseSize = insnsCount(ifRegion.getElseRegion());\n\t\tif (isSimpleExitBlock(mth, ifRegion.getElseRegion())) {\n\t\t\tif (isSimpleExitBlock(mth, ifRegion.getThenRegion())) {\n\t\t\t\tif (elseSize < thenSize) {\n\t\t\t\t\tinvertIfRegion(ifRegion);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (elseSize == 1) {\n\t\t\t\tboolean lastRegion = RegionUtils.hasExitEdge(ifRegion);\n\t\t\t\tif (lastRegion && mth.isVoidReturn()) {\n\t\t\t\t\tInsnNode lastElseInsn = RegionUtils.getLastInsn(ifRegion.getElseRegion());\n\t\t\t\t\tif (InsnUtils.isInsnType(lastElseInsn, InsnType.THROW)) {\n\t\t\t\t\t\t// move `throw` into `then` block\n\t\t\t\t\t\tinvertIfRegion(ifRegion);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// single return at method end will be removed later\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (thenSize > 2 && !(lastRegion && thenSize < 4 /* keep small code block inside else */)) {\n\t\t\t\t\tinvertIfRegion(ifRegion);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tboolean thenExit = RegionUtils.hasExitBlock(ifRegion.getThenRegion());\n\t\tboolean elseExit = RegionUtils.hasExitBlock(ifRegion.getElseRegion());\n\t\tif (elseExit && (!thenExit || elseSize < thenSize)) {\n\t\t\tinvertIfRegion(ifRegion);\n\t\t\treturn;\n\t\t}\n\t\t// move 'if' from 'then' branch to make 'else if' chain\n\t\tif (isIfRegion(ifRegion.getThenRegion())\n\t\t\t\t&& !isIfRegion(ifRegion.getElseRegion())\n\t\t\t\t&& !thenExit) {\n\t\t\tinvertIfRegion(ifRegion);\n\t\t\treturn;\n\t\t}\n\t\t// move 'break' into 'then' branch\n\t\tif (RegionUtils.hasBreakInsn(ifRegion.getElseRegion())) {\n\t\t\tinvertIfRegion(ifRegion);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate static boolean isIfRegion(IContainer container) {\n\t\tif (container instanceof IfRegion) {\n\t\t\treturn true;\n\t\t}\n\t\tif (container instanceof IRegion) {\n\t\t\tList<IContainer> subBlocks = ((IRegion) container).getSubBlocks();\n\t\t\treturn subBlocks.size() == 1 && subBlocks.get(0) instanceof IfRegion;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Mark if-else-if chains\n\t */\n\tprivate static void markElseIfChains(MethodNode mth, IfRegion ifRegion) {\n\t\tif (isSimpleExitBlock(mth, ifRegion.getThenRegion())) {\n\t\t\treturn;\n\t\t}\n\t\tIContainer elsRegion = ifRegion.getElseRegion();\n\t\tif (elsRegion instanceof Region) {\n\t\t\tList<IContainer> subBlocks = ((Region) elsRegion).getSubBlocks();\n\t\t\tif (subBlocks.size() == 1 && subBlocks.get(0) instanceof IfRegion) {\n\t\t\t\tsubBlocks.get(0).add(AFlag.ELSE_IF_CHAIN);\n\t\t\t\telsRegion.add(AFlag.ELSE_IF_CHAIN);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static class RemoveRedundantElseVisitor implements IRegionIterativeVisitor {\n\t\t@Override\n\t\tpublic boolean visitRegion(MethodNode mth, IRegion region) {\n\t\t\tif (region instanceof IfRegion) {\n\t\t\t\treturn removeRedundantElseBlock(mth, (IfRegion) region);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"UnnecessaryParentheses\")\n\tprivate static boolean removeRedundantElseBlock(MethodNode mth, IfRegion ifRegion) {\n\t\tif (ifRegion.getElseRegion() == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!RegionUtils.hasExitBlock(ifRegion.getThenRegion())) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnNode lastThanInsn = RegionUtils.getLastInsn(ifRegion.getThenRegion());\n\t\tif (InsnUtils.isInsnType(lastThanInsn, InsnType.THROW)) {\n\t\t\t// always omit else after 'throw'\n\t\t} else {\n\t\t\t// code style check:\n\t\t\t// will remove 'return;' from 'then' and 'else' with one instruction\n\t\t\t// see #jadx.tests.integration.conditions.TestConditions9\n\t\t\tif (mth.isVoidReturn()) {\n\t\t\t\tint thenSize = insnsCount(ifRegion.getThenRegion());\n\t\t\t\t// keep small blocks with same or 'similar' size unchanged\n\t\t\t\tif (thenSize < 5) {\n\t\t\t\t\tint elseSize = insnsCount(ifRegion.getElseRegion());\n\t\t\t\t\tint range = ifRegion.getElseRegion().contains(AFlag.ELSE_IF_CHAIN) ? 4 : 2;\n\t\t\t\t\tif (thenSize == elseSize || (thenSize * range > elseSize && thenSize < elseSize * range)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tIRegion parent = ifRegion.getParent();\n\t\tRegion newRegion = new Region(parent);\n\t\tif (parent.replaceSubBlock(ifRegion, newRegion)) {\n\t\t\tnewRegion.add(ifRegion);\n\t\t\tnewRegion.add(ifRegion.getElseRegion());\n\t\t\tifRegion.setElseRegion(null);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static void invertIfRegion(IfRegion ifRegion) {\n\t\tIContainer elseRegion = ifRegion.getElseRegion();\n\t\tif (elseRegion != null) {\n\t\t\tifRegion.invert();\n\t\t}\n\t}\n\n\tprivate static boolean isSimpleExitBlock(MethodNode mth, IContainer container) {\n\t\tif (container == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (container.contains(AFlag.RETURN) || RegionUtils.isExitBlock(mth, container)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (container instanceof IRegion) {\n\t\t\tList<IContainer> subBlocks = ((IRegion) container).getSubBlocks();\n\t\t\treturn subBlocks.size() == 1 && RegionUtils.isExitBlock(mth, subBlocks.get(0));\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/LoopRegionVisitor.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.ArithNode;\nimport jadx.core.dex.instructions.ArithOp;\nimport jadx.core.dex.instructions.IfOp;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.InvokeType;\nimport jadx.core.dex.instructions.PhiInsn;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.conditions.Compare;\nimport jadx.core.dex.regions.conditions.IfCondition;\nimport jadx.core.dex.regions.loops.ForEachLoop;\nimport jadx.core.dex.regions.loops.ForLoop;\nimport jadx.core.dex.regions.loops.LoopRegion;\nimport jadx.core.dex.regions.loops.LoopType;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.dex.visitors.regions.variables.ProcessVariables;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.RegionUtils;\nimport jadx.core.utils.exceptions.JadxOverflowException;\n\n@JadxVisitor(\n\t\tname = \"LoopRegionVisitor\",\n\t\tdesc = \"Convert 'while' loops to 'for' loops (indexed or for-each)\",\n\t\trunBefore = ProcessVariables.class\n)\npublic class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(LoopRegionVisitor.class);\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tDepthRegionTraversal.traverse(mth, this);\n\t\tIfRegionVisitor.processIfRequested(mth);\n\t}\n\n\t@Override\n\tpublic boolean enterRegion(MethodNode mth, IRegion region) {\n\t\tif (region instanceof LoopRegion) {\n\t\t\tif (processLoopRegion(mth, (LoopRegion) region)) {\n\t\t\t\t// optimize `if` block after instructions remove\n\t\t\t\tmth.add(AFlag.REQUEST_IF_REGION_OPTIMIZE);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean processLoopRegion(MethodNode mth, LoopRegion loopRegion) {\n\t\tif (loopRegion.isConditionAtEnd()) {\n\t\t\treturn false;\n\t\t}\n\t\tIfCondition condition = loopRegion.getCondition();\n\t\tif (condition == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (checkForIndexedLoop(mth, loopRegion, condition)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn checkIterableForEach(mth, loopRegion, condition);\n\t}\n\n\t/**\n\t * Check for indexed loop.\n\t */\n\tprivate static boolean checkForIndexedLoop(MethodNode mth, LoopRegion loopRegion, IfCondition condition) {\n\t\tBlockNode loopEndBlock = loopRegion.getInfo().getEnd();\n\t\tInsnNode incrInsn = BlockUtils.getLastInsn(BlockUtils.skipSyntheticPredecessor(loopEndBlock));\n\t\tif (incrInsn == null) {\n\t\t\treturn false;\n\t\t}\n\t\tRegisterArg incrArg = incrInsn.getResult();\n\t\tif (incrArg == null\n\t\t\t\t|| incrArg.getSVar() == null\n\t\t\t\t|| !incrArg.getSVar().isUsedInPhi()) {\n\t\t\treturn false;\n\t\t}\n\t\tList<PhiInsn> phiInsnList = incrArg.getSVar().getUsedInPhi();\n\t\tif (phiInsnList.size() != 1) {\n\t\t\treturn false;\n\t\t}\n\t\tPhiInsn phiInsn = phiInsnList.get(0);\n\t\tif (phiInsn.getArgsCount() != 2\n\t\t\t\t|| !phiInsn.containsVar(incrArg)\n\t\t\t\t|| incrArg.getSVar().getUseCount() != 1) {\n\t\t\treturn false;\n\t\t}\n\t\tRegisterArg arg = phiInsn.getResult();\n\t\tList<RegisterArg> condArgs = condition.getRegisterArgs();\n\t\tif (!condArgs.contains(arg) || arg.getSVar().isUsedInPhi()) {\n\t\t\treturn false;\n\t\t}\n\t\tRegisterArg initArg = phiInsn.getArg(0);\n\t\tInsnNode initInsn = initArg.getAssignInsn();\n\t\tif (initInsn == null\n\t\t\t\t|| initInsn.contains(AFlag.DONT_GENERATE)\n\t\t\t\t|| initArg.getSVar().getUseCount() != 1) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!usedOnlyInLoop(mth, loopRegion, arg)) {\n\t\t\treturn false;\n\t\t}\n\t\t// can't make loop if argument from increment instruction is assign in loop\n\t\tList<RegisterArg> args = new ArrayList<>();\n\t\tincrInsn.getRegisterArgs(args);\n\t\tfor (RegisterArg iArg : args) {\n\t\t\ttry {\n\t\t\t\tif (assignOnlyInLoop(mth, loopRegion, iArg)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} catch (StackOverflowError error) {\n\t\t\t\tthrow new JadxOverflowException(\"LoopRegionVisitor.assignOnlyInLoop endless recursion\");\n\t\t\t}\n\t\t}\n\n\t\t// all checks passed\n\t\tinitInsn.add(AFlag.DONT_GENERATE);\n\t\tincrInsn.add(AFlag.DONT_GENERATE);\n\n\t\tLoopType arrForEach = checkArrayForEach(mth, loopRegion, initInsn, incrInsn, condition);\n\t\tloopRegion.setType(arrForEach != null ? arrForEach : new ForLoop(initInsn, incrInsn));\n\t\treturn true;\n\t}\n\n\tprivate static LoopType checkArrayForEach(MethodNode mth, LoopRegion loopRegion, InsnNode initInsn, InsnNode incrInsn,\n\t\t\tIfCondition condition) {\n\t\tif (!(incrInsn instanceof ArithNode)) {\n\t\t\treturn null;\n\t\t}\n\t\tArithNode arithNode = (ArithNode) incrInsn;\n\t\tif (arithNode.getOp() != ArithOp.ADD) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnArg lit = incrInsn.getArg(1);\n\t\tif (!lit.isLiteral() || ((LiteralArg) lit).getLiteral() != 1) {\n\t\t\treturn null;\n\t\t}\n\t\tif (initInsn.getType() != InsnType.CONST\n\t\t\t\t|| !initInsn.getArg(0).isLiteral()\n\t\t\t\t|| ((LiteralArg) initInsn.getArg(0)).getLiteral() != 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tInsnArg condArg = incrInsn.getArg(0);\n\t\tif (!condArg.isRegister()) {\n\t\t\treturn null;\n\t\t}\n\t\tSSAVar sVar = ((RegisterArg) condArg).getSVar();\n\t\tList<RegisterArg> args = sVar.getUseList();\n\t\tif (args.size() != 3) {\n\t\t\treturn null;\n\t\t}\n\t\tcondArg = InsnUtils.getRegFromInsn(args, InsnType.IF);\n\t\tif (condArg == null) {\n\t\t\treturn null;\n\t\t}\n\t\tRegisterArg arrIndex = InsnUtils.getRegFromInsn(args, InsnType.AGET);\n\t\tif (arrIndex == null) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnNode arrGetInsn = arrIndex.getParentInsn();\n\t\tif (arrGetInsn == null || arrGetInsn.containsWrappedInsn()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!condition.isCompare()) {\n\t\t\treturn null;\n\t\t}\n\t\tCompare compare = condition.getCompare();\n\t\tif (compare.getOp() != IfOp.LT || compare.getA() != condArg) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnNode len;\n\t\tInsnArg bCondArg = compare.getB();\n\t\tif (bCondArg.isInsnWrap()) {\n\t\t\tlen = ((InsnWrapArg) bCondArg).getWrapInsn();\n\t\t} else if (bCondArg.isRegister()) {\n\t\t\tlen = ((RegisterArg) bCondArg).getAssignInsn();\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t\tif (len == null || len.getType() != InsnType.ARRAY_LENGTH) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnArg arrayArg = len.getArg(0);\n\t\tif (!arrayArg.equals(arrGetInsn.getArg(0))) {\n\t\t\treturn null;\n\t\t}\n\t\tRegisterArg iterVar = arrGetInsn.getResult();\n\t\tif (iterVar != null) {\n\t\t\tif (!usedOnlyInLoop(mth, loopRegion, iterVar)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t} else {\n\t\t\tif (!arrGetInsn.contains(AFlag.WRAPPED)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t// create new variable and replace wrapped insn\n\t\t\tInsnArg wrapArg = BlockUtils.searchWrappedInsnParent(mth, arrGetInsn);\n\t\t\tif (wrapArg == null || wrapArg.getParentInsn() == null) {\n\t\t\t\tmth.addWarnComment(\"checkArrayForEach: Wrapped insn not found: \" + arrGetInsn);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\titerVar = mth.makeSyntheticRegArg(wrapArg.getType());\n\t\t\tInsnNode parentInsn = wrapArg.getParentInsn();\n\t\t\tparentInsn.replaceArg(wrapArg, iterVar.duplicate());\n\t\t\tparentInsn.rebindArgs();\n\t\t}\n\n\t\t// array for each loop confirmed\n\t\tincrInsn.getResult().add(AFlag.DONT_GENERATE);\n\t\tcondArg.add(AFlag.DONT_GENERATE);\n\t\tbCondArg.add(AFlag.DONT_GENERATE);\n\t\tarrGetInsn.add(AFlag.DONT_GENERATE);\n\t\tcompare.getInsn().add(AFlag.DONT_GENERATE);\n\n\t\tForEachLoop forEachLoop = new ForEachLoop(iterVar, len.getArg(0));\n\t\tforEachLoop.injectFakeInsns(loopRegion);\n\t\tif (InsnUtils.dontGenerateIfNotUsed(len)) {\n\t\t\tInsnRemover.remove(mth, len);\n\t\t}\n\t\tCodeShrinkVisitor.shrinkMethod(mth);\n\t\treturn forEachLoop;\n\t}\n\n\tprivate static boolean checkIterableForEach(MethodNode mth, LoopRegion loopRegion, IfCondition condition) {\n\t\tList<RegisterArg> condArgs = condition.getRegisterArgs();\n\t\tif (condArgs.size() != 1) {\n\t\t\treturn false;\n\t\t}\n\t\tRegisterArg iteratorArg = condArgs.get(0);\n\t\tSSAVar sVar = iteratorArg.getSVar();\n\t\tif (sVar == null || sVar.isUsedInPhi()) {\n\t\t\treturn false;\n\t\t}\n\t\tList<RegisterArg> itUseList = sVar.getUseList();\n\t\tInsnNode assignInsn = iteratorArg.getAssignInsn();\n\t\tif (itUseList.size() != 2) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!checkInvoke(assignInsn, null, \"iterator()Ljava/util/Iterator;\")) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnArg iterableArg = assignInsn.getArg(0);\n\t\tInsnNode hasNextCall = itUseList.get(0).getParentInsn();\n\t\tInsnNode nextCall = itUseList.get(1).getParentInsn();\n\t\tif (!checkInvoke(hasNextCall, \"java.util.Iterator\", \"hasNext()Z\")\n\t\t\t\t|| !checkInvoke(nextCall, \"java.util.Iterator\", \"next()Ljava/lang/Object;\")) {\n\t\t\treturn false;\n\t\t}\n\t\tList<InsnNode> toSkip = new ArrayList<>();\n\t\tRegisterArg iterVar;\n\t\tif (nextCall.contains(AFlag.WRAPPED)) {\n\t\t\tInsnArg wrapArg = BlockUtils.searchWrappedInsnParent(mth, nextCall);\n\t\t\tif (wrapArg != null && wrapArg.getParentInsn() != null) {\n\t\t\t\tInsnNode parentInsn = wrapArg.getParentInsn();\n\t\t\t\tBlockNode block = BlockUtils.getBlockByInsn(mth, parentInsn);\n\t\t\t\tif (block == null) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (!RegionUtils.isRegionContainsBlock(loopRegion, block)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (parentInsn.getType() == InsnType.CHECK_CAST) {\n\t\t\t\t\titerVar = parentInsn.getResult();\n\t\t\t\t\tif (iterVar == null || !fixIterableType(mth, iterableArg, iterVar)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tInsnArg castArg = BlockUtils.searchWrappedInsnParent(mth, parentInsn);\n\t\t\t\t\tif (castArg != null && castArg.getParentInsn() != null) {\n\t\t\t\t\t\tcastArg.getParentInsn().replaceArg(castArg, iterVar);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// cast not inlined\n\t\t\t\t\t\ttoSkip.add(parentInsn);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\titerVar = nextCall.getResult();\n\t\t\t\t\tif (iterVar == null) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\titerVar.remove(AFlag.REMOVE); // restore variable from inlined insn\n\t\t\t\t\tnextCall.add(AFlag.DONT_GENERATE);\n\t\t\t\t\tif (!fixIterableType(mth, iterableArg, iterVar)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tparentInsn.replaceArg(wrapArg, iterVar);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLOG.warn(\" checkIterableForEach: Wrapped insn not found: {}, mth: {}\", nextCall, mth);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else {\n\t\t\titerVar = nextCall.getResult();\n\t\t\tif (iterVar == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!usedOnlyInLoop(mth, loopRegion, iterVar)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!assignOnlyInLoop(mth, loopRegion, iterVar)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ttoSkip.add(nextCall);\n\t\t}\n\n\t\tassignInsn.add(AFlag.DONT_GENERATE);\n\t\tassignInsn.getResult().add(AFlag.DONT_GENERATE);\n\n\t\tfor (InsnNode insnNode : toSkip) {\n\t\t\tinsnNode.setResult(null);\n\t\t\tinsnNode.add(AFlag.DONT_GENERATE);\n\t\t}\n\t\tfor (RegisterArg itArg : itUseList) {\n\t\t\titArg.add(AFlag.DONT_GENERATE);\n\t\t}\n\t\tForEachLoop forEachLoop = new ForEachLoop(iterVar, iterableArg);\n\t\tforEachLoop.injectFakeInsns(loopRegion);\n\t\tloopRegion.setType(forEachLoop);\n\t\treturn true;\n\t}\n\n\tprivate static boolean fixIterableType(MethodNode mth, InsnArg iterableArg, RegisterArg iterVar) {\n\t\tArgType iterableType = iterableArg.getType();\n\t\tArgType varType = iterVar.getType();\n\t\tif (iterableType.isGeneric()) {\n\t\t\tList<ArgType> genericTypes = iterableType.getGenericTypes();\n\t\t\tif (genericTypes == null || genericTypes.size() != 1) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tArgType gType = genericTypes.get(0);\n\t\t\tif (gType.equals(varType)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (gType.isGenericType()) {\n\t\t\t\titerVar.setType(gType);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (ArgType.isInstanceOf(mth.root(), gType, varType)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tArgType wildcardType = gType.getWildcardType();\n\t\t\tif (wildcardType != null\n\t\t\t\t\t&& gType.getWildcardBound() == ArgType.WildcardBound.EXTENDS\n\t\t\t\t\t&& ArgType.isInstanceOf(mth.root(), wildcardType, varType)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tLOG.warn(\"Generic type differs: '{}' and '{}' in {}\", gType, varType, mth);\n\t\t\treturn false;\n\t\t}\n\t\tif (!iterableArg.isRegister() || !iterableType.isObject()) {\n\t\t\treturn true;\n\t\t}\n\t\tArgType genericType = ArgType.generic(iterableType.getObject(), varType);\n\t\tif (iterableArg.isRegister()) {\n\t\t\tArgType immutableType = ((RegisterArg) iterableArg).getImmutableType();\n\t\t\tif (immutableType != null && !immutableType.equals(genericType)) {\n\t\t\t\t// can't change type\n\t\t\t\t// allow to iterate over not generified collection only for Object vars\n\t\t\t\treturn varType.equals(ArgType.OBJECT);\n\t\t\t}\n\t\t}\n\t\titerableArg.setType(genericType);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Check if instruction is a interface invoke with corresponding parameters.\n\t */\n\tprivate static boolean checkInvoke(InsnNode insn, String declClsFullName, String mthId) {\n\t\tif (insn == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (insn.getType() == InsnType.INVOKE) {\n\t\t\tInvokeNode inv = (InvokeNode) insn;\n\t\t\tMethodInfo callMth = inv.getCallMth();\n\t\t\tif ((inv.getInvokeType() == InvokeType.INTERFACE || inv.getInvokeType() == InvokeType.VIRTUAL)\n\t\t\t\t\t&& callMth.getShortId().equals(mthId)) {\n\t\t\t\tif (declClsFullName == null) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn callMth.getDeclClass().getFullName().equals(declClsFullName);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean assignOnlyInLoop(MethodNode mth, LoopRegion loopRegion, RegisterArg arg) {\n\t\tInsnNode assignInsn = arg.getAssignInsn();\n\t\tif (assignInsn == null) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!argInLoop(mth, loopRegion, assignInsn.getResult())) {\n\t\t\treturn false;\n\t\t}\n\t\tif (assignInsn instanceof PhiInsn) {\n\t\t\tPhiInsn phiInsn = (PhiInsn) assignInsn;\n\t\t\tfor (InsnArg phiArg : phiInsn.getArguments()) {\n\t\t\t\tif (!assignOnlyInLoop(mth, loopRegion, (RegisterArg) phiArg)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean usedOnlyInLoop(MethodNode mth, LoopRegion loopRegion, RegisterArg arg) {\n\t\tList<RegisterArg> useList = arg.getSVar().getUseList();\n\t\tfor (RegisterArg useArg : useList) {\n\t\t\tif (!argInLoop(mth, loopRegion, useArg)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean argInLoop(MethodNode mth, LoopRegion loopRegion, RegisterArg arg) {\n\t\tInsnNode parentInsn = arg.getParentInsn();\n\t\tif (parentInsn == null) {\n\t\t\treturn false;\n\t\t}\n\t\tBlockNode block = BlockUtils.getBlockByInsn(mth, parentInsn);\n\t\tif (block == null) {\n\t\t\tLOG.debug(\" LoopRegionVisitor: instruction not found: {}, mth: {}\", parentInsn, mth);\n\t\t\treturn false;\n\t\t}\n\t\treturn RegionUtils.isRegionContainsBlock(loopRegion, block);\n\t}\n\n\t@Override\n\tpublic void leaveRegion(MethodNode mth, IRegion region) {\n\t}\n\n\t@Override\n\tpublic void processBlock(MethodNode mth, IBlock container) {\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/PostProcessRegions.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.EdgeInsnAttr;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnContainer;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.Region;\nimport jadx.core.dex.regions.SwitchRegion;\nimport jadx.core.dex.regions.loops.LoopRegion;\nimport jadx.core.dex.visitors.regions.maker.SwitchRegionMaker;\n\npublic final class PostProcessRegions extends AbstractRegionVisitor {\n\tprivate static final IRegionVisitor INSTANCE = new PostProcessRegions();\n\n\tstatic void process(MethodNode mth) {\n\t\tDepthRegionTraversal.traverse(mth, INSTANCE);\n\t}\n\n\t@Override\n\tpublic void leaveRegion(MethodNode mth, IRegion region) {\n\t\tif (region instanceof LoopRegion) {\n\t\t\t// merge conditions in loops\n\t\t\tLoopRegion loop = (LoopRegion) region;\n\t\t\tloop.mergePreCondition();\n\t\t} else if (region instanceof SwitchRegion) {\n\t\t\tSwitchRegionMaker.insertBreaks(mth, (SwitchRegion) region);\n\t\t} else if (region instanceof Region) {\n\t\t\tinsertEdgeInsn((Region) region);\n\t\t}\n\t}\n\n\t/**\n\t * Insert insn block from edge insn attribute.\n\t */\n\tprivate static void insertEdgeInsn(Region region) {\n\t\tList<IContainer> subBlocks = region.getSubBlocks();\n\t\tif (subBlocks.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tIContainer last = subBlocks.get(subBlocks.size() - 1);\n\t\tList<EdgeInsnAttr> edgeInsnAttrs = last.getAll(AType.EDGE_INSN);\n\t\tif (edgeInsnAttrs.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tEdgeInsnAttr insnAttr = edgeInsnAttrs.get(0);\n\t\tif (!insnAttr.getStart().equals(last)) {\n\t\t\treturn;\n\t\t}\n\t\tif (last instanceof BlockNode) {\n\t\t\tBlockNode block = (BlockNode) last;\n\t\t\tif (block.getInstructions().isEmpty()) {\n\t\t\t\tblock.getInstructions().add(insnAttr.getInsn());\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tList<InsnNode> insns = Collections.singletonList(insnAttr.getInsn());\n\t\tregion.add(new InsnContainer(insns));\n\t}\n\n\tprivate PostProcessRegions() {\n\t\t// singleton\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IBranchRegion;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.AbstractRegion;\nimport jadx.core.dex.regions.Region;\nimport jadx.core.dex.regions.TryCatchRegion;\nimport jadx.core.dex.regions.loops.LoopRegion;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.dex.trycatch.TryCatchBlockAttr;\nimport jadx.core.utils.RegionUtils;\n\n/**\n * Extract blocks to separate try/catch region\n */\npublic class ProcessTryCatchRegions extends AbstractRegionVisitor {\n\n\tpublic static void process(MethodNode mth) {\n\t\tif (mth.isNoCode() || mth.isNoExceptionHandlers()) {\n\t\t\treturn;\n\t\t}\n\t\tList<TryCatchBlockAttr> tryBlocks = collectTryCatchBlocks(mth);\n\t\tif (tryBlocks.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tDepthRegionTraversal.traverseIncludingExcHandlers(mth, (regionMth, region) -> {\n\t\t\tboolean changed = checkAndWrap(regionMth, tryBlocks, region);\n\t\t\treturn changed && !tryBlocks.isEmpty();\n\t\t});\n\t}\n\n\tprivate static List<TryCatchBlockAttr> collectTryCatchBlocks(MethodNode mth) {\n\t\tList<TryCatchBlockAttr> list = mth.getAll(AType.TRY_BLOCKS_LIST);\n\t\tif (list.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<TryCatchBlockAttr> tryBlocks = new ArrayList<>(list);\n\t\ttryBlocks.sort((a, b) -> a == b ? 0 : a.getOuterTryBlock() == b ? 1 : -1); // move parent try block to top\n\t\treturn tryBlocks;\n\t}\n\n\tprivate static boolean checkAndWrap(MethodNode mth, List<TryCatchBlockAttr> tryBlocks, IRegion region) {\n\t\t// search top splitter block in this region (don't need to go deeper)\n\t\tfor (TryCatchBlockAttr tb : tryBlocks) {\n\t\t\tBlockNode topSplitter = tb.getTopSplitter();\n\t\t\tif (region.getSubBlocks().contains(topSplitter)) {\n\t\t\t\tif (!wrapBlocks(region, tb, topSplitter)) {\n\t\t\t\t\tmth.addWarn(\"Can't wrap try/catch for region: \" + region);\n\t\t\t\t}\n\t\t\t\ttryBlocks.remove(tb);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Extract all block dominated by 'dominator' to separate region and mark as try/catch block\n\t */\n\tprivate static boolean wrapBlocks(IRegion replaceRegion, TryCatchBlockAttr tb, BlockNode dominator) {\n\t\tif (replaceRegion == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (replaceRegion instanceof LoopRegion) {\n\t\t\tLoopRegion loop = (LoopRegion) replaceRegion;\n\t\t\treturn wrapBlocks(loop.getBody(), tb, dominator);\n\t\t}\n\t\tif (replaceRegion instanceof IBranchRegion) {\n\t\t\treturn wrapBlocks(replaceRegion.getParent(), tb, dominator);\n\t\t}\n\n\t\tRegion tryRegion = new Region(replaceRegion);\n\t\tList<IContainer> subBlocks = replaceRegion.getSubBlocks();\n\t\t// traverse the enclosing region for blocks that have a path from the dominator but don't have a\n\t\t// path from any of the exception handlers i.e. they are not before the end of the try block so\n\t\t// should be inside the try block.\n\t\tfor (IContainer cont : subBlocks) {\n\t\t\tif (RegionUtils.hasPathThroughBlock(dominator, cont)) {\n\t\t\t\tif (isHandlerPath(tb, cont)) {\n\t\t\t\t\t// this block/region has a path from an exception handler so is after the end of the try block\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttryRegion.getSubBlocks().add(cont);\n\t\t\t}\n\t\t}\n\t\tif (tryRegion.getSubBlocks().isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tTryCatchRegion tryCatchRegion = new TryCatchRegion(replaceRegion, tryRegion);\n\t\ttryRegion.setParent(tryCatchRegion);\n\t\ttryCatchRegion.setTryCatchBlock(tb);\n\n\t\t// replace first node by region\n\t\tIContainer firstNode = tryRegion.getSubBlocks().get(0);\n\t\tif (!replaceRegion.replaceSubBlock(firstNode, tryCatchRegion)) {\n\t\t\treturn false;\n\t\t}\n\t\tsubBlocks.removeAll(tryRegion.getSubBlocks());\n\n\t\t// fix parents for tryRegion sub blocks\n\t\tfor (IContainer cont : tryRegion.getSubBlocks()) {\n\t\t\tif (cont instanceof AbstractRegion) {\n\t\t\t\tAbstractRegion aReg = (AbstractRegion) cont;\n\t\t\t\taReg.setParent(tryRegion);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean isHandlerPath(TryCatchBlockAttr tb, IContainer cont) {\n\t\tfor (ExceptionHandler h : tb.getHandlers()) {\n\t\t\tBlockNode handlerBlock = h.getHandlerBlock();\n\t\t\tif (handlerBlock != null\n\t\t\t\t\t&& !handlerBlock.contains(AFlag.REMOVE)\n\t\t\t\t\t&& RegionUtils.hasPathThroughBlock(handlerBlock, cont)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.dex.visitors.regions.maker.ExcHandlersRegionMaker;\nimport jadx.core.dex.visitors.regions.maker.RegionMaker;\nimport jadx.core.dex.visitors.regions.maker.SynchronizedRegionMaker;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"RegionMakerVisitor\",\n\t\tdesc = \"Pack blocks into regions for code generation\"\n)\npublic class RegionMakerVisitor extends AbstractVisitor {\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (mth.isNoCode() || mth.getBasicBlocks().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tRegionMaker rm = new RegionMaker(mth);\n\t\tmth.setRegion(rm.makeMthRegion());\n\t\tif (!mth.isNoExceptionHandlers()) {\n\t\t\tnew ExcHandlersRegionMaker(mth, rm).process();\n\t\t}\n\t\tprocessForceInlineInsns(mth);\n\t\tProcessTryCatchRegions.process(mth);\n\t\tPostProcessRegions.process(mth);\n\t\tCleanRegions.process(mth);\n\t\tif (mth.getAccessFlags().isSynchronized()) {\n\t\t\tSynchronizedRegionMaker.removeSynchronized(mth);\n\t\t}\n\t}\n\n\tprivate static void processForceInlineInsns(MethodNode mth) {\n\t\tboolean needShrink = mth.getBasicBlocks().stream()\n\t\t\t\t.flatMap(block -> block.getInstructions().stream())\n\t\t\t\t.anyMatch(insn -> insn.contains(AFlag.FORCE_ASSIGN_INLINE));\n\t\tif (needShrink) {\n\t\t\tCodeShrinkVisitor.shrinkMethod(mth);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"RegionMakerVisitor\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/ReturnVisitor.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport java.util.List;\nimport java.util.ListIterator;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IBranchRegion;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.SwitchRegion;\nimport jadx.core.dex.regions.loops.LoopRegion;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.utils.exceptions.JadxException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n/**\n * Remove unnecessary return instructions for void methods\n */\npublic class ReturnVisitor extends AbstractVisitor {\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (mth.isVoidReturn()) {\n\t\t\tDepthRegionTraversal.traverse(mth, new ReturnRemoverVisitor());\n\t\t}\n\t}\n\n\tprivate static final class ReturnRemoverVisitor extends TracedRegionVisitor {\n\n\t\t@Override\n\t\tpublic boolean enterRegion(MethodNode mth, IRegion region) {\n\t\t\tsuper.enterRegion(mth, region);\n\t\t\treturn !(region instanceof SwitchRegion);\n\t\t}\n\n\t\t@Override\n\t\tpublic void processBlockTraced(MethodNode mth, IBlock container, IRegion currentRegion) {\n\t\t\tif (container.getClass() != BlockNode.class) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tBlockNode block = (BlockNode) container;\n\t\t\tif (block.contains(AFlag.RETURN)) {\n\t\t\t\tList<InsnNode> insns = block.getInstructions();\n\t\t\t\tif (insns.size() == 1\n\t\t\t\t\t\t&& blockNotInLoop(mth, block)\n\t\t\t\t\t\t&& noTrailInstructions(block)) {\n\t\t\t\t\tinsns.remove(0);\n\t\t\t\t\tblock.remove(AFlag.RETURN);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate boolean blockNotInLoop(MethodNode mth, BlockNode block) {\n\t\t\tif (mth.getLoopsCount() == 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (mth.getLoopForBlock(block) != null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfor (IRegion region : regionStack) {\n\t\t\t\tif (region.getClass() == LoopRegion.class) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t/**\n\t\t * Check that there are no code after this block in regions structure\n\t\t */\n\t\tprivate boolean noTrailInstructions(BlockNode block) {\n\t\t\tIContainer curContainer = block;\n\t\t\tfor (IRegion region : regionStack) {\n\t\t\t\t// ignore paths on other branches\n\t\t\t\tif (region instanceof IBranchRegion) {\n\t\t\t\t\tcurContainer = region;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tList<IContainer> subBlocks = region.getSubBlocks();\n\t\t\t\tif (!subBlocks.isEmpty()) {\n\t\t\t\t\tListIterator<IContainer> itSubBlock = subBlocks.listIterator(subBlocks.size());\n\t\t\t\t\twhile (itSubBlock.hasPrevious()) {\n\t\t\t\t\t\tIContainer subBlock = itSubBlock.previous();\n\t\t\t\t\t\tif (subBlock == curContainer) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} else if (!isEmpty(subBlock)) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcurContainer = region;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t/**\n\t\t * Check if container not contains instructions,\n\t\t * don't count one 'return' instruction (it will be removed later).\n\t\t */\n\t\tprivate static boolean isEmpty(IContainer container) {\n\t\t\tif (container instanceof IBlock) {\n\t\t\t\tIBlock block = (IBlock) container;\n\t\t\t\treturn block.getInstructions().isEmpty() || block.contains(AFlag.RETURN);\n\t\t\t} else if (container instanceof IRegion) {\n\t\t\t\tIRegion region = (IRegion) container;\n\t\t\t\tfor (IContainer block : region.getSubBlocks()) {\n\t\t\t\t\tif (!isEmpty(block)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\tthrow new JadxRuntimeException(\"Unknown container type: \" + container.getClass());\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/SwitchBreakVisitor.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.CodeFeaturesAttr;\nimport jadx.core.dex.attributes.nodes.RegionRefAttr;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IBranchRegion;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.SwitchRegion;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.dex.visitors.regions.maker.SwitchRegionMaker;\nimport jadx.core.utils.BlockInsnPair;\nimport jadx.core.utils.BlockParentContainer;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.RegionUtils;\nimport jadx.core.utils.exceptions.JadxException;\n\nimport static jadx.core.dex.attributes.nodes.CodeFeaturesAttr.CodeFeature.SWITCH;\n\n@JadxVisitor(\n\t\tname = \"SwitchBreakVisitor\",\n\t\tdesc = \"Optimize 'break' instruction: common code extract, remove unreachable\",\n\t\trunAfter = LoopRegionVisitor.class // can add 'continue' at case end\n)\npublic class SwitchBreakVisitor extends AbstractVisitor {\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (CodeFeaturesAttr.contains(mth, SWITCH)) {\n\t\t\trunSwitchTraverse(mth, ExtractCommonBreak::new);\n\t\t\trunSwitchTraverse(mth, RemoveUnreachableBreak::new);\n\t\t\tIfRegionVisitor.processIfRequested(mth);\n\t\t}\n\t}\n\n\tprivate static void runSwitchTraverse(MethodNode mth, Supplier<BaseSwitchRegionVisitor> builder) {\n\t\tDepthRegionTraversal.traverse(mth, new IterativeSwitchRegionVisitor(builder));\n\t}\n\n\t/**\n\t * Add common 'break' if 'break' or exit insn ('return', 'throw', 'continue') found in all branches.\n\t * Remove exist common break if all branches contain exit insn.\n\t */\n\tprivate static final class ExtractCommonBreak extends BaseSwitchRegionVisitor {\n\t\t@Override\n\t\tpublic void processRegion(MethodNode mth, IRegion region) {\n\t\t\tif (region instanceof IBranchRegion && !(region instanceof SwitchRegion)) {\n\t\t\t\t// if break in all branches extract to parent region\n\t\t\t\tprocessBranchRegion(mth, region);\n\t\t\t}\n\t\t}\n\n\t\tprivate void processBranchRegion(MethodNode mth, IRegion region) {\n\t\t\tIRegion parentRegion = region.getParent();\n\t\t\tif (parentRegion.contains(AFlag.FALL_THROUGH)) {\n\t\t\t\t// fallthrough case, can't extract break\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tboolean dontAddCommonBreak = false;\n\t\t\tIBlock lastParentBlock = RegionUtils.getLastBlock(parentRegion);\n\t\t\tif (BlockUtils.containsExitInsn(lastParentBlock)) {\n\t\t\t\tif (isBreakBlock(lastParentBlock)) {\n\t\t\t\t\t// parent block already contains 'break'\n\t\t\t\t\tdontAddCommonBreak = true;\n\t\t\t\t} else {\n\t\t\t\t\t// can't add 'break' after 'return', 'throw' or 'continue'\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tList<IContainer> branches = ((IBranchRegion) region).getBranches();\n\t\t\tboolean removeCommonBreak = true; // all branches contain exit insns, common break is unreachable\n\t\t\tList<BlockParentContainer> forBreakRemove = new ArrayList<>();\n\t\t\tfor (IContainer branch : branches) {\n\t\t\t\tif (branch == null) {\n\t\t\t\t\tremoveCommonBreak = false;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tBlockInsnPair last = RegionUtils.getLastInsnWithBlock(branch);\n\t\t\t\tif (last == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tInsnNode lastInsn = last.getInsn();\n\t\t\t\tif (lastInsn.getType() == InsnType.BREAK) {\n\t\t\t\t\tIBlock block = last.getBlock();\n\t\t\t\t\tIContainer parent = RegionUtils.getBlockContainer(branch, block);\n\t\t\t\t\tforBreakRemove.add(new BlockParentContainer(parent, block));\n\t\t\t\t\tremoveCommonBreak = false;\n\t\t\t\t} else if (!lastInsn.isExitEdgeInsn()) {\n\t\t\t\t\tremoveCommonBreak = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!forBreakRemove.isEmpty()) {\n\t\t\t\t// common 'break' confirmed\n\t\t\t\tfor (BlockParentContainer breakData : forBreakRemove) {\n\t\t\t\t\tremoveBreak(breakData.getBlock(), breakData.getParent());\n\t\t\t\t}\n\t\t\t\tif (!dontAddCommonBreak) {\n\t\t\t\t\taddBreakRegion.add(parentRegion);\n\t\t\t\t\t// new 'break' might become 'common' for upper branch region, request to run checks again\n\t\t\t\t\trequestReRun();\n\t\t\t\t}\n\t\t\t\t// removed 'break' may allow to use 'else-if' chain\n\t\t\t\tmth.add(AFlag.REQUEST_IF_REGION_OPTIMIZE);\n\t\t\t}\n\t\t\tif (removeCommonBreak && lastParentBlock != null) {\n\t\t\t\tremoveBreak(lastParentBlock, parentRegion);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static final class RemoveUnreachableBreak extends BaseSwitchRegionVisitor {\n\t\t@Override\n\t\tpublic void processRegion(MethodNode mth, IRegion region) {\n\t\t\tList<IContainer> subBlocks = region.getSubBlocks();\n\t\t\tIContainer lastContainer = ListUtils.last(subBlocks);\n\t\t\tif (lastContainer instanceof IBlock) {\n\t\t\t\tIBlock block = (IBlock) lastContainer;\n\t\t\t\tif (isBreakBlock(block) && isPrevInsnIsExit(block, subBlocks)) {\n\t\t\t\t\tremoveBreak(block, region);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate boolean isPrevInsnIsExit(IBlock breakBlock, List<IContainer> subBlocks) {\n\t\t\tInsnNode prevInsn = null;\n\t\t\tif (breakBlock.getInstructions().size() > 1) {\n\t\t\t\t// check prev insn in same block\n\t\t\t\tList<InsnNode> insns = breakBlock.getInstructions();\n\t\t\t\tprevInsn = insns.get(insns.size() - 2);\n\t\t\t} else if (subBlocks.size() > 1) {\n\t\t\t\tIContainer prev = subBlocks.get(subBlocks.size() - 2);\n\t\t\t\tif (prev instanceof IBlock) {\n\t\t\t\t\tList<InsnNode> insns = ((IBlock) prev).getInstructions();\n\t\t\t\t\tprevInsn = ListUtils.last(insns);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn prevInsn != null && prevInsn.isExitEdgeInsn();\n\t\t}\n\t}\n\n\t/**\n\t * For every 'switch' region run new instance of provided 'switch' visitor.\n\t * If rerun requested, run traverse for that visitor again.\n\t */\n\tprivate static final class IterativeSwitchRegionVisitor extends AbstractRegionVisitor {\n\t\tprivate final Supplier<BaseSwitchRegionVisitor> builder;\n\n\t\tpublic IterativeSwitchRegionVisitor(Supplier<BaseSwitchRegionVisitor> builder) {\n\t\t\tthis.builder = builder;\n\t\t}\n\n\t\t@Override\n\t\tpublic void leaveRegion(MethodNode mth, IRegion region) {\n\t\t\tif (region instanceof SwitchRegion) {\n\t\t\t\tSwitchRegion switchRegion = (SwitchRegion) region;\n\t\t\t\tBaseSwitchRegionVisitor switchVisitor = builder.get();\n\t\t\t\tswitchVisitor.setCurrentSwitch(switchRegion);\n\t\t\t\tboolean runAgain;\n\t\t\t\tint k = 0;\n\t\t\t\tdo {\n\t\t\t\t\trunAgain = false;\n\t\t\t\t\tDepthRegionTraversal.traverse(mth, switchRegion, switchVisitor);\n\t\t\t\t\tif (switchVisitor.isReRunRequested()) {\n\t\t\t\t\t\tswitchVisitor.reset();\n\t\t\t\t\t\trunAgain = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (k++ > 20) {\n\t\t\t\t\t\t// 20 nested 'if' are not expected\n\t\t\t\t\t\tmth.addWarnComment(\"Unexpected iteration count in SwitchBreakVisitor. Please report as an issue\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t} while (runAgain);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate abstract static class BaseSwitchRegionVisitor extends AbstractRegionVisitor {\n\t\tprotected final Set<IRegion> addBreakRegion = new HashSet<>();\n\t\tprotected final Set<IContainer> cleanupSet = new HashSet<>();\n\t\tprotected SwitchRegion currentSwitch;\n\t\tprivate boolean reRunRequested = false;\n\n\t\tpublic abstract void processRegion(MethodNode mth, IRegion region);\n\n\t\t@Override\n\t\tpublic boolean enterRegion(MethodNode mth, IRegion region) {\n\t\t\tprocessRegion(mth, region);\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic void leaveRegion(MethodNode mth, IRegion region) {\n\t\t\tif (addBreakRegion.contains(region)) {\n\t\t\t\taddBreakRegion.remove(region);\n\t\t\t\tregion.getSubBlocks().add(SwitchRegionMaker.buildBreakContainer(currentSwitch));\n\t\t\t}\n\t\t\tif (cleanupSet.contains(region)) {\n\t\t\t\tcleanupSet.remove(region);\n\t\t\t\tregion.getSubBlocks().removeIf(r -> r.contains(AFlag.REMOVE));\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Method called before visitor rerun\n\t\t */\n\t\tpublic void reset() {\n\t\t\treRunRequested = false;\n\t\t\taddBreakRegion.clear();\n\t\t\tcleanupSet.clear();\n\t\t}\n\n\t\tpublic void requestReRun() {\n\t\t\treRunRequested = true;\n\t\t}\n\n\t\tpublic boolean isReRunRequested() {\n\t\t\treturn reRunRequested;\n\t\t}\n\n\t\tpublic void setCurrentSwitch(SwitchRegion currentSwitch) {\n\t\t\tthis.currentSwitch = currentSwitch;\n\t\t}\n\n\t\tprotected boolean isBreakBlock(@Nullable IBlock block) {\n\t\t\tif (block != null) {\n\t\t\t\tInsnNode lastInsn = ListUtils.last(block.getInstructions());\n\t\t\t\tif (lastInsn != null && lastInsn.getType() == InsnType.BREAK) {\n\t\t\t\t\tRegionRefAttr regionRefAttr = lastInsn.get(AType.REGION_REF);\n\t\t\t\t\treturn regionRefAttr != null && regionRefAttr.getRegion() == currentSwitch;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tprotected void removeBreak(IBlock breakBlock, IContainer parentContainer) {\n\t\t\tList<InsnNode> instructions = breakBlock.getInstructions();\n\t\t\tInsnNode last = ListUtils.last(instructions);\n\t\t\tif (last != null && last.getType() == InsnType.BREAK) {\n\t\t\t\tListUtils.removeLast(instructions);\n\t\t\t\tif (instructions.isEmpty()) {\n\t\t\t\t\tbreakBlock.add(AFlag.REMOVE);\n\t\t\t\t\tcleanupSet.add(parentContainer);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"SwitchBreakVisitor\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/SwitchOverStringVisitor.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.IdentityHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.annotations.EncodedType;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.IAttributeNode;\nimport jadx.core.dex.attributes.nodes.CodeFeaturesAttr;\nimport jadx.core.dex.attributes.nodes.CodeFeaturesAttr.CodeFeature;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.IfNode;\nimport jadx.core.dex.instructions.IfOp;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.PhiInsn;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.SwitchRegion;\nimport jadx.core.dex.regions.conditions.Compare;\nimport jadx.core.dex.regions.conditions.IfCondition;\nimport jadx.core.dex.regions.conditions.IfRegion;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.RegionUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"SwitchOverStringVisitor\",\n\t\tdesc = \"Restore switch over string\",\n\t\trunAfter = IfRegionVisitor.class,\n\t\trunBefore = ReturnVisitor.class\n)\npublic class SwitchOverStringVisitor extends AbstractVisitor implements IRegionIterativeVisitor {\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (!CodeFeaturesAttr.contains(mth, CodeFeature.SWITCH)) {\n\t\t\treturn;\n\t\t}\n\t\tDepthRegionTraversal.traverseIterative(mth, this);\n\t}\n\n\t@Override\n\tpublic boolean visitRegion(MethodNode mth, IRegion region) {\n\t\tif (region instanceof SwitchRegion) {\n\t\t\treturn restoreSwitchOverString(mth, (SwitchRegion) region);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate boolean restoreSwitchOverString(MethodNode mth, SwitchRegion switchRegion) {\n\t\ttry {\n\t\t\tInsnNode swInsn = BlockUtils.getLastInsnWithType(switchRegion.getHeader(), InsnType.SWITCH);\n\t\t\tif (swInsn == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tRegisterArg strArg = getStrHashCodeArg(swInsn.getArg(0));\n\t\t\tif (strArg == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tint casesCount = switchRegion.getCases().size();\n\t\t\tboolean defaultCaseAdded = switchRegion.getCases().stream().anyMatch(SwitchRegion.CaseInfo::isDefaultCase);\n\t\t\tint casesWithString = defaultCaseAdded ? casesCount - 1 : casesCount;\n\t\t\tSSAVar strVar = strArg.getSVar();\n\t\t\tif (strVar.getUseCount() - 1 < casesWithString) {\n\t\t\t\t// one 'hashCode' invoke and at least one 'equals' per case\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// quick checks done, start collecting data to create a new switch region\n\t\t\tMap<InsnNode, String> strEqInsns = collectEqualsInsns(mth, strVar);\n\t\t\tif (strEqInsns.size() < casesWithString) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tSwitchData switchData = new SwitchData(mth, switchRegion);\n\t\t\tswitchData.setStrEqInsns(strEqInsns);\n\t\t\tswitchData.setCases(new ArrayList<>(casesCount));\n\t\t\tfor (SwitchRegion.CaseInfo swCaseInfo : switchRegion.getCases()) {\n\t\t\t\tif (!processCase(switchData, swCaseInfo)) {\n\t\t\t\t\tmth.addWarnComment(\"Failed to restore switch over string. Please report as a decompilation issue\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// match remapping var to collect code from second switch\n\t\t\tif (!mergeWithCode(switchData)) {\n\t\t\t\tmth.addWarnComment(\"Failed to restore switch over string. Please report as a decompilation issue\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// all checks passed, replace with new switch\n\t\t\tIRegion parentRegion = switchRegion.getParent();\n\t\t\tSwitchRegion replaceRegion = new SwitchRegion(parentRegion, switchRegion.getHeader());\n\t\t\tfor (SwitchRegion.CaseInfo caseInfo : switchData.getNewCases()) {\n\t\t\t\treplaceRegion.addCase(Collections.unmodifiableList(caseInfo.getKeys()), caseInfo.getContainer());\n\t\t\t}\n\t\t\tif (!parentRegion.replaceSubBlock(switchRegion, replaceRegion)) {\n\t\t\t\tmth.addWarnComment(\"Failed to restore switch over string. Please report as a decompilation issue\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// replace confirmed, remove original code\n\t\t\tmarkCodeForRemoval(switchData);\n\t\t\t// use string arg directly in switch\n\t\t\tswInsn.replaceArg(swInsn.getArg(0), strArg.duplicate());\n\t\t\treturn true;\n\t\t} catch (StackOverflowError | Exception e) {\n\t\t\tmth.addWarnComment(\"Failed to restore switch over string. Please report as a decompilation issue\", e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate static void markCodeForRemoval(SwitchData switchData) {\n\t\tMethodNode mth = switchData.getMth();\n\t\ttry {\n\t\t\tswitchData.getToRemove().forEach(i -> i.add(AFlag.REMOVE));\n\t\t\tSwitchRegion codeSwitch = switchData.getCodeSwitch();\n\t\t\tif (codeSwitch != null) {\n\t\t\t\tIRegion parentRegion = switchData.getSwitchRegion().getParent();\n\t\t\t\tparentRegion.getSubBlocks().remove(codeSwitch);\n\t\t\t\tcodeSwitch.getHeader().add(AFlag.REMOVE);\n\t\t\t}\n\t\t\tRegisterArg numArg = switchData.getNumArg();\n\t\t\tif (numArg != null) {\n\t\t\t\tfor (SSAVar ssaVar : numArg.getSVar().getCodeVar().getSsaVars()) {\n\t\t\t\t\tInsnNode assignInsn = ssaVar.getAssignInsn();\n\t\t\t\t\tif (assignInsn != null) {\n\t\t\t\t\t\tassignInsn.add(AFlag.REMOVE);\n\t\t\t\t\t}\n\t\t\t\t\tfor (RegisterArg useArg : ssaVar.getUseList()) {\n\t\t\t\t\t\tInsnNode parentInsn = useArg.getParentInsn();\n\t\t\t\t\t\tif (parentInsn != null) {\n\t\t\t\t\t\t\tparentInsn.add(AFlag.REMOVE);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tmth.removeSVar(ssaVar);\n\t\t\t\t}\n\t\t\t}\n\t\t\tInsnRemover.removeAllMarked(mth);\n\t\t} catch (StackOverflowError | Exception e) {\n\t\t\tmth.addWarnComment(\"Failed to clean up code after switch over string restore\", e);\n\t\t}\n\t}\n\n\tprivate boolean mergeWithCode(SwitchData switchData) {\n\t\t// check for second switch\n\t\tIContainer nextContainer = RegionUtils.getNextContainer(switchData.getMth(), switchData.getSwitchRegion());\n\t\tif (!(nextContainer instanceof SwitchRegion)) {\n\t\t\treturn false;\n\t\t}\n\t\tSwitchRegion codeSwitch = (SwitchRegion) nextContainer;\n\t\tInsnNode swInsn = BlockUtils.getLastInsnWithType(codeSwitch.getHeader(), InsnType.SWITCH);\n\t\tif (swInsn == null || !swInsn.getArg(0).isRegister()) {\n\t\t\treturn false;\n\t\t}\n\t\tRegisterArg numArg = (RegisterArg) swInsn.getArg(0);\n\n\t\tList<CaseData> cases = switchData.getCases();\n\t\t// search index assign in cases code\n\t\tint extracted = 0;\n\t\tfor (CaseData caseData : cases) {\n\t\t\tInsnNode numInsn = searchConstInsn(switchData, caseData, swInsn);\n\t\t\tInteger num = extractConstNumber(switchData, numInsn, numArg);\n\t\t\tif (num != null) {\n\t\t\t\tcaseData.setCodeNum(num);\n\t\t\t\textracted++;\n\t\t\t}\n\t\t}\n\t\tif (extracted == 0) {\n\t\t\t// nothing to merge, code already inside first switch cases\n\t\t\treturn true;\n\t\t}\n\t\tif (extracted != cases.size()) {\n\t\t\treturn false;\n\t\t}\n\t\t// TODO: additional checks for found index numbers\n\t\tcases.sort(Comparator.comparingInt(CaseData::getCodeNum));\n\n\t\t// extract complete\n\t\tMap<Integer, CaseData> casesMap = new HashMap<>(cases.size());\n\t\tfor (CaseData caseData : cases) {\n\t\t\tCaseData prev = casesMap.put(caseData.getCodeNum(), caseData);\n\t\t\tif (prev != null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tRegionUtils.visitBlocks(switchData.getMth(), caseData.getCode(),\n\t\t\t\t\tblock -> switchData.getToRemove().add(block));\n\t\t}\n\n\t\tList<SwitchRegion.CaseInfo> newCases = new ArrayList<>();\n\t\tfor (SwitchRegion.CaseInfo caseInfo : codeSwitch.getCases()) {\n\t\t\tSwitchRegion.CaseInfo newCase = null;\n\t\t\tfor (Object key : caseInfo.getKeys()) {\n\t\t\t\tInteger intKey = unwrapIntKey(key);\n\t\t\t\tif (intKey != null) {\n\t\t\t\t\tCaseData caseData = casesMap.remove(intKey);\n\t\t\t\t\tif (caseData == null) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (newCase == null) {\n\t\t\t\t\t\tList<Object> keys = new ArrayList<>(caseData.getStrValues());\n\t\t\t\t\t\tnewCase = new SwitchRegion.CaseInfo(keys, caseInfo.getContainer());\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// merge cases\n\t\t\t\t\t\tnewCase.getKeys().addAll(caseData.getStrValues());\n\t\t\t\t\t}\n\t\t\t\t} else if (key == SwitchRegion.DEFAULT_CASE_KEY) {\n\t\t\t\t\tvar iterator = casesMap.entrySet().iterator();\n\t\t\t\t\twhile (iterator.hasNext()) {\n\t\t\t\t\t\tCaseData caseData = iterator.next().getValue();\n\t\t\t\t\t\tif (newCase == null) {\n\t\t\t\t\t\t\tList<Object> keys = new ArrayList<>(caseData.getStrValues());\n\t\t\t\t\t\t\tnewCase = new SwitchRegion.CaseInfo(keys, caseInfo.getContainer());\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// merge cases\n\t\t\t\t\t\t\tnewCase.getKeys().addAll(caseData.getStrValues());\n\t\t\t\t\t\t}\n\t\t\t\t\t\titerator.remove();\n\t\t\t\t\t}\n\t\t\t\t\tif (newCase == null) {\n\t\t\t\t\t\tnewCase = new SwitchRegion.CaseInfo(new ArrayList<>(), caseInfo.getContainer());\n\t\t\t\t\t}\n\t\t\t\t\tnewCase.getKeys().add(SwitchRegion.DEFAULT_CASE_KEY);\n\t\t\t\t} else {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tnewCases.add(newCase);\n\t\t}\n\t\tswitchData.setCodeSwitch(codeSwitch);\n\t\tswitchData.setNumArg(numArg);\n\t\tswitchData.setNewCases(newCases);\n\t\treturn true;\n\t}\n\n\tprivate @Nullable Integer extractConstNumber(SwitchData switchData, @Nullable InsnNode numInsn, RegisterArg numArg) {\n\t\tif (numInsn == null || numInsn.getArgsCount() != 1) {\n\t\t\treturn null;\n\t\t}\n\t\tObject constVal = InsnUtils.getConstValueByArg(switchData.getMth().root(), numInsn.getArg(0));\n\t\tif (constVal instanceof LiteralArg) {\n\t\t\tif (numArg.sameCodeVar(numInsn.getResult())) {\n\t\t\t\treturn (int) ((LiteralArg) constVal).getLiteral();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static @Nullable InsnNode searchConstInsn(SwitchData switchData, CaseData caseData, InsnNode swInsn) {\n\t\tIContainer container = caseData.getCode();\n\t\tif (container != null) {\n\t\t\tList<InsnNode> insns = RegionUtils.collectInsns(switchData.getMth(), container);\n\t\t\tinsns.removeIf(i -> i.getType() == InsnType.BREAK);\n\t\t\tif (insns.size() == 1) {\n\t\t\t\treturn insns.get(0);\n\t\t\t}\n\t\t} else if (caseData.getBlockRef() != null) {\n\t\t\t// variable used unchanged on path from block ref\n\t\t\tBlockNode blockRef = caseData.getBlockRef();\n\t\t\tInsnArg swArg = swInsn.getArg(0);\n\t\t\tif (swArg.isRegister()) {\n\t\t\t\tInsnNode assignInsn = ((RegisterArg) swArg).getSVar().getAssignInsn();\n\t\t\t\tif (assignInsn != null && assignInsn.getType() == InsnType.PHI) {\n\t\t\t\t\tRegisterArg arg = ((PhiInsn) assignInsn).getArgByBlock(blockRef);\n\t\t\t\t\tif (arg != null) {\n\t\t\t\t\t\treturn arg.getAssignInsn();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate Integer unwrapIntKey(Object key) {\n\t\tif (key instanceof Integer) {\n\t\t\treturn (Integer) key;\n\t\t}\n\t\tif (key instanceof FieldNode) {\n\t\t\tEncodedValue encodedValue = ((FieldNode) key).get(JadxAttrType.CONSTANT_VALUE);\n\t\t\tif (encodedValue != null && encodedValue.getType() == EncodedType.ENCODED_INT) {\n\t\t\t\treturn (Integer) encodedValue.getValue();\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static Map<InsnNode, String> collectEqualsInsns(MethodNode mth, SSAVar strVar) {\n\t\tMap<InsnNode, String> map = new IdentityHashMap<>(strVar.getUseCount() - 1);\n\t\tfor (RegisterArg useReg : strVar.getUseList()) {\n\t\t\tInsnNode parentInsn = useReg.getParentInsn();\n\t\t\tif (parentInsn != null && parentInsn.getType() == InsnType.INVOKE) {\n\t\t\t\tInvokeNode inv = (InvokeNode) parentInsn;\n\t\t\t\tif (inv.getCallMth().getRawFullId().equals(\"java.lang.String.equals(Ljava/lang/Object;)Z\")) {\n\t\t\t\t\tInsnArg strArg = inv.getArg(1);\n\t\t\t\t\tObject strValue = InsnUtils.getConstValueByArg(mth.root(), strArg);\n\t\t\t\t\tif (strValue instanceof String) {\n\t\t\t\t\t\tmap.put(parentInsn, (String) strValue);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t}\n\n\tprivate boolean processCase(SwitchData switchData, SwitchRegion.CaseInfo caseInfo) {\n\t\tif (caseInfo.isDefaultCase()) {\n\t\t\tCaseData caseData = new CaseData();\n\t\t\tcaseData.setCode(caseInfo.getContainer());\n\t\t\treturn true;\n\t\t}\n\t\tAtomicBoolean fail = new AtomicBoolean(false);\n\t\tRegionUtils.visitRegions(switchData.getMth(), caseInfo.getContainer(), region -> {\n\t\t\tif (fail.get()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (region instanceof IfRegion) {\n\t\t\t\tCaseData caseData = fillCaseData((IfRegion) region, switchData);\n\t\t\t\tif (caseData == null) {\n\t\t\t\t\tfail.set(true);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tswitchData.getCases().add(caseData);\n\t\t\t}\n\t\t\treturn true;\n\t\t});\n\t\treturn !fail.get();\n\t}\n\n\tprivate @Nullable CaseData fillCaseData(IfRegion ifRegion, SwitchData switchData) {\n\t\tIfCondition condition = Objects.requireNonNull(ifRegion.getCondition());\n\t\tboolean neg = false;\n\t\tif (condition.getMode() == IfCondition.Mode.NOT) {\n\t\t\tcondition = condition.getArgs().get(0);\n\t\t\tneg = true;\n\t\t}\n\t\tCompare compare = condition.getCompare();\n\t\tif (compare == null) {\n\t\t\treturn null;\n\t\t}\n\t\tIfNode ifInsn = compare.getInsn();\n\t\tInsnArg firstArg = ifInsn.getArg(0);\n\t\tString str = null;\n\t\tif (firstArg.isInsnWrap()) {\n\t\t\tstr = switchData.getStrEqInsns().get(((InsnWrapArg) firstArg).getWrapInsn());\n\t\t}\n\t\tif (str == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (ifInsn.getOp() == IfOp.NE && ifInsn.getArg(1).isTrue()) {\n\t\t\tneg = true;\n\t\t}\n\t\tif (ifInsn.getOp() == IfOp.EQ && ifInsn.getArg(1).isFalse()) {\n\t\t\tneg = true;\n\t\t}\n\t\tswitchData.getToRemove().add(ifInsn);\n\t\tswitchData.getToRemove().addAll(ifRegion.getConditionBlocks());\n\n\t\tCaseData caseData = new CaseData();\n\t\tcaseData.getStrValues().add(str);\n\n\t\tIContainer codeContainer = neg ? ifRegion.getElseRegion() : ifRegion.getThenRegion();\n\t\tif (codeContainer == null) {\n\t\t\t// no code\n\t\t\t// use last condition block for later data tracing\n\t\t\tcaseData.setBlockRef(Utils.last(ifRegion.getConditionBlocks()));\n\t\t} else {\n\t\t\tcaseData.setCode(codeContainer);\n\t\t}\n\t\treturn caseData;\n\t}\n\n\tprivate @Nullable RegisterArg getStrHashCodeArg(InsnArg arg) {\n\t\tif (arg.isRegister()) {\n\t\t\treturn getStrFromInsn(((RegisterArg) arg).getAssignInsn());\n\t\t}\n\t\tif (arg.isInsnWrap()) {\n\t\t\treturn getStrFromInsn(((InsnWrapArg) arg).getWrapInsn());\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate @Nullable RegisterArg getStrFromInsn(@Nullable InsnNode insn) {\n\t\tif (insn == null || insn.getType() != InsnType.INVOKE) {\n\t\t\treturn null;\n\t\t}\n\t\tInvokeNode invInsn = (InvokeNode) insn;\n\t\tMethodInfo callMth = invInsn.getCallMth();\n\t\tif (!callMth.getRawFullId().equals(\"java.lang.String.hashCode()I\")) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnArg arg = invInsn.getInstanceArg();\n\t\tif (arg == null || !arg.isRegister()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn (RegisterArg) arg;\n\t}\n\n\tprivate static final class SwitchData {\n\t\tprivate final MethodNode mth;\n\t\tprivate final SwitchRegion switchRegion;\n\t\tprivate final List<IAttributeNode> toRemove = new ArrayList<>();\n\t\tprivate Map<InsnNode, String> strEqInsns;\n\t\tprivate List<CaseData> cases;\n\t\tprivate List<SwitchRegion.CaseInfo> newCases;\n\t\tprivate SwitchRegion codeSwitch;\n\t\tprivate RegisterArg numArg;\n\n\t\tprivate SwitchData(MethodNode mth, SwitchRegion switchRegion) {\n\t\t\tthis.mth = mth;\n\t\t\tthis.switchRegion = switchRegion;\n\t\t}\n\n\t\tpublic List<CaseData> getCases() {\n\t\t\treturn cases;\n\t\t}\n\n\t\tpublic void setCases(List<CaseData> cases) {\n\t\t\tthis.cases = cases;\n\t\t}\n\n\t\tpublic List<SwitchRegion.CaseInfo> getNewCases() {\n\t\t\treturn newCases;\n\t\t}\n\n\t\tpublic void setNewCases(List<SwitchRegion.CaseInfo> cases) {\n\t\t\tthis.newCases = cases;\n\t\t}\n\n\t\tpublic MethodNode getMth() {\n\t\t\treturn mth;\n\t\t}\n\n\t\tpublic Map<InsnNode, String> getStrEqInsns() {\n\t\t\treturn strEqInsns;\n\t\t}\n\n\t\tpublic void setStrEqInsns(Map<InsnNode, String> strEqInsns) {\n\t\t\tthis.strEqInsns = strEqInsns;\n\t\t}\n\n\t\tpublic SwitchRegion getSwitchRegion() {\n\t\t\treturn switchRegion;\n\t\t}\n\n\t\tpublic List<IAttributeNode> getToRemove() {\n\t\t\treturn toRemove;\n\t\t}\n\n\t\tpublic SwitchRegion getCodeSwitch() {\n\t\t\treturn codeSwitch;\n\t\t}\n\n\t\tpublic void setCodeSwitch(SwitchRegion codeSwitch) {\n\t\t\tthis.codeSwitch = codeSwitch;\n\t\t}\n\n\t\tpublic RegisterArg getNumArg() {\n\t\t\treturn numArg;\n\t\t}\n\n\t\tpublic void setNumArg(RegisterArg numArg) {\n\t\t\tthis.numArg = numArg;\n\t\t}\n\t}\n\n\tprivate static final class CaseData {\n\t\tprivate final List<String> strValues = new ArrayList<>();\n\t\tprivate @Nullable IContainer code = null;\n\t\tprivate @Nullable BlockNode blockRef = null;\n\t\tprivate int codeNum = -1;\n\n\t\tpublic List<String> getStrValues() {\n\t\t\treturn strValues;\n\t\t}\n\n\t\tpublic @Nullable IContainer getCode() {\n\t\t\treturn code;\n\t\t}\n\n\t\tpublic void setCode(@Nullable IContainer code) {\n\t\t\tthis.code = code;\n\t\t}\n\n\t\tpublic @Nullable BlockNode getBlockRef() {\n\t\t\treturn blockRef;\n\t\t}\n\n\t\tpublic void setBlockRef(@Nullable BlockNode blockRef) {\n\t\t\tthis.blockRef = blockRef;\n\t\t}\n\n\t\tpublic int getCodeNum() {\n\t\t\treturn codeNum;\n\t\t}\n\n\t\tpublic void setCodeNum(int codeNum) {\n\t\t\tthis.codeNum = codeNum;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"CaseData{\" + strValues + '}';\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryMod.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.PhiInsn;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.instructions.mods.TernaryInsn;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.Region;\nimport jadx.core.dex.regions.conditions.IfRegion;\nimport jadx.core.dex.visitors.shrink.CodeShrinkVisitor;\nimport jadx.core.utils.InsnList;\nimport jadx.core.utils.InsnRemover;\n\n/**\n * Convert 'if' to ternary operation\n */\npublic class TernaryMod extends AbstractRegionVisitor implements IRegionIterativeVisitor {\n\n\tprivate static final TernaryMod INSTANCE = new TernaryMod();\n\n\tpublic static void process(MethodNode mth) {\n\t\t// first: convert all found ternary nodes in one iteration\n\t\tDepthRegionTraversal.traverse(mth, INSTANCE);\n\t\tif (mth.contains(AFlag.REQUEST_CODE_SHRINK)) {\n\t\t\tCodeShrinkVisitor.shrinkMethod(mth);\n\t\t}\n\t\t// second: iterative runs with shrink after each change\n\t\tDepthRegionTraversal.traverseIterative(mth, INSTANCE);\n\t}\n\n\t@Override\n\tpublic boolean enterRegion(MethodNode mth, IRegion region) {\n\t\tif (processRegion(mth, region)) {\n\t\t\tmth.add(AFlag.REQUEST_CODE_SHRINK);\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean visitRegion(MethodNode mth, IRegion region) {\n\t\tif (processRegion(mth, region)) {\n\t\t\tCodeShrinkVisitor.shrinkMethod(mth);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean processRegion(MethodNode mth, IRegion region) {\n\t\tif (region instanceof IfRegion) {\n\t\t\treturn makeTernaryInsn(mth, (IfRegion) region);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean makeTernaryInsn(MethodNode mth, IfRegion ifRegion) {\n\t\tif (ifRegion.contains(AFlag.ELSE_IF_CHAIN)) {\n\t\t\treturn false;\n\t\t}\n\t\tIContainer thenRegion = ifRegion.getThenRegion();\n\t\tIContainer elseRegion = ifRegion.getElseRegion();\n\t\tif (thenRegion == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (elseRegion == null) {\n\t\t\treturn processOneBranchTernary(mth, ifRegion);\n\t\t}\n\t\tBlockNode tb = getTernaryInsnBlock(thenRegion);\n\t\tBlockNode eb = getTernaryInsnBlock(elseRegion);\n\t\tif (tb == null || eb == null) {\n\t\t\treturn false;\n\t\t}\n\t\tList<BlockNode> conditionBlocks = ifRegion.getConditionBlocks();\n\t\tif (conditionBlocks.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tBlockNode header = conditionBlocks.get(0);\n\t\tInsnNode thenInsn = tb.getInstructions().get(0);\n\t\tInsnNode elseInsn = eb.getInstructions().get(0);\n\n\t\tif (!verifyLineHints(mth, thenInsn, elseInsn)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tRegisterArg thenResArg = thenInsn.getResult();\n\t\tRegisterArg elseResArg = elseInsn.getResult();\n\t\tif (thenResArg != null && elseResArg != null) {\n\t\t\tPhiInsn thenPhi = thenResArg.getSVar().getOnlyOneUseInPhi();\n\t\t\tPhiInsn elsePhi = elseResArg.getSVar().getOnlyOneUseInPhi();\n\t\t\tif (thenPhi == null || thenPhi != elsePhi) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!ifRegion.getParent().replaceSubBlock(ifRegion, header)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tInsnList.remove(tb, thenInsn);\n\t\t\tInsnList.remove(eb, elseInsn);\n\n\t\t\tRegisterArg resArg;\n\t\t\tif (thenPhi.getArgsCount() == 2) {\n\t\t\t\tresArg = thenPhi.getResult();\n\t\t\t\tInsnRemover.unbindResult(mth, thenInsn);\n\t\t\t} else {\n\t\t\t\tresArg = thenResArg;\n\t\t\t\tthenPhi.removeArg(elseResArg);\n\t\t\t}\n\t\t\tInsnArg thenArg = InsnArg.wrapInsnIntoArg(thenInsn);\n\t\t\tInsnArg elseArg = InsnArg.wrapInsnIntoArg(elseInsn);\n\t\t\tTernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(), resArg, thenArg, elseArg);\n\t\t\tint branchLine = Math.max(thenInsn.getSourceLine(), elseInsn.getSourceLine());\n\t\t\tternInsn.setSourceLine(Math.max(ifRegion.getSourceLine(), branchLine));\n\n\t\t\tthenInsn.setResult(null); // unset without unbind, SSA var still in use\n\t\t\tInsnRemover.unbindResult(mth, elseInsn);\n\n\t\t\t// remove 'if' instruction\n\t\t\theader.getInstructions().clear();\n\t\t\tternInsn.rebindArgs();\n\t\t\theader.getInstructions().add(ternInsn);\n\n\t\t\tclearConditionBlocks(conditionBlocks, header);\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!mth.isVoidReturn()\n\t\t\t\t&& thenInsn.getType() == InsnType.RETURN\n\t\t\t\t&& elseInsn.getType() == InsnType.RETURN) {\n\t\t\tInsnArg thenArg = thenInsn.getArg(0);\n\t\t\tInsnArg elseArg = elseInsn.getArg(0);\n\t\t\tif (thenArg.isLiteral() != elseArg.isLiteral()) {\n\t\t\t\t// one arg is literal\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (!ifRegion.getParent().replaceSubBlock(ifRegion, header)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tInsnList.remove(tb, thenInsn);\n\t\t\tInsnList.remove(eb, elseInsn);\n\t\t\ttb.remove(AFlag.RETURN);\n\t\t\teb.remove(AFlag.RETURN);\n\n\t\t\tTernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(), null, thenArg, elseArg);\n\t\t\tInsnNode retInsn = new InsnNode(InsnType.RETURN, 1);\n\t\t\tInsnArg arg = InsnArg.wrapInsnIntoArg(ternInsn);\n\t\t\targ.setType(thenArg.getType());\n\t\t\tretInsn.addArg(arg);\n\n\t\t\theader.getInstructions().clear();\n\t\t\tretInsn.rebindArgs();\n\t\t\theader.getInstructions().add(retInsn);\n\t\t\theader.add(AFlag.RETURN);\n\n\t\t\tclearConditionBlocks(conditionBlocks, header);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean verifyLineHints(MethodNode mth, InsnNode thenInsn, InsnNode elseInsn) {\n\t\tif (mth.contains(AFlag.USE_LINES_HINTS)\n\t\t\t\t&& thenInsn.getSourceLine() != elseInsn.getSourceLine()) {\n\t\t\tif (thenInsn.getSourceLine() != 0 && elseInsn.getSourceLine() != 0) {\n\t\t\t\t// sometimes source lines incorrect\n\t\t\t\treturn checkLineStats(thenInsn, elseInsn);\n\t\t\t}\n\t\t\t// don't make nested ternary by default\n\t\t\t// TODO: add addition checks\n\t\t\treturn !containsTernary(thenInsn) && !containsTernary(elseInsn);\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static void clearConditionBlocks(List<BlockNode> conditionBlocks, BlockNode header) {\n\t\tfor (BlockNode block : conditionBlocks) {\n\t\t\tif (block != header) {\n\t\t\t\tblock.getInstructions().clear();\n\t\t\t\tblock.add(AFlag.REMOVE);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static BlockNode getTernaryInsnBlock(IContainer thenRegion) {\n\t\tif (thenRegion instanceof Region) {\n\t\t\tRegion r = (Region) thenRegion;\n\t\t\tif (r.getSubBlocks().size() == 1) {\n\t\t\t\tIContainer container = r.getSubBlocks().get(0);\n\t\t\t\tif (container instanceof BlockNode) {\n\t\t\t\t\tBlockNode block = (BlockNode) container;\n\t\t\t\t\tif (block.getInstructions().size() == 1) {\n\t\t\t\t\t\treturn block;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static boolean containsTernary(InsnNode insn) {\n\t\tif (insn.getType() == InsnType.TERNARY) {\n\t\t\treturn true;\n\t\t}\n\t\tfor (int i = 0; i < insn.getArgsCount(); i++) {\n\t\t\tInsnArg arg = insn.getArg(i);\n\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\tif (containsTernary(wrapInsn)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Return 'true' if there are several args with same source lines\n\t */\n\tprivate static boolean checkLineStats(InsnNode t, InsnNode e) {\n\t\tif (t.getResult() == null || e.getResult() == null) {\n\t\t\treturn false;\n\t\t}\n\t\tPhiInsn tPhi = t.getResult().getSVar().getOnlyOneUseInPhi();\n\t\tPhiInsn ePhi = e.getResult().getSVar().getOnlyOneUseInPhi();\n\t\tif (ePhi == null || tPhi != ePhi) {\n\t\t\treturn false;\n\t\t}\n\t\tMap<Integer, Integer> map = new HashMap<>(tPhi.getArgsCount());\n\t\tfor (InsnArg arg : tPhi.getArguments()) {\n\t\t\tif (!arg.isRegister()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tInsnNode assignInsn = ((RegisterArg) arg).getAssignInsn();\n\t\t\tif (assignInsn == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tint sourceLine = assignInsn.getSourceLine();\n\t\t\tif (sourceLine != 0) {\n\t\t\t\tmap.merge(sourceLine, 1, Integer::sum);\n\t\t\t}\n\t\t}\n\t\tfor (Map.Entry<Integer, Integer> entry : map.entrySet()) {\n\t\t\tif (entry.getValue() >= 2) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Convert one variable change with only 'then' branch:\n\t * 'if (c) {r = a;}' to 'r = c ? a : r'\n\t * Convert if 'r' used only once\n\t */\n\tprivate static boolean processOneBranchTernary(MethodNode mth, IfRegion ifRegion) {\n\t\tIContainer thenRegion = ifRegion.getThenRegion();\n\t\tBlockNode block = getTernaryInsnBlock(thenRegion);\n\t\tif (block != null) {\n\t\t\tInsnNode insn = block.getInstructions().get(0);\n\t\t\tRegisterArg result = insn.getResult();\n\t\t\tif (result != null) {\n\t\t\t\treplaceWithTernary(mth, ifRegion, block, insn);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@SuppressWarnings(\"StatementWithEmptyBody\")\n\tprivate static void replaceWithTernary(MethodNode mth, IfRegion ifRegion, BlockNode block, InsnNode insn) {\n\t\tRegisterArg resArg = insn.getResult();\n\t\tif (resArg.getSVar().getUseList().size() != 1) {\n\t\t\treturn;\n\t\t}\n\t\tPhiInsn phiInsn = resArg.getSVar().getOnlyOneUseInPhi();\n\t\tif (phiInsn == null || phiInsn.getArgsCount() != 2) {\n\t\t\treturn;\n\t\t}\n\t\tRegisterArg otherArg = null;\n\t\tfor (InsnArg arg : phiInsn.getArguments()) {\n\t\t\tif (!resArg.sameRegAndSVar(arg)) {\n\t\t\t\totherArg = (RegisterArg) arg;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (otherArg == null) {\n\t\t\treturn;\n\t\t}\n\t\tInsnNode elseAssign = otherArg.getAssignInsn();\n\t\tif (mth.isConstructor() || (mth.getParentClass().isEnum() && mth.getMethodInfo().isClassInit())) {\n\t\t\t// forcing ternary inline for constructors (will help in moving super call to the top) and enums\n\t\t\t// skip code style checks\n\t\t} else {\n\t\t\tif (elseAssign != null && elseAssign.isConstInsn()) {\n\t\t\t\tif (!verifyLineHints(mth, insn, elseAssign)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (insn.getResult().sameCodeVar(otherArg)) {\n\t\t\t\t\t// don't use same variable in else branch to prevent: l = (l == 0) ? 1 : l\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// all checks passed\n\t\tBlockNode header = ifRegion.getConditionBlocks().get(0);\n\t\tif (!ifRegion.getParent().replaceSubBlock(ifRegion, header)) {\n\t\t\treturn;\n\t\t}\n\t\tInsnArg elseArg;\n\t\tif (elseAssign != null && elseAssign.isConstInsn()) {\n\t\t\t// inline constant\n\t\t\tSSAVar elseVar = elseAssign.getResult().getSVar();\n\t\t\tif (elseVar.getUseCount() == 1 && elseVar.getOnlyOneUseInPhi() == phiInsn) {\n\t\t\t\tInsnRemover.remove(mth, elseAssign);\n\t\t\t}\n\t\t\telseArg = InsnArg.wrapInsnIntoArg(elseAssign);\n\t\t} else {\n\t\t\telseArg = otherArg.duplicate();\n\t\t}\n\t\tInsnArg thenArg = InsnArg.wrapInsnIntoArg(insn);\n\t\tRegisterArg resultArg = phiInsn.getResult().duplicate();\n\t\tTernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(), resultArg, thenArg, elseArg);\n\t\tternInsn.simplifyCondition();\n\n\t\tInsnRemover.unbindAllArgs(mth, phiInsn);\n\t\tInsnRemover.delistPhi(mth, phiInsn);\n\t\tInsnRemover.unbindResult(mth, insn);\n\t\tInsnList.remove(block, insn);\n\t\theader.getInstructions().clear();\n\t\tternInsn.rebindArgs();\n\t\theader.getInstructions().add(ternInsn);\n\n\t\tclearConditionBlocks(ifRegion.getConditionBlocks(), header);\n\n\t\t// shrink method again\n\t\tCodeShrinkVisitor.shrinkMethod(mth);\n\t}\n\n\tprivate TernaryMod() {\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/TracedRegionVisitor.java",
    "content": "package jadx.core.dex.visitors.regions;\n\nimport java.util.ArrayDeque;\nimport java.util.Deque;\n\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic abstract class TracedRegionVisitor implements IRegionVisitor {\n\n\tprotected final Deque<IRegion> regionStack = new ArrayDeque<>();\n\n\t@Override\n\tpublic boolean enterRegion(MethodNode mth, IRegion region) {\n\t\tregionStack.push(region);\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void processBlock(MethodNode mth, IBlock block) {\n\t\tIRegion curRegion = regionStack.peek();\n\t\tprocessBlockTraced(mth, block, curRegion);\n\t}\n\n\tpublic abstract void processBlockTraced(MethodNode mth, IBlock block, IRegion parentRegion);\n\n\t@Override\n\tpublic void leaveRegion(MethodNode mth, IRegion region) {\n\t\tregionStack.pop();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/ExcHandlersRegionMaker.java",
    "content": "package jadx.core.dex.visitors.regions.maker;\n\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.Region;\nimport jadx.core.dex.trycatch.ExcHandlerAttr;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.dex.trycatch.TryCatchBlockAttr;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.RegionUtils;\n\npublic class ExcHandlersRegionMaker {\n\tprivate final MethodNode mth;\n\tprivate final RegionMaker regionMaker;\n\n\tpublic ExcHandlersRegionMaker(MethodNode mth, RegionMaker regionMaker) {\n\t\tthis.mth = mth;\n\t\tthis.regionMaker = regionMaker;\n\t}\n\n\tpublic void process() {\n\t\tif (mth.isNoExceptionHandlers()) {\n\t\t\treturn;\n\t\t}\n\t\tIRegion excOutBlock = collectHandlerRegions();\n\t\tif (excOutBlock != null) {\n\t\t\tmth.getRegion().add(excOutBlock);\n\t\t}\n\t}\n\n\tprivate @Nullable IRegion collectHandlerRegions() {\n\t\tList<TryCatchBlockAttr> tcs = mth.getAll(AType.TRY_BLOCKS_LIST);\n\t\tfor (TryCatchBlockAttr tc : tcs) {\n\t\t\tList<BlockNode> blocks = new ArrayList<>(tc.getHandlersCount());\n\t\t\tSet<BlockNode> splitters = new HashSet<>();\n\t\t\tfor (ExceptionHandler handler : tc.getHandlers()) {\n\t\t\t\tBlockNode handlerBlock = handler.getHandlerBlock();\n\t\t\t\tif (handlerBlock != null) {\n\t\t\t\t\tblocks.add(handlerBlock);\n\t\t\t\t\tsplitters.add(BlockUtils.getTopSplitterForHandler(handlerBlock));\n\t\t\t\t} else {\n\t\t\t\t\tmth.addDebugComment(\"No exception handler block: \" + handler);\n\t\t\t\t}\n\t\t\t}\n\t\t\tSet<BlockNode> exits = new HashSet<>();\n\t\t\tfor (BlockNode splitter : splitters) {\n\t\t\t\tfor (BlockNode handler : blocks) {\n\t\t\t\t\tif (handler.contains(AFlag.REMOVE)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tList<BlockNode> s = splitter.getSuccessors();\n\t\t\t\t\tif (s.isEmpty()) {\n\t\t\t\t\t\tmth.addDebugComment(\"No successors for splitter: \" + splitter);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tBlockNode ss = s.get(0);\n\t\t\t\t\tBlockNode cross = BlockUtils.getPathCross(mth, ss, handler);\n\t\t\t\t\tif (cross != null && cross != ss && cross != handler) {\n\t\t\t\t\t\texits.add(cross);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (ExceptionHandler handler : tc.getHandlers()) {\n\t\t\t\tprocessExcHandler(handler, exits);\n\t\t\t}\n\t\t}\n\t\treturn processHandlersOutBlocks(tcs);\n\t}\n\n\t/**\n\t * Search handlers successor blocks aren't included in any region.\n\t */\n\tprivate @Nullable IRegion processHandlersOutBlocks(List<TryCatchBlockAttr> tcs) {\n\t\tSet<IBlock> allRegionBlocks = new HashSet<>();\n\t\tRegionUtils.getAllRegionBlocks(mth.getRegion(), allRegionBlocks);\n\n\t\tSet<IBlock> successorBlocks = new HashSet<>();\n\t\tfor (TryCatchBlockAttr tc : tcs) {\n\t\t\tfor (ExceptionHandler handler : tc.getHandlers()) {\n\t\t\t\tIContainer region = handler.getHandlerRegion();\n\t\t\t\tif (region != null) {\n\t\t\t\t\tIBlock lastBlock = RegionUtils.getLastBlock(region);\n\t\t\t\t\tif (lastBlock instanceof BlockNode) {\n\t\t\t\t\t\tsuccessorBlocks.addAll(((BlockNode) lastBlock).getSuccessors());\n\t\t\t\t\t}\n\t\t\t\t\tRegionUtils.getAllRegionBlocks(region, allRegionBlocks);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tsuccessorBlocks.removeAll(allRegionBlocks);\n\t\tif (successorBlocks.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tRegionStack stack = regionMaker.getStack();\n\t\tRegion excOutRegion = new Region(mth.getRegion());\n\t\tfor (IBlock block : successorBlocks) {\n\t\t\tif (block instanceof BlockNode) {\n\t\t\t\tstack.clear();\n\t\t\t\tstack.push(excOutRegion);\n\t\t\t\texcOutRegion.add(regionMaker.makeRegion((BlockNode) block));\n\t\t\t}\n\t\t}\n\t\treturn excOutRegion;\n\t}\n\n\tprivate void processExcHandler(ExceptionHandler handler, Set<BlockNode> exits) {\n\t\tBlockNode start = handler.getHandlerBlock();\n\t\tif (start == null) {\n\t\t\treturn;\n\t\t}\n\t\tRegionStack stack = regionMaker.getStack().clear();\n\t\tBlockNode dom;\n\t\tif (handler.isFinally()) {\n\t\t\tdom = BlockUtils.getTopSplitterForHandler(start);\n\t\t} else {\n\t\t\tdom = start;\n\t\t\tstack.addExits(exits);\n\t\t}\n\t\tif (dom.contains(AFlag.REMOVE)) {\n\t\t\treturn;\n\t\t}\n\t\tList<BlockNode> handlerExits = new ArrayList<>();\n\n\t\tBlockNode handlerOutBlock = BlockUtils.getTryAndHandlerCrossBlock(mth, handler);\n\t\tif (handlerOutBlock != null) {\n\t\t\t// ensure frontier's other predecessors comes from try end\n\t\t\thandlerExits.add(handlerOutBlock);\n\t\t} else {\n\t\t\t// fallback to simple frontier\n\t\t\tBitSet domFrontier = dom.getDomFrontier();\n\t\t\thandlerExits.addAll(BlockUtils.bitSetToBlocks(mth, domFrontier));\n\t\t}\n\n\t\tboolean inLoop = mth.getLoopForBlock(start) != null;\n\t\tfor (BlockNode exit : handlerExits) {\n\t\t\tif ((!inLoop || BlockUtils.isPathExists(start, exit))\n\t\t\t\t\t&& RegionUtils.isRegionContainsBlock(mth.getRegion(), exit)) {\n\t\t\t\tstack.addExit(exit);\n\t\t\t}\n\t\t}\n\t\thandler.setHandlerRegion(regionMaker.makeRegion(start));\n\n\t\tExcHandlerAttr excHandlerAttr = start.get(AType.EXC_HANDLER);\n\t\tif (excHandlerAttr == null) {\n\t\t\tmth.addWarn(\"Missing exception handler attribute for start block: \" + start);\n\t\t} else {\n\t\t\thandler.getHandlerRegion().addAttr(excHandlerAttr);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/IfRegionMaker.java",
    "content": "package jadx.core.dex.visitors.regions.maker;\n\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.Consts;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.EdgeInsnAttr;\nimport jadx.core.dex.attributes.nodes.LoopInfo;\nimport jadx.core.dex.instructions.IfNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnContainer;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.Region;\nimport jadx.core.dex.regions.conditions.IfCondition;\nimport jadx.core.dex.regions.conditions.IfInfo;\nimport jadx.core.dex.regions.conditions.IfRegion;\nimport jadx.core.dex.regions.loops.LoopRegion;\nimport jadx.core.dex.trycatch.ExcHandlerAttr;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.blocks.BlockSet;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.utils.BlockUtils.bitSetToBlocks;\nimport static jadx.core.utils.BlockUtils.bitSetToOneBlock;\nimport static jadx.core.utils.BlockUtils.followEmptyPath;\nimport static jadx.core.utils.BlockUtils.getBottomBlock;\nimport static jadx.core.utils.BlockUtils.getPathCross;\nimport static jadx.core.utils.BlockUtils.isEqualPaths;\nimport static jadx.core.utils.BlockUtils.isEqualReturnBlocks;\nimport static jadx.core.utils.BlockUtils.isPathExists;\nimport static jadx.core.utils.BlockUtils.newBlocksBitSet;\n\nfinal class IfRegionMaker {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(IfRegionMaker.class);\n\tprivate final MethodNode mth;\n\tprivate final RegionMaker regionMaker;\n\n\tIfRegionMaker(MethodNode mth, RegionMaker regionMaker) {\n\t\tthis.mth = mth;\n\t\tthis.regionMaker = regionMaker;\n\t}\n\n\tBlockNode process(IRegion currentRegion, BlockNode block, IfNode ifnode, RegionStack stack) {\n\n\t\tif (block.contains(AFlag.ADDED_TO_REGION)) {\n\t\t\t// block already included in other 'if' region\n\t\t\treturn ifnode.getThenBlock();\n\t\t}\n\n\t\tIfInfo currentIf = makeIfInfo(mth, block);\n\t\tif (currentIf == null) {\n\t\t\treturn null;\n\t\t}\n\t\tIfInfo mergedIf = mergeNestedIfNodes(currentIf);\n\t\tif (mergedIf != null) {\n\t\t\tcurrentIf = mergedIf;\n\t\t} else {\n\t\t\t// invert simple condition (compiler often do it)\n\t\t\t// ensure that we only ever invert once, because if multiple regions contain this block\n\t\t\t// we'll change the block after it's already been included in a region, which can cause\n\t\t\t// other regions containing the block to believe the condition has been flipped when it\n\t\t\t// has not, or vice versa.\n\t\t\tif (!block.contains(AFlag.DONT_INVERT)) {\n\t\t\t\tcurrentIf = IfInfo.invert(currentIf);\n\t\t\t\tblock.add(AFlag.DONT_INVERT);\n\t\t\t}\n\t\t}\n\t\tIfInfo modifiedIf = restructureIf(mth, block, currentIf);\n\t\tif (modifiedIf != null) {\n\t\t\tcurrentIf = modifiedIf;\n\t\t} else {\n\t\t\tif (currentIf.getMergedBlocks().size() <= 1) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tcurrentIf = makeIfInfo(mth, block);\n\t\t\tcurrentIf = restructureIf(mth, block, currentIf);\n\t\t\tif (currentIf == null) {\n\t\t\t\t// all attempts failed\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tconfirmMerge(currentIf);\n\n\t\tIfRegion ifRegion = new IfRegion(currentRegion);\n\t\tifRegion.updateCondition(currentIf);\n\t\tcurrentRegion.getSubBlocks().add(ifRegion);\n\n\t\tBlockNode outBlock = currentIf.getOutBlock();\n\t\tstack.push(ifRegion);\n\t\tstack.addExit(outBlock);\n\n\t\tBlockNode thenBlock = currentIf.getThenBlock();\n\t\tif (thenBlock == null) {\n\t\t\t// empty then block, not normal, but maybe correct\n\t\t\tifRegion.setThenRegion(new Region(ifRegion));\n\t\t} else {\n\t\t\tifRegion.setThenRegion(regionMaker.makeRegion(thenBlock));\n\t\t}\n\t\tBlockNode elseBlock = currentIf.getElseBlock();\n\t\tif (elseBlock == null || stack.containsExit(elseBlock)) {\n\t\t\tifRegion.setElseRegion(null);\n\t\t} else {\n\t\t\tifRegion.setElseRegion(regionMaker.makeRegion(elseBlock));\n\t\t}\n\n\t\t// insert edge insns in new 'else' branch\n\t\tif (ifRegion.getElseRegion() == null && outBlock != null) {\n\t\t\tList<EdgeInsnAttr> edgeInsnAttrs = outBlock.getAll(AType.EDGE_INSN);\n\t\t\tif (!edgeInsnAttrs.isEmpty()) {\n\t\t\t\tList<InsnNode> instructions = new ArrayList<>();\n\t\t\t\tfor (EdgeInsnAttr edgeInsnAttr : edgeInsnAttrs) {\n\t\t\t\t\tif (edgeInsnAttr.getEnd().equals(outBlock)) {\n\t\t\t\t\t\tif (currentIf.getMergedBlocks().contains(followEmptyPath(edgeInsnAttr.getStart(), true))) {\n\t\t\t\t\t\t\tinstructions.add(edgeInsnAttr.getInsn());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!instructions.isEmpty()) {\n\t\t\t\t\tRegion elseRegion = new Region(ifRegion);\n\t\t\t\t\tInsnContainer newBlock = new InsnContainer(instructions);\n\t\t\t\t\telseRegion.add(newBlock);\n\t\t\t\t\tifRegion.setElseRegion(elseRegion);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tstack.pop();\n\t\treturn outBlock;\n\t}\n\n\t@NotNull\n\tIfInfo buildIfInfo(LoopRegion loopRegion) {\n\t\tIfInfo condInfo = makeIfInfo(mth, loopRegion.getHeader());\n\t\tcondInfo = searchNestedIf(condInfo);\n\t\tconfirmMerge(condInfo);\n\t\treturn condInfo;\n\t}\n\n\t@Nullable\n\tstatic IfInfo makeIfInfo(MethodNode mth, BlockNode ifBlock) {\n\t\tInsnNode lastInsn = BlockUtils.getLastInsn(ifBlock);\n\t\tif (lastInsn == null || lastInsn.getType() != InsnType.IF) {\n\t\t\treturn null;\n\t\t}\n\t\tIfNode ifNode = (IfNode) lastInsn;\n\t\tIfCondition condition = IfCondition.fromIfNode(ifNode);\n\t\tIfInfo info = new IfInfo(mth, condition, ifNode.getThenBlock(), ifNode.getElseBlock());\n\t\tinfo.getMergedBlocks().add(ifBlock);\n\t\treturn info;\n\t}\n\n\tstatic IfInfo searchNestedIf(IfInfo info) {\n\t\tIfInfo next = mergeNestedIfNodes(info);\n\t\tif (next != null) {\n\t\t\treturn next;\n\t\t}\n\t\treturn info;\n\t}\n\n\tstatic IfInfo restructureIf(MethodNode mth, BlockNode block, IfInfo info) {\n\t\tBlockNode thenBlock = info.getThenBlock();\n\t\tBlockNode elseBlock = info.getElseBlock();\n\n\t\tif (Objects.equals(thenBlock, elseBlock)) {\n\t\t\tIfInfo ifInfo = new IfInfo(info, null, null);\n\t\t\tifInfo.setOutBlock(thenBlock);\n\t\t\treturn ifInfo;\n\t\t}\n\n\t\t// select 'then', 'else' and 'exit' blocks\n\t\tif (thenBlock.contains(AFlag.RETURN) && elseBlock.contains(AFlag.RETURN)) {\n\t\t\tinfo.setOutBlock(null);\n\t\t\treturn info;\n\t\t}\n\t\t// init outblock, which will be used in isBadBranchBlock to compare with branch block\n\t\tinfo.setOutBlock(findOutBlock(mth, thenBlock, elseBlock));\n\n\t\tboolean badThen = isBadBranchBlock(info, thenBlock);\n\t\tboolean badElse = isBadBranchBlock(info, elseBlock);\n\t\tif (badThen && badElse) {\n\t\t\tif (Consts.DEBUG_RESTRUCTURE) {\n\t\t\t\tLOG.debug(\"Stop processing blocks after 'if': {}, method: {}\", info.getMergedBlocks(), mth);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tif (badElse) {\n\t\t\tinfo = new IfInfo(info, thenBlock, null);\n\t\t\tinfo.setOutBlock(elseBlock);\n\t\t} else if (badThen) {\n\t\t\tinfo = IfInfo.invert(info);\n\t\t\tinfo = new IfInfo(info, elseBlock, null);\n\t\t\tinfo.setOutBlock(thenBlock);\n\t\t}\n\n\t\t// getPathCross may not find outBlock (e.g. one branch has return, outBlock definitely is\n\t\t// null), so should check further\n\t\tif (info.getOutBlock() == null) {\n\t\t\tBlockNode scopeOutBlockThen = findScopeOutBlock(mth, info.getThenBlock());\n\t\t\tBlockNode scopeOutBlockElse = findScopeOutBlock(mth, info.getElseBlock());\n\t\t\tif (scopeOutBlockThen == null && scopeOutBlockElse != null) {\n\t\t\t\tinfo.setOutBlock(scopeOutBlockElse);\n\t\t\t} else if (scopeOutBlockThen != null && scopeOutBlockElse == null) {\n\t\t\t\tinfo.setOutBlock(scopeOutBlockThen);\n\t\t\t} else if (scopeOutBlockThen != null && scopeOutBlockThen == scopeOutBlockElse) {\n\t\t\t\tinfo.setOutBlock(scopeOutBlockThen);\n\t\t\t}\n\t\t}\n\n\t\tif (BlockUtils.isBackEdge(block, info.getOutBlock())) {\n\t\t\tinfo.setOutBlock(null);\n\t\t}\n\t\treturn info;\n\t}\n\n\tstatic BlockNode findOutBlock(MethodNode mth, BlockNode thenBlock, BlockNode elseBlock) {\n\t\tif (thenBlock == elseBlock) {\n\t\t\treturn thenBlock;\n\t\t}\n\t\tif (thenBlock == null || elseBlock == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tBitSet thenDomFrontier = newBlocksBitSet(mth);\n\t\tthenDomFrontier.or(thenBlock.getDomFrontier());\n\t\tthenDomFrontier.set(thenBlock.getPos());\n\n\t\tBitSet elseDomFrontier = newBlocksBitSet(mth);\n\t\telseDomFrontier.or(elseBlock.getDomFrontier());\n\t\telseDomFrontier.set(elseBlock.getPos());\n\n\t\tBitSet intersection = newBlocksBitSet(mth);\n\t\tintersection.or(thenDomFrontier);\n\t\tintersection.and(elseDomFrontier);\n\n\t\tintersection.clear(mth.getExitBlock().getPos());\n\t\tBlockNode oneBlock = bitSetToOneBlock(mth, intersection);\n\n\t\t// Attempt one: there's a unique block in the intersection of dom frontiers, and no path from\n\t\t// then->else or else->then\n\t\tif (oneBlock != null) {\n\t\t\treturn oneBlock;\n\t\t}\n\n\t\tBitSet union = newBlocksBitSet(mth);\n\t\tunion.or(thenBlock.getDomFrontier());\n\t\tunion.or(elseBlock.getDomFrontier());\n\t\tunion.clear(mth.getExitBlock().getPos());\n\n\t\t// Attempt two: look for a suitable block in the union.\n\t\tBitSet candidates = newBlocksBitSet(mth);\n\t\tfor (BlockNode candidate : bitSetToBlocks(mth, union)) {\n\t\t\tif (isCandidateForOutBlock(mth, thenBlock, elseBlock, candidate)) {\n\t\t\t\tcandidates.set(candidate.getPos());\n\t\t\t}\n\t\t}\n\n\t\tBlockNode bottom = getBottomBlock(bitSetToBlocks(mth, candidates), true);\n\t\tif (bottom != null) {\n\t\t\treturn bottom;\n\t\t}\n\n\t\t// Attempt three: fallback to path cross again\n\t\treturn getPathCross(mth, thenBlock, elseBlock);\n\t}\n\n\tstatic boolean isCandidateForOutBlock(MethodNode mth, BlockNode thenBlock, BlockNode elseBlock, BlockNode candidate) {\n\t\t// a candidate block requires:\n\t\t// - >1 predecessor\n\t\t// - each predecessor has a clean path from elseBlock or thenBlock, and there exist predecessors\n\t\t// covering both cases\n\t\t// - inside the union of the two dom frontiers\n\n\t\tif (candidate.getPredecessors().size() < 2) {\n\t\t\treturn false; // block has only one pred, and so can't be the outblock\n\t\t}\n\n\t\tBitSet coverageThenPreds = newBlocksBitSet(mth);\n\t\tBitSet coverageElsePreds = newBlocksBitSet(mth);\n\n\t\tif (candidate == elseBlock) {\n\t\t\tcoverageElsePreds.set(candidate.getPos());\n\t\t}\n\t\tif (candidate == thenBlock) {\n\t\t\tcoverageThenPreds.set(candidate.getPos());\n\t\t}\n\n\t\tfor (BlockNode pred : candidate.getPredecessors()) {\n\t\t\tif (isPathExists(thenBlock, pred)) {\n\t\t\t\tcoverageThenPreds.set(pred.getPos());\n\t\t\t}\n\n\t\t\tif (isPathExists(elseBlock, pred)) {\n\t\t\t\tcoverageElsePreds.set(pred.getPos());\n\t\t\t}\n\t\t}\n\t\tif (coverageElsePreds.cardinality() == 0 || coverageThenPreds.cardinality() == 0) {\n\t\t\treturn false; // block has no path to both the then and else blocks\n\t\t}\n\n\t\tBlockNode coverageElsePred = bitSetToOneBlock(mth, coverageElsePreds);\n\t\tBlockNode coverageThenPred = bitSetToOneBlock(mth, coverageThenPreds);\n\t\tif (coverageElsePred != null && coverageElsePred == coverageThenPred) {\n\t\t\treturn false; // the only paths from else and then go through the same block\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate static boolean isBadBranchBlock(IfInfo info, BlockNode block) {\n\t\t// check if block at end of loop edge\n\t\tif (block.contains(AFlag.LOOP_START) && block.getPredecessors().size() == 1) {\n\t\t\tBlockNode pred = block.getPredecessors().get(0);\n\t\t\tif (pred.contains(AFlag.LOOP_END)) {\n\t\t\t\tList<LoopInfo> startLoops = block.getAll(AType.LOOP);\n\t\t\t\tList<LoopInfo> endLoops = pred.getAll(AType.LOOP);\n\t\t\t\t// search for same loop\n\t\t\t\tfor (LoopInfo startLoop : startLoops) {\n\t\t\t\t\tfor (LoopInfo endLoop : endLoops) {\n\t\t\t\t\t\tif (startLoop == endLoop) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// if branch block itself is outblock\n\t\tif (info.getOutBlock() != null) {\n\t\t\treturn block == info.getOutBlock();\n\t\t}\n\t\treturn !allPathsFromIf(block, info);\n\t}\n\n\tprivate static boolean allPathsFromIf(BlockNode block, IfInfo info) {\n\t\tList<BlockNode> preds = block.getPredecessors();\n\t\tBlockSet ifBlocks = info.getMergedBlocks();\n\t\tfor (BlockNode pred : preds) {\n\t\t\tif (pred.contains(AFlag.LOOP_END)) {\n\t\t\t\t// ignore loop back edge\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tBlockNode top = BlockUtils.skipSyntheticPredecessor(pred);\n\t\t\tif (!ifBlocks.contains(top)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * if startBlock is in a (try) scope, find the scope end as outBlock\n\t */\n\tprivate @Nullable static BlockNode findScopeOutBlock(MethodNode mth, BlockNode startBlock) {\n\t\tif (startBlock == null) {\n\t\t\treturn null;\n\t\t}\n\t\tList<BlockNode> domFrontiers = BlockUtils.bitSetToBlocks(mth, startBlock.getDomFrontier());\n\t\tBlockNode scopeOutBlock = null;\n\n\t\t// find handler from domFrontier(could be scope end), if domFrontier is handler\n\t\t// and its topSplitter dominates branch block, then branch should end\n\t\tfor (BlockNode domFrontier : domFrontiers) {\n\t\t\tExcHandlerAttr handler = domFrontier.get(AType.EXC_HANDLER);\n\t\t\tif (handler == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tBlockNode topSplitter = handler.getTryBlock().getTopSplitter();\n\t\t\tif (startBlock.isDominator(topSplitter)) {\n\t\t\t\tscopeOutBlock = BlockUtils.getTryAndHandlerCrossBlock(mth, handler.getHandler());\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn scopeOutBlock;\n\t}\n\n\tstatic IfInfo mergeNestedIfNodes(IfInfo currentIf) {\n\t\tBlockNode curThen = currentIf.getThenBlock();\n\t\tBlockNode curElse = currentIf.getElseBlock();\n\t\tif (curThen == curElse) {\n\t\t\treturn null;\n\t\t}\n\t\tif (BlockUtils.isFollowBackEdge(curThen)\n\t\t\t\t|| BlockUtils.isFollowBackEdge(curElse)) {\n\t\t\treturn null;\n\t\t}\n\t\tboolean followThenBranch;\n\t\tIfInfo nextIf = getNextIf(currentIf, curThen);\n\t\tif (nextIf != null) {\n\t\t\tfollowThenBranch = true;\n\t\t} else {\n\t\t\tnextIf = getNextIf(currentIf, curElse);\n\t\t\tif (nextIf != null) {\n\t\t\t\tfollowThenBranch = false;\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tboolean assignInlineNeeded = !nextIf.getForceInlineInsns().isEmpty();\n\t\tif (assignInlineNeeded) {\n\t\t\tfor (BlockNode mergedBlock : currentIf.getMergedBlocks()) {\n\t\t\t\tif (mergedBlock.contains(AFlag.LOOP_START)) {\n\t\t\t\t\t// don't inline assigns into loop condition\n\t\t\t\t\treturn currentIf;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (isInversionNeeded(currentIf, nextIf)) {\n\t\t\t// invert current node for match pattern\n\t\t\tnextIf = IfInfo.invert(nextIf);\n\t\t}\n\t\tboolean thenPathSame = isEqualPaths(curThen, nextIf.getThenBlock());\n\t\tboolean elsePathSame = isEqualPaths(curElse, nextIf.getElseBlock());\n\t\tif (!thenPathSame && !elsePathSame) {\n\t\t\t// complex condition, run additional checks\n\t\t\tif (checkConditionBranches(curThen, curElse)\n\t\t\t\t\t|| checkConditionBranches(curElse, curThen)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tBlockNode otherBranchBlock = followThenBranch ? curElse : curThen;\n\t\t\totherBranchBlock = BlockUtils.followEmptyPath(otherBranchBlock);\n\t\t\tif (!isPathExists(nextIf.getFirstIfBlock(), otherBranchBlock)) {\n\t\t\t\treturn checkForTernaryInCondition(currentIf);\n\t\t\t}\n\n\t\t\t// this is nested conditions with different mode (i.e (a && b) || c),\n\t\t\t// search next condition for merge, get null if failed\n\t\t\tIfInfo tmpIf = mergeNestedIfNodes(nextIf);\n\t\t\tif (tmpIf != null) {\n\t\t\t\tnextIf = tmpIf;\n\t\t\t\tif (isInversionNeeded(currentIf, nextIf)) {\n\t\t\t\t\tnextIf = IfInfo.invert(nextIf);\n\t\t\t\t}\n\t\t\t\tif (!canMerge(currentIf, nextIf, followThenBranch)) {\n\t\t\t\t\treturn currentIf;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn currentIf;\n\t\t\t}\n\t\t} else {\n\t\t\tif (assignInlineNeeded) {\n\t\t\t\tboolean sameOuts = (thenPathSame && !followThenBranch) || (elsePathSame && followThenBranch);\n\t\t\t\tif (!sameOuts) {\n\t\t\t\t\t// don't inline assigns inside simple condition\n\t\t\t\t\tcurrentIf.resetForceInlineInsns();\n\t\t\t\t\treturn currentIf;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tIfInfo result = mergeIfInfo(currentIf, nextIf, followThenBranch);\n\t\t// search next nested if block\n\t\treturn searchNestedIf(result);\n\t}\n\n\tprivate static IfInfo checkForTernaryInCondition(IfInfo currentIf) {\n\t\tIfInfo nextThen = getNextIf(currentIf, currentIf.getThenBlock());\n\t\tIfInfo nextElse = getNextIf(currentIf, currentIf.getElseBlock());\n\t\tif (nextThen == null || nextElse == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!nextThen.getFirstIfBlock().getDomFrontier().equals(nextElse.getFirstIfBlock().getDomFrontier())) {\n\t\t\treturn null;\n\t\t}\n\t\tnextThen = searchNestedIf(nextThen);\n\t\tnextElse = searchNestedIf(nextElse);\n\t\tif (nextThen.getThenBlock() == nextElse.getThenBlock()\n\t\t\t\t&& nextThen.getElseBlock() == nextElse.getElseBlock()) {\n\t\t\treturn mergeTernaryConditions(currentIf, nextThen, nextElse);\n\t\t}\n\t\tif (nextThen.getThenBlock() == nextElse.getElseBlock()\n\t\t\t\t&& nextThen.getElseBlock() == nextElse.getThenBlock()) {\n\t\t\tnextElse = IfInfo.invert(nextElse);\n\t\t\treturn mergeTernaryConditions(currentIf, nextThen, nextElse);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static IfInfo mergeTernaryConditions(IfInfo currentIf, IfInfo nextThen, IfInfo nextElse) {\n\t\tIfCondition newCondition = IfCondition.ternary(currentIf.getCondition(),\n\t\t\t\tnextThen.getCondition(), nextElse.getCondition());\n\t\tIfInfo result = new IfInfo(currentIf.getMth(), newCondition, nextThen.getThenBlock(), nextThen.getElseBlock());\n\t\tresult.merge(currentIf, nextThen, nextElse);\n\t\tconfirmMerge(result);\n\t\treturn result;\n\t}\n\n\tprivate static boolean isInversionNeeded(IfInfo currentIf, IfInfo nextIf) {\n\t\treturn isEqualPaths(currentIf.getElseBlock(), nextIf.getThenBlock())\n\t\t\t\t|| isEqualPaths(currentIf.getThenBlock(), nextIf.getElseBlock());\n\t}\n\n\tprivate static boolean canMerge(IfInfo a, IfInfo b, boolean followThenBranch) {\n\t\tif (followThenBranch) {\n\t\t\treturn isEqualPaths(a.getElseBlock(), b.getElseBlock());\n\t\t} else {\n\t\t\treturn isEqualPaths(a.getThenBlock(), b.getThenBlock());\n\t\t}\n\t}\n\n\tprivate static boolean checkConditionBranches(BlockNode from, BlockNode to) {\n\t\treturn from.getCleanSuccessors().size() == 1 && from.getCleanSuccessors().contains(to);\n\t}\n\n\tstatic IfInfo mergeIfInfo(IfInfo first, IfInfo second, boolean followThenBranch) {\n\t\tMethodNode mth = first.getMth();\n\t\tSet<BlockNode> skipBlocks = first.getSkipBlocks();\n\t\tBlockNode thenBlock;\n\t\tBlockNode elseBlock;\n\t\tif (followThenBranch) {\n\t\t\tthenBlock = second.getThenBlock();\n\t\t\telseBlock = getBranchBlock(first.getElseBlock(), second.getElseBlock(), skipBlocks, mth);\n\t\t} else {\n\t\t\tthenBlock = getBranchBlock(first.getThenBlock(), second.getThenBlock(), skipBlocks, mth);\n\t\t\telseBlock = second.getElseBlock();\n\t\t}\n\t\tIfCondition.Mode mergeOperation = followThenBranch ? IfCondition.Mode.AND : IfCondition.Mode.OR;\n\t\tIfCondition condition = IfCondition.merge(mergeOperation, first.getCondition(), second.getCondition());\n\t\tIfInfo result = new IfInfo(mth, condition, thenBlock, elseBlock);\n\t\tresult.merge(first, second);\n\t\treturn result;\n\t}\n\n\tprivate static BlockNode getBranchBlock(BlockNode first, BlockNode second, Set<BlockNode> skipBlocks, MethodNode mth) {\n\t\tif (first == second) {\n\t\t\treturn second;\n\t\t}\n\t\tif (isEqualReturnBlocks(first, second)) {\n\t\t\tskipBlocks.add(first);\n\t\t\treturn second;\n\t\t}\n\t\tif (BlockUtils.isDuplicateBlockPath(first, second)) {\n\t\t\tfirst.add(AFlag.REMOVE);\n\t\t\tskipBlocks.add(first);\n\t\t\treturn second;\n\t\t}\n\t\tBlockNode cross = BlockUtils.getPathCross(mth, first, second);\n\t\tif (cross != null) {\n\t\t\tBlockUtils.visitBlocksOnPath(mth, first, cross, skipBlocks::add);\n\t\t\tBlockUtils.visitBlocksOnPath(mth, second, cross, skipBlocks::add);\n\t\t\tskipBlocks.remove(cross);\n\t\t\treturn cross;\n\t\t}\n\t\tBlockNode firstSkip = BlockUtils.followEmptyPath(first);\n\t\tBlockNode secondSkip = BlockUtils.followEmptyPath(second);\n\t\tif (firstSkip.equals(secondSkip) || isEqualReturnBlocks(firstSkip, secondSkip)) {\n\t\t\tskipBlocks.add(first);\n\t\t\tskipBlocks.add(second);\n\t\t\tBlockUtils.visitBlocksOnEmptyPath(first, skipBlocks::add);\n\t\t\tBlockUtils.visitBlocksOnEmptyPath(second, skipBlocks::add);\n\t\t\treturn secondSkip;\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Unexpected merge pattern\");\n\t}\n\n\tstatic void confirmMerge(IfInfo info) {\n\t\tif (info.getMergedBlocks().size() > 1) {\n\t\t\tfor (BlockNode block : info.getMergedBlocks()) {\n\t\t\t\tif (block != info.getFirstIfBlock()) {\n\t\t\t\t\tblock.add(AFlag.ADDED_TO_REGION);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!info.getSkipBlocks().isEmpty()) {\n\t\t\tfor (BlockNode block : info.getSkipBlocks()) {\n\t\t\t\tblock.add(AFlag.ADDED_TO_REGION);\n\t\t\t}\n\t\t\tinfo.getSkipBlocks().clear();\n\t\t}\n\t\tfor (InsnNode forceInlineInsn : info.getForceInlineInsns()) {\n\t\t\tforceInlineInsn.add(AFlag.FORCE_ASSIGN_INLINE);\n\t\t}\n\t}\n\n\tprivate static IfInfo getNextIf(IfInfo info, BlockNode block) {\n\t\tif (!canSelectNext(info, block)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn getNextIfNodeInfo(info, block);\n\t}\n\n\tprivate static boolean canSelectNext(IfInfo info, BlockNode block) {\n\t\tif (block.getPredecessors().size() == 1) {\n\t\t\treturn true;\n\t\t}\n\t\treturn info.getMergedBlocks().containsAll(block.getPredecessors());\n\t}\n\n\tprivate static IfInfo getNextIfNodeInfo(IfInfo info, BlockNode block) {\n\t\tif (block == null || block.contains(AType.LOOP) || block.contains(AFlag.ADDED_TO_REGION)) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnNode lastInsn = BlockUtils.getLastInsn(block);\n\t\tif (lastInsn != null && lastInsn.getType() == InsnType.IF) {\n\t\t\treturn makeIfInfo(info.getMth(), block);\n\t\t}\n\t\tBlockNode next = getNextBlockInIfSuccessorChain(block);\n\t\tif (next == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (next.getPredecessors().size() != 1 || next.contains(AFlag.ADDED_TO_REGION)) {\n\t\t\treturn null;\n\t\t}\n\t\tList<InsnNode> forceInlineInsns = new ArrayList<>();\n\t\tif (!checkInsnsInline(block, next, forceInlineInsns)) {\n\t\t\treturn null;\n\t\t}\n\t\tIfInfo nextInfo = makeIfInfo(info.getMth(), next);\n\t\tif (nextInfo == null) {\n\t\t\treturn getNextIfNodeInfo(info, next);\n\t\t}\n\t\tnextInfo.addInsnsForForcedInline(forceInlineInsns);\n\t\treturn nextInfo;\n\t}\n\n\t/**\n\t * Allow singular successor to block or 2 successors where one is a EXC_BOTTOM_SPLITTER\n\t */\n\tprivate static @Nullable BlockNode getNextBlockInIfSuccessorChain(BlockNode block) {\n\n\t\t// skip this block and search in successors chain\n\t\tList<BlockNode> successors = block.getSuccessors();\n\t\tif (successors.size() > 2 || successors.size() == 0) {\n\t\t\treturn null;\n\t\t}\n\t\t// We might have the next IF and a EXC_BOTTOM_SPLITTER block to delimit a try region\n\t\tBlockNode first = successors.get(0);\n\t\tif (successors.size() == 1) {\n\t\t\treturn first;\n\t\t}\n\t\tBlockNode second = successors.get(1);\n\t\tboolean firstIsHandlerPath = first.contains(AFlag.EXC_BOTTOM_SPLITTER);\n\t\tboolean secondIsHandlerPath = second.contains(AFlag.EXC_BOTTOM_SPLITTER);\n\t\tif (!firstIsHandlerPath && !secondIsHandlerPath) {\n\t\t\t// unknown case\n\t\t\treturn null;\n\t\t}\n\t\tif (firstIsHandlerPath && secondIsHandlerPath) {\n\t\t\t// unknown case\n\t\t\treturn null;\n\t\t}\n\t\tBlockNode candidate = firstIsHandlerPath ? second : first;\n\n\t\t// Continue to recurse through blocks as long as none of them have any instructions\n\t\tif (candidate.getInstructions().isEmpty()) {\n\t\t\treturn getNextBlockInIfSuccessorChain(candidate);\n\t\t}\n\n\t\treturn candidate;\n\t}\n\n\t/**\n\t * Check that all instructions can be inlined\n\t */\n\tprivate static boolean checkInsnsInline(BlockNode block, BlockNode next, List<InsnNode> forceInlineInsns) {\n\t\tList<InsnNode> insns = block.getInstructions();\n\t\tif (insns.isEmpty()) {\n\t\t\treturn true;\n\t\t}\n\t\tboolean pass = true;\n\t\tfor (InsnNode insn : insns) {\n\t\t\tRegisterArg res = insn.getResult();\n\t\t\tif (res == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tList<RegisterArg> useList = res.getSVar().getUseList();\n\t\t\tint useCount = useList.size();\n\t\t\tif (useCount == 0) {\n\t\t\t\t// TODO?\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tInsnArg arg = useList.get(0);\n\t\t\tInsnNode usePlace = arg.getParentInsn();\n\t\t\tif (!BlockUtils.blockContains(block, usePlace)\n\t\t\t\t\t&& !BlockUtils.blockContains(next, usePlace)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (useCount > 1) {\n\t\t\t\tforceInlineInsns.add(insn);\n\t\t\t} else {\n\t\t\t\t// allow only forced assign inline\n\t\t\t\tpass = false;\n\t\t\t}\n\t\t}\n\t\treturn pass;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/LoopRegionMaker.java",
    "content": "package jadx.core.dex.visitors.regions.maker;\n\nimport java.util.ArrayList;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Queue;\nimport java.util.Set;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.EdgeInsnAttr;\nimport jadx.core.dex.attributes.nodes.LoopInfo;\nimport jadx.core.dex.attributes.nodes.LoopLabelAttr;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.Edge;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.Region;\nimport jadx.core.dex.regions.conditions.IfInfo;\nimport jadx.core.dex.regions.loops.LoopRegion;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.RegionUtils;\nimport jadx.core.utils.blocks.BlockSet;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.utils.BlockUtils.followEmptyPath;\nimport static jadx.core.utils.BlockUtils.getNextBlock;\nimport static jadx.core.utils.BlockUtils.isPathExists;\n\n/*\n * Definitions:\n * main loop body - the set of nodes that form a loop in the control flow graph e.g. they can all\n * reach the loop start and are all reachable from the loop start\n * loop exit edge - an edge with a source in the main loop body and a target outside the main loop\n * body\n * outblock - the first node after the entire loop has finished that should be regioned next\n * header block - an IF node that implements the loop condition\n * crossing - a block that is reachable from two different source nodes and represents where control\n * flow paths from the two blocks cross\n * exit block/node - an overloaded term. May mean the source or target of an exit edge, or a route\n * to exit the overall region e.g. the outblock\n */\n\nfinal class LoopRegionMaker {\n\tprivate final MethodNode mth;\n\tprivate final RegionMaker regionMaker;\n\tprivate final IfRegionMaker ifMaker;\n\n\tLoopRegionMaker(MethodNode mth, RegionMaker regionMaker, IfRegionMaker ifMaker) {\n\t\tthis.mth = mth;\n\t\tthis.regionMaker = regionMaker;\n\t\tthis.ifMaker = ifMaker;\n\t}\n\n\tBlockNode process(IRegion curRegion, LoopInfo loop, RegionStack stack) {\n\t\tBlockNode loopStart = loop.getStart();\n\t\tSet<BlockNode> exitBlocksSet = loop.getExitNodes();\n\n\t\t// set exit blocks scan order priority\n\t\t// this can help if loop has several exits (after using 'break' or 'return' in loop)\n\t\tList<BlockNode> exitBlocks = new ArrayList<>(exitBlocksSet.size());\n\t\tBlockNode nextStart = getNextBlock(loopStart);\n\t\tif (nextStart != null && exitBlocksSet.remove(nextStart)) {\n\t\t\texitBlocks.add(nextStart);\n\t\t}\n\t\tif (exitBlocksSet.remove(loopStart)) {\n\t\t\texitBlocks.add(loopStart);\n\t\t}\n\t\tif (exitBlocksSet.remove(loop.getEnd())) {\n\t\t\texitBlocks.add(loop.getEnd());\n\t\t}\n\t\texitBlocks.addAll(exitBlocksSet);\n\n\t\tLoopRegion loopRegion = makeLoopRegion(curRegion, loop, exitBlocks);\n\t\tif (loopRegion == null) {\n\t\t\tBlockNode exit = makeEndlessLoop(curRegion, stack, loop, loopStart);\n\t\t\tinsertContinue(loop);\n\t\t\treturn exit;\n\t\t}\n\t\tcurRegion.getSubBlocks().add(loopRegion);\n\t\tIRegion outerRegion = stack.peekRegion();\n\t\tstack.push(loopRegion);\n\n\t\tIfInfo condInfo = ifMaker.buildIfInfo(loopRegion);\n\t\tif (!loop.getLoopBlocks().contains(condInfo.getThenBlock())) {\n\t\t\t// invert loop condition if 'then' points to exit\n\t\t\tcondInfo = IfInfo.invert(condInfo);\n\t\t}\n\t\tloopRegion.updateCondition(condInfo);\n\t\t// prevent if's merge with loop condition\n\t\tcondInfo.getMergedBlocks().forEach(b -> b.add(AFlag.ADDED_TO_REGION));\n\t\texitBlocks.removeAll(condInfo.getMergedBlocks().toList());\n\n\t\tif (!exitBlocks.isEmpty()) {\n\n\t\t\t// Blocks associated with the loop condition\n\t\t\tList<BlockNode> loopConditionBlocks = loopRegion.getConditionBlocks();\n\n\t\t\tfor (Edge exitEdge : loop.getExitEdges()) {\n\t\t\t\t// An exit edge from the loop condition blocks\n\t\t\t\tBlockNode exitSource = exitEdge.getSource();\n\n\t\t\t\tif (loopConditionBlocks.contains(exitSource)) {\n\t\t\t\t\tBlockNode outBlock = followEmptyPath(exitEdge.getTarget());\n\n\t\t\t\t\tfor (BlockNode pred : outBlock.getPredecessors()) {\n\n\t\t\t\t\t\t// Restarting search through exit edges from the beginning (\"top\")\n\t\t\t\t\t\tfor (Edge exitEdgeTop : loop.getExitEdges()) {\n\n\t\t\t\t\t\t\tif (!loopConditionBlocks.contains(exitEdgeTop.getSource())) {\n\t\t\t\t\t\t\t\tif (isPathExists(exitEdgeTop.getTarget(), pred) || exitEdgeTop.getTarget() == outBlock) {\n\t\t\t\t\t\t\t\t\tinsertLoopBreak(stack, loop, outBlock, exitEdgeTop.getSource(), new Edge(pred, outBlock));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Exit edge found - no need to check further regardless of break outcome\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tBlockNode out;\n\t\tif (loopRegion.isConditionAtEnd()) {\n\t\t\tBlockNode thenBlock = condInfo.getThenBlock();\n\t\t\tout = thenBlock == loop.getEnd() || thenBlock == loopStart ? condInfo.getElseBlock() : thenBlock;\n\t\t\tout = BlockUtils.followEmptyPath(out);\n\t\t\tloopStart.remove(AType.LOOP);\n\t\t\tloop.getEnd().add(AFlag.ADDED_TO_REGION);\n\t\t\tstack.addExit(loop.getEnd());\n\t\t\tregionMaker.clearBlockProcessedState(loopStart);\n\t\t\tRegion body = regionMaker.makeRegion(loopStart);\n\t\t\tloopRegion.setBody(body);\n\t\t\tloopStart.addAttr(AType.LOOP, loop);\n\t\t\tloop.getEnd().remove(AFlag.ADDED_TO_REGION);\n\t\t} else {\n\t\t\tout = condInfo.getElseBlock(); // Following Jadx convention, this must be the next synthetic block, not actual (theoretical) out\n\t\t\t\t\t\t\t\t\t\t\t// block\n\t\t\tif (outerRegion != null\n\t\t\t\t\t&& out != null\n\t\t\t\t\t&& out.contains(AFlag.LOOP_START)\n\t\t\t\t\t&& !out.getAll(AType.LOOP).contains(loop)\n\t\t\t\t\t&& RegionUtils.isRegionContainsBlock(outerRegion, out)) {\n\t\t\t\t// exit to already processed outer loop\n\t\t\t\tout = null;\n\t\t\t}\n\t\t\tstack.addExit(out);\n\t\t\tBlockNode loopBody = condInfo.getThenBlock();\n\t\t\tRegion body;\n\t\t\tif (Objects.equals(loopBody, loopStart)) {\n\t\t\t\t// empty loop body\n\t\t\t\tbody = new Region(loopRegion);\n\t\t\t} else {\n\t\t\t\tbody = regionMaker.makeRegion(loopBody);\n\t\t\t}\n\t\t\t// add blocks from loop start to first condition block\n\t\t\tBlockNode conditionBlock = condInfo.getFirstIfBlock();\n\t\t\tif (loopStart != conditionBlock) {\n\t\t\t\tSet<BlockNode> blocks = BlockUtils.getAllPathsBlocks(loopStart, conditionBlock);\n\t\t\t\tblocks.remove(conditionBlock);\n\t\t\t\tfor (BlockNode block : blocks) {\n\t\t\t\t\tif (block.getInstructions().isEmpty()\n\t\t\t\t\t\t\t&& !block.contains(AFlag.ADDED_TO_REGION)\n\t\t\t\t\t\t\t&& !RegionUtils.isRegionContainsBlock(body, block)) {\n\t\t\t\t\t\tbody.add(block);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tloopRegion.setBody(body);\n\t\t}\n\t\tstack.pop();\n\t\tinsertContinue(loop);\n\t\treturn out;\n\t}\n\n\t/**\n\t * Select loop exit and construct LoopRegion\n\t */\n\tprivate LoopRegion makeLoopRegion(IRegion curRegion, LoopInfo loop, List<BlockNode> exitBlocks) {\n\t\tfor (BlockNode block : exitBlocks) {\n\t\t\t// Ignore blocks that lead to exception handlers\n\t\t\tif (block.contains(AType.EXC_HANDLER)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Ignore blocks that do not branch based on an if statement\n\t\t\tInsnNode lastInsn = BlockUtils.getLastInsn(block);\n\t\t\tif (lastInsn == null || lastInsn.getType() != InsnType.IF) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Skip any nested if statements\n\t\t\tList<LoopInfo> loops = block.getAll(AType.LOOP);\n\t\t\tif (!loops.isEmpty() && loops.get(0) != loop) {\n\t\t\t\t// skip nested loop condition\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tboolean exitAtLoopEnd = isExitAtLoopEnd(block, loop);\n\n\t\t\tLoopRegion loopRegion = new LoopRegion(curRegion, loop, block, exitAtLoopEnd);\n\n\t\t\tboolean found;\n\t\t\tif (block == loop.getStart() || exitAtLoopEnd || BlockUtils.isEmptySimplePath(loop.getStart(), block)) {\n\t\t\t\tfound = true;\n\t\t\t} else if (block.getPredecessors().contains(loop.getStart())) {\n\t\t\t\tloopRegion.setPreCondition(loop.getStart());\n\t\t\t\t// if we can't merge pre-condition this is not correct header\n\t\t\t\tfound = loopRegion.checkPreCondition();\n\t\t\t} else {\n\t\t\t\tfound = false;\n\t\t\t}\n\t\t\tif (found) {\n\t\t\t\tList<LoopInfo> list = mth.getAllLoopsForBlock(block);\n\t\t\t\tif (list.size() >= 2) {\n\t\t\t\t\t// bad condition if successors going out of all loops\n\t\t\t\t\tboolean allOuter = true;\n\t\t\t\t\tfor (BlockNode outerBlock : block.getCleanSuccessors()) {\n\t\t\t\t\t\tList<LoopInfo> outLoopList = mth.getAllLoopsForBlock(outerBlock);\n\t\t\t\t\t\toutLoopList.remove(loop);\n\t\t\t\t\t\tif (!outLoopList.isEmpty()) {\n\t\t\t\t\t\t\t// goes to outer loop\n\t\t\t\t\t\t\tallOuter = false;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (allOuter) {\n\t\t\t\t\t\tfound = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (found && !checkLoopExits(loop, block)) {\n\t\t\t\tfound = false;\n\t\t\t}\n\t\t\tif (found) {\n\t\t\t\treturn loopRegion;\n\t\t\t}\n\t\t}\n\t\t// no exit found => endless loop\n\t\treturn null;\n\t}\n\n\tprivate static boolean isExitAtLoopEnd(BlockNode exit, LoopInfo loop) {\n\t\tBlockNode loopEnd = loop.getEnd();\n\t\tif (exit == loopEnd) {\n\t\t\treturn true;\n\t\t}\n\t\tBlockNode loopStart = loop.getStart();\n\t\tif (loopStart.getInstructions().isEmpty() && ListUtils.isSingleElement(loopStart.getSuccessors(), exit)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn loopEnd.getInstructions().isEmpty() && ListUtils.isSingleElement(loopEnd.getPredecessors(), exit);\n\t}\n\n\t/*\n\t * Check that the exits suggested by treating mainExitBlock as the header block\n\t * are consistent with a loop condition\n\t */\n\tprivate boolean checkLoopExits(LoopInfo loop, BlockNode mainExitBlock) {\n\t\tList<Edge> exitEdges = loop.getExitEdges();\n\t\tif (exitEdges.size() < 2) {\n\t\t\treturn true;\n\t\t}\n\t\t// If the header selected does not have an exit edge, raise an exception\n\t\tOptional<Edge> mainEdgeOpt = exitEdges.stream().filter(edge -> edge.getSource() == mainExitBlock).findFirst();\n\t\tif (mainEdgeOpt.isEmpty()) {\n\t\t\tthrow new JadxRuntimeException(\"Not found exit edge by exit block: \" + mainExitBlock);\n\t\t}\n\n\t\tEdge mainExitEdge = mainEdgeOpt.get();\n\t\tBlockNode mainOutBlock = mainExitEdge.getTarget();\n\n\t\tBlockNode firstWorkAfterMainExitBlock = BlockUtils.followEmptyPath(mainOutBlock);\n\t\tList<InsnNode> firstInstructions = firstWorkAfterMainExitBlock.getInstructions();\n\n\t\t// If there is a direct path to a return from the header, all exits are inside the loop\n\t\tif (firstInstructions.size() == 1 && firstInstructions.get(0).getType() == InsnType.RETURN) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Otherwise the exit must lead to a valid out block\n\t\treturn validOutBlock(firstWorkAfterMainExitBlock, loop);\n\t}\n\n\t/*\n\t * An out block is valid if every exit path passes through it or doesn't cross any other exit path\n\t * (permitting one block of duplication)\n\t * @param outblock The proposed region exit block\n\t * @param exitEdges All edges leaving a section at the start of the region e.g. edges leaving a loop\n\t * body\n\t */\n\tprivate Boolean validOutBlock(BlockNode outBlock, LoopInfo loop) {\n\t\t/*\n\t\t * Not permitted:\n\t\t * - An edge which cannot reach outblock, but does cross with another exit path\n\t\t * --- This crossing could be on a path that never reaches outblock\n\t\t * --- This crossing could be after outblock\n\t\t * - An edge which can reach outblock, but has another crossing with an exit path\n\t\t * --- This crossing could be before outblock\n\t\t * --- This crossing could be after outblock\n\t\t * --- This crossing could be on a branch that does not reach outblock\n\t\t * Permitted:\n\t\t * - If any of these inconsistent crossings occur at or near the method exit\n\t\t * - If the node can reach the outblock but has no crossing there because it dominates the outnode\n\t\t * - A number of other edge cases\n\t\t */\n\t\tList<Edge> exitEdges = loop.getExitEdges();\n\t\tQueue<Edge> edgesToCheck = new LinkedList<>(exitEdges);\n\n\t\twhile (!edgesToCheck.isEmpty()) {\n\t\t\tEdge exitEdge = edgesToCheck.remove();\n\t\t\tBlockNode exitBlock = exitEdge.getTarget();\n\n\t\t\t// Get the dominance frontier of exitEdge.getTarget() only along paths through exitEdge\n\n\t\t\tList<BlockNode> dominanceFrontier;\n\t\t\tif (!exitEdge.isSynthetic()) {\n\t\t\t\tdominanceFrontier = BlockUtils.bitSetToBlocks(mth, BlockUtils.getDomFrontierThroughEdge(exitEdge));\n\t\t\t} else {\n\t\t\t\tdominanceFrontier = BlockUtils.bitSetToBlocks(mth, exitEdge.getTarget().getDomFrontier());\n\t\t\t}\n\n\t\t\tif (outBlock.isDominator(exitBlock) || outBlock == exitBlock) {\n\t\t\t\t// Accept if the loop exit block is a dominator of the suggested out block\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (BlockNode crossing : dominanceFrontier) {\n\t\t\t\tif (crossing == outBlock) {\n\t\t\t\t\t// Accept if the crossing is at the outblock\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (BlockUtils.isExitBlock(mth, crossing)) {\n\t\t\t\t\t// Accept if the crossing is at the method end\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Find the first block after the crossing with instructions\n\t\t\t\tBlockNode firstInstructionBlock = crossing;\n\t\t\t\tList<InsnNode> cInsns = crossing.getInstructions();\n\t\t\t\tif (cInsns.isEmpty()) {\n\t\t\t\t\tfirstInstructionBlock = BlockUtils.followEmptyPath(crossing);\n\t\t\t\t}\n\n\t\t\t\t// Return false if the crossing doesn't satisfy any relevant edge case\n\t\t\t\tif (!(viaValidUncleanSuccessor(exitBlock, crossing, loop)\n\t\t\t\t\t\t|| noWorkBeforeEnd(firstInstructionBlock, outBlock)\n\t\t\t\t\t\t|| oneBlockOfWorkBeforeEnd(firstInstructionBlock, outBlock)\n\t\t\t\t\t\t|| isNestedIfCross(crossing, edgesToCheck)\n\t\t\t\t\t\t|| isOuterOutblock(crossing, loop))) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/*\n\t * @param exitBlock the target of an exit edge\n\t * @param crossing the crossing block between exitBlock and a possible outblock\n\t * @param loop the loop\n\t */\n\tprivate boolean viaValidUncleanSuccessor(BlockNode exitBlock, BlockNode crossing, LoopInfo loop) {\n\t\t// Return true if the path from exitBlock is to an exception handler or via a backwards loop edge\n\t\t// with continue\n\n\t\tif (isPathExists(exitBlock, crossing)) {\n\t\t\t// This case does not apply if there is a path via clean successors\n\t\t\treturn false;\n\t\t}\n\n\t\t// If to a loop start check if the backwards edge has a branch without a valid continue\n\t\tif (crossing.contains(AFlag.LOOP_START)) {\n\t\t\t// Note: This loop start cannot be for the internal loop else exitEdge would not leave the loop\n\n\t\t\t// Find the outer loop containing the loop start\n\t\t\tLoopInfo parent = loop.getParentLoop();\n\t\t\tLoopInfo outerLoop = null;\n\t\t\twhile (parent != null) {\n\t\t\t\tif (parent.getStart() == crossing) {\n\t\t\t\t\touterLoop = parent;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tparent = parent.getParentLoop();\n\t\t\t}\n\n\t\t\tif (outerLoop != null) {\n\t\t\t\tBlockNode loopEnd = outerLoop.getEnd();\n\t\t\t\tList<BlockNode> predecessors = loopEnd.getPredecessors();\n\t\t\t\tif (predecessors.size() > 1) {\n\t\t\t\t\tfor (BlockNode predecessor : predecessors) {\n\t\t\t\t\t\t// Do not accept if a predecessor to the loop end reachable from the exit would not have a\n\t\t\t\t\t\t// continue inserted\n\t\t\t\t\t\tif (BlockUtils.isPathExists(exitBlock, predecessor)\n\t\t\t\t\t\t\t\t&& !canInsertContinue(predecessor, predecessors, loopEnd, outerLoop.getExitNodes())) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Do not accept if no continues would be placed\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t\t// Accept if all branches have a valid continue or if not to a loop start (to an exception handler)\n\t\treturn true;\n\t}\n\n\t/*\n\t * @param firstInstructionBlock the first block containing instructions off the main loop body\n\t * @param outBlock a possible outblock of the loop\n\t */\n\tprivate boolean noWorkBeforeEnd(BlockNode firstInstructionBlock, BlockNode outBlock) {\n\t\t// Return true if there is no work between the crossing and an exit block\n\t\treturn (BlockUtils.isExitBlock(mth, firstInstructionBlock) || firstInstructionBlock == outBlock);\n\t}\n\n\t/*\n\t * @param firstInstructionBlock the first block containing instructions off the main loop body\n\t * @param outBlock a possible outblock of the loop\n\t */\n\tprivate boolean oneBlockOfWorkBeforeEnd(BlockNode firstInstructionBlock, BlockNode outBlock) {\n\t\t// Return true if down every path there is no more than one block of work between the crossing and\n\t\t// an exit block\n\t\tList<BlockNode> cleanSuccessors = firstInstructionBlock.getCleanSuccessors();\n\t\tif (cleanSuccessors.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (BlockNode cleanSuccessor : cleanSuccessors) {\n\t\t\tBlockNode nextInstructionBlock = BlockUtils.followEmptyPath(cleanSuccessor);\n\t\t\tif (!BlockUtils.isExitBlock(mth, nextInstructionBlock) && nextInstructionBlock != outBlock) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/*\n\t * @param crossing the block that may be the joint block of a merged if\n\t * @param edgesToCheck the list of edges that will be processed to add to\n\t */\n\tprivate boolean isNestedIfCross(BlockNode crossing, Queue<Edge> edgesToCheck) {\n\t\t// Return true if the crossing is due to merged control flow after a nested if\n\t\t// Add the edges out of the crossing to be investigated\n\n\t\t// If the crossing is the branch of a merged if, all predecessors will be synthetic up to the if\n\t\t// statements, and the first if statement will dominate the crossing\n\t\tList<BlockNode> predecessors = crossing.getPredecessors();\n\n\t\t// Find a predecessor that dominates all other predecessors\n\t\tBlockNode possibleFirstIF = BlockUtils.followEmptyPath(predecessors.get(0), true);\n\t\tfor (BlockNode predecessor : predecessors) {\n\t\t\t// Follow the predecessor up to the first node with instructions\n\t\t\tBlockNode possibleIF = followEmptyPath(predecessor, true);\n\t\t\tif (crossing.isDominator(possibleIF)) {\n\t\t\t\tpossibleFirstIF = possibleIF;\n\t\t\t}\n\t\t}\n\n\t\t// This case does not apply if a merged if cannot be made\n\t\tIfInfo currentIf = IfRegionMaker.makeIfInfo(mth, possibleFirstIF);\n\t\tif (currentIf == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tIfInfo mergedIf = IfRegionMaker.mergeNestedIfNodes(currentIf);\n\t\tif (mergedIf == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Note: work will be repeated for large merged ifs. Results could be cached to improve performance\n\t\t// Accept if following every predecessor path from the crossing reaches a merged if node\n\t\tBlockSet mergedBlocks = mergedIf.getMergedBlocks();\n\t\tfor (BlockNode predecessor : predecessors) {\n\t\t\tBlockNode possibleIF = followEmptyPath(predecessor, true);\n\t\t\tif (!mergedBlocks.contains(possibleIF)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// If this crossing is the result of merged ifs, check the next crossing after this one\n\t\tEdge placeHolderEdge = new Edge(crossing, crossing, true);\n\t\tif (!edgesToCheck.contains(placeHolderEdge)) {\n\t\t\tedgesToCheck.add(placeHolderEdge);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/*\n\t * @param crossing the block that may be an outblock for a parent loop\n\t * @param loop the inner loop currently being considered\n\t */\n\tprivate boolean isOuterOutblock(BlockNode crossing, LoopInfo loop) {\n\t\t// Return true if the crossing is the outblock for an outer loop and is jumped to using a labelled\n\t\t// break\n\n\t\tList<EdgeInsnAttr> edgeInsns = crossing.getAll(AType.EDGE_INSN);\n\t\tfor (EdgeInsnAttr edgeInsn : edgeInsns) {\n\t\t\tInsnNode insn = edgeInsn.getInsn();\n\t\t\t// If there is a break edge instruction\n\t\t\tif (insn.getType() == InsnType.BREAK) {\n\t\t\t\tList<LoopInfo> loopsBrokenFrom = insn.get(AType.LOOP).getList();\n\t\t\t\tfor (LoopInfo loopBrokenFrom : loopsBrokenFrom) {\n\t\t\t\t\t// If it is for a parent of the current loop\n\t\t\t\t\tif (loop.hasParent(loopBrokenFrom)) {\n\t\t\t\t\t\tBlockNode target = edgeInsn.getEnd();\n\t\t\t\t\t\t// If it points at the crossing\n\t\t\t\t\t\tif (target == crossing) {\n\t\t\t\t\t\t\t// Accept if the crossing block is already the target of a break instruction from a parent loop\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate BlockNode makeEndlessLoop(IRegion curRegion, RegionStack stack, LoopInfo loop, BlockNode loopStart) {\n\t\tLoopRegion loopRegion = new LoopRegion(curRegion, loop, null, false);\n\t\tcurRegion.getSubBlocks().add(loopRegion);\n\n\t\tloopStart.remove(AType.LOOP);\n\t\tregionMaker.clearBlockProcessedState(loopStart);\n\t\tstack.push(loopRegion);\n\n\t\tBlockNode out = null;\n\t\t// insert 'break' for exits\n\t\tList<Edge> exitEdges = loop.getExitEdges();\n\t\tif (exitEdges.size() == 1) {\n\t\t\tEdge exitEdge = exitEdges.get(0);\n\t\t\tBlockNode exit = exitEdge.getTarget();\n\t\t\tif (insertLoopBreak(stack, loop, exit, exitEdge.getSource(), exitEdge)) {\n\t\t\t\tBlockNode nextBlock = getNextBlock(exit);\n\t\t\t\tif (nextBlock != null) {\n\t\t\t\t\tstack.addExit(nextBlock);\n\t\t\t\t\tout = nextBlock;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tloop0: for (Edge exitEdge : exitEdges) {\n\t\t\t\tBlockNode exit = exitEdge.getTarget();\n\t\t\t\tList<BlockNode> blocks = BlockUtils.bitSetToBlocks(mth, BlockUtils.getDomFrontierThroughEdge(exitEdge));\n\n\t\t\t\t// Only select the method exit if there is no other valid outblock\n\t\t\t\tBlockNode methodExit = mth.getExitBlock();\n\t\t\t\tif (blocks.contains(methodExit)) {\n\t\t\t\t\tblocks.remove(methodExit);\n\t\t\t\t\tblocks.add(methodExit);\n\t\t\t\t}\n\t\t\t\tfor (BlockNode block : blocks) {\n\t\t\t\t\tif (BlockUtils.isPathExists(exit, block)) {\n\t\t\t\t\t\tif (validOutBlock(block, loop)) {\n\t\t\t\t\t\t\tout = block;\n\t\t\t\t\t\t\tbreak loop0;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (block.contains(AFlag.LOOP_START)) {\n\t\t\t\t\t\t// Special case if there is no joining control flow before an outer loop back edge\n\t\t\t\t\t\tif (validOutBlock(exit, loop)) {\n\t\t\t\t\t\t\tout = exit;\n\t\t\t\t\t\t\tbreak loop0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add breaks\n\t\t\tstack.addExit(out);\n\t\t\tif (out != null && out != mth.getExitBlock()) {\n\t\t\t\t// Add a break on every incoming edge where the predecessor is reachable from the loop\n\t\t\t\tfor (BlockNode predecessor : out.getPredecessors()) {\n\t\t\t\t\tfor (Edge exitEdge : loop.getExitEdges()) {\n\t\t\t\t\t\tBlockNode target = exitEdge.getTarget();\n\t\t\t\t\t\tif (BlockUtils.isPathExists(exitEdge.getTarget(), predecessor) || target == out) {\n\t\t\t\t\t\t\tinsertLoopBreak(stack, loop, out, exitEdge.getSource(), new Edge(predecessor, out));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tRegion body = regionMaker.makeRegion(loopStart);\n\t\tBlockNode loopEnd = loop.getEnd();\n\t\tif (!RegionUtils.isRegionContainsBlock(body, loopEnd)\n\t\t\t\t&& !loopEnd.contains(AType.EXC_HANDLER)\n\t\t\t\t&& !inExceptionHandlerBlocks(loopEnd)) {\n\t\t\tbody.getSubBlocks().add(loopEnd);\n\t\t}\n\t\tloopRegion.setBody(body);\n\n\t\tif (out == null) {\n\t\t\tBlockNode next = getNextBlock(loopEnd);\n\t\t\tout = RegionUtils.isRegionContainsBlock(body, next) ? null : next;\n\t\t}\n\t\tstack.pop();\n\t\tloopStart.addAttr(AType.LOOP, loop);\n\t\treturn out;\n\t}\n\n\tprivate boolean inExceptionHandlerBlocks(BlockNode loopEnd) {\n\t\tif (mth.getExceptionHandlersCount() == 0) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (ExceptionHandler eh : mth.getExceptionHandlers()) {\n\t\t\tif (eh.getBlocks().contains(loopEnd)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate boolean canInsertBreak(BlockNode exit) {\n\t\tif (BlockUtils.containsExitInsn(exit)) {\n\t\t\treturn false;\n\t\t}\n\t\tList<BlockNode> simplePath = BlockUtils.buildSimplePath(exit);\n\t\tif (!simplePath.isEmpty()) {\n\t\t\tBlockNode lastBlock = simplePath.get(simplePath.size() - 1);\n\t\t\tif (lastBlock.isMthExitBlock()\n\t\t\t\t\t|| lastBlock.isReturnBlock()\n\t\t\t\t\t|| mth.isPreExitBlock(lastBlock)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\t// check if there no outer switch (TODO: very expensive check)\n\t\tSet<BlockNode> paths = BlockUtils.getAllPathsBlocks(mth.getEnterBlock(), exit);\n\t\tfor (BlockNode block : paths) {\n\t\t\tif (BlockUtils.checkLastInsnType(block, InsnType.SWITCH)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/*\n\t * Insert a break instruction where exitEdge meets loopExit\n\t * @param stack the region stack\n\t * @param loop the loop being broken out of\n\t * @param loopExit the outblock for loop\n\t * @param blockOnLoop an exit block on loop through which exitEdge is reachable\n\t * @param exitEdge an edge on the path between blockOnLoop and loopExit indicative of the breaking\n\t * path\n\t */\n\tprivate boolean insertLoopBreak(RegionStack stack, LoopInfo loop, BlockNode loopExit, BlockNode blockOnLoop, Edge exitEdge) {\n\t\tBlockNode exit = exitEdge.getTarget();\n\t\tEdge insertEdge = null;\n\t\tboolean confirm = false;\n\t\t// process special cases:\n\t\t// 1. jump to outer loop\n\t\tBlockNode exitEnd = BlockUtils.followEmptyPath(exit);\n\t\tList<LoopInfo> loops = exitEnd.getAll(AType.LOOP);\n\t\tfor (LoopInfo loopAtEnd : loops) {\n\t\t\tif (loopAtEnd != loop && loop.hasParent(loopAtEnd)) {\n\t\t\t\tinsertEdge = exitEdge;\n\t\t\t\tconfirm = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!confirm) {\n\t\t\t// Start search from the next edge if the target is simple (e.g. first\n\t\t\t// node after loop exit)\n\t\t\tBoolean isSimple = BlockUtils.followEmptyPath(exit) != exit;\n\t\t\tBlockNode insertBlock = isSimple ? null : exitEdge.getSource();\n\t\t\tBlockSet visited = new BlockSet(mth);\n\t\t\twhile (true) {\n\t\t\t\tif (exit == null || visited.contains(exit)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tvisited.add(exit);\n\t\t\t\tif (insertBlock != null && isPathExists(loopExit, exit)) {\n\t\t\t\t\t// found cross\n\t\t\t\t\tif (canInsertBreak(insertBlock)) {\n\t\t\t\t\t\tinsertEdge = new Edge(insertBlock, exit);\n\t\t\t\t\t\tconfirm = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tinsertBlock = exit;\n\t\t\t\tList<BlockNode> cs = exit.getCleanSuccessors();\n\t\t\t\texit = cs.size() == 1 ? cs.get(0) : null;\n\t\t\t}\n\t\t}\n\t\tif (!confirm) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnNode breakInsn = new InsnNode(InsnType.BREAK, 0);\n\t\tbreakInsn.addAttr(AType.LOOP, loop);\n\t\tEdgeInsnAttr.addEdgeInsn(insertEdge, breakInsn);\n\t\tstack.addExit(exit);\n\t\t// add label to 'break' if needed\n\t\taddBreakLabel(blockOnLoop, exit, breakInsn);\n\t\treturn true;\n\t}\n\n\t/*\n\t * Adds a label to a break instruction if reaching the exit from the loop involves leaving multiple\n\t * loops\n\t * @param blockOnLoop the exit block on the loop to which breakInsn is currently associated\n\t * @param exit the out block of the loop to which breakInsn is currently associated\n\t * @param breakInsn a break instruction\n\t */\n\tprivate void addBreakLabel(BlockNode blockOnLoop, BlockNode exit, InsnNode breakInsn) {\n\t\tList<LoopInfo> exitLoop = mth.getAllLoopsForBlock(exit);\n\t\tif (!exitLoop.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tList<LoopInfo> inLoops = mth.getAllLoopsForBlock(blockOnLoop);\n\t\tif (inLoops.size() < 2) {\n\t\t\treturn;\n\t\t}\n\t\t// search for parent loop\n\t\tLoopInfo parentLoop = null;\n\t\tfor (LoopInfo loop : inLoops) {\n\t\t\tif (loop.getParentLoop() == null) {\n\t\t\t\tparentLoop = loop;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (parentLoop == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (parentLoop.getEnd() != exit && !parentLoop.getExitNodes().contains(exit)) {\n\t\t\tLoopLabelAttr labelAttr = new LoopLabelAttr(parentLoop);\n\t\t\tbreakInsn.addAttr(labelAttr);\n\t\t\tparentLoop.getStart().addAttr(labelAttr);\n\t\t}\n\t}\n\n\tprivate static void insertContinue(LoopInfo loop) {\n\t\tBlockNode loopEnd = loop.getEnd();\n\t\tList<BlockNode> predecessors = loopEnd.getPredecessors();\n\t\tif (predecessors.size() <= 1) {\n\t\t\treturn;\n\t\t}\n\t\tSet<BlockNode> loopExitNodes = loop.getExitNodes();\n\t\tfor (BlockNode pred : predecessors) {\n\t\t\tif (canInsertContinue(pred, predecessors, loopEnd, loopExitNodes)) {\n\t\t\t\tInsnNode cont = new InsnNode(InsnType.CONTINUE, 0);\n\t\t\t\tpred.getInstructions().add(cont);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean canInsertContinue(BlockNode pred, List<BlockNode> predecessors, BlockNode loopEnd,\n\t\t\tSet<BlockNode> loopExitNodes) {\n\t\tif (!pred.contains(AFlag.SYNTHETIC)\n\t\t\t\t|| BlockUtils.checkLastInsnType(pred, InsnType.CONTINUE)) {\n\t\t\treturn false;\n\t\t}\n\t\tList<BlockNode> preds = pred.getPredecessors();\n\t\tif (preds.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tBlockNode codePred = preds.get(0);\n\t\tif (codePred.contains(AFlag.ADDED_TO_REGION)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (loopEnd.isDominator(codePred)\n\t\t\t\t|| loopExitNodes.contains(codePred)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (isDominatedOnBlocks(codePred, predecessors)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!pred.getAll(AType.EDGE_INSN).isEmpty()) {\n\t\t\t// if we've already inserted a break, don't also insert a continue in the same spot\n\t\t\tList<EdgeInsnAttr> insns = pred.getAll(AType.EDGE_INSN);\n\t\t\tfor (EdgeInsnAttr insn : insns) {\n\t\t\t\tif (insn.getInsn().getType() == InsnType.BREAK) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tboolean gotoExit = false;\n\t\tfor (BlockNode exit : loopExitNodes) {\n\t\t\tif (BlockUtils.isPathExists(codePred, exit)) {\n\t\t\t\tgotoExit = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn gotoExit;\n\t}\n\n\tprivate static boolean isDominatedOnBlocks(BlockNode dom, List<BlockNode> blocks) {\n\t\tfor (BlockNode node : blocks) {\n\t\t\tif (!node.isDominator(dom)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/RegionMaker.java",
    "content": "package jadx.core.dex.visitors.regions.maker;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.EdgeInsnAttr;\nimport jadx.core.dex.attributes.nodes.LoopInfo;\nimport jadx.core.dex.instructions.IfNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.SwitchInsn;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnContainer;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.Region;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.blocks.BlockSet;\nimport jadx.core.utils.exceptions.JadxOverflowException;\n\nimport static jadx.core.utils.BlockUtils.getNextBlock;\n\npublic class RegionMaker {\n\tprivate final MethodNode mth;\n\tprivate final RegionStack stack;\n\n\tprivate final IfRegionMaker ifMaker;\n\tprivate final LoopRegionMaker loopMaker;\n\n\tprivate final BlockSet processedBlocks;\n\tprivate final int regionsLimit;\n\n\tprivate int regionsCount;\n\n\tpublic RegionMaker(MethodNode mth) {\n\t\tthis.mth = mth;\n\t\tthis.stack = new RegionStack(mth);\n\t\tthis.ifMaker = new IfRegionMaker(mth, this);\n\t\tthis.loopMaker = new LoopRegionMaker(mth, this, ifMaker);\n\t\tthis.processedBlocks = BlockSet.empty(mth);\n\t\tthis.regionsLimit = mth.getBasicBlocks().size() * 400;\n\t}\n\n\tpublic Region makeMthRegion() {\n\t\treturn makeRegion(mth.getEnterBlock());\n\t}\n\n\tRegion makeRegion(BlockNode startBlock) {\n\t\tObjects.requireNonNull(startBlock);\n\t\tRegion region = new Region(stack.peekRegion());\n\t\tif (stack.containsExit(startBlock)) {\n\t\t\tinsertEdgeInsns(region, startBlock);\n\t\t\treturn region;\n\t\t}\n\n\t\tif (processedBlocks.addChecked(startBlock)) {\n\t\t\tmth.addWarnComment(\"Found duplicated region for block: \" + startBlock + ' ' + startBlock.getAttributesString());\n\t\t\t// Add block to multiple regions (duplicate the instructions in decompiled code) and allow\n\t\t\t// processing to continue\n\t\t}\n\n\t\tBlockNode next = startBlock;\n\n\t\twhile (next != null) {\n\t\t\tnext = traverse(region, next);\n\t\t\tregionsCount++;\n\t\t\tif (regionsCount > regionsLimit) {\n\t\t\t\tthrow new JadxOverflowException(\"Regions count limit reached at block \" + startBlock.toString());\n\t\t\t}\n\t\t}\n\t\treturn region;\n\t}\n\n\t/**\n\t * Recursively traverse all blocks from 'block' until block from 'exits'\n\t */\n\tprivate BlockNode traverse(IRegion r, BlockNode block) {\n\t\tif (block.contains(AFlag.MTH_EXIT_BLOCK)) {\n\t\t\treturn null;\n\t\t}\n\t\tBlockNode next = null;\n\t\tboolean processed = false;\n\n\t\tList<LoopInfo> loops = block.getAll(AType.LOOP);\n\t\tint loopCount = loops.size();\n\t\tif (loopCount != 0 && block.contains(AFlag.LOOP_START)) {\n\t\t\tif (loopCount == 1) {\n\t\t\t\tnext = loopMaker.process(r, loops.get(0), stack);\n\t\t\t\tprocessed = true;\n\t\t\t} else {\n\t\t\t\tfor (LoopInfo loop : loops) {\n\t\t\t\t\tif (loop.getStart() == block) {\n\t\t\t\t\t\tnext = loopMaker.process(r, loop, stack);\n\t\t\t\t\t\tprocessed = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tInsnNode insn = BlockUtils.getLastInsn(block);\n\t\tif (!processed && insn != null) {\n\t\t\tswitch (insn.getType()) {\n\t\t\t\tcase IF:\n\t\t\t\t\tnext = ifMaker.process(r, block, (IfNode) insn, stack);\n\t\t\t\t\tprocessed = true;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase SWITCH:\n\t\t\t\t\tSwitchRegionMaker switchMaker = new SwitchRegionMaker(mth, this);\n\t\t\t\t\tnext = switchMaker.process(r, block, (SwitchInsn) insn, stack);\n\t\t\t\t\tprocessed = true;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase MONITOR_ENTER:\n\t\t\t\t\tSynchronizedRegionMaker syncMaker = new SynchronizedRegionMaker(mth, this);\n\t\t\t\t\tnext = syncMaker.process(r, block, insn, stack);\n\t\t\t\t\tprocessed = true;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!processed) {\n\t\t\tr.getSubBlocks().add(block);\n\t\t\tnext = getNextBlock(block);\n\t\t}\n\t\tif (next != null && !stack.containsExit(block) && !stack.containsExit(next)) {\n\t\t\treturn next;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate void insertEdgeInsns(Region region, BlockNode exitBlock) {\n\t\tList<EdgeInsnAttr> edgeInsns = exitBlock.getAll(AType.EDGE_INSN);\n\t\tif (edgeInsns.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tList<InsnNode> insns = new ArrayList<>(edgeInsns.size());\n\t\taddOneInsnOfType(insns, edgeInsns, InsnType.BREAK);\n\t\taddOneInsnOfType(insns, edgeInsns, InsnType.CONTINUE);\n\t\tregion.add(new InsnContainer(insns));\n\t}\n\n\tprivate void addOneInsnOfType(List<InsnNode> insns, List<EdgeInsnAttr> edgeInsns, InsnType insnType) {\n\t\tfor (EdgeInsnAttr edgeInsn : edgeInsns) {\n\t\t\tInsnNode insn = edgeInsn.getInsn();\n\t\t\tif (insn.getType() == insnType) {\n\t\t\t\tinsns.add(insn);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tRegionStack getStack() {\n\t\treturn stack;\n\t}\n\n\tboolean isProcessed(BlockNode block) {\n\t\treturn processedBlocks.contains(block);\n\t}\n\n\tvoid clearBlockProcessedState(BlockNode block) {\n\t\tprocessedBlocks.remove(block);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/RegionStack.java",
    "content": "package jadx.core.dex.visitors.regions.maker;\n\nimport java.util.ArrayDeque;\nimport java.util.Collection;\nimport java.util.Deque;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.exceptions.JadxOverflowException;\n\nfinal class RegionStack {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(RegionStack.class);\n\tprivate static final boolean DEBUG = false;\n\n\tprivate static final int REGIONS_STACK_LIMIT = 1000;\n\n\tstatic {\n\t\tif (DEBUG) {\n\t\t\tLOG.debug(\"Debug enabled for {}\", RegionStack.class);\n\t\t}\n\t}\n\n\tprivate static final class State {\n\t\tfinal Set<BlockNode> exits;\n\t\tIRegion region;\n\n\t\tpublic State() {\n\t\t\texits = new HashSet<>();\n\t\t}\n\n\t\tprivate State(State c, IRegion region) {\n\t\t\tthis.exits = new HashSet<>(c.exits);\n\t\t\tthis.region = region;\n\t\t}\n\n\t\tpublic State copyWith(IRegion region) {\n\t\t\treturn new State(this, region);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Region: \" + region + \", exits: \" + exits;\n\t\t}\n\t}\n\n\tprivate final Deque<State> stack;\n\tprivate State curState;\n\n\tpublic RegionStack(MethodNode mth) {\n\t\tif (DEBUG) {\n\t\t\tLOG.debug(\"New RegionStack: {}\", mth);\n\t\t}\n\t\tthis.stack = new ArrayDeque<>();\n\t\tthis.curState = new State();\n\t}\n\n\tpublic void push(IRegion region) {\n\t\tstack.push(curState);\n\t\tif (stack.size() > REGIONS_STACK_LIMIT) {\n\t\t\tthrow new JadxOverflowException(\"Regions stack size limit reached\");\n\t\t}\n\t\tcurState = curState.copyWith(region);\n\t\tif (DEBUG) {\n\t\t\tLOG.debug(\"Stack push: {}: {}\", size(), curState);\n\t\t}\n\t}\n\n\tpublic void pop() {\n\t\tcurState = stack.pop();\n\t\tif (DEBUG) {\n\t\t\tLOG.debug(\"Stack  pop: {}: {}\", size(), curState);\n\t\t}\n\t}\n\n\t/**\n\t * Add boundary(exit) node for current stack frame\n\t *\n\t * @param exit boundary node, null will be ignored\n\t */\n\tpublic void addExit(@Nullable BlockNode exit) {\n\t\tif (exit != null) {\n\t\t\tcurState.exits.add(exit);\n\t\t}\n\t}\n\n\tpublic void addExits(Collection<BlockNode> exits) {\n\t\tfor (BlockNode exit : exits) {\n\t\t\taddExit(exit);\n\t\t}\n\t}\n\n\tpublic void removeExit(@Nullable BlockNode exit) {\n\t\tif (exit != null) {\n\t\t\tcurState.exits.remove(exit);\n\t\t}\n\t}\n\n\tpublic boolean containsExit(BlockNode exit) {\n\t\treturn curState.exits.contains(exit);\n\t}\n\n\tpublic IRegion peekRegion() {\n\t\treturn curState.region;\n\t}\n\n\tpublic int size() {\n\t\treturn stack.size();\n\t}\n\n\tpublic RegionStack clear() {\n\t\tstack.clear();\n\t\tcurState = new State();\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Region stack size: \" + size() + \", last: \" + curState;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/SwitchRegionMaker.java",
    "content": "package jadx.core.dex.visitors.regions.maker;\n\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.nodes.LoopInfo;\nimport jadx.core.dex.attributes.nodes.RegionRefAttr;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.SwitchInsn;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnContainer;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.Region;\nimport jadx.core.dex.regions.SwitchRegion;\nimport jadx.core.dex.regions.SwitchRegion.CaseInfo;\nimport jadx.core.dex.visitors.regions.AbstractRegionVisitor;\nimport jadx.core.dex.visitors.regions.DepthRegionTraversal;\nimport jadx.core.dex.visitors.regions.SwitchBreakVisitor;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.RegionUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.blocks.BlockSet;\n\npublic final class SwitchRegionMaker {\n\tprivate final MethodNode mth;\n\tprivate final RegionMaker regionMaker;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SwitchRegionMaker.class);\n\n\tSwitchRegionMaker(MethodNode mth, RegionMaker regionMaker) {\n\t\tthis.mth = mth;\n\t\tthis.regionMaker = regionMaker;\n\t}\n\n\tBlockNode process(IRegion currentRegion, BlockNode block, SwitchInsn insn, RegionStack stack) {\n\t\t// map case blocks to keys\n\t\tint len = insn.getTargets().length;\n\t\tMap<BlockNode, List<Object>> blocksMap = new LinkedHashMap<>(len);\n\t\tBlockNode[] targetBlocksArr = insn.getTargetBlocks();\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tList<Object> keys = blocksMap.computeIfAbsent(targetBlocksArr[i], k -> new ArrayList<>(2));\n\t\t\tkeys.add(insn.getKey(i));\n\t\t}\n\t\tBlockNode defCase = insn.getDefTargetBlock();\n\t\tif (defCase != null) {\n\t\t\tList<Object> keys = blocksMap.computeIfAbsent(defCase, k -> new ArrayList<>(1));\n\t\t\tkeys.add(SwitchRegion.DEFAULT_CASE_KEY);\n\t\t}\n\n\t\tSwitchRegion sw = new SwitchRegion(currentRegion, block);\n\t\tinsn.addAttr(new RegionRefAttr(sw));\n\t\tcurrentRegion.getSubBlocks().add(sw);\n\t\tstack.push(sw);\n\n\t\tBlockNode out = calcSwitchOut(block, insn, stack);\n\t\tstack.addExit(out);\n\n\t\taddCases(sw, out, stack, blocksMap);\n\t\tremoveEmptyCases(insn, sw, defCase, out);\n\n\t\tstack.pop();\n\t\treturn out;\n\t}\n\n\t/**\n\t * Insert 'break' for all cases in switch region\n\t * Executed in {@link jadx.core.dex.visitors.regions.PostProcessRegions} after try/catch wrap to\n\t * handle all blocks\n\t */\n\tpublic static void insertBreaks(MethodNode mth, SwitchRegion sw) {\n\t\tfor (SwitchRegion.CaseInfo caseInfo : sw.getCases()) {\n\t\t\tinsertBreaksForCase(mth, sw, caseInfo.getContainer());\n\t\t}\n\t}\n\n\tprivate void addCases(SwitchRegion sw, @Nullable BlockNode out,\n\t\t\tRegionStack stack, Map<BlockNode, List<Object>> blocksMap) {\n\t\tMap<BlockNode, BlockNode> fallThroughCases = new LinkedHashMap<>();\n\t\tif (out != null) {\n\t\t\t// detect fallthrough cases\n\t\t\tBitSet caseBlocks = BlockUtils.blocksToBitSet(mth, blocksMap.keySet());\n\t\t\tcaseBlocks.clear(out.getPos());\n\t\t\tfor (BlockNode successor : sw.getHeader().getSuccessors()) {\n\t\t\t\tBitSet df = successor.getDomFrontier();\n\t\t\t\tif (df.intersects(caseBlocks)) {\n\t\t\t\t\tBlockNode fallThroughBlock = getOneIntersectionBlock(out, caseBlocks, df);\n\t\t\t\t\tfallThroughCases.put(successor, fallThroughBlock);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// check fallthrough cases order\n\t\t\tif (!fallThroughCases.isEmpty() && isBadCasesOrder(blocksMap, fallThroughCases)) {\n\t\t\t\tMap<BlockNode, List<Object>> newBlocksMap = reOrderSwitchCases(blocksMap, fallThroughCases);\n\t\t\t\tif (isBadCasesOrder(newBlocksMap, fallThroughCases)) {\n\t\t\t\t\tmth.addWarnComment(\"Can't fix incorrect switch cases order, some code will duplicate\");\n\t\t\t\t\tfallThroughCases.clear();\n\t\t\t\t} else {\n\t\t\t\t\tblocksMap = newBlocksMap;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (Map.Entry<BlockNode, List<Object>> entry : blocksMap.entrySet()) {\n\t\t\tList<Object> keysList = entry.getValue();\n\t\t\tBlockNode caseBlock = entry.getKey();\n\t\t\tRegion caseRegion;\n\t\t\tif (stack.containsExit(caseBlock)) {\n\t\t\t\tcaseRegion = new Region(stack.peekRegion());\n\t\t\t} else {\n\t\t\t\tBlockNode next = fallThroughCases.get(caseBlock);\n\t\t\t\tstack.addExit(next);\n\t\t\t\tcaseRegion = regionMaker.makeRegion(caseBlock);\n\t\t\t\tstack.removeExit(next);\n\t\t\t\tif (next != null) {\n\t\t\t\t\tnext.add(AFlag.FALL_THROUGH);\n\t\t\t\t\tcaseRegion.add(AFlag.FALL_THROUGH);\n\t\t\t\t}\n\t\t\t}\n\t\t\tsw.addCase(keysList, caseRegion);\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate BlockNode getOneIntersectionBlock(BlockNode out, BitSet caseBlocks, BitSet fallThroughSet) {\n\t\tBitSet caseExits = BlockUtils.copyBlocksBitSet(mth, fallThroughSet);\n\t\tcaseExits.clear(out.getPos());\n\t\tcaseExits.and(caseBlocks);\n\t\treturn BlockUtils.bitSetToOneBlock(mth, caseExits);\n\t}\n\n\tprivate @Nullable BlockNode calcSwitchOut(BlockNode block, SwitchInsn insn, RegionStack stack) {\n\t\t// union of case blocks dominance frontier\n\t\t// works if no fallthrough cases and no returns inside switch\n\t\tBitSet outs = BlockUtils.newBlocksBitSet(mth);\n\t\tfor (BlockNode s : block.getCleanSuccessors()) {\n\t\t\tif (s.contains(AFlag.LOOP_END)) {\n\t\t\t\t// loop end dom frontier is loop start, ignore it\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\touts.or(s.getDomFrontier());\n\t\t}\n\t\touts.clear(block.getId());\n\t\touts.clear(mth.getExitBlock().getId());\n\n\t\tBlockNode out = null;\n\t\tif (outs.cardinality() == 1) {\n\t\t\t// single exit\n\t\t\tout = BlockUtils.bitSetToOneBlock(mth, outs);\n\t\t} else {\n\t\t\t// several switch exits\n\t\t\t// possible 'return', 'continue' or fallthrough in one of the cases\n\t\t\tLoopInfo loop = mth.getLoopForBlock(block);\n\t\t\tif (loop != null) {\n\t\t\t\touts.andNot(loop.getStart().getPostDoms());\n\t\t\t\touts.andNot(loop.getEnd().getPostDoms());\n\t\t\t\tBlockNode loopEnd = loop.getEnd();\n\t\t\t\tif (outs.cardinality() == 2 && outs.get(loopEnd.getId())) {\n\t\t\t\t\t// insert 'continue' for cases lead to loop end\n\t\t\t\t\t// expect only 2 exits: loop end and switch out\n\t\t\t\t\tList<BlockNode> outList = BlockUtils.bitSetToBlocks(mth, outs);\n\t\t\t\t\toutList.remove(loopEnd);\n\t\t\t\t\tBlockNode possibleOut = Utils.getOne(outList);\n\t\t\t\t\tif (possibleOut != null && insertContinueInSwitch(block, possibleOut, loopEnd)) {\n\t\t\t\t\t\touts.clear(loopEnd.getId());\n\t\t\t\t\t\tout = possibleOut;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (outs.isEmpty()) {\n\t\t\t\t\t// all exits inside switch, keep inside to exit from loop\n\t\t\t\t\treturn mth.getExitBlock();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (out == null) {\n\t\t\t\tBlockNode imPostDom = block.getIPostDom();\n\t\t\t\tif (outs.get(imPostDom.getId())) {\n\t\t\t\t\tout = imPostDom;\n\t\t\t\t} else {\n\t\t\t\t\touts.andNot(block.getPostDoms());\n\t\t\t\t\tout = BlockUtils.bitSetToOneBlock(mth, outs);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (out != null && mth.isPreExitBlock(out)) {\n\t\t\t// include 'return' or 'throw' in case blocks\n\t\t\tout = mth.getExitBlock();\n\t\t}\n\t\tBlockNode imPostDom = block.getIPostDom();\n\t\tif (out == null && imPostDom == mth.getExitBlock()) {\n\t\t\t// all exits inside switch\n\t\t\t// check if all returns are equals and should be treated as single out block\n\t\t\treturn allSameReturns(stack);\n\t\t}\n\t\tif (imPostDom == insn.getDefTargetBlock()\n\t\t\t\t&& block.getCleanSuccessors().contains(imPostDom)\n\t\t\t\t&& block.getDomFrontier().get(imPostDom.getId())) {\n\t\t\t// add exit to stop on empty 'default' block\n\t\t\tstack.addExit(imPostDom);\n\t\t}\n\t\tif (out == null) {\n\t\t\tmth.addWarnComment(\"Failed to find 'out' block for switch in \" + block + \". Please report as an issue.\");\n\t\t\t// fallback option; should work in most cases\n\t\t\tout = block.getIPostDom();\n\t\t}\n\t\tif (out != null && regionMaker.isProcessed(out)) {\n\t\t\t// 'out' block already processed, prevent endless loop\n\t\t\t// in this case it might be that 'out' is the LOOP_START of a loop and occurs before 'block'\n\t\t\t// just try the immediate post dominator as a fallback\n\t\t\tmth.addWarnComment(\"Switch 'out' block \" + out + \" for \" + block + \" already processed. Defaulting to fallback option.\");\n\t\t\tout = block.getIPostDom();\n\t\t}\n\t\treturn out;\n\t}\n\n\tprivate BlockNode allSameReturns(RegionStack stack) {\n\t\tBlockNode exitBlock = mth.getExitBlock();\n\t\tList<BlockNode> preds = exitBlock.getPredecessors();\n\t\tint count = preds.size();\n\t\tif (count == 1) {\n\t\t\treturn preds.get(0);\n\t\t}\n\t\tif (mth.getReturnType() == ArgType.VOID) {\n\t\t\tfor (BlockNode pred : preds) {\n\t\t\t\tInsnNode insn = BlockUtils.getLastInsn(pred);\n\t\t\t\tif (insn == null || insn.getType() != InsnType.RETURN) {\n\t\t\t\t\treturn exitBlock;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tList<InsnArg> returnArgs = new ArrayList<>();\n\t\t\tfor (BlockNode pred : preds) {\n\t\t\t\tInsnNode insn = BlockUtils.getLastInsn(pred);\n\t\t\t\tif (insn == null || insn.getType() != InsnType.RETURN) {\n\t\t\t\t\treturn exitBlock;\n\t\t\t\t}\n\t\t\t\treturnArgs.add(insn.getArg(0));\n\t\t\t}\n\t\t\tInsnArg firstArg = returnArgs.get(0);\n\t\t\tif (firstArg.isRegister()) {\n\t\t\t\tRegisterArg reg = (RegisterArg) firstArg;\n\t\t\t\tfor (int i = 1; i < count; i++) {\n\t\t\t\t\tInsnArg arg = returnArgs.get(i);\n\t\t\t\t\tif (!arg.isRegister() || !((RegisterArg) arg).sameCodeVar(reg)) {\n\t\t\t\t\t\treturn exitBlock;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (int i = 1; i < count; i++) {\n\t\t\t\t\tInsnArg arg = returnArgs.get(i);\n\t\t\t\t\tif (!arg.equals(firstArg)) {\n\t\t\t\t\t\treturn exitBlock;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// confirmed\n\t\tstack.addExits(preds);\n\t\t// ignore other returns\n\t\tfor (int i = 1; i < count; i++) {\n\t\t\tBlockNode block = preds.get(i);\n\t\t\tblock.add(AFlag.REMOVE);\n\t\t\tblock.add(AFlag.ADDED_TO_REGION);\n\t\t}\n\t\treturn preds.get(0);\n\t}\n\n\t/**\n\t * Remove empty case blocks:\n\t * 1. single 'default' case\n\t * 2. filler cases if switch is 'packed' and 'default' case is empty\n\t */\n\tprivate void removeEmptyCases(SwitchInsn insn, SwitchRegion sw, BlockNode defCase, BlockNode outBlock) {\n\t\tboolean defaultCaseIsEmpty;\n\t\tif (defCase == null) {\n\t\t\tdefaultCaseIsEmpty = true;\n\t\t} else {\n\t\t\tdefaultCaseIsEmpty = sw.getCases().stream()\n\t\t\t\t\t.anyMatch(c -> c.getKeys().contains(SwitchRegion.DEFAULT_CASE_KEY)\n\t\t\t\t\t\t\t&& canRemove(c.getContainer(), outBlock));\n\t\t}\n\t\tif (defaultCaseIsEmpty) {\n\t\t\tList<CaseInfo> cases = new ArrayList<>(sw.getCases());\n\t\t\tfor (CaseInfo caseInfo : cases) {\n\t\t\t\tif (canRemove(caseInfo.getContainer(), outBlock)) {\n\t\t\t\t\tList<Object> keys = caseInfo.getKeys();\n\t\t\t\t\tif (keys.contains(SwitchRegion.DEFAULT_CASE_KEY) || insn.isPacked()) {\n\t\t\t\t\t\t// Remove case and mark all blocks as don't generate\n\t\t\t\t\t\tRegionUtils.addToAll(mth, caseInfo.getContainer(), AFlag.DONT_GENERATE);\n\t\t\t\t\t\tsw.getCases().remove(caseInfo);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * Check container is empty and all paths through container are empty up until outBlock\n\t */\n\tprivate boolean canRemove(IContainer container, BlockNode outBlock) {\n\t\tif (RegionUtils.isEmpty(container)) {\n\t\t\tif (container instanceof BlockNode) {\n\t\t\t\t// Base case - empty path from block node to outBlock\n\t\t\t\treturn BlockUtils.followEmptyPath((BlockNode) container) == outBlock;\n\t\t\t} else if (container instanceof IRegion) {\n\t\t\t\t// Recursive case - every subBlock can be removed\n\t\t\t\tList<IContainer> subBlocks = ((IRegion) container).getSubBlocks();\n\t\t\t\tfor (IContainer subBlock : subBlocks) {\n\t\t\t\t\tif (!canRemove(subBlock, outBlock)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tLOG.debug(\"Unexpected container type in switch\");\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate boolean isBadCasesOrder(Map<BlockNode, List<Object>> blocksMap, Map<BlockNode, BlockNode> fallThroughCases) {\n\t\tBlockNode nextCaseBlock = null;\n\t\tfor (BlockNode caseBlock : blocksMap.keySet()) {\n\t\t\tif (nextCaseBlock != null && !caseBlock.equals(nextCaseBlock)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tnextCaseBlock = fallThroughCases.get(caseBlock);\n\t\t}\n\t\treturn nextCaseBlock != null;\n\t}\n\n\tprivate Map<BlockNode, List<Object>> reOrderSwitchCases(Map<BlockNode, List<Object>> blocksMap,\n\t\t\tMap<BlockNode, BlockNode> fallThroughCases) {\n\t\tList<BlockNode> list = new ArrayList<>(blocksMap.size());\n\t\tlist.addAll(blocksMap.keySet());\n\t\tlist.sort((a, b) -> {\n\t\t\tBlockNode nextA = fallThroughCases.get(a);\n\t\t\tif (nextA != null) {\n\t\t\t\tif (b.equals(nextA)) {\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t} else if (a.equals(fallThroughCases.get(b))) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\treturn 0;\n\t\t});\n\n\t\tMap<BlockNode, List<Object>> newBlocksMap = new LinkedHashMap<>(blocksMap.size());\n\t\tfor (BlockNode key : list) {\n\t\t\tnewBlocksMap.put(key, blocksMap.get(key));\n\t\t}\n\t\treturn newBlocksMap;\n\t}\n\n\tprivate boolean insertContinueInSwitch(BlockNode switchBlock, BlockNode switchOut, BlockNode loopEnd) {\n\t\tboolean inserted = false;\n\t\tfor (BlockNode caseBlock : switchBlock.getCleanSuccessors()) {\n\t\t\tif (caseBlock.getDomFrontier().get(loopEnd.getId()) && caseBlock != switchOut) {\n\t\t\t\t// search predecessor of loop end on path from this successor\n\t\t\t\tSet<BlockNode> list = new HashSet<>(BlockUtils.collectBlocksDominatedBy(mth, caseBlock, caseBlock));\n\t\t\t\tif (list.contains(switchOut) || switchOut.getPredecessors().stream().anyMatch(list::contains)) {\n\t\t\t\t\t// 'continue' not needed\n\t\t\t\t} else {\n\t\t\t\t\tfor (BlockNode p : loopEnd.getPredecessors()) {\n\t\t\t\t\t\tif (list.contains(p) || p == caseBlock) {\n\t\t\t\t\t\t\tif (p.isSynthetic()) {\n\t\t\t\t\t\t\t\tp.getInstructions().add(new InsnNode(InsnType.CONTINUE, 0));\n\t\t\t\t\t\t\t\tinserted = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn inserted;\n\t}\n\n\t/**\n\t * Add break to every exit edge from 'case' region.\n\t * 'Break' optimizations (code duplication, unreachable, etc.) will be done at\n\t * {@link SwitchBreakVisitor}\n\t */\n\tprivate static void insertBreaksForCase(MethodNode mth, SwitchRegion switchRegion, IContainer caseContainer) {\n\t\tBlockSet caseBlocks = new BlockSet(mth);\n\t\tRegionUtils.visitBlockNodes(mth, caseContainer, caseBlocks::add);\n\t\tDepthRegionTraversal.traverse(mth, caseContainer, new AbstractRegionVisitor() {\n\t\t\t@Override\n\t\t\tpublic void leaveRegion(MethodNode mth, IRegion region) {\n\t\t\t\tboolean insertBreak = false;\n\t\t\t\tif (region == caseContainer) {\n\t\t\t\t\t// top region\n\t\t\t\t\tinsertBreak = true;\n\t\t\t\t} else {\n\t\t\t\t\tIContainer lastContainer = ListUtils.last(region.getSubBlocks());\n\t\t\t\t\tif (lastContainer instanceof BlockNode) {\n\t\t\t\t\t\tBlockNode lastBlock = (BlockNode) lastContainer;\n\t\t\t\t\t\tfor (BlockNode successor : lastBlock.getSuccessors()) {\n\t\t\t\t\t\t\tif (!caseBlocks.contains(successor)) {\n\t\t\t\t\t\t\t\tinsertBreak = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (insertBreak && canAppendBreak(region)) {\n\t\t\t\t\tregion.getSubBlocks().add(buildBreakContainer(switchRegion));\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic static boolean canAppendBreak(IRegion region) {\n\t\treturn !region.contains(AFlag.FALL_THROUGH) && !RegionUtils.hasExitBlock(region);\n\t}\n\n\tpublic static InsnContainer buildBreakContainer(SwitchRegion switchRegion) {\n\t\tInsnNode breakInsn = new InsnNode(InsnType.BREAK, 0);\n\t\tbreakInsn.add(AFlag.SYNTHETIC);\n\t\tbreakInsn.addAttr(new RegionRefAttr(switchRegion));\n\t\treturn new InsnContainer(breakInsn);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/SynchronizedRegionMaker.java",
    "content": "package jadx.core.dex.visitors.regions.maker;\n\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.ConstClassNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.Region;\nimport jadx.core.dex.regions.SynchronizedRegion;\nimport jadx.core.dex.visitors.regions.CleanRegions;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.Utils;\n\nimport static jadx.core.utils.BlockUtils.getNextBlock;\nimport static jadx.core.utils.BlockUtils.isPathExists;\n\npublic class SynchronizedRegionMaker {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SynchronizedRegionMaker.class);\n\tprivate final MethodNode mth;\n\tprivate final RegionMaker regionMaker;\n\n\tSynchronizedRegionMaker(MethodNode mth, RegionMaker regionMaker) {\n\t\tthis.mth = mth;\n\t\tthis.regionMaker = regionMaker;\n\t}\n\n\tBlockNode process(IRegion curRegion, BlockNode block, InsnNode insn, RegionStack stack) {\n\t\tSynchronizedRegion synchRegion = new SynchronizedRegion(curRegion, insn);\n\t\tsynchRegion.getSubBlocks().add(block);\n\t\tcurRegion.getSubBlocks().add(synchRegion);\n\n\t\tSet<BlockNode> exits = new LinkedHashSet<>();\n\t\tSet<BlockNode> cacheSet = new HashSet<>();\n\t\ttraverseMonitorExits(synchRegion, insn.getArg(0), block, exits, cacheSet);\n\n\t\tfor (InsnNode exitInsn : synchRegion.getExitInsns()) {\n\t\t\tBlockNode insnBlock = BlockUtils.getBlockByInsn(mth, exitInsn);\n\t\t\tif (insnBlock != null) {\n\t\t\t\tinsnBlock.add(AFlag.DONT_GENERATE);\n\t\t\t}\n\t\t\t// remove arg from MONITOR_EXIT to allow inline in MONITOR_ENTER\n\t\t\texitInsn.removeArg(0);\n\t\t\texitInsn.add(AFlag.DONT_GENERATE);\n\t\t}\n\n\t\tBlockNode body = getNextBlock(block);\n\t\tif (body == null) {\n\t\t\tmth.addWarn(\"Unexpected end of synchronized block\");\n\t\t\treturn null;\n\t\t}\n\t\tBlockNode exit = null;\n\t\tif (exits.size() == 1) {\n\t\t\texit = getNextBlock(exits.iterator().next());\n\t\t} else if (exits.size() > 1) {\n\t\t\tcacheSet.clear();\n\t\t\texit = traverseMonitorExitsCross(body, exits, cacheSet);\n\t\t}\n\n\t\tstack.push(synchRegion);\n\t\tif (exit != null) {\n\t\t\tstack.addExit(exit);\n\t\t} else {\n\t\t\tfor (BlockNode exitBlock : exits) {\n\t\t\t\t// don't add exit blocks which leads to method end blocks ('return', 'throw', etc)\n\t\t\t\tList<BlockNode> list = BlockUtils.buildSimplePath(exitBlock);\n\t\t\t\tif (list.isEmpty() || !BlockUtils.isExitBlock(mth, Utils.last(list))) {\n\t\t\t\t\tstack.addExit(exitBlock);\n\t\t\t\t\t// we can still try using this as an exit block to make sure it's visited.\n\t\t\t\t\texit = exitBlock;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tsynchRegion.getSubBlocks().add(regionMaker.makeRegion(body));\n\t\tstack.pop();\n\t\treturn exit;\n\t}\n\n\t/**\n\t * Traverse from monitor-enter thru successors and collect blocks contains monitor-exit\n\t */\n\tprivate static void traverseMonitorExits(SynchronizedRegion region, InsnArg arg, BlockNode block, Set<BlockNode> exits,\n\t\t\tSet<BlockNode> visited) {\n\t\tvisited.add(block);\n\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\tif (insn.getType() == InsnType.MONITOR_EXIT\n\t\t\t\t\t&& insn.getArgsCount() > 0\n\t\t\t\t\t&& insn.getArg(0).equals(arg)) {\n\t\t\t\texits.add(block);\n\t\t\t\tregion.getExitInsns().add(insn);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tfor (BlockNode node : block.getSuccessors()) {\n\t\t\tif (!visited.contains(node)) {\n\t\t\t\ttraverseMonitorExits(region, arg, node, exits, visited);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Traverse from monitor-enter thru successors and search for exit paths cross\n\t */\n\tprivate static BlockNode traverseMonitorExitsCross(BlockNode block, Set<BlockNode> exits, Set<BlockNode> visited) {\n\t\tvisited.add(block);\n\t\tfor (BlockNode node : block.getCleanSuccessors()) {\n\t\t\tboolean cross = true;\n\t\t\tfor (BlockNode exitBlock : exits) {\n\t\t\t\tboolean p = isPathExists(exitBlock, node);\n\t\t\t\tif (!p) {\n\t\t\t\t\tcross = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (cross) {\n\t\t\t\treturn node;\n\t\t\t}\n\t\t\tif (!visited.contains(node)) {\n\t\t\t\tBlockNode res = traverseMonitorExitsCross(node, exits, visited);\n\t\t\t\tif (res != null) {\n\t\t\t\t\treturn res;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static void removeSynchronized(MethodNode mth) {\n\t\tRegion startRegion = mth.getRegion();\n\t\tList<IContainer> subBlocks = startRegion.getSubBlocks();\n\t\tif (!subBlocks.isEmpty() && subBlocks.get(0) instanceof SynchronizedRegion) {\n\t\t\tSynchronizedRegion synchRegion = (SynchronizedRegion) subBlocks.get(0);\n\t\t\tInsnNode syncInsn = synchRegion.getEnterInsn();\n\t\t\tif (canRemoveSyncBlock(mth, syncInsn)) {\n\t\t\t\t// replace synchronized block with an inner region\n\t\t\t\tstartRegion.getSubBlocks().set(0, synchRegion.getRegion());\n\t\t\t\t// remove 'monitor-enter' instruction\n\t\t\t\tInsnRemover.remove(mth, syncInsn);\n\t\t\t\t// remove 'monitor-exit' instruction\n\t\t\t\tfor (InsnNode exit : synchRegion.getExitInsns()) {\n\t\t\t\t\tInsnRemover.remove(mth, exit);\n\t\t\t\t}\n\t\t\t\t// run region cleaner again\n\t\t\t\tCleanRegions.process(mth);\n\t\t\t\t// assume that CodeShrinker will be run after this\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean canRemoveSyncBlock(MethodNode mth, InsnNode synchInsn) {\n\t\tInsnArg syncArg = synchInsn.getArg(0);\n\t\tif (mth.getAccessFlags().isStatic()) {\n\t\t\tif (syncArg.isInsnWrap() && syncArg.isConst()) {\n\t\t\t\tInsnNode constInsn = syncArg.unwrap();\n\t\t\t\tif (constInsn.getType() == InsnType.CONST_CLASS) {\n\t\t\t\t\tArgType clsType = ((ConstClassNode) constInsn).getClsType();\n\t\t\t\t\tif (clsType.equals(mth.getParentClass().getType())) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tmth.addWarnComment(\"In static synchronized method top region not synchronized by class const: \" + syncArg);\n\t\t} else {\n\t\t\tif (syncArg.isThis()) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tmth.addWarnComment(\"In synchronized method top region not synchronized by 'this': \" + syncArg);\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/variables/CollectUsageRegionVisitor.java",
    "content": "package jadx.core.dex.visitors.regions.variables;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.loops.ForLoop;\nimport jadx.core.dex.regions.loops.LoopRegion;\nimport jadx.core.dex.regions.loops.LoopType;\nimport jadx.core.dex.visitors.regions.TracedRegionVisitor;\n\nclass CollectUsageRegionVisitor extends TracedRegionVisitor {\n\tprivate final List<RegisterArg> args = new ArrayList<>();\n\tprivate final Map<SSAVar, VarUsage> usageMap = new LinkedHashMap<>();\n\n\tpublic Map<SSAVar, VarUsage> getUsageMap() {\n\t\treturn usageMap;\n\t}\n\n\t@Override\n\tpublic void processBlockTraced(MethodNode mth, IBlock block, IRegion curRegion) {\n\t\tUsePlace usePlace = new UsePlace(curRegion, block);\n\t\tregionProcess(usePlace);\n\t\tint len = block.getInstructions().size();\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tInsnNode insn = block.getInstructions().get(i);\n\t\t\tprocessInsn(insn, usePlace);\n\t\t}\n\t}\n\n\tprivate void regionProcess(UsePlace usePlace) {\n\t\tIRegion region = usePlace.getRegion();\n\t\tif (region instanceof LoopRegion) {\n\t\t\tLoopRegion loopRegion = (LoopRegion) region;\n\t\t\tLoopType loopType = loopRegion.getType();\n\t\t\tif (loopType instanceof ForLoop) {\n\t\t\t\tForLoop forLoop = (ForLoop) loopType;\n\t\t\t\tprocessInsn(forLoop.getInitInsn(), usePlace);\n\t\t\t\tprocessInsn(forLoop.getIncrInsn(), usePlace);\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid processInsn(InsnNode insn, UsePlace usePlace) {\n\t\tif (insn == null) {\n\t\t\treturn;\n\t\t}\n\t\t// result\n\t\tRegisterArg result = insn.getResult();\n\t\tif (result != null && result.isRegister()) {\n\t\t\tif (!result.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\tVarUsage usage = getUsage(result.getSVar());\n\t\t\t\tusage.getAssigns().add(usePlace);\n\t\t\t}\n\t\t}\n\t\t// args\n\t\targs.clear();\n\t\tinsn.getRegisterArgs(args);\n\t\tfor (RegisterArg arg : args) {\n\t\t\tif (!arg.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\tVarUsage usage = getUsage(arg.getSVar());\n\t\t\t\tusage.getUses().add(usePlace);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate VarUsage getUsage(SSAVar ssaVar) {\n\t\treturn usageMap.computeIfAbsent(ssaVar, VarUsage::new);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/variables/ProcessVariables.java",
    "content": "package jadx.core.dex.visitors.regions.variables;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.DeclareVariablesAttr;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.CodeVar;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.loops.LoopRegion;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.regions.AbstractRegionVisitor;\nimport jadx.core.dex.visitors.regions.DepthRegionTraversal;\nimport jadx.core.dex.visitors.typeinference.TypeCompare;\nimport jadx.core.dex.visitors.typeinference.TypeCompareEnum;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.RegionUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxException;\n\npublic class ProcessVariables extends AbstractVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ProcessVariables.class);\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (mth.isNoCode() || mth.getSVars().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tremoveUnusedResults(mth);\n\n\t\tList<CodeVar> codeVars = collectCodeVars(mth);\n\t\tif (codeVars.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tcheckCodeVars(mth, codeVars);\n\t\t// TODO: reduce code vars by name if debug info applied (need checks for variable scopes)\n\n\t\t// collect all variables usage\n\t\tCollectUsageRegionVisitor usageCollector = new CollectUsageRegionVisitor();\n\t\tDepthRegionTraversal.traverse(mth, usageCollector);\n\t\tMap<SSAVar, VarUsage> ssaUsageMap = usageCollector.getUsageMap();\n\t\tif (ssaUsageMap.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\n\t\tMap<CodeVar, List<VarUsage>> codeVarUsage = mergeUsageMaps(codeVars, ssaUsageMap);\n\n\t\tfor (Entry<CodeVar, List<VarUsage>> entry : codeVarUsage.entrySet()) {\n\t\t\tdeclareVar(mth, entry.getKey(), entry.getValue());\n\t\t}\n\t}\n\n\tprivate static void removeUnusedResults(MethodNode mth) {\n\t\tDepthRegionTraversal.traverse(mth, new AbstractRegionVisitor() {\n\t\t\t@Override\n\t\t\tpublic void processBlock(MethodNode mth, IBlock container) {\n\t\t\t\tfor (InsnNode insn : container.getInstructions()) {\n\t\t\t\t\tRegisterArg resultArg = insn.getResult();\n\t\t\t\t\tif (resultArg == null) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tSSAVar ssaVar = resultArg.getSVar();\n\t\t\t\t\tif (isVarUnused(mth, ssaVar)) {\n\t\t\t\t\t\tboolean remove = false;\n\t\t\t\t\t\tif (insn.canRemoveResult()) {\n\t\t\t\t\t\t\t// remove unused result\n\t\t\t\t\t\t\tremove = true;\n\t\t\t\t\t\t} else if (canRemoveInsn(insn)) {\n\t\t\t\t\t\t\t// remove whole insn\n\t\t\t\t\t\t\tinsn.add(AFlag.REMOVE);\n\t\t\t\t\t\t\tinsn.add(AFlag.DONT_GENERATE);\n\t\t\t\t\t\t\tremove = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (remove) {\n\t\t\t\t\t\t\tinsn.setResult(null);\n\t\t\t\t\t\t\tmth.removeSVar(ssaVar);\n\t\t\t\t\t\t\tfor (RegisterArg arg : ssaVar.getUseList()) {\n\t\t\t\t\t\t\t\targ.resetSSAVar();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Remove insn if a result is not used\n\t\t\t */\n\t\t\tprivate boolean canRemoveInsn(InsnNode insn) {\n\t\t\t\tif (insn.isConstInsn()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tswitch (insn.getType()) {\n\t\t\t\t\tcase CAST:\n\t\t\t\t\tcase CHECK_CAST:\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tprivate boolean isVarUnused(MethodNode mth, @Nullable SSAVar ssaVar) {\n\t\t\t\tif (ssaVar == null) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tList<RegisterArg> useList = ssaVar.getUseList();\n\t\t\t\tif (useList.isEmpty()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tif (ssaVar.isUsedInPhi()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn ListUtils.allMatch(useList, arg -> isArgUnused(mth, arg));\n\t\t\t}\n\n\t\t\tprivate boolean isArgUnused(MethodNode mth, RegisterArg arg) {\n\t\t\t\tif (arg.contains(AFlag.REMOVE)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\t// check constructors for removed args\n\t\t\t\tInsnNode parentInsn = arg.getParentInsn();\n\t\t\t\tif (parentInsn != null\n\t\t\t\t\t\t&& parentInsn.getType() == InsnType.CONSTRUCTOR\n\t\t\t\t\t\t&& parentInsn.contains(AType.METHOD_DETAILS)) {\n\t\t\t\t\tMethodNode resolveMth = mth.root().getMethodUtils().resolveMethod(((ConstructorInsn) parentInsn));\n\t\t\t\t\tif (resolveMth != null && resolveMth.contains(AType.SKIP_MTH_ARGS)) {\n\t\t\t\t\t\tint insnPos = parentInsn.getArgIndex(arg);\n\t\t\t\t\t\tList<RegisterArg> mthArgs = resolveMth.getArgRegs();\n\t\t\t\t\t\tif (0 <= insnPos && insnPos < mthArgs.size()) {\n\t\t\t\t\t\t\tRegisterArg mthArg = mthArgs.get(insnPos);\n\t\t\t\t\t\t\tif (mthArg.contains(AFlag.REMOVE) && arg.sameType(mthArg)) {\n\t\t\t\t\t\t\t\targ.add(AFlag.DONT_GENERATE);\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void checkCodeVars(MethodNode mth, List<CodeVar> codeVars) {\n\t\tint unknownTypesCount = 0;\n\t\tfor (CodeVar codeVar : codeVars) {\n\t\t\tArgType codeVarType = codeVar.getType();\n\t\t\tif (codeVarType == null) {\n\t\t\t\tcodeVar.setType(ArgType.UNKNOWN);\n\t\t\t\tunknownTypesCount++;\n\t\t\t} else {\n\t\t\t\tcodeVar.getSsaVars()\n\t\t\t\t\t\t.forEach(ssaVar -> {\n\t\t\t\t\t\t\tArgType ssaType = ssaVar.getImmutableType();\n\t\t\t\t\t\t\tif (ssaType != null && ssaType.isTypeKnown()) {\n\t\t\t\t\t\t\t\tTypeCompare comparator = mth.root().getTypeUpdate().getTypeCompare();\n\t\t\t\t\t\t\t\tTypeCompareEnum result = comparator.compareTypes(ssaType, codeVarType);\n\t\t\t\t\t\t\t\tif (result == TypeCompareEnum.CONFLICT || result.isNarrow()) {\n\t\t\t\t\t\t\t\t\tmth.addWarn(\"Incorrect type for immutable var: ssa=\" + ssaType\n\t\t\t\t\t\t\t\t\t\t\t+ \", code=\" + codeVarType\n\t\t\t\t\t\t\t\t\t\t\t+ \", for \" + ssaVar.getDetailedVarInfo(mth));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tif (unknownTypesCount != 0) {\n\t\t\tmth.addWarn(\"Unknown variable types count: \" + unknownTypesCount);\n\t\t}\n\t}\n\n\tprivate void declareVar(MethodNode mth, CodeVar codeVar, List<VarUsage> usageList) {\n\t\tif (codeVar.isDeclared()) {\n\t\t\treturn;\n\t\t}\n\n\t\tVarUsage mergedUsage = new VarUsage(null);\n\t\tfor (VarUsage varUsage : usageList) {\n\t\t\tmergedUsage.getAssigns().addAll(varUsage.getAssigns());\n\t\t\tmergedUsage.getUses().addAll(varUsage.getUses());\n\t\t}\n\t\tif (mergedUsage.getAssigns().isEmpty() && mergedUsage.getUses().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\n\t\t// check if variable can be declared at one of assigns\n\t\tif (checkDeclareAtAssign(usageList, mergedUsage)) {\n\t\t\treturn;\n\t\t}\n\t\t// TODO: search closest region for declare\n\n\t\t// region not found, declare at method start\n\t\tdeclareVarInRegion(mth.getRegion(), codeVar);\n\t}\n\n\tprivate List<CodeVar> collectCodeVars(MethodNode mth) {\n\t\tMap<CodeVar, List<SSAVar>> codeVars = new LinkedHashMap<>();\n\t\tfor (SSAVar ssaVar : mth.getSVars()) {\n\t\t\tif (ssaVar.getCodeVar().isThis()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tCodeVar codeVar = ssaVar.getCodeVar();\n\t\t\tList<SSAVar> list = codeVars.computeIfAbsent(codeVar, k -> new ArrayList<>());\n\t\t\tlist.add(ssaVar);\n\t\t}\n\n\t\tfor (Entry<CodeVar, List<SSAVar>> entry : codeVars.entrySet()) {\n\t\t\tCodeVar codeVar = entry.getKey();\n\t\t\tList<SSAVar> list = entry.getValue();\n\t\t\tfor (SSAVar ssaVar : list) {\n\t\t\t\tCodeVar localCodeVar = ssaVar.getCodeVar();\n\t\t\t\tcodeVar.mergeFlagsFrom(localCodeVar);\n\t\t\t}\n\t\t\tif (list.size() > 1) {\n\t\t\t\tfor (SSAVar ssaVar : list) {\n\t\t\t\t\tssaVar.setCodeVar(codeVar);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcodeVar.setSsaVars(list);\n\t\t}\n\t\treturn new ArrayList<>(codeVars.keySet());\n\t}\n\n\tprivate Map<CodeVar, List<VarUsage>> mergeUsageMaps(List<CodeVar> codeVars, Map<SSAVar, VarUsage> ssaUsageMap) {\n\t\tMap<CodeVar, List<VarUsage>> codeVarUsage = new LinkedHashMap<>(codeVars.size());\n\t\tfor (CodeVar codeVar : codeVars) {\n\t\t\tList<VarUsage> list = new ArrayList<>();\n\t\t\tfor (SSAVar ssaVar : codeVar.getSsaVars()) {\n\t\t\t\tVarUsage usage = ssaUsageMap.get(ssaVar);\n\t\t\t\tif (usage != null) {\n\t\t\t\t\tlist.add(usage);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcodeVarUsage.put(codeVar, Utils.lockList(list));\n\t\t}\n\t\treturn codeVarUsage;\n\t}\n\n\tprivate boolean checkDeclareAtAssign(List<VarUsage> list, VarUsage mergedUsage) {\n\t\tif (mergedUsage.getAssigns().isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (VarUsage u : list) {\n\t\t\tfor (UsePlace assign : u.getAssigns()) {\n\t\t\t\tif (canDeclareAt(mergedUsage, assign)) {\n\t\t\t\t\treturn checkDeclareAtAssign(u.getVar());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean canDeclareAt(VarUsage usage, UsePlace usePlace) {\n\t\tIRegion region = usePlace.getRegion();\n\t\t// workaround for declare variables used in several loops\n\t\tif (region instanceof LoopRegion) {\n\t\t\tfor (UsePlace use : usage.getAssigns()) {\n\t\t\t\tif (!RegionUtils.isRegionContainsRegion(region, use.getRegion())) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// can't declare in else-if chain between 'else' and next 'if'\n\t\tif (region.contains(AFlag.ELSE_IF_CHAIN)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn isAllUseAfter(usePlace, usage.getAssigns())\n\t\t\t\t&& isAllUseAfter(usePlace, usage.getUses());\n\t}\n\n\t/**\n\t * Check if all {@code usePlaces} are after {@code checkPlace}\n\t */\n\tprivate static boolean isAllUseAfter(UsePlace checkPlace, List<UsePlace> usePlaces) {\n\n\t\tIRegion region = checkPlace.getRegion();\n\t\tIBlock block = checkPlace.getBlock();\n\t\tSet<UsePlace> toCheck = new HashSet<>(usePlaces);\n\t\tboolean blockFound = false;\n\t\tfor (IContainer subBlock : region.getSubBlocks()) {\n\t\t\tif (!blockFound && subBlock == block) {\n\t\t\t\tblockFound = true;\n\t\t\t}\n\t\t\tif (blockFound) {\n\t\t\t\ttoCheck.removeIf(usePlace -> isContainerContainsUsePlace(subBlock, usePlace));\n\t\t\t\tif (toCheck.isEmpty()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean isContainerContainsUsePlace(IContainer subBlock, UsePlace usePlace) {\n\t\tif (subBlock == usePlace.getBlock()) {\n\t\t\treturn true;\n\t\t}\n\t\tif (subBlock instanceof IRegion) {\n\t\t\t// TODO: make index for faster check\n\t\t\treturn RegionUtils.isRegionContainsRegion(subBlock, usePlace.getRegion());\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean checkDeclareAtAssign(SSAVar var) {\n\t\tRegisterArg arg = var.getAssign();\n\t\tInsnNode parentInsn = arg.getParentInsn();\n\t\tif (parentInsn == null\n\t\t\t\t|| parentInsn.contains(AFlag.WRAPPED)\n\t\t\t\t|| parentInsn.getType() == InsnType.PHI) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!arg.equals(parentInsn.getResult())) {\n\t\t\treturn false;\n\t\t}\n\t\tparentInsn.add(AFlag.DECLARE_VAR);\n\t\tvar.getCodeVar().setDeclared(true);\n\t\treturn true;\n\t}\n\n\tprivate static void declareVarInRegion(IContainer region, CodeVar var) {\n\t\tif (var.isDeclared()) {\n\t\t\tLOG.warn(\"Try to declare already declared variable: {}\", var);\n\t\t\treturn;\n\t\t}\n\t\tDeclareVariablesAttr dv = region.get(AType.DECLARE_VARIABLES);\n\t\tif (dv == null) {\n\t\t\tdv = new DeclareVariablesAttr();\n\t\t\tregion.addAttr(dv);\n\t\t}\n\t\tdv.addVar(var);\n\t\tvar.setDeclared(true);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/variables/UsePlace.java",
    "content": "package jadx.core.dex.visitors.regions.variables;\n\nimport java.util.Objects;\n\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IRegion;\n\npublic class UsePlace {\n\tpublic final IRegion region;\n\tpublic final IBlock block;\n\n\tpublic UsePlace(IRegion region, IBlock block) {\n\t\tthis.region = region;\n\t\tthis.block = block;\n\t}\n\n\tpublic IRegion getRegion() {\n\t\treturn region;\n\t}\n\n\tpublic IBlock getBlock() {\n\t\treturn block;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tUsePlace usePlace = (UsePlace) o;\n\t\treturn Objects.equals(region, usePlace.region)\n\t\t\t\t&& Objects.equals(block, usePlace.block);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(region, block);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"UsePlace{region=\" + region + \", block=\" + block + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/regions/variables/VarUsage.java",
    "content": "package jadx.core.dex.visitors.regions.variables;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.core.dex.instructions.args.SSAVar;\n\nclass VarUsage {\n\tprivate final SSAVar var;\n\tprivate final List<UsePlace> assigns = new ArrayList<>(3);\n\tprivate final List<UsePlace> uses = new ArrayList<>(3);\n\n\tVarUsage(SSAVar var) {\n\t\tthis.var = var;\n\t}\n\n\tpublic SSAVar getVar() {\n\t\treturn var;\n\t}\n\n\tpublic List<UsePlace> getAssigns() {\n\t\treturn assigns;\n\t}\n\n\tpublic List<UsePlace> getUses() {\n\t\treturn uses;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn '{' + (var == null ? \"-\" : var.toShortString()) + \", a:\" + assigns + \", u:\" + uses + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/rename/CodeRenameVisitor.java",
    "content": "package jadx.core.dex.visitors.rename;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.data.ICodeData;\nimport jadx.api.data.ICodeRename;\nimport jadx.api.data.IJavaCodeRef;\nimport jadx.api.data.IJavaNodeRef;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.InitCodeVariables;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.dex.visitors.debuginfo.DebugInfoApplyVisitor;\nimport jadx.core.utils.exceptions.JadxException;\n\n@JadxVisitor(\n\t\tname = \"ApplyCodeRename\",\n\t\tdesc = \"Rename variables and other entities in methods\",\n\t\trunAfter = {\n\t\t\t\tInitCodeVariables.class,\n\t\t\t\tDebugInfoApplyVisitor.class\n\t\t}\n)\npublic class CodeRenameVisitor extends AbstractVisitor {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CodeRenameVisitor.class);\n\n\tprivate Map<String, List<ICodeRename>> clsRenamesMap;\n\n\t@Override\n\tpublic void init(RootNode root) throws JadxException {\n\t\tupdateRenamesMap(root.getArgs().getCodeData());\n\t\troot.registerCodeDataUpdateListener(this::updateRenamesMap);\n\t}\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) {\n\t\tList<ICodeRename> renames = getRenames(cls);\n\t\tif (!renames.isEmpty()) {\n\t\t\tapplyRenames(cls, renames);\n\t\t}\n\t\tcls.getInnerClasses().forEach(this::visit);\n\t\treturn false;\n\t}\n\n\tprivate static void applyRenames(ClassNode cls, List<ICodeRename> renames) {\n\t\tfor (ICodeRename rename : renames) {\n\t\t\tIJavaNodeRef nodeRef = rename.getNodeRef();\n\t\t\tif (nodeRef.getType() == IJavaNodeRef.RefType.METHOD) {\n\t\t\t\tMethodNode methodNode = cls.searchMethodByShortId(nodeRef.getShortId());\n\t\t\t\tif (methodNode == null) {\n\t\t\t\t\tLOG.warn(\"Method reference not found: {}\", nodeRef);\n\t\t\t\t} else {\n\t\t\t\t\tIJavaCodeRef codeRef = rename.getCodeRef();\n\t\t\t\t\tif (codeRef != null) {\n\t\t\t\t\t\tprocessRename(methodNode, codeRef, rename);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void processRename(MethodNode mth, IJavaCodeRef codeRef, ICodeRename rename) {\n\t\tswitch (codeRef.getAttachType()) {\n\t\t\tcase MTH_ARG: {\n\t\t\t\tList<RegisterArg> argRegs = mth.getArgRegs();\n\t\t\t\tint argNum = codeRef.getIndex();\n\t\t\t\tif (argNum < argRegs.size()) {\n\t\t\t\t\targRegs.get(argNum).getSVar().getCodeVar().setName(rename.getNewName());\n\t\t\t\t} else {\n\t\t\t\t\tLOG.warn(\"Incorrect method arg ref {}, should be less than {}\", argNum, argRegs.size());\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase VAR: {\n\t\t\t\tint regNum = codeRef.getIndex() >> 16;\n\t\t\t\tint ssaVer = codeRef.getIndex() & 0xFFFF;\n\t\t\t\tfor (SSAVar ssaVar : mth.getSVars()) {\n\t\t\t\t\tif (ssaVar.getRegNum() == regNum && ssaVar.getVersion() == ssaVer) {\n\t\t\t\t\t\tssaVar.getCodeVar().setName(rename.getNewName());\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tLOG.warn(\"Can't find variable ref by {}_{}\", regNum, ssaVer);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tLOG.warn(\"Rename code ref type {} not yet supported\", codeRef.getAttachType());\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate List<ICodeRename> getRenames(ClassNode cls) {\n\t\tif (clsRenamesMap == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<ICodeRename> clsComments = clsRenamesMap.get(cls.getClassInfo().getRawName());\n\t\tif (clsComments == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn clsComments;\n\t}\n\n\tprivate void updateRenamesMap(@Nullable ICodeData data) {\n\t\tif (data == null) {\n\t\t\tthis.clsRenamesMap = Collections.emptyMap();\n\t\t} else {\n\t\t\tthis.clsRenamesMap = data.getRenames().stream()\n\t\t\t\t\t.filter(r -> r.getCodeRef() != null)\n\t\t\t\t\t.collect(Collectors.groupingBy(r -> r.getNodeRef().getDeclaringClass()));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/rename/RenameVisitor.java",
    "content": "package jadx.core.dex.visitors.rename;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.regex.Pattern;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.deobf.IAliasProvider;\nimport jadx.core.Consts;\nimport jadx.core.deobf.NameMapper;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.MethodOverrideAttr;\nimport jadx.core.dex.attributes.nodes.RenameReasonAttr;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.utils.StringUtils;\n\npublic class RenameVisitor extends AbstractVisitor {\n\tprivate static final Pattern ANONYMOUS_CLASS_PATTERN = Pattern.compile(\"^\\\\d+$\");\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tList<File> inputFiles = root.getArgs().getInputFiles();\n\t\tif (inputFiles.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tprocess(root);\n\t\troot.registerCodeDataUpdateListener(codeData -> process(root));\n\t}\n\n\tprivate void process(RootNode root) {\n\t\tUserRenames.apply(root);\n\t\tcheckNames(root);\n\t}\n\n\tprivate static void checkNames(RootNode root) {\n\t\tJadxArgs args = root.getArgs();\n\t\tif (args.getRenameFlags().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\n\t\tIAliasProvider aliasProvider = args.getAliasProvider();\n\n\t\tList<ClassNode> classes = root.getClasses(true);\n\t\tfor (ClassNode cls : classes) {\n\t\t\tcheckClassName(aliasProvider, cls, args);\n\t\t\tcheckFields(aliasProvider, cls, args);\n\t\t\tcheckMethods(aliasProvider, cls, args);\n\t\t}\n\t\tif (!args.isFsCaseSensitive() && args.isRenameCaseSensitive()) {\n\t\t\tSet<String> clsFullPaths = new HashSet<>(classes.size());\n\t\t\tfor (ClassNode cls : classes) {\n\t\t\t\tClassInfo clsInfo = cls.getClassInfo();\n\t\t\t\tif (!clsFullPaths.add(clsInfo.getAliasFullPath().toLowerCase())) {\n\t\t\t\t\tclsInfo.changeShortName(aliasProvider.forClass(cls));\n\t\t\t\t\tcls.addAttr(new RenameReasonAttr(cls).append(\"case insensitive filesystem\"));\n\t\t\t\t\tclsFullPaths.add(clsInfo.getAliasFullPath().toLowerCase());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tboolean pkgUpdated = false;\n\t\tfor (PackageNode pkg : root.getPackages()) {\n\t\t\tpkgUpdated |= checkPackage(args, aliasProvider, pkg);\n\t\t}\n\t\tif (pkgUpdated) {\n\t\t\troot.runPackagesUpdate();\n\t\t}\n\t\tprocessRootPackages(aliasProvider, root, classes);\n\t}\n\n\tprivate static void checkClassName(IAliasProvider aliasProvider, ClassNode cls, JadxArgs args) {\n\t\tif (cls.contains(AFlag.DONT_RENAME)) {\n\t\t\treturn;\n\t\t}\n\t\tClassInfo classInfo = cls.getClassInfo();\n\t\tString clsName = classInfo.getAliasShortName();\n\n\t\tString newShortName = fixClsShortName(args, clsName);\n\t\tif (newShortName == null) {\n\t\t\t// rename failed, use deobfuscator\n\t\t\tcls.rename(aliasProvider.forClass(cls));\n\t\t\tcls.addAttr(new RenameReasonAttr(cls).notPrintable());\n\t\t\treturn;\n\t\t}\n\t\tif (!newShortName.equals(clsName)) {\n\t\t\tclassInfo.changeShortName(newShortName);\n\t\t\tcls.addAttr(new RenameReasonAttr(cls).append(\"invalid class name\"));\n\t\t}\n\t\tif (classInfo.isInner() && args.isRenameValid()) {\n\t\t\t// check inner classes names\n\t\t\tClassInfo parentClass = classInfo.getParentClass();\n\t\t\twhile (parentClass != null) {\n\t\t\t\tif (parentClass.getAliasShortName().equals(newShortName)) {\n\t\t\t\t\tcls.rename(aliasProvider.forClass(cls));\n\t\t\t\t\tcls.addAttr(new RenameReasonAttr(cls).append(\"collision with other inner class name\"));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tparentClass = parentClass.getParentClass();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean checkPackage(JadxArgs args, IAliasProvider aliasProvider, PackageNode pkg) {\n\t\tif (args.isRenameValid() && pkg.getAliasPkgInfo().isDefaultPkg()) {\n\t\t\tpkg.setFullAlias(Consts.DEFAULT_PACKAGE_NAME, false);\n\t\t\treturn true;\n\t\t}\n\t\tString pkgName = pkg.getAliasPkgInfo().getName();\n\t\tboolean notValid = args.isRenameValid() && !NameMapper.isValidIdentifier(pkgName);\n\t\tboolean notPrintable = args.isRenamePrintable() && !NameMapper.isAllCharsPrintable(pkgName);\n\t\tif (notValid || notPrintable) {\n\t\t\tpkg.setLeafAlias(aliasProvider.forPackage(pkg), false);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Nullable\n\tprivate static String fixClsShortName(JadxArgs args, String clsName) {\n\t\tif (StringUtils.isEmpty(clsName)) {\n\t\t\treturn null;\n\t\t}\n\t\tboolean renameValid = args.isRenameValid();\n\t\tif (renameValid) {\n\t\t\tif (ANONYMOUS_CLASS_PATTERN.matcher(clsName).matches()) {\n\t\t\t\treturn Consts.ANONYMOUS_CLASS_PREFIX + NameMapper.removeInvalidCharsMiddle(clsName);\n\t\t\t}\n\n\t\t\tchar firstChar = clsName.charAt(0);\n\t\t\tif (firstChar == '$' || Character.isDigit(firstChar)) {\n\t\t\t\treturn 'C' + NameMapper.removeInvalidCharsMiddle(clsName);\n\t\t\t}\n\t\t}\n\t\tString cleanClsName = args.isRenamePrintable()\n\t\t\t\t? NameMapper.removeNonPrintableCharacters(clsName)\n\t\t\t\t: clsName;\n\t\tif (cleanClsName.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (renameValid) {\n\t\t\tcleanClsName = NameMapper.removeInvalidChars(clsName, \"C\");\n\t\t\tif (!NameMapper.isValidIdentifier(cleanClsName)) {\n\t\t\t\treturn 'C' + cleanClsName;\n\t\t\t}\n\t\t}\n\t\treturn cleanClsName;\n\t}\n\n\tprivate static void checkFields(IAliasProvider aliasProvider, ClassNode cls, JadxArgs args) {\n\t\tSet<String> names = new HashSet<>();\n\t\tfor (FieldNode field : cls.getFields()) {\n\t\t\tFieldInfo fieldInfo = field.getFieldInfo();\n\t\t\tString fieldName = fieldInfo.getAlias();\n\t\t\tboolean notUnique = !names.add(fieldName);\n\t\t\tboolean notValid = args.isRenameValid() && !NameMapper.isValidIdentifier(fieldName);\n\t\t\tboolean notPrintable = args.isRenamePrintable() && !NameMapper.isAllCharsPrintable(fieldName);\n\t\t\tif (notUnique || notValid || notPrintable) {\n\t\t\t\tfield.rename(aliasProvider.forField(field));\n\t\t\t\tfield.addAttr(new RenameReasonAttr(field, notValid, notPrintable));\n\t\t\t\tif (notUnique) {\n\t\t\t\t\tfield.addAttr(new RenameReasonAttr(field).append(\"collision with other field name\"));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void checkMethods(IAliasProvider aliasProvider, ClassNode cls, JadxArgs args) {\n\t\tList<MethodNode> methods = new ArrayList<>(cls.getMethods().size());\n\t\tfor (MethodNode method : cls.getMethods()) {\n\t\t\tif (!method.getAccessFlags().isConstructor()) {\n\t\t\t\tmethods.add(method);\n\t\t\t}\n\t\t}\n\t\tfor (MethodNode mth : methods) {\n\t\t\tString alias = mth.getAlias();\n\t\t\tboolean notValid = args.isRenameValid() && !NameMapper.isValidIdentifier(alias);\n\t\t\tboolean notPrintable = args.isRenamePrintable() && !NameMapper.isAllCharsPrintable(alias);\n\t\t\tif (notValid || notPrintable) {\n\t\t\t\tmth.rename(aliasProvider.forMethod(mth));\n\t\t\t\tmth.addAttr(new RenameReasonAttr(mth, notValid, notPrintable));\n\t\t\t}\n\t\t}\n\t\t// Rename methods with same signature\n\t\tif (args.isRenameValid()) {\n\t\t\tSet<String> names = new HashSet<>(methods.size());\n\t\t\tfor (MethodNode mth : methods) {\n\t\t\t\tString signature = mth.getMethodInfo().makeSignature(true, false);\n\t\t\t\tif (!names.add(signature) && canRename(mth)) {\n\t\t\t\t\tmth.rename(aliasProvider.forMethod(mth));\n\t\t\t\t\tmth.addAttr(new RenameReasonAttr(\"collision with other method in class\"));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean canRename(MethodNode mth) {\n\t\tif (mth.contains(AFlag.DONT_RENAME)) {\n\t\t\treturn false;\n\t\t}\n\t\tMethodOverrideAttr overrideAttr = mth.get(AType.METHOD_OVERRIDE);\n\t\tif (overrideAttr != null) {\n\t\t\tfor (MethodNode relatedMth : overrideAttr.getRelatedMthNodes()) {\n\t\t\t\tif (relatedMth != mth && mth.getParentClass().equals(relatedMth.getParentClass())) {\n\t\t\t\t\t// ignore rename if exists related method from same class (bridge method in most cases)\n\t\t\t\t\t// such rename will also rename current method and will not help to resolve name collision\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static void processRootPackages(IAliasProvider aliasProvider, RootNode root, List<ClassNode> classes) {\n\t\tSet<String> rootPkgs = collectRootPkgs(root);\n\t\troot.getCacheStorage().setRootPkgs(rootPkgs);\n\n\t\tif (root.getArgs().isRenameValid()) {\n\t\t\t// rename field if collide with any root package\n\t\t\tfor (ClassNode cls : classes) {\n\t\t\t\tfor (FieldNode field : cls.getFields()) {\n\t\t\t\t\tif (rootPkgs.contains(field.getAlias())) {\n\t\t\t\t\t\tfield.rename(aliasProvider.forField(field));\n\t\t\t\t\t\tfield.addAttr(new RenameReasonAttr(\"collision with root package name\"));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static Set<String> collectRootPkgs(RootNode root) {\n\t\tSet<String> rootPkgs = new HashSet<>();\n\t\tfor (PackageNode pkg : root.getPackages()) {\n\t\t\tif (pkg.isRoot()) {\n\t\t\t\trootPkgs.add(pkg.getPkgInfo().getName());\n\t\t\t}\n\t\t}\n\t\treturn rootPkgs;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"RenameVisitor\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/rename/SourceFileRename.java",
    "content": "package jadx.core.dex.visitors.rename;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.args.UseSourceNameAsClassNameAlias;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.types.SourceFileAttr;\nimport jadx.core.deobf.NameMapper;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.nodes.RenameReasonAttr;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.utils.BetterName;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.exceptions.JadxException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class SourceFileRename extends AbstractVisitor {\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"SourceFileRename\";\n\t}\n\n\t@Override\n\tpublic void init(RootNode root) throws JadxException {\n\t\tfinal var useSourceName = root.getArgs().getUseSourceNameAsClassNameAlias();\n\t\tif (useSourceName == UseSourceNameAsClassNameAlias.NEVER) {\n\t\t\treturn;\n\t\t}\n\t\tint repeatLimit = root.getArgs().getSourceNameRepeatLimit();\n\t\tif (repeatLimit <= 1) {\n\t\t\treturn;\n\t\t}\n\n\t\tList<ClassNode> classes = root.getClasses();\n\t\tMap<String, Integer> aliasUseCount = new HashMap<>();\n\t\tfor (ClassNode cls : classes) {\n\t\t\taliasUseCount.put(cls.getClassInfo().getShortName(), 1);\n\t\t}\n\t\tList<ClsRename> renames = new ArrayList<>();\n\t\tfor (ClassNode cls : classes) {\n\t\t\tif (cls.contains(AFlag.DONT_RENAME)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString alias = getAliasFromSourceFile(cls);\n\t\t\tif (alias != null) {\n\t\t\t\tint count = aliasUseCount.merge(alias, 1, Integer::sum);\n\t\t\t\tif (count < repeatLimit) {\n\t\t\t\t\trenames.add(new ClsRename(cls, alias, count));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (ClsRename clsRename : renames) {\n\t\t\tString alias = clsRename.getAlias();\n\t\t\tInteger count = aliasUseCount.get(alias);\n\t\t\tif (count < repeatLimit) {\n\t\t\t\tapplyRename(clsRename.getCls(), clsRename.buildAlias(), useSourceName);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void applyRename(ClassNode cls, String alias, UseSourceNameAsClassNameAlias useSourceName) {\n\t\tif (cls.getClassInfo().hasAlias()) {\n\t\t\tString currentAlias = cls.getAlias();\n\t\t\tString betterName = getBetterName(currentAlias, alias, useSourceName);\n\t\t\tif (betterName.equals(currentAlias)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tcls.getClassInfo().changeShortName(alias);\n\t\tcls.addAttr(new RenameReasonAttr(cls).append(\"use source file name\"));\n\t}\n\n\tprivate static String getBetterName(String currentName, String sourceName, UseSourceNameAsClassNameAlias useSourceName) {\n\t\tswitch (useSourceName) {\n\t\t\tcase ALWAYS:\n\t\t\t\treturn sourceName;\n\t\t\tcase IF_BETTER:\n\t\t\t\treturn BetterName.getBetterClassName(sourceName, currentName);\n\t\t\tcase NEVER:\n\t\t\t\treturn currentName;\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unhandled strategy: \" + useSourceName);\n\t\t}\n\t}\n\n\tprivate static @Nullable String getAliasFromSourceFile(ClassNode cls) {\n\t\tSourceFileAttr sourceFileAttr = cls.get(JadxAttrType.SOURCE_FILE);\n\t\tif (sourceFileAttr == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (cls.getClassInfo().isInner()) {\n\t\t\treturn null;\n\t\t}\n\t\tString name = sourceFileAttr.getFileName();\n\t\tname = StringUtils.removeSuffix(name, \".java\");\n\t\tname = StringUtils.removeSuffix(name, \".kt\");\n\t\tif (!NameMapper.isValidAndPrintable(name)) {\n\t\t\treturn null;\n\t\t}\n\t\tif (name.equals(cls.getName())) {\n\t\t\treturn null;\n\t\t}\n\t\treturn name;\n\t}\n\n\tprivate static final class ClsRename {\n\t\tprivate final ClassNode cls;\n\t\tprivate final String alias;\n\t\tprivate final int suffix;\n\n\t\tprivate ClsRename(ClassNode cls, String alias, int suffix) {\n\t\t\tthis.cls = cls;\n\t\t\tthis.alias = alias;\n\t\t\tthis.suffix = suffix;\n\t\t}\n\n\t\tpublic ClassNode getCls() {\n\t\t\treturn cls;\n\t\t}\n\n\t\tpublic String getAlias() {\n\t\t\treturn alias;\n\t\t}\n\n\t\tpublic int getSuffix() {\n\t\t\treturn suffix;\n\t\t}\n\n\t\tpublic String buildAlias() {\n\t\t\treturn suffix < 2 ? alias : alias + suffix;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"ClsRename{\" + cls + \" -> '\" + alias + suffix + \"'}\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/rename/UserRenames.java",
    "content": "package jadx.core.dex.visitors.rename;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.data.ICodeData;\nimport jadx.api.data.ICodeRename;\nimport jadx.api.data.IJavaCodeRef;\nimport jadx.api.data.IJavaNodeRef;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.InfoStorage;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.StringUtils;\n\npublic class UserRenames {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(UserRenames.class);\n\n\tpublic static void apply(RootNode root) {\n\t\tICodeData codeData = root.getArgs().getCodeData();\n\t\tif (codeData == null || codeData.getRenames().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tInfoStorage infoStorage = root.getInfoStorage();\n\t\tcodeData.getRenames().stream()\n\t\t\t\t.filter(r -> r.getCodeRef() == null && r.getNodeRef().getType() != IJavaNodeRef.RefType.PKG)\n\t\t\t\t.collect(Collectors.groupingBy(r -> r.getNodeRef().getDeclaringClass()))\n\t\t\t\t.forEach((clsRawName, renames) -> {\n\t\t\t\t\tClassInfo clsInfo = infoStorage.getCls(ArgType.object(clsRawName));\n\t\t\t\t\tif (clsInfo != null) {\n\t\t\t\t\t\tClassNode cls = root.resolveClass(clsInfo);\n\t\t\t\t\t\tif (cls != null) {\n\t\t\t\t\t\t\tfor (ICodeRename rename : renames) {\n\t\t\t\t\t\t\t\tapplyRename(cls, rename);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tLOG.warn(\"Class info with reference '{}' not found\", clsRawName);\n\t\t\t\t});\n\t\tapplyPkgRenames(root, codeData.getRenames());\n\t}\n\n\tprivate static void applyRename(ClassNode cls, ICodeRename rename) {\n\t\tIJavaNodeRef nodeRef = rename.getNodeRef();\n\t\tswitch (nodeRef.getType()) {\n\t\t\tcase CLASS:\n\t\t\t\tcls.rename(rename.getNewName());\n\t\t\t\tbreak;\n\n\t\t\tcase FIELD:\n\t\t\t\tFieldNode fieldNode = cls.searchFieldByShortId(nodeRef.getShortId());\n\t\t\t\tif (fieldNode == null) {\n\t\t\t\t\tString fieldName = StringUtils.getPrefix(nodeRef.getShortId(), \":\");\n\t\t\t\t\tString fieldSign = cls.getFields().stream()\n\t\t\t\t\t\t\t.filter(f -> f.getFieldInfo().getName().equals(fieldName))\n\t\t\t\t\t\t\t.map(f -> f.getFieldInfo().getShortId())\n\t\t\t\t\t\t\t.collect(Collectors.joining());\n\t\t\t\t\tLOG.warn(\"Field reference not found: {}. Fields with same name: {}\", nodeRef, fieldSign);\n\t\t\t\t} else {\n\t\t\t\t\tfieldNode.rename(rename.getNewName());\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase METHOD:\n\t\t\t\tMethodNode mth = cls.searchMethodByShortId(nodeRef.getShortId());\n\t\t\t\tif (mth == null) {\n\t\t\t\t\tLOG.warn(\"Method reference not found: {}\", nodeRef);\n\t\t\t\t} else {\n\t\t\t\t\tIJavaCodeRef codeRef = rename.getCodeRef();\n\t\t\t\t\tif (codeRef == null) {\n\t\t\t\t\t\tmth.rename(rename.getNewName());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate static void applyPkgRenames(RootNode root, List<ICodeRename> renames) {\n\t\trenames.stream()\n\t\t\t\t.filter(r -> r.getNodeRef().getType() == IJavaNodeRef.RefType.PKG)\n\t\t\t\t.forEach(pkgRename -> {\n\t\t\t\t\tString pkgFullName = pkgRename.getNodeRef().getDeclaringClass();\n\t\t\t\t\tPackageNode pkgNode = root.resolvePackage(pkgFullName);\n\t\t\t\t\tif (pkgNode == null) {\n\t\t\t\t\t\tLOG.warn(\"Package for rename not found: {}\", pkgFullName);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpkgNode.rename(pkgRename.getNewName());\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/shrink/ArgsInfo.java",
    "content": "package jadx.core.dex.visitors.shrink;\n\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.mods.TernaryInsn;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.EmptyBitSet;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nfinal class ArgsInfo {\n\tprivate final InsnNode insn;\n\tprivate final List<ArgsInfo> argsList;\n\tprivate final List<RegisterArg> args;\n\tprivate final int pos;\n\tprivate int inlineBorder;\n\tprivate ArgsInfo inlinedInsn;\n\tprivate @Nullable List<ArgsInfo> wrappedInsns;\n\n\tpublic ArgsInfo(InsnNode insn, List<ArgsInfo> argsList, int pos) {\n\t\tthis.insn = insn;\n\t\tthis.argsList = argsList;\n\t\tthis.pos = pos;\n\t\tthis.inlineBorder = pos;\n\t\tthis.args = getArgs(insn);\n\t}\n\n\tpublic static List<RegisterArg> getArgs(InsnNode insn) {\n\t\tList<RegisterArg> args = new ArrayList<>();\n\t\taddArgs(insn, args);\n\t\treturn args;\n\t}\n\n\tprivate static void addArgs(InsnNode insn, List<RegisterArg> args) {\n\t\tif (insn.getType() == InsnType.TERNARY) {\n\t\t\targs.addAll(((TernaryInsn) insn).getCondition().getRegisterArgs());\n\t\t}\n\t\tfor (InsnArg arg : insn.getArguments()) {\n\t\t\tif (arg.isRegister()) {\n\t\t\t\targs.add((RegisterArg) arg);\n\t\t\t}\n\t\t}\n\t\tfor (InsnArg arg : insn.getArguments()) {\n\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\taddArgs(((InsnWrapArg) arg).getWrapInsn(), args);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic InsnNode getInsn() {\n\t\treturn insn;\n\t}\n\n\tList<RegisterArg> getArgs() {\n\t\treturn args;\n\t}\n\n\tpublic BitSet getArgsSet() {\n\t\tif (args.isEmpty() && Utils.isEmpty(wrappedInsns)) {\n\t\t\treturn EmptyBitSet.EMPTY;\n\t\t}\n\t\tBitSet set = new BitSet();\n\t\tfillArgsSet(set);\n\t\treturn set;\n\t}\n\n\tprivate void fillArgsSet(BitSet set) {\n\t\tfor (RegisterArg arg : args) {\n\t\t\tset.set(arg.getRegNum());\n\t\t}\n\t\tList<ArgsInfo> wrapList = wrappedInsns;\n\t\tif (wrapList != null) {\n\t\t\tfor (ArgsInfo wrappedInsn : wrapList) {\n\t\t\t\twrappedInsn.fillArgsSet(set);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic WrapInfo checkInline(int assignPos, RegisterArg arg) {\n\t\tif (assignPos >= inlineBorder || !canMove(assignPos, inlineBorder)) {\n\t\t\treturn null;\n\t\t}\n\t\tinlineBorder = assignPos;\n\t\treturn inline(assignPos, arg);\n\t}\n\n\tprivate boolean canMove(int from, int to) {\n\t\tArgsInfo startInfo = argsList.get(from);\n\t\tint start = from + 1;\n\t\tif (start == to) {\n\t\t\t// previous instruction or on edge of inline border\n\t\t\treturn true;\n\t\t}\n\t\tif (start > to) {\n\t\t\tthrow new JadxRuntimeException(\"Invalid inline insn positions: \" + start + \" - \" + to);\n\t\t}\n\t\tBitSet movedSet = startInfo.getArgsSet();\n\t\tif (movedSet == EmptyBitSet.EMPTY && startInfo.insn.isConstInsn()) {\n\t\t\treturn true;\n\t\t}\n\t\tboolean canReorder = startInfo.canReorder();\n\t\tfor (int i = start; i < to; i++) {\n\t\t\tArgsInfo argsInfo = argsList.get(i);\n\t\t\tif (argsInfo.getInlinedInsn() == this) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tInsnNode curInsn = argsInfo.insn;\n\t\t\tif (canReorder) {\n\t\t\t\tif (usedArgAssign(curInsn, movedSet)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (!curInsn.canReorder() || usedArgAssign(curInsn, movedSet)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate boolean canReorder() {\n\t\tif (!insn.canReorder()) {\n\t\t\treturn false;\n\t\t}\n\t\tList<ArgsInfo> wrapList = wrappedInsns;\n\t\tif (wrapList != null) {\n\t\t\tfor (ArgsInfo wrapInsn : wrapList) {\n\t\t\t\tif (!wrapInsn.canReorder()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tstatic boolean usedArgAssign(InsnNode insn, BitSet args) {\n\t\tif (args.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tRegisterArg result = insn.getResult();\n\t\tif (result == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn args.get(result.getRegNum());\n\t}\n\n\tWrapInfo inline(int assignInsnPos, RegisterArg arg) {\n\t\tArgsInfo argsInfo = argsList.get(assignInsnPos);\n\t\targsInfo.inlinedInsn = this;\n\t\tif (wrappedInsns == null) {\n\t\t\twrappedInsns = new ArrayList<>(args.size());\n\t\t}\n\t\twrappedInsns.add(argsInfo);\n\t\treturn new WrapInfo(argsInfo.insn, arg);\n\t}\n\n\tArgsInfo getInlinedInsn() {\n\t\tif (inlinedInsn != null) {\n\t\t\tArgsInfo parent = inlinedInsn.getInlinedInsn();\n\t\t\tif (parent != null) {\n\t\t\t\tinlinedInsn = parent;\n\t\t\t}\n\t\t}\n\t\treturn inlinedInsn;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ArgsInfo: |\" + inlineBorder\n\t\t\t\t+ \" ->\" + (inlinedInsn == null ? \"-\" : inlinedInsn.pos)\n\t\t\t\t+ ' ' + args + \" : \" + insn;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/shrink/CodeShrinkVisitor.java",
    "content": "package jadx.core.dex.visitors.shrink;\n\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeCustomNode;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.Named;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.dex.visitors.ModVisitor;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.InsnList;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.RegionUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n@JadxVisitor(\n\t\tname = \"CodeShrinkVisitor\",\n\t\tdesc = \"Inline variables to make code smaller\",\n\t\trunAfter = { ModVisitor.class }\n)\npublic class CodeShrinkVisitor extends AbstractVisitor {\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tshrinkMethod(mth);\n\t}\n\n\tpublic static void shrinkMethod(MethodNode mth) {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tmth.remove(AFlag.REQUEST_CODE_SHRINK);\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tshrinkBlock(mth, block);\n\t\t\tsimplifyMoveInsns(mth, block);\n\t\t}\n\t}\n\n\tprivate static void shrinkBlock(MethodNode mth, BlockNode block) {\n\t\tif (block.getInstructions().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tInsnList insnList = new InsnList(block.getInstructions());\n\t\tint insnCount = insnList.size();\n\t\tList<ArgsInfo> argsList = new ArrayList<>(insnCount);\n\t\tfor (int i = 0; i < insnCount; i++) {\n\t\t\targsList.add(new ArgsInfo(insnList.get(i), argsList, i));\n\t\t}\n\t\tList<WrapInfo> wrapList = new ArrayList<>();\n\t\tfor (ArgsInfo argsInfo : argsList) {\n\t\t\tList<RegisterArg> args = argsInfo.getArgs();\n\t\t\tfor (int i = args.size() - 1; i >= 0; i--) {\n\t\t\t\tRegisterArg arg = args.get(i);\n\t\t\t\tcheckInline(mth, block, insnList, wrapList, argsInfo, arg);\n\t\t\t}\n\t\t}\n\t\tif (!wrapList.isEmpty()) {\n\t\t\tfor (WrapInfo wrapInfo : wrapList) {\n\t\t\t\tinline(mth, wrapInfo.getArg(), wrapInfo.getInsn(), block);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void checkInline(MethodNode mth, BlockNode block, InsnList insnList,\n\t\t\tList<WrapInfo> wrapList, ArgsInfo argsInfo, RegisterArg arg) {\n\t\tif (arg.contains(AFlag.DONT_INLINE)\n\t\t\t\t|| arg.getParentInsn() == null\n\t\t\t\t|| arg.getParentInsn().contains(AFlag.DONT_GENERATE)) {\n\t\t\treturn;\n\t\t}\n\t\tSSAVar sVar = arg.getSVar();\n\t\tif (sVar == null || sVar.getAssign().contains(AFlag.DONT_INLINE)) {\n\t\t\treturn;\n\t\t}\n\t\tInsnNode assignInsn = sVar.getAssign().getParentInsn();\n\t\tif (assignInsn == null\n\t\t\t\t|| assignInsn.contains(AFlag.DONT_INLINE)\n\t\t\t\t|| assignInsn.contains(AFlag.WRAPPED)) {\n\t\t\treturn;\n\t\t}\n\t\tboolean assignInline = assignInsn.contains(AFlag.FORCE_ASSIGN_INLINE);\n\t\tif (!assignInline && sVar.isUsedInPhi()) {\n\t\t\treturn;\n\t\t}\n\t\t// allow inline only one use arg\n\t\tint useCount = 0;\n\t\tfor (RegisterArg useArg : sVar.getUseList()) {\n\t\t\tInsnNode parentInsn = useArg.getParentInsn();\n\t\t\tif (parentInsn != null && parentInsn.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!assignInline && useArg.contains(AFlag.DONT_INLINE_CONST)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tuseCount++;\n\t\t}\n\t\tif (!assignInline && useCount != 1) {\n\t\t\treturn;\n\t\t}\n\t\tif (!assignInline && sVar.getName() != null) {\n\t\t\tif (searchArgWithName(assignInsn, sVar.getName())) {\n\t\t\t\t// allow inline if name is reused in result\n\t\t\t} else if (varWithSameNameExists(mth, sVar)) {\n\t\t\t\t// allow inline if var name is duplicated\n\t\t\t} else {\n\t\t\t\t// reject inline of named variable\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (!checkLambdaInline(arg, assignInsn)) {\n\t\t\treturn;\n\t\t}\n\n\t\tint assignPos = insnList.getIndex(assignInsn);\n\t\tif (assignPos != -1) {\n\t\t\tWrapInfo wrapInfo = argsInfo.checkInline(assignPos, arg);\n\t\t\tif (wrapInfo != null) {\n\t\t\t\twrapList.add(wrapInfo);\n\t\t\t}\n\t\t} else {\n\t\t\t// another block\n\t\t\tBlockNode assignBlock = BlockUtils.getBlockByInsn(mth, assignInsn);\n\t\t\tif (assignBlock != null\n\t\t\t\t\t&& assignInsn != arg.getParentInsn()\n\t\t\t\t\t&& canMoveBetweenBlocks(mth, assignInsn, assignBlock, block, argsInfo.getInsn())) {\n\t\t\t\tif (assignInline) {\n\t\t\t\t\tassignInline(mth, arg, assignInsn, assignBlock);\n\t\t\t\t} else {\n\t\t\t\t\tinline(mth, arg, assignInsn, assignBlock);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Forbid inline lambda into invoke as an instance arg, i.e. this will not compile:\n\t * {@code () -> { ... }.apply(); }\n\t */\n\tprivate static boolean checkLambdaInline(RegisterArg arg, InsnNode assignInsn) {\n\t\tif (assignInsn.getType() == InsnType.INVOKE && assignInsn instanceof InvokeCustomNode) {\n\t\t\tfor (RegisterArg useArg : arg.getSVar().getUseList()) {\n\t\t\t\tInsnNode parentInsn = useArg.getParentInsn();\n\t\t\t\tif (parentInsn != null && parentInsn.getType() == InsnType.INVOKE) {\n\t\t\t\t\tInvokeNode invokeNode = (InvokeNode) parentInsn;\n\t\t\t\t\tInsnArg instArg = invokeNode.getInstanceArg();\n\t\t\t\t\tif (instArg != null && instArg == useArg) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean varWithSameNameExists(MethodNode mth, SSAVar inlineVar) {\n\t\tfor (SSAVar ssaVar : mth.getSVars()) {\n\t\t\tif (ssaVar == inlineVar || ssaVar.getCodeVar() == inlineVar.getCodeVar()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (Objects.equals(ssaVar.getName(), inlineVar.getName())) {\n\t\t\t\treturn ssaVar.getUseCount() > inlineVar.getUseCount();\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean searchArgWithName(InsnNode assignInsn, String varName) {\n\t\tInsnArg result = assignInsn.visitArgs(insnArg -> {\n\t\t\tif (insnArg instanceof Named) {\n\t\t\t\tString argName = ((Named) insnArg).getName();\n\t\t\t\tif (Objects.equals(argName, varName)) {\n\t\t\t\t\treturn insnArg;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t\treturn result != null;\n\t}\n\n\tprivate static boolean assignInline(MethodNode mth, RegisterArg arg, InsnNode assignInsn, BlockNode assignBlock) {\n\t\tRegisterArg useArg = arg.getSVar().getUseList().get(0);\n\t\tInsnNode useInsn = useArg.getParentInsn();\n\t\tif (useInsn == null || useInsn.contains(AFlag.DONT_GENERATE)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!InsnRemover.removeWithoutUnbind(mth, assignBlock, assignInsn)) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnArg replaceArg = InsnArg.wrapInsnIntoArg(assignInsn);\n\t\tuseInsn.replaceArg(useArg, replaceArg);\n\t\treturn true;\n\t}\n\n\tprivate static boolean inline(MethodNode mth, RegisterArg arg, InsnNode insn, BlockNode block) {\n\t\tif (insn.contains(AFlag.FORCE_ASSIGN_INLINE)) {\n\t\t\treturn assignInline(mth, arg, insn, block);\n\t\t}\n\t\t// just move instruction into arg, don't unbind/copy/duplicate\n\t\tInsnArg wrappedArg = arg.wrapInstruction(mth, insn, false);\n\t\tboolean replaced = wrappedArg != null;\n\t\tif (replaced) {\n\t\t\tInsnNode parentInsn = arg.getParentInsn();\n\t\t\tif (parentInsn != null) {\n\t\t\t\tparentInsn.inheritMetadata(insn);\n\t\t\t}\n\t\t\tInsnRemover.unbindResult(mth, insn);\n\t\t\tInsnRemover.removeWithoutUnbind(mth, block, insn);\n\t\t}\n\t\treturn replaced;\n\t}\n\n\tprivate static boolean canMoveBetweenBlocks(MethodNode mth, InsnNode assignInsn, BlockNode assignBlock,\n\t\t\tBlockNode useBlock, InsnNode useInsn) {\n\t\tif (!BlockUtils.isPathExists(assignBlock, useBlock)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tList<RegisterArg> argsList = ArgsInfo.getArgs(assignInsn);\n\t\tBitSet args = new BitSet();\n\t\tfor (RegisterArg arg : argsList) {\n\t\t\targs.set(arg.getRegNum());\n\t\t}\n\t\tboolean startCheck = false;\n\t\tfor (InsnNode insn : assignBlock.getInstructions()) {\n\t\t\tif (startCheck && (!insn.canReorder() || ArgsInfo.usedArgAssign(insn, args))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (insn == assignInsn) {\n\t\t\t\tstartCheck = true;\n\t\t\t}\n\t\t}\n\t\tSet<BlockNode> pathsBlocks = BlockUtils.getAllPathsBlocks(assignBlock, useBlock);\n\t\tpathsBlocks.remove(assignBlock);\n\t\tpathsBlocks.remove(useBlock);\n\t\tfor (BlockNode block : pathsBlocks) {\n\t\t\tif (block.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\tif (BlockUtils.checkLastInsnType(block, InsnType.MONITOR_EXIT)) {\n\t\t\t\t\tif (RegionUtils.isBlocksInSameRegion(mth, assignBlock, useBlock)) {\n\t\t\t\t\t\t// allow move inside same synchronized region\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// don't move from synchronized block\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// skip checks for not generated blocks\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tif (!insn.canReorder() || ArgsInfo.usedArgAssign(insn, args)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (InsnNode insn : useBlock.getInstructions()) {\n\t\t\tif (insn == useInsn) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (!insn.canReorder() || ArgsInfo.usedArgAssign(insn, args)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Can't process instruction move : \" + assignBlock);\n\t}\n\n\tprivate static void simplifyMoveInsns(MethodNode mth, BlockNode block) {\n\t\tList<InsnNode> insns = block.getInstructions();\n\t\tint size = insns.size();\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tInsnNode insn = insns.get(i);\n\t\t\tif (insn.getType() == InsnType.MOVE) {\n\t\t\t\t// replace 'move' with wrapped insn\n\t\t\t\tInsnArg arg = insn.getArg(0);\n\t\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\t\tInsnRemover.unbindResult(mth, wrapInsn);\n\t\t\t\t\twrapInsn.setResult(insn.getResult().duplicate());\n\t\t\t\t\twrapInsn.inheritMetadata(insn);\n\t\t\t\t\twrapInsn.setOffset(insn.getOffset());\n\t\t\t\t\twrapInsn.remove(AFlag.WRAPPED);\n\t\t\t\t\tblock.getInstructions().set(i, wrapInsn);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/shrink/WrapInfo.java",
    "content": "package jadx.core.dex.visitors.shrink;\n\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.InsnNode;\n\nfinal class WrapInfo {\n\tprivate final InsnNode insn;\n\tprivate final RegisterArg arg;\n\n\tWrapInfo(InsnNode assignInsn, RegisterArg arg) {\n\t\tthis.insn = assignInsn;\n\t\tthis.arg = arg;\n\t}\n\n\tInsnNode getInsn() {\n\t\treturn insn;\n\t}\n\n\tRegisterArg getArg() {\n\t\treturn arg;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"WrapInfo: \" + arg + \" -> \" + insn;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/ssa/LiveVarAnalysis.java",
    "content": "package jadx.core.dex.visitors.ssa;\n\nimport java.util.BitSet;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class LiveVarAnalysis {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(LiveVarAnalysis.class);\n\n\tprivate final MethodNode mth;\n\n\tprivate BitSet[] uses;\n\tprivate BitSet[] defs;\n\tprivate BitSet[] liveIn;\n\tprivate BitSet[] assignBlocks;\n\n\tpublic LiveVarAnalysis(MethodNode mth) {\n\t\tthis.mth = mth;\n\t}\n\n\tpublic void runAnalysis() {\n\t\tint bbCount = mth.getBasicBlocks().size();\n\t\tint regsCount = mth.getRegsCount();\n\t\tthis.uses = initBitSetArray(bbCount, regsCount);\n\t\tthis.defs = initBitSetArray(bbCount, regsCount);\n\t\tthis.assignBlocks = initBitSetArray(regsCount, bbCount);\n\t\tfillBasicBlockInfo();\n\t\tprocessLiveInfo();\n\t}\n\n\tpublic BitSet getAssignBlocks(int regNum) {\n\t\treturn assignBlocks[regNum];\n\t}\n\n\tpublic boolean isLive(int blockId, int regNum) {\n\t\tif (blockId >= liveIn.length) {\n\t\t\tLOG.warn(\"LiveVarAnalysis: out of bounds block: {}, max: {}\", blockId, liveIn.length);\n\t\t\treturn false;\n\t\t}\n\t\treturn liveIn[blockId].get(regNum);\n\t}\n\n\tpublic boolean isLive(BlockNode block, int regNum) {\n\t\treturn isLive(block.getId(), regNum);\n\t}\n\n\tprivate void fillBasicBlockInfo() {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tint blockId = block.getId();\n\t\t\tBitSet gen = uses[blockId];\n\t\t\tBitSet kill = defs[blockId];\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tfor (InsnArg arg : insn.getArguments()) {\n\t\t\t\t\tif (arg.isRegister()) {\n\t\t\t\t\t\tint regNum = ((RegisterArg) arg).getRegNum();\n\t\t\t\t\t\tif (!kill.get(regNum)) {\n\t\t\t\t\t\t\tgen.set(regNum);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tRegisterArg result = insn.getResult();\n\t\t\t\tif (result != null) {\n\t\t\t\t\tint regNum = result.getRegNum();\n\t\t\t\t\tkill.set(regNum);\n\t\t\t\t\tassignBlocks[regNum].set(blockId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void processLiveInfo() {\n\t\tint bbCount = mth.getBasicBlocks().size();\n\t\tint regsCount = mth.getRegsCount();\n\t\tBitSet[] liveInBlocks = initBitSetArray(bbCount, regsCount);\n\t\tList<BlockNode> blocks = mth.getBasicBlocks();\n\t\tint blocksCount = blocks.size();\n\t\tint iterationsLimit = blocksCount * 10;\n\t\tboolean changed;\n\t\tint k = 0;\n\t\tdo {\n\t\t\tchanged = false;\n\t\t\tfor (BlockNode block : blocks) {\n\t\t\t\tint blockId = block.getId();\n\t\t\t\tBitSet prevIn = liveInBlocks[blockId];\n\t\t\t\tBitSet newIn = new BitSet(regsCount);\n\t\t\t\tfor (BlockNode successor : block.getSuccessors()) {\n\t\t\t\t\tnewIn.or(liveInBlocks[successor.getId()]);\n\t\t\t\t}\n\t\t\t\tnewIn.andNot(defs[blockId]);\n\t\t\t\tnewIn.or(uses[blockId]);\n\t\t\t\tif (!prevIn.equals(newIn)) {\n\t\t\t\t\tchanged = true;\n\t\t\t\t\tliveInBlocks[blockId] = newIn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (k++ > iterationsLimit) {\n\t\t\t\tthrow new JadxRuntimeException(\"Live variable analysis reach iterations limit, blocks count: \" + blocksCount);\n\t\t\t}\n\t\t} while (changed);\n\n\t\tthis.liveIn = liveInBlocks;\n\t}\n\n\tprivate static BitSet[] initBitSetArray(int length, int bitsCount) {\n\t\tBitSet[] array = new BitSet[length];\n\t\tfor (int i = 0; i < length; i++) {\n\t\t\tarray[i] = new BitSet(bitsCount);\n\t\t}\n\t\treturn array;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/ssa/RenameState.java",
    "content": "package jadx.core.dex.visitors.ssa;\n\nimport java.util.Arrays;\n\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.MethodNode;\n\nfinal class RenameState {\n\tprivate final MethodNode mth;\n\tprivate final BlockNode block;\n\tprivate final SSAVar[] vars;\n\tprivate final int[] versions;\n\n\tpublic static RenameState init(MethodNode mth) {\n\t\tint regsCount = mth.getRegsCount();\n\t\tRenameState state = new RenameState(\n\t\t\t\tmth,\n\t\t\t\tmth.getEnterBlock(),\n\t\t\t\tnew SSAVar[regsCount],\n\t\t\t\tnew int[regsCount]);\n\t\tRegisterArg thisArg = mth.getThisArg();\n\t\tif (thisArg != null) {\n\t\t\tstate.startVar(thisArg);\n\t\t}\n\t\tfor (RegisterArg arg : mth.getArgRegs()) {\n\t\t\tstate.startVar(arg);\n\t\t}\n\t\treturn state;\n\t}\n\n\tpublic static RenameState copyFrom(RenameState state, BlockNode block) {\n\t\treturn new RenameState(\n\t\t\t\tstate.mth,\n\t\t\t\tblock,\n\t\t\t\tArrays.copyOf(state.vars, state.vars.length),\n\t\t\t\tstate.versions);\n\t}\n\n\tprivate RenameState(MethodNode mth, BlockNode block, SSAVar[] vars, int[] versions) {\n\t\tthis.mth = mth;\n\t\tthis.block = block;\n\t\tthis.vars = vars;\n\t\tthis.versions = versions;\n\t}\n\n\tpublic BlockNode getBlock() {\n\t\treturn block;\n\t}\n\n\tpublic SSAVar getVar(int regNum) {\n\t\treturn vars[regNum];\n\t}\n\n\tpublic SSAVar startVar(RegisterArg regArg) {\n\t\tint regNum = regArg.getRegNum();\n\t\tint version = versions[regNum]++;\n\t\tSSAVar ssaVar = mth.makeNewSVar(regNum, version, regArg);\n\t\tvars[regNum] = ssaVar;\n\t\treturn ssaVar;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java",
    "content": "package jadx.core.dex.visitors.ssa;\n\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.Deque;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.PhiListAttr;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.PhiInsn;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.trycatch.CatchAttr;\nimport jadx.core.dex.trycatch.ExcHandlerAttr;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.dex.visitors.blocks.BlockProcessor;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.InsnList;\nimport jadx.core.utils.InsnRemover;\nimport jadx.core.utils.exceptions.JadxException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n@JadxVisitor(\n\t\tname = \"SSATransform\",\n\t\tdesc = \"Calculate Single Side Assign (SSA) variables\",\n\t\trunAfter = BlockProcessor.class\n)\npublic class SSATransform extends AbstractVisitor {\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tprocess(mth);\n\t}\n\n\tprivate static void process(MethodNode mth) {\n\t\tif (!mth.getSVars().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tLiveVarAnalysis la = new LiveVarAnalysis(mth);\n\t\tla.runAnalysis();\n\t\tint regsCount = mth.getRegsCount();\n\t\tfor (int i = 0; i < regsCount; i++) {\n\t\t\tplacePhi(mth, i, la);\n\t\t}\n\t\trenameVariables(mth);\n\t\tfixLastAssignInTry(mth);\n\t\tremoveBlockerInsns(mth);\n\t\ttryToFixUselessPhi(mth);\n\t\tmarkThisArgs(mth.getThisArg());\n\t\thidePhiInsns(mth);\n\t\tremoveUnusedInvokeResults(mth);\n\t}\n\n\tprivate static void placePhi(MethodNode mth, int regNum, LiveVarAnalysis la) {\n\t\tList<BlockNode> blocks = mth.getBasicBlocks();\n\t\tint blocksCount = blocks.size();\n\t\tBitSet hasPhi = new BitSet(blocksCount);\n\t\tBitSet processed = new BitSet(blocksCount);\n\t\tDeque<BlockNode> workList = new ArrayDeque<>();\n\n\t\tBitSet assignBlocks = la.getAssignBlocks(regNum);\n\t\tfor (int id = assignBlocks.nextSetBit(0); id >= 0; id = assignBlocks.nextSetBit(id + 1)) {\n\t\t\tprocessed.set(id);\n\t\t\tworkList.add(blocks.get(id));\n\t\t}\n\t\twhile (!workList.isEmpty()) {\n\t\t\tBlockNode block = workList.pop();\n\t\t\tBitSet domFrontier = block.getDomFrontier();\n\t\t\tfor (int id = domFrontier.nextSetBit(0); id >= 0; id = domFrontier.nextSetBit(id + 1)) {\n\t\t\t\tif (!hasPhi.get(id) && la.isLive(id, regNum)) {\n\t\t\t\t\tBlockNode df = blocks.get(id);\n\t\t\t\t\tPhiInsn phiInsn = addPhi(mth, df, regNum);\n\t\t\t\t\tdf.getInstructions().add(0, phiInsn);\n\t\t\t\t\thasPhi.set(id);\n\t\t\t\t\tif (!processed.get(id)) {\n\t\t\t\t\t\tprocessed.set(id);\n\t\t\t\t\t\tworkList.add(df);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static PhiInsn addPhi(MethodNode mth, BlockNode block, int regNum) {\n\t\tPhiListAttr phiList = block.get(AType.PHI_LIST);\n\t\tif (phiList == null) {\n\t\t\tphiList = new PhiListAttr();\n\t\t\tblock.addAttr(phiList);\n\t\t}\n\t\tint size = block.getPredecessors().size();\n\t\tif (mth.getEnterBlock() == block) {\n\t\t\tRegisterArg thisArg = mth.getThisArg();\n\t\t\tif (thisArg != null && thisArg.getRegNum() == regNum) {\n\t\t\t\tsize++;\n\t\t\t} else {\n\t\t\t\tfor (RegisterArg arg : mth.getArgRegs()) {\n\t\t\t\t\tif (arg.getRegNum() == regNum) {\n\t\t\t\t\t\tsize++;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tPhiInsn phiInsn = new PhiInsn(regNum, size);\n\t\tphiList.getList().add(phiInsn);\n\t\tphiInsn.setOffset(block.getStartOffset());\n\t\treturn phiInsn;\n\t}\n\n\tprivate static void renameVariables(MethodNode mth) {\n\t\tRenameState initState = RenameState.init(mth);\n\t\tinitPhiInEnterBlock(initState);\n\n\t\tDeque<RenameState> stack = new ArrayDeque<>();\n\t\tstack.push(initState);\n\t\twhile (!stack.isEmpty()) {\n\t\t\tRenameState state = stack.pop();\n\t\t\trenameVarsInBlock(mth, state);\n\t\t\tfor (BlockNode dominated : state.getBlock().getDominatesOn()) {\n\t\t\t\tstack.push(RenameState.copyFrom(state, dominated));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void initPhiInEnterBlock(RenameState initState) {\n\t\tPhiListAttr phiList = initState.getBlock().get(AType.PHI_LIST);\n\t\tif (phiList != null) {\n\t\t\tfor (PhiInsn phiInsn : phiList.getList()) {\n\t\t\t\tbindPhiArg(initState, phiInsn);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void renameVarsInBlock(MethodNode mth, RenameState state) {\n\t\tBlockNode block = state.getBlock();\n\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\tif (insn.getType() != InsnType.PHI) {\n\t\t\t\tfor (InsnArg arg : insn.getArguments()) {\n\t\t\t\t\tif (!arg.isRegister()) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tRegisterArg reg = (RegisterArg) arg;\n\t\t\t\t\tint regNum = reg.getRegNum();\n\t\t\t\t\tSSAVar var = state.getVar(regNum);\n\t\t\t\t\tif (var == null) {\n\t\t\t\t\t\t// TODO: in most cases issue in incorrectly attached exception handlers\n\t\t\t\t\t\tmth.addWarnComment(\"Not initialized variable reg: \" + regNum + \", insn: \" + insn + \", block:\" + block);\n\t\t\t\t\t\tvar = state.startVar(reg);\n\t\t\t\t\t}\n\t\t\t\t\tvar.use(reg);\n\t\t\t\t}\n\t\t\t}\n\t\t\tRegisterArg result = insn.getResult();\n\t\t\tif (result != null) {\n\t\t\t\tstate.startVar(result);\n\t\t\t}\n\t\t}\n\t\tfor (BlockNode s : block.getSuccessors()) {\n\t\t\tPhiListAttr phiList = s.get(AType.PHI_LIST);\n\t\t\tif (phiList == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (PhiInsn phiInsn : phiList.getList()) {\n\t\t\t\tbindPhiArg(state, phiInsn);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void bindPhiArg(RenameState state, PhiInsn phiInsn) {\n\t\tint regNum = phiInsn.getResult().getRegNum();\n\t\tSSAVar var = state.getVar(regNum);\n\t\tif (var == null) {\n\t\t\treturn;\n\t\t}\n\t\tRegisterArg arg = phiInsn.bindArg(state.getBlock());\n\t\tvar.use(arg);\n\t\tvar.addUsedInPhi(phiInsn);\n\t}\n\n\t/**\n\t * Fix last try/catch assign instruction\n\t */\n\tprivate static void fixLastAssignInTry(MethodNode mth) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tPhiListAttr phiList = block.get(AType.PHI_LIST);\n\t\t\tif (phiList != null) {\n\t\t\t\tExcHandlerAttr handlerAttr = block.get(AType.EXC_HANDLER);\n\t\t\t\tif (handlerAttr != null) {\n\t\t\t\t\tfor (PhiInsn phi : phiList.getList()) {\n\t\t\t\t\t\tfixPhiInTryCatch(mth, phi, handlerAttr);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void fixPhiInTryCatch(MethodNode mth, PhiInsn phi, ExcHandlerAttr handlerAttr) {\n\t\tint argsCount = phi.getArgsCount();\n\t\tint k = 0;\n\t\twhile (k < argsCount) {\n\t\t\tRegisterArg arg = phi.getArg(k);\n\t\t\tif (shouldSkipInsnResult(mth, arg.getAssignInsn(), handlerAttr)) {\n\t\t\t\tphi.removeArg(arg);\n\t\t\t\targsCount--;\n\t\t\t} else {\n\t\t\t\tk++;\n\t\t\t}\n\t\t}\n\t\tif (phi.getArgsCount() == 0) {\n\t\t\tthrow new JadxRuntimeException(\"PHI empty after try-catch fix!\");\n\t\t}\n\t}\n\n\tprivate static boolean shouldSkipInsnResult(MethodNode mth, InsnNode insn, ExcHandlerAttr handlerAttr) {\n\t\tif (insn != null\n\t\t\t\t&& insn.getResult() != null\n\t\t\t\t&& insn.contains(AFlag.TRY_LEAVE)) {\n\t\t\tCatchAttr catchAttr = BlockUtils.getCatchAttrForInsn(mth, insn);\n\t\t\treturn catchAttr != null && catchAttr.getHandlers().contains(handlerAttr.getHandler());\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean removeBlockerInsns(MethodNode mth) {\n\t\tboolean removed = false;\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tPhiListAttr phiList = block.get(AType.PHI_LIST);\n\t\t\tif (phiList == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// check if args must be removed\n\t\t\tfor (PhiInsn phi : phiList.getList()) {\n\t\t\t\tfor (int i = 0; i < phi.getArgsCount(); i++) {\n\t\t\t\t\tRegisterArg arg = phi.getArg(i);\n\t\t\t\t\tInsnNode parentInsn = arg.getAssignInsn();\n\t\t\t\t\tif (parentInsn != null && parentInsn.contains(AFlag.REMOVE)) {\n\t\t\t\t\t\tphi.removeArg(arg);\n\t\t\t\t\t\tInsnRemover.remove(mth, block, parentInsn);\n\t\t\t\t\t\tremoved = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn removed;\n\t}\n\n\tprivate static void tryToFixUselessPhi(MethodNode mth) {\n\t\tint k = 0;\n\t\tint maxTries = mth.getSVars().size() * 2;\n\t\twhile (fixUselessPhi(mth)) {\n\t\t\tif (k++ > maxTries) {\n\t\t\t\tthrow new JadxRuntimeException(\"Phi nodes fix limit reached!\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean fixUselessPhi(MethodNode mth) {\n\t\tboolean changed = false;\n\t\tList<PhiInsn> insnToRemove = new ArrayList<>();\n\t\tfor (SSAVar var : mth.getSVars()) {\n\t\t\t// phi result not used\n\t\t\tif (var.getUseCount() == 0) {\n\t\t\t\tInsnNode assignInsn = var.getAssign().getParentInsn();\n\t\t\t\tif (assignInsn != null && assignInsn.getType() == InsnType.PHI) {\n\t\t\t\t\tinsnToRemove.add((PhiInsn) assignInsn);\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tPhiListAttr phiList = block.get(AType.PHI_LIST);\n\t\t\tif (phiList == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tIterator<PhiInsn> it = phiList.getList().iterator();\n\t\t\twhile (it.hasNext()) {\n\t\t\t\tPhiInsn phi = it.next();\n\t\t\t\tif (fixPhiWithSameArgs(mth, block, phi)) {\n\t\t\t\t\tit.remove();\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tremovePhiList(mth, insnToRemove);\n\t\treturn changed;\n\t}\n\n\tprivate static boolean fixPhiWithSameArgs(MethodNode mth, BlockNode block, PhiInsn phi) {\n\t\tif (phi.getArgsCount() == 0) {\n\t\t\tfor (RegisterArg useArg : phi.getResult().getSVar().getUseList()) {\n\t\t\t\tInsnNode useInsn = useArg.getParentInsn();\n\t\t\t\tif (useInsn != null && useInsn.getType() == InsnType.PHI) {\n\t\t\t\t\tphi.removeArg(useArg);\n\t\t\t\t}\n\t\t\t}\n\t\t\tInsnRemover.remove(mth, block, phi);\n\t\t\treturn true;\n\t\t}\n\t\tboolean allSame = phi.getArgsCount() == 1 || isSameArgs(phi);\n\t\tif (allSame) {\n\t\t\treturn replacePhiWithMove(mth, block, phi, phi.getArg(0));\n\t\t}\n\t\tSSAVar sameVar = isSameMove(phi);\n\t\tif (sameVar != null) {\n\t\t\tRegisterArg sameArg = sameVar.getAssign().duplicate();\n\t\t\tif (inlinePhiInsn(mth, block, phi, sameArg)) {\n\t\t\t\tfor (InsnArg arg : phi.getArguments()) {\n\t\t\t\t\tInsnNode moveInsn = ((RegisterArg) arg).getAssignInsn();\n\t\t\t\t\tif (moveInsn != null) {\n\t\t\t\t\t\tmoveInsn.add(AFlag.REMOVE);\n\t\t\t\t\t\tInsnRemover.remove(mth, moveInsn);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean isSameArgs(PhiInsn phi) {\n\t\tboolean allSame = true;\n\t\tSSAVar var = null;\n\t\tfor (int i = 0; i < phi.getArgsCount(); i++) {\n\t\t\tRegisterArg arg = phi.getArg(i);\n\t\t\tif (var == null) {\n\t\t\t\tvar = arg.getSVar();\n\t\t\t} else if (var != arg.getSVar()) {\n\t\t\t\tallSame = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn allSame;\n\t}\n\n\tprivate static SSAVar isSameMove(PhiInsn phi) {\n\t\tSSAVar var = null;\n\t\tint argsCount = phi.getArgsCount();\n\t\tfor (int i = 0; i < argsCount; i++) {\n\t\t\tRegisterArg arg = phi.getArg(i);\n\t\t\tif (arg.getSVar().getUseCount() != 1) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tInsnNode assignInsn = arg.getAssignInsn();\n\t\t\tif (assignInsn == null || assignInsn.getType() != InsnType.MOVE) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tInsnArg moveArg = assignInsn.getArg(0);\n\t\t\tif (!moveArg.isRegister()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tSSAVar moveVar = ((RegisterArg) moveArg).getSVar();\n\t\t\tif (var == null) {\n\t\t\t\tvar = moveVar;\n\t\t\t} else if (var != moveVar) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\treturn var;\n\t}\n\n\tprivate static boolean removePhiList(MethodNode mth, List<PhiInsn> insnToRemove) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tPhiListAttr phiList = block.get(AType.PHI_LIST);\n\t\t\tif (phiList == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tList<PhiInsn> list = phiList.getList();\n\t\t\tfor (PhiInsn phiInsn : insnToRemove) {\n\t\t\t\tif (list.remove(phiInsn)) {\n\t\t\t\t\tfor (InsnArg arg : phiInsn.getArguments()) {\n\t\t\t\t\t\tif (arg == null) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tSSAVar sVar = ((RegisterArg) arg).getSVar();\n\t\t\t\t\t\tif (sVar != null) {\n\t\t\t\t\t\t\tsVar.removeUsedInPhi(phiInsn);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tInsnRemover.remove(mth, block, phiInsn);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (list.isEmpty()) {\n\t\t\t\tblock.remove(AType.PHI_LIST);\n\t\t\t}\n\t\t}\n\t\tinsnToRemove.clear();\n\t\treturn true;\n\t}\n\n\tprivate static boolean replacePhiWithMove(MethodNode mth, BlockNode block, PhiInsn phi, RegisterArg arg) {\n\t\tList<InsnNode> insns = block.getInstructions();\n\t\tint phiIndex = InsnList.getIndex(insns, phi);\n\t\tif (phiIndex == -1) {\n\t\t\treturn false;\n\t\t}\n\t\tSSAVar assign = phi.getResult().getSVar();\n\t\tSSAVar argVar = arg.getSVar();\n\t\tif (argVar != null) {\n\t\t\targVar.removeUse(arg);\n\t\t\targVar.removeUsedInPhi(phi);\n\t\t}\n\t\t// try inline\n\t\tif (inlinePhiInsn(mth, block, phi, phi.getArg(0))) {\n\t\t\tinsns.remove(phiIndex);\n\t\t} else {\n\t\t\tassign.removeUsedInPhi(phi);\n\n\t\t\tInsnNode m = new InsnNode(InsnType.MOVE, 1);\n\t\t\tm.add(AFlag.SYNTHETIC);\n\t\t\tm.setResult(phi.getResult());\n\t\t\tm.addArg(arg);\n\t\t\targ.getSVar().use(arg);\n\t\t\tinsns.set(phiIndex, m);\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean inlinePhiInsn(MethodNode mth, BlockNode block, PhiInsn phi, RegisterArg inlineArg) {\n\t\tSSAVar resVar = phi.getResult().getSVar();\n\t\tif (resVar == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (inlineArg.getSVar() == null) {\n\t\t\treturn false;\n\t\t}\n\t\tList<RegisterArg> useList = resVar.getUseList();\n\t\tfor (RegisterArg useArg : new ArrayList<>(useList)) {\n\t\t\tInsnNode useInsn = useArg.getParentInsn();\n\t\t\tif (useInsn == null || useInsn == phi) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (useArg.getRegNum() == inlineArg.getRegNum()) {\n\t\t\t\t// replace SSAVar in 'useArg' to SSAVar from 'arg'\n\t\t\t\t// no need to replace whole RegisterArg\n\t\t\t\tuseArg.getSVar().removeUse(useArg);\n\t\t\t\tinlineArg.getSVar().use(useArg);\n\t\t\t} else {\n\t\t\t\tif (!useInsn.replaceArg(useArg, inlineArg)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (block.contains(AType.EXC_HANDLER)) {\n\t\t\t// don't inline into exception handler\n\t\t\tInsnNode assignInsn = inlineArg.getAssignInsn();\n\t\t\tif (assignInsn != null && !assignInsn.isConstInsn()) {\n\t\t\t\tassignInsn.add(AFlag.DONT_INLINE);\n\t\t\t}\n\t\t}\n\t\tInsnRemover.unbindInsn(mth, phi);\n\t\treturn true;\n\t}\n\n\tprivate static void markThisArgs(RegisterArg thisArg) {\n\t\tif (thisArg != null) {\n\t\t\tmarkOneArgAsThis(thisArg);\n\t\t\tthisArg.getSVar().getUseList().forEach(SSATransform::markOneArgAsThis);\n\t\t}\n\t}\n\n\tprivate static void markOneArgAsThis(RegisterArg arg) {\n\t\tif (arg == null) {\n\t\t\treturn;\n\t\t}\n\t\targ.add(AFlag.THIS);\n\t\targ.add(AFlag.IMMUTABLE_TYPE);\n\t\t// mark all moved 'this'\n\t\tInsnNode parentInsn = arg.getParentInsn();\n\t\tif (parentInsn != null\n\t\t\t\t&& parentInsn.getType() == InsnType.MOVE\n\t\t\t\t&& parentInsn.getArg(0) == arg) {\n\t\t\tRegisterArg resArg = parentInsn.getResult();\n\t\t\tif (resArg.getRegNum() != arg.getRegNum()\n\t\t\t\t\t&& !resArg.getSVar().isUsedInPhi()) {\n\t\t\t\tmarkThisArgs(resArg);\n\t\t\t\tparentInsn.add(AFlag.DONT_GENERATE);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void hidePhiInsns(MethodNode mth) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tblock.getInstructions().removeIf(insn -> insn.getType() == InsnType.PHI);\n\t\t}\n\t}\n\n\tprivate static void removeUnusedInvokeResults(MethodNode mth) {\n\t\tIterator<SSAVar> it = mth.getSVars().iterator();\n\t\twhile (it.hasNext()) {\n\t\t\tSSAVar ssaVar = it.next();\n\t\t\tif (ssaVar.getUseCount() == 0) {\n\t\t\t\tInsnNode parentInsn = ssaVar.getAssign().getParentInsn();\n\t\t\t\tif (parentInsn != null && parentInsn.getType() == InsnType.INVOKE) {\n\t\t\t\t\tparentInsn.setResult(null);\n\t\t\t\t\tit.remove();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/AbstractTypeConstraint.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.utils.Utils;\n\npublic abstract class AbstractTypeConstraint implements ITypeConstraint {\n\n\tprotected final InsnNode insn;\n\tprotected final List<SSAVar> relatedVars;\n\n\tpublic AbstractTypeConstraint(InsnNode insn, InsnArg arg) {\n\t\tthis.insn = insn;\n\t\tthis.relatedVars = collectRelatedVars(insn, arg);\n\t}\n\n\tprivate List<SSAVar> collectRelatedVars(InsnNode insn, InsnArg arg) {\n\t\tList<SSAVar> list = new ArrayList<>(insn.getArgsCount());\n\t\tif (insn.getResult() == arg) {\n\t\t\tfor (InsnArg insnArg : insn.getArguments()) {\n\t\t\t\tif (insnArg.isRegister()) {\n\t\t\t\t\tlist.add(((RegisterArg) insnArg).getSVar());\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tlist.add(insn.getResult().getSVar());\n\t\t\tfor (InsnArg insnArg : insn.getArguments()) {\n\t\t\t\tif (insnArg != arg && insnArg.isRegister()) {\n\t\t\t\t\tlist.add(((RegisterArg) insnArg).getSVar());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic List<SSAVar> getRelatedVars() {\n\t\treturn relatedVars;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"(\" + insn.getType() + ':' + Utils.listToString(relatedVars, SSAVar::toShortString) + ')';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/BoundEnum.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\npublic enum BoundEnum {\n\tASSIGN,\n\tUSE\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/FinishTypeInference.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.JadxVisitor;\n\n@JadxVisitor(\n\t\tname = \"Finish Type Inference\",\n\t\tdesc = \"Check used types\",\n\t\trunAfter = {\n\t\t\t\tTypeInferenceVisitor.class\n\t\t}\n)\npublic final class FinishTypeInference extends AbstractVisitor {\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tif (mth.isNoCode() || mth.getSVars().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tmth.getSVars().forEach(var -> {\n\t\t\tArgType type = var.getTypeInfo().getType();\n\t\t\tif (!type.isTypeKnown()) {\n\t\t\t\tmth.addWarnComment(\"Type inference failed for: \" + var.getDetailedVarInfo(mth));\n\t\t\t}\n\t\t\tArgType codeVarType = var.getCodeVar().getType();\n\t\t\tif (codeVarType == null) {\n\t\t\t\tvar.getCodeVar().setType(ArgType.UNKNOWN);\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"FinishTypeInference\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/FixTypesVisitor.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.Consts;\nimport jadx.core.clsp.ClspGraph;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.PhiListAttr;\nimport jadx.core.dex.instructions.ArithNode;\nimport jadx.core.dex.instructions.ArithOp;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.PhiInsn;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.PrimitiveType;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.instructions.mods.TernaryInsn;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.InitCodeVariables;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.dex.visitors.ModVisitor;\nimport jadx.core.dex.visitors.blocks.BlockSplitter;\nimport jadx.core.utils.BlockUtils;\nimport jadx.core.utils.InsnList;\nimport jadx.core.utils.InsnUtils;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxOverflowException;\n\n@JadxVisitor(\n\t\tname = \"Fix Types Visitor\",\n\t\tdesc = \"Try various methods to fix unresolved types\",\n\t\trunAfter = {\n\t\t\t\tTypeInferenceVisitor.class\n\t\t},\n\t\trunBefore = {\n\t\t\t\tFinishTypeInference.class\n\t\t}\n)\npublic final class FixTypesVisitor extends AbstractVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(FixTypesVisitor.class);\n\n\tprivate final TypeInferenceVisitor typeInference = new TypeInferenceVisitor();\n\n\tprivate TypeUpdate typeUpdate;\n\tprivate List<Function<MethodNode, Boolean>> resolvers;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tthis.typeUpdate = root.getTypeUpdate();\n\t\tthis.typeInference.init(root);\n\t\tthis.resolvers = Arrays.asList(\n\t\t\t\tthis::applyFieldType,\n\t\t\t\tthis::tryRestoreTypeVarCasts,\n\t\t\t\tthis::tryInsertCasts,\n\t\t\t\tthis::tryDeduceTypes,\n\t\t\t\tthis::trySplitConstInsns,\n\t\t\t\tthis::tryToFixIncompatiblePrimitives,\n\t\t\t\tthis::tryToForceImmutableTypes,\n\t\t\t\tthis::tryInsertAdditionalMove,\n\t\t\t\tthis::runMultiVariableSearch,\n\t\t\t\tthis::tryRemoveGenerics);\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tif (mth.isNoCode() || checkTypes(mth)) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tfor (Function<MethodNode, Boolean> resolver : resolvers) {\n\t\t\t\tif (resolver.apply(mth) && checkTypes(mth)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tmth.addError(\"Types fix failed\", e);\n\t\t}\n\t}\n\n\t/**\n\t * Check if all types resolved\n\t */\n\tprivate static boolean checkTypes(MethodNode mth) {\n\t\tfor (SSAVar var : mth.getSVars()) {\n\t\t\tArgType type = var.getTypeInfo().getType();\n\t\t\tif (!type.isTypeKnown()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate boolean runMultiVariableSearch(MethodNode mth) {\n\t\ttry {\n\t\t\tTypeSearch typeSearch = new TypeSearch(mth);\n\t\t\tif (!typeSearch.run()) {\n\t\t\t\tmth.addWarnComment(\"Multi-variable type inference failed\");\n\t\t\t}\n\t\t\tfor (SSAVar var : mth.getSVars()) {\n\t\t\t\tif (!var.getTypeInfo().getType().isTypeKnown()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Multi-variable type inference failed. Error: \" + Utils.getStackTrace(e));\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate boolean setBestType(MethodNode mth, SSAVar ssaVar) {\n\t\ttry {\n\t\t\treturn calculateFromBounds(mth, ssaVar);\n\t\t} catch (JadxOverflowException e) {\n\t\t\tthrow e;\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Failed to calculate best type for var: \" + ssaVar, e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate boolean calculateFromBounds(MethodNode mth, SSAVar ssaVar) {\n\t\tTypeInfo typeInfo = ssaVar.getTypeInfo();\n\t\tSet<ITypeBound> bounds = typeInfo.getBounds();\n\t\tOptional<ArgType> bestTypeOpt = selectBestTypeFromBounds(bounds);\n\t\tif (bestTypeOpt.isEmpty()) {\n\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\tLOG.warn(\"Failed to select best type from bounds, count={} : \", bounds.size());\n\t\t\t\tfor (ITypeBound bound : bounds) {\n\t\t\t\t\tLOG.warn(\"  {}\", bound);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tArgType candidateType = bestTypeOpt.get();\n\t\tTypeUpdateResult result = typeUpdate.apply(mth, ssaVar, candidateType);\n\t\tif (result == TypeUpdateResult.REJECT) {\n\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\tif (ssaVar.getTypeInfo().getType().equals(candidateType)) {\n\t\t\t\t\tLOG.info(\"Same type rejected: {} -> {}, bounds: {}\", ssaVar, candidateType, bounds);\n\t\t\t\t} else if (candidateType.isTypeKnown()) {\n\t\t\t\t\tLOG.debug(\"Type rejected: {} -> {}, bounds: {}\", ssaVar, candidateType, bounds);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\treturn result == TypeUpdateResult.CHANGED;\n\t}\n\n\tprivate Optional<ArgType> selectBestTypeFromBounds(Set<ITypeBound> bounds) {\n\t\treturn bounds.stream()\n\t\t\t\t.map(ITypeBound::getType)\n\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t.max(typeUpdate.getTypeCompare().getComparator());\n\t}\n\n\tprivate boolean tryPossibleTypes(MethodNode mth, SSAVar var, ArgType type) {\n\t\tList<ArgType> types = makePossibleTypesList(type, var);\n\t\tif (types.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (ArgType candidateType : types) {\n\t\t\tTypeUpdateResult result = typeUpdate.apply(mth, var, candidateType);\n\t\t\tif (result == TypeUpdateResult.CHANGED) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate List<ArgType> makePossibleTypesList(ArgType type, @Nullable SSAVar var) {\n\t\tif (type.isArray()) {\n\t\t\tList<ArgType> list = new ArrayList<>();\n\t\t\tfor (ArgType arrElemType : makePossibleTypesList(type.getArrayElement(), null)) {\n\t\t\t\tlist.add(ArgType.array(arrElemType));\n\t\t\t}\n\t\t\treturn list;\n\t\t}\n\t\tif (var != null) {\n\t\t\tfor (ITypeBound b : var.getTypeInfo().getBounds()) {\n\t\t\t\tArgType boundType = b.getType();\n\t\t\t\tif (boundType.isObject() || boundType.isArray()) {\n\t\t\t\t\t// don't add primitive types\n\t\t\t\t\treturn Collections.emptyList();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tList<ArgType> list = new ArrayList<>();\n\t\tfor (PrimitiveType possibleType : type.getPossibleTypes()) {\n\t\t\tif (possibleType == PrimitiveType.VOID) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlist.add(ArgType.convertFromPrimitiveType(possibleType));\n\t\t}\n\t\treturn list;\n\t}\n\n\tprivate boolean tryDeduceTypes(MethodNode mth) {\n\t\tboolean fixed = false;\n\t\tfor (SSAVar ssaVar : mth.getSVars()) {\n\t\t\tif (deduceType(mth, ssaVar)) {\n\t\t\t\tfixed = true;\n\t\t\t}\n\t\t}\n\t\treturn fixed;\n\t}\n\n\t@SuppressWarnings(\"RedundantIfStatement\")\n\tprivate boolean deduceType(MethodNode mth, SSAVar var) {\n\t\tif (var.isTypeImmutable()) {\n\t\t\treturn false;\n\t\t}\n\t\tArgType type = var.getTypeInfo().getType();\n\t\tif (type.isTypeKnown()) {\n\t\t\treturn false;\n\t\t}\n\t\t// try best type from bounds again\n\t\tif (setBestType(mth, var)) {\n\t\t\treturn true;\n\t\t}\n\t\t// try all possible types (useful for primitives)\n\t\tif (tryPossibleTypes(mth, var, type)) {\n\t\t\treturn true;\n\t\t}\n\t\t// for objects try super types\n\t\tif (tryWiderObjects(mth, var)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate boolean tryRemoveGenerics(MethodNode mth) {\n\t\tboolean resolved = true;\n\t\tfor (SSAVar var : mth.getSVars()) {\n\t\t\tArgType type = var.getTypeInfo().getType();\n\t\t\tif (!type.isTypeKnown()\n\t\t\t\t\t&& !var.isTypeImmutable()\n\t\t\t\t\t&& !tryRawType(mth, var)) {\n\t\t\t\tresolved = false;\n\t\t\t}\n\t\t}\n\t\treturn resolved;\n\t}\n\n\tprivate boolean tryRawType(MethodNode mth, SSAVar var) {\n\t\tSet<ArgType> objTypes = new LinkedHashSet<>();\n\t\tfor (ITypeBound bound : var.getTypeInfo().getBounds()) {\n\t\t\tArgType boundType = bound.getType();\n\t\t\tif (boundType.isTypeKnown() && boundType.isObject()) {\n\t\t\t\tobjTypes.add(boundType);\n\t\t\t}\n\t\t}\n\t\tif (objTypes.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (ArgType objType : objTypes) {\n\t\t\tif (checkRawType(mth, var, objType)) {\n\t\t\t\tmth.addDebugComment(\"Type inference failed for \" + var.toShortString() + \".\"\n\t\t\t\t\t\t+ \" Raw type applied. Possible types: \" + Utils.listToString(objTypes));\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate boolean checkRawType(MethodNode mth, SSAVar var, ArgType objType) {\n\t\tif (objType.isObject() && objType.containsGeneric()) {\n\t\t\tArgType rawType = objType.isGenericType() ? ArgType.OBJECT : ArgType.object(objType.getObject());\n\t\t\tTypeUpdateResult result = typeUpdate.applyWithWiderAllow(mth, var, rawType);\n\t\t\treturn result == TypeUpdateResult.CHANGED;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Use type for var assigned from field (IGET or SGET).\n\t * Insert additional casts at var use places.\n\t */\n\tprivate Boolean applyFieldType(MethodNode mth) {\n\t\ttry {\n\t\t\tboolean changed = false;\n\t\t\t// will add new SSA vars, can't use for-each loop\n\t\t\tList<SSAVar> sVars = mth.getSVars();\n\t\t\tfor (int i = 0, varsCount = sVars.size(); i < varsCount; i++) {\n\t\t\t\tSSAVar ssaVar = sVars.get(i);\n\t\t\t\tif (tryFieldTypeWithNewCasts(mth, ssaVar, true)) {\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!changed) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// rerun full type inference\n\t\t\tInitCodeVariables.rerun(mth);\n\t\t\ttypeInference.initTypeBounds(mth);\n\t\t\ttypeInference.runTypePropagation(mth);\n\n\t\t\t// check if changed var types are fixed\n\t\t\tboolean success = true;\n\t\t\tfor (SSAVar ssaVar : mth.getSVars()) {\n\t\t\t\tif (tryFieldTypeWithNewCasts(mth, ssaVar, false)) {\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!success) {\n\t\t\t\ttypeInference.initTypeBounds(mth);\n\t\t\t\ttypeInference.runTypePropagation(mth);\n\t\t\t\tmth.addWarnComment(\"Type inference incomplete: some casts might be missing\");\n\t\t\t}\n\t\t\treturn success;\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Type inference fix 'apply assigned field type' failed\", e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate boolean tryFieldTypeWithNewCasts(MethodNode mth, SSAVar ssaVar, boolean insertCasts) {\n\t\tArgType type = ssaVar.getTypeInfo().getType();\n\t\tif (type.isTypeKnown() || ssaVar.isTypeImmutable()) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnNode assignInsn = ssaVar.getAssignInsn();\n\t\tif (assignInsn == null) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnType insnType = assignInsn.getType();\n\t\tif (insnType != InsnType.IGET && insnType != InsnType.SGET) {\n\t\t\treturn false;\n\t\t}\n\t\tArgType fieldType = assignInsn.getResult().getInitType();\n\t\t// field type should be used\n\t\tif (insertCasts) {\n\t\t\t// try to find a use place and insert cast\n\t\t\tboolean inserted = false;\n\t\t\tfor (RegisterArg useArg : ssaVar.getUseList()) {\n\t\t\t\tif (insertExplicitUseCast(mth, ssaVar, useArg, fieldType)) {\n\t\t\t\t\tinserted = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn inserted;\n\t\t}\n\t\t// force field type, will make type inference incomplete,\n\t\t// but it is better that completely unknown type\n\t\tssaVar.setType(fieldType);\n\t\treturn true;\n\t}\n\n\tprivate boolean insertExplicitUseCast(MethodNode mth, SSAVar ssaVar, RegisterArg useArg, ArgType fieldType) {\n\t\tInsnNode parentInsn = useArg.getParentInsn();\n\t\tif (!InsnUtils.isInsnType(parentInsn, InsnType.INVOKE)) {\n\t\t\treturn false;\n\t\t}\n\t\tInvokeNode invoke = (InvokeNode) parentInsn;\n\t\tInsnArg instanceArg = invoke.getInstanceArg();\n\t\tif (instanceArg == null || !instanceArg.isSameVar(ssaVar)) {\n\t\t\treturn false;\n\t\t}\n\t\tIMethodDetails details = mth.root().getMethodUtils().getMethodDetails(invoke);\n\t\tif (details == null) {\n\t\t\treturn false;\n\t\t}\n\t\tint newCasts = 0;\n\t\tint k = -1;\n\t\tfor (InsnArg invArg : invoke.getArgList()) {\n\t\t\tif (invArg == instanceArg) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tk++;\n\t\t\tif (!invArg.isRegister()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tArgType detailsArg = details.getArgTypes().get(k);\n\t\t\tArgType invArgType = invArg.getType();\n\t\t\tArgType resolvedType = mth.root().getTypeUtils().replaceClassGenerics(fieldType, invArgType, detailsArg);\n\t\t\tif (resolvedType != null && !resolvedType.equals(invArgType)) {\n\t\t\t\tIndexInsnNode castInsn = insertUseCast(mth, (RegisterArg) invArg, resolvedType);\n\t\t\t\tif (castInsn != null) {\n\t\t\t\t\tcastInsn.add(AFlag.EXPLICIT_CAST);\n\t\t\t\t\tnewCasts++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn newCasts > 0;\n\t}\n\n\t/**\n\t * Fix check casts to type var extend type:\n\t * <br>\n\t * {@code <T extends Comparable> T var = (Comparable) obj; => T var = (T) obj; }\n\t */\n\tprivate boolean tryRestoreTypeVarCasts(MethodNode mth) {\n\t\tint changed = 0;\n\t\tList<SSAVar> mthSVars = mth.getSVars();\n\t\tfor (SSAVar var : mthSVars) {\n\t\t\tchanged += restoreTypeVarCasts(var);\n\t\t}\n\t\tif (changed == 0) {\n\t\t\treturn false;\n\t\t}\n\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\tmth.addDebugComment(\"Restore \" + changed + \" type vars casts\");\n\t\t}\n\t\ttypeInference.initTypeBounds(mth);\n\t\treturn typeInference.runTypePropagation(mth);\n\t}\n\n\tprivate int restoreTypeVarCasts(SSAVar var) {\n\t\tTypeInfo typeInfo = var.getTypeInfo();\n\t\tSet<ITypeBound> bounds = typeInfo.getBounds();\n\t\tif (!ListUtils.anyMatch(bounds, t -> t.getType().isGenericType())) {\n\t\t\treturn 0;\n\t\t}\n\t\tList<ITypeBound> casts = ListUtils.filter(bounds, TypeBoundCheckCastAssign.class::isInstance);\n\t\tif (casts.isEmpty()) {\n\t\t\treturn 0;\n\t\t}\n\t\tArgType bestType = selectBestTypeFromBounds(bounds).orElse(ArgType.UNKNOWN);\n\t\tif (!bestType.isGenericType()) {\n\t\t\treturn 0;\n\t\t}\n\t\tList<ArgType> extendTypes = bestType.getExtendTypes();\n\t\tif (extendTypes.size() != 1) {\n\t\t\treturn 0;\n\t\t}\n\t\tint fixed = 0;\n\t\tArgType extendType = extendTypes.get(0);\n\t\tfor (ITypeBound bound : casts) {\n\t\t\tTypeBoundCheckCastAssign cast = (TypeBoundCheckCastAssign) bound;\n\t\t\tArgType castType = cast.getType();\n\t\t\tTypeCompareEnum result = typeUpdate.getTypeCompare().compareTypes(extendType, castType);\n\t\t\tif (result.isEqual() || result == TypeCompareEnum.NARROW_BY_GENERIC) {\n\t\t\t\tcast.getInsn().updateIndex(bestType);\n\t\t\t\tfixed++;\n\t\t\t}\n\t\t}\n\t\treturn fixed;\n\t}\n\n\t@SuppressWarnings({ \"ForLoopReplaceableByWhile\", \"ForLoopReplaceableByForEach\" })\n\tprivate boolean tryInsertCasts(MethodNode mth) {\n\t\tint added = 0;\n\t\tList<SSAVar> mthSVars = mth.getSVars();\n\t\tint varsCount = mthSVars.size();\n\t\tfor (int i = 0; i < varsCount; i++) {\n\t\t\tSSAVar var = mthSVars.get(i);\n\t\t\tArgType type = var.getTypeInfo().getType();\n\t\t\tif (!type.isTypeKnown() && !var.isTypeImmutable()) {\n\t\t\t\tadded += tryInsertVarCast(mth, var);\n\t\t\t}\n\t\t}\n\t\tif (added != 0) {\n\t\t\tInitCodeVariables.rerun(mth);\n\t\t\ttypeInference.initTypeBounds(mth);\n\t\t\treturn typeInference.runTypePropagation(mth);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate int tryInsertVarCast(MethodNode mth, SSAVar var) {\n\t\tfor (ITypeBound bound : var.getTypeInfo().getBounds()) {\n\t\t\tArgType boundType = bound.getType();\n\t\t\tif (boundType.isTypeKnown()\n\t\t\t\t\t&& !boundType.equals(var.getTypeInfo().getType())\n\t\t\t\t\t&& boundType.containsTypeVariable()\n\t\t\t\t\t&& !mth.root().getTypeUtils().containsUnknownTypeVar(mth, boundType)) {\n\t\t\t\tIndexInsnNode castInsn = insertAssignCast(mth, var, boundType);\n\t\t\t\tif (castInsn != null) {\n\t\t\t\t\tcastInsn.add(AFlag.SOFT_CAST);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\treturn insertUseCasts(mth, var);\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\tprivate int insertUseCasts(MethodNode mth, SSAVar var) {\n\t\tList<RegisterArg> useList = var.getUseList();\n\t\tif (useList.isEmpty()) {\n\t\t\treturn 0;\n\t\t}\n\t\tint useCasts = 0;\n\t\tfor (RegisterArg useReg : new ArrayList<>(useList)) {\n\t\t\tIndexInsnNode castInsn = insertUseCast(mth, useReg, useReg.getInitType());\n\t\t\tif (castInsn != null) {\n\t\t\t\tcastInsn.add(AFlag.SOFT_CAST);\n\t\t\t\tuseCasts++;\n\t\t\t}\n\t\t}\n\t\treturn useCasts;\n\t}\n\n\tprivate @Nullable IndexInsnNode insertAssignCast(MethodNode mth, SSAVar var, ArgType castType) {\n\t\tRegisterArg assignArg = var.getAssign();\n\t\tInsnNode assignInsn = assignArg.getParentInsn();\n\t\tif (assignInsn == null || assignInsn.getType() == InsnType.PHI) {\n\t\t\treturn null;\n\t\t}\n\t\tBlockNode assignBlock = BlockUtils.getBlockByInsn(mth, assignInsn);\n\t\tif (assignBlock == null) {\n\t\t\treturn null;\n\t\t}\n\t\tassignInsn.setResult(assignArg.duplicateWithNewSSAVar(mth));\n\t\tIndexInsnNode castInsn = makeCastInsn(assignArg.duplicate(), assignInsn.getResult().duplicate(), castType);\n\t\tif (!BlockUtils.insertAfterInsn(assignBlock, assignInsn, castInsn)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn castInsn;\n\t}\n\n\tprivate @Nullable IndexInsnNode insertUseCast(MethodNode mth, RegisterArg useArg, ArgType castType) {\n\t\tInsnNode useInsn = useArg.getParentInsn();\n\t\tif (useInsn == null || useInsn.getType() == InsnType.PHI) {\n\t\t\treturn null;\n\t\t}\n\t\tif (useInsn.getType() == InsnType.IF && useInsn.getArg(1).isZeroConst()) {\n\t\t\t// cast isn't needed if compare with null\n\t\t\treturn null;\n\t\t}\n\t\tBlockNode useBlock = BlockUtils.getBlockByInsn(mth, useInsn);\n\t\tif (useBlock == null) {\n\t\t\treturn null;\n\t\t}\n\t\tIndexInsnNode castInsn = makeCastInsn(\n\t\t\t\tuseArg.duplicateWithNewSSAVar(mth),\n\t\t\t\tuseArg.duplicate(),\n\t\t\t\tcastType);\n\t\tuseInsn.replaceArg(useArg, castInsn.getResult().duplicate());\n\t\tboolean inserted = BlockUtils.insertBeforeInsn(useBlock, useInsn, castInsn);\n\t\tif (!inserted) {\n\t\t\treturn null;\n\t\t}\n\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\tLOG.info(\"Insert cast for {} before {} in {}\", useArg, useInsn, useBlock);\n\t\t}\n\t\treturn castInsn;\n\t}\n\n\tprivate IndexInsnNode makeCastInsn(RegisterArg result, RegisterArg arg, ArgType castType) {\n\t\tIndexInsnNode castInsn = new IndexInsnNode(InsnType.CHECK_CAST, castType, 1);\n\t\tcastInsn.setResult(result);\n\t\tcastInsn.addArg(arg);\n\t\tcastInsn.add(AFlag.SYNTHETIC);\n\t\treturn castInsn;\n\t}\n\n\tprivate boolean trySplitConstInsns(MethodNode mth) {\n\t\tboolean constSplit = false;\n\t\tfor (SSAVar var : new ArrayList<>(mth.getSVars())) {\n\t\t\tif (checkAndSplitConstInsn(mth, var)) {\n\t\t\t\tconstSplit = true;\n\t\t\t}\n\t\t}\n\t\tif (!constSplit) {\n\t\t\treturn false;\n\t\t}\n\t\tInitCodeVariables.rerun(mth);\n\t\ttypeInference.initTypeBounds(mth);\n\t\treturn typeInference.runTypePropagation(mth);\n\t}\n\n\tprivate boolean checkAndSplitConstInsn(MethodNode mth, SSAVar var) {\n\t\tArgType type = var.getTypeInfo().getType();\n\t\tif (type.isTypeKnown() || var.isTypeImmutable()) {\n\t\t\treturn false;\n\t\t}\n\t\treturn splitByPhi(mth, var) || dupConst(mth, var);\n\t}\n\n\tprivate boolean dupConst(MethodNode mth, SSAVar var) {\n\t\tInsnNode assignInsn = var.getAssign().getAssignInsn();\n\t\tif (!InsnUtils.isInsnType(assignInsn, InsnType.CONST)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (var.getUseList().size() < 2) {\n\t\t\treturn false;\n\t\t}\n\t\tBlockNode assignBlock = BlockUtils.getBlockByInsn(mth, assignInsn);\n\t\tif (assignBlock == null) {\n\t\t\treturn false;\n\t\t}\n\t\tassignInsn.remove(AFlag.DONT_INLINE);\n\t\tint insertIndex = 1 + BlockUtils.getInsnIndexInBlock(assignBlock, assignInsn);\n\t\tList<RegisterArg> useList = new ArrayList<>(var.getUseList());\n\t\tfor (int i = 0, useCount = useList.size(); i < useCount; i++) {\n\t\t\tRegisterArg useArg = useList.get(i);\n\t\t\tuseArg.remove(AFlag.DONT_INLINE_CONST);\n\t\t\tif (i == 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tInsnNode useInsn = useArg.getParentInsn();\n\t\t\tif (useInsn == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tInsnNode newInsn = assignInsn.copyWithNewSsaVar(mth);\n\t\t\tassignBlock.getInstructions().add(insertIndex, newInsn);\n\t\t\tuseInsn.replaceArg(useArg, newInsn.getResult().duplicate());\n\t\t}\n\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\tLOG.debug(\"Duplicate const insn {} times: {} in {}\", useList.size(), assignInsn, assignBlock);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * For every PHI make separate CONST insn\n\t */\n\tprivate static boolean splitByPhi(MethodNode mth, SSAVar var) {\n\t\tif (var.getUsedInPhi().size() < 2) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnNode assignInsn = var.getAssign().getAssignInsn();\n\t\tInsnNode constInsn = InsnUtils.checkInsnType(assignInsn, InsnType.CONST);\n\t\tif (constInsn == null) {\n\t\t\treturn false;\n\t\t}\n\t\tBlockNode blockNode = BlockUtils.getBlockByInsn(mth, constInsn);\n\t\tif (blockNode == null) {\n\t\t\treturn false;\n\t\t}\n\t\tboolean first = true;\n\t\tfor (PhiInsn phiInsn : var.getUsedInPhi()) {\n\t\t\tif (first) {\n\t\t\t\tfirst = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tInsnNode copyInsn = constInsn.copyWithNewSsaVar(mth);\n\t\t\tcopyInsn.add(AFlag.SYNTHETIC);\n\t\t\tBlockUtils.insertAfterInsn(blockNode, constInsn, copyInsn);\n\n\t\t\tRegisterArg phiArg = phiInsn.getArgBySsaVar(var);\n\t\t\tphiInsn.replaceArg(phiArg, copyInsn.getResult().duplicate());\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate boolean tryInsertAdditionalMove(MethodNode mth) {\n\t\tint insnsAdded = 0;\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tPhiListAttr phiListAttr = block.get(AType.PHI_LIST);\n\t\t\tif (phiListAttr != null) {\n\t\t\t\tfor (PhiInsn phiInsn : phiListAttr.getList()) {\n\t\t\t\t\tinsnsAdded += tryInsertAdditionalInsn(mth, phiInsn);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (insnsAdded == 0) {\n\t\t\treturn false;\n\t\t}\n\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\tmth.addDebugComment(\"Additional \" + insnsAdded + \" move instructions added to help type inference\");\n\t\t}\n\t\tInitCodeVariables.rerun(mth);\n\t\ttypeInference.initTypeBounds(mth);\n\t\tif (typeInference.runTypePropagation(mth) && checkTypes(mth)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn tryDeduceTypes(mth);\n\t}\n\n\t/**\n\t * Add MOVE instruction before PHI in bound blocks to make 'soft' type link.\n\t * This allows using different types in blocks merged by PHI.\n\t */\n\tprivate int tryInsertAdditionalInsn(MethodNode mth, PhiInsn phiInsn) {\n\t\tArgType phiType = getCommonTypeForPhiArgs(phiInsn);\n\t\tif (phiType != null && phiType.isTypeKnown()) {\n\t\t\t// all args have the same known type => nothing to do here\n\t\t\treturn 0;\n\t\t}\n\t\t// check if instructions can be inserted\n\t\tif (insertMovesForPhi(mth, phiInsn, false) == 0) {\n\t\t\treturn 0;\n\t\t}\n\t\t// check passed => apply\n\t\treturn insertMovesForPhi(mth, phiInsn, true);\n\t}\n\n\t@Nullable\n\tprivate ArgType getCommonTypeForPhiArgs(PhiInsn phiInsn) {\n\t\tArgType phiArgType = null;\n\t\tfor (InsnArg arg : phiInsn.getArguments()) {\n\t\t\tArgType type = arg.getType();\n\t\t\tif (phiArgType == null) {\n\t\t\t\tphiArgType = type;\n\t\t\t} else if (!phiArgType.equals(type)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\treturn phiArgType;\n\t}\n\n\tprivate int insertMovesForPhi(MethodNode mth, PhiInsn phiInsn, boolean apply) {\n\t\tint argsCount = phiInsn.getArgsCount();\n\t\tint count = 0;\n\t\tfor (int argIndex = 0; argIndex < argsCount; argIndex++) {\n\t\t\tRegisterArg reg = phiInsn.getArg(argIndex);\n\t\t\tBlockNode startBlock = phiInsn.getBlockByArgIndex(argIndex);\n\t\t\tBlockNode blockNode = checkBlockForInsnInsert(startBlock);\n\t\t\tif (blockNode == null) {\n\t\t\t\tmth.addDebugComment(\"Failed to insert an additional move for type inference into block \" + startBlock);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tboolean add = true;\n\t\t\tSSAVar var = reg.getSVar();\n\t\t\tInsnNode assignInsn = var.getAssign().getAssignInsn();\n\t\t\tif (assignInsn != null) {\n\t\t\t\tInsnType assignType = assignInsn.getType();\n\t\t\t\tif (assignType == InsnType.CONST\n\t\t\t\t\t\t|| (assignType == InsnType.MOVE && var.getUseCount() == 1)) {\n\t\t\t\t\tadd = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (add) {\n\t\t\t\tcount++;\n\t\t\t\tif (apply) {\n\t\t\t\t\tinsertMove(mth, blockNode, phiInsn, reg);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn count;\n\t}\n\n\tprivate void insertMove(MethodNode mth, BlockNode blockNode, PhiInsn phiInsn, RegisterArg reg) {\n\t\tSSAVar var = reg.getSVar();\n\t\tint regNum = reg.getRegNum();\n\t\tRegisterArg resultArg = reg.duplicate(regNum, null);\n\t\tSSAVar newSsaVar = mth.makeNewSVar(resultArg);\n\t\tRegisterArg arg = reg.duplicate(regNum, var);\n\n\t\tInsnNode moveInsn = new InsnNode(InsnType.MOVE, 1);\n\t\tmoveInsn.setResult(resultArg);\n\t\tmoveInsn.addArg(arg);\n\t\tmoveInsn.add(AFlag.SYNTHETIC);\n\t\tblockNode.getInstructions().add(moveInsn);\n\n\t\tphiInsn.replaceArg(reg, reg.duplicate(regNum, newSsaVar));\n\t}\n\n\t@Nullable\n\tprivate BlockNode checkBlockForInsnInsert(BlockNode blockNode) {\n\t\tif (blockNode.isSynthetic()) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnNode lastInsn = BlockUtils.getLastInsn(blockNode);\n\t\tif (lastInsn != null && BlockSplitter.isSeparate(lastInsn.getType())) {\n\t\t\t// can't insert move in a block with 'separate' instruction => try previous block by simple path\n\t\t\tList<BlockNode> preds = blockNode.getPredecessors();\n\t\t\tif (preds.size() == 1) {\n\t\t\t\treturn checkBlockForInsnInsert(preds.get(0));\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\treturn blockNode;\n\t}\n\n\tprivate boolean tryWiderObjects(MethodNode mth, SSAVar var) {\n\t\tSet<ArgType> objTypes = new LinkedHashSet<>();\n\t\tfor (ITypeBound bound : var.getTypeInfo().getBounds()) {\n\t\t\tArgType boundType = bound.getType();\n\t\t\tif (boundType.isTypeKnown() && boundType.isObject()) {\n\t\t\t\tobjTypes.add(boundType);\n\t\t\t}\n\t\t}\n\t\tif (objTypes.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tClspGraph clsp = mth.root().getClsp();\n\t\tfor (ArgType objType : objTypes) {\n\t\t\tfor (String ancestor : clsp.getSuperTypes(objType.getObject())) {\n\t\t\t\tArgType ancestorType = ArgType.object(ancestor);\n\t\t\t\tTypeUpdateResult result = typeUpdate.applyWithWiderAllow(mth, var, ancestorType);\n\t\t\t\tif (result == TypeUpdateResult.CHANGED) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@SuppressWarnings(\"ForLoopReplaceableByForEach\")\n\tprivate boolean tryToFixIncompatiblePrimitives(MethodNode mth) {\n\t\tboolean fixed = false;\n\t\tList<SSAVar> ssaVars = mth.getSVars();\n\t\tint ssaVarsCount = ssaVars.size();\n\t\t// new vars will be added at a list end if fix is applied (can't use for-each loop here)\n\t\tfor (int i = 0; i < ssaVarsCount; i++) {\n\t\t\tif (processIncompatiblePrimitives(mth, ssaVars.get(i))) {\n\t\t\t\tfixed = true;\n\t\t\t}\n\t\t}\n\t\tif (!fixed) {\n\t\t\treturn false;\n\t\t}\n\t\tInitCodeVariables.rerun(mth);\n\t\ttypeInference.initTypeBounds(mth);\n\t\treturn typeInference.runTypePropagation(mth);\n\t}\n\n\tprivate boolean processIncompatiblePrimitives(MethodNode mth, SSAVar var) {\n\t\tTypeInfo typeInfo = var.getTypeInfo();\n\t\tif (typeInfo.getType().isTypeKnown()) {\n\t\t\treturn false;\n\t\t}\n\t\tboolean assigned = false;\n\t\tfor (ITypeBound bound : typeInfo.getBounds()) {\n\t\t\tArgType boundType = bound.getType();\n\t\t\tswitch (bound.getBound()) {\n\t\t\t\tcase ASSIGN:\n\t\t\t\t\tif (!boundType.contains(PrimitiveType.BOOLEAN)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tassigned = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase USE:\n\t\t\t\t\tif (!boundType.canBeAnyNumber()) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!assigned) {\n\t\t\treturn false;\n\t\t}\n\n\t\tboolean fixed = false;\n\t\tfor (RegisterArg arg : new ArrayList<>(var.getUseList())) {\n\t\t\tif (fixBooleanUsage(mth, arg)) {\n\t\t\t\tfixed = true;\n\t\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\t\tLOG.info(\"Fixed boolean usage for arg {} from {}\", arg, arg.getParentInsn());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn fixed;\n\t}\n\n\tprivate boolean fixBooleanUsage(MethodNode mth, RegisterArg boundArg) {\n\t\tArgType boundType = boundArg.getInitType();\n\t\tif (boundType == ArgType.BOOLEAN\n\t\t\t\t|| (boundType.isTypeKnown() && !boundType.isPrimitive())) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnNode insn = boundArg.getParentInsn();\n\t\tif (insn == null || insn.getType() == InsnType.IF) {\n\t\t\treturn false;\n\t\t}\n\t\tBlockNode blockNode = BlockUtils.getBlockByInsn(mth, insn);\n\t\tif (blockNode == null) {\n\t\t\treturn false;\n\t\t}\n\t\tList<InsnNode> insnList = blockNode.getInstructions();\n\t\tint insnIndex = InsnList.getIndex(insnList, insn);\n\t\tif (insnIndex == -1) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnType insnType = insn.getType();\n\t\tif (insnType == InsnType.CAST) {\n\t\t\t// replace cast\n\t\t\tArgType type = (ArgType) ((IndexInsnNode) insn).getIndex();\n\t\t\tTernaryInsn convertInsn = prepareBooleanConvertInsn(insn.getResult(), boundArg, type);\n\t\t\tBlockUtils.replaceInsn(mth, blockNode, insnIndex, convertInsn);\n\t\t\treturn true;\n\t\t}\n\t\tif (insnType == InsnType.ARITH) {\n\t\t\tArithNode arithInsn = (ArithNode) insn;\n\t\t\tif (arithInsn.getOp() == ArithOp.XOR && arithInsn.getArgsCount() == 2) {\n\t\t\t\t// replace (boolean ^ 1) with (!boolean)\n\t\t\t\tInsnArg secondArg = arithInsn.getArg(1);\n\t\t\t\tif (secondArg.isLiteral() && ((LiteralArg) secondArg).getLiteral() == 1) {\n\t\t\t\t\tInsnNode convertInsn = notBooleanToInt(arithInsn, boundArg);\n\t\t\t\t\tBlockUtils.replaceInsn(mth, blockNode, insnIndex, convertInsn);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// insert before insn\n\t\tRegisterArg resultArg = boundArg.duplicateWithNewSSAVar(mth);\n\t\tTernaryInsn convertInsn = prepareBooleanConvertInsn(resultArg, boundArg, boundType);\n\t\tinsnList.add(insnIndex, convertInsn);\n\t\tinsn.replaceArg(boundArg, convertInsn.getResult().duplicate());\n\t\treturn true;\n\t}\n\n\tprivate InsnNode notBooleanToInt(ArithNode insn, RegisterArg boundArg) {\n\t\tInsnNode notInsn = new InsnNode(InsnType.NOT, 1);\n\t\tnotInsn.addArg(boundArg.duplicate());\n\t\tnotInsn.add(AFlag.SYNTHETIC);\n\n\t\tArgType resType = insn.getResult().getType();\n\t\tif (resType.canBePrimitive(PrimitiveType.BOOLEAN)) {\n\t\t\tnotInsn.setResult(insn.getResult());\n\t\t\treturn notInsn;\n\t\t}\n\t\tInsnArg notArg = InsnArg.wrapArg(notInsn);\n\t\tnotArg.setType(ArgType.BOOLEAN);\n\t\tTernaryInsn convertInsn = ModVisitor.makeBooleanConvertInsn(insn.getResult(), notArg, ArgType.INT);\n\t\tconvertInsn.add(AFlag.SYNTHETIC);\n\t\treturn convertInsn;\n\t}\n\n\tprivate TernaryInsn prepareBooleanConvertInsn(RegisterArg resultArg, RegisterArg boundArg, ArgType useType) {\n\t\tRegisterArg useArg = boundArg.getSVar().getAssign().duplicate();\n\t\tTernaryInsn convertInsn = ModVisitor.makeBooleanConvertInsn(resultArg, useArg, useType);\n\t\tconvertInsn.add(AFlag.SYNTHETIC);\n\t\treturn convertInsn;\n\t}\n\n\tprivate boolean tryToForceImmutableTypes(MethodNode mth) {\n\t\tboolean fixed = false;\n\t\tfor (SSAVar ssaVar : mth.getSVars()) {\n\t\t\tArgType type = ssaVar.getTypeInfo().getType();\n\t\t\tif (!type.isTypeKnown() && ssaVar.isTypeImmutable()) {\n\t\t\t\tif (forceImmutableType(ssaVar)) {\n\t\t\t\t\tfixed = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!fixed) {\n\t\t\treturn false;\n\t\t}\n\t\treturn typeInference.runTypePropagation(mth);\n\t}\n\n\tprivate boolean forceImmutableType(SSAVar ssaVar) {\n\t\tfor (RegisterArg useArg : ssaVar.getUseList()) {\n\t\t\tInsnNode parentInsn = useArg.getParentInsn();\n\t\t\tif (parentInsn != null) {\n\t\t\t\tInsnType insnType = parentInsn.getType();\n\t\t\t\tif (insnType == InsnType.AGET || insnType == InsnType.APUT) {\n\t\t\t\t\tssaVar.setType(ssaVar.getImmutableType());\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"FixTypesVisitor\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/ITypeBound.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.RegisterArg;\n\n/**\n * Information to restrict types by applying constraints (or boundaries)\n */\npublic interface ITypeBound {\n\n\tBoundEnum getBound();\n\n\tArgType getType();\n\n\t@Nullable\n\tRegisterArg getArg();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/ITypeBoundDynamic.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport jadx.core.dex.instructions.args.ArgType;\n\n/**\n * 'Dynamic' type bound allows to use requested and not yet applied types\n * from {@link TypeUpdateInfo} for more precise restrictions\n */\npublic interface ITypeBoundDynamic extends ITypeBound {\n\n\t/**\n\t * This method will be executed instead of {@link ITypeBound#getType()}\n\t * if {@link TypeUpdateInfo} is available.\n\t */\n\tArgType getType(TypeUpdateInfo updateInfo);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/ITypeConstraint.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport java.util.List;\n\nimport jadx.core.dex.instructions.args.SSAVar;\n\npublic interface ITypeConstraint {\n\n\tList<SSAVar> getRelatedVars();\n\n\tboolean check(TypeSearchState state);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/ITypeListener.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.nodes.InsnNode;\n\n@FunctionalInterface\npublic interface ITypeListener {\n\n\t/**\n\t * Listener function - triggered on type update\n\t *\n\t * @param updateInfo    store all allowed type updates\n\t * @param arg           apply suggested type for this arg\n\t * @param candidateType suggest new type\n\t */\n\tTypeUpdateResult update(TypeUpdateInfo updateInfo, InsnNode insn, InsnArg arg, @NotNull ArgType candidateType);\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeBoundCheckCastAssign.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.RootNode;\n\n/**\n * Allow ignoring down casts (return arg type instead cast type)\n * Such casts will be removed later.\n */\npublic final class TypeBoundCheckCastAssign implements ITypeBoundDynamic {\n\tprivate final RootNode root;\n\tprivate final IndexInsnNode insn;\n\n\tpublic TypeBoundCheckCastAssign(RootNode root, IndexInsnNode insn) {\n\t\tthis.root = root;\n\t\tthis.insn = insn;\n\t}\n\n\t@Override\n\tpublic BoundEnum getBound() {\n\t\treturn BoundEnum.ASSIGN;\n\t}\n\n\t@Override\n\tpublic ArgType getType(TypeUpdateInfo updateInfo) {\n\t\treturn getReturnType(updateInfo.getType(insn.getArg(0)));\n\t}\n\n\t@Override\n\tpublic ArgType getType() {\n\t\treturn getReturnType(insn.getArg(0).getType());\n\t}\n\n\tprivate ArgType getReturnType(ArgType argType) {\n\t\tArgType castType = insn.getIndexAsType();\n\t\tTypeCompareEnum result = root.getTypeCompare().compareTypes(argType, castType);\n\t\treturn result.isNarrow() ? argType : castType;\n\t}\n\n\t@Override\n\tpublic @Nullable RegisterArg getArg() {\n\t\treturn insn.getResult();\n\t}\n\n\tpublic IndexInsnNode getInsn() {\n\t\treturn insn;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"CHECK_CAST_ASSIGN{(\" + insn.getIndex() + \") \" + insn.getArg(0).getType() + \"}\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeBoundConst.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport java.util.Objects;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.RegisterArg;\n\npublic final class TypeBoundConst implements ITypeBound {\n\tprivate final BoundEnum bound;\n\tprivate final ArgType type;\n\tprivate final RegisterArg arg;\n\n\tpublic TypeBoundConst(BoundEnum bound, ArgType type) {\n\t\tthis(bound, type, null);\n\t}\n\n\tpublic TypeBoundConst(BoundEnum bound, ArgType type, RegisterArg arg) {\n\t\tthis.bound = bound;\n\t\tthis.type = type;\n\t\tthis.arg = arg;\n\t}\n\n\t@Override\n\tpublic BoundEnum getBound() {\n\t\treturn bound;\n\t}\n\n\t@Override\n\tpublic ArgType getType() {\n\t\treturn type;\n\t}\n\n\t@Override\n\tpublic RegisterArg getArg() {\n\t\treturn arg;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tTypeBoundConst that = (TypeBoundConst) o;\n\t\treturn bound == that.bound && Objects.equals(type, that.type);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(bound, type);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"{\" + bound + \": \" + type + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeBoundFieldGetAssign.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.RootNode;\n\n/**\n * Dynamic bound for instance field get of generic type.\n * Bound type calculated using instance generic type.\n */\npublic final class TypeBoundFieldGetAssign implements ITypeBoundDynamic {\n\tprivate final RootNode root;\n\tprivate final IndexInsnNode getNode;\n\tprivate final FieldInfo fieldInfo;\n\tprivate final ArgType initType;\n\n\tpublic TypeBoundFieldGetAssign(RootNode root, IndexInsnNode getNode, ArgType initType) {\n\t\tthis.root = root;\n\t\tthis.getNode = getNode;\n\t\tthis.fieldInfo = (FieldInfo) getNode.getIndex();\n\t\tthis.initType = initType;\n\t}\n\n\t@Override\n\tpublic BoundEnum getBound() {\n\t\treturn BoundEnum.ASSIGN;\n\t}\n\n\t@Override\n\tpublic ArgType getType(TypeUpdateInfo updateInfo) {\n\t\treturn getResultType(updateInfo.getType(getInstanceArg()));\n\t}\n\n\t@Override\n\tpublic ArgType getType() {\n\t\treturn getResultType(getInstanceArg().getType());\n\t}\n\n\tprivate ArgType getResultType(ArgType instanceType) {\n\t\tArgType resultGeneric = root.getTypeUtils().replaceClassGenerics(instanceType, initType);\n\t\tif (resultGeneric != null && !resultGeneric.isWildcard()) {\n\t\t\treturn resultGeneric;\n\t\t}\n\t\treturn initType; // TODO: check if this type is allowed in current scope\n\t}\n\n\tprivate InsnArg getInstanceArg() {\n\t\treturn getNode.getArg(0);\n\t}\n\n\t@Override\n\tpublic RegisterArg getArg() {\n\t\treturn getNode.getResult();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tTypeBoundFieldGetAssign that = (TypeBoundFieldGetAssign) o;\n\t\treturn getNode.equals(that.getNode);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn getNode.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"FieldGetAssign{\" + fieldInfo\n\t\t\t\t+ \", type=\" + getType()\n\t\t\t\t+ \", instanceArg=\" + getInstanceArg()\n\t\t\t\t+ '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeBoundInvokeAssign.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.dex.nodes.RootNode;\n\n/**\n * Special dynamic bound for invoke with generics.\n * Bound type calculated using instance generic type.\n * TODO: also can depends on argument types\n */\npublic final class TypeBoundInvokeAssign implements ITypeBoundDynamic {\n\tprivate final RootNode root;\n\tprivate final InvokeNode invokeNode;\n\tprivate final ArgType genericReturnType;\n\n\tpublic TypeBoundInvokeAssign(RootNode root, InvokeNode invokeNode, ArgType genericReturnType) {\n\t\tthis.root = root;\n\t\tthis.invokeNode = invokeNode;\n\t\tthis.genericReturnType = genericReturnType;\n\t}\n\n\t@Override\n\tpublic BoundEnum getBound() {\n\t\treturn BoundEnum.ASSIGN;\n\t}\n\n\t@Override\n\tpublic ArgType getType(TypeUpdateInfo updateInfo) {\n\t\treturn getReturnType(updateInfo.getType(getInstanceArg()));\n\t}\n\n\t@Override\n\tpublic ArgType getType() {\n\t\treturn getReturnType(getInstanceArg().getType());\n\t}\n\n\tprivate ArgType getReturnType(ArgType instanceType) {\n\t\tArgType mthDeclType;\n\t\tIMethodDetails methodDetails = root.getMethodUtils().getMethodDetails(invokeNode);\n\t\tif (methodDetails != null) {\n\t\t\t// use methods detail to resolve declaration class for virtual invokes\n\t\t\tmthDeclType = methodDetails.getMethodInfo().getDeclClass().getType();\n\t\t} else {\n\t\t\tmthDeclType = instanceType;\n\t\t}\n\t\tArgType resultGeneric = root.getTypeUtils().replaceClassGenerics(instanceType, mthDeclType, genericReturnType);\n\t\tArgType result = processResultType(resultGeneric);\n\t\tif (result != null) {\n\t\t\treturn result;\n\t\t}\n\t\treturn invokeNode.getCallMth().getReturnType();\n\t}\n\n\t@Nullable\n\tprivate ArgType processResultType(@Nullable ArgType resultGeneric) {\n\t\tif (resultGeneric == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!resultGeneric.isWildcard()) {\n\t\t\treturn resultGeneric;\n\t\t}\n\t\treturn resultGeneric.getWildcardType();\n\t}\n\n\tprivate InsnArg getInstanceArg() {\n\t\treturn invokeNode.getArg(0);\n\t}\n\n\t@Override\n\tpublic RegisterArg getArg() {\n\t\treturn invokeNode.getResult();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tTypeBoundInvokeAssign that = (TypeBoundInvokeAssign) o;\n\t\treturn invokeNode.equals(that.invokeNode);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn invokeNode.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"InvokeAssign{\" + invokeNode.getCallMth().getShortId()\n\t\t\t\t+ \", returnType=\" + genericReturnType\n\t\t\t\t+ \", currentType=\" + getType()\n\t\t\t\t+ \", instanceArg=\" + getInstanceArg()\n\t\t\t\t+ '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeBoundInvokeUse.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport jadx.core.dex.instructions.BaseInvokeNode;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.RootNode;\n\n/**\n * Special dynamic bound for invoke with generics.\n * Arguments bound type calculated using instance generic type.\n */\npublic final class TypeBoundInvokeUse implements ITypeBoundDynamic {\n\tprivate final RootNode root;\n\tprivate final BaseInvokeNode invokeNode;\n\tprivate final RegisterArg arg;\n\tprivate final ArgType genericArgType;\n\n\tpublic TypeBoundInvokeUse(RootNode root, BaseInvokeNode invokeNode, RegisterArg arg, ArgType genericArgType) {\n\t\tthis.root = root;\n\t\tthis.invokeNode = invokeNode;\n\t\tthis.arg = arg;\n\t\tthis.genericArgType = genericArgType;\n\t}\n\n\t@Override\n\tpublic BoundEnum getBound() {\n\t\treturn BoundEnum.USE;\n\t}\n\n\t@Override\n\tpublic ArgType getType(TypeUpdateInfo updateInfo) {\n\t\treturn getArgType(updateInfo.getType(invokeNode.getInstanceArg()), updateInfo.getType(arg));\n\t}\n\n\t@Override\n\tpublic ArgType getType() {\n\t\treturn getArgType(invokeNode.getInstanceArg().getType(), arg.getType());\n\t}\n\n\tprivate ArgType getArgType(ArgType instanceType, ArgType argType) {\n\t\tArgType resultGeneric = root.getTypeUtils().replaceClassGenerics(instanceType, genericArgType);\n\t\tif (resultGeneric != null) {\n\t\t\treturn resultGeneric;\n\t\t}\n\t\treturn argType;\n\t}\n\n\t@Override\n\tpublic RegisterArg getArg() {\n\t\treturn arg;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tTypeBoundInvokeUse that = (TypeBoundInvokeUse) o;\n\t\treturn invokeNode.equals(that.invokeNode);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn invokeNode.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"InvokeAssign{\" + invokeNode.getCallMth().getShortId()\n\t\t\t\t+ \", argType=\" + genericArgType\n\t\t\t\t+ \", currentType=\" + getType()\n\t\t\t\t+ \", instanceArg=\" + invokeNode.getInstanceArg()\n\t\t\t\t+ '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeCompare.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.ArgType.WildcardBound;\nimport jadx.core.dex.instructions.args.PrimitiveType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.dex.visitors.typeinference.TypeCompareEnum.CONFLICT;\nimport static jadx.core.dex.visitors.typeinference.TypeCompareEnum.CONFLICT_BY_GENERIC;\nimport static jadx.core.dex.visitors.typeinference.TypeCompareEnum.EQUAL;\nimport static jadx.core.dex.visitors.typeinference.TypeCompareEnum.NARROW;\nimport static jadx.core.dex.visitors.typeinference.TypeCompareEnum.NARROW_BY_GENERIC;\nimport static jadx.core.dex.visitors.typeinference.TypeCompareEnum.UNKNOWN;\nimport static jadx.core.dex.visitors.typeinference.TypeCompareEnum.WIDER;\nimport static jadx.core.dex.visitors.typeinference.TypeCompareEnum.WIDER_BY_GENERIC;\nimport static jadx.core.utils.Utils.isEmpty;\n\npublic class TypeCompare {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(TypeCompare.class);\n\n\tprivate final RootNode root;\n\tprivate final Comparator<ArgType> comparator;\n\tprivate final Comparator<ArgType> reversedComparator;\n\n\tpublic TypeCompare(RootNode root) {\n\t\tthis.root = root;\n\t\tthis.comparator = new ArgTypeComparator();\n\t\tthis.reversedComparator = comparator.reversed();\n\t}\n\n\tpublic TypeCompareEnum compareTypes(ClassNode first, ClassNode second) {\n\t\treturn compareObjects(first.getType(), second.getType());\n\t}\n\n\tpublic TypeCompareEnum compareTypes(ClassInfo first, ClassInfo second) {\n\t\treturn compareObjects(first.getType(), second.getType());\n\t}\n\n\tpublic TypeCompareEnum compareObjects(ArgType first, ArgType second) {\n\t\tif (first == second || Objects.equals(first, second)) {\n\t\t\treturn TypeCompareEnum.EQUAL;\n\t\t}\n\t\treturn compareObjectsNoPreCheck(first, second);\n\t}\n\n\t/**\n\t * Compare two type and return result for first argument (narrow, wider or conflict)\n\t */\n\tpublic TypeCompareEnum compareTypes(ArgType first, ArgType second) {\n\t\tif (first == second || Objects.equals(first, second)) {\n\t\t\treturn TypeCompareEnum.EQUAL;\n\t\t}\n\t\tboolean firstKnown = first.isTypeKnown();\n\t\tboolean secondKnown = second.isTypeKnown();\n\t\tif (firstKnown != secondKnown) {\n\t\t\tif (firstKnown) {\n\t\t\t\treturn compareWithUnknown(first, second);\n\t\t\t} else {\n\t\t\t\treturn compareWithUnknown(second, first).invert();\n\t\t\t}\n\t\t}\n\t\tboolean firstArray = first.isArray();\n\t\tboolean secondArray = second.isArray();\n\t\tif (firstArray != secondArray) {\n\t\t\tif (firstArray) {\n\t\t\t\treturn compareArrayWithOtherType(first, second);\n\t\t\t} else {\n\t\t\t\treturn compareArrayWithOtherType(second, first).invert();\n\t\t\t}\n\t\t}\n\t\tif (firstArray /* && secondArray */) {\n\t\t\t// both arrays\n\t\t\treturn compareTypes(first.getArrayElement(), second.getArrayElement());\n\t\t}\n\t\tif (!firstKnown /* && !secondKnown */) {\n\t\t\tint variantLen = Integer.compare(first.getPossibleTypes().length, second.getPossibleTypes().length);\n\t\t\treturn variantLen > 0 ? WIDER : NARROW;\n\t\t}\n\t\tboolean firstPrimitive = first.isPrimitive();\n\t\tboolean secondPrimitive = second.isPrimitive();\n\n\t\tboolean firstObj = first.isObject();\n\t\tboolean secondObj = second.isObject();\n\t\tif (firstObj && secondObj) {\n\t\t\treturn compareObjectsNoPreCheck(first, second);\n\t\t} else {\n\t\t\t// primitive types conflicts with objects\n\t\t\tif (firstObj && secondPrimitive) {\n\t\t\t\treturn CONFLICT;\n\t\t\t}\n\t\t\tif (firstPrimitive && secondObj) {\n\t\t\t\treturn CONFLICT;\n\t\t\t}\n\t\t}\n\t\tif (firstPrimitive && secondPrimitive) {\n\t\t\treturn comparePrimitives(first.getPrimitiveType(), second.getPrimitiveType());\n\t\t}\n\n\t\tLOG.warn(\"Type compare function not complete, can't compare {} and {}\", first, second);\n\t\treturn TypeCompareEnum.CONFLICT;\n\t}\n\n\tprivate TypeCompareEnum compareArrayWithOtherType(ArgType array, ArgType other) {\n\t\tif (!other.isTypeKnown()) {\n\t\t\tif (other.contains(PrimitiveType.ARRAY)) {\n\t\t\t\treturn NARROW;\n\t\t\t}\n\t\t\treturn CONFLICT;\n\t\t}\n\t\tif (other.isObject()) {\n\t\t\tif (other.equals(ArgType.OBJECT)) {\n\t\t\t\treturn NARROW;\n\t\t\t}\n\t\t\treturn CONFLICT;\n\t\t}\n\t\tif (other.isPrimitive()) {\n\t\t\treturn CONFLICT;\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Unprocessed type: \" + other + \" in array compare\");\n\t}\n\n\tprivate TypeCompareEnum compareWithUnknown(ArgType known, ArgType unknown) {\n\t\tif (unknown == ArgType.UNKNOWN) {\n\t\t\treturn NARROW;\n\t\t}\n\t\tif (unknown == ArgType.UNKNOWN_OBJECT && (known.isObject() || known.isArray())) {\n\t\t\treturn NARROW;\n\t\t}\n\t\tif (known.equals(ArgType.OBJECT) && unknown.isArray()) {\n\t\t\treturn WIDER;\n\t\t}\n\t\tPrimitiveType knownPrimitive;\n\t\tif (known.isPrimitive()) {\n\t\t\tknownPrimitive = known.getPrimitiveType();\n\t\t} else if (known.isArray()) {\n\t\t\tknownPrimitive = PrimitiveType.ARRAY;\n\t\t} else {\n\t\t\tknownPrimitive = PrimitiveType.OBJECT;\n\t\t}\n\t\tPrimitiveType[] possibleTypes = unknown.getPossibleTypes();\n\t\tfor (PrimitiveType possibleType : possibleTypes) {\n\t\t\tif (possibleType == knownPrimitive) {\n\t\t\t\treturn NARROW;\n\t\t\t}\n\t\t}\n\t\treturn CONFLICT;\n\t}\n\n\tprivate TypeCompareEnum compareObjectsNoPreCheck(ArgType first, ArgType second) {\n\t\tboolean objectsEquals = first.getObject().equals(second.getObject());\n\t\tboolean firstGenericType = first.isGenericType();\n\t\tboolean secondGenericType = second.isGenericType();\n\t\tif (firstGenericType && secondGenericType && !objectsEquals) {\n\t\t\treturn CONFLICT;\n\t\t}\n\t\tboolean firstGeneric = first.isGeneric();\n\t\tboolean secondGeneric = second.isGeneric();\n\n\t\tif (firstGenericType || secondGenericType) {\n\t\t\tArgType firstWildcardType = first.getWildcardType();\n\t\t\tArgType secondWildcardType = second.getWildcardType();\n\t\t\tif (firstWildcardType != null || secondWildcardType != null) {\n\t\t\t\tif (firstWildcardType != null && secondGenericType && first.getWildcardBound() == WildcardBound.UNBOUND) {\n\t\t\t\t\treturn CONFLICT;\n\t\t\t\t}\n\t\t\t\tif (firstGenericType && secondWildcardType != null && second.getWildcardBound() == WildcardBound.UNBOUND) {\n\t\t\t\t\treturn CONFLICT;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (firstGenericType) {\n\t\t\t\treturn compareGenericTypeWithObject(first, second);\n\t\t\t} else {\n\t\t\t\treturn compareGenericTypeWithObject(second, first).invert();\n\t\t\t}\n\t\t}\n\t\tif (objectsEquals) {\n\t\t\tif (firstGeneric != secondGeneric) {\n\t\t\t\treturn firstGeneric ? NARROW_BY_GENERIC : WIDER_BY_GENERIC;\n\t\t\t}\n\t\t\t// both generics on same object\n\t\t\tif (first.getWildcardBound() != null && second.getWildcardBound() != null) {\n\t\t\t\t// both wildcards\n\t\t\t\treturn compareWildcardTypes(first, second);\n\t\t\t}\n\t\t\tList<ArgType> firstGenericTypes = first.getGenericTypes();\n\t\t\tList<ArgType> secondGenericTypes = second.getGenericTypes();\n\t\t\tif (isEmpty(firstGenericTypes) || isEmpty(secondGenericTypes)) {\n\t\t\t\t// check outer types\n\t\t\t\tArgType firstOuterType = first.getOuterType();\n\t\t\t\tArgType secondOuterType = second.getOuterType();\n\t\t\t\tif (firstOuterType != null && secondOuterType != null) {\n\t\t\t\t\treturn compareTypes(firstOuterType, secondOuterType);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// compare generics arrays\n\t\t\t\tint len = firstGenericTypes.size();\n\t\t\t\tif (len == secondGenericTypes.size()) {\n\t\t\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\t\t\tTypeCompareEnum res = compareTypes(firstGenericTypes.get(i), secondGenericTypes.get(i));\n\t\t\t\t\t\tif (res != EQUAL) {\n\t\t\t\t\t\t\treturn res;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tboolean firstIsObjCls = first.equals(ArgType.OBJECT);\n\t\tif (firstIsObjCls || second.equals(ArgType.OBJECT)) {\n\t\t\treturn firstIsObjCls ? WIDER : NARROW;\n\t\t}\n\t\tif (ArgType.isInstanceOf(root, first, second)) {\n\t\t\treturn NARROW;\n\t\t}\n\t\tif (ArgType.isInstanceOf(root, second, first)) {\n\t\t\treturn WIDER;\n\t\t}\n\t\tif (!ArgType.isClsKnown(root, first) || !ArgType.isClsKnown(root, second)) {\n\t\t\treturn UNKNOWN;\n\t\t}\n\t\treturn TypeCompareEnum.CONFLICT;\n\t}\n\n\tprivate TypeCompareEnum compareWildcardTypes(ArgType first, ArgType second) {\n\t\tWildcardBound firstWildcardBound = first.getWildcardBound();\n\t\tWildcardBound secondWildcardBound = second.getWildcardBound();\n\t\tif (firstWildcardBound == WildcardBound.UNBOUND) {\n\t\t\treturn WIDER;\n\t\t}\n\t\tif (secondWildcardBound == WildcardBound.UNBOUND) {\n\t\t\treturn NARROW;\n\t\t}\n\t\tTypeCompareEnum wildcardCompare = compareTypes(first.getWildcardType(), second.getWildcardType());\n\t\tif (firstWildcardBound == secondWildcardBound) {\n\t\t\treturn wildcardCompare;\n\t\t}\n\t\treturn CONFLICT;\n\t}\n\n\tprivate TypeCompareEnum compareGenericTypeWithObject(ArgType genericType, ArgType objType) {\n\t\tif (objType.isGenericType()) {\n\t\t\treturn compareTypeVariables(genericType, objType);\n\t\t}\n\t\tif (objType.isWildcard()) {\n\t\t\treturn CONFLICT_BY_GENERIC;\n\t\t}\n\t\tboolean rootObject = objType.equals(ArgType.OBJECT);\n\t\tList<ArgType> extendTypes = genericType.getExtendTypes();\n\t\tif (extendTypes.isEmpty()) {\n\t\t\treturn rootObject ? NARROW : CONFLICT;\n\t\t}\n\t\tif (extendTypes.contains(objType) || rootObject) {\n\t\t\treturn NARROW;\n\t\t}\n\t\tfor (ArgType extendType : extendTypes) {\n\t\t\tTypeCompareEnum res = compareObjectsNoPreCheck(extendType, objType);\n\t\t\tif (!res.isNarrow()) {\n\t\t\t\treturn res;\n\t\t\t}\n\t\t}\n\t\treturn NARROW;\n\t}\n\n\tprivate TypeCompareEnum compareTypeVariables(ArgType first, ArgType second) {\n\t\tif (first.getObject().equals(second.getObject())) {\n\t\t\tList<ArgType> firstExtendTypes = removeObject(first.getExtendTypes());\n\t\t\tList<ArgType> secondExtendTypes = removeObject(second.getExtendTypes());\n\t\t\tif (firstExtendTypes.equals(secondExtendTypes)) {\n\t\t\t\treturn EQUAL;\n\t\t\t}\n\t\t\tint firstExtSize = firstExtendTypes.size();\n\t\t\tint secondExtSize = secondExtendTypes.size();\n\t\t\tif (firstExtSize == 0) {\n\t\t\t\treturn WIDER;\n\t\t\t}\n\t\t\tif (secondExtSize == 0) {\n\t\t\t\treturn NARROW;\n\t\t\t}\n\t\t\tif (firstExtSize == 1 && secondExtSize == 1) {\n\t\t\t\treturn compareTypes(firstExtendTypes.get(0), secondExtendTypes.get(0));\n\t\t\t}\n\t\t}\n\t\treturn CONFLICT;\n\t}\n\n\tprivate List<ArgType> removeObject(List<ArgType> extendTypes) {\n\t\tif (extendTypes.contains(ArgType.OBJECT)) {\n\t\t\tif (extendTypes.size() == 1) {\n\t\t\t\treturn Collections.emptyList();\n\t\t\t}\n\t\t\tList<ArgType> result = new ArrayList<>(extendTypes);\n\t\t\tresult.remove(ArgType.OBJECT);\n\t\t\treturn result;\n\t\t}\n\t\treturn extendTypes;\n\t}\n\n\tprivate TypeCompareEnum comparePrimitives(PrimitiveType type1, PrimitiveType type2) {\n\t\tif (type1 == PrimitiveType.BOOLEAN || type2 == PrimitiveType.BOOLEAN) {\n\t\t\treturn type1 == type2 ? EQUAL : CONFLICT;\n\t\t}\n\n\t\tif (type1 == PrimitiveType.VOID || type2 == PrimitiveType.VOID) {\n\t\t\treturn type1 == type2 ? EQUAL : CONFLICT;\n\t\t}\n\n\t\tif (type1 == PrimitiveType.BYTE && type2 == PrimitiveType.CHAR) {\n\t\t\treturn WIDER;\n\t\t}\n\n\t\tif (type1 == PrimitiveType.SHORT && type2 == PrimitiveType.CHAR) {\n\t\t\treturn WIDER;\n\t\t}\n\n\t\tfinal int type1Width = getTypeWidth(type1);\n\t\tfinal int type2Width = getTypeWidth(type2);\n\t\tif (type1Width > type2Width) {\n\t\t\treturn WIDER;\n\t\t} else if (type1Width < type2Width) {\n\t\t\treturn NARROW;\n\t\t} else {\n\t\t\treturn EQUAL;\n\t\t}\n\t}\n\n\tprivate byte getTypeWidth(PrimitiveType type) {\n\t\tswitch (type) {\n\t\t\tcase BYTE:\n\t\t\t\treturn 0;\n\t\t\tcase SHORT:\n\t\t\t\treturn 1;\n\t\t\tcase CHAR:\n\t\t\t\treturn 2;\n\t\t\tcase INT:\n\t\t\t\treturn 3;\n\t\t\tcase LONG:\n\t\t\t\treturn 4;\n\t\t\tcase FLOAT:\n\t\t\t\treturn 5;\n\t\t\tcase DOUBLE:\n\t\t\t\treturn 6;\n\t\t\tcase BOOLEAN:\n\t\t\tcase OBJECT:\n\t\t\tcase ARRAY:\n\t\t\tcase VOID:\n\t\t\t\tthrow new JadxRuntimeException(\"Type \" + type + \" should not be here\");\n\t\t}\n\n\t\tthrow new JadxRuntimeException(\"Unhandled type: \" + type);\n\t}\n\n\tpublic Comparator<ArgType> getComparator() {\n\t\treturn comparator;\n\t}\n\n\tpublic Comparator<ArgType> getReversedComparator() {\n\t\treturn reversedComparator;\n\t}\n\n\tprivate final class ArgTypeComparator implements Comparator<ArgType> {\n\t\t@Override\n\t\tpublic int compare(ArgType a, ArgType b) {\n\t\t\tTypeCompareEnum result = compareTypes(a, b);\n\t\t\tswitch (result) {\n\t\t\t\tcase CONFLICT:\n\t\t\t\t\treturn -2;\n\n\t\t\t\tcase WIDER:\n\t\t\t\tcase WIDER_BY_GENERIC:\n\t\t\t\t\treturn -1;\n\n\t\t\t\tcase NARROW:\n\t\t\t\tcase NARROW_BY_GENERIC:\n\t\t\t\t\treturn 1;\n\n\t\t\t\tcase EQUAL:\n\t\t\t\tdefault:\n\t\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeCompareEnum.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\npublic enum TypeCompareEnum {\n\tEQUAL,\n\tNARROW,\n\tNARROW_BY_GENERIC, // same basic type with generic\n\tWIDER,\n\tWIDER_BY_GENERIC, // same basic type without generic\n\tCONFLICT,\n\tCONFLICT_BY_GENERIC, // same basic type, conflict in generics\n\tUNKNOWN;\n\n\tpublic TypeCompareEnum invert() {\n\t\tswitch (this) {\n\t\t\tcase NARROW:\n\t\t\t\treturn WIDER;\n\n\t\t\tcase NARROW_BY_GENERIC:\n\t\t\t\treturn WIDER_BY_GENERIC;\n\n\t\t\tcase WIDER:\n\t\t\t\treturn NARROW;\n\n\t\t\tcase WIDER_BY_GENERIC:\n\t\t\t\treturn NARROW_BY_GENERIC;\n\n\t\t\tcase CONFLICT:\n\t\t\tcase CONFLICT_BY_GENERIC:\n\t\t\tcase EQUAL:\n\t\t\tcase UNKNOWN:\n\t\t\tdefault:\n\t\t\t\treturn this;\n\t\t}\n\t}\n\n\tpublic boolean isEqual() {\n\t\treturn this == EQUAL;\n\t}\n\n\tpublic boolean isWider() {\n\t\treturn this == WIDER || this == WIDER_BY_GENERIC;\n\t}\n\n\tpublic boolean isWiderOrEqual() {\n\t\treturn isEqual() || isWider();\n\t}\n\n\tpublic boolean isNarrow() {\n\t\treturn this == NARROW || this == NARROW_BY_GENERIC;\n\t}\n\n\tpublic boolean isNarrowOrEqual() {\n\t\treturn isEqual() || isNarrow();\n\t}\n\n\tpublic boolean isConflict() {\n\t\treturn this == CONFLICT || this == CONFLICT_BY_GENERIC;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.Consts;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.AnonymousClassAttr;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.instructions.BaseInvokeNode;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.InvokeType;\nimport jadx.core.dex.instructions.PhiInsn;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.instructions.mods.ConstructorInsn;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.nodes.utils.MethodUtils;\nimport jadx.core.dex.trycatch.ExcHandlerAttr;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.AttachMethodDetails;\nimport jadx.core.dex.visitors.ConstInlineVisitor;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.dex.visitors.ssa.SSATransform;\nimport jadx.core.utils.exceptions.JadxOverflowException;\n\n@JadxVisitor(\n\t\tname = \"Type Inference\",\n\t\tdesc = \"Calculate best types for SSA variables\",\n\t\trunAfter = {\n\t\t\t\tSSATransform.class,\n\t\t\t\tConstInlineVisitor.class,\n\t\t\t\tAttachMethodDetails.class\n\t\t}\n)\npublic final class TypeInferenceVisitor extends AbstractVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(TypeInferenceVisitor.class);\n\n\tprivate RootNode root;\n\tprivate TypeUpdate typeUpdate;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tthis.root = root;\n\t\tthis.typeUpdate = root.getTypeUpdate();\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\tLOG.info(\"Start type inference in method: {}\", mth);\n\t\t}\n\t\ttry {\n\t\t\tassignImmutableTypes(mth);\n\t\t\tinitTypeBounds(mth);\n\t\t\trunTypePropagation(mth);\n\t\t} catch (StackOverflowError | BootstrapMethodError e) {\n\t\t\tmth.addError(\"Type inference failed with stack overflow\", new JadxOverflowException(e.getMessage()));\n\t\t} catch (Exception e) {\n\t\t\tmth.addError(\"Type inference failed\", e);\n\t\t}\n\t}\n\n\t/**\n\t * Collect initial type bounds from assign and usages\n\t */\n\tvoid initTypeBounds(MethodNode mth) {\n\t\tList<SSAVar> ssaVars = mth.getSVars();\n\t\tssaVars.forEach(this::attachBounds);\n\t\tssaVars.forEach(this::mergePhiBounds);\n\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\tssaVars.stream().sorted()\n\t\t\t\t\t.forEach(ssaVar -> LOG.debug(\"Type bounds for {}: {}\", ssaVar.toShortString(), ssaVar.getTypeInfo().getBounds()));\n\t\t}\n\t}\n\n\t/**\n\t * Guess type from usage and try to set it to current variable\n\t * and all connected instructions with {@link TypeUpdate#apply(MethodNode, SSAVar, ArgType)}\n\t */\n\tboolean runTypePropagation(MethodNode mth) {\n\t\tList<SSAVar> ssaVars = mth.getSVars();\n\t\tssaVars.forEach(var -> setImmutableType(mth, var));\n\t\tssaVars.forEach(var -> setBestType(mth, var));\n\t\treturn true;\n\t}\n\n\tprivate void setImmutableType(MethodNode mth, SSAVar ssaVar) {\n\t\ttry {\n\t\t\tArgType immutableType = ssaVar.getImmutableType();\n\t\t\tif (immutableType != null) {\n\t\t\t\tTypeUpdateResult result = typeUpdate.applyWithWiderIgnSame(mth, ssaVar, immutableType);\n\t\t\t\tif (Consts.DEBUG_TYPE_INFERENCE && result == TypeUpdateResult.REJECT) {\n\t\t\t\t\tLOG.info(\"Reject initial immutable type {} for {}\", immutableType, ssaVar);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (JadxOverflowException e) {\n\t\t\tthrow e;\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Failed to set immutable type for var: \" + ssaVar, e);\n\t\t}\n\t}\n\n\tprivate void setBestType(MethodNode mth, SSAVar ssaVar) {\n\t\ttry {\n\t\t\tcalculateFromBounds(mth, ssaVar);\n\t\t} catch (JadxOverflowException e) {\n\t\t\tthrow e;\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Failed to calculate best type for var: \" + ssaVar, e);\n\t\t}\n\t}\n\n\tprivate void calculateFromBounds(MethodNode mth, SSAVar ssaVar) {\n\t\tTypeInfo typeInfo = ssaVar.getTypeInfo();\n\t\tSet<ITypeBound> bounds = typeInfo.getBounds();\n\t\tOptional<ArgType> bestTypeOpt = selectBestTypeFromBounds(bounds);\n\t\tif (bestTypeOpt.isEmpty()) {\n\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\tLOG.warn(\"Failed to select best type from bounds, count={} : \", bounds.size());\n\t\t\t\tfor (ITypeBound bound : bounds) {\n\t\t\t\t\tLOG.warn(\"  {}\", bound);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tArgType candidateType = bestTypeOpt.get();\n\t\tTypeUpdateResult result = typeUpdate.apply(mth, ssaVar, candidateType);\n\t\tif (Consts.DEBUG_TYPE_INFERENCE && result == TypeUpdateResult.REJECT) {\n\t\t\tif (ssaVar.getTypeInfo().getType().equals(candidateType)) {\n\t\t\t\tLOG.info(\"Same type rejected: {} -> {}, bounds: {}\", ssaVar, candidateType, bounds);\n\t\t\t} else if (candidateType.isTypeKnown()) {\n\t\t\t\tLOG.debug(\"Type rejected: {} -> {}, bounds: {}\", ssaVar, candidateType, bounds);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate Optional<ArgType> selectBestTypeFromBounds(Set<ITypeBound> bounds) {\n\t\treturn bounds.stream()\n\t\t\t\t.map(ITypeBound::getType)\n\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t.max(typeUpdate.getTypeCompare().getComparator());\n\t}\n\n\tprivate void attachBounds(SSAVar var) {\n\t\tTypeInfo typeInfo = var.getTypeInfo();\n\t\ttypeInfo.getBounds().clear();\n\t\tRegisterArg assign = var.getAssign();\n\t\taddAssignBound(typeInfo, assign);\n\n\t\tfor (RegisterArg regArg : var.getUseList()) {\n\t\t\taddBound(typeInfo, makeUseBound(regArg));\n\t\t}\n\t}\n\n\tprivate void mergePhiBounds(SSAVar ssaVar) {\n\t\tfor (PhiInsn usedInPhi : ssaVar.getUsedInPhi()) {\n\t\t\tSet<ITypeBound> bounds = ssaVar.getTypeInfo().getBounds();\n\t\t\tbounds.addAll(usedInPhi.getResult().getSVar().getTypeInfo().getBounds());\n\t\t\tfor (InsnArg arg : usedInPhi.getArguments()) {\n\t\t\t\tbounds.addAll(((RegisterArg) arg).getSVar().getTypeInfo().getBounds());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void addBound(TypeInfo typeInfo, ITypeBound bound) {\n\t\tif (bound == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (bound instanceof ITypeBoundDynamic\n\t\t\t\t|| bound.getType() != ArgType.UNKNOWN) {\n\t\t\ttypeInfo.getBounds().add(bound);\n\t\t}\n\t}\n\n\tprivate void addAssignBound(TypeInfo typeInfo, RegisterArg assign) {\n\t\tArgType immutableType = assign.getImmutableType();\n\t\tif (immutableType != null) {\n\t\t\taddBound(typeInfo, new TypeBoundConst(BoundEnum.ASSIGN, immutableType));\n\t\t\treturn;\n\t\t}\n\t\tInsnNode insn = assign.getParentInsn();\n\t\tif (insn == null || insn.getResult() == null) {\n\t\t\taddBound(typeInfo, new TypeBoundConst(BoundEnum.ASSIGN, assign.getInitType()));\n\t\t\treturn;\n\t\t}\n\t\tswitch (insn.getType()) {\n\t\t\tcase NEW_INSTANCE:\n\t\t\t\tArgType clsType = (ArgType) ((IndexInsnNode) insn).getIndex();\n\t\t\t\taddBound(typeInfo, new TypeBoundConst(BoundEnum.ASSIGN, clsType));\n\t\t\t\tbreak;\n\n\t\t\tcase CONSTRUCTOR:\n\t\t\t\tArgType ctrClsType = replaceAnonymousType((ConstructorInsn) insn);\n\t\t\t\taddBound(typeInfo, new TypeBoundConst(BoundEnum.ASSIGN, ctrClsType));\n\t\t\t\tbreak;\n\n\t\t\tcase CONST:\n\t\t\t\tLiteralArg constLit = (LiteralArg) insn.getArg(0);\n\t\t\t\taddBound(typeInfo, new TypeBoundConst(BoundEnum.ASSIGN, constLit.getType()));\n\t\t\t\tbreak;\n\n\t\t\tcase MOVE_EXCEPTION:\n\t\t\t\tExcHandlerAttr excHandlerAttr = insn.get(AType.EXC_HANDLER);\n\t\t\t\tif (excHandlerAttr != null) {\n\t\t\t\t\tfor (ClassInfo catchType : excHandlerAttr.getHandler().getCatchTypes()) {\n\t\t\t\t\t\taddBound(typeInfo, new TypeBoundConst(BoundEnum.ASSIGN, catchType.getType()));\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\taddBound(typeInfo, new TypeBoundConst(BoundEnum.ASSIGN, insn.getResult().getInitType()));\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase INVOKE:\n\t\t\t\taddBound(typeInfo, makeAssignInvokeBound((InvokeNode) insn));\n\t\t\t\tbreak;\n\n\t\t\tcase IGET:\n\t\t\t\taddBound(typeInfo, makeAssignFieldGetBound((IndexInsnNode) insn));\n\t\t\t\tbreak;\n\n\t\t\tcase CHECK_CAST:\n\t\t\t\tif (insn.contains(AFlag.SOFT_CAST)) {\n\t\t\t\t\t// ignore bound, will run checks on update\n\t\t\t\t} else {\n\t\t\t\t\taddBound(typeInfo, new TypeBoundCheckCastAssign(root, (IndexInsnNode) insn));\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tArgType type = insn.getResult().getInitType();\n\t\t\t\taddBound(typeInfo, new TypeBoundConst(BoundEnum.ASSIGN, type));\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate ArgType replaceAnonymousType(ConstructorInsn ctr) {\n\t\tif (ctr.isNewInstance()) {\n\t\t\tClassNode ctrCls = root.resolveClass(ctr.getClassType());\n\t\t\tif (ctrCls != null && ctrCls.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\tAnonymousClassAttr baseTypeAttr = ctrCls.get(AType.ANONYMOUS_CLASS);\n\t\t\t\tif (baseTypeAttr != null && baseTypeAttr.getInlineType() == AnonymousClassAttr.InlineType.CONSTRUCTOR) {\n\t\t\t\t\treturn baseTypeAttr.getBaseType();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn ctr.getClassType().getType();\n\t}\n\n\tprivate ITypeBound makeAssignFieldGetBound(IndexInsnNode insn) {\n\t\tArgType initType = insn.getResult().getInitType();\n\t\tif (initType.containsTypeVariable()) {\n\t\t\treturn new TypeBoundFieldGetAssign(root, insn, initType);\n\t\t}\n\t\treturn new TypeBoundConst(BoundEnum.ASSIGN, initType);\n\t}\n\n\tprivate ITypeBound makeAssignInvokeBound(InvokeNode invokeNode) {\n\t\tArgType boundType = invokeNode.getCallMth().getReturnType();\n\t\tArgType genericReturnType = root.getMethodUtils().getMethodGenericReturnType(invokeNode);\n\t\tif (genericReturnType != null) {\n\t\t\tif (genericReturnType.containsTypeVariable()) {\n\t\t\t\tInvokeType invokeType = invokeNode.getInvokeType();\n\t\t\t\tif (invokeNode.getArgsCount() != 0\n\t\t\t\t\t\t&& invokeType != InvokeType.STATIC && invokeType != InvokeType.SUPER) {\n\t\t\t\t\treturn new TypeBoundInvokeAssign(root, invokeNode, genericReturnType);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tboundType = genericReturnType;\n\t\t\t}\n\t\t}\n\t\treturn new TypeBoundConst(BoundEnum.ASSIGN, boundType);\n\t}\n\n\t@Nullable\n\tprivate ITypeBound makeUseBound(RegisterArg regArg) {\n\t\tInsnNode insn = regArg.getParentInsn();\n\t\tif (insn == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (insn instanceof BaseInvokeNode) {\n\t\t\tITypeBound invokeUseBound = makeInvokeUseBound(regArg, (BaseInvokeNode) insn);\n\t\t\tif (invokeUseBound != null) {\n\t\t\t\treturn invokeUseBound;\n\t\t\t}\n\t\t}\n\t\tif (insn.getType() == InsnType.CHECK_CAST && insn.contains(AFlag.SOFT_CAST)) {\n\t\t\t// ignore\n\t\t\treturn null;\n\t\t}\n\t\treturn new TypeBoundConst(BoundEnum.USE, regArg.getInitType(), regArg);\n\t}\n\n\tprivate ITypeBound makeInvokeUseBound(RegisterArg regArg, BaseInvokeNode invoke) {\n\t\tInsnArg instanceArg = invoke.getInstanceArg();\n\t\tif (instanceArg == null) {\n\t\t\treturn null;\n\t\t}\n\t\tMethodUtils methodUtils = root.getMethodUtils();\n\t\tIMethodDetails methodDetails = methodUtils.getMethodDetails(invoke);\n\t\tif (methodDetails == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (instanceArg != regArg) {\n\t\t\tint argIndex = invoke.getArgIndex(regArg) - invoke.getFirstArgOffset();\n\t\t\tArgType argType = methodDetails.getArgTypes().get(argIndex);\n\t\t\tif (!argType.containsTypeVariable()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn new TypeBoundInvokeUse(root, invoke, regArg, argType);\n\t\t}\n\n\t\t// for override methods use origin declared class as a type\n\t\tif (methodDetails instanceof MethodNode) {\n\t\t\tMethodNode callMth = (MethodNode) methodDetails;\n\t\t\tClassInfo declCls = methodUtils.getMethodOriginDeclClass(callMth);\n\t\t\treturn new TypeBoundConst(BoundEnum.USE, declCls.getType(), regArg);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate void assignImmutableTypes(MethodNode mth) {\n\t\tfor (SSAVar ssaVar : mth.getSVars()) {\n\t\t\tArgType immutableType = getSsaImmutableType(ssaVar);\n\t\t\tif (immutableType != null) {\n\t\t\t\tssaVar.markAsImmutable(immutableType);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate static ArgType getSsaImmutableType(SSAVar ssaVar) {\n\t\tif (ssaVar.getAssign().contains(AFlag.IMMUTABLE_TYPE)) {\n\t\t\treturn ssaVar.getAssign().getInitType();\n\t\t}\n\t\tfor (RegisterArg reg : ssaVar.getUseList()) {\n\t\t\tif (reg.contains(AFlag.IMMUTABLE_TYPE)) {\n\t\t\t\treturn reg.getInitType();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"TypeInferenceVisitor\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInfo.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport java.util.LinkedHashSet;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.core.dex.instructions.args.ArgType;\n\npublic class TypeInfo {\n\tprivate ArgType type = ArgType.UNKNOWN;\n\n\tprivate final Set<ITypeBound> bounds = new LinkedHashSet<>();\n\n\t@NotNull\n\tpublic ArgType getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setType(ArgType type) {\n\t\tthis.type = type;\n\t}\n\n\tpublic Set<ITypeBound> getBounds() {\n\t\treturn bounds;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"TypeInfo{type=\" + type + \", bounds=\" + bounds + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeSearch.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.Consts;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.PrimitiveType;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\n\n/**\n * Slow and memory consuming multi-variable type search algorithm.\n * Used only if fast type propagation is failed for some variables.\n * <p>\n * Stages description:\n * - find all possible candidate types within bounds\n * - build dynamic constraint list for every variable\n * - run search by checking all candidates\n */\npublic class TypeSearch {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(TypeSearch.class);\n\n\tprivate static final int VARS_PROCESS_LIMIT = 5_000;\n\tprivate static final int CANDIDATES_COUNT_LIMIT = 10;\n\tprivate static final int SEARCH_ITERATION_LIMIT = 1_000_000;\n\n\tprivate final MethodNode mth;\n\tprivate final TypeSearchState state;\n\tprivate final TypeCompare typeCompare;\n\tprivate final TypeUpdate typeUpdate;\n\n\tpublic TypeSearch(MethodNode mth) {\n\t\tthis.mth = mth;\n\t\tthis.state = new TypeSearchState(mth);\n\t\tthis.typeUpdate = mth.root().getTypeUpdate();\n\t\tthis.typeCompare = typeUpdate.getTypeCompare();\n\t}\n\n\tpublic boolean run() {\n\t\tif (mth.getSVars().size() > VARS_PROCESS_LIMIT) {\n\t\t\tmth.addWarnComment(\"Multi-variable search skipped. Vars limit reached: \" + mth.getSVars().size()\n\t\t\t\t\t+ \" (expected less than \" + VARS_PROCESS_LIMIT + \")\");\n\t\t\treturn false;\n\t\t}\n\t\tmth.getSVars().forEach(this::fillTypeCandidates);\n\t\tmth.getSVars().forEach(this::collectConstraints);\n\n\t\t// quick search for variables without dependencies\n\t\tstate.getUnresolvedVars().forEach(this::resolveIndependentVariables);\n\n\t\tboolean searchSuccess;\n\t\tList<TypeSearchVarInfo> vars = state.getUnresolvedVars();\n\t\tif (vars.isEmpty()) {\n\t\t\tsearchSuccess = true;\n\t\t} else {\n\t\t\tsearchSuccess = search(vars) && fullCheck(vars);\n\t\t\tif (Consts.DEBUG_TYPE_INFERENCE && !searchSuccess) {\n\t\t\t\tLOG.debug(\"Multi-variable search failed\");\n\t\t\t}\n\t\t}\n\t\tif (searchSuccess) {\n\t\t\treturn applyResolvedVars();\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate boolean applyResolvedVars() {\n\t\tList<TypeSearchVarInfo> resolvedVars = state.getResolvedVars();\n\t\tList<TypeSearchVarInfo> updatedVars = new ArrayList<>();\n\t\tfor (TypeSearchVarInfo var : resolvedVars) {\n\t\t\tSSAVar ssaVar = var.getVar();\n\t\t\tArgType resolvedType = var.getCurrentType();\n\t\t\tif (!resolvedType.isTypeKnown()) {\n\t\t\t\t// ignore unknown variables\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (resolvedType.equals(ssaVar.getTypeInfo().getType())) {\n\t\t\t\t// type already set\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tssaVar.setType(resolvedType);\n\t\t\tupdatedVars.add(var);\n\t\t}\n\t\tboolean applySuccess = true;\n\t\tfor (TypeSearchVarInfo var : updatedVars) {\n\t\t\tTypeUpdateResult res = typeUpdate.applyWithWiderIgnSame(mth, var.getVar(), var.getCurrentType());\n\t\t\tif (res == TypeUpdateResult.REJECT) {\n\t\t\t\tmth.addDebugComment(\"Multi-variable search result rejected for \" + var);\n\t\t\t\tapplySuccess = false;\n\t\t\t}\n\t\t}\n\t\treturn applySuccess;\n\t}\n\n\tprivate boolean search(List<TypeSearchVarInfo> vars) {\n\t\tint len = vars.size();\n\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\tLOG.debug(\"Run multi-variable search for {} vars: \", len);\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tlong count = 1;\n\t\t\tfor (TypeSearchVarInfo var : vars) {\n\t\t\t\tLOG.debug(\"  {}\", var);\n\t\t\t\tint size = var.getCandidateTypes().size();\n\t\t\t\tsb.append(\" * \").append(size);\n\t\t\t\tcount *= size;\n\t\t\t}\n\t\t\tsb.append(\" = \").append(count);\n\t\t\tLOG.debug(\" max iterations count = {}\", sb);\n\t\t}\n\n\t\t// prepare vars\n\t\tfor (TypeSearchVarInfo var : vars) {\n\t\t\tvar.reset();\n\t\t}\n\t\t// check all types combinations\n\t\tint n = 0;\n\t\tint i = 0;\n\t\twhile (!fullCheck(vars)) {\n\t\t\tTypeSearchVarInfo first = vars.get(i);\n\t\t\tif (first.nextType()) {\n\t\t\t\tint k = i + 1;\n\t\t\t\tif (k >= len) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tTypeSearchVarInfo next = vars.get(k);\n\t\t\t\twhile (true) {\n\t\t\t\t\tif (next.nextType()) {\n\t\t\t\t\t\tk++;\n\t\t\t\t\t\tif (k >= len) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tnext = vars.get(k);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tn++;\n\t\t\tif (n > SEARCH_ITERATION_LIMIT) {\n\t\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\t\tLOG.debug(\" > iterations limit reached: {}\", SEARCH_ITERATION_LIMIT);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\tLOG.debug(\" > done after {} iterations\", n);\n\t\t}\n\t\t// mark all vars as resolved\n\t\tfor (TypeSearchVarInfo var : vars) {\n\t\t\tvar.setTypeResolved(true);\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate boolean resolveIndependentVariables(TypeSearchVarInfo varInfo) {\n\t\tboolean allRelatedVarsResolved = varInfo.getConstraints().stream()\n\t\t\t\t.flatMap(c -> c.getRelatedVars().stream())\n\t\t\t\t.allMatch(v -> state.getVarInfo(v).isTypeResolved());\n\t\tif (!allRelatedVarsResolved) {\n\t\t\treturn false;\n\t\t}\n\t\t// variable is independent, run single search\n\t\tvarInfo.reset();\n\t\tdo {\n\t\t\tif (singleCheck(varInfo)) {\n\t\t\t\tvarInfo.setTypeResolved(true);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} while (!varInfo.nextType());\n\n\t\treturn false;\n\t}\n\n\tprivate boolean fullCheck(List<TypeSearchVarInfo> vars) {\n\t\tfor (TypeSearchVarInfo var : vars) {\n\t\t\tif (!singleCheck(var)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate boolean singleCheck(TypeSearchVarInfo var) {\n\t\tif (var.isTypeResolved()) {\n\t\t\treturn true;\n\t\t}\n\t\tfor (ITypeConstraint constraint : var.getConstraints()) {\n\t\t\tif (!constraint.check(state)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate void fillTypeCandidates(SSAVar ssaVar) {\n\t\tTypeSearchVarInfo varInfo = state.getVarInfo(ssaVar);\n\t\tArgType immutableType = ssaVar.getImmutableType();\n\t\tif (immutableType != null) {\n\t\t\tvarInfo.markResolved(immutableType);\n\t\t\treturn;\n\t\t}\n\t\tArgType currentType = ssaVar.getTypeInfo().getType();\n\t\tif (currentType.isTypeKnown()) {\n\t\t\tvarInfo.markResolved(currentType);\n\t\t\treturn;\n\t\t}\n\n\t\tSet<ArgType> assigns = new LinkedHashSet<>();\n\t\tSet<ArgType> uses = new LinkedHashSet<>();\n\t\tSet<ITypeBound> bounds = ssaVar.getTypeInfo().getBounds();\n\t\tfor (ITypeBound bound : bounds) {\n\t\t\tif (bound.getBound() == BoundEnum.ASSIGN) {\n\t\t\t\tassigns.add(bound.getType());\n\t\t\t} else {\n\t\t\t\tuses.add(bound.getType());\n\t\t\t}\n\t\t}\n\n\t\tSet<ArgType> candidateTypes = new LinkedHashSet<>();\n\t\taddCandidateTypes(bounds, candidateTypes, assigns);\n\t\taddCandidateTypes(bounds, candidateTypes, uses);\n\n\t\tfor (ArgType assignType : assigns) {\n\t\t\taddCandidateTypes(bounds, candidateTypes, getWiderTypes(assignType));\n\t\t}\n\t\tfor (ArgType useType : uses) {\n\t\t\taddCandidateTypes(bounds, candidateTypes, getNarrowTypes(useType));\n\t\t}\n\n\t\taddUsageTypeCandidates(ssaVar, bounds, candidateTypes);\n\n\t\tint size = candidateTypes.size();\n\t\tif (size == 0) {\n\t\t\tvarInfo.setTypeResolved(true);\n\t\t\tvarInfo.setCurrentType(ArgType.UNKNOWN);\n\t\t\tvarInfo.setCandidateTypes(Collections.emptyList());\n\t\t} else if (size == 1) {\n\t\t\tvarInfo.setTypeResolved(true);\n\t\t\tvarInfo.setCurrentType(candidateTypes.iterator().next());\n\t\t\tvarInfo.setCandidateTypes(Collections.emptyList());\n\t\t} else {\n\t\t\tvarInfo.setTypeResolved(false);\n\t\t\tvarInfo.setCurrentType(ArgType.UNKNOWN);\n\t\t\tList<ArgType> types = new ArrayList<>(candidateTypes);\n\t\t\ttypes.sort(typeCompare.getReversedComparator());\n\t\t\tvarInfo.setCandidateTypes(Collections.unmodifiableList(types));\n\t\t}\n\t}\n\n\tprivate void addUsageTypeCandidates(SSAVar ssaVar, Set<ITypeBound> bounds, Set<ArgType> candidateTypes) {\n\t\tfor (RegisterArg useArg : ssaVar.getUseList()) {\n\t\t\tInsnNode parentInsn = useArg.getParentInsn();\n\t\t\tif (parentInsn != null) {\n\t\t\t\tInsnType insnType = parentInsn.getType();\n\t\t\t\tif (insnType == InsnType.APUT) {\n\t\t\t\t\tArgType aputType = parentInsn.getArg(2).getType();\n\t\t\t\t\tif (aputType.isTypeKnown()) {\n\t\t\t\t\t\taddCandidateType(bounds, candidateTypes, ArgType.array(aputType));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void addCandidateTypes(Set<ITypeBound> bounds, Set<ArgType> collectedTypes, Collection<ArgType> candidateTypes) {\n\t\tfor (ArgType candidateType : candidateTypes) {\n\t\t\tif (addCandidateType(bounds, collectedTypes, candidateType)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate boolean addCandidateType(Set<ITypeBound> bounds, Set<ArgType> collectedTypes, ArgType candidateType) {\n\t\tif (candidateType.isTypeKnown() && typeUpdate.inBounds(bounds, candidateType)) {\n\t\t\tcollectedTypes.add(candidateType);\n\t\t\tif (collectedTypes.size() > CANDIDATES_COUNT_LIMIT) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate List<ArgType> getWiderTypes(ArgType type) {\n\t\tif (type.isTypeKnown()) {\n\t\t\tif (type.isObject()) {\n\t\t\t\tSet<String> ancestors = mth.root().getClsp().getSuperTypes(type.getObject());\n\t\t\t\treturn ancestors.stream().map(ArgType::object).collect(Collectors.toList());\n\t\t\t}\n\t\t} else {\n\t\t\treturn expandUnknownType(type);\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\tprivate List<ArgType> getNarrowTypes(ArgType type) {\n\t\tif (type.isTypeKnown()) {\n\t\t\tif (type.isObject()) {\n\t\t\t\tif (type.equals(ArgType.OBJECT)) {\n\t\t\t\t\t// a lot of objects to return\n\t\t\t\t\treturn Collections.singletonList(ArgType.OBJECT);\n\t\t\t\t}\n\t\t\t\tList<String> impList = mth.root().getClsp().getImplementations(type.getObject());\n\t\t\t\treturn impList.stream().map(ArgType::object).collect(Collectors.toList());\n\t\t\t}\n\t\t} else {\n\t\t\treturn expandUnknownType(type);\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\tprivate List<ArgType> expandUnknownType(ArgType type) {\n\t\tList<ArgType> list = new ArrayList<>();\n\t\tfor (PrimitiveType possibleType : type.getPossibleTypes()) {\n\t\t\tlist.add(ArgType.convertFromPrimitiveType(possibleType));\n\t\t}\n\t\treturn list;\n\t}\n\n\tprivate void collectConstraints(SSAVar var) {\n\t\tTypeSearchVarInfo varInfo = state.getVarInfo(var);\n\t\tif (varInfo.isTypeResolved()) {\n\t\t\tvarInfo.setConstraints(Collections.emptyList());\n\t\t\treturn;\n\t\t}\n\t\tvarInfo.setConstraints(new ArrayList<>());\n\t\taddConstraint(varInfo, makeConstraint(var.getAssign()));\n\t\tfor (RegisterArg regArg : var.getUseList()) {\n\t\t\taddConstraint(varInfo, makeConstraint(regArg));\n\t\t}\n\t}\n\n\tprivate void addConstraint(TypeSearchVarInfo varInfo, ITypeConstraint constraint) {\n\t\tif (constraint != null) {\n\t\t\tvarInfo.getConstraints().add(constraint);\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate ITypeConstraint makeConstraint(RegisterArg arg) {\n\t\tInsnNode insn = arg.getParentInsn();\n\t\tif (insn == null || arg.isTypeImmutable()) {\n\t\t\treturn null;\n\t\t}\n\t\tswitch (insn.getType()) {\n\t\t\tcase MOVE:\n\t\t\t\treturn makeMoveConstraint(insn, arg);\n\n\t\t\tcase PHI:\n\t\t\t\treturn makePhiConstraint(insn, arg);\n\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate ITypeConstraint makeMoveConstraint(InsnNode insn, RegisterArg arg) {\n\t\tif (!insn.getArg(0).isRegister()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new AbstractTypeConstraint(insn, arg) {\n\t\t\t@Override\n\t\t\tpublic boolean check(TypeSearchState state) {\n\t\t\t\tArgType resType = state.getArgType(insn.getResult());\n\t\t\t\tArgType argType = state.getArgType(insn.getArg(0));\n\t\t\t\tTypeCompareEnum res = typeCompare.compareTypes(resType, argType);\n\t\t\t\treturn res.isEqual() || res.isWider();\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate ITypeConstraint makePhiConstraint(InsnNode insn, RegisterArg arg) {\n\t\treturn new AbstractTypeConstraint(insn, arg) {\n\t\t\t@Override\n\t\t\tpublic boolean check(TypeSearchState state) {\n\t\t\t\tArgType resType = state.getArgType(insn.getResult());\n\t\t\t\tfor (InsnArg insnArg : insn.getArguments()) {\n\t\t\t\t\tArgType argType = state.getArgType(insnArg);\n\t\t\t\t\tif (!argType.equals(resType)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeSearchState.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class TypeSearchState {\n\n\tprivate final Map<SSAVar, TypeSearchVarInfo> varInfoMap;\n\n\tpublic TypeSearchState(MethodNode mth) {\n\t\tList<SSAVar> vars = mth.getSVars();\n\t\tthis.varInfoMap = new LinkedHashMap<>(vars.size());\n\t\tfor (SSAVar var : vars) {\n\t\t\tvarInfoMap.put(var, new TypeSearchVarInfo(var));\n\t\t}\n\t}\n\n\t@NotNull\n\tpublic TypeSearchVarInfo getVarInfo(SSAVar var) {\n\t\tTypeSearchVarInfo varInfo = this.varInfoMap.get(var);\n\t\tif (varInfo == null) {\n\t\t\tthrow new JadxRuntimeException(\"TypeSearchVarInfo not found in map for var: \" + var);\n\t\t}\n\t\treturn varInfo;\n\t}\n\n\tpublic ArgType getArgType(InsnArg arg) {\n\t\tif (arg.isRegister()) {\n\t\t\tRegisterArg reg = (RegisterArg) arg;\n\t\t\treturn getVarInfo(reg.getSVar()).getCurrentType();\n\t\t}\n\t\treturn arg.getType();\n\t}\n\n\tpublic List<TypeSearchVarInfo> getAllVars() {\n\t\treturn new ArrayList<>(varInfoMap.values());\n\t}\n\n\tpublic List<TypeSearchVarInfo> getUnresolvedVars() {\n\t\treturn varInfoMap.values().stream()\n\t\t\t\t.filter(varInfo -> !varInfo.isTypeResolved())\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tpublic List<TypeSearchVarInfo> getResolvedVars() {\n\t\treturn varInfoMap.values().stream()\n\t\t\t\t.filter(TypeSearchVarInfo::isTypeResolved)\n\t\t\t\t.collect(Collectors.toList());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeSearchVarInfo.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.SSAVar;\n\npublic class TypeSearchVarInfo {\n\tprivate final SSAVar var;\n\tprivate boolean typeResolved;\n\tprivate ArgType currentType;\n\tprivate List<ArgType> candidateTypes;\n\tprivate int currentIndex = -1;\n\tprivate List<ITypeConstraint> constraints;\n\n\tpublic TypeSearchVarInfo(SSAVar var) {\n\t\tthis.var = var;\n\t}\n\n\tpublic void markResolved(ArgType type) {\n\t\tthis.currentType = type;\n\t\tthis.typeResolved = true;\n\t\tthis.candidateTypes = Collections.emptyList();\n\t}\n\n\tpublic void reset() {\n\t\tif (typeResolved) {\n\t\t\treturn;\n\t\t}\n\t\tcurrentIndex = 0;\n\t\tcurrentType = candidateTypes.get(0);\n\t}\n\n\t/**\n\t * Switch {@code currentType} to next candidate\n\t *\n\t * @return true - if this is the first candidate\n\t */\n\tpublic boolean nextType() {\n\t\tif (typeResolved) {\n\t\t\treturn false;\n\t\t}\n\t\tint len = candidateTypes.size();\n\t\tcurrentIndex = (currentIndex + 1) % len;\n\t\tcurrentType = candidateTypes.get(currentIndex);\n\t\treturn currentIndex == 0;\n\t}\n\n\tpublic SSAVar getVar() {\n\t\treturn var;\n\t}\n\n\tpublic boolean isTypeResolved() {\n\t\treturn typeResolved;\n\t}\n\n\tpublic void setTypeResolved(boolean typeResolved) {\n\t\tthis.typeResolved = typeResolved;\n\t}\n\n\tpublic ArgType getCurrentType() {\n\t\treturn currentType;\n\t}\n\n\tpublic void setCurrentType(ArgType currentType) {\n\t\tthis.currentType = currentType;\n\t}\n\n\tpublic List<ArgType> getCandidateTypes() {\n\t\treturn candidateTypes;\n\t}\n\n\tpublic void setCandidateTypes(List<ArgType> candidateTypes) {\n\t\tthis.candidateTypes = candidateTypes;\n\t}\n\n\tpublic List<ITypeConstraint> getConstraints() {\n\t\treturn constraints;\n\t}\n\n\tpublic void setConstraints(List<ITypeConstraint> constraints) {\n\t\tthis.constraints = constraints;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(var.toShortString());\n\t\tif (typeResolved) {\n\t\t\tsb.append(\", resolved type: \").append(currentType);\n\t\t} else {\n\t\t\tsb.append(\", currentType=\").append(currentType);\n\t\t\tsb.append(\", candidateTypes=\").append(candidateTypes);\n\t\t\tsb.append(\", constraints=\").append(constraints);\n\t\t}\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdate.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport java.util.EnumMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxArgs;\nimport jadx.core.Consts;\nimport jadx.core.clsp.ClspClass;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.ArithNode;\nimport jadx.core.dex.instructions.BaseInvokeNode;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.PrimitiveType;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.nodes.utils.TypeUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.dex.visitors.typeinference.TypeUpdateResult.CHANGED;\nimport static jadx.core.dex.visitors.typeinference.TypeUpdateResult.REJECT;\nimport static jadx.core.dex.visitors.typeinference.TypeUpdateResult.SAME;\n\npublic final class TypeUpdate {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(TypeUpdate.class);\n\n\tprivate final RootNode root;\n\tprivate final Map<InsnType, ITypeListener> listenerRegistry;\n\tprivate final TypeCompare comparator;\n\tprivate final JadxArgs args;\n\n\tpublic TypeUpdate(RootNode root) {\n\t\tthis.root = root;\n\t\tthis.args = root.getArgs();\n\t\tthis.listenerRegistry = initListenerRegistry();\n\t\tthis.comparator = new TypeCompare(root);\n\t}\n\n\t/**\n\t * Perform recursive type checking and type propagation for all related variables\n\t */\n\tpublic TypeUpdateResult apply(MethodNode mth, SSAVar ssaVar, ArgType candidateType) {\n\t\treturn apply(mth, ssaVar, candidateType, TypeUpdateFlags.FLAGS_EMPTY);\n\t}\n\n\t/**\n\t * Allow wider types for apply from debug info and some special cases\n\t */\n\tpublic TypeUpdateResult applyWithWiderAllow(MethodNode mth, SSAVar ssaVar, ArgType candidateType) {\n\t\treturn apply(mth, ssaVar, candidateType, TypeUpdateFlags.FLAGS_WIDER);\n\t}\n\n\t/**\n\t * Force type setting\n\t */\n\tpublic TypeUpdateResult applyWithWiderIgnSame(MethodNode mth, SSAVar ssaVar, ArgType candidateType) {\n\t\treturn apply(mth, ssaVar, candidateType, TypeUpdateFlags.FLAGS_WIDER_IGNORE_SAME);\n\t}\n\n\tpublic TypeUpdateResult applyDebugInfo(MethodNode mth, SSAVar ssaVar, ArgType candidateType) {\n\t\treturn apply(mth, ssaVar, candidateType, TypeUpdateFlags.FLAGS_APPLY_DEBUG);\n\t}\n\n\tprivate TypeUpdateResult apply(MethodNode mth, SSAVar ssaVar, ArgType candidateType, TypeUpdateFlags flags) {\n\t\ttry {\n\t\t\tif (candidateType == null || !candidateType.isTypeKnown()) {\n\t\t\t\treturn REJECT;\n\t\t\t}\n\n\t\t\tTypeUpdateInfo updateInfo = new TypeUpdateInfo(mth, flags, args);\n\t\t\tTypeUpdateResult result = updateTypeChecked(updateInfo, ssaVar.getAssign(), candidateType);\n\t\t\tif (result == REJECT) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tif (updateInfo.isEmpty()) {\n\t\t\t\treturn SAME;\n\t\t\t}\n\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\tLOG.debug(\"Applying type {} to {}:\", candidateType, ssaVar.toShortString());\n\t\t\t\tupdateInfo.getSortedUpdates().forEach(upd -> LOG.debug(\"  {} -> {} in {}\",\n\t\t\t\t\t\tupd.getType(), upd.getArg().toShortString(), upd.getArg().getParentInsn()));\n\t\t\t}\n\t\t\tupdateInfo.applyUpdates();\n\t\t\treturn CHANGED;\n\t\t} catch (Exception e) {\n\t\t\tmth.addWarnComment(\"Type update failed for variable: \" + ssaVar + \", new type: \" + candidateType, e);\n\t\t\treturn REJECT;\n\t\t}\n\t}\n\n\tprivate TypeUpdateResult updateTypeChecked(TypeUpdateInfo updateInfo, InsnArg arg, ArgType candidateType) {\n\t\tif (candidateType == null) {\n\t\t\tthrow new JadxRuntimeException(\"Null type update for arg: \" + arg);\n\t\t}\n\t\tif (updateInfo.isProcessed(arg)) {\n\t\t\treturn CHANGED;\n\t\t}\n\t\tTypeUpdateResult res = verifyType(updateInfo, arg, candidateType);\n\t\tif (res != null) {\n\t\t\treturn res;\n\t\t}\n\t\tif (arg instanceof RegisterArg) {\n\t\t\tRegisterArg reg = (RegisterArg) arg;\n\t\t\treturn updateTypeForSsaVar(updateInfo, reg.getSVar(), candidateType);\n\t\t}\n\t\treturn requestUpdate(updateInfo, arg, candidateType);\n\t}\n\n\tprivate @Nullable TypeUpdateResult verifyType(TypeUpdateInfo updateInfo, InsnArg arg, ArgType candidateType) {\n\t\tArgType currentType = arg.getType();\n\t\tTypeUpdateFlags typeUpdateFlags = updateInfo.getFlags();\n\t\tif (Objects.equals(currentType, candidateType)) {\n\t\t\tif (!typeUpdateFlags.isIgnoreSame()) {\n\t\t\t\treturn SAME;\n\t\t\t}\n\t\t} else {\n\t\t\tif (candidateType.isWildcard()) {\n\t\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\t\tLOG.debug(\"Wildcard type rejected for {}: candidate={}, current={}\", arg, candidateType, currentType);\n\t\t\t\t}\n\t\t\t\treturn REJECT;\n\t\t\t}\n\n\t\t\tTypeCompareEnum compareResult = comparator.compareTypes(candidateType, currentType);\n\t\t\tif (compareResult.isConflict()) {\n\t\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\t\tLOG.debug(\"Type rejected for {}: candidate={} in conflict with current={}\", arg, candidateType, currentType);\n\t\t\t\t}\n\t\t\t\treturn REJECT;\n\t\t\t}\n\t\t\tif (compareResult == TypeCompareEnum.UNKNOWN && typeUpdateFlags.isIgnoreUnknown()) {\n\t\t\t\treturn REJECT;\n\t\t\t}\n\t\t\tif (arg.isTypeImmutable() && currentType != ArgType.UNKNOWN) {\n\t\t\t\t// don't changed type\n\t\t\t\tif (compareResult == TypeCompareEnum.EQUAL) {\n\t\t\t\t\treturn SAME;\n\t\t\t\t}\n\t\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\t\tLOG.debug(\"Type rejected for {} due to conflict: candidate={}, current={}\", arg, candidateType, currentType);\n\t\t\t\t}\n\t\t\t\treturn REJECT;\n\t\t\t}\n\t\t\tif (compareResult == TypeCompareEnum.WIDER_BY_GENERIC && typeUpdateFlags.isKeepGenerics()) {\n\t\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\t\tLOG.debug(\"Type rejected for {}: candidate={} is removing generic from current={}\", arg, candidateType, currentType);\n\t\t\t\t}\n\t\t\t\treturn REJECT;\n\t\t\t}\n\t\t\tif (compareResult.isWider() && !typeUpdateFlags.isAllowWider()) {\n\t\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\t\tLOG.debug(\"Type rejected for {}: candidate={} is wider than current={}\", arg, candidateType, currentType);\n\t\t\t\t}\n\t\t\t\treturn REJECT;\n\t\t\t}\n\t\t\tif (candidateType.containsTypeVariable()) {\n\t\t\t\t// reject unknown type vars\n\t\t\t\tArgType unknownTypeVar = root.getTypeUtils().checkForUnknownTypeVars(updateInfo.getMth(), candidateType);\n\t\t\t\tif (unknownTypeVar != null) {\n\t\t\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\t\t\tLOG.debug(\"Type rejected for {}: candidate: '{}' has unknown type var: '{}'\", arg, candidateType, unknownTypeVar);\n\t\t\t\t\t}\n\t\t\t\t\treturn REJECT;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate TypeUpdateResult updateTypeForSsaVar(TypeUpdateInfo updateInfo, SSAVar ssaVar, ArgType candidateType) {\n\t\tTypeInfo typeInfo = ssaVar.getTypeInfo();\n\t\tArgType immutableType = ssaVar.getImmutableType();\n\t\tif (immutableType != null && !Objects.equals(immutableType, candidateType)) {\n\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\tLOG.info(\"Reject change immutable type {} to {} for {}\", immutableType, candidateType, ssaVar);\n\t\t\t}\n\t\t\treturn REJECT;\n\t\t}\n\t\tif (!inBounds(updateInfo, ssaVar, typeInfo.getBounds(), candidateType)) {\n\t\t\treturn REJECT;\n\t\t}\n\t\tTypeUpdateResult result = requestUpdate(updateInfo, ssaVar.getAssign(), candidateType);\n\t\tboolean allSame = result == SAME;\n\t\tif (result != REJECT) {\n\t\t\tList<RegisterArg> useList = ssaVar.getUseList();\n\t\t\tfor (RegisterArg arg : useList) {\n\t\t\t\tresult = requestUpdate(updateInfo, arg, candidateType);\n\t\t\t\tif (result == REJECT) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (result != SAME) {\n\t\t\t\t\tallSame = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (result == REJECT) {\n\t\t\t// rollback update for all registers in current SSA var\n\t\t\tupdateInfo.rollbackUpdate(ssaVar.getAssign());\n\t\t\tssaVar.getUseList().forEach(updateInfo::rollbackUpdate);\n\t\t\treturn REJECT;\n\t\t}\n\t\treturn allSame ? SAME : CHANGED;\n\t}\n\n\tprivate TypeUpdateResult requestUpdate(TypeUpdateInfo updateInfo, InsnArg arg, ArgType candidateType) {\n\t\tif (updateInfo.isProcessed(arg)) {\n\t\t\treturn CHANGED;\n\t\t}\n\t\tupdateInfo.requestUpdate(arg, candidateType);\n\t\tTypeUpdateResult result = runListeners(updateInfo, arg, candidateType);\n\t\tif (result == REJECT) {\n\t\t\tupdateInfo.rollbackUpdate(arg);\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate TypeUpdateResult runListeners(TypeUpdateInfo updateInfo, InsnArg arg, ArgType candidateType) {\n\t\tInsnNode insn = arg.getParentInsn();\n\t\tif (insn == null) {\n\t\t\treturn SAME;\n\t\t}\n\t\tITypeListener listener = listenerRegistry.get(insn.getType());\n\t\tif (listener == null) {\n\t\t\treturn CHANGED;\n\t\t}\n\t\treturn listener.update(updateInfo, insn, arg, candidateType);\n\t}\n\n\tboolean inBounds(Set<ITypeBound> bounds, ArgType candidateType) {\n\t\tfor (ITypeBound bound : bounds) {\n\t\t\tArgType boundType = bound.getType();\n\t\t\tif (boundType != null && !checkBound(candidateType, bound, boundType)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate boolean inBounds(TypeUpdateInfo updateInfo, SSAVar ssaVar, Set<ITypeBound> bounds, ArgType candidateType) {\n\t\tfor (ITypeBound bound : bounds) {\n\t\t\tArgType boundType;\n\t\t\tif (updateInfo != null && bound instanceof ITypeBoundDynamic) {\n\t\t\t\tboundType = ((ITypeBoundDynamic) bound).getType(updateInfo);\n\t\t\t} else {\n\t\t\t\tboundType = bound.getType();\n\t\t\t}\n\t\t\tif (boundType != null && !checkBound(candidateType, bound, boundType)) {\n\t\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\t\tLOG.debug(\"Reject type '{}' for {} by bound: {} from {}\", candidateType, ssaVar, boundType, bound);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate boolean checkBound(ArgType candidateType, ITypeBound bound, ArgType boundType) {\n\t\tTypeCompareEnum compareResult = comparator.compareTypes(candidateType, boundType);\n\t\tswitch (compareResult) {\n\t\t\tcase EQUAL:\n\t\t\t\treturn true;\n\n\t\t\tcase WIDER:\n\t\t\t\treturn bound.getBound() != BoundEnum.USE;\n\n\t\t\tcase NARROW:\n\t\t\t\tif (bound.getBound() == BoundEnum.ASSIGN) {\n\t\t\t\t\treturn !boundType.isTypeKnown() && checkAssignForUnknown(boundType, candidateType);\n\t\t\t\t}\n\t\t\t\treturn true;\n\n\t\t\tcase WIDER_BY_GENERIC:\n\t\t\tcase NARROW_BY_GENERIC:\n\t\t\t\t// allow replace object to same object with known generic type\n\t\t\t\t// due to incomplete information about external methods and fields\n\t\t\t\treturn true;\n\n\t\t\tcase CONFLICT:\n\t\t\tcase CONFLICT_BY_GENERIC:\n\t\t\t\treturn false;\n\n\t\t\tcase UNKNOWN:\n\t\t\t\tLOG.warn(\"Can't compare types, unknown hierarchy: {} and {}\", candidateType, boundType);\n\t\t\t\tcomparator.compareTypes(candidateType, boundType);\n\t\t\t\treturn true;\n\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Not processed type compare enum: \" + compareResult);\n\t\t}\n\t}\n\n\tprivate boolean checkAssignForUnknown(ArgType boundType, ArgType candidateType) {\n\t\tif (boundType == ArgType.UNKNOWN) {\n\t\t\treturn true;\n\t\t}\n\t\tboolean candidateArray = candidateType.isArray();\n\t\tif (boundType.isArray() && candidateArray) {\n\t\t\treturn checkAssignForUnknown(boundType.getArrayElement(), candidateType.getArrayElement());\n\t\t}\n\t\tif (candidateArray && boundType.contains(PrimitiveType.ARRAY)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (candidateType.isObject() && boundType.contains(PrimitiveType.OBJECT)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (candidateType.isPrimitive() && boundType.contains(candidateType.getPrimitiveType())) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate Map<InsnType, ITypeListener> initListenerRegistry() {\n\t\tMap<InsnType, ITypeListener> registry = new EnumMap<>(InsnType.class);\n\t\tregistry.put(InsnType.CONST, this::sameFirstArgListener);\n\t\tregistry.put(InsnType.MOVE, this::moveListener);\n\t\tregistry.put(InsnType.PHI, this::allSameListener);\n\t\tregistry.put(InsnType.AGET, this::arrayGetListener);\n\t\tregistry.put(InsnType.APUT, this::arrayPutListener);\n\t\tregistry.put(InsnType.IF, this::ifListener);\n\t\tregistry.put(InsnType.ARITH, this::arithListener);\n\t\tregistry.put(InsnType.NEG, this::suggestAllSameListener);\n\t\tregistry.put(InsnType.NOT, this::suggestAllSameListener);\n\t\tregistry.put(InsnType.CHECK_CAST, this::checkCastListener);\n\t\tregistry.put(InsnType.INVOKE, this::invokeListener);\n\t\tregistry.put(InsnType.CONSTRUCTOR, this::invokeListener);\n\t\treturn registry;\n\t}\n\n\tprivate TypeUpdateResult invokeListener(TypeUpdateInfo updateInfo, InsnNode insn, InsnArg arg, ArgType candidateType) {\n\t\tBaseInvokeNode invoke = (BaseInvokeNode) insn;\n\t\tif (isAssign(invoke, arg)) {\n\t\t\t// TODO: implement backward type propagation (from result to instance)\n\t\t\treturn SAME;\n\t\t}\n\t\tif (invoke.getInstanceArg() == arg) {\n\t\t\tIMethodDetails methodDetails = root.getMethodUtils().getMethodDetails(invoke);\n\t\t\tif (methodDetails == null) {\n\t\t\t\treturn SAME;\n\t\t\t}\n\t\t\tTypeUtils typeUtils = root.getTypeUtils();\n\t\t\tSet<ArgType> knownTypeVars = typeUtils.getKnownTypeVarsAtMethod(updateInfo.getMth());\n\t\t\tMap<ArgType, ArgType> typeVarsMap = typeUtils.getTypeVariablesMapping(candidateType);\n\n\t\t\tArgType returnType = methodDetails.getReturnType();\n\t\t\tList<ArgType> argTypes = methodDetails.getArgTypes();\n\t\t\tint argsCount = argTypes.size();\n\t\t\tif (typeVarsMap.isEmpty()) {\n\t\t\t\t// generics can't be resolved => use as is\n\t\t\t\treturn applyInvokeTypes(updateInfo, invoke, argsCount, knownTypeVars, () -> returnType, argTypes::get);\n\t\t\t}\n\t\t\t// resolve types before apply\n\t\t\treturn applyInvokeTypes(updateInfo, invoke, argsCount, knownTypeVars,\n\t\t\t\t\t() -> typeUtils.replaceTypeVariablesUsingMap(returnType, typeVarsMap),\n\t\t\t\t\targNum -> typeUtils.replaceClassGenerics(candidateType, argTypes.get(argNum)));\n\t\t}\n\t\treturn SAME;\n\t}\n\n\tprivate TypeUpdateResult applyInvokeTypes(TypeUpdateInfo updateInfo, BaseInvokeNode invoke, int argsCount,\n\t\t\tSet<ArgType> knownTypeVars, Supplier<ArgType> getReturnType, Function<Integer, ArgType> getArgType) {\n\t\tboolean allSame = true;\n\t\tRegisterArg resultArg = invoke.getResult();\n\t\tif (resultArg != null && !resultArg.isTypeImmutable()) {\n\t\t\tArgType returnType = checkType(knownTypeVars, getReturnType.get());\n\t\t\tif (returnType != null) {\n\t\t\t\tTypeUpdateResult result = updateTypeChecked(updateInfo, resultArg, returnType);\n\t\t\t\tif (result == REJECT) {\n\t\t\t\t\tTypeCompareEnum compare = comparator.compareTypes(returnType, resultArg.getType());\n\t\t\t\t\tif (compare.isWider()) {\n\t\t\t\t\t\treturn REJECT;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (result == CHANGED) {\n\t\t\t\t\tallSame = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tint argOffset = invoke.getFirstArgOffset();\n\t\tfor (int i = 0; i < argsCount; i++) {\n\t\t\tInsnArg invokeArg = invoke.getArg(argOffset + i);\n\t\t\tif (!invokeArg.isTypeImmutable()) {\n\t\t\t\tArgType argType = checkType(knownTypeVars, getArgType.apply(i));\n\t\t\t\tif (argType != null) {\n\t\t\t\t\tTypeUpdateResult result = updateTypeChecked(updateInfo, invokeArg, argType);\n\t\t\t\t\tif (result == REJECT) {\n\t\t\t\t\t\tTypeCompareEnum compare = comparator.compareTypes(argType, invokeArg.getType());\n\t\t\t\t\t\tif (compare.isNarrow()) {\n\t\t\t\t\t\t\treturn REJECT;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (result == CHANGED) {\n\t\t\t\t\t\tallSame = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn allSame ? SAME : CHANGED;\n\t}\n\n\t@Nullable\n\tprivate ArgType checkType(Set<ArgType> knownTypeVars, @Nullable ArgType type) {\n\t\tif (type == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (type.isWildcard()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (type.containsTypeVariable()) {\n\t\t\tif (knownTypeVars.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tBoolean hasUnknown = type.visitTypes(t -> t.isGenericType() && !knownTypeVars.contains(t) ? Boolean.TRUE : null);\n\t\t\tif (hasUnknown != null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\treturn type;\n\t}\n\n\tprivate TypeUpdateResult sameFirstArgListener(TypeUpdateInfo updateInfo, InsnNode insn, InsnArg arg, ArgType candidateType) {\n\t\tInsnArg changeArg = isAssign(insn, arg) ? insn.getArg(0) : insn.getResult();\n\t\tif (updateInfo.hasUpdateWithType(changeArg, candidateType)) {\n\t\t\treturn CHANGED;\n\t\t}\n\t\treturn updateTypeChecked(updateInfo, changeArg, candidateType);\n\t}\n\n\tprivate TypeUpdateResult moveListener(TypeUpdateInfo updateInfo, InsnNode insn, InsnArg arg, ArgType candidateType) {\n\t\tif (insn.getResult() == null) {\n\t\t\treturn CHANGED;\n\t\t}\n\t\tboolean assignChanged = isAssign(insn, arg);\n\t\tInsnArg changeArg = assignChanged ? insn.getArg(0) : insn.getResult();\n\n\t\t// allow result to be wider\n\t\tTypeCompareEnum cmp = comparator.compareTypes(candidateType, changeArg.getType());\n\t\tboolean correctType = cmp.isEqual() || (assignChanged ? cmp.isWider() : cmp.isNarrow());\n\n\t\tTypeUpdateResult result = updateTypeChecked(updateInfo, changeArg, candidateType);\n\t\tif (result == SAME && !correctType) {\n\t\t\tif (Consts.DEBUG_TYPE_INFERENCE) {\n\t\t\t\tLOG.debug(\"Move insn types mismatch: {} -> {}, change arg: {}, insn: {}\",\n\t\t\t\t\t\tcandidateType, changeArg.getType(), changeArg, insn);\n\t\t\t}\n\t\t\treturn REJECT;\n\t\t}\n\t\tif (result == REJECT && correctType) {\n\t\t\treturn CHANGED;\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * All args must have same types\n\t */\n\tprivate TypeUpdateResult allSameListener(TypeUpdateInfo updateInfo, InsnNode insn, InsnArg arg, ArgType candidateType) {\n\t\tif (!isAssign(insn, arg)) {\n\t\t\treturn updateTypeChecked(updateInfo, insn.getResult(), candidateType);\n\t\t}\n\t\tboolean allSame = true;\n\t\tfor (InsnArg insnArg : insn.getArguments()) {\n\t\t\tif (insnArg == arg) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tTypeUpdateResult result = updateTypeChecked(updateInfo, insnArg, candidateType);\n\t\t\tif (result == REJECT) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tif (result != SAME) {\n\t\t\t\tallSame = false;\n\t\t\t}\n\t\t}\n\t\treturn allSame ? SAME : CHANGED;\n\t}\n\n\tprivate TypeUpdateResult arithListener(TypeUpdateInfo updateInfo, InsnNode insn, InsnArg arg, ArgType candidateType) {\n\t\tArithNode arithInsn = (ArithNode) insn;\n\t\tif (candidateType == ArgType.BOOLEAN && arithInsn.getOp().isBitOp()) {\n\t\t\t// force all args to boolean\n\t\t\treturn allSameListener(updateInfo, insn, arg, candidateType);\n\t\t}\n\t\treturn suggestAllSameListener(updateInfo, insn, arg, candidateType);\n\t}\n\n\t/**\n\t * Try to set candidate type to all args, don't fail on reject\n\t */\n\tprivate TypeUpdateResult suggestAllSameListener(TypeUpdateInfo updateInfo, InsnNode insn, InsnArg arg, ArgType candidateType) {\n\t\tif (!isAssign(insn, arg)) {\n\t\t\tRegisterArg resultArg = insn.getResult();\n\t\t\tif (resultArg != null) {\n\t\t\t\tupdateTypeChecked(updateInfo, resultArg, candidateType);\n\t\t\t}\n\t\t}\n\t\tboolean allSame = true;\n\t\tfor (InsnArg insnArg : insn.getArguments()) {\n\t\t\tif (insnArg != arg) {\n\t\t\t\tTypeUpdateResult result = updateTypeChecked(updateInfo, insnArg, candidateType);\n\t\t\t\tif (result == REJECT) {\n\t\t\t\t\t// ignore\n\t\t\t\t} else if (result != SAME) {\n\t\t\t\t\tallSame = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn allSame ? SAME : CHANGED;\n\t}\n\n\tprivate TypeUpdateResult checkCastListener(TypeUpdateInfo updateInfo, InsnNode insn, InsnArg arg, ArgType candidateType) {\n\t\tIndexInsnNode checkCast = (IndexInsnNode) insn;\n\t\tif (isAssign(insn, arg)) {\n\t\t\tInsnArg insnArg = insn.getArg(0);\n\t\t\tTypeUpdateResult result = updateTypeChecked(updateInfo, insnArg, candidateType);\n\t\t\treturn result == REJECT ? SAME : result;\n\t\t}\n\t\tArgType castType = (ArgType) checkCast.getIndex();\n\t\tTypeCompareEnum res = comparator.compareTypes(candidateType, castType);\n\t\tif (res == TypeCompareEnum.CONFLICT) {\n\t\t\t// allow casting one interface to another\n\t\t\tif (!isInterfaces(candidateType, castType)) {\n\t\t\t\treturn REJECT;\n\t\t\t}\n\t\t}\n\t\tif (res == TypeCompareEnum.CONFLICT_BY_GENERIC) {\n\t\t\tif (!insn.contains(AFlag.SOFT_CAST)) {\n\t\t\t\treturn REJECT;\n\t\t\t}\n\t\t}\n\t\tif (res == TypeCompareEnum.NARROW_BY_GENERIC && candidateType.containsGeneric()) {\n\t\t\t// propagate generic type to result\n\t\t\treturn updateTypeChecked(updateInfo, checkCast.getResult(), candidateType);\n\t\t}\n\t\tArgType currentType = checkCast.getArg(0).getType();\n\t\treturn candidateType.equals(currentType) ? SAME : CHANGED;\n\t}\n\n\tprivate boolean isInterfaces(ArgType firstType, ArgType secondType) {\n\t\tif (!firstType.isObject() || !secondType.isObject()) {\n\t\t\treturn false;\n\t\t}\n\t\tClspClass firstCls = root.getClsp().getClsDetails(firstType);\n\t\tClspClass secondCls = root.getClsp().getClsDetails(secondType);\n\t\tif (firstCls != null && !firstCls.isInterface()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (secondCls != null && !secondCls.isInterface()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (firstCls == null || secondCls == null) {\n\t\t\treturn true;\n\t\t}\n\t\treturn secondCls.isInterface() && firstCls.isInterface();\n\t}\n\n\tprivate TypeUpdateResult arrayGetListener(TypeUpdateInfo updateInfo, InsnNode insn, InsnArg arg, ArgType candidateType) {\n\t\tif (isAssign(insn, arg)) {\n\t\t\tTypeUpdateResult result = updateTypeChecked(updateInfo, insn.getArg(0), ArgType.array(candidateType));\n\t\t\tif (result == REJECT) {\n\t\t\t\tArgType arrType = insn.getArg(0).getType();\n\t\t\t\tif (arrType.isTypeKnown() && arrType.isArray() && arrType.getArrayElement().isPrimitive()) {\n\t\t\t\t\tTypeCompareEnum compResult = comparator.compareTypes(candidateType, arrType.getArrayElement());\n\t\t\t\t\tif (compResult == TypeCompareEnum.WIDER) {\n\t\t\t\t\t\t// allow implicit upcast for primitive types (int a = byteArr[n])\n\t\t\t\t\t\treturn CHANGED;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t\tInsnArg arrArg = insn.getArg(0);\n\t\tif (arrArg == arg) {\n\t\t\tArgType arrayElement = candidateType.getArrayElement();\n\t\t\tif (arrayElement == null) {\n\t\t\t\treturn REJECT;\n\t\t\t}\n\t\t\tTypeUpdateResult result = updateTypeChecked(updateInfo, insn.getResult(), arrayElement);\n\t\t\tif (result == REJECT) {\n\t\t\t\tArgType resType = insn.getResult().getType();\n\t\t\t\tif (resType.isTypeKnown() && resType.isPrimitive()) {\n\t\t\t\t\tTypeCompareEnum compResult = comparator.compareTypes(resType, arrayElement);\n\t\t\t\t\tif (compResult == TypeCompareEnum.WIDER) {\n\t\t\t\t\t\t// allow implicit upcast for primitive types (int a = byteArr[n])\n\t\t\t\t\t\treturn CHANGED;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t\t// index argument\n\t\treturn SAME;\n\t}\n\n\tprivate TypeUpdateResult arrayPutListener(TypeUpdateInfo updateInfo, InsnNode insn, InsnArg arg, ArgType candidateType) {\n\t\tInsnArg arrArg = insn.getArg(0);\n\t\tInsnArg putArg = insn.getArg(2);\n\t\tif (arrArg == arg) {\n\t\t\tArgType arrayElement = candidateType.getArrayElement();\n\t\t\tif (arrayElement == null) {\n\t\t\t\treturn REJECT;\n\t\t\t}\n\t\t\tTypeUpdateResult result = updateTypeChecked(updateInfo, putArg, arrayElement);\n\t\t\tif (result == REJECT) {\n\t\t\t\tArgType putType = putArg.getType();\n\t\t\t\tif (putType.isTypeKnown()) {\n\t\t\t\t\tTypeCompareEnum compResult = comparator.compareTypes(arrayElement, putType);\n\t\t\t\t\tif (compResult == TypeCompareEnum.WIDER || compResult == TypeCompareEnum.WIDER_BY_GENERIC) {\n\t\t\t\t\t\t// allow wider result (i.e. allow put any objects in Object[] or byte in int[])\n\t\t\t\t\t\treturn CHANGED;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t\tif (arrArg == putArg) {\n\t\t\treturn updateTypeChecked(updateInfo, arrArg, ArgType.array(candidateType));\n\t\t}\n\t\t// index\n\t\treturn SAME;\n\t}\n\n\tprivate TypeUpdateResult ifListener(TypeUpdateInfo updateInfo, InsnNode insn, InsnArg arg, ArgType candidateType) {\n\t\tInsnArg firstArg = insn.getArg(0);\n\t\tInsnArg secondArg = insn.getArg(1);\n\t\tInsnArg updateArg = firstArg == arg ? secondArg : firstArg;\n\t\tTypeUpdateResult result = updateTypeChecked(updateInfo, updateArg, candidateType);\n\t\tif (result == REJECT) {\n\t\t\t// soft checks for objects and array - exact type not compared\n\t\t\tArgType updateArgType = updateArg.getType();\n\t\t\tif (candidateType.isObject() && updateArgType.canBeObject()) {\n\t\t\t\treturn SAME;\n\t\t\t}\n\t\t\tif (candidateType.isArray() && updateArgType.canBeArray()) {\n\t\t\t\treturn SAME;\n\t\t\t}\n\t\t\tif (candidateType.isPrimitive()) {\n\t\t\t\tif (updateArgType.canBePrimitive(candidateType.getPrimitiveType())) {\n\t\t\t\t\treturn SAME;\n\t\t\t\t}\n\t\t\t\tif (updateArgType.isTypeKnown() && candidateType.getRegCount() == updateArgType.getRegCount()) {\n\t\t\t\t\treturn SAME;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate static boolean isAssign(InsnNode insn, InsnArg arg) {\n\t\treturn insn.getResult() == arg;\n\t}\n\n\tpublic TypeCompare getTypeCompare() {\n\t\treturn comparator;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdateEntry.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\n\npublic final class TypeUpdateEntry implements Comparable<TypeUpdateEntry> {\n\tprivate final int seq;\n\tprivate final InsnArg arg;\n\tprivate final ArgType type;\n\n\tpublic TypeUpdateEntry(int seq, InsnArg arg, ArgType type) {\n\t\tthis.seq = seq;\n\t\tthis.arg = arg;\n\t\tthis.type = type;\n\t}\n\n\tpublic int getSeq() {\n\t\treturn seq;\n\t}\n\n\tpublic InsnArg getArg() {\n\t\treturn arg;\n\t}\n\n\tpublic ArgType getType() {\n\t\treturn type;\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull TypeUpdateEntry other) {\n\t\treturn Integer.compare(this.seq, other.seq);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn type + \" -> \" + arg.toShortString() + \" in \" + arg.getParentInsn();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdateFlags.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport static jadx.core.dex.visitors.typeinference.TypeUpdateFlags.FlagsEnum.ALLOW_WIDER;\nimport static jadx.core.dex.visitors.typeinference.TypeUpdateFlags.FlagsEnum.IGNORE_SAME;\nimport static jadx.core.dex.visitors.typeinference.TypeUpdateFlags.FlagsEnum.IGNORE_UNKNOWN;\nimport static jadx.core.dex.visitors.typeinference.TypeUpdateFlags.FlagsEnum.KEEP_GENERICS;\n\npublic class TypeUpdateFlags {\n\tenum FlagsEnum {\n\t\tALLOW_WIDER,\n\t\tIGNORE_SAME,\n\t\tIGNORE_UNKNOWN,\n\t\tKEEP_GENERICS,\n\t}\n\n\tstatic final TypeUpdateFlags FLAGS_EMPTY = build();\n\tstatic final TypeUpdateFlags FLAGS_WIDER = build(ALLOW_WIDER);\n\tstatic final TypeUpdateFlags FLAGS_WIDER_IGNORE_SAME = build(ALLOW_WIDER, IGNORE_SAME);\n\tstatic final TypeUpdateFlags FLAGS_APPLY_DEBUG = build(ALLOW_WIDER, KEEP_GENERICS, IGNORE_UNKNOWN);\n\n\tprivate final Set<FlagsEnum> flags;\n\n\tprivate static TypeUpdateFlags build(FlagsEnum... flags) {\n\t\tEnumSet<FlagsEnum> set;\n\t\tif (flags.length == 0) {\n\t\t\tset = EnumSet.noneOf(FlagsEnum.class);\n\t\t} else {\n\t\t\tset = EnumSet.copyOf(List.of(flags));\n\t\t}\n\t\treturn new TypeUpdateFlags(set);\n\t}\n\n\tprivate TypeUpdateFlags(Set<FlagsEnum> flags) {\n\t\tthis.flags = flags;\n\t}\n\n\tpublic boolean isAllowWider() {\n\t\treturn flags.contains(ALLOW_WIDER);\n\t}\n\n\tpublic boolean isIgnoreSame() {\n\t\treturn flags.contains(IGNORE_SAME);\n\t}\n\n\tpublic boolean isIgnoreUnknown() {\n\t\treturn flags.contains(IGNORE_UNKNOWN);\n\t}\n\n\tpublic boolean isKeepGenerics() {\n\t\treturn flags.contains(KEEP_GENERICS);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn flags.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdateInfo.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport java.util.IdentityHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport jadx.api.JadxArgs;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxOverflowException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class TypeUpdateInfo {\n\tprivate final MethodNode mth;\n\tprivate final TypeUpdateFlags flags;\n\tprivate final Map<InsnArg, TypeUpdateEntry> updateMap = new IdentityHashMap<>();\n\tprivate final int updatesLimitCount;\n\tprivate int updateSeq = 0;\n\n\tpublic TypeUpdateInfo(MethodNode mth, TypeUpdateFlags flags, JadxArgs args) {\n\t\tthis.mth = mth;\n\t\tthis.flags = flags;\n\t\tthis.updatesLimitCount = mth.getInsnsCount() * args.getTypeUpdatesLimitCount();\n\t}\n\n\tpublic void requestUpdate(InsnArg arg, ArgType changeType) {\n\t\tTypeUpdateEntry prev = updateMap.put(arg, new TypeUpdateEntry(updateSeq++, arg, changeType));\n\t\tif (prev != null) {\n\t\t\tthrow new JadxRuntimeException(\"Unexpected type update override for arg: \" + arg\n\t\t\t\t\t+ \" types: prev=\" + prev.getType() + \", new=\" + changeType\n\t\t\t\t\t+ \", insn: \" + arg.getParentInsn());\n\t\t}\n\t\tif (updateSeq > updatesLimitCount) {\n\t\t\tthrow new JadxOverflowException(\"Type inference error: updates count limit reached\"\n\t\t\t\t\t+ \" with updateSeq = \" + updateSeq + \". Try increasing type updates limit count.\");\n\t\t}\n\t\tif (updateSeq % 100 == 0) {\n\t\t\t// check for interruption sometimes (every update is too often)\n\t\t\tUtils.checkThreadInterrupt();\n\t\t}\n\t}\n\n\tpublic void rollbackUpdate(InsnArg arg) {\n\t\tTypeUpdateEntry removed = updateMap.remove(arg);\n\t\tif (removed != null) {\n\t\t\tint seq = removed.getSeq();\n\t\t\tupdateMap.values().removeIf(upd -> upd.getSeq() > seq);\n\t\t}\n\t}\n\n\tpublic void applyUpdates() {\n\t\tupdateMap.values().stream().sorted()\n\t\t\t\t.forEach(upd -> upd.getArg().setType(upd.getType()));\n\t}\n\n\tpublic boolean isProcessed(InsnArg arg) {\n\t\treturn updateMap.containsKey(arg);\n\t}\n\n\tpublic boolean hasUpdateWithType(InsnArg arg, ArgType type) {\n\t\tTypeUpdateEntry updateEntry = updateMap.get(arg);\n\t\tif (updateEntry != null) {\n\t\t\treturn updateEntry.getType().equals(type);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic ArgType getType(InsnArg arg) {\n\t\tTypeUpdateEntry updateEntry = updateMap.get(arg);\n\t\tif (updateEntry != null) {\n\t\t\treturn updateEntry.getType();\n\t\t}\n\t\treturn arg.getType();\n\t}\n\n\tpublic MethodNode getMth() {\n\t\treturn mth;\n\t}\n\n\tpublic boolean isEmpty() {\n\t\treturn updateMap.isEmpty();\n\t}\n\n\tpublic List<TypeUpdateEntry> getSortedUpdates() {\n\t\treturn updateMap.values().stream().sorted().collect(Collectors.toList());\n\t}\n\n\tpublic TypeUpdateFlags getFlags() {\n\t\treturn flags;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"TypeUpdateInfo{\" + flags + ' ' + getSortedUpdates() + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdateRegistry.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.EnumMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.core.dex.instructions.InsnType;\n\npublic class TypeUpdateRegistry {\n\n\tprivate final Map<InsnType, List<ITypeListener>> listenersMap = new EnumMap<>(InsnType.class);\n\n\tpublic void add(InsnType insnType, ITypeListener listener) {\n\t\tlistenersMap.computeIfAbsent(insnType, k -> new ArrayList<>(3)).add(listener);\n\t}\n\n\t@NotNull\n\tpublic List<ITypeListener> getListenersForInsn(InsnType insnType) {\n\t\tList<ITypeListener> list = listenersMap.get(insnType);\n\t\tif (list == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn list;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdateResult.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\npublic enum TypeUpdateResult {\n\tREJECT,\n\tSAME,\n\tCHANGED\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/usage/UsageInfo.java",
    "content": "package jadx.core.dex.visitors.usage;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.usage.IUsageInfoData;\nimport jadx.api.usage.IUsageInfoVisitor;\nimport jadx.core.clsp.ClspClass;\nimport jadx.core.clsp.ClspClassSource;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.ICodeNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.utils.Utils.notEmpty;\n\npublic class UsageInfo implements IUsageInfoData {\n\tprivate final RootNode root;\n\n\tprivate final UseSet<ClassNode, ClassNode> clsDeps = new UseSet<>();\n\tprivate final UseSet<ClassNode, ClassNode> clsUsage = new UseSet<>();\n\tprivate final UseSet<ClassNode, MethodNode> clsUseInMth = new UseSet<>();\n\tprivate final UseSet<FieldNode, MethodNode> fieldUsage = new UseSet<>();\n\t// MethodNodeA -> Set of MethodNodes that MethodNodeA is called from\n\tprivate final UseSet<MethodNode, MethodNode> mthUsage = new UseSet<>();\n\t// MethodNodeA -> Set of MethodNodes that MethodNodeA calls\n\tprivate final UseSet<MethodNode, MethodNode> mthUses = new UseSet<>();\n\t// MethodNodeA -> Set of IMethodRefs for methods that MethodNodeA calls that cannot be resolved\n\tprivate final UseSet<MethodNode, IMethodRef> unresolvedMthUsage = new UseSet<>();\n\tprivate final Map<MethodNode, Boolean> selfCalls = new HashMap<>();\n\n\tpublic UsageInfo(RootNode root) {\n\t\tthis.root = root;\n\t}\n\n\t@Override\n\tpublic void apply() {\n\t\tclsDeps.visit((cls, deps) -> cls.setDependencies(sortedList(deps)));\n\t\tclsUsage.visit((cls, deps) -> cls.setUseIn(sortedList(deps)));\n\t\tclsUseInMth.visit((cls, methods) -> cls.setUseInMth(resolveMthList(sortedList(methods))));\n\t\tfieldUsage.visit((field, methods) -> field.setUseIn(resolveMthList(sortedList(methods))));\n\t\tmthUsage.visit((mth, methods) -> mth.setUseIn(resolveMthList(sortedList(methods))));\n\t\tmthUses.visit((mth, methods) -> mth.setUsed(resolveMthList(sortedList(methods))));\n\t\tunresolvedMthUsage.visit((mth, unresolvedMethods) -> mth.setUnresolvedUsed(new ArrayList<>(unresolvedMethods)));\n\t\tselfCalls.forEach((mth, selfCall) -> mth.setCallsSelf(selfCall));\n\t}\n\n\t@Override\n\tpublic void applyForClass(ClassNode cls) {\n\t\tcls.setDependencies(sortedList(clsDeps.getOrDefault(cls, Collections.emptySet())));\n\t\tcls.setUseIn(sortedList(clsUsage.getOrDefault(cls, Collections.emptySet())));\n\t\tcls.setUseInMth(resolveMthList(sortedList(clsUseInMth.getOrDefault(cls, Collections.emptySet()))));\n\t\tfor (FieldNode fld : cls.getFields()) {\n\t\t\tfld.setUseIn(resolveMthList(sortedList(fieldUsage.getOrDefault(fld, Collections.emptySet()))));\n\t\t}\n\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\tmth.setUseIn(resolveMthList(sortedList(mthUsage.getOrDefault(mth, Collections.emptySet()))));\n\t\t\tmth.setUsed(resolveMthList(sortedList(mthUses.getOrDefault(mth, Collections.emptySet()))));\n\t\t\tmth.setUnresolvedUsed(new ArrayList<>(unresolvedMthUsage.getOrDefault(mth, Collections.emptySet())));\n\t\t\tmth.setCallsSelf(selfCalls.getOrDefault(mth, false));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void visitUsageData(IUsageInfoVisitor visitor) {\n\t\tclsDeps.visit((cls, deps) -> visitor.visitClassDeps(cls, sortedList(deps)));\n\t\tclsUsage.visit((cls, deps) -> visitor.visitClassUsage(cls, sortedList(deps)));\n\t\tclsUseInMth.visit((cls, methods) -> visitor.visitClassUseInMethods(cls, resolveMthList(sortedList(methods))));\n\t\tfieldUsage.visit((field, methods) -> visitor.visitFieldsUsage(field, resolveMthList(sortedList(methods))));\n\t\tmthUsage.visit((mth, methods) -> visitor.visitMethodsUsage(mth, resolveMthList(sortedList(methods))));\n\t\tmthUses.visit((mth, methods) -> visitor.visitMethodsUses(mth, resolveMthList(sortedList(methods))));\n\t\tunresolvedMthUsage.visit((mth, unresolvedMethods) -> visitor.visitUnresolvedMethodsUsage(mth, new ArrayList<>(unresolvedMethods)));\n\t\tfor (Entry<MethodNode, Boolean> entry : selfCalls.entrySet()) {\n\t\t\tMethodNode mth = entry.getKey();\n\t\t\tBoolean selfCall = entry.getValue();\n\t\t\tvisitor.visitIsSelfCall(mth, selfCall);\n\t\t}\n\t\tvisitor.visitComplete();\n\t}\n\n\tpublic void clsUse(ClassNode cls, ArgType useType) {\n\t\tprocessType(useType, depCls -> clsUse(cls, depCls));\n\t}\n\n\tpublic void clsUse(MethodNode mth, ArgType useType) {\n\t\tprocessType(useType, depCls -> clsUse(mth, depCls));\n\t}\n\n\tpublic void clsUse(ICodeNode node, ArgType useType) {\n\t\tConsumer<ClassNode> consumer;\n\t\tswitch (node.getAnnType()) {\n\t\t\tcase CLASS:\n\t\t\t\tClassNode cls = (ClassNode) node;\n\t\t\t\tconsumer = depCls -> clsUse(cls, depCls);\n\t\t\t\tbreak;\n\t\t\tcase METHOD:\n\t\t\t\tMethodNode mth = (MethodNode) node;\n\t\t\t\tconsumer = depCls -> clsUse(mth, depCls);\n\t\t\t\tbreak;\n\t\t\tcase FIELD:\n\t\t\t\tFieldNode fld = (FieldNode) node;\n\t\t\t\tClassNode fldCls = fld.getParentClass();\n\t\t\t\tconsumer = depCls -> clsUse(fldCls, depCls);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unexpected use type: \" + node.getAnnType());\n\t\t}\n\t\tprocessType(useType, consumer);\n\t}\n\n\tpublic void clsUse(MethodNode mth, ClassNode useCls) {\n\t\tClassNode parentClass = mth.getParentClass();\n\t\tclsUse(parentClass, useCls);\n\t\tif (parentClass != useCls) {\n\t\t\t// exclude class usage in self methods\n\t\t\tclsUseInMth.add(useCls, mth);\n\t\t}\n\t}\n\n\tpublic void clsUse(ClassNode cls, ClassNode depCls) {\n\t\tClassNode topParentClass = cls.getTopParentClass();\n\t\tclsDeps.add(topParentClass, depCls.getTopParentClass());\n\n\t\tclsUsage.add(depCls, cls);\n\t\tclsUsage.add(depCls, topParentClass);\n\t}\n\n\t/**\n\t * Add method usage: {@code useMth} occurrence found in {@code mth} code\n\t */\n\tpublic void methodUse(MethodNode mth, MethodNode useMth) {\n\t\tclsUse(mth, useMth.getParentClass());\n\t\tmthUsage.add(useMth, mth); // useMth is used in mth\n\t\tmthUses.add(mth, useMth); // mth uses useMth\n\t\tif (mth == useMth) {\n\t\t\tselfCalls.put(mth, true);\n\t\t}\n\t\t// implicit usage\n\t\tclsUse(mth, useMth.getReturnType());\n\t\tuseMth.getMethodInfo().getArgumentsTypes().forEach(argType -> clsUse(mth, argType));\n\t}\n\n\t/**\n\t * Add method usage: {@code useMth} occurrence found in {@code mth} code\n\t */\n\tpublic void unresolvedMethodUse(MethodNode mth, IMethodRef useMth) {\n\t\tunresolvedMthUsage.add(mth, useMth);\n\t}\n\n\tpublic void fieldUse(MethodNode mth, FieldNode useFld) {\n\t\tclsUse(mth, useFld.getParentClass());\n\t\tfieldUsage.add(useFld, mth);\n\t\t// implicit usage\n\t\tclsUse(mth, useFld.getType());\n\t}\n\n\tpublic void fieldUse(ICodeNode node, FieldInfo useFld) {\n\t\tFieldNode fld = root.resolveField(useFld);\n\t\tif (fld == null) {\n\t\t\treturn;\n\t\t}\n\t\tswitch (node.getAnnType()) {\n\t\t\tcase CLASS:\n\t\t\t\t// TODO: support \"field in class\" usage?\n\t\t\t\t// now use field parent class for \"class in class\" usage\n\t\t\t\tclsUse((ClassNode) node, fld.getParentClass());\n\t\t\t\tbreak;\n\t\t\tcase METHOD:\n\t\t\t\tfieldUse((MethodNode) node, fld);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Visit all class nodes found in subtypes of the provided type.\n\t */\n\tprivate void processType(ArgType type, Consumer<ClassNode> consumer) {\n\t\tif (type == null || type == ArgType.OBJECT) {\n\t\t\treturn;\n\t\t}\n\t\tif (type.isArray()) {\n\t\t\tprocessType(type.getArrayRootElement(), consumer);\n\t\t\treturn;\n\t\t}\n\t\tif (type.isObject()) {\n\t\t\t// TODO: support custom handlers via API\n\t\t\tClspClass clsDetails = root.getClsp().getClsDetails(type);\n\t\t\tif (clsDetails != null && clsDetails.getSource() == ClspClassSource.APACHE_HTTP_LEGACY_CLIENT) {\n\t\t\t\troot.getGradleInfoStorage().setUseApacheHttpLegacy(true);\n\t\t\t}\n\t\t\tClassNode clsNode = root.resolveClass(type);\n\t\t\tif (clsNode != null) {\n\t\t\t\tconsumer.accept(clsNode);\n\t\t\t}\n\t\t\tList<ArgType> genericTypes = type.getGenericTypes();\n\t\t\tif (notEmpty(genericTypes)) {\n\t\t\t\tfor (ArgType argType : genericTypes) {\n\t\t\t\t\tprocessType(argType, consumer);\n\t\t\t\t}\n\t\t\t}\n\t\t\tList<ArgType> extendTypes = type.getExtendTypes();\n\t\t\tif (notEmpty(extendTypes)) {\n\t\t\t\tfor (ArgType extendType : extendTypes) {\n\t\t\t\t\tprocessType(extendType, consumer);\n\t\t\t\t}\n\t\t\t}\n\t\t\tArgType wildcardType = type.getWildcardType();\n\t\t\tif (wildcardType != null) {\n\t\t\t\tprocessType(wildcardType, consumer);\n\t\t\t}\n\t\t\t// TODO: process 'outer' types (check TestOuterGeneric test)\n\t\t}\n\t}\n\n\tprivate static <T extends Comparable<T>> List<T> sortedList(Set<T> nodes) {\n\t\tif (nodes == null || nodes.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<T> list = new ArrayList<>(nodes);\n\t\tCollections.sort(list);\n\t\treturn list;\n\t}\n\n\tprivate List<MethodNode> resolveMthList(List<MethodNode> mthNodeList) {\n\t\treturn Utils.collectionMap(mthNodeList,\n\t\t\t\tm -> root.resolveDirectMethod(m.getParentClass().getRawName(), m.getMethodInfo().getShortId()));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/usage/UsageInfoVisitor.java",
    "content": "package jadx.core.dex.visitors.usage;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.input.data.ICallSite;\nimport jadx.api.plugins.input.data.ICodeReader;\nimport jadx.api.plugins.input.data.IFieldRef;\nimport jadx.api.plugins.input.data.IMethodHandle;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationMethodParamsAttr;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationsAttr;\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.api.plugins.input.insns.Opcode;\nimport jadx.api.plugins.input.insns.custom.ICustomPayload;\nimport jadx.api.usage.IUsageInfoCache;\nimport jadx.api.usage.IUsageInfoData;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.ICodeNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.JadxVisitor;\nimport jadx.core.dex.visitors.OverrideMethodVisitor;\nimport jadx.core.dex.visitors.SignatureProcessor;\nimport jadx.core.dex.visitors.rename.RenameVisitor;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.input.InsnDataUtils;\n\n@JadxVisitor(\n\t\tname = \"UsageInfoVisitor\",\n\t\tdesc = \"Scan class and methods to collect usage info and class dependencies\",\n\t\trunAfter = {\n\t\t\t\tSignatureProcessor.class, // use types with generics\n\t\t\t\tOverrideMethodVisitor.class, // add method override as use\n\t\t\t\tRenameVisitor.class // sort by alias name\n\t\t}\n)\npublic class UsageInfoVisitor extends AbstractVisitor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(UsageInfoVisitor.class);\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tIUsageInfoCache usageCache = root.getArgs().getUsageInfoCache();\n\t\tIUsageInfoData usageInfoData = usageCache.get(root);\n\t\tif (usageInfoData != null) {\n\t\t\ttry {\n\t\t\t\tapply(usageInfoData);\n\t\t\t\treturn;\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to apply cached usage data\", e);\n\t\t\t}\n\t\t}\n\t\tIUsageInfoData collectedInfoData = buildUsageData(root);\n\t\tusageCache.set(root, collectedInfoData);\n\t\tapply(collectedInfoData);\n\t}\n\n\tprivate static void apply(IUsageInfoData usageInfoData) {\n\t\tlong start = System.currentTimeMillis();\n\t\tusageInfoData.apply();\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tLOG.debug(\"Apply usage data in {}ms\", System.currentTimeMillis() - start);\n\t\t}\n\t}\n\n\tprivate static IUsageInfoData buildUsageData(RootNode root) {\n\t\tUsageInfo usageInfo = new UsageInfo(root);\n\t\tfor (ClassNode cls : root.getClasses()) {\n\t\t\tprocessClass(cls, usageInfo);\n\t\t}\n\t\treturn usageInfo;\n\t}\n\n\tprivate static void processClass(ClassNode cls, UsageInfo usageInfo) {\n\t\tusageInfo.clsUse(cls, cls.getSuperClass());\n\t\tfor (ArgType interfaceType : cls.getInterfaces()) {\n\t\t\tusageInfo.clsUse(cls, interfaceType);\n\t\t}\n\t\tfor (ArgType genericTypeParameter : cls.getGenericTypeParameters()) {\n\t\t\tusageInfo.clsUse(cls, genericTypeParameter);\n\t\t}\n\t\tfor (FieldNode fieldNode : cls.getFields()) {\n\t\t\tusageInfo.clsUse(cls, fieldNode.getType());\n\t\t\tprocessAnnotations(fieldNode, usageInfo);\n\t\t\t// TODO: process types from field 'constant value'\n\t\t}\n\t\tprocessAnnotations(cls, usageInfo);\n\t\tfor (MethodNode methodNode : cls.getMethods()) {\n\t\t\tprocessMethod(methodNode, usageInfo);\n\t\t}\n\t}\n\n\tprivate static void processMethod(MethodNode mth, UsageInfo usageInfo) {\n\t\tprocessMethodAnnotations(mth, usageInfo);\n\t\tusageInfo.clsUse(mth, mth.getReturnType());\n\t\tfor (ArgType argType : mth.getArgTypes()) {\n\t\t\tusageInfo.clsUse(mth, argType);\n\t\t}\n\t\t// TODO: process exception classes from 'throws'\n\t\ttry {\n\t\t\tprocessInstructions(mth, usageInfo);\n\t\t} catch (Exception e) {\n\t\t\tmth.addError(\"Dependency scan failed\", e);\n\t\t}\n\t}\n\n\tprivate static void processInstructions(MethodNode mth, UsageInfo usageInfo) {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn;\n\t\t}\n\t\tICodeReader codeReader = mth.getCodeReader();\n\t\tif (codeReader == null) {\n\t\t\treturn;\n\t\t}\n\t\tRootNode root = mth.root();\n\t\tcodeReader.visitInstructions(insnData -> {\n\t\t\ttry {\n\t\t\t\tprocessInsn(root, mth, insnData, usageInfo);\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new JadxRuntimeException(\n\t\t\t\t\t\t\"Usage info collection failed with error: \" + e.getMessage() + \" at insn: \" + insnData, e);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate static void processInsn(RootNode root, MethodNode mth, InsnData insnData, UsageInfo usageInfo) {\n\t\tif (insnData.getOpcode() == Opcode.UNKNOWN) {\n\t\t\treturn;\n\t\t}\n\t\tswitch (insnData.getIndexType()) {\n\t\t\tcase TYPE_REF:\n\t\t\t\tinsnData.decode();\n\t\t\t\tArgType usedType = ArgType.parse(insnData.getIndexAsType());\n\t\t\t\tusageInfo.clsUse(mth, usedType);\n\t\t\t\tbreak;\n\n\t\t\tcase FIELD_REF:\n\t\t\t\tinsnData.decode();\n\t\t\t\tFieldNode fieldNode = root.resolveField(FieldInfo.fromRef(root, insnData.getIndexAsField()));\n\t\t\t\tif (fieldNode != null) {\n\t\t\t\t\tusageInfo.fieldUse(mth, fieldNode);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase METHOD_REF: {\n\t\t\t\tinsnData.decode();\n\t\t\t\tIMethodRef mthRef;\n\t\t\t\tICustomPayload payload = insnData.getPayload();\n\t\t\t\tif (payload != null) {\n\t\t\t\t\tmthRef = (IMethodRef) payload;\n\t\t\t\t} else {\n\t\t\t\t\tmthRef = insnData.getIndexAsMethod();\n\t\t\t\t}\n\t\t\t\tMethodNode methodNode = root.resolveMethod(MethodInfo.fromRef(root, mthRef));\n\t\t\t\tif (methodNode != null) {\n\t\t\t\t\tusageInfo.methodUse(mth, methodNode);\n\t\t\t\t} else {\n\t\t\t\t\tmthRef.load();\n\t\t\t\t\tif (mthRef.getName() != null || mthRef.getParentClassType() != null) {\n\t\t\t\t\t\tusageInfo.unresolvedMethodUse(mth, mthRef);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase CALL_SITE: {\n\t\t\t\tinsnData.decode();\n\t\t\t\tICallSite callSite = InsnDataUtils.getCallSite(insnData);\n\t\t\t\tIMethodHandle methodHandle = InsnDataUtils.getMethodHandleAt(callSite, 4);\n\t\t\t\tif (methodHandle != null) {\n\t\t\t\t\tIMethodRef mthRef = methodHandle.getMethodRef();\n\t\t\t\t\tMethodNode mthNode = root.resolveMethod(MethodInfo.fromRef(root, mthRef));\n\t\t\t\t\tif (mthNode != null) {\n\t\t\t\t\t\tusageInfo.methodUse(mth, mthNode);\n\t\t\t\t\t} else if (mthRef.getName() != null || mthRef.getParentClassType() != null) {\n\t\t\t\t\t\tusageInfo.unresolvedMethodUse(mth, mthRef);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void processAnnotations(ICodeNode node, UsageInfo usageInfo) {\n\t\tAnnotationsAttr annAttr = node.get(JadxAttrType.ANNOTATION_LIST);\n\t\tprocessAnnotationAttr(node, annAttr, usageInfo);\n\t}\n\n\tprivate static void processMethodAnnotations(MethodNode mth, UsageInfo usageInfo) {\n\t\tprocessAnnotations(mth, usageInfo);\n\t\tAnnotationMethodParamsAttr paramsAttr = mth.get(JadxAttrType.ANNOTATION_MTH_PARAMETERS);\n\t\tif (paramsAttr != null) {\n\t\t\tfor (AnnotationsAttr annAttr : paramsAttr.getParamList()) {\n\t\t\t\tprocessAnnotationAttr(mth, annAttr, usageInfo);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void processAnnotationAttr(ICodeNode node, AnnotationsAttr annAttr, UsageInfo usageInfo) {\n\t\tif (annAttr == null || annAttr.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (IAnnotation ann : annAttr.getList()) {\n\t\t\tprocessAnnotation(node, ann, usageInfo);\n\t\t}\n\t}\n\n\tprivate static void processAnnotation(ICodeNode node, IAnnotation ann, UsageInfo usageInfo) {\n\t\tusageInfo.clsUse(node, ArgType.parse(ann.getAnnotationClass()));\n\t\tfor (EncodedValue value : ann.getValues().values()) {\n\t\t\tprocessAnnotationValue(node, value, usageInfo);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static void processAnnotationValue(ICodeNode node, EncodedValue value, UsageInfo usageInfo) {\n\t\tObject obj = value.getValue();\n\t\tswitch (value.getType()) {\n\t\t\tcase ENCODED_TYPE:\n\t\t\t\tusageInfo.clsUse(node, ArgType.parse((String) obj));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_ENUM:\n\t\t\tcase ENCODED_FIELD:\n\t\t\t\tif (obj instanceof IFieldRef) {\n\t\t\t\t\tusageInfo.fieldUse(node, FieldInfo.fromRef(node.root(), (IFieldRef) obj));\n\t\t\t\t} else if (obj instanceof FieldInfo) {\n\t\t\t\t\tusageInfo.fieldUse(node, (FieldInfo) obj);\n\t\t\t\t} else {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Unexpected field type class: \" + value.getClass());\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_ARRAY:\n\t\t\t\tfor (EncodedValue encodedValue : (List<EncodedValue>) obj) {\n\t\t\t\t\tprocessAnnotationValue(node, encodedValue, usageInfo);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_ANNOTATION:\n\t\t\t\tprocessAnnotation(node, (IAnnotation) obj, usageInfo);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tpublic static void replaceMethodUsage(MethodNode mergeIntoMth, MethodNode sourceMth) {\n\t\tList<MethodNode> mergedUsage = ListUtils.distinctMergeSortedLists(mergeIntoMth.getUseIn(), sourceMth.getUseIn());\n\t\tmergedUsage.remove(sourceMth);\n\t\tmergeIntoMth.setUseIn(mergedUsage);\n\t\tsourceMth.setUseIn(Collections.emptyList());\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"UsageInfoVisitor\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/dex/visitors/usage/UseSet.java",
    "content": "package jadx.core.dex.visitors.usage;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.BiConsumer;\n\npublic class UseSet<K, V> {\n\tprivate final Map<K, Set<V>> useMap = new HashMap<>();\n\n\tpublic void add(K obj, V use) {\n\t\tif (obj == use) {\n\t\t\t// self excluded\n\t\t\treturn;\n\t\t}\n\t\tSet<V> set = useMap.computeIfAbsent(obj, k -> new HashSet<>());\n\t\tset.add(use);\n\t}\n\n\tpublic Set<V> get(K obj) {\n\t\treturn useMap.get(obj);\n\t}\n\n\tpublic Set<V> getOrDefault(K obj, Set<V> defaultValue) {\n\t\treturn useMap.getOrDefault(obj, defaultValue);\n\t}\n\n\tpublic void visit(BiConsumer<K, Set<V>> consumer) {\n\t\tfor (Map.Entry<K, Set<V>> entry : useMap.entrySet()) {\n\t\t\tconsumer.accept(entry.getKey(), entry.getValue());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/export/ExportGradle.java",
    "content": "package jadx.core.export;\n\nimport java.io.File;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourceType;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.export.gen.AndroidGradleGenerator;\nimport jadx.core.export.gen.IExportGradleGenerator;\nimport jadx.core.export.gen.SimpleJavaGradleGenerator;\nimport jadx.core.utils.android.AndroidManifestParser;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class ExportGradle {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ExportGradle.class);\n\tprivate final RootNode root;\n\tprivate final File projectDir;\n\tprivate final List<ResourceFile> resources;\n\tprivate IExportGradleGenerator generator;\n\n\tpublic ExportGradle(RootNode root, File projectDir, List<ResourceFile> resources) {\n\t\tthis.root = root;\n\t\tthis.projectDir = projectDir;\n\t\tthis.resources = resources;\n\t}\n\n\tpublic OutDirs init() {\n\t\tExportGradleType exportType = getExportGradleType();\n\t\tLOG.info(\"Export Gradle project using '{}' template\", exportType);\n\t\tswitch (exportType) {\n\t\t\tcase ANDROID_APP:\n\t\t\tcase ANDROID_LIBRARY:\n\t\t\t\tgenerator = new AndroidGradleGenerator(root, projectDir, resources, exportType);\n\t\t\t\tbreak;\n\t\t\tcase SIMPLE_JAVA:\n\t\t\t\tgenerator = new SimpleJavaGradleGenerator(root, projectDir, resources);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unexpected export type: \" + exportType);\n\t\t}\n\t\tgenerator.init();\n\t\tOutDirs outDirs = generator.getOutDirs();\n\t\toutDirs.makeDirs();\n\t\treturn outDirs;\n\t}\n\n\tprivate ExportGradleType getExportGradleType() {\n\t\tExportGradleType argsExportType = root.getArgs().getExportGradleType();\n\t\tExportGradleType detectedType = detectExportType(root, resources);\n\t\tif (argsExportType == null\n\t\t\t\t|| argsExportType == ExportGradleType.AUTO\n\t\t\t\t|| argsExportType == detectedType) {\n\t\t\treturn detectedType;\n\t\t}\n\t\treturn argsExportType;\n\t}\n\n\tpublic static ExportGradleType detectExportType(RootNode root, List<ResourceFile> resources) {\n\t\tResourceFile androidManifest = AndroidManifestParser.getAndroidManifest(resources);\n\t\tif (androidManifest != null) {\n\t\t\tif (resources.stream().anyMatch(r -> r.getOriginalName().equals(\"classes.jar\"))) {\n\t\t\t\treturn ExportGradleType.ANDROID_LIBRARY;\n\t\t\t}\n\t\t\tif (resources.stream().anyMatch(r -> r.getType() == ResourceType.ARSC)) {\n\t\t\t\treturn ExportGradleType.ANDROID_APP;\n\t\t\t}\n\t\t}\n\t\treturn ExportGradleType.SIMPLE_JAVA;\n\t}\n\n\tpublic void generateGradleFiles() {\n\t\tgenerator.generateFiles();\n\t}\n\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/export/ExportGradleType.java",
    "content": "package jadx.core.export;\n\npublic enum ExportGradleType {\n\tAUTO(\"Auto\"),\n\tANDROID_APP(\"Android App\"),\n\tANDROID_LIBRARY(\"Android Library\"),\n\tSIMPLE_JAVA(\"Simple Java\");\n\n\tprivate final String desc;\n\n\tExportGradleType(String desc) {\n\t\tthis.desc = desc;\n\t}\n\n\tpublic String getDesc() {\n\t\treturn desc;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn desc;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/export/GradleInfoStorage.java",
    "content": "package jadx.core.export;\n\npublic class GradleInfoStorage {\n\n\tprivate boolean vectorPathData;\n\n\tprivate boolean vectorFillType;\n\n\tprivate boolean useApacheHttpLegacy;\n\n\tprivate boolean nonFinalResIds;\n\n\tpublic boolean isVectorPathData() {\n\t\treturn vectorPathData;\n\t}\n\n\tpublic void setVectorPathData(boolean vectorPathData) {\n\t\tthis.vectorPathData = vectorPathData;\n\t}\n\n\tpublic boolean isVectorFillType() {\n\t\treturn vectorFillType;\n\t}\n\n\tpublic void setVectorFillType(boolean vectorFillType) {\n\t\tthis.vectorFillType = vectorFillType;\n\t}\n\n\tpublic boolean isUseApacheHttpLegacy() {\n\t\treturn useApacheHttpLegacy;\n\t}\n\n\tpublic void setUseApacheHttpLegacy(boolean useApacheHttpLegacy) {\n\t\tthis.useApacheHttpLegacy = useApacheHttpLegacy;\n\t}\n\n\tpublic boolean isNonFinalResIds() {\n\t\treturn nonFinalResIds;\n\t}\n\n\tpublic void setNonFinalResIds(boolean nonFinalResIds) {\n\t\tthis.nonFinalResIds = nonFinalResIds;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/export/OutDirs.java",
    "content": "package jadx.core.export;\n\nimport java.io.File;\n\nimport jadx.core.utils.files.FileUtils;\n\npublic class OutDirs {\n\tprivate final File srcOutDir;\n\tprivate final File resOutDir;\n\n\tpublic OutDirs(File srcOutDir, File resOutDir) {\n\t\tthis.srcOutDir = srcOutDir;\n\t\tthis.resOutDir = resOutDir;\n\t}\n\n\tpublic File getSrcOutDir() {\n\t\treturn srcOutDir;\n\t}\n\n\tpublic File getResOutDir() {\n\t\treturn resOutDir;\n\t}\n\n\tpublic void makeDirs() {\n\t\tFileUtils.makeDirs(srcOutDir);\n\t\tFileUtils.makeDirs(resOutDir);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/export/TemplateFile.java",
    "content": "package jadx.core.export;\n\nimport java.io.BufferedInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n/**\n * Simple template engine\n * Syntax for replace variable with value: '{{variable}}'\n */\npublic class TemplateFile {\n\n\tprivate enum State {\n\t\tNONE, START, VARIABLE, END\n\t}\n\n\tprivate static class ParserState {\n\t\tprivate State state = State.NONE;\n\t\tprivate StringBuilder curVariable;\n\t\tprivate boolean skip;\n\t}\n\n\tprivate final String templateName;\n\tprivate final InputStream template;\n\tprivate final Map<String, String> values = new HashMap<>();\n\n\tpublic static TemplateFile fromResources(String path) throws FileNotFoundException {\n\t\tInputStream res = TemplateFile.class.getResourceAsStream(path);\n\t\tif (res == null) {\n\t\t\tthrow new FileNotFoundException(\"Resource not found: \" + path);\n\t\t}\n\t\treturn new TemplateFile(path, res);\n\t}\n\n\tprivate TemplateFile(String name, InputStream in) {\n\t\tthis.templateName = name;\n\t\tthis.template = in;\n\t}\n\n\tpublic void add(String name, @Nullable Object value) {\n\t\tvalues.put(name, String.valueOf(value));\n\t}\n\n\tpublic String build() throws IOException {\n\t\ttry (ByteArrayOutputStream out = new ByteArrayOutputStream()) {\n\t\t\tprocess(out);\n\t\t\treturn out.toString();\n\t\t}\n\t}\n\n\tpublic void save(File outFile) throws IOException {\n\t\ttry (OutputStream out = new FileOutputStream(outFile)) {\n\t\t\tprocess(out);\n\t\t}\n\t}\n\n\tprivate void process(OutputStream out) throws IOException {\n\t\tif (template.available() == 0) {\n\t\t\tthrow new IOException(\"Template already processed\");\n\t\t}\n\t\ttry (InputStream in = new BufferedInputStream(template)) {\n\t\t\tParserState state = new ParserState();\n\t\t\twhile (true) {\n\t\t\t\tint ch = in.read();\n\t\t\t\tif (ch == -1) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tString str = process(state, (char) ch);\n\t\t\t\tif (str != null) {\n\t\t\t\t\tout.write(str.getBytes());\n\t\t\t\t} else if (!state.skip) {\n\t\t\t\t\tout.write(ch);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate String process(ParserState parser, char ch) {\n\t\tState state = parser.state;\n\t\tswitch (ch) {\n\t\t\tcase '{':\n\t\t\t\tswitch (state) {\n\t\t\t\t\tcase START:\n\t\t\t\t\t\tparser.state = State.VARIABLE;\n\t\t\t\t\t\tparser.curVariable = new StringBuilder();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tparser.state = State.START;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tparser.skip = true;\n\t\t\t\treturn null;\n\n\t\t\tcase '}':\n\t\t\t\tswitch (state) {\n\t\t\t\t\tcase VARIABLE:\n\t\t\t\t\t\tparser.state = State.END;\n\t\t\t\t\t\tparser.skip = true;\n\t\t\t\t\t\treturn null;\n\n\t\t\t\t\tcase END:\n\t\t\t\t\t\tparser.state = State.NONE;\n\t\t\t\t\t\tString varName = parser.curVariable.toString();\n\t\t\t\t\t\tparser.curVariable = new StringBuilder();\n\t\t\t\t\t\treturn processVar(varName);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tswitch (state) {\n\t\t\t\t\tcase VARIABLE:\n\t\t\t\t\t\tparser.curVariable.append(ch);\n\t\t\t\t\t\tparser.skip = true;\n\t\t\t\t\t\treturn null;\n\n\t\t\t\t\tcase START:\n\t\t\t\t\t\tparser.state = State.NONE;\n\t\t\t\t\t\treturn \"{\" + ch;\n\n\t\t\t\t\tcase END:\n\t\t\t\t\t\tthrow new JadxRuntimeException(\"Expected variable end: '\" + parser.curVariable\n\t\t\t\t\t\t\t\t+ \"' (missing second '}')\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t\tparser.skip = false;\n\t\treturn null;\n\t}\n\n\tprivate String processVar(String varName) {\n\t\tString str = values.get(varName);\n\t\tif (str == null) {\n\t\t\tthrow new JadxRuntimeException(\"Unknown variable: '\" + varName\n\t\t\t\t\t+ \"' in template: \" + templateName);\n\t\t}\n\t\treturn str;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/export/gen/AndroidGradleGenerator.java",
    "content": "package jadx.core.export.gen;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.regex.Pattern;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourceType;\nimport jadx.api.security.IJadxSecurity;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.export.ExportGradleType;\nimport jadx.core.export.GradleInfoStorage;\nimport jadx.core.export.OutDirs;\nimport jadx.core.export.TemplateFile;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.android.AndroidManifestParser;\nimport jadx.core.utils.android.AppAttribute;\nimport jadx.core.utils.android.ApplicationParams;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.xmlgen.ResContainer;\n\npublic class AndroidGradleGenerator implements IExportGradleGenerator {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(AndroidGradleGenerator.class);\n\tprivate static final Pattern ILLEGAL_GRADLE_CHARS = Pattern.compile(\"[/\\\\\\\\:>\\\"?*|]\");\n\n\tprivate static final ApplicationParams UNKNOWN_APP_PARAMS =\n\t\t\tnew ApplicationParams(\"UNKNOWN\", 0, 0, 0, 0, \"UNKNOWN\", \"UNKNOWN\", \"UNKNOWN\");\n\n\tprivate final RootNode root;\n\tprivate final File projectDir;\n\tprivate final List<ResourceFile> resources;\n\tprivate final boolean exportApp;\n\n\tprivate OutDirs outDirs;\n\tprivate File baseDir;\n\tprivate ApplicationParams applicationParams;\n\n\tpublic AndroidGradleGenerator(RootNode root, File projectDir, List<ResourceFile> resources, ExportGradleType exportType) {\n\t\tthis.root = root;\n\t\tthis.projectDir = projectDir;\n\t\tthis.resources = resources;\n\t\tthis.exportApp = exportType == ExportGradleType.ANDROID_APP;\n\t}\n\n\t@Override\n\tpublic void init() {\n\t\tString moduleDir = exportApp ? \"app\" : \"lib\";\n\t\tbaseDir = new File(projectDir, moduleDir);\n\t\toutDirs = new OutDirs(new File(baseDir, \"src/main/java\"), new File(baseDir, \"src/main\"));\n\t\tapplicationParams = parseApplicationParams();\n\t}\n\n\t@Override\n\tpublic void generateFiles() {\n\t\ttry {\n\t\t\tsaveProjectBuildGradle();\n\t\t\tif (exportApp) {\n\t\t\t\tsaveApplicationBuildGradle();\n\t\t\t} else {\n\t\t\t\tsaveLibraryBuildGradle();\n\t\t\t}\n\t\t\tsaveSettingsGradle();\n\t\t\tsaveGradleProperties();\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Gradle export failed\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OutDirs getOutDirs() {\n\t\treturn outDirs;\n\t}\n\n\tprivate ApplicationParams parseApplicationParams() {\n\t\ttry {\n\t\t\tResourceFile androidManifest = AndroidManifestParser.getAndroidManifest(resources);\n\t\t\tif (androidManifest == null) {\n\t\t\t\tLOG.warn(\"AndroidManifest.xml not found, exported files will contains 'UNKNOWN' fields\");\n\t\t\t\treturn UNKNOWN_APP_PARAMS;\n\t\t\t}\n\t\t\tResContainer strings = null;\n\t\t\tif (exportApp) {\n\t\t\t\tResourceFile arscFile = resources.stream()\n\t\t\t\t\t\t.filter(resourceFile -> resourceFile.getType() == ResourceType.ARSC)\n\t\t\t\t\t\t.findFirst().orElse(null);\n\t\t\t\tif (arscFile != null) {\n\t\t\t\t\tList<ResContainer> resContainers = arscFile.loadContent().getSubFiles();\n\t\t\t\t\tstrings = resContainers\n\t\t\t\t\t\t\t.stream()\n\t\t\t\t\t\t\t.filter(resContainer -> resContainer.getName().contains(\"values/strings.xml\"))\n\t\t\t\t\t\t\t.findFirst()\n\t\t\t\t\t\t\t.orElseGet(() -> resContainers.stream()\n\t\t\t\t\t\t\t\t\t.filter(resContainer -> resContainer.getName().contains(\"strings.xml\"))\n\t\t\t\t\t\t\t\t\t.findFirst().orElse(null));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tEnumSet<AppAttribute> attrs = EnumSet.noneOf(AppAttribute.class);\n\t\t\tattrs.add(AppAttribute.MIN_SDK_VERSION);\n\t\t\tif (exportApp) {\n\t\t\t\tattrs.add(AppAttribute.APPLICATION_LABEL);\n\t\t\t\tattrs.add(AppAttribute.TARGET_SDK_VERSION);\n\t\t\t\tattrs.add(AppAttribute.COMPILE_SDK_VERSION);\n\t\t\t\tattrs.add(AppAttribute.VERSION_NAME);\n\t\t\t\tattrs.add(AppAttribute.VERSION_CODE);\n\t\t\t}\n\n\t\t\tIJadxSecurity security = root.getArgs().getSecurity();\n\t\t\tAndroidManifestParser parser = new AndroidManifestParser(androidManifest, strings, attrs, security);\n\t\t\treturn parser.parse();\n\t\t} catch (Exception t) {\n\t\t\tLOG.warn(\"Failed to parse AndroidManifest.xml\", t);\n\t\t\treturn UNKNOWN_APP_PARAMS;\n\t\t}\n\t}\n\n\tprivate void saveGradleProperties() throws IOException {\n\t\tGradleInfoStorage gradleInfo = root.getGradleInfoStorage();\n\t\t/*\n\t\t * For Android Gradle Plugin >=8.0.0 the property \"android.nonFinalResIds=false\" has to be set in\n\t\t * \"gradle.properties\" when resource identifiers are used as constant expressions.\n\t\t */\n\t\tif (gradleInfo.isNonFinalResIds()) {\n\t\t\tFile gradlePropertiesFile = new File(projectDir, \"gradle.properties\");\n\t\t\ttry (FileOutputStream fos = new FileOutputStream(gradlePropertiesFile)) {\n\t\t\t\tfos.write(\"android.nonFinalResIds=false\".getBytes(StandardCharsets.UTF_8));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void saveProjectBuildGradle() throws IOException {\n\t\tTemplateFile tmpl = TemplateFile.fromResources(\"/export/android/build.gradle.tmpl\");\n\t\ttmpl.save(new File(projectDir, \"build.gradle\"));\n\t}\n\n\tprivate void saveSettingsGradle() throws IOException {\n\t\tTemplateFile tmpl = TemplateFile.fromResources(\"/export/android/settings.gradle.tmpl\");\n\t\tString appName = applicationParams.getApplicationName();\n\t\tString projectName;\n\t\tif (appName != null) {\n\t\t\tprojectName = ILLEGAL_GRADLE_CHARS.matcher(appName).replaceAll(\"\");\n\t\t} else {\n\t\t\tprojectName = GradleGeneratorTools.guessProjectName(root);\n\t\t}\n\t\ttmpl.add(\"projectName\", projectName);\n\t\ttmpl.add(\"mainModuleName\", baseDir.getName());\n\t\ttmpl.save(new File(projectDir, \"settings.gradle\"));\n\t}\n\n\tprivate void saveApplicationBuildGradle() throws IOException {\n\t\tString appPackage = Utils.getOrElse(root.getAppPackage(), \"UNKNOWN\");\n\t\tint minSdkVersion = Utils.getOrElse(applicationParams.getMinSdkVersion(), 0);\n\n\t\tTemplateFile tmpl = TemplateFile.fromResources(\"/export/android/app.build.gradle.tmpl\");\n\t\ttmpl.add(\"applicationId\", appPackage);\n\t\ttmpl.add(\"minSdkVersion\", minSdkVersion);\n\t\ttmpl.add(\"compileSdkVersion\", applicationParams.getCompileSdkVersion());\n\t\ttmpl.add(\"targetSdkVersion\", applicationParams.getTargetSdkVersion());\n\t\ttmpl.add(\"versionCode\", applicationParams.getVersionCode());\n\t\ttmpl.add(\"versionName\", applicationParams.getVersionName());\n\t\ttmpl.add(\"additionalOptions\", genAdditionalAndroidPluginOptions(minSdkVersion));\n\t\ttmpl.save(new File(baseDir, \"build.gradle\"));\n\t}\n\n\tprivate void saveLibraryBuildGradle() throws IOException {\n\t\tString pkg = Utils.getOrElse(root.getAppPackage(), \"UNKNOWN\");\n\t\tint minSdkVersion = Utils.getOrElse(applicationParams.getMinSdkVersion(), 0);\n\n\t\tTemplateFile tmpl = TemplateFile.fromResources(\"/export/android/lib.build.gradle.tmpl\");\n\t\ttmpl.add(\"packageId\", pkg);\n\t\ttmpl.add(\"minSdkVersion\", minSdkVersion);\n\t\ttmpl.add(\"compileSdkVersion\", applicationParams.getCompileSdkVersion());\n\t\ttmpl.add(\"additionalOptions\", genAdditionalAndroidPluginOptions(minSdkVersion));\n\n\t\ttmpl.save(new File(baseDir, \"build.gradle\"));\n\t}\n\n\tprivate String genAdditionalAndroidPluginOptions(int minSdkVersion) {\n\t\tList<String> additionalOptions = new ArrayList<>();\n\t\tGradleInfoStorage gradleInfo = root.getGradleInfoStorage();\n\t\tif (gradleInfo.isVectorPathData() && minSdkVersion < 21 || gradleInfo.isVectorFillType() && minSdkVersion < 24) {\n\t\t\tadditionalOptions.add(\"vectorDrawables.useSupportLibrary = true\");\n\t\t}\n\t\tif (gradleInfo.isUseApacheHttpLegacy()) {\n\t\t\tadditionalOptions.add(\"useLibrary 'org.apache.http.legacy'\");\n\t\t}\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (String additionalOption : additionalOptions) {\n\t\t\tsb.append(\"        \").append(additionalOption).append('\\n');\n\t\t}\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/export/gen/GradleGeneratorTools.java",
    "content": "package jadx.core.export.gen;\n\nimport java.io.File;\nimport java.util.List;\n\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.files.FileUtils;\n\npublic class GradleGeneratorTools {\n\n\tpublic static String guessProjectName(RootNode root) {\n\t\tList<File> inputFiles = root.getArgs().getInputFiles();\n\t\tif (inputFiles.size() == 1) {\n\t\t\treturn FileUtils.getPathBaseName(inputFiles.get(0).toPath());\n\t\t}\n\t\t// default\n\t\treturn \"PROJECT_NAME\";\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/export/gen/IExportGradleGenerator.java",
    "content": "package jadx.core.export.gen;\n\nimport jadx.core.export.OutDirs;\n\npublic interface IExportGradleGenerator {\n\n\tvoid init();\n\n\tOutDirs getOutDirs();\n\n\tvoid generateFiles();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/export/gen/SimpleJavaGradleGenerator.java",
    "content": "package jadx.core.export.gen;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.List;\n\nimport jadx.api.ResourceFile;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.export.OutDirs;\nimport jadx.core.export.TemplateFile;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class SimpleJavaGradleGenerator implements IExportGradleGenerator {\n\tprivate final RootNode root;\n\tprivate final File projectDir;\n\tprivate final List<ResourceFile> resources;\n\n\tprivate OutDirs outDirs;\n\tprivate File appDir;\n\n\tpublic SimpleJavaGradleGenerator(RootNode root, File projectDir, List<ResourceFile> resources) {\n\t\tthis.root = root;\n\t\tthis.projectDir = projectDir;\n\t\tthis.resources = resources;\n\t}\n\n\t@Override\n\tpublic void init() {\n\t\tappDir = new File(projectDir, \"app\");\n\t\tFile srcOutDir = new File(appDir, \"src/main/java\");\n\t\tFile resOutDir = new File(appDir, \"src/main/resources\");\n\t\toutDirs = new OutDirs(srcOutDir, resOutDir);\n\t}\n\n\t@Override\n\tpublic void generateFiles() {\n\t\ttry {\n\t\t\tsaveSettingsGradle();\n\t\t\tsaveBuildGradle();\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to generate gradle files\", e);\n\t\t}\n\t}\n\n\tprivate void saveSettingsGradle() throws IOException {\n\t\tTemplateFile tmpl = TemplateFile.fromResources(\"/export/java/settings.gradle.kts.tmpl\");\n\t\ttmpl.add(\"projectName\", GradleGeneratorTools.guessProjectName(root));\n\t\ttmpl.save(new File(projectDir, \"settings.gradle.kts\"));\n\t}\n\n\tprivate void saveBuildGradle() throws IOException {\n\t\tTemplateFile tmpl = TemplateFile.fromResources(\"/export/java/build.gradle.kts.tmpl\");\n\t\ttmpl.save(new File(appDir, \"build.gradle.kts\"));\n\t}\n\n\t@Override\n\tpublic OutDirs getOutDirs() {\n\t\treturn outDirs;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/plugins/AppContext.java",
    "content": "package jadx.core.plugins;\n\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.gui.JadxGuiContext;\nimport jadx.core.plugins.files.IJadxFilesGetter;\n\npublic class AppContext {\n\n\tprivate @Nullable JadxGuiContext guiContext;\n\n\tprivate IJadxFilesGetter filesGetter;\n\n\tpublic @Nullable JadxGuiContext getGuiContext() {\n\t\treturn guiContext;\n\t}\n\n\tpublic void setGuiContext(@Nullable JadxGuiContext guiContext) {\n\t\tthis.guiContext = guiContext;\n\t}\n\n\tpublic IJadxFilesGetter getFilesGetter() {\n\t\treturn Objects.requireNonNull(filesGetter);\n\t}\n\n\tpublic void setFilesGetter(IJadxFilesGetter filesGetter) {\n\t\tthis.filesGetter = filesGetter;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/plugins/JadxPluginManager.java",
    "content": "package jadx.core.plugins;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeMap;\nimport java.util.TreeSet;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxDecompiler;\nimport jadx.api.plugins.JadxPlugin;\nimport jadx.api.plugins.input.JadxCodeInput;\nimport jadx.api.plugins.loader.JadxPluginLoader;\nimport jadx.api.plugins.options.JadxPluginOptions;\nimport jadx.api.plugins.options.OptionDescription;\nimport jadx.core.plugins.versions.VerifyRequiredVersion;\n\npublic class JadxPluginManager {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxPluginManager.class);\n\n\tprivate final JadxDecompiler decompiler;\n\tprivate final JadxPluginsData pluginsData;\n\tprivate final Set<String> disabledPlugins;\n\tprivate final SortedSet<PluginContext> allPlugins = new TreeSet<>();\n\tprivate final SortedSet<PluginContext> resolvedPlugins = new TreeSet<>();\n\tprivate final Map<String, String> provideSuggestions = new TreeMap<>();\n\n\tprivate final List<Consumer<PluginContext>> addPluginListeners = new ArrayList<>();\n\n\tpublic JadxPluginManager(JadxDecompiler decompiler) {\n\t\tthis.decompiler = decompiler;\n\t\tthis.pluginsData = new JadxPluginsData(decompiler, this);\n\t\tthis.disabledPlugins = decompiler.getArgs().getDisabledPlugins();\n\t}\n\n\t/**\n\t * Add suggestion how to resolve conflicting plugins\n\t */\n\tpublic void providesSuggestion(String provides, String pluginId) {\n\t\tprovideSuggestions.put(provides, pluginId);\n\t}\n\n\tpublic void load(JadxPluginLoader pluginLoader) {\n\t\tallPlugins.clear();\n\t\tVerifyRequiredVersion verifyRequiredVersion = new VerifyRequiredVersion();\n\t\tfor (JadxPlugin plugin : pluginLoader.load()) {\n\t\t\taddPlugin(plugin, verifyRequiredVersion);\n\t\t}\n\t\tresolve();\n\t}\n\n\tpublic void register(JadxPlugin plugin) {\n\t\tObjects.requireNonNull(plugin);\n\t\tPluginContext addedPlugin = addPlugin(plugin, new VerifyRequiredVersion());\n\t\tif (addedPlugin == null) {\n\t\t\tLOG.debug(\"Can't register plugin, it was disabled: {}\", plugin.getPluginInfo().getPluginId());\n\t\t\treturn;\n\t\t}\n\t\tLOG.debug(\"Register plugin: {}\", addedPlugin.getPluginId());\n\t\tresolve();\n\t}\n\n\tprivate @Nullable PluginContext addPlugin(JadxPlugin plugin, VerifyRequiredVersion verifyRequiredVersion) {\n\t\tPluginContext pluginContext = new PluginContext(decompiler, pluginsData, plugin);\n\t\tif (disabledPlugins.contains(pluginContext.getPluginId())) {\n\t\t\treturn null;\n\t\t}\n\t\tString requiredJadxVersion = pluginContext.getPluginInfo().getRequiredJadxVersion();\n\t\tif (!verifyRequiredVersion.isCompatible(requiredJadxVersion)) {\n\t\t\tLOG.warn(\"Plugin '{}' not loaded: requires '{}' jadx version which it is not compatible with current: {}\",\n\t\t\t\t\tpluginContext, requiredJadxVersion, verifyRequiredVersion.getJadxVersion());\n\t\t\treturn null;\n\t\t}\n\t\tLOG.debug(\"Loading plugin: {}\", pluginContext);\n\t\tif (!allPlugins.add(pluginContext)) {\n\t\t\tthrow new IllegalArgumentException(\"Duplicate plugin id: \" + pluginContext + \", class \" + plugin.getClass());\n\t\t}\n\t\taddPluginListeners.forEach(l -> l.accept(pluginContext));\n\t\treturn pluginContext;\n\t}\n\n\tpublic boolean unload(String pluginId) {\n\t\tboolean result = allPlugins.removeIf(context -> {\n\t\t\tif (context.getPluginId().equals(pluginId)) {\n\t\t\t\tLOG.debug(\"Unload plugin: {}\", pluginId);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t});\n\t\tresolve();\n\t\treturn result;\n\t}\n\n\tpublic SortedSet<PluginContext> getAllPluginContexts() {\n\t\treturn allPlugins;\n\t}\n\n\tpublic SortedSet<PluginContext> getResolvedPluginContexts() {\n\t\treturn resolvedPlugins;\n\t}\n\n\tprivate synchronized void resolve() {\n\t\tMap<String, List<PluginContext>> provides = allPlugins.stream()\n\t\t\t\t.collect(Collectors.groupingBy(p -> p.getPluginInfo().getProvides()));\n\t\tList<PluginContext> resolved = new ArrayList<>(provides.size());\n\t\tprovides.forEach((provide, list) -> {\n\t\t\tif (list.size() == 1) {\n\t\t\t\tresolved.add(list.get(0));\n\t\t\t} else {\n\t\t\t\tString suggestion = provideSuggestions.get(provide);\n\t\t\t\tif (suggestion != null) {\n\t\t\t\t\tlist.stream().filter(p -> p.getPluginId().equals(suggestion))\n\t\t\t\t\t\t\t.findFirst()\n\t\t\t\t\t\t\t.ifPresent(resolved::add);\n\t\t\t\t} else {\n\t\t\t\t\tPluginContext selected = list.get(0);\n\t\t\t\t\tresolved.add(selected);\n\t\t\t\t\tLOG.debug(\"Select providing '{}' plugin '{}', candidates: {}\", provide, selected, list);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tresolvedPlugins.clear();\n\t\tresolvedPlugins.addAll(resolved);\n\t}\n\n\tpublic void initAll() {\n\t\tinit(allPlugins);\n\t}\n\n\tpublic void initResolved() {\n\t\tinit(resolvedPlugins);\n\t}\n\n\tpublic void init(SortedSet<PluginContext> pluginContexts) {\n\t\tAppContext defAppContext = buildDefaultAppContext();\n\t\tfor (PluginContext context : pluginContexts) {\n\t\t\ttry {\n\t\t\t\tif (context.getAppContext() == null) {\n\t\t\t\t\tcontext.setAppContext(defAppContext);\n\t\t\t\t}\n\t\t\t\tcontext.init();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to init plugin: {}\", context.getPluginId(), e);\n\t\t\t}\n\t\t}\n\t\tfor (PluginContext context : pluginContexts) {\n\t\t\tJadxPluginOptions options = context.getOptions();\n\t\t\tif (options != null) {\n\t\t\t\tverifyOptions(context, options);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void unloadAll() {\n\t\tunload(allPlugins);\n\t}\n\n\tpublic void unloadResolved() {\n\t\tunload(resolvedPlugins);\n\t}\n\n\tpublic void unload(SortedSet<PluginContext> pluginContexts) {\n\t\tfor (PluginContext context : pluginContexts) {\n\t\t\ttry {\n\t\t\t\tcontext.unload();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.warn(\"Failed to unload plugin: {}\", context.getPluginId(), e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate AppContext buildDefaultAppContext() {\n\t\tAppContext appContext = new AppContext();\n\t\tappContext.setGuiContext(null);\n\t\tappContext.setFilesGetter(decompiler.getArgs().getFilesGetter());\n\t\treturn appContext;\n\t}\n\n\tprivate void verifyOptions(PluginContext pluginContext, JadxPluginOptions options) {\n\t\tString pluginId = pluginContext.getPluginId();\n\t\tList<OptionDescription> descriptions = options.getOptionsDescriptions();\n\t\tif (descriptions == null) {\n\t\t\tthrow new IllegalArgumentException(\"Null option descriptions in plugin id: \" + pluginId);\n\t\t}\n\t\tString prefix = pluginId + '.';\n\t\tdescriptions.forEach(descObj -> {\n\t\t\tString optName = descObj.name();\n\t\t\tif (optName == null || !optName.startsWith(prefix)) {\n\t\t\t\tthrow new IllegalArgumentException(\"Plugin option name should start with plugin id: '\" + prefix + \"', option: \" + optName);\n\t\t\t}\n\t\t\tString desc = descObj.description();\n\t\t\tif (desc == null || desc.isEmpty()) {\n\t\t\t\tthrow new IllegalArgumentException(\"Plugin option description not set, plugin: \" + pluginId);\n\t\t\t}\n\t\t\tList<String> values = descObj.values();\n\t\t\tif (values == null) {\n\t\t\t\tthrow new IllegalArgumentException(\"Plugin option values is null, option: \" + optName + \", plugin: \" + pluginId);\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic List<JadxCodeInput> getCodeInputs() {\n\t\treturn getResolvedPluginContexts()\n\t\t\t\t.stream()\n\t\t\t\t.flatMap(p -> p.getCodeInputs().stream())\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tpublic void registerAddPluginListener(Consumer<PluginContext> listener) {\n\t\tthis.addPluginListeners.add(listener);\n\t\t// run for already added plugins\n\t\tgetAllPluginContexts().forEach(listener);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/plugins/JadxPluginsData.java",
    "content": "package jadx.core.plugins;\n\nimport jadx.api.JadxDecompiler;\nimport jadx.api.plugins.JadxPlugin;\nimport jadx.api.plugins.data.IJadxPlugins;\nimport jadx.api.plugins.data.JadxPluginRuntimeData;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class JadxPluginsData implements IJadxPlugins {\n\n\tprivate final JadxDecompiler decompiler;\n\tprivate final JadxPluginManager pluginManager;\n\n\tpublic JadxPluginsData(JadxDecompiler decompiler, JadxPluginManager pluginManager) {\n\t\tthis.decompiler = decompiler;\n\t\tthis.pluginManager = pluginManager;\n\t}\n\n\t@Override\n\tpublic JadxPluginRuntimeData getById(String pluginId) {\n\t\treturn pluginManager.getResolvedPluginContexts()\n\t\t\t\t.stream()\n\t\t\t\t.filter(p -> p.getPluginId().equals(pluginId))\n\t\t\t\t.findFirst()\n\t\t\t\t.orElseThrow(() -> new JadxRuntimeException(\"Plugin with id '\" + pluginId + \"' not found\"));\n\t}\n\n\t@Override\n\tpublic JadxPluginRuntimeData getProviding(String provideId) {\n\t\treturn pluginManager.getResolvedPluginContexts()\n\t\t\t\t.stream()\n\t\t\t\t.filter(p -> p.getPluginInfo().getProvides().equals(provideId))\n\t\t\t\t.findFirst()\n\t\t\t\t.orElseThrow(() -> new JadxRuntimeException(\"Plugin providing '\" + provideId + \"' not found\"));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic <P extends JadxPlugin> P getInstance(Class<P> pluginCls) {\n\t\treturn pluginManager.getResolvedPluginContexts()\n\t\t\t\t.stream()\n\t\t\t\t.filter(p -> p.getPluginInstance().getClass().equals(pluginCls))\n\t\t\t\t.map(p -> (P) p.getPluginInstance())\n\t\t\t\t.findFirst()\n\t\t\t\t.orElseThrow(() -> new JadxRuntimeException(\"Plugin class '\" + pluginCls + \"' not found\"));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/plugins/PluginContext.java",
    "content": "package jadx.core.plugins;\n\nimport java.io.Closeable;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Supplier;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.JadxDecompiler;\nimport jadx.api.plugins.JadxPlugin;\nimport jadx.api.plugins.JadxPluginContext;\nimport jadx.api.plugins.JadxPluginInfo;\nimport jadx.api.plugins.data.IJadxFiles;\nimport jadx.api.plugins.data.IJadxPlugins;\nimport jadx.api.plugins.data.JadxPluginRuntimeData;\nimport jadx.api.plugins.events.IJadxEvents;\nimport jadx.api.plugins.gui.JadxGuiContext;\nimport jadx.api.plugins.input.ICodeLoader;\nimport jadx.api.plugins.input.JadxCodeInput;\nimport jadx.api.plugins.input.data.impl.MergeCodeLoader;\nimport jadx.api.plugins.options.JadxPluginOptions;\nimport jadx.api.plugins.options.OptionDescription;\nimport jadx.api.plugins.options.OptionFlag;\nimport jadx.api.plugins.pass.JadxPass;\nimport jadx.api.plugins.resources.IResourcesLoader;\nimport jadx.core.plugins.files.JadxFilesData;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.zip.ZipReader;\n\npublic class PluginContext implements JadxPluginContext, JadxPluginRuntimeData, Comparable<PluginContext> {\n\tprivate final JadxDecompiler decompiler;\n\tprivate final JadxPluginsData pluginsData;\n\tprivate final JadxPlugin plugin;\n\tprivate final JadxPluginInfo pluginInfo;\n\tprivate final ClassLoader pluginClassLoader;\n\n\tprivate AppContext appContext;\n\n\tprivate final List<JadxCodeInput> codeInputs = new ArrayList<>();\n\tprivate @Nullable JadxPluginOptions options;\n\tprivate @Nullable Supplier<String> inputsHashSupplier;\n\n\tprivate boolean initialized;\n\n\tPluginContext(JadxDecompiler decompiler, JadxPluginsData pluginsData, JadxPlugin plugin) {\n\t\tthis.decompiler = decompiler;\n\t\tthis.pluginsData = pluginsData;\n\t\tthis.plugin = plugin;\n\t\tthis.pluginInfo = plugin.getPluginInfo();\n\t\tthis.pluginClassLoader = plugin.getClass().getClassLoader();\n\t}\n\n\tpublic void init() {\n\t\tclassLoaderWrap(() -> {\n\t\t\tplugin.init(this);\n\t\t\tinitialized = true;\n\t\t});\n\t}\n\n\tpublic void unload() {\n\t\tif (initialized) {\n\t\t\tclassLoaderWrap(plugin::unload);\n\t\t}\n\t}\n\n\tpublic void classLoaderWrap(Runnable task) {\n\t\tThread thread = Thread.currentThread();\n\t\tClassLoader prevClassLoader = thread.getContextClassLoader();\n\t\tthread.setContextClassLoader(pluginClassLoader);\n\t\ttry {\n\t\t\ttask.run();\n\t\t} finally {\n\t\t\tthread.setContextClassLoader(prevClassLoader);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isInitialized() {\n\t\treturn initialized;\n\t}\n\n\t@Override\n\tpublic JadxArgs getArgs() {\n\t\treturn decompiler.getArgs();\n\t}\n\n\t@Override\n\tpublic JadxDecompiler getDecompiler() {\n\t\treturn decompiler;\n\t}\n\n\t@Override\n\tpublic void addPass(JadxPass pass) {\n\t\tdecompiler.addCustomPass(pass);\n\t}\n\n\t@Override\n\tpublic void addCodeInput(JadxCodeInput codeInput) {\n\t\tthis.codeInputs.add(codeInput);\n\t}\n\n\t@Override\n\tpublic List<JadxCodeInput> getCodeInputs() {\n\t\treturn codeInputs;\n\t}\n\n\t@Override\n\tpublic void registerOptions(JadxPluginOptions options) {\n\t\ttry {\n\t\t\tthis.options = Objects.requireNonNull(options);\n\t\t\toptions.setOptions(getArgs().getPluginOptions());\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to apply options for plugin: \" + getPluginId(), e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void registerInputsHashSupplier(Supplier<String> supplier) {\n\t\tthis.inputsHashSupplier = supplier;\n\t}\n\n\t@Override\n\tpublic String getInputsHash() {\n\t\tif (inputsHashSupplier == null) {\n\t\t\treturn defaultOptionsHash();\n\t\t}\n\t\ttry {\n\t\t\treturn inputsHashSupplier.get();\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to get inputs hash for plugin: \" + getPluginId(), e);\n\t\t}\n\t}\n\n\tprivate String defaultOptionsHash() {\n\t\tif (options == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\tMap<String, String> allOptions = getArgs().getPluginOptions();\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (OptionDescription optDesc : options.getOptionsDescriptions()) {\n\t\t\tif (!optDesc.getFlags().contains(OptionFlag.NOT_CHANGING_CODE)) {\n\t\t\t\tsb.append(':').append(allOptions.get(optDesc.name()));\n\t\t\t}\n\t\t}\n\t\treturn FileUtils.md5Sum(sb.toString());\n\t}\n\n\t@Override\n\tpublic IJadxEvents events() {\n\t\treturn decompiler.events();\n\t}\n\n\t@Override\n\tpublic IResourcesLoader getResourcesLoader() {\n\t\treturn decompiler.getResourcesLoader();\n\t}\n\n\tpublic AppContext getAppContext() {\n\t\treturn appContext;\n\t}\n\n\tpublic void setAppContext(AppContext appContext) {\n\t\tthis.appContext = appContext;\n\t}\n\n\t@Override\n\tpublic @Nullable JadxGuiContext getGuiContext() {\n\t\treturn appContext.getGuiContext();\n\t}\n\n\t@Override\n\tpublic JadxPlugin getPluginInstance() {\n\t\treturn plugin;\n\t}\n\n\t@Override\n\tpublic JadxPluginInfo getPluginInfo() {\n\t\treturn pluginInfo;\n\t}\n\n\t@Override\n\tpublic String getPluginId() {\n\t\treturn pluginInfo.getPluginId();\n\t}\n\n\t@Override\n\tpublic @Nullable JadxPluginOptions getOptions() {\n\t\treturn options;\n\t}\n\n\t@Override\n\tpublic IJadxPlugins plugins() {\n\t\treturn pluginsData;\n\t}\n\n\t@Override\n\tpublic IJadxFiles files() {\n\t\treturn new JadxFilesData(pluginInfo, appContext.getFilesGetter());\n\t}\n\n\t@Override\n\tpublic ICodeLoader loadCodeFiles(List<Path> files, @Nullable Closeable closeable) {\n\t\treturn new MergeCodeLoader(\n\t\t\t\tUtils.collectionMap(codeInputs, codeInput -> codeInput.loadFiles(files)),\n\t\t\t\tcloseable);\n\t}\n\n\t@Override\n\tpublic ZipReader getZipReader() {\n\t\treturn decompiler.getZipReader();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object other) {\n\t\tif (this == other) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(other instanceof PluginContext)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn this.getPluginId().equals(((PluginContext) other).getPluginId());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn getPluginId().hashCode();\n\t}\n\n\t@Override\n\tpublic int compareTo(PluginContext other) {\n\t\treturn this.getPluginId().compareTo(other.getPluginId());\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getPluginId();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/plugins/events/JadxEventsImpl.java",
    "content": "package jadx.core.plugins.events;\n\nimport java.util.function.Consumer;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.events.IJadxEvent;\nimport jadx.api.plugins.events.IJadxEvents;\nimport jadx.api.plugins.events.JadxEventType;\nimport jadx.core.Consts;\n\npublic class JadxEventsImpl implements IJadxEvents {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxEventsImpl.class);\n\n\tprivate final JadxEventsManager manager = new JadxEventsManager();\n\n\t@Override\n\tpublic void send(IJadxEvent event) {\n\t\tif (Consts.DEBUG_EVENTS) {\n\t\t\tLOG.debug(\"Sending event: {}\", event);\n\t\t}\n\t\tmanager.send(event);\n\t}\n\n\t@Override\n\tpublic <E extends IJadxEvent> void addListener(JadxEventType<E> eventType, Consumer<E> listener) {\n\t\tmanager.addListener(eventType, listener);\n\t\tif (Consts.DEBUG_EVENTS) {\n\t\t\tLOG.debug(\"add listener for: {}, stats: {}\", eventType, manager.listenersDebugStats());\n\t\t}\n\t}\n\n\t@Override\n\tpublic <E extends IJadxEvent> void removeListener(JadxEventType<E> eventType, Consumer<E> listener) {\n\t\tmanager.removeListener(eventType, listener);\n\t\tif (Consts.DEBUG_EVENTS) {\n\t\t\tLOG.debug(\"remove listener for: {}, stats: {}\", eventType, manager.listenersDebugStats());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void reset() {\n\t\tmanager.reset();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/plugins/events/JadxEventsManager.java",
    "content": "package jadx.core.plugins.events;\n\nimport java.util.ArrayList;\nimport java.util.IdentityHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.api.plugins.events.IJadxEvent;\nimport jadx.api.plugins.events.JadxEventType;\n\n/**\n * Handle events sending and receiving\n */\npublic class JadxEventsManager {\n\n\tprivate final Map<JadxEventType<?>, List<Consumer<IJadxEvent>>> listeners = new IdentityHashMap<>();\n\n\tprivate final ExecutorService eventsThreadPool;\n\n\tpublic JadxEventsManager() {\n\t\t// TODO: allow to change threading strategy\n\t\tthis.eventsThreadPool = Executors.newSingleThreadExecutor(makeThreadFactory());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic synchronized <E extends IJadxEvent> void addListener(JadxEventType<E> eventType, Consumer<E> listener) {\n\t\tlisteners.computeIfAbsent(eventType, et -> new ArrayList<>())\n\t\t\t\t.add((Consumer<IJadxEvent>) listener);\n\t}\n\n\tpublic synchronized <E extends IJadxEvent> boolean removeListener(JadxEventType<E> eventType, Consumer<E> listener) {\n\t\tList<Consumer<IJadxEvent>> eventListeners = listeners.get(eventType);\n\t\tif (eventListeners != null) {\n\t\t\treturn eventListeners.remove(listener);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic synchronized void send(IJadxEvent event) {\n\t\tList<Consumer<IJadxEvent>> consumers = listeners.get(event.getType());\n\t\tif (consumers != null) {\n\t\t\tfor (Consumer<IJadxEvent> consumer : consumers) {\n\t\t\t\teventsThreadPool.execute(() -> consumer.accept(event));\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic synchronized void reset() {\n\t\tlisteners.clear();\n\t}\n\n\tprivate static ThreadFactory makeThreadFactory() {\n\t\treturn new ThreadFactory() {\n\t\t\tprivate final AtomicInteger threadNumber = new AtomicInteger(0);\n\n\t\t\t@Override\n\t\t\tpublic Thread newThread(@NotNull Runnable r) {\n\t\t\t\treturn new Thread(r, \"jadx-events-thread-\" + threadNumber.incrementAndGet());\n\t\t\t}\n\t\t};\n\t}\n\n\tpublic String listenersDebugStats() {\n\t\treturn listeners.entrySet()\n\t\t\t\t.stream()\n\t\t\t\t.filter(p -> !p.getValue().isEmpty())\n\t\t\t\t.map(p -> p.getKey() + \":\" + p.getValue().size())\n\t\t\t\t.collect(Collectors.joining(\", \", \"[\", \"]\"));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/plugins/files/IJadxFilesGetter.java",
    "content": "package jadx.core.plugins.files;\n\nimport java.nio.file.Path;\n\npublic interface IJadxFilesGetter {\n\n\tPath getConfigDir();\n\n\tPath getCacheDir();\n\n\tPath getTempDir();\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/plugins/files/JadxFilesData.java",
    "content": "package jadx.core.plugins.files;\n\nimport java.nio.file.Path;\n\nimport jadx.api.plugins.JadxPluginInfo;\nimport jadx.api.plugins.data.IJadxFiles;\nimport jadx.core.utils.files.FileUtils;\n\npublic class JadxFilesData implements IJadxFiles {\n\tprivate static final String PLUGINS_DATA_DIR = \"plugins-data\";\n\n\tprivate final JadxPluginInfo pluginInfo;\n\tprivate final IJadxFilesGetter filesGetter;\n\n\tpublic JadxFilesData(JadxPluginInfo pluginInfo, IJadxFilesGetter filesGetter) {\n\t\tthis.pluginInfo = pluginInfo;\n\t\tthis.filesGetter = filesGetter;\n\t}\n\n\t@Override\n\tpublic Path getPluginCacheDir() {\n\t\treturn toPluginPath(filesGetter.getCacheDir());\n\t}\n\n\t@Override\n\tpublic Path getPluginConfigDir() {\n\t\treturn toPluginPath(filesGetter.getConfigDir());\n\t}\n\n\t@Override\n\tpublic Path getPluginTempDir() {\n\t\treturn toPluginPath(filesGetter.getTempDir());\n\t}\n\n\tprivate Path toPluginPath(Path dir) {\n\t\tPath dirPath = dir.resolve(PLUGINS_DATA_DIR).resolve(pluginInfo.getPluginId());\n\t\tFileUtils.makeDirs(dirPath);\n\t\treturn dirPath;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/plugins/files/SingleDirFilesGetter.java",
    "content": "package jadx.core.plugins.files;\n\nimport java.nio.file.Path;\n\nimport jadx.core.utils.files.FileUtils;\n\n/**\n * Use single directory for all jadx files\n */\npublic class SingleDirFilesGetter implements IJadxFilesGetter {\n\tprivate final Path baseDir;\n\n\tpublic SingleDirFilesGetter(Path baseDir) {\n\t\tthis.baseDir = baseDir;\n\t}\n\n\t@Override\n\tpublic Path getConfigDir() {\n\t\treturn makeSubDir(\"config\");\n\t}\n\n\t@Override\n\tpublic Path getCacheDir() {\n\t\treturn makeSubDir(\"cache\");\n\t}\n\n\t@Override\n\tpublic Path getTempDir() {\n\t\treturn makeSubDir(\"temp\");\n\t}\n\n\tprivate Path makeSubDir(String subDir) {\n\t\tPath dir = baseDir.resolve(subDir);\n\t\tFileUtils.makeDirs(dir);\n\t\treturn dir;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/plugins/files/TempFilesGetter.java",
    "content": "package jadx.core.plugins.files;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport jadx.core.utils.files.FileUtils;\n\npublic class TempFilesGetter implements IJadxFilesGetter {\n\n\tpublic static final TempFilesGetter INSTANCE = new TempFilesGetter();\n\n\tprivate static final class TempRootHolder {\n\t\tpublic static final Path TEMP_ROOT_DIR;\n\n\t\tstatic {\n\t\t\ttry {\n\t\t\t\tTEMP_ROOT_DIR = Files.createTempDirectory(\"jadx-temp-\");\n\t\t\t\tTEMP_ROOT_DIR.toFile().deleteOnExit();\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new RuntimeException(\"Failed to create temp directory\", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate TempFilesGetter() {\n\t}\n\n\t@Override\n\tpublic Path getConfigDir() {\n\t\treturn makeSubDir(\"config\");\n\t}\n\n\t@Override\n\tpublic Path getCacheDir() {\n\t\treturn makeSubDir(\"cache\");\n\t}\n\n\t@Override\n\tpublic Path getTempDir() {\n\t\treturn makeSubDir(\"tmp\");\n\t}\n\n\tprivate Path makeSubDir(String subDir) {\n\t\tPath dir = TempRootHolder.TEMP_ROOT_DIR.resolve(subDir);\n\t\tFileUtils.makeDirs(dir);\n\t\treturn dir;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/plugins/versions/VerifyRequiredVersion.java",
    "content": "package jadx.core.plugins.versions;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.Jadx;\n\npublic class VerifyRequiredVersion {\n\n\tpublic static boolean isJadxCompatible(@Nullable String reqVersionStr) {\n\t\treturn new VerifyRequiredVersion().isCompatible(reqVersionStr);\n\t}\n\n\tpublic static void verify(String requiredJadxVersion) {\n\t\ttry {\n\t\t\tparse(requiredJadxVersion);\n\t\t} catch (Exception e) {\n\t\t\tthrow new IllegalArgumentException(\"Malformed 'requiredJadxVersion': \" + e.getMessage(), e);\n\t\t}\n\t}\n\n\tprivate final String jadxVersion;\n\tprivate final boolean unstable;\n\n\tprivate final boolean dev;\n\n\tpublic VerifyRequiredVersion() {\n\t\tthis(Jadx.getVersion());\n\t}\n\n\tpublic VerifyRequiredVersion(String jadxVersion) {\n\t\tthis.jadxVersion = jadxVersion;\n\t\tthis.unstable = jadxVersion.startsWith(\"r\");\n\t\tthis.dev = jadxVersion.equals(Jadx.VERSION_DEV);\n\t}\n\n\tpublic boolean isCompatible(@Nullable String reqVersionStr) {\n\t\tif (reqVersionStr == null || reqVersionStr.isEmpty()) {\n\t\t\treturn true;\n\t\t}\n\t\tRequiredVersionData reqVer = parse(reqVersionStr);\n\t\tif (dev) {\n\t\t\t// keep version str parsing for verification\n\t\t\treturn true;\n\t\t}\n\t\tif (unstable) {\n\t\t\treturn VersionComparator.checkAndCompare(jadxVersion, reqVer.getUnstableRev()) >= 0;\n\t\t}\n\t\treturn VersionComparator.checkAndCompare(jadxVersion, reqVer.getReleaseVer()) >= 0;\n\t}\n\n\tpublic String getJadxVersion() {\n\t\treturn jadxVersion;\n\t}\n\n\tprivate static final Pattern REQ_VER_FORMAT = Pattern.compile(\"(\\\\d+\\\\.\\\\d+\\\\.\\\\d+),\\\\s+(r\\\\d+)\");\n\n\tprivate static RequiredVersionData parse(String reqVersionStr) {\n\t\tMatcher matcher = REQ_VER_FORMAT.matcher(reqVersionStr);\n\t\tif (!matcher.matches()) {\n\t\t\tthrow new RuntimeException(\"Expect format: \" + REQ_VER_FORMAT + \", got: \" + reqVersionStr);\n\t\t}\n\t\treturn new RequiredVersionData(matcher.group(1), matcher.group(2));\n\t}\n\n\tprivate static final class RequiredVersionData {\n\t\tprivate final String releaseVer;\n\t\tprivate final String unstableRev;\n\n\t\tprivate RequiredVersionData(String releaseVer, String unstableRev) {\n\t\t\tthis.releaseVer = releaseVer;\n\t\t\tthis.unstableRev = unstableRev;\n\t\t}\n\n\t\tpublic String getReleaseVer() {\n\t\t\treturn releaseVer;\n\t\t}\n\n\t\tpublic String getUnstableRev() {\n\t\t\treturn unstableRev;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/plugins/versions/VersionComparator.java",
    "content": "package jadx.core.plugins.versions;\n\npublic class VersionComparator {\n\n\tprivate VersionComparator() {\n\t}\n\n\tpublic static int checkAndCompare(String str1, String str2) {\n\t\treturn compare(clean(str1), clean(str2));\n\t}\n\n\tprivate static String clean(String str) {\n\t\tif (str == null || str.isEmpty()) {\n\t\t\treturn \"\";\n\t\t}\n\t\tString result = str.trim().toLowerCase();\n\t\tif (result.startsWith(\"jadx-gui-\")) {\n\t\t\tresult = result.substring(9);\n\t\t}\n\t\tif (result.startsWith(\"jadx-\")) {\n\t\t\tresult = result.substring(5);\n\t\t}\n\t\tif (result.charAt(0) == 'v') {\n\t\t\tresult = result.substring(1);\n\t\t}\n\t\tif (result.charAt(0) == 'r') {\n\t\t\tresult = result.substring(1);\n\t\t\tint dot = result.indexOf('.');\n\t\t\tif (dot != -1) {\n\t\t\t\tresult = result.substring(0, dot);\n\t\t\t}\n\t\t}\n\t\t// treat a package version as part of version\n\t\tresult = result.replace('-', '.');\n\t\treturn result;\n\t}\n\n\tprivate static int compare(String str1, String str2) {\n\t\tString[] s1 = str1.split(\"\\\\.\");\n\t\tint l1 = s1.length;\n\t\tString[] s2 = str2.split(\"\\\\.\");\n\t\tint l2 = s2.length;\n\n\t\tint i = 0;\n\t\t// skip equals parts\n\t\twhile (i < l1 && i < l2) {\n\t\t\tif (!s1[i].equals(s2[i])) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ti++;\n\t\t}\n\t\t// compare first non-equal ordinal number\n\t\tif (i < l1 && i < l2) {\n\t\t\treturn Integer.valueOf(s1[i]).compareTo(Integer.valueOf(s2[i]));\n\t\t}\n\t\tboolean checkFirst = l1 > l2;\n\t\tboolean zeroTail = isZeroTail(checkFirst ? s1 : s2, i);\n\t\tif (zeroTail) {\n\t\t\treturn 0;\n\t\t}\n\t\treturn checkFirst ? 1 : -1;\n\t}\n\n\tprivate static boolean isZeroTail(String[] arr, int pos) {\n\t\tfor (int i = pos; i < arr.length; i++) {\n\t\t\tif (Integer.parseInt(arr[i]) != 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/BetterName.java",
    "content": "package jadx.core.utils;\n\nimport java.util.HashSet;\nimport java.util.Locale;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.deobf.NameMapper;\n\npublic class BetterName {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(BetterName.class);\n\n\tprivate static final boolean DEBUG = false;\n\n\tprivate static final double TOLERANCE = 0.001;\n\n\t/**\n\t * Compares two class names and returns the \"better\" one.\n\t * If both names are equally good, {@code firstName} is returned.\n\t */\n\tpublic static String getBetterClassName(String firstName, String secondName) {\n\t\treturn getBetterName(firstName, secondName);\n\t}\n\n\t/**\n\t * Compares two resource names and returns the \"better\" one.\n\t * If both names are equally good, {@code firstName} is returned.\n\t */\n\tpublic static String getBetterResourceName(String firstName, String secondName) {\n\t\treturn getBetterName(firstName, secondName);\n\t}\n\n\tprivate static String getBetterName(String firstName, String secondName) {\n\t\tif (Objects.equals(firstName, secondName)) {\n\t\t\treturn firstName;\n\t\t}\n\n\t\tif (StringUtils.isEmpty(firstName) || StringUtils.isEmpty(secondName)) {\n\t\t\treturn StringUtils.notEmpty(firstName)\n\t\t\t\t\t? firstName\n\t\t\t\t\t: secondName;\n\t\t}\n\n\t\tfinal var firstResult = analyze(firstName);\n\t\tfinal var secondResult = analyze(secondName);\n\n\t\tif (firstResult.digitCount != 0 || secondResult.digitCount != 0) {\n\t\t\tfinal var firstRatio = (float) firstResult.digitCount / firstResult.length;\n\t\t\tfinal var secondRatio = (float) secondResult.digitCount / secondResult.length;\n\n\t\t\tif (Math.abs(secondRatio - firstRatio) >= TOLERANCE) {\n\t\t\t\treturn firstRatio <= secondRatio\n\t\t\t\t\t\t? firstName\n\t\t\t\t\t\t: secondName;\n\t\t\t}\n\t\t}\n\n\t\treturn firstResult.length >= secondResult.length\n\t\t\t\t? firstName\n\t\t\t\t: secondName;\n\t}\n\n\tprivate static AnalyzeResult analyze(String name) {\n\t\tfinal var result = new AnalyzeResult();\n\n\t\tStringUtils.visitCodePoints(name, cp -> {\n\t\t\tif (Character.isDigit(cp)) {\n\t\t\t\tresult.digitCount++;\n\t\t\t}\n\n\t\t\tresult.length++;\n\t\t});\n\n\t\treturn result;\n\t}\n\n\tprivate static class AnalyzeResult {\n\t\tprivate int length;\n\t\tprivate int digitCount;\n\t}\n\n\t/**\n\t * @deprecated Use {@link #getBetterClassName(String, String)} or\n\t *             {@link #getBetterResourceName(String, String)} instead.\n\t */\n\t@Deprecated\n\tpublic static String compareAndGet(String first, String second) {\n\t\tif (Objects.equals(first, second)) {\n\t\t\treturn first;\n\t\t}\n\t\tint firstRating = calcRating(first);\n\t\tint secondRating = calcRating(second);\n\t\tboolean firstBetter = firstRating >= secondRating;\n\t\tif (DEBUG) {\n\t\t\tif (firstBetter) {\n\t\t\t\tLOG.debug(\"Better name: '{}' > '{}' ({} > {})\", first, second, firstRating, secondRating);\n\t\t\t} else {\n\t\t\t\tLOG.debug(\"Better name: '{}' > '{}' ({} > {})\", second, first, secondRating, firstRating);\n\t\t\t}\n\t\t}\n\t\treturn firstBetter ? first : second;\n\t}\n\n\t/**\n\t * @deprecated This function is an implementation detail of deprecated\n\t *             {@link #compareAndGet(String, String)} and should not be used outside tests.\n\t */\n\t@Deprecated\n\tpublic static int calcRating(String str) {\n\t\tint rating = str.length() * 3;\n\t\trating += differentCharsCount(str) * 20;\n\n\t\tif (NameMapper.isAllCharsPrintable(str)) {\n\t\t\trating += 100;\n\t\t}\n\t\tif (NameMapper.isValidIdentifier(str)) {\n\t\t\trating += 50;\n\t\t}\n\t\tif (str.contains(\"_\")) {\n\t\t\t// rare in obfuscated names\n\t\t\trating += 100;\n\t\t}\n\t\treturn rating;\n\t}\n\n\t@Deprecated\n\tprivate static int differentCharsCount(String str) {\n\t\tString lower = str.toLowerCase(Locale.ROOT);\n\t\tSet<Integer> chars = new HashSet<>();\n\t\tStringUtils.visitCodePoints(lower, chars::add);\n\t\treturn chars.size();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/BlockInsnPair.java",
    "content": "package jadx.core.utils;\n\nimport java.util.Objects;\n\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic class BlockInsnPair {\n\tprivate final IBlock block;\n\tprivate final InsnNode insn;\n\n\tpublic BlockInsnPair(IBlock block, InsnNode insn) {\n\t\tthis.block = block;\n\t\tthis.insn = insn;\n\t}\n\n\tpublic IBlock getBlock() {\n\t\treturn block;\n\t}\n\n\tpublic InsnNode getInsn() {\n\t\treturn insn;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof BlockInsnPair)) {\n\t\t\treturn false;\n\t\t}\n\t\tBlockInsnPair that = (BlockInsnPair) o;\n\t\treturn block.equals(that.block) && insn.equals(that.insn);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(block, insn);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"BlockInsnPair{\" + block + \": \" + insn + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/BlockParentContainer.java",
    "content": "package jadx.core.utils;\n\nimport java.util.Objects;\n\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IContainer;\n\npublic class BlockParentContainer {\n\n\tprivate final IContainer parent;\n\tprivate final IBlock block;\n\n\tpublic BlockParentContainer(IContainer parent, IBlock block) {\n\t\tthis.parent = Objects.requireNonNull(parent);\n\t\tthis.block = Objects.requireNonNull(block);\n\t}\n\n\tpublic IBlock getBlock() {\n\t\treturn block;\n\t}\n\n\tpublic IContainer getParent() {\n\t\treturn parent;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"BlockParentContainer{\" + block + \", parent=\" + parent + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/BlockUtils.java",
    "content": "package jadx.core.utils;\n\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.BitSet;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Deque;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.LoopInfo;\nimport jadx.core.dex.attributes.nodes.PhiListAttr;\nimport jadx.core.dex.instructions.IfNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.PhiInsn;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.mods.TernaryInsn;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.Edge;\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.conditions.IfCondition;\nimport jadx.core.dex.trycatch.CatchAttr;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.utils.blocks.BlockSet;\nimport jadx.core.utils.blocks.DFSIteration;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class BlockUtils {\n\n\tprivate BlockUtils() {\n\t}\n\n\tpublic static BlockNode getBlockByOffset(int offset, Iterable<BlockNode> casesBlocks) {\n\t\tfor (BlockNode block : casesBlocks) {\n\t\t\tif (block.getStartOffset() == offset) {\n\t\t\t\treturn block;\n\t\t\t}\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Can't find block by offset: \"\n\t\t\t\t+ InsnUtils.formatOffset(offset)\n\t\t\t\t+ \" in list \" + casesBlocks);\n\t}\n\n\tpublic static BlockNode selectOther(BlockNode node, List<BlockNode> blocks) {\n\t\tList<BlockNode> list = blocks;\n\t\tif (list.size() > 2) {\n\t\t\tlist = cleanBlockList(list);\n\t\t}\n\t\tif (list.size() != 2) {\n\t\t\tthrow new JadxRuntimeException(\"Incorrect nodes count for selectOther: \" + node + \" in \" + list);\n\t\t}\n\t\tBlockNode first = list.get(0);\n\t\tif (first != node) {\n\t\t\treturn first;\n\t\t} else {\n\t\t\treturn list.get(1);\n\t\t}\n\t}\n\n\tpublic static BlockNode selectOtherSafe(BlockNode node, List<BlockNode> blocks) {\n\t\tint size = blocks.size();\n\t\tif (size == 1) {\n\t\t\tBlockNode first = blocks.get(0);\n\t\t\treturn first != node ? first : null;\n\t\t}\n\t\tif (size == 2) {\n\t\t\tBlockNode first = blocks.get(0);\n\t\t\treturn first != node ? first : blocks.get(1);\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static boolean isExceptionHandlerPath(BlockNode b) {\n\t\tif (b.contains(AType.EXC_HANDLER)\n\t\t\t\t|| b.contains(AFlag.EXC_BOTTOM_SPLITTER)\n\t\t\t\t|| b.contains(AFlag.REMOVE)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (b.contains(AFlag.SYNTHETIC)) {\n\t\t\tList<BlockNode> s = b.getSuccessors();\n\t\t\treturn s.size() == 1 && s.get(0).contains(AType.EXC_HANDLER);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Remove exception handlers from block nodes list\n\t */\n\tprivate static List<BlockNode> cleanBlockList(List<BlockNode> list) {\n\t\tList<BlockNode> ret = new ArrayList<>(list.size());\n\t\tfor (BlockNode block : list) {\n\t\t\tif (!isExceptionHandlerPath(block)) {\n\t\t\t\tret.add(block);\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n\n\t/**\n\t * Remove exception handlers from block nodes bitset\n\t */\n\tpublic static void cleanBitSet(MethodNode mth, BitSet bs) {\n\t\tfor (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {\n\t\t\tBlockNode block = mth.getBasicBlocks().get(i);\n\t\t\tif (isExceptionHandlerPath(block)) {\n\t\t\t\tbs.clear(i);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static boolean isBackEdge(BlockNode from, BlockNode to) {\n\t\tif (to == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (from.getCleanSuccessors().contains(to)) {\n\t\t\treturn false; // already checked\n\t\t}\n\t\treturn from.getSuccessors().contains(to);\n\t}\n\n\tpublic static boolean isFollowBackEdge(BlockNode block) {\n\t\tif (block == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (block.contains(AFlag.LOOP_START)) {\n\t\t\tList<BlockNode> predecessors = block.getPredecessors();\n\t\t\tif (predecessors.size() == 1) {\n\t\t\t\tBlockNode loopEndBlock = predecessors.get(0);\n\t\t\t\tif (loopEndBlock.contains(AFlag.LOOP_END)) {\n\t\t\t\t\tList<LoopInfo> loops = loopEndBlock.getAll(AType.LOOP);\n\t\t\t\t\tfor (LoopInfo loop : loops) {\n\t\t\t\t\t\tif (loop.getStart().equals(block) && loop.getEnd().equals(loopEndBlock)) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Check if instruction contains in block (use == for comparison, not equals)\n\t */\n\tpublic static boolean blockContains(BlockNode block, InsnNode insn) {\n\t\tfor (InsnNode bi : block.getInstructions()) {\n\t\t\tif (bi == insn) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static boolean checkFirstInsn(IBlock block, Predicate<InsnNode> predicate) {\n\t\tInsnNode insn = getFirstInsn(block);\n\t\treturn insn != null && predicate.test(insn);\n\t}\n\n\tpublic static boolean checkLastInsnType(IBlock block, InsnType expectedType) {\n\t\tInsnNode insn = getLastInsn(block);\n\t\treturn insn != null && insn.getType() == expectedType;\n\t}\n\n\tpublic static InsnNode getLastInsnWithType(IBlock block, InsnType expectedType) {\n\t\tInsnNode insn = getLastInsn(block);\n\t\tif (insn != null && insn.getType() == expectedType) {\n\t\t\treturn insn;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static int getFirstSourceLine(IBlock block) {\n\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\tint line = insn.getSourceLine();\n\t\t\tif (line != 0) {\n\t\t\t\treturn line;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\t@Nullable\n\tpublic static InsnNode getFirstInsn(@Nullable IBlock block) {\n\t\tif (block == null) {\n\t\t\treturn null;\n\t\t}\n\t\tList<InsnNode> insns = block.getInstructions();\n\t\tif (insns.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn insns.get(0);\n\t}\n\n\t@Nullable\n\tpublic static InsnNode getLastInsn(@Nullable IBlock block) {\n\t\tif (block == null) {\n\t\t\treturn null;\n\t\t}\n\t\tList<InsnNode> insns = block.getInstructions();\n\t\tif (insns.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn insns.get(insns.size() - 1);\n\t}\n\n\tpublic static boolean isExitBlock(MethodNode mth, BlockNode block) {\n\t\tif (block == mth.getExitBlock()) {\n\t\t\treturn true;\n\t\t}\n\t\treturn isExitBlock(block);\n\t}\n\n\tpublic static boolean isExitBlock(BlockNode block) {\n\t\tList<BlockNode> successors = block.getSuccessors();\n\t\tif (successors.isEmpty()) {\n\t\t\treturn true;\n\t\t}\n\t\tif (successors.size() == 1) {\n\t\t\tBlockNode next = successors.get(0);\n\t\t\treturn next.getSuccessors().isEmpty();\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static boolean containsExitInsn(IBlock block) {\n\t\tInsnNode lastInsn = BlockUtils.getLastInsn(block);\n\t\tif (lastInsn == null) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnType type = lastInsn.getType();\n\t\treturn type == InsnType.RETURN\n\t\t\t\t|| type == InsnType.THROW\n\t\t\t\t|| type == InsnType.BREAK\n\t\t\t\t|| type == InsnType.CONTINUE;\n\t}\n\n\tpublic static @Nullable BlockNode getBlockByInsn(MethodNode mth, @Nullable InsnNode insn) {\n\t\treturn getBlockByInsn(mth, insn, mth.getBasicBlocks());\n\t}\n\n\tpublic static @Nullable BlockNode getBlockByInsn(MethodNode mth, @Nullable InsnNode insn, List<BlockNode> blocks) {\n\t\tif (insn == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (insn instanceof PhiInsn) {\n\t\t\treturn searchBlockWithPhi(mth, (PhiInsn) insn);\n\t\t}\n\t\tif (insn.contains(AFlag.WRAPPED)) {\n\t\t\treturn getBlockByWrappedInsn(mth, insn);\n\t\t}\n\t\tfor (BlockNode bn : blocks) {\n\t\t\tif (blockContains(bn, insn)) {\n\t\t\t\treturn bn;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static BlockNode searchBlockWithPhi(MethodNode mth, PhiInsn insn) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tPhiListAttr phiListAttr = block.get(AType.PHI_LIST);\n\t\t\tif (phiListAttr != null) {\n\t\t\t\tfor (PhiInsn phiInsn : phiListAttr.getList()) {\n\t\t\t\t\tif (phiInsn == insn) {\n\t\t\t\t\t\treturn block;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static BlockNode getBlockByWrappedInsn(MethodNode mth, InsnNode insn) {\n\t\tfor (BlockNode bn : mth.getBasicBlocks()) {\n\t\t\tfor (InsnNode bi : bn.getInstructions()) {\n\t\t\t\tif (bi == insn || foundWrappedInsn(bi, insn) != null) {\n\t\t\t\t\treturn bn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static InsnNode searchInsnParent(MethodNode mth, InsnNode insn) {\n\t\tInsnArg insnArg = searchWrappedInsnParent(mth, insn);\n\t\tif (insnArg == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn insnArg.getParentInsn();\n\t}\n\n\tpublic static InsnArg searchWrappedInsnParent(MethodNode mth, InsnNode insn) {\n\t\tif (!insn.contains(AFlag.WRAPPED)) {\n\t\t\treturn null;\n\t\t}\n\t\tfor (BlockNode bn : mth.getBasicBlocks()) {\n\t\t\tfor (InsnNode bi : bn.getInstructions()) {\n\t\t\t\tInsnArg res = foundWrappedInsn(bi, insn);\n\t\t\t\tif (res != null) {\n\t\t\t\t\treturn res;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static InsnArg foundWrappedInsn(InsnNode container, InsnNode insn) {\n\t\tfor (InsnArg arg : container.getArguments()) {\n\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\tif (wrapInsn == insn) {\n\t\t\t\t\treturn arg;\n\t\t\t\t}\n\t\t\t\tInsnArg res = foundWrappedInsn(wrapInsn, insn);\n\t\t\t\tif (res != null) {\n\t\t\t\t\treturn res;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (container instanceof TernaryInsn) {\n\t\t\treturn foundWrappedInsnInCondition(((TernaryInsn) container).getCondition(), insn);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static InsnArg foundWrappedInsnInCondition(IfCondition cond, InsnNode insn) {\n\t\tif (cond.isCompare()) {\n\t\t\tIfNode cmpInsn = cond.getCompare().getInsn();\n\t\t\treturn foundWrappedInsn(cmpInsn, insn);\n\t\t}\n\t\tfor (IfCondition nestedCond : cond.getArgs()) {\n\t\t\tInsnArg res = foundWrappedInsnInCondition(nestedCond, insn);\n\t\t\tif (res != null) {\n\t\t\t\treturn res;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static BitSet newBlocksBitSet(MethodNode mth) {\n\t\treturn new BitSet(mth.getBasicBlocks().size());\n\t}\n\n\tpublic static BitSet copyBlocksBitSet(MethodNode mth, BitSet bitSet) {\n\t\tBitSet copy = new BitSet(mth.getBasicBlocks().size());\n\t\tif (!bitSet.isEmpty()) {\n\t\t\tcopy.or(bitSet);\n\t\t}\n\t\treturn copy;\n\t}\n\n\tpublic static BitSet blocksToBitSet(MethodNode mth, Collection<BlockNode> blocks) {\n\t\tBitSet bs = newBlocksBitSet(mth);\n\t\tfor (BlockNode block : blocks) {\n\t\t\tbs.set(block.getId());\n\t\t}\n\t\treturn bs;\n\t}\n\n\t@Nullable\n\tpublic static BlockNode bitSetToOneBlock(MethodNode mth, BitSet bs) {\n\t\tif (bs == null || bs.cardinality() != 1) {\n\t\t\treturn null;\n\t\t}\n\t\treturn mth.getBasicBlocks().get(bs.nextSetBit(0));\n\t}\n\n\tpublic static List<BlockNode> bitSetToBlocks(MethodNode mth, BitSet bs) {\n\t\tif (bs == null || bs == EmptyBitSet.EMPTY) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tint size = bs.cardinality();\n\t\tif (size == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<BlockNode> blocks = new ArrayList<>(size);\n\t\tfor (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {\n\t\t\tBlockNode block = mth.getBasicBlocks().get(i);\n\t\t\tblocks.add(block);\n\t\t}\n\t\treturn blocks;\n\t}\n\n\tpublic static void forEachBlockFromBitSet(MethodNode mth, BitSet bs, Consumer<BlockNode> consumer) {\n\t\tif (bs == null || bs == EmptyBitSet.EMPTY || bs.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tList<BlockNode> blocks = mth.getBasicBlocks();\n\t\tfor (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {\n\t\t\tconsumer.accept(blocks.get(i));\n\t\t}\n\t}\n\n\t/**\n\t * Return first successor which not exception handler and not follow loop back edge\n\t */\n\t@Nullable\n\tpublic static BlockNode getNextBlock(BlockNode block) {\n\t\tList<BlockNode> s = block.getCleanSuccessors();\n\t\treturn s.isEmpty() ? null : s.get(0);\n\t}\n\n\t@Nullable\n\tpublic static BlockNode getPrevBlock(BlockNode block) {\n\t\tList<BlockNode> preds = block.getPredecessors();\n\t\treturn preds.size() == 1 ? preds.get(0) : null;\n\t}\n\n\t/**\n\t * Return successor on path to 'pathEnd' block\n\t */\n\tpublic static BlockNode getNextBlockToPath(BlockNode block, BlockNode pathEnd) {\n\t\tList<BlockNode> successors = block.getCleanSuccessors();\n\t\tif (successors.contains(pathEnd)) {\n\t\t\treturn pathEnd;\n\t\t}\n\t\tSet<BlockNode> path = getAllPathsBlocks(block, pathEnd);\n\t\tfor (BlockNode s : successors) {\n\t\t\tif (path.contains(s)) {\n\t\t\t\treturn s;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Return predecessor on path from 'pathStart' block\n\t */\n\tpublic static @Nullable BlockNode getPrevBlockOnPath(MethodNode mth, BlockNode block, BlockNode pathStart) {\n\t\tBlockSet preds = BlockSet.from(mth, block.getPredecessors());\n\t\tif (preds.contains(pathStart)) {\n\t\t\treturn pathStart;\n\t\t}\n\t\tDFSIteration dfs = new DFSIteration(mth, pathStart, BlockNode::getCleanSuccessors);\n\t\twhile (true) {\n\t\t\tBlockNode next = dfs.next();\n\t\t\tif (next == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (preds.contains(next)) {\n\t\t\t\treturn next;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Visit blocks on any path from start to end.\n\t * Only one path will be visited!\n\t */\n\tpublic static boolean visitBlocksOnPath(MethodNode mth, BlockNode start, BlockNode end, Consumer<BlockNode> visitor) {\n\t\tvisitor.accept(start);\n\t\tif (start == end) {\n\t\t\treturn true;\n\t\t}\n\t\tif (start.getCleanSuccessors().contains(end)) {\n\t\t\tvisitor.accept(end);\n\t\t\treturn true;\n\t\t}\n\t\t// DFS on clean successors\n\t\tBitSet visited = newBlocksBitSet(mth);\n\t\tDeque<BlockNode> queue = new ArrayDeque<>();\n\t\tqueue.addLast(start);\n\t\twhile (true) {\n\t\t\tBlockNode current = queue.peekLast();\n\t\t\tif (current == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tboolean added = false;\n\t\t\tfor (BlockNode next : current.getCleanSuccessors()) {\n\t\t\t\tif (next == end) {\n\t\t\t\t\tqueue.removeFirst(); // start already visited\n\t\t\t\t\tqueue.addLast(next);\n\t\t\t\t\tqueue.forEach(visitor);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tint id = next.getId();\n\t\t\t\tif (!visited.get(id)) {\n\t\t\t\t\tvisited.set(id);\n\t\t\t\t\tqueue.addLast(next);\n\t\t\t\t\tadded = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!added) {\n\t\t\t\tqueue.pollLast();\n\t\t\t\tif (queue.isEmpty()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static List<BlockNode> collectAllPredecessors(MethodNode mth, BlockNode startBlock) {\n\t\tList<BlockNode> list = new ArrayList<>(mth.getBasicBlocks().size());\n\t\tFunction<BlockNode, List<BlockNode>> nextFunc = BlockNode::getPredecessors;\n\t\tvisitDFS(mth, startBlock, nextFunc, list::add);\n\t\treturn list;\n\t}\n\n\tpublic static List<BlockNode> collectAllSuccessors(MethodNode mth, BlockNode startBlock, boolean clean) {\n\t\tList<BlockNode> list = new ArrayList<>(mth.getBasicBlocks().size());\n\t\tFunction<BlockNode, List<BlockNode>> nextFunc = clean ? BlockNode::getCleanSuccessors : BlockNode::getSuccessors;\n\t\tvisitDFS(mth, startBlock, nextFunc, list::add);\n\t\treturn list;\n\t}\n\n\tpublic static List<BlockNode> collectAllSuccessorsUntil(MethodNode mth, BlockNode startBlock, boolean clean,\n\t\t\tPredicate<BlockNode> stopCondition) {\n\t\tList<BlockNode> blocks = new ArrayList<>();\n\t\tcollectAllSuccessorsUntil(mth, blocks, startBlock, clean, stopCondition);\n\t\treturn blocks;\n\t}\n\n\tprivate static void collectAllSuccessorsUntil(MethodNode mth, List<BlockNode> blocks, BlockNode currentBlock, boolean clean,\n\t\t\tPredicate<BlockNode> stopCondition) {\n\t\tif (blocks.contains(currentBlock)) {\n\t\t\treturn;\n\t\t}\n\n\t\tblocks.add(currentBlock);\n\n\t\tif (stopCondition.test(currentBlock)) {\n\t\t\treturn;\n\t\t}\n\n\t\tList<BlockNode> successors = clean ? currentBlock.getCleanSuccessors() : currentBlock.getSuccessors();\n\t\tfor (BlockNode successor : successors) {\n\t\t\tcollectAllSuccessorsUntil(mth, blocks, successor, clean, stopCondition);\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic static BlockNode getBottomCommonPredecessor(MethodNode mth, List<BlockNode> blocks, Set<BlockNode> containedBlocks) {\n\t\treturn getBottomCommonPredecessor(mth, blocks, containedBlocks, false);\n\t}\n\n\t@Nullable\n\tpublic static BlockNode getBottomCommonPredecessor(MethodNode mth, List<BlockNode> blocks, Set<BlockNode> containedBlocks,\n\t\t\tboolean addTopBlock) {\n\t\tif (blocks.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tSet<BlockNode> visitedPredecessorsByAll = new HashSet<>(collectAllPredecessors(mth, blocks.get(0)));\n\n\t\tif (addTopBlock) {\n\t\t\tBlockNode topBlock = BlockUtils.getBottomBlock(blocks);\n\t\t\tif (topBlock == null) {\n\t\t\t\t// TODO: These nodes are not connected so there will be no common successor ????\n\t\t\t\t// return null;\n\t\t\t} else {\n\t\t\t\tvisitedPredecessorsByAll.add(topBlock);\n\t\t\t}\n\t\t}\n\n\t\tfor (int i = 1; i < blocks.size(); i++) {\n\t\t\tBlockNode nextBlock = blocks.get(i);\n\t\t\tList<BlockNode> predecessors = collectAllPredecessors(mth, nextBlock);\n\t\t\tvisitedPredecessorsByAll.retainAll(predecessors);\n\t\t}\n\n\t\treturn BlockUtils.getBottomBlock(new ArrayList<>(visitedPredecessorsByAll));\n\t}\n\n\t@Nullable\n\tpublic static BlockNode getTopCommonSuccessor(MethodNode mth, List<BlockNode> blocks, boolean cleanOnly) {\n\t\treturn getTopCommonSuccessor(mth, blocks, cleanOnly, false);\n\t}\n\n\t@Nullable\n\tpublic static BlockNode getTopCommonSuccessor(MethodNode mth, List<BlockNode> blocks, boolean cleanOnly, boolean addTopBlock) {\n\t\tif (blocks.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tSet<BlockNode> visitedSuccessorsByAll = new HashSet<>(collectAllSuccessors(mth, blocks.get(0), cleanOnly));\n\n\t\tif (addTopBlock) {\n\t\t\tBlockNode topBlock = BlockUtils.getTopBlock(blocks);\n\t\t\tif (topBlock == null) {\n\t\t\t\t// TODO: These nodes are not connected so there will be no common successor ????\n\t\t\t\t// return null;\n\t\t\t} else {\n\t\t\t\tvisitedSuccessorsByAll.add(topBlock);\n\t\t\t}\n\t\t}\n\n\t\tfor (int i = 1; i < blocks.size(); i++) {\n\t\t\tBlockNode nextBlock = blocks.get(i);\n\t\t\tList<BlockNode> successors = collectAllSuccessors(mth, nextBlock, cleanOnly);\n\t\t\tvisitedSuccessorsByAll.retainAll(successors);\n\t\t}\n\n\t\treturn BlockUtils.getTopBlock(new ArrayList<>(visitedSuccessorsByAll));\n\t}\n\n\tpublic static void visitDFS(MethodNode mth, Consumer<BlockNode> visitor) {\n\t\tvisitDFS(mth, mth.getEnterBlock(), BlockNode::getSuccessors, visitor);\n\t}\n\n\tpublic static void visitReverseDFS(MethodNode mth, Consumer<BlockNode> visitor) {\n\t\tvisitDFS(mth, mth.getExitBlock(), BlockNode::getPredecessors, visitor);\n\t}\n\n\tprivate static void visitDFS(MethodNode mth, BlockNode startBlock,\n\t\t\tFunction<BlockNode, List<BlockNode>> nextFunc, Consumer<BlockNode> visitor) {\n\t\tDFSIteration dfsIteration = new DFSIteration(mth, startBlock, nextFunc);\n\t\twhile (true) {\n\t\t\tBlockNode next = dfsIteration.next();\n\t\t\tif (next == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvisitor.accept(next);\n\t\t}\n\t}\n\n\tpublic static List<BlockNode> collectPredecessors(MethodNode mth, BlockNode start, Collection<BlockNode> stopBlocks) {\n\t\tBitSet bs = newBlocksBitSet(mth);\n\t\tif (!stopBlocks.isEmpty()) {\n\t\t\tbs.or(blocksToBitSet(mth, stopBlocks));\n\t\t}\n\t\tList<BlockNode> list = new ArrayList<>();\n\t\ttraversePredecessors(start, bs, block -> {\n\t\t\tlist.add(block);\n\t\t\treturn false;\n\t\t});\n\t\treturn list;\n\t}\n\n\tpublic static void visitPredecessorsUntil(MethodNode mth, BlockNode start, Predicate<BlockNode> visitor) {\n\t\ttraversePredecessors(start, newBlocksBitSet(mth), visitor);\n\t}\n\n\t/**\n\t * Up BFS.\n\t * To stop return true from predicate\n\t */\n\tprivate static void traversePredecessors(BlockNode start, BitSet visited, Predicate<BlockNode> visitor) {\n\t\tQueue<BlockNode> queue = new ArrayDeque<>();\n\t\tqueue.add(start);\n\t\twhile (true) {\n\t\t\tBlockNode current = queue.poll();\n\t\t\tif (current == null || visitor.test(current)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfor (BlockNode next : current.getPredecessors()) {\n\t\t\t\tint id = next.getId();\n\t\t\t\tif (!visited.get(id)) {\n\t\t\t\t\tvisited.set(id);\n\t\t\t\t\tqueue.add(next);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Collect blocks from all possible execution paths from 'start' to 'end'\n\t */\n\tpublic static Set<BlockNode> getAllPathsBlocks(BlockNode start, BlockNode end) {\n\t\tSet<BlockNode> set = new HashSet<>();\n\t\tset.add(start);\n\t\tif (start != end) {\n\t\t\taddPredecessors(set, end, start);\n\t\t}\n\t\treturn set;\n\t}\n\n\t/**\n\t * Collect blocks from one possible execution path from 'start' to 'end' containing no instructions\n\t */\n\tpublic static List<BlockNode> getOneEmptyPath(BlockNode start, BlockNode end) {\n\t\treturn collectPathUntil(start, end, false, b -> {\n\t\t\treturn b.getInstructions().isEmpty() || b.equals(end);\n\t\t});\n\t}\n\n\t/**\n\t * Collect blocks from one possible execution path from 'start' to 'end'\n\t */\n\tpublic static List<BlockNode> getOnePath(BlockNode start, BlockNode end) {\n\t\treturn collectPathUntil(start, end, false, b -> true);\n\t}\n\n\tprivate static void addPredecessors(Set<BlockNode> set, BlockNode from, BlockNode until) {\n\t\tset.add(from);\n\t\tfor (BlockNode pred : from.getPredecessors()) {\n\t\t\tif (pred != until && !set.contains(pred)) {\n\t\t\t\taddPredecessors(set, pred, until);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean traverseSuccessorsUntil(BlockNode from, BlockNode until, BitSet visited, boolean clean) {\n\t\treturn traverseSuccessorsUntil(from, until, visited, clean, b -> true);\n\t}\n\n\t/**\n\t *\n\t * Traverse succcessors until a node is found\n\t *\n\t * @param from    the source node to begin traversing\n\t * @param until   the destination node to halt traversing\n\t * @param visited the set of visited blocks so far\n\t * @param clean   use only clean successors\n\t * @param pred    a predicate that must be true to traverse a block (until or a reachable dominator\n\t *                of until must satisfy pred)\n\t * @return true if there is a path from `from` to `until` or a dominator of `until` through blocks\n\t *         that satisfy `pred`, false otherwise\n\t */\n\tprivate static boolean traverseSuccessorsUntil(BlockNode from, BlockNode until, BitSet visited, boolean clean,\n\t\t\tPredicate<BlockNode> pred) {\n\t\tList<BlockNode> nodes = clean ? from.getCleanSuccessors() : from.getSuccessors();\n\t\tfor (BlockNode s : nodes) {\n\t\t\tif (!pred.test(s)) {\n\t\t\t\t// Only explore blocks such that the predicate holds\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (s == until) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (s == from) {\n\t\t\t\t// ignore possible block self loop\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tint id = s.getPos();\n\t\t\tif (!visited.get(id)) {\n\t\t\t\tvisited.set(id);\n\t\t\t\tif (until.isDominator(s)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tif (traverseSuccessorsUntil(s, until, visited, clean, pred)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t *\n\t * Traverse succcessors until a node is found, collecting the path to the node\n\t *\n\t * @param from  the source node to begin traversing\n\t * @param until the destination node to halt traversing\n\t * @param clean use only clean successors\n\t * @param pred  a predicate that must be true to traverse a block (until must satisfy pred)\n\t * @return the list of blocks satisfying pred on a path between from and until (inclusive), or null\n\t *         if no such path exists\n\t */\n\tpublic static List<BlockNode> collectPathUntil(BlockNode from, BlockNode until, boolean clean, Predicate<BlockNode> pred) {\n\t\tList<BlockNode> path = internalCollectPathUntil(from, until, new BitSet(), clean, pred);\n\t\tif (path == null) {\n\t\t\treturn path;\n\t\t}\n\t\tpath.add(from);\n\t\tCollections.reverse(path);\n\t\treturn path;\n\t}\n\n\t/**\n\t *\n\t * Traverse succcessors until a node is found, collecting the path to the node\n\t *\n\t * @param from    the source node to begin traversing\n\t * @param until   the destination node to halt traversing\n\t * @param visited the set of visited blocks so far\n\t * @param clean   use only clean successors\n\t * @param pred    a predicate that must be true to traverse a block (until must satisfy pred)\n\t * @return the list of blocks satisfying pred on a path between from (exclusive) and until\n\t *         (inclusive) in reverse order, or null if no such path exists\n\t */\n\tprivate static List<BlockNode> internalCollectPathUntil(BlockNode from, BlockNode until, BitSet visited, boolean clean,\n\t\t\tPredicate<BlockNode> pred) {\n\t\tList<BlockNode> nodes = clean ? from.getCleanSuccessors() : from.getSuccessors();\n\t\tfor (BlockNode s : nodes) {\n\t\t\tif (!pred.test(s)) {\n\t\t\t\t// Only explore blocks such that the predicate holds\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (s == until) {\n\t\t\t\tList<BlockNode> path = new ArrayList<>();\n\t\t\t\tpath.add(s);\n\t\t\t\treturn path;\n\t\t\t}\n\t\t\tint id = s.getPos();\n\t\t\tif (!visited.get(id)) {\n\t\t\t\tvisited.set(id);\n\t\t\t\tList<BlockNode> path = internalCollectPathUntil(s, until, visited, clean, pred);\n\t\t\t\tif (path != null) {\n\t\t\t\t\tpath.add(s);\n\t\t\t\t\treturn path;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Search at least one path from startBlocks to end\n\t */\n\tpublic static boolean atLeastOnePathExists(Collection<BlockNode> startBlocks, BlockNode end) {\n\t\tfor (BlockNode startBlock : startBlocks) {\n\t\t\tif (isPathExists(startBlock, end)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Check if exist path from every startBlocks to end\n\t */\n\tpublic static boolean isAllPathExists(Collection<BlockNode> startBlocks, BlockNode end) {\n\t\tfor (BlockNode startBlock : startBlocks) {\n\t\t\tif (!isPathExists(startBlock, end)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static boolean isPathExists(BlockNode start, BlockNode end) {\n\t\tif (start == end\n\t\t\t\t|| start.getCleanSuccessors().contains(end)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn traverseSuccessorsUntil(start, end, new BitSet(), true);\n\t}\n\n\tpublic static boolean isAnyPathExists(BlockNode start, BlockNode end) {\n\t\tif (start == end\n\t\t\t\t|| end.isDominator(start)\n\t\t\t\t|| start.getSuccessors().contains(end)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn traverseSuccessorsUntil(start, end, new BitSet(), false);\n\t}\n\n\tpublic static boolean isPathExists(BlockNode start, BlockNode end, Predicate<BlockNode> pred) {\n\t\tif (start == end) {\n\t\t\treturn true;\n\t\t}\n\t\treturn traverseSuccessorsUntil(start, end, new BitSet(), false, pred);\n\t}\n\n\tpublic static BlockNode getTopBlock(List<BlockNode> blocks) {\n\t\tif (blocks.size() == 1) {\n\t\t\treturn blocks.get(0);\n\t\t}\n\t\tfor (BlockNode from : blocks) {\n\t\t\tboolean top = true;\n\t\t\tfor (BlockNode to : blocks) {\n\t\t\t\tif (from != to && !isAnyPathExists(from, to)) {\n\t\t\t\t\ttop = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (top) {\n\t\t\t\treturn from;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Search last block in control flow graph from input set.\n\t */\n\t@Nullable\n\tpublic static BlockNode getBottomBlock(List<BlockNode> blocks) {\n\t\treturn getBottomBlock(blocks, false);\n\t}\n\n\tpublic static BlockNode getBottomBlock(List<BlockNode> blocks, boolean clean) {\n\t\tif (blocks.size() == 1) {\n\t\t\treturn blocks.get(0);\n\t\t}\n\t\t// attempt 1: look for a block dominated by every other block\n\t\t// don't do this if clean, since dominators always consider all successors\n\t\tif (!clean) {\n\t\t\tfor (BlockNode bottomCandidate : blocks) {\n\t\t\t\tboolean bottom = true;\n\t\t\t\tfor (BlockNode from : blocks) {\n\t\t\t\t\tif (bottomCandidate != from && !bottomCandidate.isDominator(from)) {\n\t\t\t\t\t\tbottom = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (bottom) {\n\t\t\t\t\treturn bottomCandidate;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// attempt 2: look for a block with a path from every other block\n\t\tfor (BlockNode bottomCandidate : blocks) {\n\t\t\tboolean bottom = true;\n\t\t\tfor (BlockNode from : blocks) {\n\t\t\t\tif (clean) {\n\t\t\t\t\tif (bottomCandidate != from && !isPathExists(from, bottomCandidate)) {\n\t\t\t\t\t\tbottom = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (bottomCandidate != from && !isAnyPathExists(from, bottomCandidate)) {\n\t\t\t\t\t\tbottom = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\t\tif (bottom) {\n\t\t\t\treturn bottomCandidate;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static boolean isOnlyOnePathExists(BlockNode start, BlockNode end) {\n\t\tif (start == end) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!end.isDominator(start)) {\n\t\t\treturn false;\n\t\t}\n\t\tBlockNode currentNode = start;\n\t\twhile (currentNode.getCleanSuccessors().size() == 1) {\n\t\t\tcurrentNode = currentNode.getCleanSuccessors().get(0);\n\t\t\tif (currentNode == end) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Search for first node which not dominated by dom, starting from start\n\t */\n\tpublic static BlockNode traverseWhileDominates(BlockNode dom, BlockNode start) {\n\t\tfor (BlockNode node : start.getCleanSuccessors()) {\n\t\t\tif (!node.isDominator(dom)) {\n\t\t\t\treturn node;\n\t\t\t} else {\n\t\t\t\tBlockNode out = traverseWhileDominates(dom, node);\n\t\t\t\tif (out != null) {\n\t\t\t\t\treturn out;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Search the lowest common ancestor in dominator tree for input set.\n\t */\n\t@Nullable\n\tpublic static BlockNode getCommonDominator(MethodNode mth, List<BlockNode> blocks) {\n\t\tBitSet doms = newBlocksBitSet(mth);\n\t\t// collect all dominators from input set\n\t\tdoms.set(0, mth.getBasicBlocks().size());\n\t\tblocks.forEach(b -> doms.and(b.getDoms()));\n\t\t// exclude all dominators of immediate dominator (including self)\n\t\tBitSet combine = newBlocksBitSet(mth);\n\t\tcombine.or(doms);\n\t\tforEachBlockFromBitSet(mth, doms, block -> {\n\t\t\tBlockNode idom = block.getIDom();\n\t\t\tif (idom != null) {\n\t\t\t\tcombine.andNot(idom.getDoms());\n\t\t\t\tcombine.clear(idom.getId());\n\t\t\t}\n\t\t});\n\t\treturn bitSetToOneBlock(mth, combine);\n\t}\n\n\t/**\n\t * Return the dominace frontier of an edge - the blocks for which any path to the block must pass\n\t * through this edge\n\t */\n\tpublic static BitSet getDomFrontierThroughEdge(Edge edge) {\n\t\tBlockNode target = edge.getTarget();\n\n\t\tif (target.getPredecessors().size() > 1) {\n\t\t\t// If the target node has other incoming edges, the dominance frontier is a single block\n\t\t\tBitSet dominanceFrontier = new BitSet();\n\t\t\tdominanceFrontier.set(target.getPos());\n\t\t\treturn dominanceFrontier;\n\t\t} else {\n\t\t\t// Otherwise the dominance frontier is equivalent to the domiance frontier of the target\n\t\t\treturn target.getDomFrontier();\n\t\t}\n\t}\n\n\t/**\n\t * Return common cross block for input set.\n\t *\n\t * @return could be one of the giving blocks. null if cross is a method exit block.\n\t */\n\t@Nullable\n\tpublic static BlockNode getPathCross(MethodNode mth, Collection<BlockNode> blocks) {\n\t\tBitSet domFrontBS = newBlocksBitSet(mth);\n\t\tBitSet tmpBS = newBlocksBitSet(mth); // store block itself and its domFrontier\n\t\tboolean first = true;\n\t\tfor (BlockNode b : blocks) {\n\t\t\ttmpBS.clear();\n\t\t\ttmpBS.set(b.getId());\n\t\t\ttmpBS.or(b.getDomFrontier());\n\t\t\tif (first) {\n\t\t\t\tdomFrontBS.or(tmpBS);\n\t\t\t\tfirst = false;\n\t\t\t} else {\n\t\t\t\tdomFrontBS.and(tmpBS);\n\t\t\t}\n\t\t}\n\t\tdomFrontBS.clear(mth.getExitBlock().getId());\n\t\tif (domFrontBS.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tBlockNode oneBlock = bitSetToOneBlock(mth, domFrontBS);\n\t\tif (oneBlock != null) {\n\t\t\treturn oneBlock;\n\t\t}\n\t\tBitSet excluded = newBlocksBitSet(mth);\n\t\t// exclude method exit and loop start blocks\n\t\texcluded.set(mth.getExitBlock().getId());\n\t\t// exclude loop start blocks\n\t\tmth.getLoops().forEach(l -> excluded.set(l.getStart().getId()));\n\t\tif (!mth.isNoExceptionHandlers()) {\n\t\t\t// exclude exception handlers paths\n\t\t\tmth.getExceptionHandlers().forEach(h -> addExcHandler(mth, h, excluded));\n\t\t}\n\t\tdomFrontBS.andNot(excluded);\n\t\toneBlock = bitSetToOneBlock(mth, domFrontBS);\n\t\tif (oneBlock != null) {\n\t\t\treturn oneBlock;\n\t\t}\n\t\tBitSet combinedDF = newBlocksBitSet(mth);\n\t\tint k = mth.getBasicBlocks().size();\n\t\twhile (true) {\n\t\t\t// collect dom frontier blocks from current set until only one block left\n\t\t\tforEachBlockFromBitSet(mth, domFrontBS, block -> {\n\t\t\t\tBitSet domFrontier = block.getDomFrontier();\n\t\t\t\tif (!domFrontier.isEmpty()) {\n\t\t\t\t\tcombinedDF.or(domFrontier);\n\t\t\t\t}\n\t\t\t});\n\t\t\tcombinedDF.andNot(excluded);\n\t\t\tint cardinality = combinedDF.cardinality();\n\t\t\tif (cardinality == 1) {\n\t\t\t\treturn bitSetToOneBlock(mth, combinedDF);\n\t\t\t}\n\t\t\tif (cardinality == 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (k-- < 0) {\n\t\t\t\tmth.addWarnComment(\"Path cross not found for \" + blocks + \", limit reached: \" + mth.getBasicBlocks().size());\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t// replace domFrontBS with combinedDF\n\t\t\tdomFrontBS.clear();\n\t\t\tdomFrontBS.or(combinedDF);\n\t\t\tcombinedDF.clear();\n\t\t}\n\t}\n\n\tprivate static void addExcHandler(MethodNode mth, ExceptionHandler handler, BitSet set) {\n\t\tBlockNode handlerBlock = handler.getHandlerBlock();\n\t\tif (handlerBlock == null) {\n\t\t\tmth.addDebugComment(\"Null handler block in: \" + handler);\n\t\t\treturn;\n\t\t}\n\t\tset.set(handlerBlock.getId());\n\t}\n\n\tpublic static BlockNode getPathCross(MethodNode mth, BlockNode b1, BlockNode b2) {\n\t\tif (b1 == b2) {\n\t\t\treturn b1;\n\t\t}\n\t\tif (b1 == null || b2 == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn getPathCross(mth, Arrays.asList(b1, b2));\n\t}\n\n\t/**\n\t * Collect all block dominated by 'dominator', starting from 'start'\n\t */\n\tpublic static List<BlockNode> collectBlocksDominatedBy(MethodNode mth, BlockNode dominator, BlockNode start) {\n\t\tList<BlockNode> result = new ArrayList<>();\n\t\tcollectWhileDominates(dominator, start, result, newBlocksBitSet(mth), false);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Collect all block dominated by 'dominator', starting from 'start', including exception handlers\n\t */\n\tpublic static Set<BlockNode> collectBlocksDominatedByWithExcHandlers(MethodNode mth, BlockNode dominator, BlockNode start) {\n\t\tSet<BlockNode> result = new LinkedHashSet<>();\n\t\tcollectWhileDominates(dominator, start, result, newBlocksBitSet(mth), true);\n\t\treturn result;\n\t}\n\n\tprivate static void collectWhileDominates(BlockNode dominator, BlockNode child, Collection<BlockNode> result,\n\t\t\tBitSet visited, boolean includeExcHandlers) {\n\t\tif (visited.get(child.getId())) {\n\t\t\treturn;\n\t\t}\n\t\tvisited.set(child.getId());\n\t\tList<BlockNode> successors = includeExcHandlers ? child.getSuccessors() : child.getCleanSuccessors();\n\t\tfor (BlockNode node : successors) {\n\t\t\tif (node.isDominator(dominator)) {\n\t\t\t\tresult.add(node);\n\t\t\t\tcollectWhileDominates(dominator, node, result, visited, includeExcHandlers);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Visit blocks on path without branching or merging paths.\n\t */\n\tpublic static void visitSinglePath(BlockNode startBlock, Consumer<BlockNode> visitor) {\n\t\tif (startBlock == null) {\n\t\t\treturn;\n\t\t}\n\t\tvisitor.accept(startBlock);\n\t\tBlockNode next = getNextSinglePathBlock(startBlock);\n\t\twhile (next != null) {\n\t\t\tvisitor.accept(next);\n\t\t\tnext = getNextSinglePathBlock(next);\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic static BlockNode getNextSinglePathBlock(BlockNode block) {\n\t\tif (block == null || block.getPredecessors().size() > 1) {\n\t\t\treturn null;\n\t\t}\n\t\tList<BlockNode> successors = block.getSuccessors();\n\t\treturn successors.size() == 1 ? successors.get(0) : null;\n\t}\n\n\tpublic static List<BlockNode> buildSimplePath(BlockNode block) {\n\t\tif (block == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<BlockNode> list = new ArrayList<>();\n\t\tif (block.getCleanSuccessors().size() >= 2) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tlist.add(block);\n\n\t\tBlockNode currentBlock = getNextBlock(block);\n\t\twhile (currentBlock != null\n\t\t\t\t&& currentBlock.getCleanSuccessors().size() < 2\n\t\t\t\t&& currentBlock.getPredecessors().size() == 1) {\n\t\t\tlist.add(currentBlock);\n\t\t\tcurrentBlock = getNextBlock(currentBlock);\n\t\t}\n\t\treturn list;\n\t}\n\n\t/**\n\t * Set 'SKIP' flag for all synthetic predecessors from start block.\n\t */\n\tpublic static void skipPredSyntheticPaths(BlockNode block) {\n\t\tfor (BlockNode pred : block.getPredecessors()) {\n\t\t\tif (pred.contains(AFlag.SYNTHETIC)\n\t\t\t\t\t&& !pred.contains(AFlag.EXC_TOP_SPLITTER)\n\t\t\t\t\t&& !pred.contains(AFlag.EXC_BOTTOM_SPLITTER)\n\t\t\t\t\t&& pred.getInstructions().isEmpty()) {\n\t\t\t\tpred.add(AFlag.DONT_GENERATE);\n\t\t\t\tskipPredSyntheticPaths(pred);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Follow empty blocks and return end of path block (first not empty).\n\t * Return start block if no such path.\n\t */\n\tpublic static BlockNode followEmptyPath(BlockNode start) {\n\t\treturn followEmptyPath(start, false);\n\t}\n\n\tpublic static BlockNode followEmptyPath(BlockNode start, Boolean reverse) {\n\t\treturn followEmptyPath(start, reverse, true);\n\t}\n\n\tpublic static BlockNode followEmptyPath(BlockNode start, Boolean reverse, boolean cleanOnly) {\n\t\twhile (true) {\n\t\t\tBlockNode next = getNextBlockOnEmptyPath(start, reverse, cleanOnly);\n\t\t\tif (next == null) {\n\t\t\t\treturn start;\n\t\t\t}\n\t\t\tstart = next;\n\t\t}\n\t}\n\n\tpublic static List<BlockNode> followEmptyUpPathWithinSet(BlockNode start, Collection<BlockNode> traversableBlocks) {\n\t\tList<BlockNode> results = new LinkedList<>();\n\t\tfollowEmptyUpPathWithinSet(results, start, traversableBlocks, new HashSet<>());\n\t\treturn results;\n\t}\n\n\tpublic static void followEmptyUpPathWithinSet(List<BlockNode> results, BlockNode start, Collection<BlockNode> traversableBlocks,\n\t\t\tCollection<BlockNode> traversedBlocks) {\n\t\tList<BlockNode> predecessors = ListUtils.filter(start.getPredecessors(), traversableBlocks::contains);\n\t\tfor (BlockNode predecessor : predecessors) {\n\t\t\tif (!traversableBlocks.contains(predecessor) || traversedBlocks.contains(predecessor)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttraversedBlocks.add(predecessor);\n\t\t\tif (predecessor.getInstructions().isEmpty()) {\n\t\t\t\tfollowEmptyUpPathWithinSet(results, start, traversableBlocks, traversedBlocks);\n\t\t\t} else {\n\t\t\t\tresults.add(predecessor);\n\t\t\t}\n\t\t\tstart = predecessor;\n\t\t}\n\t}\n\n\tpublic static void visitBlocksOnEmptyPath(BlockNode start, Consumer<BlockNode> visitor) {\n\t\tvisitBlocksOnEmptyPath(start, visitor, false);\n\t}\n\n\tpublic static void visitBlocksOnEmptyPath(BlockNode start, Consumer<BlockNode> visitor, boolean reverse) {\n\t\twhile (true) {\n\t\t\tBlockNode next = getNextBlockOnEmptyPath(start, reverse);\n\t\t\tif (next == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvisitor.accept(next);\n\t\t\tstart = next;\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate static BlockNode getNextBlockOnEmptyPath(BlockNode block) {\n\t\treturn getNextBlockOnEmptyPath(block, false);\n\t}\n\n\t@Nullable\n\tprivate static BlockNode getNextBlockOnEmptyPath(BlockNode block, Boolean reverse) {\n\t\treturn getNextBlockOnEmptyPath(block, reverse, true);\n\t}\n\n\t@Nullable\n\tprivate static BlockNode getNextBlockOnEmptyPath(BlockNode block, Boolean reverse, boolean cleanOnly) {\n\t\tif (!block.getInstructions().isEmpty() || (!reverse && block.getPredecessors().size() > 1)\n\t\t\t\t|| (reverse && block.getCleanSuccessors().size() > 1)) {\n\t\t\treturn null;\n\t\t}\n\t\tList<BlockNode> nextBlocks = reverse ? block.getPredecessors() : (cleanOnly ? block.getCleanSuccessors() : block.getSuccessors());\n\t\tif (nextBlocks.size() != 1) {\n\t\t\treturn null;\n\t\t}\n\t\treturn nextBlocks.get(0);\n\t}\n\n\t/**\n\t * Return true if on path from start to end no instructions and no branches.\n\t */\n\tpublic static boolean isEmptySimplePath(BlockNode start, BlockNode end) {\n\t\tif (start == end && start.getInstructions().isEmpty()) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!start.getInstructions().isEmpty() || start.getCleanSuccessors().size() != 1) {\n\t\t\treturn false;\n\t\t}\n\t\tBlockNode block = getNextBlock(start);\n\t\twhile (block != null\n\t\t\t\t&& block != end\n\t\t\t\t&& block.getCleanSuccessors().size() < 2\n\t\t\t\t&& block.getPredecessors().size() == 1\n\t\t\t\t&& block.getInstructions().isEmpty()) {\n\t\t\tblock = getNextBlock(block);\n\t\t}\n\t\treturn block == end;\n\t}\n\n\t/**\n\t * Return predecessor of synthetic block or same block otherwise.\n\t */\n\tpublic static BlockNode skipSyntheticPredecessor(BlockNode block) {\n\t\tif (block.isSynthetic()\n\t\t\t\t&& block.getInstructions().isEmpty()\n\t\t\t\t&& block.getPredecessors().size() == 1) {\n\t\t\treturn block.getPredecessors().get(0);\n\t\t}\n\t\treturn block;\n\t}\n\n\tpublic static boolean isAllBlocksEmpty(List<BlockNode> blocks) {\n\t\tif (Utils.isEmpty(blocks)) {\n\t\t\treturn true;\n\t\t}\n\t\tfor (BlockNode block : blocks) {\n\t\t\tif (!block.getInstructions().isEmpty()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static List<InsnNode> collectAllInsns(List<BlockNode> blocks) {\n\t\tList<InsnNode> insns = new ArrayList<>();\n\t\tblocks.forEach(block -> insns.addAll(block.getInstructions()));\n\t\treturn insns;\n\t}\n\n\t/**\n\t * Return limited number of instructions from method.\n\t * Return empty list if method contains more than limit.\n\t */\n\tpublic static List<InsnNode> collectInsnsWithLimit(List<BlockNode> blocks, int limit) {\n\t\tList<InsnNode> insns = new ArrayList<>(limit);\n\t\tfor (BlockNode block : blocks) {\n\t\t\tList<InsnNode> blockInsns = block.getInstructions();\n\t\t\tint blockSize = blockInsns.size();\n\t\t\tif (blockSize == 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (insns.size() + blockSize > limit) {\n\t\t\t\treturn Collections.emptyList();\n\t\t\t}\n\t\t\tinsns.addAll(blockInsns);\n\t\t}\n\t\treturn insns;\n\t}\n\n\t/**\n\t * Return insn if it is only one instruction in this method. Return null otherwise.\n\t */\n\t@Nullable\n\tpublic static InsnNode getOnlyOneInsnFromMth(MethodNode mth) {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn null;\n\t\t}\n\t\tInsnNode insn = null;\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tList<InsnNode> blockInsns = block.getInstructions();\n\t\t\tint blockSize = blockInsns.size();\n\t\t\tif (blockSize == 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (blockSize > 1) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (insn != null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tinsn = blockInsns.get(0);\n\t\t}\n\t\treturn insn;\n\t}\n\n\tpublic static boolean isFirstInsn(MethodNode mth, InsnNode insn) {\n\t\tBlockNode startBlock = followEmptyPath(mth.getEnterBlock());\n\t\tif (startBlock != null && !startBlock.getInstructions().isEmpty()) {\n\t\t\treturn startBlock.getInstructions().get(0) == insn;\n\t\t}\n\t\t// handle branching with empty blocks\n\t\tBlockNode block = getBlockByInsn(mth, insn);\n\t\tif (block == null) {\n\t\t\tthrow new JadxRuntimeException(\"Insn not found in method: \" + insn);\n\t\t}\n\t\tif (block.getInstructions().get(0) != insn) {\n\t\t\treturn false;\n\t\t}\n\t\tSet<BlockNode> allPathsBlocks = getAllPathsBlocks(mth.getEnterBlock(), block);\n\t\tfor (BlockNode pathBlock : allPathsBlocks) {\n\t\t\tif (!pathBlock.getInstructions().isEmpty() && pathBlock != block) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Replace insn by index i in block,\n\t * for proper copy attributes, assume attributes are not overlap\n\t */\n\tpublic static void replaceInsn(MethodNode mth, BlockNode block, int i, InsnNode insn) {\n\t\tInsnNode prevInsn = block.getInstructions().get(i);\n\t\tinsn.copyAttributesFrom(prevInsn);\n\t\tinsn.inheritMetadata(prevInsn);\n\t\tinsn.setOffset(prevInsn.getOffset());\n\t\tblock.getInstructions().set(i, insn);\n\n\t\tRegisterArg result = insn.getResult();\n\t\tRegisterArg prevResult = prevInsn.getResult();\n\t\tif (result != null && prevResult != null && result.sameRegAndSVar(prevResult)) {\n\t\t\t// Don't unbind result for same register.\n\t\t\t// Unbind will remove arg from PHI and not add it back on rebind.\n\t\t\tInsnRemover.unbindAllArgs(mth, prevInsn);\n\t\t} else {\n\t\t\tInsnRemover.unbindInsn(mth, prevInsn);\n\t\t}\n\t\tinsn.rebindArgs();\n\t}\n\n\tpublic static boolean replaceInsn(MethodNode mth, BlockNode block, InsnNode oldInsn, InsnNode newInsn) {\n\t\tList<InsnNode> instructions = block.getInstructions();\n\t\tint size = instructions.size();\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tInsnNode instruction = instructions.get(i);\n\t\t\tif (instruction == oldInsn) {\n\t\t\t\treplaceInsn(mth, block, i, newInsn);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static void removeInstructions(List<IBlock> blocks) {\n\t\tfor (IBlock block : blocks) {\n\t\t\tblock.getInstructions().clear();\n\t\t}\n\t}\n\n\tpublic static boolean insertBeforeInsn(BlockNode block, InsnNode insn, InsnNode newInsn) {\n\t\tint index = getInsnIndexInBlock(block, insn);\n\t\tif (index == -1) {\n\t\t\treturn false;\n\t\t}\n\t\tblock.getInstructions().add(index, newInsn);\n\t\treturn true;\n\t}\n\n\tpublic static boolean insertAfterInsn(BlockNode block, InsnNode insn, InsnNode newInsn) {\n\t\tint index = getInsnIndexInBlock(block, insn);\n\t\tif (index == -1) {\n\t\t\treturn false;\n\t\t}\n\t\tblock.getInstructions().add(index + 1, newInsn);\n\t\treturn true;\n\t}\n\n\tpublic static int getInsnIndexInBlock(BlockNode block, InsnNode insn) {\n\t\tList<InsnNode> instructions = block.getInstructions();\n\t\tint size = instructions.size();\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tif (instructions.get(i) == insn) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\tpublic static boolean replaceInsn(MethodNode mth, InsnNode oldInsn, InsnNode newInsn) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tif (replaceInsn(mth, block, oldInsn, newInsn)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static BlockNode getTopSplitterForHandler(BlockNode handlerBlock) {\n\t\tBlockNode block = getBlockWithFlag(handlerBlock.getPredecessors(), AFlag.EXC_TOP_SPLITTER);\n\t\tif (block == null) {\n\t\t\tthrow new JadxRuntimeException(\"Can't find top splitter block for handler:\" + handlerBlock);\n\t\t}\n\t\treturn block;\n\t}\n\n\t/**\n\t * Return out block of try catch, by finding where try branch meets catch branch.\n\t * It traverse domFrontier start from handler block, find the first frontier\n\t * whose predecessor is try end.\n\t * <br>\n\t * It could return null if they never meets, but this doesn't mean that catch\n\t * ends at the method exit.\n\t * (see TestSwitchWithTryCatch and ExcHandlersRegionMaker#processExcHandler).\n\t */\n\t@Nullable\n\tpublic static BlockNode getTryAndHandlerCrossBlock(MethodNode mth, ExceptionHandler handler) {\n\t\tBlockNode start = handler.getHandlerBlock();\n\t\tBlockNode topSplitter = BlockUtils.getTopSplitterForHandler(start);\n\t\tList<ExceptionHandler> allHandlers = handler.getTryBlock().getHandlers();\n\t\tList<BlockNode> handlerExitsCandidate = new ArrayList<>(BlockUtils.bitSetToBlocks(mth, start.getDomFrontier()));\n\t\tBitSet visited = newBlocksBitSet(mth);\n\t\twhile (!handlerExitsCandidate.isEmpty()) {\n\t\t\tBlockNode frontier = handlerExitsCandidate.remove(0);\n\t\t\tif (visited.get(frontier.getPos())) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvisited.set(frontier.getPos());\n\t\t\t// In some cases, handler's domFrontier is in the half of catch block\n\t\t\t// instead of the end, so we need to make sure frontier's predecessor\n\t\t\t// comes from try branch end:\n\t\t\t// 1. not from handler branch, doesn't exist path from handler to pred\n\t\t\t// 2. from try branch, exists path from topSplitter to pred\n\t\t\t// 3. skip method exit\n\t\t\tfor (BlockNode pred : frontier.getPredecessors()) {\n\t\t\t\tboolean predFromHandler = allHandlers.stream().anyMatch(h -> isPathExists(h.getHandlerBlock(), pred));\n\t\t\t\tif (!predFromHandler && BlockUtils.isPathExists(topSplitter, pred)\n\t\t\t\t\t\t&& frontier != mth.getExitBlock()) {\n\t\t\t\t\treturn frontier;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// if not found, add this frontier's frontier to candidate list\n\t\t\thandlerExitsCandidate.addAll(BlockUtils.bitSetToBlocks(mth, frontier.getDomFrontier()));\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Nullable\n\tpublic static BlockNode getBlockWithFlag(List<BlockNode> blocks, AFlag flag) {\n\t\tfor (BlockNode block : blocks) {\n\t\t\tif (block.contains(flag)) {\n\t\t\t\treturn block;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static @Nullable CatchAttr getCatchAttrForInsn(MethodNode mth, InsnNode insn) {\n\t\tCatchAttr catchAttr = insn.get(AType.EXC_CATCH);\n\t\tif (catchAttr != null) {\n\t\t\treturn catchAttr;\n\t\t}\n\t\tBlockNode block = getBlockByInsn(mth, insn);\n\t\tif (block == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn block.get(AType.EXC_CATCH);\n\t}\n\n\tpublic static boolean isEqualPaths(BlockNode b1, BlockNode b2) {\n\t\tif (b1 == b2) {\n\t\t\treturn true;\n\t\t}\n\t\tif (b1 == null || b2 == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn isEqualReturnBlocks(b1, b2) || isEmptySyntheticPath(b1, b2) || isDuplicateBlockPath(b1, b2);\n\t}\n\n\tprivate static boolean isEmptySyntheticPath(BlockNode b1, BlockNode b2) {\n\t\tBlockNode n1 = followEmptyPath(b1);\n\t\tBlockNode n2 = followEmptyPath(b2);\n\t\treturn n1 == n2 || isEqualReturnBlocks(n1, n2);\n\t}\n\n\tpublic static boolean isEqualReturnBlocks(BlockNode b1, BlockNode b2) {\n\t\tif (!b1.isReturnBlock() || !b2.isReturnBlock()) {\n\t\t\treturn false;\n\t\t}\n\t\tList<InsnNode> b1Insns = b1.getInstructions();\n\t\tList<InsnNode> b2Insns = b2.getInstructions();\n\t\tif (b1Insns.size() != 1 || b2Insns.size() != 1) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnNode i1 = b1Insns.get(0);\n\t\tInsnNode i2 = b2Insns.get(0);\n\t\tif (i1.getArgsCount() != i2.getArgsCount()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (i1.getArgsCount() == 0) {\n\t\t\treturn true;\n\t\t}\n\t\tInsnArg firstArg = i1.getArg(0);\n\t\tInsnArg secondArg = i2.getArg(0);\n\t\tif (firstArg.isSameConst(secondArg)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (i1.getSourceLine() != i2.getSourceLine()) {\n\t\t\treturn false;\n\t\t}\n\t\treturn firstArg.equals(secondArg);\n\t}\n\n\tpublic static boolean isDuplicateBlockPath(BlockNode first, BlockNode second) {\n\t\tif (first.getSuccessors().size() == 1 && second.getSuccessors().size() == 1\n\t\t\t\t&& first.getSuccessors().get(0).equals(second.getSuccessors().get(0))) {\n\t\t\treturn isSameInsnsBlocks(first, second);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static boolean isSameInsnsBlocks(BlockNode first, BlockNode second) {\n\t\tList<InsnNode> firstInsns = first.getInstructions();\n\t\tList<InsnNode> secondInsns = second.getInstructions();\n\t\tif (firstInsns.size() != secondInsns.size()) {\n\t\t\treturn false;\n\t\t}\n\t\tint len = firstInsns.size();\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tInsnNode firstInsn = firstInsns.get(i);\n\t\t\tInsnNode secondInsn = secondInsns.get(i);\n\t\t\tif (!isInsnDeepEquals(firstInsn, secondInsn)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean isInsnDeepEquals(InsnNode first, InsnNode second) {\n\t\tif (first == second) {\n\t\t\treturn true;\n\t\t}\n\t\treturn first.isSame(second)\n\t\t\t\t&& Objects.equals(first.getArguments(), second.getArguments())\n\t\t\t\t&& resultIsSameReg(first.getResult(), second.getResult());\n\t}\n\n\tprivate static boolean resultIsSameReg(RegisterArg first, RegisterArg second) {\n\t\tif (first == null || second == null) {\n\t\t\treturn first == second;\n\t\t}\n\t\treturn first.getRegNum() == second.getRegNum();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/CacheStorage.java",
    "content": "package jadx.core.utils;\n\nimport java.util.Collections;\nimport java.util.Set;\n\npublic class CacheStorage {\n\n\tprivate Set<String> rootPkgs = Collections.emptySet();\n\n\tpublic Set<String> getRootPkgs() {\n\t\treturn rootPkgs;\n\t}\n\n\tpublic void setRootPkgs(Set<String> rootPkgs) {\n\t\tthis.rootPkgs = rootPkgs;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/DebugChecks.java",
    "content": "package jadx.core.utils;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.PhiListAttr;\nimport jadx.core.dex.attributes.nodes.TmpEdgeAttr;\nimport jadx.core.dex.instructions.IfNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.PhiInsn;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.instructions.mods.TernaryInsn;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.IDexTreeVisitor;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n/**\n * Check invariants and information consistency for blocks, instructions, registers, SSA variables.\n * These checks are very expensive and executed only in tests.\n */\npublic class DebugChecks {\n\n\tprivate static final Set<String> IGNORE_CHECKS = new HashSet<>(List.of(\n\t\t\t\"PrepareForCodeGen\",\n\t\t\t\"RenameVisitor\",\n\t\t\t\"DotGraphVisitor\"));\n\n\tpublic static List<IDexTreeVisitor> insertPasses(List<IDexTreeVisitor> passes) {\n\t\tint size = passes.size();\n\t\tList<IDexTreeVisitor> list = new ArrayList<>(size * 2);\n\t\tfor (IDexTreeVisitor pass : passes) {\n\t\t\tlist.add(pass);\n\t\t\tString name = pass.getName();\n\t\t\tif (!IGNORE_CHECKS.contains(name)) {\n\t\t\t\tlist.add(new DebugChecksPass(name));\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic static void runChecksAfterVisitor(MethodNode mth, String visitor) {\n\t\ttry {\n\t\t\tcheckMethod(mth);\n\t\t} catch (Exception e) {\n\t\t\tmth.addError(\"Debug check failed after visitor: \" + visitor, e);\n\t\t}\n\t}\n\n\tpublic static void checkMethod(MethodNode mth) {\n\t\tList<BlockNode> basicBlocks = mth.getBasicBlocks();\n\t\tif (Utils.isEmpty(basicBlocks)) {\n\t\t\treturn;\n\t\t}\n\t\tfor (BlockNode block : basicBlocks) {\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tcheckInsn(mth, block, insn);\n\t\t\t}\n\t\t}\n\t\tcheckSSAVars(mth);\n\t\tquickCheckPhiInsn(mth);\n\t\t// checkPHI(mth);\n\t}\n\n\tprivate static void checkInsn(MethodNode mth, BlockNode block, InsnNode insn) {\n\t\tif (insn.getResult() != null) {\n\t\t\tcheckVar(mth, insn, insn.getResult());\n\t\t}\n\t\tfor (InsnArg arg : insn.getArguments()) {\n\t\t\tif (arg instanceof RegisterArg) {\n\t\t\t\tcheckVar(mth, insn, (RegisterArg) arg);\n\t\t\t} else if (arg.isInsnWrap()) {\n\t\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\tcheckInsn(mth, block, wrapInsn);\n\t\t\t}\n\t\t}\n\t\tswitch (insn.getType()) {\n\t\t\tcase TERNARY:\n\t\t\t\tTernaryInsn ternaryInsn = (TernaryInsn) insn;\n\t\t\t\tfor (RegisterArg arg : ternaryInsn.getCondition().getRegisterArgs()) {\n\t\t\t\t\tcheckVar(mth, insn, arg);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase IF:\n\t\t\t\tIfNode ifNode = (IfNode) insn;\n\t\t\t\tif (!ifNode.getThenBlock().equals(ifNode.getElseBlock())) {\n\t\t\t\t\t// exclude temp edges\n\t\t\t\t\tint branches = (int) block.getSuccessors().stream().filter(b -> !hasTmpEdge(block, b)).count();\n\t\t\t\t\tif (branches != 2) {\n\t\t\t\t\t\tDebugUtils.dumpRaw(mth, \"error\");\n\t\t\t\t\t\tthrow new JadxRuntimeException(\n\t\t\t\t\t\t\t\t\"Incorrect if block successors count: \" + branches + \" (expect 2), block: \" + block);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcheckBlock(mth, ifNode.getThenBlock(), () -> \"then block in if insn: \" + ifNode);\n\t\t\t\tcheckBlock(mth, ifNode.getElseBlock(), () -> \"else block in if insn: \" + ifNode);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate static boolean hasTmpEdge(BlockNode start, BlockNode end) {\n\t\tTmpEdgeAttr tmpEdgeAttr = end.get(AType.TMP_EDGE);\n\t\tif (tmpEdgeAttr == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn tmpEdgeAttr.getBlock().equals(start);\n\t}\n\n\tprivate static void checkBlock(MethodNode mth, BlockNode block, Supplier<String> source) {\n\t\tif (!mth.getBasicBlocks().contains(block)) {\n\t\t\tthrow new JadxRuntimeException(\"Block not registered in method: \" + block + \" from \" + source.get());\n\t\t}\n\t}\n\n\tprivate static void checkVar(MethodNode mth, InsnNode insn, RegisterArg reg) {\n\t\tcheckRegisterArg(mth, reg);\n\n\t\tSSAVar sVar = reg.getSVar();\n\t\tif (sVar == null) {\n\t\t\tif (reg.contains(AFlag.DONT_GENERATE) || insn.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (Utils.notEmpty(mth.getSVars())) {\n\t\t\t\tthrow new JadxRuntimeException(\"Null SSA var in \" + reg + \" at \" + insn);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (Utils.indexInListByRef(mth.getSVars(), sVar) == -1) {\n\t\t\tthrow new JadxRuntimeException(\"SSA var not present in method vars list, var: \" + sVar + \" from insn: \" + insn);\n\t\t}\n\t\tRegisterArg resArg = insn.getResult();\n\t\tList<RegisterArg> useList = sVar.getUseList();\n\t\tif (resArg == reg) {\n\t\t\tif (sVar.getAssignInsn() != insn) {\n\t\t\t\tthrow new JadxRuntimeException(\"Incorrect assign in ssa var: \" + sVar\n\t\t\t\t\t\t+ \"\\n expected: \" + sVar.getAssignInsn()\n\t\t\t\t\t\t+ \"\\n got: \" + insn);\n\t\t\t}\n\t\t} else {\n\t\t\tif (!Utils.containsInListByRef(useList, reg)) {\n\t\t\t\tthrow new JadxRuntimeException(\"Incorrect use list in ssa var: \" + sVar + \", register not listed.\"\n\t\t\t\t\t\t+ \"\\n insn: \" + insn);\n\t\t\t}\n\t\t}\n\t\tfor (RegisterArg useArg : useList) {\n\t\t\tcheckRegisterArg(mth, useArg);\n\t\t}\n\t}\n\n\tprivate static void checkSSAVars(MethodNode mth) {\n\t\tfor (SSAVar ssaVar : mth.getSVars()) {\n\t\t\tRegisterArg assignArg = ssaVar.getAssign();\n\t\t\tif (assignArg.contains(AFlag.REMOVE)) {\n\t\t\t\t// ignore removed vars\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tInsnNode assignInsn = assignArg.getParentInsn();\n\t\t\tif (assignInsn != null) {\n\t\t\t\tif (insnMissing(mth, assignInsn)) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Insn not found for assign arg in SSAVar: \" + ssaVar + \", insn: \" + assignInsn);\n\t\t\t\t}\n\t\t\t\tRegisterArg resArg = assignInsn.getResult();\n\t\t\t\tif (resArg == null) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"SSA assign insn result missing. SSAVar: \" + ssaVar + \", insn: \" + assignInsn);\n\t\t\t\t}\n\t\t\t\tSSAVar assignVar = resArg.getSVar();\n\t\t\t\tif (!assignVar.equals(ssaVar)) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Unexpected SSAVar in assign. \"\n\t\t\t\t\t\t\t+ \"Expected: \" + ssaVar + \", got: \" + assignVar + \", insn: \" + assignInsn);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (RegisterArg arg : ssaVar.getUseList()) {\n\t\t\t\tInsnNode useInsn = arg.getParentInsn();\n\t\t\t\tif (useInsn == null) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Parent insn can't be null for arg in use list of SSAVar: \" + ssaVar);\n\t\t\t\t}\n\t\t\t\tif (insnMissing(mth, useInsn)) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Insn not found for use arg for SSAVar: \" + ssaVar + \", insn: \" + useInsn);\n\t\t\t\t}\n\t\t\t\tint argIndex = useInsn.getArgIndex(arg);\n\t\t\t\tif (argIndex == -1) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Use arg not found in insn for SSAVar: \" + ssaVar + \", insn: \" + useInsn);\n\t\t\t\t}\n\t\t\t\tInsnArg foundArg = useInsn.getArg(argIndex);\n\t\t\t\tif (!foundArg.equals(arg)) {\n\t\t\t\t\tthrow new JadxRuntimeException(\n\t\t\t\t\t\t\t\"Incorrect use arg in insn for SSAVar: \" + ssaVar + \", insn: \" + useInsn + \", arg: \" + foundArg);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean insnMissing(MethodNode mth, InsnNode insn) {\n\t\tif (insn.contains(AFlag.HIDDEN)) {\n\t\t\t// skip search\n\t\t\treturn false;\n\t\t}\n\t\tBlockNode block = BlockUtils.getBlockByInsn(mth, insn);\n\t\treturn block == null;\n\t}\n\n\tprivate static void checkRegisterArg(MethodNode mth, RegisterArg reg) {\n\t\tInsnNode parentInsn = reg.getParentInsn();\n\t\tif (parentInsn == null) {\n\t\t\tif (reg.contains(AFlag.METHOD_ARGUMENT)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow new JadxRuntimeException(\"Null parentInsn for reg: \" + reg);\n\t\t}\n\t\tif (!parentInsn.contains(AFlag.HIDDEN)) {\n\t\t\tif (parentInsn.getResult() != reg && !parentInsn.containsArg(reg)) {\n\t\t\t\tthrow new JadxRuntimeException(\"Incorrect parentInsn: \" + parentInsn + \", must contains arg: \" + reg);\n\t\t\t}\n\t\t\tBlockNode parentInsnBlock = BlockUtils.getBlockByInsn(mth, parentInsn);\n\t\t\tif (parentInsnBlock == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Parent insn not found in blocks tree for: \" + reg\n\t\t\t\t\t\t+ \"\\n insn: \" + parentInsn);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void quickCheckPhiInsn(MethodNode mth) {\n\t\tif (mth.getSVars().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tPhiListAttr phiListAttr = block.get(AType.PHI_LIST);\n\t\t\tif (phiListAttr != null) {\n\t\t\t\tfor (PhiInsn phiInsn : phiListAttr.getList()) {\n\t\t\t\t\tcheckPhiArg(mth, phiInsn, phiInsn.getResult(), () -> \"result\");\n\t\t\t\t\tint argsCount = phiInsn.getArgsCount();\n\t\t\t\t\tfor (int i = 0; i < argsCount; i++) {\n\t\t\t\t\t\tint argNum = i;\n\t\t\t\t\t\tcheckPhiArg(mth, phiInsn, phiInsn.getArg(argNum), () -> \"arg_\" + argNum);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void checkPhiArg(MethodNode mth, PhiInsn phiInsn, RegisterArg arg, Supplier<String> argName) {\n\t\tif (arg == null) {\n\t\t\tthrow new JadxRuntimeException(\"Null \" + argName.get() + \" in PHI insn: \" + phiInsn);\n\t\t}\n\t\tif (arg.getSVar() == null) {\n\t\t\tthrow new JadxRuntimeException(\"Null SSA variable in \" + argName.get() + \" in PHI insn: \" + phiInsn);\n\t\t}\n\t}\n\n\tprivate static void checkPHI(MethodNode mth) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tList<PhiInsn> phis = new ArrayList<>();\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tif (insn.getType() == InsnType.PHI) {\n\t\t\t\t\tPhiInsn phi = (PhiInsn) insn;\n\t\t\t\t\tphis.add(phi);\n\t\t\t\t\tif (phi.getArgsCount() == 0) {\n\t\t\t\t\t\tthrow new JadxRuntimeException(\"No args and binds in PHI\");\n\t\t\t\t\t}\n\t\t\t\t\tfor (InsnArg arg : insn.getArguments()) {\n\t\t\t\t\t\tif (arg instanceof RegisterArg) {\n\t\t\t\t\t\t\tBlockNode b = phi.getBlockByArg((RegisterArg) arg);\n\t\t\t\t\t\t\tif (b == null) {\n\t\t\t\t\t\t\t\tthrow new JadxRuntimeException(\"Predecessor block not found\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow new JadxRuntimeException(\"Not register in phi insn\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tPhiListAttr phiListAttr = block.get(AType.PHI_LIST);\n\t\t\tif (phiListAttr == null) {\n\t\t\t\tif (!phis.isEmpty()) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Missing PHI list attribute\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tList<PhiInsn> phiList = phiListAttr.getList();\n\t\t\t\tif (phiList.isEmpty()) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Empty PHI list attribute\");\n\t\t\t\t}\n\t\t\t\tif (!phis.containsAll(phiList) || !phiList.containsAll(phis)) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Instructions not match\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (SSAVar ssaVar : mth.getSVars()) {\n\t\t\tfor (PhiInsn usedInPhi : ssaVar.getUsedInPhi()) {\n\t\t\t\tboolean found = false;\n\t\t\t\tfor (RegisterArg useArg : ssaVar.getUseList()) {\n\t\t\t\t\tInsnNode parentInsn = useArg.getParentInsn();\n\t\t\t\t\tif (parentInsn != null && parentInsn == usedInPhi) {\n\t\t\t\t\t\tfound = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!found) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Used in phi incorrect\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/DebugChecksPass.java",
    "content": "package jadx.core.utils;\n\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.utils.exceptions.JadxException;\n\npublic class DebugChecksPass extends AbstractVisitor {\n\n\tprivate final String visitorName;\n\n\tpublic DebugChecksPass(String visitorName) {\n\t\tthis.visitorName = visitorName;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"Checks-for-\" + visitorName;\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) throws JadxException {\n\t\tif (!mth.contains(AType.JADX_ERROR)) {\n\t\t\ttry {\n\t\t\t\tDebugChecks.runChecksAfterVisitor(mth, visitorName);\n\t\t\t} catch (Exception e) {\n\t\t\t\tmth.addError(\"Check error\", e);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/DebugUtils.java",
    "content": "package jadx.core.utils;\n\nimport java.io.File;\nimport java.util.Comparator;\nimport java.util.Iterator;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeWriter;\nimport jadx.api.impl.SimpleCodeWriter;\nimport jadx.core.codegen.ConditionGen;\nimport jadx.core.codegen.InsnGen;\nimport jadx.core.codegen.MethodGen;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.IAttributeNode;\nimport jadx.core.dex.attributes.nodes.MethodOverrideAttr;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.regions.Region;\nimport jadx.core.dex.regions.conditions.IfCondition;\nimport jadx.core.dex.regions.loops.LoopRegion;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.DotGraphVisitor;\nimport jadx.core.dex.visitors.IDexTreeVisitor;\nimport jadx.core.dex.visitors.regions.DepthRegionTraversal;\nimport jadx.core.dex.visitors.regions.TracedRegionVisitor;\nimport jadx.core.utils.exceptions.CodegenException;\nimport jadx.core.utils.exceptions.JadxException;\n\n/**\n * Use these methods only for debug purpose.\n * CheckStyle will reject usage of this class.\n */\npublic class DebugUtils {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DebugUtils.class);\n\n\tpublic static final Predicate<MethodNode> TEST_MTH_FILTER = mth -> mth.getName().equals(\"test\");\n\n\tprivate DebugUtils() {\n\t}\n\n\tpublic static void dump(MethodNode mth) {\n\t\tdump(mth, \"dump\");\n\t}\n\n\tpublic static void dumpRaw(MethodNode mth, String desc, Predicate<MethodNode> dumpCondition) {\n\t\tif (dumpCondition.test(mth)) {\n\t\t\tdumpRaw(mth, desc);\n\t\t}\n\t}\n\n\tpublic static void dumpRawTest(MethodNode mth, String desc) {\n\t\tdumpRaw(mth, desc, TEST_MTH_FILTER);\n\t}\n\n\tpublic static void dumpRaw(MethodNode mth, String desc) {\n\t\tFile out = new File(\"test-graph-\" + desc + \"-tmp\");\n\t\tDotGraphVisitor.dumpRaw().save(out, mth);\n\t}\n\n\tpublic static IDexTreeVisitor dumpRawVisitor(String desc) {\n\t\treturn new AbstractVisitor() {\n\t\t\t@Override\n\t\t\tpublic void visit(MethodNode mth) throws JadxException {\n\t\t\t\tdumpRaw(mth, desc);\n\t\t\t}\n\t\t};\n\t}\n\n\tpublic static IDexTreeVisitor dumpRawVisitor(String desc, Predicate<MethodNode> filter) {\n\t\treturn new AbstractVisitor() {\n\t\t\t@Override\n\t\t\tpublic void visit(MethodNode mth) {\n\t\t\t\tif (filter.test(mth)) {\n\t\t\t\t\tdumpRaw(mth, desc);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n\n\tpublic static IDexTreeVisitor dumpRawTestVisitor(String desc) {\n\t\treturn dumpRawVisitor(desc, TEST_MTH_FILTER);\n\t}\n\n\tpublic static void dump(MethodNode mth, String desc) {\n\t\tFile out = new File(\"test-graph-\" + desc + \"-tmp\");\n\t\tDotGraphVisitor.dump().save(out, mth);\n\t\tDotGraphVisitor.dumpRaw().save(out, mth);\n\t\tDotGraphVisitor.dumpRegions().save(out, mth);\n\t}\n\n\tpublic static void printRegionsWithBlock(MethodNode mth, BlockNode block) {\n\t\tSet<IRegion> regions = new LinkedHashSet<>();\n\t\tDepthRegionTraversal.traverse(mth, new TracedRegionVisitor() {\n\t\t\t@Override\n\t\t\tpublic void processBlockTraced(MethodNode mth, IBlock container, IRegion currentRegion) {\n\t\t\t\tif (block.equals(container)) {\n\t\t\t\t\tregions.add(currentRegion);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tLOG.debug(\" Found block: {} in regions: {}\", block, regions);\n\t}\n\n\tpublic static IDexTreeVisitor printRegionsVisitor() {\n\t\treturn new AbstractVisitor() {\n\t\t\t@Override\n\t\t\tpublic void visit(MethodNode mth) throws JadxException {\n\t\t\t\tprintRegions(mth, true);\n\t\t\t}\n\t\t};\n\t}\n\n\tpublic static void printRegions(MethodNode mth) {\n\t\tprintRegions(mth, false);\n\t}\n\n\tpublic static void printRegions(MethodNode mth, boolean printInsns) {\n\t\tRegion mthRegion = mth.getRegion();\n\t\tif (mthRegion == null) {\n\t\t\treturn;\n\t\t}\n\t\tprintRegion(mth, mthRegion, printInsns);\n\t}\n\n\tpublic static void printRegion(MethodNode mth, IRegion region, boolean printInsns) {\n\t\tICodeWriter cw = new SimpleCodeWriter();\n\t\tcw.startLine('|').add(mth.toString());\n\t\tprintRegion(mth, region, cw, \"|  \", printInsns);\n\t\tLOG.debug(\"{}{}\", '\\n', cw.finish().getCodeStr());\n\t}\n\n\tprivate static void printRegion(MethodNode mth, IRegion region, ICodeWriter cw, String indent, boolean printInsns) {\n\t\tprintWithAttributes(cw, indent, region.toString(), region);\n\t\tindent += \"|  \";\n\t\tprintRegionSpecificInfo(cw, indent, mth, region, printInsns);\n\t\tfor (IContainer container : region.getSubBlocks()) {\n\t\t\tif (container instanceof IRegion) {\n\t\t\t\tprintRegion(mth, (IRegion) container, cw, indent, printInsns);\n\t\t\t} else {\n\t\t\t\tprintWithAttributes(cw, indent, container.toString(), container);\n\t\t\t\tif (printInsns && container instanceof IBlock) {\n\t\t\t\t\tIBlock block = (IBlock) container;\n\t\t\t\t\tprintInsns(mth, cw, indent, block);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void printRegionSpecificInfo(ICodeWriter cw, String indent,\n\t\t\tMethodNode mth, IRegion region, boolean printInsns) {\n\t\tif (region instanceof LoopRegion) {\n\t\t\tLoopRegion loop = (LoopRegion) region;\n\t\t\tIfCondition condition = loop.getCondition();\n\t\t\tif (printInsns && condition != null) {\n\t\t\t\tConditionGen conditionGen = new ConditionGen(new InsnGen(MethodGen.getFallbackMethodGen(mth), true));\n\t\t\t\tcw.startLine(indent).add(\"|> \");\n\t\t\t\ttry {\n\t\t\t\t\tconditionGen.add(cw, condition);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tcw.startLine(indent).add(\">!! \").add(condition.toString());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void printInsns(MethodNode mth, ICodeWriter cw, String indent, IBlock block) {\n\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\ttry {\n\t\t\t\tMethodGen mg = MethodGen.getFallbackMethodGen(mth);\n\t\t\t\tInsnGen ig = new InsnGen(mg, true);\n\t\t\t\tICodeWriter code = new SimpleCodeWriter();\n\t\t\t\tig.makeInsn(insn, code);\n\t\t\t\tString codeStr = code.getCodeStr();\n\n\t\t\t\tList<String> insnStrings = Stream.of(codeStr.split(\"\\\\R\"))\n\t\t\t\t\t\t.filter(StringUtils::notBlank)\n\t\t\t\t\t\t.map(s -> \"|> \" + s)\n\t\t\t\t\t\t.collect(Collectors.toList());\n\t\t\t\tIterator<String> it = insnStrings.iterator();\n\t\t\t\twhile (true) {\n\t\t\t\t\tString insnStr = it.next();\n\t\t\t\t\tif (it.hasNext()) {\n\t\t\t\t\t\tcw.startLine(indent).add(insnStr);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tprintWithAttributes(cw, indent, insnStr, insn);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (CodegenException e) {\n\t\t\t\tcw.startLine(indent).add(\">!! \").add(insn.toString());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void printWithAttributes(ICodeWriter cw, String indent, String codeStr, IAttributeNode attrNode) {\n\t\tString str = attrNode.isAttrStorageEmpty() ? codeStr : codeStr + ' ' + attrNode.getAttributesString();\n\t\tList<String> attrStrings = Stream.of(str.split(\"\\\\R\"))\n\t\t\t\t.filter(StringUtils::notBlank)\n\t\t\t\t.collect(Collectors.toList());\n\t\tIterator<String> it = attrStrings.iterator();\n\t\tif (!it.hasNext()) {\n\t\t\treturn;\n\t\t}\n\t\tcw.startLine(indent).add(it.next());\n\t\twhile (it.hasNext()) {\n\t\t\tcw.startLine(indent).add(\"|+  \").add(it.next());\n\t\t}\n\t}\n\n\tpublic static void printMap(Map<?, ?> map, String desc) {\n\t\tLOG.debug(\"Map {} (size = {}):\", desc, map.size());\n\t\tfor (Map.Entry<?, ?> entry : map.entrySet()) {\n\t\t\tLOG.debug(\"  {}: {}\", entry.getKey(), entry.getValue());\n\t\t}\n\t}\n\n\tpublic static void printStackTrace(String label) {\n\t\tLOG.debug(\"StackTrace: {}\\n{}\", label, Utils.getFullStackTrace(new Exception()));\n\t}\n\n\tpublic static void printMethodOverrideTop(RootNode root) {\n\t\tLOG.debug(\"Methods override top 10:\");\n\t\troot.getClasses().stream()\n\t\t\t\t.flatMap(c -> c.getMethods().stream())\n\t\t\t\t.filter(m -> m.contains(AType.METHOD_OVERRIDE))\n\t\t\t\t.map(m -> m.get(AType.METHOD_OVERRIDE))\n\t\t\t\t.filter(o -> !o.getOverrideList().isEmpty())\n\t\t\t\t.filter(distinctByKey(methodOverrideAttr -> methodOverrideAttr.getRelatedMthNodes().size()))\n\t\t\t\t.filter(distinctByKey(MethodOverrideAttr::getRelatedMthNodes))\n\t\t\t\t.sorted(Comparator.comparingInt(o -> -o.getRelatedMthNodes().size()))\n\t\t\t\t.limit(10)\n\t\t\t\t.forEach(o -> LOG.debug(\"  {} : {}\", o.getRelatedMthNodes().size(), Utils.last(o.getOverrideList())));\n\t}\n\n\tprivate static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {\n\t\tSet<Object> seen = ConcurrentHashMap.newKeySet();\n\t\treturn t -> seen.add(keyExtractor.apply(t));\n\t}\n\n\tprivate static Map<String, Long> execTimes;\n\n\tpublic static void initExecTimes() {\n\t\texecTimes = new ConcurrentHashMap<>();\n\t}\n\n\tpublic static void mergeExecTimeFromStart(String tag, long startTimeMillis) {\n\t\tmergeExecTime(tag, System.currentTimeMillis() - startTimeMillis);\n\t}\n\n\tpublic static void mergeExecTime(String tag, long execTimeMillis) {\n\t\texecTimes.merge(tag, execTimeMillis, Long::sum);\n\t}\n\n\tpublic static void printExecTimes() {\n\t\tSystem.out.println(\"Exec times:\");\n\t\texecTimes.forEach((tag, time) -> System.out.println(\" \" + tag + \": \" + time + \"ms\"));\n\t}\n\n\tpublic static void printExecTimesWithTotal(long totalMillis) {\n\t\tSystem.out.println(\"Exec times: total \" + totalMillis + \"ms\");\n\t\texecTimes.forEach((tag, time) -> System.out.println(\" \" + tag + \": \" + time + \"ms\"\n\t\t\t\t+ String.format(\" (%.2f%%)\", time * 100. / (double) totalMillis)));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/DecompilerScheduler.java",
    "content": "package jadx.core.utils;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.IDecompileScheduler;\nimport jadx.api.JavaClass;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class DecompilerScheduler implements IDecompileScheduler {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DecompilerScheduler.class);\n\n\tprivate static final int MERGED_BATCH_SIZE = 16;\n\tprivate static final boolean DEBUG_BATCHES = false;\n\n\t@Override\n\tpublic List<List<JavaClass>> buildBatches(List<JavaClass> classes) {\n\t\ttry {\n\t\t\tlong start = System.currentTimeMillis();\n\t\t\tList<List<JavaClass>> result = internalBatches(classes);\n\t\t\tif (LOG.isDebugEnabled()) {\n\t\t\t\tLOG.debug(\"Build decompilation batches in {}ms for {} classes\",\n\t\t\t\t\t\tSystem.currentTimeMillis() - start, classes.size());\n\t\t\t}\n\t\t\tif (DEBUG_BATCHES) {\n\t\t\t\tcheck(result, classes);\n\t\t\t}\n\t\t\treturn result;\n\t\t} catch (StackOverflowError | BootstrapMethodError e) {\n\t\t\tLOG.warn(\"Stack overflow while building decompile batches, continue with fallback\");\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Build batches failed (continue with fallback)\", e);\n\t\t}\n\t\treturn buildFallback(classes);\n\t}\n\n\t/**\n\t * Put classes with many dependencies at the end.\n\t * Build batches for dependencies of single class to avoid locking from another thread.\n\t */\n\tpublic List<List<JavaClass>> internalBatches(List<JavaClass> classes) {\n\t\tList<DepInfo> deps = sumDependencies(classes);\n\t\tSet<JavaClass> added = new HashSet<>(classes.size());\n\t\tComparator<JavaClass> cmpDepSize = Comparator.comparingInt(JavaClass::getTotalDepsCount);\n\t\tList<List<JavaClass>> result = new ArrayList<>();\n\t\tList<JavaClass> mergedBatch = new ArrayList<>(MERGED_BATCH_SIZE);\n\t\tfor (DepInfo depInfo : deps) {\n\t\t\tJavaClass cls = depInfo.getCls();\n\t\t\tif (!added.add(cls)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tint depsSize = cls.getTotalDepsCount();\n\t\t\tif (depsSize == 0) {\n\t\t\t\t// add classes without dependencies in merged batch\n\t\t\t\tmergedBatch.add(cls);\n\t\t\t\tif (mergedBatch.size() >= MERGED_BATCH_SIZE) {\n\t\t\t\t\tresult.add(mergedBatch);\n\t\t\t\t\tmergedBatch = new ArrayList<>(MERGED_BATCH_SIZE);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tList<JavaClass> batch = new ArrayList<>();\n\t\t\t\tfor (JavaClass dep : cls.getDependencies()) {\n\t\t\t\t\tJavaClass topDep = dep.getTopParentClass();\n\t\t\t\t\tif (!added.contains(topDep)) {\n\t\t\t\t\t\tbatch.add(topDep);\n\t\t\t\t\t\tadded.add(topDep);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbatch.sort(cmpDepSize);\n\t\t\t\tbatch.add(cls);\n\t\t\t\tresult.add(Utils.lockList(batch));\n\t\t\t}\n\t\t}\n\t\tif (!mergedBatch.isEmpty()) {\n\t\t\tresult.add(mergedBatch);\n\t\t}\n\t\tif (DEBUG_BATCHES) {\n\t\t\tdumpBatchesStats(classes, result, deps);\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate static List<DepInfo> sumDependencies(List<JavaClass> classes) {\n\t\tList<DepInfo> deps = new ArrayList<>(classes.size());\n\t\tfor (JavaClass cls : classes) {\n\t\t\tint count = 0;\n\t\t\tfor (JavaClass dep : cls.getDependencies()) {\n\t\t\t\tcount += 1 + dep.getTotalDepsCount();\n\t\t\t}\n\t\t\tdeps.add(new DepInfo(cls, count));\n\t\t}\n\t\tCollections.sort(deps);\n\t\treturn deps;\n\t}\n\n\tprivate static final class DepInfo implements Comparable<DepInfo> {\n\t\tprivate final JavaClass cls;\n\t\tprivate final int depsCount;\n\n\t\tprivate DepInfo(JavaClass cls, int depsCount) {\n\t\t\tthis.cls = cls;\n\t\t\tthis.depsCount = depsCount;\n\t\t}\n\n\t\tpublic JavaClass getCls() {\n\t\t\treturn cls;\n\t\t}\n\n\t\tpublic int getDepsCount() {\n\t\t\treturn depsCount;\n\t\t}\n\n\t\t@Override\n\t\tpublic int compareTo(@NotNull DecompilerScheduler.DepInfo o) {\n\t\t\tint deps = Integer.compare(depsCount, o.depsCount);\n\t\t\tif (deps == 0) {\n\t\t\t\treturn cls.getClassNode().compareTo(o.cls.getClassNode());\n\t\t\t}\n\t\t\treturn deps;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn cls + \":\" + depsCount;\n\t\t}\n\t}\n\n\tprivate static List<List<JavaClass>> buildFallback(List<JavaClass> classes) {\n\t\treturn classes.stream()\n\t\t\t\t.sorted(Comparator.comparingInt(c -> c.getClassNode().getTotalDepsCount()))\n\t\t\t\t.map(Collections::singletonList)\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tprivate void dumpBatchesStats(List<JavaClass> classes, List<List<JavaClass>> result, List<DepInfo> deps) {\n\t\tint clsInBatches = result.stream().mapToInt(List::size).sum();\n\t\tdouble avg = result.stream().mapToInt(List::size).average().orElse(-1);\n\t\tint maxSingleDeps = classes.stream().mapToInt(JavaClass::getTotalDepsCount).max().orElse(-1);\n\t\tint maxSubDeps = deps.stream().mapToInt(DepInfo::getDepsCount).max().orElse(-1);\n\t\tLOG.info(\"Batches stats:\"\n\t\t\t\t+ \"\\n input classes: \" + classes.size()\n\t\t\t\t+ \",\\n classes in batches: \" + clsInBatches\n\t\t\t\t+ \",\\n batches: \" + result.size()\n\t\t\t\t+ \",\\n average batch size: \" + String.format(\"%.2f\", avg)\n\t\t\t\t+ \",\\n max single deps count: \" + maxSingleDeps\n\t\t\t\t+ \",\\n max sub deps count: \" + maxSubDeps);\n\t}\n\n\tprivate static void check(List<List<JavaClass>> result, List<JavaClass> classes) {\n\t\tint classInBatches = result.stream().mapToInt(List::size).sum();\n\t\tif (classes.size() != classInBatches) {\n\t\t\tthrow new JadxRuntimeException(\n\t\t\t\t\t\"Incorrect number of classes in result batch: \" + classInBatches + \", expected: \" + classes.size());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/DotGraphUtils.java",
    "content": "package jadx.core.utils;\n\nimport java.io.File;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.stream.Collectors;\n\nimport jadx.api.ICodeWriter;\nimport jadx.api.JavaMethod;\nimport jadx.api.impl.SimpleCodeWriter;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.core.codegen.MethodGen;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.IAttributeNode;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.instructions.IfNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.SwitchRegion;\nimport jadx.core.dex.regions.SynchronizedRegion;\nimport jadx.core.dex.regions.TryCatchRegion;\nimport jadx.core.dex.regions.conditions.IfRegion;\nimport jadx.core.dex.regions.loops.LoopRegion;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.dex.visitors.SaveCode;\nimport jadx.core.utils.files.FileUtils;\n\nimport static jadx.core.codegen.MethodGen.FallbackOption.BLOCK_DUMP;\n\npublic class DotGraphUtils {\n\tprivate static final String NL = \"\\\\l\";\n\tprivate static final String NLQR = Matcher.quoteReplacement(NL);\n\tprivate static final boolean PRINT_DOMINATORS = false;\n\tprivate static final boolean PRINT_DOMINATORS_INFO = false;\n\tprivate static final int MAX_REGION_NAME_LENGTH = 2000;\n\n\tprivate final ICodeWriter dot = new SimpleCodeWriter();\n\tprivate final ICodeWriter conn = new SimpleCodeWriter();\n\n\tprivate final boolean useRegions;\n\tprivate final boolean rawInsn;\n\n\t// if present, this region and it's children will still be drawn when not in regions mode.\n\tprivate Optional<IRegion> highlightRegion;\n\n\t// flag set when the highlighted region has been processed once, to avoid processing it's children\n\t// more than once\n\tprivate boolean processedHighlightRegion = false;\n\n\tpublic DotGraphUtils(boolean useRegions, boolean rawInsn) {\n\t\tthis(useRegions, rawInsn, Optional.empty());\n\t}\n\n\tpublic DotGraphUtils(boolean useRegions, boolean rawInsn, Optional<IRegion> highlightRegion) {\n\t\tthis.useRegions = useRegions;\n\t\tthis.rawInsn = rawInsn;\n\t\tthis.highlightRegion = highlightRegion;\n\t}\n\n\t// The default out directory for the method\n\tpublic static File getOutDir(MethodNode mth) {\n\t\treturn mth.root().getArgs().getOutDir();\n\t}\n\n\t// The filename the method cfg would be stored in under the default out directory\n\tpublic File getFullFile(MethodNode mth) {\n\t\treturn getFullFile(mth, getOutDir(mth));\n\t}\n\n\t// The filename the method cfg would be stored in under the given out directory\n\tpublic File getFullFile(MethodNode mth, File outDir) {\n\t\tString fileName = StringUtils.escape(mth.getMethodInfo().getShortId())\n\t\t\t\t+ (useRegions ? \".regions\" : \"\")\n\t\t\t\t+ (rawInsn ? \".raw\" : \"\")\n\t\t\t\t+ \".dot\";\n\t\tFile file = outDir.toPath()\n\t\t\t\t.resolve(mth.getParentClass().getClassInfo().getAliasFullPath() + \"_graphs\")\n\t\t\t\t.resolve(fileName)\n\t\t\t\t.toFile();\n\t\tfile = FileUtils.cutFileName(file);\n\t\treturn file;\n\t}\n\n\tpublic void dumpToFile(MethodNode mth) {\n\t\tFile dir = getOutDir(mth);\n\t\tdumpToFile(mth, dir);\n\t}\n\n\tpublic void dumpToFile(MethodNode mth, File dir) {\n\t\tString graph = dumpToString(mth);\n\n\t\tif (graph == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tFile file = getFullFile(mth, dir);\n\t\tSaveCode.save(graph, file);\n\t}\n\n\tpublic String dumpToString(MethodNode mth) {\n\t\tdot.startLine(\"digraph \\\"CFG for\");\n\t\tdot.add(escape(mth.getMethodInfo().getFullId()));\n\t\tdot.add(\"\\\" {\");\n\n\t\tBlockNode enterBlock = mth.getEnterBlock();\n\t\tif (useRegions) {\n\t\t\tif (mth.getRegion() == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tprocessMethodRegion(mth);\n\t\t} else {\n\t\t\tList<BlockNode> blocks = mth.getBasicBlocks();\n\t\t\tif (blocks == null) {\n\t\t\t\tInsnNode[] insnArr = mth.getInstructions();\n\t\t\t\tif (insnArr == null) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tBlockNode block = new BlockNode(0, 0, 0);\n\t\t\t\tList<InsnNode> insnList = block.getInstructions();\n\t\t\t\tfor (InsnNode insn : insnArr) {\n\t\t\t\t\tif (insn != null) {\n\t\t\t\t\t\tinsnList.add(insn);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tenterBlock = block;\n\t\t\t\tblocks = Collections.singletonList(block);\n\t\t\t}\n\t\t\tfor (BlockNode block : blocks) {\n\t\t\t\tif (processedHighlightRegion && highlightRegion.isPresent()\n\t\t\t\t\t\t&& RegionUtils.isRegionContainsBlock(highlightRegion.get(), block)) {\n\t\t\t\t\t// Don't process blocks in the highlight region if it's already been processed, since processing the\n\t\t\t\t\t// region will already process all it's containing blocks.\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tprocessBlock(mth, block);\n\n\t\t\t}\n\t\t}\n\n\t\tdot.startLine(\"MethodNode[shape=record,label=\\\"{\");\n\t\tdot.add(escape(mth.getAccessFlags().makeString(true)));\n\t\tdot.add(escape(mth.getReturnType() + \" \"\n\t\t\t\t+ mth.getParentClass() + '.' + mth.getName()\n\t\t\t\t+ '(' + Utils.listToString(mth.getAllArgRegs()) + \") \"));\n\n\t\tString attrs = attributesString(mth);\n\t\tif (!attrs.isEmpty()) {\n\t\t\tdot.add(\" | \").add(attrs);\n\t\t}\n\t\tdot.add(\"}\\\"];\");\n\n\t\tdot.startLine(\"MethodNode -> \").add(makeName(enterBlock)).add(';');\n\n\t\tdot.add(conn.toString());\n\n\t\tdot.startLine('}');\n\t\tdot.startLine();\n\n\t\treturn dot.finish().getCodeStr();\n\t}\n\n\tprivate void processMethodRegion(MethodNode mth) {\n\t\tSet<IBlock> regionsBlocks = new HashSet<>(mth.getBasicBlocks().size());\n\t\tRegionUtils.getAllRegionBlocks(mth.getRegion(), regionsBlocks);\n\t\tfor (ExceptionHandler handler : mth.getExceptionHandlers()) {\n\t\t\tIContainer handlerRegion = handler.getHandlerRegion();\n\t\t\tif (handlerRegion != null) {\n\t\t\t\tRegionUtils.getAllRegionBlocks(handlerRegion, regionsBlocks);\n\t\t\t}\n\t\t}\n\n\t\tprocessRegion(mth, mth.getRegion(), regionsBlocks);\n\t\tfor (ExceptionHandler h : mth.getExceptionHandlers()) {\n\t\t\tif (h.getHandlerRegion() != null) {\n\t\t\t\tprocessRegion(mth, h.getHandlerRegion(), regionsBlocks);\n\t\t\t}\n\t\t}\n\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tif (!regionsBlocks.contains(block)) {\n\t\t\t\tprocessBlock(mth, block, true, false);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void processRegion(MethodNode mth, IContainer region, Set<IBlock> regionsBlocks) {\n\t\tif (region instanceof IRegion) {\n\t\t\tIRegion r = (IRegion) region;\n\t\t\tdot.startLine(\"subgraph \" + makeName(region) + \" {\");\n\t\t\tdot.startLine(\"color = \" + getColorForRegion(r));\n\t\t\tdot.startLine(\"label = \\\"\").add(truncateRegionName(r));\n\t\t\tdot.add(\"\\\";\");\n\t\t\tdot.startLine(\"node [shape=record,color=blue];\");\n\n\t\t\tfor (IContainer c : r.getSubBlocks()) {\n\t\t\t\tprocessRegion(mth, c, regionsBlocks);\n\t\t\t}\n\n\t\t\tdot.startLine('}');\n\t\t} else if (region instanceof BlockNode) {\n\t\t\tcheckAndFixFloatingBlocks(mth, (BlockNode) region, regionsBlocks);\n\t\t\tprocessBlock(mth, (BlockNode) region);\n\t\t} else if (region instanceof IBlock) {\n\t\t\tprocessIBlock(mth, (IBlock) region);\n\t\t}\n\t}\n\n\tprivate String getColorForRegion(IRegion region) {\n\t\tif (region instanceof IfRegion) {\n\t\t\treturn \"lightgoldenrod3\";\n\t\t} else if (region instanceof LoopRegion) {\n\t\t\treturn \"lightpink2\";\n\t\t} else if (region instanceof SwitchRegion) {\n\t\t\treturn \"lightsteelblue3\";\n\t\t} else if (region instanceof SynchronizedRegion) {\n\t\t\treturn \"mediumpurple3\";\n\t\t} else if (region instanceof TryCatchRegion) {\n\t\t\treturn \"olivedrab4\";\n\t\t} else if (region.contains(AType.EXC_HANDLER)) {\n\t\t\treturn \"orangered4\";\n\t\t}\n\t\treturn \"gray\";\n\t}\n\n\tprivate String truncateRegionName(IRegion r) {\n\t\tString regionName = r.toString();\n\t\tString attrs = attributesString(r);\n\t\tif (!attrs.isEmpty()) {\n\t\t\tregionName += \" | \" + attrs;\n\t\t}\n\t\tif (regionName.length() > MAX_REGION_NAME_LENGTH) {\n\t\t\tregionName = regionName.substring(0, MAX_REGION_NAME_LENGTH);\n\t\t\tregionName += \"...\";\n\t\t}\n\t\treturn regionName;\n\t}\n\n\t/**\n\t * A block is floating if it exists in no regions at all. These are placed in a region that makes\n\t * sense for generation of this graph only, because otherwise the generated graph is unreadable.\n\t */\n\tprivate void checkAndFixFloatingBlocks(MethodNode mth, BlockNode block, Set<IBlock> regionBlocks) {\n\t\tif (regionBlocks == null || regionBlocks.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Heuristic: place the floating block in the same region as either it's predecessor or successor,\n\t\t// depending on which it has less of. This results in a more readable graph as a block with a single\n\t\t// predecessor will be placed near it.\n\t\tfor (BlockNode floating : block.getSuccessors()) {\n\t\t\tif (!regionBlocks.contains(floating) && floating.getPredecessors().size() <= floating.getSuccessors().size()) {\n\t\t\t\t// Set true on the pseudoInRegion to draw the block with a dotted outline and apply a marker to it\n\t\t\t\t// to notify that it isn't actually in this region.\n\t\t\t\tprocessBlock(mth, floating, true, true);\n\t\t\t\tregionBlocks.add(floating);\n\t\t\t}\n\t\t}\n\n\t\tfor (BlockNode floating : block.getPredecessors()) {\n\t\t\tif (!regionBlocks.contains(floating) && floating.getPredecessors().size() > floating.getSuccessors().size()) {\n\t\t\t\tprocessBlock(mth, floating, true, true);\n\t\t\t\tregionBlocks.add(floating);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void processBlock(MethodNode mth, BlockNode block) {\n\t\tprocessBlock(mth, block, false, false);\n\t}\n\n\tprivate void processBlock(MethodNode mth, BlockNode block, boolean error, boolean pseudoInRegion) {\n\t\tif (!processedHighlightRegion && highlightRegion.isPresent()\n\t\t\t\t&& RegionUtils.isRegionContainsBlock(highlightRegion.get(), block)) {\n\t\t\tprocessedHighlightRegion = true;\n\t\t\tprocessRegion(mth, highlightRegion.get(), null);\n\t\t\treturn;\n\t\t}\n\n\t\tboolean isMthStart = block.contains(AFlag.MTH_ENTER_BLOCK);\n\t\tboolean isMthEnd = block.contains(AFlag.MTH_EXIT_BLOCK);\n\n\t\tif (isMthEnd) {\n\t\t\tdot.startLine(\"subgraph { rank = sink; \");\n\t\t}\n\n\t\tdot.startLine(makeName(block));\n\t\tdot.add(\" [shape=record,\");\n\t\tif (error) {\n\t\t\tdot.add(\"color=red,\");\n\t\t}\n\t\tif (pseudoInRegion) {\n\t\t\tdot.add(\"style = \\\"filled,dashed\\\"\");\n\t\t} else {\n\t\t\tdot.add(\"style = filled,\");\n\t\t}\n\t\tif (isMthStart || isMthEnd) {\n\t\t\tdot.add(\"fillcolor = \\\"#def3fd\\\",\");\n\t\t} else {\n\t\t\tdot.add(\"fillcolor = \\\"#f8fafb\\\",\");\n\t\t}\n\t\tdot.add(\"label=\\\"{\");\n\t\tdot.add(String.valueOf(block.getCId())).add(\"\\\\:\\\\ \");\n\t\tdot.add(InsnUtils.formatOffset(block.getStartOffset()));\n\t\tif (pseudoInRegion) {\n\t\t\tdot.add(\"\\\\nNOT IN ANY REGION\");\n\t\t}\n\n\t\tString attrs = attributesString(block);\n\t\tif (!attrs.isEmpty()) {\n\t\t\tdot.add('|').add(attrs);\n\t\t}\n\n\t\tif (PRINT_DOMINATORS_INFO) {\n\t\t\tdot.add('|');\n\t\t\tdot.startLine(\"doms: \").add(escape(block.getDoms()));\n\t\t\tdot.startLine(\"\\\\lidom: \").add(escape(block.getIDom()));\n\t\t\tdot.startLine(\"\\\\lpost-doms: \").add(escape(block.getPostDoms()));\n\t\t\tdot.startLine(\"\\\\lpost-idom: \").add(escape(block.getIPostDom()));\n\t\t\tdot.startLine(\"\\\\ldom-f: \").add(escape(block.getDomFrontier()));\n\t\t\tdot.startLine(\"\\\\ldoms-on: \").add(escape(Utils.listToString(block.getDominatesOn())));\n\t\t\tdot.startLine(\"\\\\l\");\n\t\t}\n\t\tString insns = insertInsns(mth, block);\n\t\tif (!insns.isEmpty()) {\n\t\t\tdot.add('|').add(insns);\n\t\t}\n\t\tdot.add(\"}\\\"];\");\n\n\t\tif (isMthEnd) {\n\t\t\tdot.add(\"};\");\n\t\t}\n\n\t\tBlockNode falsePath = null;\n\t\tInsnNode lastInsn = BlockUtils.getLastInsn(block);\n\t\tif (lastInsn != null && lastInsn.getType() == InsnType.IF) {\n\t\t\tfalsePath = ((IfNode) lastInsn).getElseBlock();\n\t\t}\n\t\tfor (BlockNode next : block.getSuccessors()) {\n\t\t\tString style = next == falsePath ? \"[style=dashed]\" : \"\";\n\t\t\taddEdge(block, next, style);\n\t\t}\n\n\t\tif (PRINT_DOMINATORS) {\n\t\t\tfor (BlockNode c : block.getDominatesOn()) {\n\t\t\t\tconn.startLine(block.getCId() + \" -> \" + c.getCId() + \"[color=green];\");\n\t\t\t}\n\t\t\tfor (BlockNode dom : BlockUtils.bitSetToBlocks(mth, block.getDomFrontier())) {\n\t\t\t\tconn.startLine(\"f_\" + block.getCId() + \" -> f_\" + dom.getCId() + \"[color=blue];\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void processIBlock(MethodNode mth, IBlock block) {\n\t\tprocessIBlock(mth, block, false);\n\t}\n\n\tprivate void processIBlock(MethodNode mth, IBlock block, boolean error) {\n\t\tString attrs = attributesString(block);\n\t\tdot.startLine(makeName(block));\n\t\tdot.add(\" [shape=record,\");\n\t\tif (error) {\n\t\t\tdot.add(\"color=red,\");\n\t\t}\n\t\tdot.add(\"label=\\\"{\");\n\t\tif (!attrs.isEmpty()) {\n\t\t\tdot.add(attrs);\n\t\t}\n\t\tString insns = insertInsns(mth, block);\n\t\tif (!insns.isEmpty()) {\n\t\t\tdot.add('|').add(insns);\n\t\t}\n\t\tdot.add(\"}\\\"];\");\n\t}\n\n\tprivate void addEdge(BlockNode from, BlockNode to, String style) {\n\t\tconn.startLine(makeName(from)).add(\" -> \").add(makeName(to));\n\t\tconn.add(style);\n\t\tconn.add(';');\n\t}\n\n\tprivate String attributesString(IAttributeNode block) {\n\t\tStringBuilder attrs = new StringBuilder();\n\t\tfor (String attr : block.getAttributesStringsList()) {\n\t\t\tattrs.append(escape(attr)).append(NL);\n\t\t}\n\t\treturn attrs.toString();\n\t}\n\n\tprivate String makeName(IContainer c) {\n\t\tString name;\n\t\tif (c instanceof BlockNode) {\n\t\t\tname = \"Node_\" + ((BlockNode) c).getCId();\n\t\t} else if (c instanceof IBlock) {\n\t\t\tname = \"Node_\" + c.getClass().getSimpleName() + '_' + c.hashCode();\n\t\t} else {\n\t\t\tname = \"cluster_\" + c.getClass().getSimpleName() + '_' + c.hashCode();\n\t\t}\n\t\treturn name;\n\t}\n\n\tprivate String insertInsns(MethodNode mth, IBlock block) {\n\t\tif (rawInsn) {\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tsb.append(escape(insn)).append(NL);\n\t\t\t}\n\t\t\treturn sb.toString();\n\t\t} else {\n\t\t\tICodeWriter code = new SimpleCodeWriter();\n\t\t\tList<InsnNode> instructions = block.getInstructions();\n\t\t\tMethodGen.addFallbackInsns(code, mth, instructions.toArray(new InsnNode[0]), BLOCK_DUMP);\n\t\t\t// For some reason, instructions here get put through an additional step of unescaping\n\t\t\tString str = escape(code.newLine().toString());\n\t\t\tif (str.startsWith(NL)) {\n\t\t\t\tstr = str.substring(NL.length());\n\t\t\t}\n\t\t\treturn str;\n\t\t}\n\t}\n\n\tprivate String escape(Object obj) {\n\t\tif (obj == null) {\n\t\t\treturn \"null\";\n\t\t}\n\t\treturn escape(obj.toString());\n\t}\n\n\tprivate String escape(String string) {\n\t\treturn escape(string, NLQR);\n\t}\n\n\tprivate String escape(String string, String newline) {\n\t\treturn string\n\t\t\t\t.replace(\"\\\\\", \"\") // TODO replace \\\"\n\t\t\t\t.replace(\"/\", \"\\\\/\")\n\t\t\t\t.replace(\">\", \"\\\\>\").replace(\"<\", \"\\\\<\")\n\t\t\t\t.replace(\"{\", \"\\\\{\").replace(\"}\", \"\\\\}\")\n\t\t\t\t.replace(\"\\\"\", \"\\\\\\\"\")\n\t\t\t\t.replace(\"-\", \"\\\\-\")\n\t\t\t\t.replace(\"|\", \"\\\\|\")\n\t\t\t\t.replaceAll(\"\\\\R\", newline);\n\t}\n\n\t// Consistently format names for graphs\n\n\tpublic static String classFormatName(ClassNode cls, boolean longName) {\n\t\treturn classFormatName(cls.getClassInfo(), longName);\n\t}\n\n\tpublic static String classFormatName(ClassInfo cls, boolean longName) {\n\t\treturn longName ? cls.getAliasFullName() : cls.getAliasShortName();\n\t}\n\n\tpublic static String methodFormatName(JavaMethod javaMethod, boolean longName) {\n\t\treturn methodFormatName(javaMethod.getMethodNode(), longName);\n\t}\n\n\tpublic static String methodFormatName(MethodNode methodNode, boolean longName) {\n\t\tif (longName) {\n\t\t\tClassNode parentClass = methodNode.getParentClass();\n\t\t\tList<ArgType> argTypes = methodNode.getArgTypes();\n\t\t\tArgType retType = methodNode.getReturnType();\n\t\t\treturn classFormatName(parentClass, true) + \".\" + methodFormatName(methodNode, false)\n\t\t\t\t\t+ '(' + Utils.listToString(argTypes, \", \", e -> argTypeFormatName(e, parentClass, true)) + \"):\"\n\t\t\t\t\t+ argTypeFormatName(retType, parentClass, true);\n\t\t}\n\t\treturn methodNode.getAlias();\n\t}\n\n\tpublic static String unresolvedMethodFormatName(IMethodRef methodRef, boolean longName) {\n\t\tString name = methodRef.getName();\n\t\tif (longName) {\n\t\t\tString className = methodRef.getParentClassType();\n\t\t\tclassName = Utils.cleanObjectName(className);\n\n\t\t\tString returnName = methodRef.getReturnType();\n\t\t\treturnName = Utils.smaliNameToJavaName(returnName);\n\n\t\t\tList<String> argTypes = methodRef.getArgTypes();\n\t\t\targTypes = argTypes.stream().map(c -> Utils.smaliNameToJavaName(c)).collect(Collectors.toList());\n\n\t\t\treturn String.format(\"%s.%s(%s):%s\", className, name, Utils.listToString(argTypes), returnName);\n\t\t}\n\t\treturn name;\n\t}\n\n\tpublic static String interfaceFormatName(ArgType iface, ClassNode cls, boolean longName) {\n\t\tClassInfo ifaceInfo = ClassInfo.fromType(cls.root(), iface);\n\t\treturn longName ? ifaceInfo.getAliasFullName() : ifaceInfo.getAliasShortName();\n\t}\n\n\tpublic static String argTypeFormatName(ArgType arg, ClassNode cls, boolean longName) {\n\t\tif (arg.isObject() && !arg.isGenericType()) {\n\t\t\tClassNode superCls = cls.root().resolveClass(arg);\n\t\t\tif (superCls != null) {\n\t\t\t\treturn DotGraphUtils.classFormatName(superCls, longName);\n\t\t\t}\n\t\t}\n\t\treturn arg.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/EmptyBitSet.java",
    "content": "package jadx.core.utils;\n\nimport java.util.BitSet;\n\npublic final class EmptyBitSet extends BitSet {\n\n\tprivate static final long serialVersionUID = -1194884945157778639L;\n\n\tpublic static final BitSet EMPTY = new EmptyBitSet();\n\n\tpublic EmptyBitSet() {\n\t\tsuper(0);\n\t}\n\n\t@Override\n\tpublic int cardinality() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean isEmpty() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int nextSetBit(int fromIndex) {\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic int length() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int size() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void set(int bitIndex) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic void set(int bitIndex, boolean value) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic void set(int fromIndex, int toIndex) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic void set(int fromIndex, int toIndex, boolean value) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic boolean get(int bitIndex) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic BitSet get(int fromIndex, int toIndex) {\n\t\treturn EMPTY;\n\t}\n\n\t@Override\n\tpublic void and(BitSet set) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic void or(BitSet set) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic void xor(BitSet set) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic void andNot(BitSet set) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/EncodedValueUtils.java",
    "content": "package jadx.core.utils;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.IMethodHandle;\nimport jadx.api.plugins.input.data.IMethodProto;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.input.data.MethodHandleType;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.ConstClassNode;\nimport jadx.core.dex.instructions.ConstStringNode;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.InvokeType;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.instructions.args.PrimitiveType;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class EncodedValueUtils {\n\n\t/**\n\t * Return constant literal from {@code jadx.api.plugins.input.data.annotations.EncodedValue}\n\t *\n\t * @return LiteralArg, String, ArgType or null\n\t */\n\t@Nullable\n\tpublic static Object convertToConstValue(EncodedValue encodedValue) {\n\t\tif (encodedValue == null) {\n\t\t\treturn null;\n\t\t}\n\t\tObject value = encodedValue.getValue();\n\t\tswitch (encodedValue.getType()) {\n\t\t\tcase ENCODED_NULL:\n\t\t\t\treturn InsnArg.lit(0, ArgType.OBJECT);\n\t\t\tcase ENCODED_BOOLEAN:\n\t\t\t\treturn Boolean.TRUE.equals(value) ? LiteralArg.litTrue() : LiteralArg.litFalse();\n\t\t\tcase ENCODED_BYTE:\n\t\t\t\treturn InsnArg.lit((Byte) value, ArgType.BYTE);\n\t\t\tcase ENCODED_SHORT:\n\t\t\t\treturn InsnArg.lit((Short) value, ArgType.SHORT);\n\t\t\tcase ENCODED_CHAR:\n\t\t\t\treturn InsnArg.lit((Character) value, ArgType.CHAR);\n\t\t\tcase ENCODED_INT:\n\t\t\t\treturn InsnArg.lit((Integer) value, ArgType.INT);\n\t\t\tcase ENCODED_LONG:\n\t\t\t\treturn InsnArg.lit((Long) value, ArgType.LONG);\n\t\t\tcase ENCODED_FLOAT:\n\t\t\t\treturn InsnArg.lit(Float.floatToIntBits((Float) value), ArgType.FLOAT);\n\t\t\tcase ENCODED_DOUBLE:\n\t\t\t\treturn InsnArg.lit(Double.doubleToLongBits((Double) value), ArgType.DOUBLE);\n\t\t\tcase ENCODED_STRING:\n\t\t\t\t// noinspection RedundantCast\n\t\t\t\treturn (String) value;\n\n\t\t\tcase ENCODED_TYPE:\n\t\t\t\treturn ArgType.parse((String) value);\n\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static InsnArg convertToInsnArg(RootNode root, EncodedValue value) {\n\t\tObject obj = value.getValue();\n\t\tswitch (value.getType()) {\n\t\t\tcase ENCODED_NULL:\n\t\t\tcase ENCODED_BYTE:\n\t\t\tcase ENCODED_SHORT:\n\t\t\tcase ENCODED_CHAR:\n\t\t\tcase ENCODED_INT:\n\t\t\tcase ENCODED_LONG:\n\t\t\tcase ENCODED_FLOAT:\n\t\t\tcase ENCODED_DOUBLE:\n\t\t\t\treturn (InsnArg) convertToConstValue(value);\n\n\t\t\tcase ENCODED_BOOLEAN:\n\t\t\t\treturn InsnArg.lit(((Boolean) obj) ? 0 : 1, ArgType.BOOLEAN);\n\t\t\tcase ENCODED_STRING:\n\t\t\t\treturn InsnArg.wrapArg(new ConstStringNode((String) obj));\n\t\t\tcase ENCODED_TYPE:\n\t\t\t\treturn InsnArg.wrapArg(new ConstClassNode(ArgType.parse((String) obj)));\n\t\t\tcase ENCODED_METHOD_TYPE:\n\t\t\t\treturn InsnArg.wrapArg(buildMethodType(root, (IMethodProto) obj));\n\t\t\tcase ENCODED_METHOD_HANDLE:\n\t\t\t\treturn InsnArg.wrapArg(buildMethodHandle(root, (IMethodHandle) obj));\n\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Unsupported type for raw invoke-custom: \" + value.getType());\n\t}\n\n\tprivate static InvokeNode buildMethodType(RootNode root, IMethodProto methodProto) {\n\t\tArgType retType = ArgType.parse(methodProto.getReturnType());\n\t\tList<ArgType> argTypes = Utils.collectionMap(methodProto.getArgTypes(), ArgType::parse);\n\t\tList<ArgType> callTypes = new ArrayList<>(1 + argTypes.size());\n\t\tcallTypes.add(retType);\n\t\tcallTypes.addAll(argTypes);\n\t\tArgType mthType = ArgType.object(\"java.lang.invoke.MethodType\");\n\t\tClassInfo cls = ClassInfo.fromType(root, mthType);\n\t\tMethodInfo mth = MethodInfo.fromDetails(root, cls, \"methodType\", callTypes, mthType);\n\t\tInvokeNode invoke = new InvokeNode(mth, InvokeType.STATIC, callTypes.size());\n\t\tfor (ArgType type : callTypes) {\n\t\t\tInsnNode argInsn;\n\t\t\tif (type.isPrimitive()) {\n\t\t\t\targInsn = new IndexInsnNode(InsnType.SGET, getTypeField(root, type.getPrimitiveType()), 0);\n\t\t\t} else {\n\t\t\t\targInsn = new ConstClassNode(type);\n\t\t\t}\n\t\t\tinvoke.addArg(InsnArg.wrapArg(argInsn));\n\t\t}\n\t\treturn invoke;\n\t}\n\n\tpublic static FieldInfo getTypeField(RootNode root, PrimitiveType type) {\n\t\tArgType boxType = type.getBoxType();\n\t\tClassInfo boxCls = ClassInfo.fromType(root, boxType);\n\t\treturn FieldInfo.from(root, boxCls, \"TYPE\", boxType);\n\t}\n\n\t/**\n\t * Build `MethodHandles.lookup().find{type}(methodCls, methodName, methodType)`\n\t */\n\tprivate static InsnNode buildMethodHandle(RootNode root, IMethodHandle methodHandle) {\n\t\tif (methodHandle.getType().isField()) {\n\t\t\t// TODO: lookup for field\n\t\t\treturn new ConstStringNode(\"FIELD:\" + methodHandle.getFieldRef());\n\t\t}\n\t\tIMethodRef methodRef = methodHandle.getMethodRef();\n\t\tmethodRef.load();\n\n\t\tClassInfo lookupCls = ClassInfo.fromName(root, \"java.lang.invoke.MethodHandles.Lookup\");\n\t\tMethodInfo findMethod = MethodInfo.fromDetails(root, lookupCls,\n\t\t\t\tgetFindMethodName(methodHandle.getType()),\n\t\t\t\tArrays.asList(ArgType.CLASS, ArgType.STRING, ArgType.object(\"java.lang.invoke.MethodType\")),\n\t\t\t\tArgType.object(\"java.lang.invoke.MethodHandle\"));\n\n\t\tInvokeNode invoke = new InvokeNode(findMethod, InvokeType.DIRECT, 4);\n\t\tinvoke.addArg(buildLookupArg(root));\n\t\tinvoke.addArg(InsnArg.wrapArg(new ConstClassNode(ArgType.object(methodRef.getParentClassType()))));\n\t\tinvoke.addArg(InsnArg.wrapArg(new ConstStringNode(methodRef.getName())));\n\t\tinvoke.addArg(InsnArg.wrapArg(buildMethodType(root, methodRef)));\n\t\treturn invoke;\n\t}\n\n\tpublic static InsnArg buildLookupArg(RootNode root) {\n\t\tArgType lookupType = ArgType.object(\"java.lang.invoke.MethodHandles.Lookup\");\n\t\tClassInfo cls = ClassInfo.fromName(root, \"java.lang.invoke.MethodHandles\");\n\t\tMethodInfo mth = MethodInfo.fromDetails(root, cls, \"lookup\", Collections.emptyList(), lookupType);\n\t\treturn InsnArg.wrapArg(new InvokeNode(mth, InvokeType.STATIC, 0));\n\t}\n\n\tprivate static String getFindMethodName(MethodHandleType type) {\n\t\tswitch (type) {\n\t\t\tcase INVOKE_STATIC:\n\t\t\t\treturn \"findStatic\";\n\t\t\tcase INVOKE_CONSTRUCTOR:\n\t\t\t\treturn \"findConstructor\";\n\t\t\tcase INVOKE_INSTANCE:\n\t\t\tcase INVOKE_DIRECT:\n\t\t\tcase INVOKE_INTERFACE:\n\t\t\t\treturn \"findVirtual\";\n\n\t\t\tdefault:\n\t\t\t\treturn \"<\" + type + '>';\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/ErrorsCounter.java",
    "content": "package jadx.core.utils;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.Consts;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.IAttributeNode;\nimport jadx.core.dex.attributes.nodes.JadxError;\nimport jadx.core.dex.nodes.IDexNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.exceptions.JadxOverflowException;\n\npublic class ErrorsCounter {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ErrorsCounter.class);\n\tprivate static final boolean PRINT_MTH_SIZE = Consts.DEBUG;\n\n\tprivate final Set<IAttributeNode> errorNodes = new HashSet<>();\n\tprivate int errorsCount;\n\tprivate final Set<IAttributeNode> warnNodes = new HashSet<>();\n\tprivate int warnsCount;\n\n\tpublic static <N extends IDexNode & IAttributeNode> String error(N node, String warnMsg, Throwable th) {\n\t\treturn node.root().getErrorsCounter().addError(node, warnMsg, th);\n\t}\n\n\tpublic static <N extends IDexNode & IAttributeNode> void warning(N node, String warnMsg) {\n\t\tnode.root().getErrorsCounter().addWarning(node, warnMsg);\n\t}\n\n\tpublic static String formatMsg(IDexNode node, String msg) {\n\t\treturn msg + \" in \" + node.typeName() + \": \" + node + \", file: \" + node.getInputFileName();\n\t}\n\n\tprivate synchronized <N extends IDexNode & IAttributeNode> String addError(N node, String error, @Nullable Throwable e) {\n\t\terrorNodes.add(node);\n\t\terrorsCount++;\n\n\t\tString msg = formatMsg(node, error);\n\t\tif (PRINT_MTH_SIZE && node instanceof MethodNode) {\n\t\t\tString mthSize = \"[\" + ((MethodNode) node).getInsnsCount() + \"] \";\n\t\t\tmsg = mthSize + msg;\n\t\t\terror = mthSize + error;\n\t\t}\n\t\tif (e == null) {\n\t\t\tLOG.error(msg);\n\t\t} else if (e instanceof StackOverflowError) {\n\t\t\tLOG.error(\"{}, error: StackOverflowError\", msg);\n\t\t} else if (e instanceof JadxOverflowException) {\n\t\t\t// don't print full stack trace\n\t\t\tString details = e.getMessage();\n\t\t\te = new JadxOverflowException(details);\n\t\t\tif (details == null || details.isEmpty()) {\n\t\t\t\tLOG.error(\"{}\", msg);\n\t\t\t} else {\n\t\t\t\tLOG.error(\"{}, details: {}\", msg, details);\n\t\t\t}\n\t\t} else {\n\t\t\tLOG.error(msg, e);\n\t\t}\n\t\tnode.addAttr(AType.JADX_ERROR, new JadxError(error, e));\n\t\treturn msg;\n\t}\n\n\tprivate synchronized <N extends IDexNode & IAttributeNode> void addWarning(N node, String warn) {\n\t\twarnNodes.add(node);\n\t\twarnsCount++;\n\t\tLOG.warn(formatMsg(node, warn));\n\t}\n\n\tpublic void printReport() {\n\t\tif (getErrorCount() > 0) {\n\t\t\tLOG.error(\"{} errors occurred in following nodes:\", getErrorCount());\n\t\t\tList<String> errors = new ArrayList<>(errorNodes.size());\n\t\t\tfor (IAttributeNode node : errorNodes) {\n\t\t\t\tString nodeName = node.getClass().getSimpleName().replace(\"Node\", \"\");\n\t\t\t\terrors.add(nodeName + \": \" + node);\n\t\t\t}\n\t\t\tCollections.sort(errors);\n\t\t\tfor (String err : errors) {\n\t\t\t\tLOG.error(\"  {}\", err);\n\t\t\t}\n\t\t}\n\t\tif (getWarnsCount() > 0) {\n\t\t\tLOG.warn(\"{} warnings in {} nodes\", getWarnsCount(), warnNodes.size());\n\t\t}\n\t}\n\n\tpublic int getErrorCount() {\n\t\treturn errorsCount;\n\t}\n\n\tpublic int getWarnsCount() {\n\t\treturn warnsCount;\n\t}\n\n\tpublic Set<IAttributeNode> getErrorNodes() {\n\t\treturn errorNodes;\n\t}\n\n\tpublic Set<IAttributeNode> getWarnNodes() {\n\t\treturn warnNodes;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/FileSignature.java",
    "content": "package jadx.core.utils;\n\npublic class FileSignature {\n\tprivate final byte[] signatureBytes;\n\tprivate final String fileType;\n\n\tpublic FileSignature(String fileType, String signatureHex) {\n\t\tthis.fileType = fileType;\n\t\tString[] parts = signatureHex.split(\" \");\n\t\tthis.signatureBytes = new byte[parts.length];\n\t\tfor (int i = 0; i < parts.length; i++) {\n\t\t\tif (parts[i].length() != 2) {\n\t\t\t\tthrow new RuntimeException(signatureHex);\n\t\t\t}\n\t\t\tif (!parts[i].equals(\"??\")) {\n\t\t\t\tthis.signatureBytes[i] = (byte) Integer.parseInt(parts[i], 16);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static boolean matches(FileSignature sig, byte[] data) {\n\t\tif (data.length < sig.signatureBytes.length) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (int i = 0; i < sig.signatureBytes.length; i++) {\n\t\t\tbyte b = sig.signatureBytes[i];\n\t\t\tif (b != data[i]) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic String getFileType() {\n\t\treturn fileType;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/GsonUtils.java",
    "content": "package jadx.core.utils;\n\nimport java.lang.reflect.Type;\n\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.JsonDeserializationContext;\nimport com.google.gson.JsonDeserializer;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonParseException;\nimport com.google.gson.JsonSerializationContext;\nimport com.google.gson.JsonSerializer;\nimport com.google.gson.Strictness;\n\npublic class GsonUtils {\n\n\tpublic static Gson buildGson() {\n\t\treturn defaultGsonBuilder().create();\n\t}\n\n\tpublic static GsonBuilder defaultGsonBuilder() {\n\t\treturn new GsonBuilder()\n\t\t\t\t.disableJdkUnsafe()\n\t\t\t\t.disableInnerClassSerialization()\n\t\t\t\t.setStrictness(Strictness.STRICT)\n\t\t\t\t.setPrettyPrinting();\n\t}\n\n\tpublic static <T> InterfaceReplace<T> interfaceReplace(Class<T> replaceCls) {\n\t\treturn new InterfaceReplace<>(replaceCls);\n\t}\n\n\tpublic static final class InterfaceReplace<T> implements JsonSerializer<T>, JsonDeserializer<T> {\n\t\tprivate final Class<T> replaceCls;\n\n\t\tprivate InterfaceReplace(Class<T> replaceCls) {\n\t\t\tthis.replaceCls = replaceCls;\n\t\t}\n\n\t\t@Override\n\t\tpublic T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {\n\t\t\treturn context.deserialize(json, this.replaceCls);\n\t\t}\n\n\t\t@Override\n\t\tpublic JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context) {\n\t\t\treturn context.serialize(src, this.replaceCls);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/ImmutableList.java",
    "content": "package jadx.core.utils;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.NoSuchElementException;\nimport java.util.Objects;\nimport java.util.RandomAccess;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\nimport java.util.function.UnaryOperator;\n\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * Simple immutable list implementation\n * Warning: some methods not implemented!\n */\npublic final class ImmutableList<E> implements List<E>, RandomAccess {\n\tprivate final E[] arr;\n\n\t@SuppressWarnings({ \"unchecked\", \"SuspiciousArrayCast\" })\n\tpublic ImmutableList(Collection<E> col) {\n\t\tthis((E[]) Objects.requireNonNull(col).toArray());\n\t}\n\n\tpublic ImmutableList(E[] arr) {\n\t\tthis.arr = Objects.requireNonNull(arr);\n\t}\n\n\t@Override\n\tpublic int size() {\n\t\treturn arr.length;\n\t}\n\n\t@Override\n\tpublic boolean isEmpty() {\n\t\treturn arr.length == 0;\n\t}\n\n\t@Override\n\tpublic E get(int index) {\n\t\treturn arr[index];\n\t}\n\n\t@Override\n\tpublic int indexOf(Object o) {\n\t\tint len = arr.length;\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tE e = arr[i];\n\t\t\tif (Objects.equals(e, o)) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic int lastIndexOf(Object o) {\n\t\tfor (int i = arr.length - 1; i > 0; i--) {\n\t\t\tE e = arr[i];\n\t\t\tif (Objects.equals(e, o)) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic boolean contains(Object o) {\n\t\treturn indexOf(o) != -1;\n\t}\n\n\t@Override\n\tpublic boolean containsAll(@NotNull Collection<?> c) {\n\t\tfor (Object obj : c) {\n\t\t\tif (!contains(obj)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t@NotNull\n\t@Override\n\tpublic Iterator<E> iterator() {\n\t\treturn new Iterator<E>() {\n\t\t\tprivate final int len = arr.length;\n\t\t\tprivate int index = 0;\n\n\t\t\t@Override\n\t\t\tpublic boolean hasNext() {\n\t\t\t\treturn index < len;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic E next() {\n\t\t\t\ttry {\n\t\t\t\t\treturn arr[index++];\n\t\t\t\t} catch (IndexOutOfBoundsException e) {\n\t\t\t\t\tthrow new NoSuchElementException(e.getMessage());\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n\n\t@Override\n\tpublic void forEach(Consumer<? super E> action) {\n\t\tfor (E e : arr) {\n\t\t\taction.accept(e);\n\t\t}\n\t}\n\n\t@NotNull\n\t@Override\n\tpublic Object[] toArray() {\n\t\treturn Arrays.copyOf(arr, arr.length);\n\t}\n\n\t@NotNull\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T> T[] toArray(@NotNull T[] a) {\n\t\treturn (T[]) Arrays.copyOf(arr, arr.length);\n\t}\n\n\t@Override\n\tpublic boolean add(E e) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic boolean remove(Object o) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic boolean addAll(@NotNull Collection<? extends E> c) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic boolean addAll(int index, @NotNull Collection<? extends E> c) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic boolean removeAll(@NotNull Collection<?> c) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic boolean retainAll(@NotNull Collection<?> c) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic void replaceAll(UnaryOperator<E> operator) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic void sort(Comparator<? super E> c) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic boolean removeIf(Predicate<? super E> filter) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic void clear() {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic E set(int index, E element) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic void add(int index, E element) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic E remove(int index) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@NotNull\n\t@Override\n\tpublic ListIterator<E> listIterator() {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@NotNull\n\t@Override\n\tpublic ListIterator<E> listIterator(int index) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@NotNull\n\t@Override\n\tpublic List<E> subList(int fromIndex, int toIndex) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o instanceof ImmutableList) {\n\t\t\tImmutableList<?> other = (ImmutableList<?>) o;\n\t\t\treturn Arrays.equals(arr, other.arr);\n\t\t}\n\t\tif (o instanceof List) {\n\t\t\tList<?> other = (List<?>) o;\n\t\t\tint size = size();\n\t\t\tif (size != other.size()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\tE e1 = arr[i];\n\t\t\t\tObject e2 = other.get(i);\n\t\t\t\tif (!Objects.equals(e1, e2)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Arrays.hashCode(arr);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ImmutableList{\" + Arrays.toString(arr) + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/InsnList.java",
    "content": "package jadx.core.utils;\n\nimport java.util.Iterator;\nimport java.util.List;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic final class InsnList implements Iterable<InsnNode> {\n\n\tprivate final List<InsnNode> list;\n\n\tpublic InsnList(List<InsnNode> list) {\n\t\tthis.list = list;\n\t}\n\n\tpublic static void remove(List<InsnNode> list, InsnNode insn) {\n\t\tfor (Iterator<InsnNode> iterator = list.iterator(); iterator.hasNext();) {\n\t\t\tInsnNode next = iterator.next();\n\t\t\tif (next == insn) {\n\t\t\t\titerator.remove();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void remove(BlockNode block, InsnNode insn) {\n\t\tremove(block.getInstructions(), insn);\n\t}\n\n\tpublic static int getIndex(List<InsnNode> list, InsnNode insn) {\n\t\treturn getIndex(list, insn, 0);\n\t}\n\n\tpublic static int getIndex(List<InsnNode> list, InsnNode insn, int startOffset) {\n\t\tint size = list.size();\n\t\tfor (int i = startOffset; i < size; i++) {\n\t\t\tif (list.get(i) == insn) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\tpublic static boolean contains(List<InsnNode> list, InsnNode insn) {\n\t\treturn getIndex(list, insn, 0) != -1;\n\t}\n\n\tpublic static boolean contains(List<InsnNode> list, InsnNode insn, int startOffset) {\n\t\treturn getIndex(list, insn, startOffset) != -1;\n\t}\n\n\tpublic int getIndex(InsnNode insn) {\n\t\treturn getIndex(list, insn);\n\t}\n\n\tpublic boolean contains(InsnNode insn) {\n\t\treturn getIndex(insn) != -1;\n\t}\n\n\tpublic void remove(InsnNode insn) {\n\t\tremove(list, insn);\n\t}\n\n\tpublic Iterator<InsnNode> iterator() {\n\t\treturn list.iterator();\n\t}\n\n\tpublic InsnNode get(int index) {\n\t\treturn list.get(index);\n\t}\n\n\tpublic int size() {\n\t\treturn list.size();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/InsnRemover.java",
    "content": "package jadx.core.utils;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.Consts;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.PhiListAttr;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.PhiInsn;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.utils.InsnUtils.isInsnType;\nimport static jadx.core.utils.ListUtils.allMatch;\n\n/**\n * Helper class for correct instructions removing,\n * can be used while iterating over instructions list\n */\npublic class InsnRemover {\n\n\tprivate final MethodNode mth;\n\tprivate final List<InsnNode> toRemove;\n\t@Nullable\n\tprivate List<InsnNode> instrList;\n\n\tpublic InsnRemover(MethodNode mth) {\n\t\tthis(mth, null);\n\t}\n\n\tpublic InsnRemover(MethodNode mth, BlockNode block) {\n\t\tthis.mth = mth;\n\t\tthis.toRemove = new ArrayList<>();\n\t\tif (block != null) {\n\t\t\tthis.instrList = block.getInstructions();\n\t\t}\n\t}\n\n\tpublic void setBlock(BlockNode block) {\n\t\tthis.instrList = block.getInstructions();\n\t}\n\n\tpublic void addAndUnbind(InsnNode insn) {\n\t\ttoRemove.add(insn);\n\t\tunbindInsn(mth, insn);\n\t}\n\n\tpublic void addWithoutUnbind(InsnNode insn) {\n\t\ttoRemove.add(insn);\n\t}\n\n\tpublic void perform() {\n\t\tif (toRemove.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tif (instrList == null) {\n\t\t\tfor (InsnNode remInsn : toRemove) {\n\t\t\t\tremove(mth, remInsn);\n\t\t\t}\n\t\t} else {\n\t\t\tunbindInsns(mth, toRemove);\n\t\t\tremoveAll(instrList, toRemove);\n\t\t}\n\t\ttoRemove.clear();\n\t}\n\n\tpublic void performForBlock(BlockNode block) {\n\t\tif (toRemove.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tinstrList = Objects.requireNonNull(block.getInstructions());\n\t\tunbindInsns(mth, toRemove);\n\t\tremoveAll(instrList, toRemove);\n\t\ttoRemove.clear();\n\t}\n\n\tpublic static void unbindInsn(@Nullable MethodNode mth, InsnNode insn) {\n\t\tunbindAllArgs(mth, insn);\n\t\tunbindResult(mth, insn);\n\t\tinsn.add(AFlag.DONT_GENERATE);\n\t}\n\n\tpublic static void unbindInsns(@Nullable MethodNode mth, List<InsnNode> insns) {\n\t\t// remove all usage first so on result unbind we can remove unused ssa vars\n\t\tinsns.forEach(insn -> unbindAllArgs(mth, insn));\n\t\tinsns.forEach(insn -> {\n\t\t\tunbindResult(mth, insn);\n\t\t\tinsn.add(AFlag.DONT_GENERATE);\n\t\t});\n\t}\n\n\tpublic static void unbindAllArgs(@Nullable MethodNode mth, InsnNode insn) {\n\t\tfor (InsnArg arg : insn.getArguments()) {\n\t\t\tunbindArgUsage(mth, arg);\n\t\t}\n\t\tif (insn.getType() == InsnType.PHI) {\n\t\t\tfor (InsnArg arg : insn.getArguments()) {\n\t\t\t\tif (arg instanceof RegisterArg) {\n\t\t\t\t\t((RegisterArg) arg).getSVar().updateUsedInPhiList();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tinsn.add(AFlag.REMOVE);\n\t\tinsn.add(AFlag.DONT_GENERATE);\n\t}\n\n\tpublic static void unbindResult(@Nullable MethodNode mth, InsnNode insn) {\n\t\tRegisterArg r = insn.getResult();\n\t\tif (r == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (mth != null) {\n\t\t\tSSAVar ssaVar = r.getSVar();\n\t\t\tif (ssaVar != null && ssaVar.getAssignInsn() == insn /* can be already reassigned */) {\n\t\t\t\tremoveSsaVar(mth, ssaVar);\n\t\t\t}\n\t\t}\n\t\tinsn.setResult(null);\n\t}\n\n\tprivate static void removeSsaVar(MethodNode mth, SSAVar ssaVar) {\n\t\tint useCount = ssaVar.getUseCount();\n\t\tif (useCount == 0) {\n\t\t\tmth.removeSVar(ssaVar);\n\t\t\treturn;\n\t\t}\n\t\t// check if all usage only in PHI insns\n\t\tif (allMatch(ssaVar.getUseList(), arg -> isInsnType(arg.getParentInsn(), InsnType.PHI))) {\n\t\t\tfor (RegisterArg arg : new ArrayList<>(ssaVar.getUseList())) {\n\t\t\t\tInsnNode parentInsn = arg.getParentInsn();\n\t\t\t\tif (parentInsn != null) {\n\t\t\t\t\t((PhiInsn) parentInsn).removeArg(arg);\n\t\t\t\t}\n\t\t\t}\n\t\t\tmth.removeSVar(ssaVar);\n\t\t\treturn;\n\t\t}\n\t\t// check if all usage only in not generated instructions\n\t\tif (allMatch(ssaVar.getUseList(),\n\t\t\t\targ -> arg.contains(AFlag.DONT_GENERATE) || (InsnUtils.contains(arg.getParentInsn(), AFlag.DONT_GENERATE)))) {\n\t\t\tfor (RegisterArg arg : ssaVar.getUseList()) {\n\t\t\t\targ.resetSSAVar();\n\t\t\t}\n\t\t\tmth.removeSVar(ssaVar);\n\t\t\treturn;\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Can't remove SSA var: \" + ssaVar + \", still in use, count: \" + useCount\n\t\t\t\t+ \", list:\\n  \" + ssaVar.getUseList().stream()\n\t\t\t\t\t\t.map(arg -> arg + \" from \" + arg.getParentInsn())\n\t\t\t\t\t\t.collect(Collectors.joining(\"\\n  \")));\n\t}\n\n\tpublic static void unbindArgUsage(@Nullable MethodNode mth, InsnArg arg) {\n\t\tif (arg instanceof RegisterArg) {\n\t\t\tRegisterArg reg = (RegisterArg) arg;\n\t\t\tSSAVar sVar = reg.getSVar();\n\t\t\tif (sVar != null) {\n\t\t\t\tsVar.removeUse(reg);\n\t\t\t}\n\t\t} else if (arg instanceof InsnWrapArg) {\n\t\t\tInsnWrapArg wrap = (InsnWrapArg) arg;\n\t\t\tunbindInsn(mth, wrap.getWrapInsn());\n\t\t}\n\t}\n\n\t// Don't use 'instrList.removeAll(toRemove)' because it will remove instructions by content\n\t// and here can be several instructions with same content\n\tprivate static void removeAll(List<InsnNode> insns, List<InsnNode> toRemove) {\n\t\tif (toRemove == null || toRemove.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (InsnNode rem : toRemove) {\n\t\t\tint insnsCount = insns.size();\n\t\t\tboolean found = false;\n\t\t\tfor (int i = 0; i < insnsCount; i++) {\n\t\t\t\tif (insns.get(i) == rem) {\n\t\t\t\t\tinsns.remove(i);\n\t\t\t\t\tfound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!found && Consts.DEBUG_WITH_ERRORS) {\n\t\t\t\tthrow new JadxRuntimeException(\"Can't remove insn:\"\n\t\t\t\t\t\t+ \"\\n  \" + rem\n\t\t\t\t\t\t+ \"\\n not found in list:\"\n\t\t\t\t\t\t+ \"\\n  \" + Utils.listToString(insns, \"\\n  \"));\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void remove(MethodNode mth, @Nullable InsnNode insn) {\n\t\tif (insn == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (insn.contains(AFlag.WRAPPED)) {\n\t\t\tunbindInsn(mth, insn);\n\t\t\treturn;\n\t\t}\n\t\tBlockNode block = BlockUtils.getBlockByInsn(mth, insn);\n\t\tif (block != null) {\n\t\t\tremove(mth, block, insn);\n\t\t} else {\n\t\t\tinsn.add(AFlag.DONT_GENERATE);\n\t\t\tmth.addWarnComment(\"Not found block with instruction: \" + insn);\n\t\t}\n\t}\n\n\tpublic static void remove(MethodNode mth, BlockNode block, InsnNode insn) {\n\t\tunbindInsn(mth, insn);\n\t\tremoveWithoutUnbind(mth, block, insn);\n\t}\n\n\tpublic static boolean removeWithoutUnbind(MethodNode mth, BlockNode block, InsnNode insn) {\n\t\t// remove by pointer (don't use equals)\n\t\tIterator<InsnNode> it = block.getInstructions().iterator();\n\t\twhile (it.hasNext()) {\n\t\t\tInsnNode ir = it.next();\n\t\t\tif (ir == insn) {\n\t\t\t\tit.remove();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tif (!insn.contains(AFlag.WRAPPED)) {\n\t\t\tmth.addWarnComment(\"Failed to remove instruction: \" + insn + \" from block: \" + block);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static void removeAllAndUnbind(MethodNode mth, BlockNode block, List<InsnNode> insns) {\n\t\tunbindInsns(mth, insns);\n\t\tremoveAll(block.getInstructions(), insns);\n\t}\n\n\tpublic static void removeAllAndUnbind(MethodNode mth, IContainer container, List<InsnNode> insns) {\n\t\tunbindInsns(mth, insns);\n\t\tRegionUtils.visitBlocks(mth, container, b -> removeAll(b.getInstructions(), insns));\n\t}\n\n\tpublic static void removeAllAndUnbind(MethodNode mth, List<InsnNode> insns) {\n\t\tunbindInsns(mth, insns);\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tremoveAll(block.getInstructions(), insns);\n\t\t}\n\t}\n\n\tpublic static void removeAllWithoutUnbind(BlockNode block, List<InsnNode> insns) {\n\t\tremoveAll(block.getInstructions(), insns);\n\t}\n\n\tpublic static void removeAllMarked(MethodNode mth) {\n\t\tInsnRemover insnRemover = new InsnRemover(mth);\n\t\tfor (BlockNode blockNode : mth.getBasicBlocks()) {\n\t\t\tfor (InsnNode insn : blockNode.getInstructions()) {\n\t\t\t\tif (insn.contains(AFlag.REMOVE)) {\n\t\t\t\t\tinsnRemover.addWithoutUnbind(insn);\n\t\t\t\t}\n\t\t\t}\n\t\t\tinsnRemover.setBlock(blockNode);\n\t\t\tinsnRemover.perform();\n\t\t}\n\t}\n\n\tpublic static void remove(MethodNode mth, BlockNode block, int index) {\n\t\tList<InsnNode> instructions = block.getInstructions();\n\t\tunbindInsn(mth, instructions.get(index));\n\t\tinstructions.remove(index);\n\t}\n\n\tpublic static void delistPhi(MethodNode mth, PhiInsn phiInsn) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tPhiListAttr phiListAttr = block.get(AType.PHI_LIST);\n\t\t\tif (phiListAttr != null) {\n\t\t\t\tphiListAttr.getList().removeIf(i -> i == phiInsn);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/InsnUtils.java",
    "content": "package jadx.core.utils;\n\nimport java.util.List;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.instructions.ConstClassNode;\nimport jadx.core.dex.instructions.ConstStringNode;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\n\npublic class InsnUtils {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(InsnUtils.class);\n\n\tprivate InsnUtils() {\n\t}\n\n\tpublic static String formatOffset(int offset) {\n\t\tif (offset < 0) {\n\t\t\treturn \"?\";\n\t\t}\n\t\treturn String.format(\"0x%04x\", offset);\n\t}\n\n\tpublic static String insnTypeToString(InsnType type) {\n\t\treturn type + \"  \";\n\t}\n\n\tpublic static String indexToString(Object index) {\n\t\tif (index == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\tif (index instanceof String) {\n\t\t\treturn \"\\\"\" + index + '\"';\n\t\t}\n\t\treturn index.toString();\n\t}\n\n\t/**\n\t * Search constant assigned to provided arg.\n\t *\n\t * @return LiteralArg, String, ArgType or null\n\t */\n\tpublic static Object getConstValueByArg(RootNode root, InsnArg arg) {\n\t\tif (arg.isLiteral()) {\n\t\t\treturn arg;\n\t\t}\n\t\tif (arg.isRegister()) {\n\t\t\tRegisterArg reg = (RegisterArg) arg;\n\t\t\tInsnNode parInsn = reg.getAssignInsn();\n\t\t\tif (parInsn == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (parInsn.getType() == InsnType.MOVE) {\n\t\t\t\treturn getConstValueByArg(root, parInsn.getArg(0));\n\t\t\t}\n\t\t\treturn getConstValueByInsn(root, parInsn);\n\t\t}\n\t\tif (arg.isInsnWrap()) {\n\t\t\tInsnNode insn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\treturn getConstValueByInsn(root, insn);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Return constant value from insn or null if not constant.\n\t *\n\t * @return LiteralArg, String, ArgType or null\n\t */\n\t@Nullable\n\tpublic static Object getConstValueByInsn(RootNode root, InsnNode insn) {\n\t\tswitch (insn.getType()) {\n\t\t\tcase CONST:\n\t\t\t\treturn insn.getArg(0);\n\t\t\tcase CONST_STR:\n\t\t\t\treturn ((ConstStringNode) insn).getString();\n\t\t\tcase CONST_CLASS:\n\t\t\t\treturn ((ConstClassNode) insn).getClsType();\n\t\t\tcase SGET:\n\t\t\t\tFieldInfo f = (FieldInfo) ((IndexInsnNode) insn).getIndex();\n\t\t\t\tFieldNode fieldNode = root.resolveField(f);\n\t\t\t\tif (fieldNode == null) {\n\t\t\t\t\tLOG.warn(\"Field {} not found\", f);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tEncodedValue constVal = fieldNode.get(JadxAttrType.CONSTANT_VALUE);\n\t\t\t\tif (constVal != null) {\n\t\t\t\t\treturn EncodedValueUtils.convertToConstValue(constVal);\n\t\t\t\t}\n\t\t\t\treturn null;\n\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic static InsnNode searchSingleReturnInsn(MethodNode mth, Predicate<InsnNode> test) {\n\t\tif (!mth.isNoCode() && mth.getPreExitBlocks().size() == 1) {\n\t\t\treturn searchInsn(mth, InsnType.RETURN, test);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Search instruction of specific type and condition in method.\n\t * This method support inlined instructions.\n\t */\n\t@Nullable\n\tpublic static InsnNode searchInsn(MethodNode mth, InsnType insnType, Predicate<InsnNode> test) {\n\t\tif (mth.isNoCode()) {\n\t\t\treturn null;\n\t\t}\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tfor (InsnNode insn : block.getInstructions()) {\n\t\t\t\tInsnNode foundInsn = recursiveInsnCheck(insn, insnType, test);\n\t\t\t\tif (foundInsn != null) {\n\t\t\t\t\treturn foundInsn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static void replaceInsns(MethodNode mth, Function<InsnNode, InsnNode> replaceFunction) {\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {\n\t\t\tList<InsnNode> insns = block.getInstructions();\n\t\t\tint insnsCount = insns.size();\n\t\t\tfor (int i = 0; i < insnsCount; i++) {\n\t\t\t\tInsnNode insn = insns.get(i);\n\t\t\t\treplaceInsnsInInsn(mth, insn, replaceFunction);\n\t\t\t\tInsnNode replace = replaceFunction.apply(insn);\n\t\t\t\tif (replace != null) {\n\t\t\t\t\tBlockUtils.replaceInsn(mth, block, i, replace);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void replaceInsnsInInsn(MethodNode mth, InsnNode insn, Function<InsnNode, InsnNode> replaceFunction) {\n\t\tint argsCount = insn.getArgsCount();\n\t\tfor (int i = 0; i < argsCount; i++) {\n\t\t\tInsnArg arg = insn.getArg(i);\n\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\treplaceInsnsInInsn(mth, wrapInsn, replaceFunction);\n\t\t\t\tInsnNode replace = replaceFunction.apply(wrapInsn);\n\t\t\t\tif (replace != null) {\n\t\t\t\t\tInsnRemover.unbindArgUsage(mth, arg);\n\t\t\t\t\tinsn.setArg(i, InsnArg.wrapInsnIntoArg(replace));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic static RegisterArg getRegFromInsn(List<RegisterArg> regs, InsnType insnType) {\n\t\tfor (RegisterArg reg : regs) {\n\t\t\tInsnNode parentInsn = reg.getParentInsn();\n\t\t\tif (parentInsn != null && parentInsn.getType() == insnType) {\n\t\t\t\treturn reg;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static InsnNode recursiveInsnCheck(InsnNode insn, InsnType insnType, Predicate<InsnNode> test) {\n\t\tif (insn.getType() == insnType && test.test(insn)) {\n\t\t\treturn insn;\n\t\t}\n\t\tfor (InsnArg arg : insn.getArguments()) {\n\t\t\tif (arg.isInsnWrap()) {\n\t\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\t\tInsnNode foundInsn = recursiveInsnCheck(wrapInsn, insnType, test);\n\t\t\t\tif (foundInsn != null) {\n\t\t\t\t\treturn foundInsn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Nullable\n\tpublic static InsnArg getSingleArg(InsnNode insn) {\n\t\tif (insn != null && insn.getArgsCount() == 1) {\n\t\t\treturn insn.getArg(0);\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Nullable\n\tpublic static InsnNode checkInsnType(@Nullable InsnNode insn, InsnType insnType) {\n\t\tif (insn != null && insn.getType() == insnType) {\n\t\t\treturn insn;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static boolean isInsnType(@Nullable InsnNode insn, InsnType insnType) {\n\t\treturn insn != null && insn.getType() == insnType;\n\t}\n\n\t@Nullable\n\tpublic static InsnNode getWrappedInsn(InsnArg arg) {\n\t\tif (arg != null && arg.isInsnWrap()) {\n\t\t\treturn ((InsnWrapArg) arg).getWrapInsn();\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static boolean isWrapped(InsnArg arg, InsnType insnType) {\n\t\tif (arg != null && arg.isInsnWrap()) {\n\t\t\tInsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();\n\t\t\treturn wrapInsn.getType() == insnType;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static boolean dontGenerateIfNotUsed(InsnNode insn) {\n\t\tRegisterArg resArg = insn.getResult();\n\t\tif (resArg != null) {\n\t\t\tSSAVar ssaVar = resArg.getSVar();\n\t\t\tfor (RegisterArg arg : ssaVar.getUseList()) {\n\t\t\t\tInsnNode parentInsn = arg.getParentInsn();\n\t\t\t\tif (parentInsn != null\n\t\t\t\t\t\t&& !parentInsn.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tinsn.add(AFlag.DONT_GENERATE);\n\t\treturn true;\n\t}\n\n\tpublic static <T extends InsnArg> boolean containsVar(List<T> list, RegisterArg arg) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (InsnArg insnArg : list) {\n\t\t\tif (insnArg == arg || arg.sameRegAndSVar(insnArg)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static boolean containsVar(InsnNode insn, RegisterArg arg) {\n\t\tif (insn == null) {\n\t\t\treturn false;\n\t\t}\n\t\tRegisterArg result = insn.getResult();\n\t\tif (result != null && result.sameRegAndSVar(arg)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (insn.getArgsCount() == 0) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (InsnArg insnArg : insn.getArguments()) {\n\t\t\tif (containsVar(insnArg, arg)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static boolean containsVar(InsnArg insnArg, RegisterArg arg) {\n\t\tif (insnArg.isRegister()) {\n\t\t\treturn ((RegisterArg) insnArg).sameRegAndSVar(arg);\n\t\t}\n\t\tif (insnArg.isInsnWrap()) {\n\t\t\tInsnNode wrapInsn = ((InsnWrapArg) insnArg).getWrapInsn();\n\t\t\treturn containsVar(wrapInsn, arg);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static boolean contains(InsnNode insn, AFlag flag) {\n\t\treturn insn != null && insn.contains(flag);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/ListUtils.java",
    "content": "package jadx.core.utils;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.Iterator;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.function.BiPredicate;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic class ListUtils {\n\n\tpublic static <T> boolean isSingleElement(@Nullable List<T> list, T obj) {\n\t\tif (list == null || list.size() != 1) {\n\t\t\treturn false;\n\t\t}\n\t\treturn Objects.equals(list.get(0), obj);\n\t}\n\n\tpublic static <T> boolean unorderedEquals(List<T> first, List<T> second) {\n\t\tif (first.size() != second.size()) {\n\t\t\treturn false;\n\t\t}\n\t\treturn first.containsAll(second);\n\t}\n\n\tpublic static <T, U> boolean orderedEquals(List<T> list1, List<U> list2, BiPredicate<T, U> comparer) {\n\t\tif (list1 == list2) {\n\t\t\treturn true;\n\t\t}\n\t\tif (list1.size() != list2.size()) {\n\t\t\treturn false;\n\t\t}\n\t\tfinal Iterator<T> iter1 = list1.iterator();\n\t\tfinal Iterator<U> iter2 = list2.iterator();\n\t\twhile (iter1.hasNext() && iter2.hasNext()) {\n\t\t\tfinal T item1 = iter1.next();\n\t\t\tfinal U item2 = iter2.next();\n\t\t\tif (!comparer.test(item1, item2)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn !iter1.hasNext() && !iter2.hasNext();\n\t}\n\n\tpublic static <T, R> List<R> map(Collection<T> list, Function<T, R> mapFunc) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<R> result = new ArrayList<>(list.size());\n\t\tfor (T t : list) {\n\t\t\tresult.add(mapFunc.apply(t));\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic static <T> T first(List<T> list) {\n\t\treturn list.get(0);\n\t}\n\n\tpublic static <T> @Nullable T last(List<T> list) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn list.get(list.size() - 1);\n\t}\n\n\tpublic static <T> @Nullable T removeLast(List<T> list) {\n\t\tint size = list.size();\n\t\tif (size == 0) {\n\t\t\treturn null;\n\t\t}\n\t\treturn list.remove(size - 1);\n\t}\n\n\tpublic static <T extends Comparable<T>> List<T> distinctMergeSortedLists(List<T> first, List<T> second) {\n\t\tif (first.isEmpty()) {\n\t\t\treturn second;\n\t\t}\n\t\tif (second.isEmpty()) {\n\t\t\treturn first;\n\t\t}\n\t\tSet<T> set = new TreeSet<>(first);\n\t\tset.addAll(second);\n\t\treturn new ArrayList<>(set);\n\t}\n\n\tpublic static <T> List<T> distinctList(List<T> list) {\n\t\treturn new ArrayList<>(new LinkedHashSet<>(list));\n\t}\n\n\tpublic static <T> List<T> concat(T first, T[] values) {\n\t\tList<T> list = new ArrayList<>(1 + values.length);\n\t\tlist.add(first);\n\t\tlist.addAll(Arrays.asList(values));\n\t\treturn list;\n\t}\n\n\t/**\n\t * Replace old element to new one.\n\t * Support null and empty immutable list (created by Collections.emptyList())\n\t */\n\tpublic static <T> List<T> safeReplace(List<T> list, T oldObj, T newObj) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\t// immutable empty list\n\t\t\tList<T> newList = new ArrayList<>(1);\n\t\t\tnewList.add(newObj);\n\t\t\treturn newList;\n\t\t}\n\t\tint idx = list.indexOf(oldObj);\n\t\tif (idx != -1) {\n\t\t\tlist.set(idx, newObj);\n\t\t} else {\n\t\t\tlist.add(newObj);\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic static <T> void safeRemove(List<T> list, T obj) {\n\t\tif (list != null && !list.isEmpty()) {\n\t\t\tlist.remove(obj);\n\t\t}\n\t}\n\n\tpublic static <T> List<T> safeRemoveAndTrim(List<T> list, T obj) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn list;\n\t\t}\n\t\tif (list.remove(obj)) {\n\t\t\tif (list.isEmpty()) {\n\t\t\t\treturn Collections.emptyList();\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic static <T> List<T> safeAdd(List<T> list, T obj) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\tList<T> newList = new ArrayList<>(1);\n\t\t\tnewList.add(obj);\n\t\t\treturn newList;\n\t\t}\n\t\tlist.add(obj);\n\t\treturn list;\n\t}\n\n\tpublic static <T> List<T> filter(Collection<T> list, Predicate<T> filter) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<T> result = new ArrayList<>();\n\t\tfor (T element : list) {\n\t\t\tif (filter.test(element)) {\n\t\t\t\tresult.add(element);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Search exactly one element in list by filter\n\t *\n\t * @return null if found not exactly one element (zero or more than one)\n\t */\n\t@Nullable\n\tpublic static <T> T filterOnlyOne(List<T> list, Predicate<T> filter) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tT found = null;\n\t\tfor (T element : list) {\n\t\t\tif (filter.test(element)) {\n\t\t\t\tif (found != null) {\n\t\t\t\t\t// found second\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tfound = element;\n\t\t\t}\n\t\t}\n\t\treturn found;\n\t}\n\n\tpublic static <T> boolean allMatch(Collection<T> list, Predicate<T> test) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (T element : list) {\n\t\t\tif (!test.test(element)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static <T> boolean noneMatch(Collection<T> list, Predicate<T> test) {\n\t\treturn !anyMatch(list, test);\n\t}\n\n\tpublic static <T> boolean anyMatch(Collection<T> list, Predicate<T> test) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (T element : list) {\n\t\t\tif (test.test(element)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static <T> List<T> enumerationToList(Enumeration<T> enumeration) {\n\t\tif (enumeration == null || enumeration == Collections.emptyEnumeration()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<T> list = new ArrayList<>();\n\t\twhile (enumeration.hasMoreElements()) {\n\t\t\tlist.add(enumeration.nextElement());\n\t\t}\n\t\treturn list;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/Pair.java",
    "content": "package jadx.core.utils;\n\npublic class Pair<T> {\n\n\tprivate final T first;\n\tprivate final T second;\n\n\tpublic Pair(T first, T second) {\n\t\tthis.first = first;\n\t\tthis.second = second;\n\t}\n\n\tpublic T getFirst() {\n\t\treturn first;\n\t}\n\n\tpublic T getSecond() {\n\t\treturn second;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof Pair)) {\n\t\t\treturn false;\n\t\t}\n\t\tPair<?> other = (Pair<?>) o;\n\t\treturn first.equals(other.first) && second.equals(other.second);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn first.hashCode() + 31 * second.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"(\" + first + \", \" + second + ')';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/PassMerge.java",
    "content": "package jadx.core.utils;\n\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.IdentityHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport jadx.api.plugins.pass.JadxPass;\nimport jadx.api.plugins.pass.JadxPassInfo;\nimport jadx.core.dex.visitors.IDexTreeVisitor;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class PassMerge {\n\n\tprivate final List<IDexTreeVisitor> visitors;\n\n\tprivate Set<String> mergePassesNames;\n\tprivate Map<IDexTreeVisitor, String> namesMap;\n\n\tpublic PassMerge(List<IDexTreeVisitor> visitors) {\n\t\tthis.visitors = visitors;\n\t}\n\n\tpublic void merge(List<JadxPass> customPasses, Function<JadxPass, IDexTreeVisitor> wrap) {\n\t\tif (Utils.isEmpty(customPasses)) {\n\t\t\treturn;\n\t\t}\n\t\tList<MergePass> mergePasses = ListUtils.map(customPasses, p -> new MergePass(p, wrap.apply(p), p.getInfo()));\n\t\tlinkDeps(mergePasses);\n\t\tmergePasses.sort(new ExtDepsComparator(visitors).thenComparing(InvertedDepsComparator.INSTANCE));\n\n\t\tnamesMap = new IdentityHashMap<>();\n\t\tvisitors.forEach(p -> namesMap.put(p, p.getName()));\n\t\tmergePasses.forEach(p -> namesMap.put(p.getVisitor(), p.getName()));\n\n\t\tmergePassesNames = mergePasses.stream().map(MergePass::getName).collect(Collectors.toSet());\n\n\t\tfor (MergePass mergePass : mergePasses) {\n\t\t\tint pos = searchInsertPos(mergePass);\n\t\t\tif (pos == -1) {\n\t\t\t\tvisitors.add(mergePass.getVisitor());\n\t\t\t} else {\n\t\t\t\tvisitors.add(pos, mergePass.getVisitor());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate int searchInsertPos(MergePass pass) {\n\t\tList<String> runAfter = pass.after();\n\t\tList<String> runBefore = pass.before();\n\t\tif (runAfter.isEmpty() && runBefore.isEmpty()) {\n\t\t\treturn -1; // last\n\t\t}\n\t\tif (ListUtils.isSingleElement(runAfter, JadxPassInfo.START)) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (ListUtils.isSingleElement(runBefore, JadxPassInfo.END)) {\n\t\t\treturn -1;\n\t\t}\n\t\tint visitorsCount = visitors.size();\n\t\tMap<String, Integer> namePosMap = new HashMap<>(visitorsCount);\n\t\tfor (int i = 0; i < visitorsCount; i++) {\n\t\t\tnamePosMap.put(namesMap.get(visitors.get(i)), i);\n\t\t}\n\t\tint after = -1;\n\t\tfor (String name : runAfter) {\n\t\t\tInteger pos = namePosMap.get(name);\n\t\t\tif (pos != null) {\n\t\t\t\tafter = Math.max(after, pos);\n\t\t\t} else {\n\t\t\t\tif (mergePassesNames.contains(name)) {\n\t\t\t\t\t// ignore known passes\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthrow new JadxRuntimeException(\"Ordering pass not found: \" + name\n\t\t\t\t\t\t+ \", listed in 'runAfter' of pass: \" + pass\n\t\t\t\t\t\t+ \"\\n all passes: \" + ListUtils.map(visitors, namesMap::get));\n\t\t\t}\n\t\t}\n\t\tint before = Integer.MAX_VALUE;\n\t\tfor (String name : runBefore) {\n\t\t\tInteger pos = namePosMap.get(name);\n\t\t\tif (pos != null) {\n\t\t\t\tbefore = Math.min(before, pos);\n\t\t\t} else {\n\t\t\t\tif (mergePassesNames.contains(name)) {\n\t\t\t\t\t// ignore known passes\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthrow new JadxRuntimeException(\"Ordering pass not found: \" + name\n\t\t\t\t\t\t+ \", listed in 'runBefore' of pass: \" + pass\n\t\t\t\t\t\t+ \"\\n all passes: \" + ListUtils.map(visitors, namesMap::get));\n\t\t\t}\n\t\t}\n\t\tif (before <= after) {\n\t\t\tthrow new JadxRuntimeException(\"Conflict order requirements for pass: \" + pass\n\t\t\t\t\t+ \"\\n run after: \" + runAfter\n\t\t\t\t\t+ \"\\n run before: \" + runBefore\n\t\t\t\t\t+ \"\\n passes: \" + ListUtils.map(visitors, namesMap::get));\n\t\t}\n\t\tif (after == -1) {\n\t\t\tif (before == Integer.MAX_VALUE) {\n\t\t\t\t// not ordered, put at last\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\treturn before;\n\t\t}\n\t\tint pos = after + 1;\n\t\treturn pos >= visitorsCount ? -1 : pos;\n\t}\n\n\tprivate static final class MergePass {\n\t\tprivate final JadxPass pass;\n\t\tprivate final IDexTreeVisitor visitor;\n\t\tprivate final JadxPassInfo info;\n\t\t// copy dep lists for future modifications\n\t\tprivate final List<String> before;\n\t\tprivate final List<String> after;\n\n\t\tprivate MergePass(JadxPass pass, IDexTreeVisitor visitor, JadxPassInfo info) {\n\t\t\tthis.pass = pass;\n\t\t\tthis.visitor = visitor;\n\t\t\tthis.info = info;\n\t\t\tthis.before = new ArrayList<>(info.runBefore());\n\t\t\tthis.after = new ArrayList<>(info.runAfter());\n\t\t}\n\n\t\tpublic JadxPass getPass() {\n\t\t\treturn pass;\n\t\t}\n\n\t\tpublic IDexTreeVisitor getVisitor() {\n\t\t\treturn visitor;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn info.getName();\n\t\t}\n\n\t\tpublic JadxPassInfo getInfo() {\n\t\t\treturn info;\n\t\t}\n\n\t\tpublic List<String> before() {\n\t\t\treturn before;\n\t\t}\n\n\t\tpublic List<String> after() {\n\t\t\treturn after;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn info.getName();\n\t\t}\n\t}\n\n\t/**\n\t * Make deps double linked\n\t */\n\tprivate static void linkDeps(List<MergePass> mergePasses) {\n\t\tMap<String, MergePass> map = mergePasses.stream().collect(Collectors.toMap(MergePass::getName, p -> p));\n\t\tfor (MergePass pass : mergePasses) {\n\t\t\tfor (String after : pass.getInfo().runAfter()) {\n\t\t\t\tMergePass beforePass = map.get(after);\n\t\t\t\tif (beforePass != null) {\n\t\t\t\t\tbeforePass.before().add(pass.getName());\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (String before : pass.getInfo().runBefore()) {\n\t\t\t\tMergePass afterPass = map.get(before);\n\t\t\t\tif (afterPass != null) {\n\t\t\t\t\tafterPass.after().add(pass.getName());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Place passes with visitors dependencies before others.\n\t */\n\tprivate static class ExtDepsComparator implements Comparator<MergePass> {\n\t\tprivate final Set<String> names;\n\n\t\tpublic ExtDepsComparator(List<IDexTreeVisitor> visitors) {\n\t\t\tthis.names = visitors.stream()\n\t\t\t\t\t.map(IDexTreeVisitor::getName)\n\t\t\t\t\t.collect(Collectors.toSet());\n\t\t}\n\n\t\t@Override\n\t\tpublic int compare(MergePass first, MergePass second) {\n\t\t\tboolean isFirst = containsVisitor(first.before()) || containsVisitor(first.after());\n\t\t\tboolean isSecond = containsVisitor(second.before()) || containsVisitor(second.after());\n\t\t\treturn -Boolean.compare(isFirst, isSecond);\n\t\t}\n\n\t\tprivate boolean containsVisitor(List<String> deps) {\n\t\t\tfor (String dep : deps) {\n\t\t\t\tif (names.contains(dep)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Sort to get inverted dependencies i.e. if pass depends on another place it before.\n\t */\n\tprivate static class InvertedDepsComparator implements Comparator<MergePass> {\n\t\tpublic static final InvertedDepsComparator INSTANCE = new InvertedDepsComparator();\n\n\t\t@Override\n\t\tpublic int compare(MergePass first, MergePass second) {\n\t\t\tif (first.before().contains(second.getName())\n\t\t\t\t\t|| first.after().contains(second.getName())) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif (second.before().contains(first.getName())\n\t\t\t\t\t|| second.after().contains(first.getName())) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/RegionUtils.java",
    "content": "package jadx.core.utils;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.AttrList;\nimport jadx.core.dex.attributes.nodes.LoopInfo;\nimport jadx.core.dex.attributes.nodes.LoopLabelAttr;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.IBlock;\nimport jadx.core.dex.nodes.IBranchRegion;\nimport jadx.core.dex.nodes.IConditionRegion;\nimport jadx.core.dex.nodes.IContainer;\nimport jadx.core.dex.nodes.IRegion;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.regions.Region;\nimport jadx.core.dex.regions.loops.LoopRegion;\nimport jadx.core.dex.trycatch.CatchAttr;\nimport jadx.core.dex.trycatch.ExceptionHandler;\nimport jadx.core.dex.trycatch.TryCatchBlockAttr;\nimport jadx.core.dex.visitors.regions.AbstractRegionVisitor;\nimport jadx.core.dex.visitors.regions.DepthRegionTraversal;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class RegionUtils {\n\n\tprivate RegionUtils() {\n\t}\n\n\tpublic static boolean hasExitEdge(IContainer container) {\n\t\tif (container instanceof IBlock) {\n\t\t\treturn BlockUtils.containsExitInsn((IBlock) container);\n\t\t}\n\t\tif (container instanceof IBranchRegion) {\n\t\t\t// all branches must have exit edge\n\t\t\tfor (IContainer br : ((IBranchRegion) container).getBranches()) {\n\t\t\t\tif (br == null || !hasExitEdge(br)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tif (container instanceof IRegion) {\n\t\t\tIContainer last = Utils.last(((IRegion) container).getSubBlocks());\n\t\t\treturn last != null && hasExitEdge(last);\n\t\t}\n\t\tthrow new JadxRuntimeException(unknownContainerType(container));\n\t}\n\n\t@Nullable\n\tpublic static InsnNode getFirstInsn(IContainer container) {\n\t\tif (container instanceof IBlock) {\n\t\t\tIBlock block = (IBlock) container;\n\t\t\tList<InsnNode> insnList = block.getInstructions();\n\t\t\tif (insnList.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn insnList.get(0);\n\t\t} else if (container instanceof IBranchRegion) {\n\t\t\treturn null;\n\t\t} else if (container instanceof IRegion) {\n\t\t\tIRegion region = (IRegion) container;\n\t\t\tList<IContainer> blocks = region.getSubBlocks();\n\t\t\tif (blocks.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn getFirstInsn(blocks.get(0));\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(unknownContainerType(container));\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic static IBlock getFirstBlock(IContainer container) {\n\t\tif (container == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (container instanceof IBlock) {\n\t\t\treturn (IBlock) container;\n\t\t} else if (container instanceof IBranchRegion) {\n\t\t\treturn null;\n\t\t} else if (container instanceof IRegion) {\n\t\t\tIRegion region = (IRegion) container;\n\t\t\tList<IContainer> blocks = region.getSubBlocks();\n\t\t\tif (blocks.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn getFirstBlock(blocks.get(0));\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(unknownContainerType(container));\n\t\t}\n\t}\n\n\tpublic static @Nullable BlockNode getFirstBlockNode(IContainer container) {\n\t\tif (container instanceof IBlock) {\n\t\t\tif (container instanceof BlockNode) {\n\t\t\t\treturn (BlockNode) container;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tif (container instanceof IBranchRegion) {\n\t\t\treturn null;\n\t\t}\n\t\tif (container instanceof IRegion) {\n\t\t\tList<IContainer> blocks = ((IRegion) container).getSubBlocks();\n\t\t\tif (blocks.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn getFirstBlockNode(blocks.get(0));\n\t\t}\n\t\tthrow new JadxRuntimeException(unknownContainerType(container));\n\t}\n\n\tpublic static int getFirstSourceLine(IContainer container) {\n\t\tif (container instanceof IBlock) {\n\t\t\treturn BlockUtils.getFirstSourceLine((IBlock) container);\n\t\t}\n\t\tif (container instanceof IConditionRegion) {\n\t\t\treturn ((IConditionRegion) container).getConditionSourceLine();\n\t\t}\n\t\tif (container instanceof IBranchRegion) {\n\t\t\tIBranchRegion branchRegion = (IBranchRegion) container;\n\t\t\treturn getFirstSourceLine(branchRegion.getBranches());\n\t\t}\n\t\tif (container instanceof IRegion) {\n\t\t\tIRegion region = (IRegion) container;\n\t\t\treturn getFirstSourceLine(region.getSubBlocks());\n\t\t}\n\t\treturn 0;\n\t}\n\n\tprivate static int getFirstSourceLine(List<IContainer> containers) {\n\t\tif (containers.isEmpty()) {\n\t\t\treturn 0;\n\t\t}\n\t\tfor (IContainer container : containers) {\n\t\t\tint line = getFirstSourceLine(container);\n\t\t\tif (line != 0) {\n\t\t\t\treturn line;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\tpublic static InsnNode getLastInsn(IContainer container) {\n\t\tif (container instanceof IBlock) {\n\t\t\tIBlock block = (IBlock) container;\n\t\t\tList<InsnNode> insnList = block.getInstructions();\n\t\t\tif (insnList.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn insnList.get(insnList.size() - 1);\n\t\t} else if (container instanceof IBranchRegion) {\n\t\t\treturn null;\n\t\t} else if (container instanceof IRegion) {\n\t\t\tIRegion region = (IRegion) container;\n\t\t\tList<IContainer> blocks = region.getSubBlocks();\n\t\t\tif (blocks.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn getLastInsn(blocks.get(blocks.size() - 1));\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(unknownContainerType(container));\n\t\t}\n\t}\n\n\tpublic static BlockInsnPair getLastInsnWithBlock(IContainer container) {\n\t\tif (container instanceof IBlock) {\n\t\t\tIBlock block = (IBlock) container;\n\t\t\tInsnNode lastInsn = ListUtils.last(block.getInstructions());\n\t\t\tif (lastInsn == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn new BlockInsnPair(block, lastInsn);\n\t\t}\n\t\tif (container instanceof IBranchRegion) {\n\t\t\tList<IContainer> branches = ((IBranchRegion) container).getBranches();\n\t\t\tlong count = branches.stream().filter(Objects::nonNull).count();\n\t\t\tif (count == 1) {\n\t\t\t\t// single branch\n\t\t\t\tfor (IContainer branch : branches) {\n\t\t\t\t\tif (branch != null) {\n\t\t\t\t\t\treturn getLastInsnWithBlock(branch);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// several last instructions\n\t\t\treturn null;\n\t\t}\n\t\tif (container instanceof IRegion) {\n\t\t\tIRegion region = (IRegion) container;\n\t\t\tList<IContainer> blocks = region.getSubBlocks();\n\t\t\tif (blocks.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn getLastInsnWithBlock(ListUtils.last(blocks));\n\t\t}\n\t\tthrow new JadxRuntimeException(unknownContainerType(container));\n\t}\n\n\tpublic static IBlock getLastBlock(IContainer container) {\n\t\tif (container instanceof IBlock) {\n\t\t\treturn (IBlock) container;\n\t\t} else if (container instanceof IBranchRegion) {\n\t\t\treturn null;\n\t\t} else if (container instanceof IRegion) {\n\t\t\tList<IContainer> blocks = ((IRegion) container).getSubBlocks();\n\t\t\tif (blocks.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn getLastBlock(blocks.get(blocks.size() - 1));\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(unknownContainerType(container));\n\t\t}\n\t}\n\n\tpublic static boolean isExitBlock(MethodNode mth, IContainer container) {\n\t\tif (container instanceof BlockNode) {\n\t\t\treturn BlockUtils.isExitBlock(mth, (BlockNode) container);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Return true if last block in region has no successors or jump out insn (return or break)\n\t */\n\tpublic static boolean hasExitBlock(IContainer container) {\n\t\tif (container == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn hasExitBlock(container, container);\n\t}\n\n\tprivate static boolean hasExitBlock(IContainer rootContainer, IContainer container) {\n\t\tif (container instanceof BlockNode) {\n\t\t\tBlockNode blockNode = (BlockNode) container;\n\t\t\tif (BlockUtils.isExitBlock(blockNode)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn isInsnExitContainer(rootContainer, (IBlock) container);\n\t\t}\n\t\tif (container instanceof IBranchRegion) {\n\t\t\tIBranchRegion branchRegion = (IBranchRegion) container;\n\t\t\treturn ListUtils.allMatch(branchRegion.getBranches(), RegionUtils::hasExitBlock);\n\t\t}\n\t\tif (container instanceof IBlock) {\n\t\t\treturn isInsnExitContainer(rootContainer, (IBlock) container);\n\t\t}\n\t\tif (container instanceof IRegion) {\n\t\t\tList<IContainer> blocks = ((IRegion) container).getSubBlocks();\n\t\t\treturn !blocks.isEmpty()\n\t\t\t\t\t&& hasExitBlock(rootContainer, blocks.get(blocks.size() - 1));\n\t\t}\n\t\tthrow new JadxRuntimeException(unknownContainerType(container));\n\t}\n\n\tprivate static boolean isInsnExitContainer(IContainer rootContainer, IBlock block) {\n\t\tInsnNode lastInsn = BlockUtils.getLastInsn(block);\n\t\tif (lastInsn == null) {\n\t\t\treturn false;\n\t\t}\n\t\tInsnType insnType = lastInsn.getType();\n\t\tif (insnType == InsnType.RETURN) {\n\t\t\treturn true;\n\t\t}\n\t\tif (insnType == InsnType.THROW) {\n\t\t\t// check if after throw execution can continue in current container\n\t\t\tCatchAttr catchAttr = lastInsn.get(AType.EXC_CATCH);\n\t\t\tif (catchAttr != null) {\n\t\t\t\tfor (ExceptionHandler handler : catchAttr.getHandlers()) {\n\t\t\t\t\tif (RegionUtils.isRegionContainsBlock(rootContainer, handler.getHandlerBlock())) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tif (insnType == InsnType.BREAK) {\n\t\t\tAttrList<LoopInfo> loopInfoAttrList = lastInsn.get(AType.LOOP);\n\t\t\tif (loopInfoAttrList != null) {\n\t\t\t\tfor (LoopInfo loopInfo : loopInfoAttrList.getList()) {\n\t\t\t\t\tif (!RegionUtils.isRegionContainsBlock(rootContainer, loopInfo.getStart())) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tLoopLabelAttr loopLabelAttr = lastInsn.get(AType.LOOP_LABEL);\n\t\t\tif (loopLabelAttr != null\n\t\t\t\t\t&& !RegionUtils.isRegionContainsBlock(rootContainer, loopLabelAttr.getLoop().getStart())) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static boolean hasBreakInsn(IContainer container) {\n\t\tif (container instanceof IBlock) {\n\t\t\treturn BlockUtils.checkLastInsnType((IBlock) container, InsnType.BREAK);\n\t\t} else if (container instanceof IRegion) {\n\t\t\tList<IContainer> blocks = ((IRegion) container).getSubBlocks();\n\t\t\treturn !blocks.isEmpty()\n\t\t\t\t\t&& hasBreakInsn(blocks.get(blocks.size() - 1));\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(\"Unknown container type: \" + container);\n\t\t}\n\t}\n\n\tpublic static int insnsCount(IContainer container) {\n\t\tif (container instanceof IBlock) {\n\t\t\tList<InsnNode> insnList = ((IBlock) container).getInstructions();\n\t\t\tint count = 0;\n\t\t\tfor (InsnNode insn : insnList) {\n\t\t\t\tif (insn.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tcount++;\n\t\t\t}\n\t\t\treturn count;\n\t\t}\n\t\tif (container instanceof IRegion) {\n\t\t\tIRegion region = (IRegion) container;\n\t\t\tint count = 0;\n\t\t\tfor (IContainer block : region.getSubBlocks()) {\n\t\t\t\tcount += insnsCount(block);\n\t\t\t}\n\t\t\treturn count;\n\t\t}\n\t\tthrow new JadxRuntimeException(unknownContainerType(container));\n\t}\n\n\tpublic static List<InsnNode> collectInsns(MethodNode mth, IContainer container) {\n\t\tList<InsnNode> list = new ArrayList<>();\n\t\tvisitBlocks(mth, container, block -> list.addAll(block.getInstructions()));\n\t\treturn list;\n\t}\n\n\tpublic static boolean isEmpty(IContainer container) {\n\t\treturn !notEmpty(container);\n\t}\n\n\tpublic static boolean notEmpty(@Nullable IContainer container) {\n\t\tif (container == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (container instanceof IBlock) {\n\t\t\tList<InsnNode> insnList = ((IBlock) container).getInstructions();\n\t\t\tfor (InsnNode insnNode : insnList) {\n\t\t\t\tif (!insnNode.contains(AFlag.DONT_GENERATE)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tif (container instanceof LoopRegion) {\n\t\t\treturn true;\n\t\t}\n\t\tif (container instanceof IRegion) {\n\t\t\tIRegion region = (IRegion) container;\n\t\t\tfor (IContainer block : region.getSubBlocks()) {\n\t\t\t\tif (notEmpty(block)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tthrow new JadxRuntimeException(unknownContainerType(container));\n\t}\n\n\tpublic static void getAllRegionBlocks(IContainer container, Set<IBlock> blocks) {\n\t\tif (container instanceof IBlock) {\n\t\t\tblocks.add((IBlock) container);\n\t\t} else if (container instanceof IRegion) {\n\t\t\tIRegion region = (IRegion) container;\n\t\t\tfor (IContainer block : region.getSubBlocks()) {\n\t\t\t\tgetAllRegionBlocks(block, blocks);\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(unknownContainerType(container));\n\t\t}\n\t}\n\n\tpublic static boolean isRegionContainsBlock(IContainer container, BlockNode block) {\n\t\tif (container instanceof IBlock) {\n\t\t\treturn container == block;\n\t\t} else if (container instanceof IRegion) {\n\t\t\tIRegion region = (IRegion) container;\n\t\t\tfor (IContainer b : region.getSubBlocks()) {\n\t\t\t\tif (isRegionContainsBlock(b, block)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(unknownContainerType(container));\n\t\t}\n\t}\n\n\tpublic static IContainer getSingleSubBlock(IContainer container) {\n\t\tif (container instanceof Region) {\n\t\t\tList<IContainer> subBlocks = ((Region) container).getSubBlocks();\n\t\t\tif (subBlocks.size() == 1) {\n\t\t\t\treturn ignoreSimpleRegionWrapper(subBlocks.get(0));\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static IContainer ignoreSimpleRegionWrapper(IContainer container) {\n\t\twhile (true) {\n\t\t\tif (container instanceof Region) {\n\t\t\t\tList<IContainer> subBlocks = ((Region) container).getSubBlocks();\n\t\t\t\tif (subBlocks.size() != 1) {\n\t\t\t\t\treturn container;\n\t\t\t\t}\n\t\t\t\tcontainer = subBlocks.get(0);\n\t\t\t} else {\n\t\t\t\treturn container;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static List<IContainer> getExcHandlersForRegion(IContainer region) {\n\t\tTryCatchBlockAttr tb = region.get(AType.TRY_BLOCK);\n\t\tif (tb != null) {\n\t\t\tList<IContainer> list = new ArrayList<>(tb.getHandlersCount());\n\t\t\tfor (ExceptionHandler eh : tb.getHandlers()) {\n\t\t\t\tlist.add(eh.getHandlerRegion());\n\t\t\t}\n\t\t\treturn list;\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\tpublic static List<LoopInfo> getLoopsStartInRegion(MethodNode mth, IRegion r) {\n\t\tList<LoopInfo> loops = new ArrayList<>();\n\t\tvisitBlocks(mth, r, b -> {\n\t\t\tif (b.contains(AFlag.LOOP_START)) {\n\t\t\t\tloops.addAll(b.getAll(AType.LOOP));\n\t\t\t}\n\t\t});\n\t\treturn loops;\n\t}\n\n\tprivate static boolean isRegionContainsExcHandlerRegion(IContainer container, IRegion region) {\n\t\tif (container == region) {\n\t\t\treturn true;\n\t\t}\n\t\tif (container instanceof IRegion) {\n\t\t\tIRegion r = (IRegion) container;\n\n\t\t\t// process sub blocks\n\t\t\tfor (IContainer b : r.getSubBlocks()) {\n\t\t\t\t// process try block\n\t\t\t\tTryCatchBlockAttr tb = b.get(AType.TRY_BLOCK);\n\t\t\t\tif (tb != null && b instanceof IRegion) {\n\t\t\t\t\tfor (ExceptionHandler eh : tb.getHandlers()) {\n\t\t\t\t\t\tif (isRegionContainsRegion(eh.getHandlerRegion(), region)) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (isRegionContainsRegion(b, region)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Check if {@code region} contains in {@code container}.\n\t * <br>\n\t * For simple region (not from exception handlers) search in parents\n\t * otherwise run recursive search because exception handlers can have several parents\n\t */\n\tpublic static boolean isRegionContainsRegion(IContainer container, IRegion region) {\n\t\tif (container == region) {\n\t\t\treturn true;\n\t\t}\n\t\tif (region == null) {\n\t\t\treturn false;\n\t\t}\n\t\tIRegion parent = region.getParent();\n\t\twhile (container != parent) {\n\t\t\tif (parent == null) {\n\t\t\t\tif (region.contains(AType.EXC_HANDLER)) {\n\t\t\t\t\treturn isRegionContainsExcHandlerRegion(container, region);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tregion = parent;\n\t\t\tparent = region.getParent();\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static IContainer getBlockContainer(IContainer container, IBlock block) {\n\t\tif (container instanceof IBlock) {\n\t\t\treturn container == block ? container : null;\n\t\t}\n\t\tif (container instanceof IRegion) {\n\t\t\tIRegion region = (IRegion) container;\n\t\t\tfor (IContainer c : region.getSubBlocks()) {\n\t\t\t\tIContainer res = getBlockContainer(c, block);\n\t\t\t\tif (res != null) {\n\t\t\t\t\treturn res instanceof IBlock ? region : res;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tthrow new JadxRuntimeException(unknownContainerType(container));\n\t}\n\n\t/**\n\t * Check if two blocks in same region on same level\n\t * TODO: Add 'region' annotation to all blocks to speed up checks\n\t */\n\tpublic static boolean isBlocksInSameRegion(MethodNode mth, BlockNode firstBlock, BlockNode secondBlock) {\n\t\tRegion region = mth.getRegion();\n\t\tif (region == null) {\n\t\t\treturn false;\n\t\t}\n\t\tIContainer firstContainer = getBlockContainer(region, firstBlock);\n\t\tif (firstContainer instanceof IRegion) {\n\t\t\tif (firstContainer instanceof IBranchRegion) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tList<IContainer> subBlocks = ((IRegion) firstContainer).getSubBlocks();\n\t\t\treturn subBlocks.contains(secondBlock);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static boolean isDominatedBy(BlockNode dom, IContainer cont) {\n\t\tif (dom == cont) {\n\t\t\treturn true;\n\t\t}\n\t\tif (cont instanceof BlockNode) {\n\t\t\tBlockNode block = (BlockNode) cont;\n\t\t\treturn block.isDominator(dom);\n\t\t} else if (cont instanceof IBlock) {\n\t\t\treturn false;\n\t\t} else if (cont instanceof IRegion) {\n\t\t\tIRegion region = (IRegion) cont;\n\t\t\tfor (IContainer c : region.getSubBlocks()) {\n\t\t\t\tif (!isDominatedBy(dom, c)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(unknownContainerType(cont));\n\t\t}\n\t}\n\n\tpublic static boolean hasPathThroughBlock(BlockNode block, IContainer cont) {\n\t\tif (block == cont) {\n\t\t\treturn true;\n\t\t}\n\t\tif (cont instanceof BlockNode) {\n\t\t\treturn BlockUtils.isPathExists(block, (BlockNode) cont);\n\t\t}\n\t\tif (cont instanceof IBlock) {\n\t\t\treturn false;\n\t\t}\n\t\tif (cont instanceof IRegion) {\n\t\t\tIRegion region = (IRegion) cont;\n\t\t\tfor (IContainer c : region.getSubBlocks()) {\n\t\t\t\tif (hasPathThroughBlock(block, c)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tthrow new JadxRuntimeException(unknownContainerType(cont));\n\t}\n\n\tprotected static String unknownContainerType(IContainer container) {\n\t\tif (container == null) {\n\t\t\treturn \"Null container variable\";\n\t\t}\n\t\treturn \"Unknown container type: \" + container.getClass();\n\t}\n\n\tpublic static void visitBlocks(MethodNode mth, IContainer container, Consumer<IBlock> visitor) {\n\t\tDepthRegionTraversal.traverse(mth, container, new AbstractRegionVisitor() {\n\t\t\t@Override\n\t\t\tpublic void processBlock(MethodNode mth, IBlock block) {\n\t\t\t\tvisitor.accept(block);\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic static void visitBlockNodes(MethodNode mth, IContainer container, Consumer<BlockNode> visitor) {\n\t\tDepthRegionTraversal.traverse(mth, container, new AbstractRegionVisitor() {\n\t\t\t@Override\n\t\t\tpublic void processBlock(MethodNode mth, IBlock block) {\n\t\t\t\tif (block instanceof BlockNode) {\n\t\t\t\t\tvisitor.accept((BlockNode) block);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic static void visitRegions(MethodNode mth, IContainer container, Predicate<IRegion> visitor) {\n\t\tDepthRegionTraversal.traverse(mth, container, new AbstractRegionVisitor() {\n\t\t\t@Override\n\t\t\tpublic boolean enterRegion(MethodNode mth, IRegion region) {\n\t\t\t\treturn visitor.test(region);\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic static @Nullable IContainer getNextContainer(MethodNode mth, IRegion region) {\n\t\tIRegion parent = region.getParent();\n\t\tList<IContainer> subBlocks = parent.getSubBlocks();\n\t\tint index = subBlocks.indexOf(region);\n\t\tif (index == -1 || index + 1 >= subBlocks.size()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn subBlocks.get(index + 1);\n\t}\n\n\t// Add a flag to all blocks in a region\n\tpublic static void addToAll(MethodNode mth, IContainer container, AFlag flag) {\n\t\tRegionUtils.visitBlocks(mth, container, new Consumer<IBlock>() {\n\t\t\t@Override\n\t\t\tpublic void accept(IBlock t) {\n\t\t\t\tt.add(flag);\n\t\t\t}\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/StringUtils.java",
    "content": "package jadx.core.utils;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.function.IntConsumer;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.args.IntegerFormat;\nimport jadx.core.deobf.NameMapper;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class StringUtils {\n\tprivate static final StringUtils DEFAULT_INSTANCE = new StringUtils(new JadxArgs());\n\tprivate static final String WHITES = \" \\t\\r\\n\\f\\b\";\n\tprivate static final String WORD_SEPARATORS = WHITES + \"(\\\")<,>{}=+-*/|[]\\\\:;'.`~!#^&\";\n\n\tpublic static StringUtils getInstance() {\n\t\treturn DEFAULT_INSTANCE;\n\t}\n\n\tprivate final boolean escapeUnicode;\n\tprivate final IntegerFormat integerFormat;\n\n\tpublic StringUtils(JadxArgs args) {\n\t\tthis.escapeUnicode = args.isEscapeUnicode();\n\t\tthis.integerFormat = args.getIntegerFormat();\n\t}\n\n\tpublic IntegerFormat getIntegerFormat() {\n\t\treturn integerFormat;\n\t}\n\n\tpublic static void visitCodePoints(String str, IntConsumer visitor) {\n\t\tint len = str.length();\n\t\tint offset = 0;\n\t\twhile (offset < len) {\n\t\t\tint codePoint = str.codePointAt(offset);\n\t\t\tvisitor.accept(codePoint);\n\t\t\toffset += Character.charCount(codePoint);\n\t\t}\n\t}\n\n\tpublic String unescapeString(String str) {\n\t\tint len = str.length();\n\t\tif (len == 0) {\n\t\t\treturn \"\\\"\\\"\";\n\t\t}\n\t\tStringBuilder res = new StringBuilder();\n\t\tres.append('\"');\n\t\tvisitCodePoints(str, codePoint -> processCodePoint(codePoint, res));\n\t\tres.append('\"');\n\t\treturn res.toString();\n\t}\n\n\tprivate void processCodePoint(int codePoint, StringBuilder res) {\n\t\tString str = getSpecialStringForCodePoint(codePoint);\n\t\tif (str != null) {\n\t\t\tres.append(str);\n\t\t\treturn;\n\t\t}\n\t\tif (isEscapeNeededForCodePoint(codePoint)) {\n\t\t\tres.append(\"\\\\u\").append(String.format(\"%04x\", codePoint));\n\t\t} else {\n\t\t\tres.appendCodePoint(codePoint);\n\t\t}\n\t}\n\n\tprivate boolean isEscapeNeededForCodePoint(int codePoint) {\n\t\tif (codePoint < 32) {\n\t\t\treturn true;\n\t\t}\n\t\tif (codePoint < 127) {\n\t\t\treturn false;\n\t\t}\n\t\tif (escapeUnicode) {\n\t\t\treturn true;\n\t\t}\n\t\treturn !NameMapper.isPrintableCodePoint(codePoint);\n\t}\n\n\t/**\n\t * Represent single char the best way possible\n\t */\n\tpublic String unescapeChar(char c, boolean explicitCast) {\n\t\tif (c == '\\'') {\n\t\t\treturn \"'\\\\''\";\n\t\t}\n\t\tString str = getSpecialStringForCodePoint(c);\n\t\tif (str != null) {\n\t\t\treturn '\\'' + str + '\\'';\n\t\t}\n\t\tif (c >= 127 && escapeUnicode) {\n\t\t\treturn String.format(\"'\\\\u%04x'\", (int) c);\n\t\t}\n\t\tif (NameMapper.isPrintableChar(c)) {\n\t\t\treturn \"'\" + c + '\\'';\n\t\t}\n\t\tString intStr = Integer.toString(c);\n\t\treturn explicitCast ? \"(char) \" + intStr : intStr;\n\t}\n\n\tpublic String unescapeChar(char ch) {\n\t\treturn unescapeChar(ch, false);\n\t}\n\n\t@Nullable\n\tprivate String getSpecialStringForCodePoint(int c) {\n\t\tswitch (c) {\n\t\t\tcase '\\n':\n\t\t\t\treturn \"\\\\n\";\n\t\t\tcase '\\r':\n\t\t\t\treturn \"\\\\r\";\n\t\t\tcase '\\t':\n\t\t\t\treturn \"\\\\t\";\n\t\t\tcase '\\b':\n\t\t\t\treturn \"\\\\b\";\n\t\t\tcase '\\f':\n\t\t\t\treturn \"\\\\f\";\n\t\t\tcase '\\'':\n\t\t\t\treturn \"'\";\n\t\t\tcase '\"':\n\t\t\t\treturn \"\\\\\\\"\";\n\t\t\tcase '\\\\':\n\t\t\t\treturn \"\\\\\\\\\";\n\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static String escape(String str) {\n\t\tint len = str.length();\n\t\tStringBuilder sb = new StringBuilder(len);\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tchar c = str.charAt(i);\n\t\t\tswitch (c) {\n\t\t\t\tcase '.':\n\t\t\t\tcase '/':\n\t\t\t\tcase ';':\n\t\t\t\tcase '$':\n\t\t\t\tcase ' ':\n\t\t\t\tcase ',':\n\t\t\t\tcase '<':\n\t\t\t\t\tsb.append('_');\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase '[':\n\t\t\t\t\tsb.append('A');\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase ']':\n\t\t\t\tcase '>':\n\t\t\t\tcase '?':\n\t\t\t\tcase '*':\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tsb.append(c);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static String escapeXML(String str) {\n\t\tint len = str.length();\n\t\tStringBuilder sb = new StringBuilder(len);\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tchar c = str.charAt(i);\n\t\t\tString replace = escapeXmlChar(c);\n\t\t\tif (replace != null) {\n\t\t\t\tsb.append(replace);\n\t\t\t} else {\n\t\t\t\tsb.append(c);\n\t\t\t}\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static String escapeResValue(String str) {\n\t\tint len = str.length();\n\t\tStringBuilder sb = new StringBuilder(len);\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tchar c = str.charAt(i);\n\t\t\tcommonEscapeAndAppend(sb, c);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static String escapeResStrValue(String str) {\n\t\tint len = str.length();\n\t\tStringBuilder sb = new StringBuilder(len);\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tchar c = str.charAt(i);\n\t\t\tswitch (c) {\n\t\t\t\tcase '\"':\n\t\t\t\t\tsb.append(\"\\\\\\\"\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\\'':\n\t\t\t\t\tsb.append(\"\\\\'\");\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tcommonEscapeAndAppend(sb, c);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tprivate static String escapeXmlChar(char c) {\n\t\tif (c <= 0x1F) {\n\t\t\treturn \"\\\\\" + (int) c;\n\t\t}\n\t\tswitch (c) {\n\t\t\tcase '&':\n\t\t\t\treturn \"&amp;\";\n\t\t\tcase '<':\n\t\t\t\treturn \"&lt;\";\n\t\t\tcase '>':\n\t\t\t\treturn \"&gt;\";\n\t\t\tcase '\"':\n\t\t\t\treturn \"&quot;\";\n\t\t\tcase '\\'':\n\t\t\t\treturn \"&apos;\";\n\t\t\tcase '\\\\':\n\t\t\t\treturn \"\\\\\\\\\";\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static String escapeWhiteSpaceChar(char c) {\n\t\tswitch (c) {\n\t\t\tcase '\\n':\n\t\t\t\treturn \"\\\\n\";\n\t\t\tcase '\\r':\n\t\t\t\treturn \"\\\\r\";\n\t\t\tcase '\\t':\n\t\t\t\treturn \"\\\\t\";\n\t\t\tcase '\\b':\n\t\t\t\treturn \"\\\\b\";\n\t\t\tcase '\\f':\n\t\t\t\treturn \"\\\\f\";\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static void commonEscapeAndAppend(StringBuilder sb, char c) {\n\t\tString replace = escapeWhiteSpaceChar(c);\n\t\tif (replace == null) {\n\t\t\treplace = escapeXmlChar(c);\n\t\t}\n\t\tif (replace != null) {\n\t\t\tsb.append(replace);\n\t\t} else {\n\t\t\tsb.append(c);\n\t\t}\n\t}\n\n\tpublic static boolean notEmpty(String str) {\n\t\treturn str != null && !str.isEmpty();\n\t}\n\n\tpublic static boolean isEmpty(String str) {\n\t\treturn str == null || str.isEmpty();\n\t}\n\n\tpublic static boolean notBlank(String str) {\n\t\treturn notEmpty(str) && !str.trim().isEmpty();\n\t}\n\n\tpublic static int countMatches(String str, String subStr) {\n\t\tif (str == null || str.isEmpty() || subStr == null || subStr.isEmpty()) {\n\t\t\treturn 0;\n\t\t}\n\t\tint subStrLen = subStr.length();\n\t\tint count = 0;\n\t\tint idx = 0;\n\t\twhile ((idx = str.indexOf(subStr, idx)) != -1) {\n\t\t\tcount++;\n\t\t\tidx += subStrLen;\n\t\t}\n\t\treturn count;\n\t}\n\n\tpublic static boolean containsChar(String str, char ch) {\n\t\treturn str.indexOf(ch) != -1;\n\t}\n\n\tpublic static String removeChar(String str, char ch) {\n\t\tint pos = str.indexOf(ch);\n\t\tif (pos == -1) {\n\t\t\treturn str;\n\t\t}\n\t\tStringBuilder sb = new StringBuilder(str.length());\n\t\tint cur = 0;\n\t\tint next = pos;\n\t\twhile (true) {\n\t\t\tsb.append(str, cur, next);\n\t\t\tcur = next + 1;\n\t\t\tnext = str.indexOf(ch, cur);\n\t\t\tif (next == -1) {\n\t\t\t\tsb.append(str, cur, str.length());\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\t/**\n\t * returns how many lines does it have between start to pos in content.\n\t */\n\tpublic static int countLinesByPos(String content, int pos, int start) {\n\t\tif (start >= pos) {\n\t\t\treturn 0;\n\t\t}\n\t\tint count = 0;\n\t\tint tempPos = start;\n\t\tdo {\n\t\t\ttempPos = content.indexOf(\"\\n\", tempPos);\n\t\t\tif (tempPos == -1) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (tempPos >= pos) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcount += 1;\n\t\t\ttempPos += 1;\n\t\t} while (tempPos < content.length());\n\t\treturn count;\n\t}\n\n\t/**\n\t * returns lines that contain pos to end if end is not -1.\n\t */\n\tpublic static String getLine(String content, int pos, int end) {\n\t\tif (pos >= content.length()) {\n\t\t\treturn \"\";\n\t\t}\n\t\tif (end != -1) {\n\t\t\tif (end > content.length()) {\n\t\t\t\tend = content.length() - 1;\n\t\t\t}\n\t\t} else {\n\t\t\tend = pos + 1;\n\t\t}\n\t\t// get to line head\n\t\tint headPos = content.lastIndexOf(\"\\n\", pos);\n\t\tif (headPos == -1) {\n\t\t\theadPos = 0;\n\t\t}\n\t\t// get to line end\n\t\tint endPos = content.indexOf(\"\\n\", end);\n\t\tif (endPos == -1) {\n\t\t\tendPos = content.length();\n\t\t}\n\t\treturn content.substring(headPos, endPos);\n\t}\n\n\tpublic static boolean isWhite(char chr) {\n\t\treturn WHITES.indexOf(chr) != -1;\n\t}\n\n\tpublic static boolean isWordSeparator(char chr) {\n\t\treturn WORD_SEPARATORS.indexOf(chr) != -1;\n\t}\n\n\tpublic static String removeSuffix(String str, String suffix) {\n\t\tif (str.endsWith(suffix)) {\n\t\t\treturn str.substring(0, str.length() - suffix.length());\n\t\t}\n\t\treturn str;\n\t}\n\n\tpublic static @Nullable String getPrefix(String str, String delim) {\n\t\tint idx = str.indexOf(delim);\n\t\tif (idx != -1) {\n\t\t\treturn str.substring(0, idx);\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static String getDateText() {\n\t\treturn new SimpleDateFormat(\"HH:mm:ss\").format(new Date());\n\t}\n\n\tprivate String formatNumber(long number, int bytesLen, boolean cast) {\n\t\tString numStr;\n\t\tif (integerFormat.isHexadecimal()) {\n\t\t\tString hexStr = Long.toHexString(number);\n\t\t\tif (number < 0) {\n\t\t\t\t// cut leading 'f' for negative numbers to match number type length\n\t\t\t\tint len = hexStr.length();\n\t\t\t\tnumStr = \"0x\" + hexStr.substring(len - bytesLen * 2, len);\n\t\t\t\t// force cast, because unsigned negative numbers are bigger\n\t\t\t\t// than signed max value allowed by compiler\n\t\t\t\tcast = true;\n\t\t\t} else {\n\t\t\t\tnumStr = \"0x\" + hexStr;\n\t\t\t}\n\t\t} else {\n\t\t\tnumStr = Long.toString(number);\n\t\t}\n\t\tif (bytesLen == 8 && (number == Long.MIN_VALUE || Math.abs(number) >= Integer.MAX_VALUE)) {\n\t\t\t// force cast for long values bigger than min/max int\n\t\t\t// to resolve compiler error: \"integer number too large\"\n\t\t\tcast = true;\n\t\t}\n\t\tif (cast) {\n\t\t\tif (bytesLen == 8) {\n\t\t\t\treturn numStr + 'L';\n\t\t\t}\n\t\t\treturn getCastStr(bytesLen) + numStr;\n\t\t}\n\t\treturn numStr;\n\t}\n\n\tprivate static String getCastStr(int bytesLen) {\n\t\tswitch (bytesLen) {\n\t\t\tcase 1:\n\t\t\t\treturn \"(byte) \";\n\t\t\tcase 2:\n\t\t\t\treturn \"(short) \";\n\t\t\tcase 4:\n\t\t\t\treturn \"(int) \";\n\t\t\tcase 8:\n\t\t\t\treturn \"(long) \";\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unexpected number type length: \" + bytesLen);\n\t\t}\n\t}\n\n\tpublic String formatByte(long l, boolean cast) {\n\t\treturn formatNumber(l, 1, cast);\n\t}\n\n\tpublic String formatShort(long l, boolean cast) {\n\t\tif (integerFormat == IntegerFormat.AUTO) {\n\t\t\tswitch ((short) l) {\n\t\t\t\tcase Short.MAX_VALUE:\n\t\t\t\t\treturn \"Short.MAX_VALUE\";\n\t\t\t\tcase Short.MIN_VALUE:\n\t\t\t\t\treturn \"Short.MIN_VALUE\";\n\t\t\t}\n\t\t}\n\t\treturn formatNumber(l, 2, cast);\n\t}\n\n\tpublic String formatInteger(long l, boolean cast) {\n\t\tif (integerFormat == IntegerFormat.AUTO) {\n\t\t\tswitch ((int) l) {\n\t\t\t\tcase Integer.MAX_VALUE:\n\t\t\t\t\treturn \"Integer.MAX_VALUE\";\n\t\t\t\tcase Integer.MIN_VALUE:\n\t\t\t\t\treturn \"Integer.MIN_VALUE\";\n\t\t\t}\n\t\t}\n\t\treturn formatNumber(l, 4, cast);\n\t}\n\n\tpublic String formatLong(long l, boolean cast) {\n\t\tif (integerFormat == IntegerFormat.AUTO) {\n\t\t\tif (l == Long.MAX_VALUE) {\n\t\t\t\treturn \"Long.MAX_VALUE\";\n\t\t\t}\n\t\t\tif (l == Long.MIN_VALUE) {\n\t\t\t\treturn \"Long.MIN_VALUE\";\n\t\t\t}\n\t\t}\n\t\treturn formatNumber(l, 8, cast);\n\t}\n\n\tpublic static String formatDouble(double d) {\n\t\tif (Double.isNaN(d)) {\n\t\t\treturn \"Double.NaN\";\n\t\t}\n\t\tif (d == Double.NEGATIVE_INFINITY) {\n\t\t\treturn \"Double.NEGATIVE_INFINITY\";\n\t\t}\n\t\tif (d == Double.POSITIVE_INFINITY) {\n\t\t\treturn \"Double.POSITIVE_INFINITY\";\n\t\t}\n\t\tif (d == Double.MIN_VALUE) {\n\t\t\treturn \"Double.MIN_VALUE\";\n\t\t}\n\t\tif (d == Double.MAX_VALUE) {\n\t\t\treturn \"Double.MAX_VALUE\";\n\t\t}\n\t\tif (d == Double.MIN_NORMAL) {\n\t\t\treturn \"Double.MIN_NORMAL\";\n\t\t}\n\t\treturn Double.toString(d) + 'd';\n\t}\n\n\tpublic static String formatFloat(float f) {\n\t\tif (Float.isNaN(f)) {\n\t\t\treturn \"Float.NaN\";\n\t\t}\n\t\tif (f == Float.NEGATIVE_INFINITY) {\n\t\t\treturn \"Float.NEGATIVE_INFINITY\";\n\t\t}\n\t\tif (f == Float.POSITIVE_INFINITY) {\n\t\t\treturn \"Float.POSITIVE_INFINITY\";\n\t\t}\n\t\tif (f == Float.MIN_VALUE) {\n\t\t\treturn \"Float.MIN_VALUE\";\n\t\t}\n\t\tif (f == Float.MAX_VALUE) {\n\t\t\treturn \"Float.MAX_VALUE\";\n\t\t}\n\t\tif (f == Float.MIN_NORMAL) {\n\t\t\treturn \"Float.MIN_NORMAL\";\n\t\t}\n\t\treturn Float.toString(f) + 'f';\n\t}\n\n\tpublic static String capitalizeFirstChar(String str) {\n\t\tif (isEmpty(str)) {\n\t\t\treturn str;\n\t\t}\n\t\treturn Character.toUpperCase(str.charAt(0)) + str.substring(1);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/Utils.java",
    "content": "package jadx.core.utils;\n\nimport java.io.OutputStream;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Deque;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeWriter;\nimport jadx.api.JadxDecompiler;\nimport jadx.core.dex.visitors.DepthTraversal;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class Utils {\n\n\tprivate static final String JADX_API_PACKAGE = JadxDecompiler.class.getPackage().getName();\n\tprivate static final String STACKTRACE_STOP_CLS_NAME = DepthTraversal.class.getName();\n\n\tprivate Utils() {\n\t}\n\n\tpublic static String cleanObjectName(String obj) {\n\t\tif (obj.charAt(0) == 'L') {\n\t\t\tint last = obj.length() - 1;\n\t\t\tif (obj.charAt(last) == ';') {\n\t\t\t\treturn obj.substring(1, last).replace('/', '.');\n\t\t\t}\n\t\t}\n\t\treturn obj;\n\t}\n\n\tpublic static String cutObject(String obj) {\n\t\tif (obj.charAt(0) == 'L') {\n\t\t\treturn obj.substring(1, obj.length() - 1);\n\t\t}\n\t\treturn obj;\n\t}\n\n\tpublic static String makeQualifiedObjectName(String obj) {\n\t\treturn 'L' + obj.replace('.', '/') + ';';\n\t}\n\n\tpublic static String smaliNameToJavaName(String descString) {\n\t\tif (descString.isEmpty()) {\n\t\t\treturn descString;\n\t\t}\n\n\t\tString javaName;\n\t\tswitch (descString.charAt(0)) {\n\t\t\tcase 'V':\n\t\t\t\tjavaName = \"void\";\n\t\t\t\tbreak;\n\t\t\tcase 'Z':\n\t\t\t\tjavaName = \"boolean\";\n\t\t\t\tbreak;\n\t\t\tcase 'C':\n\t\t\t\tjavaName = \"char\";\n\t\t\t\tbreak;\n\t\t\tcase 'B':\n\t\t\t\tjavaName = \"byte\";\n\t\t\t\tbreak;\n\t\t\tcase 'S':\n\t\t\t\tjavaName = \"short\";\n\t\t\t\tbreak;\n\t\t\tcase 'I':\n\t\t\t\tjavaName = \"int\";\n\t\t\t\tbreak;\n\t\t\tcase 'F':\n\t\t\t\tjavaName = \"float\";\n\t\t\t\tbreak;\n\t\t\tcase 'J':\n\t\t\t\tjavaName = \"long\";\n\t\t\t\tbreak;\n\t\t\tcase 'D':\n\t\t\t\tjavaName = \"double\";\n\t\t\t\tbreak;\n\t\t\tcase 'L':\n\t\t\t\tjavaName = cleanObjectNameWithInnerClass(descString);\n\t\t\t\tbreak;\n\t\t\tcase '[':\n\t\t\t\tjavaName = String.format(\"%s[]\", smaliNameToJavaName(descString.substring(1, descString.length())));\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tjavaName = descString;\n\t\t\t\tbreak;\n\t\t}\n\t\treturn javaName;\n\t}\n\n\tprivate static String cleanObjectNameWithInnerClass(String obj) {\n\t\t// Probably can just update the Utils.cleanObjectName method?\n\t\tString result = Utils.cleanObjectName(obj);\n\t\treturn result.replace('$', '.');\n\t}\n\n\tpublic static String javaNameToSmaliName(String descString) {\n\t\tif (descString.isEmpty()) {\n\t\t\treturn descString;\n\t\t}\n\n\t\tif (descString.endsWith(\"[]\")) {\n\t\t\treturn String.format(\"[%s\", javaNameToSmaliName(descString.substring(0, descString.length() - 2)));\n\t\t}\n\n\t\tString javaName;\n\t\tswitch (descString) {\n\t\t\tcase \"void\":\n\t\t\t\tjavaName = \"V\";\n\t\t\t\tbreak;\n\t\t\tcase \"boolean\":\n\t\t\t\tjavaName = \"Z\";\n\t\t\t\tbreak;\n\t\t\tcase \"char\":\n\t\t\t\tjavaName = \"C\";\n\t\t\t\tbreak;\n\t\t\tcase \"byte\":\n\t\t\t\tjavaName = \"B\";\n\t\t\t\tbreak;\n\t\t\tcase \"short\":\n\t\t\t\tjavaName = \"S\";\n\t\t\t\tbreak;\n\t\t\tcase \"int\":\n\t\t\t\tjavaName = \"I\";\n\t\t\t\tbreak;\n\t\t\tcase \"float\":\n\t\t\t\tjavaName = \"F\";\n\t\t\t\tbreak;\n\t\t\tcase \"long\":\n\t\t\t\tjavaName = \"J\";\n\t\t\t\tbreak;\n\t\t\tcase \"double\":\n\t\t\t\tjavaName = \"D\";\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tjavaName = Utils.makeQualifiedObjectName(descString);\n\t\t\t\tbreak;\n\t\t}\n\t\treturn javaName;\n\t}\n\n\t@SuppressWarnings(\"StringRepeatCanBeUsed\")\n\tpublic static String strRepeat(String str, int count) {\n\t\tif (count < 1) {\n\t\t\treturn \"\";\n\t\t}\n\t\tif (count == 1) {\n\t\t\treturn str;\n\t\t}\n\t\tStringBuilder sb = new StringBuilder(str.length() * count);\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tsb.append(str);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static String listToString(Iterable<?> objects) {\n\t\treturn listToString(objects, \", \");\n\t}\n\n\tpublic static String listToString(Iterable<?> objects, String joiner) {\n\t\tif (objects == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn listToString(objects, joiner, Objects::toString);\n\t}\n\n\tpublic static <T> String listToString(Iterable<T> objects, Function<T, String> toStr) {\n\t\treturn listToString(objects, \", \", toStr);\n\t}\n\n\tpublic static <T> String listToString(Iterable<T> objects, String joiner, Function<T, String> toStr) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tlistToString(sb, objects, joiner, toStr);\n\t\treturn sb.toString();\n\t}\n\n\tpublic static <T> void listToString(StringBuilder sb, Iterable<T> objects, String joiner) {\n\t\tlistToString(sb, objects, joiner, Objects::toString);\n\t}\n\n\tpublic static <T> void listToString(StringBuilder sb, Iterable<T> objects, String joiner, Function<T, String> toStr) {\n\t\tif (objects == null) {\n\t\t\treturn;\n\t\t}\n\t\tIterator<T> it = objects.iterator();\n\t\tif (it.hasNext()) {\n\t\t\tsb.append(toStr.apply(it.next()));\n\t\t}\n\t\twhile (it.hasNext()) {\n\t\t\tsb.append(joiner).append(toStr.apply(it.next()));\n\t\t}\n\t}\n\n\tpublic static <T> String arrayToStr(T[] arr) {\n\t\tint len = arr == null ? 0 : arr.length;\n\t\tif (len == 0) {\n\t\t\treturn \"\";\n\t\t}\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(arr[0]);\n\t\tfor (int i = 1; i < len; i++) {\n\t\t\tsb.append(\", \").append(arr[i]);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static String concatStrings(List<String> list) {\n\t\tif (isEmpty(list)) {\n\t\t\treturn \"\";\n\t\t}\n\t\tif (list.size() == 1) {\n\t\t\treturn list.get(0);\n\t\t}\n\t\tStringBuilder sb = new StringBuilder();\n\t\tlist.forEach(sb::append);\n\t\treturn sb.toString();\n\t}\n\n\tpublic static String currentStackTrace() {\n\t\treturn getStackTrace(new Exception());\n\t}\n\n\tpublic static String currentStackTrace(int skipFrames) {\n\t\tException e = new Exception();\n\t\tStackTraceElement[] stackTrace = e.getStackTrace();\n\t\tint len = stackTrace.length;\n\t\tif (skipFrames < len) {\n\t\t\te.setStackTrace(Arrays.copyOfRange(stackTrace, skipFrames, len));\n\t\t}\n\t\treturn getStackTrace(e);\n\t}\n\n\tpublic static String getFullStackTrace(Throwable throwable) {\n\t\treturn getStackTrace(throwable, false);\n\t}\n\n\tpublic static String getStackTrace(Throwable throwable) {\n\t\treturn getStackTrace(throwable, true);\n\t}\n\n\tprivate static String getStackTrace(Throwable throwable, boolean filter) {\n\t\tif (throwable == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\tStringWriter sw = new StringWriter();\n\t\tPrintWriter pw = new PrintWriter(sw, true);\n\t\tif (filter) {\n\t\t\tfilterRecursive(throwable);\n\t\t}\n\t\tthrowable.printStackTrace(pw);\n\t\treturn sw.getBuffer().toString();\n\t}\n\n\tpublic static void appendStackTrace(ICodeWriter code, Throwable throwable) {\n\t\tif (throwable == null) {\n\t\t\treturn;\n\t\t}\n\t\tcode.startLine();\n\t\tOutputStream w = new OutputStream() {\n\t\t\t@Override\n\t\t\tpublic void write(int b) {\n\t\t\t\tchar c = (char) b;\n\t\t\t\tswitch (c) {\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tcode.startLine();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase '\\r':\n\t\t\t\t\t\t// ignore\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tcode.add(c);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\ttry (PrintWriter pw = new PrintWriter(w, true)) {\n\t\t\tfilterRecursive(throwable);\n\t\t\tthrowable.printStackTrace(pw);\n\t\t\tpw.flush();\n\t\t}\n\t}\n\n\tprivate static void filterRecursive(Throwable th) {\n\t\ttry {\n\t\t\tfilter(th);\n\t\t} catch (Exception e) {\n\t\t\t// ignore filter exceptions\n\t\t}\n\t\tThrowable cause = th.getCause();\n\t\tif (cause != null) {\n\t\t\tfilterRecursive(cause);\n\t\t}\n\t}\n\n\tprivate static void filter(Throwable th) {\n\t\tStackTraceElement[] stackTrace = th.getStackTrace();\n\t\tint length = stackTrace.length;\n\t\tStackTraceElement prevElement = null;\n\t\tfor (int i = 0; i < length; i++) {\n\t\t\tStackTraceElement stackTraceElement = stackTrace[i];\n\t\t\tString clsName = stackTraceElement.getClassName();\n\t\t\tif (clsName.equals(STACKTRACE_STOP_CLS_NAME)\n\t\t\t\t\t|| clsName.startsWith(JADX_API_PACKAGE)\n\t\t\t\t\t|| Objects.equals(prevElement, stackTraceElement)) {\n\t\t\t\tth.setStackTrace(Arrays.copyOfRange(stackTrace, 0, i));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tprevElement = stackTraceElement;\n\t\t}\n\t\t// stop condition not found -> just cut tail to any jadx class\n\t\tfor (int i = length - 1; i >= 0; i--) {\n\t\t\tString clsName = stackTrace[i].getClassName();\n\t\t\tif (clsName.startsWith(\"jadx.\")) {\n\t\t\t\tif (clsName.startsWith(\"jadx.tests.\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tth.setStackTrace(Arrays.copyOfRange(stackTrace, 0, i));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static <T, R> List<R> collectionMap(Collection<T> list, Function<T, R> mapFunc) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<R> result = new ArrayList<>(list.size());\n\t\tfor (T t : list) {\n\t\t\tresult.add(mapFunc.apply(t));\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic static <T, R> List<R> collectionMapNoNull(Collection<T> list, Function<T, R> mapFunc) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<R> result = new ArrayList<>(list.size());\n\t\tfor (T t : list) {\n\t\t\tR r = mapFunc.apply(t);\n\t\t\tif (r != null) {\n\t\t\t\tresult.add(r);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic static <T> boolean containsInListByRef(List<T> list, T element) {\n\t\tif (isEmpty(list)) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (T t : list) {\n\t\t\tif (t == element) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static <T> int indexInListByRef(List<T> list, T element) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn -1;\n\t\t}\n\t\tint size = list.size();\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tT t = list.get(i);\n\t\t\tif (t == element) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\tpublic static <T> List<T> lockList(List<T> list) {\n\t\tif (list.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tif (list.size() == 1) {\n\t\t\treturn Collections.singletonList(list.get(0));\n\t\t}\n\t\treturn new ImmutableList<>(list);\n\t}\n\n\t/**\n\t * Sub list from startIndex (inclusive) to list end\n\t */\n\tpublic static <T> List<T> listTail(List<T> list, int startIndex) {\n\t\tif (startIndex == 0) {\n\t\t\treturn list;\n\t\t}\n\t\tint size = list.size();\n\t\tif (startIndex >= size) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn list.subList(startIndex, size);\n\t}\n\n\tpublic static <T> List<T> mergeLists(List<T> first, List<T> second) {\n\t\tif (isEmpty(first)) {\n\t\t\treturn second;\n\t\t}\n\t\tif (isEmpty(second)) {\n\t\t\treturn first;\n\t\t}\n\t\tList<T> result = new ArrayList<>(first.size() + second.size());\n\t\tresult.addAll(first);\n\t\tresult.addAll(second);\n\t\treturn result;\n\t}\n\n\tpublic static <T> Set<T> mergeSets(Set<T> first, Set<T> second) {\n\t\tif (isEmpty(first)) {\n\t\t\treturn second;\n\t\t}\n\t\tif (isEmpty(second)) {\n\t\t\treturn first;\n\t\t}\n\t\tSet<T> result = new HashSet<>(first.size() + second.size());\n\t\tresult.addAll(first);\n\t\tresult.addAll(second);\n\t\treturn result;\n\t}\n\n\tpublic static Map<String, String> newConstStringMap(String... parameters) {\n\t\tint len = parameters.length;\n\t\tif (len == 0) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tif (len % 2 != 0) {\n\t\t\tthrow new IllegalArgumentException(\"Incorrect arguments count: \" + len);\n\t\t}\n\t\tMap<String, String> result = new HashMap<>(len / 2);\n\t\tfor (int i = 0; i < len - 1; i += 2) {\n\t\t\tresult.put(parameters[i], parameters[i + 1]);\n\t\t}\n\t\treturn Collections.unmodifiableMap(result);\n\t}\n\n\t/**\n\t * Merge two maps. Return HashMap as result. Second map will override values from first map.\n\t */\n\tpublic static <K, V> Map<K, V> mergeMaps(Map<K, V> first, Map<K, V> second) {\n\t\tif (isEmpty(first)) {\n\t\t\treturn second;\n\t\t}\n\t\tif (isEmpty(second)) {\n\t\t\treturn first;\n\t\t}\n\t\tMap<K, V> result = new HashMap<>(first.size() + second.size());\n\t\tresult.putAll(first);\n\t\tresult.putAll(second);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Build map from list of values with value to key mapping function\n\t * <br>\n\t * Similar to:\n\t * <br>\n\t * {@code list.stream().collect(Collectors.toMap(mapKey, Function.identity())); }\n\t */\n\tpublic static <K, V> Map<K, V> groupBy(List<V> list, Function<V, K> mapKey) {\n\t\tMap<K, V> map = new HashMap<>(list.size());\n\t\tfor (V v : list) {\n\t\t\tmap.put(mapKey.apply(v), v);\n\t\t}\n\t\treturn map;\n\t}\n\n\t/**\n\t * Simple DFS visit for tree (cycles not allowed)\n\t */\n\tpublic static <T> void treeDfsVisit(T root, Function<T, List<T>> childrenProvider, Consumer<T> visitor) {\n\t\tmultiRootTreeDfsVisit(Collections.singletonList(root), childrenProvider, visitor);\n\t}\n\n\tpublic static <T> void multiRootTreeDfsVisit(List<T> roots, Function<T, List<T>> childrenProvider, Consumer<T> visitor) {\n\t\tDeque<T> queue = new ArrayDeque<>(roots);\n\t\twhile (true) {\n\t\t\tT current = queue.pollLast();\n\t\t\tif (current == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvisitor.accept(current);\n\t\t\tfor (T child : childrenProvider.apply(current)) {\n\t\t\t\tqueue.addLast(child);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic static <T> T getOne(@Nullable List<T> list) {\n\t\tif (list == null || list.size() != 1) {\n\t\t\treturn null;\n\t\t}\n\t\treturn list.get(0);\n\t}\n\n\t@Nullable\n\tpublic static <T> T getOne(@Nullable Collection<T> collection) {\n\t\tif (collection == null || collection.size() != 1) {\n\t\t\treturn null;\n\t\t}\n\t\treturn collection.iterator().next();\n\t}\n\n\tpublic static <T> boolean isSetContainsAny(Set<T> inputSet, Set<T> searchKeys) {\n\t\tfor (T t : inputSet) {\n\t\t\tif (searchKeys.contains(t)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Nullable\n\tpublic static <T> T first(List<T> list) {\n\t\tif (list.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn list.get(0);\n\t}\n\n\t@Nullable\n\tpublic static <T> T first(Iterable<T> list) {\n\t\tIterator<T> it = list.iterator();\n\t\tif (!it.hasNext()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn it.next();\n\t}\n\n\t@Nullable\n\tpublic static <T> T last(List<T> list) {\n\t\tif (list.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn list.get(list.size() - 1);\n\t}\n\n\t@Nullable\n\tpublic static <T> T last(Iterable<T> list) {\n\t\tIterator<T> it = list.iterator();\n\t\tif (!it.hasNext()) {\n\t\t\treturn null;\n\t\t}\n\t\twhile (true) {\n\t\t\tT next = it.next();\n\t\t\tif (!it.hasNext()) {\n\t\t\t\treturn next;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static <T> T getOrElse(@Nullable T obj, T defaultObj) {\n\t\tif (obj == null) {\n\t\t\treturn defaultObj;\n\t\t}\n\t\treturn obj;\n\t}\n\n\tpublic static <T> boolean isEmpty(Collection<T> col) {\n\t\treturn col == null || col.isEmpty();\n\t}\n\n\tpublic static <T> boolean notEmpty(Collection<T> col) {\n\t\treturn col != null && !col.isEmpty();\n\t}\n\n\tpublic static <K, V> boolean isEmpty(Map<K, V> map) {\n\t\treturn map == null || map.isEmpty();\n\t}\n\n\tpublic static <T> boolean isEmpty(T[] arr) {\n\t\treturn arr == null || arr.length == 0;\n\t}\n\n\tpublic static <T> boolean notEmpty(T[] arr) {\n\t\treturn arr != null && arr.length != 0;\n\t}\n\n\tpublic static void checkThreadInterrupt() {\n\t\tif (Thread.currentThread().isInterrupted()) {\n\t\t\tthrow new JadxRuntimeException(\"Thread interrupted\");\n\t\t}\n\t}\n\n\tpublic static ThreadFactory simpleThreadFactory(String name) {\n\t\treturn new SimpleThreadFactory(name);\n\t}\n\n\tprivate static final class SimpleThreadFactory implements ThreadFactory {\n\t\tprivate static final AtomicInteger POOL = new AtomicInteger(0);\n\t\tprivate static final Thread.UncaughtExceptionHandler EXC_HANDLER = new SimpleUncaughtExceptionHandler();\n\n\t\tprivate final AtomicInteger number = new AtomicInteger(0);\n\t\tprivate final String name;\n\n\t\tpublic SimpleThreadFactory(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\t@Override\n\t\tpublic Thread newThread(@NotNull Runnable r) {\n\t\t\tThread thread = new Thread(r, \"jadx-\" + name\n\t\t\t\t\t+ '-' + POOL.incrementAndGet()\n\t\t\t\t\t+ '-' + number.incrementAndGet());\n\t\t\tthread.setUncaughtExceptionHandler(EXC_HANDLER);\n\t\t\treturn thread;\n\t\t}\n\n\t\tprivate static class SimpleUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {\n\t\t\tprivate static final Logger LOG = LoggerFactory.getLogger(SimpleUncaughtExceptionHandler.class);\n\n\t\t\t@Override\n\t\t\tpublic void uncaughtException(Thread thread, Throwable e) {\n\t\t\t\tif (e instanceof OutOfMemoryError) {\n\t\t\t\t\tthread.interrupt();\n\t\t\t\t\tLOG.error(\"OutOfMemoryError in thread: {}, forcing interrupt\", thread.getName());\n\t\t\t\t} else {\n\t\t\t\t\tLOG.error(\"Uncaught thread exception, thread: {}\", thread.getName(), e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @deprecated env vars shouldn't be used in core modules.\n\t *             Prefer to parse in `app` (use JadxCommonEnv from 'app-commons') and set in jadx args.\n\t */\n\t@Deprecated\n\tpublic static boolean getEnvVarBool(String varName, boolean defValue) {\n\t\tString strValue = System.getenv(varName);\n\t\tif (strValue == null) {\n\t\t\treturn defValue;\n\t\t}\n\t\treturn strValue.equalsIgnoreCase(\"true\");\n\t}\n\n\t/**\n\t * @deprecated env vars shouldn't be used in core modules.\n\t *             Prefer to parse in `app` (use JadxCommonEnv from 'app-commons') and set in jadx args.\n\t */\n\t@Deprecated\n\tpublic static int getEnvVarInt(String varName, int defValue) {\n\t\tString strValue = System.getenv(varName);\n\t\tif (strValue == null) {\n\t\t\treturn defValue;\n\t\t}\n\t\treturn Integer.parseInt(strValue);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/android/AndroidManifestParser.java",
    "content": "package jadx.core.utils.android;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.NodeList;\n\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourceType;\nimport jadx.api.security.IJadxSecurity;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.xmlgen.ResContainer;\n\npublic class AndroidManifestParser {\n\tprivate final Document androidManifest;\n\tprivate final @Nullable Document appStrings;\n\tprivate final EnumSet<AppAttribute> parseAttrs;\n\tprivate final IJadxSecurity security;\n\n\tpublic AndroidManifestParser(ResourceFile androidManifestRes, EnumSet<AppAttribute> parseAttrs, IJadxSecurity security) {\n\t\tthis(androidManifestRes, null, parseAttrs, security);\n\t}\n\n\tpublic AndroidManifestParser(ResourceFile androidManifestRes, @Nullable ResContainer appStrings,\n\t\t\tEnumSet<AppAttribute> parseAttrs, IJadxSecurity security) {\n\t\tthis.parseAttrs = parseAttrs;\n\t\tthis.security = Objects.requireNonNull(security);\n\n\t\tthis.androidManifest = parseAndroidManifest(androidManifestRes);\n\t\tthis.appStrings = parseAppStrings(appStrings);\n\t}\n\n\tpublic boolean isManifestFound() {\n\t\treturn androidManifest != null;\n\t}\n\n\t@Nullable\n\tpublic static ResourceFile getAndroidManifest(List<ResourceFile> resources) {\n\t\treturn resources.stream()\n\t\t\t\t.filter(resourceFile -> resourceFile.getType() == ResourceType.MANIFEST)\n\t\t\t\t.findFirst()\n\t\t\t\t.orElse(null);\n\t}\n\n\tpublic ApplicationParams parse() {\n\t\tif (!isManifestFound()) {\n\t\t\tthrow new JadxRuntimeException(\"AndroidManifest.xml is missing\");\n\t\t}\n\n\t\treturn parseAttributes();\n\t}\n\n\tprivate ApplicationParams parseAttributes() {\n\t\tString applicationLabel = null;\n\t\tInteger minSdkVersion = null;\n\t\tInteger targetSdkVersion = null;\n\t\tInteger compileSdkVersion = null;\n\t\tInteger versionCode = null;\n\t\tString versionName = null;\n\t\tString mainActivity = null;\n\t\tString application = null;\n\n\t\t@Nullable\n\t\tElement manifest = (Element) androidManifest.getElementsByTagName(\"manifest\").item(0);\n\t\t@Nullable\n\t\tElement usesSdk = (Element) androidManifest.getElementsByTagName(\"uses-sdk\").item(0);\n\n\t\tif (parseAttrs.contains(AppAttribute.APPLICATION_LABEL)) {\n\t\t\tapplicationLabel = getApplicationLabel();\n\t\t}\n\t\tif (usesSdk != null) {\n\t\t\tif (parseAttrs.contains(AppAttribute.MIN_SDK_VERSION)) {\n\t\t\t\tminSdkVersion = Integer.valueOf(usesSdk.getAttribute(\"android:minSdkVersion\"));\n\t\t\t}\n\t\t\tif (parseAttrs.contains(AppAttribute.TARGET_SDK_VERSION)) {\n\t\t\t\tString stringTargetSdk = usesSdk.getAttribute(\"android:targetSdkVersion\");\n\t\t\t\tif (!stringTargetSdk.isEmpty()) {\n\t\t\t\t\ttargetSdkVersion = Integer.valueOf(stringTargetSdk);\n\t\t\t\t} else {\n\t\t\t\t\tif (minSdkVersion == null) {\n\t\t\t\t\t\tminSdkVersion = Integer.valueOf(usesSdk.getAttribute(\"android:minSdkVersion\"));\n\t\t\t\t\t}\n\t\t\t\t\ttargetSdkVersion = minSdkVersion;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (parseAttrs.contains(AppAttribute.COMPILE_SDK_VERSION)) {\n\t\t\t\tString stringCompileSdk = usesSdk.getAttribute(\"android:compileSdkVersion\");\n\t\t\t\tif (!stringCompileSdk.isEmpty()) {\n\t\t\t\t\tcompileSdkVersion = Integer.valueOf(stringCompileSdk);\n\t\t\t\t} else {\n\t\t\t\t\tcompileSdkVersion = targetSdkVersion;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (manifest != null) {\n\t\t\tif (parseAttrs.contains(AppAttribute.VERSION_CODE)) {\n\t\t\t\tversionCode = Integer.valueOf(manifest.getAttribute(\"android:versionCode\"));\n\t\t\t}\n\t\t\tif (parseAttrs.contains(AppAttribute.VERSION_NAME)) {\n\t\t\t\tversionName = manifest.getAttribute(\"android:versionName\");\n\t\t\t}\n\t\t}\n\t\tif (parseAttrs.contains(AppAttribute.MAIN_ACTIVITY)) {\n\t\t\tmainActivity = getMainActivityName();\n\t\t}\n\t\tif (parseAttrs.contains(AppAttribute.APPLICATION)) {\n\t\t\tapplication = getApplicationName();\n\t\t}\n\n\t\treturn new ApplicationParams(applicationLabel, minSdkVersion, targetSdkVersion, compileSdkVersion,\n\t\t\t\tversionCode, versionName, mainActivity, application);\n\t}\n\n\tprivate String getApplicationLabel() {\n\t\tElement application = (Element) androidManifest.getElementsByTagName(\"application\").item(0);\n\t\tif (application.hasAttribute(\"android:label\")) {\n\t\t\tString appLabelName = application.getAttribute(\"android:label\");\n\t\t\tif (appLabelName.startsWith(\"@string\")) {\n\t\t\t\tif (appStrings == null) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"APPLICATION_LABEL attribute requires non null appStrings\");\n\t\t\t\t}\n\t\t\t\tappLabelName = appLabelName.split(\"/\")[1];\n\t\t\t\tNodeList strings = appStrings.getElementsByTagName(\"string\");\n\n\t\t\t\tfor (int i = 0; i < strings.getLength(); i++) {\n\t\t\t\t\tString stringName = strings.item(i)\n\t\t\t\t\t\t\t.getAttributes()\n\t\t\t\t\t\t\t.getNamedItem(\"name\")\n\t\t\t\t\t\t\t.getNodeValue();\n\n\t\t\t\t\tif (stringName.equals(appLabelName)) {\n\t\t\t\t\t\treturn strings.item(i).getTextContent();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn appLabelName;\n\t\t\t}\n\t\t}\n\n\t\treturn \"UNKNOWN\";\n\t}\n\n\tprivate String getMainActivityName() {\n\t\tString mainActivityName = getMainActivityNameThroughActivityTag();\n\t\tif (mainActivityName == null) {\n\t\t\tmainActivityName = getMainActivityNameThroughActivityAliasTag();\n\t\t}\n\t\treturn mainActivityName;\n\t}\n\n\tprivate String getApplicationName() {\n\t\tElement application = (Element) androidManifest.getElementsByTagName(\"application\").item(0);\n\t\tif (application.hasAttribute(\"android:name\")) {\n\t\t\treturn application.getAttribute(\"android:name\");\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String getMainActivityNameThroughActivityAliasTag() {\n\t\tNodeList activityAliasNodes = androidManifest.getElementsByTagName(\"activity-alias\");\n\t\tfor (int i = 0; i < activityAliasNodes.getLength(); i++) {\n\t\t\tElement activityElement = (Element) activityAliasNodes.item(i);\n\t\t\tif (isMainActivityElement(activityElement)) {\n\t\t\t\treturn activityElement.getAttribute(\"android:targetActivity\");\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String getMainActivityNameThroughActivityTag() {\n\t\tNodeList activityNodes = androidManifest.getElementsByTagName(\"activity\");\n\t\tfor (int i = 0; i < activityNodes.getLength(); i++) {\n\t\t\tElement activityElement = (Element) activityNodes.item(i);\n\t\t\tif (isMainActivityElement(activityElement)) {\n\t\t\t\treturn activityElement.getAttribute(\"android:name\");\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate boolean isMainActivityElement(Element element) {\n\t\tNodeList intentFilterNodes = element.getElementsByTagName(\"intent-filter\");\n\t\tfor (int j = 0; j < intentFilterNodes.getLength(); j++) {\n\t\t\tElement intentFilterElement = (Element) intentFilterNodes.item(j);\n\t\t\tNodeList actionNodes = intentFilterElement.getElementsByTagName(\"action\");\n\t\t\tNodeList categoryNodes = intentFilterElement.getElementsByTagName(\"category\");\n\n\t\t\tboolean isMainAction = false;\n\t\t\tboolean isLauncherCategory = false;\n\n\t\t\tfor (int k = 0; k < actionNodes.getLength(); k++) {\n\t\t\t\tElement actionElement = (Element) actionNodes.item(k);\n\t\t\t\tString actionName = actionElement.getAttribute(\"android:name\");\n\t\t\t\tif (\"android.intent.action.MAIN\".equals(actionName)) {\n\t\t\t\t\tisMainAction = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (int k = 0; k < categoryNodes.getLength(); k++) {\n\t\t\t\tElement categoryElement = (Element) categoryNodes.item(k);\n\t\t\t\tString categoryName = categoryElement.getAttribute(\"android:name\");\n\t\t\t\tif (\"android.intent.category.LAUNCHER\".equals(categoryName)) {\n\t\t\t\t\tisLauncherCategory = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isMainAction && isLauncherCategory) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate Document parseXml(String xmlContent) {\n\t\ttry (InputStream xmlStream = new ByteArrayInputStream(xmlContent.getBytes(StandardCharsets.UTF_8))) {\n\t\t\tDocument document = security.parseXml(xmlStream);\n\t\t\tdocument.getDocumentElement().normalize();\n\t\t\treturn document;\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Can not parse xml content\", e);\n\t\t}\n\t}\n\n\tprivate Document parseAppStrings(ResContainer appStrings) {\n\t\tif (appStrings == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString content = appStrings.getText().getCodeStr();\n\t\treturn parseXml(content);\n\t}\n\n\tprivate Document parseAndroidManifest(ResourceFile androidManifest) {\n\t\tif (androidManifest == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString content = androidManifest.loadContent().getText().getCodeStr();\n\t\treturn parseXml(content);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesMap.java",
    "content": "package jadx.core.utils.android;\n\nimport java.io.InputStream;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n/**\n * Store resources id to name mapping\n */\npublic class AndroidResourcesMap {\n\tprivate static final Map<Integer, String> RES_MAP = loadBundled();\n\n\tpublic static @Nullable String getResName(int resId) {\n\t\treturn RES_MAP.get(resId);\n\t}\n\n\tpublic static Map<Integer, String> getMap() {\n\t\treturn RES_MAP;\n\t}\n\n\tprivate static Map<Integer, String> loadBundled() {\n\t\ttry (InputStream is = AndroidResourcesMap.class.getResourceAsStream(\"/android/res-map.txt\")) {\n\t\t\treturn TextResMapFile.read(is);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to load android resource file (res-map.txt)\", e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java",
    "content": "package jadx.core.utils.android;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeWriter;\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.api.plugins.input.data.annotations.EncodedType;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.core.codegen.ClassGen;\nimport jadx.core.deobf.NameMapper;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.ConstStorage;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.xmlgen.ResourceStorage;\nimport jadx.core.xmlgen.entry.ResourceEntry;\n\n/**\n * Android resources specific handlers\n */\npublic class AndroidResourcesUtils {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(AndroidResourcesUtils.class);\n\n\tprivate AndroidResourcesUtils() {\n\t}\n\n\tpublic static ClassNode searchAppResClass(RootNode root, ResourceStorage resStorage) {\n\t\tString appPackage = root.getAppPackage();\n\t\tString fullName = appPackage != null ? appPackage + \".R\" : \"R\";\n\t\tClassInfo clsInfo = ClassInfo.fromName(root, fullName);\n\t\tClassNode resCls = root.resolveClass(clsInfo);\n\t\tif (resCls != null) {\n\t\t\taddResourceFields(resCls, resStorage, true);\n\t\t\treturn resCls;\n\t\t}\n\t\tLOG.debug(\"Can't find 'R' class in app package: {}\", appPackage);\n\t\tList<ClassNode> candidates = root.searchClassByShortName(\"R\");\n\t\tif (candidates.size() == 1) {\n\t\t\tClassNode resClsCandidate = candidates.get(0);\n\t\t\taddResourceFields(resClsCandidate, resStorage, true);\n\t\t\treturn resClsCandidate;\n\t\t}\n\t\tif (!candidates.isEmpty()) {\n\t\t\tLOG.info(\"Found several 'R' class candidates: {}\", candidates);\n\t\t}\n\t\tLOG.info(\"App 'R' class not found, put all resources ids into : '{}'\", fullName);\n\t\tClassNode rCls = ClassNode.addSyntheticClass(root, clsInfo, AccessFlags.PUBLIC | AccessFlags.FINAL);\n\t\trCls.addInfoComment(\"This class is generated by JADX\");\n\t\taddResourceFields(rCls, resStorage, false);\n\t\treturn rCls;\n\t}\n\n\tpublic static boolean handleAppResField(ICodeWriter code, ClassGen clsGen, ClassInfo declClass) {\n\t\tClassInfo parentClass = declClass.getParentClass();\n\t\tif (parentClass != null && parentClass.getShortName().equals(\"R\")) {\n\t\t\tclsGen.useClass(code, parentClass);\n\t\t\tcode.add('.');\n\t\t\tcode.add(declClass.getAliasShortName());\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Force hex format for Android resources ids\n\t */\n\tpublic static boolean isResourceFieldValue(ClassNode cls, ArgType type) {\n\t\treturn type.equals(ArgType.INT) && isResourceClass(cls);\n\t}\n\n\tpublic static boolean isResourceClass(ClassNode cls) {\n\t\tClassNode parentClass = cls.getParentClass();\n\t\treturn parentClass != null && parentClass.getAlias().equals(\"R\");\n\t}\n\n\tprivate static final class ResClsInfo {\n\t\tprivate final ClassNode typeCls;\n\t\tprivate final Map<String, FieldNode> fieldsMap = new HashMap<>();\n\n\t\tprivate ResClsInfo(ClassNode typeCls) {\n\t\t\tthis.typeCls = typeCls;\n\t\t}\n\n\t\tpublic ClassNode getTypeCls() {\n\t\t\treturn typeCls;\n\t\t}\n\n\t\tpublic Map<String, FieldNode> getFieldsMap() {\n\t\t\treturn fieldsMap;\n\t\t}\n\t}\n\n\tprivate static void addResourceFields(ClassNode resCls, ResourceStorage resStorage, boolean rClsExists) {\n\t\tMap<Integer, FieldNode> resFieldsMap = fillResFieldsMap(resCls);\n\t\tMap<String, ResClsInfo> innerClsMap = new TreeMap<>();\n\t\tif (rClsExists) {\n\t\t\tfor (ClassNode innerClass : resCls.getInnerClasses()) {\n\t\t\t\tResClsInfo innerResCls = new ResClsInfo(innerClass);\n\t\t\t\tinnerClass.getFields().forEach(field -> innerResCls.getFieldsMap().put(field.getName(), field));\n\t\t\t\tinnerClsMap.put(innerClass.getAlias(), innerResCls);\n\t\t\t}\n\t\t}\n\t\tfor (ResourceEntry resource : resStorage.getResources()) {\n\t\t\tString resTypeName = resource.getTypeName();\n\t\t\tString resName = resource.getKeyName().replace('.', '_');\n\n\t\t\tResClsInfo typeClsInfo = innerClsMap.computeIfAbsent(\n\t\t\t\t\tresTypeName,\n\t\t\t\t\tname -> getClassForResType(resCls, rClsExists, name));\n\t\t\ttypeClsInfo.getFieldsMap().computeIfAbsent(resName, name -> {\n\t\t\t\tClassNode typeCls = typeClsInfo.getTypeCls();\n\t\t\t\tFieldInfo rFieldInfo = FieldInfo.from(typeCls.root(), typeCls.getClassInfo(), resName, ArgType.INT);\n\t\t\t\tFieldNode newResField = new FieldNode(typeCls, rFieldInfo,\n\t\t\t\t\t\tAccessFlags.PUBLIC | AccessFlags.STATIC | AccessFlags.FINAL);\n\t\t\t\tnewResField.addAttr(new EncodedValue(EncodedType.ENCODED_INT, resource.getId()));\n\t\t\t\ttypeCls.addField(newResField);\n\t\t\t\tif (rClsExists) {\n\t\t\t\t\tnewResField.addInfoComment(\"Added by JADX\");\n\t\t\t\t}\n\t\t\t\treturn newResField;\n\t\t\t});\n\t\t\tFieldNode fieldNode = resFieldsMap.get(resource.getId());\n\t\t\tif (fieldNode != null\n\t\t\t\t\t&& !fieldNode.getName().equals(resName)\n\t\t\t\t\t&& NameMapper.isValidAndPrintable(resName)\n\t\t\t\t\t&& resCls.root().getArgs().isRenameValid()) {\n\t\t\t\tfieldNode.add(AFlag.DONT_RENAME);\n\t\t\t\tfieldNode.getFieldInfo().setAlias(resName);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static ResClsInfo getClassForResType(ClassNode resCls, boolean rClsExists, String typeName) {\n\t\tRootNode root = resCls.root();\n\t\tString typeClsFullName = resCls.getClassInfo().makeRawFullName() + '$' + typeName;\n\t\tClassInfo clsInfo = ClassInfo.fromName(root, typeClsFullName);\n\t\tClassNode existCls = root.resolveClass(clsInfo);\n\t\tif (existCls != null) {\n\t\t\tResClsInfo resClsInfo = new ResClsInfo(existCls);\n\t\t\texistCls.getFields().forEach(field -> resClsInfo.getFieldsMap().put(field.getName(), field));\n\t\t\treturn resClsInfo;\n\t\t}\n\t\tClassNode newTypeCls = ClassNode.addSyntheticClass(root, clsInfo,\n\t\t\t\tAccessFlags.PUBLIC | AccessFlags.STATIC | AccessFlags.FINAL);\n\t\tif (rClsExists) {\n\t\t\tnewTypeCls.addInfoComment(\"Added by JADX\");\n\t\t}\n\t\treturn new ResClsInfo(newTypeCls);\n\t}\n\n\t@NotNull\n\tprivate static Map<Integer, FieldNode> fillResFieldsMap(ClassNode resCls) {\n\t\tMap<Integer, FieldNode> resFieldsMap = new HashMap<>();\n\t\tConstStorage constStorage = resCls.root().getConstValues();\n\t\tconstStorage.getGlobalConstFields().forEach((key, field) -> {\n\t\t\tif (field.getFieldInfo().getType().equals(ArgType.INT)\n\t\t\t\t\t&& field instanceof FieldNode\n\t\t\t\t\t&& key instanceof Integer) {\n\t\t\t\tFieldNode fldNode = (FieldNode) field;\n\t\t\t\tAccessInfo accessFlags = fldNode.getAccessFlags();\n\t\t\t\tif (accessFlags.isStatic() && accessFlags.isFinal()) {\n\t\t\t\t\tresFieldsMap.put((Integer) key, fldNode);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn resFieldsMap;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/android/AppAttribute.java",
    "content": "package jadx.core.utils.android;\n\npublic enum AppAttribute {\n\tAPPLICATION_LABEL,\n\tMIN_SDK_VERSION,\n\tCOMPILE_SDK_VERSION,\n\tTARGET_SDK_VERSION,\n\tVERSION_CODE,\n\tVERSION_NAME,\n\tMAIN_ACTIVITY,\n\tAPPLICATION,\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/android/ApplicationParams.java",
    "content": "package jadx.core.utils.android;\n\nimport jadx.api.JadxDecompiler;\nimport jadx.api.JavaClass;\n\npublic class ApplicationParams {\n\n\tprivate final String applicationLabel;\n\tprivate final Integer minSdkVersion;\n\tprivate final Integer targetSdkVersion;\n\tprivate final Integer compileSdkVersion;\n\tprivate final Integer versionCode;\n\tprivate final String versionName;\n\tprivate final String mainActivity;\n\tprivate final String application;\n\n\tpublic ApplicationParams(String applicationLabel, Integer minSdkVersion, Integer targetSdkVersion, Integer compileSdkVersion,\n\t\t\tInteger versionCode, String versionName, String mainActivity, String application) {\n\t\tthis.applicationLabel = applicationLabel;\n\t\tthis.minSdkVersion = minSdkVersion;\n\t\tthis.targetSdkVersion = targetSdkVersion;\n\t\tthis.compileSdkVersion = compileSdkVersion;\n\t\tthis.versionCode = versionCode;\n\t\tthis.versionName = versionName;\n\t\tthis.mainActivity = mainActivity;\n\t\tthis.application = application;\n\t}\n\n\tpublic String getApplicationName() {\n\t\treturn applicationLabel;\n\t}\n\n\tpublic Integer getMinSdkVersion() {\n\t\treturn minSdkVersion;\n\t}\n\n\tpublic Integer getTargetSdkVersion() {\n\t\treturn targetSdkVersion;\n\t}\n\n\tpublic Integer getCompileSdkVersion() {\n\t\treturn compileSdkVersion;\n\t}\n\n\tpublic Integer getVersionCode() {\n\t\treturn versionCode;\n\t}\n\n\tpublic String getVersionName() {\n\t\treturn versionName;\n\t}\n\n\tpublic String getMainActivity() {\n\t\treturn mainActivity;\n\t}\n\n\tpublic JavaClass getMainActivityJavaClass(JadxDecompiler decompiler) {\n\t\treturn decompiler.searchJavaClassByAliasFullName(mainActivity);\n\t}\n\n\tpublic String getApplication() {\n\t\treturn application;\n\t}\n\n\tpublic JavaClass getApplicationJavaClass(JadxDecompiler decompiler) {\n\t\treturn decompiler.searchJavaClassByAliasFullName(application);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/android/DataInputDelegate.java",
    "content": "/**\n * Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>\n * <p>\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * <p>\n * http://www.apache.org/licenses/LICENSE-2.0\n * <p>\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage jadx.core.utils.android;\n\nimport java.io.DataInput;\nimport java.io.IOException;\n\n/**\n * @author Ryszard Wiśniewski \"brut.alll@gmail.com\"\n */\npublic abstract class DataInputDelegate implements DataInput {\n\tprotected final DataInput mDelegate;\n\n\tpublic DataInputDelegate(DataInput delegate) {\n\t\tthis.mDelegate = delegate;\n\t}\n\n\tpublic int skipBytes(int n) throws IOException {\n\t\treturn mDelegate.skipBytes(n);\n\t}\n\n\tpublic int readUnsignedShort() throws IOException {\n\t\treturn mDelegate.readUnsignedShort();\n\t}\n\n\tpublic int readUnsignedByte() throws IOException {\n\t\treturn mDelegate.readUnsignedByte();\n\t}\n\n\tpublic String readUTF() throws IOException {\n\t\treturn mDelegate.readUTF();\n\t}\n\n\tpublic short readShort() throws IOException {\n\t\treturn mDelegate.readShort();\n\t}\n\n\tpublic long readLong() throws IOException {\n\t\treturn mDelegate.readLong();\n\t}\n\n\tpublic String readLine() throws IOException {\n\t\treturn mDelegate.readLine();\n\t}\n\n\tpublic int readInt() throws IOException {\n\t\treturn mDelegate.readInt();\n\t}\n\n\tpublic void readFully(byte[] b, int off, int len) throws IOException {\n\t\tmDelegate.readFully(b, off, len);\n\t}\n\n\tpublic void readFully(byte[] b) throws IOException {\n\t\tmDelegate.readFully(b);\n\t}\n\n\tpublic float readFloat() throws IOException {\n\t\treturn mDelegate.readFloat();\n\t}\n\n\tpublic double readDouble() throws IOException {\n\t\treturn mDelegate.readDouble();\n\t}\n\n\tpublic char readChar() throws IOException {\n\t\treturn mDelegate.readChar();\n\t}\n\n\tpublic byte readByte() throws IOException {\n\t\treturn mDelegate.readByte();\n\t}\n\n\tpublic boolean readBoolean() throws IOException {\n\t\treturn mDelegate.readBoolean();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/android/ExtDataInput.java",
    "content": "/**\n * Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>\n * <p>\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * <p>\n * http://www.apache.org/licenses/LICENSE-2.0\n * <p>\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage jadx.core.utils.android;\n\nimport java.io.DataInput;\nimport java.io.DataInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/**\n * @author Ryszard Wiśniewski \"brut.alll@gmail.com\"\n */\npublic class ExtDataInput extends DataInputDelegate {\n\tpublic ExtDataInput(InputStream in) {\n\t\tthis((DataInput) new DataInputStream(in));\n\t}\n\n\tpublic ExtDataInput(DataInput delegate) {\n\t\tsuper(delegate);\n\t}\n\n\tpublic int[] readIntArray(int length) throws IOException {\n\t\tint[] array = new int[length];\n\t\tfor (int i = 0; i < length; i++) {\n\t\t\tarray[i] = readInt();\n\t\t}\n\t\treturn array;\n\t}\n\n\tpublic void skipInt() throws IOException {\n\t\tskipBytes(4);\n\t}\n\n\tpublic void skipCheckInt(int expected) throws IOException {\n\t\tint got = readInt();\n\t\tif (got != expected) {\n\t\t\tthrow new IOException(String.format(\"Expected: 0x%08x, got: 0x%08x\", expected, got));\n\t\t}\n\t}\n\n\tpublic void skipCheckShort(short expected) throws IOException {\n\t\tshort got = readShort();\n\t\tif (got != expected) {\n\t\t\tthrow new IOException(String.format(\"Expected: 0x%08x, got: 0x%08x\", expected, got));\n\t\t}\n\t}\n\n\tpublic void skipCheckByte(byte expected) throws IOException {\n\t\tbyte got = readByte();\n\t\tif (got != expected) {\n\t\t\tthrow new IOException(String.format(\"Expected: 0x%08x, got: 0x%08x\", expected, got));\n\t\t}\n\t}\n\n\tpublic void skipCheckChunkTypeInt(int expected, int possible) throws IOException {\n\t\tint got = readInt();\n\t\tif (got == possible) {\n\t\t\tskipCheckChunkTypeInt(expected, -1);\n\t\t} else if (got != expected) {\n\t\t\tthrow new IOException(String.format(\"Expected: 0x%08x, got: 0x%08x\", expected, got));\n\t\t}\n\t}\n\n\t/**\n\t * The general contract of DataInput doesn't guarantee all the bytes requested will be skipped\n\t * and failure can occur for many reasons. We override this to try harder to skip all the bytes\n\t * requested (this is similar to DataInputStream's wrapper).\n\t */\n\t@Override\n\t@SuppressWarnings(\"InnerAssignment\")\n\tpublic final int skipBytes(int n) throws IOException {\n\t\tint total = 0;\n\t\tint cur;\n\n\t\twhile ((total < n) && ((cur = super.skipBytes(n - total)) > 0)) {\n\t\t\ttotal += cur;\n\t\t}\n\t\treturn total;\n\t}\n\n\tpublic String readNullEndedString(int length, boolean fixed) throws IOException {\n\t\tStringBuilder string = new StringBuilder(16);\n\t\twhile (length-- != 0) {\n\t\t\tshort ch = readShort();\n\t\t\tif (ch == 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tstring.append((char) ch);\n\t\t}\n\t\tif (fixed) {\n\t\t\tskipBytes(length * 2);\n\t\t}\n\t\treturn string.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/android/Res9patchStreamDecoder.java",
    "content": "/**\n * Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>\n * <p>\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * <p>\n * http://www.apache.org/licenses/LICENSE-2.0\n * <p>\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage jadx.core.utils.android;\n\nimport java.awt.image.BufferedImage;\nimport java.io.DataInput;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\nimport javax.imageio.ImageIO;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\n/**\n * @author Ryszard Wiśniewski \"brut.alll@gmail.com\"\n */\npublic class Res9patchStreamDecoder {\n\n\tpublic boolean decode(InputStream in, OutputStream out) {\n\t\ttry {\n\t\t\tBufferedImage im = ImageIO.read(in);\n\t\t\tNinePatch np = getNinePatch(in);\n\t\t\tif (np == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tint w = im.getWidth();\n\t\t\tint h = im.getHeight();\n\n\t\t\tBufferedImage im2 = new BufferedImage(w + 2, h + 2, BufferedImage.TYPE_INT_ARGB);\n\t\t\tim2.createGraphics().drawImage(im, 1, 1, w, h, null);\n\n\t\t\tdrawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight);\n\t\t\tdrawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom);\n\n\t\t\tint[] xDivs = np.xDivs;\n\t\t\tfor (int i = 0; i < xDivs.length - 1; i += 2) {\n\t\t\t\tdrawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]);\n\t\t\t}\n\n\t\t\tint[] yDivs = np.yDivs;\n\t\t\tfor (int i = 0; i < yDivs.length - 1; i += 2) {\n\t\t\t\tdrawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]);\n\t\t\t}\n\n\t\t\tImageIO.write(im2, \"png\", out);\n\t\t\treturn true;\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"9patch image decode error\", e);\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate NinePatch getNinePatch(InputStream in) throws IOException {\n\t\tExtDataInput di = new ExtDataInput(in);\n\t\tif (!find9patchChunk(di)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn NinePatch.decode(di);\n\t}\n\n\tprivate boolean find9patchChunk(DataInput di) throws IOException {\n\t\tdi.skipBytes(8);\n\t\twhile (true) {\n\t\t\tint size;\n\t\t\ttry {\n\t\t\t\tsize = di.readInt();\n\t\t\t} catch (IOException ex) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (di.readInt() == NP_CHUNK_TYPE) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tdi.skipBytes(size + 4);\n\t\t}\n\t}\n\n\tprivate void drawHLine(BufferedImage im, int y, int x1, int x2) {\n\t\tfor (int x = x1; x <= x2; x++) {\n\t\t\tim.setRGB(x, y, NP_COLOR);\n\t\t}\n\t}\n\n\tprivate void drawVLine(BufferedImage im, int x, int y1, int y2) {\n\t\tfor (int y = y1; y <= y2; y++) {\n\t\t\tim.setRGB(x, y, NP_COLOR);\n\t\t}\n\t}\n\n\tprivate static final int NP_CHUNK_TYPE = 0x6e705463; // npTc\n\tprivate static final int NP_COLOR = 0xff000000;\n\n\tprivate static class NinePatch {\n\t\tpublic final int padLeft;\n\t\tpublic final int padRight;\n\t\tpublic final int padTop;\n\t\tpublic final int padBottom;\n\t\tpublic final int[] xDivs;\n\t\tpublic final int[] yDivs;\n\n\t\tpublic NinePatch(int padLeft, int padRight, int padTop, int padBottom,\n\t\t\t\tint[] xDivs, int[] yDivs) {\n\t\t\tthis.padLeft = padLeft;\n\t\t\tthis.padRight = padRight;\n\t\t\tthis.padTop = padTop;\n\t\t\tthis.padBottom = padBottom;\n\t\t\tthis.xDivs = xDivs;\n\t\t\tthis.yDivs = yDivs;\n\t\t}\n\n\t\tpublic static NinePatch decode(ExtDataInput di) throws IOException {\n\t\t\tdi.skipBytes(1);\n\t\t\tbyte numXDivs = di.readByte();\n\t\t\tbyte numYDivs = di.readByte();\n\t\t\tdi.skipBytes(1);\n\t\t\tdi.skipBytes(8);\n\t\t\tint padLeft = di.readInt();\n\t\t\tint padRight = di.readInt();\n\t\t\tint padTop = di.readInt();\n\t\t\tint padBottom = di.readInt();\n\t\t\tdi.skipBytes(4);\n\t\t\tint[] xDivs = di.readIntArray(numXDivs);\n\t\t\tint[] yDivs = di.readIntArray(numYDivs);\n\n\t\t\treturn new NinePatch(padLeft, padRight, padTop, padBottom, xDivs, yDivs);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/android/TextResMapFile.java",
    "content": "package jadx.core.utils.android;\n\nimport java.io.BufferedReader;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class TextResMapFile {\n\tprivate static final int SPLIT_POS = 8;\n\n\tpublic static Map<Integer, String> read(InputStream is) {\n\t\ttry (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {\n\t\t\tMap<Integer, String> resMap = new HashMap<>();\n\t\t\twhile (true) {\n\t\t\t\tString line = br.readLine();\n\t\t\t\tif (line == null) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tparseLine(resMap, line);\n\t\t\t}\n\t\t\treturn resMap;\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to read res-map file\", e);\n\t\t}\n\t}\n\n\tprivate static void parseLine(Map<Integer, String> resMap, String line) {\n\t\tint id = Integer.parseInt(line.substring(0, SPLIT_POS), 16);\n\t\tString name = line.substring(SPLIT_POS + 1);\n\t\tresMap.put(id, name);\n\t}\n\n\tpublic static Map<Integer, String> read(Path resMapFile) {\n\t\ttry (InputStream in = Files.newInputStream(resMapFile)) {\n\t\t\treturn read(in);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to read res-map file\", e);\n\t\t}\n\t}\n\n\tpublic static void write(Path resMapFile, Map<Integer, String> inputResMap) {\n\t\ttry {\n\t\t\tMap<Integer, String> resMap = new TreeMap<>(inputResMap);\n\t\t\tList<String> lines = new ArrayList<>(resMap.size());\n\t\t\tfor (Map.Entry<Integer, String> entry : resMap.entrySet()) {\n\t\t\t\tlines.add(String.format(\"%08x=%s\", entry.getKey(), entry.getValue()));\n\t\t\t}\n\t\t\tFiles.write(resMapFile, lines, StandardCharsets.UTF_8);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to write res-map file\", e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/blocks/BlockPair.java",
    "content": "package jadx.core.utils.blocks;\n\nimport jadx.core.dex.nodes.BlockNode;\n\npublic class BlockPair {\n\tprivate final BlockNode first;\n\tprivate final BlockNode second;\n\n\tpublic BlockPair(BlockNode first, BlockNode second) {\n\t\tthis.first = first;\n\t\tthis.second = second;\n\t}\n\n\tpublic BlockNode getFirst() {\n\t\treturn first;\n\t}\n\n\tpublic BlockNode getSecond() {\n\t\treturn second;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof BlockPair)) {\n\t\t\treturn false;\n\t\t}\n\t\tBlockPair other = (BlockPair) o;\n\t\treturn first.equals(other.first) && second.equals(other.second);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn first.hashCode() + 31 * second.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"(\" + first + \", \" + second + ')';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/blocks/BlockSet.java",
    "content": "package jadx.core.utils.blocks;\n\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.NoSuchElementException;\nimport java.util.Spliterator;\nimport java.util.Spliterators;\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.EmptyBitSet;\n\n/**\n * BlockNode set implementation based on BitSet.\n */\npublic class BlockSet implements Iterable<BlockNode> {\n\n\tpublic static BlockSet empty(MethodNode mth) {\n\t\treturn new BlockSet(mth);\n\t}\n\n\tpublic static BlockSet from(MethodNode mth, Collection<BlockNode> blocks) {\n\t\tBlockSet newBS = new BlockSet(mth);\n\t\tnewBS.addAll(blocks);\n\t\treturn newBS;\n\t}\n\n\tprivate final MethodNode mth;\n\tprivate final BitSet bs;\n\n\tpublic BlockSet(MethodNode mth) {\n\t\tthis.mth = mth;\n\t\tthis.bs = new BitSet(mth.getBasicBlocks().size());\n\t}\n\n\tpublic boolean contains(BlockNode block) {\n\t\treturn bs.get(block.getPos());\n\t}\n\n\tpublic void add(BlockNode block) {\n\t\tbs.set(block.getPos());\n\t}\n\n\tpublic void addAll(Collection<BlockNode> blocks) {\n\t\tblocks.forEach(this::add);\n\t}\n\n\tpublic void addAll(BlockSet otherBlockSet) {\n\t\tbs.or(otherBlockSet.bs);\n\t}\n\n\tpublic void remove(BlockNode block) {\n\t\tbs.clear(block.getPos());\n\t}\n\n\tpublic void remove(Collection<BlockNode> blocks) {\n\t\tblocks.forEach(this::remove);\n\t}\n\n\tpublic boolean addChecked(BlockNode block) {\n\t\tint id = block.getPos();\n\t\tboolean state = bs.get(id);\n\t\tbs.set(id);\n\t\treturn state;\n\t}\n\n\tpublic boolean containsAll(List<BlockNode> blocks) {\n\t\tfor (BlockNode block : blocks) {\n\t\t\tif (!contains(block)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic boolean intersects(List<BlockNode> blocks) {\n\t\tfor (BlockNode block : blocks) {\n\t\t\tif (contains(block)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic BlockSet intersect(List<BlockNode> blocks) {\n\t\tBlockSet input = from(mth, blocks);\n\t\tBlockSet result = new BlockSet(mth);\n\t\tBitSet resultBS = result.bs;\n\t\tresultBS.or(this.bs);\n\t\tresultBS.and(input.bs);\n\t\treturn result;\n\t}\n\n\tpublic boolean isEmpty() {\n\t\treturn bs.isEmpty();\n\t}\n\n\tpublic int size() {\n\t\treturn bs.cardinality();\n\t}\n\n\tpublic void remove() {\n\t\tbs.clear();\n\t}\n\n\tpublic @Nullable BlockNode getOne() {\n\t\tif (bs.cardinality() == 1) {\n\t\t\treturn mth.getBasicBlocks().get(bs.nextSetBit(0));\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic BlockNode getFirst() {\n\t\treturn mth.getBasicBlocks().get(bs.nextSetBit(0));\n\t}\n\n\t@Override\n\tpublic void forEach(Consumer<? super BlockNode> consumer) {\n\t\tif (bs.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tList<BlockNode> blocks = mth.getBasicBlocks();\n\t\tfor (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {\n\t\t\tconsumer.accept(blocks.get(i));\n\t\t}\n\t}\n\n\t@Override\n\tpublic @NotNull Iterator<BlockNode> iterator() {\n\t\treturn new BlockSetIterator(bs, size(), mth.getBasicBlocks());\n\t}\n\n\t@Override\n\tpublic Spliterator<BlockNode> spliterator() {\n\t\tint size = size();\n\t\tBlockSetIterator iterator = new BlockSetIterator(bs, size, mth.getBasicBlocks());\n\t\treturn Spliterators.spliterator(iterator, size, Spliterator.ORDERED | Spliterator.DISTINCT);\n\t}\n\n\tpublic List<BlockNode> toList() {\n\t\tif (bs == null || bs == EmptyBitSet.EMPTY) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tint size = bs.cardinality();\n\t\tif (size == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<BlockNode> mthBlocks = mth.getBasicBlocks();\n\t\tList<BlockNode> blocks = new ArrayList<>(size);\n\t\tfor (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {\n\t\t\tblocks.add(mthBlocks.get(i));\n\t\t}\n\t\treturn blocks;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn toList().toString();\n\t}\n\n\tprivate static final class BlockSetIterator implements Iterator<BlockNode> {\n\t\tprivate final BitSet bs;\n\t\tprivate final int size;\n\t\tprivate final List<BlockNode> blocks;\n\n\t\tprivate int cursor;\n\t\tprivate int start;\n\n\t\tpublic BlockSetIterator(BitSet bs, int size, List<BlockNode> blocks) {\n\t\t\tthis.bs = bs;\n\t\t\tthis.size = size;\n\t\t\tthis.blocks = blocks;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean hasNext() {\n\t\t\treturn cursor != size;\n\t\t}\n\n\t\t@Override\n\t\tpublic BlockNode next() {\n\t\t\tint pos = bs.nextSetBit(start);\n\t\t\tif (pos == -1) {\n\t\t\t\tthrow new NoSuchElementException();\n\t\t\t}\n\t\t\tstart = pos + 1;\n\t\t\tcursor++;\n\t\t\treturn blocks.get(pos);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/blocks/DFSIteration.java",
    "content": "package jadx.core.utils.blocks;\n\nimport java.util.ArrayDeque;\nimport java.util.Deque;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic class DFSIteration {\n\tprivate final Function<BlockNode, List<BlockNode>> nextFunc;\n\tprivate final Deque<BlockNode> queue;\n\tprivate final BlockSet visited;\n\n\tpublic DFSIteration(MethodNode mth, BlockNode startBlock, Function<BlockNode, List<BlockNode>> next) {\n\t\tnextFunc = next;\n\t\tqueue = new ArrayDeque<>();\n\t\tvisited = new BlockSet(mth);\n\t\tqueue.addLast(startBlock);\n\t\tvisited.add(startBlock);\n\t}\n\n\tpublic @Nullable BlockNode next() {\n\t\tBlockNode current = queue.pollLast();\n\t\tif (current == null) {\n\t\t\treturn null;\n\t\t}\n\t\tList<BlockNode> nextBlocks = nextFunc.apply(current);\n\t\tint count = nextBlocks.size();\n\t\tfor (int i = count - 1; i >= 0; i--) { // to preserve order in queue\n\t\t\tBlockNode next = nextBlocks.get(i);\n\t\t\tif (!visited.addChecked(next)) {\n\t\t\t\tqueue.addLast(next);\n\t\t\t}\n\t\t}\n\t\treturn current;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/exceptions/CodegenException.java",
    "content": "package jadx.core.utils.exceptions;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic class CodegenException extends JadxException {\n\n\tprivate static final long serialVersionUID = 39344288912966824L;\n\n\tpublic CodegenException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic CodegenException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n\tpublic CodegenException(ClassNode mth, String msg) {\n\t\tsuper(mth, msg, null);\n\t}\n\n\tpublic CodegenException(ClassNode mth, String msg, Throwable th) {\n\t\tsuper(mth, msg, th);\n\t}\n\n\tpublic CodegenException(MethodNode mth, String msg) {\n\t\tsuper(mth, msg, null);\n\t}\n\n\tpublic CodegenException(MethodNode mth, String msg, Throwable th) {\n\t\tsuper(mth, msg, th);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/exceptions/DecodeException.java",
    "content": "package jadx.core.utils.exceptions;\n\nimport jadx.core.dex.nodes.MethodNode;\n\npublic class DecodeException extends JadxException {\n\n\tprivate static final long serialVersionUID = -6611189094923499636L;\n\n\tpublic DecodeException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic DecodeException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n\tpublic DecodeException(MethodNode mth, String msg) {\n\t\tsuper(mth, msg, null);\n\t}\n\n\tpublic DecodeException(MethodNode mth, String msg, Throwable th) {\n\t\tsuper(mth, msg, th);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/exceptions/InvalidDataException.java",
    "content": "package jadx.core.utils.exceptions;\n\npublic class InvalidDataException extends JadxRuntimeException {\n\tpublic InvalidDataException(String message) {\n\t\tsuper(message);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/exceptions/JadxArgsValidateException.java",
    "content": "package jadx.core.utils.exceptions;\n\npublic class JadxArgsValidateException extends RuntimeException {\n\n\tprivate static final long serialVersionUID = -7457621776087311909L;\n\n\tpublic JadxArgsValidateException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic JadxArgsValidateException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/exceptions/JadxException.java",
    "content": "package jadx.core.utils.exceptions;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.ErrorsCounter;\n\npublic class JadxException extends Exception {\n\n\tprivate static final long serialVersionUID = 3577449089978463557L;\n\n\tpublic JadxException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic JadxException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n\tpublic JadxException(ClassNode cls, String msg, Throwable th) {\n\t\tsuper(ErrorsCounter.formatMsg(cls, msg), th);\n\t}\n\n\tpublic JadxException(MethodNode mth, String msg, Throwable th) {\n\t\tsuper(ErrorsCounter.formatMsg(mth, msg), th);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/exceptions/JadxOverflowException.java",
    "content": "package jadx.core.utils.exceptions;\n\npublic class JadxOverflowException extends JadxRuntimeException {\n\n\tprivate static final long serialVersionUID = 2568659798680154204L;\n\n\tpublic JadxOverflowException(String message) {\n\t\tsuper(message);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/exceptions/JadxRuntimeException.java",
    "content": "package jadx.core.utils.exceptions;\n\npublic class JadxRuntimeException extends RuntimeException {\n\tprivate static final long serialVersionUID = -7410848445429898248L;\n\n\tpublic JadxRuntimeException() {\n\t\tsuper();\n\t}\n\n\tpublic JadxRuntimeException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic JadxRuntimeException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/files/FileUtils.java",
    "content": "package jadx.core.utils.files;\n\nimport java.io.BufferedInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.Closeable;\nimport java.io.DataOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.FileAlreadyExistsException;\nimport java.nio.file.FileVisitOption;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.Files;\nimport java.nio.file.NoSuchFileException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.nio.file.SimpleFileVisitor;\nimport java.nio.file.StandardCopyOption;\nimport java.nio.file.StandardOpenOption;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.nio.file.attribute.FileTime;\nimport java.security.MessageDigest;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Predicate;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarOutputStream;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.plugins.files.IJadxFilesGetter;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class FileUtils {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(FileUtils.class);\n\n\tpublic static final int READ_BUFFER_SIZE = 8 * 1024;\n\tprivate static final int MAX_FILENAME_LENGTH = 128;\n\tprivate static final int MAX_UNIQUE_ID_LENGTH = 3;\n\n\tpublic static final String JADX_TMP_INSTANCE_PREFIX = \"jadx-instance-\";\n\tpublic static final String JADX_TMP_PREFIX = \"jadx-tmp-\";\n\n\tprivate static Path tempRootDir = createTempRootDir();\n\n\tprivate FileUtils() {\n\t\t// utility class\n\t}\n\n\tpublic static synchronized Path updateTempRootDir(Path newTempRootDir) {\n\t\ttry {\n\t\t\tmakeDirs(newTempRootDir);\n\t\t\tPath dir = Files.createTempDirectory(newTempRootDir, JADX_TMP_INSTANCE_PREFIX);\n\t\t\ttempRootDir = dir;\n\t\t\tdir.toFile().deleteOnExit();\n\t\t\treturn dir;\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to update temp root directory\", e);\n\t\t}\n\t}\n\n\tprivate static Path createTempRootDir() {\n\t\ttry {\n\t\t\tPath dir = Files.createTempDirectory(JADX_TMP_INSTANCE_PREFIX);\n\t\t\tdir.toFile().deleteOnExit();\n\t\t\treturn dir;\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to create temp root directory\", e);\n\t\t}\n\t}\n\n\tpublic static List<Path> listFiles(Path dir) {\n\t\ttry (Stream<Path> files = Files.list(dir)) {\n\t\t\treturn files.collect(Collectors.toList());\n\t\t} catch (IOException e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to list files in directory: \" + dir, e);\n\t\t}\n\t}\n\n\tpublic static List<Path> listFiles(Path dir, Predicate<? super Path> filter) {\n\t\ttry (Stream<Path> files = Files.list(dir)) {\n\t\t\treturn files.filter(filter).collect(Collectors.toList());\n\t\t} catch (IOException e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to list files in directory: \" + dir, e);\n\t\t}\n\t}\n\n\tpublic static List<Path> expandDirs(List<Path> paths) {\n\t\tList<Path> files = new ArrayList<>(paths.size());\n\t\tfor (Path path : paths) {\n\t\t\tif (Files.isDirectory(path)) {\n\t\t\t\texpandDir(path, files);\n\t\t\t} else {\n\t\t\t\tfiles.add(path);\n\t\t\t}\n\t\t}\n\t\treturn files;\n\t}\n\n\tprivate static void expandDir(Path dir, List<Path> files) {\n\t\ttry (Stream<Path> walk = Files.walk(dir, FileVisitOption.FOLLOW_LINKS)) {\n\t\t\twalk.filter(Files::isRegularFile).forEach(files::add);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to list files in directory: {}\", dir, e);\n\t\t}\n\t}\n\n\tpublic static void addFileToJar(JarOutputStream jar, File source, String entryName) throws IOException {\n\t\ttry (BufferedInputStream in = new BufferedInputStream(new FileInputStream(source))) {\n\t\t\tJarEntry entry = new JarEntry(entryName);\n\t\t\tentry.setTime(source.lastModified());\n\t\t\tjar.putNextEntry(entry);\n\n\t\t\tcopyStream(in, jar);\n\t\t\tjar.closeEntry();\n\t\t}\n\t}\n\n\tpublic static void makeDirsForFile(Path path) {\n\t\tif (path != null) {\n\t\t\tmakeDirs(path.toAbsolutePath().getParent().toFile());\n\t\t}\n\t}\n\n\tpublic static void makeDirsForFile(File file) {\n\t\tif (file != null) {\n\t\t\tmakeDirs(file.getParentFile());\n\t\t}\n\t}\n\n\tprivate static final Object MKDIR_SYNC = new Object();\n\n\tpublic static void makeDirs(@Nullable File dir) {\n\t\tif (dir != null) {\n\t\t\tsynchronized (MKDIR_SYNC) {\n\t\t\t\tif (!dir.mkdirs() && !dir.isDirectory()) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Can't create directory \" + dir);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void makeDirs(@Nullable Path dir) {\n\t\tif (dir != null) {\n\t\t\tmakeDirs(dir.toFile());\n\t\t}\n\t}\n\n\tpublic static void deleteFileIfExists(Path filePath) throws IOException {\n\t\tFiles.deleteIfExists(filePath);\n\t}\n\n\tpublic static boolean deleteDir(File dir) {\n\t\tdeleteDir(dir.toPath());\n\t\treturn true;\n\t}\n\n\tpublic static void deleteDir(Path dir) {\n\t\tdeleteDir(dir, false);\n\t}\n\n\tpublic static void deleteDirIfExists(Path dir) {\n\t\tif (Files.exists(dir)) {\n\t\t\ttry {\n\t\t\t\tdeleteDir(dir);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to delete dir: {}\", dir.toAbsolutePath(), e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void deleteDir(Path dir, boolean keepRootDir) {\n\t\ttry {\n\t\t\tList<Path> files = new ArrayList<>();\n\t\t\tList<Path> directories = new ArrayList<>();\n\t\t\tFiles.walkFileTree(dir, Collections.emptySet(), Integer.MAX_VALUE, new SimpleFileVisitor<>() {\n\t\t\t\t@Override\n\t\t\t\tpublic @NotNull FileVisitResult visitFile(@NotNull Path file, @NotNull BasicFileAttributes attrs) {\n\t\t\t\t\tfiles.add(file);\n\t\t\t\t\treturn FileVisitResult.CONTINUE;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic @NotNull FileVisitResult postVisitDirectory(@NotNull Path directory, IOException exc) {\n\t\t\t\t\tdirectories.add(directory);\n\t\t\t\t\treturn FileVisitResult.CONTINUE;\n\t\t\t\t}\n\t\t\t});\n\t\t\t// delete files in parallel\n\t\t\tif (!files.isEmpty()) {\n\t\t\t\tfiles.parallelStream().forEach(path -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tFiles.delete(path);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLOG.warn(\"Failed to delete file {}\", path.toAbsolutePath(), e);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\t// after all files are deleted, remove empty directories\n\t\t\tif (keepRootDir) {\n\t\t\t\t// root dir always last\n\t\t\t\tListUtils.removeLast(directories);\n\t\t\t}\n\t\t\tfor (Path directory : directories) {\n\t\t\t\ttry {\n\t\t\t\t\tFiles.delete(directory);\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tLOG.warn(\"Failed to delete directory {}\", directory.toAbsolutePath(), e);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to delete directory \" + dir, e);\n\t\t}\n\t}\n\n\tpublic static void clearTempRootDir() {\n\t\tif (Files.isDirectory(tempRootDir)) {\n\t\t\tclearDir(tempRootDir);\n\t\t}\n\t}\n\n\tpublic static void clearDir(Path clearDir) {\n\t\ttry {\n\t\t\tdeleteDir(clearDir, true);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to clear directory \" + clearDir, e);\n\t\t}\n\t}\n\n\t/**\n\t * Deprecated.\n\t * Migrate to {@link IJadxFilesGetter} from jadx args to get temp dir\n\t */\n\t@Deprecated\n\tpublic static Path createTempDir(String prefix) {\n\t\ttry {\n\t\t\tPath dir = Files.createTempDirectory(tempRootDir, prefix);\n\t\t\tdir.toFile().deleteOnExit();\n\t\t\treturn dir;\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to create temp directory with suffix: \" + prefix, e);\n\t\t}\n\t}\n\n\t/**\n\t * Deprecated.\n\t * Migrate to {@link IJadxFilesGetter} from jadx args to get temp dir\n\t */\n\t@Deprecated\n\tpublic static Path createTempFile(String suffix) {\n\t\ttry {\n\t\t\tPath path = Files.createTempFile(tempRootDir, JADX_TMP_PREFIX, suffix);\n\t\t\tpath.toFile().deleteOnExit();\n\t\t\treturn path;\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to create temp file with suffix: \" + suffix, e);\n\t\t}\n\t}\n\n\t/**\n\t * Deprecated.\n\t * Prefer {@link IJadxFilesGetter} from jadx args to get temp dir\n\t */\n\t@Deprecated\n\tpublic static Path createTempFileNoDelete(String suffix) {\n\t\ttry {\n\t\t\treturn Files.createTempFile(Files.createTempDirectory(\"jadx-persist\"), \"jadx-\", suffix);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to create temp file with suffix: \" + suffix, e);\n\t\t}\n\t}\n\n\t/**\n\t * Deprecated.\n\t * Migrate to {@link IJadxFilesGetter} from jadx args to get temp dir\n\t */\n\t@Deprecated\n\tpublic static Path createTempFileNonPrefixed(String fileName) {\n\t\ttry {\n\t\t\tPath path = Files.createFile(tempRootDir.resolve(fileName));\n\t\t\tpath.toFile().deleteOnExit();\n\t\t\treturn path;\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to create non-prefixed temp file: \" + fileName, e);\n\t\t}\n\t}\n\n\tpublic static void copyStream(InputStream input, OutputStream output) throws IOException {\n\t\tbyte[] buffer = new byte[READ_BUFFER_SIZE];\n\t\twhile (true) {\n\t\t\tint count = input.read(buffer);\n\t\t\tif (count == -1) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\toutput.write(buffer, 0, count);\n\t\t}\n\t}\n\n\tpublic static byte[] streamToByteArray(InputStream input) throws IOException {\n\t\treturn input.readAllBytes();\n\t}\n\n\tpublic static String streamToString(InputStream input) throws IOException {\n\t\treturn new String(streamToByteArray(input), StandardCharsets.UTF_8);\n\t}\n\n\tpublic static void close(Closeable c) {\n\t\tif (c == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tc.close();\n\t\t} catch (IOException e) {\n\t\t\tLOG.error(\"Close exception for {}\", c, e);\n\t\t}\n\t}\n\n\tpublic static void writeFile(Path file, String data) throws IOException {\n\t\tFileUtils.makeDirsForFile(file);\n\t\tFiles.writeString(file, data, StandardCharsets.UTF_8,\n\t\t\t\tStandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);\n\t}\n\n\tpublic static void writeFile(Path file, byte[] data) throws IOException {\n\t\tFileUtils.makeDirsForFile(file);\n\t\tFiles.write(file, data, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);\n\t}\n\n\tpublic static void writeFile(Path file, InputStream is) throws IOException {\n\t\tFileUtils.makeDirsForFile(file);\n\t\tFiles.copy(is, file, StandardCopyOption.REPLACE_EXISTING);\n\t}\n\n\tpublic static String readFile(Path textFile) throws IOException {\n\t\treturn Files.readString(textFile);\n\t}\n\n\tpublic static boolean renameFile(Path sourcePath, Path targetPath) {\n\t\ttry {\n\t\t\tFiles.move(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);\n\t\t\treturn true;\n\t\t} catch (NoSuchFileException e) {\n\t\t\tLOG.error(\"File to rename not found {}\", sourcePath, e);\n\t\t} catch (FileAlreadyExistsException e) {\n\t\t\tLOG.error(\"File with that name already exists {}\", targetPath, e);\n\t\t} catch (IOException e) {\n\t\t\tLOG.error(\"Error renaming file {}\", e.getMessage(), e);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@NotNull\n\tpublic static File prepareFile(File file) {\n\t\tFile saveFile = cutFileName(file);\n\t\tmakeDirsForFile(saveFile);\n\t\treturn saveFile;\n\t}\n\n\tpublic static File cutFileName(File file) {\n\t\tString name = file.getName();\n\t\tif (name.length() <= MAX_FILENAME_LENGTH) {\n\t\t\treturn file;\n\t\t}\n\n\t\tString uniqueID = String.valueOf(name.hashCode());\n\t\tif (uniqueID.length() > MAX_UNIQUE_ID_LENGTH) {\n\t\t\tuniqueID = uniqueID.substring(0, MAX_UNIQUE_ID_LENGTH);\n\t\t}\n\t\tint dotIndex = name.indexOf('.');\n\t\tint lengthOfSuffix = name.length() - dotIndex;\n\t\tint cutAt = MAX_FILENAME_LENGTH - lengthOfSuffix - uniqueID.length() - 1;\n\t\tif (cutAt <= 0) {\n\t\t\tname = name.substring(0, MAX_FILENAME_LENGTH - 1);\n\t\t} else {\n\t\t\tname = name.substring(0, cutAt) + uniqueID + name.substring(dotIndex);\n\t\t}\n\t\treturn new File(file.getParentFile(), name);\n\t}\n\n\tprivate static final byte[] HEX_ARRAY = \"0123456789abcdef\".getBytes(StandardCharsets.US_ASCII);\n\n\tpublic static String bytesToHex(byte[] bytes) {\n\t\tif (bytes == null || bytes.length == 0) {\n\t\t\treturn \"\";\n\t\t}\n\t\tbyte[] hexChars = new byte[bytes.length * 2];\n\t\tfor (int j = 0; j < bytes.length; j++) {\n\t\t\tint v = bytes[j] & 0xFF;\n\t\t\thexChars[j * 2] = HEX_ARRAY[v >>> 4];\n\t\t\thexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];\n\t\t}\n\t\treturn new String(hexChars, StandardCharsets.UTF_8);\n\t}\n\n\t/**\n\t * Zero padded hex string for first byte\n\t */\n\tpublic static String byteToHex(int value) {\n\t\tint v = value & 0xFF;\n\t\tbyte[] hexChars = new byte[] { HEX_ARRAY[v >>> 4], HEX_ARRAY[v & 0x0F] };\n\t\treturn new String(hexChars, StandardCharsets.US_ASCII);\n\t}\n\n\t/**\n\t * Zero padded hex string for int value\n\t */\n\tpublic static String intToHex(int value) {\n\t\tbyte[] hexChars = new byte[8];\n\t\tint v = value;\n\t\tfor (int i = 7; i >= 0; i--) {\n\t\t\thexChars[i] = HEX_ARRAY[v & 0x0F];\n\t\t\tv >>>= 4;\n\t\t}\n\t\treturn new String(hexChars, StandardCharsets.US_ASCII);\n\t}\n\n\tprivate static final byte[] ZIP_FILE_MAGIC = { 0x50, 0x4B, 0x03, 0x04 };\n\n\tpublic static boolean isZipFile(File file) {\n\t\ttry (InputStream is = new FileInputStream(file)) {\n\t\t\tint len = ZIP_FILE_MAGIC.length;\n\t\t\tbyte[] headers = new byte[len];\n\t\t\tint read = is.read(headers);\n\t\t\treturn read == len && Arrays.equals(headers, ZIP_FILE_MAGIC);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to read zip file: {}\", file.getAbsolutePath(), e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic static String getPathBaseName(Path file) {\n\t\tString fileName = file.getFileName().toString();\n\t\tint extEndIndex = fileName.lastIndexOf('.');\n\t\tif (extEndIndex == -1) {\n\t\t\treturn fileName;\n\t\t}\n\t\treturn fileName.substring(0, extEndIndex);\n\t}\n\n\tpublic static boolean hasExtension(Path path, String extension) {\n\t\tString fileName = path.getFileName().toString();\n\t\treturn fileName.toLowerCase().endsWith(extension);\n\t}\n\n\tpublic static File toFile(String path) {\n\t\tif (path == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new File(path);\n\t}\n\n\tpublic static List<Path> toPaths(List<File> files) {\n\t\treturn files.stream().map(File::toPath).collect(Collectors.toList());\n\t}\n\n\tpublic static List<Path> toPaths(File[] files) {\n\t\treturn Stream.of(files).map(File::toPath).collect(Collectors.toList());\n\t}\n\n\tpublic static List<Path> toPathsWithTrim(File[] files) {\n\t\treturn Stream.of(files).map(FileUtils::toPathWithTrim).collect(Collectors.toList());\n\t}\n\n\tpublic static Path toPathWithTrim(File file) {\n\t\treturn toPathWithTrim(file.getPath());\n\t}\n\n\tpublic static Path toPathWithTrim(String file) {\n\t\treturn Path.of(file.trim());\n\t}\n\n\tpublic static List<Path> fileNamesToPaths(List<String> fileNames) {\n\t\treturn fileNames.stream().map(Paths::get).collect(Collectors.toList());\n\t}\n\n\tpublic static List<File> toFiles(List<Path> paths) {\n\t\treturn paths.stream().map(Path::toFile).collect(Collectors.toList());\n\t}\n\n\tpublic static String md5Sum(String str) {\n\t\treturn md5Sum(str.getBytes(StandardCharsets.UTF_8));\n\t}\n\n\tpublic static String md5Sum(byte[] data) {\n\t\ttry {\n\t\t\tMessageDigest md = MessageDigest.getInstance(\"MD5\");\n\t\t\tmd.update(data);\n\t\t\treturn bytesToHex(md.digest());\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to build hash\", e);\n\t\t}\n\t}\n\n\t/**\n\t * Hash timestamps of input files\n\t */\n\tpublic static String buildInputsHash(List<Path> inputPaths) {\n\t\ttry (ByteArrayOutputStream bout = new ByteArrayOutputStream();\n\t\t\t\tDataOutputStream data = new DataOutputStream(bout)) {\n\t\t\tList<Path> inputFiles = FileUtils.expandDirs(inputPaths);\n\t\t\tCollections.sort(inputFiles);\n\t\t\tdata.write(inputPaths.size());\n\t\t\tdata.write(inputFiles.size());\n\t\t\tfor (Path inputFile : inputFiles) {\n\t\t\t\tFileTime modifiedTime = Files.getLastModifiedTime(inputFile);\n\t\t\t\tdata.writeLong(modifiedTime.toMillis());\n\t\t\t}\n\t\t\treturn FileUtils.md5Sum(bout.toByteArray());\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to build hash for inputs\", e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/input/InsnDataUtils.java",
    "content": "package jadx.core.utils.input;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.ICallSite;\nimport jadx.api.plugins.input.data.IMethodHandle;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.input.data.annotations.EncodedType;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.api.plugins.input.insns.InsnIndexType;\nimport jadx.api.plugins.input.insns.custom.ICustomPayload;\n\npublic class InsnDataUtils {\n\n\t@Nullable\n\tpublic static ICallSite getCallSite(InsnData insnData) {\n\t\tif (insnData.getIndexType() != InsnIndexType.CALL_SITE) {\n\t\t\treturn null;\n\t\t}\n\t\tICustomPayload payload = insnData.getPayload();\n\t\tif (payload != null) {\n\t\t\treturn (ICallSite) payload;\n\t\t}\n\t\treturn insnData.getIndexAsCallSite();\n\t}\n\n\t@Nullable\n\tpublic static IMethodRef getMethodRef(InsnData insnData) {\n\t\tif (insnData.getIndexType() != InsnIndexType.METHOD_REF) {\n\t\t\treturn null;\n\t\t}\n\t\tICustomPayload payload = insnData.getPayload();\n\t\tif (payload != null) {\n\t\t\treturn (IMethodRef) payload;\n\t\t}\n\t\treturn insnData.getIndexAsMethod();\n\t}\n\n\t@Nullable\n\tpublic static IMethodHandle getMethodHandleAt(ICallSite callSite, int argNum) {\n\t\tif (callSite == null) {\n\t\t\treturn null;\n\t\t}\n\t\tList<EncodedValue> values = callSite.getValues();\n\t\tif (argNum < values.size()) {\n\t\t\tEncodedValue encodedValue = values.get(argNum);\n\t\t\tif (encodedValue.getType() == EncodedType.ENCODED_METHOD_HANDLE) {\n\t\t\t\treturn (IMethodHandle) encodedValue.getValue();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/log/LogUtils.java",
    "content": "package jadx.core.utils.log;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.regex.Pattern;\n\n/**\n * Escape input from untrusted source before pass to logger.\n * Suggested by CodeQL: https://codeql.github.com/codeql-query-help/java/java-log-injection/\n */\npublic class LogUtils {\n\n\t/**\n\t * We replace everything except alphanumeric characters, underscore, dots, colon, semicolon, comma,\n\t * spaces, minus\n\t */\n\tprivate static final Pattern REPLACE_PATTERN = Pattern.compile(\"[^\\\\w\\\\.:;, -]\");\n\n\tpublic static String escape(String input) {\n\t\tif (input == null) {\n\t\t\treturn \"null\";\n\t\t}\n\n\t\treturn REPLACE_PATTERN.matcher(input).replaceAll(\".\");\n\t}\n\n\tpublic static String escape(byte[] input) {\n\t\tif (input == null) {\n\t\t\treturn \"null\";\n\t\t}\n\t\treturn escape(new String(input, StandardCharsets.UTF_8));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/utils/tasks/TaskExecutor.java",
    "content": "package jadx.core.utils.tasks;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.utils.tasks.ITaskExecutor;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class TaskExecutor implements ITaskExecutor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(TaskExecutor.class);\n\n\tprivate enum ExecType {\n\t\tPARALLEL,\n\t\tSEQUENTIAL,\n\t}\n\n\tprivate static final class ExecStage {\n\t\tprivate final ExecType type;\n\t\tprivate final List<? extends Runnable> tasks;\n\n\t\tprivate ExecStage(ExecType type, List<? extends Runnable> tasks) {\n\t\t\tthis.type = type;\n\t\t\tthis.tasks = tasks;\n\t\t}\n\n\t\tpublic ExecType getType() {\n\t\t\treturn type;\n\t\t}\n\n\t\tpublic List<? extends Runnable> getTasks() {\n\t\t\treturn tasks;\n\t\t}\n\t}\n\n\tprivate final List<ExecStage> stages = new ArrayList<>();\n\tprivate final AtomicInteger threadsCount = new AtomicInteger(JadxArgs.DEFAULT_THREADS_COUNT);\n\tprivate final AtomicInteger progress = new AtomicInteger(0);\n\tprivate final AtomicBoolean running = new AtomicBoolean(false);\n\tprivate final AtomicBoolean terminating = new AtomicBoolean(false);\n\tprivate final Object executorSync = new Object();\n\tprivate @Nullable ExecutorService executor;\n\tprivate int tasksCount = 0;\n\tprivate @Nullable Error terminateError;\n\n\t@Override\n\tpublic void addParallelTasks(List<? extends Runnable> parallelTasks) {\n\t\tif (parallelTasks.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\ttasksCount += parallelTasks.size();\n\t\tstages.add(new ExecStage(ExecType.PARALLEL, parallelTasks));\n\t}\n\n\t@Override\n\tpublic void addSequentialTasks(List<? extends Runnable> seqTasks) {\n\t\tif (seqTasks.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\ttasksCount += seqTasks.size();\n\t\tstages.add(new ExecStage(ExecType.SEQUENTIAL, seqTasks));\n\t}\n\n\t@Override\n\tpublic void addSequentialTask(Runnable seqTask) {\n\t\taddSequentialTasks(Collections.singletonList(seqTask));\n\t}\n\n\t@Override\n\tpublic int getThreadsCount() {\n\t\treturn threadsCount.get();\n\t}\n\n\t@Override\n\tpublic void setThreadsCount(int count) {\n\t\tthreadsCount.set(count);\n\t}\n\n\t@Override\n\tpublic int getTasksCount() {\n\t\treturn tasksCount;\n\t}\n\n\t@Override\n\tpublic int getProgress() {\n\t\treturn progress.get();\n\t}\n\n\t@Override\n\tpublic void execute() {\n\t\tsynchronized (executorSync) {\n\t\t\tif (running.get() || executor != null) {\n\t\t\t\tthrow new IllegalStateException(\"Already executing\");\n\t\t\t}\n\t\t\texecutor = Executors.newFixedThreadPool(1, Utils.simpleThreadFactory(\"task-s\"));\n\t\t\trunning.set(true);\n\t\t\tterminating.set(false);\n\t\t\tprogress.set(0);\n\t\t\texecutor.execute(this::runStages);\n\t\t}\n\t}\n\n\tprivate void stopExecution() {\n\t\tsynchronized (executorSync) {\n\t\t\trunning.set(false);\n\t\t\tterminating.set(true);\n\t\t\tif (executor != null) {\n\t\t\t\texecutor.shutdown();\n\t\t\t\texecutor = null;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void awaitTermination() {\n\t\tExecutorService activeExecutor = executor;\n\t\tif (activeExecutor != null && running.get()) {\n\t\t\tawaitExecutorTermination(activeExecutor);\n\t\t}\n\t\tError error = terminateError;\n\t\tif (error != null) {\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void terminate() {\n\t\tterminating.set(true);\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\tprivate void terminateWithError(Error error) {\n\t\tif (terminating.get()) {\n\t\t\treturn;\n\t\t}\n\t\tterminateError = error;\n\t\tterminate();\n\t\texecutor.shutdownNow();\n\t}\n\n\t@Override\n\tpublic boolean isTerminating() {\n\t\treturn terminating.get();\n\t}\n\n\t@Override\n\tpublic boolean isRunning() {\n\t\treturn running.get();\n\t}\n\n\t@Override\n\tpublic @Nullable ExecutorService getInternalExecutor() {\n\t\treturn executor;\n\t}\n\n\tprivate void runStages() {\n\t\ttry {\n\t\t\tfor (ExecStage stage : stages) {\n\t\t\t\tint threads = Math.min(stage.getTasks().size(), threadsCount.get());\n\t\t\t\tif (stage.getType() == ExecType.SEQUENTIAL || threads == 1) {\n\t\t\t\t\tfor (Runnable task : stage.getTasks()) {\n\t\t\t\t\t\twrapTask(task);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tExecutorService parallelExecutor = Executors.newFixedThreadPool(\n\t\t\t\t\t\t\tthreads, Utils.simpleThreadFactory(\"task-p\"));\n\t\t\t\t\tfor (Runnable task : stage.getTasks()) {\n\t\t\t\t\t\tparallelExecutor.execute(() -> wrapTask(task));\n\t\t\t\t\t}\n\t\t\t\t\tparallelExecutor.shutdown();\n\t\t\t\t\tawaitExecutorTermination(parallelExecutor);\n\t\t\t\t}\n\t\t\t\tif (terminating.get()) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tstopExecution();\n\t\t}\n\t}\n\n\tprivate void wrapTask(Runnable task) {\n\t\tif (terminating.get()) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\ttask.run();\n\t\t\tprogress.incrementAndGet();\n\t\t} catch (Error e) {\n\t\t\tterminateWithError(e);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Unhandled task exception:\", e);\n\t\t}\n\t}\n\n\tpublic static void awaitExecutorTermination(ExecutorService executor) {\n\t\ttry {\n\t\t\tboolean complete = executor.awaitTermination(10, TimeUnit.DAYS);\n\t\t\tif (!complete) {\n\t\t\t\tthrow new JadxRuntimeException(\"Executor timeout\");\n\t\t\t}\n\t\t} catch (InterruptedException e) {\n\t\t\tThread.currentThread().interrupt();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ICodeWriter;\nimport jadx.api.ResourcesLoader;\nimport jadx.core.dex.info.ConstStorage;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.android.AndroidResourcesMap;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.xmlgen.entry.ValuesParser;\n\npublic class BinaryXMLParser extends CommonBinaryParser {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(BinaryXMLParser.class);\n\n\tprivate final RootNode rootNode;\n\tprivate final ManifestAttributes manifestAttributes;\n\tprivate final boolean attrNewLine;\n\n\tprivate final Map<Integer, String> resNames;\n\tprivate Map<String, String> nsMap;\n\tprivate Set<String> nsMapGenerated;\n\tprivate Set<String> definedNamespaces;\n\tprivate final Map<String, String> tagAttrDeobfNames = new HashMap<>();\n\n\tprivate ICodeWriter writer;\n\tprivate BinaryXMLStrings strings;\n\tprivate String currentTag = \"ERROR\";\n\tprivate boolean firstElement;\n\tprivate ValuesParser valuesParser;\n\tprivate boolean isLastEnd = true;\n\tprivate boolean isOneLine = true;\n\tprivate int namespaceDepth = 0;\n\tprivate @Nullable int[] resourceIds;\n\tprivate String appPackageName;\n\n\tprivate Map<String, ClassNode> classNameCache;\n\n\tpublic BinaryXMLParser(RootNode rootNode) {\n\t\tthis.rootNode = rootNode;\n\t\tthis.manifestAttributes = rootNode.initManifestAttributes();\n\t\tthis.attrNewLine = !rootNode.getArgs().isSkipXmlPrettyPrint();\n\t\ttry {\n\t\t\tConstStorage constStorage = rootNode.getConstValues();\n\t\t\tresNames = constStorage.getResourcesNames();\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"BinaryXMLParser init error\", e);\n\t\t}\n\t}\n\n\tpublic synchronized ICodeInfo parse(InputStream inputStream) throws IOException {\n\t\tresourceIds = null;\n\t\tis = new ParserStream(inputStream);\n\t\tif (!isBinaryXml()) {\n\t\t\treturn ResourcesLoader.loadToCodeWriter(is);\n\t\t}\n\t\tnsMapGenerated = new HashSet<>();\n\t\tnsMap = new HashMap<>();\n\t\tdefinedNamespaces = new HashSet<>();\n\t\twriter = rootNode.makeCodeWriter();\n\t\twriter.add(\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\");\n\t\tfirstElement = true;\n\t\tdecode();\n\t\tnsMap = null;\n\t\tdefinedNamespaces = null;\n\t\tICodeInfo codeInfo = writer.finish();\n\t\tthis.classNameCache = null; // reset class name cache\n\t\treturn codeInfo;\n\t}\n\n\tprivate boolean isBinaryXml() throws IOException {\n\t\tis.mark(4);\n\t\tint v = is.readInt16(); // version\n\t\tint h = is.readInt16(); // header size\n\t\t// Some APK Manifest.xml the version is 0\n\t\tif (h == 0x0008) {\n\t\t\treturn true;\n\t\t}\n\t\tis.reset();\n\t\treturn false;\n\t}\n\n\tvoid decode() throws IOException {\n\t\tint size = is.readInt32();\n\t\twhile (is.getPos() < size) {\n\t\t\tint type = is.readInt16();\n\t\t\tswitch (type) {\n\t\t\t\tcase RES_NULL_TYPE:\n\t\t\t\t\t// NullType is just doing nothing\n\t\t\t\t\tbreak;\n\t\t\t\tcase RES_STRING_POOL_TYPE:\n\t\t\t\t\tstrings = parseStringPoolNoType();\n\t\t\t\t\tvaluesParser = new ValuesParser(strings, resNames);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RES_XML_RESOURCE_MAP_TYPE:\n\t\t\t\t\tparseResourceMap();\n\t\t\t\t\tbreak;\n\t\t\t\tcase RES_XML_START_NAMESPACE_TYPE:\n\t\t\t\t\tparseNameSpace();\n\t\t\t\t\tbreak;\n\t\t\t\tcase RES_XML_CDATA_TYPE:\n\t\t\t\t\tparseCData();\n\t\t\t\t\tbreak;\n\t\t\t\tcase RES_XML_END_NAMESPACE_TYPE:\n\t\t\t\t\tparseNameSpaceEnd();\n\t\t\t\t\tbreak;\n\t\t\t\tcase RES_XML_START_ELEMENT_TYPE:\n\t\t\t\t\tparseElement();\n\t\t\t\t\tbreak;\n\t\t\t\tcase RES_XML_END_ELEMENT_TYPE:\n\t\t\t\t\tparseElementEnd();\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tif (namespaceDepth == 0) {\n\t\t\t\t\t\t// skip padding on file end\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tdie(\"Type: 0x\" + Integer.toHexString(type) + \" not yet implemented\");\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void parseResourceMap() throws IOException {\n\t\tif (is.readInt16() != 0x8) {\n\t\t\tdie(\"Header size of resmap is not 8!\");\n\t\t}\n\t\tint size = is.readInt32();\n\t\tint len = (size - 8) / 4;\n\t\tresourceIds = new int[len];\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tresourceIds[i] = is.readInt32();\n\t\t}\n\t}\n\n\tprivate void parseNameSpace() throws IOException {\n\t\tint headerSize = is.readInt16();\n\t\tif (headerSize > 0x10) {\n\t\t\tLOG.warn(\"Invalid namespace header\");\n\t\t} else if (headerSize < 0x10) {\n\t\t\tdie(\"NAMESPACE header is not 0x10 big\");\n\t\t}\n\t\tint size = is.readInt32();\n\t\tif (size > 0x18) {\n\t\t\tLOG.warn(\"Invalid namespace size\");\n\t\t} else if (size < 0x18) {\n\t\t\tdie(\"NAMESPACE header chunk is not 0x18 big\");\n\t\t}\n\n\t\tint beginLineNumber = is.readInt32();\n\t\tint comment = is.readInt32();\n\t\tint beginPrefix = is.readInt32();\n\t\tint beginURI = is.readInt32();\n\t\tis.skip(headerSize - 0x10);\n\n\t\tString nsKey = getString(beginURI);\n\t\tString nsValue = getString(beginPrefix);\n\t\tif (StringUtils.notBlank(nsKey) && !nsMap.containsValue(nsValue)) {\n\t\t\tnsMap.putIfAbsent(nsKey, nsValue);\n\t\t}\n\t\tnamespaceDepth++;\n\t}\n\n\tprivate void parseNameSpaceEnd() throws IOException {\n\t\tint headerSize = is.readInt16();\n\t\tif (headerSize > 0x10) {\n\t\t\tLOG.warn(\"Invalid namespace end\");\n\t\t} else if (headerSize < 0x10) {\n\t\t\tdie(\"NAMESPACE end is not 0x10 big\");\n\t\t}\n\t\tint dataSize = is.readInt32();\n\t\tif (dataSize != 0x18) {\n\t\t\tLOG.warn(\"Invalid namespace end size\");\n\t\t}\n\t\tint endLineNumber = is.readInt32();\n\t\tint comment = is.readInt32();\n\t\tint endPrefix = is.readInt32();\n\t\tint endURI = is.readInt32();\n\t\tis.skip(headerSize - 0x10);\n\t\tnamespaceDepth--;\n\n\t\tString nsKey = getString(endURI);\n\t\tString nsValue = getString(endPrefix);\n\t\tif (StringUtils.notBlank(nsKey) && !nsMap.containsValue(nsValue)) {\n\t\t\tnsMap.putIfAbsent(nsKey, nsValue);\n\t\t}\n\t}\n\n\tprivate void parseCData() throws IOException {\n\t\tif (is.readInt16() != 0x10) {\n\t\t\tdie(\"CDATA header is not 0x10\");\n\t\t}\n\t\tif (is.readInt32() != 0x1C) {\n\t\t\tdie(\"CDATA header chunk is not 0x1C\");\n\t\t}\n\t\tint lineNumber = is.readInt32();\n\t\tis.skip(4);\n\n\t\tint strIndex = is.readInt32();\n\t\tString str = getString(strIndex);\n\t\tif (!isLastEnd) {\n\t\t\tisLastEnd = true;\n\t\t\twriter.add('>');\n\t\t}\n\t\twriter.attachSourceLine(lineNumber);\n\t\tString escapedStr = StringUtils.escapeXML(str);\n\t\twriter.add(escapedStr);\n\n\t\tlong size = is.readInt16();\n\t\tis.skip(size - 2);\n\t}\n\n\tprivate void parseElement() throws IOException {\n\t\tif (firstElement) {\n\t\t\tfirstElement = false;\n\t\t} else {\n\t\t\twriter.incIndent();\n\t\t}\n\t\tif (is.readInt16() != 0x10) {\n\t\t\tdie(\"ELEMENT HEADER SIZE is not 0x10\");\n\t\t}\n\t\t// TODO: Check element chunk size\n\t\tlong startPos = is.getPos();\n\t\tint elementSize = is.readInt32();\n\t\tint elementBegLineNumber = is.readInt32();\n\t\tint comment = is.readInt32();\n\t\tint startNS = is.readInt32();\n\t\tint startNSName = is.readInt32(); // actually is elementName...\n\t\tif (!isLastEnd && !\"ERROR\".equals(currentTag)) {\n\t\t\twriter.add('>');\n\t\t}\n\t\tisOneLine = true;\n\t\tisLastEnd = false;\n\t\tcurrentTag = deobfClassName(getString(startNSName));\n\t\tcurrentTag = getValidTagAttributeName(currentTag);\n\t\twriter.startLine('<').add(currentTag);\n\t\twriter.attachSourceLine(elementBegLineNumber);\n\t\tint attributeStart = is.readInt16();\n\t\tif (attributeStart != 0x14) {\n\t\t\tdie(\"startNS's attributeStart is not 0x14\");\n\t\t}\n\t\tint attributeSize = is.readInt16();\n\t\tif (attributeSize < 0x14) {\n\t\t\tdie(\"startNS's attributeSize is less than 0x14\");\n\t\t}\n\n\t\tint attributeCount = is.readInt16();\n\t\tint idIndex = is.readInt16();\n\t\tint classIndex = is.readInt16();\n\t\tint styleIndex = is.readInt16();\n\t\tif (\"manifest\".equals(currentTag) || definedNamespaces.size() != nsMap.size()) {\n\t\t\tfor (Map.Entry<String, String> entry : nsMap.entrySet()) {\n\t\t\t\tif (!definedNamespaces.contains(entry.getKey())) {\n\t\t\t\t\tdefinedNamespaces.add(entry.getKey());\n\t\t\t\t\tString nsValue = getValidTagAttributeName(entry.getValue());\n\t\t\t\t\twriter.add(\" xmlns\");\n\t\t\t\t\tif (nsValue != null && !nsValue.trim().isEmpty()) {\n\t\t\t\t\t\twriter.add(':');\n\t\t\t\t\t\twriter.add(nsValue);\n\t\t\t\t\t}\n\t\t\t\t\twriter.add(\"=\\\"\").add(StringUtils.escapeXML(entry.getKey())).add('\"');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tSet<String> attrCache = new HashSet<>();\n\t\tboolean attrNewLine = attributeCount != 1 && this.attrNewLine;\n\t\tfor (int i = 0; i < attributeCount; i++) {\n\t\t\tparseAttribute(i, attrNewLine, attrCache, attributeSize);\n\t\t}\n\t\tlong endPos = is.getPos();\n\t\tif (endPos - startPos + 0x4 < elementSize) {\n\t\t\tis.skip(elementSize - (endPos - startPos + 0x4));\n\t\t}\n\t}\n\n\tprivate void parseAttribute(int i, boolean newLine, Set<String> attrCache, int attributeSize) throws IOException {\n\t\tint attributeNS = is.readInt32();\n\t\tint attributeName = is.readInt32();\n\t\tint attributeRawValue = is.readInt32();\n\t\tis.skip(3);\n\t\tint attrValDataType = is.readInt8();\n\t\tint attrValData = is.readInt32();\n\n\t\tis.skip(attributeSize - 0x14);\n\n\t\tString shortNsName = null;\n\t\tif (attributeNS != -1) {\n\t\t\tshortNsName = getAttributeNS(attributeNS, newLine);\n\t\t}\n\t\tString attrName = getValidTagAttributeName(getAttributeName(attributeName));\n\t\tString attrFullName = shortNsName != null ? shortNsName + \":\" + attrName : attrName;\n\t\t// do not dump duplicated values\n\t\tif (XmlDeobf.isDuplicatedAttr(attrFullName, attrCache)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (newLine) {\n\t\t\twriter.startLine().addIndent();\n\t\t} else {\n\t\t\twriter.add(' ');\n\t\t}\n\t\twriter.add(attrFullName).add(\"=\\\"\");\n\t\tString decodedAttr = manifestAttributes.decode(attrFullName, attrValData);\n\t\tif (decodedAttr != null) {\n\t\t\tmemorizePackageName(attrName, decodedAttr);\n\t\t\tif (isDeobfCandidateAttr(attrFullName)) {\n\t\t\t\tdecodedAttr = deobfClassName(decodedAttr);\n\t\t\t}\n\t\t\tattachClassNode(writer, attrName, decodedAttr);\n\t\t\twriter.add(StringUtils.escapeXML(decodedAttr));\n\t\t} else {\n\t\t\tdecodeAttribute(attributeNS, attrValDataType, attrValData,\n\t\t\t\t\tattrFullName);\n\t\t}\n\t\tif (shortNsName != null && shortNsName.equals(\"android\")) {\n\t\t\tif (attrName.equals(\"pathData\")) {\n\t\t\t\trootNode.getGradleInfoStorage().setVectorPathData(true);\n\t\t\t} else if (attrName.equals(\"fillType\")) {\n\t\t\t\trootNode.getGradleInfoStorage().setVectorFillType(true);\n\t\t\t}\n\t\t}\n\t\twriter.add('\"');\n\t}\n\n\tprivate String getAttributeNS(int attributeNS, boolean newLine) {\n\t\tString attrUrl = getString(attributeNS);\n\t\tif (attrUrl == null || attrUrl.isEmpty()) {\n\t\t\tif (isResInternalId(attributeNS)) {\n\t\t\t\treturn null;\n\t\t\t} else {\n\t\t\t\tattrUrl = ANDROID_NS_URL;\n\t\t\t}\n\t\t}\n\t\tString attrName = nsMap.get(attrUrl);\n\t\tif (attrName == null) {\n\t\t\tattrName = generateNameForNS(attrUrl, newLine);\n\t\t}\n\t\treturn attrName;\n\t}\n\n\tprivate String generateNameForNS(String attrUrl, boolean newLine) {\n\t\tString attrName;\n\t\tif (ANDROID_NS_URL.equals(attrUrl)) {\n\t\t\tattrName = ANDROID_NS_VALUE;\n\t\t\tnsMap.put(ANDROID_NS_URL, attrName);\n\t\t} else {\n\t\t\tfor (int i = 1;; i++) {\n\t\t\t\tattrName = \"ns\" + i;\n\t\t\t\tif (!nsMapGenerated.contains(attrName) && !nsMap.containsValue(attrName)) {\n\t\t\t\t\tnsMapGenerated.add(attrName);\n\t\t\t\t\t// do not add generated value to nsMap\n\t\t\t\t\t// because attrUrl might be used in a neighbor element, but never defined\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (newLine) {\n\t\t\twriter.startLine().addIndent();\n\t\t} else {\n\t\t\twriter.add(' ');\n\t\t}\n\t\twriter.add(\"xmlns:\").add(attrName).add(\"=\\\"\").add(attrUrl).add(\"\\\" \");\n\t\treturn attrName;\n\t}\n\n\tprivate String getAttributeName(int id) {\n\t\t// As the outcome of https://github.com/skylot/jadx/issues/1208\n\t\t// Android seems to favor entries from AndroidResMap and only if\n\t\t// there is no entry uses the values form the XML string pool\n\t\tif (resourceIds != null && 0 <= id && id < resourceIds.length) {\n\t\t\tint resId = resourceIds[id];\n\t\t\tString str = AndroidResourcesMap.getResName(resId);\n\t\t\tif (str != null) {\n\t\t\t\t// cut type before /\n\t\t\t\tint typeEnd = str.indexOf('/');\n\t\t\t\tif (typeEnd != -1) {\n\t\t\t\t\treturn str.substring(typeEnd + 1);\n\t\t\t\t}\n\t\t\t\treturn str;\n\t\t\t}\n\t\t}\n\n\t\tString str = getString(id);\n\t\tif (str == null || str.isEmpty()) {\n\t\t\treturn \"NOT_FOUND_0x\" + Integer.toHexString(id);\n\t\t}\n\t\treturn str;\n\t}\n\n\tprivate String getString(int strId) {\n\t\tif (0 <= strId && strId < strings.size()) {\n\t\t\treturn strings.get(strId);\n\t\t}\n\t\treturn \"NOT_FOUND_STR_0x\" + Integer.toHexString(strId);\n\t}\n\n\tprivate void decodeAttribute(int attributeNS, int attrValDataType, int attrValData, String attrFullName) {\n\t\tif (attrValDataType == TYPE_REFERENCE) {\n\t\t\t// reference custom processing\n\t\t\tString resName = resNames.get(attrValData);\n\t\t\tif (resName != null) {\n\t\t\t\twriter.add('@');\n\t\t\t\tif (resName.startsWith(\"id/\")) {\n\t\t\t\t\twriter.add('+');\n\t\t\t\t}\n\t\t\t\twriter.add(resName);\n\t\t\t} else {\n\t\t\t\tString androidResName = AndroidResourcesMap.getResName(attrValData);\n\t\t\t\tif (androidResName != null) {\n\t\t\t\t\twriter.add(\"@android:\").add(androidResName);\n\t\t\t\t} else if (attrValData == 0) {\n\t\t\t\t\twriter.add(\"@null\");\n\t\t\t\t} else {\n\t\t\t\t\twriter.add(\"0x\").add(Integer.toHexString(attrValData));\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tString str;\n\t\t\ttry {\n\t\t\t\tstr = valuesParser.decodeValue(attrValDataType, attrValData);\n\t\t\t} catch (JadxRuntimeException e) {\n\t\t\t\tLOG.error(\"Failed to decode attribute value of \\\"{}\\\"\", attrFullName, e);\n\t\t\t\tstr = null;\n\t\t\t}\n\t\t\tmemorizePackageName(attrFullName, str);\n\t\t\tif (isDeobfCandidateAttr(attrFullName)) {\n\t\t\t\tstr = deobfClassName(str);\n\t\t\t}\n\t\t\tattachClassNode(writer, attrFullName, str);\n\t\t\twriter.add(str != null ? StringUtils.escapeXML(str) : \"null\");\n\t\t}\n\t}\n\n\tprivate void parseElementEnd() throws IOException {\n\t\tif (is.readInt16() != 0x10) {\n\t\t\tdie(\"ELEMENT END header is not 0x10\");\n\t\t}\n\t\tif (is.readInt32() != 0x18) {\n\t\t\tdie(\"ELEMENT END header chunk is not 0x18 big\");\n\t\t}\n\t\tint endLineNumber = is.readInt32();\n\t\tint comment = is.readInt32();\n\t\tint elementNS = is.readInt32();\n\t\tint elementNameId = is.readInt32();\n\t\tString elemName = deobfClassName(getString(elementNameId));\n\t\telemName = getValidTagAttributeName(elemName);\n\t\tif (currentTag.equals(elemName) && isOneLine && !isLastEnd) {\n\t\t\twriter.add(\"/>\");\n\t\t} else {\n\t\t\twriter.startLine(\"</\");\n\t\t\twriter.attachSourceLine(endLineNumber);\n\t\t\t// if (elementNS != -1) {\n\t\t\t// writer.add(getString(elementNS)).add(':');\n\t\t\t// }\n\t\t\twriter.add(elemName).add('>');\n\t\t}\n\t\tisLastEnd = true;\n\t\tif (writer.getIndent() != 0) {\n\t\t\twriter.decIndent();\n\t\t}\n\t}\n\n\tprivate String getValidTagAttributeName(String originalName) {\n\t\tif (XMLChar.isValidName(originalName)) {\n\t\t\treturn originalName;\n\t\t}\n\t\tif (tagAttrDeobfNames.containsKey(originalName)) {\n\t\t\treturn tagAttrDeobfNames.get(originalName);\n\t\t}\n\t\tString generated;\n\t\tdo {\n\t\t\tgenerated = generateTagAttrName();\n\t\t} while (tagAttrDeobfNames.containsValue(generated));\n\t\ttagAttrDeobfNames.put(originalName, generated);\n\t\treturn generated;\n\t}\n\n\tprivate static String generateTagAttrName() {\n\t\tfinal int length = 6;\n\t\tRandom r = new Random();\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (int i = 1; i <= length; i++) {\n\t\t\tsb.append((char) (r.nextInt(26) + 'a'));\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tprivate void attachClassNode(ICodeWriter writer, String attrFullName, String clsName) {\n\t\tif (!writer.isMetadataSupported()) {\n\t\t\treturn;\n\t\t}\n\t\tif (clsName == null || !attrFullName.equals(\"android:name\")) {\n\t\t\treturn;\n\t\t}\n\t\tString clsFullName;\n\t\tif (clsName.startsWith(\".\")) {\n\t\t\tclsFullName = appPackageName + clsName;\n\t\t} else {\n\t\t\tclsFullName = clsName;\n\t\t}\n\t\tif (classNameCache == null) {\n\t\t\tclassNameCache = rootNode.buildFullAliasClassCache();\n\t\t}\n\t\tClassNode classNode = classNameCache.get(clsFullName);\n\t\tif (classNode != null) {\n\t\t\twriter.attachAnnotation(classNode);\n\t\t}\n\t}\n\n\tprivate String deobfClassName(String className) {\n\t\tString newName = XmlDeobf.deobfClassName(rootNode, className, appPackageName);\n\t\tif (newName != null) {\n\t\t\treturn newName;\n\t\t}\n\t\treturn className;\n\t}\n\n\tprivate boolean isDeobfCandidateAttr(String attrFullName) {\n\t\treturn \"android:name\".equals(attrFullName);\n\t}\n\n\tprivate void memorizePackageName(String attrFullName, String attrValue) {\n\t\tif (\"manifest\".equals(currentTag) && \"package\".equals(attrFullName)) {\n\t\t\tappPackageName = attrValue;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLStrings.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class BinaryXMLStrings {\n\tpublic static final String INVALID_STRING_PLACEHOLDER = \"⟨STRING_DECODE_ERROR⟩\";\n\tprivate final int stringCount;\n\n\tprivate final long stringsStart;\n\n\tprivate final ByteBuffer buffer;\n\n\tprivate final boolean isUtf8;\n\n\t// This cache include strings that have been overridden by the deobfuscator.\n\tprivate final Map<Integer, String> cache = new HashMap<>();\n\n\tpublic BinaryXMLStrings() {\n\t\tstringCount = 0;\n\t\tstringsStart = 0;\n\t\tbuffer = ByteBuffer.allocate(0);\n\t\tbuffer.order(ByteOrder.LITTLE_ENDIAN);\n\t\tisUtf8 = false;\n\t}\n\n\tpublic BinaryXMLStrings(int stringCount, long stringsStart, byte[] buffer, boolean isUtf8) {\n\t\tthis.stringCount = stringCount;\n\t\tthis.stringsStart = stringsStart;\n\t\tthis.buffer = ByteBuffer.wrap(buffer);\n\t\tthis.buffer.order(ByteOrder.LITTLE_ENDIAN);\n\t\tthis.isUtf8 = isUtf8;\n\t}\n\n\tpublic String get(int id) {\n\t\tString cached = cache.get(id);\n\t\tif (cached != null) {\n\t\t\treturn cached;\n\t\t}\n\n\t\tif (id * 4 >= buffer.limit() - 3) {\n\t\t\treturn INVALID_STRING_PLACEHOLDER;\n\t\t}\n\n\t\tint off = buffer.getInt(id * 4);\n\t\tif (off < 0) {\n\t\t\t// read unsigned offset value is larger than Integer.MAX_VALUE\n\t\t\t// In reality this should only happen in obfuscated APKs with invalid offsets\n\t\t\treturn INVALID_STRING_PLACEHOLDER;\n\t\t}\n\t\tlong offset = stringsStart + off;\n\t\tString extracted;\n\t\tif (isUtf8) {\n\t\t\textracted = extractString8(this.buffer.array(), (int) offset);\n\t\t} else {\n\t\t\t// don't trust specified string length, read until \\0\n\t\t\t// stringsOffset can be same for different indexes\n\t\t\textracted = extractString16(this.buffer.array(), (int) offset);\n\t\t}\n\t\tcache.put(id, extracted);\n\t\treturn extracted;\n\t}\n\n\tpublic void put(int id, String content) {\n\t\tcache.put(id, content);\n\t}\n\n\tpublic int size() {\n\t\treturn this.stringCount;\n\t}\n\n\tprivate static String extractString8(byte[] strArray, int offset) {\n\t\tif (offset >= strArray.length) {\n\t\t\treturn INVALID_STRING_PLACEHOLDER;\n\t\t}\n\t\tint start = offset + skipStrLen8(strArray, offset);\n\t\tint len = strArray[start++];\n\t\tif (len == 0) {\n\t\t\treturn \"\";\n\t\t}\n\t\tif ((len & 0x80) != 0) {\n\t\t\tlen = (len & 0x7F) << 8 | strArray[start++] & 0xFF;\n\t\t}\n\t\tbyte[] arr = Arrays.copyOfRange(strArray, start, start + len);\n\t\treturn new String(arr, ParserStream.STRING_CHARSET_UTF8);\n\t}\n\n\tprivate static String extractString16(byte[] strArray, int offset) {\n\t\tif (offset + 2 >= strArray.length) {\n\t\t\treturn INVALID_STRING_PLACEHOLDER;\n\t\t}\n\n\t\tint len = strArray.length;\n\t\tint start = offset + skipStrLen16(strArray, offset);\n\t\tint end = start;\n\t\twhile (true) {\n\t\t\tif (end + 1 >= len) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (strArray[end] == 0 && strArray[end + 1] == 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tend += 2;\n\t\t}\n\t\tbyte[] arr = Arrays.copyOfRange(strArray, start, end);\n\t\treturn new String(arr, ParserStream.STRING_CHARSET_UTF16);\n\t}\n\n\tprivate static int skipStrLen8(byte[] strArray, int offset) {\n\t\treturn (strArray[offset] & 0x80) == 0 ? 1 : 2;\n\t}\n\n\tprivate static int skipStrLen16(byte[] strArray, int offset) {\n\t\treturn (strArray[offset + 1] & 0x80) == 0 ? 2 : 4;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/CommonBinaryParser.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.io.IOException;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class CommonBinaryParser extends ParserConstants {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CommonBinaryParser.class);\n\n\tprotected ParserStream is;\n\n\tprotected BinaryXMLStrings parseStringPool() throws IOException {\n\t\tis.checkInt16(RES_STRING_POOL_TYPE, \"String pool expected\");\n\t\treturn parseStringPoolNoType();\n\t}\n\n\tprotected BinaryXMLStrings parseStringPoolNoType() throws IOException {\n\t\tlong start = is.getPos() - 2;\n\t\tint headerSize = is.readInt16();\n\t\tif (headerSize != 0x1c) {\n\t\t\tLOG.warn(\"Unexpected string pool header size: 0x{}, expected: 0x1C\", Integer.toHexString(headerSize));\n\t\t}\n\t\tlong size = is.readUInt32();\n\t\tlong chunkEnd = start + size;\n\n\t\treturn parseStringPoolNoSize(start, chunkEnd);\n\t}\n\n\tprotected BinaryXMLStrings parseStringPoolNoSize(long start, long chunkEnd) throws IOException {\n\t\tint stringCount = is.readInt32();\n\t\tint styleCount = is.readInt32();\n\t\tint flags = is.readInt32();\n\t\tlong stringsStart = is.readInt32();\n\t\tlong stylesStart = is.readInt32();\n\n\t\t// Correct the offset of actual strings, as the header is already read.\n\t\tstringsStart = stringsStart - (is.getPos() - start);\n\t\tbyte[] buffer = is.readInt8Array((int) (chunkEnd - is.getPos()));\n\t\tis.checkPos(chunkEnd, \"Expected strings pool end\");\n\n\t\treturn new BinaryXMLStrings(\n\t\t\t\tstringCount,\n\t\t\t\tstringsStart,\n\t\t\t\tbuffer,\n\t\t\t\t(flags & UTF8_FLAG) != 0);\n\t}\n\n\tprotected void die(String message) throws IOException {\n\t\tthrow new IOException(\"Decode error: \" + message\n\t\t\t\t+ \", position: 0x\" + Long.toHexString(is.getPos()));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/IResTableParser.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic interface IResTableParser {\n\n\tvoid decode(InputStream inputStream) throws IOException;\n\n\tResContainer decodeFiles();\n\n\tResourceStorage getResStorage();\n\n\tBinaryXMLStrings getStrings();\n\n\tdefault void setBaseFileName(String fileName) {\n\t\t// optional\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/ManifestAttributes.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.NamedNodeMap;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\n\nimport jadx.api.security.IJadxSecurity;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.xmlgen.entry.RawNamedValue;\nimport jadx.core.xmlgen.entry.ResourceEntry;\nimport jadx.core.xmlgen.entry.ValuesParser;\n\n// TODO: move to Android specific module!\n\n/**\n * Load and store Android Manifest attributes specification\n */\npublic class ManifestAttributes {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ManifestAttributes.class);\n\n\tprivate static final String ATTR_XML = \"/android/attrs.xml\";\n\tprivate static final String MANIFEST_ATTR_XML = \"/android/attrs_manifest.xml\";\n\n\tprivate enum MAttrType {\n\t\tENUM, FLAG\n\t}\n\n\tprivate static class MAttr {\n\t\tprivate final MAttrType type;\n\t\tprivate final Map<Long, String> values = new LinkedHashMap<>();\n\n\t\tpublic MAttr(MAttrType type) {\n\t\t\tthis.type = type;\n\t\t}\n\n\t\tpublic MAttrType getType() {\n\t\t\treturn type;\n\t\t}\n\n\t\tpublic Map<Long, String> getValues() {\n\t\t\treturn values;\n\t\t}\n\n\t\tpublic void addValue(long key, String value) {\n\t\t\tvalues.put(key, value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"[\" + type + \", \" + values + ']';\n\t\t}\n\t}\n\n\tprivate final IJadxSecurity security;\n\n\t/**\n\t * Map containing default Android resource attribute definitions.\n\t * Keys are Android attribute names (e.g., \"android:layout_width\"),\n\t * and values are their corresponding {@link MAttr} objects.\n\t */\n\tprivate final Map<String, MAttr> attrMap = new HashMap<>();\n\tprivate final Map<String, MAttr> appAttrMap = new HashMap<>();\n\n\tpublic ManifestAttributes(IJadxSecurity security) {\n\t\tthis.security = security;\n\t\tparseAll();\n\t}\n\n\tprivate void parseAll() {\n\t\tparse(loadXML(ATTR_XML));\n\t\tparse(loadXML(MANIFEST_ATTR_XML));\n\t\tLOG.debug(\"Loaded android attributes count: {}\", attrMap.size());\n\t}\n\n\tprivate Document loadXML(String xml) {\n\t\tDocument doc;\n\t\ttry (InputStream xmlStream = ManifestAttributes.class.getResourceAsStream(xml)) {\n\t\t\tif (xmlStream == null) {\n\t\t\t\tthrow new JadxRuntimeException(xml + \" not found in classpath\");\n\t\t\t}\n\t\t\tdoc = security.parseXml(xmlStream);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Xml load error, file: \" + xml, e);\n\t\t}\n\t\treturn doc;\n\t}\n\n\tprivate void parse(Document doc) {\n\t\tNodeList nodeList = doc.getChildNodes();\n\t\tfor (int count = 0; count < nodeList.getLength(); count++) {\n\t\t\tNode node = nodeList.item(count);\n\t\t\tif (node.getNodeType() == Node.ELEMENT_NODE\n\t\t\t\t\t&& node.hasChildNodes()) {\n\t\t\t\tparseAttrList(node.getChildNodes());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void parseAttrList(NodeList nodeList) {\n\t\tfor (int count = 0; count < nodeList.getLength(); count++) {\n\t\t\tNode tempNode = nodeList.item(count);\n\t\t\tif (tempNode.getNodeType() == Node.ELEMENT_NODE\n\t\t\t\t\t&& tempNode.hasAttributes()\n\t\t\t\t\t&& tempNode.hasChildNodes()) {\n\t\t\t\tString name = null;\n\t\t\t\tNamedNodeMap nodeMap = tempNode.getAttributes();\n\t\t\t\tfor (int i = 0; i < nodeMap.getLength(); i++) {\n\t\t\t\t\tNode node = nodeMap.item(i);\n\t\t\t\t\tif (node.getNodeName().equals(\"name\")) {\n\t\t\t\t\t\tname = node.getNodeValue();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (name != null && tempNode.getNodeName().equals(\"attr\")) {\n\t\t\t\t\tparseValues(name, tempNode.getChildNodes());\n\t\t\t\t} else {\n\t\t\t\t\tparseAttrList(tempNode.getChildNodes());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void parseValues(String name, NodeList nodeList) {\n\t\tMAttr attr = null;\n\t\tfor (int count = 0; count < nodeList.getLength(); count++) {\n\t\t\tNode tempNode = nodeList.item(count);\n\t\t\tif (tempNode.getNodeType() == Node.ELEMENT_NODE\n\t\t\t\t\t&& tempNode.hasAttributes()) {\n\t\t\t\tif (attr == null) {\n\t\t\t\t\tif (tempNode.getNodeName().equals(\"enum\")) {\n\t\t\t\t\t\tattr = new MAttr(MAttrType.ENUM);\n\t\t\t\t\t} else if (tempNode.getNodeName().equals(\"flag\")) {\n\t\t\t\t\t\tattr = new MAttr(MAttrType.FLAG);\n\t\t\t\t\t}\n\t\t\t\t\tif (attr == null) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tattrMap.put(\"android:\" + name, attr);\n\t\t\t\t}\n\t\t\t\tNamedNodeMap attributes = tempNode.getAttributes();\n\t\t\t\tNode nameNode = attributes.getNamedItem(\"name\");\n\t\t\t\tif (nameNode != null) {\n\t\t\t\t\tNode valueNode = attributes.getNamedItem(\"value\");\n\t\t\t\t\tif (valueNode != null) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tlong key;\n\t\t\t\t\t\t\tString nodeValue = valueNode.getNodeValue();\n\t\t\t\t\t\t\tif (nodeValue.startsWith(\"0x\")) {\n\t\t\t\t\t\t\t\tnodeValue = nodeValue.substring(2);\n\t\t\t\t\t\t\t\tkey = Long.parseLong(nodeValue, 16);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tkey = Long.parseLong(nodeValue);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tattr.addValue(key, nameNode.getNodeValue());\n\t\t\t\t\t\t} catch (NumberFormatException e) {\n\t\t\t\t\t\t\tLOG.debug(\"Failed parse manifest number\", e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic String decode(String attrName, long value) {\n\t\tMAttr attr = attrMap.get(attrName);\n\t\tif (attr == null) {\n\t\t\tif (attrName.contains(\":\")) {\n\t\t\t\tattrName = attrName.split(\":\", 2)[1];\n\t\t\t}\n\t\t\tattr = appAttrMap.get(attrName);\n\t\t\tif (attr == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tMap<Long, String> attrValuesMap = attr.getValues();\n\t\tif (attr.getType() == MAttrType.ENUM) {\n\t\t\treturn attrValuesMap.get(value);\n\t\t} else if (attr.getType() == MAttrType.FLAG) {\n\t\t\tList<String> flagList = new ArrayList<>();\n\t\t\tList<Long> attrKeys = new ArrayList<>(attrValuesMap.keySet());\n\t\t\tattrKeys.sort((a, b) -> Long.compare(b, a)); // sort descending\n\t\t\tfor (Long key : attrKeys) {\n\t\t\t\tString attrValue = attrValuesMap.get(key);\n\t\t\t\tif (value == key) {\n\t\t\t\t\tflagList.add(attrValue);\n\t\t\t\t\tbreak;\n\t\t\t\t} else if ((key != 0) && ((value & key) == key)) {\n\t\t\t\t\tflagList.add(attrValue);\n\t\t\t\t\tvalue ^= key;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn String.join(\"|\", flagList);\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic void updateAttributes(IResTableParser parser) {\n\t\tappAttrMap.clear();\n\n\t\tResourceStorage resStorage = parser.getResStorage();\n\t\tValuesParser vp = new ValuesParser(parser.getStrings(), resStorage.getResourcesNames());\n\n\t\tfor (ResourceEntry ri : resStorage.getResources()) {\n\t\t\tif (ri.getProtoValue() != null) {\n\t\t\t\t// Aapt proto decoder resolves attributes by itself.\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (ri.getTypeName().equals(\"attr\") && ri.getNamedValues().size() > 1) {\n\t\t\t\tRawNamedValue first = ri.getNamedValues().get(0);\n\t\t\t\tMAttrType attrTyp;\n\t\t\t\tint attrTypeVal = first.getRawValue().getData() & 0xff0000;\n\t\t\t\tif (attrTypeVal == ValuesParser.ATTR_TYPE_FLAGS) {\n\t\t\t\t\tattrTyp = MAttrType.FLAG;\n\t\t\t\t} else if (attrTypeVal == ValuesParser.ATTR_TYPE_ENUM) {\n\t\t\t\t\tattrTyp = MAttrType.ENUM;\n\t\t\t\t} else {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tMAttr attr = new MAttr(attrTyp);\n\t\t\t\tfor (int i = 1; i < ri.getNamedValues().size(); i++) {\n\t\t\t\t\tRawNamedValue rv = ri.getNamedValues().get(i);\n\t\t\t\t\tString value = vp.decodeNameRef(rv.getNameRef());\n\t\t\t\t\tattr.addValue(rv.getRawValue().getData(), value.startsWith(\"id.\") ? value.substring(3) : value);\n\t\t\t\t}\n\t\t\t\tappAttrMap.put(ri.getKeyName(), attr);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/ParserConstants.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class ParserConstants {\n\n\tprotected ParserConstants() {\n\t}\n\n\tprotected static final String ANDROID_NS_URL = \"http://schemas.android.com/apk/res/android\";\n\tprotected static final String ANDROID_NS_VALUE = \"android\";\n\n\t/**\n\t * Chunk types as defined in frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h (AOSP)\n\t */\n\tprotected static final int RES_NULL_TYPE = 0x0000;\n\tprotected static final int RES_STRING_POOL_TYPE = 0x0001;\n\tprotected static final int RES_TABLE_TYPE = 0x0002;\n\n\tprotected static final int RES_XML_TYPE = 0x0003;\n\tprotected static final int RES_XML_FIRST_CHUNK_TYPE = 0x0100;\n\tprotected static final int RES_XML_START_NAMESPACE_TYPE = 0x0100;\n\tprotected static final int RES_XML_END_NAMESPACE_TYPE = 0x0101;\n\tprotected static final int RES_XML_START_ELEMENT_TYPE = 0x0102;\n\tprotected static final int RES_XML_END_ELEMENT_TYPE = 0x0103;\n\tprotected static final int RES_XML_CDATA_TYPE = 0x0104;\n\tprotected static final int RES_XML_LAST_CHUNK_TYPE = 0x017f;\n\tprotected static final int RES_XML_RESOURCE_MAP_TYPE = 0x0180;\n\n\tprotected static final int RES_TABLE_PACKAGE_TYPE = 0x0200; // 512\n\tprotected static final int RES_TABLE_TYPE_TYPE = 0x0201; // 513\n\tprotected static final int RES_TABLE_TYPE_SPEC_TYPE = 0x0202; // 514\n\tprotected static final int RES_TABLE_TYPE_LIBRARY = 0x0203; // 515\n\tprotected static final int RES_TABLE_TYPE_OVERLAY = 0x0204; // 516\n\tprotected static final int RES_TABLE_TYPE_OVERLAY_POLICY = 0x0205; // 517\n\tprotected static final int RES_TABLE_TYPE_STAGED_ALIAS = 0x0206; // 518\n\n\t/**\n\t * Type constants\n\t */\n\t// Contains no data.\n\tprotected static final int TYPE_NULL = 0x00;\n\t// The 'data' holds a ResTable_ref, a reference to another resource table entry.\n\tprotected static final int TYPE_REFERENCE = 0x01;\n\t// The 'data' holds an attribute resource identifier.\n\tprotected static final int TYPE_ATTRIBUTE = 0x02;\n\t// The 'data' holds an index into the containing resource table's global value string pool.\n\tprotected static final int TYPE_STRING = 0x03;\n\t// The 'data' holds a single-precision floating point number.\n\tprotected static final int TYPE_FLOAT = 0x04;\n\t// The 'data' holds a complex number encoding a dimension value, such as \"100in\".\n\tprotected static final int TYPE_DIMENSION = 0x05;\n\t// The 'data' holds a complex number encoding a fraction of a container.\n\tprotected static final int TYPE_FRACTION = 0x06;\n\n\t/**\n\t * The 'data' holds a dynamic reference, a reference to another resource table entry.\n\t * See https://github.com/skylot/jadx/issues/919\n\t */\n\tprotected static final int TYPE_DYNAMIC_REFERENCE = 0x07;\n\n\t/**\n\t * According to the sources of apktool this type seem to be related to themes\n\t * See https://github.com/skylot/jadx/issues/919\n\t */\n\tprotected static final int TYPE_DYNAMIC_ATTRIBUTE = 0x08;\n\t// Beginning of integer flavors...\n\tprotected static final int TYPE_FIRST_INT = 0x10;\n\t// The 'data' is a raw integer value of the form n..n.\n\tprotected static final int TYPE_INT_DEC = 0x10;\n\t// The 'data' is a raw integer value of the form 0xn..n.\n\tprotected static final int TYPE_INT_HEX = 0x11;\n\t// The 'data' is either 0 or 1, for input \"false\" or \"true\" respectively.\n\tprotected static final int TYPE_INT_BOOLEAN = 0x12;\n\t// Beginning of color integer flavors...\n\tprotected static final int TYPE_FIRST_COLOR_INT = 0x1c;\n\t// The 'data' is a raw integer value of the form #aarrggbb.\n\tprotected static final int TYPE_INT_COLOR_ARGB8 = 0x1c;\n\t// The 'data' is a raw integer value of the form #rrggbb.\n\tprotected static final int TYPE_INT_COLOR_RGB8 = 0x1d;\n\t// The 'data' is a raw integer value of the form #argb.\n\tprotected static final int TYPE_INT_COLOR_ARGB4 = 0x1e;\n\t// The 'data' is a raw integer value of the form #rgb.\n\tprotected static final int TYPE_INT_COLOR_RGB4 = 0x1f;\n\t// ...end of integer flavors.\n\tprotected static final int TYPE_LAST_COLOR_INT = 0x1f;\n\t// ...end of integer flavors.\n\tprotected static final int TYPE_LAST_INT = 0x1f;\n\n\t// Where the unit type information is. This gives us 16 possible\n\t// types, as defined below.\n\tprotected static final int COMPLEX_UNIT_SHIFT = 0;\n\tprotected static final int COMPLEX_UNIT_MASK = 0xf;\n\n\t// TYPE_DIMENSION: Value is raw pixels.\n\tprotected static final int COMPLEX_UNIT_PX = 0;\n\t// TYPE_DIMENSION: Value is Device Independent Pixels.\n\tprotected static final int COMPLEX_UNIT_DIP = 1;\n\t// TYPE_DIMENSION: Value is a Scaled device independent Pixels.\n\tprotected static final int COMPLEX_UNIT_SP = 2;\n\t// TYPE_DIMENSION: Value is in points.\n\tprotected static final int COMPLEX_UNIT_PT = 3;\n\t// TYPE_DIMENSION: Value is in inches.\n\tprotected static final int COMPLEX_UNIT_IN = 4;\n\t// TYPE_DIMENSION: Value is in millimeters.\n\tprotected static final int COMPLEX_UNIT_MM = 5;\n\n\t// TYPE_FRACTION: A basic fraction of the overall size.\n\tprotected static final int COMPLEX_UNIT_FRACTION = 0;\n\t// TYPE_FRACTION: A fraction of the parent size.\n\tprotected static final int COMPLEX_UNIT_FRACTION_PARENT = 1;\n\n\t// Where the radix information is, telling where the decimal place\n\t// appears in the mantissa. This give us 4 possible fixed point\n\t// representations as defined below.\n\tprotected static final int COMPLEX_RADIX_SHIFT = 4;\n\tprotected static final int COMPLEX_RADIX_MASK = 0x3;\n\n\t// The mantissa is an integral number -- i.e., 0xnnnnnn.0\n\tprotected static final int COMPLEX_RADIX_23P0 = 0;\n\t// The mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn\n\tprotected static final int COMPLEX_RADIX_16P7 = 1;\n\t// The mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn\n\tprotected static final int COMPLEX_RADIX_8P15 = 2;\n\t// The mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn\n\tprotected static final int COMPLEX_RADIX_0P23 = 3;\n\n\t// Where the actual value is. This gives us 23 bits of\n\t// precision. The top bit is the sign.\n\tprotected static final int COMPLEX_MANTISSA_SHIFT = 8;\n\tprotected static final int COMPLEX_MANTISSA_MASK = 0xffffff;\n\n\tprotected static final double MANTISSA_MULT = 1.0f / (1 << COMPLEX_MANTISSA_SHIFT);\n\tprotected static final double[] RADIX_MULTS = new double[] {\n\t\t\t1.0f * MANTISSA_MULT,\n\t\t\t1.0f / (1 << 7) * MANTISSA_MULT,\n\t\t\t1.0f / (1 << 15) * MANTISSA_MULT,\n\t\t\t1.0f / (1 << 23) * MANTISSA_MULT\n\t};\n\n\t/**\n\t * String pool flags\n\t */\n\tprotected static final int SORTED_FLAG = 1;\n\tprotected static final int UTF8_FLAG = 1 << 8;\n\n\t/**\n\t * ResTable_type\n\t */\n\tprotected static final int NO_ENTRY = 0xFFFFFFFF;\n\n\t// If set, the entry is sparse, and encodes both the entry ID and offset into each entry,\n\t// and a binary search is used to find the key. Only available on platforms >= O.\n\t// Mark any types that use this with a v26 qualifier to prevent runtime issues on older\n\t// platforms.\n\tprotected static final int FLAG_SPARSE = 0x01;\n\t// If set, the offsets to the entries are encoded in 16-bit, real_offset = offset * 4u\n\t// An 16-bit offset of 0xffffu means a NO_ENTRY\n\tprotected static final int FLAG_OFFSET16 = 0x02;\n\n\t/**\n\t * ResTable_entry\n\t */\n\t// If set, this is a complex entry, holding a set of name/value mappings.\n\t// It is followed by an array of ResTable_map structures.\n\tprotected static final int FLAG_COMPLEX = 0x0001;\n\t// If set, this resource has been declared public, so libraries are allowed to reference it.\n\tprotected static final int FLAG_PUBLIC = 0x0002;\n\t// If set, this is a weak resource and may be overridden by strong resources of the same name/type.\n\t// This is only useful during linking with other resource tables.\n\tprotected static final int FLAG_WEAK = 0x0004;\n\t// If set, this is a compact entry with data type and value directly\n\t// encoded in the entry, see ResTable_entry::compact\n\tprotected static final int FLAG_COMPACT = 0x0008;\n\n\t/**\n\t * ResTable_map\n\t */\n\tprotected static final int ATTR_TYPE = makeResInternal(0);\n\t// For integral attributes, this is the minimum value it can hold.\n\tprotected static final int ATTR_MIN = makeResInternal(1);\n\t// For integral attributes, this is the maximum value it can hold.\n\tprotected static final int ATTR_MAX = makeResInternal(2);\n\t// Localization of this resource is can be encouraged or required with an aapt flag if this is set\n\tprotected static final int ATTR_L10N = makeResInternal(3);\n\n\t// for plural support, see android.content.res.PluralRules#attrForQuantity(int)\n\tprotected static final int ATTR_OTHER = makeResInternal(4);\n\tprotected static final int ATTR_ZERO = makeResInternal(5);\n\tprotected static final int ATTR_ONE = makeResInternal(6);\n\tprotected static final int ATTR_TWO = makeResInternal(7);\n\tprotected static final int ATTR_FEW = makeResInternal(8);\n\tprotected static final int ATTR_MANY = makeResInternal(9);\n\n\tprotected static final Map<Integer, String> PLURALS_MAP;\n\n\tstatic {\n\t\tPLURALS_MAP = new HashMap<>();\n\t\tPLURALS_MAP.put(ATTR_OTHER, \"other\");\n\t\tPLURALS_MAP.put(ATTR_ZERO, \"zero\");\n\t\tPLURALS_MAP.put(ATTR_ONE, \"one\");\n\t\tPLURALS_MAP.put(ATTR_TWO, \"two\");\n\t\tPLURALS_MAP.put(ATTR_FEW, \"few\");\n\t\tPLURALS_MAP.put(ATTR_MANY, \"many\");\n\t}\n\n\tprivate static int makeResInternal(int entry) {\n\t\treturn 0x01000000 | entry & 0xFFFF;\n\t}\n\n\tprotected static boolean isResInternalId(int resid) {\n\t\treturn (resid & 0xFFFF0000) != 0 && (resid & 0xFF0000) == 0;\n\t}\n\n\t// Bit mask of allowed types, for use with ATTR_TYPE.\n\tprotected static final int ATTR_TYPE_ANY = 0x0000FFFF;\n\t// Attribute holds a references to another resource.\n\tprotected static final int ATTR_TYPE_REFERENCE = 1;\n\t// Attribute holds a generic string.\n\tprotected static final int ATTR_TYPE_STRING = 1 << 1;\n\t// Attribute holds an integer value. ATTR_MIN and ATTR_MIN can\n\t// optionally specify a constrained range of possible integer values.\n\tprotected static final int ATTR_TYPE_INTEGER = 1 << 2;\n\t// Attribute holds a boolean integer.\n\tprotected static final int ATTR_TYPE_BOOLEAN = 1 << 3;\n\t// Attribute holds a color value.\n\tprotected static final int ATTR_TYPE_COLOR = 1 << 4;\n\t// Attribute holds a floating point value.\n\tprotected static final int ATTR_TYPE_FLOAT = 1 << 5;\n\t// Attribute holds a dimension value, such as \"20px\".\n\tprotected static final int ATTR_TYPE_DIMENSION = 1 << 6;\n\t// Attribute holds a fraction value, such as \"20%\".\n\tprotected static final int ATTR_TYPE_FRACTION = 1 << 7;\n\t// Attribute holds an enumeration. The enumeration values are\n\t// supplied as additional entries in the map.\n\tprotected static final int ATTR_TYPE_ENUM = 1 << 16;\n\t// Attribute holds a bitmaks of flags. The flag bit values are\n\t// supplied as additional entries in the map.\n\tprotected static final int ATTR_TYPE_FLAGS = 1 << 17;\n\n\t// Enum of localization modes, for use with ATTR_L10N\n\tprotected static final int ATTR_L10N_NOT_REQUIRED = 0;\n\tprotected static final int ATTR_L10N_SUGGESTED = 1;\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/ParserStream.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.io.BufferedInputStream;\nimport java.io.EOFException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class ParserStream extends InputStream {\n\n\tprotected static final Charset STRING_CHARSET_UTF16 = StandardCharsets.UTF_16LE;\n\tprotected static final Charset STRING_CHARSET_UTF8 = StandardCharsets.UTF_8;\n\n\tprivate static final int[] EMPTY_INT_ARRAY = new int[0];\n\tprivate static final byte[] EMPTY_BYTE_ARRAY = new byte[0];\n\n\tprivate final InputStream input;\n\tprivate long readPos = 0;\n\tprivate long markPos = 0;\n\n\tpublic ParserStream(@NotNull InputStream inputStream) {\n\t\tthis.input = inputStream.markSupported() ? inputStream : new BufferedInputStream(inputStream);\n\t}\n\n\tpublic long getPos() {\n\t\treturn readPos;\n\t}\n\n\tpublic int readInt8() throws IOException {\n\t\treadPos++;\n\t\treturn input.read();\n\t}\n\n\tpublic int readInt16() throws IOException {\n\t\treadPos += 2;\n\t\tint b1 = input.read();\n\t\tint b2 = input.read();\n\t\treturn (b2 & 0xFF) << 8 | b1 & 0xFF;\n\t}\n\n\tpublic int readInt32() throws IOException {\n\t\treadPos += 4;\n\t\tInputStream in = input;\n\t\tint b1 = in.read();\n\t\tint b2 = in.read();\n\t\tint b3 = in.read();\n\t\tint b4 = in.read();\n\t\treturn b4 << 24 | (b3 & 0xFF) << 16 | (b2 & 0xFF) << 8 | b1 & 0xFF;\n\t}\n\n\tpublic long readUInt32() throws IOException {\n\t\treturn readInt32() & 0xFFFFFFFFL;\n\t}\n\n\tpublic String readString16Fixed(int len) throws IOException {\n\t\tString str = new String(readInt8Array(len * 2), STRING_CHARSET_UTF16);\n\t\treturn str.trim();\n\t}\n\n\tpublic int[] readInt32Array(int count) throws IOException {\n\t\tif (count == 0) {\n\t\t\treturn EMPTY_INT_ARRAY;\n\t\t}\n\t\tint[] arr = new int[count];\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tarr[i] = readInt32();\n\t\t}\n\t\treturn arr;\n\t}\n\n\tpublic byte[] readInt8Array(int count) throws IOException {\n\t\tif (count == 0) {\n\t\t\treturn EMPTY_BYTE_ARRAY;\n\t\t}\n\t\treadPos += count;\n\t\tbyte[] arr = new byte[count];\n\t\tint pos = input.read(arr, 0, count);\n\t\twhile (pos < count) {\n\t\t\tint read = input.read(arr, pos, count - pos);\n\t\t\tif (read == -1) {\n\t\t\t\tthrow new IOException(\"No data, can't read \" + count + \" bytes\");\n\t\t\t}\n\t\t\tpos += read;\n\t\t}\n\t\treturn arr;\n\t}\n\n\t@Override\n\tpublic long skip(long count) throws IOException {\n\t\treadPos += count;\n\t\tlong pos = input.skip(count);\n\t\twhile (pos < count) {\n\t\t\tlong skipped = input.skip(count - pos);\n\t\t\tif (skipped == 0) {\n\t\t\t\tthrow new IOException(\"No data, can't skip \" + count + \" bytes\");\n\t\t\t}\n\t\t\tpos += skipped;\n\t\t}\n\t\treturn pos;\n\t}\n\n\tpublic void checkInt8(int expected, String error) throws IOException {\n\t\tint v = readInt8();\n\t\tif (v != expected) {\n\t\t\tthrowException(error, expected, v);\n\t\t}\n\t}\n\n\tpublic void checkInt16(int expected, String error) throws IOException {\n\t\tint v = readInt16();\n\t\tif (v != expected) {\n\t\t\tthrowException(error, expected, v);\n\t\t}\n\t}\n\n\tprivate void throwException(String error, int expected, int actual) throws IOException {\n\t\tthrow new IOException(error\n\t\t\t\t+ \", expected: 0x\" + Integer.toHexString(expected)\n\t\t\t\t+ \", actual: 0x\" + Integer.toHexString(actual)\n\t\t\t\t+ \", offset: 0x\" + Long.toHexString(getPos()));\n\t}\n\n\tpublic void checkPos(long expectedOffset, String error) throws IOException {\n\t\tif (getPos() != expectedOffset) {\n\t\t\tthrow new IOException(error + \", expected offset: 0x\" + Long.toHexString(expectedOffset)\n\t\t\t\t\t+ \", actual: 0x\" + Long.toHexString(getPos()));\n\t\t}\n\t}\n\n\tpublic void skipToPos(long expectedOffset, String error) throws IOException {\n\t\tlong pos = getPos();\n\t\tif (pos > expectedOffset) {\n\t\t\tthrow new IOException(error + \", expected offset not reachable: 0x\" + Long.toHexString(expectedOffset)\n\t\t\t\t\t+ \", actual: 0x\" + Long.toHexString(getPos()));\n\t\t}\n\t\tif (pos < expectedOffset) {\n\t\t\tskip(expectedOffset - pos);\n\t\t}\n\t\tcheckPos(expectedOffset, error);\n\t}\n\n\t@Override\n\tpublic void mark(int len) {\n\t\tif (!input.markSupported()) {\n\t\t\tthrow new RuntimeException(\"Mark not supported for input stream \" + input.getClass());\n\t\t}\n\t\tinput.mark(len);\n\t\tmarkPos = readPos;\n\t}\n\n\t@Override\n\tpublic void reset() throws IOException {\n\t\tinput.reset();\n\t\treadPos = markPos;\n\t}\n\n\tpublic void readFully(byte[] b) throws IOException {\n\t\treadFully(b, 0, b.length);\n\t}\n\n\tpublic void readFully(byte[] b, int off, int len) throws IOException {\n\t\treadPos += len;\n\t\tif (len < 0) {\n\t\t\tthrow new IndexOutOfBoundsException();\n\t\t}\n\t\tint n = 0;\n\t\twhile (n < len) {\n\t\t\tint count = input.read(b, off + n, len - n);\n\t\t\tif (count < 0) {\n\t\t\t\tthrow new EOFException();\n\t\t\t}\n\t\t\tn += count;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int read() throws IOException {\n\t\treturn input.read();\n\t}\n\n\t@Override\n\tpublic int read(@NotNull byte[] b, int off, int len) throws IOException {\n\t\treturn input.read(b, off, len);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"pos: 0x\" + Long.toHexString(readPos);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/ResContainer.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.io.File;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ResourceFile;\n\npublic class ResContainer implements Comparable<ResContainer> {\n\n\tpublic enum DataType {\n\t\tTEXT, DECODED_DATA, RES_LINK, RES_TABLE\n\t}\n\n\tprivate final DataType dataType;\n\tprivate final String name;\n\tprivate final Object data;\n\tprivate final List<ResContainer> subFiles;\n\n\tpublic static ResContainer textResource(String name, ICodeInfo content) {\n\t\treturn new ResContainer(name, Collections.emptyList(), content, DataType.TEXT);\n\t}\n\n\tpublic static ResContainer decodedData(String name, byte[] data) {\n\t\treturn new ResContainer(name, Collections.emptyList(), data, DataType.DECODED_DATA);\n\t}\n\n\tpublic static ResContainer resourceFileLink(ResourceFile resFile) {\n\t\treturn new ResContainer(resFile.getDeobfName(), Collections.emptyList(), resFile, DataType.RES_LINK);\n\t}\n\n\tpublic static ResContainer resourceTable(String name, List<ResContainer> subFiles, ICodeInfo rootContent) {\n\t\treturn new ResContainer(name, subFiles, rootContent, DataType.RES_TABLE);\n\t}\n\n\tprivate ResContainer(String name, List<ResContainer> subFiles, Object data, DataType dataType) {\n\t\tthis.name = Objects.requireNonNull(name);\n\t\tthis.subFiles = Objects.requireNonNull(subFiles);\n\t\tthis.data = Objects.requireNonNull(data);\n\t\tthis.dataType = Objects.requireNonNull(dataType);\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic String getFileName() {\n\t\treturn name.replace('/', File.separatorChar);\n\t}\n\n\tpublic List<ResContainer> getSubFiles() {\n\t\treturn subFiles;\n\t}\n\n\tpublic DataType getDataType() {\n\t\treturn dataType;\n\t}\n\n\tpublic ICodeInfo getText() {\n\t\treturn (ICodeInfo) data;\n\t}\n\n\tpublic byte[] getDecodedData() {\n\t\treturn (byte[]) data;\n\t}\n\n\tpublic ResourceFile getResLink() {\n\t\treturn (ResourceFile) data;\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull ResContainer o) {\n\t\treturn name.compareTo(o.name);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof ResContainer)) {\n\t\t\treturn false;\n\t\t}\n\t\tResContainer that = (ResContainer) o;\n\t\treturn name.equals(that.name);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn name.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Res{\" + name + \", type=\" + dataType + \", subFiles=\" + subFiles + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/ResDecoder.java",
    "content": "package jadx.core.xmlgen;\n\npublic class ResDecoder {\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/ResNameUtils.java",
    "content": "package jadx.core.xmlgen;\n\nimport jadx.core.deobf.NameMapper;\n\nimport static jadx.core.deobf.NameMapper.*;\n\nclass ResNameUtils {\n\n\tprivate ResNameUtils() {\n\t}\n\n\t/**\n\t * Sanitizes the name so that it can be used as a resource name.\n\t * By resource name is meant that:\n\t * <ul>\n\t * <li>It can be used by aapt2 as a resource entry name.\n\t * <li>It can be converted to a valid R class field name.\n\t * </ul>\n\t * <p>\n\t * If the {@code name} is already a valid resource name, the method returns it unchanged.\n\t * If not, the method creates a valid resource name based on {@code name}, appends the\n\t * {@code postfix}, and returns the result.\n\t */\n\tstatic String sanitizeAsResourceName(String name, String postfix, boolean allowNonPrintable) {\n\t\tif (name.isEmpty()) {\n\t\t\treturn postfix;\n\t\t}\n\n\t\tfinal StringBuilder sb = new StringBuilder(name.length() + 1);\n\t\tboolean nameChanged = false;\n\n\t\tint cp = name.codePointAt(0);\n\t\tif (isValidResourceNameStart(cp, allowNonPrintable)) {\n\t\t\tsb.appendCodePoint(cp);\n\t\t} else {\n\t\t\tsb.append('_');\n\t\t\tnameChanged = true;\n\n\t\t\tif (isValidResourceNamePart(cp, allowNonPrintable)) {\n\t\t\t\tsb.appendCodePoint(cp);\n\t\t\t}\n\t\t}\n\n\t\tfor (int i = Character.charCount(cp); i < name.length(); i += Character.charCount(cp)) {\n\t\t\tcp = name.codePointAt(i);\n\t\t\tif (isValidResourceNamePart(cp, allowNonPrintable)) {\n\t\t\t\tsb.appendCodePoint(cp);\n\t\t\t} else {\n\t\t\t\tsb.append('_');\n\t\t\t\tnameChanged = true;\n\t\t\t}\n\t\t}\n\n\t\tfinal String sanitizedName = sb.toString();\n\t\tif (NameMapper.isReserved(sanitizedName)) {\n\t\t\tnameChanged = true;\n\t\t}\n\n\t\treturn nameChanged\n\t\t\t\t? sanitizedName + postfix\n\t\t\t\t: sanitizedName;\n\t}\n\n\t/**\n\t * Converts the resource name to a field name of the R class.\n\t */\n\tstatic String convertToRFieldName(String resourceName) {\n\t\treturn resourceName.replace('.', '_');\n\t}\n\n\t/**\n\t * Determines whether the code point may be part of a resource name as the first character (aapt2 +\n\t * R class gen).\n\t */\n\tprivate static boolean isValidResourceNameStart(int codePoint, boolean allowNonPrintable) {\n\t\treturn (allowNonPrintable || isPrintableAsciiCodePoint(codePoint))\n\t\t\t\t&& (isValidAapt2ResourceNameStart(codePoint) && isValidIdentifierStart(codePoint));\n\t}\n\n\t/**\n\t * Determines whether the code point may be part of a resource name as other than the first\n\t * character\n\t * (aapt2 + R class gen).\n\t */\n\tprivate static boolean isValidResourceNamePart(int codePoint, boolean allowNonPrintable) {\n\t\treturn (allowNonPrintable || isPrintableAsciiCodePoint(codePoint))\n\t\t\t\t&& ((isValidAapt2ResourceNamePart(codePoint) && isValidIdentifierPart(codePoint)) || codePoint == '.');\n\t}\n\n\t/**\n\t * Determines whether the code point may be part of a resource name as the first character (aapt2).\n\t * <p>\n\t * Source: <a href=\n\t * \"https://cs.android.com/android/platform/superproject/+/android15-release:frameworks/base/tools/aapt2/text/Unicode.cpp;l=112\">aapt2/text/Unicode.cpp#L112</a>\n\t */\n\tprivate static boolean isValidAapt2ResourceNameStart(int codePoint) {\n\t\treturn isXidStart(codePoint) || codePoint == '_';\n\t}\n\n\t/**\n\t * Determines whether the code point may be part of a resource name as other than the first\n\t * character (aapt2).\n\t * <p>\n\t * Source: <a href=\n\t * \"https://cs.android.com/android/platform/superproject/+/android15-release:frameworks/base/tools/aapt2/text/Unicode.cpp;l=118\">aapt2/text/Unicode.cpp#L118</a>\n\t */\n\tprivate static boolean isValidAapt2ResourceNamePart(int codePoint) {\n\t\treturn isXidContinue(codePoint) || codePoint == '.' || codePoint == '-';\n\t}\n\n\tprivate static boolean isXidStart(int codePoint) {\n\t\t// TODO: Need to implement a full check if the code point is XID_Start.\n\t\treturn codePoint < 0x0370 && Character.isUnicodeIdentifierStart(codePoint);\n\t}\n\n\tprivate static boolean isXidContinue(int codePoint) {\n\t\t// TODO: Need to implement a full check if the code point is XID_Continue.\n\t\treturn codePoint < 0x0370\n\t\t\t\t&& (Character.isUnicodeIdentifierPart(codePoint) && !Character.isIdentifierIgnorable(codePoint));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/ResTableBinaryParser.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.io.BufferedInputStream;\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.StringJoiner;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.args.ResourceNameSource;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.IFieldInfoRef;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.BetterName;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.xmlgen.entry.EntryConfig;\nimport jadx.core.xmlgen.entry.RawNamedValue;\nimport jadx.core.xmlgen.entry.RawValue;\nimport jadx.core.xmlgen.entry.ResourceEntry;\nimport jadx.core.xmlgen.entry.ValuesParser;\n\npublic class ResTableBinaryParser extends CommonBinaryParser implements IResTableParser {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ResTableBinaryParser.class);\n\n\tprivate static final class PackageChunk {\n\t\tprivate final int id;\n\t\tprivate final String name;\n\t\tprivate final BinaryXMLStrings typeStrings;\n\t\tprivate final BinaryXMLStrings keyStrings;\n\n\t\tprivate PackageChunk(int id, String name, BinaryXMLStrings typeStrings, BinaryXMLStrings keyStrings) {\n\t\t\tthis.id = id;\n\t\t\tthis.name = name;\n\t\t\tthis.typeStrings = typeStrings;\n\t\t\tthis.keyStrings = keyStrings;\n\t\t}\n\n\t\tpublic int getId() {\n\t\t\treturn id;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\n\t\tpublic BinaryXMLStrings getTypeStrings() {\n\t\t\treturn typeStrings;\n\t\t}\n\n\t\tpublic BinaryXMLStrings getKeyStrings() {\n\t\t\treturn keyStrings;\n\t\t}\n\t}\n\n\t/**\n\t * No renaming, pattern checking or name generation. Required for res-map.txt building\n\t */\n\tprivate final boolean useRawResName;\n\tprivate final RootNode root;\n\n\tprivate ResourceStorage resStorage;\n\tprivate BinaryXMLStrings strings;\n\tprivate String baseFileName = \"\";\n\n\tpublic ResTableBinaryParser(RootNode root) {\n\t\tthis(root, false);\n\t}\n\n\tpublic ResTableBinaryParser(RootNode root, boolean useRawResNames) {\n\t\tthis.root = root;\n\t\tthis.useRawResName = useRawResNames;\n\t}\n\n\t@Override\n\tpublic void setBaseFileName(String fileName) {\n\t\tthis.baseFileName = fileName;\n\t}\n\n\t@Override\n\tpublic void decode(InputStream inputStream) throws IOException {\n\t\tlong start = System.currentTimeMillis();\n\t\tis = new ParserStream(new BufferedInputStream(inputStream, 32768));\n\t\tresStorage = new ResourceStorage(root.getArgs().getSecurity());\n\t\tdecodeTableChunk();\n\t\tresStorage.finish();\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tLOG.debug(\"Resource table parsed: size: {}, time: {}ms\",\n\t\t\t\t\tresStorage.size(), System.currentTimeMillis() - start);\n\t\t}\n\t}\n\n\t@Override\n\tpublic ResContainer decodeFiles() {\n\t\tValuesParser vp = new ValuesParser(strings, resStorage.getResourcesNames());\n\t\tResXmlGen resGen = new ResXmlGen(resStorage, vp, root.initManifestAttributes());\n\n\t\tICodeInfo content = XmlGenUtils.makeXmlDump(root.makeCodeWriter(), resStorage);\n\t\tList<ResContainer> xmlFiles = resGen.makeResourcesXml(root.getArgs());\n\t\treturn ResContainer.resourceTable(baseFileName, xmlFiles, content);\n\t}\n\n\tvoid decodeTableChunk() throws IOException {\n\t\tis.checkInt16(RES_TABLE_TYPE, \"Not a table chunk\");\n\t\tis.checkInt16(0x000c, \"Unexpected table header size\");\n\t\tint size = is.readInt32();\n\t\tint pkgCount = is.readInt32();\n\n\t\tint pkgNum = 0;\n\t\twhile (is.getPos() < size) {\n\t\t\tlong chuckStart = is.getPos();\n\t\t\tint type = is.readInt16();\n\t\t\tint headerSize = is.readInt16();\n\t\t\tlong chunkSize = is.readUInt32();\n\t\t\tlong chunkEnd = chuckStart + chunkSize;\n\t\t\tswitch (type) {\n\t\t\t\tcase RES_NULL_TYPE:\n\t\t\t\t\t// skip\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase RES_STRING_POOL_TYPE:\n\t\t\t\t\tstrings = parseStringPoolNoSize(chuckStart, chunkEnd);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase RES_TABLE_PACKAGE_TYPE:\n\t\t\t\t\tparsePackage(chuckStart, headerSize, chunkEnd);\n\t\t\t\t\tpkgNum++;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tis.skipToPos(chunkEnd, \"Skip to table chunk end\");\n\t\t}\n\t\tif (pkgNum != pkgCount) {\n\t\t\tLOG.warn(\"Unexpected package chunks, read: {}, expected: {}\", pkgNum, pkgCount);\n\t\t}\n\t}\n\n\tprivate void parsePackage(long pkgChunkStart, int headerSize, long pkgChunkEnd) throws IOException {\n\t\tif (headerSize < 0x011c) {\n\t\t\tdie(\"Package header size too small\");\n\t\t\treturn;\n\t\t}\n\t\tint id = is.readInt32();\n\t\tString name = is.readString16Fixed(128);\n\t\tlong typeStringsOffset = pkgChunkStart + is.readInt32();\n\t\tint lastPublicType = is.readInt32();\n\t\tlong keyStringsOffset = pkgChunkStart + is.readInt32();\n\t\tint lastPublicKey = is.readInt32();\n\t\tif (headerSize >= 0x0120) {\n\t\t\tint typeIdOffset = is.readInt32();\n\t\t}\n\t\tis.skipToPos(pkgChunkStart + headerSize, \"package header end\");\n\n\t\tBinaryXMLStrings typeStrings = null;\n\t\tif (typeStringsOffset != 0) {\n\t\t\tis.skipToPos(typeStringsOffset, \"Expected typeStrings string pool\");\n\t\t\ttypeStrings = parseStringPool();\n\t\t}\n\t\tBinaryXMLStrings keyStrings = null;\n\t\tif (keyStringsOffset != 0) {\n\t\t\tis.skipToPos(keyStringsOffset, \"Expected keyStrings string pool\");\n\t\t\tkeyStrings = parseStringPool();\n\t\t}\n\n\t\tPackageChunk pkg = new PackageChunk(id, name, typeStrings, keyStrings);\n\t\tresStorage.setAppPackage(name);\n\n\t\twhile (is.getPos() < pkgChunkEnd) {\n\t\t\tlong chunkStart = is.getPos();\n\t\t\tint type = is.readInt16();\n\t\t\tLOG.trace(\"res package chunk start at {} type {}\", chunkStart, type);\n\t\t\tswitch (type) {\n\t\t\t\tcase RES_NULL_TYPE:\n\t\t\t\t\tLOG.info(\"Null chunk type encountered at offset {}\", chunkStart);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RES_TABLE_TYPE_TYPE: // 0x0201\n\t\t\t\t\tparseTypeChunk(chunkStart, pkg);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RES_TABLE_TYPE_SPEC_TYPE: // 0x0202\n\t\t\t\t\tparseTypeSpecChunk(chunkStart);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RES_TABLE_TYPE_LIBRARY: // 0x0203\n\t\t\t\t\tparseLibraryTypeChunk(chunkStart);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RES_TABLE_TYPE_OVERLAY: // 0x0204\n\t\t\t\t\tparseOverlayTypeChunk(chunkStart);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RES_TABLE_TYPE_OVERLAY_POLICY: // 0x0205\n\t\t\t\t\tthrow new IOException(\n\t\t\t\t\t\t\tString.format(\"Encountered unsupported chunk type RES_TABLE_TYPE_OVERLAY_POLICY at offset 0x%x \", chunkStart));\n\t\t\t\tcase RES_TABLE_TYPE_STAGED_ALIAS: // 0x0206\n\t\t\t\t\tparseStagedAliasChunk(chunkStart);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tLOG.warn(\"Unknown chunk type {} encountered at offset {}\", type, chunkStart);\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void parseTypeSpecChunk(long chunkStart) throws IOException {\n\t\tis.checkInt16(0x0010, \"Unexpected type spec header size\");\n\t\tint chunkSize = is.readInt32();\n\t\tlong expectedEndPos = chunkStart + chunkSize;\n\n\t\tint id = is.readInt8();\n\t\tis.skip(3);\n\t\tint entryCount = is.readInt32();\n\t\tfor (int i = 0; i < entryCount; i++) {\n\t\t\tint entryFlag = is.readInt32();\n\t\t}\n\t\tif (is.getPos() != expectedEndPos) {\n\t\t\tthrow new IOException(String.format(\"Error reading type spec chunk at offset 0x%x\", chunkStart));\n\t\t}\n\t}\n\n\tprivate void parseLibraryTypeChunk(long chunkStart) throws IOException {\n\t\tLOG.trace(\"parsing library type chunk starting at offset {}\", chunkStart);\n\t\tis.checkInt16(12, \"Unexpected header size\");\n\t\tint chunkSize = is.readInt32();\n\t\tlong expectedEndPos = chunkStart + chunkSize;\n\t\tint count = is.readInt32();\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tint packageId = is.readInt32();\n\t\t\tString packageName = is.readString16Fixed(128);\n\t\t\tLOG.info(\"Found resource shared library {}, pkgId: {}\", packageName, packageId);\n\t\t\tif (is.getPos() > expectedEndPos) {\n\t\t\t\tthrow new IOException(\"reading after chunk end\");\n\t\t\t}\n\t\t}\n\t\tif (is.getPos() != expectedEndPos) {\n\t\t\tthrow new IOException(String.format(\"Error reading library chunk at offset 0x%x\", chunkStart));\n\t\t}\n\t}\n\n\t/**\n\t * Parse an <code>ResTable_type</code> (except for the 2 bytes <code>uint16_t</code>\n\t * from <code>ResChunk_header</code>).\n\t *\n\t * @see <a href=\n\t *      \"https://github.com/aosp-mirror/platform_frameworks_base/blob/master/libs/androidfw/include/androidfw/ResourceTypes.h\"></a>ResourceTypes.h</a>\n\t */\n\tprivate void parseTypeChunk(long start, PackageChunk pkg) throws IOException {\n\t\t/* int headerSize = */\n\t\tis.readInt16();\n\t\t/* int size = */\n\t\tlong chunkSize = is.readUInt32();\n\t\tlong chunkEnd = start + chunkSize;\n\t\tis.mark((int) chunkSize);\n\n\t\t// The type identifier this chunk is holding. Type IDs start at 1 (corresponding\n\t\t// to the value of the type bits in a resource identifier). 0 is invalid.\n\t\tint typeId = is.readInt8();\n\t\tString typeName = pkg.getTypeStrings().get(typeId - 1);\n\n\t\tint flags = is.readInt8();\n\t\tboolean isSparse = (flags & FLAG_SPARSE) != 0;\n\t\tboolean isOffset16 = (flags & FLAG_OFFSET16) != 0;\n\n\t\tis.readInt16(); // ignore reserved value - should be zero but in some apps it is not zero; see #2402\n\t\tint entryCount = is.readInt32();\n\t\tlong entriesStart = start + is.readInt32();\n\n\t\tEntryConfig config = parseConfig();\n\n\t\tif (config.isInvalid) {\n\t\t\tLOG.warn(\"Invalid config flags detected: {}{}\", typeName, config.getQualifiers());\n\t\t}\n\n\t\tList<EntryOffset> offsets = new ArrayList<>(entryCount);\n\t\tif (isSparse) {\n\t\t\tfor (int i = 0; i < entryCount; i++) {\n\t\t\t\tint idx = is.readInt16();\n\t\t\t\tint offset = is.readInt16() * 4; // The offset in ResTable_sparseTypeEntry::offset is stored divided by 4.\n\t\t\t\toffsets.add(new EntryOffset(idx, offset));\n\t\t\t}\n\t\t} else if (isOffset16) {\n\t\t\tfor (int i = 0; i < entryCount; i++) {\n\t\t\t\tint offset = is.readInt16();\n\t\t\t\tif (offset != 0xFFFF) {\n\t\t\t\t\toffsets.add(new EntryOffset(i, offset * 4));\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor (int i = 0; i < entryCount; i++) {\n\t\t\t\toffsets.add(new EntryOffset(i, is.readInt32()));\n\t\t\t}\n\t\t}\n\t\tis.skipToPos(entriesStart, \"Failed to skip to entries start\");\n\t\tint ignoredEoc = 0; // ignored entries because they are located after end of chunk\n\t\tSet<Integer> processedIndices = new HashSet<>(offsets.size() * 2);\n\t\tfor (EntryOffset entryOffset : offsets) {\n\t\t\tint offset = entryOffset.getOffset();\n\t\t\tif (offset == NO_ENTRY) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tint index = entryOffset.getIdx();\n\t\t\tif (isSparse && !processedIndices.add(index)) {\n\t\t\t\t// Sometimes sparse type chunks contain multiple entries with the same index.\n\t\t\t\t// If we have processed the index once, we assume can ignore other entries with the same index.\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlong entryStartOffset = entriesStart + offset;\n\t\t\tif (entryStartOffset >= chunkEnd) {\n\t\t\t\t// Certain resource obfuscated apps like com.facebook.orca have more entries defined\n\t\t\t\t// than actually fit into the chunk size -> ignore this entry\n\t\t\t\tignoredEoc++;\n\t\t\t\t// LOG.debug(\"Pos is after chunk end: {} end {}\", entryStartOffset, chunkEnd);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (entryStartOffset < is.getPos()) {\n\t\t\t\t// workaround for issue #2343: if the entryStartOffset is located before our current position\n\t\t\t\tis.reset();\n\t\t\t}\n\t\t\tis.skipToPos(entryStartOffset, \"Expected start of entry \" + index);\n\t\t\tparseEntry(pkg, typeId, index, config.getQualifiers());\n\t\t}\n\t\tif (ignoredEoc > 0) {\n\t\t\t// invalid = data offset is after the chunk end\n\t\t\tLOG.warn(\"{} entries of type {} has been ignored (invalid offset)\", ignoredEoc, typeName);\n\t\t}\n\t\tis.skipToPos(chunkEnd, \"End of chunk\");\n\t}\n\n\tprivate static class EntryOffset {\n\t\tprivate final int idx;\n\t\tprivate final int offset;\n\n\t\tprivate EntryOffset(int idx, int offset) {\n\t\t\tthis.idx = idx;\n\t\t\tthis.offset = offset;\n\t\t}\n\n\t\tpublic int getIdx() {\n\t\t\treturn idx;\n\t\t}\n\n\t\tpublic int getOffset() {\n\t\t\treturn offset;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn new StringJoiner(\", \", EntryOffset.class.getSimpleName() + \"[\", \"]\")\n\t\t\t\t\t.add(\"idx=\" + idx)\n\t\t\t\t\t.add(\"offset=\" + offset)\n\t\t\t\t\t.toString();\n\t\t}\n\t}\n\n\tprivate void parseOverlayTypeChunk(long chunkStart) throws IOException {\n\t\tLOG.trace(\"parsing overlay type chunk starting at offset {}\", chunkStart);\n\t\t// read ResTable_overlayable_header\n\t\t/* headerSize = */\n\t\tis.readInt16(); // usually 1032 bytes\n\t\tint chunkSize = is.readInt32(); // e.g. 1056 bytes\n\t\tlong expectedEndPos = chunkStart + chunkSize;\n\t\tString name = is.readString16Fixed(256); // 512 bytes\n\t\tString actor = is.readString16Fixed(256); // 512 bytes\n\t\tLOG.trace(\"Overlay header data: name={} actor={}\", name, actor);\n\t\t// skip: ResTable_overlayable_policy_header + ResTable_ref * x\n\t\tis.skipToPos(expectedEndPos, \"overlay chunk end\");\n\t}\n\n\tprivate void parseStagedAliasChunk(long chunkStart) throws IOException {\n\t\t// read ResTable_staged_alias_header\n\t\tLOG.trace(\"parsing staged alias chunk starting at offset {}\", chunkStart);\n\t\t/* headerSize = */\n\t\tis.readInt16();\n\t\tint chunkSize = is.readInt32();\n\t\tlong expectedEndPos = chunkStart + chunkSize;\n\t\tint count = is.readInt32();\n\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\t// read ResTable_staged_alias_entry\n\t\t\tint stagedResId = is.readInt32();\n\t\t\tint finalizedResId = is.readInt32();\n\t\t\tLOG.debug(\"Staged alias: stagedResId {} finalizedResId {}\", stagedResId, finalizedResId);\n\t\t}\n\t\tis.skipToPos(expectedEndPos, \"staged alias chunk end\");\n\t}\n\n\tprivate void parseEntry(PackageChunk pkg, int typeId, int entryId, String config) throws IOException {\n\t\tint size = is.readInt16();\n\t\tint flags = is.readInt16();\n\t\tboolean isComplex = (flags & FLAG_COMPLEX) != 0;\n\t\tboolean isCompact = (flags & FLAG_COMPACT) != 0;\n\n\t\tint key = isCompact ? size : is.readInt32();\n\t\tif (key == -1) {\n\t\t\treturn;\n\t\t}\n\n\t\t// resourceID as defined in AOSP make_resid()\n\t\tint resId = pkg.getId() << 24 | typeId << 16 | entryId;\n\t\tString typeName = pkg.getTypeStrings().get(typeId - 1);\n\t\tString origKeyName = pkg.getKeyStrings().get(key);\n\n\t\tResourceEntry newResEntry = buildResourceEntry(pkg, config, resId, typeName, origKeyName);\n\t\tif (isCompact) {\n\t\t\tint dataType = flags >> 8;\n\t\t\tint data = is.readInt32();\n\t\t\tnewResEntry.setSimpleValue(new RawValue(dataType, data));\n\t\t} else if (isComplex || size == 16) {\n\t\t\tint parentRef = is.readInt32();\n\t\t\tint count = is.readInt32();\n\t\t\tnewResEntry.setParentRef(parentRef);\n\t\t\tList<RawNamedValue> values = new ArrayList<>(count);\n\t\t\tfor (int i = 0; i < count; i++) {\n\t\t\t\tvalues.add(parseValueMap());\n\t\t\t}\n\t\t\tnewResEntry.setNamedValues(values);\n\t\t} else {\n\t\t\tnewResEntry.setSimpleValue(parseValue());\n\t\t}\n\t}\n\n\tprivate static final ResourceEntry STUB_ENTRY = new ResourceEntry(-1, \"stub\", \"stub\", \"stub\", \"\");\n\n\tprivate ResourceEntry buildResourceEntry(PackageChunk pkg, String config, int resId, String typeName, String origKeyName) {\n\t\tif (!root.getArgs().getSecurity().isValidEntryName(origKeyName)) {\n\t\t\t// malicious entry, ignore it\n\t\t\t// can't return null here, return stub without adding it to storage\n\t\t\treturn STUB_ENTRY;\n\t\t}\n\n\t\tResourceEntry newResEntry;\n\t\tif (useRawResName) {\n\t\t\tnewResEntry = new ResourceEntry(resId, pkg.getName(), typeName, origKeyName, config);\n\t\t} else {\n\t\t\tString resName = getResName(resId, origKeyName);\n\t\t\tnewResEntry = new ResourceEntry(resId, pkg.getName(), typeName, resName, config);\n\t\t\tResourceEntry prevResEntry = resStorage.searchEntryWithSameName(newResEntry);\n\t\t\tif (prevResEntry != null) {\n\t\t\t\tif (prevResEntry.getId() == newResEntry.getId()) {\n\t\t\t\t\t// Check that every resource (identified by its resource ID) is only processed once.\n\t\t\t\t\t// We should not get resource entries with an identical id. This check is just for safety purposes,\n\t\t\t\t\t// otherwise Jadx can accumulate many GB of RAM as described in issue #2775 because resource names\n\t\t\t\t\t// are extended by every rename operation and are getting longer and longer...\n\t\t\t\t\tLOG.error(\"ResourceEntries with duplicate resource id found: {} {}\", prevResEntry, newResEntry);\n\t\t\t\t\tresName = origKeyName; // use the original name, not the renamed one\n\t\t\t\t}\n\t\t\t\tnewResEntry = newResEntry.copyWithId(resName);\n\n\t\t\t\t// rename also previous entry for consistency\n\t\t\t\tResourceEntry replaceForPrevEntry = prevResEntry.copyWithId(resName);\n\t\t\t\tLOG.trace(\"Resource name collision - renamed to {} and {}\", newResEntry.getKeyName(), replaceForPrevEntry.getKeyName());\n\t\t\t\tresStorage.replace(prevResEntry, replaceForPrevEntry);\n\t\t\t\tresStorage.addRename(replaceForPrevEntry);\n\t\t\t}\n\t\t\tif (!Objects.equals(origKeyName, newResEntry.getKeyName())) {\n\t\t\t\tresStorage.addRename(newResEntry);\n\t\t\t}\n\t\t}\n\n\t\tresStorage.add(newResEntry);\n\t\treturn newResEntry;\n\t}\n\n\tprivate String getResName(int resRef, String origKeyName) {\n\t\tif (this.useRawResName) {\n\t\t\treturn origKeyName;\n\t\t}\n\t\tString renamedKey = resStorage.getRename(resRef);\n\t\tif (renamedKey != null) {\n\t\t\treturn renamedKey;\n\t\t}\n\n\t\tIFieldInfoRef fldRef = root.getConstValues().getGlobalConstFields().get(resRef);\n\t\tFieldNode constField = fldRef instanceof FieldNode ? (FieldNode) fldRef : null;\n\n\t\tString newResName = getNewResName(resRef, origKeyName, constField);\n\t\tif (!origKeyName.equals(newResName)) {\n\t\t\tresStorage.addRename(resRef, newResName);\n\t\t}\n\n\t\tif (constField != null) {\n\t\t\tfinal String newFieldName = ResNameUtils.convertToRFieldName(newResName);\n\t\t\tconstField.rename(newFieldName);\n\t\t\tconstField.add(AFlag.DONT_RENAME);\n\t\t}\n\n\t\treturn newResName;\n\t}\n\n\tprivate String getNewResName(int resRef, String origKeyName, @Nullable FieldNode constField) {\n\t\tString newResName;\n\t\tif (constField == null || constField.getTopParentClass().isSynthetic()) {\n\t\t\tnewResName = origKeyName;\n\t\t} else {\n\t\t\tnewResName = getBetterName(root.getArgs().getResourceNameSource(), origKeyName, constField.getName());\n\t\t}\n\n\t\tif (root.getArgs().isRenameValid()) {\n\t\t\tfinal boolean allowNonPrintable = !root.getArgs().isRenamePrintable();\n\t\t\tnewResName = ResNameUtils.sanitizeAsResourceName(newResName, String.format(\"_res_0x%08x\", resRef), allowNonPrintable);\n\t\t}\n\n\t\treturn newResName;\n\t}\n\n\tpublic static String getBetterName(ResourceNameSource nameSource, String resName, String codeName) {\n\t\tswitch (nameSource) {\n\t\t\tcase AUTO:\n\t\t\t\treturn BetterName.getBetterResourceName(resName, codeName);\n\t\t\tcase RESOURCES:\n\t\t\t\treturn resName;\n\t\t\tcase CODE:\n\t\t\t\treturn codeName;\n\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unexpected ResourceNameSource value: \" + nameSource);\n\t\t}\n\t}\n\n\tprivate RawNamedValue parseValueMap() throws IOException {\n\t\tint nameRef = is.readInt32();\n\t\treturn new RawNamedValue(nameRef, parseValue());\n\t}\n\n\tprivate RawValue parseValue() throws IOException {\n\t\tis.checkInt16(8, \"value size\");\n\t\tis.checkInt8(0, \"value res0 not 0\");\n\t\tint dataType = is.readInt8();\n\t\tint data = is.readInt32();\n\t\treturn new RawValue(dataType, data);\n\t}\n\n\tprivate EntryConfig parseConfig() throws IOException {\n\t\tlong start = is.getPos();\n\t\tint size = is.readInt32();\n\t\tif (size < 4) {\n\t\t\tthrow new IOException(\"Config size < 4\");\n\t\t}\n\n\t\t// Android zero fill this structure and only read the data present\n\t\tvar configData = new byte[Math.max(52, size - 4)];\n\t\tis.readFully(configData, 0, size - 4);\n\t\tvar configIs = new ParserStream(new ByteArrayInputStream(configData));\n\n\t\tshort mcc = (short) configIs.readInt16();\n\t\tshort mnc = (short) configIs.readInt16();\n\n\t\tchar[] language = unpackLocaleOrRegion((byte) configIs.readInt8(), (byte) configIs.readInt8(), 'a');\n\t\tchar[] country = unpackLocaleOrRegion((byte) configIs.readInt8(), (byte) configIs.readInt8(), '0');\n\n\t\tbyte orientation = (byte) configIs.readInt8();\n\t\tbyte touchscreen = (byte) configIs.readInt8();\n\t\tint density = configIs.readInt16();\n\n\t\tbyte keyboard = (byte) configIs.readInt8();\n\t\tbyte navigation = (byte) configIs.readInt8();\n\t\tbyte inputFlags = (byte) configIs.readInt8();\n\t\tbyte grammaticalInflection = (byte) configIs.readInt8();\n\n\t\tshort screenWidth = (short) configIs.readInt16();\n\t\tshort screenHeight = (short) configIs.readInt16();\n\n\t\tshort sdkVersion = (short) configIs.readInt16();\n\t\tconfigIs.readInt16(); // minorVersion must always be 0\n\n\t\tbyte screenLayout = (byte) configIs.readInt8();\n\t\tbyte uiMode = (byte) configIs.readInt8();\n\t\tshort smallestScreenWidthDp = (short) configIs.readInt16();\n\t\tshort screenWidthDp = (short) configIs.readInt16();\n\t\tshort screenHeightDp = (short) configIs.readInt16();\n\n\t\tchar[] localeScript = readScriptOrVariantChar(4, configIs).toCharArray();\n\t\tchar[] localeVariant = readScriptOrVariantChar(8, configIs).toCharArray();\n\n\t\tbyte screenLayout2 = (byte) configIs.readInt8();\n\t\tbyte colorMode = (byte) configIs.readInt8();\n\t\tconfigIs.readInt16(); // reserved padding\n\n\t\tis.checkPos(start + size, \"Config skip trailing bytes\");\n\n\t\treturn new EntryConfig(mcc, mnc, language, country,\n\t\t\t\torientation, touchscreen, density, keyboard, navigation,\n\t\t\t\tinputFlags, grammaticalInflection, screenWidth, screenHeight, sdkVersion,\n\t\t\t\tscreenLayout, uiMode, smallestScreenWidthDp, screenWidthDp,\n\t\t\t\tscreenHeightDp,\n\t\t\t\tlocaleScript.length == 0 ? null : localeScript,\n\t\t\t\tlocaleVariant.length == 0 ? null : localeVariant,\n\t\t\t\tscreenLayout2,\n\t\t\t\tcolorMode, false, size);\n\t}\n\n\tprivate char[] unpackLocaleOrRegion(byte in0, byte in1, char base) {\n\t\t// check high bit, if so we have a packed 3 letter code\n\t\tif (((in0 >> 7) & 1) == 1) {\n\t\t\tint first = in1 & 0x1F;\n\t\t\tint second = ((in1 & 0xE0) >> 5) + ((in0 & 0x03) << 3);\n\t\t\tint third = (in0 & 0x7C) >> 2;\n\n\t\t\t// since this function handles languages & regions, we add the value(s) to the base char\n\t\t\t// which is usually 'a' or '0' depending on language or region.\n\t\t\treturn new char[] { (char) (first + base), (char) (second + base), (char) (third + base) };\n\t\t}\n\t\treturn new char[] { (char) in0, (char) in1 };\n\t}\n\n\tprivate String readScriptOrVariantChar(int length) throws IOException {\n\t\treturn readScriptOrVariantChar(length, is);\n\t}\n\n\tprivate static String readScriptOrVariantChar(int length, ParserStream ps) throws IOException {\n\t\tlong start = ps.getPos();\n\t\tStringBuilder sb = new StringBuilder(16);\n\t\tfor (int i = 0; i < length; i++) {\n\t\t\tshort ch = (short) ps.readInt8();\n\t\t\tif (ch == 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tsb.append((char) ch);\n\t\t}\n\t\tps.skipToPos(start + length, \"readScriptOrVariantChar\");\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic ResourceStorage getResStorage() {\n\t\treturn resStorage;\n\t}\n\n\t@Override\n\tpublic BinaryXMLStrings getStrings() {\n\t\treturn strings;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/ResTableBinaryParserProvider.java",
    "content": "package jadx.core.xmlgen;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ResourceFile;\nimport jadx.api.plugins.resources.IResTableParserProvider;\nimport jadx.core.dex.nodes.RootNode;\n\npublic class ResTableBinaryParserProvider implements IResTableParserProvider {\n\tprivate RootNode root;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tthis.root = root;\n\t}\n\n\t@Override\n\tpublic @Nullable IResTableParser getParser(ResourceFile resFile) {\n\t\tString fileName = resFile.getOriginalName();\n\t\tif (!fileName.endsWith(\".arsc\")) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new ResTableBinaryParser(root);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/ResXmlGen.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ICodeWriter;\nimport jadx.api.JadxArgs;\nimport jadx.api.impl.SimpleCodeWriter;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.xmlgen.entry.ProtoValue;\nimport jadx.core.xmlgen.entry.RawNamedValue;\nimport jadx.core.xmlgen.entry.ResourceEntry;\nimport jadx.core.xmlgen.entry.ValuesParser;\n\nimport static jadx.core.xmlgen.ParserConstants.PLURALS_MAP;\nimport static jadx.core.xmlgen.ParserConstants.TYPE_REFERENCE;\n\npublic class ResXmlGen {\n\n\t/**\n\t * Skip only file based resource type\n\t */\n\tprivate static final Set<String> SKIP_RES_TYPES = new HashSet<>(Arrays.asList(\n\t\t\t\"anim\",\n\t\t\t\"animator\",\n\t\t\t\"font\",\n\t\t\t\"id\", // skip id type, it is usually auto generated when used this syntax \"@+id/my_id\"\n\t\t\t\"interpolator\",\n\t\t\t\"layout\",\n\t\t\t\"menu\",\n\t\t\t\"mipmap\",\n\t\t\t\"navigation\",\n\t\t\t\"raw\",\n\t\t\t\"transition\",\n\t\t\t\"xml\"));\n\n\tprivate final ResourceStorage resStorage;\n\tprivate final ValuesParser vp;\n\tprivate final ManifestAttributes manifestAttributes;\n\n\tpublic ResXmlGen(ResourceStorage resStorage, ValuesParser vp, ManifestAttributes manifestAttributes) {\n\t\tthis.resStorage = resStorage;\n\t\tthis.vp = vp;\n\t\tthis.manifestAttributes = manifestAttributes;\n\t}\n\n\tpublic List<ResContainer> makeResourcesXml(JadxArgs args) {\n\t\tMap<String, ICodeWriter> contMap = new HashMap<>();\n\t\tfor (ResourceEntry ri : resStorage.getResources()) {\n\t\t\tif (SKIP_RES_TYPES.contains(ri.getTypeName())) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString fn = getFileName(ri);\n\t\t\tICodeWriter cw = contMap.get(fn);\n\t\t\tif (cw == null) {\n\t\t\t\tcw = new SimpleCodeWriter(args);\n\t\t\t\tcw.add(\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\");\n\t\t\t\tcw.startLine(\"<resources>\");\n\t\t\t\tcw.incIndent();\n\t\t\t\tcontMap.put(fn, cw);\n\t\t\t}\n\t\t\taddValue(cw, ri);\n\t\t}\n\n\t\tList<ResContainer> files = new ArrayList<>(contMap.size());\n\t\tfor (Map.Entry<String, ICodeWriter> entry : contMap.entrySet()) {\n\t\t\tString fileName = entry.getKey();\n\t\t\tICodeWriter content = entry.getValue();\n\t\t\tcontent.decIndent();\n\t\t\tcontent.startLine(\"</resources>\");\n\t\t\tICodeInfo codeInfo = content.finish();\n\t\t\tfiles.add(ResContainer.textResource(fileName, codeInfo));\n\t\t}\n\t\tCollections.sort(files);\n\t\treturn files;\n\t}\n\n\tprivate void addValue(ICodeWriter cw, ResourceEntry ri) {\n\t\tif (ri.getProtoValue() != null) {\n\t\t\tProtoValue protoValue = ri.getProtoValue();\n\t\t\tif (protoValue.getValue() != null && protoValue.getNamedValues() == null) {\n\t\t\t\taddSimpleValue(cw, ri.getTypeName(), ri.getTypeName(), \"name\", ri.getKeyName(), protoValue.getValue());\n\t\t\t} else {\n\t\t\t\tcw.startLine();\n\t\t\t\tcw.add('<').add(ri.getTypeName()).add(' ');\n\t\t\t\tString itemTag = \"item\";\n\t\t\t\tcw.add(\"name=\\\"\").add(ri.getKeyName()).add('\\\"');\n\t\t\t\tif (ri.getTypeName().equals(\"attr\") && protoValue.getValue() != null) {\n\t\t\t\t\tcw.add(\" format=\\\"\").add(protoValue.getValue()).add('\\\"');\n\t\t\t\t}\n\t\t\t\tif (protoValue.getParent() != null) {\n\t\t\t\t\tcw.add(\" parent=\\\"\").add(protoValue.getParent()).add('\\\"');\n\t\t\t\t}\n\t\t\t\tcw.add(\">\");\n\n\t\t\t\tcw.incIndent();\n\t\t\t\tfor (ProtoValue value : protoValue.getNamedValues()) {\n\t\t\t\t\taddProtoItem(cw, itemTag, ri.getTypeName(), value);\n\t\t\t\t}\n\t\t\t\tcw.decIndent();\n\t\t\t\tcw.startLine().add(\"</\").add(ri.getTypeName()).add('>');\n\t\t\t}\n\t\t} else if (ri.getSimpleValue() != null) {\n\t\t\tString valueStr = vp.decodeValue(ri.getSimpleValue());\n\t\t\taddSimpleValue(cw, ri.getTypeName(), ri.getTypeName(), \"name\", ri.getKeyName(), valueStr);\n\t\t} else {\n\t\t\tboolean skipNamedValues = false;\n\t\t\tcw.startLine();\n\t\t\tcw.add('<').add(ri.getTypeName()).add(\" name=\\\"\");\n\t\t\tString itemTag = \"item\";\n\t\t\tif (ri.getTypeName().equals(\"attr\") && !ri.getNamedValues().isEmpty()) {\n\t\t\t\tcw.add(ri.getKeyName());\n\t\t\t\tint type = ri.getNamedValues().get(0).getRawValue().getData();\n\t\t\t\tif ((type & ValuesParser.ATTR_TYPE_ENUM) != 0) {\n\t\t\t\t\titemTag = \"enum\";\n\t\t\t\t} else if ((type & ValuesParser.ATTR_TYPE_FLAGS) != 0) {\n\t\t\t\t\titemTag = \"flag\";\n\t\t\t\t}\n\t\t\t\tString formatValue = XmlGenUtils.getAttrTypeAsString(type);\n\t\t\t\tif (formatValue != null) {\n\t\t\t\t\tcw.add(\"\\\" format=\\\"\").add(formatValue);\n\t\t\t\t}\n\t\t\t\tif (ri.getNamedValues().size() > 1) {\n\t\t\t\t\tfor (RawNamedValue rv : ri.getNamedValues()) {\n\t\t\t\t\t\tif (rv.getNameRef() == ParserConstants.ATTR_MIN) {\n\t\t\t\t\t\t\tcw.add(\"\\\" min=\\\"\").add(String.valueOf(rv.getRawValue().getData()));\n\t\t\t\t\t\t\tskipNamedValues = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcw.add(ri.getKeyName());\n\t\t\t}\n\t\t\tif (ri.getTypeName().equals(\"style\") || ri.getParentRef() != 0) {\n\t\t\t\tcw.add(\"\\\" parent=\\\"\");\n\t\t\t\tif (ri.getParentRef() != 0) {\n\t\t\t\t\tString parent = vp.decodeValue(TYPE_REFERENCE, ri.getParentRef());\n\t\t\t\t\tcw.add(parent);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcw.add(\"\\\">\");\n\n\t\t\tif (!skipNamedValues) {\n\t\t\t\tcw.incIndent();\n\t\t\t\tfor (RawNamedValue value : ri.getNamedValues()) {\n\t\t\t\t\taddItem(cw, itemTag, ri.getTypeName(), value);\n\t\t\t\t}\n\t\t\t\tcw.decIndent();\n\t\t\t}\n\t\t\tcw.startLine().add(\"</\").add(ri.getTypeName()).add('>');\n\t\t}\n\t}\n\n\tprivate void addProtoItem(ICodeWriter cw, String itemTag, String typeName, ProtoValue protoValue) {\n\t\tString name = protoValue.getName();\n\t\tString value = protoValue.getValue();\n\t\tswitch (typeName) {\n\t\t\tcase \"attr\":\n\t\t\t\tif (name != null) {\n\t\t\t\t\taddSimpleValue(cw, typeName, itemTag, name, value, \"\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"style\":\n\t\t\t\tif (name != null) {\n\t\t\t\t\taddSimpleValue(cw, typeName, itemTag, name, \"\", value);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"plurals\":\n\t\t\t\taddSimpleValue(cw, typeName, itemTag, \"quantity\", name, value);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\taddSimpleValue(cw, typeName, itemTag, null, null, value);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate void addItem(ICodeWriter cw, String itemTag, String typeName, RawNamedValue value) {\n\t\tString nameStr = vp.decodeNameRef(value.getNameRef());\n\t\tString valueStr = vp.decodeValue(value.getRawValue());\n\t\tint dataType = value.getRawValue().getDataType();\n\n\t\tif (!typeName.equals(\"attr\")) {\n\t\t\tif (dataType == ParserConstants.TYPE_REFERENCE && (valueStr == null || valueStr.equals(\"0\"))) {\n\t\t\t\tvalueStr = \"@null\";\n\t\t\t}\n\t\t\tif (dataType == ParserConstants.TYPE_INT_DEC && nameStr != null) {\n\t\t\t\ttry {\n\t\t\t\t\tint intVal = Integer.parseInt(valueStr);\n\t\t\t\t\tString newVal = manifestAttributes.decode(nameStr.replace(\"android:\", \"\").replace(\"attr.\", \"\"), intVal);\n\t\t\t\t\tif (newVal != null) {\n\t\t\t\t\t\tvalueStr = newVal;\n\t\t\t\t\t}\n\t\t\t\t} catch (NumberFormatException e) {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (dataType == ParserConstants.TYPE_INT_HEX && nameStr != null) {\n\t\t\t\ttry {\n\t\t\t\t\tint intVal = Integer.decode(valueStr);\n\t\t\t\t\tString newVal = manifestAttributes.decode(nameStr.replace(\"android:\", \"\").replace(\"attr.\", \"\"), intVal);\n\t\t\t\t\tif (newVal != null) {\n\t\t\t\t\t\tvalueStr = newVal;\n\t\t\t\t\t}\n\t\t\t\t} catch (NumberFormatException e) {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tswitch (typeName) {\n\t\t\tcase \"attr\":\n\t\t\t\tif (nameStr != null) {\n\t\t\t\t\taddSimpleValue(cw, typeName, itemTag, nameStr, valueStr, \"\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"style\":\n\t\t\t\tif (nameStr != null) {\n\t\t\t\t\taddSimpleValue(cw, typeName, itemTag, nameStr, \"\", valueStr);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"plurals\":\n\t\t\t\tfinal String quantity = PLURALS_MAP.get(value.getNameRef());\n\t\t\t\taddSimpleValue(cw, typeName, itemTag, \"quantity\", quantity, valueStr);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\taddSimpleValue(cw, typeName, itemTag, null, null, valueStr);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate void addSimpleValue(ICodeWriter cw, String typeName, String itemTag, String attrName, String attrValue, String valueStr) {\n\t\tif (valueStr == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (valueStr.startsWith(\"res/\")) {\n\t\t\t// remove duplicated resources.\n\t\t\treturn;\n\t\t}\n\t\tcw.startLine();\n\t\tcw.add('<').add(itemTag);\n\t\tif (attrName != null && attrValue != null) {\n\t\t\tif (typeName.equals(\"attr\")) {\n\t\t\t\tcw.add(' ').add(\"name=\\\"\").add(attrName.replace(\"id.\", \"\")).add(\"\\\" value=\\\"\").add(attrValue).add('\"');\n\t\t\t} else if (typeName.equals(\"style\")) {\n\t\t\t\tcw.add(' ').add(\"name=\\\"\").add(attrName.replace(\"attr.\", \"\")).add('\"');\n\t\t\t} else {\n\t\t\t\tcw.add(' ').add(attrName).add(\"=\\\"\").add(attrValue).add('\"');\n\t\t\t}\n\t\t}\n\n\t\tif (itemTag.equals(\"string\") && valueStr.contains(\"%\") && StringFormattedCheck.hasMultipleNonPositionalSubstitutions(valueStr)) {\n\t\t\tcw.add(\" formatted=\\\"false\\\"\");\n\t\t}\n\n\t\tif (valueStr.isEmpty()) {\n\t\t\tcw.add(\" />\");\n\t\t} else {\n\t\t\tcw.add('>');\n\t\t\tif (itemTag.equals(\"string\") || (typeName.equals(\"array\") && valueStr.charAt(0) != '@')) {\n\t\t\t\tcw.add(StringUtils.escapeResStrValue(valueStr));\n\t\t\t} else {\n\t\t\t\tcw.add(StringUtils.escapeResValue(valueStr));\n\t\t\t}\n\t\t\tcw.add(\"</\").add(itemTag).add('>');\n\t\t}\n\t}\n\n\tprivate String getFileName(ResourceEntry ri) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tString qualifiers = ri.getConfig();\n\t\tsb.append(\"res/values\");\n\t\tif (!qualifiers.isEmpty()) {\n\t\t\tsb.append(qualifiers);\n\t\t}\n\t\tsb.append('/');\n\t\tsb.append(ri.getTypeName());\n\t\tif (!ri.getTypeName().endsWith(\"s\")) {\n\t\t\tsb.append('s');\n\t\t}\n\t\tsb.append(\".xml\");\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/ResourceStorage.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\nimport jadx.api.security.IJadxSecurity;\nimport jadx.core.xmlgen.entry.ResourceEntry;\n\npublic class ResourceStorage {\n\tprivate static final Comparator<ResourceEntry> RES_ENTRY_NAME_COMPARATOR = Comparator\n\t\t\t.comparing(ResourceEntry::getConfig)\n\t\t\t.thenComparing(ResourceEntry::getTypeName)\n\t\t\t.thenComparing(ResourceEntry::getKeyName);\n\n\tprivate final List<ResourceEntry> list = new ArrayList<>();\n\tprivate final IJadxSecurity security;\n\n\tprivate String appPackage;\n\n\t/**\n\t * Names in one config and type must be unique\n\t */\n\tprivate final Map<ResourceEntry, ResourceEntry> uniqNameEntries = new TreeMap<>(RES_ENTRY_NAME_COMPARATOR);\n\n\t/**\n\t * Preserve same name for same id across different configs\n\t */\n\tprivate final Map<Integer, String> renames = new HashMap<>();\n\n\tpublic ResourceStorage(IJadxSecurity security) {\n\t\tthis.security = security;\n\t}\n\n\tpublic void add(ResourceEntry resEntry) {\n\t\tlist.add(resEntry);\n\t\tuniqNameEntries.put(resEntry, resEntry);\n\t}\n\n\tpublic void replace(ResourceEntry prevResEntry, ResourceEntry newResEntry) {\n\t\tint idx = list.indexOf(prevResEntry);\n\t\tif (idx != -1) {\n\t\t\tlist.set(idx, newResEntry);\n\t\t}\n\t\t// don't remove from unique names so old name stays occupied\n\t}\n\n\tpublic void addRename(ResourceEntry entry) {\n\t\taddRename(entry.getId(), entry.getKeyName());\n\t}\n\n\tpublic void addRename(int id, String keyName) {\n\t\trenames.put(id, keyName);\n\t}\n\n\tpublic String getRename(int id) {\n\t\treturn renames.get(id);\n\t}\n\n\tpublic ResourceEntry searchEntryWithSameName(ResourceEntry resourceEntry) {\n\t\treturn uniqNameEntries.get(resourceEntry);\n\t}\n\n\tpublic void finish() {\n\t\tlist.sort(Comparator.comparingInt(ResourceEntry::getId));\n\t\tuniqNameEntries.clear();\n\t\trenames.clear();\n\t}\n\n\tpublic int size() {\n\t\treturn list.size();\n\t}\n\n\tpublic Iterable<ResourceEntry> getResources() {\n\t\treturn list;\n\t}\n\n\tpublic String getAppPackage() {\n\t\treturn appPackage;\n\t}\n\n\tpublic void setAppPackage(String appPackage) {\n\t\tthis.appPackage = security.verifyAppPackage(appPackage);\n\t}\n\n\tpublic Map<Integer, String> getResourcesNames() {\n\t\tMap<Integer, String> map = new HashMap<>();\n\t\tfor (ResourceEntry entry : list) {\n\t\t\tmap.put(entry.getId(), entry.getTypeName() + '/' + entry.getKeyName());\n\t\t}\n\t\treturn map;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/ResourcesSaver.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.io.File;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.StandardCopyOption;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxDecompiler;\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourcesLoader;\nimport jadx.api.security.IJadxSecurity;\nimport jadx.core.dex.visitors.SaveCode;\nimport jadx.core.utils.exceptions.JadxException;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\n\npublic class ResourcesSaver implements Runnable {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ResourcesSaver.class);\n\n\tprivate final ResourceFile resourceFile;\n\tprivate final File outDir;\n\tprivate final IJadxSecurity security;\n\n\tpublic ResourcesSaver(JadxDecompiler decompiler, File outDir, ResourceFile resourceFile) {\n\t\tthis.resourceFile = resourceFile;\n\t\tthis.outDir = outDir;\n\t\tthis.security = decompiler.getArgs().getSecurity();\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\ttry {\n\t\t\tsaveResources(resourceFile.loadContent());\n\t\t} catch (StackOverflowError | Exception e) {\n\t\t\tLOG.warn(\"Failed to save resource: {}\", resourceFile.getOriginalName(), e);\n\t\t}\n\t}\n\n\tprivate void saveResources(ResContainer rc) {\n\t\tif (rc == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (rc.getDataType() == ResContainer.DataType.RES_TABLE) {\n\t\t\tsaveToFile(rc, new File(outDir, \"res/values/public.xml\"));\n\t\t\tfor (ResContainer subFile : rc.getSubFiles()) {\n\t\t\t\tsaveResources(subFile);\n\t\t\t}\n\t\t} else {\n\t\t\tsave(rc, outDir);\n\t\t}\n\t}\n\n\tprivate void save(ResContainer rc, File outDir) {\n\t\tFile outFile = new File(outDir, rc.getFileName());\n\t\tif (!security.isInSubDirectory(outDir, outFile)) {\n\t\t\tLOG.error(\"Invalid resource name or path traversal attack detected: {}\", outFile.getPath());\n\t\t\treturn;\n\t\t}\n\t\tsaveToFile(rc, outFile);\n\t}\n\n\tprivate void saveToFile(ResContainer rc, File outFile) {\n\t\tswitch (rc.getDataType()) {\n\t\t\tcase TEXT:\n\t\t\tcase RES_TABLE:\n\t\t\t\tSaveCode.save(rc.getText(), outFile);\n\t\t\t\treturn;\n\n\t\t\tcase DECODED_DATA:\n\t\t\t\tbyte[] data = rc.getDecodedData();\n\t\t\t\tFileUtils.makeDirsForFile(outFile);\n\t\t\t\ttry {\n\t\t\t\t\tFiles.write(outFile.toPath(), data);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.warn(\"Resource '{}' not saved, got exception\", rc.getName(), e);\n\t\t\t\t}\n\t\t\t\treturn;\n\n\t\t\tcase RES_LINK:\n\t\t\t\tResourceFile resFile = rc.getResLink();\n\t\t\t\tFileUtils.makeDirsForFile(outFile);\n\t\t\t\ttry {\n\t\t\t\t\tsaveResourceFile(resFile, outFile);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.warn(\"Resource '{}' not saved, got exception\", rc.getName(), e);\n\t\t\t\t}\n\t\t\t\treturn;\n\n\t\t\tdefault:\n\t\t\t\tLOG.warn(\"Resource '{}' not saved, unknown type\", rc.getName());\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate void saveResourceFile(ResourceFile resFile, File outFile) throws JadxException {\n\t\tResourcesLoader.decodeStream(resFile, (size, is) -> {\n\t\t\tPath target = outFile.toPath();\n\t\t\ttry {\n\t\t\t\tFiles.copy(is, target, StandardCopyOption.REPLACE_EXISTING);\n\t\t\t} catch (Exception e) {\n\t\t\t\tFiles.deleteIfExists(target); // delete partially written file\n\t\t\t\tthrow new JadxRuntimeException(\"Resource file save error\", e);\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/StringFormattedCheck.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\n/*\n * This class contains source code form https://github.com/iBotPeaches/Apktool/\n * see:\n * https://github.com/iBotPeaches/Apktool/blob/master/brut.apktool/apktool-lib/src/main/java/brut/\n * androlib/res/xml/ResXmlEncoders.java\n */\npublic class StringFormattedCheck {\n\n\tpublic static boolean hasMultipleNonPositionalSubstitutions(String str) {\n\t\tDuo<List<Integer>, List<Integer>> tuple = findSubstitutions(str, 4);\n\t\treturn !tuple.m1.isEmpty() && tuple.m1.size() + tuple.m2.size() > 1;\n\t}\n\n\t@SuppressWarnings(\"checkstyle:ClassTypeParameterName\")\n\tprivate static class Duo<T1, T2> {\n\t\tpublic final T1 m1;\n\t\tpublic final T2 m2;\n\n\t\tpublic Duo(T1 t1, T2 t2) {\n\t\t\tthis.m1 = t1;\n\t\t\tthis.m2 = t2;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tif (obj == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (getClass() != obj.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\tfinal Duo<T1, T2> other = (Duo<T1, T2>) obj;\n\t\t\tif (!Objects.equals(this.m1, other.m1)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn Objects.equals(this.m2, other.m2);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\tint hash = 3;\n\t\t\thash = 71 * hash + (this.m1 != null ? this.m1.hashCode() : 0);\n\t\t\thash = 71 * hash + (this.m2 != null ? this.m2.hashCode() : 0);\n\t\t\treturn hash;\n\t\t}\n\t}\n\n\t/**\n\t * It returns a tuple of:\n\t * - a list of offsets of non positional substitutions. non-pos is defined as any \"%\" which isn't\n\t * \"%%\" nor \"%\\d+\\$\"\n\t * - a list of offsets of positional substitutions\n\t */\n\t@SuppressWarnings({ \"checkstyle:NeedBraces\", \"checkstyle:EmptyStatement\" })\n\tprivate static Duo<List<Integer>, List<Integer>> findSubstitutions(String str, int nonPosMax) {\n\t\tif (nonPosMax == -1) {\n\t\t\tnonPosMax = Integer.MAX_VALUE;\n\t\t}\n\t\tint pos;\n\t\tint pos2 = 0;\n\t\tList<Integer> nonPositional = new ArrayList<>();\n\t\tList<Integer> positional = new ArrayList<>();\n\n\t\tif (str == null) {\n\t\t\treturn new Duo<>(nonPositional, positional);\n\t\t}\n\n\t\tint length = str.length();\n\n\t\twhile ((pos = str.indexOf('%', pos2)) != -1) {\n\t\t\tpos2 = pos + 1;\n\t\t\tif (pos2 == length) {\n\t\t\t\tnonPositional.add(pos);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tchar c = str.charAt(pos2++);\n\t\t\tif (c == '%') {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (c >= '0' && c <= '9' && pos2 < length) {\n\t\t\t\twhile ((c = str.charAt(pos2++)) >= '0' && c <= '9' && pos2 < length)\n\t\t\t\t\t;\n\t\t\t\tif (c == '$') {\n\t\t\t\t\tpositional.add(pos);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tnonPositional.add(pos);\n\t\t\tif (nonPositional.size() >= nonPosMax) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn new Duo<>(nonPositional, positional);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/XMLChar.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage jadx.core.xmlgen;\n\nimport java.util.Arrays;\n\n/**\n * This class defines the basic XML character properties. The data\n * in this class can be used to verify that a character is a valid\n * XML character or if the character is a space, name start, or name\n * character.\n * <p>\n * A series of convenience methods are supplied to ease the burden\n * of the developer. Because inlining the checks can improve per\n * character performance, the tables of character properties are\n * public. Using the character as an index into the <code>CHARS</code>\n * array and applying the appropriate mask flag (e.g.\n * <code>MASK_VALID</code>), yields the same results as calling the\n * convenience methods. There is one exception: check the comments\n * for the <code>isValid</code> method for details.\n *\n * @author Glenn Marcy, IBM\n * @author Andy Clark, IBM\n * @author Eric Ye, IBM\n * @author Arnaud Le Hors, IBM\n * @author Michael Glavassevich, IBM\n * @author Rahul Srivastava, Sun Microsystems Inc.\n * @version $Id: XMLChar.java 674378 2008-07-07 00:52:45Z mrglavas $\n */\npublic class XMLChar {\n\n\t//\n\t// Constants\n\t//\n\n\t/**\n\t * Character flags.\n\t */\n\tprivate static final byte[] CHARS = new byte[1 << 16];\n\n\t/**\n\t * Valid character mask.\n\t */\n\tpublic static final int MASK_VALID = 0x01;\n\n\t/**\n\t * Space character mask.\n\t */\n\tpublic static final int MASK_SPACE = 0x02;\n\n\t/**\n\t * Name start character mask.\n\t */\n\tpublic static final int MASK_NAME_START = 0x04;\n\n\t/**\n\t * Name character mask.\n\t */\n\tpublic static final int MASK_NAME = 0x08;\n\n\t/**\n\t * Pubid character mask.\n\t */\n\tpublic static final int MASK_PUBID = 0x10;\n\n\t/**\n\t * Content character mask. Special characters are those that can\n\t * be considered the start of markup, such as '&lt;' and '&amp;'.\n\t * The various newline characters are considered special as well.\n\t * All other valid XML characters can be considered content.\n\t * <p>\n\t * This is an optimization for the inner loop of character scanning.\n\t */\n\tpublic static final int MASK_CONTENT = 0x20;\n\n\t/**\n\t * NCName start character mask.\n\t */\n\tpublic static final int MASK_NCNAME_START = 0x40;\n\n\t/**\n\t * NCName character mask.\n\t */\n\tpublic static final int MASK_NCNAME = 0x80;\n\n\t//\n\t// Static initialization\n\t//\n\n\tstatic {\n\n\t\t// Initializing the Character Flag Array\n\t\t// Code generated by: XMLCharGenerator.\n\n\t\tCHARS[9] = 35;\n\t\tCHARS[10] = 19;\n\t\tCHARS[13] = 19;\n\t\tCHARS[32] = 51;\n\t\tCHARS[33] = 49;\n\t\tCHARS[34] = 33;\n\t\tArrays.fill(CHARS, 35, 38, (byte) 49); // Fill 3 of value (byte) 49\n\t\tCHARS[38] = 1;\n\t\tArrays.fill(CHARS, 39, 45, (byte) 49); // Fill 6 of value (byte) 49\n\t\tArrays.fill(CHARS, 45, 47, (byte) -71); // Fill 2 of value (byte) -71\n\t\tCHARS[47] = 49;\n\t\tArrays.fill(CHARS, 48, 58, (byte) -71); // Fill 10 of value (byte) -71\n\t\tCHARS[58] = 61;\n\t\tCHARS[59] = 49;\n\t\tCHARS[60] = 1;\n\t\tCHARS[61] = 49;\n\t\tCHARS[62] = 33;\n\t\tArrays.fill(CHARS, 63, 65, (byte) 49); // Fill 2 of value (byte) 49\n\t\tArrays.fill(CHARS, 65, 91, (byte) -3); // Fill 26 of value (byte) -3\n\t\tArrays.fill(CHARS, 91, 93, (byte) 33); // Fill 2 of value (byte) 33\n\t\tCHARS[93] = 1;\n\t\tCHARS[94] = 33;\n\t\tCHARS[95] = -3;\n\t\tCHARS[96] = 33;\n\t\tArrays.fill(CHARS, 97, 123, (byte) -3); // Fill 26 of value (byte) -3\n\t\tArrays.fill(CHARS, 123, 183, (byte) 33); // Fill 60 of value (byte) 33\n\t\tCHARS[183] = -87;\n\t\tArrays.fill(CHARS, 184, 192, (byte) 33); // Fill 8 of value (byte) 33\n\t\tArrays.fill(CHARS, 192, 215, (byte) -19); // Fill 23 of value (byte) -19\n\t\tCHARS[215] = 33;\n\t\tArrays.fill(CHARS, 216, 247, (byte) -19); // Fill 31 of value (byte) -19\n\t\tCHARS[247] = 33;\n\t\tArrays.fill(CHARS, 248, 306, (byte) -19); // Fill 58 of value (byte) -19\n\t\tArrays.fill(CHARS, 306, 308, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 308, 319, (byte) -19); // Fill 11 of value (byte) -19\n\t\tArrays.fill(CHARS, 319, 321, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 321, 329, (byte) -19); // Fill 8 of value (byte) -19\n\t\tCHARS[329] = 33;\n\t\tArrays.fill(CHARS, 330, 383, (byte) -19); // Fill 53 of value (byte) -19\n\t\tCHARS[383] = 33;\n\t\tArrays.fill(CHARS, 384, 452, (byte) -19); // Fill 68 of value (byte) -19\n\t\tArrays.fill(CHARS, 452, 461, (byte) 33); // Fill 9 of value (byte) 33\n\t\tArrays.fill(CHARS, 461, 497, (byte) -19); // Fill 36 of value (byte) -19\n\t\tArrays.fill(CHARS, 497, 500, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 500, 502, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 502, 506, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 506, 536, (byte) -19); // Fill 30 of value (byte) -19\n\t\tArrays.fill(CHARS, 536, 592, (byte) 33); // Fill 56 of value (byte) 33\n\t\tArrays.fill(CHARS, 592, 681, (byte) -19); // Fill 89 of value (byte) -19\n\t\tArrays.fill(CHARS, 681, 699, (byte) 33); // Fill 18 of value (byte) 33\n\t\tArrays.fill(CHARS, 699, 706, (byte) -19); // Fill 7 of value (byte) -19\n\t\tArrays.fill(CHARS, 706, 720, (byte) 33); // Fill 14 of value (byte) 33\n\t\tArrays.fill(CHARS, 720, 722, (byte) -87); // Fill 2 of value (byte) -87\n\t\tArrays.fill(CHARS, 722, 768, (byte) 33); // Fill 46 of value (byte) 33\n\t\tArrays.fill(CHARS, 768, 838, (byte) -87); // Fill 70 of value (byte) -87\n\t\tArrays.fill(CHARS, 838, 864, (byte) 33); // Fill 26 of value (byte) 33\n\t\tArrays.fill(CHARS, 864, 866, (byte) -87); // Fill 2 of value (byte) -87\n\t\tArrays.fill(CHARS, 866, 902, (byte) 33); // Fill 36 of value (byte) 33\n\t\tCHARS[902] = -19;\n\t\tCHARS[903] = -87;\n\t\tArrays.fill(CHARS, 904, 907, (byte) -19); // Fill 3 of value (byte) -19\n\t\tCHARS[907] = 33;\n\t\tCHARS[908] = -19;\n\t\tCHARS[909] = 33;\n\t\tArrays.fill(CHARS, 910, 930, (byte) -19); // Fill 20 of value (byte) -19\n\t\tCHARS[930] = 33;\n\t\tArrays.fill(CHARS, 931, 975, (byte) -19); // Fill 44 of value (byte) -19\n\t\tCHARS[975] = 33;\n\t\tArrays.fill(CHARS, 976, 983, (byte) -19); // Fill 7 of value (byte) -19\n\t\tArrays.fill(CHARS, 983, 986, (byte) 33); // Fill 3 of value (byte) 33\n\t\tCHARS[986] = -19;\n\t\tCHARS[987] = 33;\n\t\tCHARS[988] = -19;\n\t\tCHARS[989] = 33;\n\t\tCHARS[990] = -19;\n\t\tCHARS[991] = 33;\n\t\tCHARS[992] = -19;\n\t\tCHARS[993] = 33;\n\t\tArrays.fill(CHARS, 994, 1012, (byte) -19); // Fill 18 of value (byte) -19\n\t\tArrays.fill(CHARS, 1012, 1025, (byte) 33); // Fill 13 of value (byte) 33\n\t\tArrays.fill(CHARS, 1025, 1037, (byte) -19); // Fill 12 of value (byte) -19\n\t\tCHARS[1037] = 33;\n\t\tArrays.fill(CHARS, 1038, 1104, (byte) -19); // Fill 66 of value (byte) -19\n\t\tCHARS[1104] = 33;\n\t\tArrays.fill(CHARS, 1105, 1117, (byte) -19); // Fill 12 of value (byte) -19\n\t\tCHARS[1117] = 33;\n\t\tArrays.fill(CHARS, 1118, 1154, (byte) -19); // Fill 36 of value (byte) -19\n\t\tCHARS[1154] = 33;\n\t\tArrays.fill(CHARS, 1155, 1159, (byte) -87); // Fill 4 of value (byte) -87\n\t\tArrays.fill(CHARS, 1159, 1168, (byte) 33); // Fill 9 of value (byte) 33\n\t\tArrays.fill(CHARS, 1168, 1221, (byte) -19); // Fill 53 of value (byte) -19\n\t\tArrays.fill(CHARS, 1221, 1223, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 1223, 1225, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 1225, 1227, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 1227, 1229, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 1229, 1232, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 1232, 1260, (byte) -19); // Fill 28 of value (byte) -19\n\t\tArrays.fill(CHARS, 1260, 1262, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 1262, 1270, (byte) -19); // Fill 8 of value (byte) -19\n\t\tArrays.fill(CHARS, 1270, 1272, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 1272, 1274, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 1274, 1329, (byte) 33); // Fill 55 of value (byte) 33\n\t\tArrays.fill(CHARS, 1329, 1367, (byte) -19); // Fill 38 of value (byte) -19\n\t\tArrays.fill(CHARS, 1367, 1369, (byte) 33); // Fill 2 of value (byte) 33\n\t\tCHARS[1369] = -19;\n\t\tArrays.fill(CHARS, 1370, 1377, (byte) 33); // Fill 7 of value (byte) 33\n\t\tArrays.fill(CHARS, 1377, 1415, (byte) -19); // Fill 38 of value (byte) -19\n\t\tArrays.fill(CHARS, 1415, 1425, (byte) 33); // Fill 10 of value (byte) 33\n\t\tArrays.fill(CHARS, 1425, 1442, (byte) -87); // Fill 17 of value (byte) -87\n\t\tCHARS[1442] = 33;\n\t\tArrays.fill(CHARS, 1443, 1466, (byte) -87); // Fill 23 of value (byte) -87\n\t\tCHARS[1466] = 33;\n\t\tArrays.fill(CHARS, 1467, 1470, (byte) -87); // Fill 3 of value (byte) -87\n\t\tCHARS[1470] = 33;\n\t\tCHARS[1471] = -87;\n\t\tCHARS[1472] = 33;\n\t\tArrays.fill(CHARS, 1473, 1475, (byte) -87); // Fill 2 of value (byte) -87\n\t\tCHARS[1475] = 33;\n\t\tCHARS[1476] = -87;\n\t\tArrays.fill(CHARS, 1477, 1488, (byte) 33); // Fill 11 of value (byte) 33\n\t\tArrays.fill(CHARS, 1488, 1515, (byte) -19); // Fill 27 of value (byte) -19\n\t\tArrays.fill(CHARS, 1515, 1520, (byte) 33); // Fill 5 of value (byte) 33\n\t\tArrays.fill(CHARS, 1520, 1523, (byte) -19); // Fill 3 of value (byte) -19\n\t\tArrays.fill(CHARS, 1523, 1569, (byte) 33); // Fill 46 of value (byte) 33\n\t\tArrays.fill(CHARS, 1569, 1595, (byte) -19); // Fill 26 of value (byte) -19\n\t\tArrays.fill(CHARS, 1595, 1600, (byte) 33); // Fill 5 of value (byte) 33\n\t\tCHARS[1600] = -87;\n\t\tArrays.fill(CHARS, 1601, 1611, (byte) -19); // Fill 10 of value (byte) -19\n\t\tArrays.fill(CHARS, 1611, 1619, (byte) -87); // Fill 8 of value (byte) -87\n\t\tArrays.fill(CHARS, 1619, 1632, (byte) 33); // Fill 13 of value (byte) 33\n\t\tArrays.fill(CHARS, 1632, 1642, (byte) -87); // Fill 10 of value (byte) -87\n\t\tArrays.fill(CHARS, 1642, 1648, (byte) 33); // Fill 6 of value (byte) 33\n\t\tCHARS[1648] = -87;\n\t\tArrays.fill(CHARS, 1649, 1720, (byte) -19); // Fill 71 of value (byte) -19\n\t\tArrays.fill(CHARS, 1720, 1722, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 1722, 1727, (byte) -19); // Fill 5 of value (byte) -19\n\t\tCHARS[1727] = 33;\n\t\tArrays.fill(CHARS, 1728, 1743, (byte) -19); // Fill 15 of value (byte) -19\n\t\tCHARS[1743] = 33;\n\t\tArrays.fill(CHARS, 1744, 1748, (byte) -19); // Fill 4 of value (byte) -19\n\t\tCHARS[1748] = 33;\n\t\tCHARS[1749] = -19;\n\t\tArrays.fill(CHARS, 1750, 1765, (byte) -87); // Fill 15 of value (byte) -87\n\t\tArrays.fill(CHARS, 1765, 1767, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 1767, 1769, (byte) -87); // Fill 2 of value (byte) -87\n\t\tCHARS[1769] = 33;\n\t\tArrays.fill(CHARS, 1770, 1774, (byte) -87); // Fill 4 of value (byte) -87\n\t\tArrays.fill(CHARS, 1774, 1776, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 1776, 1786, (byte) -87); // Fill 10 of value (byte) -87\n\t\tArrays.fill(CHARS, 1786, 2305, (byte) 33); // Fill 519 of value (byte) 33\n\t\tArrays.fill(CHARS, 2305, 2308, (byte) -87); // Fill 3 of value (byte) -87\n\t\tCHARS[2308] = 33;\n\t\tArrays.fill(CHARS, 2309, 2362, (byte) -19); // Fill 53 of value (byte) -19\n\t\tArrays.fill(CHARS, 2362, 2364, (byte) 33); // Fill 2 of value (byte) 33\n\t\tCHARS[2364] = -87;\n\t\tCHARS[2365] = -19;\n\t\tArrays.fill(CHARS, 2366, 2382, (byte) -87); // Fill 16 of value (byte) -87\n\t\tArrays.fill(CHARS, 2382, 2385, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 2385, 2389, (byte) -87); // Fill 4 of value (byte) -87\n\t\tArrays.fill(CHARS, 2389, 2392, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 2392, 2402, (byte) -19); // Fill 10 of value (byte) -19\n\t\tArrays.fill(CHARS, 2402, 2404, (byte) -87); // Fill 2 of value (byte) -87\n\t\tArrays.fill(CHARS, 2404, 2406, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 2406, 2416, (byte) -87); // Fill 10 of value (byte) -87\n\t\tArrays.fill(CHARS, 2416, 2433, (byte) 33); // Fill 17 of value (byte) 33\n\t\tArrays.fill(CHARS, 2433, 2436, (byte) -87); // Fill 3 of value (byte) -87\n\t\tCHARS[2436] = 33;\n\t\tArrays.fill(CHARS, 2437, 2445, (byte) -19); // Fill 8 of value (byte) -19\n\t\tArrays.fill(CHARS, 2445, 2447, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 2447, 2449, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 2449, 2451, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 2451, 2473, (byte) -19); // Fill 22 of value (byte) -19\n\t\tCHARS[2473] = 33;\n\t\tArrays.fill(CHARS, 2474, 2481, (byte) -19); // Fill 7 of value (byte) -19\n\t\tCHARS[2481] = 33;\n\t\tCHARS[2482] = -19;\n\t\tArrays.fill(CHARS, 2483, 2486, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 2486, 2490, (byte) -19); // Fill 4 of value (byte) -19\n\t\tArrays.fill(CHARS, 2490, 2492, (byte) 33); // Fill 2 of value (byte) 33\n\t\tCHARS[2492] = -87;\n\t\tCHARS[2493] = 33;\n\t\tArrays.fill(CHARS, 2494, 2501, (byte) -87); // Fill 7 of value (byte) -87\n\t\tArrays.fill(CHARS, 2501, 2503, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 2503, 2505, (byte) -87); // Fill 2 of value (byte) -87\n\t\tArrays.fill(CHARS, 2505, 2507, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 2507, 2510, (byte) -87); // Fill 3 of value (byte) -87\n\t\tArrays.fill(CHARS, 2510, 2519, (byte) 33); // Fill 9 of value (byte) 33\n\t\tCHARS[2519] = -87;\n\t\tArrays.fill(CHARS, 2520, 2524, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 2524, 2526, (byte) -19); // Fill 2 of value (byte) -19\n\t\tCHARS[2526] = 33;\n\t\tArrays.fill(CHARS, 2527, 2530, (byte) -19); // Fill 3 of value (byte) -19\n\t\tArrays.fill(CHARS, 2530, 2532, (byte) -87); // Fill 2 of value (byte) -87\n\t\tArrays.fill(CHARS, 2532, 2534, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 2534, 2544, (byte) -87); // Fill 10 of value (byte) -87\n\t\tArrays.fill(CHARS, 2544, 2546, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 2546, 2562, (byte) 33); // Fill 16 of value (byte) 33\n\t\tCHARS[2562] = -87;\n\t\tArrays.fill(CHARS, 2563, 2565, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 2565, 2571, (byte) -19); // Fill 6 of value (byte) -19\n\t\tArrays.fill(CHARS, 2571, 2575, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 2575, 2577, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 2577, 2579, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 2579, 2601, (byte) -19); // Fill 22 of value (byte) -19\n\t\tCHARS[2601] = 33;\n\t\tArrays.fill(CHARS, 2602, 2609, (byte) -19); // Fill 7 of value (byte) -19\n\t\tCHARS[2609] = 33;\n\t\tArrays.fill(CHARS, 2610, 2612, (byte) -19); // Fill 2 of value (byte) -19\n\t\tCHARS[2612] = 33;\n\t\tArrays.fill(CHARS, 2613, 2615, (byte) -19); // Fill 2 of value (byte) -19\n\t\tCHARS[2615] = 33;\n\t\tArrays.fill(CHARS, 2616, 2618, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 2618, 2620, (byte) 33); // Fill 2 of value (byte) 33\n\t\tCHARS[2620] = -87;\n\t\tCHARS[2621] = 33;\n\t\tArrays.fill(CHARS, 2622, 2627, (byte) -87); // Fill 5 of value (byte) -87\n\t\tArrays.fill(CHARS, 2627, 2631, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 2631, 2633, (byte) -87); // Fill 2 of value (byte) -87\n\t\tArrays.fill(CHARS, 2633, 2635, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 2635, 2638, (byte) -87); // Fill 3 of value (byte) -87\n\t\tArrays.fill(CHARS, 2638, 2649, (byte) 33); // Fill 11 of value (byte) 33\n\t\tArrays.fill(CHARS, 2649, 2653, (byte) -19); // Fill 4 of value (byte) -19\n\t\tCHARS[2653] = 33;\n\t\tCHARS[2654] = -19;\n\t\tArrays.fill(CHARS, 2655, 2662, (byte) 33); // Fill 7 of value (byte) 33\n\t\tArrays.fill(CHARS, 2662, 2674, (byte) -87); // Fill 12 of value (byte) -87\n\t\tArrays.fill(CHARS, 2674, 2677, (byte) -19); // Fill 3 of value (byte) -19\n\t\tArrays.fill(CHARS, 2677, 2689, (byte) 33); // Fill 12 of value (byte) 33\n\t\tArrays.fill(CHARS, 2689, 2692, (byte) -87); // Fill 3 of value (byte) -87\n\t\tCHARS[2692] = 33;\n\t\tArrays.fill(CHARS, 2693, 2700, (byte) -19); // Fill 7 of value (byte) -19\n\t\tCHARS[2700] = 33;\n\t\tCHARS[2701] = -19;\n\t\tCHARS[2702] = 33;\n\t\tArrays.fill(CHARS, 2703, 2706, (byte) -19); // Fill 3 of value (byte) -19\n\t\tCHARS[2706] = 33;\n\t\tArrays.fill(CHARS, 2707, 2729, (byte) -19); // Fill 22 of value (byte) -19\n\t\tCHARS[2729] = 33;\n\t\tArrays.fill(CHARS, 2730, 2737, (byte) -19); // Fill 7 of value (byte) -19\n\t\tCHARS[2737] = 33;\n\t\tArrays.fill(CHARS, 2738, 2740, (byte) -19); // Fill 2 of value (byte) -19\n\t\tCHARS[2740] = 33;\n\t\tArrays.fill(CHARS, 2741, 2746, (byte) -19); // Fill 5 of value (byte) -19\n\t\tArrays.fill(CHARS, 2746, 2748, (byte) 33); // Fill 2 of value (byte) 33\n\t\tCHARS[2748] = -87;\n\t\tCHARS[2749] = -19;\n\t\tArrays.fill(CHARS, 2750, 2758, (byte) -87); // Fill 8 of value (byte) -87\n\t\tCHARS[2758] = 33;\n\t\tArrays.fill(CHARS, 2759, 2762, (byte) -87); // Fill 3 of value (byte) -87\n\t\tCHARS[2762] = 33;\n\t\tArrays.fill(CHARS, 2763, 2766, (byte) -87); // Fill 3 of value (byte) -87\n\t\tArrays.fill(CHARS, 2766, 2784, (byte) 33); // Fill 18 of value (byte) 33\n\t\tCHARS[2784] = -19;\n\t\tArrays.fill(CHARS, 2785, 2790, (byte) 33); // Fill 5 of value (byte) 33\n\t\tArrays.fill(CHARS, 2790, 2800, (byte) -87); // Fill 10 of value (byte) -87\n\t\tArrays.fill(CHARS, 2800, 2817, (byte) 33); // Fill 17 of value (byte) 33\n\t\tArrays.fill(CHARS, 2817, 2820, (byte) -87); // Fill 3 of value (byte) -87\n\t\tCHARS[2820] = 33;\n\t\tArrays.fill(CHARS, 2821, 2829, (byte) -19); // Fill 8 of value (byte) -19\n\t\tArrays.fill(CHARS, 2829, 2831, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 2831, 2833, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 2833, 2835, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 2835, 2857, (byte) -19); // Fill 22 of value (byte) -19\n\t\tCHARS[2857] = 33;\n\t\tArrays.fill(CHARS, 2858, 2865, (byte) -19); // Fill 7 of value (byte) -19\n\t\tCHARS[2865] = 33;\n\t\tArrays.fill(CHARS, 2866, 2868, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 2868, 2870, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 2870, 2874, (byte) -19); // Fill 4 of value (byte) -19\n\t\tArrays.fill(CHARS, 2874, 2876, (byte) 33); // Fill 2 of value (byte) 33\n\t\tCHARS[2876] = -87;\n\t\tCHARS[2877] = -19;\n\t\tArrays.fill(CHARS, 2878, 2884, (byte) -87); // Fill 6 of value (byte) -87\n\t\tArrays.fill(CHARS, 2884, 2887, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 2887, 2889, (byte) -87); // Fill 2 of value (byte) -87\n\t\tArrays.fill(CHARS, 2889, 2891, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 2891, 2894, (byte) -87); // Fill 3 of value (byte) -87\n\t\tArrays.fill(CHARS, 2894, 2902, (byte) 33); // Fill 8 of value (byte) 33\n\t\tArrays.fill(CHARS, 2902, 2904, (byte) -87); // Fill 2 of value (byte) -87\n\t\tArrays.fill(CHARS, 2904, 2908, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 2908, 2910, (byte) -19); // Fill 2 of value (byte) -19\n\t\tCHARS[2910] = 33;\n\t\tArrays.fill(CHARS, 2911, 2914, (byte) -19); // Fill 3 of value (byte) -19\n\t\tArrays.fill(CHARS, 2914, 2918, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 2918, 2928, (byte) -87); // Fill 10 of value (byte) -87\n\t\tArrays.fill(CHARS, 2928, 2946, (byte) 33); // Fill 18 of value (byte) 33\n\t\tArrays.fill(CHARS, 2946, 2948, (byte) -87); // Fill 2 of value (byte) -87\n\t\tCHARS[2948] = 33;\n\t\tArrays.fill(CHARS, 2949, 2955, (byte) -19); // Fill 6 of value (byte) -19\n\t\tArrays.fill(CHARS, 2955, 2958, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 2958, 2961, (byte) -19); // Fill 3 of value (byte) -19\n\t\tCHARS[2961] = 33;\n\t\tArrays.fill(CHARS, 2962, 2966, (byte) -19); // Fill 4 of value (byte) -19\n\t\tArrays.fill(CHARS, 2966, 2969, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 2969, 2971, (byte) -19); // Fill 2 of value (byte) -19\n\t\tCHARS[2971] = 33;\n\t\tCHARS[2972] = -19;\n\t\tCHARS[2973] = 33;\n\t\tArrays.fill(CHARS, 2974, 2976, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 2976, 2979, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 2979, 2981, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 2981, 2984, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 2984, 2987, (byte) -19); // Fill 3 of value (byte) -19\n\t\tArrays.fill(CHARS, 2987, 2990, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 2990, 2998, (byte) -19); // Fill 8 of value (byte) -19\n\t\tCHARS[2998] = 33;\n\t\tArrays.fill(CHARS, 2999, 3002, (byte) -19); // Fill 3 of value (byte) -19\n\t\tArrays.fill(CHARS, 3002, 3006, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 3006, 3011, (byte) -87); // Fill 5 of value (byte) -87\n\t\tArrays.fill(CHARS, 3011, 3014, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 3014, 3017, (byte) -87); // Fill 3 of value (byte) -87\n\t\tCHARS[3017] = 33;\n\t\tArrays.fill(CHARS, 3018, 3022, (byte) -87); // Fill 4 of value (byte) -87\n\t\tArrays.fill(CHARS, 3022, 3031, (byte) 33); // Fill 9 of value (byte) 33\n\t\tCHARS[3031] = -87;\n\t\tArrays.fill(CHARS, 3032, 3047, (byte) 33); // Fill 15 of value (byte) 33\n\t\tArrays.fill(CHARS, 3047, 3056, (byte) -87); // Fill 9 of value (byte) -87\n\t\tArrays.fill(CHARS, 3056, 3073, (byte) 33); // Fill 17 of value (byte) 33\n\t\tArrays.fill(CHARS, 3073, 3076, (byte) -87); // Fill 3 of value (byte) -87\n\t\tCHARS[3076] = 33;\n\t\tArrays.fill(CHARS, 3077, 3085, (byte) -19); // Fill 8 of value (byte) -19\n\t\tCHARS[3085] = 33;\n\t\tArrays.fill(CHARS, 3086, 3089, (byte) -19); // Fill 3 of value (byte) -19\n\t\tCHARS[3089] = 33;\n\t\tArrays.fill(CHARS, 3090, 3113, (byte) -19); // Fill 23 of value (byte) -19\n\t\tCHARS[3113] = 33;\n\t\tArrays.fill(CHARS, 3114, 3124, (byte) -19); // Fill 10 of value (byte) -19\n\t\tCHARS[3124] = 33;\n\t\tArrays.fill(CHARS, 3125, 3130, (byte) -19); // Fill 5 of value (byte) -19\n\t\tArrays.fill(CHARS, 3130, 3134, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 3134, 3141, (byte) -87); // Fill 7 of value (byte) -87\n\t\tCHARS[3141] = 33;\n\t\tArrays.fill(CHARS, 3142, 3145, (byte) -87); // Fill 3 of value (byte) -87\n\t\tCHARS[3145] = 33;\n\t\tArrays.fill(CHARS, 3146, 3150, (byte) -87); // Fill 4 of value (byte) -87\n\t\tArrays.fill(CHARS, 3150, 3157, (byte) 33); // Fill 7 of value (byte) 33\n\t\tArrays.fill(CHARS, 3157, 3159, (byte) -87); // Fill 2 of value (byte) -87\n\t\tArrays.fill(CHARS, 3159, 3168, (byte) 33); // Fill 9 of value (byte) 33\n\t\tArrays.fill(CHARS, 3168, 3170, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 3170, 3174, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 3174, 3184, (byte) -87); // Fill 10 of value (byte) -87\n\t\tArrays.fill(CHARS, 3184, 3202, (byte) 33); // Fill 18 of value (byte) 33\n\t\tArrays.fill(CHARS, 3202, 3204, (byte) -87); // Fill 2 of value (byte) -87\n\t\tCHARS[3204] = 33;\n\t\tArrays.fill(CHARS, 3205, 3213, (byte) -19); // Fill 8 of value (byte) -19\n\t\tCHARS[3213] = 33;\n\t\tArrays.fill(CHARS, 3214, 3217, (byte) -19); // Fill 3 of value (byte) -19\n\t\tCHARS[3217] = 33;\n\t\tArrays.fill(CHARS, 3218, 3241, (byte) -19); // Fill 23 of value (byte) -19\n\t\tCHARS[3241] = 33;\n\t\tArrays.fill(CHARS, 3242, 3252, (byte) -19); // Fill 10 of value (byte) -19\n\t\tCHARS[3252] = 33;\n\t\tArrays.fill(CHARS, 3253, 3258, (byte) -19); // Fill 5 of value (byte) -19\n\t\tArrays.fill(CHARS, 3258, 3262, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 3262, 3269, (byte) -87); // Fill 7 of value (byte) -87\n\t\tCHARS[3269] = 33;\n\t\tArrays.fill(CHARS, 3270, 3273, (byte) -87); // Fill 3 of value (byte) -87\n\t\tCHARS[3273] = 33;\n\t\tArrays.fill(CHARS, 3274, 3278, (byte) -87); // Fill 4 of value (byte) -87\n\t\tArrays.fill(CHARS, 3278, 3285, (byte) 33); // Fill 7 of value (byte) 33\n\t\tArrays.fill(CHARS, 3285, 3287, (byte) -87); // Fill 2 of value (byte) -87\n\t\tArrays.fill(CHARS, 3287, 3294, (byte) 33); // Fill 7 of value (byte) 33\n\t\tCHARS[3294] = -19;\n\t\tCHARS[3295] = 33;\n\t\tArrays.fill(CHARS, 3296, 3298, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 3298, 3302, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 3302, 3312, (byte) -87); // Fill 10 of value (byte) -87\n\t\tArrays.fill(CHARS, 3312, 3330, (byte) 33); // Fill 18 of value (byte) 33\n\t\tArrays.fill(CHARS, 3330, 3332, (byte) -87); // Fill 2 of value (byte) -87\n\t\tCHARS[3332] = 33;\n\t\tArrays.fill(CHARS, 3333, 3341, (byte) -19); // Fill 8 of value (byte) -19\n\t\tCHARS[3341] = 33;\n\t\tArrays.fill(CHARS, 3342, 3345, (byte) -19); // Fill 3 of value (byte) -19\n\t\tCHARS[3345] = 33;\n\t\tArrays.fill(CHARS, 3346, 3369, (byte) -19); // Fill 23 of value (byte) -19\n\t\tCHARS[3369] = 33;\n\t\tArrays.fill(CHARS, 3370, 3386, (byte) -19); // Fill 16 of value (byte) -19\n\t\tArrays.fill(CHARS, 3386, 3390, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 3390, 3396, (byte) -87); // Fill 6 of value (byte) -87\n\t\tArrays.fill(CHARS, 3396, 3398, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 3398, 3401, (byte) -87); // Fill 3 of value (byte) -87\n\t\tCHARS[3401] = 33;\n\t\tArrays.fill(CHARS, 3402, 3406, (byte) -87); // Fill 4 of value (byte) -87\n\t\tArrays.fill(CHARS, 3406, 3415, (byte) 33); // Fill 9 of value (byte) 33\n\t\tCHARS[3415] = -87;\n\t\tArrays.fill(CHARS, 3416, 3424, (byte) 33); // Fill 8 of value (byte) 33\n\t\tArrays.fill(CHARS, 3424, 3426, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 3426, 3430, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 3430, 3440, (byte) -87); // Fill 10 of value (byte) -87\n\t\tArrays.fill(CHARS, 3440, 3585, (byte) 33); // Fill 145 of value (byte) 33\n\t\tArrays.fill(CHARS, 3585, 3631, (byte) -19); // Fill 46 of value (byte) -19\n\t\tCHARS[3631] = 33;\n\t\tCHARS[3632] = -19;\n\t\tCHARS[3633] = -87;\n\t\tArrays.fill(CHARS, 3634, 3636, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 3636, 3643, (byte) -87); // Fill 7 of value (byte) -87\n\t\tArrays.fill(CHARS, 3643, 3648, (byte) 33); // Fill 5 of value (byte) 33\n\t\tArrays.fill(CHARS, 3648, 3654, (byte) -19); // Fill 6 of value (byte) -19\n\t\tArrays.fill(CHARS, 3654, 3663, (byte) -87); // Fill 9 of value (byte) -87\n\t\tCHARS[3663] = 33;\n\t\tArrays.fill(CHARS, 3664, 3674, (byte) -87); // Fill 10 of value (byte) -87\n\t\tArrays.fill(CHARS, 3674, 3713, (byte) 33); // Fill 39 of value (byte) 33\n\t\tArrays.fill(CHARS, 3713, 3715, (byte) -19); // Fill 2 of value (byte) -19\n\t\tCHARS[3715] = 33;\n\t\tCHARS[3716] = -19;\n\t\tArrays.fill(CHARS, 3717, 3719, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 3719, 3721, (byte) -19); // Fill 2 of value (byte) -19\n\t\tCHARS[3721] = 33;\n\t\tCHARS[3722] = -19;\n\t\tArrays.fill(CHARS, 3723, 3725, (byte) 33); // Fill 2 of value (byte) 33\n\t\tCHARS[3725] = -19;\n\t\tArrays.fill(CHARS, 3726, 3732, (byte) 33); // Fill 6 of value (byte) 33\n\t\tArrays.fill(CHARS, 3732, 3736, (byte) -19); // Fill 4 of value (byte) -19\n\t\tCHARS[3736] = 33;\n\t\tArrays.fill(CHARS, 3737, 3744, (byte) -19); // Fill 7 of value (byte) -19\n\t\tCHARS[3744] = 33;\n\t\tArrays.fill(CHARS, 3745, 3748, (byte) -19); // Fill 3 of value (byte) -19\n\t\tCHARS[3748] = 33;\n\t\tCHARS[3749] = -19;\n\t\tCHARS[3750] = 33;\n\t\tCHARS[3751] = -19;\n\t\tArrays.fill(CHARS, 3752, 3754, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 3754, 3756, (byte) -19); // Fill 2 of value (byte) -19\n\t\tCHARS[3756] = 33;\n\t\tArrays.fill(CHARS, 3757, 3759, (byte) -19); // Fill 2 of value (byte) -19\n\t\tCHARS[3759] = 33;\n\t\tCHARS[3760] = -19;\n\t\tCHARS[3761] = -87;\n\t\tArrays.fill(CHARS, 3762, 3764, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 3764, 3770, (byte) -87); // Fill 6 of value (byte) -87\n\t\tCHARS[3770] = 33;\n\t\tArrays.fill(CHARS, 3771, 3773, (byte) -87); // Fill 2 of value (byte) -87\n\t\tCHARS[3773] = -19;\n\t\tArrays.fill(CHARS, 3774, 3776, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 3776, 3781, (byte) -19); // Fill 5 of value (byte) -19\n\t\tCHARS[3781] = 33;\n\t\tCHARS[3782] = -87;\n\t\tCHARS[3783] = 33;\n\t\tArrays.fill(CHARS, 3784, 3790, (byte) -87); // Fill 6 of value (byte) -87\n\t\tArrays.fill(CHARS, 3790, 3792, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 3792, 3802, (byte) -87); // Fill 10 of value (byte) -87\n\t\tArrays.fill(CHARS, 3802, 3864, (byte) 33); // Fill 62 of value (byte) 33\n\t\tArrays.fill(CHARS, 3864, 3866, (byte) -87); // Fill 2 of value (byte) -87\n\t\tArrays.fill(CHARS, 3866, 3872, (byte) 33); // Fill 6 of value (byte) 33\n\t\tArrays.fill(CHARS, 3872, 3882, (byte) -87); // Fill 10 of value (byte) -87\n\t\tArrays.fill(CHARS, 3882, 3893, (byte) 33); // Fill 11 of value (byte) 33\n\t\tCHARS[3893] = -87;\n\t\tCHARS[3894] = 33;\n\t\tCHARS[3895] = -87;\n\t\tCHARS[3896] = 33;\n\t\tCHARS[3897] = -87;\n\t\tArrays.fill(CHARS, 3898, 3902, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 3902, 3904, (byte) -87); // Fill 2 of value (byte) -87\n\t\tArrays.fill(CHARS, 3904, 3912, (byte) -19); // Fill 8 of value (byte) -19\n\t\tCHARS[3912] = 33;\n\t\tArrays.fill(CHARS, 3913, 3946, (byte) -19); // Fill 33 of value (byte) -19\n\t\tArrays.fill(CHARS, 3946, 3953, (byte) 33); // Fill 7 of value (byte) 33\n\t\tArrays.fill(CHARS, 3953, 3973, (byte) -87); // Fill 20 of value (byte) -87\n\t\tCHARS[3973] = 33;\n\t\tArrays.fill(CHARS, 3974, 3980, (byte) -87); // Fill 6 of value (byte) -87\n\t\tArrays.fill(CHARS, 3980, 3984, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 3984, 3990, (byte) -87); // Fill 6 of value (byte) -87\n\t\tCHARS[3990] = 33;\n\t\tCHARS[3991] = -87;\n\t\tCHARS[3992] = 33;\n\t\tArrays.fill(CHARS, 3993, 4014, (byte) -87); // Fill 21 of value (byte) -87\n\t\tArrays.fill(CHARS, 4014, 4017, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 4017, 4024, (byte) -87); // Fill 7 of value (byte) -87\n\t\tCHARS[4024] = 33;\n\t\tCHARS[4025] = -87;\n\t\tArrays.fill(CHARS, 4026, 4256, (byte) 33); // Fill 230 of value (byte) 33\n\t\tArrays.fill(CHARS, 4256, 4294, (byte) -19); // Fill 38 of value (byte) -19\n\t\tArrays.fill(CHARS, 4294, 4304, (byte) 33); // Fill 10 of value (byte) 33\n\t\tArrays.fill(CHARS, 4304, 4343, (byte) -19); // Fill 39 of value (byte) -19\n\t\tArrays.fill(CHARS, 4343, 4352, (byte) 33); // Fill 9 of value (byte) 33\n\t\tCHARS[4352] = -19;\n\t\tCHARS[4353] = 33;\n\t\tArrays.fill(CHARS, 4354, 4356, (byte) -19); // Fill 2 of value (byte) -19\n\t\tCHARS[4356] = 33;\n\t\tArrays.fill(CHARS, 4357, 4360, (byte) -19); // Fill 3 of value (byte) -19\n\t\tCHARS[4360] = 33;\n\t\tCHARS[4361] = -19;\n\t\tCHARS[4362] = 33;\n\t\tArrays.fill(CHARS, 4363, 4365, (byte) -19); // Fill 2 of value (byte) -19\n\t\tCHARS[4365] = 33;\n\t\tArrays.fill(CHARS, 4366, 4371, (byte) -19); // Fill 5 of value (byte) -19\n\t\tArrays.fill(CHARS, 4371, 4412, (byte) 33); // Fill 41 of value (byte) 33\n\t\tCHARS[4412] = -19;\n\t\tCHARS[4413] = 33;\n\t\tCHARS[4414] = -19;\n\t\tCHARS[4415] = 33;\n\t\tCHARS[4416] = -19;\n\t\tArrays.fill(CHARS, 4417, 4428, (byte) 33); // Fill 11 of value (byte) 33\n\t\tCHARS[4428] = -19;\n\t\tCHARS[4429] = 33;\n\t\tCHARS[4430] = -19;\n\t\tCHARS[4431] = 33;\n\t\tCHARS[4432] = -19;\n\t\tArrays.fill(CHARS, 4433, 4436, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 4436, 4438, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 4438, 4441, (byte) 33); // Fill 3 of value (byte) 33\n\t\tCHARS[4441] = -19;\n\t\tArrays.fill(CHARS, 4442, 4447, (byte) 33); // Fill 5 of value (byte) 33\n\t\tArrays.fill(CHARS, 4447, 4450, (byte) -19); // Fill 3 of value (byte) -19\n\t\tCHARS[4450] = 33;\n\t\tCHARS[4451] = -19;\n\t\tCHARS[4452] = 33;\n\t\tCHARS[4453] = -19;\n\t\tCHARS[4454] = 33;\n\t\tCHARS[4455] = -19;\n\t\tCHARS[4456] = 33;\n\t\tCHARS[4457] = -19;\n\t\tArrays.fill(CHARS, 4458, 4461, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 4461, 4463, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 4463, 4466, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 4466, 4468, (byte) -19); // Fill 2 of value (byte) -19\n\t\tCHARS[4468] = 33;\n\t\tCHARS[4469] = -19;\n\t\tArrays.fill(CHARS, 4470, 4510, (byte) 33); // Fill 40 of value (byte) 33\n\t\tCHARS[4510] = -19;\n\t\tArrays.fill(CHARS, 4511, 4520, (byte) 33); // Fill 9 of value (byte) 33\n\t\tCHARS[4520] = -19;\n\t\tArrays.fill(CHARS, 4521, 4523, (byte) 33); // Fill 2 of value (byte) 33\n\t\tCHARS[4523] = -19;\n\t\tArrays.fill(CHARS, 4524, 4526, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 4526, 4528, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 4528, 4535, (byte) 33); // Fill 7 of value (byte) 33\n\t\tArrays.fill(CHARS, 4535, 4537, (byte) -19); // Fill 2 of value (byte) -19\n\t\tCHARS[4537] = 33;\n\t\tCHARS[4538] = -19;\n\t\tCHARS[4539] = 33;\n\t\tArrays.fill(CHARS, 4540, 4547, (byte) -19); // Fill 7 of value (byte) -19\n\t\tArrays.fill(CHARS, 4547, 4587, (byte) 33); // Fill 40 of value (byte) 33\n\t\tCHARS[4587] = -19;\n\t\tArrays.fill(CHARS, 4588, 4592, (byte) 33); // Fill 4 of value (byte) 33\n\t\tCHARS[4592] = -19;\n\t\tArrays.fill(CHARS, 4593, 4601, (byte) 33); // Fill 8 of value (byte) 33\n\t\tCHARS[4601] = -19;\n\t\tArrays.fill(CHARS, 4602, 7680, (byte) 33); // Fill 3078 of value (byte) 33\n\t\tArrays.fill(CHARS, 7680, 7836, (byte) -19); // Fill 156 of value (byte) -19\n\t\tArrays.fill(CHARS, 7836, 7840, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 7840, 7930, (byte) -19); // Fill 90 of value (byte) -19\n\t\tArrays.fill(CHARS, 7930, 7936, (byte) 33); // Fill 6 of value (byte) 33\n\t\tArrays.fill(CHARS, 7936, 7958, (byte) -19); // Fill 22 of value (byte) -19\n\t\tArrays.fill(CHARS, 7958, 7960, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 7960, 7966, (byte) -19); // Fill 6 of value (byte) -19\n\t\tArrays.fill(CHARS, 7966, 7968, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 7968, 8006, (byte) -19); // Fill 38 of value (byte) -19\n\t\tArrays.fill(CHARS, 8006, 8008, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 8008, 8014, (byte) -19); // Fill 6 of value (byte) -19\n\t\tArrays.fill(CHARS, 8014, 8016, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 8016, 8024, (byte) -19); // Fill 8 of value (byte) -19\n\t\tCHARS[8024] = 33;\n\t\tCHARS[8025] = -19;\n\t\tCHARS[8026] = 33;\n\t\tCHARS[8027] = -19;\n\t\tCHARS[8028] = 33;\n\t\tCHARS[8029] = -19;\n\t\tCHARS[8030] = 33;\n\t\tArrays.fill(CHARS, 8031, 8062, (byte) -19); // Fill 31 of value (byte) -19\n\t\tArrays.fill(CHARS, 8062, 8064, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 8064, 8117, (byte) -19); // Fill 53 of value (byte) -19\n\t\tCHARS[8117] = 33;\n\t\tArrays.fill(CHARS, 8118, 8125, (byte) -19); // Fill 7 of value (byte) -19\n\t\tCHARS[8125] = 33;\n\t\tCHARS[8126] = -19;\n\t\tArrays.fill(CHARS, 8127, 8130, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 8130, 8133, (byte) -19); // Fill 3 of value (byte) -19\n\t\tCHARS[8133] = 33;\n\t\tArrays.fill(CHARS, 8134, 8141, (byte) -19); // Fill 7 of value (byte) -19\n\t\tArrays.fill(CHARS, 8141, 8144, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 8144, 8148, (byte) -19); // Fill 4 of value (byte) -19\n\t\tArrays.fill(CHARS, 8148, 8150, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 8150, 8156, (byte) -19); // Fill 6 of value (byte) -19\n\t\tArrays.fill(CHARS, 8156, 8160, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 8160, 8173, (byte) -19); // Fill 13 of value (byte) -19\n\t\tArrays.fill(CHARS, 8173, 8178, (byte) 33); // Fill 5 of value (byte) 33\n\t\tArrays.fill(CHARS, 8178, 8181, (byte) -19); // Fill 3 of value (byte) -19\n\t\tCHARS[8181] = 33;\n\t\tArrays.fill(CHARS, 8182, 8189, (byte) -19); // Fill 7 of value (byte) -19\n\t\tArrays.fill(CHARS, 8189, 8400, (byte) 33); // Fill 211 of value (byte) 33\n\t\tArrays.fill(CHARS, 8400, 8413, (byte) -87); // Fill 13 of value (byte) -87\n\t\tArrays.fill(CHARS, 8413, 8417, (byte) 33); // Fill 4 of value (byte) 33\n\t\tCHARS[8417] = -87;\n\t\tArrays.fill(CHARS, 8418, 8486, (byte) 33); // Fill 68 of value (byte) 33\n\t\tCHARS[8486] = -19;\n\t\tArrays.fill(CHARS, 8487, 8490, (byte) 33); // Fill 3 of value (byte) 33\n\t\tArrays.fill(CHARS, 8490, 8492, (byte) -19); // Fill 2 of value (byte) -19\n\t\tArrays.fill(CHARS, 8492, 8494, (byte) 33); // Fill 2 of value (byte) 33\n\t\tCHARS[8494] = -19;\n\t\tArrays.fill(CHARS, 8495, 8576, (byte) 33); // Fill 81 of value (byte) 33\n\t\tArrays.fill(CHARS, 8576, 8579, (byte) -19); // Fill 3 of value (byte) -19\n\t\tArrays.fill(CHARS, 8579, 12293, (byte) 33); // Fill 3714 of value (byte) 33\n\t\tCHARS[12293] = -87;\n\t\tCHARS[12294] = 33;\n\t\tCHARS[12295] = -19;\n\t\tArrays.fill(CHARS, 12296, 12321, (byte) 33); // Fill 25 of value (byte) 33\n\t\tArrays.fill(CHARS, 12321, 12330, (byte) -19); // Fill 9 of value (byte) -19\n\t\tArrays.fill(CHARS, 12330, 12336, (byte) -87); // Fill 6 of value (byte) -87\n\t\tCHARS[12336] = 33;\n\t\tArrays.fill(CHARS, 12337, 12342, (byte) -87); // Fill 5 of value (byte) -87\n\t\tArrays.fill(CHARS, 12342, 12353, (byte) 33); // Fill 11 of value (byte) 33\n\t\tArrays.fill(CHARS, 12353, 12437, (byte) -19); // Fill 84 of value (byte) -19\n\t\tArrays.fill(CHARS, 12437, 12441, (byte) 33); // Fill 4 of value (byte) 33\n\t\tArrays.fill(CHARS, 12441, 12443, (byte) -87); // Fill 2 of value (byte) -87\n\t\tArrays.fill(CHARS, 12443, 12445, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 12445, 12447, (byte) -87); // Fill 2 of value (byte) -87\n\t\tArrays.fill(CHARS, 12447, 12449, (byte) 33); // Fill 2 of value (byte) 33\n\t\tArrays.fill(CHARS, 12449, 12539, (byte) -19); // Fill 90 of value (byte) -19\n\t\tCHARS[12539] = 33;\n\t\tArrays.fill(CHARS, 12540, 12543, (byte) -87); // Fill 3 of value (byte) -87\n\t\tArrays.fill(CHARS, 12543, 12549, (byte) 33); // Fill 6 of value (byte) 33\n\t\tArrays.fill(CHARS, 12549, 12589, (byte) -19); // Fill 40 of value (byte) -19\n\t\tArrays.fill(CHARS, 12589, 19968, (byte) 33); // Fill 7379 of value (byte) 33\n\t\tArrays.fill(CHARS, 19968, 40870, (byte) -19); // Fill 20902 of value (byte) -19\n\t\tArrays.fill(CHARS, 40870, 44032, (byte) 33); // Fill 3162 of value (byte) 33\n\t\tArrays.fill(CHARS, 44032, 55204, (byte) -19); // Fill 11172 of value (byte) -19\n\t\tArrays.fill(CHARS, 55204, 55296, (byte) 33); // Fill 92 of value (byte) 33\n\t\tArrays.fill(CHARS, 57344, 65534, (byte) 33); // Fill 8190 of value (byte) 33\n\t} // <clinit>()\n\n\t//\n\t// Public static methods\n\t//\n\n\t/**\n\t * Returns true if the specified character is a supplemental character.\n\t *\n\t * @param c The character to check.\n\t */\n\tpublic static boolean isSupplemental(int c) {\n\t\treturn (c >= 0x10000 && c <= 0x10FFFF);\n\t}\n\n\t/**\n\t * Returns true the supplemental character corresponding to the given\n\t * surrogates.\n\t *\n\t * @param h The high surrogate.\n\t * @param l The low surrogate.\n\t */\n\tpublic static int supplemental(char h, char l) {\n\t\treturn (h - 0xD800) * 0x400 + (l - 0xDC00) + 0x10000;\n\t}\n\n\t/**\n\t * Returns the high surrogate of a supplemental character\n\t *\n\t * @param c The supplemental character to \"split\".\n\t */\n\tpublic static char highSurrogate(int c) {\n\t\treturn (char) (((c - 0x00010000) >> 10) + 0xD800);\n\t}\n\n\t/**\n\t * Returns the low surrogate of a supplemental character\n\t *\n\t * @param c The supplemental character to \"split\".\n\t */\n\tpublic static char lowSurrogate(int c) {\n\t\treturn (char) (((c - 0x00010000) & 0x3FF) + 0xDC00);\n\t}\n\n\t/**\n\t * Returns whether the given character is a high surrogate\n\t *\n\t * @param c The character to check.\n\t */\n\tpublic static boolean isHighSurrogate(int c) {\n\t\treturn (0xD800 <= c && c <= 0xDBFF);\n\t}\n\n\t/**\n\t * Returns whether the given character is a low surrogate\n\t *\n\t * @param c The character to check.\n\t */\n\tpublic static boolean isLowSurrogate(int c) {\n\t\treturn (0xDC00 <= c && c <= 0xDFFF);\n\t}\n\n\t/**\n\t * Returns true if the specified character is valid. This method\n\t * also checks the surrogate character range from 0x10000 to 0x10FFFF.\n\t * <p>\n\t * If the program chooses to apply the mask directly to the\n\t * <code>CHARS</code> array, then they are responsible for checking\n\t * the surrogate character range.\n\t *\n\t * @param c The character to check.\n\t */\n\tpublic static boolean isValid(int c) {\n\t\treturn (c < 0x10000 && (CHARS[c] & MASK_VALID) != 0)\n\t\t\t\t|| (0x10000 <= c && c <= 0x10FFFF);\n\t} // isValid(int):boolean\n\n\t/**\n\t * Returns true if the specified character is invalid.\n\t *\n\t * @param c The character to check.\n\t */\n\tpublic static boolean isInvalid(int c) {\n\t\treturn !isValid(c);\n\t} // isInvalid(int):boolean\n\n\t/**\n\t * Returns true if the specified character can be considered content.\n\t *\n\t * @param c The character to check.\n\t */\n\tpublic static boolean isContent(int c) {\n\t\treturn (c < 0x10000 && (CHARS[c] & MASK_CONTENT) != 0)\n\t\t\t\t|| (0x10000 <= c && c <= 0x10FFFF);\n\t} // isContent(int):boolean\n\n\t/**\n\t * Returns true if the specified character can be considered markup.\n\t * Markup characters include '&lt;', '&amp;', and '%'.\n\t *\n\t * @param c The character to check.\n\t */\n\tpublic static boolean isMarkup(int c) {\n\t\treturn c == '<' || c == '&' || c == '%';\n\t} // isMarkup(int):boolean\n\n\t/**\n\t * Returns true if the specified character is a space character\n\t * as defined by production [3] in the XML 1.0 specification.\n\t *\n\t * @param c The character to check.\n\t */\n\tpublic static boolean isSpace(int c) {\n\t\treturn c <= 0x20 && (CHARS[c] & MASK_SPACE) != 0;\n\t} // isSpace(int):boolean\n\n\t/**\n\t * Returns true if the specified character is a valid name start\n\t * character as defined by production [5] in the XML 1.0\n\t * specification.\n\t *\n\t * @param c The character to check.\n\t */\n\tpublic static boolean isNameStart(int c) {\n\t\treturn c < 0x10000 && (CHARS[c] & MASK_NAME_START) != 0;\n\t} // isNameStart(int):boolean\n\n\t/**\n\t * Returns true if the specified character is a valid name\n\t * character as defined by production [4] in the XML 1.0\n\t * specification.\n\t *\n\t * @param c The character to check.\n\t */\n\tpublic static boolean isName(int c) {\n\t\treturn c < 0x10000 && (CHARS[c] & MASK_NAME) != 0;\n\t} // isName(int):boolean\n\n\t/**\n\t * Returns true if the specified character is a valid NCName start\n\t * character as defined by production [4] in Namespaces in XML\n\t * recommendation.\n\t *\n\t * @param c The character to check.\n\t */\n\tpublic static boolean isNCNameStart(int c) {\n\t\treturn c < 0x10000 && (CHARS[c] & MASK_NCNAME_START) != 0;\n\t} // isNCNameStart(int):boolean\n\n\t/**\n\t * Returns true if the specified character is a valid NCName\n\t * character as defined by production [5] in Namespaces in XML\n\t * recommendation.\n\t *\n\t * @param c The character to check.\n\t */\n\tpublic static boolean isNCName(int c) {\n\t\treturn c < 0x10000 && (CHARS[c] & MASK_NCNAME) != 0;\n\t} // isNCName(int):boolean\n\n\t/**\n\t * Returns true if the specified character is a valid Pubid\n\t * character as defined by production [13] in the XML 1.0\n\t * specification.\n\t *\n\t * @param c The character to check.\n\t */\n\tpublic static boolean isPubid(int c) {\n\t\treturn c < 0x10000 && (CHARS[c] & MASK_PUBID) != 0;\n\t} // isPubid(int):boolean\n\n\t/*\n\t * [5] Name ::= (Letter | '_' | ':') (NameChar)*\n\t */\n\n\t/**\n\t * Check to see if a string is a valid Name according to [5]\n\t * in the XML 1.0 Recommendation\n\t *\n\t * @param name string to check\n\t * @return true if name is a valid Name\n\t */\n\tpublic static boolean isValidName(String name) {\n\t\tfinal int length = name.length();\n\t\tif (length == 0) {\n\t\t\treturn false;\n\t\t}\n\t\tchar ch = name.charAt(0);\n\t\tif (!isNameStart(ch)) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (int i = 1; i < length; ++i) {\n\t\t\tch = name.charAt(i);\n\t\t\tif (!isName(ch)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t} // isValidName(String):boolean\n\n\t/*\n\t * from the namespace rec\n\t * [4] NCName ::= (Letter | '_') (NCNameChar)*\n\t */\n\n\t/**\n\t * Check to see if a string is a valid NCName according to [4]\n\t * from the XML Namespaces 1.0 Recommendation\n\t *\n\t * @param ncName string to check\n\t * @return true if name is a valid NCName\n\t */\n\tpublic static boolean isValidNCName(String ncName) {\n\t\tfinal int length = ncName.length();\n\t\tif (length == 0) {\n\t\t\treturn false;\n\t\t}\n\t\tchar ch = ncName.charAt(0);\n\t\tif (!isNCNameStart(ch)) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (int i = 1; i < length; ++i) {\n\t\t\tch = ncName.charAt(i);\n\t\t\tif (!isNCName(ch)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t} // isValidNCName(String):boolean\n\n\t/*\n\t * [7] Nmtoken ::= (NameChar)+\n\t */\n\n\t/**\n\t * Check to see if a string is a valid Nmtoken according to [7]\n\t * in the XML 1.0 Recommendation\n\t *\n\t * @param nmtoken string to check\n\t * @return true if nmtoken is a valid Nmtoken\n\t */\n\tpublic static boolean isValidNmtoken(String nmtoken) {\n\t\tfinal int length = nmtoken.length();\n\t\tif (length == 0) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (int i = 0; i < length; ++i) {\n\t\t\tchar ch = nmtoken.charAt(i);\n\t\t\tif (!isName(ch)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t} // isValidName(String):boolean\n\n\t// encodings\n\n\t/**\n\t * Returns true if the encoding name is a valid IANA encoding.\n\t * This method does not verify that there is a decoder available\n\t * for this encoding, only that the characters are valid for an\n\t * IANA encoding name.\n\t *\n\t * @param ianaEncoding The IANA encoding name.\n\t */\n\tpublic static boolean isValidIANAEncoding(String ianaEncoding) {\n\t\tif (ianaEncoding != null) {\n\t\t\tint length = ianaEncoding.length();\n\t\t\tif (length > 0) {\n\t\t\t\tchar c = ianaEncoding.charAt(0);\n\t\t\t\tif ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {\n\t\t\t\t\tfor (int i = 1; i < length; i++) {\n\t\t\t\t\t\tc = ianaEncoding.charAt(i);\n\t\t\t\t\t\tif ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')\n\t\t\t\t\t\t\t\t&& (c < '0' || c > '9') && c != '.' && c != '_'\n\t\t\t\t\t\t\t\t&& c != '-') {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t} // isValidIANAEncoding(String):boolean\n\n\t/**\n\t * Returns true if the encoding name is a valid Java encoding.\n\t * This method does not verify that there is a decoder available\n\t * for this encoding, only that the characters are valid for an\n\t * Java encoding name.\n\t *\n\t * @param javaEncoding The Java encoding name.\n\t */\n\tpublic static boolean isValidJavaEncoding(String javaEncoding) {\n\t\tif (javaEncoding != null) {\n\t\t\tint length = javaEncoding.length();\n\t\t\tif (length > 0) {\n\t\t\t\tfor (int i = 1; i < length; i++) {\n\t\t\t\t\tchar c = javaEncoding.charAt(i);\n\t\t\t\t\tif ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')\n\t\t\t\t\t\t\t&& (c < '0' || c > '9') && c != '.' && c != '_'\n\t\t\t\t\t\t\t&& c != '-') {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t} // isValidIANAEncoding(String):boolean\n\n\t// other methods\n\n\t/**\n\t * Trims space characters as defined by production [3] in\n\t * the XML 1.0 specification from both ends of the given string.\n\t *\n\t * @param value the string to be trimmed\n\t * @return the given string with the space characters trimmed\n\t *         from both ends\n\t */\n\tpublic static String trim(String value) {\n\t\tint start;\n\t\tint end;\n\t\tfinal int lengthMinusOne = value.length() - 1;\n\t\tfor (start = 0; start <= lengthMinusOne; ++start) {\n\t\t\tif (!isSpace(value.charAt(start))) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tfor (end = lengthMinusOne; end >= start; --end) {\n\t\t\tif (!isSpace(value.charAt(end))) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (start == 0 && end == lengthMinusOne) {\n\t\t\treturn value;\n\t\t}\n\t\tif (start > lengthMinusOne) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn value.substring(start, end + 1);\n\t} // trim(String):String\n} // class XMLChar\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/XmlDeobf.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.RootNode;\n\n/*\n * Modifies android:name attributes and xml tags which were changed during deobfuscation\n */\npublic class XmlDeobf {\n\n\tprivate XmlDeobf() {\n\t}\n\n\t@Nullable\n\tpublic static String deobfClassName(RootNode root, String potentialClassName, String packageName) {\n\t\tif (potentialClassName.indexOf('.') == -1) {\n\t\t\treturn null;\n\t\t}\n\t\tif (packageName != null && potentialClassName.startsWith(\".\")) {\n\t\t\tpotentialClassName = packageName + potentialClassName;\n\t\t}\n\t\tArgType clsType = ArgType.object(potentialClassName);\n\t\tClassInfo classInfo = root.getInfoStorage().getCls(clsType);\n\t\tif (classInfo == null) {\n\t\t\t// unknown class reference\n\t\t\treturn null;\n\t\t}\n\t\treturn classInfo.getAliasFullName();\n\t}\n\n\tpublic static boolean isDuplicatedAttr(String attrFullName, Set<String> attrCache) {\n\t\treturn !attrCache.add(attrFullName);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/XmlGenUtils.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.text.NumberFormat;\nimport java.util.HashSet;\nimport java.util.Locale;\nimport java.util.Set;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ICodeWriter;\nimport jadx.core.xmlgen.entry.ResourceEntry;\nimport jadx.core.xmlgen.entry.ValuesParser;\n\npublic class XmlGenUtils {\n\tprivate XmlGenUtils() {\n\t}\n\n\tpublic static byte[] readData(InputStream i) throws IOException {\n\t\tByteArrayOutputStream buffer = new ByteArrayOutputStream();\n\t\tbyte[] data = new byte[16384];\n\t\tint read;\n\t\twhile ((read = i.read(data, 0, data.length)) != -1) {\n\t\t\tbuffer.write(data, 0, read);\n\t\t}\n\t\treturn buffer.toByteArray();\n\t}\n\n\tpublic static ICodeInfo makeXmlDump(ICodeWriter writer, ResourceStorage resStorage) {\n\t\twriter.add(\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\");\n\t\twriter.startLine(\"<resources>\");\n\t\twriter.incIndent();\n\n\t\tSet<String> addedValues = new HashSet<>();\n\t\tfor (ResourceEntry ri : resStorage.getResources()) {\n\t\t\tif (addedValues.add(ri.getTypeName() + '.' + ri.getKeyName())) {\n\t\t\t\tString format = String.format(\"<public type=\\\"%s\\\" name=\\\"%s\\\" id=\\\"0x%08x\\\" />\",\n\t\t\t\t\t\tri.getTypeName(), ri.getKeyName(), ri.getId());\n\t\t\t\twriter.startLine(format);\n\t\t\t}\n\t\t}\n\t\twriter.decIndent();\n\t\twriter.startLine(\"</resources>\");\n\t\treturn writer.finish();\n\t}\n\n\tpublic static String decodeComplex(int data, boolean isFraction) {\n\t\tdouble value = (data & ParserConstants.COMPLEX_MANTISSA_MASK << ParserConstants.COMPLEX_MANTISSA_SHIFT)\n\t\t\t\t* ParserConstants.RADIX_MULTS[data >> ParserConstants.COMPLEX_RADIX_SHIFT & ParserConstants.COMPLEX_RADIX_MASK];\n\t\tint unitType = data & ParserConstants.COMPLEX_UNIT_MASK;\n\t\tString unit;\n\t\tif (isFraction) {\n\t\t\tvalue *= 100;\n\t\t\tswitch (unitType) {\n\t\t\t\tcase ParserConstants.COMPLEX_UNIT_FRACTION:\n\t\t\t\t\tunit = \"%\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase ParserConstants.COMPLEX_UNIT_FRACTION_PARENT:\n\t\t\t\t\tunit = \"%p\";\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tunit = \"?f\" + Integer.toHexString(unitType);\n\t\t\t}\n\t\t} else {\n\t\t\tswitch (unitType) {\n\t\t\t\tcase ParserConstants.COMPLEX_UNIT_PX:\n\t\t\t\t\tunit = \"px\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase ParserConstants.COMPLEX_UNIT_DIP:\n\t\t\t\t\tunit = \"dp\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase ParserConstants.COMPLEX_UNIT_SP:\n\t\t\t\t\tunit = \"sp\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase ParserConstants.COMPLEX_UNIT_PT:\n\t\t\t\t\tunit = \"pt\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase ParserConstants.COMPLEX_UNIT_IN:\n\t\t\t\t\tunit = \"in\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase ParserConstants.COMPLEX_UNIT_MM:\n\t\t\t\t\tunit = \"mm\";\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tunit = \"?d\" + Integer.toHexString(unitType);\n\t\t\t}\n\t\t}\n\t\treturn doubleToString(value) + unit;\n\t}\n\n\tpublic static String doubleToString(double value) {\n\t\tif (Double.compare(value, Math.floor(value)) == 0\n\t\t\t\t&& !Double.isInfinite(value)) {\n\t\t\treturn Integer.toString((int) value);\n\t\t}\n\t\t// remove trailing zeroes\n\t\tNumberFormat f = NumberFormat.getInstance(Locale.ROOT);\n\t\tf.setMaximumFractionDigits(4);\n\t\tf.setMinimumIntegerDigits(1);\n\t\treturn f.format(value);\n\t}\n\n\tpublic static String floatToString(float value) {\n\t\treturn doubleToString(value);\n\t}\n\n\tpublic static String getAttrTypeAsString(int type) {\n\t\tString s = \"\";\n\t\tif ((type & ValuesParser.ATTR_TYPE_REFERENCE) != 0) {\n\t\t\ts += \"|reference\";\n\t\t}\n\t\tif ((type & ValuesParser.ATTR_TYPE_STRING) != 0) {\n\t\t\ts += \"|string\";\n\t\t}\n\t\tif ((type & ValuesParser.ATTR_TYPE_INTEGER) != 0) {\n\t\t\ts += \"|integer\";\n\t\t}\n\t\tif ((type & ValuesParser.ATTR_TYPE_BOOLEAN) != 0) {\n\t\t\ts += \"|boolean\";\n\t\t}\n\t\tif ((type & ValuesParser.ATTR_TYPE_COLOR) != 0) {\n\t\t\ts += \"|color\";\n\t\t}\n\t\tif ((type & ValuesParser.ATTR_TYPE_FLOAT) != 0) {\n\t\t\ts += \"|float\";\n\t\t}\n\t\tif ((type & ValuesParser.ATTR_TYPE_DIMENSION) != 0) {\n\t\t\ts += \"|dimension\";\n\t\t}\n\t\tif ((type & ValuesParser.ATTR_TYPE_FRACTION) != 0) {\n\t\t\ts += \"|fraction\";\n\t\t}\n\t\tif (s.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn s.substring(1);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/entry/EntryConfig.java",
    "content": "/**\n * Copyright (C) 2018 Ryszard Wiśniewski <brut.alll@gmail.com>\n * Copyright (C) 2018 Connor Tumbleson <connor.tumbleson@gmail.com>\n * <p>\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * <p>\n * http://www.apache.org/licenses/LICENSE-2.0\n * <p>\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage jadx.core.xmlgen.entry;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Original source code can be found\n * <a href=\n * \"https://raw.githubusercontent.com/iBotPeaches/Apktool/master/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java\">here</a>\n */\n\npublic class EntryConfig {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(EntryConfig.class);\n\n\tpublic final short mcc;\n\tpublic final short mnc;\n\n\tpublic final char[] language;\n\tpublic final char[] region;\n\n\tpublic final byte orientation;\n\tpublic final byte touchscreen;\n\tpublic final int density;\n\n\tpublic final byte keyboard;\n\tpublic final byte navigation;\n\tpublic final byte inputFlags;\n\tpublic final byte grammaticalInflection;\n\n\tpublic final short screenWidth;\n\tpublic final short screenHeight;\n\n\tpublic final short sdkVersion;\n\n\tpublic final byte screenLayout;\n\tpublic final byte uiMode;\n\tpublic final short smallestScreenWidthDp;\n\n\tpublic final short screenWidthDp;\n\tpublic final short screenHeightDp;\n\n\tprivate final char[] localeScript;\n\tprivate final char[] localeVariant;\n\n\tprivate final byte screenLayout2;\n\tprivate final byte colorMode;\n\n\tpublic final boolean isInvalid;\n\n\tprivate final String mQualifiers;\n\n\tprivate final int size;\n\n\tpublic EntryConfig(short mcc, short mnc, char[] language,\n\t\t\tchar[] region, byte orientation,\n\t\t\tbyte touchscreen, int density, byte keyboard, byte navigation,\n\t\t\tbyte inputFlags, byte grammaticalInflection, short screenWidth, short screenHeight,\n\t\t\tshort sdkVersion, byte screenLayout, byte uiMode,\n\t\t\tshort smallestScreenWidthDp, short screenWidthDp,\n\t\t\tshort screenHeightDp, char[] localeScript, char[] localeVariant,\n\t\t\tbyte screenLayout2, byte colorMode, boolean isInvalid, int size) {\n\t\tif (orientation < 0 || orientation > 3) {\n\t\t\tLOG.warn(\"Invalid orientation value: {}\", orientation);\n\t\t\torientation = 0;\n\t\t\tisInvalid = true;\n\t\t}\n\t\tif (touchscreen < 0 || touchscreen > 3) {\n\t\t\tLOG.warn(\"Invalid touchscreen value: {}\", touchscreen);\n\t\t\ttouchscreen = 0;\n\t\t\tisInvalid = true;\n\t\t}\n\t\tif (density < -1) {\n\t\t\tLOG.warn(\"Invalid density value: {}\", density);\n\t\t\tdensity = 0;\n\t\t\tisInvalid = true;\n\t\t}\n\t\tif (keyboard < 0 || keyboard > 3) {\n\t\t\tLOG.warn(\"Invalid keyboard value: {}\", keyboard);\n\t\t\tkeyboard = 0;\n\t\t\tisInvalid = true;\n\t\t}\n\t\tif (navigation < 0 || navigation > 4) {\n\t\t\tLOG.warn(\"Invalid navigation value: {}\", navigation);\n\t\t\tnavigation = 0;\n\t\t\tisInvalid = true;\n\t\t}\n\n\t\tif (localeScript != null && localeScript.length != 0) {\n\t\t\tif (localeScript[0] == '\\00') {\n\t\t\t\tlocaleScript = null;\n\t\t\t}\n\t\t} else {\n\t\t\tlocaleScript = null;\n\t\t}\n\n\t\tif (localeVariant != null && localeVariant.length != 0) {\n\t\t\tif (localeVariant[0] == '\\00') {\n\t\t\t\tlocaleVariant = null;\n\t\t\t}\n\t\t} else {\n\t\t\tlocaleVariant = null;\n\t\t}\n\n\t\tthis.mcc = mcc;\n\t\tthis.mnc = mnc;\n\t\tthis.language = language;\n\t\tthis.region = region;\n\t\tthis.orientation = orientation;\n\t\tthis.touchscreen = touchscreen;\n\t\tthis.density = density;\n\t\tthis.keyboard = keyboard;\n\t\tthis.navigation = navigation;\n\t\tthis.inputFlags = inputFlags;\n\t\tthis.grammaticalInflection = grammaticalInflection;\n\t\tthis.screenWidth = screenWidth;\n\t\tthis.screenHeight = screenHeight;\n\t\tthis.sdkVersion = sdkVersion;\n\t\tthis.screenLayout = screenLayout;\n\t\tthis.uiMode = uiMode;\n\t\tthis.smallestScreenWidthDp = smallestScreenWidthDp;\n\t\tthis.screenWidthDp = screenWidthDp;\n\t\tthis.screenHeightDp = screenHeightDp;\n\t\tthis.localeScript = localeScript;\n\t\tthis.localeVariant = localeVariant;\n\t\tthis.screenLayout2 = screenLayout2;\n\t\tthis.colorMode = colorMode;\n\t\tthis.isInvalid = isInvalid;\n\t\tthis.size = size;\n\t\tmQualifiers = generateQualifiers();\n\t}\n\n\tpublic String getQualifiers() {\n\t\treturn mQualifiers;\n\t}\n\n\tprivate String generateQualifiers() {\n\t\tStringBuilder ret = new StringBuilder();\n\t\tif (mcc != 0) {\n\t\t\tret.append(\"-mcc\").append(String.format(\"%03d\", mcc));\n\t\t\tif (mnc != MNC_ZERO) {\n\t\t\t\tif (mnc != 0) {\n\t\t\t\t\tret.append(\"-mnc\");\n\t\t\t\t\tif (size <= 32) {\n\t\t\t\t\t\tif (mnc > 0 && mnc < 10) {\n\t\t\t\t\t\t\tret.append(String.format(\"%02d\", mnc));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tret.append(String.format(\"%03d\", mnc));\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tret.append(mnc);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tret.append(\"-mnc00\");\n\t\t\t}\n\t\t} else {\n\t\t\tif (mnc != 0) {\n\t\t\t\tret.append(\"-mnc\").append(mnc);\n\t\t\t}\n\t\t}\n\t\tret.append(getLocaleString());\n\n\t\tswitch (grammaticalInflection) {\n\t\t\tcase GRAMMATICAL_GENDER_NEUTER:\n\t\t\t\tret.append(\"-neuter\");\n\t\t\t\tbreak;\n\t\t\tcase GRAMMATICAL_GENDER_FEMININE:\n\t\t\t\tret.append(\"-feminine\");\n\t\t\t\tbreak;\n\t\t\tcase GRAMMATICAL_GENDER_MASCULINE:\n\t\t\t\tret.append(\"-masculine\");\n\t\t\t\tbreak;\n\t\t}\n\n\t\tswitch (screenLayout & MASK_LAYOUTDIR) {\n\t\t\tcase SCREENLAYOUT_LAYOUTDIR_RTL:\n\t\t\t\tret.append(\"-ldrtl\");\n\t\t\t\tbreak;\n\t\t\tcase SCREENLAYOUT_LAYOUTDIR_LTR:\n\t\t\t\tret.append(\"-ldltr\");\n\t\t\t\tbreak;\n\t\t}\n\t\tif (smallestScreenWidthDp != 0) {\n\t\t\tret.append(\"-sw\").append(smallestScreenWidthDp).append(\"dp\");\n\t\t}\n\t\tif (screenWidthDp != 0) {\n\t\t\tret.append(\"-w\").append(screenWidthDp).append(\"dp\");\n\t\t}\n\t\tif (screenHeightDp != 0) {\n\t\t\tret.append(\"-h\").append(screenHeightDp).append(\"dp\");\n\t\t}\n\t\tswitch (screenLayout & MASK_SCREENSIZE) {\n\t\t\tcase SCREENSIZE_SMALL:\n\t\t\t\tret.append(\"-small\");\n\t\t\t\tbreak;\n\t\t\tcase SCREENSIZE_NORMAL:\n\t\t\t\tret.append(\"-normal\");\n\t\t\t\tbreak;\n\t\t\tcase SCREENSIZE_LARGE:\n\t\t\t\tret.append(\"-large\");\n\t\t\t\tbreak;\n\t\t\tcase SCREENSIZE_XLARGE:\n\t\t\t\tret.append(\"-xlarge\");\n\t\t\t\tbreak;\n\t\t}\n\t\tswitch (screenLayout & MASK_SCREENLONG) {\n\t\t\tcase SCREENLONG_YES:\n\t\t\t\tret.append(\"-long\");\n\t\t\t\tbreak;\n\t\t\tcase SCREENLONG_NO:\n\t\t\t\tret.append(\"-notlong\");\n\t\t\t\tbreak;\n\t\t}\n\t\tswitch (screenLayout2 & MASK_SCREENROUND) {\n\t\t\tcase SCREENLAYOUT_ROUND_NO:\n\t\t\t\tret.append(\"-notround\");\n\t\t\t\tbreak;\n\t\t\tcase SCREENLAYOUT_ROUND_YES:\n\t\t\t\tret.append(\"-round\");\n\t\t\t\tbreak;\n\t\t}\n\t\tswitch (colorMode & COLOR_HDR_MASK) {\n\t\t\tcase COLOR_HDR_YES:\n\t\t\t\tret.append(\"-highdr\");\n\t\t\t\tbreak;\n\t\t\tcase COLOR_HDR_NO:\n\t\t\t\tret.append(\"-lowdr\");\n\t\t\t\tbreak;\n\t\t}\n\t\tswitch (colorMode & COLOR_WIDE_MASK) {\n\t\t\tcase COLOR_WIDE_YES:\n\t\t\t\tret.append(\"-widecg\");\n\t\t\t\tbreak;\n\t\t\tcase COLOR_WIDE_NO:\n\t\t\t\tret.append(\"-nowidecg\");\n\t\t\t\tbreak;\n\t\t}\n\t\tswitch (orientation) {\n\t\t\tcase ORIENTATION_PORT:\n\t\t\t\tret.append(\"-port\");\n\t\t\t\tbreak;\n\t\t\tcase ORIENTATION_LAND:\n\t\t\t\tret.append(\"-land\");\n\t\t\t\tbreak;\n\t\t\tcase ORIENTATION_SQUARE:\n\t\t\t\tret.append(\"-square\");\n\t\t\t\tbreak;\n\t\t}\n\t\tswitch (uiMode & MASK_UI_MODE_TYPE) {\n\t\t\tcase UI_MODE_TYPE_CAR:\n\t\t\t\tret.append(\"-car\");\n\t\t\t\tbreak;\n\t\t\tcase UI_MODE_TYPE_DESK:\n\t\t\t\tret.append(\"-desk\");\n\t\t\t\tbreak;\n\t\t\tcase UI_MODE_TYPE_TELEVISION:\n\t\t\t\tret.append(\"-television\");\n\t\t\t\tbreak;\n\t\t\tcase UI_MODE_TYPE_SMALLUI:\n\t\t\t\tret.append(\"-smallui\");\n\t\t\t\tbreak;\n\t\t\tcase UI_MODE_TYPE_MEDIUMUI:\n\t\t\t\tret.append(\"-mediumui\");\n\t\t\t\tbreak;\n\t\t\tcase UI_MODE_TYPE_LARGEUI:\n\t\t\t\tret.append(\"-largeui\");\n\t\t\t\tbreak;\n\t\t\tcase UI_MODE_TYPE_GODZILLAUI:\n\t\t\t\tret.append(\"-godzillaui\");\n\t\t\t\tbreak;\n\t\t\tcase UI_MODE_TYPE_HUGEUI:\n\t\t\t\tret.append(\"-hugeui\");\n\t\t\t\tbreak;\n\t\t\tcase UI_MODE_TYPE_APPLIANCE:\n\t\t\t\tret.append(\"-appliance\");\n\t\t\t\tbreak;\n\t\t\tcase UI_MODE_TYPE_WATCH:\n\t\t\t\tret.append(\"-watch\");\n\t\t\t\tbreak;\n\t\t\tcase UI_MODE_TYPE_VR_HEADSET:\n\t\t\t\tret.append(\"-vrheadset\");\n\t\t\t\tbreak;\n\t\t}\n\t\tswitch (uiMode & MASK_UI_MODE_NIGHT) {\n\t\t\tcase UI_MODE_NIGHT_YES:\n\t\t\t\tret.append(\"-night\");\n\t\t\t\tbreak;\n\t\t\tcase UI_MODE_NIGHT_NO:\n\t\t\t\tret.append(\"-notnight\");\n\t\t\t\tbreak;\n\t\t}\n\t\tswitch (density) {\n\t\t\tcase DENSITY_DEFAULT:\n\t\t\t\tbreak;\n\t\t\tcase DENSITY_LOW:\n\t\t\t\tret.append(\"-ldpi\");\n\t\t\t\tbreak;\n\t\t\tcase DENSITY_MEDIUM:\n\t\t\t\tret.append(\"-mdpi\");\n\t\t\t\tbreak;\n\t\t\tcase DENSITY_HIGH:\n\t\t\t\tret.append(\"-hdpi\");\n\t\t\t\tbreak;\n\t\t\tcase DENSITY_TV:\n\t\t\t\tret.append(\"-tvdpi\");\n\t\t\t\tbreak;\n\t\t\tcase DENSITY_XHIGH:\n\t\t\t\tret.append(\"-xhdpi\");\n\t\t\t\tbreak;\n\t\t\tcase DENSITY_XXHIGH:\n\t\t\t\tret.append(\"-xxhdpi\");\n\t\t\t\tbreak;\n\t\t\tcase DENSITY_XXXHIGH:\n\t\t\t\tret.append(\"-xxxhdpi\");\n\t\t\t\tbreak;\n\t\t\tcase DENSITY_ANY:\n\t\t\t\tret.append(\"-anydpi\");\n\t\t\t\tbreak;\n\t\t\tcase DENSITY_NONE:\n\t\t\t\tret.append(\"-nodpi\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tret.append('-').append(density).append(\"dpi\");\n\t\t}\n\t\tswitch (touchscreen) {\n\t\t\tcase TOUCHSCREEN_NOTOUCH:\n\t\t\t\tret.append(\"-notouch\");\n\t\t\t\tbreak;\n\t\t\tcase TOUCHSCREEN_STYLUS:\n\t\t\t\tret.append(\"-stylus\");\n\t\t\t\tbreak;\n\t\t\tcase TOUCHSCREEN_FINGER:\n\t\t\t\tret.append(\"-finger\");\n\t\t\t\tbreak;\n\t\t}\n\t\tswitch (inputFlags & MASK_KEYSHIDDEN) {\n\t\t\tcase KEYSHIDDEN_NO:\n\t\t\t\tret.append(\"-keysexposed\");\n\t\t\t\tbreak;\n\t\t\tcase KEYSHIDDEN_YES:\n\t\t\t\tret.append(\"-keyshidden\");\n\t\t\t\tbreak;\n\t\t\tcase KEYSHIDDEN_SOFT:\n\t\t\t\tret.append(\"-keyssoft\");\n\t\t\t\tbreak;\n\t\t}\n\t\tswitch (keyboard) {\n\t\t\tcase KEYBOARD_NOKEYS:\n\t\t\t\tret.append(\"-nokeys\");\n\t\t\t\tbreak;\n\t\t\tcase KEYBOARD_QWERTY:\n\t\t\t\tret.append(\"-qwerty\");\n\t\t\t\tbreak;\n\t\t\tcase KEYBOARD_12KEY:\n\t\t\t\tret.append(\"-12key\");\n\t\t\t\tbreak;\n\t\t}\n\t\tswitch (inputFlags & MASK_NAVHIDDEN) {\n\t\t\tcase NAVHIDDEN_NO:\n\t\t\t\tret.append(\"-navexposed\");\n\t\t\t\tbreak;\n\t\t\tcase NAVHIDDEN_YES:\n\t\t\t\tret.append(\"-navhidden\");\n\t\t\t\tbreak;\n\t\t}\n\t\tswitch (navigation) {\n\t\t\tcase NAVIGATION_NONAV:\n\t\t\t\tret.append(\"-nonav\");\n\t\t\t\tbreak;\n\t\t\tcase NAVIGATION_DPAD:\n\t\t\t\tret.append(\"-dpad\");\n\t\t\t\tbreak;\n\t\t\tcase NAVIGATION_TRACKBALL:\n\t\t\t\tret.append(\"-trackball\");\n\t\t\t\tbreak;\n\t\t\tcase NAVIGATION_WHEEL:\n\t\t\t\tret.append(\"-wheel\");\n\t\t\t\tbreak;\n\t\t}\n\t\tif (screenWidth != 0 && screenHeight != 0) {\n\t\t\tif (screenWidth > screenHeight) {\n\t\t\t\tret.append(String.format(\"-%dx%d\", screenWidth, screenHeight));\n\t\t\t} else {\n\t\t\t\tret.append(String.format(\"-%dx%d\", screenHeight, screenWidth));\n\t\t\t}\n\t\t}\n\t\tif (sdkVersion > 0 && sdkVersion >= getNaturalSdkVersionRequirement()) {\n\t\t\tret.append(\"-v\").append(sdkVersion);\n\t\t}\n\t\tif (isInvalid) {\n\t\t\tret.append(\"-ERR\").append(sErrCounter++);\n\t\t}\n\n\t\treturn ret.toString();\n\t}\n\n\tprivate short getNaturalSdkVersionRequirement() {\n\t\tif ((uiMode & MASK_UI_MODE_TYPE) == UI_MODE_TYPE_VR_HEADSET || (colorMode & COLOR_WIDE_MASK) != 0\n\t\t\t\t|| ((colorMode & COLOR_HDR_MASK) != 0)) {\n\t\t\treturn SDK_OREO;\n\t\t}\n\t\tif ((screenLayout2 & MASK_SCREENROUND) != 0) {\n\t\t\treturn SDK_MNC;\n\t\t}\n\t\tif (density == DENSITY_ANY) {\n\t\t\treturn SDK_LOLLIPOP;\n\t\t}\n\t\tif (smallestScreenWidthDp != 0 || screenWidthDp != 0 || screenHeightDp != 0) {\n\t\t\treturn SDK_HONEYCOMB_MR2;\n\t\t}\n\t\tif ((uiMode & (MASK_UI_MODE_TYPE | MASK_UI_MODE_NIGHT)) != UI_MODE_NIGHT_ANY) {\n\t\t\treturn SDK_FROYO;\n\t\t}\n\t\tif ((screenLayout & (MASK_SCREENSIZE | MASK_SCREENLONG)) != SCREENSIZE_ANY || density != DENSITY_DEFAULT) {\n\t\t\treturn SDK_DONUT;\n\t\t}\n\t\treturn 0;\n\t}\n\n\tprivate String getLocaleString() {\n\t\tStringBuilder sb = new StringBuilder();\n\n\t\t// check for old style non BCP47 tags\n\t\t// allows values-xx-rXX, values-xx, values-xxx-rXX\n\t\t// denies values-xxx, anything else\n\t\tif (localeVariant == null && localeScript == null && (region[0] != '\\00' || language[0] != '\\00')\n\t\t\t\t&& region.length != 3) {\n\t\t\tsb.append('-').append(language);\n\t\t\tif (region[0] != '\\00') {\n\t\t\t\tsb.append(\"-r\").append(region);\n\t\t\t}\n\t\t} else { // BCP47\n\t\t\tif (language[0] == '\\00' && region[0] == '\\00') {\n\t\t\t\treturn sb.toString(); // early return, no language or region\n\t\t\t}\n\t\t\tsb.append(\"-b+\");\n\t\t\tif (language[0] != '\\00') {\n\t\t\t\tsb.append(language);\n\t\t\t}\n\t\t\tif (localeScript != null && localeScript.length == 4) {\n\t\t\t\tsb.append('+').append(localeScript);\n\t\t\t}\n\t\t\tif ((region.length == 2 || region.length == 3) && region[0] != '\\00') {\n\t\t\t\tsb.append('+').append(region);\n\t\t\t}\n\t\t\tif (localeVariant != null && localeVariant.length >= 5) {\n\t\t\t\tsb.append('+').append(toUpper(localeVariant));\n\t\t\t}\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tprivate String toUpper(char[] character) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (char ch : character) {\n\t\t\tsb.append(Character.toUpperCase(ch));\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn !getQualifiers().isEmpty() ? getQualifiers() : \"[DEFAULT]\";\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tfinal EntryConfig other = (EntryConfig) obj;\n\t\treturn this.mQualifiers.equals(other.mQualifiers);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint hash = 17;\n\t\thash = 31 * hash + this.mQualifiers.hashCode();\n\t\treturn hash;\n\t}\n\n\t// TODO: Dirty static hack. This counter should be a part of ResPackage,\n\t// but it would be hard right now and this feature is very rarely used.\n\tprivate static int sErrCounter = 0;\n\n\tpublic static final byte SDK_BASE = 1;\n\tpublic static final byte SDK_BASE_1_1 = 2;\n\tpublic static final byte SDK_CUPCAKE = 3;\n\tpublic static final byte SDK_DONUT = 4;\n\tpublic static final byte SDK_ECLAIR = 5;\n\tpublic static final byte SDK_ECLAIR_0_1 = 6;\n\tpublic static final byte SDK_ECLAIR_MR1 = 7;\n\tpublic static final byte SDK_FROYO = 8;\n\tpublic static final byte SDK_GINGERBREAD = 9;\n\tpublic static final byte SDK_GINGERBREAD_MR1 = 10;\n\tpublic static final byte SDK_HONEYCOMB = 11;\n\tpublic static final byte SDK_HONEYCOMB_MR1 = 12;\n\tpublic static final byte SDK_HONEYCOMB_MR2 = 13;\n\tpublic static final byte SDK_ICE_CREAM_SANDWICH = 14;\n\tpublic static final byte SDK_ICE_CREAM_SANDWICH_MR1 = 15;\n\tpublic static final byte SDK_JELLY_BEAN = 16;\n\tpublic static final byte SDK_JELLY_BEAN_MR1 = 17;\n\tpublic static final byte SDK_JELLY_BEAN_MR2 = 18;\n\tpublic static final byte SDK_KITKAT = 19;\n\tpublic static final byte SDK_LOLLIPOP = 21;\n\tpublic static final byte SDK_LOLLIPOP_MR1 = 22;\n\tpublic static final byte SDK_MNC = 23;\n\tpublic static final byte SDK_NOUGAT = 24;\n\tpublic static final byte SDK_NOUGAT_MR1 = 25;\n\tpublic static final byte SDK_OREO = 26;\n\tpublic static final byte SDK_OREO_MR1 = 27;\n\tpublic static final byte SDK_P = 28;\n\n\tpublic static final byte ORIENTATION_ANY = 0;\n\tpublic static final byte ORIENTATION_PORT = 1;\n\tpublic static final byte ORIENTATION_LAND = 2;\n\tpublic static final byte ORIENTATION_SQUARE = 3;\n\n\tpublic static final byte TOUCHSCREEN_ANY = 0;\n\tpublic static final byte TOUCHSCREEN_NOTOUCH = 1;\n\tpublic static final byte TOUCHSCREEN_STYLUS = 2;\n\tpublic static final byte TOUCHSCREEN_FINGER = 3;\n\n\tpublic static final int DENSITY_DEFAULT = 0;\n\tpublic static final int DENSITY_LOW = 120;\n\tpublic static final int DENSITY_MEDIUM = 160;\n\tpublic static final int DENSITY_400 = 190;\n\tpublic static final int DENSITY_TV = 213;\n\tpublic static final int DENSITY_HIGH = 240;\n\tpublic static final int DENSITY_XHIGH = 320;\n\tpublic static final int DENSITY_XXHIGH = 480;\n\tpublic static final int DENSITY_XXXHIGH = 640;\n\tpublic static final int DENSITY_ANY = 0xFFFE;\n\tpublic static final int DENSITY_NONE = 0xFFFF;\n\n\tpublic static final int MNC_ZERO = -1;\n\n\tpublic static final short MASK_LAYOUTDIR = 0xc0;\n\tpublic static final short SCREENLAYOUT_LAYOUTDIR_ANY = 0x00;\n\tpublic static final short SCREENLAYOUT_LAYOUTDIR_LTR = 0x40;\n\tpublic static final short SCREENLAYOUT_LAYOUTDIR_RTL = 0x80;\n\tpublic static final short SCREENLAYOUT_LAYOUTDIR_SHIFT = 0x06;\n\n\tpublic static final short MASK_SCREENROUND = 0x03;\n\tpublic static final short SCREENLAYOUT_ROUND_ANY = 0;\n\tpublic static final short SCREENLAYOUT_ROUND_NO = 0x1;\n\tpublic static final short SCREENLAYOUT_ROUND_YES = 0x2;\n\n\tpublic static final byte KEYBOARD_ANY = 0;\n\tpublic static final byte KEYBOARD_NOKEYS = 1;\n\tpublic static final byte KEYBOARD_QWERTY = 2;\n\tpublic static final byte KEYBOARD_12KEY = 3;\n\n\tpublic static final byte NAVIGATION_ANY = 0;\n\tpublic static final byte NAVIGATION_NONAV = 1;\n\tpublic static final byte NAVIGATION_DPAD = 2;\n\tpublic static final byte NAVIGATION_TRACKBALL = 3;\n\tpublic static final byte NAVIGATION_WHEEL = 4;\n\n\tpublic static final byte MASK_KEYSHIDDEN = 0x3;\n\tpublic static final byte KEYSHIDDEN_ANY = 0x0;\n\tpublic static final byte KEYSHIDDEN_NO = 0x1;\n\tpublic static final byte KEYSHIDDEN_YES = 0x2;\n\tpublic static final byte KEYSHIDDEN_SOFT = 0x3;\n\n\tpublic static final byte MASK_NAVHIDDEN = 0xc;\n\tpublic static final byte NAVHIDDEN_ANY = 0x0;\n\tpublic static final byte NAVHIDDEN_NO = 0x4;\n\tpublic static final byte NAVHIDDEN_YES = 0x8;\n\n\tpublic static final byte MASK_SCREENSIZE = 0x0f;\n\tpublic static final byte SCREENSIZE_ANY = 0x00;\n\tpublic static final byte SCREENSIZE_SMALL = 0x01;\n\tpublic static final byte SCREENSIZE_NORMAL = 0x02;\n\tpublic static final byte SCREENSIZE_LARGE = 0x03;\n\tpublic static final byte SCREENSIZE_XLARGE = 0x04;\n\n\tpublic static final byte MASK_SCREENLONG = 0x30;\n\tpublic static final byte SCREENLONG_ANY = 0x00;\n\tpublic static final byte SCREENLONG_NO = 0x10;\n\tpublic static final byte SCREENLONG_YES = 0x20;\n\n\tpublic static final byte MASK_UI_MODE_TYPE = 0x0f;\n\tpublic static final byte UI_MODE_TYPE_ANY = 0x00;\n\tpublic static final byte UI_MODE_TYPE_NORMAL = 0x01;\n\tpublic static final byte UI_MODE_TYPE_DESK = 0x02;\n\tpublic static final byte UI_MODE_TYPE_CAR = 0x03;\n\tpublic static final byte UI_MODE_TYPE_TELEVISION = 0x04;\n\tpublic static final byte UI_MODE_TYPE_APPLIANCE = 0x05;\n\tpublic static final byte UI_MODE_TYPE_WATCH = 0x06;\n\tpublic static final byte UI_MODE_TYPE_VR_HEADSET = 0x07;\n\n\t// start - miui\n\tpublic static final byte UI_MODE_TYPE_GODZILLAUI = 0x0b;\n\tpublic static final byte UI_MODE_TYPE_SMALLUI = 0x0c;\n\tpublic static final byte UI_MODE_TYPE_MEDIUMUI = 0x0d;\n\tpublic static final byte UI_MODE_TYPE_LARGEUI = 0x0e;\n\tpublic static final byte UI_MODE_TYPE_HUGEUI = 0x0f;\n\t// end - miui\n\n\tpublic static final byte MASK_UI_MODE_NIGHT = 0x30;\n\tpublic static final byte UI_MODE_NIGHT_ANY = 0x00;\n\tpublic static final byte UI_MODE_NIGHT_NO = 0x10;\n\tpublic static final byte UI_MODE_NIGHT_YES = 0x20;\n\n\tpublic static final byte COLOR_HDR_MASK = 0xC;\n\tpublic static final byte COLOR_HDR_NO = 0x4;\n\tpublic static final byte COLOR_HDR_SHIFT = 0x2;\n\tpublic static final byte COLOR_HDR_UNDEFINED = 0x0;\n\tpublic static final byte COLOR_HDR_YES = 0x8;\n\n\tpublic static final byte COLOR_UNDEFINED = 0x0;\n\n\tpublic static final byte COLOR_WIDE_UNDEFINED = 0x0;\n\tpublic static final byte COLOR_WIDE_NO = 0x1;\n\tpublic static final byte COLOR_WIDE_YES = 0x2;\n\tpublic static final byte COLOR_WIDE_MASK = 0x3;\n\n\tpublic static final byte GRAMMATICAL_GENDER_ANY = 0;\n\tpublic static final byte GRAMMATICAL_GENDER_NEUTER = 1;\n\tpublic static final byte GRAMMATICAL_GENDER_FEMININE = 2;\n\tpublic static final byte GRAMMATICAL_GENDER_MASCULINE = 3;\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/entry/ProtoValue.java",
    "content": "package jadx.core.xmlgen.entry;\n\nimport java.util.List;\n\npublic class ProtoValue {\n\tprivate String parent;\n\tprivate String name;\n\tprivate String value;\n\tprivate int type;\n\tprivate List<ProtoValue> namedValues;\n\n\tpublic ProtoValue(String value) {\n\t\tthis.value = value;\n\t}\n\n\tpublic ProtoValue() {\n\t}\n\n\tpublic int getType() {\n\t\treturn type;\n\t}\n\n\tpublic ProtoValue setType(int type) {\n\t\tthis.type = type;\n\t\treturn this;\n\t}\n\n\tpublic String getValue() {\n\t\treturn value;\n\t}\n\n\tpublic String getParent() {\n\t\treturn parent;\n\t}\n\n\tpublic ProtoValue setParent(String parent) {\n\t\tthis.parent = parent;\n\t\treturn this;\n\t}\n\n\tpublic ProtoValue setName(String name) {\n\t\tthis.name = name;\n\t\treturn this;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic ProtoValue setNamedValues(List<ProtoValue> namedValues) {\n\t\tthis.namedValues = namedValues;\n\t\treturn this;\n\t}\n\n\tpublic List<ProtoValue> getNamedValues() {\n\t\treturn namedValues;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/entry/RawNamedValue.java",
    "content": "package jadx.core.xmlgen.entry;\n\npublic class RawNamedValue {\n\tprivate final int nameRef;\n\tprivate final RawValue rawValue;\n\n\tpublic RawNamedValue(int nameRef, RawValue rawValue) {\n\t\tthis.nameRef = nameRef;\n\t\tthis.rawValue = rawValue;\n\t}\n\n\tpublic int getNameRef() {\n\t\treturn nameRef;\n\t}\n\n\tpublic RawValue getRawValue() {\n\t\treturn rawValue;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"RawNamedValue{nameRef=\" + nameRef + \", rawValue=\" + rawValue + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/entry/RawValue.java",
    "content": "package jadx.core.xmlgen.entry;\n\npublic final class RawValue {\n\tprivate final int dataType;\n\tprivate final int data;\n\n\tpublic RawValue(int dataType, int data) {\n\t\tthis.dataType = dataType;\n\t\tthis.data = data;\n\t}\n\n\tpublic int getDataType() {\n\t\treturn dataType;\n\t}\n\n\tpublic int getData() {\n\t\treturn data;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"RawValue: type=0x\" + Integer.toHexString(dataType) + \", value=\" + data;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/entry/ResourceEntry.java",
    "content": "package jadx.core.xmlgen.entry;\n\nimport java.util.List;\n\npublic final class ResourceEntry {\n\n\tprivate final int id;\n\tprivate final String pkgName;\n\tprivate final String typeName;\n\tprivate final String keyName;\n\tprivate final String config;\n\n\tprivate int parentRef;\n\tprivate ProtoValue protoValue;\n\tprivate RawValue simpleValue;\n\tprivate List<RawNamedValue> namedValues;\n\n\tpublic ResourceEntry(int id, String pkgName, String typeName, String keyName, String config) {\n\t\tthis.id = id;\n\t\tthis.pkgName = pkgName;\n\t\tthis.typeName = typeName;\n\t\tthis.keyName = keyName;\n\t\tthis.config = config;\n\t}\n\n\tpublic ResourceEntry copy(String newKeyName) {\n\t\tResourceEntry copy = new ResourceEntry(id, pkgName, typeName, newKeyName, config);\n\t\tcopy.parentRef = this.parentRef;\n\t\tcopy.protoValue = this.protoValue;\n\t\tcopy.simpleValue = this.simpleValue;\n\t\tcopy.namedValues = this.namedValues;\n\t\treturn copy;\n\t}\n\n\tpublic ResourceEntry copyWithId(String resName) {\n\t\treturn copy(String.format(\"%s_res_0x%08x\", resName, id));\n\t}\n\n\t/**\n\t * 32 bit resource ID as defined in AOSP.\n\t *\n\t * <ol>\n\t * <li>Package ID (8 bit)</li>\n\t * <li>Type ID (8 bit)</li>\n\t * <li>Entry ID (16 bit)</li>\n\t * </ol>\n\t *\n\t * See <code>make_resid()</code> in ResourceUtils.h\n\t *\n\t * @return resource ID\n\t */\n\tpublic int getId() {\n\t\treturn id;\n\t}\n\n\tpublic String getPkgName() {\n\t\treturn pkgName;\n\t}\n\n\tpublic String getTypeName() {\n\t\treturn typeName;\n\t}\n\n\tpublic String getKeyName() {\n\t\treturn keyName;\n\t}\n\n\tpublic String getConfig() {\n\t\treturn config;\n\t}\n\n\tpublic void setParentRef(int parentRef) {\n\t\tthis.parentRef = parentRef;\n\t}\n\n\tpublic int getParentRef() {\n\t\treturn parentRef;\n\t}\n\n\tpublic ProtoValue getProtoValue() {\n\t\treturn protoValue;\n\t}\n\n\tpublic void setProtoValue(ProtoValue protoValue) {\n\t\tthis.protoValue = protoValue;\n\t}\n\n\tpublic RawValue getSimpleValue() {\n\t\treturn simpleValue;\n\t}\n\n\tpublic void setSimpleValue(RawValue simpleValue) {\n\t\tthis.simpleValue = simpleValue;\n\t}\n\n\tpublic void setNamedValues(List<RawNamedValue> namedValues) {\n\t\tthis.namedValues = namedValues;\n\t}\n\n\tpublic List<RawNamedValue> getNamedValues() {\n\t\treturn namedValues;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"  0x\" + Integer.toHexString(id) + \" (\" + id + ')' + config + \" = \" + typeName + '.' + keyName;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/java/jadx/core/xmlgen/entry/ValuesParser.java",
    "content": "package jadx.core.xmlgen.entry;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.android.AndroidResourcesMap;\nimport jadx.core.xmlgen.BinaryXMLStrings;\nimport jadx.core.xmlgen.ParserConstants;\nimport jadx.core.xmlgen.XmlGenUtils;\n\npublic class ValuesParser extends ParserConstants {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ValuesParser.class);\n\n\tprivate final BinaryXMLStrings strings;\n\tprivate final Map<Integer, String> resMap;\n\n\tpublic ValuesParser(BinaryXMLStrings strings, Map<Integer, String> resMap) {\n\t\tthis.strings = strings;\n\t\tthis.resMap = resMap;\n\t}\n\n\t@Nullable\n\tpublic String getSimpleValueString(ResourceEntry ri) {\n\t\tProtoValue protoValue = ri.getProtoValue();\n\t\tif (protoValue != null) {\n\t\t\treturn protoValue.getValue();\n\t\t}\n\t\tRawValue simpleValue = ri.getSimpleValue();\n\t\tif (simpleValue == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn decodeValue(simpleValue);\n\t}\n\n\t@Nullable\n\tpublic String getValueString(ResourceEntry ri) {\n\t\tProtoValue protoValue = ri.getProtoValue();\n\t\tif (protoValue != null) {\n\t\t\tif (protoValue.getValue() != null) {\n\t\t\t\treturn protoValue.getValue();\n\t\t\t}\n\t\t\tList<ProtoValue> values = protoValue.getNamedValues();\n\t\t\tList<String> strList = new ArrayList<>(values.size());\n\t\t\tfor (ProtoValue value : values) {\n\t\t\t\tif (value.getName() == null) {\n\t\t\t\t\tstrList.add(value.getValue());\n\t\t\t\t} else {\n\t\t\t\t\tstrList.add(value.getName() + '=' + value.getValue());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn strList.toString();\n\t\t}\n\t\tRawValue simpleValue = ri.getSimpleValue();\n\t\tif (simpleValue != null) {\n\t\t\treturn decodeValue(simpleValue);\n\t\t}\n\t\tList<RawNamedValue> namedValues = ri.getNamedValues();\n\t\tList<String> strList = new ArrayList<>(namedValues.size());\n\t\tfor (RawNamedValue value : namedValues) {\n\t\t\tString nameStr = decodeNameRef(value.getNameRef());\n\t\t\tString valueStr = decodeValue(value.getRawValue());\n\t\t\tif (nameStr == null) {\n\t\t\t\tstrList.add(valueStr);\n\t\t\t} else {\n\t\t\t\tstrList.add(nameStr + '=' + valueStr);\n\t\t\t}\n\t\t}\n\t\treturn strList.toString();\n\t}\n\n\t@Nullable\n\tpublic String decodeValue(RawValue value) {\n\t\tint dataType = value.getDataType();\n\t\tint data = value.getData();\n\t\treturn decodeValue(dataType, data);\n\t}\n\n\t@Nullable\n\tpublic String decodeValue(int dataType, int data) {\n\t\tswitch (dataType) {\n\t\t\tcase TYPE_NULL:\n\t\t\t\treturn null;\n\t\t\tcase TYPE_STRING:\n\t\t\t\treturn strings.get(data);\n\t\t\tcase TYPE_INT_DEC:\n\t\t\t\treturn Integer.toString(data);\n\t\t\tcase TYPE_INT_HEX:\n\t\t\t\treturn \"0x\" + Integer.toHexString(data);\n\t\t\tcase TYPE_INT_BOOLEAN:\n\t\t\t\treturn data == 0 ? \"false\" : \"true\";\n\t\t\tcase TYPE_FLOAT:\n\t\t\t\treturn XmlGenUtils.floatToString(Float.intBitsToFloat(data));\n\t\t\tcase TYPE_INT_COLOR_ARGB8:\n\t\t\t\treturn String.format(\"#%08x\", data);\n\t\t\tcase TYPE_INT_COLOR_RGB8:\n\t\t\t\treturn String.format(\"#%06x\", data & 0xFFFFFF);\n\t\t\tcase TYPE_INT_COLOR_ARGB4:\n\t\t\t\treturn String.format(\"#%04x\", data & 0xFFFF);\n\t\t\tcase TYPE_INT_COLOR_RGB4:\n\t\t\t\treturn String.format(\"#%03x\", data & 0xFFF);\n\n\t\t\tcase TYPE_DYNAMIC_REFERENCE:\n\t\t\tcase TYPE_REFERENCE: {\n\t\t\t\tString ri = resMap.get(data);\n\t\t\t\tif (ri == null) {\n\t\t\t\t\tString androidRi = AndroidResourcesMap.getResName(data);\n\t\t\t\t\tif (androidRi != null) {\n\t\t\t\t\t\treturn \"@android:\" + androidRi;\n\t\t\t\t\t}\n\t\t\t\t\tif (data == 0) {\n\t\t\t\t\t\treturn \"0\";\n\t\t\t\t\t}\n\t\t\t\t\treturn \"?unknown_ref: \" + Integer.toHexString(data);\n\t\t\t\t}\n\t\t\t\treturn '@' + ri;\n\t\t\t}\n\n\t\t\tcase TYPE_ATTRIBUTE: {\n\t\t\t\tString ri = resMap.get(data);\n\t\t\t\tif (ri == null) {\n\t\t\t\t\tString androidRi = AndroidResourcesMap.getResName(data);\n\t\t\t\t\tif (androidRi != null) {\n\t\t\t\t\t\treturn \"?android:\" + androidRi;\n\t\t\t\t\t}\n\t\t\t\t\treturn \"?unknown_attr_ref: \" + Integer.toHexString(data);\n\t\t\t\t}\n\t\t\t\treturn '?' + ri;\n\t\t\t}\n\n\t\t\tcase TYPE_DIMENSION:\n\t\t\t\treturn XmlGenUtils.decodeComplex(data, false);\n\t\t\tcase TYPE_FRACTION:\n\t\t\t\treturn XmlGenUtils.decodeComplex(data, true);\n\t\t\tcase TYPE_DYNAMIC_ATTRIBUTE:\n\t\t\t\tLOG.warn(\"Data type TYPE_DYNAMIC_ATTRIBUTE not yet supported: {}\", data);\n\t\t\t\treturn \"  TYPE_DYNAMIC_ATTRIBUTE: \" + data;\n\n\t\t\tdefault:\n\t\t\t\tLOG.warn(\"Unknown data type: 0x{} {}\", Integer.toHexString(dataType), data);\n\t\t\t\treturn \"  ?0x\" + Integer.toHexString(dataType) + ' ' + data;\n\t\t}\n\t}\n\n\tpublic String decodeNameRef(int nameRef) {\n\t\tint ref = nameRef;\n\t\tif (isResInternalId(nameRef)) {\n\t\t\tref = nameRef & ATTR_TYPE_ANY;\n\t\t\tif (ref == 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tString ri = resMap.get(ref);\n\t\tif (ri != null) {\n\t\t\treturn ri.replace('/', '.');\n\t\t} else {\n\t\t\tString androidRi = AndroidResourcesMap.getResName(ref);\n\t\t\tif (androidRi != null) {\n\t\t\t\treturn \"android:\" + androidRi.replace('/', '.');\n\t\t\t}\n\t\t}\n\t\treturn \"?0x\" + Integer.toHexString(nameRef);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/main/resources/android/attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2006 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<!-- Formatting note: terminate all comments with a period, to avoid breaking\n     the documentation output. To suppress comment lines from the documentation\n     output, insert an eat-comment element after the comment lines.\n-->\n\n<resources>\n    <!-- These are the standard attributes that make up a complete theme. -->\n    <declare-styleable name=\"Theme\">\n        <!-- ============== -->\n        <!-- Generic styles -->\n        <!-- ============== -->\n        <eat-comment />\n\n        <!-- Specifies that a theme has a light background with dark text on top.  -->\n        <attr name=\"isLightTheme\" format=\"boolean\" />\n\n        <!-- Default color of foreground imagery. -->\n        <attr name=\"colorForeground\" format=\"color\" />\n        <!-- Default color of foreground imagery on an inverted background. -->\n        <attr name=\"colorForegroundInverse\" format=\"color\" />\n        <!-- Default color of background imagery, ex. full-screen windows. -->\n        <attr name=\"colorBackground\" format=\"color\" />\n        <!-- Default color of background imagery for floating components, ex. dialogs, popups, and cards. -->\n        <attr name=\"colorBackgroundFloating\" format=\"color\" />\n        <!-- This is a hint for a solid color that can be used for caching\n             rendered views.  This should be the color of the background when\n             there is a solid background color; it should be null when the\n             background is a texture or translucent.  When a device is able\n             to use accelerated drawing (thus setting state_accelerated), the\n             cache hint is ignored and always assumed to be transparent. -->\n        <attr name=\"colorBackgroundCacheHint\" format=\"color\" />\n\n        <!-- Default highlight color for items that are pressed. -->\n        <attr name=\"colorPressedHighlight\" format=\"color\" />\n        <!-- Default highlight color for items that are long-pressed. -->\n        <attr name=\"colorLongPressedHighlight\" format=\"color\" />\n        <!-- Default highlight color for items that are\n             focused. (Focused meaning cursor-based selection.) -->\n        <attr name=\"colorFocusedHighlight\" format=\"color\" />\n        <!-- Default highlight color for items that are\n             activated. (Activated meaning persistent selection.) -->\n        <attr name=\"colorActivatedHighlight\" format=\"color\" />\n        <!-- Default highlight color for items in multiple selection\n             mode. -->\n        <attr name=\"colorMultiSelectHighlight\" format=\"color\" />\n\n        <!-- Drawable to be drawn over the view to mark it as autofilled-->\n        <attr name=\"autofilledHighlight\" format=\"reference\" />\n\n        <!-- Max width of the autofill data set picker as a fraction of the screen width -->\n        <attr name=\"autofillDatasetPickerMaxWidth\" format=\"reference\" />\n\n        <!-- Max height of the autofill data set picker as a fraction of the screen height -->\n        <attr name=\"autofillDatasetPickerMaxHeight\" format=\"reference\" />\n\n        <!-- Max height of the the autofill save custom subtitle as a fraction of the screen width/height -->\n        <attr name=\"autofillSaveCustomSubtitleMaxHeight\" format=\"reference\" />\n\n        <!-- Default disabled alpha for widgets that set enabled/disabled alpha programmatically. -->\n        <attr name=\"disabledAlpha\" format=\"float\" />\n        <!-- The alpha applied to the foreground color to create the primary text color. -->\n        <attr name=\"primaryContentAlpha\" format=\"float\" />\n        <!-- The alpha applied to the foreground color to create the secondary text color. -->\n        <attr name=\"secondaryContentAlpha\" format=\"float\" />\n        <!-- Color used for error states and things that need to be drawn to\n             the users attention.. -->\n        <attr name=\"colorError\" format=\"reference|color\" />\n        <!-- Default background dim amount when a menu, dialog, or something similar pops up. -->\n        <attr name=\"backgroundDimAmount\" format=\"float\" />\n        <!-- Control whether dimming behind the window is enabled.  The default\n             theme does not set this value, meaning it is based on whether the\n             window is floating. -->\n        <attr name=\"backgroundDimEnabled\" format=\"boolean\" />\n        <!-- When windowBlurBehindEnabled is set, this is the amount of blur to apply\n             behind the window. The range is from 0, which means no blur, to 150.  -->\n        <attr name=\"windowBlurBehindRadius\" format=\"dimension\"/>\n        <!-- If set, everything behind the window will be blurred with radius\n             windowBackgroundBlurRadius. -->\n        <attr name=\"windowBlurBehindEnabled\" format=\"boolean\" />\n\n\n        <!-- Color of background imagery used for popup windows. -->\n        <attr name=\"colorPopupBackground\" format=\"color\" />\n        <!-- Color used for list divider. -->\n        <attr name=\"colorListDivider\" format=\"color\" />\n        <!-- Opacity used for list divider. -->\n        <attr name=\"opacityListDivider\" format=\"color\" />\n\n        <!-- =========== -->\n        <!-- Text styles -->\n        <!-- =========== -->\n        <eat-comment />\n\n        <!-- Default appearance of text: color, typeface, size, and style. -->\n        <attr name=\"textAppearance\" format=\"reference\" />\n        <!-- Default appearance of text against an inverted background:\n             color, typeface, size, and style. -->\n        <attr name=\"textAppearanceInverse\" format=\"reference\" />\n\n        <!-- The most prominent text color.  -->\n        <attr name=\"textColorPrimary\" format=\"reference|color\" />\n        <!-- Secondary text color. -->\n        <attr name=\"textColorSecondary\" format=\"reference|color\" />\n        <!-- Tertiary text color. -->\n        <attr name=\"textColorTertiary\" format=\"reference|color\" />\n\n        <!-- Primary inverse text color, useful for inverted backgrounds. -->\n        <attr name=\"textColorPrimaryInverse\" format=\"reference|color\" />\n        <!-- Secondary inverse text color, useful for inverted backgrounds. -->\n        <attr name=\"textColorSecondaryInverse\" format=\"reference|color\" />\n        <!-- Tertiary inverse text color, useful for inverted backgrounds. -->\n        <attr name=\"textColorTertiaryInverse\" format=\"reference|color\" />\n\n        <!-- Inverse hint text color. -->\n        <attr name=\"textColorHintInverse\" format=\"reference|color\" />\n\n        <!-- Bright text color. Only differentiates based on the disabled state. -->\n        <attr name=\"textColorPrimaryDisableOnly\" format=\"reference|color\" />\n\n        <!-- Bright inverse text color. Only differentiates based on the disabled state. -->\n        <attr name=\"textColorPrimaryInverseDisableOnly\" format=\"reference|color\" />\n\n        <!-- Bright text color. This does not differentiate the disabled state. As an example,\n             buttons use this since they display the disabled state via the background and not the\n             foreground text color. -->\n        <attr name=\"textColorPrimaryNoDisable\" format=\"reference|color\" />\n        <!-- Dim text color. This does not differentiate the disabled state. -->\n        <attr name=\"textColorSecondaryNoDisable\" format=\"reference|color\" />\n\n        <!-- Bright inverse text color. This does not differentiate the disabled state. -->\n        <attr name=\"textColorPrimaryInverseNoDisable\" format=\"reference|color\" />\n        <!-- Dim inverse text color. This does not differentiate the disabled state. -->\n        <attr name=\"textColorSecondaryInverseNoDisable\" format=\"reference|color\" />\n\n        <!-- Bright text color for use over activated backgrounds. -->\n        <attr name=\"textColorPrimaryActivated\" format=\"reference|color\" />\n        <!-- Dim text color for use over activated backgrounds. -->\n        <attr name=\"textColorSecondaryActivated\" format=\"reference|color\" />\n\n        <!-- Text color for urls in search suggestions, used by things like global search and the browser. @hide -->\n        <attr name=\"textColorSearchUrl\" format=\"reference|color\" />\n\n        <!-- Color of highlighted text, when used in a light theme. -->\n        <attr name=\"textColorHighlightInverse\" format=\"reference|color\" />\n        <!-- Color of link text (URLs), when used in a light theme. -->\n        <attr name=\"textColorLinkInverse\" format=\"reference|color\" />\n\n        <!-- Color of list item text in alert dialogs. -->\n        <attr name=\"textColorAlertDialogListItem\" format=\"reference|color\" />\n\n        <!-- Search widget more corpus result item background. -->\n        <attr name=\"searchWidgetCorpusItemBackground\" format=\"reference|color\" />\n\n        <!-- Text color, typeface, size, and style for \"large\" text. Defaults to primary text color. -->\n        <attr name=\"textAppearanceLarge\" format=\"reference\" />\n        <!-- Text color, typeface, size, and style for \"medium\" text. Defaults to primary text color. -->\n        <attr name=\"textAppearanceMedium\" format=\"reference\" />\n        <!-- Text color, typeface, size, and style for \"small\" text. Defaults to secondary text color. -->\n        <attr name=\"textAppearanceSmall\" format=\"reference\" />\n\n        <!-- Text color, typeface, size, and style for \"large\" inverse text. Defaults to primary inverse text color. -->\n        <attr name=\"textAppearanceLargeInverse\" format=\"reference\" />\n        <!-- Text color, typeface, size, and style for \"medium\" inverse text. Defaults to primary inverse text color. -->\n        <attr name=\"textAppearanceMediumInverse\" format=\"reference\" />\n        <!-- Text color, typeface, size, and style for \"small\" inverse text. Defaults to secondary inverse text color. -->\n        <attr name=\"textAppearanceSmallInverse\" format=\"reference\" />\n\n        <!-- Text color, typeface, size, and style for system search result title. Defaults to primary inverse text color. -->\n        <attr name=\"textAppearanceSearchResultTitle\" format=\"reference\" />\n        <!-- Text color, typeface, size, and style for system search result subtitle. Defaults to primary inverse text color. -->\n        <attr name=\"textAppearanceSearchResultSubtitle\" format=\"reference\" />\n\n        <!-- Text color, typeface, size, and style for the text inside of a button. -->\n        <attr name=\"textAppearanceButton\" format=\"reference\" />\n\n        <!-- Text color, typeface, size, and style for the text inside of a popup menu. -->\n        <attr name=\"textAppearanceLargePopupMenu\" format=\"reference\" />\n\n        <!-- Text color, typeface, size, and style for small text inside of a popup menu. -->\n        <attr name=\"textAppearanceSmallPopupMenu\" format=\"reference\" />\n\n        <!-- Text color, typeface, size, and style for header text inside of a popup menu. -->\n        <attr name=\"textAppearancePopupMenuHeader\" format=\"reference\" />\n\n        <!-- The underline color and thickness for easy correct suggestion -->\n        <attr name=\"textAppearanceEasyCorrectSuggestion\" format=\"reference\" />\n\n        <!-- The underline color and thickness for misspelled suggestion -->\n        <attr name=\"textAppearanceMisspelledSuggestion\" format=\"reference\" />\n\n        <!-- The underline color and thickness for auto correction suggestion -->\n        <attr name=\"textAppearanceAutoCorrectionSuggestion\" format=\"reference\" />\n\n        <!-- The underline color and thickness for grammar error suggestion -->\n        <attr name=\"textAppearanceGrammarErrorSuggestion\" format=\"reference\" />\n\n        <!--  The underline color -->\n        <attr name=\"textUnderlineColor\" format=\"reference|color\" />\n        <!--  The underline thickness -->\n        <attr name=\"textUnderlineThickness\" format=\"reference|dimension\" />\n\n        <!-- EditText text foreground color. -->\n        <attr name=\"editTextColor\" format=\"reference|color\" />\n        <!-- EditText background drawable. -->\n        <attr name=\"editTextBackground\" format=\"reference\" />\n\n        <!-- Popup text displayed in TextView when setError is used. -->\n        <attr name=\"errorMessageBackground\" format=\"reference\" />\n        <!-- Background used instead of errorMessageBackground when the popup has to be above. -->\n        <attr name=\"errorMessageAboveBackground\" format=\"reference\" />\n\n        <!-- A styled string, specifying the style to be used for showing\n             inline candidate text when composing with an input method.  The\n             text itself will be ignored, but the style spans will be applied\n             to the candidate text as it is edited. -->\n        <attr name=\"candidatesTextStyleSpans\" format=\"reference|string\" />\n\n        <!-- Drawable to use for check marks. -->\n        <attr name=\"textCheckMark\" format=\"reference\" />\n        <attr name=\"textCheckMarkInverse\" format=\"reference\" />\n\n        <!-- Drawable to use for multiple choice indicators. -->\n        <attr name=\"listChoiceIndicatorMultiple\" format=\"reference\" />\n\n        <!-- Drawable to use for single choice indicators. -->\n        <attr name=\"listChoiceIndicatorSingle\" format=\"reference\" />\n\n        <!-- Drawable used as a background for selected list items. -->\n        <attr name=\"listChoiceBackgroundIndicator\" format=\"reference\" />\n\n        <!-- Drawable used as a background for activated items. -->\n        <attr name=\"activatedBackgroundIndicator\" format=\"reference\" />\n\n        <!-- ============= -->\n        <!-- Button styles -->\n        <!-- ============= -->\n        <eat-comment />\n\n        <!-- Normal Button style. -->\n        <attr name=\"buttonStyle\" format=\"reference\" />\n\n        <!-- Small Button style. -->\n        <attr name=\"buttonStyleSmall\" format=\"reference\" />\n\n        <!-- Button style to inset into an EditText. -->\n        <attr name=\"buttonStyleInset\" format=\"reference\" />\n\n        <!-- ToggleButton style. -->\n        <attr name=\"buttonStyleToggle\" format=\"reference\" />\n\n        <!-- ============== -->\n        <!-- Gallery styles -->\n        <!-- ============== -->\n        <eat-comment />\n\n        <!-- The preferred background for gallery items. This should be set\n             as the background of any Views you provide from the Adapter. -->\n        <attr name=\"galleryItemBackground\" format=\"reference\" />\n\n        <!-- =========== -->\n        <!-- List styles -->\n        <!-- =========== -->\n        <eat-comment />\n\n        <!-- The preferred list item height. -->\n        <attr name=\"listPreferredItemHeight\" format=\"dimension\" />\n        <!-- A smaller, sleeker list item height. -->\n        <attr name=\"listPreferredItemHeightSmall\" format=\"dimension\" />\n        <!-- A larger, more robust list item height. -->\n        <attr name=\"listPreferredItemHeightLarge\" format=\"dimension\" />\n        <!-- The list item height for search results. @hide -->\n        <attr name=\"searchResultListItemHeight\" format=\"dimension\" />\n\n        <!-- The preferred padding along the left edge of list items. -->\n        <attr name=\"listPreferredItemPaddingLeft\" format=\"dimension\" />\n        <!-- The preferred padding along the right edge of list items. -->\n        <attr name=\"listPreferredItemPaddingRight\" format=\"dimension\" />\n\n        <!-- The preferred TextAppearance for the primary text of list items. -->\n        <attr name=\"textAppearanceListItem\" format=\"reference\" />\n        <!-- The preferred TextAppearance for the secondary text of list items. -->\n        <attr name=\"textAppearanceListItemSecondary\" format=\"reference\" />\n        <!-- The preferred TextAppearance for the primary text of small list items. -->\n        <attr name=\"textAppearanceListItemSmall\" format=\"reference\" />\n\n        <!-- The drawable for the list divider. -->\n        <attr name=\"listDivider\" format=\"reference\" />\n        <!-- The list divider used in alert dialogs. -->\n        <attr name=\"listDividerAlertDialog\" format=\"reference\" />\n        <!-- TextView style for list separators. -->\n        <attr name=\"listSeparatorTextViewStyle\" format=\"reference\" />\n        <!-- The preferred left padding for an expandable list item (for child-specific layouts,\n             use expandableListPreferredChildPaddingLeft). This takes into account\n             the indicator that will be shown to next to the item. -->\n        <attr name=\"expandableListPreferredItemPaddingLeft\" format=\"dimension\" />\n        <!-- The preferred left padding for an expandable list item that is a child.\n             If this is not provided, it defaults to the expandableListPreferredItemPaddingLeft. -->\n        <attr name=\"expandableListPreferredChildPaddingLeft\" format=\"dimension\" />\n        <!-- The preferred left bound for an expandable list item's indicator. For a child-specific\n             indicator, use expandableListPreferredChildIndicatorLeft. -->\n        <attr name=\"expandableListPreferredItemIndicatorLeft\" format=\"dimension\" />\n        <!-- The preferred right bound for an expandable list item's indicator. For a child-specific\n             indicator, use expandableListPreferredChildIndicatorRight. -->\n        <attr name=\"expandableListPreferredItemIndicatorRight\" format=\"dimension\" />\n        <!-- The preferred left bound for an expandable list child's indicator. -->\n        <attr name=\"expandableListPreferredChildIndicatorLeft\" format=\"dimension\" />\n        <!-- The preferred right bound for an expandable list child's indicator. -->\n        <attr name=\"expandableListPreferredChildIndicatorRight\" format=\"dimension\" />\n\n        <!-- The preferred item height for dropdown lists. -->\n        <attr name=\"dropdownListPreferredItemHeight\" format=\"dimension\" />\n\n        <!-- The preferred padding along the start edge of list items. -->\n        <attr name=\"listPreferredItemPaddingStart\" format=\"dimension\" />\n        <!-- The preferred padding along the end edge of list items. -->\n        <attr name=\"listPreferredItemPaddingEnd\" format=\"dimension\" />\n\n        <!-- ============= -->\n        <!-- Window styles -->\n        <!-- ============= -->\n        <eat-comment />\n\n        <!-- Drawable to use as the overall window background.  As of\n             {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this may\n             be a selector that uses state_accelerated to pick a non-solid\n             color when running on devices that can draw such a bitmap\n             with complex compositing on top at 60fps.\n\n             <p>There are a few special considerations to use when setting this\n             drawable:\n             <ul>\n             <li> This information will be used to infer the pixel format\n                  for your window's surface.  If the drawable has any\n                  non-opaque pixels, your window will be translucent\n                  (32 bpp).\n             <li> If you want to draw the entire background\n                  yourself, you should set this drawable to some solid\n                  color that closely matches that background (so the\n                  system's preview of your window will match), and\n                  then in code manually set your window's background to\n                  null so it will not be drawn.\n             </ul> -->\n        <attr name=\"windowBackground\" format=\"reference|color\" />\n        <!-- Drawable to draw selectively within the inset areas when the windowBackground\n             has been set to null. This protects against seeing visual garbage in the\n             surface when the app has not drawn any content into this area. One example is\n             when the user is resizing a window of an activity in multi-window mode. -->\n        <attr name=\"windowBackgroundFallback\" format=\"reference|color\" />\n        <!-- Blur the screen behind the window with the bounds of the window.\n             The radius defines the size of the neighbouring area, from which pixels will be\n             averaged to form the final color for each pixel in the region.\n             A radius of 0 means no blur. The higher the radius, the denser the blur.\n             Corresponds to {@link android.view.Window#setBackgroundBlurRadius}. -->\n        <attr name=\"windowBackgroundBlurRadius\" format=\"dimension\" />\n        <!-- Drawable to use as a frame around the window. -->\n        <attr name=\"windowFrame\" format=\"reference\" />\n        <!-- Flag indicating whether there should be no title on this window. -->\n        <attr name=\"windowNoTitle\" format=\"boolean\" />\n        <!-- Flag indicating whether this window should fill the entire screen.  Corresponds\n             to {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN}. -->\n        <attr name=\"windowFullscreen\" format=\"boolean\" />\n        <!-- Flag indicating whether this window should extend into overscan region.  Corresponds\n             to {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_OVERSCAN}.\n             @deprecated Overscan areas aren't set by any Android product anymore as of Android 11.\n             -->\n        <attr name=\"windowOverscan\" format=\"boolean\" />\n        <!-- Flag indicating whether this is a floating window. -->\n        <attr name=\"windowIsFloating\" format=\"boolean\" />\n        <!-- Flag indicating whether this is a translucent window. -->\n        <attr name=\"windowIsTranslucent\" format=\"boolean\" />\n        <!-- Flag indicating that this window's background should be the\n             user's current wallpaper.  Corresponds\n             to {@link android.view.WindowManager.LayoutParams#FLAG_SHOW_WALLPAPER}. -->\n        <attr name=\"windowShowWallpaper\" format=\"boolean\" />\n        <!-- This Drawable is overlaid over the foreground of the Window's content area, usually\n             to place a shadow below the title.  -->\n        <attr name=\"windowContentOverlay\" format=\"reference\" />\n        <!-- The style resource to use for a window's title bar height. -->\n        <attr name=\"windowTitleSize\" format=\"dimension\" />\n        <!-- The style resource to use for a window's title text. -->\n        <attr name=\"windowTitleStyle\" format=\"reference\" />\n        <!-- The style resource to use for a window's title area. -->\n        <attr name=\"windowTitleBackgroundStyle\" format=\"reference\" />\n\n        <!-- Reference to a style resource holding\n             the set of window animations to use, which can be\n             any of the attributes defined by\n             {@link android.R.styleable#WindowAnimation}. -->\n        <attr name=\"windowAnimationStyle\" format=\"reference\" />\n\n        <!-- Flag indicating whether this window should have an Action Bar\n             in place of the usual title bar. -->\n        <attr name=\"windowActionBar\" format=\"boolean\" />\n\n        <!-- Flag indicating whether this window's Action Bar should overlay\n             application content. Does nothing if the window would not\n             have an Action Bar. -->\n        <attr name=\"windowActionBarOverlay\" format=\"boolean\" />\n\n        <!-- Flag indicating whether action modes should overlay window content\n             when there is not reserved space for their UI (such as an Action Bar). -->\n        <attr name=\"windowActionModeOverlay\" format=\"boolean\" />\n\n        <!-- Defines the default soft input state that this window would\n             like when it is displayed.  Corresponds\n             to {@link android.view.WindowManager.LayoutParams#softInputMode}. -->\n        <attr name=\"windowSoftInputMode\">\n            <!-- Not specified, use what the system thinks is best.  This\n                 is the default. -->\n            <flag name=\"stateUnspecified\" value=\"0\" />\n            <!-- Leave the soft input window as-is, in whatever state it\n                 last was. -->\n            <flag name=\"stateUnchanged\" value=\"1\" />\n            <!-- Make the soft input area hidden when normally appropriate\n                 (when the user is navigating forward to your window). -->\n            <flag name=\"stateHidden\" value=\"2\" />\n            <!-- Always make the soft input area hidden when this window\n                 has input focus. -->\n            <flag name=\"stateAlwaysHidden\" value=\"3\" />\n            <!-- Make the soft input area visible when normally appropriate\n                 (when the user is navigating forward to your window). -->\n            <flag name=\"stateVisible\" value=\"4\" />\n            <!-- Always make the soft input area visible when this window\n                 has input focus. -->\n            <flag name=\"stateAlwaysVisible\" value=\"5\" />\n\n            <!-- The window resize/pan adjustment has not been specified,\n                 the system will automatically select between resize and pan\n                 modes, depending\n                 on whether the content of the window has any layout views\n                 that can scroll their contents.  If there is such a view,\n                 then the window will be resized, with the assumption being\n                 that the resizeable area can be reduced to make room for\n                 the input UI. -->\n            <flag name=\"adjustUnspecified\" value=\"0x00\" />\n            <!-- Always resize the window: the content area of the window is\n                 reduced to make room for the soft input area. -->\n            <flag name=\"adjustResize\" value=\"0x10\" />\n            <!-- Don't resize the window to make room for the soft input area;\n                 instead pan the contents of the window as focus moves inside\n                 of it so that the user can see what they are typing.  This is\n                 generally less desireable than panning because the user may\n                 need to close the input area to get at and interact with\n                 parts of the window. -->\n            <flag name=\"adjustPan\" value=\"0x20\" />\n            <!-- Don't resize <em>or</em> pan the window to make room for the\n                 soft input area; the window is never adjusted for it. -->\n            <flag name=\"adjustNothing\" value=\"0x30\" />\n        </attr>\n\n        <!-- Flag allowing you to disable the splash screen for a window. The default value is\n             false; if set to true, the system can never use the window's theme to show a splash\n             screen before your actual instance is shown to the user. -->\n        <attr name=\"windowDisablePreview\" format=\"boolean\" />\n\n        <!-- Flag indicating that this window should not be displayed at all.\n             The default value is false; if set to true, and this window is\n             the main window of an Activity, then it will never actually\n             be added to the window manager.  This means that your activity\n             must immediately quit without waiting for user interaction,\n             because there will be no such interaction coming. -->\n        <attr name=\"windowNoDisplay\" format=\"boolean\" />\n\n        <!-- Flag indicating that this window should allow touches to be split\n             across other windows that also support split touch.\n             The default value is true for applications with a targetSdkVersion\n             of Honeycomb or newer; false otherwise.\n             When this flag is false, the first pointer that goes down determines\n             the window to which all subsequent touches go until all pointers go up.\n             When this flag is true, each pointer (not necessarily the first) that\n             goes down determines the window to which all subsequent touches of that\n             pointer will go until that pointers go up thereby enabling touches\n             with multiple pointers to be split across multiple windows. -->\n        <attr name=\"windowEnableSplitTouch\" format=\"boolean\" />\n\n        <!-- Control whether a container should automatically close itself if\n             the user touches outside of it.  This only applies to activities\n             and dialogs.\n\n             <p>Note: this attribute will only be respected for applications\n             that are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}\n             or later. -->\n        <attr name=\"windowCloseOnTouchOutside\" format=\"boolean\" />\n\n        <!-- Flag indicating whether this window requests a translucent status bar.  Corresponds\n             to {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS}. -->\n        <attr name=\"windowTranslucentStatus\" format=\"boolean\" />\n\n        <!-- Flag indicating whether this window requests a translucent navigation bar.  Corresponds\n             to {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_NAVIGATION}. -->\n        <attr name=\"windowTranslucentNavigation\" format=\"boolean\" />\n\n        <!-- Flag to indicate that a window can be swiped away to be dismissed.\n             Corresponds to {@link android.view.Window#FEATURE_SWIPE_TO_DISMISS}. It will also\n             dynamically change translucency of the window, if the windowIsTranslucent is not set.\n             If windowIsTranslucent is set (to either true or false) it will obey that setting.\n             @deprecated Swipe-to-dismiss isn't functional anymore.\n             -->\n        <attr name=\"windowSwipeToDismiss\" format=\"boolean\" />\n\n        <!-- Flag indicating whether this window requests that content changes be performed\n             as scene changes with transitions. Corresponds to\n             {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS}. -->\n        <attr name=\"windowContentTransitions\" format=\"boolean\" />\n\n        <!-- Reference to a TransitionManager XML resource defining the desired\n             transitions between different window content. -->\n        <attr name=\"windowContentTransitionManager\" format=\"reference\" />\n\n        <!-- Flag indicating whether this window allows Activity Transitions.\n             Corresponds to {@link android.view.Window#FEATURE_ACTIVITY_TRANSITIONS}. -->\n        <attr name=\"windowActivityTransitions\" format=\"boolean\" />\n\n        <!-- Reference to a Transition XML resource defining the desired Transition\n             used to move Views into the initial Window's content Scene. Corresponds to\n             {@link android.view.Window#setEnterTransition(android.transition.Transition)}. -->\n        <attr name=\"windowEnterTransition\" format=\"reference\"/>\n\n        <!-- Reference to a Transition XML resource defining the desired Transition\n             used to move Views out of the scene when the Window is\n             preparing to close. Corresponds to\n             {@link android.view.Window#setReturnTransition(android.transition.Transition)}. -->\n        <attr name=\"windowReturnTransition\" format=\"reference\"/>\n\n        <!-- Reference to a Transition XML resource defining the desired Transition\n             used to move Views out of the Window's content Scene when launching a new Activity.\n             Corresponds to\n             {@link android.view.Window#setExitTransition(android.transition.Transition)}. -->\n        <attr name=\"windowExitTransition\" format=\"reference\"/>\n\n        <!-- Reference to a Transition XML resource defining the desired Transition\n             used to move Views in to the scene when returning from a previously-started Activity.\n             Corresponds to\n             {@link android.view.Window#setReenterTransition(android.transition.Transition)}. -->\n        <attr name=\"windowReenterTransition\" format=\"reference\"/>\n\n        <!-- Reference to a Transition XML resource defining the desired Transition\n             used to move shared elements transferred into the Window's initial content Scene.\n             Corresponds to {@link android.view.Window#setSharedElementEnterTransition(\n             android.transition.Transition)}. -->\n        <attr name=\"windowSharedElementEnterTransition\" format=\"reference\"/>\n\n        <!-- Reference to a Transition XML resource defining the desired Transition\n             used to move shared elements transferred back to a calling Activity.\n             Corresponds to {@link android.view.Window#setSharedElementReturnTransition(\n             android.transition.Transition)}. -->\n        <attr name=\"windowSharedElementReturnTransition\" format=\"reference\"/>\n\n        <!-- Reference to a Transition XML resource defining the desired Transition\n             used when starting a new Activity to move shared elements prior to transferring\n             to the called Activity.\n             Corresponds to {@link android.view.Window#setSharedElementExitTransition(\n             android.transition.Transition)}. -->\n        <attr name=\"windowSharedElementExitTransition\" format=\"reference\"/>\n\n        <!-- Reference to a Transition XML resource defining the desired Transition\n             used for shared elements transferred back to a calling Activity.\n             Corresponds to {@link android.view.Window#setSharedElementReenterTransition(\n             android.transition.Transition)}. -->\n        <attr name=\"windowSharedElementReenterTransition\" format=\"reference\"/>\n\n        <!-- Flag indicating whether this Window's transition should overlap with\n             the exiting transition of the calling Activity. Corresponds to\n             {@link android.view.Window#setAllowEnterTransitionOverlap(boolean)}.\n             The default value is true. -->\n        <attr name=\"windowAllowEnterTransitionOverlap\" format=\"boolean\"/>\n\n        <!-- Flag indicating whether this Window's transition should overlap with\n             the exiting transition of the called Activity when the called Activity\n             finishes. Corresponds to\n             {@link android.view.Window#setAllowReturnTransitionOverlap(boolean)}.\n             The default value is true. -->\n        <attr name=\"windowAllowReturnTransitionOverlap\" format=\"boolean\"/>\n\n        <!-- Indicates whether or not shared elements should use an overlay\n             during transitions. The default value is true. -->\n        <attr name=\"windowSharedElementsUseOverlay\" format=\"boolean\"/>\n\n        <!-- Internal layout used internally for window decor -->\n        <attr name=\"windowActionBarFullscreenDecorLayout\" format=\"reference\" />\n\n        <!-- The duration, in milliseconds, of the window background fade duration\n             when transitioning into or away from an Activity when called with an\n             Activity Transition. Corresponds to\n             {@link android.view.Window#setTransitionBackgroundFadeDuration(long)}. -->\n        <attr name=\"windowTransitionBackgroundFadeDuration\" format=\"integer\"/>\n\n        <!-- ============ -->\n        <!-- Floating toolbar styles -->\n        <!-- ============ -->\n       <eat-comment />\n       <attr name=\"floatingToolbarCloseDrawable\" format=\"reference\" />\n       <attr name=\"floatingToolbarItemBackgroundBorderlessDrawable\" format=\"reference\" />\n       <attr name=\"floatingToolbarItemBackgroundDrawable\" format=\"reference\" />\n       <attr name=\"floatingToolbarOpenDrawable\" format=\"reference\" />\n       <attr name=\"floatingToolbarDividerColor\" format=\"reference\" />\n\n        <!-- ============ -->\n        <!-- Alert Dialog styles -->\n        <!-- ============ -->\n        <eat-comment />\n        <attr name=\"alertDialogStyle\" format=\"reference\" />\n        <attr name=\"alertDialogButtonGroupStyle\" format=\"reference\" />\n        <attr name=\"alertDialogCenterButtons\" format=\"boolean\" />\n\n        <!-- ============== -->\n        <!-- Image elements -->\n        <!-- ============== -->\n        <eat-comment />\n\n        <!-- Background that can be used behind parts of a UI that provide\n             details on data the user is selecting.  For example, this is\n             the background element of PreferenceActivity's embedded\n             preference fragment. -->\n        <attr name=\"detailsElementBackground\" format=\"reference\" />\n\n        <!-- Icon that should be used to indicate that an app is waiting for a fingerprint scan.\n             This should be used whenever an app is requesting the user to place a finger on the\n             fingerprint sensor. It can be combined with other drawables such as colored circles, so\n             the appearance matches the branding of the app requesting the fingerprint scan.-->\n        <attr name=\"fingerprintAuthDrawable\" format=\"reference\" />\n\n        <!-- ============ -->\n        <!-- Panel styles -->\n        <!-- ============ -->\n        <eat-comment />\n\n        <!-- The background of a panel when it is inset from the left and right edges of the screen. -->\n        <attr name=\"panelBackground\" format=\"reference|color\" />\n        <!-- The background of a panel when it extends to the left and right edges of the screen. -->\n        <attr name=\"panelFullBackground\" format=\"reference|color\" />\n        <!-- Default color of foreground panel imagery. -->\n        <attr name=\"panelColorForeground\" format=\"reference|color\" />\n        <!-- Color that matches (as closely as possible) the panel background. -->\n        <attr name=\"panelColorBackground\" format=\"reference|color\" />\n        <!-- Default appearance of panel text. -->\n        <attr name=\"panelTextAppearance\" format=\"reference\" />\n\n        <attr name=\"panelMenuIsCompact\" format=\"boolean\" />\n        <attr name=\"panelMenuListWidth\" format=\"dimension\" />\n        <attr name=\"panelMenuListTheme\" format=\"reference\" />\n\n        <!-- =================== -->\n        <!-- Other widget styles -->\n        <!-- =================== -->\n        <eat-comment />\n\n        <!-- Default AbsListView style. -->\n        <attr name=\"absListViewStyle\" format=\"reference\" />\n        <!-- Default AutoCompleteTextView style. -->\n        <attr name=\"autoCompleteTextViewStyle\" format=\"reference\" />\n        <!-- Default Checkbox style. -->\n        <attr name=\"checkboxStyle\" format=\"reference\" />\n        <!-- Default CheckedTextView style. -->\n        <attr name=\"checkedTextViewStyle\" format=\"reference\" />\n        <!-- Default ListView style for drop downs. -->\n        <attr name=\"dropDownListViewStyle\" format=\"reference\" />\n        <!-- Default EditText style. -->\n        <attr name=\"editTextStyle\" format=\"reference\" />\n        <!-- Default ExpandableListView style. -->\n        <attr name=\"expandableListViewStyle\" format=\"reference\" />\n        <!-- ExpandableListView with white background. -->\n        <attr name=\"expandableListViewWhiteStyle\" format=\"reference\" />\n        <!-- Default Gallery style. -->\n        <attr name=\"galleryStyle\" format=\"reference\" />\n        <!-- Default GestureOverlayView style. -->\n        <attr name=\"gestureOverlayViewStyle\" format=\"reference\" />\n        <!-- Default GridView style. -->\n        <attr name=\"gridViewStyle\" format=\"reference\" />\n        <!-- The style resource to use for an ImageButton. -->\n        <attr name=\"imageButtonStyle\" format=\"reference\" />\n        <!-- The style resource to use for an ImageButton that is an image well. -->\n        <attr name=\"imageWellStyle\" format=\"reference\" />\n        <!-- Default menu-style ListView style. -->\n        <attr name=\"listMenuViewStyle\" format=\"reference\" />\n        <!-- Default ListView style. -->\n        <attr name=\"listViewStyle\" format=\"reference\" />\n        <!-- ListView with white background. -->\n        <attr name=\"listViewWhiteStyle\" format=\"reference\" />\n        <!-- Default PopupWindow style. -->\n        <attr name=\"popupWindowStyle\" format=\"reference\" />\n        <!-- Default ProgressBar style. This is a medium circular progress bar. -->\n        <attr name=\"progressBarStyle\" format=\"reference\" />\n        <!-- Horizontal ProgressBar style. This is a horizontal progress bar. -->\n        <attr name=\"progressBarStyleHorizontal\" format=\"reference\" />\n        <!-- Small ProgressBar style. This is a small circular progress bar. -->\n        <attr name=\"progressBarStyleSmall\" format=\"reference\" />\n        <!-- Small ProgressBar in title style. This is a small circular progress bar that will be placed in title bars. -->\n        <attr name=\"progressBarStyleSmallTitle\" format=\"reference\" />\n        <!-- Large ProgressBar style. This is a large circular progress bar. -->\n        <attr name=\"progressBarStyleLarge\" format=\"reference\" />\n        <!-- Inverse ProgressBar style. This is a medium circular progress bar. -->\n        <attr name=\"progressBarStyleInverse\" format=\"reference\" />\n        <!-- Small inverse ProgressBar style. This is a small circular progress bar. -->\n        <attr name=\"progressBarStyleSmallInverse\" format=\"reference\" />\n        <!-- Large inverse ProgressBar style. This is a large circular progress bar. -->\n        <attr name=\"progressBarStyleLargeInverse\" format=\"reference\" />\n        <!-- Default SeekBar style. -->\n        <attr name=\"seekBarStyle\" format=\"reference\" />\n        <!-- Default RatingBar style. -->\n        <attr name=\"ratingBarStyle\" format=\"reference\" />\n        <!-- Indicator RatingBar style. -->\n        <attr name=\"ratingBarStyleIndicator\" format=\"reference\" />\n        <!-- Small indicator RatingBar style. -->\n        <attr name=\"ratingBarStyleSmall\" format=\"reference\" />\n        <!-- Default RadioButton style. -->\n        <attr name=\"radioButtonStyle\" format=\"reference\" />\n        <!-- Default ScrollView style. -->\n        <attr name=\"scrollViewStyle\" format=\"reference\" />\n        <!-- Default HorizontalScrollView style. -->\n        <attr name=\"horizontalScrollViewStyle\" format=\"reference\" />\n        <!-- Default Spinner style. -->\n        <attr name=\"spinnerStyle\" format=\"reference\" />\n        <!-- Default dropdown Spinner style. -->\n        <attr name=\"dropDownSpinnerStyle\" format=\"reference\" />\n        <!-- Default ActionBar dropdown style. -->\n        <attr name=\"actionDropDownStyle\" format=\"reference\" />\n        <!-- Default action button style. -->\n        <attr name=\"actionButtonStyle\" format=\"reference\" />\n        <!-- Default Star style. -->\n        <attr name=\"starStyle\" format=\"reference\" />\n        <!-- Default TabWidget style. -->\n        <attr name=\"tabWidgetStyle\" format=\"reference\" />\n        <!-- Default TextView style. -->\n        <attr name=\"textViewStyle\" format=\"reference\" />\n        <!-- Default WebTextView style. -->\n        <attr name=\"webTextViewStyle\" format=\"reference\" />\n        <!-- Default WebView style. -->\n        <attr name=\"webViewStyle\" format=\"reference\" />\n        <!-- Default style for drop down items. -->\n        <attr name=\"dropDownItemStyle\" format=\"reference\" />\n         <!-- Default style for spinner drop down items. -->\n        <attr name=\"spinnerDropDownItemStyle\" format=\"reference\" />\n        <!-- Default style for drop down hints. -->\n        <attr name=\"dropDownHintAppearance\" format=\"reference\" />\n        <!-- Default spinner item style. -->\n        <attr name=\"spinnerItemStyle\" format=\"reference\" />\n        <!-- Default MapView style. -->\n        <attr name=\"mapViewStyle\" format=\"reference\" />\n        <!-- Drawable used as an overlay on top of quickcontact photos. -->\n        <attr name=\"quickContactBadgeOverlay\" format=\"reference\" />\n        <!-- Default quickcontact badge style with small quickcontact window. -->\n        <attr name=\"quickContactBadgeStyleWindowSmall\" format=\"reference\" />\n        <!-- Default quickcontact badge style with medium quickcontact window. -->\n        <attr name=\"quickContactBadgeStyleWindowMedium\" format=\"reference\" />\n        <!-- Default quickcontact badge style with large quickcontact window. -->\n        <attr name=\"quickContactBadgeStyleWindowLarge\" format=\"reference\" />\n        <!-- Default quickcontact badge style with small quickcontact window. -->\n        <attr name=\"quickContactBadgeStyleSmallWindowSmall\" format=\"reference\" />\n        <!-- Default quickcontact badge style with medium quickcontact window. -->\n        <attr name=\"quickContactBadgeStyleSmallWindowMedium\" format=\"reference\" />\n        <!-- Default quickcontact badge style with large quickcontact window. -->\n        <attr name=\"quickContactBadgeStyleSmallWindowLarge\" format=\"reference\" />\n        <!-- Reference to a style that will be used for the window containing a text\n             selection anchor. -->\n        <attr name=\"textSelectHandleWindowStyle\" format=\"reference\" />\n        <!-- Reference to a style that will be used for the window containing a list of possible\n             text suggestions in an EditText. -->\n        <attr name=\"textSuggestionsWindowStyle\" format=\"reference\" />\n        <!-- Default ListPopupWindow style. -->\n        <attr name=\"listPopupWindowStyle\" format=\"reference\" />\n        <!-- Default PopupMenu style. -->\n        <attr name=\"popupMenuStyle\" format=\"reference\" />\n        <!-- Default context menu PopupMenu style. -->\n        <attr name=\"contextPopupMenuStyle\" format=\"reference\" />\n        <!-- Default StackView style. -->\n        <attr name=\"stackViewStyle\" format=\"reference\" />\n        <!-- Magnifier style. -->\n        <attr name=\"magnifierStyle\" format=\"reference\" />\n\n        <!-- Default style for the FragmentBreadCrumbs widget. This widget is deprecated\n             starting in API level 21 ({@link android.os.Build.VERSION_CODES#.L}). -->\n        <attr name=\"fragmentBreadCrumbsStyle\" format=\"reference\" />\n\n        <!-- NumberPicker style. -->\n        <attr name=\"numberPickerStyle\" format=\"reference\" />\n\n        <!-- The CalendarView style. -->\n        <attr name=\"calendarViewStyle\" format=\"reference\" />\n\n        <!-- The TimePicker style. -->\n        <attr name=\"timePickerStyle\" format=\"reference\" />\n\n        <!-- The TimePicker dialog theme. -->\n        <attr name=\"timePickerDialogTheme\" format=\"reference\" />\n\n        <!-- The DatePicker style. -->\n        <attr name=\"datePickerStyle\" format=\"reference\" />\n\n        <!-- The DatePicker dialog theme. -->\n        <attr name=\"datePickerDialogTheme\" format=\"reference\" />\n\n        <!-- Default ActivityChooserView style. -->\n        <attr name=\"activityChooserViewStyle\" format=\"reference\" />\n\n        <!-- Default Toolbar style. -->\n        <attr name=\"toolbarStyle\" format=\"reference\" />\n\n        <!-- Fast scroller styles -->\n        <eat-comment />\n\n        <!-- Drawable to use as the fast scroll thumb. -->\n        <attr name=\"fastScrollThumbDrawable\" format=\"reference\" />\n        <!-- Drawable to use as the fast scroll index preview window background\n             when shown on the right. -->\n        <attr name=\"fastScrollPreviewBackgroundRight\" format=\"reference\" />\n        <!-- Drawable to use as the fast scroll index preview window background\n             when shown on the left. -->\n        <attr name=\"fastScrollPreviewBackgroundLeft\" format=\"reference\" />\n        <!-- Drawable to use as the track for the fast scroll thumb.\n             This may be null. -->\n        <attr name=\"fastScrollTrackDrawable\" format=\"reference\" />\n        <!-- Position of the fast scroll index overlay window. -->\n        <attr name=\"fastScrollOverlayPosition\">\n            <enum name=\"floating\" value=\"0\" />\n            <enum name=\"atThumb\" value=\"1\" />\n            <enum name=\"aboveThumb\" value=\"2\" />\n        </attr>\n        <!-- Text color for the fast scroll index overlay. Make sure it\n             plays nicely with fastScrollPreviewBackground[Left|Right]. -->\n        <attr name=\"fastScrollTextColor\" format=\"color\" />\n\n        <!-- =================== -->\n        <!-- Action bar styles   -->\n        <!-- =================== -->\n        <eat-comment />\n        <!-- Default style for tabs within an action bar. -->\n        <attr name=\"actionBarTabStyle\" format=\"reference\" />\n        <!-- Reference to a style for the Action Bar Tab Bar. -->\n        <attr name=\"actionBarTabBarStyle\" format=\"reference\" />\n        <!-- Reference to a style for the Action Bar Tab text. -->\n        <attr name=\"actionBarTabTextStyle\" format=\"reference\" />\n        <!-- Reference to a style for Action Bar overflow buttons. -->\n        <attr name=\"actionOverflowButtonStyle\" format=\"reference\" />\n        <!-- Reference to a style for the Action Bar menu. -->\n        <attr name=\"actionOverflowMenuStyle\" format=\"reference\" />\n        <!-- Reference to a theme that should be used to inflate popups\n             shown by widgets in the action bar. -->\n        <attr name=\"actionBarPopupTheme\" format=\"reference\" />\n        <!-- Reference to a style for the Action Bar. -->\n        <attr name=\"actionBarStyle\" format=\"reference\" />\n        <!-- Reference to a style for the split Action Bar. This style\n             controls the split component that holds the menu/action\n             buttons. actionBarStyle is still used for the primary\n             bar. -->\n        <attr name=\"actionBarSplitStyle\" format=\"reference\" />\n        <!-- Reference to a theme that should be used to inflate the\n             action bar. This will be inherited by any widget inflated\n             into the action bar. -->\n        <attr name=\"actionBarTheme\" format=\"reference\" />\n        <!-- Reference to a theme that should be used to inflate widgets\n             and layouts destined for the action bar. Most of the time\n             this will be a reference to the current theme, but when\n             the action bar has a significantly different contrast\n             profile than the rest of the activity the difference\n             can become important. If this is set to @null the current\n             theme will be used.-->\n        <attr name=\"actionBarWidgetTheme\" format=\"reference\" />\n        <!-- Size of the Action Bar, including the contextual\n             bar used to present Action Modes. -->\n        <attr name=\"actionBarSize\" format=\"dimension\" >\n            <enum name=\"wrap_content\" value=\"0\" />\n        </attr>\n        <!-- Custom divider drawable to use for elements in the action bar. -->\n        <attr name=\"actionBarDivider\" format=\"reference\" />\n        <!-- Custom item state list drawable background for action bar items. -->\n        <attr name=\"actionBarItemBackground\" format=\"reference\" />\n        <!-- TextAppearance style that will be applied to text that\n             appears within action menu items. -->\n        <attr name=\"actionMenuTextAppearance\" format=\"reference\" />\n        <!-- Color for text that appears within action menu items. -->\n        <attr name=\"actionMenuTextColor\" format=\"color|reference\" />\n\n        <!-- =================== -->\n        <!-- Action mode styles  -->\n        <!-- =================== -->\n        <eat-comment />\n        <!-- Reference to a style for the Action Mode. -->\n        <attr name=\"actionModeStyle\" format=\"reference\" />\n        <!-- Reference to a style for the Action Mode close button. -->\n        <attr name=\"actionModeCloseButtonStyle\" format=\"reference\" />\n        <!-- Background drawable to use for action mode UI. -->\n        <attr name=\"actionModeBackground\" format=\"reference\" />\n        <!-- Background drawable to use for action mode UI in the lower split bar. -->\n        <attr name=\"actionModeSplitBackground\" format=\"reference\" />\n        <!-- Drawable to use for the close action mode button. -->\n        <attr name=\"actionModeCloseDrawable\" format=\"reference\" />\n\n        <!-- Drawable to use for the Cut action button in Contextual Action Bar. -->\n        <attr name=\"actionModeCutDrawable\" format=\"reference\" />\n        <!-- Drawable to use for the Copy action button in Contextual Action Bar. -->\n        <attr name=\"actionModeCopyDrawable\" format=\"reference\" />\n        <!-- Drawable to use for the Paste action button in Contextual Action Bar. -->\n        <attr name=\"actionModePasteDrawable\" format=\"reference\" />\n        <!-- Drawable to use for the Select all action button in Contextual Action Bar. -->\n        <attr name=\"actionModeSelectAllDrawable\" format=\"reference\" />\n        <!-- Drawable to use for the Share action button in WebView selection action modes. -->\n        <attr name=\"actionModeShareDrawable\" format=\"reference\" />\n        <!-- Drawable to use for the Find action button in WebView selection action modes. -->\n        <attr name=\"actionModeFindDrawable\" format=\"reference\" />\n        <!-- Drawable to use for the Web Search action button in WebView selection action modes. -->\n        <attr name=\"actionModeWebSearchDrawable\" format=\"reference\" />\n        <!-- Drawable to use for the Undo action button in WebView selection action modes. -->\n        <attr name=\"actionModeUndoDrawable\" format=\"reference\" />\n        <!-- Drawable to use for the Redo action button in WebView selection action modes. -->\n        <attr name=\"actionModeRedoDrawable\" format=\"reference\" />\n\n        <!-- PopupWindow style to use for action modes when showing as a window overlay. -->\n        <attr name=\"actionModePopupWindowStyle\" format=\"reference\" />\n\n        <!-- =================== -->\n        <!-- Preference styles   -->\n        <!-- =================== -->\n        <eat-comment />\n\n        <!-- Default style for PreferenceScreen. -->\n        <attr name=\"preferenceScreenStyle\" format=\"reference\" />\n        <!-- Default style for the PreferenceActivity. -->\n        <attr name=\"preferenceActivityStyle\" format=\"reference\" />\n        <!-- Default style for Headers pane in PreferenceActivity. -->\n        <attr name=\"preferenceFragmentStyle\" format=\"reference\" />\n        <!-- Default style for PreferenceCategory. -->\n        <attr name=\"preferenceCategoryStyle\" format=\"reference\" />\n        <!-- Default style for Preference. -->\n        <attr name=\"preferenceStyle\" format=\"reference\" />\n        <!-- Default style for informational Preference. -->\n        <attr name=\"preferenceInformationStyle\" format=\"reference\" />\n        <!-- Default style for CheckBoxPreference. -->\n        <attr name=\"checkBoxPreferenceStyle\" format=\"reference\" />\n        <!-- Default style for YesNoPreference. -->\n        <attr name=\"yesNoPreferenceStyle\" format=\"reference\" />\n        <!-- Default style for DialogPreference. -->\n        <attr name=\"dialogPreferenceStyle\" format=\"reference\" />\n        <!-- Default style for EditTextPreference. -->\n        <attr name=\"editTextPreferenceStyle\" format=\"reference\" />\n        <!-- @hide Default style for SeekBarDialogPreference. -->\n        <attr name=\"seekBarDialogPreferenceStyle\" format=\"reference\" />\n        <!-- Default style for RingtonePreference. -->\n        <attr name=\"ringtonePreferenceStyle\" format=\"reference\" />\n        <!-- The preference layout that has the child/tabbed effect. -->\n        <attr name=\"preferenceLayoutChild\" format=\"reference\" />\n        <!-- Preference panel style -->\n        <attr name=\"preferencePanelStyle\" format=\"reference\" />\n        <!-- Preference headers panel style -->\n        <attr name=\"preferenceHeaderPanelStyle\" format=\"reference\" />\n        <!-- Preference list style -->\n        <attr name=\"preferenceListStyle\" format=\"reference\" />\n        <!-- Preference fragment list style -->\n        <attr name=\"preferenceFragmentListStyle\" format=\"reference\" />\n        <!-- Preference fragment padding side -->\n        <attr name=\"preferenceFragmentPaddingSide\" format=\"dimension\" />\n        <!-- Default style for switch preferences. -->\n        <attr name=\"switchPreferenceStyle\" format=\"reference\" />\n        <!-- Default style for seekbar preferences. -->\n        <attr name=\"seekBarPreferenceStyle\" format=\"reference\" />\n\n        <!-- ============================ -->\n        <!-- Text selection handle styles -->\n        <!-- ============================ -->\n        <eat-comment />\n\n        <!-- Reference to a drawable that will be used to display a text selection\n             anchor on the left side of a selection region. -->\n        <attr name=\"textSelectHandleLeft\" format=\"reference\" />\n        <!-- Reference to a drawable that will be used to display a text selection\n             anchor on the right side of a selection region. -->\n        <attr name=\"textSelectHandleRight\" format=\"reference\" />\n        <!-- Reference to a drawable that will be used to display a text selection\n             anchor for positioning the cursor within text. -->\n        <attr name=\"textSelectHandle\" format=\"reference\" />\n        <!-- The layout of the view that is displayed on top of the cursor to paste inside a\n             TextEdit field. -->\n        <attr name=\"textEditPasteWindowLayout\" format=\"reference\" />\n        <!-- Variation of textEditPasteWindowLayout displayed when the clipboard is empty. -->\n        <attr name=\"textEditNoPasteWindowLayout\" format=\"reference\" />\n        <!-- Used instead of textEditPasteWindowLayout when the window is moved on the side of the\n             insertion cursor because it would be clipped if it were positioned on top. -->\n        <attr name=\"textEditSidePasteWindowLayout\" format=\"reference\" />\n        <!-- Variation of textEditSidePasteWindowLayout displayed when the clipboard is empty. -->\n        <attr name=\"textEditSideNoPasteWindowLayout\" format=\"reference\" />\n\n        <!-- Layout of the TextView item that will populate the suggestion popup window. -->\n        <attr name=\"textEditSuggestionItemLayout\" format=\"reference\" />\n        <!-- Layout of the container of the suggestion popup window. -->\n        <attr name=\"textEditSuggestionContainerLayout\" format=\"reference\" />\n        <!-- Text appearance of the focused words to be replaced by suggested word. -->\n        <attr name=\"textEditSuggestionHighlightStyle\" format=\"reference\" />\n\n        <!-- Theme to use for dialogs spawned from this theme. -->\n        <attr name=\"dialogTheme\" format=\"reference\" />\n        <!-- Window decor layout to use in dialog mode with icons. -->\n        <attr name=\"dialogTitleIconsDecorLayout\" format=\"reference\" />\n        <!-- Window decor layout to use in dialog mode with custom titles. -->\n        <attr name=\"dialogCustomTitleDecorLayout\" format=\"reference\" />\n        <!-- Window decor layout to use in dialog mode with title only. -->\n        <attr name=\"dialogTitleDecorLayout\" format=\"reference\" />\n        <!-- Preferred padding for dialog content. -->\n        <attr name=\"dialogPreferredPadding\" format=\"dimension\" />\n        <!-- Corner radius of dialogs. -->\n        <attr name=\"dialogCornerRadius\" format=\"dimension\" />\n\n        <!-- Theme to use for alert dialogs spawned from this theme. -->\n        <attr name=\"alertDialogTheme\" format=\"reference\" />\n        <!-- Icon drawable to use for alerts. -->\n        <attr name=\"alertDialogIcon\" format=\"reference\" />\n\n        <!-- Theme to use for presentations spawned from this theme. -->\n        <attr name=\"presentationTheme\" format=\"reference\" />\n\n        <!-- Drawable to use for generic vertical dividers. -->\n        <attr name=\"dividerVertical\" format=\"reference\" />\n\n        <!-- Drawable to use for generic horizontal dividers. -->\n        <attr name=\"dividerHorizontal\" format=\"reference\" />\n\n        <!-- Style for button bars. -->\n        <attr name=\"buttonBarStyle\" format=\"reference\" />\n\n        <!-- Style for buttons within button bars. -->\n        <attr name=\"buttonBarButtonStyle\" format=\"reference\" />\n\n        <!-- Style for the \"positive\" buttons within button bars. -->\n        <attr name=\"buttonBarPositiveButtonStyle\" format=\"reference\" />\n\n        <!-- Style for the \"negative\" buttons within button bars. -->\n        <attr name=\"buttonBarNegativeButtonStyle\" format=\"reference\" />\n\n        <!-- Style for the \"neutral\" buttons within button bars. -->\n        <attr name=\"buttonBarNeutralButtonStyle\" format=\"reference\" />\n\n        <!-- Corner radius of buttons. -->\n        <attr name=\"buttonCornerRadius\" format=\"dimension\" />\n\n        <!-- Corner radius of progress bars. -->\n        <attr name=\"progressBarCornerRadius\" format=\"dimension\" />\n\n        <!-- Style for the search query widget. -->\n        <attr name=\"searchViewStyle\" format=\"reference\" />\n\n        <!-- Style for segmented buttons - a container that houses several buttons\n             with the appearance of a singel button broken into segments. -->\n        <attr name=\"segmentedButtonStyle\" format=\"reference\" />\n\n        <!-- Background drawable for bordered standalone items that need focus/pressed states. -->\n        <attr name=\"selectableItemBackground\" format=\"reference\" />\n\n        <!-- Background drawable for borderless standalone items that need focus/pressed states. -->\n        <attr name=\"selectableItemBackgroundBorderless\" format=\"reference\" />\n\n        <!-- Style for buttons without an explicit border, often used in groups. -->\n        <attr name=\"borderlessButtonStyle\" format=\"reference\" />\n\n        <!-- Background to use for toasts. -->\n        <attr name=\"toastFrameBackground\" format=\"reference\" />\n\n        <!-- Background to use for tooltip popups. -->\n        <attr name=\"tooltipFrameBackground\" format=\"reference\" />\n\n        <!-- Foreground color to use for tooltip popups. -->\n        <attr name=\"tooltipForegroundColor\" format=\"reference|color\" />\n\n        <!-- Background color to use for tooltip popups. -->\n        <attr name=\"tooltipBackgroundColor\" format=\"reference|color\" />\n\n        <attr name=\"tooltipCornerRadius\" format=\"dimension\" />\n        <attr name=\"tooltipHorizontalPadding\" format=\"dimension\" />\n        <attr name=\"tooltipVerticalPadding\" format=\"dimension\" />\n        <attr name=\"tooltipFontSize\" format=\"dimension\" />\n\n        <!-- Theme to use for Search Dialogs. -->\n        <attr name=\"searchDialogTheme\" format=\"reference\" />\n\n        <!-- Specifies a drawable to use for the 'home as up' indicator. -->\n        <attr name=\"homeAsUpIndicator\" format=\"reference\" />\n\n        <!-- Preference frame layout styles. -->\n        <attr name=\"preferenceFrameLayoutStyle\" format=\"reference\" />\n\n        <!-- Default style for the Switch widget. -->\n        <attr name=\"switchStyle\" format=\"reference\" />\n\n        <!-- Default style for the MediaRouteButton widget. -->\n        <attr name=\"mediaRouteButtonStyle\" format=\"reference\" />\n\n        <!-- ============== -->\n        <!-- Pointer styles -->\n        <!-- ============== -->\n        <eat-comment />\n\n        <!-- The drawable for accessibility focused views. -->\n        <attr name=\"accessibilityFocusedDrawable\" format=\"reference\" />\n\n        <!-- Drawable for WebView find-on-page dialogue's \"next\" button. @hide -->\n        <attr name=\"findOnPageNextDrawable\" format=\"reference\" />\n\n        <!-- Drawable for WebView find-on-page dialogue's \"previous\" button. @hide -->\n        <attr name=\"findOnPagePreviousDrawable\" format=\"reference\" />\n\n        <!-- ============= -->\n        <!-- Color palette -->\n        <!-- ============= -->\n        <eat-comment />\n\n        <!-- The primary branding color for the app. By default, this is the color applied to the\n             action bar background. -->\n        <attr name=\"colorPrimary\" format=\"color\" />\n\n        <!-- Dark variant of the primary branding color. By default, this is the color applied to\n             the status bar (via statusBarColor) and navigation bar (via navigationBarColor). -->\n        <attr name=\"colorPrimaryDark\" format=\"color\" />\n\n        <!-- The secondary branding color for the app. -->\n        <attr name=\"colorSecondary\" format=\"color\" />\n\n        <!-- Bright complement to the primary branding color. By default, this is the color applied\n             to framework controls (via colorControlActivated). -->\n        <attr name=\"colorAccent\" format=\"color\" />\n\n        <!-- Light accent color used on Material NEXT buttons. @hide -->\n        <attr name=\"colorAccentPrimary\" format=\"color\" />\n\n        <!-- Secondary accent color used on Material NEXT buttons. @hide -->\n        <attr name=\"colorAccentSecondary\" format=\"color\" />\n\n        <!-- Tertiary accent color used on Material NEXT buttons. @hide -->\n        <attr name=\"colorAccentTertiary\" format=\"color\" />\n\n        <!-- Darker accent color used on Material NEXT buttons. @hide -->\n        <attr name=\"colorAccentPrimaryVariant\" format=\"color\" />\n\n        <!-- Text color used on top of Material NEXT accent colors. @hide -->\n        <attr name=\"textColorOnAccent\" format=\"color\" />\n\n        <!-- Secondary darker accent color used on Material NEXT buttons. @hide -->\n        <attr name=\"colorAccentSecondaryVariant\" format=\"color\" />\n\n        <!-- Tertiary darker accent color used on Material NEXT buttons. @hide -->\n        <attr name=\"colorAccentTertiaryVariant\" format=\"color\" />\n\n        <!-- The color applied to framework controls in their normal state. -->\n        <attr name=\"colorControlNormal\" format=\"color\" />\n\n        <!-- The color applied to framework controls in their activated (ex. checked) state. -->\n        <attr name=\"colorControlActivated\" format=\"color\" />\n\n        <!-- The color applied to framework control highlights (ex. ripples, list selectors). -->\n        <attr name=\"colorControlHighlight\" format=\"color\" />\n\n        <!-- The color applied to framework buttons in their normal state. -->\n        <attr name=\"colorButtonNormal\" format=\"color\" />\n\n        <!-- The color applied to framework switch thumbs in their normal state. -->\n        <attr name=\"colorSwitchThumbNormal\" format=\"color\" />\n\n        <!-- The color applied to framework progress and seek bar backgrounds in their normal state. -->\n        <attr name=\"colorProgressBackgroundNormal\" format=\"color\" />\n\n        <!-- The color applied to the edge effect on scrolling containers. -->\n        <attr name=\"colorEdgeEffect\" format=\"color\" />\n\n        <!-- The color applied to surfaces on top of colorBackground. @hide -->\n        <attr name=\"colorSurface\" format=\"color\" />\n\n        <!-- Alternative color applied to surfaces on top of colorBackground. @hide -->\n        <attr name=\"colorSurfaceHighlight\" format=\"color\" />\n\n        <!-- Alternative color applied to surfaces on top of colorBackground. @hide -->\n        <attr name=\"colorSurfaceVariant\" format=\"color\" />\n\n        <!-- Alternative color applied to surfaces on top of colorBackground. @hide -->\n        <attr name=\"colorSurfaceHeader\" format=\"color\" />\n\n        <!-- Color applied to effects. -->\n        <attr name=\"effectColor\" format=\"color\" />\n\n        <!-- =================== -->\n        <!-- Lighting properties -->\n        <!-- =================== -->\n        <eat-comment />\n\n        <!-- @hide The default Y position of the light used to project view shadows. -->\n        <attr name=\"lightY\" format=\"dimension\" />\n\n        <!-- @hide The default Z position of the light used to project view shadows. -->\n        <attr name=\"lightZ\" format=\"dimension\" />\n\n        <!-- @hide The default radius of the light used to project view shadows. -->\n        <attr name=\"lightRadius\" format=\"dimension\" />\n\n        <!-- Alpha value of the ambient shadow projected by elevated views, between 0 and 1. -->\n        <attr name=\"ambientShadowAlpha\" format=\"float\" />\n\n        <!-- Alpha value of the spot shadow projected by elevated views, between 0 and 1. -->\n        <attr name=\"spotShadowAlpha\" format=\"float\" />\n\n        <!-- <p>Whether or not the force dark feature is allowed to be applied to this theme.\n             <p>Setting this to false will disable the auto-dark feature on everything this\n             theme is applied to along with anything drawn by any children of views using\n             this theme.\n             <p>Setting this to true will allow this view to be automatically made dark, however\n             a value of 'true' will not override any 'false' value in its parent chain nor will\n             it prevent any 'false' in any of its children. -->\n        <attr name=\"forceDarkAllowed\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- **************************************************************** -->\n    <!-- Other non-theme attributes. -->\n    <!-- **************************************************************** -->\n    <eat-comment />\n\n    <!-- Size of text. Recommended dimension type for text is \"sp\" for scaled-pixels (example: 15sp).\n         Supported values include the following:<p/>\n    <ul>\n        <li><b>px</b> Pixels</li>\n        <li><b>sp</b> Scaled pixels (scaled to relative pixel size on screen). See {@link android.util.DisplayMetrics} for more information.</li>\n        <li><b>pt</b> Points</li>\n        <li><b>dip</b> Device independent pixels. See {@link android.util.DisplayMetrics} for more information.</li>\n    </ul>\n        -->\n    <attr name=\"textSize\" format=\"dimension\" />\n\n    <!-- Default font family. -->\n    <attr name=\"fontFamily\" format=\"string\" />\n\n    <!-- Default text typeface. -->\n    <attr name=\"typeface\">\n        <enum name=\"normal\" value=\"0\" />\n        <enum name=\"sans\" value=\"1\" />\n        <enum name=\"serif\" value=\"2\" />\n        <enum name=\"monospace\" value=\"3\" />\n    </attr>\n\n    <!-- Default text typeface style. -->\n    <attr name=\"textStyle\">\n        <flag name=\"normal\" value=\"0\" />\n        <flag name=\"bold\" value=\"1\" />\n        <flag name=\"italic\" value=\"2\" />\n    </attr>\n\n    <!-- Color of text (usually same as colorForeground). -->\n    <attr name=\"textColor\" format=\"reference|color\" />\n\n    <!-- Color of highlighted text. -->\n    <attr name=\"textColorHighlight\" format=\"reference|color\" />\n\n    <!-- Color of hint text (displayed when the field is empty). -->\n    <attr name=\"textColorHint\" format=\"reference|color\" />\n\n    <!-- Color of link text (URLs). -->\n    <attr name=\"textColorLink\" format=\"reference|color\" />\n\n    <!-- Reference to a drawable that will be drawn under the insertion cursor. -->\n    <attr name=\"textCursorDrawable\" format=\"reference\" />\n\n    <!-- Indicates that the content of a non-editable TextView can be selected.\n     Default value is false. EditText content is always selectable. -->\n    <attr name=\"textIsSelectable\" format=\"boolean\" />\n\n    <!-- Where to ellipsize text. -->\n    <attr name=\"ellipsize\">\n        <enum name=\"none\" value=\"0\" />\n        <enum name=\"start\" value=\"1\" />\n        <enum name=\"middle\" value=\"2\" />\n        <enum name=\"end\" value=\"3\" />\n        <enum name=\"marquee\" value=\"4\" />\n    </attr>\n\n    <!-- The type of data being placed in a text field, used to help an\n         input method decide how to let the user enter text.  The constants\n         here correspond to those defined by\n         {@link android.text.InputType}.  Generally you can select\n         a single value, though some can be combined together as\n         indicated.  Setting this attribute to anything besides\n         <var>none</var> also implies that the text is editable. -->\n    <attr name=\"inputType\">\n        <!-- There is no content type.  The text is not editable. -->\n        <flag name=\"none\" value=\"0x00000000\" />\n        <!-- Just plain old text.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_TEXT} |\n             {@link android.text.InputType#TYPE_TEXT_VARIATION_NORMAL}. -->\n        <flag name=\"text\" value=\"0x00000001\" />\n        <!-- Can be combined with <var>text</var> and its variations to\n             request capitalization of all characters.  Corresponds to\n             {@link android.text.InputType#TYPE_TEXT_FLAG_CAP_CHARACTERS}. -->\n        <flag name=\"textCapCharacters\" value=\"0x00001001\" />\n        <!-- Can be combined with <var>text</var> and its variations to\n             request capitalization of the first character of every word.  Corresponds to\n             {@link android.text.InputType#TYPE_TEXT_FLAG_CAP_WORDS}. -->\n        <flag name=\"textCapWords\" value=\"0x00002001\" />\n        <!-- Can be combined with <var>text</var> and its variations to\n             request capitalization of the first character of every sentence.  Corresponds to\n             {@link android.text.InputType#TYPE_TEXT_FLAG_CAP_SENTENCES}. -->\n        <flag name=\"textCapSentences\" value=\"0x00004001\" />\n        <!-- Can be combined with <var>text</var> and its variations to\n             request auto-correction of text being input.  Corresponds to\n             {@link android.text.InputType#TYPE_TEXT_FLAG_AUTO_CORRECT}. -->\n        <flag name=\"textAutoCorrect\" value=\"0x00008001\" />\n        <!-- Can be combined with <var>text</var> and its variations to\n             specify that this field will be doing its own auto-completion and\n             talking with the input method appropriately.  Corresponds to\n             {@link android.text.InputType#TYPE_TEXT_FLAG_AUTO_COMPLETE}. -->\n        <flag name=\"textAutoComplete\" value=\"0x00010001\" />\n        <!-- Can be combined with <var>text</var> and its variations to\n             allow multiple lines of text in the field.  If this flag is not set,\n             the text field will be constrained to a single line.  Corresponds to\n             {@link android.text.InputType#TYPE_TEXT_FLAG_MULTI_LINE}.\n\n             Note: If this flag is not set and the text field doesn't have max length limit, the\n             framework automatically set maximum length of the characters to 5000 for the\n             performance reasons.\n             -->\n        <flag name=\"textMultiLine\" value=\"0x00020001\" />\n        <!-- Can be combined with <var>text</var> and its variations to\n             indicate that though the regular text view should not be multiple\n             lines, the IME should provide multiple lines if it can.  Corresponds to\n             {@link android.text.InputType#TYPE_TEXT_FLAG_IME_MULTI_LINE}. -->\n        <flag name=\"textImeMultiLine\" value=\"0x00040001\" />\n        <!-- Can be combined with <var>text</var> and its variations to\n             indicate that the IME should not show any\n             dictionary-based word suggestions.  Corresponds to\n             {@link android.text.InputType#TYPE_TEXT_FLAG_NO_SUGGESTIONS}. -->\n        <flag name=\"textNoSuggestions\" value=\"0x00080001\" />\n        <!-- Can be combined with <var>text</var> and its variations to\n             indicate that if there is extra information, the IME should provide\n             {@link android.view.inputmethod.TextAttribute}.  Corresponds to\n             {@link android.text.InputType#TYPE_TEXT_FLAG_ENABLE_TEXT_CONVERSION_SUGGESTIONS}. -->\n        <flag name=\"textEnableTextConversionSuggestions\" value=\"0x00100001\" />\n        <!-- Text that will be used as a URI.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_TEXT} |\n             {@link android.text.InputType#TYPE_TEXT_VARIATION_URI}. -->\n        <flag name=\"textUri\" value=\"0x00000011\" />\n        <!-- Text that will be used as an e-mail address.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_TEXT} |\n             {@link android.text.InputType#TYPE_TEXT_VARIATION_EMAIL_ADDRESS}. -->\n        <flag name=\"textEmailAddress\" value=\"0x00000021\" />\n        <!-- Text that is being supplied as the subject of an e-mail.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_TEXT} |\n             {@link android.text.InputType#TYPE_TEXT_VARIATION_EMAIL_SUBJECT}. -->\n        <flag name=\"textEmailSubject\" value=\"0x00000031\" />\n        <!-- Text that is the content of a short message.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_TEXT} |\n             {@link android.text.InputType#TYPE_TEXT_VARIATION_SHORT_MESSAGE}. -->\n        <flag name=\"textShortMessage\" value=\"0x00000041\" />\n        <!-- Text that is the content of a long message.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_TEXT} |\n             {@link android.text.InputType#TYPE_TEXT_VARIATION_LONG_MESSAGE}. -->\n        <flag name=\"textLongMessage\" value=\"0x00000051\" />\n        <!-- Text that is the name of a person.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_TEXT} |\n             {@link android.text.InputType#TYPE_TEXT_VARIATION_PERSON_NAME}. -->\n        <flag name=\"textPersonName\" value=\"0x00000061\" />\n        <!-- Text that is being supplied as a postal mailing address.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_TEXT} |\n             {@link android.text.InputType#TYPE_TEXT_VARIATION_POSTAL_ADDRESS}. -->\n        <flag name=\"textPostalAddress\" value=\"0x00000071\" />\n        <!-- Text that is a password.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_TEXT} |\n             {@link android.text.InputType#TYPE_TEXT_VARIATION_PASSWORD}. -->\n        <flag name=\"textPassword\" value=\"0x00000081\" />\n        <!-- Text that is a password that should be visible.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_TEXT} |\n             {@link android.text.InputType#TYPE_TEXT_VARIATION_VISIBLE_PASSWORD}. -->\n        <flag name=\"textVisiblePassword\" value=\"0x00000091\" />\n        <!-- Text that is being supplied as text in a web form.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_TEXT} |\n             {@link android.text.InputType#TYPE_TEXT_VARIATION_WEB_EDIT_TEXT}. -->\n        <flag name=\"textWebEditText\" value=\"0x000000a1\" />\n        <!-- Text that is filtering some other data.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_TEXT} |\n             {@link android.text.InputType#TYPE_TEXT_VARIATION_FILTER}. -->\n        <flag name=\"textFilter\" value=\"0x000000b1\" />\n        <!-- Text that is for phonetic pronunciation, such as a phonetic name\n             field in a contact entry.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_TEXT} |\n             {@link android.text.InputType#TYPE_TEXT_VARIATION_PHONETIC}. -->\n        <flag name=\"textPhonetic\" value=\"0x000000c1\" />\n        <!-- Text that will be used as an e-mail address on a web form.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_TEXT} |\n             {@link android.text.InputType#TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS}. -->\n        <flag name=\"textWebEmailAddress\" value=\"0x000000d1\" />\n        <!-- Text that will be used as a password on a web form.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_TEXT} |\n             {@link android.text.InputType#TYPE_TEXT_VARIATION_WEB_PASSWORD}. -->\n        <flag name=\"textWebPassword\" value=\"0x000000e1\" />\n        <!-- A numeric only field.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_NUMBER} |\n             {@link android.text.InputType#TYPE_NUMBER_VARIATION_NORMAL}. -->\n        <flag name=\"number\" value=\"0x00000002\" />\n        <!-- Can be combined with <var>number</var> and its other options to\n             allow a signed number.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_NUMBER} |\n             {@link android.text.InputType#TYPE_NUMBER_FLAG_SIGNED}. -->\n        <flag name=\"numberSigned\" value=\"0x00001002\" />\n        <!-- Can be combined with <var>number</var> and its other options to\n             allow a decimal (fractional) number.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_NUMBER} |\n             {@link android.text.InputType#TYPE_NUMBER_FLAG_DECIMAL}. -->\n        <flag name=\"numberDecimal\" value=\"0x00002002\" />\n        <!-- A numeric password field.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_NUMBER} |\n             {@link android.text.InputType#TYPE_NUMBER_VARIATION_PASSWORD}. -->\n        <flag name=\"numberPassword\" value=\"0x00000012\" />\n        <!-- For entering a phone number.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_PHONE}. -->\n        <flag name=\"phone\" value=\"0x00000003\" />\n        <!-- For entering a date and time.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_DATETIME} |\n             {@link android.text.InputType#TYPE_DATETIME_VARIATION_NORMAL}. -->\n        <flag name=\"datetime\" value=\"0x00000004\" />\n        <!-- For entering a date.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_DATETIME} |\n             {@link android.text.InputType#TYPE_DATETIME_VARIATION_DATE}. -->\n        <flag name=\"date\" value=\"0x00000014\" />\n        <!-- For entering a time.  Corresponds to\n             {@link android.text.InputType#TYPE_CLASS_DATETIME} |\n             {@link android.text.InputType#TYPE_DATETIME_VARIATION_TIME}. -->\n        <flag name=\"time\" value=\"0x00000024\" />\n    </attr>\n\n    <!-- Additional features you can enable in an IME associated with an editor\n         to improve the integration with your application.  The constants\n         here correspond to those defined by\n         {@link android.view.inputmethod.EditorInfo#imeOptions}. -->\n    <attr name=\"imeOptions\">\n        <!-- There are no special semantics associated with this editor. -->\n        <flag name=\"normal\" value=\"0x00000000\" />\n        <!-- There is no specific action associated with this editor, let the\n             editor come up with its own if it can.\n             Corresponds to\n             {@link android.view.inputmethod.EditorInfo#IME_NULL}. -->\n        <flag name=\"actionUnspecified\" value=\"0x00000000\" />\n        <!-- This editor has no action associated with it.\n             Corresponds to\n             {@link android.view.inputmethod.EditorInfo#IME_ACTION_NONE}. -->\n        <flag name=\"actionNone\" value=\"0x00000001\" />\n        <!-- The action key performs a \"go\"\n             operation to take the user to the target of the text they typed.\n             Typically used, for example, when entering a URL.\n             Corresponds to\n             {@link android.view.inputmethod.EditorInfo#IME_ACTION_GO}. -->\n        <flag name=\"actionGo\" value=\"0x00000002\" />\n        <!-- The action key performs a \"search\"\n             operation, taking the user to the results of searching for the text\n             the have typed (in whatever context is appropriate).\n             Corresponds to\n             {@link android.view.inputmethod.EditorInfo#IME_ACTION_SEARCH}. -->\n        <flag name=\"actionSearch\" value=\"0x00000003\" />\n        <!-- The action key performs a \"send\"\n             operation, delivering the text to its target.  This is typically used\n             when composing a message.\n             Corresponds to\n             {@link android.view.inputmethod.EditorInfo#IME_ACTION_SEND}. -->\n        <flag name=\"actionSend\" value=\"0x00000004\" />\n        <!-- The action key performs a \"next\"\n             operation, taking the user to the next field that will accept text.\n             Corresponds to\n             {@link android.view.inputmethod.EditorInfo#IME_ACTION_NEXT}. -->\n        <flag name=\"actionNext\" value=\"0x00000005\" />\n        <!-- The action key performs a \"done\"\n             operation, closing the soft input method.\n             Corresponds to\n             {@link android.view.inputmethod.EditorInfo#IME_ACTION_DONE}. -->\n        <flag name=\"actionDone\" value=\"0x00000006\" />\n        <!-- The action key performs a \"previous\"\n             operation, taking the user to the previous field that will accept text.\n             Corresponds to\n             {@link android.view.inputmethod.EditorInfo#IME_ACTION_PREVIOUS}. -->\n        <flag name=\"actionPrevious\" value=\"0x00000007\" />\n        <!-- Used to request that the IME should not update any personalized data such as typing\n             history and personalized language model based on what the user typed on this text\n             editing object. Typical use cases are:\n             <ul>\n                 <li>When the application is in a special mode, where user's activities are expected\n                 to be not recorded in the application's history. Some web browsers and chat\n                 applications may have this kind of modes.</li>\n                 <li>When storing typing history does not make much sense.  Specifying this flag in\n                 typing games may help to avoid typing history from being filled up with words that\n                 the user is less likely to type in their daily life.  Another example is that when\n                 the application already knows that the expected input is not a valid word (e.g. a\n                 promotion code that is not a valid word in any natural language).</li>\n             </ul>\n             <p>Applications need to be aware that the flag is not a guarantee, and some IMEs may\n             not respect it.</p> -->\n        <flag name=\"flagNoPersonalizedLearning\" value=\"0x1000000\" />\n        <!-- Used to request that the IME never go\n             into fullscreen mode.  Applications need to be aware that the flag is not\n             a guarantee, and not all IMEs will respect it.\n             <p>Corresponds to\n             {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_FULLSCREEN}. -->\n        <flag name=\"flagNoFullscreen\" value=\"0x2000000\" />\n        <!-- Like flagNavigateNext, but\n             specifies there is something interesting that a backward navigation\n             can focus on.  If the user selects the IME's facility to backward\n             navigate, this will show up in the application as an actionPrevious\n             at {@link android.view.inputmethod.InputConnection#performEditorAction(int)\n             InputConnection.performEditorAction(int)}.\n             <p>Corresponds to\n             {@link android.view.inputmethod.EditorInfo#IME_FLAG_NAVIGATE_PREVIOUS}. -->\n        <flag name=\"flagNavigatePrevious\" value=\"0x4000000\" />\n        <!-- Used to specify that there is something\n             interesting that a forward navigation can focus on. This is like using\n             actionNext, except allows the IME to be multiline (with\n             an enter key) as well as provide forward navigation.  Note that some\n             IMEs may not be able to do this, especially when running on a small\n             screen where there is little space.  In that case it does not need to\n             present a UI for this option.  Like actionNext, if the\n             user selects the IME's facility to forward navigate, this will show up\n             in the application at\n             {@link android.view.inputmethod.InputConnection#performEditorAction(int)\n             InputConnection.performEditorAction(int)}.\n             <p>Corresponds to\n             {@link android.view.inputmethod.EditorInfo#IME_FLAG_NAVIGATE_NEXT}. -->\n        <flag name=\"flagNavigateNext\" value=\"0x8000000\" />\n        <!-- Used to specify that the IME does not need\n             to show its extracted text UI.  For input methods that may be fullscreen,\n             often when in landscape mode, this allows them to be smaller and let part\n             of the application be shown behind.  Though there will likely be limited\n             access to the application available from the user, it can make the\n             experience of a (mostly) fullscreen IME less jarring.  Note that when\n             this flag is specified the IME may <em>not</em> be set up to be able\n             to display text, so it should only be used in situations where this is\n             not needed.\n             <p>Corresponds to\n             {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_EXTRACT_UI}. -->\n        <flag name=\"flagNoExtractUi\" value=\"0x10000000\" />\n        <!-- Used in conjunction with a custom action, this indicates that the\n             action should not be available as an accessory button when the\n             input method is full-screen.\n             Note that by setting this flag, there can be cases where the action\n             is simply never available to the user.  Setting this generally means\n             that you think showing text being edited is more important than the\n             action you have supplied.\n             <p>Corresponds to\n             {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_ACCESSORY_ACTION}. -->\n        <flag name=\"flagNoAccessoryAction\" value=\"0x20000000\" />\n        <!-- Used in conjunction with a custom action,\n             this indicates that the action should not be available in-line as\n             a replacement for the \"enter\" key.  Typically this is\n             because the action has such a significant impact or is not recoverable\n             enough that accidentally hitting it should be avoided, such as sending\n             a message.    Note that {@link android.widget.TextView} will\n             automatically set this flag for you on multi-line text views.\n             <p>Corresponds to\n             {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_ENTER_ACTION}. -->\n        <flag name=\"flagNoEnterAction\" value=\"0x40000000\" />\n        <!-- Used to request that the IME should be capable of inputting ASCII\n             characters.  The intention of this flag is to ensure that the user\n             can type Roman alphabet characters in a {@link android.widget.TextView}\n             used for, typically, account ID or password input.  It is expected that IMEs\n             normally are able to input ASCII even without being told so (such IMEs\n             already respect this flag in a sense), but there could be some cases they\n             aren't when, for instance, only non-ASCII input languages like Arabic,\n             Greek, Hebrew, Russian are enabled in the IME.  Applications need to be\n             aware that the flag is not a guarantee, and not all IMEs will respect it.\n             However, it is strongly recommended for IME authors to respect this flag\n             especially when their IME could end up with a state that has only non-ASCII\n             input languages enabled.\n             <p>Corresponds to\n             {@link android.view.inputmethod.EditorInfo#IME_FLAG_FORCE_ASCII}. -->\n        <flag name=\"flagForceAscii\" value=\"0x80000000\" />\n    </attr>\n\n    <!-- A coordinate in the X dimension. -->\n    <attr name=\"x\" format=\"dimension\" />\n    <!-- A coordinate in the Y dimension. -->\n    <attr name=\"y\" format=\"dimension\" />\n\n    <!-- Specifies how an object should position its content, on both the X and Y axes,\n         within its own bounds.  -->\n    <attr name=\"gravity\">\n        <!-- Push object to the top of its container, not changing its size. -->\n        <flag name=\"top\" value=\"0x30\" />\n        <!-- Push object to the bottom of its container, not changing its size. -->\n        <flag name=\"bottom\" value=\"0x50\" />\n        <!-- Push object to the left of its container, not changing its size. -->\n        <flag name=\"left\" value=\"0x03\" />\n        <!-- Push object to the right of its container, not changing its size. -->\n        <flag name=\"right\" value=\"0x05\" />\n        <!-- Place object in the vertical center of its container, not changing its size. -->\n        <flag name=\"center_vertical\" value=\"0x10\" />\n        <!-- Grow the vertical size of the object if needed so it completely fills its container. -->\n        <flag name=\"fill_vertical\" value=\"0x70\" />\n        <!-- Place object in the horizontal center of its container, not changing its size. -->\n        <flag name=\"center_horizontal\" value=\"0x01\" />\n        <!-- Grow the horizontal size of the object if needed so it completely fills its container. -->\n        <flag name=\"fill_horizontal\" value=\"0x07\" />\n        <!-- Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. -->\n        <flag name=\"center\" value=\"0x11\" />\n        <!-- Grow the horizontal and vertical size of the object if needed so it completely fills its container. -->\n        <flag name=\"fill\" value=\"0x77\" />\n        <!-- Additional option that can be set to have the top and/or bottom edges of\n             the child clipped to its container's bounds.\n             The clip will be based on the vertical gravity: a top gravity will clip the bottom\n             edge, a bottom gravity will clip the top edge, and neither will clip both edges. -->\n        <flag name=\"clip_vertical\" value=\"0x80\" />\n        <!-- Additional option that can be set to have the left and/or right edges of\n             the child clipped to its container's bounds.\n             The clip will be based on the horizontal gravity: a left gravity will clip the right\n             edge, a right gravity will clip the left edge, and neither will clip both edges. -->\n        <flag name=\"clip_horizontal\" value=\"0x08\" />\n        <!-- Push object to the beginning of its container, not changing its size. -->\n        <flag name=\"start\" value=\"0x00800003\" />\n        <!-- Push object to the end of its container, not changing its size. -->\n        <flag name=\"end\" value=\"0x00800005\" />\n    </attr>\n\n    <!-- Controls whether links such as urls and email addresses are\n         automatically found and converted to clickable links.  The default\n         value is \"none\", disabling this feature. -->\n    <attr name=\"autoLink\">\n        <!-- Match no patterns (default). -->\n        <flag name=\"none\" value=\"0x00\" />\n        <!-- Match Web URLs. -->\n        <flag name=\"web\" value=\"0x01\" />\n        <!-- Match email addresses. -->\n        <flag name=\"email\" value=\"0x02\" />\n        <!-- Match phone numbers. -->\n        <flag name=\"phone\" value=\"0x04\" />\n        <!-- Match map addresses.\n             Deprecated: see {@link android.text.util.Linkify#MAP_ADDRESSES}. -->\n        <flag name=\"map\" value=\"0x08\" />\n        <!-- Match all patterns (equivalent to web|email|phone|map). -->\n        <flag name=\"all\" value=\"0x0f\" />\n    </attr>\n\n    <!-- Reference to an array resource that will populate a list/adapter. -->\n    <attr name=\"entries\" format=\"reference\" />\n\n    <!-- Standard gravity constant that a child supplies to its parent.\n         Defines how the child view should be positioned, on both the X and Y axes, within its enclosing layout. -->\n    <attr name=\"layout_gravity\">\n        <!-- Push object to the top of its container, not changing its size. -->\n        <flag name=\"top\" value=\"0x30\" />\n        <!-- Push object to the bottom of its container, not changing its size. -->\n        <flag name=\"bottom\" value=\"0x50\" />\n        <!-- Push object to the left of its container, not changing its size. -->\n        <flag name=\"left\" value=\"0x03\" />\n        <!-- Push object to the right of its container, not changing its size. -->\n        <flag name=\"right\" value=\"0x05\" />\n        <!-- Place object in the vertical center of its container, not changing its size. -->\n        <flag name=\"center_vertical\" value=\"0x10\" />\n        <!-- Grow the vertical size of the object if needed so it completely fills its container. -->\n        <flag name=\"fill_vertical\" value=\"0x70\" />\n        <!-- Place object in the horizontal center of its container, not changing its size. -->\n        <flag name=\"center_horizontal\" value=\"0x01\" />\n        <!-- Grow the horizontal size of the object if needed so it completely fills its container. -->\n        <flag name=\"fill_horizontal\" value=\"0x07\" />\n        <!-- Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. -->\n        <flag name=\"center\" value=\"0x11\" />\n        <!-- Grow the horizontal and vertical size of the object if needed so it completely fills its container. -->\n        <flag name=\"fill\" value=\"0x77\" />\n        <!-- Additional option that can be set to have the top and/or bottom edges of\n             the child clipped to its container's bounds.\n             The clip will be based on the vertical gravity: a top gravity will clip the bottom\n             edge, a bottom gravity will clip the top edge, and neither will clip both edges. -->\n        <flag name=\"clip_vertical\" value=\"0x80\" />\n        <!-- Additional option that can be set to have the left and/or right edges of\n             the child clipped to its container's bounds.\n             The clip will be based on the horizontal gravity: a left gravity will clip the right\n             edge, a right gravity will clip the left edge, and neither will clip both edges. -->\n        <flag name=\"clip_horizontal\" value=\"0x08\" />\n        <!-- Push object to the beginning of its container, not changing its size. -->\n        <flag name=\"start\" value=\"0x00800003\" />\n        <!-- Push object to the end of its container, not changing its size. -->\n        <flag name=\"end\" value=\"0x00800005\" />\n    </attr>\n\n    <!-- Standard orientation constant. -->\n    <attr name=\"orientation\">\n        <!-- Defines an horizontal widget. -->\n        <enum name=\"horizontal\" value=\"0\" />\n        <!-- Defines a vertical widget. -->\n        <enum name=\"vertical\" value=\"1\" />\n    </attr>\n\n    <!-- Alignment constants. -->\n    <attr name=\"alignmentMode\">\n        <!-- Align the bounds of the children.\n        See {@link android.widget.GridLayout#ALIGN_BOUNDS}. -->\n        <enum name=\"alignBounds\" value=\"0\" />\n        <!-- Align the margins of the children.\n        See {@link android.widget.GridLayout#ALIGN_MARGINS}. -->\n        <enum name=\"alignMargins\" value=\"1\" />\n    </attr>\n\n    <!-- ========================== -->\n    <!-- Key Codes                  -->\n    <!-- ========================== -->\n    <eat-comment />\n\n    <!-- This enum provides the same keycode values as can be found in\n        {@link android.view.KeyEvent}. -->\n    <attr name=\"keycode\">\n        <enum name=\"KEYCODE_UNKNOWN\" value=\"0\" />\n        <enum name=\"KEYCODE_SOFT_LEFT\" value=\"1\" />\n        <enum name=\"KEYCODE_SOFT_RIGHT\" value=\"2\" />\n        <enum name=\"KEYCODE_HOME\" value=\"3\" />\n        <enum name=\"KEYCODE_BACK\" value=\"4\" />\n        <enum name=\"KEYCODE_CALL\" value=\"5\" />\n        <enum name=\"KEYCODE_ENDCALL\" value=\"6\" />\n        <enum name=\"KEYCODE_0\" value=\"7\" />\n        <enum name=\"KEYCODE_1\" value=\"8\" />\n        <enum name=\"KEYCODE_2\" value=\"9\" />\n        <enum name=\"KEYCODE_3\" value=\"10\" />\n        <enum name=\"KEYCODE_4\" value=\"11\" />\n        <enum name=\"KEYCODE_5\" value=\"12\" />\n        <enum name=\"KEYCODE_6\" value=\"13\" />\n        <enum name=\"KEYCODE_7\" value=\"14\" />\n        <enum name=\"KEYCODE_8\" value=\"15\" />\n        <enum name=\"KEYCODE_9\" value=\"16\" />\n        <enum name=\"KEYCODE_STAR\" value=\"17\" />\n        <enum name=\"KEYCODE_POUND\" value=\"18\" />\n        <enum name=\"KEYCODE_DPAD_UP\" value=\"19\" />\n        <enum name=\"KEYCODE_DPAD_DOWN\" value=\"20\" />\n        <enum name=\"KEYCODE_DPAD_LEFT\" value=\"21\" />\n        <enum name=\"KEYCODE_DPAD_RIGHT\" value=\"22\" />\n        <enum name=\"KEYCODE_DPAD_CENTER\" value=\"23\" />\n        <enum name=\"KEYCODE_VOLUME_UP\" value=\"24\" />\n        <enum name=\"KEYCODE_VOLUME_DOWN\" value=\"25\" />\n        <enum name=\"KEYCODE_POWER\" value=\"26\" />\n        <enum name=\"KEYCODE_CAMERA\" value=\"27\" />\n        <enum name=\"KEYCODE_CLEAR\" value=\"28\" />\n        <enum name=\"KEYCODE_A\" value=\"29\" />\n        <enum name=\"KEYCODE_B\" value=\"30\" />\n        <enum name=\"KEYCODE_C\" value=\"31\" />\n        <enum name=\"KEYCODE_D\" value=\"32\" />\n        <enum name=\"KEYCODE_E\" value=\"33\" />\n        <enum name=\"KEYCODE_F\" value=\"34\" />\n        <enum name=\"KEYCODE_G\" value=\"35\" />\n        <enum name=\"KEYCODE_H\" value=\"36\" />\n        <enum name=\"KEYCODE_I\" value=\"37\" />\n        <enum name=\"KEYCODE_J\" value=\"38\" />\n        <enum name=\"KEYCODE_K\" value=\"39\" />\n        <enum name=\"KEYCODE_L\" value=\"40\" />\n        <enum name=\"KEYCODE_M\" value=\"41\" />\n        <enum name=\"KEYCODE_N\" value=\"42\" />\n        <enum name=\"KEYCODE_O\" value=\"43\" />\n        <enum name=\"KEYCODE_P\" value=\"44\" />\n        <enum name=\"KEYCODE_Q\" value=\"45\" />\n        <enum name=\"KEYCODE_R\" value=\"46\" />\n        <enum name=\"KEYCODE_S\" value=\"47\" />\n        <enum name=\"KEYCODE_T\" value=\"48\" />\n        <enum name=\"KEYCODE_U\" value=\"49\" />\n        <enum name=\"KEYCODE_V\" value=\"50\" />\n        <enum name=\"KEYCODE_W\" value=\"51\" />\n        <enum name=\"KEYCODE_X\" value=\"52\" />\n        <enum name=\"KEYCODE_Y\" value=\"53\" />\n        <enum name=\"KEYCODE_Z\" value=\"54\" />\n        <enum name=\"KEYCODE_COMMA\" value=\"55\" />\n        <enum name=\"KEYCODE_PERIOD\" value=\"56\" />\n        <enum name=\"KEYCODE_ALT_LEFT\" value=\"57\" />\n        <enum name=\"KEYCODE_ALT_RIGHT\" value=\"58\" />\n        <enum name=\"KEYCODE_SHIFT_LEFT\" value=\"59\" />\n        <enum name=\"KEYCODE_SHIFT_RIGHT\" value=\"60\" />\n        <enum name=\"KEYCODE_TAB\" value=\"61\" />\n        <enum name=\"KEYCODE_SPACE\" value=\"62\" />\n        <enum name=\"KEYCODE_SYM\" value=\"63\" />\n        <enum name=\"KEYCODE_EXPLORER\" value=\"64\" />\n        <enum name=\"KEYCODE_ENVELOPE\" value=\"65\" />\n        <enum name=\"KEYCODE_ENTER\" value=\"66\" />\n        <enum name=\"KEYCODE_DEL\" value=\"67\" />\n        <enum name=\"KEYCODE_GRAVE\" value=\"68\" />\n        <enum name=\"KEYCODE_MINUS\" value=\"69\" />\n        <enum name=\"KEYCODE_EQUALS\" value=\"70\" />\n        <enum name=\"KEYCODE_LEFT_BRACKET\" value=\"71\" />\n        <enum name=\"KEYCODE_RIGHT_BRACKET\" value=\"72\" />\n        <enum name=\"KEYCODE_BACKSLASH\" value=\"73\" />\n        <enum name=\"KEYCODE_SEMICOLON\" value=\"74\" />\n        <enum name=\"KEYCODE_APOSTROPHE\" value=\"75\" />\n        <enum name=\"KEYCODE_SLASH\" value=\"76\" />\n        <enum name=\"KEYCODE_AT\" value=\"77\" />\n        <enum name=\"KEYCODE_NUM\" value=\"78\" />\n        <enum name=\"KEYCODE_HEADSETHOOK\" value=\"79\" />\n        <enum name=\"KEYCODE_FOCUS\" value=\"80\" />\n        <enum name=\"KEYCODE_PLUS\" value=\"81\" />\n        <enum name=\"KEYCODE_MENU\" value=\"82\" />\n        <enum name=\"KEYCODE_NOTIFICATION\" value=\"83\" />\n        <enum name=\"KEYCODE_SEARCH\" value=\"84\" />\n        <enum name=\"KEYCODE_MEDIA_PLAY_PAUSE\" value=\"85\" />\n        <enum name=\"KEYCODE_MEDIA_STOP\" value=\"86\" />\n        <enum name=\"KEYCODE_MEDIA_NEXT\" value=\"87\" />\n        <enum name=\"KEYCODE_MEDIA_PREVIOUS\" value=\"88\" />\n        <enum name=\"KEYCODE_MEDIA_REWIND\" value=\"89\" />\n        <enum name=\"KEYCODE_MEDIA_FAST_FORWARD\" value=\"90\" />\n        <enum name=\"KEYCODE_MUTE\" value=\"91\" />\n        <enum name=\"KEYCODE_PAGE_UP\" value=\"92\" />\n        <enum name=\"KEYCODE_PAGE_DOWN\" value=\"93\" />\n        <enum name=\"KEYCODE_PICTSYMBOLS\" value=\"94\" />\n        <enum name=\"KEYCODE_SWITCH_CHARSET\" value=\"95\" />\n        <enum name=\"KEYCODE_BUTTON_A\" value=\"96\" />\n        <enum name=\"KEYCODE_BUTTON_B\" value=\"97\" />\n        <enum name=\"KEYCODE_BUTTON_C\" value=\"98\" />\n        <enum name=\"KEYCODE_BUTTON_X\" value=\"99\" />\n        <enum name=\"KEYCODE_BUTTON_Y\" value=\"100\" />\n        <enum name=\"KEYCODE_BUTTON_Z\" value=\"101\" />\n        <enum name=\"KEYCODE_BUTTON_L1\" value=\"102\" />\n        <enum name=\"KEYCODE_BUTTON_R1\" value=\"103\" />\n        <enum name=\"KEYCODE_BUTTON_L2\" value=\"104\" />\n        <enum name=\"KEYCODE_BUTTON_R2\" value=\"105\" />\n        <enum name=\"KEYCODE_BUTTON_THUMBL\" value=\"106\" />\n        <enum name=\"KEYCODE_BUTTON_THUMBR\" value=\"107\" />\n        <enum name=\"KEYCODE_BUTTON_START\" value=\"108\" />\n        <enum name=\"KEYCODE_BUTTON_SELECT\" value=\"109\" />\n        <enum name=\"KEYCODE_BUTTON_MODE\" value=\"110\" />\n        <enum name=\"KEYCODE_ESCAPE\" value=\"111\" />\n        <enum name=\"KEYCODE_FORWARD_DEL\" value=\"112\" />\n        <enum name=\"KEYCODE_CTRL_LEFT\" value=\"113\" />\n        <enum name=\"KEYCODE_CTRL_RIGHT\" value=\"114\" />\n        <enum name=\"KEYCODE_CAPS_LOCK\" value=\"115\" />\n        <enum name=\"KEYCODE_SCROLL_LOCK\" value=\"116\" />\n        <enum name=\"KEYCODE_META_LEFT\" value=\"117\" />\n        <enum name=\"KEYCODE_META_RIGHT\" value=\"118\" />\n        <enum name=\"KEYCODE_FUNCTION\" value=\"119\" />\n        <enum name=\"KEYCODE_SYSRQ\" value=\"120\" />\n        <enum name=\"KEYCODE_BREAK\" value=\"121\" />\n        <enum name=\"KEYCODE_MOVE_HOME\" value=\"122\" />\n        <enum name=\"KEYCODE_MOVE_END\" value=\"123\" />\n        <enum name=\"KEYCODE_INSERT\" value=\"124\" />\n        <enum name=\"KEYCODE_FORWARD\" value=\"125\" />\n        <enum name=\"KEYCODE_MEDIA_PLAY\" value=\"126\" />\n        <enum name=\"KEYCODE_MEDIA_PAUSE\" value=\"127\" />\n        <enum name=\"KEYCODE_MEDIA_CLOSE\" value=\"128\" />\n        <enum name=\"KEYCODE_MEDIA_EJECT\" value=\"129\" />\n        <enum name=\"KEYCODE_MEDIA_RECORD\" value=\"130\" />\n        <enum name=\"KEYCODE_F1\" value=\"131\" />\n        <enum name=\"KEYCODE_F2\" value=\"132\" />\n        <enum name=\"KEYCODE_F3\" value=\"133\" />\n        <enum name=\"KEYCODE_F4\" value=\"134\" />\n        <enum name=\"KEYCODE_F5\" value=\"135\" />\n        <enum name=\"KEYCODE_F6\" value=\"136\" />\n        <enum name=\"KEYCODE_F7\" value=\"137\" />\n        <enum name=\"KEYCODE_F8\" value=\"138\" />\n        <enum name=\"KEYCODE_F9\" value=\"139\" />\n        <enum name=\"KEYCODE_F10\" value=\"140\" />\n        <enum name=\"KEYCODE_F11\" value=\"141\" />\n        <enum name=\"KEYCODE_F12\" value=\"142\" />\n        <enum name=\"KEYCODE_NUM_LOCK\" value=\"143\" />\n        <enum name=\"KEYCODE_NUMPAD_0\" value=\"144\" />\n        <enum name=\"KEYCODE_NUMPAD_1\" value=\"145\" />\n        <enum name=\"KEYCODE_NUMPAD_2\" value=\"146\" />\n        <enum name=\"KEYCODE_NUMPAD_3\" value=\"147\" />\n        <enum name=\"KEYCODE_NUMPAD_4\" value=\"148\" />\n        <enum name=\"KEYCODE_NUMPAD_5\" value=\"149\" />\n        <enum name=\"KEYCODE_NUMPAD_6\" value=\"150\" />\n        <enum name=\"KEYCODE_NUMPAD_7\" value=\"151\" />\n        <enum name=\"KEYCODE_NUMPAD_8\" value=\"152\" />\n        <enum name=\"KEYCODE_NUMPAD_9\" value=\"153\" />\n        <enum name=\"KEYCODE_NUMPAD_DIVIDE\" value=\"154\" />\n        <enum name=\"KEYCODE_NUMPAD_MULTIPLY\" value=\"155\" />\n        <enum name=\"KEYCODE_NUMPAD_SUBTRACT\" value=\"156\" />\n        <enum name=\"KEYCODE_NUMPAD_ADD\" value=\"157\" />\n        <enum name=\"KEYCODE_NUMPAD_DOT\" value=\"158\" />\n        <enum name=\"KEYCODE_NUMPAD_COMMA\" value=\"159\" />\n        <enum name=\"KEYCODE_NUMPAD_ENTER\" value=\"160\" />\n        <enum name=\"KEYCODE_NUMPAD_EQUALS\" value=\"161\" />\n        <enum name=\"KEYCODE_NUMPAD_LEFT_PAREN\" value=\"162\" />\n        <enum name=\"KEYCODE_NUMPAD_RIGHT_PAREN\" value=\"163\" />\n        <enum name=\"KEYCODE_VOLUME_MUTE\" value=\"164\" />\n        <enum name=\"KEYCODE_INFO\" value=\"165\" />\n        <enum name=\"KEYCODE_CHANNEL_UP\" value=\"166\" />\n        <enum name=\"KEYCODE_CHANNEL_DOWN\" value=\"167\" />\n        <enum name=\"KEYCODE_ZOOM_IN\" value=\"168\" />\n        <enum name=\"KEYCODE_ZOOM_OUT\" value=\"169\" />\n        <enum name=\"KEYCODE_TV\" value=\"170\" />\n        <enum name=\"KEYCODE_WINDOW\" value=\"171\" />\n        <enum name=\"KEYCODE_GUIDE\" value=\"172\" />\n        <enum name=\"KEYCODE_DVR\" value=\"173\" />\n        <enum name=\"KEYCODE_BOOKMARK\" value=\"174\" />\n        <enum name=\"KEYCODE_CAPTIONS\" value=\"175\" />\n        <enum name=\"KEYCODE_SETTINGS\" value=\"176\" />\n        <enum name=\"KEYCODE_TV_POWER\" value=\"177\" />\n        <enum name=\"KEYCODE_TV_INPUT\" value=\"178\" />\n        <enum name=\"KEYCODE_STB_POWER\" value=\"179\" />\n        <enum name=\"KEYCODE_STB_INPUT\" value=\"180\" />\n        <enum name=\"KEYCODE_AVR_POWER\" value=\"181\" />\n        <enum name=\"KEYCODE_AVR_INPUT\" value=\"182\" />\n        <enum name=\"KEYCODE_PROG_GRED\" value=\"183\" />\n        <enum name=\"KEYCODE_PROG_GREEN\" value=\"184\" />\n        <enum name=\"KEYCODE_PROG_YELLOW\" value=\"185\" />\n        <enum name=\"KEYCODE_PROG_BLUE\" value=\"186\" />\n        <enum name=\"KEYCODE_APP_SWITCH\" value=\"187\" />\n        <enum name=\"KEYCODE_BUTTON_1\" value=\"188\" />\n        <enum name=\"KEYCODE_BUTTON_2\" value=\"189\" />\n        <enum name=\"KEYCODE_BUTTON_3\" value=\"190\" />\n        <enum name=\"KEYCODE_BUTTON_4\" value=\"191\" />\n        <enum name=\"KEYCODE_BUTTON_5\" value=\"192\" />\n        <enum name=\"KEYCODE_BUTTON_6\" value=\"193\" />\n        <enum name=\"KEYCODE_BUTTON_7\" value=\"194\" />\n        <enum name=\"KEYCODE_BUTTON_8\" value=\"195\" />\n        <enum name=\"KEYCODE_BUTTON_9\" value=\"196\" />\n        <enum name=\"KEYCODE_BUTTON_10\" value=\"197\" />\n        <enum name=\"KEYCODE_BUTTON_11\" value=\"198\" />\n        <enum name=\"KEYCODE_BUTTON_12\" value=\"199\" />\n        <enum name=\"KEYCODE_BUTTON_13\" value=\"200\" />\n        <enum name=\"KEYCODE_BUTTON_14\" value=\"201\" />\n        <enum name=\"KEYCODE_BUTTON_15\" value=\"202\" />\n        <enum name=\"KEYCODE_BUTTON_16\" value=\"203\" />\n        <enum name=\"KEYCODE_LANGUAGE_SWITCH\" value=\"204\" />\n        <enum name=\"KEYCODE_MANNER_MODE\" value=\"205\" />\n        <enum name=\"KEYCODE_3D_MODE\" value=\"206\" />\n        <enum name=\"KEYCODE_CONTACTS\" value=\"207\" />\n        <enum name=\"KEYCODE_CALENDAR\" value=\"208\" />\n        <enum name=\"KEYCODE_MUSIC\" value=\"209\" />\n        <enum name=\"KEYCODE_CALCULATOR\" value=\"210\" />\n        <enum name=\"KEYCODE_ZENKAKU_HANKAKU\" value=\"211\" />\n        <enum name=\"KEYCODE_EISU\" value=\"212\" />\n        <enum name=\"KEYCODE_MUHENKAN\" value=\"213\" />\n        <enum name=\"KEYCODE_HENKAN\" value=\"214\" />\n        <enum name=\"KEYCODE_KATAKANA_HIRAGANA\" value=\"215\" />\n        <enum name=\"KEYCODE_YEN\" value=\"216\" />\n        <enum name=\"KEYCODE_RO\" value=\"217\" />\n        <enum name=\"KEYCODE_KANA\" value=\"218\" />\n        <enum name=\"KEYCODE_ASSIST\" value=\"219\" />\n        <enum name=\"KEYCODE_BRIGHTNESS_DOWN\" value=\"220\" />\n        <enum name=\"KEYCODE_BRIGHTNESS_UP\" value=\"221\" />\n        <enum name=\"KEYCODE_MEDIA_AUDIO_TRACK\" value=\"222\" />\n        <enum name=\"KEYCODE_MEDIA_SLEEP\" value=\"223\" />\n        <enum name=\"KEYCODE_MEDIA_WAKEUP\" value=\"224\" />\n        <enum name=\"KEYCODE_PAIRING\" value=\"225\" />\n        <enum name=\"KEYCODE_MEDIA_TOP_MENU\" value=\"226\" />\n        <enum name=\"KEYCODE_11\" value=\"227\" />\n        <enum name=\"KEYCODE_12\" value=\"228\" />\n        <enum name=\"KEYCODE_LAST_CHANNEL\" value=\"229\" />\n        <enum name=\"KEYCODE_TV_DATA_SERVICE\" value=\"230\" />\n        <enum name=\"KEYCODE_VOICE_ASSIST\" value=\"231\" />\n        <enum name=\"KEYCODE_TV_RADIO_SERVICE\" value=\"232\" />\n        <enum name=\"KEYCODE_TV_TELETEXT\" value=\"233\" />\n        <enum name=\"KEYCODE_TV_NUMBER_ENTRY\" value=\"234\" />\n        <enum name=\"KEYCODE_TV_TERRESTRIAL_ANALOG\" value=\"235\" />\n        <enum name=\"KEYCODE_TV_TERRESTRIAL_DIGITAL\" value=\"236\" />\n        <enum name=\"KEYCODE_TV_SATELLITE\" value=\"237\" />\n        <enum name=\"KEYCODE_TV_SATELLITE_BS\" value=\"238\" />\n        <enum name=\"KEYCODE_TV_SATELLITE_CS\" value=\"239\" />\n        <enum name=\"KEYCODE_TV_SATELLITE_SERVICE\" value=\"240\" />\n        <enum name=\"KEYCODE_TV_NETWORK\" value=\"241\" />\n        <enum name=\"KEYCODE_TV_ANTENNA_CABLE\" value=\"242\" />\n        <enum name=\"KEYCODE_TV_INPUT_HDMI_1\" value=\"243\" />\n        <enum name=\"KEYCODE_TV_INPUT_HDMI_2\" value=\"244\" />\n        <enum name=\"KEYCODE_TV_INPUT_HDMI_3\" value=\"245\" />\n        <enum name=\"KEYCODE_TV_INPUT_HDMI_4\" value=\"246\" />\n        <enum name=\"KEYCODE_TV_INPUT_COMPOSITE_1\" value=\"247\" />\n        <enum name=\"KEYCODE_TV_INPUT_COMPOSITE_2\" value=\"248\" />\n        <enum name=\"KEYCODE_TV_INPUT_COMPONENT_1\" value=\"249\" />\n        <enum name=\"KEYCODE_TV_INPUT_COMPONENT_2\" value=\"250\" />\n        <enum name=\"KEYCODE_TV_INPUT_VGA_1\" value=\"251\" />\n        <enum name=\"KEYCODE_TV_AUDIO_DESCRIPTION\" value=\"252\" />\n        <enum name=\"KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP\" value=\"253\" />\n        <enum name=\"KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN\" value=\"254\" />\n        <enum name=\"KEYCODE_TV_ZOOM_MODE\" value=\"255\" />\n        <enum name=\"KEYCODE_TV_CONTENTS_MENU\" value=\"256\" />\n        <enum name=\"KEYCODE_TV_MEDIA_CONTEXT_MENU\" value=\"257\" />\n        <enum name=\"KEYCODE_TV_TIMER_PROGRAMMING\" value=\"258\" />\n        <enum name=\"KEYCODE_HELP\" value=\"259\" />\n        <enum name=\"KEYCODE_NAVIGATE_PREVIOUS\" value=\"260\" />\n        <enum name=\"KEYCODE_NAVIGATE_NEXT\" value=\"261\" />\n        <enum name=\"KEYCODE_NAVIGATE_IN\" value=\"262\" />\n        <enum name=\"KEYCODE_NAVIGATE_OUT\" value=\"263\" />\n        <enum name=\"KEYCODE_STEM_PRIMARY\" value=\"264\" />\n        <enum name=\"KEYCODE_STEM_1\" value=\"265\" />\n        <enum name=\"KEYCODE_STEM_2\" value=\"266\" />\n        <enum name=\"KEYCODE_STEM_3\" value=\"267\" />\n        <enum name=\"KEYCODE_DPAD_UP_LEFT\" value=\"268\" />\n        <enum name=\"KEYCODE_DPAD_DOWN_LEFT\" value=\"269\" />\n        <enum name=\"KEYCODE_DPAD_UP_RIGHT\" value=\"270\" />\n        <enum name=\"KEYCODE_DPAD_DOWN_RIGHT\" value=\"271\" />\n        <enum name=\"KEYCODE_MEDIA_SKIP_FORWARD\" value=\"272\" />\n        <enum name=\"KEYCODE_MEDIA_SKIP_BACKWARD\" value=\"273\" />\n        <enum name=\"KEYCODE_MEDIA_STEP_FORWARD\" value=\"274\" />\n        <enum name=\"KEYCODE_MEDIA_STEP_BACKWARD\" value=\"275\" />\n        <enum name=\"KEYCODE_SOFT_SLEEP\" value=\"276\" />\n        <enum name=\"KEYCODE_CUT\" value=\"277\" />\n        <enum name=\"KEYCODE_COPY\" value=\"278\" />\n        <enum name=\"KEYCODE_PASTE\" value=\"279\" />\n        <enum name=\"KEYCODE_SYSTEM_NAVIGATION_UP\" value=\"280\" />\n        <enum name=\"KEYCODE_SYSTEM_NAVIGATION_DOWN\" value=\"281\" />\n        <enum name=\"KEYCODE_SYSTEM_NAVIGATION_LEFT\" value=\"282\" />\n        <enum name=\"KEYCODE_SYSTEM_NAVIGATION_RIGHT\" value=\"283\" />\n        <enum name=\"KEYCODE_ALL_APPS\" value=\"284\" />\n        <enum name=\"KEYCODE_REFRESH\" value=\"285\" />\n        <enum name=\"KEYCODE_THUMBS_UP\" value=\"286\" />\n        <enum name=\"KEYCODE_THUMBS_DOWN\" value=\"287\" />\n        <enum name=\"KEYCODE_PROFILE_SWITCH\" value=\"288\" />\n        <enum name=\"KEYCODE_VIDEO_APP_1\" value=\"289\" />\n        <enum name=\"KEYCODE_VIDEO_APP_2\" value=\"290\" />\n        <enum name=\"KEYCODE_VIDEO_APP_3\" value=\"291\" />\n        <enum name=\"KEYCODE_VIDEO_APP_4\" value=\"292\" />\n        <enum name=\"KEYCODE_VIDEO_APP_5\" value=\"293\" />\n        <enum name=\"KEYCODE_VIDEO_APP_6\" value=\"294\" />\n        <enum name=\"KEYCODE_VIDEO_APP_7\" value=\"295\" />\n        <enum name=\"KEYCODE_VIDEO_APP_8\" value=\"296\" />\n        <enum name=\"KEYCODE_FEATURED_APP_1\" value=\"297\" />\n        <enum name=\"KEYCODE_FEATURED_APP_2\" value=\"298\" />\n        <enum name=\"KEYCODE_FEATURED_APP_3\" value=\"299\" />\n        <enum name=\"KEYCODE_FEATURED_APP_4\" value=\"300\" />\n        <enum name=\"KEYCODE_DEMO_APP_1\" value=\"301\" />\n        <enum name=\"KEYCODE_DEMO_APP_2\" value=\"302\" />\n        <enum name=\"KEYCODE_DEMO_APP_3\" value=\"303\" />\n        <enum name=\"KEYCODE_DEMO_APP_4\" value=\"304\" />\n        <enum name=\"KEYCODE_KEYBOARD_BACKLIGHT_DOWN\" value=\"305\" />\n        <enum name=\"KEYCODE_KEYBOARD_BACKLIGHT_UP\" value=\"306\" />\n        <enum name=\"KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE\" value=\"307\" />\n        <enum name=\"KEYCODE_STYLUS_BUTTON_PRIMARY\" value=\"308\" />\n        <enum name=\"KEYCODE_STYLUS_BUTTON_SECONDARY\" value=\"309\" />\n        <enum name=\"KEYCODE_STYLUS_BUTTON_TERTIARY\" value=\"310\" />\n        <enum name=\"KEYCODE_STYLUS_BUTTON_TAIL\" value=\"311\" />\n        <enum name=\"KEYCODE_RECENT_APPS\" value=\"312\" />\n        <enum name=\"KEYCODE_MACRO_1\" value=\"313\" />\n        <enum name=\"KEYCODE_MACRO_2\" value=\"314\" />\n        <enum name=\"KEYCODE_MACRO_3\" value=\"315\" />\n        <enum name=\"KEYCODE_MACRO_4\" value=\"316\" />\n        <enum name=\"KEYCODE_EMOJI_PICKER\" value=\"317\" />\n        <enum name=\"KEYCODE_SCREENSHOT\" value=\"318\" />\n    </attr>\n\n    <!-- @hide same as keycode enum defined above, but to be used to define keycode output.\n         (redefining it to allow keycode and outKeycode to be part of same styleable attribute) -->\n    <attr name=\"outKeycode\">\n        <enum name=\"KEYCODE_UNKNOWN\" value=\"0\" />\n        <enum name=\"KEYCODE_SOFT_LEFT\" value=\"1\" />\n        <enum name=\"KEYCODE_SOFT_RIGHT\" value=\"2\" />\n        <enum name=\"KEYCODE_HOME\" value=\"3\" />\n        <enum name=\"KEYCODE_BACK\" value=\"4\" />\n        <enum name=\"KEYCODE_CALL\" value=\"5\" />\n        <enum name=\"KEYCODE_ENDCALL\" value=\"6\" />\n        <enum name=\"KEYCODE_0\" value=\"7\" />\n        <enum name=\"KEYCODE_1\" value=\"8\" />\n        <enum name=\"KEYCODE_2\" value=\"9\" />\n        <enum name=\"KEYCODE_3\" value=\"10\" />\n        <enum name=\"KEYCODE_4\" value=\"11\" />\n        <enum name=\"KEYCODE_5\" value=\"12\" />\n        <enum name=\"KEYCODE_6\" value=\"13\" />\n        <enum name=\"KEYCODE_7\" value=\"14\" />\n        <enum name=\"KEYCODE_8\" value=\"15\" />\n        <enum name=\"KEYCODE_9\" value=\"16\" />\n        <enum name=\"KEYCODE_STAR\" value=\"17\" />\n        <enum name=\"KEYCODE_POUND\" value=\"18\" />\n        <enum name=\"KEYCODE_DPAD_UP\" value=\"19\" />\n        <enum name=\"KEYCODE_DPAD_DOWN\" value=\"20\" />\n        <enum name=\"KEYCODE_DPAD_LEFT\" value=\"21\" />\n        <enum name=\"KEYCODE_DPAD_RIGHT\" value=\"22\" />\n        <enum name=\"KEYCODE_DPAD_CENTER\" value=\"23\" />\n        <enum name=\"KEYCODE_VOLUME_UP\" value=\"24\" />\n        <enum name=\"KEYCODE_VOLUME_DOWN\" value=\"25\" />\n        <enum name=\"KEYCODE_POWER\" value=\"26\" />\n        <enum name=\"KEYCODE_CAMERA\" value=\"27\" />\n        <enum name=\"KEYCODE_CLEAR\" value=\"28\" />\n        <enum name=\"KEYCODE_A\" value=\"29\" />\n        <enum name=\"KEYCODE_B\" value=\"30\" />\n        <enum name=\"KEYCODE_C\" value=\"31\" />\n        <enum name=\"KEYCODE_D\" value=\"32\" />\n        <enum name=\"KEYCODE_E\" value=\"33\" />\n        <enum name=\"KEYCODE_F\" value=\"34\" />\n        <enum name=\"KEYCODE_G\" value=\"35\" />\n        <enum name=\"KEYCODE_H\" value=\"36\" />\n        <enum name=\"KEYCODE_I\" value=\"37\" />\n        <enum name=\"KEYCODE_J\" value=\"38\" />\n        <enum name=\"KEYCODE_K\" value=\"39\" />\n        <enum name=\"KEYCODE_L\" value=\"40\" />\n        <enum name=\"KEYCODE_M\" value=\"41\" />\n        <enum name=\"KEYCODE_N\" value=\"42\" />\n        <enum name=\"KEYCODE_O\" value=\"43\" />\n        <enum name=\"KEYCODE_P\" value=\"44\" />\n        <enum name=\"KEYCODE_Q\" value=\"45\" />\n        <enum name=\"KEYCODE_R\" value=\"46\" />\n        <enum name=\"KEYCODE_S\" value=\"47\" />\n        <enum name=\"KEYCODE_T\" value=\"48\" />\n        <enum name=\"KEYCODE_U\" value=\"49\" />\n        <enum name=\"KEYCODE_V\" value=\"50\" />\n        <enum name=\"KEYCODE_W\" value=\"51\" />\n        <enum name=\"KEYCODE_X\" value=\"52\" />\n        <enum name=\"KEYCODE_Y\" value=\"53\" />\n        <enum name=\"KEYCODE_Z\" value=\"54\" />\n        <enum name=\"KEYCODE_COMMA\" value=\"55\" />\n        <enum name=\"KEYCODE_PERIOD\" value=\"56\" />\n        <enum name=\"KEYCODE_ALT_LEFT\" value=\"57\" />\n        <enum name=\"KEYCODE_ALT_RIGHT\" value=\"58\" />\n        <enum name=\"KEYCODE_SHIFT_LEFT\" value=\"59\" />\n        <enum name=\"KEYCODE_SHIFT_RIGHT\" value=\"60\" />\n        <enum name=\"KEYCODE_TAB\" value=\"61\" />\n        <enum name=\"KEYCODE_SPACE\" value=\"62\" />\n        <enum name=\"KEYCODE_SYM\" value=\"63\" />\n        <enum name=\"KEYCODE_EXPLORER\" value=\"64\" />\n        <enum name=\"KEYCODE_ENVELOPE\" value=\"65\" />\n        <enum name=\"KEYCODE_ENTER\" value=\"66\" />\n        <enum name=\"KEYCODE_DEL\" value=\"67\" />\n        <enum name=\"KEYCODE_GRAVE\" value=\"68\" />\n        <enum name=\"KEYCODE_MINUS\" value=\"69\" />\n        <enum name=\"KEYCODE_EQUALS\" value=\"70\" />\n        <enum name=\"KEYCODE_LEFT_BRACKET\" value=\"71\" />\n        <enum name=\"KEYCODE_RIGHT_BRACKET\" value=\"72\" />\n        <enum name=\"KEYCODE_BACKSLASH\" value=\"73\" />\n        <enum name=\"KEYCODE_SEMICOLON\" value=\"74\" />\n        <enum name=\"KEYCODE_APOSTROPHE\" value=\"75\" />\n        <enum name=\"KEYCODE_SLASH\" value=\"76\" />\n        <enum name=\"KEYCODE_AT\" value=\"77\" />\n        <enum name=\"KEYCODE_NUM\" value=\"78\" />\n        <enum name=\"KEYCODE_HEADSETHOOK\" value=\"79\" />\n        <enum name=\"KEYCODE_FOCUS\" value=\"80\" />\n        <enum name=\"KEYCODE_PLUS\" value=\"81\" />\n        <enum name=\"KEYCODE_MENU\" value=\"82\" />\n        <enum name=\"KEYCODE_NOTIFICATION\" value=\"83\" />\n        <enum name=\"KEYCODE_SEARCH\" value=\"84\" />\n        <enum name=\"KEYCODE_MEDIA_PLAY_PAUSE\" value=\"85\" />\n        <enum name=\"KEYCODE_MEDIA_STOP\" value=\"86\" />\n        <enum name=\"KEYCODE_MEDIA_NEXT\" value=\"87\" />\n        <enum name=\"KEYCODE_MEDIA_PREVIOUS\" value=\"88\" />\n        <enum name=\"KEYCODE_MEDIA_REWIND\" value=\"89\" />\n        <enum name=\"KEYCODE_MEDIA_FAST_FORWARD\" value=\"90\" />\n        <enum name=\"KEYCODE_MUTE\" value=\"91\" />\n        <enum name=\"KEYCODE_PAGE_UP\" value=\"92\" />\n        <enum name=\"KEYCODE_PAGE_DOWN\" value=\"93\" />\n        <enum name=\"KEYCODE_PICTSYMBOLS\" value=\"94\" />\n        <enum name=\"KEYCODE_SWITCH_CHARSET\" value=\"95\" />\n        <enum name=\"KEYCODE_BUTTON_A\" value=\"96\" />\n        <enum name=\"KEYCODE_BUTTON_B\" value=\"97\" />\n        <enum name=\"KEYCODE_BUTTON_C\" value=\"98\" />\n        <enum name=\"KEYCODE_BUTTON_X\" value=\"99\" />\n        <enum name=\"KEYCODE_BUTTON_Y\" value=\"100\" />\n        <enum name=\"KEYCODE_BUTTON_Z\" value=\"101\" />\n        <enum name=\"KEYCODE_BUTTON_L1\" value=\"102\" />\n        <enum name=\"KEYCODE_BUTTON_R1\" value=\"103\" />\n        <enum name=\"KEYCODE_BUTTON_L2\" value=\"104\" />\n        <enum name=\"KEYCODE_BUTTON_R2\" value=\"105\" />\n        <enum name=\"KEYCODE_BUTTON_THUMBL\" value=\"106\" />\n        <enum name=\"KEYCODE_BUTTON_THUMBR\" value=\"107\" />\n        <enum name=\"KEYCODE_BUTTON_START\" value=\"108\" />\n        <enum name=\"KEYCODE_BUTTON_SELECT\" value=\"109\" />\n        <enum name=\"KEYCODE_BUTTON_MODE\" value=\"110\" />\n        <enum name=\"KEYCODE_ESCAPE\" value=\"111\" />\n        <enum name=\"KEYCODE_FORWARD_DEL\" value=\"112\" />\n        <enum name=\"KEYCODE_CTRL_LEFT\" value=\"113\" />\n        <enum name=\"KEYCODE_CTRL_RIGHT\" value=\"114\" />\n        <enum name=\"KEYCODE_CAPS_LOCK\" value=\"115\" />\n        <enum name=\"KEYCODE_SCROLL_LOCK\" value=\"116\" />\n        <enum name=\"KEYCODE_META_LEFT\" value=\"117\" />\n        <enum name=\"KEYCODE_META_RIGHT\" value=\"118\" />\n        <enum name=\"KEYCODE_FUNCTION\" value=\"119\" />\n        <enum name=\"KEYCODE_SYSRQ\" value=\"120\" />\n        <enum name=\"KEYCODE_BREAK\" value=\"121\" />\n        <enum name=\"KEYCODE_MOVE_HOME\" value=\"122\" />\n        <enum name=\"KEYCODE_MOVE_END\" value=\"123\" />\n        <enum name=\"KEYCODE_INSERT\" value=\"124\" />\n        <enum name=\"KEYCODE_FORWARD\" value=\"125\" />\n        <enum name=\"KEYCODE_MEDIA_PLAY\" value=\"126\" />\n        <enum name=\"KEYCODE_MEDIA_PAUSE\" value=\"127\" />\n        <enum name=\"KEYCODE_MEDIA_CLOSE\" value=\"128\" />\n        <enum name=\"KEYCODE_MEDIA_EJECT\" value=\"129\" />\n        <enum name=\"KEYCODE_MEDIA_RECORD\" value=\"130\" />\n        <enum name=\"KEYCODE_F1\" value=\"131\" />\n        <enum name=\"KEYCODE_F2\" value=\"132\" />\n        <enum name=\"KEYCODE_F3\" value=\"133\" />\n        <enum name=\"KEYCODE_F4\" value=\"134\" />\n        <enum name=\"KEYCODE_F5\" value=\"135\" />\n        <enum name=\"KEYCODE_F6\" value=\"136\" />\n        <enum name=\"KEYCODE_F7\" value=\"137\" />\n        <enum name=\"KEYCODE_F8\" value=\"138\" />\n        <enum name=\"KEYCODE_F9\" value=\"139\" />\n        <enum name=\"KEYCODE_F10\" value=\"140\" />\n        <enum name=\"KEYCODE_F11\" value=\"141\" />\n        <enum name=\"KEYCODE_F12\" value=\"142\" />\n        <enum name=\"KEYCODE_NUM_LOCK\" value=\"143\" />\n        <enum name=\"KEYCODE_NUMPAD_0\" value=\"144\" />\n        <enum name=\"KEYCODE_NUMPAD_1\" value=\"145\" />\n        <enum name=\"KEYCODE_NUMPAD_2\" value=\"146\" />\n        <enum name=\"KEYCODE_NUMPAD_3\" value=\"147\" />\n        <enum name=\"KEYCODE_NUMPAD_4\" value=\"148\" />\n        <enum name=\"KEYCODE_NUMPAD_5\" value=\"149\" />\n        <enum name=\"KEYCODE_NUMPAD_6\" value=\"150\" />\n        <enum name=\"KEYCODE_NUMPAD_7\" value=\"151\" />\n        <enum name=\"KEYCODE_NUMPAD_8\" value=\"152\" />\n        <enum name=\"KEYCODE_NUMPAD_9\" value=\"153\" />\n        <enum name=\"KEYCODE_NUMPAD_DIVIDE\" value=\"154\" />\n        <enum name=\"KEYCODE_NUMPAD_MULTIPLY\" value=\"155\" />\n        <enum name=\"KEYCODE_NUMPAD_SUBTRACT\" value=\"156\" />\n        <enum name=\"KEYCODE_NUMPAD_ADD\" value=\"157\" />\n        <enum name=\"KEYCODE_NUMPAD_DOT\" value=\"158\" />\n        <enum name=\"KEYCODE_NUMPAD_COMMA\" value=\"159\" />\n        <enum name=\"KEYCODE_NUMPAD_ENTER\" value=\"160\" />\n        <enum name=\"KEYCODE_NUMPAD_EQUALS\" value=\"161\" />\n        <enum name=\"KEYCODE_NUMPAD_LEFT_PAREN\" value=\"162\" />\n        <enum name=\"KEYCODE_NUMPAD_RIGHT_PAREN\" value=\"163\" />\n        <enum name=\"KEYCODE_VOLUME_MUTE\" value=\"164\" />\n        <enum name=\"KEYCODE_INFO\" value=\"165\" />\n        <enum name=\"KEYCODE_CHANNEL_UP\" value=\"166\" />\n        <enum name=\"KEYCODE_CHANNEL_DOWN\" value=\"167\" />\n        <enum name=\"KEYCODE_ZOOM_IN\" value=\"168\" />\n        <enum name=\"KEYCODE_ZOOM_OUT\" value=\"169\" />\n        <enum name=\"KEYCODE_TV\" value=\"170\" />\n        <enum name=\"KEYCODE_WINDOW\" value=\"171\" />\n        <enum name=\"KEYCODE_GUIDE\" value=\"172\" />\n        <enum name=\"KEYCODE_DVR\" value=\"173\" />\n        <enum name=\"KEYCODE_BOOKMARK\" value=\"174\" />\n        <enum name=\"KEYCODE_CAPTIONS\" value=\"175\" />\n        <enum name=\"KEYCODE_SETTINGS\" value=\"176\" />\n        <enum name=\"KEYCODE_TV_POWER\" value=\"177\" />\n        <enum name=\"KEYCODE_TV_INPUT\" value=\"178\" />\n        <enum name=\"KEYCODE_STB_POWER\" value=\"179\" />\n        <enum name=\"KEYCODE_STB_INPUT\" value=\"180\" />\n        <enum name=\"KEYCODE_AVR_POWER\" value=\"181\" />\n        <enum name=\"KEYCODE_AVR_INPUT\" value=\"182\" />\n        <enum name=\"KEYCODE_PROG_GRED\" value=\"183\" />\n        <enum name=\"KEYCODE_PROG_GREEN\" value=\"184\" />\n        <enum name=\"KEYCODE_PROG_YELLOW\" value=\"185\" />\n        <enum name=\"KEYCODE_PROG_BLUE\" value=\"186\" />\n        <enum name=\"KEYCODE_APP_SWITCH\" value=\"187\" />\n        <enum name=\"KEYCODE_BUTTON_1\" value=\"188\" />\n        <enum name=\"KEYCODE_BUTTON_2\" value=\"189\" />\n        <enum name=\"KEYCODE_BUTTON_3\" value=\"190\" />\n        <enum name=\"KEYCODE_BUTTON_4\" value=\"191\" />\n        <enum name=\"KEYCODE_BUTTON_5\" value=\"192\" />\n        <enum name=\"KEYCODE_BUTTON_6\" value=\"193\" />\n        <enum name=\"KEYCODE_BUTTON_7\" value=\"194\" />\n        <enum name=\"KEYCODE_BUTTON_8\" value=\"195\" />\n        <enum name=\"KEYCODE_BUTTON_9\" value=\"196\" />\n        <enum name=\"KEYCODE_BUTTON_10\" value=\"197\" />\n        <enum name=\"KEYCODE_BUTTON_11\" value=\"198\" />\n        <enum name=\"KEYCODE_BUTTON_12\" value=\"199\" />\n        <enum name=\"KEYCODE_BUTTON_13\" value=\"200\" />\n        <enum name=\"KEYCODE_BUTTON_14\" value=\"201\" />\n        <enum name=\"KEYCODE_BUTTON_15\" value=\"202\" />\n        <enum name=\"KEYCODE_BUTTON_16\" value=\"203\" />\n        <enum name=\"KEYCODE_LANGUAGE_SWITCH\" value=\"204\" />\n        <enum name=\"KEYCODE_MANNER_MODE\" value=\"205\" />\n        <enum name=\"KEYCODE_3D_MODE\" value=\"206\" />\n        <enum name=\"KEYCODE_CONTACTS\" value=\"207\" />\n        <enum name=\"KEYCODE_CALENDAR\" value=\"208\" />\n        <enum name=\"KEYCODE_MUSIC\" value=\"209\" />\n        <enum name=\"KEYCODE_CALCULATOR\" value=\"210\" />\n        <enum name=\"KEYCODE_ZENKAKU_HANKAKU\" value=\"211\" />\n        <enum name=\"KEYCODE_EISU\" value=\"212\" />\n        <enum name=\"KEYCODE_MUHENKAN\" value=\"213\" />\n        <enum name=\"KEYCODE_HENKAN\" value=\"214\" />\n        <enum name=\"KEYCODE_KATAKANA_HIRAGANA\" value=\"215\" />\n        <enum name=\"KEYCODE_YEN\" value=\"216\" />\n        <enum name=\"KEYCODE_RO\" value=\"217\" />\n        <enum name=\"KEYCODE_KANA\" value=\"218\" />\n        <enum name=\"KEYCODE_ASSIST\" value=\"219\" />\n        <enum name=\"KEYCODE_BRIGHTNESS_DOWN\" value=\"220\" />\n        <enum name=\"KEYCODE_BRIGHTNESS_UP\" value=\"221\" />\n        <enum name=\"KEYCODE_MEDIA_AUDIO_TRACK\" value=\"222\" />\n        <enum name=\"KEYCODE_MEDIA_SLEEP\" value=\"223\" />\n        <enum name=\"KEYCODE_MEDIA_WAKEUP\" value=\"224\" />\n        <enum name=\"KEYCODE_PAIRING\" value=\"225\" />\n        <enum name=\"KEYCODE_MEDIA_TOP_MENU\" value=\"226\" />\n        <enum name=\"KEYCODE_11\" value=\"227\" />\n        <enum name=\"KEYCODE_12\" value=\"228\" />\n        <enum name=\"KEYCODE_LAST_CHANNEL\" value=\"229\" />\n        <enum name=\"KEYCODE_TV_DATA_SERVICE\" value=\"230\" />\n        <enum name=\"KEYCODE_VOICE_ASSIST\" value=\"231\" />\n        <enum name=\"KEYCODE_TV_RADIO_SERVICE\" value=\"232\" />\n        <enum name=\"KEYCODE_TV_TELETEXT\" value=\"233\" />\n        <enum name=\"KEYCODE_TV_NUMBER_ENTRY\" value=\"234\" />\n        <enum name=\"KEYCODE_TV_TERRESTRIAL_ANALOG\" value=\"235\" />\n        <enum name=\"KEYCODE_TV_TERRESTRIAL_DIGITAL\" value=\"236\" />\n        <enum name=\"KEYCODE_TV_SATELLITE\" value=\"237\" />\n        <enum name=\"KEYCODE_TV_SATELLITE_BS\" value=\"238\" />\n        <enum name=\"KEYCODE_TV_SATELLITE_CS\" value=\"239\" />\n        <enum name=\"KEYCODE_TV_SATELLITE_SERVICE\" value=\"240\" />\n        <enum name=\"KEYCODE_TV_NETWORK\" value=\"241\" />\n        <enum name=\"KEYCODE_TV_ANTENNA_CABLE\" value=\"242\" />\n        <enum name=\"KEYCODE_TV_INPUT_HDMI_1\" value=\"243\" />\n        <enum name=\"KEYCODE_TV_INPUT_HDMI_2\" value=\"244\" />\n        <enum name=\"KEYCODE_TV_INPUT_HDMI_3\" value=\"245\" />\n        <enum name=\"KEYCODE_TV_INPUT_HDMI_4\" value=\"246\" />\n        <enum name=\"KEYCODE_TV_INPUT_COMPOSITE_1\" value=\"247\" />\n        <enum name=\"KEYCODE_TV_INPUT_COMPOSITE_2\" value=\"248\" />\n        <enum name=\"KEYCODE_TV_INPUT_COMPONENT_1\" value=\"249\" />\n        <enum name=\"KEYCODE_TV_INPUT_COMPONENT_2\" value=\"250\" />\n        <enum name=\"KEYCODE_TV_INPUT_VGA_1\" value=\"251\" />\n        <enum name=\"KEYCODE_TV_AUDIO_DESCRIPTION\" value=\"252\" />\n        <enum name=\"KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP\" value=\"253\" />\n        <enum name=\"KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN\" value=\"254\" />\n        <enum name=\"KEYCODE_TV_ZOOM_MODE\" value=\"255\" />\n        <enum name=\"KEYCODE_TV_CONTENTS_MENU\" value=\"256\" />\n        <enum name=\"KEYCODE_TV_MEDIA_CONTEXT_MENU\" value=\"257\" />\n        <enum name=\"KEYCODE_TV_TIMER_PROGRAMMING\" value=\"258\" />\n        <enum name=\"KEYCODE_HELP\" value=\"259\" />\n        <enum name=\"KEYCODE_NAVIGATE_PREVIOUS\" value=\"260\" />\n        <enum name=\"KEYCODE_NAVIGATE_NEXT\" value=\"261\" />\n        <enum name=\"KEYCODE_NAVIGATE_IN\" value=\"262\" />\n        <enum name=\"KEYCODE_NAVIGATE_OUT\" value=\"263\" />\n        <enum name=\"KEYCODE_STEM_PRIMARY\" value=\"264\" />\n        <enum name=\"KEYCODE_STEM_1\" value=\"265\" />\n        <enum name=\"KEYCODE_STEM_2\" value=\"266\" />\n        <enum name=\"KEYCODE_STEM_3\" value=\"267\" />\n        <enum name=\"KEYCODE_DPAD_UP_LEFT\" value=\"268\" />\n        <enum name=\"KEYCODE_DPAD_DOWN_LEFT\" value=\"269\" />\n        <enum name=\"KEYCODE_DPAD_UP_RIGHT\" value=\"270\" />\n        <enum name=\"KEYCODE_DPAD_DOWN_RIGHT\" value=\"271\" />\n        <enum name=\"KEYCODE_MEDIA_SKIP_FORWARD\" value=\"272\" />\n        <enum name=\"KEYCODE_MEDIA_SKIP_BACKWARD\" value=\"273\" />\n        <enum name=\"KEYCODE_MEDIA_STEP_FORWARD\" value=\"274\" />\n        <enum name=\"KEYCODE_MEDIA_STEP_BACKWARD\" value=\"275\" />\n        <enum name=\"KEYCODE_SOFT_SLEEP\" value=\"276\" />\n        <enum name=\"KEYCODE_CUT\" value=\"277\" />\n        <enum name=\"KEYCODE_COPY\" value=\"278\" />\n        <enum name=\"KEYCODE_PASTE\" value=\"279\" />\n        <enum name=\"KEYCODE_SYSTEM_NAVIGATION_UP\" value=\"280\" />\n        <enum name=\"KEYCODE_SYSTEM_NAVIGATION_DOWN\" value=\"281\" />\n        <enum name=\"KEYCODE_SYSTEM_NAVIGATION_LEFT\" value=\"282\" />\n        <enum name=\"KEYCODE_SYSTEM_NAVIGATION_RIGHT\" value=\"283\" />\n        <enum name=\"KEYCODE_ALL_APPS\" value=\"284\" />\n        <enum name=\"KEYCODE_REFRESH\" value=\"285\" />\n        <enum name=\"KEYCODE_THUMBS_UP\" value=\"286\" />\n        <enum name=\"KEYCODE_THUMBS_DOWN\" value=\"287\" />\n        <enum name=\"KEYCODE_PROFILE_SWITCH\" value=\"288\" />\n        <enum name=\"KEYCODE_VIDEO_APP_1\" value=\"289\" />\n        <enum name=\"KEYCODE_VIDEO_APP_2\" value=\"290\" />\n        <enum name=\"KEYCODE_VIDEO_APP_3\" value=\"291\" />\n        <enum name=\"KEYCODE_VIDEO_APP_4\" value=\"292\" />\n        <enum name=\"KEYCODE_VIDEO_APP_5\" value=\"293\" />\n        <enum name=\"KEYCODE_VIDEO_APP_6\" value=\"294\" />\n        <enum name=\"KEYCODE_VIDEO_APP_7\" value=\"295\" />\n        <enum name=\"KEYCODE_VIDEO_APP_8\" value=\"296\" />\n        <enum name=\"KEYCODE_FEATURED_APP_1\" value=\"297\" />\n        <enum name=\"KEYCODE_FEATURED_APP_2\" value=\"298\" />\n        <enum name=\"KEYCODE_FEATURED_APP_3\" value=\"299\" />\n        <enum name=\"KEYCODE_FEATURED_APP_4\" value=\"300\" />\n        <enum name=\"KEYCODE_DEMO_APP_1\" value=\"301\" />\n        <enum name=\"KEYCODE_DEMO_APP_2\" value=\"302\" />\n        <enum name=\"KEYCODE_DEMO_APP_3\" value=\"303\" />\n        <enum name=\"KEYCODE_DEMO_APP_4\" value=\"304\" />\n        <enum name=\"KEYCODE_KEYBOARD_BACKLIGHT_DOWN\" value=\"305\" />\n        <enum name=\"KEYCODE_KEYBOARD_BACKLIGHT_UP\" value=\"306\" />\n        <enum name=\"KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE\" value=\"307\" />\n        <enum name=\"KEYCODE_STYLUS_BUTTON_PRIMARY\" value=\"308\" />\n        <enum name=\"KEYCODE_STYLUS_BUTTON_SECONDARY\" value=\"309\" />\n        <enum name=\"KEYCODE_STYLUS_BUTTON_TERTIARY\" value=\"310\" />\n        <enum name=\"KEYCODE_STYLUS_BUTTON_TAIL\" value=\"311\" />\n        <enum name=\"KEYCODE_RECENT_APPS\" value=\"312\" />\n        <enum name=\"KEYCODE_MACRO_1\" value=\"313\" />\n        <enum name=\"KEYCODE_MACRO_2\" value=\"314\" />\n        <enum name=\"KEYCODE_MACRO_3\" value=\"315\" />\n        <enum name=\"KEYCODE_MACRO_4\" value=\"316\" />\n        <enum name=\"KEYCODE_EMOJI_PICKER\" value=\"317\" />\n        <enum name=\"KEYCODE_SCREENSHOT\" value=\"318\" />\n    </attr>\n\n    <!-- ***************************************************************** -->\n    <!-- These define collections of attributes that can are with classes. -->\n    <!-- ***************************************************************** -->\n\n    <!-- ========================== -->\n    <!-- Special attribute classes. -->\n    <!-- ========================== -->\n    <eat-comment />\n\n    <!-- The set of attributes that describe a Windows's theme. -->\n    <declare-styleable name=\"Window\">\n        <attr name=\"windowBackground\" />\n        <attr name=\"windowBackgroundFallback\" />\n        <attr name=\"windowBackgroundBlurRadius\" />\n        <attr name=\"windowContentOverlay\" />\n        <attr name=\"windowFrame\" />\n        <attr name=\"windowNoTitle\" />\n        <attr name=\"windowFullscreen\" />\n        <attr name=\"windowOverscan\" />\n        <attr name=\"windowIsFloating\" />\n        <attr name=\"windowIsTranslucent\" />\n        <attr name=\"windowShowWallpaper\" />\n        <attr name=\"windowAnimationStyle\" />\n        <attr name=\"windowSoftInputMode\" />\n        <attr name=\"windowDisablePreview\" />\n        <attr name=\"windowNoDisplay\" />\n        <attr name=\"textColor\" />\n        <attr name=\"backgroundDimEnabled\" />\n        <attr name=\"backgroundDimAmount\" />\n        <attr name=\"windowBlurBehindEnabled\" />\n        <attr name=\"windowBlurBehindRadius\" />\n        <attr name=\"windowActionBar\" />\n        <attr name=\"windowActionModeOverlay\" />\n        <attr name=\"windowActionBarOverlay\" />\n        <attr name=\"windowEnableSplitTouch\" />\n        <attr name=\"windowCloseOnTouchOutside\" />\n        <attr name=\"windowTranslucentStatus\" />\n        <attr name=\"windowTranslucentNavigation\" />\n        <attr name=\"windowContentTransitions\" />\n        <attr name=\"windowActivityTransitions\" />\n        <attr name=\"windowContentTransitionManager\" />\n        <attr name=\"windowActionBarFullscreenDecorLayout\" />\n\n        <!-- The minimum width the window is allowed to be, along the major\n             axis of the screen.  That is, when in landscape.  Can be either\n             an absolute dimension or a fraction of the screen size in that\n             dimension. -->\n        <attr name=\"windowMinWidthMajor\" format=\"dimension|fraction\" />\n        <!-- The minimum width the window is allowed to be, along the minor\n             axis of the screen.  That is, when in portrait.  Can be either\n             an absolute dimension or a fraction of the screen size in that\n             dimension. -->\n        <attr name=\"windowMinWidthMinor\" format=\"dimension|fraction\" />\n\n        <!-- A fixed width for the window along the major axis of the screen,\n             that is, when in landscape. Can be either an absolute dimension\n             or a fraction of the screen size in that dimension. -->\n        <attr name=\"windowFixedWidthMajor\" format=\"dimension|fraction\" />\n        <!-- A fixed height for the window along the minor axis of the screen,\n             that is, when in landscape. Can be either an absolute dimension\n             or a fraction of the screen size in that dimension. -->\n        <attr name=\"windowFixedHeightMinor\" format=\"dimension|fraction\" />\n\n        <!-- A fixed width for the window along the minor axis of the screen,\n             that is, when in portrait. Can be either an absolute dimension\n             or a fraction of the screen size in that dimension. -->\n        <attr name=\"windowFixedWidthMinor\" format=\"dimension|fraction\" />\n        <!-- A fixed height for the window along the major axis of the screen,\n             that is, when in portrait. Can be either an absolute dimension\n             or a fraction of the screen size in that dimension. -->\n        <attr name=\"windowFixedHeightMajor\" format=\"dimension|fraction\" />\n        <attr name=\"windowOutsetBottom\" format=\"dimension\" />\n        <!-- Reference to a Transition XML resource defining the desired Transition\n             used to move Views into the initial Window's content Scene. Corresponds to\n             {@link android.view.Window#setEnterTransition(android.transition.Transition)}. -->\n        <attr name=\"windowEnterTransition\"/>\n\n        <!-- Reference to a Transition XML resource defining the desired Transition\n             used to move Views out of the scene when the Window is\n             preparing to close. Corresponds to\n             {@link android.view.Window#setReturnTransition(android.transition.Transition)}. -->\n        <attr name=\"windowReturnTransition\"/>\n\n        <!-- Reference to a Transition XML resource defining the desired Transition\n             used to move Views out of the Window's content Scene when launching a new Activity.\n             Corresponds to\n             {@link android.view.Window#setExitTransition(android.transition.Transition)}. -->\n        <attr name=\"windowExitTransition\"/>\n\n        <!-- Reference to a Transition XML resource defining the desired Transition\n             used to move Views in to the scene when returning from a previously-started Activity.\n             Corresponds to\n             {@link android.view.Window#setReenterTransition(android.transition.Transition)}. -->\n        <attr name=\"windowReenterTransition\"/>\n\n        <!-- Reference to a Transition XML resource defining the desired Transition\n             used to move shared elements transferred into the Window's initial content Scene.\n             Corresponds to {@link android.view.Window#setSharedElementEnterTransition(\n             android.transition.Transition)}. -->\n        <attr name=\"windowSharedElementEnterTransition\"/>\n\n        <!-- Reference to a Transition XML resource defining the desired Transition\n             used to move shared elements transferred back to a calling Activity.\n             Corresponds to {@link android.view.Window#setSharedElementReturnTransition(\n             android.transition.Transition)}. -->\n        <attr name=\"windowSharedElementReturnTransition\"/>\n\n        <!-- Reference to a Transition XML resource defining the desired Transition\n             used when starting a new Activity to move shared elements prior to transferring\n             to the called Activity.\n             Corresponds to {@link android.view.Window#setSharedElementExitTransition(\n             android.transition.Transition)}. -->\n        <attr name=\"windowSharedElementExitTransition\"/>\n\n        <!-- Reference to a Transition XML resource defining the desired Transition\n             used for shared elements transferred back to a calling Activity.\n             Corresponds to {@link android.view.Window#setSharedElementReenterTransition(\n             android.transition.Transition)}. -->\n        <attr name=\"windowSharedElementReenterTransition\"/>\n\n\n        <!-- Flag indicating whether this Window's transition should overlap with\n             the exiting transition of the calling Activity. Corresponds to\n             {@link android.view.Window#setAllowEnterTransitionOverlap(boolean)}. -->\n        <attr name=\"windowAllowEnterTransitionOverlap\"/>\n\n        <!-- Flag indicating whether this Window's transition should overlap with\n             the exiting transition of the called Activity when the called Activity\n             finishes. Corresponds to\n             {@link android.view.Window#setAllowReturnTransitionOverlap(boolean)}. -->\n        <attr name=\"windowAllowReturnTransitionOverlap\"/>\n\n        <!-- Indicates whether or not shared elements should use an overlay\n             during transitions. The default value is true. -->\n        <attr name=\"windowSharedElementsUseOverlay\"/>\n\n        <!-- Flag indicating whether this Window is responsible for drawing the background for the\n             system bars. If true and the window is not floating, the system bars are drawn with a\n             transparent background and the corresponding areas in this window are filled with the\n             colors specified in {@link android.R.attr#statusBarColor} and\n             {@link android.R.attr#navigationBarColor}. Corresponds to\n             {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS}. -->\n        <attr name=\"windowDrawsSystemBarBackgrounds\" format=\"boolean\" />\n\n        <!-- The color for the status bar. If the color is not opaque, consider setting\n             {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and\n             {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.\n             For this to take effect, the window must be drawing the system bar backgrounds with\n             {@link android.R.attr#windowDrawsSystemBarBackgrounds} and the status bar must not\n             have been requested to be translucent with\n             {@link android.R.attr#windowTranslucentStatus}.\n             Corresponds to {@link android.view.Window#setStatusBarColor(int)}.\n             <p>If the color is transparent and the window enforces the status bar contrast, the\n             system will determine whether a scrim is necessary and draw one on behalf of the app to\n             ensure that the status bar has enough contrast with the contents of this app, and set\n             an appropriate effective bar background accordingly.\n             See: {@link android.R.attr#enforceStatusBarContrast}\n             <p>If the window belongs to an app targeting\n             {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM VANILLA_ICE_CREAM} or above,\n             this attribute is ignored.\n             @deprecated Draw proper background behind\n                         {@link android.view.WindowInsets.Type#statusBars()}} instead. -->\n        <attr name=\"statusBarColor\" format=\"color\" />\n\n        <!-- The color for the navigation bar. If the color is not opaque, consider setting\n             {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and\n             {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}.\n             For this to take effect, the window must be drawing the system bar backgrounds with\n             {@link android.R.attr#windowDrawsSystemBarBackgrounds} and the navigation bar must not\n             have been requested to be translucent with\n             {@link android.R.attr#windowTranslucentNavigation}.\n             Corresponds to {@link android.view.Window#setNavigationBarColor(int)}.\n             <p>If the color is transparent and the window enforces the navigation bar contrast, the\n             system will determine whether a scrim is necessary and draw one on behalf of the app to\n             ensure that the navigation bar has enough contrast with the contents of this app, and\n             set an appropriate effective bar background accordingly.\n             See: {@link android.R.attr#enforceNavigationBarContrast}\n             <p>If the window belongs to an app targeting\n             {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM VANILLA_ICE_CREAM} or above,\n             this attribute is ignored.\n             @deprecated Draw proper background behind\n                         {@link android.view.WindowInsets.Type#navigationBars()} or\n                         {@link android.view.WindowInsets.Type#tappableElement()} instead. -->\n        <attr name=\"navigationBarColor\" format=\"color\" />\n\n        <!-- Shows a thin line of the specified color between the navigation bar and the app\n             content.\n             <p>For this to take effect, the window must be drawing the system bar backgrounds with\n             {@link android.R.attr#windowDrawsSystemBarBackgrounds} and the navigation bar must not\n             have been requested to be translucent with\n             {@link android.R.attr#windowTranslucentNavigation}.\n             Corresponds to {@link android.view.Window#setNavigationBarDividerColor(int)}.\n             <p>If the window belongs to an app targeting\n             {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM VANILLA_ICE_CREAM} or above,\n             this attribute is ignored.\n             @deprecated Draw proper background behind\n                         {@link android.view.WindowInsets.Type#navigationBars()} or\n                         {@link android.view.WindowInsets.Type#tappableElement()} instead. -->\n        <attr name=\"navigationBarDividerColor\" format=\"color\" />\n\n        <!-- Sets whether the system should ensure that the status bar has enough\n             contrast when a fully transparent background is requested.\n\n             <p>If set to this value, the system will determine whether a scrim is necessary\n             to ensure that the status bar has enough contrast with the contents of\n             this app, and set an appropriate effective bar background color accordingly.\n\n             <p>When the status bar color has a non-zero alpha value, the value of this\n             attribute has no effect.\n\n             <p>If the app does not target at least {@link android.os.Build.VERSION_CODES#Q Q},\n             this attribute is ignored.\n\n             @see android.view.Window#setStatusBarContrastEnforced\n             @deprecated Draw proper background behind\n                         {@link android.view.WindowInsets.Type#statusBars()}} instead. -->\n        <attr name=\"enforceStatusBarContrast\" format=\"boolean\" />\n\n        <!-- Sets whether the system should ensure that the navigation bar has enough\n             contrast when a fully transparent background is requested.\n\n             <p>If set to this value, the system will determine whether a scrim is necessary\n             to ensure that the navigation bar has enough contrast with the contents of\n             this app, and set an appropriate effective bar background color accordingly.\n\n             <p>When the navigation bar color has a non-zero alpha value, the value of this\n             attribute has no effect.\n\n             <p>If the app does not target at least {@link android.os.Build.VERSION_CODES#Q Q},\n             this attribute is ignored.\n\n             @see android.view.Window#setNavigationBarContrastEnforced -->\n        <attr name=\"enforceNavigationBarContrast\" format=\"boolean\" />\n\n        <!-- The duration, in milliseconds, of the window background fade duration\n             when transitioning into or away from an Activity when called with an\n             Activity Transition. Corresponds to\n             {@link android.view.Window#setTransitionBackgroundFadeDuration(long)}. -->\n        <attr name=\"windowTransitionBackgroundFadeDuration\" />\n\n        <!-- Elevation to use for the window. -->\n        <attr name=\"windowElevation\" format=\"dimension\" />\n\n        <!-- Flag indicating whether this window should skip movement animations.\n             See also {@link android.view.WindowManager.LayoutParams#setCanPlayMoveAnimation} -->\n        <attr name=\"windowNoMoveAnimation\" format=\"boolean\" />\n\n        <!-- Whether to clip window content to the outline of the window background. -->\n        <attr name=\"windowClipToOutline\" format=\"boolean\" />\n\n        <!-- If set, the status bar will be drawn such that it is compatible with a light\n             status bar background.\n             <p>For this to take effect, the window must be drawing the system bar backgrounds with\n             {@link android.R.attr#windowDrawsSystemBarBackgrounds} and the status bar must not\n             have been requested to be translucent with\n             {@link android.R.attr#windowTranslucentStatus}.\n             Corresponds to setting {@link android.view.View#SYSTEM_UI_FLAG_LIGHT_STATUS_BAR} on\n             the decor view and\n             {@link android.view.WindowInsetsController#APPEARANCE_LIGHT_STATUS_BARS} on the\n             {@link android.view.WindowInsetsController}. -->\n        <attr name=\"windowLightStatusBar\" format=\"boolean\" />\n\n        <!-- Reference to a drawable to be used as the splash screen content of the window. This\n             drawable will be placed on top of the {@link android.R.attr#windowBackground} with its\n             bounds inset by the system bars. If the drawable should not be inset by the system\n             bars, use a fullscreen theme.\n             <p>\n             Note that even if no splashscreen content is set on the theme, the system may still\n             show a splash screen using the other attributes on the theme, like the\n             {@link android.R.attr#windowBackground}.\n             {@deprecated Use windowSplashscreenAnimatedIcon instead.}\n             -->\n        <attr name=\"windowSplashscreenContent\" format=\"reference\" />\n\n        <!-- If set, the navigation bar will be drawn such that it is compatible with a light\n             navigation bar background.\n             <p>For this to take effect, the window must be drawing the system bar backgrounds with\n             {@link android.R.attr#windowDrawsSystemBarBackgrounds} and the navigation bar must not\n             have been requested to be translucent with\n             {@link android.R.attr#windowTranslucentNavigation}.\n             Corresponds to setting {@link android.view.View#SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR} on\n             the decor view and\n             {@link android.view.WindowInsetsController#APPEARANCE_LIGHT_NAVIGATION_BARS} on the\n             {@link android.view.WindowInsetsController}. -->\n        <attr name=\"windowLightNavigationBar\" format=\"boolean\" />\n\n        <!-- Controls how the window is laid out if there is a {@code DisplayCutout}.\n        <p>\n        Defaults to {@code default}. But if the window fills the screen, and it belongs to an app\n        targeting {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM VANILLA_ICE_CREAM} or\n        above, the behavior will be the same as specifying {@code always} regardless.\n        <p>\n        See also\n        {@link android.view.WindowManager.LayoutParams#layoutInDisplayCutoutMode\n                WindowManager.LayoutParams.layoutInDisplayCutoutMode},\n        {@link android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT},\n        {@link android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES},\n        {@link android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER},\n        and {@link android.view.DisplayCutout DisplayCutout}\n        -->\n        <attr name=\"windowLayoutInDisplayCutoutMode\">\n            <!-- <p>\n            The window is allowed to extend into the <code>DisplayCutout</code> area, only if\n            the <code>DisplayCutout</code> is fully contained within a system bar. Otherwise, the\n            window is laid out such that it does not overlap with the <code>DisplayCutout</code>\n            area.\n            <p>\n            Corresponds to <code>LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT</code>.\n            -->\n            <enum name=\"default\" value=\"0\" />\n            <!-- <p>\n            The window is always allowed to extend into the <code>DisplayCutout</code> areas on the\n            short edges of the screen even if fullscreen or in landscape.\n            The window will never extend into a <code>DisplayCutout</code> area on the long edges of\n            the screen.\n            <p>\n            The window must make sure that no important content overlaps with the\n            <code>DisplayCutout</code>.\n            <p>\n            Corresponds to <code>LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES</code>.\n            -->\n            <enum name=\"shortEdges\" value=\"1\" />\n            <!-- <p>\n            The window is never allowed to overlap with the <code>DisplayCutout</code> area.\n            <p>\n            This should be used with windows that transiently set\n            <code>SYSTEM_UI_FLAG_FULLSCREEN</code> to avoid a relayout of the window when the\n            flag is set or cleared.\n            <p>\n            Corresponds to <code>LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER</code>.\n            -->\n            <enum name=\"never\" value=\"2\" />\n            <!-- <p>\n            The window is always allowed to extend into the <code>DisplayCutout</code> areas on the\n            all edges of the screen.\n            <p>\n            The window must make sure that no important content overlaps with the\n            <code>DisplayCutout</code>.\n            <p>\n            Corresponds to <code>LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS</code>.\n            -->\n            <enum name=\"always\" value=\"3\" />\n        </attr>\n\n        <!-- The background color for the splash screen, if not specify then system will\n             calculate from windowBackground. -->\n        <attr name=\"windowSplashScreenBackground\" format=\"color\"/>\n\n        <!-- Replace an icon in the center of the starting window, if the object is animated\n             and drawable(e.g. AnimationDrawable, AnimatedVectorDrawable), then it will also\n             play the animation while showing the starting window. -->\n        <attr name=\"windowSplashScreenAnimatedIcon\" format=\"reference\"/>\n        <!-- The duration, in milliseconds, of the window splash screen icon animation duration\n             when playing the splash screen starting window. The maximum animation duration should\n             be limited below 1000ms.\n              @deprecated Not used by framework starting from API level 33. The system estimates the\n               duration of the vector animation automatically. -->\n        <attr name=\"windowSplashScreenAnimationDuration\" format=\"integer\"/>\n\n        <!-- Place a drawable image in the bottom of the starting window. The image can be used to\n             represent the branding of the application. -->\n        <attr name=\"windowSplashScreenBrandingImage\" format=\"reference\"/>\n        <!-- Set a background behind the splash screen icon. This is useful if there is not enough\n             contrast between the window background and the icon. Note the shape would also be\n             masking like an icon. -->\n        <attr name=\"windowSplashScreenIconBackgroundColor\" format=\"color\"/>\n\n        <!-- Specify whether this application always wants the icon to be displayed on the splash\n             screen. -->\n        <attr name=\"windowSplashScreenBehavior\">\n            <!-- The icon is shown when the launching activity sets the splashScreenStyle to\n                 SPLASH_SCREEN_STYLE_ICON. If the launching activity does not specify any style,\n                 follow the system behavior. -->\n            <enum name=\"default\" value=\"0\" />\n            <!-- The icon is shown unless the launching app specified SPLASH_SCREEN_STYLE_EMPTY -->\n            <enum name=\"icon_preferred\" value=\"1\" />\n        </attr>\n        <!-- Offer Window the ability to opt out the UI Toolkit discrete variable refresh rate.\n             This feature allows device to adjust refresh rate as needed and\n             can be useful for power saving.\n             Set to false to reduce the frame rate optimizations on devices with\n             variable refresh rate screens.\n             The default is true. -->\n        <attr name=\"windowIsFrameRatePowerSavingsBalanced\" format=\"boolean\"/>\n\n        <!-- Flag indicating whether this window would opt out the edge-to-edge enforcement.\n\n             <p>If this is false, the edge-to-edge enforcement will be applied to the window if it\n             belongs to an app targeting\n             {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM VANILLA_ICE_CREAM} or above.\n             The affected behaviors are:\n             <ul>\n                 <li>The framework will not fit the content view to the insets and will just pass\n                 through the {@link android.view.WindowInsets} to the content view, as if calling\n                 {@link android.view.Window#setDecorFitsSystemWindows(boolean)} with false.\n                 <li>{@link android.view.WindowManager.LayoutParams#layoutInDisplayCutoutMode} of\n                 the fill-screen windows will behave as specifying {@link\n                 android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS}.\n                 <li>The framework will set {@link android.R.attr#statusBarColor},\n                 {@link android.R.attr#navigationBarColor}, and\n                 {@link android.R.attr#navigationBarDividerColor} to transparent.\n                 <li>The frameworks will send Configuration no longer considering system insets.\n                 The Configuration will be stable regardless of the system insets change.\n             </ul>\n\n             <p>If this is true, the edge-to-edge enforcement won't be applied. But if the window\n             belongs to an app targeting {@link android.os.Build.VERSION_CODES#BAKLAVA BAKLAVA} or\n             above, this attribute is ignored and the enforcement is applied regardless.\n\n             <p>This is false by default. -->\n        <attr name=\"windowOptOutEdgeToEdgeEnforcement\" format=\"boolean\"/>\n    </declare-styleable>\n\n    <!-- The set of attributes that describe a AlertDialog's theme. -->\n    <declare-styleable name=\"AlertDialog\">\n        <attr name=\"fullDark\" format=\"reference|color\" />\n        <attr name=\"topDark\" format=\"reference|color\" />\n        <attr name=\"centerDark\" format=\"reference|color\" />\n        <attr name=\"bottomDark\" format=\"reference|color\" />\n        <attr name=\"fullBright\" format=\"reference|color\" />\n        <attr name=\"topBright\" format=\"reference|color\" />\n        <attr name=\"centerBright\" format=\"reference|color\" />\n        <attr name=\"bottomBright\" format=\"reference|color\" />\n        <attr name=\"bottomMedium\" format=\"reference|color\" />\n        <attr name=\"centerMedium\" format=\"reference|color\" />\n        <attr name=\"layout\" />\n        <attr name=\"buttonPanelSideLayout\" format=\"reference\" />\n        <attr name=\"listLayout\" format=\"reference\" />\n        <attr name=\"multiChoiceItemLayout\" format=\"reference\" />\n        <attr name=\"singleChoiceItemLayout\" format=\"reference\" />\n        <attr name=\"listItemLayout\" format=\"reference\" />\n        <attr name=\"progressLayout\" format=\"reference\" />\n        <attr name=\"horizontalProgressLayout\" format=\"reference\" />\n        <!-- @hide Not ready for public use. -->\n        <attr name=\"showTitle\" format=\"boolean\" />\n        <!-- @hide Whether fullDark, etc. should use default values if null. -->\n        <attr name=\"needsDefaultBackgrounds\" format=\"boolean\" />\n        <!-- @hide Workaround until we replace AlertController with custom layout. -->\n        <attr name=\"controllerType\">\n            <!-- The default controller. -->\n            <enum name=\"normal\" value=\"0\" />\n            <!-- Controller for micro specific layout. -->\n            <enum name=\"micro\" value=\"1\" />\n        </attr>\n        <!-- @hide Offset when scrolling to a selection. -->\n        <attr name=\"selectionScrollOffset\" format=\"dimension\" />\n    </declare-styleable>\n\n    <!-- @hide -->\n    <declare-styleable name=\"ButtonBarLayout\">\n        <!-- Whether to automatically stack the buttons when there is not\n             enough space to lay them out side-by-side. -->\n        <attr name=\"allowStacking\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- Fragment animation class attributes. -->\n    <declare-styleable name=\"FragmentAnimation\">\n        <attr name=\"fragmentOpenEnterAnimation\" format=\"reference\" />\n        <attr name=\"fragmentOpenExitAnimation\" format=\"reference\" />\n        <attr name=\"fragmentCloseEnterAnimation\" format=\"reference\" />\n        <attr name=\"fragmentCloseExitAnimation\" format=\"reference\" />\n        <attr name=\"fragmentFadeEnterAnimation\" format=\"reference\" />\n        <attr name=\"fragmentFadeExitAnimation\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- Window animation class attributes. -->\n    <declare-styleable name=\"WindowAnimation\">\n        <!-- The animation used when a window is being added. -->\n        <attr name=\"windowEnterAnimation\" format=\"reference\" />\n        <!-- The animation used when a window is being removed. -->\n        <attr name=\"windowExitAnimation\" format=\"reference\" />\n        <!-- The animation used when a window is going from INVISIBLE to VISIBLE. -->\n        <attr name=\"windowShowAnimation\" format=\"reference\" />\n        <!-- The animation used when a window is going from VISIBLE to INVISIBLE. -->\n        <attr name=\"windowHideAnimation\" format=\"reference\" />\n\n        <!--  When opening a new activity, this is the animation that is\n              run on the next activity (which is entering the screen). -->\n        <attr name=\"activityOpenEnterAnimation\" format=\"reference\" />\n        <!--  When opening a new activity, this is the animation that is\n              run on the previous activity (which is exiting the screen). -->\n        <attr name=\"activityOpenExitAnimation\" format=\"reference\" />\n        <!--  When closing the current activity, this is the animation that is\n              run on the next activity (which is entering the screen). -->\n        <attr name=\"activityCloseEnterAnimation\" format=\"reference\" />\n        <!--  When closing the current activity, this is the animation that is\n              run on the current activity (which is exiting the screen). -->\n        <attr name=\"activityCloseExitAnimation\" format=\"reference\" />\n        <!--  When closing a dream activity, this is the animation that is\n              run on the dream activity (which is exiting the screen). -->\n        <attr name=\"dreamActivityCloseExitAnimation\" format=\"reference\" />\n        <!--  When opening a dream activity, this is the animation that is\n              run on the dream activity (which is entering the screen). -->\n        <attr name=\"dreamActivityOpenEnterAnimation\" format=\"reference\" />\n        <!--  When opening a dream activity, this is the animation that is\n              run on the old activity (which is exiting the screen). -->\n        <attr name=\"dreamActivityOpenExitAnimation\" format=\"reference\" />\n        <!--  When opening an activity in a new task, this is the animation that is\n              run on the activity of the new task (which is entering the screen). -->\n        <attr name=\"taskOpenEnterAnimation\" format=\"reference\" />\n        <!--  When opening an activity in a new task, this is the animation that is\n              run on the activity of the old task (which is exiting the screen). -->\n        <attr name=\"taskOpenExitAnimation\" format=\"reference\" />\n        <!--  When opening an activity in a new task using Intent/FLAG_ACTIVITY_LAUNCH_BEHIND,\n              this is the animation that is run on the activity of the new task (which is\n              entering the screen and then leaving). -->\n        <attr name=\"launchTaskBehindTargetAnimation\" format=\"reference\" />\n        <!--  When opening an activity in a new task using Intent.FLAG_ACTIVITY_LAUNCH_BEHIND,\n              this is the animation that is run on the activity of the old task (which is\n              already on the screen and then stays on). -->\n        <attr name=\"launchTaskBehindSourceAnimation\" format=\"reference\" />\n        <!--  When closing the last activity of a task, this is the animation that is\n              run on the activity of the next task (which is entering the screen). -->\n        <attr name=\"taskCloseEnterAnimation\" format=\"reference\" />\n        <!--  When opening an activity in a new task, this is the animation that is\n              run on the activity of the old task (which is exiting the screen). -->\n        <attr name=\"taskCloseExitAnimation\" format=\"reference\" />\n        <!--  When bringing an existing task to the foreground, this is the\n              animation that is run on the top activity of the task being brought\n              to the foreground (which is entering the screen). -->\n        <attr name=\"taskToFrontEnterAnimation\" format=\"reference\" />\n        <!--  When bringing an existing task to the foreground, this is the\n              animation that is run on the current foreground activity\n              (which is exiting the screen). -->\n        <attr name=\"taskToFrontExitAnimation\" format=\"reference\" />\n        <!--  When sending the current task to the background, this is the\n              animation that is run on the top activity of the task behind\n              it (which is entering the screen). -->\n        <attr name=\"taskToBackEnterAnimation\" format=\"reference\" />\n        <!--  When sending the current task to the background, this is the\n              animation that is run on the top activity of the current task\n              (which is exiting the screen). -->\n        <attr name=\"taskToBackExitAnimation\" format=\"reference\" />\n\n        <!--  When opening a new activity that shows the wallpaper, while\n              currently not showing the wallpaper, this is the animation that\n              is run on the new wallpaper activity (which is entering the screen). -->\n        <attr name=\"wallpaperOpenEnterAnimation\" format=\"reference\" />\n        <!--  When opening a new activity that shows the wallpaper, while\n              currently not showing the wallpaper, this is the animation that\n              is run on the current activity (which is exiting the screen). -->\n        <attr name=\"wallpaperOpenExitAnimation\" format=\"reference\" />\n        <!--  When opening a new activity that hides the wallpaper, while\n              currently showing the wallpaper, this is the animation that\n              is run on the new activity (which is entering the screen). -->\n        <attr name=\"wallpaperCloseEnterAnimation\" format=\"reference\" />\n        <!--  When opening a new activity that hides the wallpaper, while\n              currently showing the wallpaper, this is the animation that\n              is run on the old wallpaper activity (which is exiting the screen). -->\n        <attr name=\"wallpaperCloseExitAnimation\" format=\"reference\" />\n\n        <!--  When opening a new activity that is on top of the wallpaper\n              when the current activity is also on top of the wallpaper,\n              this is the animation that is run on the new activity\n              (which is entering the screen).  The wallpaper remains\n              static behind the animation. -->\n        <attr name=\"wallpaperIntraOpenEnterAnimation\" format=\"reference\" />\n        <!--  When opening a new activity that is on top of the wallpaper\n              when the current activity is also on top of the wallpaper,\n              this is the animation that is run on the current activity\n              (which is exiting the screen).  The wallpaper remains\n              static behind the animation. -->\n        <attr name=\"wallpaperIntraOpenExitAnimation\" format=\"reference\" />\n        <!--  When closing a foreround activity that is on top of the wallpaper\n              when the previous activity is also on top of the wallpaper,\n              this is the animation that is run on the previous activity\n              (which is entering the screen).  The wallpaper remains\n              static behind the animation. -->\n        <attr name=\"wallpaperIntraCloseEnterAnimation\" format=\"reference\" />\n        <!--  When closing a foreround activity that is on top of the wallpaper\n              when the previous activity is also on top of the wallpaper,\n              this is the animation that is run on the current activity\n              (which is exiting the screen).  The wallpaper remains\n              static behind the animation. -->\n        <attr name=\"wallpaperIntraCloseExitAnimation\" format=\"reference\" />\n\n        <!--  When opening a new activity from a RemoteViews, this is the\n              animation that is run on the next activity (which is entering the\n              screen). Requires config_overrideRemoteViewsActivityTransition to\n              be true. -->\n        <attr name=\"activityOpenRemoteViewsEnterAnimation\" format=\"reference\" />\n\n    </declare-styleable>\n\n    <!-- ============================= -->\n    <!-- View package class attributes -->\n    <!-- ============================= -->\n    <eat-comment />\n\n    <!-- Removed View attributes without a specified format (b/131100106) -->\n    <attr name=\"__removed3\" />\n    <attr name=\"__removed4\" />\n    <attr name=\"__removed5\" />\n    <attr name=\"__removed6\" />\n\n    <!-- Attributes that can be used with {@link android.view.View} or\n         any of its subclasses.  Also see {@link #ViewGroup_Layout} for\n         attributes that are processed by the view's parent. -->\n    <declare-styleable name=\"View\">\n        <!-- Supply an identifier name for this view, to later retrieve it\n             with {@link android.view.View#findViewById View.findViewById()} or\n             {@link android.app.Activity#findViewById Activity.findViewById()}.\n             This must be a\n             resource reference; typically you set this using the\n             <code>@+</code> syntax to create a new ID resources.\n             For example: <code>android:id=\"@+id/my_id\"</code> which\n             allows you to later retrieve the view\n             with <code>findViewById(R.id.my_id)</code>. -->\n        <attr name=\"id\" format=\"reference\" />\n\n        <!-- Supply a tag for this view containing a String, to be retrieved\n             later with {@link android.view.View#getTag View.getTag()} or\n             searched for with {@link android.view.View#findViewWithTag\n             View.findViewWithTag()}.  It is generally preferable to use\n             IDs (through the android:id attribute) instead of tags because\n             they are faster and allow for compile-time type checking. -->\n        <attr name=\"tag\" format=\"string\" />\n\n        <!-- The initial horizontal scroll offset, in pixels.-->\n        <attr name=\"scrollX\" format=\"dimension\" />\n\n        <!-- The initial vertical scroll offset, in pixels. -->\n        <attr name=\"scrollY\" format=\"dimension\" />\n\n        <!-- A drawable to use as the background.  This can be either a reference\n             to a full drawable resource (such as a PNG image, 9-patch,\n             XML state list description, etc), or a solid color such as \"#ff000000\"\n            (black). -->\n        <attr name=\"background\" format=\"reference|color\" />\n\n        <!-- Sets the padding, in pixels, of all four edges. Padding is defined as\n             space between the edges of the view and the view's content. This value will take\n             precedence over any of the edge-specific values (paddingLeft, paddingTop,\n             paddingRight, paddingBottom, paddingHorizontal and paddingVertical), but will\n             not override paddingStart or paddingEnd, if set. A view's size\n             will include its padding. If a {@link android.R.attr#background}\n             is provided, the padding will initially be set to that (0 if the\n             drawable does not have padding). Explicitly setting a padding value\n             will override the corresponding padding found in the background. -->\n        <attr name=\"padding\" format=\"dimension\" />\n        <!-- Sets the padding, in pixels, of the left and right edges; see\n             {@link android.R.attr#padding}. This value will take precedence over\n             paddingLeft and paddingRight, but not paddingStart or paddingEnd (if set). -->\n        <attr name=\"paddingHorizontal\" format=\"dimension\" />\n        <!-- Sets the padding, in pixels, of the top and bottom edges; see\n             {@link android.R.attr#padding}. This value will take precedence over\n             paddingTop and paddingBottom, if set. -->\n        <attr name=\"paddingVertical\" format=\"dimension\" />\n        <!-- Sets the padding, in pixels, of the left edge; see {@link android.R.attr#padding}. -->\n        <attr name=\"paddingLeft\" format=\"dimension\" />\n        <!-- Sets the padding, in pixels, of the top edge; see {@link android.R.attr#padding}. -->\n        <attr name=\"paddingTop\" format=\"dimension\" />\n        <!-- Sets the padding, in pixels, of the right edge; see {@link android.R.attr#padding}. -->\n        <attr name=\"paddingRight\" format=\"dimension\" />\n        <!-- Sets the padding, in pixels, of the bottom edge; see {@link android.R.attr#padding}. -->\n        <attr name=\"paddingBottom\" format=\"dimension\" />\n        <!-- Sets the padding, in pixels, of the start edge; see {@link android.R.attr#padding}. -->\n        <attr name=\"paddingStart\" format=\"dimension\" />\n        <!-- Sets the padding, in pixels, of the end edge; see {@link android.R.attr#padding}. -->\n        <attr name=\"paddingEnd\" format=\"dimension\" />\n\n        <!-- Controls whether a view can take focus.  By default, this is \"auto\" which lets the\n             framework determine whether a user can move focus to a view.  By setting this attribute\n             to true the view is allowed to take focus. By setting it to \"false\" the view will not\n             take focus. This value does not impact the behavior of\n             directly calling {@link android.view.View#requestFocus}, which will\n             always request focus regardless of this view.  It only impacts where\n             focus navigation will try to move focus. -->\n        <attr name=\"focusable\" format=\"boolean|enum\">\n            <enum name=\"auto\" value=\"0x00000010\" />\n        </attr>\n\n        <attr name=\"__removed3\" />\n        <attr name=\"__removed4\" />\n        <attr name=\"__removed5\" />\n\n        <!-- Describes the content of a view so that a autofill service can fill in the appropriate\n             data. Multiple hints can be combined in a comma separated list or an array of strings\n             to mean e.g. emailAddress or postalAddress. -->\n        <attr name=\"autofillHints\" format=\"string|reference\" />\n\n        <!-- Hints the Android System whether the view node associated with this View should be\n             included in a view structure used for autofill purposes. -->\n        <attr name=\"importantForAutofill\">\n            <!-- Let the Android System use its heuristics to determine if the view is important for autofill. -->\n            <flag name=\"auto\" value=\"0\" />\n            <!-- Hint the Android System that this view is important for autofill,\n                  and its children (if any) will be traversed.. -->\n            <flag name=\"yes\" value=\"0x1\" />\n            <!-- Hint the Android System that this view is *not* important for autofill,\n                  but its children (if any) will be traversed.. -->\n            <flag name=\"no\" value=\"0x2\" />\n            <!-- Hint the Android System that this view is important for autofill,\n                 but its children (if any) will not be traversed. -->\n            <flag name=\"yesExcludeDescendants\" value=\"0x4\" />\n            <!-- Hint the Android System that this view is *not* important for autofill,\n                 and its children (if any) will not be traversed. -->\n            <flag name=\"noExcludeDescendants\" value=\"0x8\" />\n        </attr>\n\n        <!-- Hints the Android System whether the view node associated with this View should be\n             use for content capture purposes. -->\n        <attr name=\"importantForContentCapture\">\n            <!-- Let the Android System use its heuristics to determine if the view is important for content capture. -->\n            <flag name=\"auto\" value=\"0\" />\n            <!-- Hint the Android System that this view is important for content capture,\n                  and its children (if any) will be traversed.. -->\n            <flag name=\"yes\" value=\"0x1\" />\n            <!-- Hint the Android System that this view is *not* important for content capture,\n                  but its children (if any) will be traversed.. -->\n            <flag name=\"no\" value=\"0x2\" />\n            <!-- Hint the Android System that this view is important for content capture,\n                 but its children (if any) will not be traversed. -->\n            <flag name=\"yesExcludeDescendants\" value=\"0x4\" />\n            <!-- Hint the Android System that this view is *not* important for content capture,\n                 and its children (if any) will not be traversed. -->\n            <flag name=\"noExcludeDescendants\" value=\"0x8\" />\n        </attr>\n\n        <!-- Boolean that hints the Android System that the view is credential and associated with\n             CredentialManager -->\n        <attr name=\"isCredential\" format=\"boolean\" />\n\n        <!-- Hints the Android System whether the this View should be considered a scroll capture target. -->\n        <attr name=\"scrollCaptureHint\">\n            <!-- Let the Android System  determine if the view can be a scroll capture target. -->\n            <flag name=\"auto\" value=\"0\" />\n            <!-- Hint the Android System that this view is a likely target. If capable, it will\n                 be ranked above other views without this flag. -->\n            <flag name=\"include\" value=\"0x1\" />\n            <!-- Hint the Android System that this view should never be considered a scroll capture\n                 target. -->\n            <flag name=\"exclude\" value=\"0x2\" />\n            <!-- Hint the Android System that this view's children should not be examined and should\n                 be excluded as a scroll capture target. -->\n            <flag name=\"excludeDescendants\" value=\"0x4\" />\n        </attr>\n\n        <!-- Boolean that controls whether a view can take focus while in touch mode.\n             If this is true for a view, that view can gain focus when clicked on, and can keep\n             focus if another view is clicked on that doesn't have this attribute set to true. -->\n        <attr name=\"focusableInTouchMode\" format=\"boolean\" />\n\n        <!-- Controls the initial visibility of the view.  -->\n        <attr name=\"visibility\">\n            <!-- Visible on screen; the default value. -->\n            <enum name=\"visible\" value=\"0\" />\n            <!-- Not displayed, but taken into account during layout (space is left for it). -->\n            <enum name=\"invisible\" value=\"1\" />\n            <!-- Completely hidden, as if the view had not been added. -->\n            <enum name=\"gone\" value=\"2\" />\n        </attr>\n\n        <!-- Boolean internal attribute to adjust view layout based on\n             system windows such as the status bar.\n             If true, adjusts the padding of this view to leave space for the system windows.\n             Will only take effect if this view is in a non-embedded activity. -->\n        <attr name=\"fitsSystemWindows\" format=\"boolean\" />\n\n        <!-- Defines which scrollbars should be displayed on scrolling or not. -->\n        <attr name=\"scrollbars\">\n            <!-- No scrollbar is displayed. -->\n            <flag name=\"none\" value=\"0x00000000\" />\n            <!-- Displays horizontal scrollbar only. -->\n            <flag name=\"horizontal\" value=\"0x00000100\" />\n            <!-- Displays vertical scrollbar only. -->\n            <flag name=\"vertical\" value=\"0x00000200\" />\n        </attr>\n\n        <!-- Controls the scrollbar style and position. The scrollbars can be overlaid or\n             inset. When inset, they add to the padding of the view. And the\n             scrollbars can be drawn inside the padding area or on the edge of\n             the view. For example, if a view has a background drawable and you\n             want to draw the scrollbars inside the padding specified by the\n             drawable, you can use insideOverlay or insideInset. If you want them\n             to appear at the edge of the view, ignoring the padding, then you can\n             use outsideOverlay or outsideInset.-->\n        <attr name=\"scrollbarStyle\">\n            <!-- Inside the padding and overlaid. -->\n            <enum name=\"insideOverlay\" value=\"0x0\" />\n            <!-- Inside the padding and inset. -->\n            <enum name=\"insideInset\" value=\"0x01000000\" />\n            <!-- Edge of the view and overlaid. -->\n            <enum name=\"outsideOverlay\" value=\"0x02000000\" />\n            <!-- Edge of the view and inset. -->\n            <enum name=\"outsideInset\" value=\"0x03000000\" />\n        </attr>\n\n        <!-- Set this if the view will serve as a scrolling container, meaning\n             that it can be resized to shrink its overall window so that there\n             will be space for an input method.  If not set, the default\n             value will be true if \"scrollbars\" has the vertical scrollbar\n             set, else it will be false. -->\n        <attr name=\"isScrollContainer\" format=\"boolean\" />\n\n          <!-- Defines whether to fade out scrollbars when they are not in use. -->\n         <attr name=\"fadeScrollbars\" format=\"boolean\" />\n         <!-- Defines the delay in milliseconds that a scrollbar takes to fade out. -->\n         <attr name=\"scrollbarFadeDuration\" format=\"integer\" />\n         <!-- Defines the delay in milliseconds that a scrollbar waits before fade out. -->\n        <attr name=\"scrollbarDefaultDelayBeforeFade\" format=\"integer\" />\n        <!-- Sets the width of vertical scrollbars and height of horizontal scrollbars. -->\n        <attr name=\"scrollbarSize\" format=\"dimension\" />\n        <!-- Defines the horizontal scrollbar thumb drawable. -->\n        <attr name=\"scrollbarThumbHorizontal\" format=\"reference\" />\n        <!-- Defines the vertical scrollbar thumb drawable. -->\n        <attr name=\"scrollbarThumbVertical\" format=\"reference\" />\n        <!-- Defines the horizontal scrollbar track drawable. -->\n        <attr name=\"scrollbarTrackHorizontal\" format=\"reference\" />\n        <!-- Defines the vertical scrollbar track drawable. -->\n        <attr name=\"scrollbarTrackVertical\" format=\"reference\" />\n        <!-- Defines whether the horizontal scrollbar track should always be drawn. -->\n        <attr name=\"scrollbarAlwaysDrawHorizontalTrack\" format=\"boolean\" />\n        <!-- Defines whether the vertical scrollbar track should always be drawn. -->\n        <attr name=\"scrollbarAlwaysDrawVerticalTrack\" format=\"boolean\" />\n\n        <!-- This attribute is ignored in API level 14\n             ({@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}) and higher.\n             Using fading edges may introduce noticeable performance\n             degradations and should be used only when required by the application's\n             visual design. To request fading edges with API level 14 and above,\n             use the <code>android:requiresFadingEdge</code> attribute instead. -->\n        <attr name=\"fadingEdge\">\n            <!-- No edge is faded. -->\n            <flag name=\"none\" value=\"0x00000000\" />\n            <!-- Fades horizontal edges only. -->\n            <flag name=\"horizontal\" value=\"0x00001000\" />\n            <!-- Fades vertical edges only. -->\n            <flag name=\"vertical\" value=\"0x00002000\" />\n        </attr>\n        <!-- Defines which edges should be faded on scrolling. -->\n        <attr name=\"requiresFadingEdge\">\n            <!-- No edge is faded. -->\n            <flag name=\"none\" value=\"0x00000000\" />\n            <!-- Fades horizontal edges only. -->\n            <flag name=\"horizontal\" value=\"0x00001000\" />\n            <!-- Fades vertical edges only. -->\n            <flag name=\"vertical\" value=\"0x00002000\" />\n        </attr>\n        <!-- Defines the length of the fading edges. -->\n        <attr name=\"fadingEdgeLength\" format=\"dimension\" />\n\n        <!-- Defines the next view to give focus to when the next focus is\n             {@link android.view.View#FOCUS_LEFT}.\n\n             If the reference refers to a view that does not exist or is part\n             of a hierarchy that is invisible, a {@link java.lang.RuntimeException}\n             will result when the reference is accessed.-->\n        <attr name=\"nextFocusLeft\" format=\"reference\"/>\n\n        <!-- Defines the next view to give focus to when the next focus is\n             {@link android.view.View#FOCUS_RIGHT}\n\n             If the reference refers to a view that does not exist or is part\n             of a hierarchy that is invisible, a {@link java.lang.RuntimeException}\n             will result when the reference is accessed.-->\n        <attr name=\"nextFocusRight\" format=\"reference\"/>\n\n        <!-- Defines the next view to give focus to when the next focus is\n             {@link android.view.View#FOCUS_UP}\n\n             If the reference refers to a view that does not exist or is part\n             of a hierarchy that is invisible, a {@link java.lang.RuntimeException}\n             will result when the reference is accessed.-->\n        <attr name=\"nextFocusUp\" format=\"reference\"/>\n\n        <!-- Defines the next view to give focus to when the next focus is\n             {@link android.view.View#FOCUS_DOWN}\n\n             If the reference refers to a view that does not exist or is part\n             of a hierarchy that is invisible, a {@link java.lang.RuntimeException}\n             will result when the reference is accessed.-->\n        <attr name=\"nextFocusDown\" format=\"reference\"/>\n\n        <!-- Defines the next view to give focus to when the next focus is\n             {@link android.view.View#FOCUS_FORWARD}\n\n             If the reference refers to a view that does not exist or is part\n             of a hierarchy that is invisible, a {@link java.lang.RuntimeException}\n             will result when the reference is accessed.-->\n        <attr name=\"nextFocusForward\" format=\"reference\"/>\n\n        <!-- Defines whether this view reacts to click events. -->\n        <attr name=\"clickable\" format=\"boolean\" />\n\n        <!-- Defines whether this view reacts to long click events. -->\n        <attr name=\"longClickable\" format=\"boolean\" />\n\n        <!--  Defines whether this view reacts to context click events. -->\n        <attr name=\"contextClickable\" format=\"boolean\" />\n\n        <!-- If false, no state will be saved for this view when it is being\n             frozen. The default is true, allowing the view to be saved\n             (however it also must have an ID assigned to it for its\n             state to be saved).  Setting this to false only disables the\n             state for this view, not for its children which may still\n             be saved. -->\n        <attr name=\"saveEnabled\" format=\"boolean\" />\n\n        <!-- Specifies whether to filter touches when the view's window is obscured by\n             another visible window.  When set to true, the view will not receive touches\n             whenever a toast, dialog or other window appears above the view's window.\n             Refer to the {@link android.view.View} security documentation for more details. -->\n        <attr name=\"filterTouchesWhenObscured\" format=\"boolean\" />\n\n        <!-- Defines the quality of translucent drawing caches. This property is used\n             only when the drawing cache is enabled and translucent. The default value is auto.\n             Deprecated: The view drawing cache was largely made obsolete with the introduction of\n             hardware-accelerated rendering in API 11. -->\n        <attr name=\"drawingCacheQuality\">\n            <!-- Lets the framework decide what quality level should be used\n                 for the drawing cache.\n                 Deprecated: The view drawing cache was largely made obsolete with the introduction\n                 of hardware-accelerated rendering in API 11. -->\n            <enum name=\"auto\" value=\"0\" />\n            <!-- Low quality. When set to low quality, the drawing cache uses a lower color\n                 depth, thus losing precision in rendering gradients, but uses less memory.\n                 Deprecated: The view drawing cache was largely made obsolete with the introduction\n                 of hardware-accelerated rendering in API 11. -->\n            <enum name=\"low\" value=\"1\" />\n            <!-- High quality. When set to high quality, the drawing cache uses a higher\n                 color depth but uses more memory.\n                 Deprecated: The view drawing cache was largely made obsolete with the introduction\n                 of hardware-accelerated rendering in API 11. -->\n            <enum name=\"high\" value=\"2\" />\n        </attr>\n\n        <!-- Controls whether the view's window should keep the screen on\n             while visible. -->\n        <attr name=\"keepScreenOn\" format=\"boolean\" />\n\n        <!-- When this attribute is set to true, the view gets its drawable state\n             (focused, pressed, etc.) from its direct parent rather than from itself. -->\n        <attr name=\"duplicateParentState\" format=\"boolean\" />\n\n        <!-- Defines the minimum height of the view. It is not guaranteed\n             the view will be able to achieve this minimum height (for example,\n             if its parent layout constrains it with less available height). -->\n        <attr name=\"minHeight\" />\n\n        <!-- Defines the minimum width of the view. It is not guaranteed\n             the view will be able to achieve this minimum width (for example,\n             if its parent layout constrains it with less available width). -->\n        <attr name=\"minWidth\" />\n\n        <!-- Boolean that controls whether a view should have sound effects\n             enabled for events such as clicking and touching. -->\n        <attr name=\"soundEffectsEnabled\" format=\"boolean\" />\n\n        <!-- Boolean that controls whether a view should have haptic feedback\n             enabled for events such as long presses. -->\n        <attr name=\"hapticFeedbackEnabled\" format=\"boolean\" />\n\n        <!-- Defines text that briefly describes content of the view. This property is used\n             primarily for accessibility. Since some views do not have textual\n             representation this attribute can be used for providing such. -->\n        <attr name=\"contentDescription\" format=\"string\" localization=\"suggested\" />\n\n        <!-- Provides brief supplemental information for the view, such as the purpose of\n             the view when that purpose is not conveyed within its textual representation.\n             This property is used primarily for accessibility. -->\n        <attr name=\"supplementalDescription\" format=\"string\" localization=\"suggested\" />\n\n        <!-- Sets the id of a view that screen readers are requested to visit after this view.\n             Requests that a screen-reader visits the content of this view before the content of the\n             one it precedes. This does nothing if either view is not important for accessibility.\n             {@see android.view.View#setAccessibilityTraversalBefore(int)} -->\n        <attr name=\"accessibilityTraversalBefore\" format=\"integer\" />\n\n        <!-- Sets the id of a view that screen readers are requested to visit before this view.\n             Requests that a screen-reader visits the content of the other view before the content\n             of this one. This does nothing if either view is not important for accessibility.\n             {@see android.view.View#setAccessibilityTraversalAfter(int)} -->\n        <attr name=\"accessibilityTraversalAfter\" format=\"integer\" />\n\n        <!-- Name of the method in this View's context to invoke when the view is\n             clicked. This name must correspond to a public method that takes\n             exactly one parameter of type View. For instance, if you specify\n             <code>android:onClick=\"sayHello\"</code>, you must declare a\n             <code>public void sayHello(View v)</code> method of your context\n             (typically, your Activity).\n             {@deprecated View actually traverses the Context\n             hierarchy looking for the relevant method, which is fragile (an intermediate\n             ContextWrapper adding a same-named method would change behavior) and restricts\n             bytecode optimizers such as R8. Instead, use View.setOnClickListener.}-->\n        <attr name=\"onClick\" format=\"string\" />\n\n        <!-- Defines over-scrolling behavior. This property is used only if the\n             View is scrollable. Over-scrolling is the ability for the user to\n             receive feedback when attempting to scroll beyond meaningful content. -->\n        <attr name=\"overScrollMode\">\n            <!-- Always show over-scroll effects, even if the content fits entirely\n                 within the available space. -->\n            <enum name=\"always\" value=\"0\" />\n            <!-- Only show over-scroll effects if the content is large\n                 enough to meaningfully scroll. -->\n            <enum name=\"ifContentScrolls\" value=\"1\" />\n            <!-- Never show over-scroll effects. -->\n            <enum name=\"never\" value=\"2\" />\n        </attr>\n\n        <!-- alpha property of the view, as a value between 0 (completely transparent) and 1\n             (completely opaque). -->\n        <attr name=\"alpha\" format=\"float\" />\n\n        <!-- base z depth of the view. -->\n        <attr name=\"elevation\" format=\"dimension\" />\n\n        <!-- translation in x of the view. This value is added post-layout to the left\n             property of the view, which is set by its layout. -->\n        <attr name=\"translationX\" format=\"dimension\" />\n\n        <!-- translation in y of the view. This value is added post-layout to the top\n             property of the view, which is set by its layout. -->\n        <attr name=\"translationY\" format=\"dimension\" />\n\n        <!-- translation in z of the view. This value is added to its elevation. -->\n        <attr name=\"translationZ\" format=\"dimension\" />\n\n        <!-- x location of the pivot point around which the view will rotate and scale.\n             This xml attribute sets the pivotX property of the View. -->\n        <attr name=\"transformPivotX\" format=\"dimension\" />\n\n        <!-- y location of the pivot point around which the view will rotate and scale.\n             This xml attribute sets the pivotY property of the View. -->\n        <attr name=\"transformPivotY\" format=\"dimension\" />\n\n        <!-- rotation of the view, in degrees. -->\n        <attr name=\"rotation\" format=\"float\" />\n\n        <!-- rotation of the view around the x axis, in degrees. -->\n        <attr name=\"rotationX\" format=\"float\" />\n\n        <!-- rotation of the view around the y axis, in degrees. -->\n        <attr name=\"rotationY\" format=\"float\" />\n\n        <!-- scale of the view in the x direction. -->\n        <attr name=\"scaleX\" format=\"float\" />\n\n        <!-- scale of the view in the y direction. -->\n        <attr name=\"scaleY\" format=\"float\" />\n\n        <!-- Determines which side the vertical scroll bar should be placed on. -->\n        <attr name=\"verticalScrollbarPosition\">\n            <!-- Place the scroll bar wherever the system default determines. -->\n            <enum name=\"defaultPosition\" value=\"0\" />\n            <!-- Place the scroll bar on the left. -->\n            <enum name=\"left\" value=\"1\" />\n            <!-- Place the scroll bar on the right. -->\n            <enum name=\"right\" value=\"2\" />\n        </attr>\n\n        <!-- Specifies the type of layer backing this view. The default value is none.\n             Refer to {@link android.view.View#setLayerType(int, android.graphics.Paint)}\n             for more information.-->\n        <attr name=\"layerType\">\n            <!-- Don't use a layer. -->\n            <enum name=\"none\" value=\"0\" />\n            <!-- Use a software layer. Refer to\n                 {@link android.view.View#setLayerType(int, android.graphics.Paint)} for\n                 more information. -->\n            <enum name=\"software\" value=\"1\" />\n            <!-- Use a hardware layer. Refer to\n                 {@link android.view.View#setLayerType(int, android.graphics.Paint)} for\n                 more information. -->\n            <enum name=\"hardware\" value=\"2\" />\n        </attr>\n\n        <!-- Defines the direction of layout drawing. This typically is associated with writing\n             direction of the language script used. The possible values are \"ltr\" for Left-to-Right,\n             \"rtl\" for Right-to-Left, \"locale\", and \"inherit\" from parent view. If there is nothing\n             to inherit, \"locale\" is used. \"locale\" falls back to \"en-US\". \"ltr\" is the direction\n             used in \"en-US\". The default for this attribute is \"inherit\". -->\n        <attr name=\"layoutDirection\">\n            <!-- Left-to-Right. -->\n            <enum name=\"ltr\" value=\"0\" />\n            <!-- Right-to-Left. -->\n            <enum name=\"rtl\" value=\"1\" />\n            <!-- Inherit from parent. -->\n            <enum name=\"inherit\" value=\"2\" />\n            <!-- Locale. -->\n            <enum name=\"locale\" value=\"3\" />\n        </attr>\n\n        <!-- Defines the direction of the text. -->\n         <attr name=\"textDirection\" format=\"integer\">\n            <!-- Default. -->\n            <enum name=\"inherit\" value=\"0\" />\n            <!-- Default for the root view. The first strong directional character determines the\n                 paragraph direction.  If there is no strong directional character, the paragraph\n                 direction is the view’s resolved layout direction. -->\n            <enum name=\"firstStrong\" value=\"1\" />\n            <!-- The paragraph direction is RTL if it contains any strong RTL character, otherwise\n                 it is LTR if it contains any strong LTR characters.  If there are neither, the\n                 paragraph direction is the view’s resolved layout direction. -->\n            <enum name=\"anyRtl\" value=\"2\" />\n            <!-- The paragraph direction is left to right. -->\n            <enum name=\"ltr\" value=\"3\" />\n            <!-- The paragraph direction is right to left. -->\n            <enum name=\"rtl\" value=\"4\" />\n            <!-- The paragraph direction is coming from the system Locale. -->\n            <enum name=\"locale\" value=\"5\" />\n            <!-- The first strong directional character determines the paragraph direction. If\n                 there is no strong directional character, the paragraph direction is LTR. -->\n            <enum name=\"firstStrongLtr\" value=\"6\" />\n            <!-- The first strong directional character determines the paragraph direction. If\n                 there is no strong directional character, the paragraph direction is RTL. -->\n            <enum name=\"firstStrongRtl\" value=\"7\" />\n        </attr>\n\n        <!-- Defines the alignment of the text. -->\n        <attr name=\"textAlignment\" format=\"integer\">\n            <!-- Default. -->\n            <enum name=\"inherit\" value=\"0\" />\n            <!-- Default for the root view. The gravity determines the alignment, ALIGN_NORMAL,\n                ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph’s\n                text direction. -->\n            <enum name=\"gravity\" value=\"1\" />\n            <!-- Align to the start of the paragraph, for example: ALIGN_NORMAL. -->\n            <enum name=\"textStart\" value=\"2\" />\n            <!-- Align to the end of the paragraph, for example: ALIGN_OPPOSITE. -->\n            <enum name=\"textEnd\" value=\"3\" />\n            <!-- Center the paragraph, for example: ALIGN_CENTER. -->\n            <enum name=\"center\" value=\"4\" />\n            <!-- Align to the start of the view, which is ALIGN_LEFT if the view’s resolved\n                layoutDirection is LTR, and ALIGN_RIGHT otherwise. -->\n            <enum name=\"viewStart\" value=\"5\" />\n            <!-- Align to the end of the view, which is ALIGN_RIGHT if the view’s resolved\n                layoutDirection is LTR, and ALIGN_LEFT otherwise. -->\n            <enum name=\"viewEnd\" value=\"6\" />\n        </attr>\n\n        <!-- Describes whether or not this view is important for accessibility.\n             If it is important, the view fires accessibility events and is\n             reported to accessibility services that query the screen. Note:\n             While not recommended, an accessibility service may decide to\n             ignore this attribute and operate on all views in the view tree. -->\n        <attr name=\"importantForAccessibility\" format=\"integer\">\n            <!-- The system determines whether the view is important for accessibility - default\n                 (recommended). -->\n            <enum name=\"auto\" value=\"0\" />\n            <!-- The view is important for accessibility. -->\n            <enum name=\"yes\" value=\"1\" />\n            <!-- The view is not important for accessibility. -->\n            <enum name=\"no\" value=\"2\" />\n            <!-- The view is not important for accessibility, nor are any of its descendant\n                 views. -->\n            <enum name=\"noHideDescendants\" value=\"4\" />\n        </attr>\n\n        <!-- Describes whether this view should allow interactions from AccessibilityServices only\n             if the service sets the isAccessibilityTool property. -->\n        <attr name=\"accessibilityDataSensitive\" format=\"integer\">\n            <!-- The system determines whether the view's accessibility data is sensitive\n                 - default (recommended). -->\n            <enum name=\"auto\" value=\"0\" />\n            <!-- Allow interactions from AccessibilityServices only if the service sets the\n                 isAccessibilityTool property. -->\n            <enum name=\"yes\" value=\"1\" />\n            <!-- Allow interactions from all AccessibilityServices, regardless of their\n                 isAccessibilityTool property. -->\n            <enum name=\"no\" value=\"2\" />\n        </attr>\n\n        <!-- Indicates to accessibility services whether the user should be notified when\n             this view changes. -->\n        <attr name=\"accessibilityLiveRegion\" format=\"integer\">\n            <!-- Accessibility services should not announce changes to this view. -->\n            <enum name=\"none\" value=\"0\" />\n            <!-- Accessibility services should announce changes to this view. -->\n            <enum name=\"polite\" value=\"1\" />\n            <!-- Accessibility services should interrupt ongoing speech to immediately\n                 announce changes to this view. -->\n            <enum name=\"assertive\" value=\"2\" />\n        </attr>\n\n        <!-- Specifies the id of a view for which this view serves as a label for\n             accessibility purposes. For example, a TextView before an EditText in\n             the UI usually specifies what information is contained in the EditText.\n             Hence, the TextView is a label for the EditText. -->\n        <attr name=\"labelFor\" format=\"reference\" />\n\n        <!-- Specifies a theme override for a view. When a theme override is set, the\n             view will be inflated using a {@link android.content.Context} themed with\n             the specified resource. During XML inflation, any child views under the\n             view with a theme override will inherit the themed context. -->\n        <attr name=\"theme\" />\n\n        <!-- Names a View such that it can be identified for Transitions. Names should be\n             unique in the View hierarchy. -->\n        <attr name=\"transitionName\" format=\"string\" />\n\n        <!-- Specifies that this view should permit nested scrolling within a compatible\n             ancestor view. -->\n        <attr name=\"nestedScrollingEnabled\" format=\"boolean\" />\n\n        <!-- Sets the state-based animator for the View. -->\n        <attr name=\"stateListAnimator\" format=\"reference\"/>\n\n        <!-- Tint to apply to the background. -->\n        <attr name=\"backgroundTint\" format=\"color\" />\n\n        <!-- Blending mode used to apply the background tint. -->\n        <attr name=\"backgroundTintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n\n        <!-- ViewOutlineProvider used to determine the View's Outline. -->\n        <attr name=\"outlineProvider\">\n            <!-- Default, background drawable-driven outline. -->\n            <enum name=\"background\" value=\"0\" />\n            <!-- No outline provider. -->\n            <enum name=\"none\" value=\"1\" />\n            <!-- Generates an opaque outline for the bounds of the view. -->\n            <enum name=\"bounds\" value=\"2\" />\n            <!-- Generates an opaque outline for the padded bounds of the view. -->\n            <enum name=\"paddedBounds\" value=\"3\" />\n        </attr>\n\n        <!-- Defines the drawable to draw over the content. This can be used as an overlay.\n             The foreground drawable participates in the padding of the content if the gravity\n             is set to fill. -->\n        <attr name=\"foreground\" format=\"reference|color\" />\n        <!-- Defines the gravity to apply to the foreground drawable. The gravity defaults\n             to fill. -->\n        <attr name=\"foregroundGravity\">\n            <!-- Push object to the top of its container, not changing its size. -->\n            <flag name=\"top\" value=\"0x30\" />\n            <!-- Push object to the bottom of its container, not changing its size. -->\n            <flag name=\"bottom\" value=\"0x50\" />\n            <!-- Push object to the left of its container, not changing its size. -->\n            <flag name=\"left\" value=\"0x03\" />\n            <!-- Push object to the right of its container, not changing its size. -->\n            <flag name=\"right\" value=\"0x05\" />\n            <!-- Place object in the vertical center of its container, not changing its size. -->\n            <flag name=\"center_vertical\" value=\"0x10\" />\n            <!-- Grow the vertical size of the object if needed so it completely fills its container. -->\n            <flag name=\"fill_vertical\" value=\"0x70\" />\n            <!-- Place object in the horizontal center of its container, not changing its size. -->\n            <flag name=\"center_horizontal\" value=\"0x01\" />\n            <!-- Grow the horizontal size of the object if needed so it completely fills its container. -->\n            <flag name=\"fill_horizontal\" value=\"0x07\" />\n            <!-- Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. -->\n            <flag name=\"center\" value=\"0x11\" />\n            <!-- Grow the horizontal and vertical size of the object if needed so it completely fills its container. -->\n            <flag name=\"fill\" value=\"0x77\" />\n            <!-- Additional option that can be set to have the top and/or bottom edges of\n                 the child clipped to its container's bounds.\n                 The clip will be based on the vertical gravity: a top gravity will clip the bottom\n                 edge, a bottom gravity will clip the top edge, and neither will clip both edges. -->\n            <flag name=\"clip_vertical\" value=\"0x80\" />\n            <!-- Additional option that can be set to have the left and/or right edges of\n                 the child clipped to its container's bounds.\n                 The clip will be based on the horizontal gravity: a left gravity will clip the right\n                 edge, a right gravity will clip the left edge, and neither will clip both edges. -->\n            <flag name=\"clip_horizontal\" value=\"0x08\" />\n        </attr>\n        <!-- Defines whether the foreground drawable should be drawn inside the padding.\n             This property is turned on by default. -->\n        <attr name=\"foregroundInsidePadding\" format=\"boolean\" />\n        <!-- Tint to apply to the foreground. -->\n        <attr name=\"foregroundTint\" format=\"color\" />\n        <!-- Blending mode used to apply the foreground tint. -->\n        <attr name=\"foregroundTintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n\n        <!-- Defines which scroll indicators should be displayed when the view\n             can be scrolled. Multiple values may be combined using logical OR,\n             for example \"top|bottom\". -->\n        <attr name=\"scrollIndicators\">\n            <!-- No scroll indicators are displayed. -->\n            <flag name=\"none\" value=\"0x00\" />\n            <!-- Displays top scroll indicator when view can be scrolled up. -->\n            <flag name=\"top\" value=\"0x01\" />\n            <!-- Displays bottom scroll indicator when vew can be scrolled down. -->\n            <flag name=\"bottom\" value=\"0x02\" />\n            <!-- Displays left scroll indicator when vew can be scrolled left. -->\n            <flag name=\"left\" value=\"0x04\" />\n            <!-- Displays right scroll indicator when vew can be scrolled right. -->\n            <flag name=\"right\" value=\"0x08\" />\n            <!-- Displays right scroll indicator when vew can be scrolled in the\n                 start direction. -->\n            <flag name=\"start\" value=\"0x10\" />\n            <!-- Displays right scroll indicator when vew can be scrolled in the\n                 end direction. -->\n            <flag name=\"end\" value=\"0x20\" />\n        </attr>\n\n        <attr name=\"pointerIcon\">\n            <!-- Null icon, pointer becomes invisible. -->\n            <enum name=\"none\" value=\"0\" />\n            <!-- The default icon of arrow pointer. -->\n            <enum name=\"arrow\" value=\"1000\" />\n            <!-- Pointer icon indicating context-menu will appear. -->\n            <enum name=\"context_menu\" value=\"1001\" />\n            <!-- Pointer icon of a hand with the index finger. -->\n            <enum name=\"hand\" value=\"1002\" />\n            <!-- Pointer icon indicating help. -->\n            <enum name=\"help\" value=\"1003\" />\n            <!-- Pointer icon indicating something is going on and waiting. -->\n            <enum name=\"wait\" value=\"1004\" />\n            <!-- Pointer icon for cell and grid. -->\n            <enum name=\"cell\" value=\"1006\" />\n            <!-- Pointer icon of crosshair, indicating to spot a location. -->\n            <enum name=\"crosshair\" value=\"1007\" />\n            <!-- Pointer icon of I-beam, usually for text. -->\n            <enum name=\"text\" value=\"1008\" />\n            <!-- Pointer icon of I-beam with 90-degree rotated, for vertical text. -->\n            <enum name=\"vertical_text\" value=\"1009\" />\n            <!-- Pointer icon of 'alias', indicating an alias of/shortcut to something is to be\n                 created. -->\n            <enum name=\"alias\" value=\"1010\" />\n            <!-- Pointer icon of 'copy', used for drag/drop. -->\n            <enum name=\"copy\" value=\"1011\" />\n            <!-- Pointer icon of 'no-drop', indicating the drop will not be accepted at the\n                 current location. -->\n            <enum name=\"no_drop\" value=\"1012\" />\n            <!-- Pointer icon of four-way arrows, indicating scrolling all direction. -->\n            <enum name=\"all_scroll\" value=\"1013\" />\n            <!-- Pointer icon of horizontal double arrow, indicating horizontal resize. -->\n            <enum name=\"horizontal_double_arrow\" value=\"1014\" />\n            <!-- Pointer icon of vertical double arrow, indicating vertical resize. -->\n            <enum name=\"vertical_double_arrow\" value=\"1015\" />\n            <!-- Pointer icon of diagonal double arrow, starting from top-right to bottom-left.\n                 Indicating freeform resize. -->\n            <enum name=\"top_right_diagonal_double_arrow\" value=\"1016\" />\n            <!-- Pointer icon of diagonal double arrow, starting from top-left to bottom-right.\n                 Indicating freeform resize. -->\n            <enum name=\"top_left_diagonal_double_arrow\" value=\"1017\" />\n            <!-- Pointer icon indicating zoom-in. -->\n            <enum name=\"zoom_in\" value=\"1018\" />\n            <!-- Pointer icon indicating zoom-out. -->\n            <enum name=\"zoom_out\" value=\"1019\" />\n            <!-- Pointer icon of a hand sign to grab something. -->\n            <enum name=\"grab\" value=\"1020\" />\n            <!-- Pointer icon of a hand sign while grabbing something. -->\n            <enum name=\"grabbing\" value=\"1021\" />\n            <!-- Pointer icon indicating handwriting. -->\n            <enum name=\"handwriting\" value=\"1022\"/>\n        </attr>\n\n        <!-- Whether this view has elements that may overlap when drawn. See\n             {@link android.view.View#forceHasOverlappingRendering(boolean)}. -->\n        <attr name=\"forceHasOverlappingRendering\" format=\"boolean\" />\n\n        <!-- Defines text displayed in a small popup window on hover or long press. -->\n        <attr name=\"tooltipText\" format=\"string\" localization=\"suggested\" />\n\n        <!-- Whether this view is a root of a keyboard navigation cluster.\n             See {@link android.view.View#setKeyboardNavigationCluster(boolean)}. -->\n        <attr name=\"keyboardNavigationCluster\" format=\"boolean\" />\n\n        <attr name=\"__removed0\" format=\"boolean\" />\n\n        <!-- Defines the next keyboard navigation cluster.\n\n             If the reference refers to a view that does not exist or is part\n             of a hierarchy that is invisible, a {@link java.lang.RuntimeException}\n             will result when the reference is accessed.-->\n        <attr name=\"nextClusterForward\" format=\"reference\"/>\n\n        <attr name=\"__removed1\" format=\"reference\"/>\n\n        <!-- Whether this view is a default-focus view.\n             Only one view per keyboard navigation cluster can have this attribute set to true.\n             See {@link android.view.View#setFocusedByDefault(boolean)}. -->\n        <attr name=\"focusedByDefault\" format=\"boolean\" />\n\n        <!-- Whether this View should use a default focus highlight when it gets focused but\n             doesn't have {@link android.R.attr#state_focused} defined in its background. -->\n        <attr name=\"defaultFocusHighlightEnabled\" format=\"boolean\" />\n\n        <!-- Whether this view should be treated as a focusable unit by screen reader accessibility\n             tools. See {@link android.view.View#setScreenReaderFocusable(boolean)}. The default\n             value, {@code false}, leaves the screen reader to consider other signals, such as\n             focusability or the presence of text, to decide what it focus.-->\n        <attr name=\"screenReaderFocusable\" format=\"boolean\" />\n\n        <!-- The title this view should present to accessibility as a pane title.\n             See {@link android.view.View#setAccessibilityPaneTitle(CharSequence)} -->\n        <attr name=\"accessibilityPaneTitle\" format=\"string\" />\n\n        <!-- Whether or not this view is a heading for accessibility purposes. -->\n        <attr name=\"accessibilityHeading\" format=\"boolean\"/>\n\n        <!-- Whether or not allow clicks on disabled view. -->\n        <attr name=\"allowClickWhenDisabled\" format=\"boolean\"/>\n\n        <!-- Sets the color of the spot shadow that is drawn when the view has a positive Z or\n             elevation value.\n             <p>\n             By default the shadow color is black. Generally, this color will be opaque so the\n             intensity of the shadow is consistent between different views with different colors.\n             <p>\n             The opacity of the final spot shadow is a function of the shadow caster height, the\n             alpha channel of the outlineSpotShadowColor (typically opaque), and the\n             {@link android.R.attr#spotShadowAlpha} theme attribute. -->\n        <attr name=\"outlineSpotShadowColor\" format=\"color\" />\n\n        <!-- Sets the color of the ambient shadow that is drawn when the view has a positive Z\n             or elevation value.\n             <p>\n             By default the shadow color is black. Generally, this color will be opaque so the\n             intensity of the shadow is consistent between different views with different colors.\n             <p>\n             The opacity of the final ambient shadow is a function of the shadow caster height,\n             the alpha channel of the outlineAmbientShadowColor (typically opaque), and the\n             {@link android.R.attr#ambientShadowAlpha} theme attribute. -->\n        <attr name=\"outlineAmbientShadowColor\" format=\"color\" />\n\n        <!-- <p>Whether or not the force dark feature is allowed to be applied to this View.\n             <p>Setting this to false will disable the auto-dark feature on this View draws\n             including any descendants.\n             <p>Setting this to true will allow this view to be automatically made dark, however\n             a value of 'true' will not override any 'false' value in its parent chain nor will\n             it prevent any 'false' in any of its children. -->\n        <attr name=\"forceDarkAllowed\" format=\"boolean\" />\n\n        <!-- <p>Whether the View's Outline should be used to clip the contents of the View.\n             <p>Only a single non-rectangular clip can be applied on a View at any time. Circular\n             clips from a\n             {@link android.view.ViewAnimationUtils#createCircularReveal(View, int, int, float,\n             float)} circular reveal animation take priority over Outline clipping, and child\n             Outline clipping takes priority over Outline clipping done by a parent.\n             <p>Note that this flag will only be respected if the View's Outline returns true from\n             {@link android.graphics.Outline#canClip()}. -->\n        <attr name=\"clipToOutline\" format=\"boolean\" />\n\n        <!-- <p> Sets a preference to keep the bounds of this view clear from floating windows\n            above this view's window. This informs the system that the view is considered a vital\n            area for the user and that ideally it should not be covered. Setting this is only\n            appropriate for UI where the user would likely take action to uncover it.\n            <p>The system will try to respect this, but when not possible will ignore it.\n            <p>This is equivalent to {@link android.view.View#setPreferKeepClear}.-->\n        <attr name=\"preferKeepClear\" format=\"boolean\" />\n\n        <!-- <p>Whether or not the auto handwriting initiation is enabled in this View.\n             <p>For a view with an active {@link android.view.inputmethod.InputConnection},\n             if auto handwriting initiation is enabled, stylus movement within its view boundary\n             will automatically trigger the handwriting mode.\n             See {@link android.view.View#setAutoHandwritingEnabled}.\n             <p>The default value of this flag is configurable by the device manufacturer. -->\n        <attr name=\"autoHandwritingEnabled\" format=\"boolean\" />\n\n        <!-- <p>The amount of offset that is applied to the left edge of the view's stylus\n             handwriting bounds, which by default is the view's visible bounds.\n\n             <p>This attribute is mainly used to enlarge the view's handwriting bounds for better\n             user experience. Note that a positive offset means the bounds is extended outwards,\n             and vice versa. See {@link android.view.View#setHandwritingBoundsOffsets}\n\n             <p> The default value is 10dp for {@link android.widget.TextView} and\n             {@link android.widget.EditText}, and 0dp for other views. -->\n        <attr name=\"handwritingBoundsOffsetLeft\" format=\"dimension\" />\n\n        <!-- <p>The amount of offset that is applied to the top edge of the view's stylus\n             handwriting bounds, which by default is the view's visible bounds.\n\n             <p>This attribute is mainly used to enlarge the view's handwriting bounds for better\n             user experience. Note that a positive offset means the bounds is extended outwards,\n             and vice versa. See {@link android.view.View#setHandwritingBoundsOffsets}\n\n             <p> The default value is 40dp for {@link android.widget.TextView} and\n             {@link android.widget.EditText}, and 0dp for other views. -->\n        <attr name=\"handwritingBoundsOffsetTop\" format=\"dimension\" />\n\n        <!-- <p>The amount of offset that is applied to the right edge of the view's stylus\n             handwriting bounds, which by default is the view's visible bounds.\n\n             <p>This attribute is mainly used to enlarge the view's handwriting bounds for better\n             user experience. Note that a positive offset means the bounds is extended outwards,\n             and vice versa. See {@link android.view.View#setHandwritingBoundsOffsets}\n\n             <p> The default value is 10dp for {@link android.widget.TextView} and\n             {@link android.widget.EditText}, and 0dp for other views. -->\n        <attr name=\"handwritingBoundsOffsetRight\" format=\"dimension\" />\n\n        <!-- <p>The amount of offset that is applied to the bottom edge of the view's stylus\n             handwriting bounds, which by default is the view's visible bounds.\n\n             <p>This attribute is mainly used to enlarge the view's handwriting bounds for better\n             user experience. Note that a positive offset means the bounds is extended outwards,\n             and vice versa. See {@link android.view.View#setHandwritingBoundsOffsets}\n\n             <p> The default value is 40dp for {@link android.widget.TextView} and\n             {@link android.widget.EditText}, and 0dp for all other views. -->\n        <attr name=\"handwritingBoundsOffsetBottom\" format=\"dimension\" />\n\n        <!-- Sets whether this view renders sensitive content. -->\n        <!-- @FlaggedApi(\"android.view.flags.sensitive_content_app_protection_api\") -->\n        <attr name=\"contentSensitivity\">\n            <!-- Let the Android System use its heuristics to determine if the view renders\n             sensitive content. -->\n            <enum name=\"auto\" value=\"0\" />\n            <!-- This view renders sensitive content. -->\n            <enum name=\"sensitive\" value=\"0x1\" />\n            <!-- This view doesn't render sensitive content. -->\n            <enum name=\"notSensitive\" value=\"0x2\" />\n        </attr>\n    </declare-styleable>\n\n    <!-- Attributes that can be assigned to a tag for a particular View. -->\n    <declare-styleable name=\"ViewTag\">\n        <!-- Specifies the key identifying a tag. This must be a resource reference. -->\n        <attr name=\"id\" />\n        <!-- Specifies the value with which to tag the view. -->\n        <attr name=\"value\" />\n    </declare-styleable>\n\n    <!-- Attributes that can be assigned to an &lt;include&gt; tag.\n         @hide -->\n    <declare-styleable name=\"Include\">\n        <attr name=\"id\" />\n        <attr name=\"visibility\" />\n    </declare-styleable>\n\n    <!-- Attributes that can be used with a {@link android.view.ViewGroup} or any\n         of its subclasses.  Also see {@link #ViewGroup_Layout} for\n         attributes that this class processes in its children. -->\n    <declare-styleable name=\"ViewGroup\">\n        <!-- Defines whether changes in layout (caused by adding and removing items) should\n             cause a LayoutTransition to run. When this flag is set to true, a default\n             LayoutTransition object will be set on the ViewGroup container and default\n             animations will run when these layout changes occur.-->\n        <attr name=\"animateLayoutChanges\" format=\"boolean\" />\n        <!-- Defines whether a child is limited to draw inside of its bounds or not.\n             This is useful with animations that scale the size of the children to more\n             than 100% for instance. In such a case, this property should be set to false\n             to allow the children to draw outside of their bounds. The default value of\n             this property is true. -->\n        <attr name=\"clipChildren\" format=\"boolean\" />\n        <!-- Defines whether the ViewGroup will clip its children and resize (but not clip) any\n             EdgeEffect to its padding, if padding is not zero. This property is set to true by\n             default. -->\n        <attr name=\"clipToPadding\" format=\"boolean\" />\n        <!-- Defines the layout animation to use the first time the ViewGroup is laid out.\n             Layout animations can also be started manually after the first layout. -->\n        <attr name=\"layoutAnimation\" format=\"reference\" />\n        <!-- Defines whether layout animations should create a drawing cache for their\n             children. Enabling the animation cache consumes more memory and requires\n             a longer initialization but provides better performance. The animation\n             cache is enabled by default. -->\n        <attr name=\"animationCache\" format=\"boolean\" />\n        <!-- Defines the persistence of the drawing cache. The drawing cache might be\n             enabled by a ViewGroup for all its children in specific situations (for\n             instance during a scrolling.) This property lets you persist the cache\n             in memory after its initial usage. Persisting the cache consumes more\n             memory but may prevent frequent garbage collection if the cache is created\n             over and over again. By default the persistence is set to scrolling.\n             Deprecated: The view drawing cache was largely made obsolete with the introduction of\n             hardware-accelerated rendering in API 11. -->\n        <attr name=\"persistentDrawingCache\">\n            <!-- The drawing cache is not persisted after use. -->\n            <flag name=\"none\" value=\"0x0\" />\n            <!-- The drawing cache is persisted after a layout animation. -->\n            <flag name=\"animation\" value=\"0x1\" />\n            <!-- The drawing cache is persisted after a scroll. -->\n            <flag name=\"scrolling\" value=\"0x2\" />\n            <!-- The drawing cache is always persisted. -->\n            <flag name=\"all\" value=\"0x3\" />\n        </attr>\n        <!-- Defines whether the ViewGroup should always draw its children using their\n             drawing cache or not. The default value is true.\n             Deprecated: The view drawing cache was largely made obsolete with the introduction of\n             hardware-accelerated rendering in API 11. -->\n        <attr name=\"alwaysDrawnWithCache\" format=\"boolean\" />\n        <!-- Sets whether this ViewGroup's drawable states also include\n             its children's drawable states.  This is used, for example, to\n             make a group appear to be focused when its child EditText or button\n             is focused. -->\n        <attr name=\"addStatesFromChildren\" format=\"boolean\" />\n\n        <!-- Defines the relationship between the ViewGroup and its descendants\n             when looking for a View to take focus. -->\n        <attr name=\"descendantFocusability\">\n            <!-- The ViewGroup will get focus before any of its descendants. -->\n            <enum name=\"beforeDescendants\" value=\"0\" />\n            <!-- The ViewGroup will get focus only if none of its descendants want it. -->\n            <enum name=\"afterDescendants\" value=\"1\" />\n            <!-- The ViewGroup will block its descendants from receiving focus. -->\n            <enum name=\"blocksDescendants\" value=\"2\" />\n        </attr>\n\n        <!-- Set to true if this ViewGroup blocks focus in the presence of a touchscreen. -->\n        <attr name=\"touchscreenBlocksFocus\" format=\"boolean\" />\n\n        <!-- Sets whether this ViewGroup should split MotionEvents\n             to separate child views during touch event dispatch.\n             If false (default prior to HONEYCOMB), touch events will be dispatched to\n             the child view where the first pointer went down until\n             the last pointer goes up.\n             If true (default for HONEYCOMB and later), touch events may be dispatched to\n             multiple children. MotionEvents for each pointer will be dispatched to the child\n             view where the initial ACTION_DOWN event happened.\n             See {@link android.view.ViewGroup#setMotionEventSplittingEnabled(boolean)}\n             for more information. -->\n        <attr name=\"splitMotionEvents\" format=\"boolean\" />\n\n        <!-- Defines the layout mode of this ViewGroup. -->\n        <attr name=\"layoutMode\">\n            <!-- Use the children's clip bounds when laying out this container. -->\n            <enum name=\"clipBounds\" value=\"0\" />\n            <!-- Use the children's optical bounds when laying out this container. -->\n            <enum name=\"opticalBounds\" value=\"1\" />\n        </attr>\n\n        <!-- Sets whether or not this ViewGroup should be treated as a single entity\n             when doing an Activity transition. Typically, the elements inside a\n             ViewGroup are each transitioned from the scene individually. The default\n             for a ViewGroup is false unless it has a background. See\n             {@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.app.Activity,\n             android.view.View, String)} for more information. Corresponds to\n             {@link android.view.ViewGroup#setTransitionGroup(boolean)}.-->\n        <attr name=\"transitionGroup\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- A {@link android.view.ViewStub} lets you lazily include other XML layouts\n         inside your application at runtime. -->\n    <declare-styleable name=\"ViewStub\">\n        <!-- Supply an identifier name for this view. -->\n        <attr name=\"id\" />\n        <!-- Supply an identifier for the layout resource to inflate when the ViewStub\n             becomes visible or when forced to do so. The layout resource must be a\n             valid reference to a layout. -->\n        <attr name=\"layout\" format=\"reference\" />\n        <!-- Overrides the id of the inflated View with this value. -->\n        <attr name=\"inflatedId\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- ===================================== -->\n    <!-- View package parent layout attributes -->\n    <!-- ===================================== -->\n    <eat-comment />\n\n    <!-- This is the basic set of layout attributes that are common to all\n         layout managers.  These attributes are specified with the rest of\n         a view's normal attributes (such as {@link android.R.attr#background},\n         but will be parsed by the view's parent and ignored by the child.\n        <p>The values defined here correspond to the base layout attribute\n        class {@link android.view.ViewGroup.LayoutParams}. -->\n    <declare-styleable name=\"ViewGroup_Layout\">\n        <!-- Specifies the basic width of the view.  This is a required attribute\n             for any view inside of a containing layout manager.  Its value may\n             be a dimension (such as \"12dip\") for a constant width or one of\n             the special constants. -->\n        <attr name=\"layout_width\" format=\"dimension\">\n            <!-- The view should be as big as its parent (minus padding).\n                 This constant is deprecated starting from API Level 8 and\n                 is replaced by {@code match_parent}. -->\n            <enum name=\"fill_parent\" value=\"-1\" />\n            <!-- The view should be as big as its parent (minus padding).\n                 Introduced in API Level 8. -->\n            <enum name=\"match_parent\" value=\"-1\" />\n            <!-- The view should be only big enough to enclose its content (plus padding). -->\n            <enum name=\"wrap_content\" value=\"-2\" />\n        </attr>\n\n        <!-- Specifies the basic height of the view.  This is a required attribute\n             for any view inside of a containing layout manager.  Its value may\n             be a dimension (such as \"12dip\") for a constant height or one of\n             the special constants. -->\n        <attr name=\"layout_height\" format=\"dimension\">\n            <!-- The view should be as big as its parent (minus padding).\n                 This constant is deprecated starting from API Level 8 and\n                 is replaced by {@code match_parent}. -->\n            <enum name=\"fill_parent\" value=\"-1\" />\n            <!-- The view should be as big as its parent (minus padding).\n                 Introduced in API Level 8. -->\n            <enum name=\"match_parent\" value=\"-1\" />\n            <!-- The view should be only big enough to enclose its content (plus padding). -->\n            <enum name=\"wrap_content\" value=\"-2\" />\n        </attr>\n    </declare-styleable>\n\n    <!-- This is the basic set of layout attributes for layout managers that\n         wish to place margins around their child views.\n         These attributes are specified with the rest of\n         a view's normal attributes (such as {@link android.R.attr#background},\n         but will be parsed by the view's parent and ignored by the child.\n        <p>The values defined here correspond to the base layout attribute\n        class {@link android.view.ViewGroup.MarginLayoutParams}. -->\n    <declare-styleable name=\"ViewGroup_MarginLayout\">\n        <attr name=\"layout_width\" />\n        <attr name=\"layout_height\" />\n        <!--  Specifies extra space on the left, top, right and bottom\n              sides of this view.  If both layout_margin and any of layout_marginLeft,\n              layout_marginRight, layout_marginStart, layout_marginEnd,\n              layout_marginTop, and layout_marginBottom are\n              also specified, the layout_margin value will take precedence over the\n              edge-specific values. This space is outside this view's bounds.\n              Margin values should be positive. -->\n        <attr name=\"layout_margin\" format=\"dimension\"  />\n        <!--  Specifies extra space on the left side of this view.\n              This space is outside this view's bounds.\n              Margin values should be positive. -->\n        <attr name=\"layout_marginLeft\" format=\"dimension\"  />\n        <!--  Specifies extra space on the top side of this view.\n              This space is outside this view's bounds.\n              Margin values should be positive.-->\n        <attr name=\"layout_marginTop\" format=\"dimension\" />\n        <!--  Specifies extra space on the right side of this view.\n              This space is outside this view's bounds.\n              Margin values should be positive.-->\n        <attr name=\"layout_marginRight\" format=\"dimension\"  />\n        <!--  Specifies extra space on the bottom side of this view.\n              This space is outside this view's bounds.\n              Margin values should be positive.-->\n        <attr name=\"layout_marginBottom\" format=\"dimension\"  />\n        <!--  Specifies extra space on the start side of this view.\n              This space is outside this view's bounds.\n              Margin values should be positive.-->\n        <attr name=\"layout_marginStart\" format=\"dimension\"  />\n        <!--  Specifies extra space on the end side of this view.\n              This space is outside this view's bounds.\n              Margin values should be positive.-->\n        <attr name=\"layout_marginEnd\" format=\"dimension\"  />\n        <!--  Specifies extra space on the left and right sides of this view.\n              Specifying layout_marginHorizontal is equivalent to specifying\n              layout_marginLeft and layout_marginRight.\n              If both layout_marginHorizontal and either/both of layout_marginLeft\n              and layout_marginRight are also specified, the layout_marginHorizontal\n              value will take precedence over the\n              edge-specific values. Also, layout_margin will always take precedence over\n              any of these values, including layout_marginHorizontal.\n              This space is outside this view's bounds.\n              Margin values should be positive.-->\n        <attr name=\"layout_marginHorizontal\" format=\"dimension\"  />\n        <!--  Specifies extra space on the top and bottom sides of this view.\n              Specifying layout_marginVertical is equivalent to specifying\n              layout_marginTop and layout_marginBottom with that same value.\n              If both layout_marginVertical and either/both of layout_marginTop and\n              layout_marginBottom are also specified, the layout_marginVertical value\n              will take precedence over the edge-specific values.\n              Also, layout_margin will always take precedence over\n              any of these values, including layout_marginVertical.\n              This space is outside this view's bounds.\n              Margin values should be positive.-->\n        <attr name=\"layout_marginVertical\" format=\"dimension\"  />\n    </declare-styleable>\n\n    <!-- Use <code>input-method</code> as the root tag of the XML resource that\n         describes an\n         {@link android.view.inputmethod.InputMethod} service, which is\n         referenced from its\n         {@link android.view.inputmethod.InputMethod#SERVICE_META_DATA}\n         meta-data entry.  Described here are the attributes that can be\n         included in that tag. -->\n    <declare-styleable name=\"InputMethod\">\n        <!-- Component name of an activity that allows the user to modify\n             the settings for this service. -->\n        <attr name=\"settingsActivity\" format=\"string\" />\n        <!-- Component name of an activity that allows the user to modify\n             on-screen keyboards variants (e.g. different language or layout) for this service. -->\n        <!-- @FlaggedApi(\"android.view.inputmethod.ime_switcher_revamp_api\") -->\n        <attr name=\"languageSettingsActivity\" format=\"string\"/>\n        <!-- Set to true in all of the configurations for which this input\n             method should be considered an option as the default. -->\n        <attr name=\"isDefault\" format=\"boolean\" />\n        <!-- Set to true if this input method supports ways to switch to\n             a next input method (for example, a globe key.). When this is true and\n             InputMethodManager#shouldOfferSwitchingToNextInputMethod() returns true,\n             the IME has to offer ways to invoke InputMethodManager#switchToNextInputMethod()\n             accordingly.\n             <p> Note that the system determines the most appropriate next input method\n             and subtype in order to provide the consistent user experience in switching\n             between IMEs and subtypes. -->\n        <attr name=\"supportsSwitchingToNextInputMethod\" format=\"boolean\" />\n        <!-- Specifies if an IME can only be used while a device is in VR mode or on a dedicated\n             device -->\n        <attr name=\"isVrOnly\" format=\"boolean\"/>\n        <!-- Specifies if an IME can only be used on a display created by a virtual device.\n             @see android.companion.virtual.VirtualDeviceParams.Builder#setInputMethodComponent\n             @hide @SystemApi -->\n        <!-- @FlaggedApi(\"android.companion.virtual.flags.vdm_custom_ime\") -->\n        <attr name=\"isVirtualDeviceOnly\" format=\"boolean\"/>\n        <attr name=\"__removed2\" format=\"boolean\" />\n        <!-- Specifies whether the IME supports showing inline suggestions. -->\n        <attr name=\"supportsInlineSuggestions\" format=\"boolean\" />\n        <!-- Specifies whether the IME supports showing inline suggestions when touch\n             exploration is enabled. This does nothing if supportsInlineSuggestions is false.\n             The default value is false and most IMEs should not set this\n             to true since the older menu-style Autofill works better with touch exploration.\n             This attribute should be set to true in special situations, such as if this is an\n             accessibility-focused IME which blocks user interaction with the app window while the\n             IME is displayed. -->\n        <attr name=\"supportsInlineSuggestionsWithTouchExploration\" format=\"boolean\" />\n        <!-- Specifies whether the IME suppresses system spell checker.\n             The default value is false. If an IME sets this attribute to true,\n             the system spell checker will be disabled while the IME has an\n             active input session. -->\n        <attr name=\"suppressesSpellChecker\" format=\"boolean\" />\n        <!-- Specifies whether the IME wants to be shown in the Input Method picker. Defaults to\n             true. Set this to false if the IME is intended to be accessed programmatically.\n             <p>\n             Note: This functions as a hint to the system, which may choose to ignore this\n             preference in certain situations or in future releases.-->\n        <attr name=\"showInInputMethodPicker\" format=\"boolean\" />\n        <!-- Specify one or more configuration changes that the IME will handle itself. If not\n             specified, the IME will be restarted if any of these configuration changes happen in\n              the system.  Otherwise, the IME will remain running and its\n             {@link android.inputmethodservice.InputMethodService#onConfigurationChanged}\n             method is called with the new configuration.\n             <p>Note that all of these configuration changes can impact the\n             resource values seen by the application, so you will generally need\n             to re-retrieve all resources (including view layouts, drawables, etc)\n             to correctly handle any configuration change.-->\n        <attr name=\"configChanges\" />\n        <!-- Specifies whether the IME supports Handwriting using stylus. Defaults to false.\n            When IME implements support for stylus handwriting, on every ACTION_DOWN with stylus\n            on an editor,\n            {@link android.inputmethodservice.InputMethodService#onStartStylusHandwriting()}\n            is called.\n            If IME is ready for stylus input, it must return {@code true} for Handwriting sessions\n            to start. IME should attach it's View that renders Ink on screen to stylus handwriting\n            inking window\n            {@link android.inputmethodservice.InputMethodService#getStylusHandwritingWindow()}.\n            IME will then receive Stylus MotionEvent(s) on DecorView i.e. the Inking view\n            {@link android.view.View#onTouchEvent(MotionEvent)} attached by IME to Ink window.\n            Handwriting mode can be finished by calling\n            {@link android.inputmethodservice.InputMethodService#finishStylusHandwriting()} or will\n            be finished by framework on next\n            {@link android.inputmethodservice.InputMethodService#onFinishInput()}.\n        -->\n        <attr name=\"supportsStylusHandwriting\" format=\"boolean\" />\n        <!-- Specifies whether the IME supports connectionless stylus handwriting sessions. A\n             connectionless session differs from a regular session in that the IME does not use an\n             input connection to communicate with a text editor. Instead, the IME directly returns\n             recognised handwritten text via an {@link\n             android.inputmethodservice.InputMethodService} handwriting lifecycle API.\n\n             <p>If the IME supports connectionless sessions, apps or framework may start a\n             connectionless session when a stylus motion event sequence begins. {@link\n             android.inputmethodservice.InputMethodService#onStartConnectionlessStylusHandwriting}\n             is called. If the IME is ready for stylus input, it should return {code true} to start\n             the basic mode session. As in the regular session, the IME will receive stylus motion\n             events to the stylus handwriting window and should render ink to a view in this window.\n             When the user has stopped handwriting, the IME should end the session and deliver the\n             result by calling {@link\n             android.inputmethodservice.InputMethodService#finishConnectionlessStylusHandwriting}.\n\n             The default value is {code false}. If {code true}, {@link\n             android.R.attr#supportsStylusHandwriting} should also be {code true}.\n        -->\n        <attr name=\"supportsConnectionlessStylusHandwriting\" format=\"boolean\" />\n        <!-- Class name of an activity that allows the user to modify the stylus handwriting\n            settings for this service -->\n        <attr name=\"stylusHandwritingSettingsActivity\" format=\"string\" />\n\n    </declare-styleable>\n\n    <!-- This is the subtype of InputMethod. Subtype can describe locales (for example, en_US and\n         fr_FR) and modes (for example, voice and keyboard), and is used for IME switch. This\n         subtype allows the system to call the specified subtype of the IME directly. -->\n    <declare-styleable name=\"InputMethod_Subtype\">\n        <!-- The name of the subtype. -->\n        <attr name=\"label\" />\n        <!-- The layout label of the subtype.\n             {@link android.view.inputmethod.InputMethodSubtype#getLayoutDisplayName} returns the\n             value specified in this attribute. -->\n        <attr name=\"layoutLabel\" format=\"reference\" />\n        <!-- The icon of the subtype. -->\n        <attr name=\"icon\" />\n        <!-- The locale of the subtype. This string should be a locale (for example en_US and fr_FR)\n             and will be passed to the IME when the framework calls the IME\n             with the subtype. This is also used by the framework to know the supported locales\n             of the IME.  -->\n        <attr name=\"imeSubtypeLocale\" format=\"string\" />\n        <!-- The mode of the subtype. This string can be a mode (for example, voice and keyboard)\n             and this string will be passed to the IME when the framework calls the IME with the\n             subtype.  {@link android.view.inputmethod.InputMethodSubtype#getLocale()} returns the\n             value specified in this attribute.  -->\n        <attr name=\"imeSubtypeMode\" format=\"string\" />\n        <!-- Set true if the subtype is auxiliary.  An auxiliary subtype won't be shown in the\n             input method selection list in the settings app.\n             InputMethodManager#switchToLastInputMethod will ignore auxiliary subtypes when it\n             chooses a target subtype. -->\n        <attr name=\"isAuxiliary\" format=\"boolean\" />\n        <!-- Set true when this subtype should be selected by default if no other subtypes are\n             selected explicitly. Note that a subtype with this parameter being true will\n             not be shown in the subtypes list. -->\n        <attr name=\"overridesImplicitlyEnabledSubtype\" format=\"boolean\" />\n        <!-- The extra value of the subtype. This string can be any string and will be passed to\n             the IME when the framework calls the IME with the subtype.  -->\n        <attr name=\"imeSubtypeExtraValue\" format=\"string\" />\n        <!-- The unique id for the subtype. The input method framework keeps track of enabled\n             subtypes by ID. When the IME package gets upgraded, enabled IDs will stay enabled even\n             if other attributes are different. If the ID is unspecified (by calling the other\n             constructor or 0. Arrays.hashCode(new Object[] {locale, mode, extraValue,\n             isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead. -->\n        <attr name=\"subtypeId\" format=\"integer\"/>\n        <!-- Set to {@code true} if this subtype is ASCII capable. If the subtype is ASCII\n             capable, it should guarantee that the user can input ASCII characters with\n             this subtype. This is important because many password fields only allow\n             ASCII-characters.\n\n             <p>Note: In order to avoid some known system issues on\n             {@link android.os.Build.VERSION_CODES#P} and prior OSes, you may want to include\n             {@code \"AsciiCapable\"} in\n             {@link android.R.styleable#InputMethod_Subtype_imeSubtypeExtraValue} when you specify\n             {@code true} to this attribute.-->\n        <attr name=\"isAsciiCapable\" format=\"boolean\" />\n        <!-- The BCP-47 Language Tag of the subtype.  This replaces\n        {@link android.R.styleable#InputMethod_Subtype_imeSubtypeLocale}.  -->\n        <attr name=\"languageTag\" format=\"string\" />\n        <!-- The BCP-47 Language Tag of the preferred physical keyboard of the subtype. If it's not\n             specified, {@link android.R.styleable#InputMethod_Subtype_languageTag} will be used.\n             See also\n             {@link android.view.inputmethod.InputMethodSubtype#getPhysicalKeyboardHintLanguageTag}.\n             -->\n        <attr name=\"physicalKeyboardHintLanguageTag\" format=\"string\" />\n        <!-- The layout type of the preferred physical keyboard of the subtype.\n             It matches the layout type string in the keyboard layout definition. See also\n             {@link android.view.inputmethod.InputMethodSubtype#getPhysicalKeyboardHintLayoutType}.\n             -->\n        <attr name=\"physicalKeyboardHintLayoutType\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- Use <code>spell-checker</code> as the root tag of the XML resource that\n         describes an\n         {@link android.service.textservice.SpellCheckerService} service, which is\n         referenced from its\n         {@link android.view.textservice.SpellCheckerSession#SERVICE_META_DATA}\n         meta-data entry.  Described here are the attributes that can be\n         included in that tag. -->\n    <declare-styleable name=\"SpellChecker\">\n        <!-- The name of the spell checker. -->\n        <attr name=\"label\" />\n        <!-- Component name of an activity that allows the user to modify\n             the settings for this service. -->\n        <attr name=\"settingsActivity\"/>\n    </declare-styleable>\n\n    <!-- This is the subtype of the spell checker. Subtype can describe locales (for example,\n             en_US and fr_FR). -->\n    <declare-styleable name=\"SpellChecker_Subtype\">\n        <!-- The name of the subtype. -->\n        <attr name=\"label\" />\n        <!-- The locale of the subtype. This string should be a locale (for example, en_US and\n             fr_FR). This is also used by the framework to know the supported locales\n             of the spell checker. {@link android.view.textservice.SpellCheckerSubtype#getLocale()}\n             returns the value specified in this attribute.  -->\n        <attr name=\"subtypeLocale\" format=\"string\" />\n        <!-- The extra value of the subtype. This string can be any string and will be passed to\n             the SpellChecker.  -->\n        <attr name=\"subtypeExtraValue\" format=\"string\" />\n        <!-- The unique id for the subtype. The text service (spell checker) framework keeps track\n             of enabled subtypes by ID. When the spell checker package gets upgraded, enabled IDs\n             will stay enabled even if other attributes are different. If the ID is unspecified or\n             explicitly specified to 0 in XML resources,\n             {@code Arrays.hashCode(new Object[] {subtypeLocale, extraValue})} will be used instead.\n              -->\n        <attr name=\"subtypeId\" />\n        <!-- The BCP-47 Language Tag of the subtype.  This replaces\n        {@link android.R.styleable#SpellChecker_Subtype_subtypeLocale}.  -->\n        <attr name=\"languageTag\" />\n    </declare-styleable>\n\n    <!-- Use <code>accessibility-service</code> as the root tag of the XML resource that\n         describes an {@link android.accessibilityservice.AccessibilityService} service,\n         which is referenced from its\n         {@link android.accessibilityservice.AccessibilityService#SERVICE_META_DATA}\n         meta-data entry. -->\n    <declare-styleable name=\"AccessibilityService\">\n        <!-- The event types this service would like to receive as specified in\n             {@link android.view.accessibility.AccessibilityEvent}. This setting\n             can be changed at runtime by calling\n             {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)\n             android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->\n        <attr name=\"accessibilityEventTypes\">\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED} events.-->\n            <flag name=\"typeViewClicked\" value=\"0x00000001\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_LONG_CLICKED} events. -->\n            <flag name=\"typeViewLongClicked\" value=\"0x00000002\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SELECTED} events. -->\n            <flag name=\"typeViewSelected\" value=\"0x00000004\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED} events. -->\n            <flag name=\"typeViewFocused\" value=\"0x00000008\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED} events. -->\n            <flag name=\"typeViewTextChanged\" value=\"0x00000010\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED} events. -->\n            <flag name=\"typeWindowStateChanged\" value=\"0x00000020\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED} events. -->\n            <flag name=\"typeNotificationStateChanged\" value=\"0x00000040\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_ENTER} events. -->\n            <flag name=\"typeViewHoverEnter\" value=\"0x00000080\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_EXIT} events. -->\n            <flag name=\"typeViewHoverExit\" value=\"0x00000100\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START} events. -->\n            <flag name=\"typeTouchExplorationGestureStart\" value=\"0x00000200\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END} events. -->\n            <flag name=\"typeTouchExplorationGestureEnd\" value=\"0x00000400\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} events. -->\n            <flag name=\"typeWindowContentChanged\" value=\"0x00000800\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED} events. -->\n            <flag name=\"typeViewScrolled\" value=\"0x000001000\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED} events. -->\n            <flag name=\"typeViewTextSelectionChanged\" value=\"0x000002000\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_ANNOUNCEMENT} events. -->\n            <flag name=\"typeAnnouncement\" value=\"0x00004000\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED} events. -->\n            <flag name=\"typeViewAccessibilityFocused\" value=\"0x00008000\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED} events. -->\n            <flag name=\"typeViewAccessibilityFocusCleared\" value=\"0x00010000\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY} events. -->\n            <flag name=\"typeViewTextTraversedAtMovementGranularity\" value=\"0x00020000\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_GESTURE_DETECTION_START} events. -->\n            <flag name=\"typeGestureDetectionStart\" value=\"0x00040000\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_GESTURE_DETECTION_END} events. -->\n            <flag name=\"typeGestureDetectionEnd\" value=\"0x00080000\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_INTERACTION_START} events. -->\n            <flag name=\"typeTouchInteractionStart\" value=\"0x00100000\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_INTERACTION_END} events. -->\n            <flag name=\"typeTouchInteractionEnd\" value=\"0x00200000\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED} events. -->\n            <flag name=\"typeWindowsChanged\" value=\"0x00400000\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CONTEXT_CLICKED} events. -->\n            <flag name=\"typeContextClicked\" value=\"0x00800000\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_ASSIST_READING_CONTEXT} events. -->\n            <flag name=\"typeAssistReadingContext\" value=\"0x01000000\" />\n            <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPES_ALL_MASK} i.e. all events. -->\n            <flag name=\"typeAllMask\" value=\"0xffffffff\" />\n        </attr>\n        <!-- Comma separated package names from which this service would like to receive events (leave out for all packages).\n             {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)\n             android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->\n        <attr name=\"packageNames\" format=\"string\" />\n        <!-- The feedback types this service provides as specified in\n             {@link android.accessibilityservice.AccessibilityServiceInfo}. This setting\n             can be changed at runtime by calling\n             {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)\n             android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->\n        <attr name=\"accessibilityFeedbackType\">\n            <!-- Provides {@link android.accessibilityservice.AccessibilityServiceInfo#FEEDBACK_SPOKEN} feedback. -->\n            <flag name=\"feedbackSpoken\" value=\"0x00000001\" />\n            <!-- Provides {@link android.accessibilityservice.AccessibilityServiceInfo#FEEDBACK_HAPTIC} feedback. -->\n            <flag name=\"feedbackHaptic\" value=\"0x00000002\" />\n            <!-- Provides {@link android.accessibilityservice.AccessibilityServiceInfo#FEEDBACK_AUDIBLE} feedback. -->\n            <flag name=\"feedbackAudible\" value=\"0x00000004\" />\n            <!-- Provides {@link android.accessibilityservice.AccessibilityServiceInfo#FEEDBACK_VISUAL} feedback. -->\n            <flag name=\"feedbackVisual\" value=\"0x00000008\" />\n            <!-- Provides {@link android.accessibilityservice.AccessibilityServiceInfo#FEEDBACK_GENERIC} feedback. -->\n            <flag name=\"feedbackGeneric\" value=\"0x00000010\" />\n            <!-- Provides {@link android.accessibilityservice.AccessibilityServiceInfo#FEEDBACK_ALL_MASK} feedback. -->\n            <flag name=\"feedbackAllMask\" value=\"0xffffffff\" />\n        </attr>\n        <!-- The minimal period in milliseconds between two accessibility events of the same type\n             are sent to this service. This setting can be changed at runtime by calling\n             {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)\n             android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->\n        <attr name=\"notificationTimeout\" format=\"integer\" />\n        <!-- A recommended timeout in milliseconds used in\n             {@link android.view.accessibility.AccessibilityManager#getRecommendedTimeoutMillis(int, int)\n             android.view.accessibility.AccessibilityManager.getRecommendedTimeoutMillis(int, int)}\n             to return a suitable value for UIs that do not include interactive controls.\n             This setting can be changed at runtime by calling\n             {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)\n             android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->\n        <attr name=\"nonInteractiveUiTimeout\" format=\"integer\" />\n        <!-- A recommended timeout in milliseconds used in\n             {@link android.view.accessibility.AccessibilityManager#getRecommendedTimeoutMillis(int, int)\n             android.view.accessibility.AccessibilityManager.getRecommendedTimeoutMillis(int, int)}\n             to return a suitable value for interactive controls.\n             This setting can be changed at runtime by calling\n             {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)\n             android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->\n        <attr name=\"interactiveUiTimeout\" format=\"integer\" />\n        <!-- Additional flags as specified in\n             {@link android.accessibilityservice.AccessibilityServiceInfo}.\n             This setting can be changed at runtime by calling\n             {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)\n             android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->\n        <attr name=\"accessibilityFlags\">\n            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#DEFAULT}. -->\n            <flag name=\"flagDefault\" value=\"0x00000001\" />\n            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS}. -->\n            <flag name=\"flagIncludeNotImportantViews\" value=\"0x00000002\" />\n            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE}. -->\n            <flag name=\"flagRequestTouchExplorationMode\" value=\"0x00000004\" />\n            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY}.\n                 Not used by the framework.\n            -->\n            <flag name=\"flagRequestEnhancedWebAccessibility\" value=\"0x00000008\" />\n            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}. -->\n            <flag name=\"flagReportViewIds\" value=\"0x00000010\" />\n            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_FILTER_KEY_EVENTS}. -->\n            <flag name=\"flagRequestFilterKeyEvents\" value=\"0x00000020\" />\n            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}. -->\n            <flag name=\"flagRetrieveInteractiveWindows\" value=\"0x00000040\" />\n            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_ENABLE_ACCESSIBILITY_VOLUME}. -->\n            <flag name=\"flagEnableAccessibilityVolume\" value=\"0x00000080\" />\n            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_ACCESSIBILITY_BUTTON}. -->\n            <flag name=\"flagRequestAccessibilityButton\" value=\"0x00000100\" />\n            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_FINGERPRINT_GESTURES}. -->\n            <flag name=\"flagRequestFingerprintGestures\" value=\"0x00000200\" />\n            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK}. -->\n            <flag name=\"flagRequestShortcutWarningDialogSpokenFeedback\" value=\"0x00000400\" />\n            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_SERVICE_HANDLES_DOUBLE_TAP}. -->\n            <flag name=\"flagServiceHandlesDoubleTap\" value=\"0x00000800\" />\n            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_MULTI_FINGER_GESTURES}. -->\n            <flag name=\"flagRequestMultiFingerGestures\" value=\"0x00001000\" />\n            <flag name=\"flagSendMotionEvents\" value=\"0x0004000\" />\n            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_INPUT_METHOD_EDITOR}. -->\n            <flag name=\"flagInputMethodEditor\" value=\"0x0008000\" />\n        </attr>\n        <!-- Fully qualified class name of an activity that allows the user to modify the settings\n             for this service. This setting cannot be changed at runtime. -->\n        <attr name=\"settingsActivity\" />\n        <!-- Fully qualified class name of {@link android.service.quicksettings.TileService} is\n             associated with this accessibility service for one to one mapping. It is used by system\n             settings to remind users this accessibility service has a\n             {@link android.service.quicksettings.TileService}. -->\n        <attr name=\"tileService\" format=\"string\" />\n        <!-- Attribute whether the accessibility service wants to be able to retrieve the\n             active window content. This setting cannot be changed at runtime.\n             <p>\n             Required to allow setting the {@link android.accessibilityservice\n             #AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag.\n             </p>\n         -->\n        <attr name=\"canRetrieveWindowContent\" format=\"boolean\" />\n        <!-- Attribute whether the accessibility service wants to be able to request touch\n             exploration mode in which touched items are spoken aloud and the UI can be\n             explored via gestures.\n             <p>\n             Required to allow setting the {@link android.accessibilityservice\n             #AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} flag.\n             </p>\n         -->\n        <attr name=\"canRequestTouchExplorationMode\" format=\"boolean\" />\n        <!-- Attribute whether the accessibility service wants to be able to request enhanced\n             web accessibility enhancements.\n             {@deprecated Not used by the framework}\n         -->\n        <attr name=\"canRequestEnhancedWebAccessibility\" format=\"boolean\" />\n        <!-- Attribute whether the accessibility service wants to be able to request to\n             filter key events.\n             <p>\n             Required to allow setting the {@link android.accessibilityservice\n             #AccessibilityServiceInfo#FLAG_REQUEST_FILTER_KEY_EVENTS} flag.\n             </p>\n         -->\n        <attr name=\"canRequestFilterKeyEvents\" format=\"boolean\" />\n        <!-- Attribute whether the accessibility service wants to be able to control\n             display magnification.\n         -->\n        <attr name=\"canControlMagnification\" format=\"boolean\" />\n        <!-- Attribute whether the accessibility service wants to be able to perform gestures. -->\n        <attr name=\"canPerformGestures\" format=\"boolean\" />\n        <!-- Attribute whether the accessibility service wants to be able to capture gestures from\n             the fingerprint sensor.\n             <p>\n             Required to allow setting the {@link android.accessibilityservice\n             #AccessibilityServiceInfo#FLAG_REQUEST_FINGERPRINT_GESTURES} flag to have any effect.\n             </p>\n         -->\n        <attr name=\"canRequestFingerprintGestures\" format=\"boolean\" />\n        <!-- Attribute whether the accessibility service wants to be able to take screenshot. -->\n        <attr name=\"canTakeScreenshot\" format=\"boolean\" />\n\n        <!-- Attribute indicating whether the accessibility service is used to assist users with\n             disabilities. This criteria might be defined by the installer. The default is false.\n             <p>\n             Note: If this flag is false, system will show a notification after a duration to\n             inform the user about the privacy implications of the service.\n             </p>\n        -->\n        <attr name=\"isAccessibilityTool\" format=\"boolean\" />\n\n        <!-- Animated image of the accessibility service purpose or behavior, to help users\n             understand how the service can help them. -->\n        <attr name=\"animatedImageDrawable\" format=\"reference\"/>\n        <!-- Html description of the accessibility service usage, availability, or limitations (e.g.\n             isn't supported by all apps). -->\n        <attr name=\"htmlDescription\" format=\"reference\"/>\n        <!-- Description of the accessibility service usage, availability, or limitations (e.g.\n             isn't supported by all apps). -->\n        <attr name=\"description\" />\n        <!-- Brief summary of the accessibility service purpose or behavior. -->\n        <attr name=\"summary\" />\n        <!-- Detailed intro of the accessibility service purpose or behavior. -->\n        <attr name=\"intro\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- Use <code>accessibility-shortcut-target</code> as the root tag of the XML resource that\n         describes an activity, which is referenced from the\n         <code>android.accessibilityshortcut.target</code> meta-data entry. -->\n    <declare-styleable name=\"AccessibilityShortcutTarget\">\n        <!-- Description of the target of accessibility shortcut usage, availability, or limitations\n             (e.g. isn't supported by all apps). -->\n        <attr name=\"description\" />\n        <!-- Brief summary of the target of accessibility shortcut purpose or behavior. -->\n        <attr name=\"summary\" />\n        <!-- Animated image of the target of accessibility shortcut purpose or behavior, to help\n             users understand how the target of accessibility shortcut can help them.-->\n        <attr name=\"animatedImageDrawable\" format=\"reference\"/>\n        <!-- Html description of the target of accessibility shortcut usage, availability, or\n             limitations (e.g. isn't supported by all apps). -->\n        <attr name=\"htmlDescription\" format=\"reference\"/>\n        <!-- Fully qualified class name of an activity that allows the user to modify the settings\n             for this target of accessibility shortcut. -->\n        <attr name=\"settingsActivity\" />\n        <!-- Fully qualified class name of {@link android.service.quicksettings.TileService} is\n             associated with this accessibility shortcut target for one to one mapping. It is used\n             by system settings to remind users this accessibility service has a\n             {@link android.service.quicksettings.TileService}. -->\n        <attr name=\"tileService\" format=\"string\" />\n        <!-- Detailed intro of the target of accessibility shortcut purpose or behavior. -->\n        <attr name=\"intro\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- Use <code>print-service</code> as the root tag of the XML resource that\n         describes an {@link android.printservice.PrintService} service, which is\n         referenced from its {@link android.printservice.PrintService#SERVICE_META_DATA}\n         meta-data entry. -->\n    <declare-styleable name=\"PrintService\">\n        <!-- Fully qualified class name of an activity that allows the user to modify\n             the settings for this service. -->\n        <attr name=\"settingsActivity\" />\n        <!-- Fully qualified class name of an activity that allows the user to manually\n             add printers to this print service. -->\n        <attr name=\"addPrintersActivity\" format=\"string\"/>\n        <!-- Fully qualified class name of an activity with advanced print options\n             specific to this print service. -->\n        <attr name=\"advancedPrintOptionsActivity\" format=\"string\"/>\n        <!-- The vendor name if this print service is vendor specific. -->\n        <attr name=\"vendor\" format=\"string\"/>\n    </declare-styleable>\n\n    <!-- Use <code>host-apdu-service</code> as the root tag of the XML resource that\n         describes an {@link android.nfc.cardemulation.HostApduService} service, which\n         is referenced from its {@link android.nfc.cardemulation.HostApduService#SERVICE_META_DATA}\n         entry. -->\n    <declare-styleable name=\"HostApduService\">\n        <!-- Short description of the functionality the service implements. This attribute\n             is mandatory.-->\n        <attr name=\"description\" />\n        <!-- Whether the device must be unlocked before routing data to this service.\n             The default is false.-->\n        <attr name=\"requireDeviceUnlock\" format=\"boolean\"/>\n        <!-- A drawable that can be rendered in Android's system UI for representing\n             the service. -->\n        <attr name=\"apduServiceBanner\" format=\"reference\"/>\n        <!-- Component name of an activity that allows the user to modify\n             the settings for this service. -->\n        <attr name=\"settingsActivity\"/>\n        <!-- Whether the device must be screen on before routing data to this service.\n             The default is true.-->\n        <attr name=\"requireDeviceScreenOn\" format=\"boolean\"/>\n        <!-- Whether the device should default to observe mode when this service is\n             default or in the foreground. -->\n        <attr name=\"shouldDefaultToObserveMode\" format=\"boolean\"/>\n        <!-- Whether this service should share the same AID routing priority as the role\n             owner. This package and the role owner must have the same signature, and the\n             role owner must opt into this behavior by using the property named by\n             {@link android.nfc.cardemulation.CardEmulation.PROPERTY_ALLOW_SHARED_ROLE_PRIORITY }\n             in the <code>&lt;application&rt;</code> tag. -->\n        <attr name=\"wantsRoleHolderPriority\" format=\"boolean\"/>\n    </declare-styleable>\n\n    <!-- Use <code>offhost-apdu-service</code> as the root tag of the XML resource that\n         describes an {@link android.nfc.cardemulation.OffHostApduService}\n         service, which is referenced from its\n         {@link android.nfc.cardemulation.OffHostApduService#SERVICE_META_DATA} entry. -->\n    <declare-styleable name=\"OffHostApduService\">\n        <!-- Short description of the functionality the service implements. This attribute\n             is mandatory.-->\n        <attr name=\"description\" />\n        <!-- A drawable that can be rendered in Android's system UI for representing\n             the service. -->\n        <attr name=\"apduServiceBanner\"/>\n        <!-- Component name of an activity that allows the user to modify\n             the settings for this service. -->\n        <attr name=\"settingsActivity\"/>\n        <!-- Secure Element which the AIDs should be routed to -->\n        <attr name=\"secureElementName\" format=\"string\"/>\n        <!-- Whether the device must be unlocked before routing data to this service.\n             The default is false.-->\n        <attr name=\"requireDeviceUnlock\"/>\n        <!-- Whether the device must be screen on before routing data to this service.\n             The default is false.-->\n        <attr name=\"requireDeviceScreenOn\"/>\n        <!-- Whether the device should default to observe mode when this service is\n             default or in the foreground. -->\n        <attr name=\"shouldDefaultToObserveMode\"/>\n        <attr name=\"wantsRoleHolderPriority\"/>\n    </declare-styleable>\n\n    <!-- Specify one or more <code>aid-group</code> elements inside a\n         <code>host-apdu-service</code> or <code>offhost-apdu-service</code>\n         element to define a group of ISO7816 Application ID (AIDs) that\n         your service can handle.-->\n    <declare-styleable name=\"AidGroup\">\n        <!-- Short description of what the AID group implements. This attribute is mandatory.-->\n        <attr name=\"description\" />\n        <!-- The category attribute will be used by the Android platform to present\n             multiple applications that register ISO 7816 Application IDs (AIDs) in the\n             same category uniformly.\n             Additionally, when a category is specified, Android will ensure that either\n             all AIDs in this group are routed to this application, or none at all.\n             This attribute is optional.-->\n        <attr name=\"category\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- Specify one or more <code>aid-filter</code> elements inside a\n         <code>aid-group</code> element to specify an ISO7816 Application ID (AID)\n         your service can handle. -->\n    <declare-styleable name=\"AidFilter\">\n        <!-- The ISO7816 Application ID. This attribute is mandatory. -->\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- Specify one or more <code>aid-prefix-filter</code> elements inside a\n         <code>aid-group</code> element to specify an ISO7816 Application ID (AID)\n         prefix your service can handle. -->\n    <declare-styleable name=\"AidPrefixFilter\">\n        <!-- The ISO7816 Application ID. This attribute is mandatory. -->\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- Specify one or more <code>polling-loop-filter</code> elements inside a\n         <code>host-apdu-service</code> or <code>offhost-apdu-service</code> to indicate polling\n         loop frames that your service can handle. -->\n    <!-- @FlaggedApi(\"android.nfc.nfc_read_polling_loop\") -->\n    <declare-styleable name=\"PollingLoopFilter\">\n        <!-- The polling loop frame. This attribute is mandatory. -->\n        <attr name=\"name\" />\n        <!-- Whether or not the system should automatically start a transaction when this polling\n         loop filter matches. If not set, default value is false. -->\n        <!-- @FlaggedApi(\"android.nfc.nfc_read_polling_loop\") -->\n        <attr name=\"autoTransact\" format=\"boolean\"/>\n    </declare-styleable>\n\n    <!-- Specify one or more <code>polling-loop-pattern-filter</code> elements inside a\n         <code>host-apdu-service</code> or <code>offhost-apdu-service</code>  to indicate polling\n         loop frames that your service can handle. -->\n    <!-- @FlaggedApi(\"android.nfc.nfc_read_polling_loop\") -->\n    <declare-styleable name=\"PollingLoopPatternFilter\">\n        <!-- The patter to match polling loop frames to, must to be compatible with\n         {@link java.util.regex.Pattern#compile(String)} and only contain hexadecimal numbers and\n         `.`, `?` and `*` operators. This attribute is mandatory. -->\n        <attr name=\"name\" />\n        <!-- Whether or not the system should automatically start a transaction when this polling\n         loop filter matches. If not set, default value is false. -->\n        <!-- @FlaggedApi(\"android.nfc.nfc_read_polling_loop\") -->\n        <attr name=\"autoTransact\" format=\"boolean\"/>\n    </declare-styleable>\n\n    <!-- Use <code>host-nfcf-service</code> as the root tag of the XML resource that\n         describes an {@link android.nfc.cardemulation.HostNfcFService} service, which\n         is referenced from its {@link android.nfc.cardemulation.HostNfcFService#SERVICE_META_DATA}\n         entry. -->\n    <declare-styleable name=\"HostNfcFService\">\n        <!-- Short description of the functionality the service implements. This attribute\n             is mandatory.-->\n        <attr name=\"description\" />\n    </declare-styleable>\n\n    <!-- Specify one or more <code>system-code-filter</code> elements inside a\n         <code>host-nfcf-service</code> element to specify a System Code\n         your service can handle. -->\n    <declare-styleable name=\"SystemCodeFilter\">\n        <!-- The System Code. This attribute is mandatory. -->\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- Specify one or more <code>nfcid2-filter</code> elements inside a\n         <code>host-nfcf-service</code> element to specify a NFCID2\n         your service can handle. -->\n    <declare-styleable name=\"Nfcid2Filter\">\n        <!-- The NFCID2. This attribute is mandatory. -->\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- Specify one or more <code>t3tPmm-filter</code> elements inside a\n         <code>host-nfcf-service</code> element to specify a LF_T3T_PMM. -->\n    <declare-styleable name=\"T3tPmmFilter\">\n        <attr name=\"name\" />\n\n    </declare-styleable>\n\n    <declare-styleable name=\"ActionMenuItemView\">\n        <attr name=\"minWidth\" />\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- Widget package class attributes -->\n    <!-- =============================== -->\n    <eat-comment />\n\n    <declare-styleable name=\"AbsListView\">\n         <!-- Drawable used to indicate the currently selected item in the list. -->\n        <attr name=\"listSelector\" format=\"color|reference\" />\n        <!-- When set to true, the selector will be drawn over the selected item.\n             Otherwise the selector is drawn behind the selected item. The default\n             value is false. -->\n        <attr name=\"drawSelectorOnTop\" format=\"boolean\" />\n        <!-- Used by ListView and GridView to stack their content from the bottom. -->\n        <attr name=\"stackFromBottom\" format=\"boolean\" />\n        <!-- When set to true, the list uses a drawing cache during scrolling.\n             This makes the rendering faster but uses more memory. The default\n             value is true. -->\n        <attr name=\"scrollingCache\" format=\"boolean\" />\n        <!-- When set to true, the list will filter results as the user types. The\n             List's adapter must support the Filterable interface for this to work. -->\n        <attr name=\"textFilterEnabled\" format=\"boolean\" />\n        <!-- Sets the transcript mode for the list. In transcript mode, the list\n             scrolls to the bottom to make new items visible when they are added. -->\n        <attr name=\"transcriptMode\">\n            <!-- Disables transcript mode. This is the default value. -->\n            <enum name=\"disabled\" value=\"0\"/>\n            <!-- The list will automatically scroll to the bottom when\n                 a data set change notification is received and only if the last item is\n                 already visible on screen. -->\n            <enum name=\"normal\" value=\"1\" />\n            <!-- The list will automatically scroll to the bottom, no matter what items\n                 are currently visible. -->\n            <enum name=\"alwaysScroll\" value=\"2\" />\n        </attr>\n        <!-- Indicates that this list will always be drawn on top of solid, single-color\n             opaque background. This allows the list to optimize drawing. -->\n        <attr name=\"cacheColorHint\" format=\"color\" />\n        <!-- Enables the fast scroll thumb that can be dragged to quickly scroll through\n             the list. -->\n        <attr name=\"fastScrollEnabled\" format=\"boolean\" />\n        <!-- Specifies the style of the fast scroll decorations. -->\n        <attr name=\"fastScrollStyle\" format=\"reference\" />\n        <!-- When set to true, the list will use a more refined calculation\n             method based on the pixels height of the items visible on screen. This\n             property is set to true by default but should be set to false if your adapter\n             will display items of varying heights. When this property is set to true and\n             your adapter displays items of varying heights, the scrollbar thumb will\n             change size as the user scrolls through the list. When set to false, the list\n             will use only the number of items in the adapter and the number of items visible\n             on screen to determine the scrollbar's properties. -->\n        <attr name=\"smoothScrollbar\" format=\"boolean\" />\n        <!-- Defines the choice behavior for the view. By default, lists do not have\n             any choice behavior. By setting the choiceMode to singleChoice, the list\n             allows up to one item to be in a chosen state. By setting the choiceMode to\n             multipleChoice, the list allows any number of items to be chosen.\n             Finally, by setting the choiceMode to multipleChoiceModal the list allows\n             any number of items to be chosen in a special selection mode.\n             The application will supply a\n             {@link android.widget.AbsListView.MultiChoiceModeListener} using\n             {@link android.widget.AbsListView#setMultiChoiceModeListener} to control the\n             selection mode. This uses the {@link android.view.ActionMode} API. -->\n        <attr name=\"choiceMode\">\n            <!-- Normal list that does not indicate choices. -->\n            <enum name=\"none\" value=\"0\" />\n            <!-- The list allows up to one choice. -->\n            <enum name=\"singleChoice\" value=\"1\" />\n            <!-- The list allows multiple choices. -->\n            <enum name=\"multipleChoice\" value=\"2\" />\n            <!-- The list allows multiple choices in a custom selection mode. -->\n            <enum name=\"multipleChoiceModal\" value=\"3\" />\n        </attr>\n\n        <!-- When set to true, the list will always show the fast scroll interface.\n             This setting implies fastScrollEnabled. -->\n        <attr name=\"fastScrollAlwaysVisible\" format=\"boolean\" />\n    </declare-styleable>\n    <!-- @hide -->\n    <declare-styleable name=\"RecycleListView\">\n        <!-- Bottom padding to use when no buttons are present. -->\n        <attr name=\"paddingBottomNoButtons\" format=\"dimension\" />\n        <!-- Top padding to use when no title is present. -->\n        <attr name=\"paddingTopNoTitle\" format=\"dimension\" />\n    </declare-styleable>\n    <declare-styleable name=\"AbsSpinner\">\n        <!-- Reference to an array resource that will populate the Spinner.  For static content,\n             this is simpler than populating the Spinner programmatically. -->\n        <attr name=\"entries\" />\n    </declare-styleable>\n    <declare-styleable name=\"AnalogClock\">\n        <attr name=\"dial\" format=\"reference\"/>\n        <attr name=\"hand_hour\" format=\"reference\"/>\n        <attr name=\"hand_minute\" format=\"reference\"/>\n        <attr name=\"hand_second\" format=\"reference\"/>\n        <!-- Specifies the time zone to use. When this attribute is specified, the\n             TextClock will ignore the time zone of the system. To use the user's\n             time zone, do not specify this attribute. The default value is the\n             user's time zone. Please refer to {@link java.util.TimeZone} for more\n             information about time zone ids. -->\n        <attr name=\"timeZone\" format=\"string\"/>\n        <!-- Tint to apply to the dial graphic. -->\n        <attr name=\"dialTint\" format=\"color\" />\n        <!-- Blending mode used to apply the dial graphic tint. -->\n        <attr name=\"dialTintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n        <!-- Tint to apply to the hour hand graphic. -->\n        <attr name=\"hand_hourTint\" format=\"color\" />\n        <!-- Blending mode used to apply the hour hand graphic tint. -->\n        <attr name=\"hand_hourTintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n        <!-- Tint to apply to the minute hand graphic. -->\n        <attr name=\"hand_minuteTint\" format=\"color\" />\n        <!-- Blending mode used to apply the minute hand graphic tint. -->\n        <attr name=\"hand_minuteTintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n        <!-- Tint to apply to the second hand graphic. -->\n        <attr name=\"hand_secondTint\" format=\"color\" />\n        <!-- Blending mode used to apply the second hand graphic tint. -->\n        <attr name=\"hand_secondTintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n    </declare-styleable>\n    <declare-styleable name=\"Button\">\n    </declare-styleable>\n    <declare-styleable name=\"Chronometer\">\n        <!-- Format string: if specified, the Chronometer will display this\n             string, with the first \"%s\" replaced by the current timer value\n             in \"MM:SS\" or \"H:MM:SS\" form.\n             If no format string is specified, the Chronometer will simply display\n             \"MM:SS\" or \"H:MM:SS\". -->\n        <attr name=\"format\" format=\"string\" localization=\"suggested\" />\n        <!-- Specifies whether this Chronometer counts down or counts up from the base.\n              If not specified this is false and the Chronometer counts up. -->\n        <attr name=\"countDown\" format=\"boolean\" />\n    </declare-styleable>\n    <declare-styleable name=\"CompoundButton\">\n        <!-- Indicates the initial checked state of this button. -->\n        <attr name=\"checked\" format=\"boolean\" />\n        <!-- Drawable used for the button graphic (for example, checkbox and radio button). -->\n        <attr name=\"button\" format=\"reference\" />\n        <!-- Tint to apply to the button graphic. -->\n        <attr name=\"buttonTint\" format=\"color\" />\n        <!-- Blending mode used to apply the button graphic tint. -->\n        <attr name=\"buttonTintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n    </declare-styleable>\n    <declare-styleable name=\"CheckedTextView\">\n        <!-- Indicates the initial checked state of this text. -->\n        <attr name=\"checked\" />\n        <!-- Drawable used for the check mark graphic. -->\n        <attr name=\"checkMark\" format=\"reference\"/>\n        <!-- Tint to apply to the check mark. -->\n        <attr name=\"checkMarkTint\" format=\"color\" />\n        <!-- Blending mode used to apply the check mark tint. -->\n        <attr name=\"checkMarkTintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n        <!-- Gravity for aligning a CheckedTextView's checkmark to one side or the other. -->\n        <attr name=\"checkMarkGravity\">\n            <!-- Push object to the left of its container, not changing its size. -->\n            <flag name=\"left\" value=\"0x03\" />\n            <!-- Push object to the right of its container, not changing its size. -->\n            <flag name=\"right\" value=\"0x05\" />\n            <!-- Push object to the beginning of its container, not changing its size. -->\n            <flag name=\"start\" value=\"0x00800003\" />\n            <!-- Push object to the end of its container, not changing its size. -->\n            <flag name=\"end\" value=\"0x00800005\" />\n        </attr>\n    </declare-styleable>\n    <declare-styleable name=\"EditText\">\n        <!-- Enables styling shortcuts, e.g. Ctrl+B for bold. This is off by default.  -->\n        <attr name=\"enableTextStylingShortcuts\" format=\"boolean\" />\n    </declare-styleable>\n    <declare-styleable name=\"FastScroll\">\n        <!-- Drawable used for the scroll bar thumb. -->\n        <attr name=\"thumbDrawable\" format=\"reference\" />\n        <!-- Minimum width of the thumb. -->\n        <attr name=\"thumbMinWidth\" format=\"dimension\" />\n        <!-- Minimum height of the thumb. -->\n        <attr name=\"thumbMinHeight\" format=\"dimension\" />\n        <!-- Drawable used for the scroll bar track. -->\n        <attr name=\"trackDrawable\" format=\"reference\" />\n        <!-- Drawable used for the section header preview when right-aligned. -->\n        <attr name=\"backgroundRight\" format=\"reference\" />\n        <!-- Drawable used for the section header preview when left-aligned. -->\n        <attr name=\"backgroundLeft\" format=\"reference\" />\n        <!-- Position of section header preview. -->\n        <attr name=\"position\">\n            <!-- Floating at the top of the content. -->\n            <enum name=\"floating\" value=\"0\" />\n            <!-- Pinned to the thumb, vertically centered with the middle of the thumb. -->\n            <enum name=\"atThumb\" value=\"1\" />\n            <!-- Pinned to the thumb, vertically centered with the top edge of the thumb. -->\n            <enum name=\"aboveThumb\" value=\"2\" />\n        </attr>\n        <attr name=\"textAppearance\" />\n        <attr name=\"textColor\" />\n        <attr name=\"textSize\" />\n        <!-- Minimum width of the section header preview. -->\n        <attr name=\"minWidth\" />\n        <!-- Minimum height of the section header preview. -->\n        <attr name=\"minHeight\" />\n        <!-- Padding for the section header preview. -->\n        <attr name=\"padding\" />\n        <!-- Position of thumb in relation to the track. -->\n        <attr name=\"thumbPosition\">\n            <!-- The thumb's midpoint is anchored to the track. At its\n                 extremes, the thumb will extend half-way outside the\n                 track. -->\n            <enum name=\"midpoint\" value=\"0\" />\n            <!-- The thumb is entirely inside the track. At its extremes,\n                 the thumb will be contained entirely within the track. -->\n            <enum name=\"inside\" value=\"1\" />\n        </attr>\n    </declare-styleable>\n    <declare-styleable name=\"FrameLayout\">\n        <!-- Determines whether to measure all children or just those in\n             the VISIBLE or INVISIBLE state when measuring. Defaults to false. -->\n        <attr name=\"measureAllChildren\" format=\"boolean\" />\n    </declare-styleable>\n    <!-- @hide -->\n    <declare-styleable name=\"MaxHeightFrameLayout\">\n        <!-- An optional argument to supply a maximum height for this view. -->\n        <attr name=\"maxHeight\" format=\"dimension\" />\n    </declare-styleable>\n    <declare-styleable name=\"ExpandableListView\">\n        <!-- Indicator shown beside the group View. This can be a stateful Drawable. -->\n        <attr name=\"groupIndicator\" format=\"reference\" />\n        <!-- Indicator shown beside the child View. This can be a stateful Drawable. -->\n        <attr name=\"childIndicator\" format=\"reference\" />\n        <!-- The left bound for an item's indicator. To specify a left bound specific to children,\n             use childIndicatorLeft. -->\n        <attr name=\"indicatorLeft\" format=\"dimension\" />\n        <!-- The right bound for an item's indicator. To specify a right bound specific to children,\n             use childIndicatorRight. -->\n        <attr name=\"indicatorRight\" format=\"dimension\" />\n        <!-- The left bound for a child's indicator. -->\n        <attr name=\"childIndicatorLeft\" format=\"dimension\" />\n        <!-- The right bound for a child's indicator. -->\n        <attr name=\"childIndicatorRight\" format=\"dimension\" />\n        <!-- Drawable or color that is used as a divider for children. (It will drawn\n             below and above child items.) The height of this will be the same as\n             the height of the normal list item divider. -->\n        <attr name=\"childDivider\" format=\"reference|color\" />\n        <!-- The start bound for an item's indicator. To specify a start bound specific to children,\n             use childIndicatorStart. -->\n        <attr name=\"indicatorStart\" format=\"dimension\" />\n        <!-- The end bound for an item's indicator. To specify a right bound specific to children,\n             use childIndicatorEnd. -->\n        <attr name=\"indicatorEnd\" format=\"dimension\" />\n        <!-- The start bound for a child's indicator. -->\n        <attr name=\"childIndicatorStart\" format=\"dimension\" />\n        <!-- The end bound for a child's indicator. -->\n        <attr name=\"childIndicatorEnd\" format=\"dimension\" />\n    </declare-styleable>\n    <declare-styleable name=\"Gallery\">\n        <attr name=\"gravity\" />\n        <!-- Sets how long a transition animation should run (in milliseconds)\n             when layout has changed.  Only relevant if animation is turned on. -->\n        <attr name=\"animationDuration\" format=\"integer\" min=\"0\" />\n        <attr name=\"spacing\" format=\"dimension\" />\n        <!-- Sets the alpha on the items that are not selected. -->\n        <attr name=\"unselectedAlpha\" format=\"float\" />\n    </declare-styleable>\n    <declare-styleable name=\"GridView\">\n        <!-- Defines the default horizontal spacing between columns. -->\n        <attr name=\"horizontalSpacing\" format=\"dimension\" />\n        <!-- Defines the default vertical spacing between rows. -->\n        <attr name=\"verticalSpacing\" format=\"dimension\" />\n        <!-- Defines how columns should stretch to fill the available empty space, if any. -->\n        <attr name=\"stretchMode\">\n            <!-- Stretching is disabled. -->\n            <enum name=\"none\" value=\"0\"/>\n            <!-- The spacing between each column is stretched. -->\n            <enum name=\"spacingWidth\" value=\"1\" />\n            <!-- Each column is stretched equally. -->\n            <enum name=\"columnWidth\" value=\"2\" />\n            <!-- The spacing between each column is uniformly stretched.. -->\n            <enum name=\"spacingWidthUniform\" value=\"3\" />\n        </attr>\n        <!-- Specifies the fixed width for each column. -->\n        <attr name=\"columnWidth\" format=\"dimension\" />\n        <!-- Defines how many columns to show. -->\n        <attr name=\"numColumns\" format=\"integer\" min=\"0\">\n            <!-- Display as many columns as possible to fill the available space. -->\n            <enum name=\"auto_fit\" value=\"-1\" />\n        </attr>\n        <!-- Specifies the gravity within each cell. -->\n        <attr name=\"gravity\" />\n    </declare-styleable>\n    <declare-styleable name=\"ImageSwitcher\">\n    </declare-styleable>\n    <declare-styleable name=\"ImageView\">\n        <!-- Sets a drawable as the content of this ImageView. -->\n        <attr name=\"src\" format=\"reference|color\" />\n        <!-- Controls how the image should be resized or moved to match the size\n             of this ImageView.  See {@link android.widget.ImageView.ScaleType} -->\n        <attr name=\"scaleType\">\n            <!-- Scale using the image matrix when drawing. See\n                 {@link android.widget.ImageView#setImageMatrix(Matrix)}. -->\n            <enum name=\"matrix\" value=\"0\" />\n            <!-- Scale the image using {@link android.graphics.Matrix.ScaleToFit#FILL}. -->\n            <enum name=\"fitXY\" value=\"1\" />\n            <!-- Scale the image using {@link android.graphics.Matrix.ScaleToFit#START}. -->\n            <enum name=\"fitStart\" value=\"2\" />\n            <!-- Scale the image using {@link android.graphics.Matrix.ScaleToFit#CENTER}. -->\n            <enum name=\"fitCenter\" value=\"3\" />\n            <!-- Scale the image using {@link android.graphics.Matrix.ScaleToFit#END}. -->\n            <enum name=\"fitEnd\" value=\"4\" />\n            <!-- Center the image in the view, but perform no scaling. -->\n            <enum name=\"center\" value=\"5\" />\n            <!-- Scale the image uniformly (maintain the image's aspect ratio) so both dimensions\n                 (width and height) of the image will be equal to or larger than the corresponding\n                 dimension of the view (minus padding). The image is then centered in the view. -->\n            <enum name=\"centerCrop\" value=\"6\" />\n            <!-- Scale the image uniformly (maintain the image's aspect ratio) so that both\n                 dimensions (width and height) of the image will be equal to or less than the\n                 corresponding dimension of the view (minus padding). The image is then centered in\n                 the view. -->\n            <enum name=\"centerInside\" value=\"7\" />\n        </attr>\n        <!-- Set this to true if you want the ImageView to adjust its bounds\n             to preserve the aspect ratio of its drawable. -->\n        <attr name=\"adjustViewBounds\" format=\"boolean\" />\n        <!-- An optional argument to supply a maximum width for this view.\n             See {see android.widget.ImageView#setMaxWidth} for details. -->\n        <attr name=\"maxWidth\" format=\"dimension\" />\n        <!-- An optional argument to supply a maximum height for this view.\n             See {see android.widget.ImageView#setMaxHeight} for details. -->\n        <attr name=\"maxHeight\" format=\"dimension\" />\n        <!-- The tinting color for the image. By default, the tint will blend using SRC_ATOP mode.\n             Please note that for compatibility reasons, this is NOT consistent with the default\n             SRC_IN tint mode used by {@link android.widget.ImageView#setImageTintList} and by\n             similar tint attributes on other views. -->\n        <attr name=\"tint\" format=\"color\" />\n        <!-- If true, the image view will be baseline aligned with based on its\n             bottom edge. -->\n        <attr name=\"baselineAlignBottom\" format=\"boolean\" />\n         <!-- If true, the image will be cropped to fit within its padding. -->\n        <attr name=\"cropToPadding\" format=\"boolean\" />\n        <!-- The offset of the baseline within this view. See {see android.view.View#getBaseline}\n             for details -->\n        <attr name=\"baseline\" format=\"dimension\" />\n        <!-- @hide The alpha value (0-255) set on the ImageView's drawable. Equivalent\n             to calling ImageView.setAlpha(int), not the same as View.setAlpha(float). -->\n        <attr name=\"drawableAlpha\" format=\"integer\" />\n        <!-- Blending mode used to apply the image tint. -->\n        <attr name=\"tintMode\" />\n    </declare-styleable>\n    <declare-styleable name=\"ToggleButton\">\n        <!-- The text for the button when it is checked. -->\n        <attr name=\"textOn\" format=\"string\" />\n        <!-- The text for the button when it is not checked. -->\n        <attr name=\"textOff\" format=\"string\" />\n        <!-- The alpha to apply to the indicator when disabled. -->\n        <attr name=\"disabledAlpha\" />\n    </declare-styleable>\n    <declare-styleable name=\"RelativeLayout\">\n        <attr name=\"gravity\" />\n        <!-- Indicates what view should not be affected by gravity. -->\n        <attr name=\"ignoreGravity\" format=\"reference\" />\n    </declare-styleable>\n    <declare-styleable name=\"LinearLayout\">\n        <!-- Should the layout be a column or a row?  Use \"horizontal\"\n             for a row, \"vertical\" for a column.  The default is\n             horizontal. -->\n        <attr name=\"orientation\" />\n        <attr name=\"gravity\" />\n        <!-- When set to false, prevents the layout from aligning its children's\n             baselines. This attribute is particularly useful when the children\n             use different values for gravity. The default value is true. -->\n        <attr name=\"baselineAligned\" format=\"boolean\" />\n        <!-- When a linear layout is part of another layout that is baseline\n          aligned, it can specify which of its children to baseline align to\n          (that is, which child TextView).-->\n        <attr name=\"baselineAlignedChildIndex\" format=\"integer\" min=\"0\"/>\n        <!-- Defines the maximum weight sum. If unspecified, the sum is computed\n             by adding the layout_weight of all of the children. This can be\n             used for instance to give a single child 50% of the total available\n             space by giving it a layout_weight of 0.5 and setting the weightSum\n             to 1.0. -->\n        <attr name=\"weightSum\" format=\"float\" />\n        <!-- When set to true, all children with a weight will be considered having\n             the minimum size of the largest child. If false, all children are\n             measured normally. -->\n        <attr name=\"measureWithLargestChild\" format=\"boolean\" />\n        <!-- Drawable to use as a vertical divider between buttons. -->\n        <attr name=\"divider\" />\n        <!-- Setting for which dividers to show. -->\n        <attr name=\"showDividers\">\n            <flag name=\"none\" value=\"0\" />\n            <flag name=\"beginning\" value=\"1\" />\n            <flag name=\"middle\" value=\"2\" />\n            <flag name=\"end\" value=\"4\" />\n        </attr>\n        <!-- Size of padding on either end of a divider. -->\n        <attr name=\"dividerPadding\" format=\"dimension\" />\n    </declare-styleable>\n    <declare-styleable name=\"GridLayout\">\n        <!-- The orientation property is not used during layout. It is only used to\n        allocate row and column parameters when they are not specified by its children's\n        layout paramters. GridLayout works like LinearLayout in this case;\n        putting all the components either in a single row or in a single column -\n        depending on the value of this flag. In the horizontal case, a columnCount\n        property may be additionally supplied to force new rows to be created when a\n        row is full. The rowCount attribute may be used similarly in the vertical case.\n        The default is horizontal. -->\n        <attr name=\"orientation\" />\n        <!-- The maximum number of rows to create when automatically positioning children. -->\n        <attr name=\"rowCount\" format=\"integer\" />\n        <!-- The maximum number of columns to create when automatically positioning children. -->\n        <attr name=\"columnCount\" format=\"integer\" />\n        <!-- When set to true, tells GridLayout to use default margins when none are specified\n        in a view's layout parameters.\n        The default value is false.\n        See {@link android.widget.GridLayout#setUseDefaultMargins(boolean)}.-->\n        <attr name=\"useDefaultMargins\" format=\"boolean\" />\n        <!-- When set to alignMargins, causes alignment to take place between the outer\n        boundary of a view, as defined by its margins. When set to alignBounds,\n        causes alignment to take place between the edges of the view.\n        The default is alignMargins.\n        See {@link android.widget.GridLayout#setAlignmentMode(int)}.-->\n        <attr name=\"alignmentMode\" />\n        <!-- When set to true, forces row boundaries to appear in the same order\n        as row indices.\n        The default is true.\n        See {@link android.widget.GridLayout#setRowOrderPreserved(boolean)}.-->\n        <attr name=\"rowOrderPreserved\" format=\"boolean\" />\n        <!-- When set to true, forces column boundaries to appear in the same order\n        as column indices.\n        The default is true.\n        See {@link android.widget.GridLayout#setColumnOrderPreserved(boolean)}.-->\n        <attr name=\"columnOrderPreserved\" format=\"boolean\" />\n    </declare-styleable>\n    <declare-styleable name=\"ListView\">\n        <!-- Reference to an array resource that will populate the ListView.  For static content,\n             this is simpler than populating the ListView programmatically. -->\n        <attr name=\"entries\" />\n        <!-- Drawable or color to draw between list items. -->\n        <attr name=\"divider\" format=\"reference|color\" />\n        <!-- Height of the divider. Will use the intrinsic height of the divider if this\n             is not specified. -->\n        <attr name=\"dividerHeight\" format=\"dimension\" />\n        <!-- When set to false, the ListView will not draw the divider after each header view.\n             The default value is true. -->\n        <attr name=\"headerDividersEnabled\" format=\"boolean\" />\n        <!-- When set to false, the ListView will not draw the divider before each footer view.\n             The default value is true. -->\n        <attr name=\"footerDividersEnabled\" format=\"boolean\" />\n        <!-- Drawable to draw above list content. -->\n        <attr name=\"overScrollHeader\" format=\"reference|color\" />\n        <!-- Drawable to draw below list content. -->\n        <attr name=\"overScrollFooter\" format=\"reference|color\" />\n    </declare-styleable>\n    <declare-styleable name=\"PreferenceFrameLayout\">\n        <!-- Padding to use at the top of the prefs content. -->\n        <attr name=\"borderTop\" format=\"dimension\" />\n        <!-- Padding to use at the bottom of the prefs content. -->\n        <attr name=\"borderBottom\" format=\"dimension\" />\n        <!-- Padding to use at the left of the prefs content. -->\n        <attr name=\"borderLeft\" format=\"dimension\" />\n        <!-- Padding to use at the right of the prefs content. -->\n        <attr name=\"borderRight\" format=\"dimension\" />\n    </declare-styleable>\n    <declare-styleable name=\"PreferenceFrameLayout_Layout\">\n        <!-- Padding to use at the top of the prefs content. -->\n        <attr name=\"layout_removeBorders\" format=\"boolean\" />\n    </declare-styleable>\n    <declare-styleable name=\"MenuView\">\n        <!-- Default appearance of menu item text. -->\n        <attr name=\"itemTextAppearance\" format=\"reference\" />\n        <!-- Default horizontal divider between rows of menu items. -->\n        <attr name=\"horizontalDivider\" format=\"reference\" />\n        <!-- Default vertical divider between menu items. -->\n        <attr name=\"verticalDivider\" format=\"reference\" />\n        <!-- Default background for the menu header. -->\n        <attr name=\"headerBackground\" format=\"color|reference\" />\n        <!-- Default background for each menu item. -->\n        <attr name=\"itemBackground\" format=\"color|reference\" />\n        <!-- Default animations for the menu. -->\n        <attr name=\"windowAnimationStyle\" />\n        <!-- Default disabled icon alpha for each menu item that shows an icon. -->\n        <attr name=\"itemIconDisabledAlpha\" format=\"float\" />\n        <!-- Whether space should be reserved in layout when an icon is missing. -->\n        <attr name=\"preserveIconSpacing\" format=\"boolean\" />\n        <!-- Drawable for the arrow icon indicating a particular item is a submenu. -->\n        <attr name=\"subMenuArrow\" format=\"reference\" />\n    </declare-styleable>\n    <declare-styleable name=\"IconMenuView\">\n        <!-- Defines the height of each row. -->\n        <attr name=\"rowHeight\" format=\"dimension\" />\n        <!-- Defines the maximum number of rows displayed. -->\n        <attr name=\"maxRows\" format=\"integer\" />\n        <!-- Defines the maximum number of items per row. -->\n        <attr name=\"maxItemsPerRow\" format=\"integer\" />\n        <!-- Defines the maximum number of items to show. -->\n        <attr name=\"maxItems\" format=\"integer\" />\n        <!-- 'More' icon. -->\n        <attr name=\"moreIcon\" format=\"reference\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"ProgressBar\">\n        <!-- Defines the minimum value. -->\n        <attr name=\"min\" format=\"integer\" />\n        <!-- Defines the maximum value. -->\n        <attr name=\"max\" format=\"integer\" />\n        <!-- Defines the default progress value, between 0 and max. -->\n        <attr name=\"progress\" format=\"integer\" />\n        <!-- Defines the secondary progress value, between 0 and max. This progress is drawn between\n             the primary progress and the background.  It can be ideal for media scenarios such as\n             showing the buffering progress while the default progress shows the play progress. -->\n        <attr name=\"secondaryProgress\" format=\"integer\" />\n        <!-- Allows to enable the indeterminate mode. In this mode the progress\n         bar plays an infinite looping animation. -->\n        <attr name=\"indeterminate\" format=\"boolean\" />\n        <!-- Restricts to ONLY indeterminate mode (state-keeping progress mode will not work). -->\n        <attr name=\"indeterminateOnly\" format=\"boolean\" />\n        <!-- Drawable used for the indeterminate mode. One that implements Animatable offers more\n             control over the animation.-->\n        <attr name=\"indeterminateDrawable\" format=\"reference\" />\n        <!-- Drawable used for the progress mode. -->\n        <attr name=\"progressDrawable\" format=\"reference\" />\n        <!-- Duration of the indeterminate animation. Only affects the indeterminate animation\n             if the indeterminate Drawable does not implement\n             android.graphics.drawable.Animatable. -->\n        <attr name=\"indeterminateDuration\" format=\"integer\" min=\"1\" />\n        <!-- Defines how the indeterminate mode should behave when the progress reaches max. Only\n             affects the indeterminate animation if the indeterminate Drawable does not implement\n             android.graphics.drawable.Animatable. -->\n        <attr name=\"indeterminateBehavior\">\n            <!-- Progress starts over from 0. -->\n            <enum name=\"repeat\" value=\"1\" />\n            <!-- Progress keeps the current value and goes back to 0. -->\n            <enum name=\"cycle\" value=\"2\" />\n        </attr>\n        <attr name=\"minWidth\" format=\"dimension\" />\n        <attr name=\"maxWidth\" />\n        <attr name=\"minHeight\" format=\"dimension\" />\n        <attr name=\"maxHeight\" />\n        <!-- Sets the acceleration curve for the indeterminate animation. Defaults to a linear\n             interpolation. Only affects the indeterminate animation if the indeterminate Drawable\n             does not implement android.graphics.drawable.Animatable.-->\n        <attr name=\"interpolator\" format=\"reference\" />\n        <!-- Timeout between frames of animation in milliseconds.\n             {@deprecated Not used by the framework}. -->\n        <attr name=\"animationResolution\" format=\"integer\" />\n        <!-- Defines if the associated drawables need to be mirrored when in RTL mode.\n             Default is false. -->\n        <attr name=\"mirrorForRtl\" format=\"boolean\" />\n        <!-- Tint to apply to the progress indicator. -->\n        <attr name=\"progressTint\" format=\"color\" />\n        <!-- Blending mode used to apply the progress indicator tint. -->\n        <attr name=\"progressTintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n        <!-- Tint to apply to the progress indicator background. -->\n        <attr name=\"progressBackgroundTint\" format=\"color\" />\n        <!-- Blending mode used to apply the progress indicator background tint. -->\n        <attr name=\"progressBackgroundTintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n        <!-- Tint to apply to the secondary progress indicator. -->\n        <attr name=\"secondaryProgressTint\" format=\"color\" />\n        <!-- Blending mode used to apply the secondary progress indicator tint. -->\n        <attr name=\"secondaryProgressTintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n        <!-- Tint to apply to the indeterminate progress indicator. -->\n        <attr name=\"indeterminateTint\" format=\"color\" />\n        <!-- Blending mode used to apply the indeterminate progress indicator tint. -->\n        <attr name=\"indeterminateTintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n        <!-- Tint to apply to the background. -->\n        <attr name=\"backgroundTint\" />\n        <!-- Blending mode used to apply the background tint. -->\n        <attr name=\"backgroundTintMode\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"SeekBar\">\n        <!-- Draws the thumb on a seekbar. -->\n        <attr name=\"thumb\" format=\"reference\" />\n        <!-- An offset for the thumb that allows it to extend out of the range of the track. -->\n        <attr name=\"thumbOffset\" format=\"dimension\" />\n        <!-- Whether to split the track and leave a gap for the thumb drawable. -->\n        <attr name=\"splitTrack\" format=\"boolean\" />\n        <!-- Whether to force the track's alpha to ?android:attr/disabledAlpha\n             when disabled. This is required for Holo and Gingerbread, but\n             should always be false for Material and  beyond.\n             @hide Developers shouldn't need to change this. -->\n        <attr name=\"useDisabledAlpha\" format=\"boolean\" />\n        <!-- Tint to apply to the thumb drawable. -->\n        <attr name=\"thumbTint\" format=\"color\" />\n        <!-- Blending mode used to apply the thumb tint. -->\n        <attr name=\"thumbTintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n        <!-- Drawable displayed at each progress position on a seekbar. -->\n        <attr name=\"tickMark\" format=\"reference\" />\n        <!-- Tint to apply to the tick mark drawable. -->\n        <attr name=\"tickMarkTint\" format=\"color\" />\n        <!-- Blending mode used to apply the tick mark tint. -->\n        <attr name=\"tickMarkTintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n    </declare-styleable>\n\n    <!-- @hide internal use only -->\n    <declare-styleable name=\"NotificationProgressBar\">\n        <!-- Minimum required drawing width for segments. The drawing width refers to the width\n             after the original segments have been adjusted for the neighboring Points and gaps.\n             This is enforced by stretching the segments that are too short. -->\n        <attr name=\"segMinWidth\" format=\"dimension\" />\n        <!-- The gap between two segments. -->\n        <attr name=\"segSegGap\" format=\"dimension\" />\n        <!-- The gap between a segment and a point. -->\n        <attr name=\"segPointGap\" format=\"dimension\" />\n        <!-- Draws the tracker on a NotificationProgressBar. -->\n        <attr name=\"tracker\" format=\"reference\" />\n        <!-- Height of the tracker. -->\n        <attr name=\"trackerHeight\" format=\"dimension\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"StackView\">\n        <!-- Color of the res-out outline. -->\n        <attr name=\"resOutColor\" format=\"color\" />\n        <!-- Color of the outline of click feedback. -->\n        <attr name=\"clickColor\" format=\"color\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"RatingBar\">\n        <!-- The number of stars (or rating items) to show. -->\n        <attr name=\"numStars\" format=\"integer\" />\n        <!-- The rating to set by default. -->\n        <attr name=\"rating\" format=\"float\" />\n        <!-- The step size of the rating. -->\n        <attr name=\"stepSize\" format=\"float\" />\n        <!-- Whether this rating bar is an indicator (and non-changeable by the user). -->\n        <attr name=\"isIndicator\" format=\"boolean\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"RadioGroup\">\n        <!-- The id of the child radio button that should be checked by default\n             within this radio group. -->\n        <attr name=\"checkedButton\" format=\"integer\" />\n        <!-- Should the radio group be a column or a row?  Use \"horizontal\"\n             for a row, \"vertical\" for a column.  The default is\n             vertical. -->\n        <attr name=\"orientation\" />\n    </declare-styleable>\n    <declare-styleable name=\"TableLayout\">\n        <!-- The zero-based index of the columns to stretch. The column indices\n             must be separated by a comma: 1, 2, 5. Illegal and duplicate\n             indices are ignored. You can stretch all columns by using the\n             value \"*\" instead. Note that a column can be marked stretchable\n             and shrinkable at the same time. -->\n        <attr name=\"stretchColumns\" format=\"string\" />\n       <!-- The zero-based index of the columns to shrink. The column indices\n             must be separated by a comma: 1, 2, 5. Illegal and duplicate\n             indices are ignored. You can shrink all columns by using the\n             value \"*\" instead. Note that a column can be marked stretchable\n             and shrinkable at the same time. -->\n        <attr name=\"shrinkColumns\" format=\"string\" />\n        <!-- The zero-based index of the columns to collapse. The column indices\n             must be separated by a comma: 1, 2, 5. Illegal and duplicate\n             indices are ignored. -->\n        <attr name=\"collapseColumns\" format=\"string\" />\n    </declare-styleable>\n    <declare-styleable name=\"TableRow\">\n\n    </declare-styleable>\n    <declare-styleable name=\"TableRow_Cell\">\n        <!-- The index of the column in which this child should be. -->\n        <attr name=\"layout_column\" format=\"integer\" />\n        <!-- Defines how many columns this child should span.  Must be >= 1.-->\n        <attr name=\"layout_span\" format=\"integer\" />\n    </declare-styleable>\n    <declare-styleable name=\"TabWidget\">\n        <!-- Drawable used to draw the divider between tabs. -->\n        <attr name=\"divider\" />\n        <!-- Determines whether the strip under the tab indicators is drawn or not. -->\n        <attr name=\"tabStripEnabled\" format=\"boolean\" />\n        <!-- Drawable used to draw the left part of the strip underneath the tabs. -->\n        <attr name=\"tabStripLeft\" format=\"reference\" />\n        <!-- Drawable used to draw the right part of the strip underneath the tabs. -->\n        <attr name=\"tabStripRight\" format=\"reference\" />\n        <!-- Layout used to organize each tab's content. -->\n        <attr name=\"tabLayout\" format=\"reference\" />\n    </declare-styleable>\n    <declare-styleable name=\"TextAppearance\">\n        <!-- Text color. -->\n        <attr name=\"textColor\" />\n        <!-- Size of the text. Recommended dimension type for text is \"sp\" for scaled-pixels (example: 15sp). -->\n        <attr name=\"textSize\" />\n        <!-- Style (normal, bold, italic, bold|italic) for the text. -->\n        <attr name=\"textStyle\" />\n        <!-- Weight for the font used in the TextView. -->\n        <attr name=\"textFontWeight\" />\n        <!-- Typeface (normal, sans, serif, monospace) for the text. -->\n        <attr name=\"typeface\" />\n        <!-- Font family (named by string or as a font resource reference) for the text. -->\n        <attr name=\"fontFamily\" />\n        <!-- Specifies the {@link android.os.LocaleList} for the text.\n             May be a string value, which is a comma-separated language tag list, such as \"ja-JP,zh-CN\".\n             When not specified or an empty string is given, it will fallback to the default one.\n             {@see android.os.LocaleList#forLanguageTags(String)} -->\n        <attr name=\"textLocale\" format=\"string\" />\n        <!-- Color of the text selection highlight. -->\n        <attr name=\"textColorHighlight\" />\n        <!-- Color of search results highlight.\n             This color is typically used when TextView/EditText shows search result in-app text\n             search invoked with Ctrl+F. -->\n        <attr name=\"searchResultHighlightColor\" format=\"color\" />\n        <!-- Color of focused search result highlight.\n             This color is typically used when TextView/EditText shows search result in-app text\n             search invoked with Ctrl+F. -->\n        <attr name=\"focusedSearchResultHighlightColor\" format=\"color\" />\n\n        <!-- Color of the hint text. -->\n        <attr name=\"textColorHint\" />\n        <!-- Color of the links. -->\n        <attr name=\"textColorLink\" />\n        <!-- Present the text in ALL CAPS. This may use a small-caps form when available. -->\n        <attr name=\"textAllCaps\" format=\"boolean\" />\n        <!-- Place a blurred shadow of text underneath the text, drawn with the\n             specified color. The text shadow produced does not interact with\n             properties on View that are responsible for real time shadows,\n             {@link android.R.styleable#View_elevation elevation} and\n             {@link android.R.styleable#View_translationZ translationZ}. -->\n        <attr name=\"shadowColor\" format=\"color\" />\n        <!-- Horizontal offset of the text shadow. -->\n        <attr name=\"shadowDx\" format=\"float\" />\n        <!-- Vertical offset of the text shadow. -->\n        <attr name=\"shadowDy\" format=\"float\" />\n        <!-- Blur radius of the text shadow. -->\n        <attr name=\"shadowRadius\" format=\"float\" />\n        <!-- Elegant text height, especially for less compacted complex script text. -->\n        <attr name=\"elegantTextHeight\" format=\"boolean\" />\n        <!-- Whether to respect the ascent and descent of the fallback fonts that are used in\n        displaying the text. When true, fallback fonts that end up getting used can increase\n        the ascent and descent of the lines that they are used on. -->\n        <attr name=\"fallbackLineSpacing\" format=\"boolean\"/>\n        <!-- Text letter-spacing. -->\n        <attr name=\"letterSpacing\" format=\"float\" />\n        <!-- Font feature settings. -->\n        <attr name=\"fontFeatureSettings\" format=\"string\" />\n        <!-- Font variation settings. -->\n        <attr name=\"fontVariationSettings\" format=\"string\"/>\n        <!-- Specifies the strictness of line-breaking rules applied within an element. -->\n        <attr name=\"lineBreakStyle\" />\n        <!-- Specifies the phrase-based breaking opportunities. -->\n        <attr name=\"lineBreakWordStyle\" />\n    </declare-styleable>\n    <declare-styleable name=\"TextClock\">\n        <!-- Specifies the formatting pattern used to show the time and/or date\n             in 12-hour mode. Please refer to {@link android.text.format.DateFormat}\n             for a complete description of accepted formatting patterns.\n             The default pattern is a locale-appropriate equivalent of \"h:mm a\". -->\n        <attr name=\"format12Hour\" format=\"string\"/>\n        <!-- Specifies the formatting pattern used to show the time and/or date\n             in 24-hour mode. Please refer to {@link android.text.format.DateFormat}\n             for a complete description of accepted formatting patterns.\n             The default pattern is a locale-appropriate equivalent of \"H:mm\". -->\n        <attr name=\"format24Hour\" format=\"string\"/>\n        <!-- Specifies the time zone to use. When this attribute is specified, the\n             TextClock will ignore the time zone of the system. To use the user's\n             time zone, do not specify this attribute. The default value is the\n             user's time zone. Please refer to {@link java.util.TimeZone} for more\n             information about time zone ids. -->\n        <attr name=\"timeZone\" format=\"string\"/>\n    </declare-styleable>\n    <declare-styleable name=\"TextSwitcher\">\n    </declare-styleable>\n    <declare-styleable name=\"TextView\">\n        <!-- Determines the minimum type that getText() will return.\n             The default is \"normal\".\n             Note that EditText and LogTextBox always return Editable,\n             even if you specify something less powerful here. -->\n        <attr name=\"bufferType\">\n            <!-- Can return any CharSequence, possibly a\n             Spanned one if the source text was Spanned. -->\n            <enum name=\"normal\" value=\"0\" />\n            <!-- Can only return Spannable. -->\n            <enum name=\"spannable\" value=\"1\" />\n            <!-- Can only return Spannable and Editable. -->\n            <enum name=\"editable\" value=\"2\" />\n        </attr>\n        <!-- Text to display. -->\n        <attr name=\"text\" format=\"string\" localization=\"suggested\" />\n        <!-- Hint text to display when the text is empty. -->\n        <attr name=\"hint\" format=\"string\" />\n        <!-- Text color. -->\n        <attr name=\"textColor\" />\n        <!-- Color of the text selection highlight. -->\n        <attr name=\"textColorHighlight\" />\n        <!-- Color of search results highlight.\n             This color is typically used when TextView/EditText shows search result in-app text\n             search invoked with Ctrl+F. -->\n        <attr name=\"searchResultHighlightColor\" format=\"color\" />\n        <!-- Color of focused search result highlight.\n             This color is typically used when TextView/EditText shows search result in-app text\n             search invoked with Ctrl+F. -->\n        <attr name=\"focusedSearchResultHighlightColor\" format=\"color\" />\n        <!-- Color of the hint text. -->\n        <attr name=\"textColorHint\" />\n        <!-- Base text color, typeface, size, and style. -->\n        <attr name=\"textAppearance\" />\n        <!-- Size of the text. Recommended dimension type for text is \"sp\" for scaled-pixels (example: 15sp). -->\n        <attr name=\"textSize\" />\n        <!-- Sets the horizontal scaling factor for the text. -->\n        <attr name=\"textScaleX\" format=\"float\" />\n        <!-- Typeface (normal, sans, serif, monospace) for the text. -->\n        <attr name=\"typeface\" />\n        <!-- Style (normal, bold, italic, bold|italic) for the text. -->\n        <attr name=\"textStyle\" />\n        <!-- Weight for the font used in the TextView. -->\n        <attr name=\"textFontWeight\" format=\"integer\"/>\n        <!-- Font family (named by string or as a font resource reference) for the text. -->\n        <attr name=\"fontFamily\" />\n        <!-- Specifies the {@link android.os.LocaleList} for the text in this TextView.\n             If not given, the system default will be used.\n             May be a string value, which is a comma-separated language tag list, such as \"ja-JP,zh-CN\".\n             When not specified or an empty string is given, it will fallback to the default one.\n             {@see android.os.LocaleList#forLanguageTags(String)}\n             {@see android.widget.TextView#setTextLocales(android.os.LocaleList)} -->\n        <attr name=\"textLocale\" format=\"string\" />\n        <!-- Text color for links. -->\n        <attr name=\"textColorLink\" />\n        <!-- Makes the cursor visible (the default) or invisible. -->\n        <attr name=\"cursorVisible\" format=\"boolean\" />\n        <!-- Makes the TextView be at most this many lines tall.\n\n        When used on an editable text, the <code>inputType</code> attribute's value must be\n        combined with the <code>textMultiLine</code> flag for the maxLines attribute to apply. -->\n        <attr name=\"maxLines\" format=\"integer\" min=\"0\" />\n        <!-- Makes the TextView be at most this many pixels tall. -->\n        <attr name=\"maxHeight\" />\n        <!-- Makes the TextView be exactly this many lines tall. -->\n        <attr name=\"lines\" format=\"integer\" min=\"0\" />\n        <!-- Makes the TextView be exactly this tall.\n             You could get the same effect by specifying this number in the\n             layout parameters. -->\n        <attr name=\"height\" format=\"dimension\" />\n        <!-- Makes the TextView be at least this many lines tall.\n\n        When used on an editable text, the <code>inputType</code> attribute's value must be\n        combined with the <code>textMultiLine</code> flag for the minLines attribute to apply. -->\n        <attr name=\"minLines\" format=\"integer\" min=\"0\" />\n        <!-- Makes the TextView be at least this many pixels tall. -->\n        <attr name=\"minHeight\" />\n        <!-- Makes the TextView be at most this many ems wide. -->\n        <attr name=\"maxEms\" format=\"integer\" min=\"0\" />\n        <!-- Makes the TextView be at most this many pixels wide. -->\n        <attr name=\"maxWidth\" />\n        <!-- Makes the TextView be exactly this many ems wide. -->\n        <attr name=\"ems\" format=\"integer\" min=\"0\" />\n        <!-- Makes the TextView be exactly this wide.\n             You could get the same effect by specifying this number in the\n             layout parameters. -->\n        <attr name=\"width\" format=\"dimension\" />\n        <!-- Makes the TextView be at least this many ems wide. -->\n        <attr name=\"minEms\" format=\"integer\" min=\"0\" />\n        <!-- Makes the TextView be at least this many pixels wide. -->\n        <attr name=\"minWidth\" />\n        <!-- Specifies how to align the text by the view's x- and/or y-axis\n             when the text is smaller than the view. -->\n        <attr name=\"gravity\" />\n        <!-- Whether the text is allowed to be wider than the view (and\n             therefore can be scrolled horizontally). -->\n        <attr name=\"scrollHorizontally\" format=\"boolean\" />\n        <!-- Whether the characters of the field are displayed as\n             password dots instead of themselves.\n             {@deprecated Use inputType instead.} -->\n        <attr name=\"password\" format=\"boolean\" />\n        <!-- Constrains the text to a single horizontally scrolling line\n             instead of letting it wrap onto multiple lines, and advances\n             focus instead of inserting a newline when you press the\n             enter key.\n\n             The default value is false (multi-line wrapped text mode) for non-editable text, but if\n             you specify any value for inputType, the default is true (single-line input field mode).\n\n             {@deprecated This attribute is deprecated. Use <code>maxLines</code> instead to change\n             the layout of a static text, and use the <code>textMultiLine</code> flag in the\n             inputType attribute instead for editable text views (if both singleLine and inputType\n             are supplied, the inputType flags will override the value of singleLine). } -->\n        <attr name=\"singleLine\" format=\"boolean\" />\n        <!-- Specifies whether the widget is enabled. The interpretation of the enabled state varies by subclass.\n             For example, a non-enabled EditText prevents the user from editing the contained text, and\n             a non-enabled Button prevents the user from tapping the button.\n             The appearance of enabled and non-enabled widgets may differ, if the drawables referenced\n             from evaluating state_enabled differ. -->\n        <attr name=\"enabled\" format=\"boolean\" />\n        <!-- If the text is selectable, select it all when the view takes\n             focus. -->\n        <attr name=\"selectAllOnFocus\" format=\"boolean\" />\n        <!-- Leave enough room for ascenders and descenders instead of\n             using the font ascent and descent strictly.  (Normally true). -->\n        <attr name=\"includeFontPadding\" format=\"boolean\" />\n        <!-- Set an input filter to constrain the text length to the\n             specified number. -->\n        <attr name=\"maxLength\" format=\"integer\" min=\"0\" />\n        <!-- Place a blurred shadow of text underneath the text, drawn with the\n             specified color. The text shadow produced does not interact with\n             properties on View that are responsible for real time shadows,\n             {@link android.R.styleable#View_elevation elevation} and\n             {@link android.R.styleable#View_translationZ translationZ}. -->\n        <attr name=\"shadowColor\" />\n        <!-- Horizontal offset of the text shadow. -->\n        <attr name=\"shadowDx\" />\n        <!-- Vertical offset of the text shadow. -->\n        <attr name=\"shadowDy\" />\n        <!-- Blur radius of the text shadow. -->\n        <attr name=\"shadowRadius\" />\n        <attr name=\"autoLink\" />\n        <!-- If set to false, keeps the movement method from being set\n             to the link movement method even if autoLink causes links\n             to be found. -->\n        <attr name=\"linksClickable\" format=\"boolean\" />\n        <!-- If set, specifies that this TextView has a numeric input method.\n             The default is false.\n             {@deprecated Use inputType instead.} -->\n        <attr name=\"numeric\">\n            <!-- Input is numeric. -->\n            <flag name=\"integer\" value=\"0x01\" />\n            <!-- Input is numeric, with sign allowed. -->\n            <flag name=\"signed\" value=\"0x03\" />\n            <!-- Input is numeric, with decimals allowed. -->\n            <flag name=\"decimal\" value=\"0x05\" />\n        </attr>\n        <!-- If set, specifies that this TextView has a numeric input method\n             and that these specific characters are the ones that it will\n             accept.\n             If this is set, numeric is implied to be true.\n             The default is false. -->\n        <attr name=\"digits\" format=\"string\" />\n        <!-- If set, specifies that this TextView has a phone number input\n             method. The default is false.\n             {@deprecated Use inputType instead.} -->\n        <attr name=\"phoneNumber\" format=\"boolean\" />\n        <!-- If set, specifies that this TextView should use the specified\n             input method (specified by fully-qualified class name).\n             {@deprecated Use inputType instead.} -->\n        <attr name=\"inputMethod\" format=\"string\" />\n        <!-- If set, specifies that this TextView has a textual input method\n             and should automatically capitalize what the user types.\n             The default is \"none\".\n             {@deprecated Use inputType instead.} -->\n        <attr name=\"capitalize\">\n            <!-- Don't automatically capitalize anything. -->\n            <enum name=\"none\" value=\"0\" />\n            <!-- Capitalize the first word of each sentence. -->\n            <enum name=\"sentences\" value=\"1\" />\n            <!-- Capitalize the first letter of every word. -->\n            <enum name=\"words\" value=\"2\" />\n            <!-- Capitalize every character. -->\n            <enum name=\"characters\" value=\"3\" />\n        </attr>\n        <!-- If set, specifies that this TextView has a textual input method\n             and automatically corrects some common spelling errors.\n             The default is \"false\".\n             {@deprecated Use inputType instead.} -->\n        <attr name=\"autoText\" format=\"boolean\" />\n        <!-- If set, specifies that this TextView has an input method.\n             It will be a textual one unless it has otherwise been specified.\n             For TextView, this is false by default.  For EditText, it is\n             true by default.\n             {@deprecated Use inputType instead.} -->\n        <attr name=\"editable\" format=\"boolean\" />\n        <!-- If set, the text view will include its current complete text\n             inside of its frozen icicle in addition to meta-data such as\n             the current cursor position.  By default this is disabled;\n             it can be useful when the contents of a text view is not stored\n             in a persistent place such as a content provider. For\n             {@link android.widget.EditText} it is always enabled, regardless\n             of the value of the attribute. -->\n        <attr name=\"freezesText\" format=\"boolean\" />\n        <!-- If set, causes words that are longer than the view is wide\n             to be ellipsized instead of broken in the middle.\n             You will often also want to set scrollHorizontally or singleLine\n             as well so that the text as a whole is also constrained to\n             a single line instead of still allowed to be broken onto\n             multiple lines. -->\n        <attr name=\"ellipsize\" />\n        <!-- The drawable to be drawn above the text. -->\n        <attr name=\"drawableTop\" format=\"reference|color\" />\n        <!-- The drawable to be drawn below the text. -->\n        <attr name=\"drawableBottom\" format=\"reference|color\" />\n        <!-- The drawable to be drawn to the left of the text. -->\n        <attr name=\"drawableLeft\" format=\"reference|color\" />\n        <!-- The drawable to be drawn to the right of the text. -->\n        <attr name=\"drawableRight\" format=\"reference|color\" />\n        <!-- The drawable to be drawn to the start of the text. -->\n        <attr name=\"drawableStart\" format=\"reference|color\" />\n        <!-- The drawable to be drawn to the end of the text. -->\n        <attr name=\"drawableEnd\" format=\"reference|color\" />\n        <!-- The padding between the drawables and the text. -->\n        <attr name=\"drawablePadding\" format=\"dimension\" />\n        <!-- Tint to apply to the compound (left, top, etc.) drawables. -->\n        <attr name=\"drawableTint\" format=\"color\" />\n        <!-- Blending mode used to apply the compound (left, top, etc.) drawables tint. -->\n        <attr name=\"drawableTintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n        <!-- Extra spacing between lines of text. The value will not be applied for the last\n             line of text. -->\n        <attr name=\"lineSpacingExtra\" format=\"dimension\" />\n        <!-- Extra spacing between lines of text, as a multiplier. The value will not be applied\n             for the last line of text.-->\n        <attr name=\"lineSpacingMultiplier\" format=\"float\" />\n        <!-- Explicit height between lines of text. If set, this will override the values set\n             for lineSpacingExtra and lineSpacingMultiplier. -->\n        <attr name=\"lineHeight\" format=\"dimension\" />\n        <!-- Distance from the top of the TextView to the first text baseline. If set, this\n             overrides the value set for paddingTop. -->\n        <attr name=\"firstBaselineToTopHeight\" format=\"dimension\" />\n        <!-- Distance from the bottom of the TextView to the last text baseline. If set, this\n             overrides the value set for paddingBottom. -->\n        <attr name=\"lastBaselineToBottomHeight\" format=\"dimension\" />\n        <!-- The number of times to repeat the marquee animation. Only applied if the\n             TextView has marquee enabled. -->\n        <attr name=\"marqueeRepeatLimit\" format=\"integer\">\n            <!-- Indicates that marquee should repeat indefinitely. -->\n            <enum name=\"marquee_forever\" value=\"-1\" />\n        </attr>\n        <attr name=\"inputType\" />\n        <!-- Whether undo should be allowed for editable text. Defaults to true. -->\n        <attr name=\"allowUndo\" format=\"boolean\" />\n        <attr name=\"imeOptions\" />\n        <!-- An addition content type description to supply to the input\n             method attached to the text view, which is private to the\n             implementation of the input method.  This simply fills in\n             the {@link android.view.inputmethod.EditorInfo#privateImeOptions\n             EditorInfo.privateImeOptions} field when the input\n             method is connected. -->\n        <attr name=\"privateImeOptions\" format=\"string\" />\n        <!-- Supply a value for\n             {@link android.view.inputmethod.EditorInfo#actionLabel EditorInfo.actionLabel}\n             used when an input method is connected to the text view. -->\n        <attr name=\"imeActionLabel\" format=\"string\" />\n        <!-- Supply a value for\n             {@link android.view.inputmethod.EditorInfo#actionId EditorInfo.actionId}\n             used when an input method is connected to the text view. -->\n        <attr name=\"imeActionId\" format=\"integer\" />\n        <!-- Reference to an\n             {@link android.R.styleable#InputExtras &lt;input-extras&gt;}\n             XML resource containing additional data to\n             supply to an input method, which is private to the implementation\n             of the input method.  This simply fills in\n             the {@link android.view.inputmethod.EditorInfo#extras\n             EditorInfo.extras} field when the input\n             method is connected. -->\n        <attr name=\"editorExtras\" format=\"reference\" />\n\n        <!-- Reference to a drawable that will be used to display a text selection\n             anchor on the left side of a selection region. -->\n        <attr name=\"textSelectHandleLeft\" />\n        <!-- Reference to a drawable that will be used to display a text selection\n             anchor on the right side of a selection region. -->\n        <attr name=\"textSelectHandleRight\" />\n        <!-- Reference to a drawable that will be used to display a text selection\n             anchor for positioning the cursor within text. -->\n        <attr name=\"textSelectHandle\" />\n        <!-- The layout of the view that is displayed on top of the cursor to paste inside a\n             TextEdit field. -->\n        <attr name=\"textEditPasteWindowLayout\" />\n        <!-- Variation of textEditPasteWindowLayout displayed when the clipboard is empty. -->\n        <attr name=\"textEditNoPasteWindowLayout\" />\n        <!-- Used instead of textEditPasteWindowLayout when the window is moved on the side of the\n             insertion cursor because it would be clipped if it were positioned on top. -->\n        <attr name=\"textEditSidePasteWindowLayout\" />\n        <!-- Variation of textEditSidePasteWindowLayout displayed when the clipboard is empty. -->\n        <attr name=\"textEditSideNoPasteWindowLayout\" />\n\n        <!-- Layout of the TextView item that will populate the suggestion popup window. -->\n        <attr name=\"textEditSuggestionItemLayout\" />\n        <!-- Layout of the container of the suggestion popup window. -->\n        <attr name=\"textEditSuggestionContainerLayout\" />\n        <!-- Style of the highlighted string in the suggestion popup window. -->\n        <attr name=\"textEditSuggestionHighlightStyle\" />\n\n\n        <!-- Reference to a drawable that will be drawn under the insertion cursor. -->\n        <attr name=\"textCursorDrawable\" />\n\n        <!-- Indicates that the content of a non-editable text can be selected. -->\n        <attr name=\"textIsSelectable\" />\n        <!-- Present the text in ALL CAPS. This may use a small-caps form when available. -->\n        <attr name=\"textAllCaps\" />\n        <!-- Elegant text height, especially for less compacted complex script text. -->\n        <attr name=\"elegantTextHeight\" />\n        <!-- Whether to respect the ascent and descent of the fallback fonts that are used in\n        displaying the text. When true, fallback fonts that end up getting used can increase\n        the ascent and descent of the lines that they are used on. -->\n        <attr name=\"fallbackLineSpacing\" format=\"boolean\"/>\n        <!-- Text letter-spacing. -->\n        <attr name=\"letterSpacing\" />\n        <!-- Font feature settings. -->\n        <attr name=\"fontFeatureSettings\" />\n        <!-- Font variation settings. -->\n        <attr name=\"fontVariationSettings\" />\n        <!-- Break strategy (control over paragraph layout). -->\n        <attr name=\"breakStrategy\">\n            <!-- Line breaking uses simple strategy. -->\n            <enum name=\"simple\" value=\"0\" />\n            <!-- Line breaking uses high-quality strategy, including hyphenation. -->\n            <enum name=\"high_quality\" value=\"1\" />\n            <!-- Line breaking strategy balances line lengths. -->\n            <enum name=\"balanced\" value=\"2\" />\n        </attr>\n        <!-- Frequency of automatic hyphenation. -->\n        <attr name=\"hyphenationFrequency\">\n            <!-- No hyphenation. -->\n            <enum name=\"none\" value=\"0\" />\n            <!-- Less frequent hyphenation, useful for informal use cases, such\n            as chat messages. -->\n            <enum name=\"normal\" value=\"1\" />\n            <!-- Standard amount of hyphenation, useful for running text and for\n            screens with limited space for text. -->\n            <enum name=\"full\" value=\"2\" />\n\n            <!-- Same to hyphenationFrequency=\"normal\" but using faster algorithm for measuring\n            hyphenation break points. To make text rendering faster with hyphenation, this algorithm\n             ignores some hyphen character related typographic features, e.g. kerning. -->\n            <enum name=\"normalFast\" value=\"3\" />\n\n            <!-- Same to hyphenationFrequency=\"full\" but using faster algorithm for measuring\n            hyphenation break points. To make text rendering faster with hyphenation, this algorithm\n             ignores some hyphen character related typographic features, e.g. kerning. -->\n            <enum name=\"fullFast\" value=\"4\" />\n        </attr>\n        <!-- Specifies the line-break strategies for text wrapping. -->\n        <attr name=\"lineBreakStyle\">\n            <!-- No line-break rules are used for line breaking. -->\n            <enum name=\"none\" value=\"0\" />\n            <!-- The least restrictive line-break rules are used for line breaking. -->\n            <enum name=\"loose\" value=\"1\" />\n            <!-- The most common line-break rules are used for line breaking. -->\n            <enum name=\"normal\" value=\"2\" />\n            <!-- The most strict line-break rules are used for line breaking. -->\n            <enum name=\"strict\" value=\"3\" />\n        </attr>\n        <!-- Specifies the line-break word strategies for text wrapping.-->\n        <attr name=\"lineBreakWordStyle\">\n            <!-- No line-break word style is used for line breaking. -->\n            <enum name=\"none\" value=\"0\" />\n            <!-- Line breaking is based on phrases, which results in text wrapping only on meaningful words. -->\n            <enum name=\"phrase\" value=\"1\" />\n        </attr>\n        <!-- Specify the type of auto-size. Note that this feature is not supported by EditText,\n        works only for TextView. -->\n        <attr name=\"autoSizeTextType\" format=\"enum\">\n            <!-- No auto-sizing (default). -->\n            <enum name=\"none\" value=\"0\" />\n            <!-- Uniform horizontal and vertical text size scaling to fit within the\n            container. -->\n            <enum name=\"uniform\" value=\"1\" />\n        </attr>\n        <!-- Specify the auto-size step size if <code>autoSizeTextType</code> is set to\n        <code>uniform</code>. The default is 1px. Overwrites\n        <code>autoSizePresetSizes</code> if set. -->\n        <attr name=\"autoSizeStepGranularity\" format=\"dimension\" />\n        <!-- Resource array of dimensions to be used in conjunction with\n        <code>autoSizeTextType</code> set to <code>uniform</code>. Overrides\n        <code>autoSizeStepGranularity</code> if set. -->\n        <attr name=\"autoSizePresetSizes\"/>\n        <!-- The minimum text size constraint to be used when auto-sizing text. -->\n        <attr name=\"autoSizeMinTextSize\" format=\"dimension\" />\n        <!-- The maximum text size constraint to be used when auto-sizing text. -->\n        <attr name=\"autoSizeMaxTextSize\" format=\"dimension\" />\n        <!-- Mode for justification. -->\n        <attr name=\"justificationMode\">\n            <!-- No justification. -->\n            <enum name=\"none\" value=\"0\" />\n            <!-- Justification by stretching word spacing. -->\n            <enum name=\"inter_word\" value = \"1\" />\n            <!-- Justification by stretching letter spacing. -->\n            <!-- @FlaggedApi(\"com.android.text.flags.inter_character_justification\") -->\n            <enum name=\"inter_character\" value = \"2\" />\n        </attr>\n        <!-- Whether to use width of bounding box as a source of automatic line breaking and\n          drawing.\n          If this value is false, the TextView determines the View width, drawing offset and\n          automatic line breaking based on total advances as text widths. By setting true,\n          use glyph bound's as a source of text width.  -->\n        <!-- @FlaggedApi(\"com.android.text.flags.use_bounds_for_width\") -->\n        <attr name=\"useBoundsForWidth\" format=\"boolean\" />\n\n\n        <!-- Whether to shift the drawing offset for prevent clipping start drawing offset.\n          This value is ignored when the useBoundsForWidth attribute is false.\n\n          If this value is false, the TextView draws text from the zero X coordinate. This is\n          useful for aligning multiple TextViews vertically.\n          If this value is true, the TextView shift the drawing offset not to clip the\n          stroke in the region where the X coordinate is negative. -->\n        <!-- @FlaggedApi(\"com.android.text.flags.use_bounds_for_width\") -->\n        <attr name=\"shiftDrawingOffsetForStartOverhang\" format=\"boolean\" />\n        <!-- Whether to use the locale preferred line height for the minimum line height.\n\n          This flag is useful for preventing jitter of entering letters into empty EditText.\n          The line height of the text is determined by the font files used for drawing text in a\n          line. However, in case of the empty text case, the line height cannot be determined and\n          the default line height: usually it is came from a font of Latin script. By making this\n          attribute to true, the TextView/EditText uses a line height that is likely used for the\n          locale associated with the widget. For example, if the system locale is Japanese, the\n          height of the EditText will be adjusted to meet the height of the Japanese font even if\n          the text is empty.\n\n          The default value for EditText is true if targetSdkVersion is\n          {@link android.os.Build.VERSION_CODE#VANILLA_ICE_CREAM} or later, otherwise false.\n          For other TextViews, the default value is false.\n        -->\n        <!-- @FlaggedApi(\"com.android.text.flags.fix_line_height_for_locale\") -->\n        <attr name=\"useLocalePreferredLineHeightForMinimum\" format=\"boolean\" />\n    </declare-styleable>\n    <declare-styleable name=\"TextViewAppearance\">\n        <!-- Base text color, typeface, size, and style. -->\n        <attr name=\"textAppearance\" />\n    </declare-styleable>\n    <declare-styleable name=\"SelectionModeDrawables\">\n        <attr name=\"actionModeSelectAllDrawable\" />\n        <attr name=\"actionModeCutDrawable\" />\n        <attr name=\"actionModeCopyDrawable\" />\n        <attr name=\"actionModePasteDrawable\" />\n    </declare-styleable>\n    <declare-styleable name=\"SuggestionSpan\">\n        <attr name=\"textUnderlineColor\" />\n        <attr name=\"textUnderlineThickness\" />\n    </declare-styleable>\n    <!-- An <code>input-extras</code> is a container for extra data to supply to\n         an input method.  Contains\n         one more more {@link #Extra <extra>} tags.  -->\n    <declare-styleable name=\"InputExtras\">\n    </declare-styleable>\n    <declare-styleable name=\"AutoCompleteTextView\">\n        <!-- Defines the hint displayed in the drop down menu. -->\n        <attr name=\"completionHint\" format=\"string\" />\n        <!-- Defines the hint view displayed in the drop down menu. -->\n        <attr name=\"completionHintView\" format=\"reference\" />\n        <!-- Defines the number of characters that the user must type before\n         completion suggestions are displayed in a drop down menu. -->\n        <attr name=\"completionThreshold\" format=\"integer\" min=\"1\" />\n        <!-- Selector in a drop down list. -->\n        <attr name=\"dropDownSelector\" format=\"reference|color\" />\n        <!-- View to anchor the auto-complete dropdown to. If not specified, the text view itself\n             is used. -->\n        <attr name=\"dropDownAnchor\" format=\"reference\" />\n        <!-- Specifies the basic width of the dropdown. Its value may\n             be a dimension (such as \"12dip\") for a constant width,\n             fill_parent or match_parent to match the width of the\n             screen, or wrap_content to match the width of\n             the anchored view. -->\n        <attr name=\"dropDownWidth\" format=\"dimension\">\n            <!-- The dropdown should fill the width of the screen.\n                 This constant is deprecated starting from API Level 8 and\n                 is replaced by {@code match_parent}. -->\n            <enum name=\"fill_parent\" value=\"-1\" />\n            <!-- The dropdown should fit the width of the screen.\n                 Introduced in API Level 8. -->\n            <enum name=\"match_parent\" value=\"-1\" />\n            <!-- The dropdown should fit the width of its anchor. -->\n            <enum name=\"wrap_content\" value=\"-2\" />\n        </attr>\n        <!-- Specifies the basic height of the dropdown. Its value may\n             be a dimension (such as \"12dip\") for a constant height,\n             fill_parent or match_parent to fill the height of the\n             screen, or wrap_content to match the height of\n             the content of the drop down. -->\n        <attr name=\"dropDownHeight\" format=\"dimension\">\n            <!-- The dropdown should fit the height of the screen.\n                 This constant is deprecated starting from API Level 8 and\n                 is replaced by {@code match_parent}. -->\n            <enum name=\"fill_parent\" value=\"-1\" />\n            <!-- The dropdown should fit the height of the screen.\n                 Introduced in API Level 8. -->\n            <enum name=\"match_parent\" value=\"-1\" />\n            <!-- The dropdown should fit the height of the content. -->\n            <enum name=\"wrap_content\" value=\"-2\" />\n        </attr>\n        <attr name=\"inputType\" />\n        <!-- Theme to use for the completion popup window. -->\n        <attr name=\"popupTheme\" />\n    </declare-styleable>\n    <declare-styleable name=\"PopupWindow\">\n        <!-- The background to use for the popup window. -->\n        <attr name=\"popupBackground\" format=\"reference|color\" />\n        <!-- Window elevation to use for the popup window. -->\n        <attr name=\"popupElevation\" format=\"dimension\" />\n        <!-- The animation style to use for the popup window. -->\n        <attr name=\"popupAnimationStyle\" format=\"reference\" />\n        <!-- Whether the popup window should overlap its anchor view. -->\n        <attr name=\"overlapAnchor\" format=\"boolean\" />\n        <!-- Transition used to move views into the popup window. -->\n        <attr name=\"popupEnterTransition\" format=\"reference\" />\n        <!-- Transition used to move views out of the popup window. -->\n        <attr name=\"popupExitTransition\" format=\"reference\" />\n    </declare-styleable>\n    <declare-styleable name=\"ListPopupWindow\">\n        <!-- Amount of pixels by which the drop down should be offset vertically. -->\n        <attr name=\"dropDownVerticalOffset\" format=\"dimension\" />\n        <!-- Amount of pixels by which the drop down should be offset horizontally. -->\n        <attr name=\"dropDownHorizontalOffset\" format=\"dimension\" />\n    </declare-styleable>\n    <declare-styleable name=\"ViewAnimator\">\n        <!-- Identifier for the animation to use when a view is shown. -->\n        <attr name=\"inAnimation\" format=\"reference\" />\n        <!-- Identifier for the animation to use when a view is hidden. -->\n        <attr name=\"outAnimation\" format=\"reference\" />\n        <!-- Defines whether to animate the current View when the ViewAnimation\n             is first displayed. -->\n        <attr name=\"animateFirstView\" format=\"boolean\" />\n    </declare-styleable>\n    <declare-styleable name=\"ViewFlipper\">\n        <attr name=\"flipInterval\" format=\"integer\" min=\"0\" />\n        <!-- When true, automatically start animating. -->\n        <attr name=\"autoStart\" format=\"boolean\" />\n    </declare-styleable>\n    <declare-styleable name=\"AdapterViewAnimator\">\n        <!-- Identifier for the animation to use when a view is shown. -->\n        <attr name=\"inAnimation\" />\n        <!-- Identifier for the animation to use when a view is hidden. -->\n        <attr name=\"outAnimation\" />\n        <!--Defines whether the animator loops to the first view once it\n        has reached the end of the list. -->\n        <attr name=\"loopViews\" format=\"boolean\" />\n        <!-- Defines whether to animate the current View when the ViewAnimation\n        is first displayed. -->\n        <attr name=\"animateFirstView\" />\n    </declare-styleable>\n    <declare-styleable name=\"AdapterViewFlipper\">\n        <attr name=\"flipInterval\" />\n        <!-- When true, automatically start animating. -->\n        <attr name=\"autoStart\" />\n    </declare-styleable>\n    <declare-styleable name=\"ViewSwitcher\">\n    </declare-styleable>\n    <declare-styleable name=\"ScrollView\">\n        <!-- Defines whether the scrollview should stretch its content to fill the viewport. -->\n        <attr name=\"fillViewport\" format=\"boolean\" />\n    </declare-styleable>\n    <declare-styleable name=\"HorizontalScrollView\">\n        <!-- Defines whether the scrollview should stretch its content to fill the viewport. -->\n        <attr name=\"fillViewport\" />\n    </declare-styleable>\n    <declare-styleable name=\"Spinner\">\n        <!-- The prompt to display when the spinner's dialog is shown. -->\n        <attr name=\"prompt\" format=\"reference\" />\n        <!-- Display mode for spinner options. -->\n        <attr name=\"spinnerMode\" format=\"enum\">\n            <!-- Spinner options will be presented to the user as a dialog window. -->\n            <enum name=\"dialog\" value=\"0\" />\n            <!-- Spinner options will be presented to the user as an inline dropdown\n                 anchored to the spinner widget itself. -->\n            <enum name=\"dropdown\" value=\"1\" />\n        </attr>\n        <!-- List selector to use for spinnerMode=\"dropdown\" display. -->\n        <attr name=\"dropDownSelector\" />\n        <!-- Theme to use for the drop-down or dialog popup window. -->\n        <attr name=\"popupTheme\" />\n        <!-- Background drawable to use for the dropdown in spinnerMode=\"dropdown\". -->\n        <attr name=\"popupBackground\" />\n        <!-- Window elevation to use for the dropdown in spinnerMode=\"dropdown\". -->\n        <attr name=\"popupElevation\" />\n        <!-- Width of the dropdown in spinnerMode=\"dropdown\". -->\n        <attr name=\"dropDownWidth\" />\n        <!-- Reference to a layout to use for displaying a prompt in the dropdown for\n             spinnerMode=\"dropdown\". This layout must contain a TextView with the id\n             {@code @android:id/text1} to be populated with the prompt text. -->\n        <attr name=\"popupPromptView\" format=\"reference\" />\n        <!-- Gravity setting for positioning the currently selected item. -->\n        <attr name=\"gravity\" />\n        <!-- Whether this spinner should mark child views as enabled/disabled when\n             the spinner itself is enabled/disabled. -->\n        <attr name=\"disableChildrenWhenDisabled\" format=\"boolean\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"DatePicker\">\n        <!-- The first day of week according to {@link java.util.Calendar}. -->\n        <attr name=\"firstDayOfWeek\" />\n        <!-- The minimal date shown by this calendar view in mm/dd/yyyy format. -->\n        <attr name=\"minDate\" format=\"string\" />\n        <!-- The maximal date shown by this calendar view in mm/dd/yyyy format. -->\n        <attr name=\"maxDate\" format=\"string\" />\n\n        <!-- Whether the spinners are shown. Only valid for \"spinner\" mode. -->\n        <attr name=\"spinnersShown\" format=\"boolean\" />\n        <!-- Whether the calendar view is shown. Only valid for \"spinner\" mode. -->\n        <attr name=\"calendarViewShown\" format=\"boolean\" />\n\n        <!-- @hide The layout of the date picker. -->\n        <attr name=\"internalLayout\" format=\"reference\"  />\n        <!-- @hide The layout of the legacy DatePicker. -->\n        <attr name=\"legacyLayout\" />\n\n        <!-- The text color for the selected date header text, ex. \"2014\" or\n             \"Tue, Mar 18\". This should be a color state list where the\n             activated state will be used when the year picker or day picker is\n             active.-->\n        <attr name=\"headerTextColor\" format=\"color\" />\n        <!-- The background for the selected date header. -->\n        <attr name=\"headerBackground\" />\n\n        <!-- The list year's text appearance in the list.\n             {@deprecated Use yearListTextColor. }-->\n        <attr name=\"yearListItemTextAppearance\" format=\"reference\" />\n        <!-- @hide The list year's text appearance in the list when activated. -->\n        <attr name=\"yearListItemActivatedTextAppearance\" format=\"reference\" />\n        <!-- The text color list of the calendar. -->\n        <attr name=\"calendarTextColor\" format=\"color\" />\n\n        <!-- Defines the look of the widget. Prior to the L release, the only choice was\n             spinner. As of L, with the Material theme selected, the default layout is calendar,\n             but this attribute can be used to force spinner to be used instead. -->\n        <attr name=\"datePickerMode\">\n            <!-- Date picker with spinner controls to select the date. -->\n            <enum name=\"spinner\" value=\"1\" />\n            <!-- Date picker with calendar to select the date. -->\n            <enum name=\"calendar\" value=\"2\" />\n        </attr>\n\n        <!-- The first year (inclusive), for example \"1940\".\n             {@deprecated Use minDate instead.} -->\n        <attr name=\"startYear\" format=\"integer\" />\n        <!-- The last year (inclusive), for example \"2010\".\n             {@deprecated Use maxDate instead.} -->\n        <attr name=\"endYear\" format=\"integer\" />\n        <!-- The text appearance for the month (ex. May) in the selected date header.\n             {@deprecated Use headerTextColor instead.} -->\n        <attr name=\"headerMonthTextAppearance\" format=\"reference\" />\n        <!-- The text appearance for the day of month (ex. 28) in the selected date header.\n             {@deprecated Use headerTextColor instead.} -->\n        <attr name=\"headerDayOfMonthTextAppearance\" format=\"reference\" />\n        <!-- The text appearance for the year (ex. 2014) in the selected date header.\n             {@deprecated Use headerTextColor instead.} -->\n        <attr name=\"headerYearTextAppearance\" format=\"reference\" />\n        <!-- The background color for the header's day of week.\n             {@deprecated No longer displayed.} -->\n        <attr name=\"dayOfWeekBackground\" format=\"color\" />\n        <!-- The text color for the header's day of week.\n             {@deprecated No longer displayed.} -->\n        <attr name=\"dayOfWeekTextAppearance\" format=\"reference\" />\n        <!-- The list year's selected circle color in the list.\n             {@deprecated No longer displayed.} -->\n        <attr name=\"yearListSelectorColor\" format=\"color\" />\n\n        <!-- @hide Whether this time picker is being displayed within a dialog,\n             in which case it may ignore the requested time picker mode due to\n             space considerations. -->\n        <attr name=\"dialogMode\" format=\"boolean\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"TwoLineListItem\">\n        <attr name=\"mode\">\n            <!-- Always show only the first line. -->\n            <enum name=\"oneLine\" value=\"1\" />\n            <!-- When selected show both lines, otherwise show only the first line.\n                 This is the default mode. -->\n            <enum name=\"collapsing\" value=\"2\" />\n            <!-- Always show both lines. -->\n            <enum name=\"twoLine\" value=\"3\" />\n        </attr>\n    </declare-styleable>\n\n    <!-- SlidingDrawer specific attributes. These attributes are used to configure\n         a SlidingDrawer from XML. -->\n    <declare-styleable name=\"SlidingDrawer\">\n        <!-- Identifier for the child that represents the drawer's handle. -->\n        <attr name=\"handle\" format=\"reference\" />\n        <!-- Identifier for the child that represents the drawer's content. -->\n        <attr name=\"content\" format=\"reference\" />\n        <!-- Orientation of the SlidingDrawer. -->\n        <attr name=\"orientation\" />\n        <!-- Extra offset for the handle at the bottom of the SlidingDrawer. -->\n        <attr name=\"bottomOffset\" format=\"dimension\"  />\n        <!-- Extra offset for the handle at the top of the SlidingDrawer. -->\n        <attr name=\"topOffset\" format=\"dimension\"  />\n        <!-- Indicates whether the drawer can be opened/closed by a single tap\n             on the handle.  (If false, the user must drag or fling, or click\n             using the trackball, to open/close the drawer.)  Default is true. -->\n        <attr name=\"allowSingleTap\" format=\"boolean\" />\n        <!-- Indicates whether the drawer should be opened/closed with an animation\n             when the user clicks the handle. Default is true. -->\n        <attr name=\"animateOnClick\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- GestureOverlayView specific attributes. These attributes are used to configure\n         a GestureOverlayView from XML. -->\n    <declare-styleable name=\"GestureOverlayView\">\n        <!-- Width of the stroke used to draw the gesture. -->\n        <attr name=\"gestureStrokeWidth\" format=\"float\" />\n        <!-- Color used to draw a gesture. -->\n        <attr name=\"gestureColor\" format=\"color\" />\n        <!-- Color used to draw the user's strokes until we are sure it's a gesture. -->\n        <attr name=\"uncertainGestureColor\" format=\"color\" />\n        <!-- Time, in milliseconds, to wait before the gesture fades out after the user\n             is done drawing it. -->\n        <attr name=\"fadeOffset\" format=\"integer\" />\n        <!-- Duration, in milliseconds, of the fade out effect after the user is done\n             drawing a gesture. -->\n        <attr name=\"fadeDuration\" format=\"integer\" />\n        <!-- Defines the type of strokes that define a gesture. -->\n        <attr name=\"gestureStrokeType\">\n            <!-- A gesture is made of only one stroke. -->\n            <enum name=\"single\" value=\"0\" />\n            <!-- A gesture is made of multiple strokes. -->\n            <enum name=\"multiple\" value=\"1\" />\n        </attr>\n        <!-- Minimum length of a stroke before it is recognized as a gesture. -->\n        <attr name=\"gestureStrokeLengthThreshold\" format=\"float\" />\n        <!-- Squareness threshold of a stroke before it is recognized as a gesture. -->\n        <attr name=\"gestureStrokeSquarenessThreshold\" format=\"float\" />\n        <!-- Minimum curve angle a stroke must contain before it is recognized as a gesture. -->\n        <attr name=\"gestureStrokeAngleThreshold\" format=\"float\" />\n        <!-- Defines whether the overlay should intercept the motion events when a gesture\n             is recognized. -->\n        <attr name=\"eventsInterceptionEnabled\" format=\"boolean\" />\n        <!-- Defines whether the gesture will automatically fade out after being recognized. -->\n        <attr name=\"fadeEnabled\" format=\"boolean\" />\n        <!-- Indicates whether horizontal (when the orientation is vertical) or vertical\n             (when orientation is horizontal) strokes automatically define a gesture. -->\n        <attr name=\"orientation\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QuickContactBadge\">\n        <attr name=\"quickContactWindowSize\">\n            <enum name=\"modeSmall\" value=\"1\" />\n            <enum name=\"modeMedium\" value=\"2\" />\n            <enum name=\"modeLarge\" value=\"3\" />\n        </attr>\n    </declare-styleable>\n\n    <!-- ======================================= -->\n    <!-- Widget package parent layout attributes -->\n    <!-- ======================================= -->\n    <eat-comment />\n\n    <declare-styleable name=\"AbsoluteLayout_Layout\">\n        <attr name=\"layout_x\" format=\"dimension\" />\n        <attr name=\"layout_y\" format=\"dimension\" />\n    </declare-styleable>\n    <declare-styleable name=\"LinearLayout_Layout\">\n        <attr name=\"layout_width\" />\n        <attr name=\"layout_height\" />\n        <!-- Indicates how much of the extra space in the LinearLayout is\n        allocated to the view associated with these LayoutParams. Specify\n        0 if the view should not be stretched. Otherwise the extra pixels\n        will be pro-rated among all views whose weight is greater than 0. -->\n        <attr name=\"layout_weight\" format=\"float\" />\n        <!-- Gravity specifies how a component should be placed in its group of cells.\n        The default is {@link android.view.Gravity#TOP}.\n        See {@link android.widget.LinearLayout#setGravity(int)}. -->\n        <attr name=\"layout_gravity\" />\n    </declare-styleable>\n    <declare-styleable name=\"GridLayout_Layout\">\n        <!-- The row boundary delimiting the top of the group of cells\n        occupied by this view. -->\n        <attr name=\"layout_row\" format=\"integer\" />\n        <!-- The row span: the difference between the top and bottom\n        boundaries delimiting the group of cells occupied by this view.\n        The default is one.\n        See {@link android.widget.GridLayout.Spec}. -->\n        <attr name=\"layout_rowSpan\" format=\"integer\" min=\"1\" />\n        <!-- The relative proportion of vertical space that should be allocated to this view\n        during excess space distribution. -->\n        <attr name=\"layout_rowWeight\" format=\"float\" />\n        <!-- The column boundary delimiting the left of the group of cells\n        occupied by this view. -->\n        <attr name=\"layout_column\" />\n        <!-- The column span: the difference between the right and left\n        boundaries delimiting the group of cells occupied by this view.\n        The default is one.\n        See {@link android.widget.GridLayout.Spec}. -->\n        <attr name=\"layout_columnSpan\" format=\"integer\" min=\"1\" />\n        <!-- The relative proportion of horizontal space that should be allocated to this view\n        during excess space distribution. -->\n        <attr name=\"layout_columnWeight\" format=\"float\" />\n        <!-- Gravity specifies how a component should be placed in its group of cells.\n        The default is LEFT | BASELINE.\n        See {@link android.widget.GridLayout.LayoutParams#setGravity(int)}. -->\n        <attr name=\"layout_gravity\" />\n    </declare-styleable>\n    <declare-styleable name=\"FrameLayout_Layout\">\n        <attr name=\"layout_gravity\" />\n    </declare-styleable>\n    <declare-styleable name=\"RelativeLayout_Layout\">\n        <!-- Positions the right edge of this view to the left of the given anchor view ID.\n             Accommodates right margin of this view and left margin of anchor view. -->\n        <attr name=\"layout_toLeftOf\" format=\"reference\" />\n        <!-- Positions the left edge of this view to the right of the given anchor view ID.\n            Accommodates left margin of this view and right margin of anchor view. -->\n        <attr name=\"layout_toRightOf\" format=\"reference\" />\n        <!-- Positions the bottom edge of this view above the given anchor view ID.\n            Accommodates bottom margin of this view and top margin of anchor view. -->\n        <attr name=\"layout_above\" format=\"reference\" />\n        <!-- Positions the top edge of this view below the given anchor view ID.\n            Accommodates top margin of this view and bottom margin of anchor view. -->\n        <attr name=\"layout_below\" format=\"reference\" />\n        <!-- Positions the baseline of this view on the baseline of the given anchor view ID. -->\n        <attr name=\"layout_alignBaseline\" format=\"reference\" />\n        <!-- Makes the left edge of this view match the left edge of the given anchor view ID.\n            Accommodates left margin. -->\n        <attr name=\"layout_alignLeft\" format=\"reference\" />\n        <!-- Makes the top edge of this view match the top edge of the given anchor view ID.\n            Accommodates top margin. -->\n        <attr name=\"layout_alignTop\" format=\"reference\" />\n        <!-- Makes the right edge of this view match the right edge of the given anchor view ID.\n            Accommodates right margin. -->\n        <attr name=\"layout_alignRight\" format=\"reference\" />\n        <!-- Makes the bottom edge of this view match the bottom edge of the given anchor view ID.\n            Accommodates bottom margin. -->\n        <attr name=\"layout_alignBottom\" format=\"reference\" />\n        <!-- If true, makes the left edge of this view match the left edge of the parent.\n            Accommodates left margin. -->\n        <attr name=\"layout_alignParentLeft\" format=\"boolean\" />\n        <!-- If true, makes the top edge of this view match the top edge of the parent.\n            Accommodates top margin. -->\n        <attr name=\"layout_alignParentTop\" format=\"boolean\" />\n        <!-- If true, makes the right edge of this view match the right edge of the parent.\n            Accommodates right margin. -->\n        <attr name=\"layout_alignParentRight\" format=\"boolean\" />\n        <!-- If true, makes the bottom edge of this view match the bottom edge of the parent.\n            Accommodates bottom margin. -->\n        <attr name=\"layout_alignParentBottom\" format=\"boolean\" />\n        <!-- If true, centers this child horizontally and vertically within its parent. -->\n        <attr name=\"layout_centerInParent\" format=\"boolean\" />\n        <!-- If true, centers this child horizontally within its parent. -->\n        <attr name=\"layout_centerHorizontal\" format=\"boolean\" />\n        <!-- If true, centers this child vertically within its parent. -->\n        <attr name=\"layout_centerVertical\" format=\"boolean\" />\n        <!-- If set to true, the parent will be used as the anchor when the anchor cannot be\n             be found for layout_toLeftOf, layout_toRightOf, etc. -->\n        <attr name=\"layout_alignWithParentIfMissing\" format=\"boolean\" />\n        <!-- Positions the end edge of this view to the start of the given anchor view ID.\n             Accommodates end margin of this view and start margin of anchor view. -->\n        <attr name=\"layout_toStartOf\" format=\"reference\" />\n        <!-- Positions the start edge of this view to the end of the given anchor view ID.\n             Accommodates start margin of this view and end margin of anchor view. -->\n        <attr name=\"layout_toEndOf\" format=\"reference\" />\n        <!-- Makes the start edge of this view match the start edge of the given anchor view ID.\n            Accommodates start margin. -->\n        <attr name=\"layout_alignStart\" format=\"reference\" />\n        <!-- Makes the end edge of this view match the end edge of the given anchor view ID.\n            Accommodates end margin. -->\n        <attr name=\"layout_alignEnd\" format=\"reference\" />\n        <!-- If true, makes the start edge of this view match the start edge of the parent.\n            Accommodates start margin. -->\n        <attr name=\"layout_alignParentStart\" format=\"boolean\" />\n        <!-- If true, makes the end edge of this view match the end edge of the parent.\n            Accommodates end margin. -->\n        <attr name=\"layout_alignParentEnd\" format=\"boolean\" />\n    </declare-styleable>\n    <declare-styleable name=\"VerticalSlider_Layout\">\n        <attr name=\"layout_scale\" format=\"float\" />\n    </declare-styleable>\n\n    <!-- @hide -->\n    <declare-styleable name=\"WeightedLinearLayout\">\n        <attr name=\"majorWeightMin\" format=\"float\" />\n        <attr name=\"minorWeightMin\" format=\"float\" />\n        <attr name=\"majorWeightMax\" format=\"float\" />\n        <attr name=\"minorWeightMax\" format=\"float\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"CalendarView\">\n        <!-- The first day of week according to {@link java.util.Calendar}. -->\n        <attr name=\"firstDayOfWeek\" format=\"integer\" />\n        <!-- The minimal date shown by this calendar view in mm/dd/yyyy format. -->\n        <attr name=\"minDate\" />\n        <!-- The maximal date shown by this calendar view in mm/dd/yyyy format. -->\n        <attr name=\"maxDate\" />\n        <!-- The text appearance for the month and year in the calendar header. -->\n        <attr name=\"monthTextAppearance\" format=\"reference\" />\n        <!-- The text appearance for the week day abbreviation in the calendar header. -->\n        <attr name=\"weekDayTextAppearance\" format=\"reference\" />\n        <!-- The text appearance for the day numbers in the calendar grid. -->\n        <attr name=\"dateTextAppearance\" format=\"reference\" />\n        <!-- @hide The background color used for the day selection indicator. -->\n        <attr name=\"daySelectorColor\" format=\"color\" />\n        <!-- @hide The background color used for the day highlight indicator. -->\n        <attr name=\"dayHighlightColor\" format=\"color\" />\n        <!-- @hide Which style of calendar delegate to use. -->\n        <attr name=\"calendarViewMode\">\n            <enum name=\"holo\" value=\"0\" />\n            <enum name=\"material\" value=\"1\" />\n        </attr>\n\n        <!-- @deprecated Whether do show week numbers. -->\n        <attr name=\"showWeekNumber\" format=\"boolean\" />\n        <!-- @deprecated The number of weeks to be shown. -->\n        <attr name=\"shownWeekCount\" format=\"integer\"/>\n        <!-- @deprecated The background color for the selected week. -->\n        <attr name=\"selectedWeekBackgroundColor\" format=\"color|reference\" />\n        <!-- @deprecated The color for the dates of the focused month. -->\n        <attr name=\"focusedMonthDateColor\" format=\"color|reference\" />\n        <!-- @deprecated The color for the dates of an unfocused month. -->\n        <attr name=\"unfocusedMonthDateColor\" format=\"color|reference\" />\n        <!-- @deprecated The color for the week numbers. -->\n        <attr name=\"weekNumberColor\" format=\"color|reference\" />\n        <!-- @deprecated The color for the separator line between weeks. -->\n        <attr name=\"weekSeparatorLineColor\" format=\"color|reference\" />\n        <!-- @deprecated Drawable for the vertical bar shown at the beginning and at the end of the selected date. -->\n        <attr name=\"selectedDateVerticalBar\" format=\"reference\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"NumberPicker\">\n        <!-- @hide Color for the solid color background if such for optimized rendering. -->\n        <attr name=\"solidColor\" format=\"color|reference\" />\n        <!-- @hide The divider for making the selection area. -->\n        <attr name=\"selectionDivider\" format=\"reference\" />\n        <!-- The height of the selection divider. -->\n        <attr name=\"selectionDividerHeight\" format=\"dimension\" />\n        <!-- @hide The distance between the two selection dividers. -->\n        <attr name=\"selectionDividersDistance\" format=\"dimension\" />\n        <!-- @hide The min height of the NumberPicker. -->\n        <attr name=\"internalMinHeight\" format=\"dimension\" />\n        <!-- @hide The max height of the NumberPicker. -->\n        <attr name=\"internalMaxHeight\" format=\"dimension\" />\n        <!-- @hide The min width of the NumberPicker. -->\n        <attr name=\"internalMinWidth\" format=\"dimension\" />\n        <!-- @hide The max width of the NumberPicker. -->\n        <attr name=\"internalMaxWidth\" format=\"dimension\" />\n        <!-- @hide The layout of the number picker. -->\n        <attr name=\"internalLayout\" />\n        <!-- @hide The drawable for pressed virtual (increment/decrement) buttons. -->\n        <attr name=\"virtualButtonPressedDrawable\" format=\"reference\"/>\n        <!-- @hide If true then the selector wheel is hidden until the picker has focus. -->\n        <attr name=\"hideWheelUntilFocused\" format=\"boolean\"/>\n    </declare-styleable>\n\n    <declare-styleable name=\"TimePicker\">\n        <!-- @hide The layout of the legacy time picker. -->\n        <attr name=\"legacyLayout\" format=\"reference\" />\n        <!-- @hide The layout of the time picker. -->\n        <attr name=\"internalLayout\" />\n\n        <!-- The text color for the selected time header text, ex. \"12\" or\n             \"PM\". This should be a color state list where the activated state\n             will be used when the minute picker or hour picker is active.-->\n        <attr name=\"headerTextColor\" />\n        <!-- The background for the header containing the currently selected time. -->\n        <attr name=\"headerBackground\" />\n\n        <!-- The color for the hours/minutes numbers. This should be a color\n             state list where the activated state will be used when the number\n             is active.-->\n        <attr name=\"numbersTextColor\" format=\"color\" />\n        <!-- The color for the inner hours numbers used in 24-hour mode. This\n             should be a color state list where the activated state will be\n             used when the number is active.-->\n        <attr name=\"numbersInnerTextColor\" format=\"color\" />\n        <!-- The background color for the hours/minutes numbers. -->\n        <attr name=\"numbersBackgroundColor\" format=\"color\" />\n        <!-- The color for the hours/minutes selector. -->\n        <attr name=\"numbersSelectorColor\" format=\"color\" />\n\n        <!-- Defines the look of the widget. Prior to the L release, the only choice was\n             spinner. As of L, with the Material theme selected, the default layout is clock,\n             but this attribute can be used to force spinner to be used instead. -->\n        <attr name=\"timePickerMode\">\n            <!-- Time picker with spinner controls to select the time. -->\n            <enum name=\"spinner\" value=\"1\" />\n            <!-- Time picker with clock face to select the time. -->\n            <enum name=\"clock\" value=\"2\" />\n        </attr>\n\n        <!-- The text appearance for the AM/PM header.\n             @deprecated Use headerTextColor instead. -->\n        <attr name=\"headerAmPmTextAppearance\" format=\"reference\" />\n        <!-- The text appearance for the time header.\n             @deprecated Use headerTextColor instead. -->\n        <attr name=\"headerTimeTextAppearance\" format=\"reference\" />\n        <!-- The color for the AM/PM selectors.\n             {@deprecated Use headerTextColor instead.}-->\n        <attr name=\"amPmTextColor\" format=\"color\" />\n        <!-- The background color state list for the AM/PM selectors.\n             {@deprecated Use headerBackground instead.}-->\n        <attr name=\"amPmBackgroundColor\" format=\"color\" />\n\n        <!-- @hide Whether this time picker is being displayed within a dialog,\n             in which case it may ignore the requested time picker mode due to\n             space considerations. -->\n        <attr name=\"dialogMode\" />\n    </declare-styleable>\n\n    <!-- ========================= -->\n    <!-- Drawable class attributes -->\n    <!-- ========================= -->\n    <eat-comment />\n\n    <!-- Base attributes that are available to all Drawable objects. -->\n    <declare-styleable name=\"Drawable\">\n        <!-- Provides initial visibility state of the drawable; the default\n             value is false.  See\n             {@link android.graphics.drawable.Drawable#setVisible}. -->\n        <attr name=\"visible\" format=\"boolean\" />\n        <!-- Indicates if the drawable needs to be mirrored when its layout direction is\n             RTL (right-to-left).  See\n             {@link android.graphics.drawable.Drawable#setAutoMirrored}. -->\n        <attr name=\"autoMirrored\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- Drawable class used to wrap other drawables. -->\n    <declare-styleable name=\"DrawableWrapper\">\n        <!-- The wrapped drawable. -->\n        <attr name=\"drawable\" />\n    </declare-styleable>\n\n    <!-- Drawable used to render several states. Each state is represented by\n         a child drawable. -->\n    <declare-styleable name=\"StateListDrawable\">\n        <!-- Indicates whether the drawable should be initially visible. -->\n        <attr name=\"visible\" />\n        <!-- If true, allows the drawable's padding to change based on the\n             current state that is selected.  If false, the padding will\n             stay the same (based on the maximum padding of all the states).\n             Enabling this feature requires that the owner of the drawable\n             deal with performing layout when the state changes, which is\n             often not supported. -->\n        <attr name=\"variablePadding\" format=\"boolean\" />\n        <!-- If true, the drawable's reported internal size will remain\n             constant as the state changes; the size is the maximum of all\n             of the states.  If false, the size will vary based on the\n             current state. -->\n        <attr name=\"constantSize\" format=\"boolean\" />\n        <!-- Enables or disables dithering of the bitmap if the bitmap does not have the\n             same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with\n             an RGB 565 screen). -->\n        <attr name=\"dither\" format=\"boolean\" />\n        <!-- Amount of time (in milliseconds) to fade in a new state drawable. -->\n        <attr name=\"enterFadeDuration\" format=\"integer\" />\n        <!-- Amount of time (in milliseconds) to fade out an old state drawable. -->\n        <attr name=\"exitFadeDuration\" format=\"integer\" />\n        <!-- Indicates if the drawable needs to be mirrored when its layout direction is\n             RTL (right-to-left). -->\n        <attr name=\"autoMirrored\"/>\n    </declare-styleable>\n\n    <!-- Drawable used to render several states with animated transitions. Each state\n         is represented by a child drawable with an optional keyframe ID. -->\n    <declare-styleable name=\"AnimatedStateListDrawable\">\n        <!-- Indicates whether the drawable should be initially visible. -->\n        <attr name=\"visible\" />\n        <!-- If true, allows the drawable's padding to change based on the\n             current state that is selected.  If false, the padding will\n             stay the same (based on the maximum padding of all the states).\n             Enabling this feature requires that the owner of the drawable\n             deal with performing layout when the state changes, which is\n             often not supported. -->\n        <attr name=\"variablePadding\" />\n        <!-- If true, the drawable's reported internal size will remain\n             constant as the state changes; the size is the maximum of all\n             of the states.  If false, the size will vary based on the\n             current state. -->\n        <attr name=\"constantSize\" />\n        <!-- Enables or disables dithering of the bitmap if the bitmap does not have the\n             same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with\n             an RGB 565 screen). -->\n        <attr name=\"dither\" />\n        <!-- Amount of time (in milliseconds) to fade in a new state drawable. -->\n        <attr name=\"enterFadeDuration\" />\n        <!-- Amount of time (in milliseconds) to fade out an old state drawable. -->\n        <attr name=\"exitFadeDuration\" />\n        <!-- Indicates if the drawable needs to be mirrored when its layout direction is\n             RTL (right-to-left). -->\n        <attr name=\"autoMirrored\"/>\n    </declare-styleable>\n\n    <!-- Represents a single state inside a StateListDrawable. -->\n    <declare-styleable name=\"StateListDrawableItem\">\n        <!-- Reference to a drawable resource to use for the state. If not\n             given, the drawable must be defined by the first child tag. -->\n        <attr name=\"drawable\" />\n    </declare-styleable>\n\n    <!-- Transition used to animate between states with keyframe IDs. -->\n    <declare-styleable name=\"AnimatedStateListDrawableItem\">\n        <!-- Reference to a drawable resource to use for the frame.  If not\n             given, the drawable must be defined by the first child tag. -->\n        <attr name=\"drawable\" />\n        <!-- Keyframe identifier for use in specifying transitions. -->\n        <attr name=\"id\" />\n    </declare-styleable>\n\n    <!-- Transition used to animate between states with keyframe IDs. -->\n    <declare-styleable name=\"AnimatedStateListDrawableTransition\">\n        <!-- Keyframe identifier for the starting state. -->\n        <attr name=\"fromId\" format=\"reference\" />\n        <!-- Keyframe identifier for the ending state. -->\n        <attr name=\"toId\" format=\"reference\" />\n        <!-- Reference to a animation drawable resource to use for the frame.  If not\n             given, the animation drawable must be defined by the first child tag. -->\n        <attr name=\"drawable\" />\n        <!-- Whether this transition is reversible. -->\n        <attr name=\"reversible\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- Drawable used to render several animated frames. -->\n    <declare-styleable name=\"AnimationDrawable\">\n        <attr name=\"visible\" />\n        <attr name=\"variablePadding\" />\n        <!-- If true, the animation will only run a single time and then\n             stop.  If false (the default), it will continually run,\n             restarting at the first frame after the last has finished. -->\n        <attr name=\"oneshot\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- Represents a single frame inside an AnimationDrawable. -->\n    <declare-styleable name=\"AnimationDrawableItem\">\n        <!-- Amount of time (in milliseconds) to display this frame. -->\n        <attr name=\"duration\" format=\"integer\" />\n        <!-- Reference to a drawable resource to use for the frame.  If not\n             given, the drawable must be defined by the first child tag. -->\n        <attr name=\"drawable\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- Attributes that can be assigned to a StateListAnimator item. -->\n    <declare-styleable name=\"StateListAnimatorItem\">\n        <attr name=\"animation\"/>\n    </declare-styleable>\n\n    <!-- Attributes that can be assigned to a ColorStateList item. -->\n    <declare-styleable name=\"ColorStateListItem\">\n        <!-- Base color for this state. -->\n        <attr name=\"color\" />\n        <!-- Alpha multiplier applied to the base color. -->\n        <attr name=\"alpha\" />\n        <!-- Perceptual luminance applied to the base color. From 0 to 100. -->\n        <attr name=\"lStar\" format=\"float\" />\n    </declare-styleable>\n\n    <!-- Drawable used to render according to the animation scale. Esp. when it is 0 due to battery\n         saver mode. It should contain one animatable drawable and one static drawable.\n         @hide -->\n    <declare-styleable name=\"AnimationScaleListDrawable\">\n    </declare-styleable>\n\n    <!-- Attributes that can be assigned to a AnimationScaleListDrawable item.\n         @hide -->\n    <declare-styleable name=\"AnimationScaleListDrawableItem\">\n        <!-- Reference to a drawable resource to use for the state. If not\n             given, the drawable must be defined by the first child tag. -->\n        <attr name=\"drawable\" />\n    </declare-styleable>\n\n\n    <!-- Drawable used to render a geometric shape, with a gradient or a solid color. -->\n    <declare-styleable name=\"GradientDrawable\">\n        <!-- Indicates whether the drawable should intially be visible. -->\n        <attr name=\"visible\" />\n        <!-- Enables or disables dithering. -->\n        <attr name=\"dither\" />\n        <!-- Indicates what shape to fill with a gradient. -->\n        <attr name=\"shape\">\n            <!-- Rectangle shape, with optional rounder corners. -->\n            <enum name=\"rectangle\" value=\"0\" />\n            <!-- Oval shape. -->\n            <enum name=\"oval\" value=\"1\" />\n            <!-- Line shape. -->\n            <enum name=\"line\" value=\"2\" />\n            <!-- Ring shape. -->\n            <enum name=\"ring\" value=\"3\" />\n            <!-- ARC shape. -->\n            <enum name=\"arc\" value=\"4\"/>\n        </attr>\n        <!-- Inner radius of the ring expressed as a ratio of the ring's width. For instance,\n             if innerRadiusRatio=9, then the inner radius equals the ring's width divided by 9.\n             This value is ignored if innerRadius is defined. Default value is 9. -->\n        <attr name=\"innerRadiusRatio\" format=\"float\" />\n        <!-- Thickness of the ring expressed as a ratio of the ring's width. For instance,\n             if thicknessRatio=3, then the thickness equals the ring's width divided by 3.\n             This value is ignored if innerRadius is defined. Default value is 3. -->\n        <attr name=\"thicknessRatio\" format=\"float\" />\n        <!-- Inner radius of the ring. When defined, innerRadiusRatio is ignored. -->\n        <attr name=\"innerRadius\" format=\"dimension\" />\n        <!-- Thickness of the ring. When defined, thicknessRatio is ignored. -->\n        <attr name=\"thickness\" format=\"dimension\" />\n        <!-- Whether the drawable level value (see\n             {@link android.graphics.drawable.Drawable#getLevel()}) is used to scale the shape.\n             Scaling behavior depends on the shape type. For \"ring\", the angle is scaled from 0 to\n             360. For all other types, there is no effect. The default value is true. -->\n        <attr name=\"useLevel\" />\n        <!-- If set, specifies the color to apply to the drawable as a tint. By default,\n             no tint is applied. May be a color state list. -->\n        <attr name=\"tint\" />\n        <!-- When a tint color is set, specifies its Porter-Duff blending mode. The\n             default value is src_in, which treats the drawable as an alpha mask. -->\n        <attr name=\"tintMode\" />\n        <!-- Left optical inset. -->\n        <attr name=\"opticalInsetLeft\" />\n        <!-- Top optical inset. -->\n        <attr name=\"opticalInsetTop\" />\n        <!-- Right optical inset. -->\n        <attr name=\"opticalInsetRight\" />\n        <!-- Bottom optical inset. -->\n        <attr name=\"opticalInsetBottom\" />\n        <!-- Attributes that customize the stroke line cap. @hide -->\n        <attr name=\"strokeCap\" format=\"enum\">\n            <enum name=\"butt\" value=\"0\"/>\n            <enum name=\"round\" value=\"1\"/>\n            <enum name=\"square\" value=\"2\"/>\n        </attr>\n    </declare-styleable>\n\n    <!-- Used to specify the size of the shape for GradientDrawable. -->\n    <declare-styleable name=\"GradientDrawableSize\">\n        <!-- Width of the gradient shape. -->\n        <attr name=\"width\" />\n        <!-- Height of the gradient shape. -->\n        <attr name=\"height\" />\n    </declare-styleable>\n\n    <!-- Used to describe the gradient used to fill the shape of a GradientDrawable. -->\n    <declare-styleable name=\"GradientDrawableGradient\">\n        <!-- Start color of the gradient. -->\n        <attr name=\"startColor\" format=\"color\" />\n        <!-- Optional center color. For linear gradients, use centerX or centerY to place the center\n             color. -->\n        <attr name=\"centerColor\" format=\"color\" />\n        <!-- End color of the gradient. -->\n        <attr name=\"endColor\" format=\"color\" />\n        <!-- Whether the drawable level value (see\n             {@link android.graphics.drawable.Drawable#getLevel()}) is used to scale the gradient.\n             Scaling behavior varies based on gradient type. For \"linear\", adjusts the ending\n             position along the gradient's axis of orientation. For \"radial\", adjusts the outer\n             radius. For \"sweep\", adjusts the ending angle. The default value is false. -->\n        <attr name=\"useLevel\" format=\"boolean\" />\n        <!-- Angle of the gradient, used only with linear gradient. Must be a multiple of 45 in the\n             range [0, 315]. -->\n        <attr name=\"angle\" format=\"float\" />\n        <!-- Type of gradient. The default type is linear. -->\n        <attr name=\"type\">\n            <!-- Linear gradient extending across the center point. -->\n            <enum name=\"linear\" value=\"0\" />\n            <!-- Radial gradient extending from the center point outward. -->\n            <enum name=\"radial\" value=\"1\" />\n            <!-- Sweep (or angular) gradient sweeping counter-clockwise around the center point. -->\n            <enum name=\"sweep\"  value=\"2\" />\n        </attr>\n        <!-- X-position of the center point of the gradient within the shape as a fraction of the\n             width. The default value is 0.5. -->\n        <attr name=\"centerX\" format=\"float|fraction\" />\n        <!-- Y-position of the center point of the gradient within the shape as a fraction of the\n             height. The default value is 0.5. -->\n        <attr name=\"centerY\" format=\"float|fraction\" />\n        <!-- Radius of the gradient, used only with radial gradient. May be an explicit dimension\n             or a fractional value relative to the shape's minimum dimension. -->\n        <attr name=\"gradientRadius\" format=\"float|fraction|dimension\" />\n    </declare-styleable>\n\n    <!-- Used to fill the shape of GradientDrawable with a solid color. -->\n    <declare-styleable name=\"GradientDrawableSolid\">\n        <!-- Solid color for the gradient shape. -->\n        <attr name=\"color\" format=\"color\" />\n    </declare-styleable>\n\n    <!-- Used to describe the optional stroke of a GradientDrawable. -->\n    <declare-styleable name=\"GradientDrawableStroke\">\n        <!-- Width of the gradient shape's stroke. -->\n        <attr name=\"width\" />\n        <!-- Color of the gradient shape's stroke. -->\n        <attr name=\"color\" />\n        <!-- Length of a dash in the stroke. -->\n        <attr name=\"dashWidth\" format=\"dimension\" />\n        <!-- Gap between dashes in the stroke. -->\n        <attr name=\"dashGap\" format=\"dimension\" />\n    </declare-styleable>\n\n    <!-- Describes the corners for the rectangle shape of a GradientDrawable.\n         This can be used to render rounded corners. -->\n    <declare-styleable name=\"DrawableCorners\">\n        <!-- Defines the radius of the four corners. -->\n        <attr name=\"radius\" format=\"dimension\" />\n        <!-- Radius of the top left corner. -->\n        <attr name=\"topLeftRadius\" format=\"dimension\" />\n        <!-- Radius of the top right corner. -->\n        <attr name=\"topRightRadius\" format=\"dimension\" />\n        <!-- Radius of the bottom left corner. -->\n        <attr name=\"bottomLeftRadius\" format=\"dimension\" />\n        <!-- Radius of the bottom right corner. -->\n        <attr name=\"bottomRightRadius\" format=\"dimension\" />\n    </declare-styleable>\n\n    <!-- Used to specify the optional padding of a GradientDrawable. -->\n    <declare-styleable name=\"GradientDrawablePadding\">\n        <!-- Amount of left padding inside the gradient shape. -->\n        <attr name=\"left\" format=\"dimension\" />\n        <!-- Amount of top padding inside the gradient shape. -->\n        <attr name=\"top\" format=\"dimension\" />\n        <!-- Amount of right padding inside the gradient shape. -->\n        <attr name=\"right\" format=\"dimension\" />\n        <!-- Amount of bottom padding inside the gradient shape. -->\n        <attr name=\"bottom\" format=\"dimension\" />\n    </declare-styleable>\n\n    <!-- Drawable used to render several drawables stacked on top of each other.\n         Each child drawable can be controlled individually. -->\n    <declare-styleable name=\"LayerDrawable\">\n        <!-- Indicates the opacity of the layer. This can be useful to allow the\n              system to enable drawing optimizations. The default value is\n              translucent. -->\n        <attr name=\"opacity\">\n            <!-- Indicates that the layer is opaque and contains no transparent\n                 nor translucent pixels. -->\n            <enum name=\"opaque\" value=\"-1\" />\n            <!-- The layer is completely transparent (no pixel will be drawn). -->\n            <enum name=\"transparent\" value=\"-2\" />\n            <!-- The layer has translucent pixels. -->\n            <enum name=\"translucent\" value=\"-3\" />\n        </attr>\n        <!-- Indicates if the drawable needs to be mirrored when its layout direction is\n             RTL (right-to-left). -->\n        <attr name=\"autoMirrored\" />\n        <!-- Indicates how layer padding should affect the bounds of subsequent layers.\n             The default padding mode value is nest. -->\n        <attr name=\"paddingMode\">\n            <!-- Nest each layer inside the padding of the previous layer. -->\n            <enum name=\"nest\" value=\"0\" />\n            <!-- Stack each layer directly atop the previous layer. -->\n            <enum name=\"stack\" value=\"1\" />\n        </attr>\n        <!-- Explicit top padding. Overrides child padding. -->\n        <attr name=\"paddingTop\" />\n        <!-- Explicit bottom padding. Overrides child padding. -->\n        <attr name=\"paddingBottom\" />\n        <!-- Explicit left padding. Overrides child padding. -->\n        <attr name=\"paddingLeft\" />\n        <!-- Explicit right padding. Overrides child padding. -->\n        <attr name=\"paddingRight\" />\n        <!-- Explicit start padding. Overrides child padding. Takes precedence\n             over absolute padding (for example, left when layout direction is LTR). -->\n        <attr name=\"paddingStart\" />\n        <!-- Explicit end padding. Overrides child padding. Takes precedence\n             over absolute padding (for example, right when layout direction is LTR). -->\n        <attr name=\"paddingEnd\" />\n    </declare-styleable>\n\n    <!-- Describes an item (or child) of a LayerDrawable. -->\n    <declare-styleable name=\"LayerDrawableItem\">\n        <!-- Left inset to apply to the layer. -->\n        <attr name=\"left\" />\n        <!-- Top inset to apply to the layer. -->\n        <attr name=\"top\" />\n        <!-- Right inset to apply to the layer. -->\n        <attr name=\"right\" />\n        <!-- Bottom inset to apply to the layer. -->\n        <attr name=\"bottom\" />\n        <!-- Start inset to apply to the layer. Overrides {@code left} or\n             {@code right} depending on layout direction. -->\n        <attr name=\"start\" format=\"dimension\" />\n        <!-- End inset to apply to the layer. Overrides {@code left} or\n             {@code right} depending on layout direction. -->\n        <attr name=\"end\" format=\"dimension\" />\n        <!-- Width of the layer. Defaults to the layer's intrinsic width. -->\n        <attr name=\"width\" />\n        <!-- Height of the layer. Defaults to the layer's intrinsic height. -->\n        <attr name=\"height\" />\n        <!-- Gravity used to align the layer within its container. If no value\n             is specified, the default behavior depends on whether an explicit\n             width or height has been set, If no dimension is set, gravity in\n             that direction defaults to {@code fill_horizontal} or\n             {@code fill_vertical}; otherwise, it defaults to {@code left} or\n             {@code top}. -->\n        <attr name=\"gravity\" />\n        <!-- Drawable used to render the layer. -->\n        <attr name=\"drawable\" />\n        <!-- Identifier of the layer. This can be used to retrieve the layer\n             from a drawable container. -->\n        <attr name=\"id\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"LevelListDrawableItem\">\n        <!-- The minimum level allowed for this item. -->\n        <attr name=\"minLevel\" format=\"integer\" />\n        <!-- The maximum level allowed for this item. -->\n        <attr name=\"maxLevel\" format=\"integer\" />\n        <attr name=\"drawable\" />\n    </declare-styleable>\n\n    <!-- Drawable used to rotate another drawable. -->\n    <declare-styleable name=\"RotateDrawable\">\n        <attr name=\"visible\" />\n        <attr name=\"fromDegrees\" format=\"float\" />\n        <attr name=\"toDegrees\" format=\"float\" />\n        <attr name=\"pivotX\" format=\"float|fraction\" />\n        <attr name=\"pivotY\" format=\"float|fraction\" />\n        <attr name=\"drawable\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"AnimatedRotateDrawable\">\n        <attr name=\"visible\" />\n        <attr name=\"frameDuration\" format=\"integer\" />\n        <attr name=\"framesCount\" format=\"integer\" />\n        <attr name=\"pivotX\" />\n        <attr name=\"pivotY\" />\n        <attr name=\"drawable\" />\n    </declare-styleable>\n\n    <!-- Drawable used to render the Material progress indicator. -->\n    <declare-styleable name=\"MaterialProgressDrawable\">\n        <attr name=\"visible\" />\n        <attr name=\"thickness\" />\n        <attr name=\"innerRadius\" />\n        <attr name=\"width\" />\n        <attr name=\"height\" />\n        <attr name=\"color\" />\n    </declare-styleable>\n\n    <!-- Drawable used to wrap and inset another drawable. -->\n    <declare-styleable name=\"InsetDrawable\">\n        <attr name=\"visible\" />\n        <attr name=\"drawable\" />\n        <attr name=\"inset\"  format=\"fraction|dimension\"/>\n        <attr name=\"insetLeft\" format=\"fraction|dimension\" />\n        <attr name=\"insetRight\" format=\"fraction|dimension\" />\n        <attr name=\"insetTop\" format=\"fraction|dimension\" />\n        <attr name=\"insetBottom\" format=\"fraction|dimension\" />\n    </declare-styleable>\n\n    <!-- Drawable used to draw animated images (gif). -->\n    <declare-styleable name=\"AnimatedImageDrawable\">\n        <!-- Identifier of the image file. This attribute is mandatory.\n             It must be an image file with multiple frames, e.g. gif or webp -->\n        <attr name=\"src\" />\n        <!-- Indicates if the drawable needs to be mirrored when its layout direction is\n             RTL (right-to-left). -->\n        <attr name=\"autoMirrored\" />\n        <!-- Replace the loop count in the encoded data. A repeat count of 0 means that\n             the animation will play once, regardless of the number of times specified\n             in the encoded data. Setting this to infinite (-1) will result in the\n             animation repeating as long as it is displayed (once start() is called). -->\n        <attr name=\"repeatCount\"/>\n        <!-- When true, automatically start animating. The default is false, meaning\n             that the animation will not start until start() is called. -->\n        <attr name=\"autoStart\" />\n    </declare-styleable>\n\n    <!-- Drawable used to draw bitmaps. -->\n    <declare-styleable name=\"BitmapDrawable\">\n        <!-- Identifier of the bitmap file. This attribute is mandatory. -->\n        <attr name=\"src\" />\n        <!-- Enables or disables antialiasing. Antialiasing can be used to smooth the\n             edges of a bitmap when rotated. Default value is false. -->\n        <attr name=\"antialias\" format=\"boolean\" />\n        <!-- Enables or disables bitmap filtering. Filtering is used when the bitmap is\n             shrunk or stretched to smooth its appearance. Default value is true. -->\n        <attr name=\"filter\" format=\"boolean\" />\n        <!-- Enables or disables dithering of the bitmap if the bitmap does not have the\n             same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with\n             an RGB 565 screen). Default value is true. -->\n        <attr name=\"dither\" />\n        <!-- Defines the gravity for the bitmap. The gravity indicates where to position\n             the drawable in its container if the bitmap is smaller than the container. -->\n        <attr name=\"gravity\" />\n        <!-- Defines the tile mode. When the tile mode is enabled, the bitmap is repeated.\n             Gravity is ignored when the tile mode is enabled. Default value is \"disabled\". -->\n        <attr name=\"tileMode\">\n            <!-- Do not tile the bitmap. This is the default value. -->\n            <enum name=\"disabled\" value=\"-1\" />\n            <!-- Replicates the edge color. -->\n            <enum name=\"clamp\" value=\"0\" />\n            <!-- Repeats the bitmap in both direction. -->\n            <enum name=\"repeat\" value=\"1\" />\n            <!-- Repeats the shader's image horizontally and vertically, alternating\n                 mirror images so that adjacent images always seam. -->\n            <enum name=\"mirror\" value=\"2\" />\n        </attr>\n        <!-- Defines the horizontal tile mode. When the tile mode is enabled, the bitmap is repeated.\n             Gravity is ignored when the tile mode is enabled. Default value is \"disabled\". -->\n        <attr name=\"tileModeX\">\n            <!-- Do not tile the bitmap. This is the default value. -->\n            <enum name=\"disabled\" value=\"-1\" />\n            <!-- Replicates the edge color. -->\n            <enum name=\"clamp\" value=\"0\" />\n            <!-- Repeats the bitmap horizontally. -->\n            <enum name=\"repeat\" value=\"1\" />\n            <!-- Repeats the shader's image horizontally, alternating\n                 mirror images so that adjacent images always seam. -->\n            <enum name=\"mirror\" value=\"2\" />\n        </attr>\n        <!-- Defines the vertical tile mode. When the tile mode is enabled, the bitmap is repeated.\n             Gravity is ignored when the tile mode is enabled. Default value is \"disabled\". -->\n        <attr name=\"tileModeY\">\n            <!-- Do not tile the bitmap. This is the default value. -->\n            <enum name=\"disabled\" value=\"-1\" />\n            <!-- Replicates the edge color. -->\n            <enum name=\"clamp\" value=\"0\" />\n            <!-- Repeats the bitmap vertically. -->\n            <enum name=\"repeat\" value=\"1\" />\n            <!-- Repeats the shader's image vertically, alternating\n                 mirror images so that adjacent images always seam. -->\n            <enum name=\"mirror\" value=\"2\" />\n        </attr>\n        <!-- Enables or disables the mipmap hint. See\n            {@link android.graphics.Bitmap#setHasMipMap(boolean)} for more information.\n            Default value is false. -->\n        <attr name=\"mipMap\" format=\"boolean\" />\n        <!-- Indicates if the drawable needs to be mirrored when its layout direction is\n             RTL (right-to-left). -->\n        <attr name=\"autoMirrored\" />\n        <!-- If set, specifies the color to apply to the drawable as a tint. By default,\n             no tint is applied. May be a color state list. -->\n        <attr name=\"tint\" />\n        <!-- When a tint color is set, specifies its Porter-Duff blending mode. The\n             default value is src_in, which treats the drawable as an alpha mask. -->\n        <attr name=\"tintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n        <!-- Specifies the alpha multiplier to apply to the base drawable. -->\n        <attr name=\"alpha\" />\n    </declare-styleable>\n\n    <!-- Drawable used to draw 9-patches. -->\n    <declare-styleable name=\"NinePatchDrawable\">\n        <!-- Identifier of the bitmap file. This attribute is mandatory. -->\n        <attr name=\"src\" />\n        <!-- Enables or disables dithering of the bitmap if the bitmap does not have the\n             same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with\n             an RGB 565 screen). -->\n        <attr name=\"dither\" />\n        <!-- Indicates if the drawable needs to be mirrored when its layout direction is\n             RTL (right-to-left). -->\n        <attr name=\"autoMirrored\" />\n        <!-- If set, specifies the color to apply to the drawable as a tint. By default,\n             no tint is applied. May be a color state list. -->\n        <attr name=\"tint\" />\n        <!-- When a tint color is set, specifies its Porter-Duff blending mode. The\n             default value is src_in, which treats the drawable as an alpha mask. -->\n        <attr name=\"tintMode\" />\n        <!-- Specifies the alpha multiplier to apply to the base drawable. -->\n        <attr name=\"alpha\" />\n    </declare-styleable>\n\n    <!-- Drawable used to draw a single color. -->\n    <declare-styleable name=\"ColorDrawable\">\n        <!-- The color to use. -->\n        <attr name=\"color\" />\n    </declare-styleable>\n\n    <!-- Drawable used to draw adaptive icons with foreground and background layers. -->\n    <declare-styleable name=\"AdaptiveIconDrawableLayer\">\n        <!-- The drawable to use for the layer. -->\n        <attr name=\"drawable\" />\n     </declare-styleable>\n\n    <!-- Drawable used to show animated touch feedback. -->\n    <declare-styleable name=\"RippleDrawable\">\n        <!-- The color to use for ripple effects. This attribute is required. -->\n        <attr name=\"color\" />\n        <!-- The radius of the ripple when fully expanded. By default, the\n             radius is computed based on the size of the ripple's container. -->\n        <attr name=\"radius\" />\n        <!-- Secondary color of the ripple effect. -->\n        <attr name=\"effectColor\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"ScaleDrawable\">\n        <!-- Scale width, expressed as a percentage of the drawable's bound. The value's\n             format is XX%. For instance: 100%, 12.5%, etc.-->\n        <attr name=\"scaleWidth\" format=\"string\" />\n        <!-- Scale height, expressed as a percentage of the drawable's bound. The value's\n             format is XX%. For instance: 100%, 12.5%, etc.-->\n        <attr name=\"scaleHeight\" format=\"string\" />\n        <!-- Specifies where the drawable is positioned after scaling. The default value is\n             left. -->\n        <attr name=\"scaleGravity\">\n            <!-- Push object to the top of its container, not changing its size. -->\n            <flag name=\"top\" value=\"0x30\" />\n            <!-- Push object to the bottom of its container, not changing its size. -->\n            <flag name=\"bottom\" value=\"0x50\" />\n            <!-- Push object to the left of its container, not changing its size. -->\n            <flag name=\"left\" value=\"0x03\" />\n            <!-- Push object to the right of its container, not changing its size. -->\n            <flag name=\"right\" value=\"0x05\" />\n            <!-- Place object in the vertical center of its container, not changing its size. -->\n            <flag name=\"center_vertical\" value=\"0x10\" />\n            <!-- Grow the vertical size of the object if needed so it completely fills its container. -->\n            <flag name=\"fill_vertical\" value=\"0x70\" />\n            <!-- Place object in the horizontal center of its container, not changing its size. -->\n            <flag name=\"center_horizontal\" value=\"0x01\" />\n            <!-- Grow the horizontal size of the object if needed so it completely fills its container. -->\n            <flag name=\"fill_horizontal\" value=\"0x07\" />\n            <!-- Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. -->\n            <flag name=\"center\" value=\"0x11\" />\n            <!-- Grow the horizontal and vertical size of the object if needed so it completely fills its container. -->\n            <flag name=\"fill\" value=\"0x77\" />\n            <!-- Additional option that can be set to have the top and/or bottom edges of\n                 the child clipped to its container's bounds.\n                 The clip will be based on the vertical gravity: a top gravity will clip the bottom\n                 edge, a bottom gravity will clip the top edge, and neither will clip both edges. -->\n            <flag name=\"clip_vertical\" value=\"0x80\" />\n            <!-- Additional option that can be set to have the left and/or right edges of\n                 the child clipped to its container's bounds.\n                 The clip will be based on the horizontal gravity: a left gravity will clip the right\n                 edge, a right gravity will clip the left edge, and neither will clip both edges. -->\n            <flag name=\"clip_horizontal\" value=\"0x08\" />\n            <!-- Push object to the beginning of its container, not changing its size. -->\n            <flag name=\"start\" value=\"0x00800003\" />\n            <!-- Push object to the end of its container, not changing its size. -->\n            <flag name=\"end\" value=\"0x00800005\" />\n        </attr>\n        <!-- Specifies the initial drawable level in the range 0 to 10000. -->\n        <attr name=\"level\" format=\"integer\" />\n        <!-- Reference to a drawable resource to draw with the specified scale. -->\n        <attr name=\"drawable\" />\n        <!-- Use the drawable's intrinsic width and height as minimum size values.\n             Useful if the target drawable is a 9-patch or otherwise should not be scaled\n             down beyond a minimum size. -->\n        <attr name=\"useIntrinsicSizeAsMinimum\" format=\"boolean\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"ClipDrawable\">\n        <!-- The orientation for the clip. -->\n        <attr name=\"clipOrientation\">\n            <!-- Clip the drawable horizontally. -->\n            <flag name=\"horizontal\" value=\"1\" />\n            <!-- Clip the drawable vertically. -->\n            <flag name=\"vertical\" value=\"2\" />\n        </attr>\n        <!-- Specifies where to clip within the drawable. The default value is\n             left. -->\n        <attr name=\"gravity\" />\n        <!-- Reference to a drawable resource to draw with the specified scale. -->\n        <attr name=\"drawable\" />\n    </declare-styleable>\n\n    <!-- Defines the padding of a ShapeDrawable. -->\n    <declare-styleable name=\"ShapeDrawablePadding\">\n        <!-- Left padding. -->\n        <attr name=\"left\" />\n        <!-- Top padding. -->\n        <attr name=\"top\" />\n        <!-- Right padding. -->\n        <attr name=\"right\" />\n        <!-- Bottom padding. -->\n        <attr name=\"bottom\" />\n    </declare-styleable>\n\n    <!-- Drawable used to draw shapes. -->\n    <declare-styleable name=\"ShapeDrawable\">\n        <!-- Defines the color of the shape. -->\n        <attr name=\"color\" />\n        <!-- Defines the width of the shape. -->\n        <attr name=\"width\" />\n        <!-- Defines the height of the shape. -->\n        <attr name=\"height\" />\n        <!-- Enables or disables dithering. -->\n        <attr name=\"dither\" />\n        <!-- If set, specifies the color to apply to the drawable as a tint. By default,\n             no tint is applied. May be a color state list. -->\n        <attr name=\"tint\" />\n        <!-- When a tint color is set, specifies its Porter-Duff blending mode. The\n             default value is src_in, which treats the drawable as an alpha mask. -->\n        <attr name=\"tintMode\" />\n    </declare-styleable>\n\n    <!-- ========================== -->\n    <!--   VectorDrawable class   -->\n    <!-- ========================== -->\n    <eat-comment />\n\n    <!-- Drawable used to draw vector paths. -->\n    <declare-styleable name=\"VectorDrawable\">\n        <!-- If set, specifies the color to apply to the drawable as a tint. By default,\n             no tint is applied. May be a color state list. -->\n        <attr name=\"tint\" />\n        <!-- When a tint color is set, specifies its Porter-Duff blending mode. The\n             default value is src_in, which treats the drawable as an alpha mask. -->\n        <attr name=\"tintMode\" />\n        <!-- Indicates if the drawable needs to be mirrored when its layout direction is\n             RTL (right-to-left). -->\n        <attr name=\"autoMirrored\" />\n        <!-- The intrinsic width of the Vector Drawable. -->\n        <attr name=\"width\" />\n        <!-- The intrinsic height of the Vector Drawable. -->\n        <attr name=\"height\" />\n        <!-- The width of the canvas the drawing is on. -->\n        <attr name=\"viewportWidth\" format=\"float\"/>\n        <!-- The height of the canvas the drawing is on. -->\n        <attr name=\"viewportHeight\" format=\"float\"/>\n        <!-- The name of this vector drawable. -->\n        <attr name=\"name\" />\n        <!-- The opacity of the whole vector drawable, as a value between 0\n             (completely transparent) and 1 (completely opaque). -->\n        <attr name=\"alpha\" />\n        <!-- Left optical inset. -->\n        <attr name=\"opticalInsetLeft\" format=\"dimension\" />\n        <!-- Top optical inset. -->\n        <attr name=\"opticalInsetTop\" format=\"dimension\" />\n        <!-- Right optical inset. -->\n        <attr name=\"opticalInsetRight\" format=\"dimension\" />\n        <!-- Bottom optical inset. -->\n        <attr name=\"opticalInsetBottom\" format=\"dimension\" />\n    </declare-styleable>\n\n    <!-- Defines the group used in VectorDrawables. -->\n    <declare-styleable name=\"VectorDrawableGroup\">\n        <!-- The name of this group. -->\n        <attr name=\"name\" />\n        <!-- The amount to rotate the group. -->\n        <attr name=\"rotation\" />\n        <!-- The X coordinate of the center of rotation of a group. -->\n        <attr name=\"pivotX\" />\n        <!-- The Y coordinate of the center of rotation of a group. -->\n        <attr name=\"pivotY\" />\n        <!-- The amount to translate the group on X coordinate. -->\n        <attr name=\"translateX\" format=\"float\"/>\n        <!-- The amount to translate the group on Y coordinate. -->\n        <attr name=\"translateY\" format=\"float\"/>\n        <!-- The amount to scale the group on X coordinate. -->\n        <attr name=\"scaleX\" />\n        <!-- The amount to scale the group on X coordinate. -->\n        <attr name=\"scaleY\" />\n    </declare-styleable>\n\n    <!-- Defines the path used in VectorDrawables. -->\n    <declare-styleable name=\"VectorDrawablePath\">\n        <!-- The name of this path. -->\n        <attr name=\"name\" />\n        <!-- The width a path stroke. -->\n        <attr name=\"strokeWidth\" format=\"float\" />\n        <!-- The color to stroke the path if not defined implies no stroke. -->\n        <attr name=\"strokeColor\" format=\"color\" />\n        <!-- The opacity of a path stroke, as a value between 0 (completely transparent)\n             and 1 (completely opaque). -->\n        <attr name=\"strokeAlpha\" format=\"float\" />\n        <!-- The color to fill the path if not defined implies no fill. -->\n        <attr name=\"fillColor\" format=\"color\" />\n        <!-- The alpha of the path fill, as a value between 0 (completely transparent)\n             and 1 (completely opaque). -->\n        <attr name=\"fillAlpha\" format=\"float\" />\n        <!-- The specification of the operations that define the path. -->\n        <attr name=\"pathData\" format=\"string\" />\n        <!-- The fraction of the path to trim from the start from 0 to 1. -->\n        <attr name=\"trimPathStart\" format=\"float\" />\n        <!-- The fraction of the path to trim from the end from 0 to 1 . -->\n        <attr name=\"trimPathEnd\" format=\"float\" />\n        <!-- Shift trim region (allows visible region to include the start and end) from 0 to 1. -->\n        <attr name=\"trimPathOffset\" format=\"float\" />\n        <!-- sets the linecap for a stroked path. -->\n        <attr name=\"strokeLineCap\" format=\"enum\">\n            <enum name=\"butt\" value=\"0\"/>\n            <enum name=\"round\" value=\"1\"/>\n            <enum name=\"square\" value=\"2\"/>\n        </attr>\n        <!-- sets the lineJoin for a stroked path. -->\n        <attr name=\"strokeLineJoin\" format=\"enum\">\n            <enum name=\"miter\" value=\"0\"/>\n            <enum name=\"round\" value=\"1\"/>\n            <enum name=\"bevel\" value=\"2\"/>\n        </attr>\n        <!-- sets the Miter limit for a stroked path. -->\n        <attr name=\"strokeMiterLimit\" format=\"float\"/>\n        <!-- sets the fillType for a path. It is the same as SVG's \"fill-rule\" properties.\n             For more details, see https://www.w3.org/TR/SVG/painting.html#FillRuleProperty. -->\n        <attr name=\"fillType\" format=\"enum\">\n            <enum name=\"nonZero\" value=\"0\"/>\n            <enum name=\"evenOdd\" value=\"1\"/>\n        </attr>\n    </declare-styleable>\n\n    <!-- Defines the clip path used in VectorDrawables. -->\n    <declare-styleable name=\"VectorDrawableClipPath\">\n        <!-- The Name of this path. -->\n        <attr name=\"name\" />\n        <!-- The specification of the operations that define the path. -->\n        <attr name=\"pathData\"/>\n    </declare-styleable>\n\n    <!-- ========================== -->\n    <!--   AnimatedVectorDrawable class   -->\n    <!-- ========================== -->\n    <eat-comment />\n\n    <!-- Define the AnimatedVectorDrawable. -->\n    <declare-styleable name=\"AnimatedVectorDrawable\">\n        <!-- The static vector drawable. -->\n        <attr name=\"drawable\" />\n    </declare-styleable>\n\n    <!-- Defines the target used in the AnimatedVectorDrawable. -->\n    <declare-styleable name=\"AnimatedVectorDrawableTarget\">\n        <!-- The name of the target path, group or vector drawable. -->\n        <attr name=\"name\" />\n        <!-- The animation for the target path, group or vector drawable. -->\n        <attr name=\"animation\" />\n    </declare-styleable>\n\n    <!-- ================================== -->\n    <!-- NotificationProgressDrawable class -->\n    <!-- ================================== -->\n\n    <!-- Used to config the segments of a NotificationProgressDrawable. -->\n    <!-- @hide internal use only -->\n    <declare-styleable name=\"NotificationProgressDrawableSegments\">\n        <!-- Height of the solid segments. -->\n        <attr name=\"height\" />\n        <!-- Height of the faded segments. -->\n        <attr name=\"fadedHeight\" format=\"dimension\" />\n        <!-- Corner radius of the segment rect. -->\n        <attr name=\"cornerRadius\" format=\"dimension\" />\n    </declare-styleable>\n\n    <!-- Used to config the points of a NotificationProgressDrawable. -->\n    <!-- @hide internal use only -->\n    <declare-styleable name=\"NotificationProgressDrawablePoints\">\n        <!-- Radius (1/2 size) of the point rect. -->\n        <attr name=\"radius\" />\n        <!-- Inset of the point icon or rect. -->\n        <attr name=\"inset\" />\n        <!-- Corner radius of the point rect. -->\n        <attr name=\"cornerRadius\"/>\n    </declare-styleable>\n\n    <!-- ========================== -->\n    <!-- Animation class attributes -->\n    <!-- ========================== -->\n    <eat-comment />\n\n    <declare-styleable name=\"Animation\">\n        <!-- Defines the interpolator used to smooth the animation movement in time. -->\n        <attr name=\"interpolator\" />\n        <!-- When set to true, the value of fillBefore is taken into account. -->\n        <attr name=\"fillEnabled\" format=\"boolean\" />\n        <!-- When set to true or when fillEnabled is not set to true, the animation transformation\n             is applied before the animation has started. The default value is true. -->\n        <attr name=\"fillBefore\" format=\"boolean\" />\n        <!-- When set to true, the animation transformation is applied after the animation is\n             over. The default value is false. If fillEnabled is not set to true and the\n             animation is not set on a View, fillAfter is assumed to be true.-->\n        <attr name=\"fillAfter\" format=\"boolean\" />\n        <!-- Amount of time (in milliseconds) for the animation to run. -->\n        <attr name=\"duration\" />\n        <!-- Delay in milliseconds before the animation runs, once start time is reached. -->\n        <attr name=\"startOffset\" format=\"integer\" />\n        <!-- Defines how many times the animation should repeat. The default value is 0. -->\n        <attr name=\"repeatCount\" format=\"integer\">\n            <enum name=\"infinite\" value=\"-1\" />\n        </attr>\n        <!-- Defines the animation behavior when it reaches the end and the repeat count is\n             greater than 0 or infinite. The default value is restart. -->\n        <attr name=\"repeatMode\">\n            <!-- The animation starts again from the beginning. -->\n            <enum name=\"restart\" value=\"1\" />\n            <!-- The animation plays backward. -->\n            <enum name=\"reverse\" value=\"2\" />\n        </attr>\n        <!-- Allows for an adjustment of the Z ordering of the content being\n             animated for the duration of the animation.  The default value is normal. -->\n        <attr name=\"zAdjustment\">\n            <!-- The content being animated be kept in its current Z order. -->\n            <enum name=\"normal\" value=\"0\" />\n            <!-- The content being animated is forced on top of all other\n                 content for the duration of the animation. -->\n            <enum name=\"top\" value=\"1\" />\n            <!-- The content being animated is forced under all other\n                 content for the duration of the animation. -->\n            <enum name=\"bottom\" value=\"-1\" />\n        </attr>\n        <!-- Special background behind animation.  Only for use with window\n             animations.  Can only be a color, and only black.  If 0, the\n             default, there is no background. -->\n        <attr name=\"background\" />\n        <!-- Special option for window animations: if this window is on top\n             of a wallpaper, don't animate the wallpaper with it. -->\n        <attr name=\"detachWallpaper\" format=\"boolean\" />\n        <!-- Special option for window animations: show the wallpaper behind when running this\n             animation. -->\n        <attr name=\"showWallpaper\" format=\"boolean\" />\n        <!-- Special option for window animations: whether window should have rounded corners.\n             @see ScreenDecorationsUtils#getWindowCornerRadius(Resources) -->\n        <attr name=\"hasRoundedCorners\" format=\"boolean\" />\n        <!-- Special option for window animations: whether to show a background behind the animating\n             windows. By default the window's background is used unless overridden by the\n             animation. -->\n        <attr name=\"showBackdrop\" format=\"boolean\" />\n        <!-- Special option for window animations: whether the window's background should be used as\n             a background to the animation. -->\n        <attr name=\"backdropColor\" format=\"color\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"AnimationSet\">\n        <attr name=\"shareInterpolator\" format=\"boolean\" />\n        <attr name=\"fillBefore\" />\n        <attr name=\"fillAfter\" />\n        <attr name=\"duration\" />\n        <attr name=\"startOffset\" />\n        <attr name=\"repeatMode\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"RotateAnimation\">\n        <attr name=\"fromDegrees\" />\n        <attr name=\"toDegrees\" />\n        <attr name=\"pivotX\" />\n        <attr name=\"pivotY\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"ScaleAnimation\">\n        <attr name=\"fromXScale\" format=\"float|fraction|dimension\" />\n        <attr name=\"toXScale\" format=\"float|fraction|dimension\" />\n        <attr name=\"fromYScale\" format=\"float|fraction|dimension\" />\n        <attr name=\"toYScale\" format=\"float|fraction|dimension\" />\n        <attr name=\"pivotX\" />\n        <attr name=\"pivotY\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"TranslateAnimation\">\n        <attr name=\"fromXDelta\" format=\"float|fraction|dimension\" />\n        <attr name=\"toXDelta\" format=\"float|fraction|dimension\" />\n        <attr name=\"fromYDelta\" format=\"float|fraction|dimension\" />\n        <attr name=\"toYDelta\" format=\"float|fraction|dimension\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"AlphaAnimation\">\n        <attr name=\"fromAlpha\" format=\"float\" />\n        <attr name=\"toAlpha\" format=\"float\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"ClipRectAnimation\">\n        <attr name=\"fromLeft\" format=\"fraction\" />\n        <attr name=\"fromTop\" format=\"fraction\" />\n        <attr name=\"fromRight\" format=\"fraction\" />\n        <attr name=\"fromBottom\" format=\"fraction\" />\n        <attr name=\"toLeft\" format=\"fraction\" />\n        <attr name=\"toTop\" format=\"fraction\" />\n        <attr name=\"toRight\" format=\"fraction\" />\n        <attr name=\"toBottom\" format=\"fraction\" />\n    </declare-styleable>\n\n    <!-- Defines the ExtendAnimation used to extend windows during animations -->\n    <declare-styleable name=\"ExtendAnimation\">\n        <!-- Defines the amount a window should be extended outward from the left at the start of\n             the animation in an absolute dimension (interpreted as pixels if no dimension unit is\n             provided) or as a percentage of the animation target's width. -->\n        <attr name=\"fromExtendLeft\" format=\"float|fraction|dimension\" />\n        <!-- Defines the amount a window should be extended outward from the top at the start of\n             the animation in an absolute dimension (interpreted as pixels if no dimension unit is\n             provided) or as a percentage of the animation target's height. -->\n        <attr name=\"fromExtendTop\" format=\"float|fraction|dimension\" />\n        <!-- Defines the amount a window should be extended outward from the right at the start of\n             the animation in an absolute dimension (interpreted as pixels if no dimension unit is\n             provided) or as a percentage of the animation target's width. -->\n        <attr name=\"fromExtendRight\" format=\"float|fraction|dimension\" />\n        <!-- Defines the amount a window should be extended outward from the bottom at the start of\n             the animation in an absolute dimension (interpreted as pixels if no dimension unit is\n             provided) or as a percentage of the animation target's height. -->\n        <attr name=\"fromExtendBottom\" format=\"float|fraction|dimension\" />\n        <!-- Defines the amount a window should be extended outward from the left by the end of the\n             animation by transitioning from the fromExtendLeft amount in an absolute dimension\n             (interpreted as pixels if no dimension unit is provided) or as a percentage of the\n             animation target's width. -->\n        <attr name=\"toExtendLeft\" format=\"float|fraction|dimension\" />\n        <!-- Defines the amount a window should be extended outward from the top by the end of the\n             animation by transitioning from the fromExtendTop amount in an absolute dimension\n             (interpreted as pixels if no dimension unit is provided) or as a percentage of the\n             animation target's height. -->\n        <attr name=\"toExtendTop\" format=\"float|fraction|dimension\" />\n        <!-- Defines the amount a window should be extended outward from the right by the end of\n             the animation by transitioning from the fromExtendRight amount in an absolute\n             dimension (interpreted as pixels if no dimension unit is provided) or as a percentage\n             of the animation target's width. -->\n        <attr name=\"toExtendRight\" format=\"float|fraction|dimension\" />\n        <!-- Defines the amount a window should be extended outward from the bottom by the end of\n             the animation by transitioning from the fromExtendBottom amount in an absolute\n             dimension (interpreted as pixels if no dimension unit is provided) or as a percentage\n             of the animation target's height. -->\n        <attr name=\"toExtendBottom\" format=\"float|fraction|dimension\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"LayoutAnimation\">\n        <!-- Fraction of the animation duration used to delay the beginning of\n         the animation of each child. -->\n        <attr name=\"delay\" format=\"float|fraction\" />\n        <!-- Animation to use on each child. -->\n        <attr name=\"animation\" format=\"reference\" />\n        <!-- The order in which the animations will be started. -->\n        <attr name=\"animationOrder\">\n            <!-- Animations are started in the natural order. -->\n            <enum name=\"normal\" value=\"0\" />\n            <!-- Animations are started in the reverse order. -->\n            <enum name=\"reverse\" value=\"1\" />\n            <!-- Animations are started randomly. -->\n            <enum name=\"random\" value=\"2\" />\n        </attr>\n        <!-- Interpolator used to interpolate the delay between the start of\n         each animation. -->\n        <attr name=\"interpolator\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"GridLayoutAnimation\">\n        <!-- Fraction of the animation duration used to delay the beginning of\n         the animation of each column. -->\n        <attr name=\"columnDelay\" format=\"float|fraction\" />\n        <!-- Fraction of the animation duration used to delay the beginning of\n         the animation of each row. -->\n        <attr name=\"rowDelay\" format=\"float|fraction\" />\n        <!-- Direction of the animation in the grid. -->\n        <attr name=\"direction\">\n            <!-- Animates columns from left to right. -->\n            <flag name=\"left_to_right\" value=\"0x0\" />\n            <!-- Animates columns from right to left. -->\n            <flag name=\"right_to_left\" value=\"0x1\" />\n            <!-- Animates rows from top to bottom. -->\n            <flag name=\"top_to_bottom\" value=\"0x0\" />\n            <!-- Animates rows from bottom to top. -->\n            <flag name=\"bottom_to_top\" value=\"0x2\" />\n        </attr>\n        <!-- Priority of the rows and columns. When the priority is none,\n         both rows and columns have the same priority. When the priority is\n         column, the animations will be applied on the columns first. The same\n         goes for rows. -->\n        <attr name=\"directionPriority\">\n            <!-- Rows and columns are animated at the same time. -->\n            <enum name=\"none\"   value=\"0\" />\n            <!-- Columns are animated first. -->\n            <enum name=\"column\" value=\"1\" />\n            <!-- Rows are animated first. -->\n            <enum name=\"row\"    value=\"2\" />\n        </attr>\n    </declare-styleable>\n\n    <declare-styleable name=\"AccelerateInterpolator\">\n        <!-- This is the amount of deceleration to add when easing in. -->\n        <attr name=\"factor\" format=\"float\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"DecelerateInterpolator\">\n        <!-- This is the amount of acceleration to add when easing out. -->\n        <attr name=\"factor\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"CycleInterpolator\">\n        <attr name=\"cycles\" format=\"float\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"AnticipateInterpolator\">\n        <!-- This is the amount of tension. -->\n        <attr name=\"tension\" format=\"float\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"OvershootInterpolator\">\n        <!-- This is the amount of tension. -->\n        <attr name=\"tension\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"AnticipateOvershootInterpolator\">\n        <!-- This is the amount of tension. -->\n        <attr name=\"tension\" />\n        <!-- This is the amount by which to multiply the tension. -->\n        <attr name=\"extraTension\" format=\"float\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"PathInterpolator\">\n        <!-- The x coordinate of the first control point of the cubic Bezier. -->\n        <attr name=\"controlX1\" format=\"float\" />\n        <!-- The y coordinate of the first control point of the cubic Bezier. -->\n        <attr name=\"controlY1\" format=\"float\" />\n        <!-- The x coordinate of the second control point of the cubic Bezier. -->\n        <attr name=\"controlX2\" format=\"float\" />\n        <!-- The y coordinate of the second control point of the cubic Bezier. -->\n        <attr name=\"controlY2\" format=\"float\" />\n        <!-- The control points defined as a path.\n             When pathData is defined, then both of the control points of the\n             cubic Bezier will be ignored. -->\n        <attr name=\"pathData\"/>\n    </declare-styleable>\n\n    <!-- ========================== -->\n    <!-- Transition attributes -->\n    <!-- ========================== -->\n    <eat-comment />\n\n    <!-- Use specific transition subclass names as the root tag of the XML resource that\n         describes a {@link android.transition.Transition Transition},\n         such as <code>changeBounds</code>, <code>fade</code>, and <code>transitionSet</code>. -->\n    <declare-styleable name=\"Transition\">\n        <!-- Amount of time (in milliseconds) that the transition should run. -->\n        <attr name=\"duration\" />\n        <!-- Delay in milliseconds before the transition starts. -->\n        <attr name=\"startDelay\" format=\"integer\" />\n        <!-- Interpolator to be used in the animations spawned by this transition. -->\n        <attr name=\"interpolator\" />\n        <!-- The match order to use for the transition. This is a comma-separated\n             list of values, containing one or more of the following:\n             id, itemId, name, instance. These correspond to\n             {@link android.transition.Transition#MATCH_ID},\n             {@link android.transition.Transition#MATCH_ITEM_ID},\n             {@link android.transition.Transition#MATCH_NAME}, and\n             {@link android.transition.Transition#MATCH_INSTANCE}, respectively.\n             This corresponds to {@link android.transition.Transition#setMatchOrder(int...)}. -->\n        <attr name=\"matchOrder\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- @hide For internal use only. Use only as directed. -->\n    <declare-styleable name=\"EpicenterTranslateClipReveal\">\n        <attr name=\"interpolatorX\" format=\"reference\" />\n        <attr name=\"interpolatorY\" format=\"reference\" />\n        <attr name=\"interpolatorZ\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- Use <code>fade</code>as the root tag of the XML resource that\n         describes a {@link android.transition.Fade Fade} transition.\n         The attributes of the {@link android.R.styleable#Transition Transition}\n         resource are available in addition to the specific attributes of Fade\n         described here. -->\n    <declare-styleable name=\"Fade\">\n        <!-- Equivalent to <code>transitionVisibilityMode</code>, fadingMode works only\n             with the Fade transition. -->\n        <attr name=\"fadingMode\">\n            <!-- Fade will only fade appearing items in. -->\n            <enum name=\"fade_in\" value=\"1\" />\n            <!-- Fade will only fade disappearing items out. -->\n            <enum name=\"fade_out\" value=\"2\" />\n            <!-- Fade will fade appearing items in and disappearing items out. -->\n            <enum name=\"fade_in_out\" value=\"3\" />\n        </attr>\n    </declare-styleable>\n\n    <!-- Use <code>slide</code>as the root tag of the XML resource that\n         describes a {@link android.transition.Slide Slide} transition.\n         The attributes of the {@link android.R.styleable#Transition Transition}\n         resource are available in addition to the specific attributes of Slide\n         described here. -->\n    <declare-styleable name=\"Slide\">\n        <attr name=\"slideEdge\">\n            <!-- Slide to and from the left edge of the Scene. -->\n            <enum name=\"left\" value=\"0x03\" />\n            <!-- Slide to and from the top edge of the Scene. -->\n            <enum name=\"top\" value=\"0x30\" />\n            <!-- Slide to and from the right edge of the Scene. -->\n            <enum name=\"right\" value=\"0x05\" />\n            <!-- Slide to and from the bottom edge of the Scene. -->\n            <enum name=\"bottom\" value=\"0x50\" />\n            <!-- Slide to and from the x-axis position at the start of the Scene root. -->\n            <enum name=\"start\" value=\"0x00800003\"/>\n            <!-- Slide to and from the x-axis position at the end of the Scene root. -->\n            <enum name=\"end\" value=\"0x00800005\"/>\n        </attr>\n    </declare-styleable>\n\n    <!-- Use with {@link android.transition.Visibility} transitions, such as\n         <code>slide</code>, <code>explode</code>, and <code>fade</code> to mark which\n         views are supported. -->\n    <declare-styleable name=\"VisibilityTransition\">\n        <!-- Changes whether the transition supports appearing and/or disappearing Views.\n             Corresponds to {@link android.transition.Visibility#setMode(int)}. -->\n        <attr name=\"transitionVisibilityMode\">\n            <!-- Only appearing Views will be supported. -->\n            <flag name=\"mode_in\" value=\"1\" />\n            <!-- Only disappearing Views will be supported. -->\n            <flag name=\"mode_out\" value=\"2\" />\n        </attr>\n    </declare-styleable>\n    <!-- Use <code>target</code> as the root tag of the XML resource that\n     describes a {@link android.transition.Transition#addTarget(int)\n     targetId} of a transition. There can be one or more targets inside\n     a <code>targets</code> tag, which is itself inside an appropriate\n     {@link android.R.styleable#Transition Transition} tag.\n     -->\n    <declare-styleable name=\"TransitionTarget\">\n        <!-- The id of a target on which this transition will animate changes. -->\n        <attr name=\"targetId\" format=\"reference\" />\n        <!-- The id of a target to exclude from this transition. -->\n        <attr name=\"excludeId\" format=\"reference\" />\n        <!-- The fully-qualified name of the Class to include in this transition. -->\n        <attr name=\"targetClass\" />\n        <!-- The fully-qualified name of the Class to exclude from this transition. -->\n        <attr name=\"excludeClass\" format=\"string\" />\n        <!-- The transitionName of the target on which this transition will animation changes. -->\n        <attr name=\"targetName\" format=\"string\" />\n        <!-- The transitionName of the target to exclude from this transition. -->\n        <attr name=\"excludeName\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- Use <code>set</code> as the root tag of the XML resource that\n         describes a {@link android.transition.TransitionSet\n         TransitionSet} transition. -->\n    <declare-styleable name=\"TransitionSet\">\n        <attr name=\"transitionOrdering\">\n            <!-- child transitions should be played together. -->\n            <enum name=\"together\" value=\"0\" />\n            <!-- child transitions should be played sequentially, in the same order\n            as the xml. -->\n            <enum name=\"sequential\" value=\"1\" />\n        </attr>\n    </declare-styleable>\n\n    <!-- Use <code>changeTransform</code> as the root tag of the XML resource that\n         describes a {@link android.transition.ChangeTransform} transition. -->\n    <declare-styleable name=\"ChangeTransform\">\n        <!-- A parent change should use an overlay or affect the transform of the\n             transitionining View. Default is true. Corresponds to\n             {@link android.transition.ChangeTransform#setReparentWithOverlay(boolean)}. -->\n        <attr name=\"reparentWithOverlay\" format=\"boolean\"/>\n\n        <!-- Tells ChangeTransform to track parent changes. Default is true. Corresponds to\n             {@link android.transition.ChangeTransform#setReparent(boolean)}. -->\n        <attr name=\"reparent\" format=\"boolean\"/>\n    </declare-styleable>\n\n    <!-- Use <code>changeBounds</code>as the root tag of the XML resource that\n         describes a {@link android.transition.ChangeBounds} transition.\n         The attributes of the {@link android.R.styleable#Transition Transition}\n         resource are available in addition to the specific attributes of ChangeBounds\n         described here. -->\n    <declare-styleable name=\"ChangeBounds\">\n        <!-- Resize the view by adjusting the clipBounds rather than changing the\n             dimensions of the view itself. The default value is false. -->\n        <attr name=\"resizeClip\" format=\"boolean\"/>\n    </declare-styleable>\n\n    <!-- Use <code>transitionManager</code> as the root tag of the XML resource that\n         describes a {@link android.transition.TransitionManager\n         TransitionManager}. -->\n    <declare-styleable name=\"TransitionManager\">\n        <!-- The id of a transition to be used in a particular scene change. -->\n        <attr name=\"transition\" format=\"reference\" />\n        <!-- The originating scene in this scene change. -->\n        <attr name=\"fromScene\" format=\"reference\" />\n        <!-- The destination scene in this scene change. -->\n        <attr name=\"toScene\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- Use <code>arcMotion</code> as the root tag of the XML resource that\n         describes a {@link android.transition.ArcMotion}. This must be used\n         within a transition with which the PathMotion should be associated. -->\n    <declare-styleable name=\"ArcMotion\">\n        <!-- The minimum arc angle in degrees between the start and end points when\n             they are close to horizontal. -->\n        <attr name=\"minimumHorizontalAngle\" format=\"float\" />\n        <!-- The minimum arc angle in degrees between the start and end points when\n             they are close to vertical. -->\n        <attr name=\"minimumVerticalAngle\" format=\"float\" />\n        <!-- The maximum arc angle in degrees between the start and end points. -->\n        <attr name=\"maximumAngle\" format=\"float\" />\n    </declare-styleable>\n\n    <!-- Use <code>patternPathMotion</code> as the root tag of the XML resource that\n         describes a {@link android.transition.PatternPathMotion}. This must be used\n         within a transition with which the PathMotion should be associated. -->\n    <declare-styleable name=\"PatternPathMotion\">\n        <!-- The path string describing the pattern to use for the PathPathMotion. -->\n        <attr name=\"patternPathData\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- ========================== -->\n    <!-- ValueAnimator class attributes -->\n    <!-- ========================== -->\n    <eat-comment />\n\n    <declare-styleable name=\"Animator\">\n        <!-- Defines the interpolator used to smooth the animation movement in time. -->\n        <attr name=\"interpolator\" />\n        <!-- Amount of time (in milliseconds) for the animation to run. -->\n        <attr name=\"duration\" />\n        <!-- Delay in milliseconds before the animation runs, once start time is reached. -->\n        <attr name=\"startOffset\"/>\n        <!-- Defines how many times the animation should repeat. The default value is 0. -->\n        <attr name=\"repeatCount\"/>\n        <!-- Defines the animation behavior when it reaches the end and the repeat count is\n             greater than 0 or infinite. The default value is restart. -->\n        <attr name=\"repeatMode\"/>\n        <!-- Value the animation starts from. -->\n        <attr name=\"valueFrom\" format=\"float|integer|color|dimension|string\"/>\n        <!-- Value the animation animates to. -->\n        <attr name=\"valueTo\" format=\"float|integer|color|dimension|string\"/>\n        <!-- The type of valueFrom and valueTo. -->\n        <attr name=\"valueType\">\n            <!-- The given values are floats. This is the default value if valueType is\n                 unspecified. Note that if any value attribute has a color value\n                 (beginning with \"#\"), then this attribute is ignored and the color values are\n                 interpreted as integers. -->\n            <enum name=\"floatType\" value=\"0\" />\n            <!-- values are integers. -->\n            <enum name=\"intType\"   value=\"1\" />\n            <!-- values are paths defined as strings.\n                 This type is used for path morphing in AnimatedVectorDrawable. -->\n            <enum name=\"pathType\"   value=\"2\" />\n            <!-- values are colors, which are integers starting with \"#\". -->\n            <enum name=\"colorType\"   value=\"3\" />\n        </attr>\n        <!-- Placeholder for a deleted attribute. This should be removed before M release. -->\n        <attr name=\"removeBeforeMRelease\" format=\"integer\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"PropertyValuesHolder\">\n        <attr name=\"valueType\" />\n        <attr name=\"propertyName\" />\n        <attr name=\"valueFrom\" />\n        <attr name=\"valueTo\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"Keyframe\">\n        <attr name=\"valueType\" />\n        <attr name=\"value\" />\n        <attr name=\"fraction\" format=\"float\" />\n        <!-- Defines a per-interval interpolator for this keyframe. This interpolator will be used\n             to interpolate between this keyframe and the previous keyframe. -->\n        <attr name=\"interpolator\" />\n    </declare-styleable>\n\n    <!-- ========================== -->\n    <!-- ObjectAnimator class attributes -->\n    <!-- ========================== -->\n    <eat-comment />\n\n    <declare-styleable name=\"PropertyAnimator\">\n        <!-- Name of the property being animated. -->\n        <attr name=\"propertyName\" format=\"string\"/>\n        <!-- Name of the property being animated as the X coordinate of the pathData. -->\n        <attr name=\"propertyXName\" format=\"string\"/>\n        <!-- Name of the property being animated as the Y coordinate of the pathData. -->\n        <attr name=\"propertyYName\" format=\"string\"/>\n        <!-- The path used to animate the properties in the ObjectAnimator. -->\n        <attr name=\"pathData\"/>\n    </declare-styleable>\n\n\n    <!-- ========================== -->\n    <!-- AnimatorSet class attributes -->\n    <!-- ========================== -->\n    <eat-comment />\n\n    <declare-styleable name=\"AnimatorSet\">\n        <!-- Name of the property being animated. -->\n        <attr name=\"ordering\">\n            <!-- child animations should be played together. -->\n            <enum name=\"together\" value=\"0\" />\n            <!-- child animations should be played sequentially, in the same order as the xml. -->\n            <enum name=\"sequentially\" value=\"1\" />\n        </attr>\n    </declare-styleable>\n\n    <!-- ========================== -->\n    <!-- State attributes           -->\n    <!-- ========================== -->\n    <eat-comment />\n\n    <!-- Set of framework-provided states that may be specified on a Drawable. Actual usage of\n         states may vary between view implementations, as documented on the individual state\n         attributes. -->\n    <declare-styleable name=\"DrawableStates\">\n        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},\n             set when a view has input focus. -->\n        <attr name=\"state_focused\" format=\"boolean\" />\n        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},\n             set when a view's window has input focus. -->\n        <attr name=\"state_window_focused\" format=\"boolean\" />\n        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},\n             set when a view is enabled. -->\n        <attr name=\"state_enabled\" format=\"boolean\" />\n        <!-- State identifier indicating that the object <var>may</var> display a check mark. See\n             {@link android.R.attr#state_checked} for the identifier that indicates whether it is\n             actually checked. -->\n        <attr name=\"state_checkable\" format=\"boolean\"/>\n        <!-- State identifier indicating that the object is currently checked.  See\n             {@link android.R.attr#state_checkable} for an additional identifier that can indicate\n             if any object may ever display a check, regardless of whether state_checked is\n             currently set. -->\n        <attr name=\"state_checked\" format=\"boolean\"/>\n        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},\n             set when a view (or one of its parents) is currently selected. -->\n        <attr name=\"state_selected\" format=\"boolean\" />\n        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},\n             set when the user is pressing down in a view. -->\n        <attr name=\"state_pressed\" format=\"boolean\" />\n        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},\n             set when a view or its parent has been \"activated\" meaning the user has currently\n             marked it as being of interest.  This is an alternative representation of\n             state_checked for when the state should be propagated down the view hierarchy. -->\n        <attr name=\"state_activated\" format=\"boolean\" />\n        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},\n             set when a view or drawable is considered \"active\" by its host. Actual usage may vary\n             between views. Consult the host view documentation for details. -->\n        <attr name=\"state_active\" format=\"boolean\" />\n        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},\n             set when a view or drawable is considered \"single\" by its host. Actual usage may vary\n             between views. Consult the host view documentation for details. -->\n        <attr name=\"state_single\" format=\"boolean\" />\n        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},\n             set when a view or drawable is in the first position in an ordered set. Actual usage\n             may vary between views. Consult the host view documentation for details. -->\n        <attr name=\"state_first\" format=\"boolean\" />\n        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},\n             set when a view or drawable is in the middle position in an ordered set. Actual usage\n             may vary between views. Consult the host view documentation for details. -->\n        <attr name=\"state_middle\" format=\"boolean\" />\n        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},\n             set when a view or drawable is in the last position in an ordered set. Actual usage\n             may vary between views. Consult the host view documentation for details. -->\n        <attr name=\"state_last\" format=\"boolean\" />\n        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},\n             indicating that the Drawable is in a view that is hardware accelerated.\n             This means that the device can at least render a full-screen scaled\n             bitmap with one layer of text and bitmaps composited on top of it\n             at 60fps.  When this is set, the colorBackgroundCacheHint will be\n             ignored even if it specifies a solid color, since that optimization\n             is not needed. -->\n        <attr name=\"state_accelerated\" format=\"boolean\" />\n        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},\n             set when a pointer is hovering over the view. -->\n        <attr name=\"state_hovered\" format=\"boolean\" />\n        <!-- State for {@link android.graphics.drawable.StateListDrawable StateListDrawable}\n             indicating that the Drawable is in a view that is capable of accepting a drop of\n             the content currently being manipulated in a drag-and-drop operation. -->\n        <attr name=\"state_drag_can_accept\" format=\"boolean\" />\n        <!-- State for {@link android.graphics.drawable.StateListDrawable StateListDrawable}\n             indicating that a drag operation (for which the Drawable's view is a valid recipient)\n             is currently positioned over the Drawable. -->\n        <attr name=\"state_drag_hovered\" format=\"boolean\" />\n        <!-- State for {@link android.graphics.drawable.StateListDrawable StateListDrawable}\n             indicating that a View has accessibility focus. -->\n        <attr name=\"state_accessibility_focused\" format=\"boolean\" />\n    </declare-styleable>\n    <declare-styleable name=\"ViewDrawableStates\">\n        <attr name=\"state_pressed\" />\n        <attr name=\"state_focused\" />\n        <attr name=\"state_selected\" />\n        <attr name=\"state_window_focused\" />\n        <attr name=\"state_enabled\" />\n        <attr name=\"state_activated\" />\n        <attr name=\"state_accelerated\" />\n        <attr name=\"state_hovered\" />\n        <attr name=\"state_drag_can_accept\" />\n        <attr name=\"state_drag_hovered\" />\n    </declare-styleable>\n    <!-- State array representing a menu item that is currently checked. -->\n    <declare-styleable name=\"MenuItemCheckedState\">\n        <attr name=\"state_checkable\" />\n        <attr name=\"state_checked\" />\n    </declare-styleable>\n    <!-- State array representing a menu item that is checkable but is not currently checked. -->\n    <declare-styleable name=\"MenuItemUncheckedState\">\n        <attr name=\"state_checkable\" />\n    </declare-styleable>\n    <!-- State array representing a menu item that is currently focused and checked. -->\n    <declare-styleable name=\"MenuItemCheckedFocusedState\">\n        <attr name=\"state_checkable\" />\n        <attr name=\"state_checked\" />\n        <attr name=\"state_focused\" />\n    </declare-styleable>\n    <!-- State array representing a menu item that is focused and checkable but is not currently checked. -->\n    <declare-styleable name=\"MenuItemUncheckedFocusedState\">\n        <attr name=\"state_checkable\" />\n        <attr name=\"state_focused\" />\n    </declare-styleable>\n    <!-- State array representing an expandable list child's indicator. -->\n    <declare-styleable name=\"ExpandableListChildIndicatorState\">\n        <!-- State identifier indicating the child is the last child within its group. -->\n        <attr name=\"state_last\" />\n    </declare-styleable>\n    <!-- State array representing an expandable list group's indicator. -->\n    <declare-styleable name=\"ExpandableListGroupIndicatorState\">\n        <!-- State identifier indicating the group is expanded. -->\n        <attr name=\"state_expanded\" format=\"boolean\" />\n        <!-- State identifier indicating the group is empty (has no children). -->\n        <attr name=\"state_empty\" format=\"boolean\" />\n    </declare-styleable>\n    <declare-styleable name=\"PopupWindowBackgroundState\">\n        <!-- State identifier indicating the popup will be above the anchor. -->\n        <attr name=\"state_above_anchor\" format=\"boolean\" />\n    </declare-styleable>\n    <declare-styleable name=\"TextViewMultiLineBackgroundState\">\n        <!-- State identifier indicating a TextView has a multi-line layout. -->\n        <attr name=\"state_multiline\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- ***************************************************************** -->\n    <!-- Support for Searchable activities. -->\n    <!-- ***************************************************************** -->\n    <eat-comment />\n\n    <!-- Searchable activities and applications must provide search configuration information\n        in an XML file, typically called searchable.xml.  This file is referenced in your manifest.\n        For a more in-depth discussion of search configuration, please refer to\n        {@link android.app.SearchManager}. -->\n    <declare-styleable name=\"Searchable\">\n          <!--<strong>This is deprecated.</strong><br/>The default\n              application icon is now always used, so this attribute is\n              obsolete.-->\n        <attr name=\"icon\" />\n        <!-- This is the user-displayed name of the searchable activity.  <i>Required\n            attribute.</i> -->\n        <attr name=\"label\" />\n        <!-- If supplied, this string will be displayed as a hint to the user.  <i>Optional\n            attribute.</i> -->\n        <attr name=\"hint\" />\n        <!-- If supplied, this string will be displayed as the text of the \"Search\" button.\n          <i>Optional attribute.</i>\n          {@deprecated This will create a non-standard UI appearance, because the search bar UI is\n                       changing to use only icons for its buttons.}-->\n        <attr name=\"searchButtonText\" format=\"string\" />\n        <attr name=\"inputType\" />\n        <attr name=\"imeOptions\" />\n\n        <!-- Additional features are controlled by mode bits in this field.  Omitting\n            this field, or setting to zero, provides default behavior.  <i>Optional attribute.</i>\n        -->\n        <attr name=\"searchMode\">\n          <!-- If set, this flag enables the display of the search target (label) within the\n               search bar.  If neither bad mode is selected, no badge will be shown. -->\n          <flag name=\"showSearchLabelAsBadge\" value=\"0x04\" />\n          <!--<strong>This is deprecated.</strong><br/>The default\n              application icon is now always used, so this option is\n              obsolete.-->\n          <flag name=\"showSearchIconAsBadge\" value=\"0x08\" />\n          <!-- If set, this flag causes the suggestion column SUGGEST_COLUMN_INTENT_DATA to\n               be considered as the text for suggestion query rewriting.  This should only\n               be used when the values in SUGGEST_COLUMN_INTENT_DATA are suitable for user\n               inspection and editing - typically, HTTP/HTTPS Uri's. -->\n          <flag name=\"queryRewriteFromData\" value=\"0x10\" />\n          <!-- If set, this flag causes the suggestion column SUGGEST_COLUMN_TEXT_1 to\n               be considered as the text for suggestion query rewriting.  This should be used\n               for suggestions in which no query text is provided and the SUGGEST_COLUMN_INTENT_DATA\n               values are not suitable for user inspection and editing. -->\n          <flag name=\"queryRewriteFromText\" value=\"0x20\" />\n        </attr>\n\n        <!-- Voice search features are controlled by mode bits in this field.  Omitting\n            this field, or setting to zero, provides default behavior.\n            If showVoiceSearchButton is set, then launchWebSearch or launchRecognizer must\n            also be set.  <i>Optional attribute.</i>\n        -->\n        <attr name=\"voiceSearchMode\">\n          <!-- If set, display a voice search button.  This only takes effect if voice search is\n               available on the device. -->\n          <flag name=\"showVoiceSearchButton\" value=\"0x01\" />\n          <!-- If set, the voice search button will take the user directly to a built-in\n               voice web search activity.  Most applications will not use this flag, as it\n               will take the user away from the activity in which search was invoked. -->\n          <flag name=\"launchWebSearch\" value=\"0x02\" />\n          <!-- If set, the voice search button will take the user directly to a built-in\n               voice recording activity.  This activity will prompt the user to speak,\n               transcribe the spoken text, and forward the resulting query\n               text to the searchable activity, just as if the user had typed it into\n               the search UI and clicked the search button. -->\n          <flag name=\"launchRecognizer\" value=\"0x04\" />\n        </attr>\n\n        <!-- If provided, this specifies the language model that should be used by the\n             voice recognition system.  See\n             {@link android.speech.RecognizerIntent#EXTRA_LANGUAGE_MODEL } for more information.\n             If not provided, the default value\n             {@link android.speech.RecognizerIntent#LANGUAGE_MODEL_FREE_FORM } will be used. -->\n        <attr name=\"voiceLanguageModel\" format=\"string\" />\n        <!-- If provided, this specifies a prompt that will be displayed during voice input. -->\n        <attr name=\"voicePromptText\" format=\"string\" />\n        <!-- If provided, this specifies the spoken language to be expected, and that it will be\n             different than the one set in the {@link java.util.Locale#getDefault()}. -->\n        <attr name=\"voiceLanguage\" format=\"string\" />\n        <!-- If provided, enforces the maximum number of results to return, including the \"best\"\n             result which will always be provided as the SEARCH intent's primary query.  Must be one\n             or greater.  If not provided, the recognizer will choose how many results to return.\n             -->\n        <attr name=\"voiceMaxResults\" format=\"integer\" />\n\n        <!-- If provided, this is the trigger indicating that the searchable activity\n            provides suggestions as well.  The value must be a fully-qualified content provider\n            authority (for example, \"com.example.android.apis.SuggestionProvider\") and should match\n            the \"android:authorities\" tag in your content provider's manifest entry.  <i>Optional\n            attribute.</i> -->\n        <attr name=\"searchSuggestAuthority\" format=\"string\" />\n        <!-- If provided, this will be inserted in the suggestions query Uri, after the authority\n            you have provide but before the standard suggestions path. <i>Optional attribute.</i>\n            -->\n        <attr name=\"searchSuggestPath\" format=\"string\" />\n        <!-- If provided, suggestion queries will be passed into your query function\n            as the <i>selection</i> parameter.  Typically this will be a WHERE clause for your\n            database, and will contain a single question mark, which represents the actual query\n            string that has been typed by the user.  If not provided, then the user query text\n            will be appended to the query Uri (after an additional \"/\".)  <i>Optional\n            attribute.</i> -->\n        <attr name=\"searchSuggestSelection\" format=\"string\" />\n\n        <!-- If provided, and not overridden by an action in the selected suggestion, this\n            string will be placed in the action field of the {@link android.content.Intent Intent}\n            when the user clicks a suggestion.  <i>Optional attribute.</i> -->\n        <attr name=\"searchSuggestIntentAction\" format=\"string\" />\n        <!-- If provided, and not overridden by an action in the selected suggestion, this\n            string will be placed in the data field of the {@link android.content.Intent Intent}\n            when the user clicks a suggestion.  <i>Optional attribute.</i> -->\n        <attr name=\"searchSuggestIntentData\" format=\"string\" />\n\n        <!-- If provided, this is the minimum number of characters needed to trigger\n             search suggestions. The default value is 0. <i>Optional attribute.</i> -->\n        <attr name=\"searchSuggestThreshold\" format=\"integer\" />\n\n        <!-- If provided and <code>true</code>, this searchable activity will be\n             included in any global lists of search targets.\n             The default value is <code>false</code>. <i>Optional attribute.</i>. -->\n        <attr name=\"includeInGlobalSearch\" format=\"boolean\" />\n\n        <!-- If provided and <code>true</code>, this searchable activity will be invoked for all\n             queries in a particular session. If set to <code>false</code> and the activity\n             returned zero results for a query, it will not be invoked again in that session for\n             supersets of that zero-results query. For example, if the activity returned zero\n             results for \"bo\", it would not be queried again for \"bob\".\n             The default value is <code>false</code>. <i>Optional attribute.</i>. -->\n        <attr name=\"queryAfterZeroResults\" format=\"boolean\" />\n        <!-- If provided, this string will be used to describe the searchable item in the\n             searchable items settings within system search settings. <i>Optional\n             attribute.</i> -->\n        <attr name=\"searchSettingsDescription\" format=\"string\" />\n\n        <!-- If provided and <code>true</code>, URLs entered in the search dialog while searching\n             within this activity would be detected and treated as URLs (show a 'go' button in the\n             keyboard and invoke the browser directly when user launches the URL instead of passing\n             the URL to the activity). If set to <code>false</code> any URLs entered are treated as\n             normal query text.\n             The default value is <code>false</code>. <i>Optional attribute.</i>. -->\n        <attr name=\"autoUrlDetect\" format=\"boolean\" />\n\n    </declare-styleable>\n\n    <!-- In order to process special action keys during search, you must define them using\n            one or more \"ActionKey\" elements in your Searchable metadata.  For a more in-depth\n            discussion of action code handling, please refer to {@link android.app.SearchManager}.\n    -->\n    <declare-styleable name=\"SearchableActionKey\">\n        <!-- This attribute denotes the action key you wish to respond to.  Note that not\n            all action keys are actually supported using this mechanism, as many of them are\n            used for typing, navigation, or system functions.  This will be added to the\n            {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to your\n            searchable activity.  To examine the key code, use\n            {@link android.content.Intent#getIntExtra getIntExtra(SearchManager.ACTION_KEY)}.\n            <p>Note, in addition to the keycode, you must also provide one or more of the action\n            specifier attributes.  <i>Required attribute.</i> -->\n        <attr name=\"keycode\" />\n\n        <!-- If you wish to handle an action key during normal search query entry, you\n            must define an action string here.  This will be added to the\n            {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to your\n            searchable activity.  To examine the string, use\n            {@link android.content.Intent#getStringExtra getStringExtra(SearchManager.ACTION_MSG)}.\n            <i>Optional attribute.</i> -->\n        <attr name=\"queryActionMsg\"  format=\"string\" />\n\n        <!-- If you wish to handle an action key while a suggestion is being displayed <i>and\n            selected</i>, there are two ways to handle this.  If <i>all</i> of your suggestions\n            can handle the action key, you can simply define the action message using this\n            attribute.  This will be added to the\n            {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to your\n            searchable activity.  To examine the string, use\n            {@link android.content.Intent#getStringExtra getStringExtra(SearchManager.ACTION_MSG)}.\n            <i>Optional attribute.</i> -->\n        <attr name=\"suggestActionMsg\"  format=\"string\" />\n\n        <!-- If you wish to handle an action key while a suggestion is being displayed <i>and\n            selected</i>, but you do not wish to enable this action key for every suggestion,\n            then you can use this attribute to control it on a suggestion-by-suggestion basis.\n            First, you must define a column (and name it here) where your suggestions will include\n            the action string.  Then, in your content provider, you must provide this column, and\n            when desired, provide data in this column.\n            The search manager will look at your suggestion cursor, using the string\n            provided here in order to select a column, and will use that to select a string from\n            the cursor.  That string will be added to the\n            {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to\n            your searchable activity.  To examine the string, use\n            {@link android.content.Intent#getStringExtra\n            getStringExtra(SearchManager.ACTION_MSG)}.  <i>If the data does not exist for the\n            selection suggestion, the action key will be ignored.</i><i>Optional attribute.</i> -->\n        <attr name=\"suggestActionMsgColumn\" format=\"string\" />\n\n    </declare-styleable>\n\n    <!-- ***************************************************************** -->\n    <!-- Support for MapView. -->\n    <!-- ***************************************************************** -->\n    <eat-comment />\n\n    <!-- The set of attributes for a MapView. -->\n    <declare-styleable name=\"MapView\">\n        <!-- Value is a string that specifies the Maps API Key to use. -->\n        <attr name=\"apiKey\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- **************************************************************** -->\n    <!-- Menu XML inflation. -->\n    <!-- **************************************************************** -->\n    <eat-comment />\n\n    <!-- Base attributes that are available to all Menu objects. -->\n    <declare-styleable name=\"Menu\">\n    </declare-styleable>\n\n    <!-- Base attributes that are available to all groups. -->\n    <declare-styleable name=\"MenuGroup\">\n\n        <!-- The ID of the group. -->\n        <attr name=\"id\" />\n\n        <!-- The category applied to all items within this group.\n             (This will be or'ed with the orderInCategory attribute.) -->\n        <attr name=\"menuCategory\">\n            <!-- Items are part of a container. -->\n            <enum name=\"container\" value=\"0x00010000\" />\n            <!-- Items are provided by the system. -->\n            <enum name=\"system\" value=\"0x00020000\" />\n            <!-- Items are user-supplied secondary (infrequently used). -->\n            <enum name=\"secondary\" value=\"0x00030000\" />\n            <!-- Items are alternative actions. -->\n            <enum name=\"alternative\" value=\"0x00040000\" />\n        </attr>\n\n        <!-- The order within the category applied to all items within this group.\n             (This will be or'ed with the category attribute.) -->\n        <attr name=\"orderInCategory\" format=\"integer\" />\n\n        <!-- Whether the items are capable of displaying a check mark. -->\n        <attr name=\"checkableBehavior\">\n            <!-- The items are not checkable. -->\n            <enum name=\"none\" value=\"0\" />\n            <!-- The items are all checkable. -->\n            <enum name=\"all\" value=\"1\" />\n            <!-- The items are checkable and there will only be a single checked item in\n                 this group. -->\n            <enum name=\"single\" value=\"2\" />\n        </attr>\n\n        <!-- Whether the items are shown/visible. -->\n        <attr name=\"visible\" />\n\n        <!-- Whether the items are enabled. -->\n        <attr name=\"enabled\" />\n\n    </declare-styleable>\n\n    <!-- Base attributes that are available to all Item objects. -->\n    <declare-styleable name=\"MenuItem\">\n\n        <!-- The ID of the item. -->\n        <attr name=\"id\" />\n\n        <!-- The category applied to the item.\n             (This will be or'ed with the orderInCategory attribute.) -->\n        <attr name=\"menuCategory\" />\n\n        <!-- The order within the category applied to the item.\n             (This will be or'ed with the category attribute.) -->\n        <attr name=\"orderInCategory\" />\n\n        <!-- The title associated with the item. -->\n        <attr name=\"title\" format=\"string\" />\n\n        <!-- The condensed title associated with the item.  This is used in situations where the\n             normal title may be too long to be displayed. -->\n        <attr name=\"titleCondensed\" format=\"string\" />\n\n        <!-- The icon associated with this item.  This icon will not always be shown, so\n             the title should be sufficient in describing this item. -->\n        <attr name=\"icon\" />\n\n        <!-- Tint to apply to the icon. -->\n        <attr name=\"iconTint\" format=\"color\" />\n\n        <!-- Blending mode used to apply the icon tint. -->\n        <attr name=\"iconTintMode\">\n            <!-- The tint is drawn on top of the icon.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the icon. The icon’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the icon, but with the icon’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the icon with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and icon color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n\n        <!-- The alphabetic shortcut key.  This is the shortcut when using a keyboard\n             with alphabetic keys. -->\n        <attr name=\"alphabeticShortcut\" format=\"string\" />\n\n        <!-- The alphabetic modifier key. This is the modifier when using a keyboard\n             with alphabetic keys. The values should be kept in sync with KeyEvent -->\n        <attr name=\"alphabeticModifiers\">\n            <flag name=\"META\" value=\"0x10000\" />\n            <flag name=\"CTRL\" value=\"0x1000\" />\n            <flag name=\"ALT\" value=\"0x02\" />\n            <flag name=\"SHIFT\" value=\"0x1\" />\n            <flag name=\"SYM\" value=\"0x4\" />\n            <flag name=\"FUNCTION\" value=\"0x8\" />\n        </attr>\n\n        <!-- The numeric shortcut key.  This is the shortcut when using a numeric (for example,\n             12-key) keyboard. -->\n        <attr name=\"numericShortcut\" format=\"string\" />\n\n        <!-- The numeric modifier key. This is the modifier when using a numeric (for example,\n             12-key) keyboard. The values should be kept in sync with KeyEvent -->\n        <attr name=\"numericModifiers\">\n            <flag name=\"META\" value=\"0x10000\" />\n            <flag name=\"CTRL\" value=\"0x1000\" />\n            <flag name=\"ALT\" value=\"0x02\" />\n            <flag name=\"SHIFT\" value=\"0x1\" />\n            <flag name=\"SYM\" value=\"0x4\" />\n            <flag name=\"FUNCTION\" value=\"0x8\" />\n        </attr>\n\n        <!-- Whether the item is capable of displaying a check mark. -->\n        <attr name=\"checkable\" format=\"boolean\" />\n\n        <!-- Whether the item is checked.  Note that you must first have enabled checking with\n             the checkable attribute or else the check mark will not appear. -->\n        <attr name=\"checked\" />\n\n        <!-- Whether the item is shown/visible. -->\n        <attr name=\"visible\" />\n\n        <!-- Whether the item is enabled. -->\n        <attr name=\"enabled\" />\n\n        <!-- Name of a method on the Context used to inflate the menu that will be\n             called when the item is clicked.\n             {@deprecated Menu actually traverses the Context hierarchy looking for the\n             relevant method, which is fragile (an intermediate ContextWrapper adding a\n             same-named method would change behavior) and restricts bytecode optimizers\n             such as R8. Instead, use MenuItem.setOnMenuItemClickListener.} -->\n        <attr name=\"onClick\" />\n\n        <!-- How this item should display in the Action Bar, if present. -->\n        <attr name=\"showAsAction\">\n            <!-- Never show this item in an action bar, show it in the overflow menu instead.\n                 Mutually exclusive with \"ifRoom\" and \"always\". -->\n            <flag name=\"never\" value=\"0\" />\n            <!-- Show this item in an action bar if there is room for it as determined\n                 by the system. Favor this option over \"always\" where possible.\n                 Mutually exclusive with \"never\" and \"always\". -->\n            <flag name=\"ifRoom\" value=\"1\" />\n            <!-- Always show this item in an actionbar, even if it would override\n                 the system's limits of how much stuff to put there. This may make\n                 your action bar look bad on some screens. In most cases you should\n                 use \"ifRoom\" instead. Mutually exclusive with \"ifRoom\" and \"never\". -->\n            <flag name=\"always\" value=\"2\" />\n            <!-- When this item is shown as an action in the action bar, show a text\n                 label with it even if it has an icon representation. -->\n            <flag name=\"withText\" value=\"4\" />\n            <!-- This item's action view collapses to a normal menu\n                 item. When expanded, the action view takes over a\n                 larger segment of its container. -->\n            <flag name=\"collapseActionView\" value=\"8\" />\n        </attr>\n\n        <!-- An optional layout to be used as an action view.\n             See {@link android.view.MenuItem#setActionView(android.view.View)}\n             for more info. -->\n        <attr name=\"actionLayout\" format=\"reference\" />\n\n        <!-- The name of an optional View class to instantiate and use as an\n             action view. See {@link android.view.MenuItem#setActionView(android.view.View)}\n             for more info. -->\n        <attr name=\"actionViewClass\" format=\"string\" />\n\n        <!-- The name of an optional ActionProvider class to instantiate an action view\n             and perform operations such as default action for that menu item.\n             See {@link android.view.MenuItem#setActionProvider(android.view.ActionProvider)}\n             for more info. -->\n        <attr name=\"actionProviderClass\" format=\"string\" />\n\n        <!-- The content description associated with the item. -->\n        <attr name=\"contentDescription\" format=\"string\" />\n\n        <!-- The tooltip text associated with the item. -->\n        <attr name=\"tooltipText\" format=\"string\" />\n\n    </declare-styleable>\n\n    <!-- Attrbitutes for a ActvityChooserView. -->\n    <declare-styleable name=\"ActivityChooserView\">\n        <!-- The maximal number of items initially shown in the activity list. -->\n        <attr name=\"initialActivityCount\" format=\"string\" />\n        <!-- The drawable to show in the button for expanding the activities overflow popup.\n             <strong>Note:</strong> Clients would like to set this drawable\n             as a clue about the action the chosen activity will perform. For\n             example, if share activity is to be chosen the drawable should\n             give a clue that sharing is to be performed.\n         -->\n        <attr name=\"expandActivityOverflowButtonDrawable\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- **************************************************************** -->\n    <!-- Preferences framework. -->\n    <!-- **************************************************************** -->\n    <eat-comment />\n\n    <!-- Base attributes available to PreferenceGroup. -->\n    <declare-styleable name=\"PreferenceGroup\">\n        <!-- Whether to order the Preference under this group as they appear in the XML file.\n             If this is false, the ordering will follow the Preference order attribute and\n             default to alphabetic for those without the order attribute. -->\n        <attr name=\"orderingFromXml\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- Attribute for a header describing the item shown in the top-level list\n         from which the selects the set of preference to dig in to. -->\n    <declare-styleable name=\"PreferenceHeader\">\n        <!-- Identifier value for the header. -->\n        <attr name=\"id\" />\n        <!-- The title of the item that is shown to the user. -->\n        <attr name=\"title\" />\n        <!-- The summary for the item. -->\n        <attr name=\"summary\" format=\"string\" />\n        <!-- The title for the bread crumb of this item. -->\n        <attr name=\"breadCrumbTitle\" format=\"string\" />\n        <!-- The short title for the bread crumb of this item. -->\n        <attr name=\"breadCrumbShortTitle\" format=\"string\" />\n        <!-- An icon for the item. -->\n        <attr name=\"icon\" />\n        <!-- The fragment that is displayed when the user selects this item. -->\n        <attr name=\"fragment\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- WARNING:  If adding attributes to Preference, make sure it does not conflict\n                   with a View's attributes.  Some subclasses (for example, EditTextPreference)\n                   proxy all attributes to its EditText widget. -->\n    <eat-comment />\n\n    <!-- Base attributes available to Preference. -->\n    <declare-styleable name=\"Preference\">\n        <!-- The optional icon for the preference. -->\n        <attr name=\"icon\" />\n        <!-- The key to store the Preference value. -->\n        <attr name=\"key\" format=\"string\" />\n        <!-- The title for the Preference. In API 25 and earlier, this value is read as a\n         plain string with styling information stripped. -->\n        <attr name=\"title\" />\n        <!-- The summary for the Preference. In API 25 and earlier, this value is read as a\n         plain string with styling information stripped. -->\n        <attr name=\"summary\" />\n        <!-- The order for the Preference (lower values are to be ordered first). If this is not\n             specified, the default ordering will be alphabetic. -->\n        <attr name=\"order\" format=\"integer\" />\n        <!-- When used inside of a modern PreferenceActivity, this declares\n             a new PreferenceFragment to be shown when the user selects this item. -->\n        <attr name=\"fragment\" />\n        <!-- The layout for the Preference in a PreferenceActivity screen. This should\n             rarely need to be changed, look at widgetLayout instead. -->\n        <attr name=\"layout\" />\n        <!-- The layout for the controllable widget portion of a Preference. This is inflated\n             into the layout for a Preference and should be used more frequently than\n             the layout attribute. For example, a checkbox preference would specify\n             a custom layout (consisting of just the CheckBox) here. -->\n        <attr name=\"widgetLayout\" format=\"reference\" />\n        <!-- Whether the Preference is enabled. -->\n        <attr name=\"enabled\" />\n        <!-- Whether the Preference is selectable. -->\n        <attr name=\"selectable\" format=\"boolean\" />\n        <!-- The key of another Preference that this Preference will depend on.  If the other\n             Preference is not set or is off, this Preference will be disabled. -->\n        <attr name=\"dependency\" format=\"string\" />\n        <!-- Whether the Preference stores its value to the storage. -->\n        <attr name=\"persistent\" />\n        <!-- The default value for the preference, which will be set either if persistence\n             is off or persistence is on and the preference is not found in the persistent\n             storage.  -->\n        <attr name=\"defaultValue\" format=\"string|boolean|integer|reference|float\" />\n        <!-- Whether the view of this Preference should be disabled when\n             this Preference is disabled. -->\n        <attr name=\"shouldDisableView\" format=\"boolean\" />\n        <!-- Whether the preference has enabled to have its view recycled when used in the list\n             view. This is true by default. -->\n        <attr name=\"recycleEnabled\" format=\"boolean\" />\n        <!-- Whether to use single line for the preference title text. By default, preference title\n             will be constrained to one line, so the default value of this attribute is true. -->\n        <attr name=\"singleLineTitle\" format=\"boolean\" />\n        <!-- Whether the space for the preference icon view will be reserved. By default, preference\n             icon view visibility will be set to GONE when there is no icon provided, so the default\n             value of this attribute is false. -->\n        <attr name=\"iconSpaceReserved\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- Base attributes available to CheckBoxPreference. -->\n    <declare-styleable name=\"CheckBoxPreference\">\n        <!-- The summary for the Preference in a PreferenceActivity screen when the\n             CheckBoxPreference is checked. If separate on/off summaries are not\n             needed, the summary attribute can be used instead. -->\n        <attr name=\"summaryOn\" format=\"string\" />\n        <!-- The summary for the Preference in a PreferenceActivity screen when the\n             CheckBoxPreference is unchecked. If separate on/off summaries are not\n             needed, the summary attribute can be used instead. -->\n        <attr name=\"summaryOff\" format=\"string\" />\n        <!-- The state (true for on, or false for off) that causes dependents to be disabled. By default,\n             dependents will be disabled when this is unchecked, so the value of this preference is false. -->\n        <attr name=\"disableDependentsState\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- Base attributes available to DialogPreference. -->\n    <declare-styleable name=\"DialogPreference\">\n        <!-- The title in the dialog. -->\n        <attr name=\"dialogTitle\" format=\"string\" />\n        <!-- The message in the dialog. If a dialogLayout is provided and contains\n             a TextView with ID android:id/message, this message will be placed in there. -->\n        <attr name=\"dialogMessage\" format=\"string\" />\n        <!-- The icon for the dialog. -->\n        <attr name=\"dialogIcon\" format=\"reference\" />\n        <!-- The positive button text for the dialog. Set to @null to hide the positive button. -->\n        <attr name=\"positiveButtonText\" format=\"string\" />\n        <!-- The negative button text for the dialog. Set to @null to hide the negative button. -->\n        <attr name=\"negativeButtonText\" format=\"string\" />\n        <!-- A layout to be used as the content View for the dialog. By default, this shouldn't\n             be needed. If a custom DialogPreference is required, this should be set. For example,\n             the EditTextPreference uses a layout with an EditText as this attribute. -->\n        <attr name=\"dialogLayout\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- Base attributes available to ListPreference. -->\n    <declare-styleable name=\"ListPreference\">\n        <!-- The human-readable array to present as a list. Each entry must have a corresponding\n             index in entryValues. -->\n        <attr name=\"entries\" />\n        <!-- The array to find the value to save for a preference when an entry from\n             entries is selected. If a user clicks on the second item in entries, the\n             second item in this array will be saved to the preference. -->\n        <attr name=\"entryValues\" format=\"reference\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"MultiSelectListPreference\">\n        <!-- The human-readable array to present as a list. Each entry must have a corresponding\n             index in entryValues. -->\n        <attr name=\"entries\" />\n        <!-- The array to find the value to save for a preference when an entry from\n             entries is selected. If a user clicks the second item in entries, the\n             second item in this array will be saved to the preference. -->\n        <attr name=\"entryValues\" />\n    </declare-styleable>\n\n    <!-- Base attributes available to RingtonePreference. -->\n    <declare-styleable name=\"RingtonePreference\">\n        <!-- Which ringtone type(s) to show in the picker. -->\n        <attr name=\"ringtoneType\">\n            <!-- Ringtones. -->\n            <flag name=\"ringtone\" value=\"1\" />\n            <!-- Notification sounds. -->\n            <flag name=\"notification\" value=\"2\" />\n            <!-- Alarm sounds. -->\n            <flag name=\"alarm\" value=\"4\" />\n            <!-- All available ringtone sounds. -->\n            <flag name=\"all\" value=\"7\" />\n        </attr>\n        <!-- Whether to show an item for a default sound. -->\n        <attr name=\"showDefault\" format=\"boolean\" />\n        <!-- Whether to show an item for 'Silent'. -->\n        <attr name=\"showSilent\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- Base attributes available to VolumePreference. -->\n    <declare-styleable name=\"VolumePreference\">\n        <!-- Different audio stream types. -->\n        <attr name=\"streamType\">\n            <enum name=\"voice\" value=\"0\" />\n            <enum name=\"system\" value=\"1\" />\n            <enum name=\"ring\" value=\"2\" />\n            <enum name=\"music\" value=\"3\" />\n            <enum name=\"alarm\" value=\"4\" />\n        </attr>\n    </declare-styleable>\n\n    <declare-styleable name=\"InputMethodService\">\n        <!-- Background to use for entire input method when it is being\n             shown in fullscreen mode with the extract view, to ensure\n             that it completely covers the application.  This allows,\n             for example, the candidate view to be hidden\n             while in fullscreen mode without having the application show through\n             behind it.-->\n        <attr name=\"imeFullscreenBackground\" format=\"reference|color\" />\n        <!-- Animation to use when showing the fullscreen extract UI after\n             it had previously been hidden. -->\n        <attr name=\"imeExtractEnterAnimation\" format=\"reference\" />\n        <!-- Animation to use when hiding the fullscreen extract UI after\n             it had previously been shown. -->\n        <attr name=\"imeExtractExitAnimation\" format=\"reference\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"VoiceInteractionSession\">\n    </declare-styleable>\n\n    <!-- {@deprecated Copy this definition into your own application project.} -->\n    <declare-styleable name=\"KeyboardView\">\n        <!-- Default KeyboardView style.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"keyboardViewStyle\" format=\"reference\" />\n\n        <!-- Image for the key. This image needs to be a StateListDrawable, with the following\n             possible states: normal, pressed, checkable, checkable+pressed, checkable+checked,\n             checkable+checked+pressed.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"keyBackground\" format=\"reference\" />\n\n        <!-- Size of the text for character keys.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"keyTextSize\" format=\"dimension\" />\n\n        <!-- Size of the text for custom keys with some text and no icon.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"labelTextSize\" format=\"dimension\" />\n\n        <!-- Color to use for the label in a key.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"keyTextColor\" format=\"color\" />\n\n        <!-- Layout resource for key press feedback.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"keyPreviewLayout\" format=\"reference\" />\n\n        <!-- Vertical offset of the key press feedback from the key.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"keyPreviewOffset\" format=\"dimension\" />\n\n        <!-- Height of the key press feedback popup.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"keyPreviewHeight\" format=\"dimension\" />\n\n        <!-- Amount to offset the touch Y coordinate by, for bias correction.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"verticalCorrection\" format=\"dimension\" />\n\n        <!-- Layout resource for popup keyboards.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"popupLayout\" format=\"reference\" />\n\n        <!-- {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"shadowColor\" />\n        <!-- {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"shadowRadius\" />\n    </declare-styleable>\n\n    <!-- {@deprecated Copy this definition into your own application project.} -->\n    <declare-styleable name=\"KeyboardViewPreviewState\">\n        <!-- State for {@link android.inputmethodservice.KeyboardView KeyboardView}\n                key preview background.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"state_long_pressable\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- {@deprecated Copy this definition into your own application project.} -->\n    <declare-styleable name=\"Keyboard\">\n        <!-- Default width of a key, in pixels or percentage of display width.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"keyWidth\" format=\"dimension|fraction\" />\n        <!-- Default height of a key, in pixels or percentage of display width.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"keyHeight\" format=\"dimension|fraction\" />\n        <!-- Default horizontal gap between keys.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"horizontalGap\" format=\"dimension|fraction\" />\n        <!-- Default vertical gap between rows of keys.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"verticalGap\" format=\"dimension|fraction\" />\n    </declare-styleable>\n\n    <!-- {@deprecated Copy this definition into your own application project.} -->\n    <declare-styleable name=\"Keyboard_Row\">\n        <!-- Row edge flags.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"rowEdgeFlags\">\n            <!-- Row is anchored to the top of the keyboard.\n             {@deprecated Copy this definition into your own application project.} -->\n            <flag name=\"top\" value=\"4\" />\n            <!-- Row is anchored to the bottom of the keyboard.\n             {@deprecated Copy this definition into your own application project.} -->\n            <flag name=\"bottom\" value=\"8\" />\n        </attr>\n        <!-- Mode of the keyboard. If the mode doesn't match the\n             requested keyboard mode, the row will be skipped.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"keyboardMode\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- {@deprecated Copy this definition into your own application project.} -->\n    <declare-styleable name=\"Keyboard_Key\">\n        <!-- The unicode value or comma-separated values that this key outputs.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"codes\" format=\"integer|string\" />\n        <!-- The XML keyboard layout of any popup keyboard.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"popupKeyboard\" format=\"reference\" />\n        <!-- The characters to display in the popup keyboard.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"popupCharacters\" format=\"string\" />\n        <!-- Key edge flags.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"keyEdgeFlags\">\n            <!-- Key is anchored to the left of the keyboard.\n                 {@deprecated Copy this definition into your own application project.} -->\n            <flag name=\"left\" value=\"1\" />\n            <!-- Key is anchored to the right of the keyboard.\n                 {@deprecated Copy this definition into your own application project.} -->\n            <flag name=\"right\" value=\"2\" />\n        </attr>\n        <!-- Whether this is a modifier key such as Alt or Shift.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"isModifier\" format=\"boolean\" />\n        <!-- Whether this is a toggle key.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"isSticky\" format=\"boolean\" />\n        <!-- Whether long-pressing on this key will make it repeat.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"isRepeatable\" format=\"boolean\" />\n        <!-- The icon to show in the popup preview.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"iconPreview\" format=\"reference\" />\n        <!-- The string of characters to output when this key is pressed.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"keyOutputText\" format=\"string\" />\n        <!-- The label to display on the key.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"keyLabel\" format=\"string\" />\n        <!-- The icon to display on the key instead of the label.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"keyIcon\" format=\"reference\" />\n        <!-- Mode of the keyboard. If the mode doesn't match the\n             requested keyboard mode, the key will be skipped.\n             {@deprecated Copy this definition into your own application project.} -->\n        <attr name=\"keyboardMode\" />\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- AppWidget package class attributes -->\n    <!-- =============================== -->\n    <eat-comment />\n\n    <!-- Use <code>appwidget-provider</code> as the root tag of the XML resource that\n         describes an AppWidget provider.  See {@link android.appwidget android.appwidget}\n         package for more info.\n     -->\n    <declare-styleable name=\"AppWidgetProviderInfo\">\n        <!-- Minimum width of the AppWidget. -->\n        <attr name=\"minWidth\"/>\n        <!-- Minimum height of the AppWidget. -->\n        <attr name=\"minHeight\"/>\n        <!-- Minimum width that the AppWidget can be resized to. -->\n        <attr name=\"minResizeWidth\" format=\"dimension\"/>\n        <!-- Minimum height that the AppWidget can be resized to. -->\n        <attr name=\"minResizeHeight\" format=\"dimension\"/>\n        <!-- Maximum width that the AppWidget can be resized to. -->\n        <attr name=\"maxResizeWidth\" format=\"dimension\"/>\n        <!-- Maximum height that the AppWidget can be resized to. -->\n        <attr name=\"maxResizeHeight\" format=\"dimension\"/>\n        <!-- Default width of the AppWidget in units of launcher grid cells. -->\n        <attr name=\"targetCellWidth\" format=\"integer\"/>\n        <!-- Default height of the AppWidget in units of launcher grid cells. -->\n        <attr name=\"targetCellHeight\" format=\"integer\"/>\n        <!-- Update period in milliseconds, or 0 if the AppWidget will update itself. -->\n        <attr name=\"updatePeriodMillis\" format=\"integer\" />\n        <!-- A resource id of a layout. -->\n        <attr name=\"initialLayout\" format=\"reference\" />\n        <!-- A resource id of a layout. -->\n        <attr name=\"initialKeyguardLayout\" format=\"reference\" />\n        <!-- A class name in the AppWidget's package to be launched to configure.\n             If not supplied, then no activity will be launched. -->\n        <attr name=\"configure\" format=\"string\" />\n        <!-- A preview, in a drawable resource id, of what the AppWidget will look like after it's\n             configured.\n             If not supplied, the AppWidget's icon will be used. -->\n        <attr name=\"previewImage\" format=\"reference\" />\n        <!-- The layout resource id of a preview of what the AppWidget will look like after it's\n             configured.\n             Unlike previewImage, previewLayout can better showcase AppWidget in different locales,\n             system themes, display sizes & density etc.\n             If supplied, this will take precedence over the previewImage on supported widget hosts.\n             Otherwise, previewImage will be used. -->\n        <attr name=\"previewLayout\" format=\"reference\" />\n        <!-- The view id of the AppWidget subview which should be auto-advanced.\n             by the widget's host. -->\n        <attr name=\"autoAdvanceViewId\" format=\"reference\" />\n        <!-- Optional parameter which indicates if and how this widget can be\n             resized. Supports combined values using | operator. -->\n        <attr name=\"resizeMode\" format=\"integer\">\n            <flag name=\"none\" value=\"0x0\" />\n            <flag name=\"horizontal\" value=\"0x1\" />\n            <flag name=\"vertical\" value=\"0x2\" />\n        </attr>\n        <!-- Optional parameter which indicates where this widget can be shown,\n             ie. home screen, keyguard, search bar or any combination thereof.\n             Supports combined values using | operator. -->\n        <attr name=\"widgetCategory\" format=\"integer\">\n            <flag name=\"home_screen\" value=\"0x1\" />\n            <flag name=\"keyguard\" value=\"0x2\" />\n            <flag name=\"searchbox\" value=\"0x4\" />\n            <!-- @FlaggedApi(\"android.appwidget.flags.not_keyguard_category\") -->\n            <flag name=\"not_keyguard\" value=\"0x8\" />\n        </attr>\n        <!-- Flags indicating various features supported by the widget. These are hints to the\n         widget host, and do not actually change the behavior of the widget. -->\n        <attr name=\"widgetFeatures\" format=\"integer\">\n            <!-- The widget can be reconfigured anytime after it is bound -->\n            <flag name=\"reconfigurable\" value=\"0x1\" />\n            <!-- The widget is added directly by the app, and does not need to appear in\n                 the global list of available widgets -->\n            <flag name=\"hide_from_picker\" value=\"0x2\" />\n              <!-- The widget provides a default configuration. The host may decide not to launch\n                   the provided configuration activity. -->\n           <flag name=\"configuration_optional\" value=\"0x4\" />\n        </attr>\n        <!-- A resource identifier for a string containing a short description of the widget. -->\n        <attr name=\"description\" />\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- Wallpaper preview attributes    -->\n    <!-- =============================== -->\n    <eat-comment />\n\n    <!-- Use <code>wallpaper-preview</code> as the root tag of the XML resource that\n         describes a wallpaper preview. -->\n    <declare-styleable name=\"WallpaperPreviewInfo\">\n        <!-- A resource id of a static drawable. -->\n        <attr name=\"staticWallpaperPreview\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- App package class attributes -->\n    <!-- =============================== -->\n    <eat-comment />\n\n    <!-- ============================= -->\n    <!-- View package class attributes -->\n    <!-- ============================= -->\n    <eat-comment />\n\n    <!-- Attributes that can be used with <code>&lt;fragment&gt;</code>\n         tags inside of the layout of an Activity.  This instantiates\n         the given {@link android.app.Fragment} and inserts its content\n         view into the current location in the layout. -->\n    <declare-styleable name=\"Fragment\">\n        <!-- Supply the name of the fragment class to instantiate. -->\n        <attr name=\"name\" />\n\n        <!-- Supply an identifier name for the top-level view, to later retrieve it\n             with {@link android.view.View#findViewById View.findViewById()} or\n             {@link android.app.Activity#findViewById Activity.findViewById()}.\n             This must be a\n             resource reference; typically you set this using the\n             <code>@+</code> syntax to create a new ID resources.\n             For example: <code>android:id=\"@+id/my_id\"</code> which\n             allows you to later retrieve the view\n             with <code>findViewById(R.id.my_id)</code>. -->\n        <attr name=\"id\" />\n\n        <!-- Supply a tag for the top-level view containing a String, to be retrieved\n             later with {@link android.view.View#getTag View.getTag()} or\n             searched for with {@link android.view.View#findViewWithTag\n             View.findViewWithTag()}.  It is generally preferable to use\n             IDs (through the android:id attribute) instead of tags because\n             they are faster and allow for compile-time type checking. -->\n        <attr name=\"tag\" />\n\n        <!-- The Transition that will be used to move Views out of the scene when the\n             fragment is removed, hidden, or detached when not popping the back stack.\n             Corresponds to {@link android.app.Fragment#setExitTransition(\n             android.transition.Transition)} -->\n        <attr name=\"fragmentExitTransition\" format=\"reference\"/>\n\n        <!-- The Transition that will be used to move Views into the initial scene.\n             Corresponds to {@link android.app.Fragment#setEnterTransition(\n             android.transition.Transition)} -->\n        <attr name=\"fragmentEnterTransition\" format=\"reference\"/>\n\n        <!-- The Transition that will be used for shared elements transferred into the content\n             Scene.\n             Corresponds to {@link android.app.Fragment#setSharedElementEnterTransition(\n             android.transition.Transition)} -->\n        <attr name=\"fragmentSharedElementEnterTransition\" format=\"reference\"/>\n\n        <!-- The Transition that will be used to move Views out of the scene when the Fragment is\n             preparing to be removed, hidden, or detached because of popping the back stack.\n             Corresponds to {@link android.app.Fragment#setReturnTransition(\n             android.transition.Transition)} -->\n        <attr name=\"fragmentReturnTransition\" format=\"reference\"/>\n\n        <!-- The Transition that will be used for shared elements transferred back during a\n             pop of the back stack. This Transition acts in the leaving Fragment.\n             Corresponds to {@link android.app.Fragment#setSharedElementReturnTransition(\n             android.transition.Transition)} -->\n        <attr name=\"fragmentSharedElementReturnTransition\" format=\"reference\"/>\n\n        <!-- The Transition that will be used to move Views in to the scene when returning due\n             to popping a back stack.\n             Corresponds to {@link android.app.Fragment#setReenterTransition(\n             android.transition.Transition)} -->\n        <attr name=\"fragmentReenterTransition\" format=\"reference\"/>\n\n        <!-- Sets whether the enter and exit transitions should overlap when transitioning\n             forward.\n             Corresponds to {@link android.app.Fragment#setAllowEnterTransitionOverlap(\n             boolean)} -->\n        <attr name=\"fragmentAllowEnterTransitionOverlap\" format=\"reference\"/>\n\n        <!-- Sets whether the enter and exit transitions should overlap when transitioning\n             because of popping the back stack.\n             Corresponds to {@link android.app.Fragment#setAllowReturnTransitionOverlap(\n             boolean)} -->\n        <attr name=\"fragmentAllowReturnTransitionOverlap\" format=\"reference\"/>\n    </declare-styleable>\n\n    <!-- Use <code>device-admin</code> as the root tag of the XML resource that\n         describes a\n         {@link android.app.admin.DeviceAdminReceiver}, which is\n         referenced from its\n         {@link android.app.admin.DeviceAdminReceiver#DEVICE_ADMIN_META_DATA}\n         meta-data entry.  Described here are the attributes that can be\n         included in that tag. -->\n    <declare-styleable name=\"DeviceAdmin\">\n        <!-- Control whether the admin is visible to the user, even when it\n             is not enabled.  This is true by default.  You may want to make\n             it false if your admin does not make sense to be turned on\n             unless some explicit action happens in your app. -->\n        <attr name=\"visible\" />\n    </declare-styleable>\n\n    <!-- Use <code>wallpaper</code> as the root tag of the XML resource that\n         describes an\n         {@link android.service.wallpaper.WallpaperService}, which is\n         referenced from its\n         {@link android.service.wallpaper.WallpaperService#SERVICE_META_DATA}\n         meta-data entry.  Described here are the attributes that can be\n         included in that tag. -->\n    <declare-styleable name=\"Wallpaper\">\n        <attr name=\"settingsActivity\" />\n\n        <!-- Reference to the wallpaper's thumbnail bitmap. -->\n        <attr name=\"thumbnail\" format=\"reference\" />\n\n        <!-- Name of the author and/or source/collection of this component, for example,\n             Art Collection, Picasso. -->\n        <attr name=\"author\" format=\"reference\" />\n\n        <!-- Short description of the component's purpose or behavior. -->\n        <attr name=\"description\" />\n\n        <!-- Uri that specifies a link for further context of this wallpaper, for example,\n             http://www.picasso.org. -->\n        <attr name=\"contextUri\" format=\"reference\" />\n\n        <!-- Title of the uri that specifies a link for further context of this wallpaper,\n             for example, Explore collection. -->\n        <attr name=\"contextDescription\" format=\"reference\" />\n\n        <!-- Whether to show any metadata when previewing the wallpaper. If this value is\n             set to true, any component that shows a preview of this live wallpaper should also show\n             accompanying information like the title, the description, the author and the context\n             description of this wallpaper so the user gets to know further information about this\n             wallpaper. -->\n        <attr name=\"showMetadataInPreview\" format=\"boolean\" />\n\n        <!-- Wallpapers optimized and capable of drawing in ambient mode will return true.\n             This feature requires the android.permission.AMBIENT_WALLPAPER permission.\n             @hide @SystemApi -->\n        <attr name=\"supportsAmbientMode\" format=\"boolean\" />\n\n        <!-- Indicates that this wallpaper service should receive zoom transition updates when\n             changing the structural state of the device (e.g. when folding or unfolding\n             a foldable device). When this value is set to true\n             {@link android.service.wallpaper.WallpaperService.Engine} could receive zoom updates\n             before or after changing the device state. Wallpapers receive zoom updates using\n             {@link android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)} and\n             zoom rendering should be handled manually. Zoom updates are delivered only when\n             {@link android.service.wallpaper.WallpaperService.Engine} is created and not destroyed.\n             Default value is true.\n             Corresponds to\n             {@link android.app.WallpaperInfo#shouldUseDefaultUnfoldTransition()} -->\n        <attr name=\"shouldUseDefaultUnfoldTransition\" format=\"boolean\" />\n\n        <!-- Uri that specifies a settings Slice for this wallpaper. -->\n        <attr name=\"settingsSliceUri\" format=\"string\"/>\n\n        <!-- Indicates that this wallpaper service can support multiple engines to render on each\n             surface independently. An example use case is a multi-display set-up where the\n             wallpaper service can render surfaces to each of the connected displays. Corresponds to\n             {@link android.app.WallpaperInfo#supportsMultipleDisplays()} -->\n        <attr name=\"supportsMultipleDisplays\" format=\"boolean\" />\n\n    </declare-styleable>\n\n    <!-- Use <code>dream</code> as the root tag of the XML resource that\n         describes an\n         {@link android.service.dreams.DreamService}, which is\n         referenced from its\n         {@link android.service.dreams.DreamService#DREAM_META_DATA}\n         meta-data entry.  Described here are the attributes that can be\n         included in that tag. -->\n    <declare-styleable name=\"Dream\">\n        <!-- Component name of an activity that allows the user to modify\n             the settings for this dream. -->\n        <attr name=\"settingsActivity\" />\n        <!-- A preview, in a drawable resource id, of what the Dream will look like. -->\n        <attr name=\"previewImage\" format=\"reference\" />\n        <!-- Whether to show clock and other complications such as weather in the overlay. Default\n             to true. Note that the overlay on dreams is currently only supported on tablets. -->\n        <attr name=\"showClockAndComplications\" format=\"boolean\" />\n\n        <!-- Dream Category to determine the type of dream. Default to default.\n        @hide -->\n        <attr name=\"dreamCategory\" format=\"integer\">\n            <flag name=\"none\" value=\"0x0\" />\n            <flag name=\"low_light\" value=\"0x1\" />\n            <flag name=\"home_panel\" value=\"0x2\" />\n        </attr>\n    </declare-styleable>\n\n    <!--  Use <code>trust-agent</code> as the root tag of the XML resource that\n         describes an {@link android.service.trust.TrustAgentService}, which is\n         referenced from its {@link android.service.trust.TrustAgentService#TRUST_AGENT_META_DATA}\n         meta-data entry.  Described here are the attributes that can be included in that tag.\n         @hide -->\n    <declare-styleable name=\"TrustAgent\">\n        <!--  Component name of an activity that allows the user to modify\n             the settings for this trust agent. @hide -->\n        <attr name=\"settingsActivity\" />\n        <!--  Title for a preference that allows that user to launch the\n             activity to modify trust agent settings. @hide -->\n        <attr name=\"title\" />\n        <!--  Summary for the same preference as the title. @hide -->\n        <attr name=\"summary\" />\n        <!--  Whether trust agent can unlock a user profile @hide -->\n        <attr name=\"unlockProfile\" format=\"boolean\"/>\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- Accounts package class attributes -->\n    <!-- =============================== -->\n    <eat-comment />\n\n    <!-- Use <code>account-authenticator</code> as the root tag of the XML resource that\n         describes an account authenticator.\n     -->\n    <declare-styleable name=\"AccountAuthenticator\">\n        <!-- The account type this authenticator handles. -->\n        <attr name=\"accountType\" format=\"string\"/>\n        <!-- The user-visible name of the authenticator. -->\n        <attr name=\"label\"/>\n        <!-- The icon of the authenticator. -->\n        <attr name=\"icon\"/>\n        <!-- Smaller icon of the authenticator. -->\n        <attr name=\"smallIcon\" format=\"reference\"/>\n        <!-- A preferences.xml file for authenticator-specific settings. -->\n        <attr name=\"accountPreferences\" format=\"reference\"/>\n        <!-- Account handles its own token storage and permissions.\n             Default to false\n          -->\n        <attr name=\"customTokens\" format=\"boolean\"/>\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- Accounts package class attributes -->\n    <!-- =============================== -->\n    <eat-comment />\n\n    <!-- Use <code>account-authenticator</code> as the root tag of the XML resource that\n         describes an account authenticator.\n     -->\n    <declare-styleable name=\"SyncAdapter\">\n        <!-- the authority of a content provider. -->\n        <attr name=\"contentAuthority\" format=\"string\"/>\n        <attr name=\"accountType\"/>\n        <attr name=\"userVisible\" format=\"boolean\"/>\n        <attr name=\"supportsUploading\" format=\"boolean\"/>\n        <!-- Set to true to tell the SyncManager that this SyncAdapter supports\n             multiple simultaneous syncs for the same account type and authority.\n             Otherwise the SyncManager will be sure not to issue a start sync request\n             to this SyncAdapter if the SyncAdapter is already syncing another account.\n             Defaults to false.\n             -->\n        <attr name=\"allowParallelSyncs\" format=\"boolean\"/>\n        <!-- Set to true to tell the SyncManager to automatically call setIsSyncable(..., ..., 1)\n             for the SyncAdapter instead of issuaing an initialization sync to the SyncAdapter.\n             Defaults to false.\n             -->\n        <attr name=\"isAlwaysSyncable\" format=\"boolean\"/>\n        <!-- If provided, specifies the action of the settings\n             activity for this SyncAdapter.\n             -->\n        <attr name=\"settingsActivity\"/>\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- Autofill attributes -->\n    <!-- =============================== -->\n    <eat-comment />\n\n    <!-- Use <code>autofill-service</code> as the root tag of the XML resource that describes a\n         {@link android.service.autofill.AutofillService}, which is referenced from its\n         {@link android.service.autofill.AutofillService#SERVICE_META_DATA} meta-data entry.\n    -->\n    <declare-styleable name=\"AutofillService\">\n        <!-- Fully qualified class name of an activity that allows the user to modify\n             the settings for this service. -->\n        <attr name=\"settingsActivity\" />\n        <!-- Fully qualified class name of an activity that allows the user to view any passwords\n             saved by this service. -->\n        <attr name=\"passwordsActivity\" format=\"string\" />\n\n        <!-- Specifies whether the AutofillService supports inline suggestions-->\n        <attr name=\"supportsInlineSuggestions\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- Use <code>compatibility-package</code> as a child tag of <code>autofill-service</code>\n         in the XML resource that describes an {@link android.service.autofill.AutofillService}\n         to specify a package and an optional max version code for which to request compatibility\n         mode. If no max version code is specified compatibility mode is requested for all package\n         versions. The max version code is useful to avoid requesting compatibility mode for newer\n         package versions that are known to natively support autofill.\n    -->\n    <declare-styleable name=\"AutofillService_CompatibilityPackage\">\n        <!-- The package name for which compatibility mode is requested. -->\n        <attr name=\"name\" />\n        <!-- The max version code of the package for which compatibility mode is\n             requested. This corresponds to the long value returned by {@link\n             android.content.pm.PackageInfo#getLongVersionCode()} for the target package.\n        -->\n        <attr name=\"maxLongVersionCode\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- System Speech Recognition attributes -->\n    <!-- =============================== -->\n    <eat-comment />\n\n    <!-- Use <code>on-device-recognition-service</code> as the root tag of the XML resource that\n         describes a {@link android.service.speech.RecognitionService}, which is referenced\n         from its {@link android.service.speech.RecognitionService#SERVICE_META_DATA} meta-data\n         entry.\n         @hide @SystemApi\n    -->\n    <declare-styleable name=\"OnDeviceRecognitionService\">\n        <!-- Fully qualified class name of an activity that allows the user to modify\n             the settings for this service. -->\n        <attr name=\"settingsActivity\" />\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- Content Capture attributes -->\n    <!-- =============================== -->\n    <eat-comment />\n\n    <!-- Use <code>content-capture-service</code> as the root tag of the XML resource that describes\n         a {@link android.service.contentcapture.ContentCaptureService}, which is referenced from\n         its {@link android.service.contentcapture.ContentCaptureService#SERVICE_META_DATA}\n         meta-data entry.\n         @hide @SystemApi\n    -->\n    <declare-styleable name=\"ContentCaptureService\">\n        <!-- Fully qualified class name of an activity that allows the user to modify\n             the settings for this service. -->\n        <attr name=\"settingsActivity\" />\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- Translation attributes -->\n    <!-- =============================== -->\n    <eat-comment />\n\n    <!-- Use <code>translation-service</code> as the root tag of the XML resource that describes\n         a {@link android.service.translation.TranslationService}, which is referenced from\n         its {@link android.service.translation.TranslationService#SERVICE_META_DATA} meta-data\n         entry.\n         @hide @SystemApi\n    -->\n    <declare-styleable name=\"TranslationService\">\n        <!-- Fully qualified class name of an activity that allows the user to modify\n             the settings for this service. -->\n        <attr name=\"settingsActivity\" />\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- Contacts meta-data attributes -->\n    <!-- =============================== -->\n    <eat-comment />\n\n    <!-- TODO: remove this deprecated styleable. -->\n    <eat-comment />\n    <declare-styleable name=\"Icon\">\n        <attr name=\"icon\" />\n        <attr name=\"mimeType\" />\n    </declare-styleable>\n\n    <!-- TODO: remove this deprecated styleable -->\n    <eat-comment />\n    <declare-styleable name=\"IconDefault\">\n        <attr name=\"icon\" />\n    </declare-styleable>\n\n    <!-- Maps a specific contact data MIME-type to styling information. -->\n    <declare-styleable name=\"ContactsDataKind\">\n        <!-- Mime-type handled by this mapping. -->\n        <attr name=\"mimeType\" />\n        <!-- Icon used to represent data of this kind. -->\n        <attr name=\"icon\" />\n        <!-- Column in data table that summarizes this data. -->\n        <attr name=\"summaryColumn\" format=\"string\" />\n        <!-- Column in data table that contains details for this data. -->\n        <attr name=\"detailColumn\" format=\"string\" />\n        <!-- Flag indicating that detail should be built from SocialProvider. -->\n        <attr name=\"detailSocialSummary\" format=\"boolean\" />\n        <!-- Resource representing the term \"All Contacts\" (for example, \"All Friends\" or\n        \"All connections\"). Optional (Default is \"All Contacts\"). -->\n        <attr name=\"allContactsName\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- TabSelector class attributes -->\n    <!-- =============================== -->\n    <eat-comment />\n\n    <declare-styleable name=\"SlidingTab\">\n        <!-- Use \"horizontal\" for a row, \"vertical\" for a column.  The default is horizontal. -->\n        <attr name=\"orientation\" />\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- GlowPadView class attributes -->\n    <!-- =============================== -->\n    <eat-comment />\n    <declare-styleable name=\"GlowPadView\">\n        <!-- Reference to an array resource that be used as description for the targets around the circle.\n             {@deprecated Removed.} -->\n        <attr name=\"targetDescriptions\" format=\"reference\" />\n\n        <!-- Reference to an array resource that be used to announce the directions with targets around the circle.\n             {@deprecated Removed.} -->\n        <attr name=\"directionDescriptions\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- Location package class attributes -->\n    <!-- =============================== -->\n    <eat-comment />\n\n    <!-- Use <code>injected-location-setting</code> as the root tag of the XML resource that\n         describes an injected \"Location services\" setting. Note that the status value (subtitle)\n         for the setting is specified dynamically by a subclass of SettingInjectorService.\n     -->\n    <declare-styleable name=\"SettingInjectorService\">\n        <!-- The title for the preference. -->\n        <attr name=\"title\"/>\n        <!-- The icon for the preference, should refer to all apps covered by the setting. Typically\n             a generic icon for the developer. -->\n        <attr name=\"icon\"/>\n        <!-- The activity to launch when the setting is clicked on. -->\n        <attr name=\"settingsActivity\"/>\n        <!-- The user restriction for this preference. -->\n        <attr name=\"userRestriction\" format=\"string\"/>\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- LockPatternView class attributes -->\n    <!-- =============================== -->\n    <eat-comment />\n\n    <declare-styleable name=\"LockPatternView\">\n        <!-- Aspect to use when drawing LockPatternView. Choices are \"square\"(default), \"lock_width\"\n             or \"lock_height\" -->\n        <attr name=\"aspect\" format=\"string\" />\n        <!-- Color to use when drawing LockPatternView paths. -->\n        <attr name=\"pathColor\" format=\"color|reference\" />\n        <!-- The regular pattern color -->\n        <attr name=\"regularColor\" format=\"color|reference\" />\n        <!-- The error color -->\n        <attr name=\"errorColor\" format=\"color|reference\" />\n        <!-- The success color -->\n        <attr name=\"successColor\" format=\"color|reference\"/>\n        <!-- The dot color -->\n        <attr name=\"dotColor\" format=\"color|reference\"/>\n        <!-- Color of the dot when it's activated -->\n        <attr name=\"dotActivatedColor\" format=\"color|reference\"/>\n        <!-- Keep dot in activated state until segment completion -->\n        <attr name=\"keepDotActivated\" format=\"boolean\"/>\n        <!-- Enlarge vertex entry area for some form factors -->\n        <attr name=\"enlargeVertexEntryArea\" format=\"boolean\"/>\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- QuickAccessWallet attributes -->\n    <!-- =============================== -->\n    <eat-comment />\n\n    <!-- Use <code>quickaccesswallet-service</code> as the root tag of the XML resource\n         that describes a {@link android.service.quickaccesswallet.QuickAccessWalletService},\n         which is referenced from its\n         {@link android.service.quickaccesswallet.QuickAccessWalletService#SERVICE_META_DATA}\n         meta-data entry.\n    -->\n    <declare-styleable name=\"QuickAccessWalletService\">\n        <!-- Fully qualified class name of an activity that allows the user to modify\n             the settings for this service. -->\n        <attr name=\"settingsActivity\" format=\"string\"/>\n        <!-- Fully qualified class name of an activity that allows the user to view\n             their entire wallet -->\n        <attr name=\"targetActivity\" format=\"string\"/>\n        <!-- Text shown on the empty state button if no cards are provided -->\n        <attr name=\"shortcutLongLabel\"/>\n        <!-- Text shown on the button that takes users to the wallet application -->\n        <attr name=\"shortcutShortLabel\"/>\n    </declare-styleable>\n\n    <!-- Use <code>recognition-service</code> as the root tag of the XML resource that\n         describes a {@link android.speech.RecognitionService}, which is referenced from\n         its {@link android.speech.RecognitionService#SERVICE_META_DATA} meta-data entry.\n         Described here are the attributes that can be included in that tag. -->\n    <declare-styleable name=\"RecognitionService\">\n        <attr name=\"settingsActivity\" />\n        <!-- Flag indicating whether a recognition service can be selected as default. The default\n             value of this flag is true. -->\n        <attr name=\"selectableAsDefault\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- Use <code>voice-interaction-service</code> as the root tag of the XML resource that\n         describes a {@link android.service.voice.VoiceInteractionService}, which is referenced from\n         its {@link android.service.voice.VoiceInteractionService#SERVICE_META_DATA} meta-data entry.\n         Described here are the attributes that can be included in that tag. -->\n    <declare-styleable name=\"VoiceInteractionService\">\n        <!-- The service that hosts active voice interaction sessions.  This is required. -->\n        <attr name=\"sessionService\" format=\"string\" />\n        <!-- The service that provides voice recognition. This is required. On Android 11 and\n             earlier, this must be a valid RecognitionService.\n             <p>\n             From Android 12 onward, this attribute does nothing. However, we still require it to\n             be set to something to reduce the risk that an app with an unspecified value gets\n             pushed to older platform versions, where it will cause a boot loop. To make sure\n             developers don't miss it, the system will reset the current assistant if this isn't\n             specified.-->\n        <attr name=\"recognitionService\" format=\"string\" />\n        <attr name=\"settingsActivity\" />\n        <!-- Flag indicating whether this voice interaction service is capable of handling the\n             assist action. -->\n        <attr name=\"supportsAssist\" format=\"boolean\" />\n        <!-- Flag indicating whether this voice interaction service is capable of being launched\n             from the keyguard. -->\n        <attr name=\"supportsLaunchVoiceAssistFromKeyguard\" format=\"boolean\" />\n        <!-- Flag indicating whether this voice interaction service can handle local voice\n             interaction requests from an Activity. This flag is new in\n             {@link android.os.Build.VERSION_CODES#N} and not used in previous versions. -->\n        <attr name=\"supportsLocalInteraction\" format=\"boolean\" />\n        <!-- The service that provides {@link android.service.voice.HotwordDetectionService}.\n             Expect a component name to be provided. @hide @SystemApi -->\n        <attr name=\"hotwordDetectionService\" format=\"string\" />\n        <!-- The service that provides {@link android.service.voice.VisualQueryDetectionService}.\n             Expect a component name to be provided. @hide @SystemApi -->\n        <attr name=\"visualQueryDetectionService\" format=\"string\" />\n\n    </declare-styleable>\n\n    <!-- Use <code>game-service</code> as the root tag of the XML resource that\n         describes a GameService.\n         Described here are the attributes that can be included in that tag. -->\n    <declare-styleable name=\"GameService\">\n        <!-- The service that hosts active game sessions.  This is required. -->\n        <attr name=\"gameSessionService\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- Use <code>game-mode-config</code> as the root tag of the XML resource that\n         describes a GameModeConfig.\n         Described here are the attributes that can be included in that tag. -->\n    <declare-styleable name=\"GameModeConfig\">\n        <!-- Set true to opt in BATTERY mode. -->\n        <attr name=\"supportsBatteryGameMode\" format=\"boolean\" />\n        <!-- Set true to opt in PERFORMANCE mode. -->\n        <attr name=\"supportsPerformanceGameMode\" format=\"boolean\" />\n        <!-- Set true to enable ANGLE. -->\n        <attr name=\"allowGameAngleDriver\" format=\"boolean\" />\n        <!-- Set true to allow resolution downscaling intervention. -->\n        <attr name=\"allowGameDownscaling\" format=\"boolean\" />\n        <!-- Set true to allow FPS override intervention. -->\n        <attr name=\"allowGameFpsOverride\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- Use <code>voice-enrollment-application</code>\n         as the root tag of the XML resource that escribes the supported keyphrases (hotwords)\n         by the enrollment application.\n         Described here are the attributes that can be included in that tag.\n         @hide\n          -->\n    <declare-styleable name=\"VoiceEnrollmentApplication\">\n        <!-- A globally unique ID for the keyphrase. @hide  -->\n        <attr name=\"searchKeyphraseId\" format=\"integer\" />\n        <!-- The actual keyphrase/hint text, or empty if not keyphrase dependent. @hide  -->\n        <attr name=\"searchKeyphrase\" format=\"string\" />\n        <!-- A comma separated list of BCP-47 language tag for locales that are supported\n             for this keyphrase, or empty if not locale dependent. @hide  -->\n        <attr name=\"searchKeyphraseSupportedLocales\" format=\"string\" />\n        <!-- Flags for supported recognition modes. @hide  -->\n        <attr name=\"searchKeyphraseRecognitionFlags\">\n            <flag name=\"none\" value=\"0\" />\n            <flag name=\"voiceTrigger\" value=\"0x1\" />\n            <flag name=\"userIdentification\" value=\"0x2\" />\n        </attr>\n    </declare-styleable>\n\n    <!-- Attributes used to style the Action Bar. -->\n    <declare-styleable name=\"ActionBar\">\n        <!-- The type of navigation to use. -->\n        <attr name=\"navigationMode\">\n            <!-- Normal static title text. -->\n            <enum name=\"normal\" value=\"0\" />\n            <!-- The action bar will use a selection list for navigation. -->\n            <enum name=\"listMode\" value=\"1\" />\n            <!-- The action bar will use a series of horizontal tabs for navigation. -->\n            <enum name=\"tabMode\" value=\"2\" />\n        </attr>\n        <!-- Options affecting how the action bar is displayed. -->\n        <attr name=\"displayOptions\">\n            <flag name=\"none\" value=\"0\" />\n            <flag name=\"useLogo\" value=\"0x1\" />\n            <flag name=\"showHome\" value=\"0x2\" />\n            <flag name=\"homeAsUp\" value=\"0x4\" />\n            <flag name=\"showTitle\" value=\"0x8\" />\n            <flag name=\"showCustom\" value=\"0x10\" />\n            <flag name=\"disableHome\" value=\"0x20\" />\n        </attr>\n        <!-- Specifies title text used for navigationMode=\"normal\". -->\n        <attr name=\"title\" />\n        <!-- Specifies subtitle text used for navigationMode=\"normal\". -->\n        <attr name=\"subtitle\" format=\"string\" />\n        <!-- Specifies a style to use for title text. -->\n        <attr name=\"titleTextStyle\" format=\"reference\" />\n        <!-- Specifies a style to use for subtitle text. -->\n        <attr name=\"subtitleTextStyle\" format=\"reference\" />\n        <!-- Specifies the drawable used for the application icon. -->\n        <attr name=\"icon\" />\n        <!-- Specifies the drawable used for the application logo. -->\n        <attr name=\"logo\" />\n        <!-- Specifies the drawable used for item dividers. -->\n        <attr name=\"divider\" />\n        <!-- Specifies a background drawable for the action bar. -->\n        <attr name=\"background\" />\n        <!-- Specifies a background drawable for a second stacked row of the action bar. -->\n        <attr name=\"backgroundStacked\" format=\"reference|color\" />\n        <!-- Specifies a background drawable for the bottom component of a split action bar. -->\n        <attr name=\"backgroundSplit\" format=\"reference|color\" />\n        <!-- Specifies a layout for custom navigation. Overrides navigationMode. -->\n        <attr name=\"customNavigationLayout\" format=\"reference\" />\n        <!-- Specifies a fixed height. -->\n        <attr name=\"height\" />\n        <!-- Specifies a layout to use for the \"home\" section of the action bar. -->\n        <attr name=\"homeLayout\" format=\"reference\" />\n        <!-- Specifies a style resource to use for an embedded progress bar. -->\n        <attr name=\"progressBarStyle\" />\n        <!-- Specifies a style resource to use for an indeterminate progress spinner. -->\n        <attr name=\"indeterminateProgressStyle\" format=\"reference\" />\n        <!-- Specifies the horizontal padding on either end for an embedded progress bar. -->\n        <attr name=\"progressBarPadding\" format=\"dimension\" />\n        <!-- Up navigation glyph. -->\n        <attr name=\"homeAsUpIndicator\" />\n        <!-- Specifies padding that should be applied to the left and right sides of\n             system-provided items in the bar. -->\n        <attr name=\"itemPadding\" format=\"dimension\" />\n        <!-- Set true to hide the action bar on a vertical nested scroll of content. -->\n        <attr name=\"hideOnContentScroll\" format=\"boolean\" />\n        <!-- Minimum inset for content views within a bar. Navigation buttons and\n             menu views are excepted. Only valid for some themes and configurations. -->\n        <attr name=\"contentInsetStart\" format=\"dimension\" />\n        <!-- Minimum inset for content views within a bar. Navigation buttons and\n             menu views are excepted. Only valid for some themes and configurations. -->\n        <attr name=\"contentInsetEnd\" format=\"dimension\" />\n        <!-- Minimum inset for content views within a bar. Navigation buttons and\n             menu views are excepted. Only valid for some themes and configurations. -->\n        <attr name=\"contentInsetLeft\" format=\"dimension\" />\n        <!-- Minimum inset for content views within a bar. Navigation buttons and\n             menu views are excepted. Only valid for some themes and configurations. -->\n        <attr name=\"contentInsetRight\" format=\"dimension\" />\n        <!-- Minimum inset for content views within a bar when a navigation button\n             is present, such as the Up button. Only valid for some themes and configurations. -->\n        <attr name=\"contentInsetStartWithNavigation\" format=\"dimension\" />\n        <!-- Minimum inset for content views within a bar when actions from a menu\n             are present. Only valid for some themes and configurations. -->\n        <attr name=\"contentInsetEndWithActions\" format=\"dimension\" />\n        <!-- Elevation for the action bar itself. -->\n        <attr name=\"elevation\" />\n        <!-- Reference to a theme that should be used to inflate popups\n             shown by widgets in the action bar. -->\n        <attr name=\"popupTheme\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"ActionMode\">\n        <!-- Specifies a style to use for title text. -->\n        <attr name=\"titleTextStyle\" />\n        <!-- Specifies a style to use for subtitle text. -->\n        <attr name=\"subtitleTextStyle\" />\n        <!-- Specifies a background for the action mode bar. -->\n        <attr name=\"background\" />\n        <!-- Specifies a background for the split action mode bar. -->\n        <attr name=\"backgroundSplit\" />\n        <!-- Specifies a fixed height for the action mode bar. -->\n        <attr name=\"height\" />\n        <!-- Specifies a layout to use for the \"close\" item at the starting edge. -->\n        <attr name=\"closeItemLayout\" format=\"reference\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"SearchView\">\n        <!-- The layout to use for the search view. -->\n        <attr name=\"layout\" />\n        <!-- The default state of the SearchView. If true, it will be iconified when not in\n             use and expanded when clicked. -->\n        <attr name=\"iconifiedByDefault\" format=\"boolean\" />\n        <!-- An optional maximum width of the SearchView. -->\n        <attr name=\"maxWidth\" />\n        <!-- An optional query hint string to be displayed in the empty query field. -->\n        <attr name=\"queryHint\" format=\"string\" />\n        <!-- Default query hint used when {@code queryHint} is undefined and\n             the search view's {@code SearchableInfo} does not provide a hint.\n             @hide -->\n        <attr name=\"defaultQueryHint\" format=\"string\" />\n        <!-- The IME options to set on the query text field. -->\n        <attr name=\"imeOptions\" />\n        <!-- The input type to set on the query text field. -->\n        <attr name=\"inputType\" />\n        <!-- Close button icon. -->\n        <attr name=\"closeIcon\" format=\"reference\" />\n        <!-- Go button icon. -->\n        <attr name=\"goIcon\" format=\"reference\" />\n        <!-- Search icon. -->\n        <attr name=\"searchIcon\" format=\"reference\" />\n        <!-- Search icon displayed as a text field hint. -->\n        <attr name=\"searchHintIcon\" format=\"reference\" />\n        <!-- Voice button icon. -->\n        <attr name=\"voiceIcon\" format=\"reference\" />\n        <!-- Commit icon shown in the query suggestion row. -->\n        <attr name=\"commitIcon\" format=\"reference\" />\n        <!-- Layout for query suggestion rows. -->\n        <attr name=\"suggestionRowLayout\" format=\"reference\" />\n        <!-- Background for the section containing the search query. -->\n        <attr name=\"queryBackground\" format=\"reference\" />\n        <!-- Background for the section containing the action (for example, voice search). -->\n        <attr name=\"submitBackground\" format=\"reference\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"Switch\">\n        <!-- Drawable to use as the \"thumb\" that switches back and forth. -->\n        <attr name=\"thumb\" />\n        <!-- Tint to apply to the thumb. -->\n        <attr name=\"thumbTint\" />\n        <!-- Blending mode used to apply the thumb tint. -->\n        <attr name=\"thumbTintMode\" />\n        <!-- Drawable to use as the \"track\" that the switch thumb slides within. -->\n        <attr name=\"track\" format=\"reference\" />\n        <!-- Tint to apply to the track. -->\n        <attr name=\"trackTint\" format=\"color\" />\n        <!-- Blending mode used to apply the track tint. -->\n        <attr name=\"trackTintMode\">\n            <!-- The tint is drawn on top of the drawable.\n                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->\n            <enum name=\"src_over\" value=\"3\" />\n            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s\n                 color channels are thrown out. [Sa * Da, Sc * Da] -->\n            <enum name=\"src_in\" value=\"5\" />\n            <!-- The tint is drawn above the drawable, but with the drawable’s alpha\n                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->\n            <enum name=\"src_atop\" value=\"9\" />\n            <!-- Multiplies the color and alpha channels of the drawable with those of\n                 the tint. [Sa * Da, Sc * Dc] -->\n            <enum name=\"multiply\" value=\"14\" />\n            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->\n            <enum name=\"screen\" value=\"15\" />\n            <!-- Combines the tint and drawable color and alpha channels, clamping the\n                 result to valid color values. Saturate(S + D) -->\n            <enum name=\"add\" value=\"16\" />\n        </attr>\n        <!-- Text to use when the switch is in the checked/\"on\" state. -->\n        <attr name=\"textOn\" />\n        <!-- Text to use when the switch is in the unchecked/\"off\" state. -->\n        <attr name=\"textOff\" />\n        <!-- Amount of padding on either side of text within the switch thumb. -->\n        <attr name=\"thumbTextPadding\" format=\"dimension\" />\n        <!-- TextAppearance style for text displayed on the switch thumb. -->\n        <attr name=\"switchTextAppearance\" format=\"reference\" />\n        <!-- Minimum width for the switch component. -->\n        <attr name=\"switchMinWidth\" format=\"dimension\" />\n        <!-- Minimum space between the switch and caption text. -->\n        <attr name=\"switchPadding\" format=\"dimension\" />\n        <!-- Whether to split the track and leave a gap for the thumb drawable. -->\n        <attr name=\"splitTrack\" />\n        <!-- Whether to draw on/off text. -->\n        <attr name=\"showText\" format=\"boolean\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"Pointer\">\n        <!-- Reference to a pointer icon drawable with STYLE_ARROW. -->\n        <attr name=\"pointerIconArrow\" format=\"reference\" />\n        <!-- Reference to a pointer icon drawable with STYLE_SPOT_HOVER. -->\n        <attr name=\"pointerIconSpotHover\" format=\"reference\" />\n        <!-- Reference to a pointer icon drawable with STYLE_SPOT_TOUCH. -->\n        <attr name=\"pointerIconSpotTouch\" format=\"reference\" />\n        <!-- Reference to a pointer icon drawable with STYLE_SPOT_ANCHOR. -->\n        <attr name=\"pointerIconSpotAnchor\" format=\"reference\" />\n        <!-- Reference to a pointer drawable with STYLE_CONTEXT_MENU. -->\n        <attr name=\"pointerIconContextMenu\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_HAND. -->\n        <attr name=\"pointerIconHand\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_HELP. -->\n        <attr name=\"pointerIconHelp\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_WAIT. -->\n        <attr name=\"pointerIconWait\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_CELL. -->\n        <attr name=\"pointerIconCell\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_CROSSHAIR. -->\n        <attr name=\"pointerIconCrosshair\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_TEXT. -->\n        <attr name=\"pointerIconText\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_VERTICAL_TEXT. -->\n        <attr name=\"pointerIconVerticalText\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_ALIAS. -->\n        <attr name=\"pointerIconAlias\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_COPY. -->\n        <attr name=\"pointerIconCopy\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_NODROP. -->\n        <attr name=\"pointerIconNodrop\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_ALL_SCROLL. -->\n        <attr name=\"pointerIconAllScroll\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_HORIZONTAL_DOUBLE_ARROW. -->\n        <attr name=\"pointerIconHorizontalDoubleArrow\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_VERTICAL_DOUBLE_ARROW. -->\n        <attr name=\"pointerIconVerticalDoubleArrow\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW. -->\n        <attr name=\"pointerIconTopRightDiagonalDoubleArrow\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW. -->\n        <attr name=\"pointerIconTopLeftDiagonalDoubleArrow\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_ZOOM_IN. -->\n        <attr name=\"pointerIconZoomIn\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_ZOOM_OUT. -->\n        <attr name=\"pointerIconZoomOut\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_GRAB. -->\n        <attr name=\"pointerIconGrab\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with STYLE_GRABBING. -->\n        <attr name=\"pointerIconGrabbing\" format=\"reference\"/>\n        <!-- Reference to a pointer drawable with HANDWRITING. -->\n        <attr name=\"pointerIconHandwriting\" format=\"reference\"/>\n    </declare-styleable>\n\n    <declare-styleable name=\"PointerIcon\">\n        <!-- Drawable to use as the icon bitmap. -->\n        <attr name=\"bitmap\" format=\"reference\" />\n        <!-- X coordinate of the icon hot spot. -->\n        <attr name=\"hotSpotX\" format=\"dimension\" />\n        <!-- Y coordinate of the icon hot spot. -->\n        <attr name=\"hotSpotY\" format=\"dimension\" />\n    </declare-styleable>\n\n    <!-- @hide -->\n    <declare-styleable name=\"PointerIconVectorTheme\">\n        <attr name=\"pointerIconVectorFill\" format=\"color\" />\n        <attr name=\"pointerIconVectorFillInverse\" format=\"color\" />\n        <attr name=\"pointerIconVectorStroke\" format=\"color\" />\n        <attr name=\"pointerIconVectorStrokeInverse\" format=\"color\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"Storage\">\n        <!-- path to mount point for the storage. -->\n        <attr name=\"mountPoint\" format=\"string\" />\n        <!-- user visible description of the storage. -->\n        <attr name=\"storageDescription\" format=\"string\" />\n        <!-- true if the storage is the primary external storage. -->\n        <attr name=\"primary\" format=\"boolean\" />\n        <!-- true if the storage is removable. -->\n        <attr name=\"removable\" format=\"boolean\" />\n        <!-- true if the storage is emulated via the FUSE sdcard daemon. -->\n        <attr name=\"emulated\" format=\"boolean\" />\n        <!-- number of megabytes of storage MTP should reserve for free storage\n             (used for emulated storage that is shared with system's data partition). -->\n        <attr name=\"mtpReserve\" format=\"integer\" />\n        <!-- true if the storage can be shared via USB mass storage. -->\n        <attr name=\"allowMassStorage\" format=\"boolean\" />\n        <!-- maximum file size for the volume in megabytes, zero or unspecified if it is unbounded. -->\n        <attr name=\"maxFileSize\" format=\"integer\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"SwitchPreference\">\n        <!-- The summary for the Preference in a PreferenceActivity screen when the\n             SwitchPreference is checked. If separate on/off summaries are not\n             needed, the summary attribute can be used instead. -->\n        <attr name=\"summaryOn\" />\n        <!-- The summary for the Preference in a PreferenceActivity screen when the\n             SwitchPreference is unchecked. If separate on/off summaries are not\n             needed, the summary attribute can be used instead. -->\n        <attr name=\"summaryOff\" />\n        <!-- The text used on the switch itself when in the \"on\" state.\n             This should be a very SHORT string, as it appears in a small space. -->\n        <attr name=\"switchTextOn\" format=\"string\" />\n        <!-- The text used on the switch itself when in the \"off\" state.\n             This should be a very SHORT string, as it appears in a small space. -->\n        <attr name=\"switchTextOff\" format=\"string\" />\n        <!-- The state (true for on, or false for off) that causes dependents to be disabled. By default,\n             dependents will be disabled when this is unchecked, so the value of this preference is false. -->\n        <attr name=\"disableDependentsState\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"SeekBarPreference\">\n        <attr name=\"layout\" />\n        <!-- Attribute indicating whether the slider within this preference can be adjusted, that is\n        pressing left/right keys when this preference is focused will move the slider accordingly\n        (for example, inline adjustable preferences). False, if the slider within the preference is\n        read-only and cannot be adjusted. By default, the seekbar is adjustable. -->\n        <attr name=\"adjustable\" format=\"boolean\" />\n        <!-- Flag indicating whether the TextView next to the seekbar that shows the current seekbar value will be\n        displayed. If true, the view is VISIBLE; if false, the view will be GONE. By default, this view is VISIBLE. -->\n        <attr name=\"showSeekBarValue\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- Base attributes available to PreferenceFragment. -->\n    <declare-styleable name=\"PreferenceFragment\">\n        <!-- The layout for the PreferenceFragment. This should rarely need to be changed. -->\n        <attr name=\"layout\" />\n        <attr name=\"divider\" />\n    </declare-styleable>\n\n    <!-- Base attributes available to PreferenceScreen. -->\n    <declare-styleable name=\"PreferenceScreen\">\n        <!-- The layout for the PreferenceScreen. This should rarely need to be changed. -->\n        <attr name=\"screenLayout\" format=\"reference\" />\n        <attr name=\"divider\" />\n    </declare-styleable>\n\n    <!-- Base attributes available to PreferenceActivity. -->\n    <declare-styleable name=\"PreferenceActivity\">\n        <!-- The layout for the Preference Activity. This should rarely need to be changed. -->\n        <attr name=\"layout\" />\n        <!-- The layout for the Preference Header. This should rarely need to be changed. -->\n        <attr name=\"headerLayout\" format=\"reference\" />\n        <!-- true if the Icon view will be removed when there is none and thus not showing\n             the fixed margins. -->\n        <attr name=\"headerRemoveIconIfEmpty\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- Use <code>tts-engine</code> as the root tag of the XML resource that\n         describes a text to speech engine implemented as a subclass of\n         {@link android.speech.tts.TextToSpeechService}.\n\n         The XML resource must be referenced from its\n         {@link android.speech.tts.TextToSpeech.Engine#SERVICE_META_DATA} meta-data\n         entry. -->\n    <declare-styleable name=\"TextToSpeechEngine\">\n        <attr name=\"settingsActivity\" />\n    </declare-styleable>\n\n    <!-- Use <code>keyboard-layouts</code> as the root tag of the XML resource that\n         describes a collection of keyboard layouts provided by an application.\n         Each keyboard layout is declared by a <code>keyboard-layout</code> tag\n         with these attributes.\n\n         The XML resource that contains the keyboard layouts must be referenced from its\n         {@link android.hardware.input.InputManager#META_DATA_KEYBOARD_LAYOUTS}\n         meta-data entry used with broadcast receivers for\n         {@link android.hardware.input.InputManager#ACTION_QUERY_KEYBOARD_LAYOUTS}. -->\n    <declare-styleable name=\"KeyboardLayout\">\n        <!-- The name of the keyboard layout, must be unique in the receiver. -->\n        <attr name=\"name\" />\n        <!-- The display label of the keyboard layout. -->\n        <attr name=\"label\" />\n        <!-- The key character map file resource. -->\n        <attr name=\"keyboardLayout\" format=\"reference\" />\n        <!-- The locales the given keyboard layout corresponds to. This is a list of\n             BCP-47 conformant language tags separated by the delimiter ',' or '|'.\n             Some examples of language tags are: en-US, zh-Hans-CN, el-Grek-polyton.\n             It includes information for language code, country code, variant, and script\n             code like ‘Latn’, ‘Cyrl’, etc. -->\n        <attr name=\"keyboardLocale\" format=\"string\"/>\n        <!-- The layout type of the given keyboardLayout.\n             NOTE: The enum to int value mapping must remain stable -->\n        <attr name=\"keyboardLayoutType\" format=\"enum\">\n            <!-- Qwerty-based keyboard layout. -->\n            <enum name=\"qwerty\" value=\"1\" />\n            <!-- Qwertz-based keyboard layout. -->\n            <enum name=\"qwertz\" value=\"2\" />\n            <!-- Azerty-based keyboard layout. -->\n            <enum name=\"azerty\" value=\"3\" />\n            <!-- Dvorak keyboard layout. -->\n            <enum name=\"dvorak\" value=\"4\" />\n            <!-- Colemak keyboard layout. -->\n            <enum name=\"colemak\" value=\"5\" />\n            <!-- Workman keyboard layout. -->\n            <enum name=\"workman\" value=\"6\" />\n            <!-- Turkish-Q keyboard layout. -->\n            <enum name=\"turkish_q\" value=\"7\" />\n            <!-- Turkish-F keyboard layout. -->\n            <enum name=\"turkish_f\" value=\"8\" />\n            <!-- Keyboard layout that has been enhanced with a large number of extra characters. -->\n            <enum name=\"extended\" value=\"9\" />\n        </attr>\n        <!-- The vendor ID of the hardware the given layout corresponds to. @hide -->\n        <attr name=\"vendorId\" format=\"integer\" />\n        <!-- The product ID of the hardware the given layout corresponds to. @hide -->\n        <attr name=\"productId\" format=\"integer\" />\n    </declare-styleable>\n\n    <!-- @hide -->\n    <declare-styleable name=\"KeyboardGlyphMap\">\n        <attr name=\"glyphMap\" format=\"reference\" />\n        <attr name=\"vendorId\" format=\"integer\" />\n        <attr name=\"productId\" format=\"integer\" />\n    </declare-styleable>\n\n    <!-- @hide -->\n    <declare-styleable name=\"KeyGlyph\">\n        <attr name=\"keycode\" />\n        <attr name=\"glyphDrawable\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- @hide -->\n    <declare-styleable name=\"ModifierGlyph\">\n        <!-- The values are taken from public constants for modifier state defined in\n             {@see KeyEvent.java}. Here we explicitly allow only one modifier bit as value, since\n             this represents the modifier key -->\n        <attr name=\"modifier\">\n            <enum name=\"META\" value=\"0x10000\" />\n            <enum name=\"CTRL\" value=\"0x1000\" />\n            <enum name=\"ALT\" value=\"0x02\" />\n            <enum name=\"SHIFT\" value=\"0x1\" />\n            <enum name=\"SYM\" value=\"0x4\" />\n            <enum name=\"FUNCTION\" value=\"0x8\" />\n            <enum name=\"CAPS_LOCK\" value=\"0x100000\" />\n            <enum name=\"NUM_LOCK\" value=\"0x200000\" />\n            <enum name=\"SCROLL_LOCK\" value=\"0x400000\" />\n        </attr>\n        <attr name=\"glyphDrawable\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- @hide -->\n    <attr name=\"modifierState\">\n        <flag name=\"META\" value=\"0x10000\" />\n        <flag name=\"CTRL\" value=\"0x1000\" />\n        <flag name=\"ALT\" value=\"0x02\" />\n        <flag name=\"SHIFT\" value=\"0x1\" />\n        <flag name=\"SYM\" value=\"0x4\" />\n        <flag name=\"FUNCTION\" value=\"0x8\" />\n        <flag name=\"CAPS_LOCK\" value=\"0x100000\" />\n        <flag name=\"NUM_LOCK\" value=\"0x200000\" />\n        <flag name=\"SCROLL_LOCK\" value=\"0x400000\" />\n    </attr>\n\n    <!-- @hide -->\n    <declare-styleable name=\"HardwareDefinedShortcut\">\n        <attr name=\"keycode\" />\n        <!-- The values are taken from public constants for modifier state defined in\n             {@see KeyEvent.java}. Here we allow multiple modifier flags as value, since this\n             represents the modifier state -->\n        <attr name=\"modifierState\" />\n        <attr name=\"outKeycode\" />\n    </declare-styleable>\n\n    <!-- @hide -->\n    <declare-styleable name=\"FunctionRowKey\">\n        <attr name=\"keycode\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"Bookmark\">\n        <attr name=\"keycode\" />\n        <attr name=\"modifierState\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"MediaRouteButton\">\n        <!-- This drawable is a state list where the \"activated\" state\n             indicates active media routing. Non-activated indicates\n             that media is playing to the local device only.\n             @hide -->\n        <attr name=\"externalRouteEnabledDrawable\" format=\"reference\" />\n\n        <!-- The types of media routes the button and its resulting\n             chooser will filter by. -->\n        <attr name=\"mediaRouteTypes\" format=\"integer\">\n            <!-- Allow selection of live audio routes. -->\n            <enum name=\"liveAudio\" value=\"0x1\" />\n            <!-- Allow selection of user (app-specified) routes. -->\n            <enum name=\"user\" value=\"0x800000\" />\n        </attr>\n\n        <attr name=\"minWidth\" />\n        <attr name=\"minHeight\" />\n    </declare-styleable>\n\n    <!-- PagedView specific attributes. These attributes are used to customize\n         a PagedView view in XML files. -->\n    <declare-styleable name=\"PagedView\">\n        <!-- The space between adjacent pages of the PagedView. -->\n        <attr name=\"pageSpacing\" format=\"dimension\" />\n        <!-- The padding for the scroll indicator area. -->\n        <attr name=\"scrollIndicatorPaddingLeft\" format=\"dimension\" />\n        <attr name=\"scrollIndicatorPaddingRight\" format=\"dimension\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"KeyguardGlowStripView\">\n        <attr name=\"dotSize\" format=\"dimension\" />\n        <attr name=\"numDots\" format=\"integer\" />\n        <attr name=\"glowDot\" format=\"reference\" />\n        <attr name=\"leftToRight\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- Some child types have special behavior. -->\n    <attr name=\"layout_childType\">\n        <!-- No special behavior. Layout will proceed as normal. -->\n        <enum name=\"none\" value=\"0\" />\n        <!-- Widget container.\n             This will be resized in response to certain events. -->\n        <enum name=\"widget\" value=\"1\" />\n        <!-- Security challenge container.\n             This will be dismissed/shown in response to certain events,\n             possibly obscuring widget elements. -->\n        <enum name=\"challenge\" value=\"2\" />\n        <!-- User switcher.\n             This will consume space from the total layout area. -->\n        <enum name=\"userSwitcher\" value=\"3\" />\n        <!-- Scrim. This will block access to child views that\n             come before it in the child list in bouncer mode. -->\n        <enum name=\"scrim\" value=\"4\" />\n        <!-- The home for widgets. All widgets will be descendents of this. -->\n        <enum name=\"widgets\" value=\"5\" />\n        <!-- This is a handle that is used for expanding the\n             security challenge container when it is collapsed. -->\n        <enum name=\"expandChallengeHandle\" value=\"6\" />\n        <!-- Delete drop target.  This will be the drop target to delete pages. -->\n        <enum name=\"pageDeleteDropTarget\" value=\"7\" />\n    </attr>\n\n    <!-- Attributes that can be used with <code>&lt;FragmentBreadCrumbs&gt;</code>\n    tags. -->\n    <declare-styleable name=\"FragmentBreadCrumbs\">\n        <attr name=\"gravity\" />\n        <attr name=\"itemLayout\" format=\"reference\" />\n        <attr name=\"itemColor\" format=\"color|reference\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"Toolbar\">\n        <attr name=\"titleTextAppearance\" format=\"reference\" />\n        <attr name=\"subtitleTextAppearance\" format=\"reference\" />\n        <attr name=\"title\" />\n        <attr name=\"subtitle\" />\n        <attr name=\"gravity\" />\n        <!--  Specifies extra space on the left, start, right and end sides\n              of the toolbar's title. Margin values should be positive. -->\n        <attr name=\"titleMargin\" format=\"dimension\" />\n        <!--  Specifies extra space on the start side of the toolbar's title.\n              If both this attribute and titleMargin are specified, then this\n              attribute takes precedence. Margin values should be positive. -->\n        <attr name=\"titleMarginStart\" format=\"dimension\" />\n        <!--  Specifies extra space on the end side of the toolbar's title.\n              If both this attribute and titleMargin are specified, then this\n              attribute takes precedence. Margin values should be positive. -->\n        <attr name=\"titleMarginEnd\" format=\"dimension\" />\n        <!--  Specifies extra space on the top side of the toolbar's title.\n              If both this attribute and titleMargin are specified, then this\n              attribute takes precedence. Margin values should be positive. -->\n        <attr name=\"titleMarginTop\" format=\"dimension\" />\n        <!--  Specifies extra space on the bottom side of the toolbar's title.\n              If both this attribute and titleMargin are specified, then this\n              attribute takes precedence. Margin values should be positive. -->\n        <attr name=\"titleMarginBottom\" format=\"dimension\" />\n        <attr name=\"contentInsetStart\" />\n        <attr name=\"contentInsetEnd\" />\n        <attr name=\"contentInsetLeft\" />\n        <attr name=\"contentInsetRight\" />\n        <attr name=\"contentInsetStartWithNavigation\" />\n        <attr name=\"contentInsetEndWithActions\" />\n        <attr name=\"maxButtonHeight\" format=\"dimension\" />\n        <attr name=\"navigationButtonStyle\" format=\"reference\" />\n        <attr name=\"buttonGravity\">\n            <!-- Push object to the top of its container, not changing its size. -->\n            <flag name=\"top\" value=\"0x30\" />\n            <!-- Push object to the bottom of its container, not changing its size. -->\n            <flag name=\"bottom\" value=\"0x50\" />\n        </attr>\n        <!-- Icon drawable to use for the collapse button. -->\n        <attr name=\"collapseIcon\" format=\"reference\" />\n        <!-- Text to set as the content description for the collapse button. -->\n        <attr name=\"collapseContentDescription\" format=\"string\" />\n        <!-- Reference to a theme that should be used to inflate popups\n             shown by widgets in the toolbar. -->\n        <attr name=\"popupTheme\" format=\"reference\" />\n        <!-- Icon drawable to use for the navigation button located at\n             the start of the toolbar. -->\n        <attr name=\"navigationIcon\" format=\"reference\" />\n        <!-- Text to set as the content description for the navigation button\n             located at the start of the toolbar. -->\n        <attr name=\"navigationContentDescription\" format=\"string\" />\n        <!-- Drawable to set as the logo that appears at the starting side of\n             the Toolbar, just after the navigation button. -->\n        <attr name=\"logo\" />\n        <!-- A content description string to describe the appearance of the\n             associated logo image. -->\n        <attr name=\"logoDescription\" format=\"string\" />\n        <!-- A color to apply to the title string. -->\n        <attr name=\"titleTextColor\" format=\"color\" />\n        <!-- A color to apply to the subtitle string. -->\n        <attr name=\"subtitleTextColor\" format=\"color\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"Toolbar_LayoutParams\">\n        <attr name=\"layout_gravity\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"ActionBar_LayoutParams\">\n        <attr name=\"layout_gravity\" />\n    </declare-styleable>\n\n    <!-- Used as a filter array on the theme to pull out only the EdgeEffect-relevant bits. -->\n    <declare-styleable name=\"EdgeEffect\">\n        <attr name=\"colorEdgeEffect\" />\n    </declare-styleable>\n\n    <!-- Use <code>tv-input</code> as the root tag of the XML resource that describes a\n         {@link android.media.tv.TvInputService}, which is referenced from its\n         {@link android.media.tv.TvInputService#SERVICE_META_DATA} meta-data entry.\n         Described here are the attributes that can be included in that tag. -->\n    <declare-styleable name=\"TvInputService\">\n        <!-- Component name of an activity that allows the user to set up this service. -->\n        <attr name=\"setupActivity\" format=\"string\" />\n        <!-- Component name of an activity that allows the user to modify the settings for this\n             service.\n             {@deprecated This value is deprecated and not used by the framework starting from API\n                         level 26. Use setupActivity instead.} -->\n        <attr name=\"settingsActivity\" />\n        <!-- Attribute whether the TV input service can record programs. This value can be changed\n             at runtime by calling\n             {@link android.media.tv.TvInputManager#updateTvInputInfo(android.media.tv.TvInputInfo)}. -->\n        <attr name=\"canRecord\" format=\"boolean\" />\n        <!-- The number of tuners that the TV input service is associated with. This value can be\n             changed at runtime by calling\n             {@link android.media.tv.TvInputManager#updateTvInputInfo(android.media.tv.TvInputInfo)}. -->\n        <attr name=\"tunerCount\" format=\"integer\" />\n        <!-- Attribute whether the TV input service can pause recording programs.\n             This value can be changed at runtime by calling\n             {@link android.media.tv.TvInputManager#updateTvInputInfo(android.media.tv.TvInputInfo)}\n             . -->\n        <attr name=\"canPauseRecording\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- Use <code>tv-interactive-app</code> as the root tag of the XML resource that describes a\n         {@link android.media.tv.interactive.TvInteractiveAppService}, which is referenced\n         from its\n         {@link android.media.tv.interactive.TvInteractiveAppService#SERVICE_META_DATA}\n         meta-data entry. Described here are the attributes that can be included in that tag. -->\n    <declare-styleable name=\"TvInteractiveAppService\">\n        <!-- The interactive app types that the TV interactive app service supports.\n             Reference to a string array resource that describes the supported types,\n             e.g. HbbTv, Ginga. -->\n        <attr name=\"supportedTypes\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- Use <code>tv-ad-service</code> as the root tag of the XML resource that describes a\n         android.media.tv.ad.TvAdService, which is referenced from its\n         android.media.tv.ad.TvAdService#SERVICE_META_DATA meta-data entry. Described here\n         are the attributes that can be included in that tag. -->\n    <declare-styleable name=\"TvAdService\">\n        <!-- The advertisement types that the TV ad service supports.\n             Reference to a string array resource that describes the supported types,\n             e.g. linear, overlay. -->\n        <attr name=\"adServiceTypes\" format=\"reference\" />\n    </declare-styleable>\n\n\n    <!-- Attributes that can be used with <code>rating-system-definition</code> tags inside of the\n         XML resource that describes TV content rating of a {@link android.media.tv.TvInputService},\n         which is referenced from its\n         {@link android.media.tv.TvInputManager#META_DATA_CONTENT_RATING_SYSTEMS}. -->\n    <declare-styleable name=\"RatingSystemDefinition\">\n        <!-- The unique name of the content rating system. -->\n        <attr name=\"name\" />\n        <!-- The title of the content rating system which is shown to the user. -->\n        <attr name=\"title\" />\n        <!-- The short description of the content rating system. -->\n        <attr name=\"description\" />\n        <!-- The country code associated with the content rating system, which consists of two\n             uppercase letters that conform to the ISO 3166 standard. -->\n        <attr name=\"country\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- Attributes that can be used with <code>rating-definition</code> tags inside of the XML\n         resource that describes TV content rating of a {@link android.media.tv.TvInputService},\n         which is referenced from its\n         {@link android.media.tv.TvInputManager#META_DATA_CONTENT_RATING_SYSTEMS}. -->\n    <declare-styleable name=\"RatingDefinition\">\n        <!-- The unique name of the content rating. -->\n        <attr name=\"name\" />\n        <!-- The title of the content rating which is shown to the user. -->\n        <attr name=\"title\" />\n        <!-- The short description of the content rating. -->\n        <attr name=\"description\" />\n        <!-- The age associated with the content rating. The content of this rating is suitable for\n             people of this age or above. -->\n        <attr name=\"contentAgeHint\" format=\"integer\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"ResolverDrawerLayout\">\n        <attr name=\"maxWidth\" />\n        <attr name=\"maxCollapsedHeight\" format=\"dimension\" />\n        <attr name=\"maxCollapsedHeightSmall\" format=\"dimension\" />\n        <!-- Whether the Drawer should be positioned at the top rather than at the bottom. -->\n        <attr name=\"showAtTop\" format=\"boolean\" />\n        <!-- By default `ResolverDrawerLayout`’s children views with `layout_ignoreOffset` property\n             set to true have a fixed position in the layout that won’t be affected by the drawer’s\n             movements. This property alternates that behavior. It specifies a child view’s id that\n             will push all ignoreOffset siblings below it when the drawer is moved i.e. setting the\n             top limit the ignoreOffset elements. -->\n        <attr name=\"ignoreOffsetTopLimit\" format=\"reference\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"MessagingLinearLayout\">\n        <attr name=\"spacing\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"DateTimeView\">\n        <attr name=\"showRelative\" format=\"boolean\" />\n        <!-- For relative times, controls what kinds of times get disambiguation text.\n\n             The default value is \"future\".\n\n             Does nothing if showRelative=false.\n             -->\n        <attr name=\"relativeTimeDisambiguationText\">\n            <!-- Times in the past will have extra clarifying text indicating that the time is in\n                 the past. For example, 1 minute ago is represented as \"1m ago\". If this flag is not\n                 set, times in the past are represented as just \"1m\". -->\n            <flag name=\"past\" value=\"0x01\" />\n            <!-- Times in the future will have extra clarifying text indicating that the time is in\n                 the future. For example, 1 minute in the future is represented as \"in 1m\". If this\n                 flag is not set, times in the future are represented as just \"1m\". -->\n            <flag name=\"future\" value=\"0x02\" />\n        </attr>\n        <!-- For relative times, sets the length of the time unit displayed (minutes, hours, etc.).\n\n             Does nothing if showRelative=false.\n             -->\n        <attr name=\"relativeTimeUnitDisplayLength\">\n            <!-- The time unit will be shown as a short as possible (1 character if possible). -->\n            <enum name=\"shortest\" value=\"0\" />\n            <!-- The time unit will be shortened to a medium length (2-3 characters in general). -->\n            <enum name=\"medium\" value=\"1\" />\n        </attr>\n    </declare-styleable>\n\n    <declare-styleable name=\"ResolverDrawerLayout_LayoutParams\">\n        <attr name=\"layout_alwaysShow\" format=\"boolean\" />\n        <attr name=\"layout_ignoreOffset\" format=\"boolean\" />\n        <attr name=\"layout_gravity\" />\n        <attr name=\"layout_hasNestedScrollIndicator\" format=\"boolean\" />\n        <attr name=\"layout_maxHeight\" format=\"dimension\"/>\n    </declare-styleable>\n\n    <!-- @hide -->\n    <declare-styleable name=\"Lighting\">\n        <attr name=\"lightY\" />\n        <attr name=\"lightZ\" />\n        <attr name=\"lightRadius\" />\n        <attr name=\"ambientShadowAlpha\" />\n        <attr name=\"spotShadowAlpha\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"RestrictionEntry\">\n        <attr name=\"key\" />\n        <attr name=\"restrictionType\">\n            <enum name=\"hidden\" value=\"0\" />\n            <enum name=\"bool\" value=\"1\" />\n            <enum name=\"choice\" value=\"2\" />\n            <enum name=\"multi-select\" value=\"4\" />\n            <enum name=\"integer\" value=\"5\" />\n            <enum name=\"string\" value=\"6\" />\n            <enum name=\"bundle\" value=\"7\" />\n            <enum name=\"bundle_array\" value=\"8\" />\n        </attr>\n        <attr name=\"title\" />\n        <attr name=\"description\" />\n        <attr name=\"defaultValue\" />\n        <attr name=\"entries\" />\n        <attr name=\"entryValues\" />\n    </declare-styleable>\n\n    <!-- Used to describe the gradient for fill or stroke in a path of VectorDrawable. -->\n    <declare-styleable name=\"GradientColor\">\n        <!-- Start color of the gradient. -->\n        <attr name=\"startColor\" />\n        <!-- Optional center color. -->\n        <attr name=\"centerColor\" />\n        <!-- End color of the gradient. -->\n        <attr name=\"endColor\" />\n        <!-- Type of gradient. The default type is linear. -->\n        <attr name=\"type\" />\n\n        <!-- Only applied to RadialGradient-->\n        <!-- Radius of the gradient, used only with radial gradient. -->\n        <attr name=\"gradientRadius\" />\n\n        <!-- Only applied to SweepGradient / RadialGradient-->\n        <!-- X coordinate of the center of the gradient within the path. -->\n        <attr name=\"centerX\" />\n        <!-- Y coordinate of the center of the gradient within the path. -->\n        <attr name=\"centerY\" />\n\n        <!-- LinearGradient specific -->\n        <!-- X coordinate of the start point origin of the gradient.\n             Defined in same coordinates as the path itself -->\n        <attr name=\"startX\" format=\"float\" />\n        <!-- Y coordinate of the start point of the gradient within the shape.\n             Defined in same coordinates as the path itself -->\n        <attr name=\"startY\" format=\"float\" />\n        <!-- X coordinate of the end point origin of the gradient.\n             Defined in same coordinates as the path itself -->\n        <attr name=\"endX\" format=\"float\" />\n        <!-- Y coordinate of the end point of the gradient within the shape.\n             Defined in same coordinates as the path itself -->\n        <attr name=\"endY\" format=\"float\" />\n\n        <!-- Defines the tile mode of the gradient. SweepGradient don't support tiling. -->\n        <attr name=\"tileMode\"/>\n    </declare-styleable>\n\n    <!-- Describes an item of a GradientColor. Minimally need 2 items to define the gradient\n         Colors defined in <item> override the simple color attributes such as\n         \"startColor / centerColor / endColor\" are ignored. -->\n    <declare-styleable name=\"GradientColorItem\">\n        <!-- The offset (or ratio) of this current color item inside the gradient.\n             The value is only meaningful when it is between 0 and 1. -->\n        <attr name=\"offset\" format=\"float\" />\n        <!-- The current color for the offset inside the gradient. -->\n        <attr name=\"color\" />\n    </declare-styleable>\n\n    <!-- @hide Attributes which will be read by the Activity to intialize the\n               base activity TaskDescription. -->\n    <declare-styleable name=\"ActivityTaskDescription\">\n        <!-- @hide From Theme.colorPrimary, used for the TaskDescription primary\n                   color. -->\n        <attr name=\"colorPrimary\" />\n        <!-- @hide From Theme.colorBackground, used for the TaskDescription background\n                   color. -->\n        <attr name=\"colorBackground\" />\n        <!-- @hide From Theme.colorBackgroundFloating, used for the TaskDescription background\n                   color floating. -->\n        <attr name=\"colorBackgroundFloating\" />\n        <!-- @hide From Theme.statusBarColor, used for the TaskDescription status bar color. -->\n        <attr name=\"statusBarColor\"/>\n        <!-- @hide From Theme.navigationBarColor, used for the TaskDescription navigation bar\n                   color. -->\n        <attr name=\"navigationBarColor\"/>\n        <!-- @hide From Window.enforceStatusBarContrast -->\n        <attr name=\"enforceStatusBarContrast\"/>\n        <!-- @hide From Window.enforceNavigationBarContrast -->\n        <attr name=\"enforceNavigationBarContrast\"/>\n    </declare-styleable>\n\n    <declare-styleable name=\"Shortcut\">\n        <attr name=\"shortcutId\" format=\"string\" />\n        <attr name=\"enabled\" />\n        <attr name=\"icon\" />\n        <attr name=\"shortcutShortLabel\" format=\"reference\" />\n        <attr name=\"shortcutLongLabel\" format=\"reference\" />\n        <attr name=\"shortcutDisabledMessage\" format=\"reference\" />\n        <attr name=\"splashScreenTheme\" format=\"reference\"/>\n    </declare-styleable>\n\n    <declare-styleable name=\"ShortcutCategories\">\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- Attributes that are read when parsing a &lt;font&gt; tag, which is a child of\n         &lt;font-family&gt;. This represents an actual font file and its attributes. -->\n    <declare-styleable name=\"FontFamilyFont\">\n        <!-- The style of the given font file. This will be used when the font is being loaded into\n         the font stack and will override any style information in the font's header tables. If\n         unspecified, the value in the font's header tables will be used. -->\n        <attr name=\"fontStyle\">\n            <enum name=\"normal\" value=\"0\" />\n            <enum name=\"italic\" value=\"1\" />\n        </attr>\n        <!-- The reference to the font file to be used. This should be a file in the res/font folder\n         and should therefore have an R reference value. E.g. @font/myfont -->\n        <attr name=\"font\" format=\"reference\" />\n        <!-- The weight of the given font file. This will be used when the font is being loaded into\n         the font stack and will override any weight information in the font's header tables. Must\n         be a positive number, a multiple of 100, and between 100 and 900, inclusive. The most\n         common values are 400 for regular weight and 700 for bold weight. If unspecified, the value\n         in the font's header tables will be used. -->\n        <attr name=\"fontWeight\" format=\"integer\" />\n        <!-- The index of the font in the ttc (TrueType Collection) font file. If the font file\n         referenced is not in the ttc format, this attribute needs not be specified.\n         {@link android.graphics.Typeface.Builder#setTtcIndex(int)}.\n         The default value is 0. More details about the TrueType Collection font format can be found\n         here: https://en.wikipedia.org/wiki/TrueType#TrueType_Collection. -->\n        <attr name=\"ttcIndex\" format=\"integer\" />\n        <!-- The variation settings to be applied to the font. The string should be in the following\n         format: \"'tag1' value1, 'tag2' value2, ...\". If the default variation settings should be\n         used, or the font used does not support variation settings, this attribute needs not be\n         specified. -->\n        <attr name=\"fontVariationSettings\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- Attributes that are read when parsing a &lt;fontfamily&gt; tag.\n         {@deprecated Use Jetpack Core library instead.}\n     -->\n    <declare-styleable name=\"FontFamily\">\n        <!-- The authority of the Font Provider to be used for the request.\n             {@deprecated Use app:fontProviderAuthority with Jetpack Core library instead for\n              consistent behavior across all devices.}\n         -->\n        <attr name=\"fontProviderAuthority\" format=\"string\" />\n        <!-- The package for the Font Provider to be used for the request. This is used to verify\n        the identity of the provider.\n             {@deprecated Use app:fontProviderPackage with Jetpack Core library instead.}\n         -->\n        <attr name=\"fontProviderPackage\" format=\"string\" />\n        <!-- The query to be sent over to the provider. Refer to your font provider's documentation\n        on the format of this string.\n             {@deprecated Use app:fontProviderQuery with Jetpack Core library instead.}\n         -->\n        <attr name=\"fontProviderQuery\" format=\"string\" />\n        <!-- The sets of hashes for the certificates the provider should be signed with. This is\n        used to verify the identity of the provider, and is only required if the provider is not\n        part of the system image. This value may point to one list or a list of lists, where each\n        individual list represents one collection of signature hashes. Refer to your font provider's\n        documentation for these values.\n             {@deprecated Use app:fontProviderCerts with Jetpack Core library instead.}\n         -->\n        <attr name=\"fontProviderCerts\" format=\"reference\" />\n        <!-- Provides the system font family name to check before downloading the font. For example\n        if the fontProviderQuery asked for \"Sans Serif\", it is possible to define\n        fontProviderSystemFontFamily as \"sans-serif\" to tell the system to use \"sans-serif\" font\n        family if it exists on the system.\n         -->\n        <attr name=\"fontProviderSystemFontFamily\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- Attributes that are read when parsing a  tag. -->\n    <declare-styleable name=\"VideoView2\">\n        <attr name=\"enableControlView\" format=\"boolean\" />\n        <attr name=\"enableSubtitle\" format=\"boolean\" />\n        <attr name=\"viewType\" format=\"enum\">\n            <enum name=\"surfaceView\" value=\"0\" />\n            <enum name=\"textureView\" value=\"1\" />\n        </attr>\n    </declare-styleable>\n\n    <!-- @hide -->\n    <declare-styleable name=\"RecyclerView\">\n        <attr name=\"layoutManager\" format=\"string\" />\n        <attr name=\"orientation\" />\n        <attr name=\"descendantFocusability\" />\n        <attr name=\"spanCount\" format=\"integer\"/>\n        <attr name=\"reverseLayout\" format=\"boolean\" />\n        <attr name=\"stackFromEnd\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- @hide -->\n    <declare-styleable name=\"NotificationTheme\">\n        <attr name=\"notificationHeaderStyle\" format=\"reference\" />\n        <attr name=\"notificationHeaderTextAppearance\" format=\"reference\" />\n        <attr name=\"notificationHeaderIconSize\" format=\"dimension\" />\n        <attr name=\"notificationHeaderAppNameVisibility\" format=\"enum\">\n            <!-- Visible on screen; the default value. -->\n            <enum name=\"visible\" value=\"0\" />\n            <!-- Not displayed, but taken into account during layout (space is left for it). -->\n            <enum name=\"invisible\" value=\"1\" />\n            <!-- Completely hidden, as if the view had not been added. -->\n            <enum name=\"gone\" value=\"2\" />\n        </attr>\n    </declare-styleable>\n\n    <attr name=\"lockPatternStyle\" format=\"reference\" />\n\n    <declare-styleable name=\"Magnifier\">\n        <attr name=\"magnifierWidth\" format=\"dimension\" />\n        <attr name=\"magnifierHeight\" format=\"dimension\" />\n        <attr name=\"magnifierZoom\" format=\"float\" />\n        <attr name=\"magnifierElevation\" format=\"dimension\" />\n        <attr name=\"magnifierVerticalOffset\" format=\"dimension\" />\n        <attr name=\"magnifierHorizontalOffset\" format=\"dimension\" />\n        <attr name=\"magnifierColorOverlay\" format=\"color\" />\n    </declare-styleable>\n\n    <attr name=\"autoSizePresetSizes\" />\n\n    <attr name=\"iconfactoryIconSize\" format=\"dimension\"/>\n    <attr name=\"iconfactoryBadgeSize\" format=\"dimension\"/>\n    <!-- Perceptual luminance of a color, in accessibility friendly color space. From 0 to 100. -->\n    <attr name=\"lStar\" format=\"float\"/>\n\n    <!-- The attributes of the {@code <locale-config>} tag. -->\n    <!-- @FlaggedApi(\"android.content.res.default_locale\") -->\n    <declare-styleable name=\"LocaleConfig\">\n        <!-- The <a href=\"https://www.rfc-editor.org/rfc/bcp/bcp47.txt\">IETF BCP47 language tag</a>\n       the strings in values/strings.xml (the default strings in the directory with no locale\n       qualifier) are in. -->\n        <attr name=\"defaultLocale\" format=\"string\"/>\n    </declare-styleable>\n\n    <!-- The attributes of the {@code <locale>} tag within {@code <locale-config>}. -->\n    <declare-styleable name=\"LocaleConfig_Locale\">\n        <!-- The <a href=\"https://www.rfc-editor.org/rfc/bcp/bcp47.txt\">IETF BCP47 language tag</a>\n        of the supported locale. {@link android.app.LocaleConfig} -->\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- @hide -->\n    <declare-styleable name=\"CachingIconView\">\n        <!-- Maximum width of displayed drawable. Drawables exceeding this size will be downsampled. -->\n        <attr name=\"maxDrawableWidth\" format=\"dimension\"/>\n        <!-- Maximum width of height drawable. Drawables exceeding this size will be downsampled. -->\n        <attr name=\"maxDrawableHeight\" format=\"dimension\"/>\n    </declare-styleable>\n\n    <!-- =============================== -->\n    <!-- Credential Manager attributes -->\n    <!-- =============================== -->\n    <eat-comment />\n\n    <!-- Contains Credential Provider related metadata. Since providers are exposed\n         as services these should live under the service.\n    -->\n    <declare-styleable name=\"CredentialProvider\">\n        <!-- A string that is displayed to the user in the Credential Manager settings\n             screen that can be used to provide more information about a provider. For\n             longer strings it will be truncated. -->\n        <attr name=\"settingsSubtitle\" format=\"string\" />\n        <!-- Fully qualified class name of an activity that allows the user to modify\n             the settings for this service. -->\n        <attr name=\"settingsActivity\" />\n    </declare-styleable>\n\n    <!-- A list of capabilities that indicates to the OS what kinds of credentials\n             this provider supports. -->\n    <declare-styleable name=\"CredentialProvider_Capabilities\" parent=\"CredentialProvider\">\n        <!-- An individual capability declared by the provider. -->\n        <attr name=\"capability\" format=\"string\" />\n    </declare-styleable>\n    </resources>\n"
  },
  {
    "path": "jadx-core/src/main/resources/android/attrs_manifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n/* Copyright 2006, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\");\n** you may not use this file except in compliance with the License.\n** You may obtain a copy of the License at\n**\n**     http://www.apache.org/licenses/LICENSE-2.0\n**\n** Unless required by applicable law or agreed to in writing, software\n** distributed under the License is distributed on an \"AS IS\" BASIS,\n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n** See the License for the specific language governing permissions and\n** limitations under the License.\n*/\n-->\n<resources>\n    <!-- **************************************************************** -->\n    <!-- These are the attributes used in AndroidManifest.xml. -->\n    <!-- **************************************************************** -->\n    <eat-comment />\n\n    <!-- The overall theme to use for an activity.  Use with either the\n         application tag (to supply a default theme for all activities) or\n         the activity tag (to supply a specific theme for that activity).\n\n         <p>This automatically sets\n         your activity's Context to use this theme, and may also be used\n         for \"starting\" animations prior to the activity being launched (to\n         better match what the activity actually looks like).  It is a reference\n         to a style resource defining the theme.  If not set, the default\n         system theme will be used. -->\n    <attr name=\"theme\" format=\"reference\" />\n\n    <!-- A user-legible name for the given item.  Use with the\n         application tag (to supply a default label for all application\n         components), or with the activity, receiver, service, or instrumentation\n         tag (to supply a specific label for that component).  It may also be\n         used with the intent-filter tag to supply a label to show to the\n         user when an activity is being selected based on a particular Intent.\n\n         <p>The given label will be used wherever the user sees information\n         about its associated component; for example, as the name of a\n         main activity that is displayed in the launcher.  You should\n         generally set this to a reference to a string resource, so that\n         it can be localized, however it is also allowed to supply a plain\n         string for quick and dirty programming. -->\n    <attr name=\"label\" format=\"reference|string\" />\n\n    <!-- A Drawable resource providing a graphical representation of its\n         associated item.  Use with the\n         application tag (to supply a default icon for all application\n         components), or with the activity, receiver, service, or instrumentation\n         tag (to supply a specific icon for that component).  It may also be\n         used with the intent-filter tag to supply an icon to show to the\n         user when an activity is being selected based on a particular Intent.\n\n         <p>The given icon will be used to display to the user a graphical\n         representation of its associated component; for example, as the icon\n         for main activity that is displayed in the launcher.  This must be\n         a reference to a Drawable resource containing the image definition. -->\n    <attr name=\"icon\" format=\"reference\" />\n\n    <!-- A Drawable resource providing a graphical representation of its\n         associated item.  Use with the\n         application tag (to supply a default round icon for all application\n         components), or with the activity, receiver, service, or instrumentation\n         tag (to supply a specific round icon for that component).  It may also be\n         used with the intent-filter tag to supply a round icon to show to the\n         user when an activity is being selected based on a particular Intent.\n\n         <p>The given round icon will be used to display to the user a graphical\n         representation of its associated component; for example, as the round icon\n         for main activity that is displayed in the launcher.  This must be\n         a reference to a Drawable resource containing the image definition. -->\n    <attr name=\"roundIcon\" format=\"reference\" />\n\n    <!-- A Drawable resource providing an extended graphical banner for its\n         associated item. Use with the application tag (to supply a default\n         banner for all application activities), or with the activity, tag to\n         supply a banner for a specific activity.\n\n         <p>The given banner will be used to display to the user a graphical\n         representation of an activity in the Leanback application launcher.\n         Since banners are displayed only in the Leanback launcher, they should\n         only be used with activities (and applications) that support Leanback\n         mode. These are activities that handle Intents of category\n         {@link android.content.Intent#CATEGORY_LEANBACK_LAUNCHER\n         Intent.CATEGORY_LEANBACK_LAUNCHER}.\n         <p>This must be a reference to a Drawable resource containing the image definition. -->\n    <attr name=\"banner\" format=\"reference\" />\n\n    <!-- A Drawable resource providing an extended graphical logo for its\n         associated item. Use with the application tag (to supply a default\n         logo for all application components), or with the activity, receiver,\n         service, or instrumentation tag (to supply a specific logo for that\n         component). It may also be used with the intent-filter tag to supply\n         a logo to show to the user when an activity is being selected based\n         on a particular Intent.\n\n         <p>The given logo will be used to display to the user a graphical\n         representation of its associated component; for example as the\n         header in the Action Bar. The primary differences between an icon\n         and a logo are that logos are often wider and more detailed, and are\n         used without an accompanying text caption. This must be a reference\n         to a Drawable resource containing the image definition. -->\n    <attr name=\"logo\" format=\"reference\" />\n\n    <!-- Name of the activity to be launched to manage application's space on\n         device. The specified activity gets automatically launched when the\n         application's space needs to be managed and is usually invoked\n         through user actions. Applications can thus provide their own custom\n         behavior for managing space for various scenarios like out of memory\n         conditions. This is an optional attribute and\n         applications can choose not to specify a default activity to\n         manage space. -->\n    <attr name=\"manageSpaceActivity\" format=\"string\" />\n\n    <!-- Option to let applications specify that user data can/cannot be\n         cleared. This flag is turned on by default.\n         <p>Starting from API level 29 this flag only controls if the user can\n         clear app data from Settings. To control clearing the data after a\n         failed restore use allowClearUserDataOnFailedRestore flag.\n         <p><em>This attribute is usable only by applications\n         included in the system image. Third-party apps cannot use it.</em> -->\n    <attr name=\"allowClearUserData\" format=\"boolean\" />\n\n    <!-- Option to indicate this application is only for testing purposes.\n         For example, it may expose functionality or data outside of itself\n         that would cause a security hole, but is useful for testing.  This\n         kind of application can not be installed without the\n         INSTALL_ALLOW_TEST flag, which means only through adb install.  -->\n    <attr name=\"testOnly\" format=\"boolean\" />\n\n    <!-- A unique name for the given item.  This must use a Java-style naming\n         convention to ensure the name is unique, for example\n         \"com.mycompany.MyName\". -->\n    <attr name=\"name\" format=\"string\" />\n\n    <!-- Specify a permission that a client is required to have in order to\n    \t use the associated object.  If the client does not hold the named\n    \t permission, its request will fail.  See the\n         <a href=\"{@docRoot}guide/topics/security/security.html\">Security and Permissions</a>\n         document for more information on permissions. -->\n    <attr name=\"permission\" format=\"string\" />\n\n    <!-- A specific {@link android.R.attr#permission} name for read-only\n         access to a {@link android.content.ContentProvider}.  See the\n         <a href=\"{@docRoot}guide/topics/security/security.html\">Security and Permissions</a>\n         document for more information on permissions. -->\n    <attr name=\"readPermission\" format=\"string\" />\n\n    <!-- A specific {@link android.R.attr#permission} name for write\n         access to a {@link android.content.ContentProvider}.  See the\n         <a href=\"{@docRoot}guide/topics/security/security.html\">Security and Permissions</a>\n         document for more information on permissions. -->\n    <attr name=\"writePermission\" format=\"string\" />\n\n    <!-- If true, the {@link android.content.Context#grantUriPermission\n         Context.grantUriPermission} or corresponding Intent flags can\n         be used to allow others to access specific URIs in the content\n         provider, even if they do not have an explicit read or write\n         permission.  If you are supporting this feature, you must be\n         sure to call {@link android.content.Context#revokeUriPermission\n         Context.revokeUriPermission} when URIs are deleted from your\n         provider.-->\n    <attr name=\"grantUriPermissions\" format=\"boolean\" />\n\n    <!-- If true, the system will always create URI permission grants\n         in the cases where {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}\n         or {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} would apply.\n         This is useful for a content provider that dynamically enforces permissions\n         on calls in to the provider, instead of through the manifest: the system\n         needs to know that it should always apply permission grants, even if it\n         looks like the target of the grant would already have access to the URI. -->\n    <attr name=\"forceUriPermissions\" format=\"boolean\" />\n\n    <!-- Characterizes the potential risk implied in a permission and\n         indicates the procedure the system should follow when determining\n         whether to grant the permission to an application requesting it. {@link\n         android.Manifest.permission Standard permissions} have a predefined and\n         permanent protectionLevel. If you are creating a custom permission in an\n         application, you can define a protectionLevel attribute with one of the\n         values listed below. If no protectionLevel is defined for a custom\n         permission, the system assigns the default (\"normal\").\n         <p>Each protection level consists of a base permission type and zero or\n         more flags. Use the following functions to extract those.\n         <pre>\n         int basePermissionType = permissionInfo.getProtection();\n         int permissionFlags = permissionInfo.getProtectionFlags();\n         </pre>\n         -->\n    <attr name=\"protectionLevel\">\n        <!-- <strong>Base permission type</strong>: a lower-risk permission that gives\n             an application access to isolated application-level features, with minimal\n             risk to other applications, the system, or the user. The system\n             automatically grants this type of permission to a requesting application at\n             installation, without asking for the user's explicit approval (though the\n             user always has the option to review these permissions before installing). -->\n        <flag name=\"normal\" value=\"0\" />\n        <!-- <strong>Base permission type</strong>: a higher-risk permission that\n             would give a requesting application access to private user data or\n             control over the device that can negatively impact the user.  Because\n             this type of permission introduces potential risk, the system may\n             not automatically grant it to the requesting application.  For example,\n             any dangerous permissions requested by an application may be displayed\n             to the user and require confirmation before proceeding, or some other\n             approach may be taken to avoid the user automatically allowing\n             the use of such facilities.  -->\n        <flag name=\"dangerous\" value=\"1\" />\n        <!-- <strong>Base permission type</strong>: a permission that the system is\n             to grant only if the requesting application is signed with the same\n             certificate as the application that declared the permission. If the\n             certificates match, the system automatically grants the permission\n             without notifying the user or asking for the user's explicit approval. -->\n        <flag name=\"signature\" value=\"2\" />\n        <!-- Old synonym for \"signature|privileged\". Deprecated in API level 23.\n             Base permission type: a permission that the system is to grant only\n             to packages in the Android system image <em>or</em> that are signed\n             with the same certificates. Please avoid using this option, as the\n             signature protection level should be sufficient for most needs and\n             works regardless of exactly where applications are installed.  This\n             permission is used for certain special situations where multiple\n             vendors have applications built in to a system image which need\n             to share specific features explicitly because they are being built\n             together. -->\n        <flag name=\"signatureOrSystem\" value=\"3\" />\n        <!-- <strong>Base permission type</strong>: a permission that is managed internally by the\n             system and only granted according to the protection flags. -->\n        <flag name=\"internal\" value=\"4\" />\n        <!-- Additional flag from base permission type: this permission can also\n             be granted to any applications installed as privileged apps on the system image.\n             Please avoid using this option, as the\n             signature protection level should be sufficient for most needs and\n             works regardless of exactly where applications are installed.  This\n             permission flag is used for certain special situations where multiple\n             vendors have applications built in to a system image which need\n             to share specific features explicitly because they are being built\n             together. -->\n        <flag name=\"privileged\" value=\"0x10\" />\n        <!-- Old synonym for \"privileged\". Deprecated in API level 23. -->\n        <flag name=\"system\" value=\"0x10\" />\n        <!-- Additional flag from base permission type: this permission can also\n             (optionally) be granted to development applications. Although undocumented, the\n              permission state used to be shared by all users (including future users), but it is\n              managed per-user since API level 31. -->\n        <flag name=\"development\" value=\"0x20\" />\n        <!-- Additional flag from base permission type: this permission is closely\n             associated with an app op for controlling access. -->\n        <flag name=\"appop\" value=\"0x40\" />\n        <!-- Additional flag from base permission type: this permission can be automatically\n             granted to apps that target API levels below\n             {@link android.os.Build.VERSION_CODES#M} (before runtime permissions\n             were introduced). -->\n        <flag name=\"pre23\" value=\"0x80\" />\n        <!-- Additional flag from base permission type: this permission can be automatically\n            granted to system apps that install packages. -->\n        <flag name=\"installer\" value=\"0x100\" />\n        <!-- Additional flag from base permission type: this permission can be automatically\n            granted to system apps that verify packages. -->\n        <flag name=\"verifier\" value=\"0x200\" />\n        <!-- Additional flag from base permission type: this permission can be automatically\n            granted any application pre-installed on the system image (not just privileged\n            apps). -->\n        <flag name=\"preinstalled\" value=\"0x400\" />\n        <!-- Additional flag from base permission type: this permission can be automatically\n            granted to the setup wizard app -->\n        <flag name=\"setup\" value=\"0x800\" />\n        <!-- Additional flag from base permission type: this permission can be granted to instant\n             apps -->\n        <flag name=\"instant\" value=\"0x1000\" />\n        <!-- Additional flag from base permission type: this permission can only be granted to apps\n             that target runtime permissions ({@link android.os.Build.VERSION_CODES#M} and above)\n             -->\n        <flag name=\"runtime\" value=\"0x2000\" />\n        <!-- Additional flag from base permission type: this permission can be granted only\n             if its protection level is signature, the requesting app resides on the OEM partition,\n             and the OEM has allowlisted the app to receive this permission by the OEM.\n         -->\n        <flag name=\"oem\" value=\"0x4000\" />\n        <!-- Additional flag from base permission type: this permission can be granted to\n             privileged apps in vendor partition. -->\n        <flag name=\"vendorPrivileged\" value=\"0x8000\" />\n        <!-- Additional flag from base permission type: this permission can be automatically\n            granted to the system default text classifier -->\n        <flag name=\"textClassifier\" value=\"0x10000\" />\n        <!-- Additional flag from base permission type: this permission automatically\n            granted to device configurator -->\n        <flag name=\"configurator\" value=\"0x80000\" />\n        <!-- Additional flag from base permission type: this permission designates the app\n            that will approve the sharing of incident reports. -->\n        <flag name=\"incidentReportApprover\" value=\"0x100000\" />\n        <!-- Additional flag from base permission type: this permission can be automatically\n            granted to the system app predictor -->\n        <flag name=\"appPredictor\" value=\"0x200000\" />\n        <!-- Additional flag from base permission type: this permission can also be granted if the\n             requesting application is included in the mainline module}. -->\n        <flag name=\"module\" value=\"0x400000\" />\n        <!-- Additional flag from base permission type: this permission can be automatically\n            granted to the system companion device manager service -->\n        <flag name=\"companion\" value=\"0x800000\" />\n        <!-- Additional flag from base permission type: this permission will be granted to the\n             retail demo app, as defined by the OEM.\n             This flag has been replaced by the retail demo role and is a no-op since Android V.\n          -->\n        <flag name=\"retailDemo\" value=\"0x1000000\" />\n        <!-- Additional flag from base permission type: this permission will be granted to the\n             recents app. -->\n        <flag name=\"recents\" value=\"0x2000000\" />\n        <!-- Additional flag from base permission type: this permission is managed by role. -->\n        <flag name=\"role\" value=\"0x4000000\" />\n        <!-- Additional flag from base permission type: this permission can also be granted if the\n             requesting application is signed by, or has in its signing lineage, any of the\n             certificate digests declared in {@link android.R.attr#knownCerts}. -->\n        <flag name=\"knownSigner\" value=\"0x8000000\" />\n    </attr>\n\n    <!-- Flags indicating more context for a permission group. -->\n    <attr name=\"permissionGroupFlags\">\n        <!-- Set to indicate that this permission group contains permissions\n             protecting access to some information that is considered\n             personal to the user (such as contacts, e-mails, etc). -->\n        <flag name=\"personalInfo\" value=\"0x0001\" />\n    </attr>\n\n    <!-- Flags indicating more context for a permission. -->\n    <attr name=\"permissionFlags\">\n        <!-- Set to indicate that this permission allows an operation that\n             may cost the user money.  Such permissions may be highlighted\n             when shown to the user with this additional information.  -->\n        <flag name=\"costsMoney\" value=\"0x1\" />\n        <!-- Additional flag from base permission type: this permission has been\n             removed and it is no longer enforced. It shouldn't be shown in the\n             UI. Removed permissions are kept as normal permissions for backwards\n             compatibility as apps may be checking them before calling an API.\n        -->\n        <flag name=\"removed\" value=\"0x2\" />\n        <!-- This permission is restricted by the platform and it would be\n             grantable only to apps that meet special criteria per platform\n             policy.\n        -->\n        <flag name=\"hardRestricted\" value=\"0x4\" />\n        <!-- This permission is restricted by the platform and it would be\n             grantable in its full form to apps that meet special criteria\n             per platform policy. Otherwise, a weaker form of the permission\n             would be granted. The weak grant depends on the permission.\n             <p>What weak grant means is described in the documentation of\n             the permissions.\n        -->\n        <flag name=\"softRestricted\" value=\"0x8\" />\n        <!-- This permission is restricted immutably which means that its\n             restriction state may be specified only on the first install of\n             the app and will stay in this initial allowlist state until\n             the app is uninstalled.\n        -->\n        <flag name=\"immutablyRestricted\" value=\"0x10\" />\n        <!--\n             Modifier for permission restriction. This permission cannot\n             be exempted by the installer.\n        -->\n        <flag name=\"installerExemptIgnored\" value=\"0x20\" />\n    </attr>\n\n    <!-- Specified the name of a group that this permission is associated\n         with.  The group must have been defined with the\n         {@link android.R.styleable#AndroidManifestPermissionGroup permission-group} tag. -->\n    <attr name=\"permissionGroup\" format=\"string\" />\n\n    <!-- A reference to an array resource containing the signing certificate digests to be granted\n         this permission when using the {@code knownSigner} protection flag. The digest should\n         be computed over the DER encoding of the trusted certificate using the SHA-256 digest\n         algorithm.\n         <p>\n         If only a single signer is declared this can also be a string resource, or the digest\n         can be declared inline as the value for this attribute. -->\n    <attr name=\"knownCerts\" format=\"reference|string\" />\n\n    <!-- Specify the name of a user ID that will be shared between multiple\n         packages.  By default, each package gets its own unique user-id.\n         By setting this value on two or more packages, each of these packages\n         will be given a single shared user ID, so they can for example run\n         in the same process.  Note that for them to actually get the same\n         user ID, they must also be signed with the same signature.\n         @deprecated Shared user IDs cause non-deterministic behavior within the\n         package manager. As such, its use is strongly discouraged and may be\n         removed in a future version of Android. Instead, apps should use proper\n         communication mechanisms, such as services and content providers,\n         to facilitate interoperability between shared components. Note that\n         existing apps cannot remove this value, as migrating off a\n         shared user ID is not supported. -->\n    <attr name=\"sharedUserId\" format=\"string\" />\n\n    <!-- Specify a label for the shared user UID of this package.  This is\n         only used if you have also used android:sharedUserId.  This must\n         be a reference to a string resource; it can not be an explicit\n         string.\n         @deprecated There is no replacement for this attribute.\n         {@link android.R.attr#sharedUserId} has been deprecated making\n         this attribute unnecessary. -->\n    <attr name=\"sharedUserLabel\" format=\"reference\" />\n\n    <!-- The maximum device SDK version for which the application will remain in the user ID\n         defined in sharedUserId. Used when the application wants to migrate out of using shared\n         user ID, but has to maintain backwards compatibility with the API level specified\n         and before. -->\n    <attr name=\"sharedUserMaxSdkVersion\" format=\"integer\" />\n\n    <!-- Internal version code.  This is the number used to determine whether\n         one version is more recent than another: it has no other meaning than\n         that higher numbers are more recent.  You could use this number to\n         encode a \"x.y\" in the lower and upper 16 bits, make it a build\n         number, simply increase it by one each time a new version is\n         released, or define it however else you want, as long as each\n         successive version has a higher number.  This is not a version\n         number generally shown to the user, that is usually supplied\n         with {@link android.R.attr#versionName}.  When an app is delivered\n         as multiple split APKs, each APK must have the exact same versionCode. -->\n    <attr name=\"versionCode\" format=\"integer\" />\n\n    <!-- Internal major version code.  This is essentially additional high bits\n         for the base version code; it has no other meaning than\n         that higher numbers are more recent.  This is not a version\n         number generally shown to the user, that is usually supplied\n         with {@link android.R.attr#versionName}. -->\n    <attr name=\"versionCodeMajor\" format=\"integer\" />\n\n    <!-- Internal revision code.  This number is the number used to determine\n         whether one APK is more recent than another: it has no other meaning\n         than that higher numbers are more recent.  This value is only meaningful\n         when the two {@link android.R.attr#versionCode} values are already\n         identical.  When an app is delivered as multiple split APKs, each\n         APK may have a different revisionCode value. -->\n    <attr name=\"revisionCode\" format=\"integer\" />\n\n    <!-- The text shown to the user to indicate the version they have.  This\n         is used for no other purpose than display to the user; the actual\n         significant version number is given by {@link android.R.attr#versionCode}. -->\n    <attr name=\"versionName\" format=\"string\" />\n\n    <!-- Flag to control special persistent mode of an application.  This should\n         not normally be used by applications; it requires that the system keep\n         your application running at all times. -->\n    <attr name=\"persistent\" format=\"boolean\" />\n\n    <!-- If set, the \"persistent\" attribute will only be honored if the feature\n         specified here is present on the device. -->\n    <attr name=\"persistentWhenFeatureAvailable\" format=\"string\" />\n\n    <!-- Flag to specify if this application needs to be present for all users. Only pre-installed\n         applications can request this feature. Default value is false. -->\n    <attr name=\"requiredForAllUsers\" format=\"boolean\" />\n\n    <!-- Flag indicating whether the application can be debugged, even when\n         running on a device that is running in user mode. -->\n    <attr name=\"debuggable\" format=\"boolean\" />\n\n    <!-- Flag indicating whether the application requests the VM to operate in\n         the safe mode.  -->\n    <attr name=\"vmSafeMode\" format=\"boolean\" />\n\n    <!-- <p>Flag indicating whether the application's rendering should be hardware\n         accelerated if possible. This flag is turned on by default for applications\n         that are targeting {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}\n         or later.</p>\n         <p>This flag can be set on the application and any activity declared\n         in the manifest. When enabled for the application, each activity is\n         automatically assumed to be hardware accelerated. This flag can be\n         overridden in the activity tags, either turning it off (if on for the\n         application) or on (if off for the application.)</p>\n         <p>When this flag is turned on for an activity (either directly or via\n         the application tag), every window created from the activity, including\n         the activity's own window, will be hardware accelerated, if possible.</p>\n         <p>Please refer to the documentation of\n         {@link android.view.WindowManager.LayoutParams#FLAG_HARDWARE_ACCELERATED}\n         for more information on how to control this flag programmatically.</p> -->\n    <attr name=\"hardwareAccelerated\" format=\"boolean\" />\n\n    <!-- Flag indicating whether the given application component is available\n         to other applications.  If false, it can only be accessed by\n         applications with its same user id (which usually means only by\n         code in its own package).  If true, it can be invoked by external\n         entities, though which ones can do so may be controlled through\n         permissions.  The default value is false for activity, receiver,\n         and service components that do not specify any intent filters; it\n         is true for activity, receiver, and service components that do\n         have intent filters (implying they expect to be invoked by others\n         who do not know their particular component name) and for all\n         content providers. -->\n    <attr name=\"exported\" format=\"boolean\" />\n\n    <!-- A boolean flag used to indicate if an application is a Game or not.\n         <p>This information can be used by the system to group together\n         applications that are classified as games, and display them separately\n         from the other applications. -->\n    <attr name=\"isGame\" format=\"boolean\" />\n\n    <!-- If set to true, a single instance of this component will run for\n         all users.  That instance will run as user 0, the default/primary\n         user.  When the app running is in processes for other users and interacts\n         with this component (by binding to a service for example) those processes will\n         always interact with the instance running for user 0.  Enabling\n         single user mode forces \"exported\" of the component to be false, to\n         help avoid introducing multi-user security bugs.  This feature is only\n         available to applications built in to the system image; you must hold the\n         permission INTERACT_ACROSS_USERS in order\n         to use this feature.  This flag can only be used with services,\n         receivers, and providers; it can not be used with activities. -->\n    <attr name=\"singleUser\" format=\"boolean\" />\n\n    <!-- If set to true, only a single instance of this component will\n    run and be available for the SYSTEM user. Non SYSTEM users will not be\n    allowed to access the component if this flag is enabled.\n    This flag can be used with services, receivers, providers and activities. -->\n    <attr name=\"systemUserOnly\" format=\"boolean\" />\n\n    <!-- Specify a specific process that the associated code is to run in.\n         Use with the application tag (to supply a default process for all\n         application components), or with the activity, receiver, service,\n         or provider tag (to supply a specific icon for that component).\n\n         <p>Application components are normally run in a single process that\n         is created for the entire application.  You can use this tag to modify\n         where they run.  If the process name begins with a ':' character,\n         a new process private to that application will be created when needed\n         to run that component (allowing you to spread your application across\n         multiple processes).  If the process name begins with a lower-case\n         character, the component will be run in a global process of that name,\n         provided that you have permission to do so, allowing multiple\n         applications to share one process to reduce resource usage. -->\n    <attr name=\"process\" format=\"string\" />\n\n    <!-- Specify a task name that activities have an \"affinity\" to.\n         Use with the application tag (to supply a default affinity for all\n         activities in the application), or with the activity tag (to supply\n         a specific affinity for that component).\n\n         <p>The default value for this attribute is the same as the package\n         name, indicating that all activities in the manifest should generally\n         be considered a single \"application\" to the user.  You can use this\n         attribute to modify that behavior: either giving them an affinity\n         for another task, if the activities are intended to be part of that\n         task from the user's perspective, or using an empty string for\n         activities that have no affinity to a task. -->\n    <attr name=\"taskAffinity\" format=\"string\" />\n\n    <!-- Specify that an activity can be moved out of a task it is in to\n         the task it has an affinity for when appropriate.  Use with the\n         application tag (to supply a default for all activities in the\n         application), or with an activity tag (to supply a specific\n         setting for that component).\n\n         <p>Normally when an application is started, it is associated with\n         the task of the activity that started it and stays there for its\n         entire lifetime.  You can use the allowTaskReparenting feature to force an\n         activity to be re-parented to a different task when the task it is\n         in goes to the background.  Typically this is used to cause the\n         activities of an application to move back to the main task associated\n         with that application.  The activity is re-parented to the task\n         with the same {@link android.R.attr#taskAffinity} as it has. -->\n    <attr name=\"allowTaskReparenting\" format=\"boolean\" />\n\n    <!-- Declare that this application may use cleartext traffic, such as HTTP rather than HTTPS;\n         WebSockets rather than WebSockets Secure; XMPP, IMAP, SMTP without STARTTLS or TLS.\n         Defaults to true. If set to false {@code false}, the application declares that it does not\n         intend to use cleartext network traffic, in which case platform components (e.g. HTTP\n         stacks, {@code DownloadManager}, {@code MediaPlayer}) will refuse applications's requests\n         to use cleartext traffic. Third-party libraries are encouraged to honor this flag as well.\n         -->\n    <attr name=\"usesCleartextTraffic\" format=\"boolean\" />\n\n    <!-- Declare that code from this application will need to be loaded into other\n         applications' processes. On devices that support multiple instruction sets,\n         this implies the code might be loaded into a process that's using any of the devices\n         supported instruction sets.\n\n         <p> The system might treat such applications specially, for eg., by\n         extracting the application's native libraries for all supported instruction\n         sets or by compiling the application's dex code for all supported instruction\n         sets. -->\n    <attr name=\"multiArch\" format =\"boolean\" />\n\n    <!-- Specify whether the 32 bit version of the ABI should be used in a\n         multiArch application. If both abioverride flag (i.e. using abi option of abd install)\n         and use32bitAbi are used, then use32bit is ignored.-->\n    <attr name=\"use32bitAbi\" />\n\n    <!-- Specify whether a component is allowed to have multiple instances\n         of itself running in different processes.  Use with the activity\n         and provider tags.\n\n         <p>Normally the system will ensure that all instances of a particular\n         component are only running in a single process.  You can use this\n         attribute to disable that behavior, allowing the system to create\n         instances wherever they are used (provided permissions allow it).\n         This is most often used with content providers, so that instances\n         of a provider can be created in each client process, allowing them\n         to be used without performing IPC.  -->\n    <attr name=\"multiprocess\" format=\"boolean\" />\n\n    <!-- Specify whether an activity should be finished when its task is\n         brought to the foreground by relaunching from the home screen.\n\n         <p>If both this option and {@link android.R.attr#allowTaskReparenting} are\n         specified, the finish trumps the affinity: the affinity will be\n         ignored and the activity simply finished. -->\n    <attr name=\"finishOnTaskLaunch\" format=\"boolean\" />\n\n    <!-- Specify whether an activity should be finished when a \"close system\n         windows\" request has been made.  This happens, for example, when\n         the home key is pressed, when the device is locked, when a system\n         dialog showing recent applications is displayed, etc. -->\n    <attr name=\"finishOnCloseSystemDialogs\" format=\"boolean\" />\n\n    <!-- Specify whether an activity's task should be cleared when it\n         is re-launched from the home screen.  As a result, every time the\n         user starts the task, they will be brought to its root activity,\n         regardless of whether they used BACK or HOME to last leave it.\n         This flag only applies to activities that\n         are used to start the root of a new task.\n\n         <p>An example of the use of this flag would be for the case where\n         a user launches activity A from home, and from there goes to\n         activity B.  They now press home, and then return to activity A.\n         Normally they would see activity B, since that is what they were\n         last doing in A's task.  However, if A has set this flag to true,\n         then upon going to the background all of the tasks on top of it (B\n         in this case) are removed, so when the user next returns to A they\n         will restart at its original activity.\n\n         <p>When this option is used in conjunction with\n         {@link android.R.attr#allowTaskReparenting}, the allowTaskReparenting trumps the\n         clear.  That is, all activities above the root activity of the\n         task will be removed: those that have an affinity will be moved\n         to the task they are associated with, otherwise they will simply\n         be dropped as described here. -->\n    <attr name=\"clearTaskOnLaunch\" format=\"boolean\" />\n\n    <!-- Specify whether an activity should be kept in its history stack.\n         If this attribute is set, then as soon as the user navigates away\n         from the activity it will be finished and they will no longer be\n         able to return to it. -->\n    <attr name=\"noHistory\" format=\"boolean\" />\n\n    <!-- Specify whether an activity's task state should always be maintained\n         by the system, or if it is allowed to reset the task to its initial\n         state in certain situations.\n\n         <p>Normally the system will reset a task (remove all activities from\n         the stack and reset the root activity) in certain situations when\n         the user re-selects that task from the home screen.  Typically this\n         will be done if the user hasn't visited that task for a certain\n         amount of time, such as 30 minutes.\n\n         <p>By setting this attribute, the user will always return to your\n         task in its last state, regardless of how they get there.  This is\n         useful, for example, in an application like the web browser where there\n         is a lot of state (such as multiple open tabs) that the application\n         would not like to lose. -->\n    <attr name=\"alwaysRetainTaskState\" format=\"boolean\" />\n\n    <!-- Indicates that an Activity does not need to have its freeze state\n         (as returned by {@link android.app.Activity#onSaveInstanceState}\n         retained in order to be restarted.  Generally you use this for activities\n         that do not store any state.  When this flag is set, if for some reason\n         the activity is killed before it has a chance to save its state,\n         then the system will not remove it from the activity stack like\n         it normally would.  Instead, the next time the user navigates to\n         it its {@link android.app.Activity#onCreate} method will be called\n         with a null icicle, just like it was starting for the first time.\n\n         <p>This is used by the Home activity to make sure it does not get\n         removed if it crashes for some reason. -->\n    <attr name=\"stateNotNeeded\" format=\"boolean\" />\n\n    <!-- Indicates that an Activity should be excluded from the list of\n         recently launched activities. -->\n    <attr name=\"excludeFromRecents\" format=\"boolean\" />\n\n    <!-- Specify that an Activity should be shown over the lock screen and,\n         in a multiuser environment, across all users' windows.\n         @deprecated use {@link android.R.attr#showForAllUsers} instead. -->\n    <attr name=\"showOnLockScreen\" format=\"boolean\" />\n\n    <!-- Specify that an Activity should be shown even if the current/foreground user\n         is different from the user of the Activity. This will also force the\n         <code>android.view.LayoutParams.FLAG_SHOW_WHEN_LOCKED</code> flag\n         to be set for all windows of this activity -->\n    <attr name=\"showForAllUsers\" format=\"boolean\" />\n\n    <!-- Specifies whether an {@link android.app.Activity} should be shown on top of the lock screen\n         whenever the lockscreen is up and the activity is resumed. Normally an activity will be\n         transitioned to the stopped state if it is started while the lockscreen is up, but with\n         this flag set the activity will remain in the resumed state visible on-top of the lock\n         screen.\n\n         <p>This should be used instead of {@link android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED}\n         flag set for Windows. When using the Window flag during activity startup, there may not be\n         time to add it before the system stops your activity for being behind the lock-screen.\n         This leads to a double life-cycle as it is then restarted.</p> -->\n    <attr name=\"showWhenLocked\" format=\"boolean\" />\n\n    <!-- Specifies whether the screen should be turned on when the {@link android.app.Activity} is resumed.\n         Normally an activity will be transitioned to the stopped state if it is started while the\n         screen if off, but with this flag set the activity will cause the screen to turn on if the\n         activity will be visible and resumed due to the screen coming on. The screen will not be\n         turned on if the activity won't be visible after the screen is turned on. This flag is\n         normally used in conjunction with the {@link android.R.attr#showWhenLocked} flag to make\n         sure the activity is visible after the screen is turned on when the lockscreen is up. In\n         addition, if this flag is set and the activity calls\n         {@link android.app.KeyguardManager#requestDismissKeyguard}\n         the screen will turn on.\n\n         <p>This should be used instead of {@link android.view.WindowManager.LayoutParams#FLAG_TURN_SCREEN_ON}\n         flag set for Windows. When using the Window flag during activity startup, there may not be\n         time to add it before the system stops your activity because the screen has not yet turned\n         on. This leads to a double life-cycle as it is then restarted.</p> -->\n    <attr name=\"turnScreenOn\" format=\"boolean\" />\n\n    <!-- Specify the authorities under which this content provider can be\n         found.  Multiple authorities may be supplied by separating them\n         with a semicolon.  Authority names should use a Java-style naming\n         convention (such as <code>com.google.provider.MyProvider</code>)\n         in order to avoid conflicts.  Typically this name is the same\n         as the class implementation describing the provider's data structure. -->\n    <attr name=\"authorities\" format=\"string\" />\n\n    <!-- Flag indicating whether this content provider would like to\n         participate in data synchronization. -->\n    <attr name=\"syncable\" format=\"boolean\" />\n\n    <!-- Flag declaring this activity to be 'immersive'; immersive activities\n         should not be interrupted with other activities or notifications. -->\n    <attr name=\"immersive\" format=\"boolean\" />\n\n    <!-- Flag declaring that this activity will be run in VR mode, and specifying\n         the component of the {@link android.service.vr.VrListenerService} that should be\n         bound while this Activity is visible if it is installed and enabled on this device.\n         This is equivalent to calling {@link android.app.Activity#setVrModeEnabled} with the\n         the given component name within the Activity that this attribute is set for.\n         Declaring this will prevent the system from leaving VR mode during an Activity\n         transition from one VR activity to another. -->\n    <attr name=\"enableVrMode\" format=\"string\" />\n\n    <!-- Flag that specifies the activity's preferred screen rotation animation.\n         Valid values are \"rotate\", \"crossfade\", \"jumpcut\", and \"seamless\" as\n         described in\n         {@link android.view.WindowManager.LayoutParams#rotationAnimation}.\n         Specifying your rotation animation in\n         <code>WindowManager.LayoutParams</code> may be racy with app startup\n         and update transitions that occur during application startup; and so,\n         specify the animation in the manifest attribute.\n    -->\n    <attr name=\"rotationAnimation\">\n      <flag name=\"rotate\" value= \"0\" />\n      <flag name=\"crossfade\" value = \"1\" />\n      <flag name=\"jumpcut\" value = \"2\" />\n      <flag name=\"seamless\" value = \"3\" />\n    </attr>\n\n    <!-- Specify the order in which content providers hosted by a process\n         are instantiated when that process is created.  Not needed unless\n         you have providers with dependencies between each other, to make\n         sure that they are created in the order needed by those dependencies.\n         The value is a simple integer, with higher numbers being\n         initialized first. -->\n    <attr name=\"initOrder\" format=\"integer\" />\n\n    <!-- Specify the relative importance or ability in handling a particular\n         Intent.  For receivers, this controls the order in which they are\n         executed to receive a broadcast (note that for\n         asynchronous broadcasts, this order is ignored).  For activities,\n         this provides information about how good an activity is handling an\n         Intent; when multiple activities match an intent and have different\n         priorities, only those with the higher priority value will be\n         considered a match.\n\n         <p>Only use if you really need to impose some specific\n         order in which the broadcasts are received, or want to forcibly\n         place an activity to always be preferred over others.  The value is a\n         single integer, with higher numbers considered to be better. -->\n    <attr name=\"priority\" format=\"integer\" />\n\n    <!-- Indicate if this component is aware of direct boot lifecycle, and can be\n         safely run before the user has entered their credentials (such as a lock\n         pattern or PIN). -->\n    <attr name=\"directBootAware\" format=\"boolean\" />\n\n    <!-- Specify how an activity should be launched.  See the\n         <a href=\"{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html\">Tasks and Back\n         Stack</a> document for important information on how these options impact\n         the behavior of your application.\n\n         <p>If this attribute is not specified, <code>standard</code> launch\n         mode will be used.  Note that the particular launch behavior can\n         be changed in some ways at runtime through the\n         {@link android.content.Intent} flags\n         {@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP},\n         {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}, and\n         {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}. -->\n    <attr name=\"launchMode\">\n        <!-- The default mode, which will usually create a new instance of\n             the activity when it is started, though this behavior may change\n             with the introduction of other options such as\n             {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK\n             Intent.FLAG_ACTIVITY_NEW_TASK}. -->\n        <enum name=\"standard\" value=\"0\" />\n        <!-- If, when starting the activity, there is already an\n            instance of the same activity class in the foreground that is\n            interacting with the user, then\n            re-use that instance.  This existing instance will receive a call to\n            {@link android.app.Activity#onNewIntent Activity.onNewIntent()} with\n            the new Intent that is being started. -->\n        <enum name=\"singleTop\" value=\"1\" />\n        <!-- If, when starting the activity, there is already a task running\n            that starts with this activity, then instead of starting a new\n            instance the current task is brought to the front.  The existing\n            instance will receive a call to {@link android.app.Activity#onNewIntent\n            Activity.onNewIntent()}\n            with the new Intent that is being started, and with the\n            {@link android.content.Intent#FLAG_ACTIVITY_BROUGHT_TO_FRONT\n            Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT} flag set.  This is a superset\n            of the singleTop mode, where if there is already an instance\n            of the activity being started at the top of the stack, it will\n            receive the Intent as described there (without the\n            FLAG_ACTIVITY_BROUGHT_TO_FRONT flag set).  See the\n            <a href=\"{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html\">Tasks and Back\n            Stack</a> document for more details about tasks.-->\n        <enum name=\"singleTask\" value=\"2\" />\n        <!-- Only allow one instance of this activity to ever be\n            running.  This activity gets a unique task with only itself running\n            in it; if it is ever launched again with the same Intent, then that\n            task will be brought forward and its\n            {@link android.app.Activity#onNewIntent Activity.onNewIntent()}\n            method called.  If this\n            activity tries to start a new activity, that new activity will be\n            launched in a separate task.  See the\n            <a href=\"{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html\">Tasks and Back\n            Stack</a> document for more details about tasks.-->\n        <enum name=\"singleInstance\" value=\"3\" />\n        <!-- The activity can only be running as the root activity of the task, the first activity\n            that created the task, and therefore there will only be one instance of this activity\n            in a task. In contrast to the {@code singleTask} launch mode, this activity can be\n            started in multiple instances in different tasks if the\n            {@code FLAG_ACTIVITY_MULTIPLE_TASK} or {@code FLAG_ACTIVITY_NEW_DOCUMENT} is set.\n            This enum value is introduced in API level 31. -->\n        <enum name=\"singleInstancePerTask\" value=\"4\" />\n    </attr>\n    <!-- Specify the orientation an activity should run in. If the orientation is not specified, the\n         system determines the best orientation for the activity.\n         <p>This attribute is supported by the <a\n            href=\"{@docRoot}guide/topics/manifest/activity-element.html\">{@code <activity>}</a>\n            element.\n         <aside class=\"note\"><b>Note:</b>\n            <ul>\n                <li>To improve the layout of apps on form factors with smallest width >= 600dp, the\n                    system ignores the following values of this attribute for apps that target\n                    Android 16 (API level 36) or higher:\n                    <ul>\n                      <li><code>portrait</code></li>\n                      <li><code>landscape</code></li>\n                      <li><code>reversePortrait</code></li>\n                      <li><code>reverseLandscape</code></li>\n                      <li><code>sensorPortrait</code></li>\n                      <li><code>sensorLandscape</code></li>\n                      <li><code>userPortrait</code></li>\n                      <li><code>userLandscape</code></li>\n                    </ul>\n                    <p>The values are treated as if the app had set orientation as\n                       <code>unspecified</code>.</p>\n                </li>\n                <li>Device manufacturers can configure devices to override (ignore) this attribute\n                    to improve the layout of apps.</li>\n                <li>On devices with Android 16 (API level 36) or higher installed, virtual device\n                    owners (select trusted and privileged apps) can configure devices they manage to\n                    override (ignore) this attribute to improve app layout. See also\n                    <a href=\"https://source.android.com/docs/core/permissions/app-streaming\">\n                    Companion app streaming</a>.</li>\n             </ul>\n             <p>See <a href=\"{@docRoot}guide/practices/device-compatibility-mode\">Device\n             compatibility mode</a>.</p>\n         </aside> -->\n    <attr name=\"screenOrientation\">\n        <!-- No preference specified: let the system decide the best\n             orientation.  This will either be the orientation selected\n             by the activity below, or the user's preferred orientation\n             if this activity is the bottom of a task. If the user\n             explicitly turned off sensor based orientation through settings\n             sensor based device rotation will be ignored. If not by default\n             sensor based orientation will be taken into account and the\n             orientation will changed based on how the user rotates the device.\n             Corresponds to\n             {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED}. -->\n        <enum name=\"unspecified\" value=\"-1\" />\n        <!-- Would like to have the screen in a landscape orientation: that\n             is, with the display wider than it is tall, ignoring sensor data.\n             Corresponds to\n             {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}. -->\n        <enum name=\"landscape\" value=\"0\" />\n        <!-- Would like to have the screen in a portrait orientation: that\n             is, with the display taller than it is wide, ignoring sensor data.\n             Corresponds to\n             {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_PORTRAIT}. -->\n        <enum name=\"portrait\" value=\"1\" />\n        <!-- Use the user's current preferred orientation of the handset.\n             Corresponds to\n             {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_USER}. -->\n        <enum name=\"user\" value=\"2\" />\n        <!-- Keep the screen in the same orientation as whatever is behind\n             this activity.\n             Corresponds to\n             {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_BEHIND}. -->\n        <enum name=\"behind\" value=\"3\" />\n        <!-- Orientation is determined by a physical orientation sensor:\n             the display will rotate based on how the user moves the device.\n             Ignores user's setting to turn off sensor-based rotation.\n             Corresponds to\n             {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_SENSOR}. -->\n        <enum name=\"sensor\" value=\"4\" />\n        <!-- Always ignore orientation determined by orientation sensor:\n             the display will not rotate when the user moves the device.\n             Corresponds to\n             {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_NOSENSOR}. -->\n        <enum name=\"nosensor\" value=\"5\" />\n        <!-- Would like to have the screen in landscape orientation, but can\n             use the sensor to change which direction the screen is facing.\n             Corresponds to\n             {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_SENSOR_LANDSCAPE}. -->\n        <enum name=\"sensorLandscape\" value=\"6\" />\n        <!-- Would like to have the screen in portrait orientation, but can\n             use the sensor to change which direction the screen is facing.\n             Corresponds to\n             {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_SENSOR_PORTRAIT}. -->\n        <enum name=\"sensorPortrait\" value=\"7\" />\n        <!-- Would like to have the screen in landscape orientation, turned in\n             the opposite direction from normal landscape.\n             Corresponds to\n             {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_REVERSE_LANDSCAPE}. -->\n        <enum name=\"reverseLandscape\" value=\"8\" />\n        <!-- Would like to have the screen in portrait orientation, turned in\n             the opposite direction from normal portrait.\n             Corresponds to\n             {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_REVERSE_PORTRAIT}. -->\n        <enum name=\"reversePortrait\" value=\"9\" />\n        <!-- Orientation is determined by a physical orientation sensor:\n             the display will rotate based on how the user moves the device.\n             This allows any of the 4 possible rotations, regardless of what\n             the device will normally do (for example some devices won't\n             normally use 180 degree rotation).\n             Corresponds to\n             {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_FULL_SENSOR}. -->\n        <enum name=\"fullSensor\" value=\"10\" />\n        <!-- Would like to have the screen in landscape orientation, but if\n             the user has enabled sensor-based rotation then we can use the\n             sensor to change which direction the screen is facing.\n             Corresponds to\n             {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_USER_LANDSCAPE}. -->\n        <enum name=\"userLandscape\" value=\"11\" />\n        <!-- Would like to have the screen in portrait orientation, but if\n             the user has enabled sensor-based rotation then we can use the\n             sensor to change which direction the screen is facing.\n             Corresponds to\n             {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_USER_PORTRAIT}. -->\n        <enum name=\"userPortrait\" value=\"12\" />\n        <!-- Respect the user's sensor-based rotation preference, but if\n             sensor-based rotation is enabled then allow the screen to rotate\n             in all 4 possible directions regardless of what\n             the device will normally do (for example some devices won't\n             normally use 180 degree rotation).\n             Corresponds to\n             {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_FULL_USER}. -->\n        <enum name=\"fullUser\" value=\"13\" />\n        <!-- Screen is locked to its current rotation, whatever that is.\n             Corresponds to\n             {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LOCKED}. -->\n        <enum name=\"locked\" value=\"14\" />\n    </attr>\n\n    <!-- Specify the configuration changes that trigger the system to recreate the\n         current activity if any of these configuration changes happen in the system.\n         The valid configuration changes include mcc and mnc which are the same with\n         those in configChanges. By default from Android O, we don't recreate the activity\n         even the app doesn't specify mcc or mnc in configChanges. If the app wants to\n         be recreated, specify them in recreateOnConfigChanges. -->\n    <attr name=\"recreateOnConfigChanges\">\n        <!-- The IMSI MCC has changed, that is a SIM has been detected and\n             updated the Mobile Country Code. -->\n        <flag name=\"mcc\" value=\"0x0001\" />\n        <!-- The IMSI MNC has changed, that is a SIM has been detected and\n             updated the Mobile Network Code. -->\n        <flag name=\"mnc\" value=\"0x0002\" />\n    </attr>\n\n    <!-- Specify one or more configuration changes that the activity will\n         handle itself.  If not specified, the activity will be restarted\n         if any of these configuration changes happen in the system.  Otherwise,\n         the activity will remain running and its\n         {@link android.app.Activity#onConfigurationChanged Activity.onConfigurationChanged}\n         method called with the new configuration.\n\n         <p>Note that all of these configuration changes can impact the\n         resource values seen by the application, so you will generally need\n         to re-retrieve all resources (including view layouts, drawables, etc)\n         to correctly handle any configuration change.\n\n         <p>These values must be kept in sync with those in\n         {@link android.content.pm.ActivityInfo} and\n         include/utils/ResourceTypes.h. -->\n    <attr name=\"configChanges\">\n        <!-- The IMSI MCC has changed, that is a SIM has been detected and\n             updated the Mobile Country Code. By default from Android O, we\n             don't recreate the activity even the app doesn't specify mcc in\n             configChanges. If the app wants to recreate the activity, specify\n             mcc in recreateOnConfigChanges. -->\n        <flag name=\"mcc\" value=\"0x0001\" />\n        <!-- The IMSI MNC has changed, that is a SIM has been detected and\n             updated the Mobile Network Code. By default from Android O, we\n             don't recreate the activity even the app doesn't specify mnc in\n             configChanges. If the app wants to recreate the acvitity, specify\n             mnc in recreateOnConfigChanges. -->\n        <flag name=\"mnc\" value=\"0x0002\" />\n        <!-- The locale has changed, that is the user has selected a new\n             language that text should be displayed in. -->\n        <flag name=\"locale\" value=\"0x0004\" />\n        <!-- The touchscreen has changed.  Should never normally happen. -->\n        <flag name=\"touchscreen\" value=\"0x0008\" />\n        <!-- The keyboard type has changed, for example the user has plugged\n             in an external keyboard. -->\n        <flag name=\"keyboard\" value=\"0x0010\" />\n        <!-- The keyboard or navigation accessibility has changed, for example\n             the user has slid the keyboard out to expose it.  Note that\n             despite its name, this applied to any accessibility: keyboard\n             or navigation. -->\n        <flag name=\"keyboardHidden\" value=\"0x0020\" />\n        <!-- The navigation type has changed.  Should never normally happen. -->\n        <flag name=\"navigation\" value=\"0x0040\" />\n        <!-- The screen orientation has changed, that is the user has\n             rotated the device. -->\n        <flag name=\"orientation\" value=\"0x0080\" />\n        <!-- The screen layout has changed.  This might be caused by a\n             different display being activated. -->\n        <flag name=\"screenLayout\" value=\"0x0100\" />\n        <!-- The global user interface mode has changed.  For example,\n             going in or out of car mode, night mode changing, etc. -->\n        <flag name=\"uiMode\" value=\"0x0200\" />\n        <!-- The current available screen size has changed.  If applications don't\n             target at least {@link android.os.Build.VERSION_CODES#HONEYCOMB_MR2}\n             then the activity will always handle this itself (the change\n             will not result in a restart).  This represents a change in the\n             currently available size, so will change when the user switches\n             between landscape and portrait. -->\n        <flag name=\"screenSize\" value=\"0x0400\" />\n        <!-- The physical screen size has changed.  If applications don't\n             target at least {@link android.os.Build.VERSION_CODES#HONEYCOMB_MR2}\n             then the activity will always handle this itself (the change\n             will not result in a restart).  This represents a change in size\n             regardless of orientation, so will only change when the actual\n             physical screen size has changed such as switching to an external\n             display. -->\n        <flag name=\"smallestScreenSize\" value=\"0x0800\" />\n        <!-- The display density has changed. This might be caused by the user\n             specifying a different display scale, or it might be caused by a\n             different display being activated. -->\n        <flag name=\"density\" value=\"0x1000\" />\n        <!-- The layout direction has changed. For example going from LTR to RTL. -->\n        <flag name=\"layoutDirection\" value=\"0x2000\" />\n        <!-- The color mode of the screen has changed (color gamut or dynamic range). -->\n        <flag name=\"colorMode\" value=\"0x4000\" />\n        <!-- The grammatical gender has changed, for example the user set the grammatical gender\n             from the UI. -->\n        <flag name=\"grammaticalGender\" value=\"0x8000\" />\n        <!-- The font scaling factor has changed, that is the user has\n             selected a new global font size. -->\n        <flag name=\"fontScale\" value=\"0x40000000\" />\n        <!-- The font weight adjustment value has changed. Used to reflect the user increasing font\n             weight. -->\n        <flag name=\"fontWeightAdjustment\" value=\"0x10000000\" />\n        <!-- The assets paths have changed. For example a runtime overlay is installed and enabled.\n             Corresponds to {@link android.content.pm.ActivityInfo#CONFIG_ASSETS_PATHS}. -->\n        <flag name=\"assetsPaths\" value=\"0x80000000\" />\n        <!-- This is probably not the flag you want, the resources compiler supports a less\n             dangerous version of it, 'allKnown', that only suppresses all currently existing\n             configuration change restarts depending on your target SDK rather than whatever the\n             latest SDK supports, allowing the application to work with resources on future Platform\n             versions.\n             Activity doesn't use Android Resources at all and doesn't need to be restarted on any\n             configuration changes. This overrides all other flags, and this is recommended to be\n             used individually. Corresponds to\n             {@link android.content.pm.ActivityInfo#CONFIG_RESOURCES_UNUSED}. -->\n        <flag name=\"resourcesUnused\" value=\"0x8000000\" />\n    </attr>\n\n    <!-- Indicate that the activity can be launched as the embedded child of another\n         activity. Particularly in the case where the child lives in a container\n         such as a Display owned by another activity.\n\n         <p>The default value of this attribute is <code>false</code>. -->\n    <attr name=\"allowEmbedded\" format=\"boolean\" />\n\n    <!-- A reference to an array resource containing the signing certificate digests, one of which a\n         client is required to be signed with in order to embed the activity. If the client is not\n         signed with one of the certificates in the set, and the activity does not allow embedding\n         by untrusted hosts via {@link android.R.attr#allowUntrustedActivityEmbedding} flag, the\n         embedding request will fail.\n         <p>The digest should be computed over the DER encoding of the trusted certificate using the\n         SHA-256 digest algorithm.\n         <p>If only a single signer is declared this can also be a string resource, or the digest\n         can be declared inline as the value for this attribute.\n         <p>If the attribute is declared both on the application and the activity level, the value\n         on the activity level takes precedence. -->\n    <attr name=\"knownActivityEmbeddingCerts\" format=\"reference|string\" />\n\n    <!-- Indicate that the activity can be embedded by untrusted hosts. In this case the\n         interactions and visibility of the embedded activity may be limited.\n         <p>The default value of this attribute is <code>false</code>. -->\n    <attr name=\"allowUntrustedActivityEmbedding\" format=\"boolean\" />\n\n    <!-- Specifies whether this {@link android.app.Activity} should be shown on\n         top of the lock screen whenever the lockscreen is up and this activity has another\n         activity behind it with the {@link android.R.attr#showWhenLocked} attribute set. That\n         is, this activity is only visible on the lock screen if there is another activity with\n         the {@link android.R.attr#showWhenLocked} attribute visible at the same time on the\n         lock screen. A use case for this is permission dialogs, that should only be visible on\n         the lock screen if their requesting activity is also visible.\n\n         <p>The default value of this attribute is <code>false</code>. -->\n    <attr name=\"inheritShowWhenLocked\" format=\"boolean\" />\n\n    <!-- Descriptive text for the associated data. -->\n    <attr name=\"description\" format=\"reference\" />\n\n    <!-- The name of the application package that an Instrumentation object\n         will run against. -->\n    <attr name=\"targetPackage\" format=\"string\" />\n\n    <!-- The name of an application's processes that an Instrumentation object\n         will run against.  If not specified, only runs in the main process of the targetPackage.\n         Can either be a comma-separated list of process names or '*' for any process that\n         launches to run targetPackage code. -->\n    <attr name=\"targetProcesses\" format=\"string\" />\n\n    <!-- Flag indicating that an Instrumentation class wants to take care\n         of starting/stopping profiling itself, rather than relying on\n         the default behavior of profiling the complete time it is running.\n         This allows it to target profiling data at a specific set of\n         operations. -->\n    <attr name=\"handleProfiling\" format=\"boolean\" />\n\n    <!-- Flag indicating that an Instrumentation class should be run as a\n         functional test. -->\n    <attr name=\"functionalTest\" format=\"boolean\" />\n\n    <!-- The touch screen type used by an application. -->\n    <attr name=\"reqTouchScreen\">\n        <enum name=\"undefined\" value=\"0\" />\n        <enum name=\"notouch\" value=\"1\" />\n        <enum name=\"stylus\" value=\"2\" />\n        <enum name=\"finger\" value=\"3\" />\n    </attr>\n\n    <!-- The input method preferred by an application. -->\n    <attr name=\"reqKeyboardType\">\n        <enum name=\"undefined\" value=\"0\" />\n        <enum name=\"nokeys\" value=\"1\" />\n        <enum name=\"qwerty\" value=\"2\" />\n        <enum name=\"twelvekey\" value=\"3\" />\n    </attr>\n\n    <!-- Application's requirement for a hard keyboard -->\n    <attr name=\"reqHardKeyboard\" format=\"boolean\" />\n\n    <!-- The navigation device preferred by an application. -->\n    <attr name=\"reqNavigation\">\n        <enum name=\"undefined\" value=\"0\" />\n        <enum name=\"nonav\" value=\"1\" />\n        <enum name=\"dpad\" value=\"2\" />\n        <enum name=\"trackball\" value=\"3\" />\n        <enum name=\"wheel\" value=\"4\" />\n    </attr>\n\n    <!-- Application's requirement for five way navigation -->\n    <attr name=\"reqFiveWayNav\" format=\"boolean\" />\n\n    <!-- The name of the class subclassing <code>BackupAgent</code> to manage\n         backup and restore of the application's data on external storage. -->\n    <attr name=\"backupAgent\" format=\"string\" />\n\n    <!-- Whether to allow the application to participate in the backup\n         and restore infrastructure.  If this attribute is set to <code>false</code>,\n         no backup or restore of the application will ever be performed, even by a\n         full-system backup that would otherwise cause all application data to be saved\n         via adb.  The default value of this attribute is <code>true</code>. -->\n    <attr name=\"allowBackup\" format=\"boolean\" />\n\n    <!-- Applications will set this in their manifest to opt-in to or out of full app data back-up\n         and restore. Alternatively they can set it to an xml resource within their app that will\n         be parsed by the BackupAgent to selectively backup files indicated within that xml. -->\n    <attr name=\"fullBackupContent\" format=\"reference|boolean\" />\n\n    <!-- Indicates that even though the application provides a <code>BackupAgent</code>,\n         only full-data streaming backup operations are to be performed to save the app's\n         data.  This lets the app rely on full-data backups while still participating in\n         the backup and restore process via the BackupAgent's full-data backup APIs.\n         When this attribute is <code>true</code> the app's BackupAgent overrides of\n         the onBackup() and onRestore() callbacks can be empty stubs. -->\n    <attr name=\"fullBackupOnly\" format=\"boolean\" />\n\n    <!-- Whether the application in question should be terminated after its\n         settings have been restored during a full-system restore operation.\n         Single-package restore operations will never cause the application to\n         be shut down.  Full-system restore operations typically only occur once,\n         when the phone is first set up.  Third-party applications will not usually\n         need to use this attribute.\n\n         <p>The default is <code>true</code>, which means that after the application\n         has finished processing its data during a full-system restore, it will be\n         terminated. -->\n    <attr name=\"killAfterRestore\" format=\"boolean\" />\n\n    <!-- @deprecated This attribute is not used by the Android operating system. -->\n    <attr name=\"restoreNeedsApplication\" format=\"boolean\" />\n\n    <!-- Indicate that the application is prepared to attempt a restore of any\n         backed-up dataset, even if the backup is apparently from a newer version\n         of the application than is currently installed on the device.  Setting\n         this attribute to <code>true</code> will permit the Backup Manager to\n         attempt restore even when a version mismatch suggests that the data are\n         incompatible.  <em>Use with caution!</em>\n\n         <p>The default value of this attribute is <code>false</code>. -->\n    <attr name=\"restoreAnyVersion\" format=\"boolean\" />\n\n    <!-- Indicates that full-data backup operations for this application may\n         be performed even if the application is in a foreground-equivalent\n         state.  <em>Use with caution!</em>  Setting this flag to <code>true</code>\n         can impact app behavior while the user is interacting with the device.\n\n         <p>If unspecified, the default value of this attribute is <code>false</code>,\n         which means that the OS will avoid backing up the application while it is\n         running in the foreground (such as a music app that is actively playing\n         music via a service in the startForeground() state). -->\n    <attr name=\"backupInForeground\" format=\"boolean\" />\n\n    <!-- The default install location defined by an application. -->\n    <attr name=\"installLocation\">\n        <!-- Let the system decide ideal install location -->\n        <enum name=\"auto\" value=\"0\" />\n        <!-- Explicitly request to be installed on internal phone storage\n             only. -->\n        <enum name=\"internalOnly\" value=\"1\" />\n        <!-- Prefer to be installed on SD card. There is no guarantee that\n             the system will honor this request. The application might end\n             up being installed on internal storage if external media\n             is unavailable or too full. -->\n        <enum name=\"preferExternal\" value=\"2\" />\n    </attr>\n\n    <!-- If set to <code>true</code>, indicates to the platform that any split APKs\n         installed for this application should be loaded into their own Context\n         objects and not appear in the base application's Context.\n\n         <p>The default value of this attribute is <code>false</code>. -->\n    <attr name=\"isolatedSplits\" format=\"boolean\" />\n\n    <!-- The classname of the classloader used to load the application's classes\n         from its APK. The APK in question can either be the 'base' APK or any\n         of the application's 'split' APKs if it's using a feature split.\n\n         <p>\n         The supported values for this attribute are\n         <code>dalvik.system.PathClassLoader</code> and\n         <code>dalvik.system.DelegateLastClassLoader</code>. If unspecified,\n         the default value of this attribute is <code>dalvik.system.PathClassLoader</code>.\n\n         If an unknown classloader is provided, a PackageManagerException with cause\n         <code>PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED</code> will be\n         thrown and the app will not be installed.\n         -->\n    <attr name=\"classLoader\" format=\"string\" />\n\n    <!-- Name of the class that gets invoked for preloading application code, when starting an\n         {@link android.R.attr#isolatedProcess} service that has\n         {@link android.R.attr#useAppZygote} set to <code>true</code>. This is a fully\n         qualified class name (for example, com.mycompany.myapp.MyZygotePreload); as a\n         short-hand if the first character of the class is a period then it is appended\n         to your package name. The class must implement the {@link android.app.ZygotePreload}\n         interface. -->\n    <attr name=\"zygotePreloadName\" format=\"string\"/>\n\n    <!-- If set to <code>true</code>, indicates to the platform that this APK is\n         a 'feature' split and that it implicitly depends on the base APK. This distinguishes\n         this split APK from a 'configuration' split, which provides resource overrides\n         for a particular 'feature' split. Only useful when the base APK specifies\n         <code>android:isolatedSplits=\"true\"</code>.\n\n         <p>The default value of this attribute is <code>false</code>. -->\n    <attr name=\"isFeatureSplit\" format=\"boolean\" />\n\n    <!-- Flag to specify if this APK requires at least one split [either feature or\n         resource] to be present in order to function. Default value is false.\n         @deprecated Use {@link android.R.attr#requiredSplitTypes} instead. -->\n    <attr name=\"isSplitRequired\" format=\"boolean\" />\n\n    <!-- List of split types required by this APK to be present in order to function properly,\n         separated by commas. The platform will reject installation of an app that is missing\n         any required split types. Each split type is a string, and is only used for matching\n         <code>requiredSplitTypes</code> and <code>splitTypes</code>. As an example, if this\n         APK requires localized string resources, screen density resources, and native code\n         this value could be \"language,density,abi\". Default value is null to indicate no split\n         types are required. -->\n    <attr name=\"requiredSplitTypes\" format=\"string\" />\n\n    <!-- List of split types offered by this APK, separated by commas. Each split type is a\n         string, and is only used for matching <code>requiredSplitTypes</code> and\n         <code>splitTypes</code>. As an example, if this split offers localized string resources,\n         and screen density resources the value could be \"language,density\". Default value is\n         null to indicate no split types are offered. -->\n    <attr name=\"splitTypes\" format=\"string\" />\n\n    <!-- Flag to specify if this app (or process) wants to run the dex within its APK but not\n         extracted or locally compiled variants. This keeps the dex code protected by the APK\n         signature. Such apps (or processes) will always run in JIT mode (same when they are first\n         installed). If enabled at the app level, the system will never generate ahead-of-time\n         compiled code for the app. Depending on the app's workload, there may be some run time\n         performance change, noteably the cold start time.\n\n         <p>This attribute can be applied to either\n         {@link android.R.styleable#AndroidManifestProcess process} or\n         {@link android.R.styleable#AndroidManifestApplication application} tags. If enabled at the\n         app level, any process level attribute is effectively ignored.  -->\n    <attr name=\"useEmbeddedDex\" format=\"boolean\" />\n\n    <!-- Extra options for an activity's UI. Applies to either the {@code <activity>} or\n         {@code <application>} tag. If specified on the {@code <application>}\n         tag these will be considered defaults for all activities in the\n         application. -->\n    <attr name=\"uiOptions\">\n        <!-- No extra UI options. This is the default. -->\n        <flag name=\"none\" value=\"0\" />\n        <!-- Split the options menu into a separate bar at the bottom of\n             the screen when severely constrained for horizontal space.\n             (e.g. portrait mode on a phone.) Instead of a small number\n             of action buttons appearing in the action bar at the top\n             of the screen, the action bar will split into the top navigation\n             section and the bottom menu section. Menu items will not be\n             split across the two bars; they will always appear together. -->\n        <flag name=\"splitActionBarWhenNarrow\" value=\"1\" />\n    </attr>\n\n    <!-- The name of the logical parent of the activity as it appears in the manifest. -->\n    <attr name=\"parentActivityName\" format=\"string\" />\n\n    <!-- Define how an activity persist across reboots. Activities defined as \"never\" will not\n         be persisted. Those defined as \"always\" will be persisted. Those defined as \"taskOnly\"\n         will persist the root activity of the task only. See below for more detail as to\n         what gets persisted. -->\n    <attr name=\"persistableMode\">\n        <!-- The default. If this activity forms the root of a task then that task will be\n             persisted across reboots but only the launching intent will be used. If the task\n             relinquishes its identity then the intent used is that of the topmost inherited\n             identity. All activities above this activity in the task will not be persisted.\n             In addition this activity will not be passed a PersistableBundle into which it\n             could have stored its state. -->\n        <enum name=\"persistRootOnly\" value=\"0\" />\n        <!-- If this activity forms the root of a task then that task will not be persisted\n             across reboots -->\n        <enum name=\"persistNever\" value=\"1\" />\n        <!-- If this activity forms the root of a task then the task and this activity will\n             be persisted across reboots. If the activity above this activity is also\n             tagged with the attribute <code>\"persist\"</code> then it will be persisted as well.\n             And so on up the task stack until either an activity without the\n             <code>persistableMode=\"persistAcrossReboots\"</code> attribute or one that was launched\n             with the flag Intent.FLAG_CLEAR_TASK_WHEN_RESET is encountered.\n\n             <p>Activities that are declared with the persistAcrossReboots attribute will be\n             provided with a PersistableBundle in onSavedInstanceState(), These activities may\n             use this PeristableBundle to save their state. Then, following a reboot, that\n             PersistableBundle will be provided back to the activity in its onCreate() method. -->\n        <enum name=\"persistAcrossReboots\" value=\"2\" />\n    </attr>\n\n    <!-- This attribute specifies that an activity shall become the root activity of a\n         new task each time it is launched. Using this attribute permits the user to\n         have multiple documents from the same applications appear in the recent tasks list.\n\n         <p>Such a document is any kind of item for which an application may want to\n         maintain multiple simultaneous instances. Examples might be text files, web\n         pages, spreadsheets, or emails. Each such document will be in a separate\n         task in the recent tasks list.\n\n         <p>This attribute is equivalent to adding the flag {@link\n         android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} to every Intent used to launch\n         the activity.\n\n         <p>The documentLaunchMode attribute may be assigned one of four values, \"none\",\n         \"intoExisting\", \"always\" and \"never\", described in detail below. For values other than\n         <code>none</code> and <code>never</code> the activity must be defined with\n         {@link android.R.attr#launchMode} <code>standard</code>.\n         If this attribute is not specified, <code>none</code> will be used.\n         Note that <code>none</code> can be overridden at run time if the Intent used\n         to launch it contains the flag {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT\n         Intent.FLAG_ACTIVITY_NEW_DOCUMENT}.\n         Similarly <code>intoExisting</code> will be overridden by the flag\n         {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT\n         Intent.FLAG_ACTIVITY_NEW_DOCUMENT} combined with\n         {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK\n         Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. If the value of\n         documentLaunchModes is <code>never</code> then any use of\n         {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT\n         Intent.FLAG_ACTIVITY_NEW_DOCUMENT} to launch this activity will be ignored. -->\n    <attr name=\"documentLaunchMode\">\n        <!-- The default mode, which will create a new task only when\n             {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK\n             Intent.FLAG_ACTIVITY_NEW_TASK} is set. -->\n        <enum name=\"none\" value=\"0\" />\n        <!-- All tasks will be searched for one whose base Intent's ComponentName and\n             data URI match those of the launching Intent. If such a task is found\n             that task will be cleared and restarted with the root activity receiving a call\n             to {@link android.app.Activity#onNewIntent Activity.onNewIntent}. If no\n             such task is found a new task will be created.\n             <p>This is the equivalent of launching an activity with {@link\n             android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NEW_DOCUMENT}\n             set and without {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK\n             Intent.FLAG_ACTIVITY_MULTIPLE_TASK} set. -->\n        <enum name=\"intoExisting\" value=\"1\" />\n        <!-- A new task rooted at this activity will be created. This will happen whether or\n             not there is an existing task whose ComponentName and data URI match\n             that of the launcing intent This is the equivalent of launching an activity\n             with {@link\n             android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NEW_DOCUMENT}\n             and {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK\n             Intent.FLAG_ACTIVITY_MULTIPLE_TASK} both set. -->\n        <enum name=\"always\" value=\"2\" />\n        <!-- This activity will not be launched into a new document even if the Intent contains\n             {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT\n             Intent.FLAG_ACTIVITY_NEW_DOCUMENT}. This gives the activity writer ultimate\n             control over how their activity is used. Note that applications prior to api\n             21 will default to documentLaunchMode=\"none\" so only activities that explicitly\n             opt out with <code>\"never\"</code> may do so. -->\n        <enum name=\"never\" value=\"3\" />\n    </attr>\n\n    <!-- The maximum number of entries of tasks rooted at this activity in the recent task list.\n         When this number of entries is reached the least recently used instance of this activity\n         will be removed from recents. The value will be clamped between 1 and 100 inclusive.\n         The default value for this if it is not specified is 15. -->\n    <attr name=\"maxRecents\" format=\"integer\" />\n\n    <!-- Tasks launched by activities with this attribute will remain in the recent tasks\n         list until the last activity in the task is completed.  When that happens the task\n         will be automatically removed from the recent tasks list.  This overrides the caller's\n         use of {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS\n         Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS} -->\n    <attr name=\"autoRemoveFromRecents\" format=\"boolean\" />\n\n    <!-- Tasks whose root has this attribute set to true will replace baseIntent with that of the\n         next activity in the task. If the next activity also has this attribute set to true then\n         it will yield the baseIntent to any activity that it launches in the same task. This\n         continues until an activity is encountered which has this attribute set to false. False\n         is the default. This attribute set to true also permits activity's use of the\n         TaskDescription to change labels, colors and icons in the recent task list.\n\n         <p>NOTE: Setting this flag to <code>true</code> will not change the affinity of the task,\n         which is used for intent resolution during activity launch. The task's root activity will\n         always define its affinity. -->\n    <attr name=\"relinquishTaskIdentity\" format=\"boolean\" />\n\n    <!-- Indicate that it is okay for this activity be resumed while the previous\n         activity is in the process of pausing, without waiting for the previous pause\n         to complete.  Use this with caution: your activity can not acquire any exclusive\n         resources (such as opening the camera or recording audio) when it launches, or it\n         may conflict with the previous activity and fail.\n\n         <p>The default value of this attribute is <code>false</code>. -->\n    <attr name=\"resumeWhilePausing\" format=\"boolean\" />\n\n    <!-- Hint to platform that the activity works well in multi-window mode. Intended for a\n         multi-window device where there can be multiple activities of various sizes on the screen\n         at the same time.\n\n         <p>The default value is <code>false</code> for applications with\n         <code>targetSdkVersion</code> lesser than {@link android.os.Build.VERSION_CODES#N} and\n         <code>true</code> otherwise.\n\n         <p>Setting this flag to <code>false</code> lets the system know that the app may not be\n         tested or optimized for multi-window environment. The system may still put such activity in\n         multi-window with compatibility mode applied. It also does not guarantee that there will be\n         no other apps in multi-window visible on screen (e.g. picture-in-picture) or on other\n         displays. Therefore, this flag cannot be used to assure an exclusive resource access.\n\n         <p>A task's root activity value is applied to all additional activities launched in\n         the task. That is if the root activity of a task is resizeable then the system will treat\n         all other activities in the task as resizeable and will not if the root activity isn't\n         resizeable.\n\n         <aside class=\"note\"><b>Note:</b>\n            <ul>\n                <li>On Android 11 (API level 30) and lower, the value of\n                    {@link android.R.attr#screenOrientation} is ignored for resizeable activities\n                    in multi-window mode.</li>\n                <li>To improve the layout of apps on form factors with smallest width >= 600dp, the\n                    system ignores this attribute for apps that target Android 16 (API level 36) or\n                    higher.</li>\n                <li>Device manufacturers can configure devices to override (ignore) this attribute\n                    to force apps to resize. The override does not affect the app's support for\n                    multi-window mode.</li>\n                <li>On devices with Android 16 (API level 36) or higher installed, virtual device\n                    owners (select trusted and privileged apps) can configure devices they manage to\n                    override (ignore) this attribute to force apps to resize. See\n                    also <a href=\"https://source.android.com/docs/core/permissions/app-streaming\">\n                    Companion app streaming</a>.</li>\n            </ul>\n            <p>See <a href=\"{@docRoot}guide/practices/device-compatibility-mode\"> Device\n            compatibility mode</a>.</p>\n         </aside> -->\n    <attr name=\"resizeableActivity\" format=\"boolean\" />\n\n    <!-- Indicates that the activity specifically supports the picture-in-picture form of\n         multi-window. If true, this activity will support entering picture-in-picture, but will\n         only support split-screen and other forms of multi-window if\n         {@link android.R.attr#resizeableActivity} is also set to true.\n\n         Note that your activity may still be resized even if this attribute is true and\n         {@link android.R.attr#resizeableActivity} is false.\n\n         <p>The default value is <code>false</code>.  -->\n    <attr name=\"supportsPictureInPicture\" format=\"boolean\" />\n\n    <!-- This value indicates the maximum aspect ratio the activity supports. If the app runs on a\n         device with a wider aspect ratio, the system automatically letterboxes the app, leaving\n         portions of the screen unused so the app can run at its specified maximum aspect ratio.\n         <p>Maximum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal\n         form. For example, if the maximum aspect ratio is 7:3, set value to 2.33.\n         <p>Value needs to be greater or equal to 1.0, otherwise it is ignored.\n         <aside class=\"note\"><b>Note:</b>\n            <ul>\n                <li>This attribute is ignored if the activity has\n                    {@link android.R.attr#resizeableActivity} set to {@code true}.</li>\n                <li>To improve the layout of apps on form factors with smallest width >= 600dp, the\n                    system ignores this attribute for apps that target Android 16 (API level 36) or\n                    higher.</li>\n                <li>Device manufacturers can configure devices to override (ignore) this attribute\n                    to improve the layout of apps.</li>\n                <li>On devices with Android 16 (API level 36) or higher installed, virtual device\n                    owners (select trusted and privileged apps) can configure devices they manage to\n                    override (ignore) this attribute to improve app layout. See\n                    <a href=\"https://source.android.com/docs/core/permissions/app-streaming\">\n                    Companion app streaming</a>.</li>\n            </ul>\n            <p>See <a href=\"{@docRoot}guide/practices/device-compatibility-mode\"> Device\n            compatibility mode</a>.</p>\n         </aside> -->\n    <attr name=\"maxAspectRatio\" format=\"float\" />\n\n    <!-- This value indicates the minimum aspect ratio the activity supports. If the app runs on a\n         device with a narrower aspect ratio, the system automatically letterboxes the app, leaving\n         portions of the screen unused so the app can run at its specified minimum aspect ratio.\n         <p>Minimum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal\n            form. For example, if the minimum aspect ratio is 4:3, set value to 1.33.\n         <p>Value needs to be greater or equal to 1.0, otherwise it is ignored.\n          <aside class=\"note\"><b>Note:</b>\n            <ul>\n                <li>This attribute is ignored if the activity has\n                    {@link android.R.attr#resizeableActivity} set to {@code true}.</li>\n                <li>To improve the layout of apps on form factors with smallest width >= 600dp, the\n                    system ignores this attribute for apps that target Android 16 (API level 36) or\n                    higher.</li>\n                <li>Device manufacturers can configure devices to override (ignore) this attribute\n                    to improve the layout of apps.</li>\n                <li>On devices with Android 16 (API level 36) or higher installed, virtual device\n                    owners (select trusted and privileged apps) can configure devices they manage to\n                    override (ignore) this attribute to improve app layout. See\n                    <a href=\"https://source.android.com/docs/core/permissions/app-streaming\">\n                    Companion app streaming</a>.</li>\n            </ul>\n            <p>See <a href=\"{@docRoot}guide/practices/device-compatibility-mode\"> Device\n            compatibility mode</a>.</p>\n         </aside> -->\n   <attr name=\"minAspectRatio\" format=\"float\" />\n\n    <!-- This value indicates how tasks rooted at this activity will behave in lockTask mode.\n         While in lockTask mode the system will not launch non-permitted tasks until\n         lockTask mode is disabled.\n         <p>While in lockTask mode with multiple permitted tasks running, each launched task is\n         permitted to finish, transitioning to the previous locked task, until there is only one\n         task remaining. At that point the last task running is not permitted to finish, unless it\n         uses the value always. -->\n    <attr name=\"lockTaskMode\">\n        <!-- This is the default value. Tasks will not launch into lockTask mode but can be\n             placed there by calling {@link android.app.Activity#startLockTask}. If a task with\n             this mode has been allowlisted using {@link\n             android.app.admin.DevicePolicyManager#setLockTaskPackages} then calling\n             {@link android.app.Activity#startLockTask} will enter lockTask mode immediately,\n             otherwise the user will be presented with a dialog to approve entering pinned mode.\n             <p>If the system is already in lockTask mode when a new task rooted at this activity\n             is launched that task will or will not start depending on whether the package of this\n             activity has been allowlisted.\n             <p>Tasks rooted at this activity can only exit lockTask mode using\n             {@link android.app.Activity#stopLockTask}. -->\n        <enum name=\"normal\" value=\"0\"/>\n        <!-- Tasks will not launch into lockTask mode and cannot be placed there using\n             {@link android.app.Activity#startLockTask} or be pinned from the Overview screen.\n             If the system is already in lockTask mode when a new task rooted at this activity is\n             launched that task will not be started.\n             <p>Note: This mode is only available to system and privileged applications.\n             Non-privileged apps with this value will be treated as normal.\n             -->\n        <enum name=\"never\" value=\"1\"/>\n        <!-- Tasks rooted at this activity will always launch into lockTask mode. If the system is\n             already in lockTask mode when this task is launched then the new task will be launched\n             on top of the current task. Tasks launched in this mode are capable of exiting\n             lockTask mode using {@link android.app.Activity#finish()}.\n             <p>Note: This mode is only available to system and privileged applications.\n             Non-privileged apps with this value will be treated as normal.\n             -->\n        <enum name=\"always\" value=\"2\"/>\n        <!-- If the DevicePolicyManager (DPM) authorizes this package ({@link\n             android.app.admin.DevicePolicyManager#setLockTaskPackages}) then this mode is\n             identical to always, except that the activity needs to call\n             {@link android.app.Activity#stopLockTask} before being able to finish if it is the last\n             locked task.\n             If the DPM does not authorize this package then this mode is identical to normal. -->\n        <enum name=\"if_whitelisted\" value=\"3\"/>\n    </attr>\n    <!-- When set installer will extract native libraries. If set to false\n         libraries in the apk must be stored and page-aligned.  -->\n    <attr name=\"extractNativeLibs\" format=\"boolean\"/>\n\n    <!-- Specify whether an activity intent filter will need to be verified thru its set\n         of data URIs. This will only be used when the Intent's action is set to\n         {@link android.content.Intent#ACTION_VIEW Intent.ACTION_VIEW} and the Intent's category is\n         set to {@link android.content.Intent#CATEGORY_BROWSABLE Intent.CATEGORY_BROWSABLE} and the\n         intern filter data scheme is set to \"http\" or \"https\". When set to true, the intent filter\n         will need to use its data tag for getting the URIs to verify with.\n\n         For each URI, an HTTPS network request will be done to <code>/.well-known/statements.json</code>\n         host to verify that the web site is okay with the app intercepting the URI.\n         -->\n    <attr name=\"autoVerify\" format=\"boolean\" />\n\n    <!-- Specify whether a component should be visible to instant apps.\n         -->\n    <attr name=\"visibleToInstantApps\" format=\"boolean\" />\n\n    <!-- An XML resource with the application's Network Security Config. -->\n    <attr name=\"networkSecurityConfig\" format=\"reference\" />\n\n    <!-- An XML resource with the application's {@link android.app.LocaleConfig} -->\n    <attr name=\"localeConfig\" format=\"reference\" />\n\n    <!-- When an application is partitioned into splits, this is the name of the\n         split that contains the defined component. -->\n    <attr name=\"splitName\" format=\"string\" />\n\n    <!-- Specifies the target sandbox this app wants to use. Higher sandbox versions\n         will have increasing levels of security.\n\n         <p>The default value of this attribute is <code>1</code>.\n         <p>\n         @deprecated The security properties have been moved to\n         {@link android.os.Build.VERSION Build.VERSION} 27 and 28. -->\n    <attr name=\"targetSandboxVersion\" format=\"integer\" />\n\n    <!-- The user-visible SDK version (ex. 26) of the framework against which the application was\n         compiled. This attribute is automatically specified by the Android build tools and should\n         NOT be manually specified.\n         <p>\n         This attribute is the compile-time equivalent of\n         {@link android.os.Build.VERSION#SDK_INT Build.VERSION.SDK_INT}. -->\n    <attr name=\"compileSdkVersion\" format=\"integer\" />\n\n    <!-- The development codename (ex. \"O\") of the framework against which the application was\n         compiled, or \"REL\" if the application was compiled against a release build. This attribute\n         is automatically specified by the Android build tools and should NOT be manually\n         specified.\n         <p>\n         This attribute is the compile-time equivalent of\n         {@link android.os.Build.VERSION#CODENAME Build.VERSION.CODENAME}. -->\n    <attr name=\"compileSdkVersionCodename\" format=\"string\" />\n\n    <!-- The (optional) fully-qualified name for a subclass of\n         {@link android.app.AppComponentFactory} that the system uses to instantiate\n         every other manifest defined class. Most applications\n         don't need this attribute. If it's not specified, the system\n         instantiates items without it.-->\n    <attr name=\"appComponentFactory\" format=\"string\" />\n\n    <attr name=\"usesNonSdkApi\" format=\"boolean\" />\n\n    <!-- Whether attributions provided are meant to be user-visible. -->\n    <attr name=\"attributionsAreUserVisible\" format=\"boolean\" />\n\n    <!-- If a preloaded APK is marked updatableSystem = false, any request for an update will be rejected.\n         If an APK marked updatableSystem = false is being installed, regardless of the updatableSystem state\n         of the version it's replacing, the install will be rejected.\n         This is a private attribute, used without android: namespace. -->\n    <attr name=\"updatableSystem\" format=\"boolean\" />\n\n    <!-- Allows each installer in the system image to designate another app in the system image to\n        update the installer. -->\n    <attr name=\"emergencyInstaller\" format=\"string\" />\n\n    <!-- Specify the type of foreground service. Multiple types can be specified by ORing the flags\n         together. -->\n    <attr name=\"foregroundServiceType\">\n        <!-- Data (photo, file, account) upload/download, backup/restore, import/export, fetch,\n            transfer over network between device and cloud.\n\n            <p>For apps with <code>targetSdkVersion</code>\n            {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, this type should NOT\n            be used: calling\n            {@link android.app.Service#startForeground(int, android.app.Notification, int)} with\n            this type on devices running {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}\n            is still allowed, but calling it with this type on devices running future platform\n            releases may get a {@link android.app.InvalidForegroundServiceTypeException}.\n        -->\n        <flag name=\"dataSync\" value=\"0x01\" />\n        <!-- Music, video, news or other media play.\n\n            <p>For apps with <code>targetSdkVersion</code>\n            {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, starting a foreground\n            service with this type will require permission\n            {@link android.Manifest.permission#FOREGROUND_SERVICE_MEDIA_PLAYBACK}.\n        -->\n        <flag name=\"mediaPlayback\" value=\"0x02\" />\n        <!-- Ongoing operations related to phone calls, video conferencing,\n            or similar interactive communication.\n\n            <p>For apps with <code>targetSdkVersion</code>\n            {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, starting a foreground\n            service with this type will require permission\n            {@link android.Manifest.permission#FOREGROUND_SERVICE_PHONE_CALL} and\n            {@link android.Manifest.permission#MANAGE_OWN_CALLS} or holding the default\n            {@link android.app.role.RoleManager#ROLE_DIALER dialer role}.\n        -->\n        <flag name=\"phoneCall\" value=\"0x04\" />\n        <!-- GPS, map, navigation location update.\n\n            <p>For apps with <code>targetSdkVersion</code>\n            {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, starting a foreground\n            service with this type will require permission\n            {@link android.Manifest.permission#FOREGROUND_SERVICE_LOCATION} and one of the\n            following permissions:\n            {@link android.Manifest.permission#ACCESS_COARSE_LOCATION},\n            {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.\n        -->\n        <flag name=\"location\" value=\"0x08\" />\n        <!-- Auto, bluetooth, TV or other devices connection, monitoring and interaction.\n\n            <p>For apps with <code>targetSdkVersion</code>\n            {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, starting a foreground\n            service with this type will require permission\n            {@link android.Manifest.permission#FOREGROUND_SERVICE_CONNECTED_DEVICE} and one of the\n            following permissions:\n            {@link android.Manifest.permission#BLUETOOTH_CONNECT},\n            {@link android.Manifest.permission#CHANGE_NETWORK_STATE},\n            {@link android.Manifest.permission#CHANGE_WIFI_STATE},\n            {@link android.Manifest.permission#CHANGE_WIFI_MULTICAST_STATE},\n            {@link android.Manifest.permission#NFC},\n            {@link android.Manifest.permission#TRANSMIT_IR},\n            or has been granted the access to one of the attached USB devices/accessories.\n        -->\n        <flag name=\"connectedDevice\" value=\"0x10\" />\n        <!-- Managing a {@link android.media.projection.MediaProjection MediaProjection} session,\n             e.g., for screen recording or takingscreenshots.\n\n             <p>\n             To capture through {@link android.media.projection.MediaProjection}, an app must start\n             a foreground service with the type corresponding to this constant. This type should\n             only be used for {@link android.media.projection.MediaProjection}. Capturing screen\n             contents via\n             {@link android.media.projection.MediaProjection#createVirtualDisplay(String, int, int,\n             int, int, android.view.Surface, android.hardware.display.VirtualDisplay.Callback,\n             android.os.Handler) createVirtualDisplay} conveniently allows recording, presenting\n             screen contents into a meeting, taking screenshots, or several other scenarios.\n             </p>\n\n             <p>For apps with <code>targetSdkVersion</code>\n             {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, starting a\n             foreground service with this type will require permission\n             {@link android.Manifest.permission#FOREGROUND_SERVICE_MEDIA_PROJECTION}, and the user\n             must have allowed the screen capture request from this app.\n        -->\n        <flag name=\"mediaProjection\" value=\"0x20\" />\n        <!-- Use the camera device or record video.\n\n            <p>For apps with <code>targetSdkVersion</code> {@link android.os.Build.VERSION_CODES#R}\n            and above, a foreground service will not be able to access the camera if this type is\n            not specified in the manifest and in\n            {@link android.app.Service#startForeground(int, android.app.Notification, int)}.\n\n            <p>For apps with <code>targetSdkVersion</code>\n            {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, starting a foreground\n            service with this type will require permission\n            {@link android.Manifest.permission#FOREGROUND_SERVICE_CAMERA} and\n            {@link android.Manifest.permission#CAMERA}.\n            -->\n        <flag name=\"camera\" value=\"0x40\" />\n        <!--Use the microphone device or record audio.\n\n            <p>For apps with <code>targetSdkVersion</code> {@link android.os.Build.VERSION_CODES#R}\n            and above, a foreground service will not be able to access the microphone if this type\n            is not specified in the manifest and in\n            {@link android.app.Service#startForeground(int, android.app.Notification, int)}.\n\n            <p>For apps with <code>targetSdkVersion</code>\n            {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, starting a foreground\n            service with this type will require permission\n            {@link android.Manifest.permission#FOREGROUND_SERVICE_MICROPHONE} and one of the\n            following permissions:\n            {@link android.Manifest.permission#CAPTURE_AUDIO_OUTPUT},\n            {@link android.Manifest.permission#RECORD_AUDIO}.\n            -->\n        <flag name=\"microphone\" value=\"0x80\" />\n        <!--Health, wellness and fitness.\n            <p>Requires the app to hold the permission\n            {@link android.Manifest.permission#FOREGROUND_SERVICE_HEALTH} and one of the following\n            permissions\n            {@link android.Manifest.permission#ACTIVITY_RECOGNITION},\n            {@link android.Manifest.permission#BODY_SENSORS},\n            {@link android.Manifest.permission#HIGH_SAMPLING_RATE_SENSORS}.\n        -->\n        <flag name=\"health\" value=\"0x100\" />\n        <!-- Messaging use cases which host local server to relay messages across devices.\n            <p>Requires the app to hold the permission\n            {@link android.Manifest.permission#FOREGROUND_SERVICE_REMOTE_MESSAGING} in order to use\n            this type.\n        -->\n        <flag name=\"remoteMessaging\" value=\"0x200\" />\n        <!-- The system exempted foreground service use cases.\n            <p>Requires the app to hold the permission\n            {@link android.Manifest.permission#FOREGROUND_SERVICE_SYSTEM_EXEMPTED} in order to use\n            this type. Apps are allowed to use this type only in the use cases listed in\n            {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED}.\n        -->\n        <flag name=\"systemExempted\" value=\"0x400\" />\n        <!-- \"Short service\" foreground service type. See\n           {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SHORT_SERVICE}.\n           for more details.\n        -->\n        <flag name=\"shortService\" value=\"0x800\" />\n        <!-- The file management use case which manages files/directories, often involving file I/O\n            across the file system.\n            <p>Requires the app to hold the permission\n            {@link android.Manifest.permission#FOREGROUND_SERVICE_FILE_MANAGEMENT} in order to use\n            this type.\n\n            TODO: b/258855262 mark this field as {@code hide} once this bug is fixed.\n            <flag name=\"fileManagement\" value=\"0x1000\" />\n        -->\n        <!-- Media processing use cases such as video or photo editing and processing.\n            <p>Requires the app to hold the permission\n            {@link android.Manifest.permission#FOREGROUND_SERVICE_MEDIA_PROCESSING} in order to use\n            this type.\n        -->\n        <flag name=\"mediaProcessing\" value=\"0x2000\" />\n        <!-- Use cases that can't be categorized into any other foreground service types, but also\n            can't use @link android.app.job.JobInfo.Builder} APIs.\n            See {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE} for the\n            best practice of the use of this type.\n\n            <p>Requires the app to hold the permission\n            {@link android.Manifest.permission#FOREGROUND_SERVICE_SPECIAL_USE} in order to use\n            this type.\n        -->\n        <flag name=\"specialUse\" value=\"0x40000000\" />\n    </attr>\n\n    <!-- Enable sampled memory bug detection in this process.\n         When enabled, a very small, random subset of native\n         memory allocations are protected with guard pages, providing an\n         ASan-like error report in case of a memory corruption bug.\n\n         GWP-ASan is a recursive acronym. It stands for “GWP-ASan Will Provide Allocation SANity”.\n         See the <a href=\"http://llvm.org/docs/GwpAsan.html\">LLVM documentation</a>\n         for more information about this feature.\n\n         <p>This attribute can be applied to a\n         {@link android.R.styleable#AndroidManifestProcess process} tag, or to an\n         {@link android.R.styleable#AndroidManifestApplication application} tag (to supply\n         a default setting for all application components). -->\n    <attr name=\"gwpAsanMode\">\n        <!-- Default behavior: GwpAsan is disabled in user apps, randomly enabled in system apps. -->\n        <enum name=\"default\" value=\"-1\" />\n        <!-- Never enable GwpAsan. -->\n        <enum name=\"never\" value=\"0\" />\n        <!-- Always enable GwpAsan. -->\n       <enum name=\"always\" value=\"1\" />\n    </attr>\n\n    <!-- Enable hardware memory tagging (ARM MTE) in this process.\n         When enabled, heap memory bugs like use-after-free and buffer overflow\n         are detected and result in an immediate (\"sync\" mode) or delayed (\"async\"\n         mode) crash instead of a silent memory corruption. Sync mode, while slower,\n         provides enhanced bug reports including stack traces at the time of allocation\n         and deallocation of memory, similar to AddressSanitizer.\n\n         See the <a href=\"https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/enhancing-memory-safety\">ARM announcement</a>\n         for more details.\n\n         <p>This attribute can be applied to a\n         {@link android.R.styleable#AndroidManifestProcess process} tag, or to an\n         {@link android.R.styleable#AndroidManifestApplication application} tag (to supply\n         a default setting for all application components). -->\n     <attr name=\"memtagMode\">\n       <enum name=\"default\" value=\"-1\" />\n       <enum name=\"off\" value=\"0\" />\n       <enum name=\"async\" value=\"1\" />\n       <enum name=\"sync\" value=\"2\" />\n    </attr>\n\n    <!-- This attribute overrides the user-set or platform-set 16 KB page size\n         compatibility mode, so that page agnostic compatibility is always enabled\n         or always disabled, rather than according to the user's preference.\n\n         <p>On 4 KB systems, this attribute is ignored and apps are installed\n         normally.\n\n         <p>On 16 KB systems, if an app is built for 16 KB page sizes, this\n         attribute is ignored and apps are installed normally.\n\n         <p>This attribute only affects 16 KB systems for apps that are built\n         with 4 KB page size (old) options.\n\n         <p>When page agnostic compatibility is enabled (either through this\n         flag or via the user's preference), the system specializes the app\n         installation process in ways known to improve compatibility of 4 KB\n         built apps on 16 KB systems. That is, apps which do not have aligned\n         libraries in APK files are extracted, requiring more space on the\n         device. An additional specialization when this option is enabled is\n         that the linker loads the application in a special mode intended\n         to allow 4 KB aligned program segments to load on a 16 KB page system.\n\n         <p>Here are the situations where this attribute should be most useful:\n         <ul>\n            <li>If an app works on 16 KB mode, but is not built for it, enabling this\n            attribute forces the app to be installed in 16 KB mode without\n            the user having to set these options themself.\n            <li>If an app is fully working in 16 KB mode, you can set this\n            attribute to disabled, so that any regression causes a clear failure\n            and this compatibility mode is not used.\n         </ul>\n\n         @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->\n    <attr name=\"pageSizeCompat\">\n        <!-- value for enabled must match with\n        ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED -->\n        <enum name=\"enabled\" value=\"32\" />\n        <!-- value for disabled must match with\n        ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_DISABLED -->\n        <enum name=\"disabled\" value=\"64\" />\n    </attr>\n\n\n    <!-- Attribution tag to be used for permission sub-attribution if a\n      permission is checked in  {@link android.content.Context#sendBroadcast(Intent, String)}.\n      Multiple tags can be specified separated by '|'.\n    -->\n    <attr name=\"attributionTags\" format=\"string\" />\n\n    <attr name=\"allowUpdateOwnership\" format=\"boolean\" />\n\n    <!-- This attribute can be applied to any tag in the manifest. The system uses its value to\n         determine whether the element (e.g., a permission) should be enabled or disabled\n         depending on the state of feature flags.\n         @hide @SystemApi @FlaggedApi(\"android.content.res.manifest_flagging\") -->\n    <attr name=\"featureFlag\" format=\"string\" />\n\n    <!-- This attribute provides a way to fine-tune how incoming intents are matched to application\n    components. By default, no special matching rules are applied. This attribute can be specified\n    on the {@code <application>} tag as well as at the component tags such as {@code <activity>},\n    {@code <activity-alias>}, {@code <receiver>}, {@code <service>}, {@code <provider>} and the\n    attribute on the component can be used to override what's on the {@code <application>} tag. -->\n    <attr name=\"intentMatchingFlags\">\n        <!-- Disables all special matching rules for incoming intents. When specifying multiple\n        flags, conflicting values are resolved by giving precedence to the \"none\" flag. -->\n        <flag name=\"none\" value=\"0x0001\" />\n\n        <!-- Enforces stricter matching for incoming intents:\n        <ul>\n             <li>Explicit intents should match the target component's intent filter\n             <li>Intents without an action should not match any intent filter\n        </ul>\n        -->\n        <flag name=\"enforceIntentFilter\" value=\"0x0002\" />\n\n        <!-- Relaxes the matching rules to allow intents without a action to match. This flag to be\n        used in conjunction with enforceIntentFilter to achieve a specific behavior:\n        <ul>\n            <li>Explicit intents should match the target component's intent filter\n            <li>Intents without an action are allowed to match any intent filter\n        </ul>\n        -->\n        <flag name=\"allowNullAction\" value=\"0x0004\" />\n    </attr>\n\n    <!-- The <code>manifest</code> tag is the root of an\n         <code>AndroidManifest.xml</code> file,\n         describing the contents of an Android package (.apk) file.  One\n         attribute must always be supplied: <code>package</code> gives a\n         unique name for the package, using a Java-style naming convention\n         to avoid name collisions.  For example, applications published\n         by Google could have names of the form\n         <code>com.google.app.<em>appname</em></code>\n\n         <p>Inside of the manifest tag, may appear the following tags\n         in any order: {@link #AndroidManifestAttribution attribution},\n         {@link #AndroidManifestPermission permission},\n         {@link #AndroidManifestPermissionGroup permission-group},\n         {@link #AndroidManifestPermissionTree permission-tree},\n         {@link #AndroidManifestUsesSdk uses-sdk},\n         {@link #AndroidManifestUsesPermission uses-permission},\n         {@link #AndroidManifestUsesConfiguration uses-configuration},\n         {@link #AndroidManifestApplication application},\n         {@link #AndroidManifestInstrumentation instrumentation},\n         {@link #AndroidManifestUsesFeature uses-feature}.  -->\n    <declare-styleable name=\"AndroidManifest\">\n        <attr name=\"versionCode\" />\n        <attr name=\"versionCodeMajor\" />\n        <attr name=\"versionName\" />\n        <attr name=\"revisionCode\" />\n        <attr name=\"sharedUserId\" />\n        <attr name=\"sharedUserLabel\" />\n        <attr name=\"sharedUserMaxSdkVersion\" />\n        <attr name=\"installLocation\" />\n        <attr name=\"isolatedSplits\" />\n        <attr name=\"isFeatureSplit\" />\n        <attr name=\"targetSandboxVersion\" />\n        <attr name=\"compileSdkVersion\" />\n        <attr name=\"compileSdkVersionCodename\" />\n        <attr name=\"isSplitRequired\" />\n        <attr name=\"requiredSplitTypes\" />\n        <attr name=\"splitTypes\" />\n    </declare-styleable>\n\n    <!-- The <code>application</code> tag describes application-level components\n         contained in the package, as well as general application\n         attributes.  Many of the attributes you can supply here (such\n         as theme, label, icon, permission, process, taskAffinity,\n         and allowTaskReparenting) serve\n         as default values for the corresponding attributes of components\n         declared inside of the application.\n\n         <p>Inside of this element you specify what the application contains,\n         using the elements {@link #AndroidManifestProvider provider},\n         {@link #AndroidManifestService service},\n         {@link #AndroidManifestReceiver receiver},\n         {@link #AndroidManifestActivity activity},\n         {@link #AndroidManifestActivityAlias activity-alias},\n         {@link #AndroidManifestUsesLibrary uses-library},\n         {@link #AndroidManifestUsesStaticLibrary uses-static-library}, and\n         {@link #AndroidManifestUsesPackage uses-package}.\n         The application tag\n         appears as a child of the root {@link #AndroidManifest manifest} tag in\n         an application's manifest file. -->\n    <declare-styleable name=\"AndroidManifestApplication\" parent=\"AndroidManifest\">\n        <!-- The (optional) fully-qualified name for a subclass of\n             {@link android.app.Application} that the system instantiates before\n             any other class when an app's process starts. Most applications\n             don't need this attribute. If it's not specified, the system\n             instantiates the base Application class instead.-->\n        <attr name=\"name\" />\n        <attr name=\"theme\" />\n        <attr name=\"label\" />\n        <attr name=\"icon\" />\n        <attr name=\"roundIcon\" />\n        <attr name=\"banner\" />\n        <attr name=\"logo\" />\n        <attr name=\"description\" />\n        <attr name=\"permission\" />\n        <attr name=\"process\" />\n        <attr name=\"taskAffinity\" />\n        <attr name=\"allowTaskReparenting\" />\n        <!-- Indicate whether this application contains code.  If set to false,\n             there is no code associated with it and thus the system will not\n             try to load its code when launching components.  The default is true\n             for normal behavior. -->\n        <attr name=\"hasCode\" format=\"boolean\" />\n        <!-- Specifies if activities can be launched on top of this application by activities from\n             other applications in the same task. If set to false, activity launches which would\n             replace this application with another when in the user's view will be blocked.\n             The default is true. -->\n        <!-- @FlaggedApi(\"android.security.asm_restrictions_enabled\") -->\n        <attr name=\"allowCrossUidActivitySwitchFromBelow\" format=\"boolean\" />\n        <attr name=\"persistent\" />\n        <attr name=\"persistentWhenFeatureAvailable\" />\n        <attr name=\"requiredForAllUsers\" />\n        <!-- Specify whether the components in this application are enabled or not (that is, can be\n             instantiated by the system).\n             If \"false\", it overrides any component specific values (a value of \"true\" will not\n             override the component specific values). -->\n        <attr name=\"enabled\" />\n        <attr name=\"debuggable\" />\n        <attr name=\"vmSafeMode\" />\n        <attr name=\"hardwareAccelerated\" />\n        <!-- Name of activity to be launched for managing the application's space on the device. -->\n        <attr name=\"manageSpaceActivity\" />\n        <attr name=\"allowClearUserData\" />\n        <attr name=\"testOnly\" />\n        <attr name=\"backupAgent\" />\n        <attr name=\"allowBackup\" />\n        <attr name=\"fullBackupOnly\" />\n        <attr name=\"fullBackupContent\" />\n        <attr name=\"killAfterRestore\" />\n        <attr name=\"restoreNeedsApplication\" />\n        <attr name=\"restoreAnyVersion\" />\n        <attr name=\"backupInForeground\" />\n        <!-- Request that your application's processes be created with\n             a large Dalvik heap.  This applies to <em>all</em> processes\n             created for the application.  It only applies to the first\n             application loaded into a process; if using a sharedUserId\n             to allow multiple applications to use a process, they all must\n             use this option consistently or will get unpredictable results. -->\n        <attr name=\"largeHeap\" format=\"boolean\" />\n        <!-- Declare that this application can't participate in the normal\n             state save/restore mechanism.  Since it is not able to save and\n             restore its state on demand,\n             it can not participate in the normal activity lifecycle.  It will\n             not be killed while in the background; the user must explicitly\n             quit it.  Only one such app can be running at a time; if the user\n             tries to launch a second such app, they will be prompted\n             to quit the first before doing so.  While the\n             application is running, the user will be informed of this. -->\n        <attr name=\"cantSaveState\" format=\"boolean\" />\n        <attr name=\"uiOptions\" />\n        <!-- Declare that your application will be able to deal with RTL (right to left) layouts.\n             The default value is false. -->\n        <attr name=\"supportsRtl\" format=\"boolean\" />\n        <!-- Declare that this application requires access to restricted accounts of a certain\n             type. The default value is null and restricted accounts won\\'t be visible to this\n             application. The type should correspond to the account authenticator type, such as\n             \"com.google\". -->\n        <attr name=\"restrictedAccountType\" format=\"string\"/>\n        <!-- Declare that this application requires an account of a certain\n             type. The default value is null and indicates that the application can work without\n             any accounts. The type should correspond to the account authenticator type, such as\n             \"com.google\". -->\n        <attr name=\"requiredAccountType\" format=\"string\"/>\n        <!-- @deprecated replaced by setting appCategory attribute to \"game\" -->\n        <attr name=\"isGame\" />\n        <!-- Declare that this application may use cleartext traffic, such as HTTP rather than\n             HTTPS; WebSockets rather than WebSockets Secure; XMPP, IMAP, SMTP without STARTTLS or\n             TLS). Defaults to true. If set to false {@code false}, the application declares that it\n             does not intend to use cleartext network traffic, in which case platform components\n             (e.g. HTTP stacks, {@code DownloadManager}, {@code MediaPlayer}) will refuse\n             applications's requests to use cleartext traffic. Third-party libraries are encouraged\n             to honor this flag as well. -->\n        <attr name=\"usesCleartextTraffic\" />\n        <attr name=\"multiArch\" />\n        <attr name=\"useEmbeddedDex\" />\n        <attr name=\"extractNativeLibs\" />\n        <attr name=\"defaultToDeviceProtectedStorage\" format=\"boolean\" />\n        <attr name=\"directBootAware\" />\n        <attr name=\"resizeableActivity\" />\n        <attr name=\"maxAspectRatio\" />\n        <attr name=\"minAspectRatio\" />\n        <attr name=\"networkSecurityConfig\" />\n        <attr name=\"localeConfig\" />\n        <!-- Declare the category of this app. Categories are used to cluster multiple apps\n             together into meaningful groups, such as when summarizing battery, network, or\n             disk usage. Apps should only define this value when they fit well into one of\n             the specific categories. -->\n        <attr name=\"appCategory\">\n            <!-- Apps which are primarily games. -->\n            <enum name=\"game\" value=\"0\" />\n            <!-- Apps which primarily work with audio or music, such as music players. -->\n            <enum name=\"audio\" value=\"1\" />\n            <!-- Apps which primarily work with video or movies, such as streaming video apps. -->\n            <enum name=\"video\" value=\"2\" />\n            <!-- Apps which primarily work with images or photos, such as camera or gallery apps. -->\n            <enum name=\"image\" value=\"3\" />\n            <!-- Apps which are primarily social apps, such as messaging, communication, email, or social network apps. -->\n            <enum name=\"social\" value=\"4\" />\n            <!-- Apps which are primarily news apps, such as newspapers, magazines, or sports apps. -->\n            <enum name=\"news\" value=\"5\" />\n            <!-- Apps which are primarily maps apps, such as navigation apps. -->\n            <enum name=\"maps\" value=\"6\" />\n            <!-- Apps which are primarily productivity apps, such as cloud storage or workplace apps. -->\n            <enum name=\"productivity\" value=\"7\" />\n            <!-- Apps which are primarily accessibility apps, such as screen-readers. -->\n            <enum name=\"accessibility\" value=\"8\" />\n        </attr>\n\n        <!-- Declares the kind of classloader this application's classes must be loaded with -->\n        <attr name=\"classLoader\" />\n\n        <attr name=\"appComponentFactory\" />\n\n        <!-- Declares that this application should be invoked without non-SDK API enforcement -->\n        <attr name=\"usesNonSdkApi\" />\n\n        <!-- If {@code true} the user is prompted to keep the app's data on uninstall -->\n        <attr name=\"hasFragileUserData\" format=\"boolean\"/>\n\n        <attr name=\"zygotePreloadName\" />\n\n        <!-- If {@code true} the system will clear app's data if a restore operation fails.\n             This flag is turned on by default. <em>This attribute is usable only by system apps.\n             </em> -->\n        <attr name=\"allowClearUserDataOnFailedRestore\" format=\"boolean\"/>\n        <!-- If {@code true} the app's non sensitive audio can be captured by other apps with\n             {@link android.media.AudioPlaybackCaptureConfiguration} and a\n             {@link android.media.projection.MediaProjection}.\n\n             If {@code false} the audio played by the application will never be captured by non\n             system apps. It is equivalent to limiting\n             {@link android.media.AudioManager#setAllowedCapturePolicy(int)} to\n             {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM}.\n\n             <p>\n             Non sensitive audio is defined as audio whose {@code AttributeUsage} is\n             {@code USAGE_UNKNOWN}), {@code USAGE_MEDIA}) or {@code USAGE_GAME}).\n             All other usages like {@code USAGE_VOICE_COMMUNICATION} will not be captured.\n\n             <p>\n             The default value is:\n                 - {@code true} for apps with targetSdkVersion >= 29 (Q).\n                 - {@code false} for apps with targetSdkVersion < 29.\n\n             <p>\n             See {@link android.media.AudioPlaybackCaptureConfiguration} for more detail.\n             -->\n        <attr name=\"allowAudioPlaybackCapture\" format=\"boolean\" />\n        <!-- If {@code true} this app would like to run under the legacy storage\n             model. Note that this may not always be respected due to policy or\n             backwards compatibility reasons.\n\n             <p>Apps not requesting legacy storage can continue to discover and\n             read media belonging to other apps via {@code MediaStore}.\n             <p>\n             The default value is:\n                 - {@code false} for apps with targetSdkVersion >= 29 (Q).\n                 - {@code true} for apps with targetSdkVersion < 29.\n             -->\n        <attr name=\"requestLegacyExternalStorage\" format=\"boolean\" />\n\n        <!-- If {@code true} this app would like to preserve the legacy storage\n             model from a previously installed version. Note that this may not always be\n             respected due to policy or backwards compatibility reasons.\n\n             <p>This has no effect on the first install of an app on a device.\n             For an updating app, setting this to {@code true} will preserve the legacy behaviour\n             configured by the {@code requestLegacyExternalStorage} flag. If on an update, this\n             flag is set to {@code false} then the legacy access is not preserved, such an app can\n             only have legacy access with the {@code requestLegacyExternalStorage} flag.\n             <p>\n\n             The default value is {@code false}.\n             -->\n        <attr name=\"preserveLegacyExternalStorage\" format=\"boolean\" />\n\n        <!-- If {@code true} this app would like raw external storage access.\n\n        <p> This flag can only be used by apps holding\n        <ul>\n        <li>{@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} permission or\n        <li>{@link android.app.role}#SYSTEM_GALLERY role.\n        </ul>\n        <p> When the flag is set, all file path access on external storage will bypass database\n        operations that update MediaStore collection. Raw external storage access as a side effect\n        can improve performance of bulk file path operations but can cause unexpected behavior in\n        apps due to inconsistencies in MediaStore collection and lower file system.\n        When the flag is set, app should scan the file after file path operations to ensure\n        consistency of MediaStore collection.\n        <p> The flag can be set to false if the app doesn't do many bulk file path operations or if\n        app prefers the system to ensure the consistency of the MediaStore collection for file path\n        operations without scanning the file.\n\n        <p> The default value is {@code true} if\n        <ul>\n        <li>app has {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} permission and\n        targets targetSDK<=30.\n        <li>app has {@link android.app.role}#SYSTEM_GALLERY role and targetSDK<=29\n        </ul>\n        {@code false} otherwise.\n        -->\n        <attr name=\"requestRawExternalStorageAccess\" format=\"boolean\" />\n\n        <!-- If {@code true} this app declares that it should be visible to all other apps on\n             device, regardless of what they declare via the {@code queries} tags in their\n             manifest.\n\n             The default value is {@code false}. -->\n        <attr name=\"forceQueryable\" format=\"boolean\" />\n\n        <!-- If {@code true} indicates that this application is capable of presenting a unified\n             interface representing multiple profiles.\n\n             The default value is {@code false}. -->\n        <attr name=\"crossProfile\" format=\"boolean\" />\n\n        <!-- If {@code true} this app will receive tagged pointers to native heap allocations\n             from functions like malloc() on compatible devices. Note that this may not always\n             be respected due to policy or backwards compatibility reasons. See the\n             <a href=\"https://source.android.com/devices/tech/debug/tagged-pointers\">Tagged Pointers</a>\n             document for more information on this feature.\n\n             The default value is {@code true}. -->\n        <attr name=\"allowNativeHeapPointerTagging\" format=\"boolean\" />\n\n        <attr name=\"gwpAsanMode\" />\n\n        <attr name=\"memtagMode\" />\n\n        <!-- @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->\n        <attr name=\"pageSizeCompat\" />\n\n        <!-- If {@code true} enables automatic zero initialization of all native heap\n             allocations. -->\n        <attr name=\"nativeHeapZeroInitialized\" format=\"boolean\" />\n\n        <!-- @hide no longer used, kept to preserve padding -->\n        <attr name=\"allowAutoRevokePermissionsExemption\" format=\"boolean\" />\n\n        <!-- No longer used. Declaring this does nothing -->\n        <attr name=\"autoRevokePermissions\">\n            <!-- No longer used -->\n            <enum name=\"allowed\" value=\"0\" />\n            <!-- No longer used -->\n            <enum name=\"discouraged\" value=\"1\" />\n            <!-- No longer used -->\n            <enum name=\"disallowed\" value=\"2\" />\n        </attr>\n\n        <!-- Declare the policy to deal with user data when rollback is committed. -->\n        <attr name=\"rollbackDataPolicy\">\n            <!-- User data will be restored during rollback. -->\n            <enum name=\"restore\" value=\"0\" />\n            <!-- User data will be wiped out during rollback. -->\n            <enum name=\"wipe\" value=\"1\" />\n            <!-- User data will remain unchanged during rollback. -->\n            <enum name=\"retain\" value=\"2\" />\n        </attr>\n\n        <!-- Applications can set this attribute to an xml resource within their app where they\n         specified the rules determining which files and directories can be copied from the device\n         as part of backup or transfer operations.\n\n         See the <a href=\"{@docRoot}about/versions/12/backup-restore\">Changes in backup and restore</a>\n         document for the format of the XML file.-->\n        <attr name=\"dataExtractionRules\" format=\"reference\"/>\n\n        <!-- @hide Request exemption from the foreground service restrictions introduced in S\n        (https://developer.android.com/about/versions/12/foreground-services)\n        Note the framework <b>ignores</b> this attribute at this time. Once apps target S or above,\n        there's no way to be exempted (without using a privileged permission).\n        -->\n        <attr name=\"requestForegroundServiceExemption\" format=\"boolean\" />\n\n        <!-- Whether attributions provided are meant to be user-visible. -->\n        <attr name=\"attributionsAreUserVisible\" format=\"boolean\" />\n\n        <!-- Specifies whether enabled settings of components in the application should be\n             reset to {@link android.content.pm.PackageManager#COMPONENT_ENABLED_STATE_DEFAULT}\n             when the application's user data is cleared. The default value is false.\n        -->\n        <attr name=\"resetEnabledSettingsOnAppDataCleared\" format=\"boolean\" />\n        <attr name=\"knownActivityEmbeddingCerts\" />\n\n        <!-- If false, {@link android.view.KeyEvent#KEYCODE_BACK KEYCODE_BACK} and\n             {@link android.app.Activity#onBackPressed Activity.onBackPressed()}\n             and related event will be forwarded to the Activities and View, otherwise those events\n             will be replaced by a call to\n             {@link android.window.OnBackInvokedCallback#onBackInvoked\n             OnBackInvokedCallback.onBackInvoked()} on the focused window. -->\n        <attr name=\"enableOnBackInvokedCallback\" format=\"boolean\"/>\n\n        <attr name=\"intentMatchingFlags\"/>\n\n        <!-- Specifies the set of drawable resources that can be used in place\n             of an existing declared icon or banner for activities that appear\n             in the app launcher. The resource referenced must be an array of\n             drawable resources and can contain at most 500 items.\n             {@link android.content.pm.PackageManager#changeLauncherIconConfig}\n             @FlaggedApi(android.content.pm.Flags.FLAG_CHANGE_LAUNCHER_BADGING) -->\n        <attr name=\"alternateLauncherIcons\" format=\"reference\" />\n\n        <!-- Specifies the set of string resources that can be used in place\n             of an existing declared label for activities that appear\n             in the app launcher. The resource referenced must be an array of\n             string resources and can contain at most 500 items.\n             {@link android.content.pm.PackageManager#changeLauncherIconConfig}\n             @FlaggedApi(android.content.pm.Flags.FLAG_CHANGE_LAUNCHER_BADGING) -->\n        <attr name=\"alternateLauncherLabels\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- An attribution is a logical part of an app and is identified by a tag.\n    E.g. a photo sharing app might include a direct messaging component. To tag certain code as\n    belonging to an attribution, use a context created via\n    {@link android.content.Context#createAttributionContext(String)} for any interaction with the\n    system.\n\n    <p>This appears as a child tag of the root {@link #AndroidManifest manifest} tag.\n\n    <p>In case this attribution inherits from another attribution, this tag can contain one or\n    multiple {@link #AndroidManifestAttributionInheritFrom inherit-from} tags. -->\n    <declare-styleable name=\"AndroidManifestAttribution\" parent=\"AndroidManifest\">\n        <!-- Required identifier for a attribution. Can be passed to\n        {@link android.content.Context#createAttributionContext} to create a context tagged with\n        this attribution\n        -->\n        <attr name=\"tag\" format=\"string\" />\n        <!-- Required user visible label for a attribution. -->\n        <attr name=\"label\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- Declares previously declared attributions this attribution inherits from. -->\n    <declare-styleable name=\"AndroidManifestAttributionInheritFrom\"\n                       parent=\"AndroidManifestAttribution\">\n        <!-- Identifier of the attribution this attribution inherits from -->\n        <attr name=\"tag\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- The <code>permission</code> tag declares a security permission that can be\n         used to control access from other packages to specific components or\n         features in your package (or other packages).  See the\n         <a href=\"{@docRoot}guide/topics/security/security.html\">Security and Permissions</a>\n         document for more information on permissions.\n\n         <p>This appears as a child tag of the root\n         {@link #AndroidManifest manifest} tag. -->\n    <declare-styleable name=\"AndroidManifestPermission\" parent=\"AndroidManifest\">\n        <!-- Required public name of the permission, which other components and\n        packages will use when referring to this permission.  This is a string using\n        Java-style scoping to ensure it is unique.  The prefix will often\n        be the same as our overall package name, for example\n        \"com.mycompany.android.myapp.SomePermission\". -->\n        <attr name=\"name\" />\n        <attr name=\"label\" />\n        <attr name=\"icon\" />\n        <attr name=\"roundIcon\" />\n        <attr name=\"banner\" />\n        <attr name=\"logo\" />\n        <attr name=\"permissionGroup\" />\n        <attr name=\"backgroundPermission\" format=\"string\"/>\n        <attr name=\"description\" />\n        <attr name=\"request\" />\n        <attr name=\"protectionLevel\" />\n        <attr name=\"permissionFlags\" />\n        <attr name=\"knownCerts\" />\n        <!-- Optional: specify the maximum version of the Android OS for which the\n             application wishes to create the permission.  When running on a version\n             of Android higher than the number given here, the permission will not\n             be created.  -->\n        <attr name=\"maxSdkVersion\" />\n    </declare-styleable>\n\n    <!-- The <code>permission-group</code> tag declares a logical grouping of\n         related permissions.\n\n         <p>Note that this tag does not declare a permission itself, only\n         a namespace in which further permissions can be placed.  See\n         the {@link #AndroidManifestPermission &lt;permission&gt;} tag for\n         more information.\n\n         <p>This appears as a child tag of the root\n         {@link #AndroidManifest manifest} tag. -->\n    <declare-styleable name=\"AndroidManifestPermissionGroup\" parent=\"AndroidManifest\">\n        <!-- Required public name of the permission group, permissions will use\n        to specify the group they are in.  This is a string using\n        Java-style scoping to ensure it is unique.  The prefix will often\n        be the same as our overall package name, for example\n        \"com.mycompany.android.myapp.SomePermission\". -->\n        <attr name=\"name\" />\n        <attr name=\"label\" />\n        <attr name=\"icon\" />\n        <attr name=\"roundIcon\" />\n        <attr name=\"banner\" />\n        <attr name=\"logo\" />\n        <attr name=\"description\" />\n        <attr name=\"request\" format=\"string\"/>\n        <attr name=\"requestDetail\" format=\"string\"/>\n        <attr name=\"backgroundRequest\" format=\"string\"/>\n        <attr name=\"backgroundRequestDetail\" format=\"string\"/>\n        <attr name=\"permissionGroupFlags\" />\n        <attr name=\"priority\" />\n    </declare-styleable>\n\n    <!-- The <code>permission-tree</code> tag declares the base of a tree of\n         permission values: it declares that this package has ownership of\n         the given permission name, as well as all names underneath it\n         (separated by '.').  This allows you to use the\n         {@link android.content.pm.PackageManager#addPermission\n         PackageManager.addPermission()} method to dynamically add new\n         permissions under this tree.\n\n         <p>Note that this tag does not declare a permission itself, only\n         a namespace in which further permissions can be placed.  See\n         the {@link #AndroidManifestPermission &lt;permission&gt;} tag for\n         more information.\n\n         <p>This appears as a child tag of the root\n         {@link #AndroidManifest manifest} tag. -->\n    <declare-styleable name=\"AndroidManifestPermissionTree\" parent=\"AndroidManifest\">\n        <!-- Required public name of the permission tree, which is the base name\n        of all permissions under it.  This is a string using\n        Java-style scoping to ensure it is unique.  The prefix will often\n        be the same as our overall package name, for example\n        \"com.mycompany.android.myapp.SomePermission\".  A permission tree name\n        must have more than two segments in its path; that is,\n        \"com.me.foo\" is okay, but not \"com.me\" or \"com\". -->\n        <attr name=\"name\" />\n        <attr name=\"label\" />\n        <attr name=\"icon\" />\n        <attr name=\"roundIcon\" />\n        <attr name=\"banner\" />\n        <attr name=\"logo\" />\n    </declare-styleable>\n\n    <!-- The <code>uses-permission</code> tag requests a\n         {@link #AndroidManifestPermission &lt;permission&gt;} that the containing\n         package must be granted in order for it to operate correctly. For runtime\n         permissions, i.e. ones with <code>dangerous</code> protection level, on a\n         platform that supports runtime permissions, the permission will not be\n         granted until the app explicitly requests it at runtime and the user approves\n         the grant. You cannot request at runtime permissions that are not declared\n         as used in the manifest. See the\n         <a href=\"{@docRoot}guide/topics/security/security.html\">Security and Permissions</a>\n         document for more information on permissions.  Also available is a\n         {@link android.Manifest.permission list of permissions} included\n         with the base platform.\n\n         <p>This appears as a child tag of the root\n         {@link #AndroidManifest manifest} tag. -->\n    <declare-styleable name=\"AndroidManifestUsesPermission\" parent=\"AndroidManifest\">\n        <!-- Required name of the permission you use, as published with the\n        corresponding name attribute of a\n        {@link android.R.styleable#AndroidManifestPermission &lt;permission&gt;}\n        tag; often this is one of the {@link android.Manifest.permission standard\n        system permissions}. -->\n        <attr name=\"name\" />\n        <!-- Optional: specify the minimum version of the Android OS for which the\n             application wishes to request the permission.  When running on a version\n             of Android lower than the number given here, the permission will not\n             be requested. -->\n        <attr name=\"minSdkVersion\" format=\"integer|string\" />\n        <!-- Optional: specify the maximum version of the Android OS for which the\n             application wishes to request the permission.  When running on a version\n             of Android higher than the number given here, the permission will not\n             be requested.  -->\n        <attr name=\"maxSdkVersion\" format=\"integer\" />\n        <!-- Optional: the system must support this feature for the permission to be\n        requested.  If it doesn't support the feature, it will be as if the manifest didn't\n        request it at all. -->\n        <attr name=\"requiredFeature\" format=\"string\" />\n        <!-- Optional: the system must NOT support this feature for the permission to be\n        requested.  If it does support the feature, it will be as if the manifest didn't\n        request it at all. -->\n        <attr name=\"requiredNotFeature\" format=\"string\" />\n        <!-- Optional: set of flags that should apply to this permission request. Note that\n             these flags start at 0x4 to match PackageInfo.requestedPermissionsFlags. -->\n        <attr name=\"usesPermissionFlags\">\n            <!-- Strong assertion by a developer that they will never use this\n                 permission to derive the physical location of the device, even\n                 when the app has been granted the ACCESS_FINE_LOCATION and/or\n                 ACCESS_COARSE_LOCATION permissions. -->\n            <flag name=\"neverForLocation\" value=\"0x00010000\" />\n        </attr>\n    </declare-styleable>\n\n    <!-- <code>required-feature</code> and <code>required-not-feature</code> elements inside\n         <code>uses-permission<code/> can be used to request the permission based on the fact\n         whether the system supports or does not support certain features.\n         If multiple <code>required-feature</code> and/or <code>required-not-feature</code> elements\n         are present, the permission will be “requested” only if the system supports all of the\n         listed \"required-features\" and does not support any of the \"required-not-features\".\n         -->\n    <declare-styleable name=\"AndroidManifestRequiredFeature\">\n        <!-- The name of the feature. -->\n        <attr name=\"name\" />\n    </declare-styleable>\n    <declare-styleable name=\"AndroidManifestRequiredNotFeature\">\n        <!-- The name of the feature. -->\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- The <code>uses-configuration</code> tag specifies\n         a specific hardware configuration value used by the application.\n         For example an application might specify that it requires\n         a physical keyboard or a particular navigation method like\n         trackball. Multiple such attribute values can be specified by the\n         application.\n\n         <p>This appears as a child tag of the root\n         {@link #AndroidManifest manifest} tag.\n\n         @deprecated Use <code>feature-group</code> instead.-->\n    <declare-styleable name=\"AndroidManifestUsesConfiguration\" parent=\"AndroidManifest\">\n        <!-- The type of touch screen used by an application. -->\n        <attr name=\"reqTouchScreen\" />\n        <attr name=\"reqKeyboardType\" />\n        <attr name=\"reqHardKeyboard\" />\n        <attr name=\"reqNavigation\" />\n        <attr name=\"reqFiveWayNav\" />\n    </declare-styleable>\n\n    <!-- The <code>uses-feature</code> tag specifies a specific device\n         hardware or software feature used by the application. For\n         example an application might specify that it requires\n         a camera. Multiple attribute values can be specified by the\n         application.\n\n         <p>This appears as a child tag of the root\n         {@link #AndroidManifest manifest} tag. -->\n    <declare-styleable name=\"AndroidManifestUsesFeature\" parent=\"AndroidManifest\">\n        <!-- The name of the feature that is being used. -->\n        <attr name=\"name\" />\n        <!-- The version of the feature that is being used. -->\n        <attr name=\"version\" format=\"integer\" />\n        <!-- The GLES driver version number needed by an application.\n             The higher 16 bits represent the major number and the lower 16 bits\n             represent the minor number. For example for GL 1.2 referring to\n             0x00000102, the actual value should be set as 0x00010002. -->\n        <attr name=\"glEsVersion\" format=\"integer\" />\n        <!--  Specify whether this feature is required for the application.\n              The default is true, meaning the application requires the\n              feature, and does not want to be installed on devices that\n              don't support it.  If you set this to false, then this will\n              not impose a restriction on where the application can be\n              installed. -->\n        <attr name=\"required\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- The <code>feature-group</code> tag specifies\n         a set of one or more <code>uses-feature</code> elements that\n         the application can utilize. An application uses multiple\n         <code>feature-group</code> sets to indicate that it can support\n         different combinations of features.\n\n         <p>This appears as a child tag of the root\n         {@link #AndroidManifest manifest} tag. -->\n    <declare-styleable name=\"AndroidManifestFeatureGroup\">\n        <!-- The human-readable name of the feature group. -->\n        <attr name=\"label\" />\n    </declare-styleable>\n\n    <!-- The <code>uses-sdk</code> tag describes the SDK features that the\n         containing package must be running on to operate correctly.\n\n         <p>This appears as a child tag of the root\n         {@link #AndroidManifest manifest} tag. -->\n    <declare-styleable name=\"AndroidManifestUsesSdk\" parent=\"AndroidManifest\">\n        <!-- This is the minimum SDK version number that the application\n             requires.  This number is an abstract integer, from the list\n             in {@link android.os.Build.VERSION_CODES}  If\n             not supplied, the application will work on any SDK.  This\n             may also be string (such as \"Donut\") if the application was built\n             against a development branch, in which case it will only work against\n             the development builds. -->\n        <attr name=\"minSdkVersion\" format=\"integer|string\" />\n        <!-- This is the SDK version number that the application is targeting.\n             It is able to run on older versions (down to minSdkVersion), but\n             was explicitly tested to work with the version specified here.\n             Specifying this version allows the platform to disable compatibility\n             code that are not required or enable newer features that are not\n             available to older applications.  This may also be a string\n             (such as \"Donut\") if this is built against a development\n             branch, in which case minSdkVersion is also forced to be that\n             string. -->\n        <attr name=\"targetSdkVersion\" format=\"integer|string\" />\n        <!-- This is the maximum SDK version number that an application works\n             on.  You can use this to ensure your application is filtered out\n             of later versions of the platform when you know you have\n             incompatibility with them. -->\n        <attr name=\"maxSdkVersion\" />\n    </declare-styleable>\n\n    <!-- The <code>extension-sdk</code> tag is a child of the <uses-sdk> tag,\n         and specifies required extension sdk features. -->\n    <declare-styleable name=\"AndroidManifestExtensionSdk\">\n        <!-- The extension SDK version that this tag refers to. -->\n        <attr name=\"sdkVersion\" format=\"integer\" />\n        <!-- The minimum version of the extension SDK this application requires.-->\n        <attr name=\"minExtensionVersion\" format=\"integer\" />\n    </declare-styleable>\n\n    <!-- The <code>library</code> tag declares that this apk is providing itself\n         as a shared library for other applications to use.  It can only be used\n         with apks that are built in to the system image.  Other apks can link to\n         it with the {@link #AndroidManifestUsesLibrary uses-library} tag.\n\n         <p>This appears as a child tag of the\n         {@link #AndroidManifestApplication application} tag. -->\n    <declare-styleable name=\"AndroidManifestLibrary\" parent=\"AndroidManifest\">\n        <!-- Required public name of the library, which other components and\n        packages will use when referring to this library.  This is a string using\n        Java-style scoping to ensure it is unique.  The name should typically\n        be the same as the apk's package name. -->\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"AndroidManifestQueries\" parent=\"AndroidManifest\" />\n    <declare-styleable name=\"AndroidManifestQueriesPackage\" parent=\"AndroidManifestQueries\">\n        <attr name=\"name\" />\n    </declare-styleable>\n    <declare-styleable name=\"AndroidManifestQueriesIntent\" parent=\"AndroidManifestQueries\" />\n    <declare-styleable name=\"AndroidManifestQueriesProvider\" parent=\"AndroidManifestQueries\" >\n        <attr name=\"authorities\" />\n    </declare-styleable>\n\n    <!-- The <code>sdk-library</code> tag declares that this apk is providing itself\n    as an SDK library for other applications to use. Any app can declare an SDK library and there\n    can be only one SDK library per package. These SDK libraries are updatable, multiple major\n    versions can be installed at the same time, and an app depends on a specific version.\n    Other apks can link to it with the {@link #AndroidManifestUsesSdkLibrary uses-sdk-library} tag.\n\n    <p>This appears as a child tag of the {@link #AndroidManifestApplication application} tag. -->\n    <declare-styleable name=\"AndroidManifestSdkLibrary\" parent=\"AndroidManifestApplication\">\n        <!-- Required public name of the SDK library, which other components and packages will use\n        when referring to this SDK library. This is a string using Java-style scoping to ensure\n        it is unique.\n        Both name and version should typically form the apk's package name: name_versionMajor. -->\n        <attr name=\"name\" />\n        <!-- Required major version of the SDK library. -->\n        <attr name=\"versionMajor\" format=\"integer\" />\n    </declare-styleable>\n\n\n    <!-- The <code>uses-sdk-library</code> specifies a shared <strong>SDK</strong> library that this\n    package requires to be present on the device.\n\n    <p>This appears as a child tag of the {@link #AndroidManifestApplication application} tag. -->\n    <declare-styleable name=\"AndroidManifestUsesSdkLibrary\" parent=\"AndroidManifestApplication\">\n        <!-- Required name of the SDK library you use. -->\n        <attr name=\"name\" />\n        <!-- Specify which major version of the SDK library you use. -->\n        <attr name=\"versionMajor\" format=\"integer\" />\n        <!-- The SHA-256 digest of the SDK library signing certificate. -->\n        <attr name=\"certDigest\" format=\"string\" />\n        <!-- Specify whether the SDK is optional. The default is false, false means app can be\n        installed even if the SDK library doesn't exist, and the SDK library can be uninstalled\n        when the app is still installed. -->\n        <attr name=\"optional\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- The <code>static-library</code> tag declares that this apk is providing itself\n       as a static shared library for other applications to use. Any app can declare such\n       a library and there can be only one static shared library per package. These libraries\n       are updatable, multiple versions can be installed at the same time, and an app links\n       against a specific version simulating static linking while allowing code sharing.\n       Other apks can link to it with the {@link #AndroidManifestUsesLibrary uses-static-library}\n       tag.\n\n     <p>This appears as a child tag of the\n     {@link #AndroidManifestApplication application} tag. -->\n    <declare-styleable name=\"AndroidManifestStaticLibrary\" parent=\"AndroidManifestApplication\">\n        <!-- Required public name of the library, which other components and\n        packages will use when referring to this library.  This is a string using\n        Java-style scoping to ensure it is unique.  The name should typically\n        be the same as the apk's package name. -->\n        <attr name=\"name\" />\n        <!-- Required specific library version. -->\n        <attr name=\"version\" />\n        <!-- Required specific library major version code.  This matches\n             android:versionCodeMajor of the library. -->\n        <!-- Required specific library version. -->\n        <attr name=\"versionMajor\" format=\"integer\" />\n    </declare-styleable>\n\n    <!-- The <code>uses-libraries</code> specifies a shared library that this\n         package requires to be linked against.  Specifying this flag tells the\n         system to include this library's code in your class loader.\n\n         <p>This appears as a child tag of the\n         {@link #AndroidManifestApplication application} tag. -->\n    <declare-styleable name=\"AndroidManifestUsesLibrary\" parent=\"AndroidManifestApplication\">\n        <!-- Required name of the library you use. -->\n        <attr name=\"name\" />\n        <!--  Specify whether this library is required for the application.\n              The default is true, meaning the application requires the\n              library, and does not want to be installed on devices that\n              don't support it.  If you set this to false, then this will\n              allow the application to be installed even if the library\n              doesn't exist, and you will need to check for its presence\n              dynamically at runtime. -->\n        <attr name=\"required\" />\n    </declare-styleable>\n\n    <!-- The <code>uses-native-library</code> specifies a native shared library that this\n         package requires to be linked against.  Specifying this flag tells the\n         system to make the native library to be available to your app.\n\n         <p>On devices running R or lower, this is ignored and the app has access to all\n         the public native shared libraries that are exported from the platform. This is\n         also ignored if the app is targeting R or lower.\n\n         <p>This appears as a child tag of the\n         {@link #AndroidManifestApplication application} tag. -->\n    <declare-styleable name=\"AndroidManifestUsesNativeLibrary\" parent=\"AndroidManifestApplication\">\n        <!-- Required name of the library you use. -->\n        <attr name=\"name\" />\n        <!--  Specify whether this native library is required for the application.\n              The default is true, meaning the application requires the\n              library, and does not want to be installed on devices that\n              don't support it. If you set this to false, then this will\n              allow the application to be installed even if the library\n              doesn't exist, and you will need to check for its presence\n              dynamically at runtime. -->\n        <attr name=\"required\" />\n    </declare-styleable>\n\n    <!-- The <code>uses-static-library</code> specifies a shared <strong>static</strong>\n         library that this package requires to be statically linked against. Specifying\n         this tag tells the system to include this library's code in your class loader.\n         Depending on a static shared library is equivalent to statically linking with\n         the library at build time while it offers apps to share code defined in such\n         libraries. Hence, static libraries are strictly required.\n\n         <p>On devices running O MR1 or higher, if the library is singed with multiple\n         signing certificates you must to specify the SHA-256 hashes of the additional\n         certificates via adding\n         {@link #AndroidManifestAdditionalCertificate additional-certificate} tags.\n\n         <p>This appears as a child tag of the\n         {@link #AndroidManifestApplication application} tag. -->\n    <declare-styleable name=\"AndroidManifestUsesStaticLibrary\" parent=\"AndroidManifestApplication\">\n        <!-- Required name of the library you use. -->\n        <attr name=\"name\" />\n        <!-- Specify which version of the shared library should be statically linked. -->\n        <attr name=\"version\" />\n        <!-- The SHA-256 digest of the library signing certificate. -->\n        <attr name=\"certDigest\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- The <code>additional-certificate</code> specifies the SHA-256 digest of a static\n         shared library's additional signing certificate. You need to use this tag if the\n         library is singed with more than one certificate.\n\n         <p>This appears as a child tag of the\n         {@link #AndroidManifestUsesStaticLibrary uses-static-library} or\n         {@link #AndroidManifestUsesPackage uses-package} tag. -->\n    <declare-styleable name=\"AndroidManifestAdditionalCertificate\" parent=\"AndroidManifestUsesStaticLibrary\">\n        <!-- The SHA-256 digest of the library signing certificate. -->\n        <attr name=\"certDigest\" />\n    </declare-styleable>\n\n    <!-- The <code>uses-package</code> specifies some kind of dependency on another\n         package.  It does not have any impact on the app's execution on the device,\n         but provides information about dependencies it has on other packages that need\n         to  be satisfied for it to run correctly.  That is, this is primarily for\n         installers to know what other apps need to be installed along with this one.\n\n         <p>This appears as a child tag of the\n         {@link #AndroidManifestApplication application} tag. -->\n    <declare-styleable name=\"AndroidManifestUsesPackage\" parent=\"AndroidManifestApplication\">\n        <!-- Required type of association with the package, for example \"android.package.ad_service\"\n             if it provides an advertising service.  This should use the standard scoped naming\n             convention as used for other things such as package names, based on the Java naming\n             convention. -->\n        <attr name=\"packageType\" format=\"string\" />\n        <!-- Required name of the package you use. -->\n        <attr name=\"name\" />\n        <!-- Optional minimum version of the package that satisfies the dependency. -->\n        <attr name=\"version\" />\n        <!-- Optional minimum major version of the package that satisfies the dependency. -->\n        <attr name=\"versionMajor\" format=\"integer\" />\n        <!-- Optional SHA-256 digest of the package signing certificate. -->\n        <attr name=\"certDigest\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- The <code>supports-screens</code> specifies the screen dimensions an\n         application supports.  By default a modern application supports all\n         screen sizes and must explicitly disable certain screen sizes here;\n         older applications are assumed to only support the traditional normal\n         (HVGA) screen size.  Note that screen size is a separate axis from\n         density, and is determined as the available pixels to an application\n         after density scaling has been applied.\n\n         <p>This appears as a child tag of the\n         {@link #AndroidManifest manifest} tag. -->\n    <declare-styleable name=\"AndroidManifestSupportsScreens\" parent=\"AndroidManifest\">\n        <!-- Starting with {@link android.os.Build.VERSION_CODES#HONEYCOMB_MR2},\n             this is the new way to specify the minimum screen size an application is\n             compatible with.  This attribute provides the required minimum\n             \"smallest screen width\" (as per the -swNNNdp resource configuration)\n             that the application can run on.  For example, a typical phone\n             screen is 320, a 7\" tablet 600, and a 10\" tablet 720.  If the\n             smallest screen width of the device is below the value supplied here,\n             then the application is considered incompatible with that device.\n             If not supplied, then any old smallScreens, normalScreens, largeScreens,\n             or xlargeScreens attributes will be used instead. -->\n        <attr name=\"requiresSmallestWidthDp\" format=\"integer\" />\n        <!-- Starting with {@link android.os.Build.VERSION_CODES#HONEYCOMB_MR2},\n             this is the new way to specify the largest screens an application is\n             compatible with.  This attribute provides the maximum\n             \"smallest screen width\" (as per the -swNNNdp resource configuration)\n             that the application is designed for.  If this value is smaller than\n             the \"smallest screen width\" of the device it is running on, the user\n             is offered to run it in a compatibility mode that emulates a\n             smaller screen and zooms it to fit the screen. Currently the compatibility mode only\n             emulates phone screens with a 320dp width, so compatibility mode is not applied if the\n             value for compatibleWidthLimitDp is larger than 320. -->\n        <attr name=\"compatibleWidthLimitDp\" format=\"integer\" />\n        <!-- Starting with {@link android.os.Build.VERSION_CODES#HONEYCOMB_MR2},\n             this is the new way to specify the screens an application is\n             compatible with.  This attribute provides the maximum\n             \"smallest screen width\" (as per the -swNNNdp resource configuration)\n             that the application can work well on.  If this value is smaller than\n             the \"smallest screen width\" of the device it is running on, the\n             application will be forced in to screen compatibility mode with\n             no way for the user to turn it off. Currently the compatibility mode only\n             emulates phone screens with a 320dp width, so compatibility mode is not applied if the\n             value for largestWidthLimitDp is larger than 320. -->\n        <attr name=\"largestWidthLimitDp\" format=\"integer\" />\n        <!-- Indicates whether the application supports smaller screen form-factors.\n             A small screen is defined as one with a smaller aspect ratio than\n             the traditional HVGA screen; that is, for a portrait screen, less\n             tall than an HVGA screen.  In practice, this means a QVGA low\n             density or VGA high density screen.  An application that does\n             not support small screens <em>will not be available</em> for\n             small screen devices, since there is little the platform can do\n             to make such an application work on a smaller screen. -->\n        <attr name=\"smallScreens\" format=\"boolean\" />\n        <!-- Indicates whether an application supports the normal screen\n             form-factors.  Traditionally this is an HVGA normal density\n             screen, but WQVGA low density and WVGA high density are also\n             considered to be normal.  This attribute is true by default,\n             and applications currently should leave it that way. -->\n        <attr name=\"normalScreens\" format=\"boolean\" />\n        <!-- Indicates whether the application supports larger screen form-factors.\n             A large screen is defined as a screen that is significantly larger\n             than a normal phone screen, and thus may require some special care\n             on the application's part to make good use of it.  An example would\n             be a VGA <em>normal density</em> screen, though even larger screens\n             are certainly possible.  An application that does not support\n             large screens will be placed as a postage stamp on such a\n             screen, so that it retains the dimensions it was originally\n             designed for. -->\n        <attr name=\"largeScreens\" format=\"boolean\" />\n        <!-- Indicates whether the application supports extra large screen form-factors. -->\n        <attr name=\"xlargeScreens\" format=\"boolean\" />\n        <!-- Indicates whether the application can resize itself to newer\n             screen sizes.  This is mostly used to distinguish between old\n             applications that may not be compatible with newly introduced\n             screen sizes and newer applications that should be; it will be\n             set for you automatically based on whether you are targeting\n             a newer platform that supports more screens. -->\n        <attr name=\"resizeable\" format=\"boolean\" />\n        <!-- Indicates whether the application can accommodate any screen\n             density. This is assumed true if targetSdkVersion is 4 or higher.\n             @deprecated Should always be true by default and not overridden.\n              -->\n        <attr name=\"anyDensity\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- Private tag to declare system protected broadcast actions.\n\n         <p>This appears as a child tag of the root\n         {@link #AndroidManifest manifest} tag. -->\n    <declare-styleable name=\"AndroidManifestProtectedBroadcast\" parent=\"AndroidManifest\">\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- Private tag to declare the original package name that this package is\n         based on.  Only used for packages installed in the system image.  If\n         given, and different than the actual package name, and the given\n         original package was previously installed on the device but the new\n         one was not, then the data for the old one will be renamed to be\n         for the new package.\n\n         <p>This appears as a child tag of the root\n         {@link #AndroidManifest manifest} tag. -->\n    <declare-styleable name=\"AndroidManifestOriginalPackage\" parent=\"AndroidManifest\">\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- Private tag to declare the package name that the permissions of this package\n         is based on.  Only used for packages installed in the system image.  If\n         given, the permissions from the other package will be propagated into the\n         new package.\n\n         <p>This appears as a child tag of the root\n         {@link #AndroidManifest manifest} tag. -->\n    <declare-styleable name=\"AndroidManifestAdoptPermissions\" parent=\"AndroidManifest\">\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- The <code>processes</code> tag specifies the processes the application will run code in\n         and optionally characteristics of those processes.  This tag is optional; if not\n         specified, components will simply run in the processes they specify.  If supplied,\n         they can only specify processes that are enumerated here, and if they don't this\n         will be treated as a corrupt apk and result in an install failure.\n\n         <p>This appears as a child tag of the\n         {@link #AndroidManifestApplication application} tag. -->\n    <declare-styleable name=\"AndroidManifestProcesses\" parent=\"AndroidManifestApplication\">\n    </declare-styleable>\n\n    <!-- The <code>process</code> tag enumerates one of the available processes under its\n         containing <code>processes</code> tag.\n\n         <p>This appears as a child tag of the\n         {@link #AndroidManifestProcesses processes} tag. -->\n    <declare-styleable name=\"AndroidManifestProcess\" parent=\"AndroidManifestProcesses\">\n        <!-- Required name of the process that is allowed -->\n        <attr name=\"process\" />\n        <!-- custom Application class name. We use call it \"name\", not \"className\", to be\n             consistent with the Application tag. -->\n        <attr name=\"name\" />\n        <attr name=\"gwpAsanMode\" />\n        <attr name=\"memtagMode\" />\n        <attr name=\"nativeHeapZeroInitialized\" />\n        <attr name=\"useEmbeddedDex\" />\n    </declare-styleable>\n\n    <!-- The <code>deny-permission</code> tag specifies that a permission is to be denied\n         for a particular process (if specified under the\n         {@link #AndroidManifestProcess process} tag) or by default for all\n         processes {if specified under the\n         @link #AndroidManifestProcesses processes} tag).\n\n         <p>This appears as a child tag of the\n         {@link #AndroidManifestProcesses processes} and\n         {@link #AndroidManifestProcess process} tags. -->\n    <declare-styleable name=\"AndroidManifestDenyPermission\"\n            parent=\"AndroidManifestProcesses\">\n        <!-- Required name of the permission that is to be denied -->\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- The <code>allow-permission</code> tag specifies that a permission is to be allowed\n         for a particular process, when it was previously denied for all processes through\n         {@link #AndroidManifestDenyPermission deny-permission}\n\n         <p>This appears as a child tag of the\n         {@link #AndroidManifestProcesses processes} and\n         {@link #AndroidManifestProcess process} tags. -->\n    <declare-styleable name=\"AndroidManifestAllowPermission\"\n            parent=\"AndroidManifestProcesses\">\n        <!-- Required name of the permission that is to be allowed. -->\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- The <code>provider</code> tag declares a\n         {@link android.content.ContentProvider} class that is available\n         as part of the package's application components, supplying structured\n         access to data managed by the application.\n\n         <p>This appears as a child tag of the\n         {@link #AndroidManifestApplication application} tag. -->\n    <declare-styleable name=\"AndroidManifestProvider\" parent=\"AndroidManifestApplication\">\n        <!-- Required name of the class implementing the provider, deriving from\n            {@link android.content.ContentProvider}.  This is a fully\n            qualified class name (for example, com.mycompany.myapp.MyProvider); as a\n            short-hand if the first character of the class\n            is a period then it is appended to your package name. -->\n        <attr name=\"name\" />\n        <attr name=\"label\" />\n        <attr name=\"description\" />\n        <attr name=\"icon\" />\n        <attr name=\"roundIcon\" />\n        <attr name=\"banner\" />\n        <attr name=\"logo\" />\n        <attr name=\"process\" />\n        <attr name=\"authorities\" />\n        <attr name=\"syncable\" />\n        <attr name=\"readPermission\" />\n        <attr name=\"writePermission\" />\n        <attr name=\"grantUriPermissions\" />\n        <attr name=\"forceUriPermissions\" />\n        <attr name=\"permission\" />\n        <attr name=\"multiprocess\" />\n        <attr name=\"initOrder\" />\n        <!-- Specify whether this provider is enabled or not (that is, can be instantiated by the system).\n             It can also be specified for an application as a whole, in which case a value of \"false\"\n             will override any component specific values (a value of \"true\" will not override the\n             component specific values). -->\n        <attr name=\"enabled\" />\n        <attr name=\"exported\" />\n        <attr name=\"singleUser\" />\n        <attr name=\"directBootAware\" />\n        <attr name=\"visibleToInstantApps\" />\n        <!-- The code for this component is located in the given split.\n             <p>NOTE: This is only applicable to instant app. -->\n        <attr name=\"splitName\" />\n        <!-- Set of attribution tags that should be automatically applied to this component.\n             <p>\n             Each instance of this ContentProvider will be automatically configured with\n             Context.createAttributionContext() using the first attribution tag\n             contained here. -->\n        <attr name=\"attributionTags\" />\n        <attr name=\"systemUserOnly\" format=\"boolean\" />\n        <attr name=\"intentMatchingFlags\"/>\n    </declare-styleable>\n\n    <!-- Attributes that can be supplied in an AndroidManifest.xml\n         <code>grant-uri-permission</code> tag, a child of the\n         {@link #AndroidManifestProvider provider} tag, describing a specific\n         URI path that can be granted as a permission.  This tag can be\n         specified multiple time to supply multiple paths. If multiple\n         path matching attributes are supplied, they will be evaluated in the\n         following order with the first attribute being the only one honored:\n          <code>pathAdvancedPattern</code>, <code>pathPattern</code>,\n          <code>pathPrefix</code>, <code>pathSuffix</code>, <code>path</code>. -->\n    <declare-styleable name=\"AndroidManifestGrantUriPermission\"  parent=\"AndroidManifestProvider\">\n        <!-- Specify a URI path that must exactly match, as per\n             {@link android.os.PatternMatcher} with\n             {@link android.os.PatternMatcher#PATTERN_LITERAL}. -->\n        <attr name=\"path\" format=\"string\" />\n        <!-- Specify a URI path that must be a prefix to match, as per\n             {@link android.os.PatternMatcher} with\n             {@link android.os.PatternMatcher#PATTERN_PREFIX}. -->\n        <attr name=\"pathPrefix\" format=\"string\" />\n        <!-- Specify a URI path that matches a simple pattern, as per\n             {@link android.os.PatternMatcher} with\n             {@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}.\n             Note that because '\\' is used as an escape character when\n             reading the string from XML (before it is parsed as a pattern),\n             you will need to double-escape: for example a literal \"*\" would\n             be written as \"\\\\*\" and a literal \"\\\" would be written as\n             \"\\\\\\\\\".  This is basically the same as what you would need to\n             write if constructing the string in Java code. -->\n        <attr name=\"pathPattern\" format=\"string\" />\n        <!-- Specify a URI path that matches an advanced pattern, as per\n             {@link android.os.PatternMatcher} with\n             {@link android.os.PatternMatcher#PATTERN_ADVANCED_GLOB}.\n             Note that because '\\' is used as an escape character when\n             reading the string from XML (before it is parsed as a pattern),\n             you will need to double-escape: for example a literal \"*\" would\n             be written as \"\\\\*\" and a literal \"\\\" would be written as\n             \"\\\\\\\\\".  This is basically the same as what you would need to\n             write if constructing the string in Java code. -->\n        <attr name=\"pathAdvancedPattern\" format=\"string\"/>\n        <!-- Specify a URI path that must be a suffix to match, as per\n             {@link android.os.PatternMatcher} with\n             {@link android.os.PatternMatcher#PATTERN_SUFFIX}. -->\n        <attr name=\"pathSuffix\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- Attributes that can be supplied in an AndroidManifest.xml\n         <code>path-permission</code> tag, a child of the\n         {@link #AndroidManifestProvider provider} tag, describing a permission\n         that allows access to a specific path in the provider.  This tag can be\n         specified multiple time to supply multiple paths. If multiple\n         path matching attributes are supplied, they will be evaluated in the\n         following order with the first attribute being the only one honored:\n          <code>pathAdvancedPattern</code>, <code>pathPattern</code>,\n          <code>pathPrefix</code>, <code>pathSuffix</code>, <code>path</code>.-->\n    <declare-styleable name=\"AndroidManifestPathPermission\"  parent=\"AndroidManifestProvider\">\n        <attr name=\"path\" />\n        <attr name=\"pathPrefix\" />\n        <attr name=\"pathPattern\" />\n        <attr name=\"pathAdvancedPattern\" format=\"string\"/>\n        <attr name=\"pathSuffix\" />\n        <attr name=\"permission\" />\n        <attr name=\"readPermission\" />\n        <attr name=\"writePermission\" />\n    </declare-styleable>\n\n    <!-- The <code>service</code> tag declares a\n         {@link android.app.Service} class that is available\n         as part of the package's application components, implementing\n         long-running background operations or a rich communication API\n         that can be called by other packages.\n\n         <p>Zero or more {@link #AndroidManifestIntentFilter intent-filter}\n         tags can be included inside of a service, to specify the Intents\n         that can connect with it.  If none are specified, the service can\n         only be accessed by direct specification of its class name.\n         The service tag appears as a child tag of the\n         {@link #AndroidManifestApplication application} tag. -->\n    <declare-styleable name=\"AndroidManifestService\" parent=\"AndroidManifestApplication\">\n        <!-- Required name of the class implementing the service, deriving from\n            {@link android.app.Service}.  This is a fully\n            qualified class name (for example, com.mycompany.myapp.MyService); as a\n            short-hand if the first character of the class\n            is a period then it is appended to your package name. -->\n        <attr name=\"name\" />\n        <attr name=\"label\" />\n        <attr name=\"description\" />\n        <attr name=\"icon\" />\n        <attr name=\"roundIcon\" />\n        <attr name=\"banner\" />\n        <attr name=\"logo\" />\n        <attr name=\"permission\" />\n        <attr name=\"process\" />\n        <!-- Specify whether the service is enabled or not (that is, can be instantiated by the system).\n             It can also be specified for an application as a whole, in which case a value of \"false\"\n             will override any component specific values (a value of \"true\" will not override the\n             component specific values). -->\n        <attr name=\"enabled\" />\n        <attr name=\"exported\" />\n        <!-- If set to true, this service with be automatically stopped\n             when the user remove a task rooted in an activity owned by\n             the application.  The default is false. -->\n        <attr name=\"stopWithTask\" format=\"boolean\" />\n        <!-- If set to true, this service will run under a special process\n             that is isolated from the rest of the system.  The only communication\n             with it is through the Service API (binding and starting). -->\n        <attr name=\"isolatedProcess\" format=\"boolean\" />\n        <attr name=\"singleUser\" />\n        <attr name=\"directBootAware\" />\n        <!-- If the service is an {@link android.R.attr#isolatedProcess} service, this permits a\n             client to bind to the service as if it were running it its own package.  The service\n             must also be {@link android.R.attr#exported} if this flag is set. -->\n        <attr name=\"externalService\" format=\"boolean\" />\n        <attr name=\"visibleToInstantApps\" />\n        <!-- The code for this component is located in the given split.\n             <p>NOTE: This is only applicable to instant app. -->\n        <attr name=\"splitName\" />\n        <!-- If true, and this is an {@link android.R.attr#isolatedProcess} service, the service\n             will be spawned from an Application Zygote, instead of the regular Zygote.\n             <p>\n             The Application Zygote will first pre-initialize the application's class loader. Then,\n             if the application has defined the {@link android.R.attr#zygotePreloadName} attribute,\n             the Application Zygote will call into that class to allow it to perform\n             application-specific preloads (such as loading a shared library). Therefore,\n             spawning from the Application Zygote will typically reduce the service\n             launch time and reduce its memory usage. The downside of using this flag\n             is that you will have an additional process (the app zygote itself) that\n             is taking up memory. Whether actual memory usage is improved therefore strongly\n             depends on the number of isolated services that an application starts,\n             and how much memory those services save by preloading and sharing memory with\n             the app zygote. Therefore, it is recommended to measure memory usage under\n             typical workloads to determine whether it makes sense to use this flag.\n\n             <p>There is a limit to the number of isolated services that can be spawned from\n                the Application Zygote; the absolute limit is 100, but due to potential\n                delays in service process cleanup, a much safer limit to use in practice is 50.\n             -->\n        <attr name=\"useAppZygote\" format=\"boolean\" />\n        <!-- If this is a foreground service, specify its category. -->\n        <attr name=\"foregroundServiceType\" />\n        <!-- Set of attribution tags that should be automatically applied to this component.\n             <p>\n             Each instance of this Service will be automatically configured with\n             Context.createAttributionContext() using the first attribution tag\n             contained here. -->\n        <attr name=\"attributionTags\" />\n        <!-- If true, and this is an {@link android.R.attr#isolatedProcess} service, the service\n             is allowed to be bound in a shared isolated process with other isolated services.\n             Note that these other isolated services can also belong to other apps from different\n             vendors.\n             <p>\n             Shared isolated processes are created when using the\n             {@link android.content.Context#BIND_SHARED_ISOLATED_PROCESS) during service binding.\n             <p>\n             Note that when this flag is used, the {@link android.R.attr#process} attribute is\n             ignored when the process is bound into a shared isolated process by a client.\n        -->\n        <attr name=\"allowSharedIsolatedProcess\" format=\"boolean\" />\n        <attr name=\"systemUserOnly\" format=\"boolean\" />\n        <attr name=\"intentMatchingFlags\"/>\n    </declare-styleable>\n\n    <!-- @hide The <code>apex-system-service</code> tag declares an apex system service\n         that is contained within an application.\n\n         The apex system service tag appears as a child tag of the\n         {@link #AndroidManifestApplication application} tag. -->\n    <declare-styleable name=\"AndroidManifestApexSystemService\"\n                       parent=\"AndroidManifestApplication\">\n        <!-- The fully qualified class name of the system service. -->\n        <attr name=\"name\" />\n        <!-- The filepath to the .jar that contains the system service. If this is not provided, it\n             is assumed that the system service exists in SYSTEMSERVERCLASSPATH. -->\n        <attr name=\"path\" />\n        <attr name=\"minSdkVersion\" />\n        <attr name=\"maxSdkVersion\" />\n        <!-- The order in which the apex system services are initiated. When there are dependencies\n        among apex system services, setting this attribute for each of them ensures that they are\n        created in the order required by those dependencies. The apex-system-services that are\n        started manually within SystemServer ignore the initOrder and are not considered for\n        automatic starting of the other services.\n        The value is a simple integer, with higher number being initialized first. If not specified,\n        the default order is 0. -->\n        <attr name=\"initOrder\" format=\"integer\" />\n    </declare-styleable>\n\n    <!-- The <code>receiver</code> tag declares an\n         {@link android.content.BroadcastReceiver} class that is available\n         as part of the package's application components, allowing the\n         application to receive actions or data broadcast by other\n         applications even if it is not currently running.\n\n         <p>Zero or more {@link #AndroidManifestIntentFilter intent-filter}\n         tags can be included inside of a receiver, to specify the Intents\n         it will receive.  If none are specified, the receiver will only\n         be run when an Intent is broadcast that is directed at its specific\n         class name.  The receiver tag appears as a child tag of the\n         {@link #AndroidManifestApplication application} tag. -->\n    <declare-styleable name=\"AndroidManifestReceiver\" parent=\"AndroidManifestApplication\">\n        <!-- Required name of the class implementing the receiver, deriving from\n            {@link android.content.BroadcastReceiver}.  This is a fully\n            qualified class name (for example, com.mycompany.myapp.MyReceiver); as a\n            short-hand if the first character of the class\n            is a period then it is appended to your package name. -->\n        <attr name=\"name\" />\n        <attr name=\"label\" />\n        <attr name=\"description\" />\n        <attr name=\"icon\" />\n        <attr name=\"roundIcon\" />\n        <attr name=\"banner\" />\n        <attr name=\"logo\" />\n        <attr name=\"permission\" />\n        <attr name=\"process\" />\n        <!-- Specify whether the receiver is enabled or not (that is, can be instantiated by the system).\n             It can also be specified for an application as a whole, in which case a value of \"false\"\n             will override any component specific values (a value of \"true\" will not override the\n             component specific values). -->\n        <attr name=\"enabled\" />\n        <attr name=\"exported\" />\n        <attr name=\"singleUser\" />\n        <attr name=\"directBootAware\" />\n        <!-- Set of attribution tags that should be automatically applied to this component.\n             <p>\n             Each instance of this BroadcastReceiver will be automatically configured with\n             Context.createAttributionContext() using the first attribution tag\n             contained here. -->\n        <attr name=\"attributionTags\" />\n        <attr name=\"intentMatchingFlags\"/>\n    </declare-styleable>\n\n    <!-- The <code>activity</code> tag declares an\n         {@link android.app.Activity} class that is available\n         as part of the package's application components, implementing\n         a part of the application's user interface.\n\n         <p>Zero or more {@link #AndroidManifestIntentFilter intent-filter}\n         tags can be included inside of an activity, to specify the Intents\n         that it can handle.  If none are specified, the activity can\n         only be started through direct specification of its class name.\n         The activity tag appears as a child tag of the\n         {@link #AndroidManifestApplication application} tag. -->\n    <declare-styleable name=\"AndroidManifestActivity\" parent=\"AndroidManifestApplication\">\n        <!-- Required name of the class implementing the activity, deriving from\n            {@link android.app.Activity}.  This is a fully\n            qualified class name (for example, com.mycompany.myapp.MyActivity); as a\n            short-hand if the first character of the class\n            is a period then it is appended to your package name. -->\n        <attr name=\"name\" />\n        <attr name=\"theme\" />\n        <attr name=\"label\" />\n        <attr name=\"description\" />\n        <attr name=\"icon\" />\n        <attr name=\"roundIcon\" />\n        <attr name=\"banner\" />\n        <attr name=\"logo\" />\n        <attr name=\"launchMode\" />\n        <attr name=\"screenOrientation\" />\n        <attr name=\"configChanges\" />\n        <attr name=\"recreateOnConfigChanges\" />\n        <attr name=\"permission\" />\n        <attr name=\"multiprocess\" />\n        <attr name=\"process\" />\n        <attr name=\"taskAffinity\" />\n        <attr name=\"allowTaskReparenting\" />\n        <attr name=\"finishOnTaskLaunch\" />\n        <attr name=\"finishOnCloseSystemDialogs\" />\n        <attr name=\"clearTaskOnLaunch\" />\n        <attr name=\"noHistory\" />\n        <attr name=\"alwaysRetainTaskState\" />\n        <attr name=\"stateNotNeeded\" />\n        <attr name=\"excludeFromRecents\" />\n        <!-- @deprecated use {@link android.R.attr#showForAllUsers} instead. -->\n        <attr name=\"showOnLockScreen\" />\n        <!-- Specify whether the activity is enabled or not (that is, can be instantiated by the system).\n             It can also be specified for an application as a whole, in which case a value of \"false\"\n             will override any component specific values (a value of \"true\" will not override the\n             component specific values). -->\n        <attr name=\"enabled\" />\n        <attr name=\"exported\" />\n        <!-- Specify the default soft-input mode for the main window of\n             this activity.  A value besides \"unspecified\" here overrides\n             any value in the theme. -->\n        <attr name=\"windowSoftInputMode\" />\n        <attr name=\"immersive\" />\n        <attr name=\"hardwareAccelerated\" />\n        <attr name=\"uiOptions\" />\n        <attr name=\"parentActivityName\" />\n        <attr name=\"singleUser\" />\n        <!-- This broadcast receiver or activity will only receive broadcasts for the\n             system user-->\n        <attr name=\"systemUserOnly\" format=\"boolean\" />\n        <attr name=\"persistableMode\" />\n        <attr name=\"allowEmbedded\" />\n        <attr name=\"documentLaunchMode\" />\n        <attr name=\"maxRecents\" />\n        <attr name=\"autoRemoveFromRecents\" />\n        <attr name=\"relinquishTaskIdentity\" />\n        <attr name=\"resumeWhilePausing\" />\n        <attr name=\"resizeableActivity\" />\n        <attr name=\"supportsPictureInPicture\" />\n        <attr name=\"maxAspectRatio\" />\n        <attr name=\"minAspectRatio\" />\n        <attr name=\"lockTaskMode\" />\n        <attr name=\"showForAllUsers\" />\n\n        <attr name=\"showWhenLocked\" />\n        <attr name=\"inheritShowWhenLocked\" />\n        <attr name=\"turnScreenOn\" />\n\n        <attr name=\"directBootAware\" />\n        <!-- @hide This activity is always focusable regardless of if it is in a task/stack whose\n             activities are normally not focusable.\n             For example, {@link android.R.attr#supportsPictureInPicture} activities are placed\n             in a task/stack that isn't focusable. This flag allows them to be focusable.-->\n        <attr name=\"alwaysFocusable\" format=\"boolean\" />\n        <attr name=\"enableVrMode\" />\n        <attr name=\"rotationAnimation\" />\n        <attr name=\"visibleToInstantApps\" />\n        <!-- The code for this component is located in the given split. -->\n        <attr name=\"splitName\" />\n        <!-- Specify the color mode the activity desires. The requested color mode may be ignored\n             depending on the capabilities of the display the activity is displayed on. -->\n        <attr name=\"colorMode\">\n            <!-- The default color mode (typically sRGB, low-dynamic range). -->\n            <enum name=\"default\" value=\"0\" />\n            <!-- Wide color gamut color mode. -->\n            <enum name=\"wideColorGamut\" value=\"1\" />\n            <!-- High dynamic range color mode. -->\n            <enum name=\"hdr\" value=\"2\" />\n        </attr>\n        <attr name=\"forceQueryable\" format=\"boolean\" />\n        <!-- Indicates whether the activity wants the connected display to do minimal\n             post processing on the produced image or video frames. This will only be\n             requested if this activity's main window is visible on the screen.\n\n             <p> This setting should be used when low latency has a higher priority than\n             image enhancement processing (e.g. for games or video conferencing).\n\n             <p> If the Display sink is connected via HDMI, the device will begin to\n             send infoframes with Auto Low Latency Mode enabled and Game Content Type.\n             This will switch the connected display to a minimal image processing  mode\n             (if available), which reduces latency, improving the user experience for\n             gaming or video conferencing applications. For more information,\n             see HDMI 2.1 specification.\n\n             <p> If the Display sink has an internal connection or uses some other\n             protocol than HDMI, effects may be similar but implementation-defined.\n\n             <p> The ability to switch to a mode with minimal post proessing may be\n             disabled by a user setting in the system settings menu. In that case,\n             this field is ignored and the display will remain in its current\n             mode.\n\n             <p> See {@link android.content.pm.ActivityInfo#FLAG_PREFER_MINIMAL_POST_PROCESSING} -->\n        <attr name=\"preferMinimalPostProcessing\" format=\"boolean\"/>\n        <!-- Set of attribution tags that should be automatically applied to this component.\n             <p>\n             Each instance of this Activity will be automatically configured with\n             Context.createAttributionContext() using the first attribution tag\n             contained here. -->\n        <attr name=\"attributionTags\" />\n        <!-- Specifies whether a home sound effect should be played if the home app moves to\n             front after an activity with this flag set to <code>true</code>.\n             <p>The default value of this attribute is <code>true</code>.\n             <p>Also note that home sounds are only played if the device supports home sounds,\n             usually TVs.\n             <p>Requires permission {@code android.permission.DISABLE_SYSTEM_SOUND_EFFECTS}. -->\n        <attr name=\"playHomeTransitionSound\" format=\"boolean\"/>\n        <!-- Indicates whether the activity can be displayed on a display that may belong to a\n             remote device which may or may not be running Android. -->\n        <attr name=\"canDisplayOnRemoteDevices\" format=\"boolean\"/>\n        <attr name=\"allowUntrustedActivityEmbedding\" />\n        <attr name=\"knownActivityEmbeddingCerts\" />\n        <!-- Specifies the required display category of the activity. Upon creation, a display can\n             specify which display categories it supports and one of the categories must be present\n             in the {@code <activity>} element to allow this activity to run. The default value is\n             {@code null}, which indicates the activity does not have a required display category\n             and thus can only run on a display that didn't specify any display categories. Each\n             activity can only specify one required category but a display can accommodate multiple\n             display categories.\n\n             <p> This field should be formatted as a Java-language-style free form string(for\n             example, com.google.automotive_entertainment), which may contain uppercase or lowercase\n             letters ('A' through 'Z'), numbers, and underscores ('_') but may only start with\n             letters.\n         -->\n        <attr name=\"requiredDisplayCategory\" format=\"string\"/>\n        <!-- If false, {@link android.view.KeyEvent#KEYCODE_BACK KEYCODE_BACK} and\n             {@link android.app.Activity#onBackPressed Activity.onBackPressed()}\n             and related event will be forwarded to the Activity and its views.\n\n             <p> If true, those events will be replaced by a call to\n             {@link android.window.OnBackInvokedCallback#onBackInvoked} on the focused window.\n\n             <p> By default, the behavior is configured by the same attribute in application.\n        -->\n        <attr name=\"enableOnBackInvokedCallback\" format=\"boolean\"/>\n\n        <!-- Specifies permissions necessary to launch this activity when passing content URIs. The\n             default value is {@code none}, meaning no specific permissions are required. Setting\n             this attribute restricts activity invocation based on the invoker's permissions. If the\n             invoker doesn't have the required permissions, the activity start will be denied via a\n             {@link java.lang.SecurityException}.\n\n             <p> Note that the enforcement works for content URIs inside\n             {@link android.content.Intent#getData}, {@link android.content.Intent#EXTRA_STREAM},\n             and {@link android.content.Intent#getClipData}.\n             @FlaggedApi(\"android.security.content_uri_permission_apis\") -->\n        <attr name=\"requireContentUriPermissionFromCaller\" format=\"string\">\n            <!-- Default, no specific permissions are required. -->\n            <enum name=\"none\" value=\"0\" />\n            <!-- Enforces the invoker to have read access to the passed content URIs. -->\n            <enum name=\"read\" value=\"1\" />\n            <!-- Enforces the invoker to have write access to the passed content URIs. -->\n            <enum name=\"write\" value=\"2\" />\n            <!-- Enforces the invoker to have either read or write access to the passed content\n                 URIs. -->\n            <enum name=\"readOrWrite\" value=\"3\" />\n            <!-- Enforces the invoker to have both read and write access to the passed content\n                 URIs. -->\n            <enum name=\"readAndWrite\" value=\"4\" />\n        </attr>\n        <attr name=\"intentMatchingFlags\"/>\n    </declare-styleable>\n\n    <!-- The <code>activity-alias</code> tag declares a new\n         name for an existing {@link #AndroidManifestActivity activity}\n         tag.\n\n         <p>Zero or more {@link #AndroidManifestIntentFilter intent-filter}\n         tags can be included inside of an activity-alias, to specify the Intents\n         that it can handle.  If none are specified, the activity can\n         only be started through direct specification of its class name.\n         The activity-alias tag appears as a child tag of the\n         {@link #AndroidManifestApplication application} tag. -->\n    <declare-styleable name=\"AndroidManifestActivityAlias\" parent=\"AndroidManifestApplication\">\n        <!-- Required name of the class implementing the activity, deriving from\n            {@link android.app.Activity}.  This is a fully\n            qualified class name (for example, com.mycompany.myapp.MyActivity); as a\n            short-hand if the first character of the class\n            is a period then it is appended to your package name. -->\n        <attr name=\"name\" />\n        <!-- The name of the activity this alias should launch.  The activity\n             must be in the same manifest as the alias, and have been defined\n             in that manifest before the alias here.  This must use a Java-style\n             naming convention to ensure the name is unique, for example\n             \"com.mycompany.MyName\". -->\n        <attr name=\"targetActivity\" format=\"string\" />\n        <attr name=\"label\" />\n        <attr name=\"description\" />\n        <attr name=\"icon\" />\n        <attr name=\"roundIcon\" />\n        <attr name=\"banner\" />\n        <attr name=\"logo\" />\n        <attr name=\"permission\" />\n        <!-- Specify whether the activity-alias is enabled or not (that is, can be instantiated by the system).\n             It can also be specified for an application as a whole, in which case a value of \"false\"\n             will override any component specific values (a value of \"true\" will not override the\n             component specific values). -->\n        <attr name=\"enabled\" />\n        <attr name=\"exported\" />\n        <attr name=\"parentActivityName\" />\n        <attr name=\"attributionTags\" />\n        <attr name=\"allowUntrustedActivityEmbedding\" />\n        <attr name=\"knownActivityEmbeddingCerts\" />\n        <attr name=\"intentMatchingFlags\"/>\n    </declare-styleable>\n\n    <!-- The <code>meta-data</code> tag is used to attach additional\n         arbitrary data to an application component.  The data can later\n         be retrieved programmatically from the\n         {@link android.content.pm.ComponentInfo#metaData\n         ComponentInfo.metaData} field.  There is no meaning given to this\n         data by the system.  You may supply the data through either the\n         <code>value</code> or <code>resource</code> attribute; if both\n         are given, then <code>resource</code> will be used.\n\n         <p>It is highly recommended that you avoid supplying related data as\n         multiple separate meta-data entries.  Instead, if you have complex\n         data to associate with a component, then use the <code>resource</code>\n         attribute to assign an XML resource that the client can parse to\n         retrieve the complete data. -->\n    <declare-styleable name=\"AndroidManifestMetaData\"\n         parent=\"AndroidManifestApplication\n                 AndroidManifestActivity\n                 AndroidManifestReceiver\n                 AndroidManifestProvider\n                 AndroidManifestService\n                 AndroidManifestPermission\n                 AndroidManifestPermissionGroup\n                 AndroidManifestInstrumentation\">\n        <attr name=\"name\" />\n        <!-- Concrete value to assign to this piece of named meta-data.\n             The data can later be retrieved from the meta data Bundle\n             through {@link android.os.Bundle#getString Bundle.getString},\n             {@link android.os.Bundle#getInt Bundle.getInt},\n             {@link android.os.Bundle#getBoolean Bundle.getBoolean},\n             or {@link android.os.Bundle#getFloat Bundle.getFloat} depending\n             on the type used here. -->\n        <attr name=\"value\" format=\"string|integer|color|float|boolean\" />\n        <!-- Resource identifier to assign to this piece of named meta-data.\n             The resource identifier can later be retrieved from the meta data\n             Bundle through {@link android.os.Bundle#getInt Bundle.getInt}. -->\n        <attr name=\"resource\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- The <code>property</code> tag is used to attach additional data that can\n         be supplied to the parent component. A component element can contain any\n         number of <code>property</code> subelements. Valid names are any of the\n         <code>PROPERTY_</code> constants defined in the\n         {@link android.content.pm.PackageManager PackageManager} class. Values\n         are obtained using the appropriate method on the\n         {@link android.content.pm.PackageManager.Property PackageManager.Property} class.\n         <p>Ordinary values are specified through the value attribute. Resource IDs are\n         specified through the resource attribute.\n         <p>It is invalid to specify both a value and resource attributes. -->\n    <declare-styleable name=\"AndroidManifestProperty\"\n         parent=\"AndroidManifestApplication\n                 AndroidManifestActivity\n                 AndroidManifestReceiver\n                 AndroidManifestProvider\n                 AndroidManifestService\">\n        <attr name=\"name\" />\n        <!-- Concrete value to assign to this property.\n             The data can later be retrieved from the property object\n             through\n             {@link android.content.pm.PackageManager.Property#getString Property.getString},\n             {@link android.content.pm.PackageManager.Property#getInteger Property.getInteger},\n             {@link android.content.pm.PackageManager.Property#getBoolean Property.getBoolean},\n             or {@link android.content.pm.PackageManager.Property#getFloat Property.getFloat}\n             depending on the type used here. -->\n        <attr name=\"value\" />\n        <!-- The resource identifier to assign to this property.\n             The resource identifier can later be retrieved from the property object through\n             {@link android.content.pm.PackageManager.Property#getResourceId Property.getResourceId}. -->\n        <attr name=\"resource\" />\n    </declare-styleable>\n\n    <!-- The <code>intent-filter</code> tag is used to construct an\n         {@link android.content.IntentFilter} object that will be used\n         to determine which component can handle a particular\n         {@link android.content.Intent} that has been given to the system.\n         It can be used as a child of the\n         {@link #AndroidManifestActivity activity},\n         {@link #AndroidManifestReceiver receiver} and\n         {@link #AndroidManifestService service}\n         tags.\n\n         <p> Zero or more {@link #AndroidManifestAction action},\n         {@link #AndroidManifestCategory category}, and/or\n         {@link #AndroidManifestData data} tags should be\n         included inside to describe the contents of the filter.\n\n         <p> The optional label and icon attributes here are used with\n         an activity to supply an alternative description of that activity\n         when it is being started through an Intent matching this filter. -->\n    <declare-styleable name=\"AndroidManifestIntentFilter\"\n         parent=\"AndroidManifestActivity AndroidManifestReceiver AndroidManifestService\">\n        <attr name=\"label\" />\n        <attr name=\"icon\" />\n        <attr name=\"roundIcon\" />\n        <attr name=\"banner\" />\n        <attr name=\"logo\" />\n        <attr name=\"priority\" />\n        <attr name=\"autoVerify\" />\n        <!-- Within an application, multiple intent filters may match a particular\n             intent. This allows the app author to specify the order filters should\n             be considered. We don't want to use priority because that is global\n             across applications.\n             <p>Only use if you really need to forcibly set the order in which\n             filters are evaluated. It is preferred to target an activity with a\n             directed intent instead.\n             <p>The value is a single integer, with higher numbers considered to\n             be better. If not specified, the default order is 0. -->\n        <attr name=\"order\" />\n    </declare-styleable>\n\n    <!-- Attributes that can be supplied in an AndroidManifest.xml\n         <code>action</code> tag, a child of the\n         {@link #AndroidManifestIntentFilter intent-filter} tag.\n         See {@link android.content.IntentFilter#addAction} for\n         more information. -->\n    <declare-styleable name=\"AndroidManifestAction\" parent=\"AndroidManifestIntentFilter\">\n        <!-- The name of an action that is handled, using the Java-style\n             naming convention.  For example, to support\n             {@link android.content.Intent#ACTION_VIEW Intent.ACTION_VIEW}\n             you would put <code>android.intent.action.VIEW</code> here.\n             Custom actions should generally use a prefix matching the\n             package name. -->\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- Attributes that can be supplied in an AndroidManifest.xml\n         <code>data</code> tag, a child of the\n         {@link #AndroidManifestIntentFilter intent-filter} tag, describing\n         a group matching rule consisting of one or more\n         {@link #AndroidManifestData data} tags that must all match.  This\n         tag can be specified multiple times to create multiple groups that\n         will be matched in the order they are defined. -->\n    <declare-styleable name=\"AndroidManifestUriRelativeFilterGroup\"\n        parent=\"AndroidManifestIntentFilter\">\n        <!-- Specify if this group is allow rule or disallow rule.  If this\n             attribute is not specified then it is assumed to be true -->\n        <attr name=\"allow\" format=\"boolean\"/>\n    </declare-styleable>\n\n    <!-- Attributes that can be supplied in an AndroidManifest.xml\n         <code>data</code> tag, a child of the\n         {@link #AndroidManifestIntentFilter intent-filter} tag, describing\n         the types of data that match.  This tag can be specified multiple\n         times to supply multiple data options, as described in the\n         {@link android.content.IntentFilter} class.  Note that all such\n         tags are adding options to the same IntentFilter so that, for example,\n         <code>&lt;data android:scheme=\"myscheme\" android:host=\"me.com\" /&gt;</code>\n         is equivalent to <code>&lt;data android:scheme=\"myscheme\" /&gt;\n         &lt;data android:host=\"me.com\" /&gt;</code>. -->\n    <declare-styleable name=\"AndroidManifestData\"\n        parent=\"AndroidManifestIntentFilter AndroidManifestUriRelativeFilterGroup\">\n        <!-- Specify a MIME type that is handled, as per\n             {@link android.content.IntentFilter#addDataType\n             IntentFilter.addDataType()}.\n             <p><em>Note: MIME type matching in the Android framework is\n             case-sensitive, unlike formal RFC MIME types.  As a result,\n             MIME types here should always use lower case letters.</em></p> -->\n        <attr name=\"mimeType\" format=\"string\" />\n        <!-- Specify a group of MIME types that are handled. MIME types can be added and\n             removed to a package's MIME group via the PackageManager. -->\n        <attr name=\"mimeGroup\" format=\"string\" />\n        <!-- Specify a URI scheme that is handled, as per\n             {@link android.content.IntentFilter#addDataScheme\n             IntentFilter.addDataScheme()}.\n             <p><em>Note: scheme matching in the Android framework is\n             case-sensitive, unlike the formal RFC.  As a result,\n             schemes here should always use lower case letters.</em></p> -->\n        <attr name=\"scheme\" format=\"string\" />\n        <!-- Specify a URI scheme specific part that must exactly match, as per\n             {@link android.content.IntentFilter#addDataSchemeSpecificPart\n             IntentFilter.addDataSchemeSpecificPart()} with\n             {@link android.os.PatternMatcher#PATTERN_LITERAL}. -->\n        <attr name=\"ssp\" format=\"string\" />\n        <!-- Specify a URI scheme specific part that must be a prefix to match, as per\n             {@link android.content.IntentFilter#addDataSchemeSpecificPart\n             IntentFilter.addDataSchemeSpecificPart()} with\n             {@link android.os.PatternMatcher#PATTERN_PREFIX}. -->\n        <attr name=\"sspPrefix\" format=\"string\" />\n        <!-- Specify a URI scheme specific part that matches a simple pattern, as per\n             {@link android.content.IntentFilter#addDataSchemeSpecificPart\n             IntentFilter.addDataSchemeSpecificPart()} with\n             {@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}.\n             Note that because '\\' is used as an escape character when\n             reading the string from XML (before it is parsed as a pattern),\n             you will need to double-escape: for example a literal \"*\" would\n             be written as \"\\\\*\" and a literal \"\\\" would be written as\n             \"\\\\\\\\\".  This is basically the same as what you would need to\n             write if constructing the string in Java code. -->\n        <attr name=\"sspPattern\" format=\"string\" />\n        <!-- Specify a URI scheme specific part that matches an advanced pattern, as per\n             {@link android.content.IntentFilter#addDataSchemeSpecificPart\n             IntentFilter.addDataSchemeSpecificPart()} with\n             {@link android.os.PatternMatcher#PATTERN_ADVANCED_GLOB}.\n             Note that because '\\' is used as an escape character when\n             reading the string from XML (before it is parsed as a pattern),\n             you will need to double-escape: for example a literal \"*\" would\n             be written as \"\\\\*\" and a literal \"\\\" would be written as\n             \"\\\\\\\\\".  This is basically the same as what you would need to\n             write if constructing the string in Java code. -->\n        <attr name=\"sspAdvancedPattern\" format=\"string\" />\n        <!-- Specify a URI scheme specific part that must be a suffix to match, as per\n             {@link android.content.IntentFilter#addDataSchemeSpecificPart\n             IntentFilter.addDataSchemeSpecificPart()} with\n             {@link android.os.PatternMatcher#PATTERN_SUFFIX}. -->\n        <attr name=\"sspSuffix\" format=\"string\" />\n        <!-- Specify a URI authority host that is handled, as per\n             {@link android.content.IntentFilter#addDataAuthority\n             IntentFilter.addDataAuthority()}.\n             <p><em>Note: host name matching in the Android framework is\n             case-sensitive, unlike the formal RFC.  As a result,\n             host names here should always use lower case letters.</em></p> -->\n        <attr name=\"host\" format=\"string\" />\n        <!-- Specify a URI authority port that is handled, as per\n             {@link android.content.IntentFilter#addDataAuthority\n             IntentFilter.addDataAuthority()}.  If a host is supplied\n             but not a port, any port is matched. -->\n        <attr name=\"port\" format=\"string\" />\n        <!-- Specify a URI path that must exactly match, as per\n             {@link android.content.IntentFilter#addDataPath\n             IntentFilter.addDataPath()} with\n             {@link android.os.PatternMatcher#PATTERN_LITERAL}. -->\n        <attr name=\"path\" />\n        <!-- Specify a URI path that must be a prefix to match, as per\n             {@link android.content.IntentFilter#addDataPath\n             IntentFilter.addDataPath()} with\n             {@link android.os.PatternMatcher#PATTERN_PREFIX}. -->\n        <attr name=\"pathPrefix\" />\n        <!-- Specify a URI path that matches a simple pattern, as per\n             {@link android.content.IntentFilter#addDataPath\n             IntentFilter.addDataPath()} with\n             {@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}.\n             Note that because '\\' is used as an escape character when\n             reading the string from XML (before it is parsed as a pattern),\n             you will need to double-escape: for example a literal \"*\" would\n             be written as \"\\\\*\" and a literal \"\\\" would be written as\n             \"\\\\\\\\\".  This is basically the same as what you would need to\n             write if constructing the string in Java code. -->\n        <attr name=\"pathPattern\" />\n        <!-- Specify a URI path that matches an advanced pattern, as per\n             {@link android.content.IntentFilter#addDataPath\n             IntentFilter.addDataPath()} with\n             {@link android.os.PatternMatcher#PATTERN_ADVANCED_GLOB}.\n             Note that because '\\' is used as an escape character when\n             reading the string from XML (before it is parsed as a pattern),\n             you will need to double-escape: for example a literal \"*\" would\n             be written as \"\\\\*\" and a literal \"\\\" would be written as\n             \"\\\\\\\\\".  This is basically the same as what you would need to\n             write if constructing the string in Java code. -->\n        <attr name=\"pathAdvancedPattern\" />\n        <!-- Specify a URI path that must be a suffix to match, as per\n             {@link android.content.IntentFilter#addDataPath\n             IntentFilter.addDataPath()} with\n             {@link android.os.PatternMatcher#PATTERN_SUFFIX}. -->\n        <attr name=\"pathSuffix\" />\n        <!-- Specify a URI query that must exactly match, as a\n             {@link android.content.UriRelativeFilter UriRelativeFilter} with\n             {@link android.os.PatternMatcher#PATTERN_LITERAL}. -->\n        <attr name=\"query\" format=\"string\" />\n        <!-- Specify a URI query that must be a prefix to match, as a\n             {@link android.content.UriRelativeFilter UriRelativeFilter} with\n             {@link android.os.PatternMatcher#PATTERN_PREFIX}. -->\n        <attr name=\"queryPrefix\" format=\"string\" />\n        <!-- Specify a URI query that matches a simple pattern, as a\n             {@link android.content.UriRelativeFilter UriRelativeFilter} with\n             {@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}.\n             Note that because '\\' is used as an escape character when\n             reading the string from XML (before it is parsed as a pattern),\n             you will need to double-escape: for example a literal \"*\" would\n             be written as \"\\\\*\" and a literal \"\\\" would be written as\n             \"\\\\\\\\\".  This is basically the same as what you would need to\n             write if constructing the string in Java code. -->\n        <attr name=\"queryPattern\" format=\"string\" />\n        <!-- Specify a URI query that matches an advanced pattern, as a\n             {@link android.content.UriRelativeFilter UriRelativeFilter} with\n             {@link android.os.PatternMatcher#PATTERN_ADVANCED_GLOB}.\n             Note that because '\\' is used as an escape character when\n             reading the string from XML (before it is parsed as a pattern),\n             you will need to double-escape: for example a literal \"*\" would\n             be written as \"\\\\*\" and a literal \"\\\" would be written as\n             \"\\\\\\\\\".  This is basically the same as what you would need to\n             write if constructing the string in Java code. -->\n        <attr name=\"queryAdvancedPattern\" format=\"string\" />\n        <!-- Specify a URI query that must be a suffix to match, as a\n             {@link android.content.UriRelativeFilter UriRelativeFilter} with\n             {@link android.os.PatternMatcher#PATTERN_SUFFIX}. -->\n        <attr name=\"querySuffix\" format=\"string\" />\n        <!-- Specify a URI fragment that must exactly match, as a\n             {@link android.content.UriRelativeFilter UriRelativeFilter} with\n             {@link android.os.PatternMatcher#PATTERN_LITERAL}. -->\n        <attr name=\"fragment\" format=\"string\" />\n        <!-- Specify a URI fragment that must be a prefix to match, as a\n             {@link android.content.UriRelativeFilter UriRelativeFilter} with\n             {@link android.os.PatternMatcher#PATTERN_PREFIX}. -->\n        <attr name=\"fragmentPrefix\" format=\"string\" />\n        <!-- Specify a URI fragment that matches a simple pattern, as a\n             {@link android.content.UriRelativeFilter UriRelativeFilter} with\n             {@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}.\n             Note that because '\\' is used as an escape character when\n             reading the string from XML (before it is parsed as a pattern),\n             you will need to double-escape: for example a literal \"*\" would\n             be written as \"\\\\*\" and a literal \"\\\" would be written as\n             \"\\\\\\\\\".  This is basically the same as what you would need to\n             write if constructing the string in Java code. -->\n        <attr name=\"fragmentPattern\" format=\"string\" />\n        <!-- Specify a URI fragment that matches an advanced pattern, as a\n             {@link android.content.UriRelativeFilter UriRelativeFilter} with\n             {@link android.os.PatternMatcher#PATTERN_ADVANCED_GLOB}.\n             Note that because '\\' is used as an escape character when\n             reading the string from XML (before it is parsed as a pattern),\n             you will need to double-escape: for example a literal \"*\" would\n             be written as \"\\\\*\" and a literal \"\\\" would be written as\n             \"\\\\\\\\\".  This is basically the same as what you would need to\n             write if constructing the string in Java code. -->\n        <attr name=\"fragmentAdvancedPattern\" format=\"string\" />\n        <!-- Specify a URI fragment that must be a suffix to match, as a\n             {@link android.content.UriRelativeFilter UriRelativeFilter} with\n             {@link android.os.PatternMatcher#PATTERN_SUFFIX}. -->\n        <attr name=\"fragmentSuffix\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- Attributes that can be supplied in an AndroidManifest.xml\n         <code>category</code> tag, a child of the\n         {@link #AndroidManifestIntentFilter intent-filter} tag.\n         See {@link android.content.IntentFilter#addCategory} for\n         more information. -->\n    <declare-styleable name=\"AndroidManifestCategory\" parent=\"AndroidManifestIntentFilter\">\n        <!-- The name of category that is handled, using the Java-style\n             naming convention.  For example, to support\n             {@link android.content.Intent#CATEGORY_LAUNCHER Intent.CATEGORY_LAUNCHER}\n             you would put <code>android.intent.category.LAUNCHER</code> here.\n             Custom actions should generally use a prefix matching the\n             package name. -->\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- Attributes that can be supplied in an AndroidManifest.xml\n         <code>instrumentation</code> tag, a child of the root\n         {@link #AndroidManifest manifest} tag. -->\n    <declare-styleable name=\"AndroidManifestInstrumentation\" parent=\"AndroidManifest\">\n        <!-- Required name of the class implementing the instrumentation, deriving from\n            {@link android.app.Instrumentation}.  This is a fully\n            qualified class name (for example, com.mycompany.myapp.MyActivity); as a\n            short-hand if the first character of the class\n            is a period then it is appended to your package name. -->\n        <attr name=\"name\" />\n        <attr name=\"targetPackage\" />\n        <attr name=\"targetProcesses\" />\n        <attr name=\"label\" />\n        <attr name=\"icon\" />\n        <attr name=\"roundIcon\" />\n        <attr name=\"banner\" />\n        <attr name=\"logo\" />\n        <attr name=\"handleProfiling\" />\n        <attr name=\"functionalTest\" />\n    </declare-styleable>\n\n    <!-- Attributes that can be supplied in an AndroidManifest.xml\n         <code>screen</code> tag, a child of <code>compatible-screens</code>,\n         which is itself a child of the root\n         {@link #AndroidManifest manifest} tag. -->\n    <declare-styleable name=\"AndroidManifestCompatibleScreensScreen\"\n                       parent=\"AndroidManifest.AndroidManifestCompatibleScreens\">\n        <!-- Specifies a compatible screen size, as per the device\n             configuration screen size bins. -->\n        <attr name=\"screenSize\">\n            <!-- A small screen configuration, at least 240x320dp. -->\n            <enum name=\"small\" value=\"200\" />\n            <!-- A normal screen configuration, at least 320x480dp. -->\n            <enum name=\"normal\" value=\"300\" />\n            <!-- A large screen configuration, at least 400x530dp. -->\n            <enum name=\"large\" value=\"400\" />\n            <!-- An extra large screen configuration, at least 600x800dp. -->\n            <enum name=\"xlarge\" value=\"500\" />\n        </attr>\n        <!-- Specifies a compatible screen density, as per the device\n             configuration screen density bins. -->\n        <attr name=\"screenDensity\" format=\"integer\">\n            <!-- A low density screen, approximately 120dpi. -->\n            <enum name=\"ldpi\" value=\"120\" />\n            <!-- A medium density screen, approximately 160dpi. -->\n            <enum name=\"mdpi\" value=\"160\" />\n            <!-- A high density screen, approximately 240dpi. -->\n            <enum name=\"hdpi\" value=\"240\" />\n            <!-- An extra high density screen, approximately 320dpi. -->\n            <enum name=\"xhdpi\" value=\"320\" />\n            <!-- An extra extra high density screen, approximately 480dpi. -->\n            <enum name=\"xxhdpi\" value=\"480\" />\n            <!-- An extra extra extra high density screen, approximately 640dpi. -->\n            <enum name=\"xxxhdpi\" value=\"640\" />\n        </attr>\n    </declare-styleable>\n\n    <!-- The <code>input-type</code> tag is a child of the <code>supports-input</code> tag, which\n         is itself a child of the root {@link #AndroidManifest manifest} tag. Each\n         <code>input-type</code> tag specifices the name of a specific input device type. When\n         grouped with the other elements of the parent <code>supports-input</code> tag it defines\n         a collection of input devices, which when all used together, are considered a supported\n         input mechanism for the application. There may be multiple <code>supports-input</code>\n         tags defined, each containing a different combination of input device types. -->\n    <declare-styleable name=\"AndroidManifestSupportsInputInputType\"\n                       parent=\"AndroidManifest.AndroidManifestSupportsInput\">\n        <!-- Specifices the name of the input device type -->\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- The attribute that holds a Base64-encoded public key. -->\n    <attr name=\"publicKey\" format=\"string\" />\n\n    <!-- Attributes relating to a package verifier. -->\n    <declare-styleable name=\"AndroidManifestPackageVerifier\" parent=\"AndroidManifest\">\n        <!-- Specifies the Java-style package name that defines this\n             package verifier. -->\n        <attr name=\"name\" />\n\n        <!-- The Base64 encoded public key of the package verifier's\n             signature. -->\n        <attr name=\"publicKey\" />\n    </declare-styleable>\n\n    <!-- Attributes relating to resource overlay packages. -->\n    <declare-styleable name=\"AndroidManifestResourceOverlay\" parent=\"AndroidManifest\">\n        <!-- Package name of base package whose resources will be overlaid. -->\n        <attr name=\"targetPackage\" />\n\n        <!-- Category of the resource overlay. -->\n        <attr name=\"category\" format=\"string\"/>\n\n        <!-- Load order of overlay package. -->\n        <attr name=\"priority\" />\n\n        <!-- Whether the given RRO is static or not. -->\n        <attr name=\"isStatic\" format=\"boolean\" />\n\n        <!-- Required property name/value pair used to enable this overlay.\n             e.g. name=ro.oem.sku value=MKT210.\n             Overlay will be ignored unless system property exists and is\n             set to specified value -->\n        <!-- @hide This shouldn't be public. -->\n        <attr name=\"requiredSystemPropertyName\" format=\"string\" />\n        <!-- @hide This shouldn't be public. -->\n        <attr name=\"requiredSystemPropertyValue\" format=\"string\" />\n\n        <!-- The name of the overlayable whose resources will be overlaid. -->\n        <attr name=\"targetName\" />\n\n        <!-- The xml file that defines the target id to overlay value mappings. -->\n        <attr name=\"resourcesMap\" format=\"reference\" />\n    </declare-styleable>\n\n    <!-- Declaration of an {@link android.content.Intent} object in XML.  May\n         also include zero or more {@link #IntentCategory <category>} and\n         {@link #Extra <extra>} tags. -->\n    <declare-styleable name=\"Intent\">\n        <!-- The action name to assign to the Intent, as per\n            {@link android.content.Intent#setAction Intent.setAction()}. -->\n        <attr name=\"action\" format=\"string\" />\n        <!-- The data URI to assign to the Intent, as per\n            {@link android.content.Intent#setData Intent.setData()}.\n            <p><em>Note: scheme and host name matching in the Android framework is\n            case-sensitive, unlike the formal RFC.  As a result,\n            URIs here should always be normalized to use lower case letters\n            for these elements (as well as other proper Uri normalization).</em></p> -->\n        <attr name=\"data\" format=\"string\" />\n        <!-- The MIME type name to assign to the Intent, as per\n            {@link android.content.Intent#setType Intent.setType()}.\n            <p><em>Note: MIME type matching in the Android framework is\n            case-sensitive, unlike formal RFC MIME types.  As a result,\n            MIME types here should always use lower case letters.</em></p> -->\n        <attr name=\"mimeType\" />\n        <!-- The identifier to assign to the intent, as per\n            {@link android.content.Intent#setIdentifier Intent.setIdentifier()}. -->\n        <attr name=\"identifier\" format=\"string\" />\n        <!-- The package part of the ComponentName to assign to the Intent, as per\n            {@link android.content.Intent#setComponent Intent.setComponent()}. -->\n        <attr name=\"targetPackage\" />\n        <!-- The class part of the ComponentName to assign to the Intent, as per\n            {@link android.content.Intent#setComponent Intent.setComponent()}. -->\n        <attr name=\"targetClass\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- A category to add to an Intent, as per\n            {@link android.content.Intent#addCategory Intent.addCategory()}. -->\n    <declare-styleable name=\"IntentCategory\" parent=\"Intent\">\n        <!-- Required name of the category. -->\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- An extra data value to place into a an extra/name value pair held\n            in a Bundle, as per {@link android.os.Bundle}. -->\n    <declare-styleable name=\"Extra\" parent=\"Intent\">\n        <!-- Required name of the extra data. -->\n        <attr name=\"name\" />\n        <!-- Concrete value to put for this named extra data. -->\n        <attr name=\"value\" />\n    </declare-styleable>\n\n    <!-- Groups signing keys into a {@code KeySet} for easier reference in\n            other APIs. However, currently no APIs use this. -->\n    <attr name=\"keySet\" />\n    <declare-styleable name=\"AndroidManifestPublicKey\">\n        <attr name=\"name\" />\n        <attr name=\"value\" />\n    </declare-styleable>\n    <declare-styleable name=\"AndroidManifestKeySet\">\n        <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- Associate declared KeySets with upgrading capability. -->\n    <declare-styleable name=\"AndroidManifestUpgradeKeySet\" parent=\"AndroidManifest\">\n      <attr name=\"name\" />\n    </declare-styleable>\n\n    <!-- <code>layout</code> tag allows configuring the layout for the activity within multi-window\n         environment. -->\n    <declare-styleable name=\"AndroidManifestLayout\" parent=\"AndroidManifestActivity\">\n        <!-- Default width of the activity. Can be either a fixed value or fraction, in which case\n             the width will be constructed as a fraction of the total available width. -->\n        <attr name=\"defaultWidth\" format=\"dimension|fraction\" />\n        <!-- Default height of the activity. Can be either a fixed value or fraction, in which case\n             the height will be constructed as a fraction of the total available height. -->\n        <attr name=\"defaultHeight\" format=\"dimension|fraction\" />\n        <!-- Where to initially position the activity inside the available space. Uses constants\n             defined in {@link android.view.Gravity}. -->\n        <attr name=\"gravity\" />\n        <!-- Minimal width of the activity.\n\n         <p><strong>NOTE:</strong> A task's root activity value is applied to all additional\n         activities launched in the task. That is if the root activity of a task set minimal width,\n         then the system will set the same minimal width on all other activities in the task. It\n         will also ignore any other minimal width attributes of non-root activities. -->\n        <attr name=\"minWidth\" />\n        <!-- Minimal height of the activity.\n\n         <p><strong>NOTE:</strong> A task's root activity value is applied to all additional\n         activities launched in the task. That is if the root activity of a task set minimal height,\n         then the system will set the same minimal height on all other activities in the task. It\n         will also ignore any other minimal height attributes of non-root activities. -->\n        <attr name=\"minHeight\" />\n\n        <!-- Window layout affinity of this activity. Activities with the same window layout\n          affinity will share the same layout record. That is, if a user is opening an activity in\n          a new task on a display that can host freeform windows, and the user had opened a task\n          before and that task had a root activity who had the same window layout affinity, the\n          new task's window will be created in the same window mode and around the location which\n          the previously opened task was in.\n\n          <p>For example, if a user maximizes a task with root activity A and opens another\n          activity B that has the same window layout affinity as activity A has, activity B will\n          be created in fullscreen window mode. Similarly, if they move/resize a task with root\n          activity C and open another activity D that has the same window layout affinity as\n          activity C has, activity D will be in freeform window mode and as close to the position\n          of activity C as conditions permit. It doesn't require the user to keep the task with\n          activity A or activity C open. It won't, however, put any task into split-screen or PIP\n          window mode on launch.\n\n          <p>If the user is opening an activity with its window layout affinity for the first time,\n          the window mode and position is OEM defined.\n\n          <p>By default activity doesn't share any affinity with other activities. -->\n        <attr name=\"windowLayoutAffinity\" format=\"string\" />\n    </declare-styleable>\n\n    <!-- <code>restrict-update</code> tag restricts system apps from being updated unless the\n        SHA-512 hash equals the specified value.\n        @hide -->\n    <declare-styleable name=\"AndroidManifestRestrictUpdate\" parent=\"AndroidManifest\">\n        <!-- The SHA-512 hash of the only APK that can be used to update a package.\n             <p>NOTE: This is only applicable to system packages.\n             @hide -->\n        <attr name=\"hash\" format=\"string\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"AndroidManifestUsesSplit\" parent=\"AndroidManifest\">\n        <attr name=\"name\" format=\"string\" />\n    </declare-styleable>\n\n\n    <declare-styleable name=\"AndroidManifestProfileable\" parent=\"AndroidManifestApplication\">\n        <!-- Flag indicating whether the application can be profiled by the shell user,\n             even when running on a device that is running in user mode. -->\n        <attr name=\"shell\" format=\"boolean\" />\n        <!-- Flag indicating whether the application can be profiled by system services, but not\n             necessarily via shell tools (for which also android:shell=\"true\" must be set). If\n             false, the application cannot be profiled at all. Defaults to true. -->\n        <attr name=\"enabled\" format=\"boolean\" />\n    </declare-styleable>\n\n    <!-- <code>install-constraints</code> tag rejects installs unless one the constraints defined by\n         its child elements is true.\n         It is possible to have multiple <code>install-constraints</code> tags in a single manifest,\n         where each tag is evaluated independently.\n         @hide -->\n    <declare-styleable name=\"AndroidManifestInstallConstraints\" parent=\"AndroidManifest\" />\n\n    <!-- A constraint for <code>install-constraints</code>. Checks that the device fingerprint\n         starts with the given prefix.\n         @hide -->\n    <declare-styleable name=\"AndroidManifestInstallConstraintsFingerprintPrefix\"\n                       parent=\"AndroidManifestInstallConstraints\">\n        <attr name=\"value\" />\n    </declare-styleable>\n</resources>\n"
  },
  {
    "path": "jadx-core/src/main/resources/android/res-map.txt",
    "content": "01010000=attr/theme\n01010001=attr/label\n01010002=attr/icon\n01010003=attr/name\n01010004=attr/manageSpaceActivity\n01010005=attr/allowClearUserData\n01010006=attr/permission\n01010007=attr/readPermission\n01010008=attr/writePermission\n01010009=attr/protectionLevel\n0101000a=attr/permissionGroup\n0101000b=attr/sharedUserId\n0101000c=attr/hasCode\n0101000d=attr/persistent\n0101000e=attr/enabled\n0101000f=attr/debuggable\n01010010=attr/exported\n01010011=attr/process\n01010012=attr/taskAffinity\n01010013=attr/multiprocess\n01010014=attr/finishOnTaskLaunch\n01010015=attr/clearTaskOnLaunch\n01010016=attr/stateNotNeeded\n01010017=attr/excludeFromRecents\n01010018=attr/authorities\n01010019=attr/syncable\n0101001a=attr/initOrder\n0101001b=attr/grantUriPermissions\n0101001c=attr/priority\n0101001d=attr/launchMode\n0101001e=attr/screenOrientation\n0101001f=attr/configChanges\n01010020=attr/description\n01010021=attr/targetPackage\n01010022=attr/handleProfiling\n01010023=attr/functionalTest\n01010024=attr/value\n01010025=attr/resource\n01010026=attr/mimeType\n01010027=attr/scheme\n01010028=attr/host\n01010029=attr/port\n0101002a=attr/path\n0101002b=attr/pathPrefix\n0101002c=attr/pathPattern\n0101002d=attr/action\n0101002e=attr/data\n0101002f=attr/targetClass\n01010030=attr/colorForeground\n01010031=attr/colorBackground\n01010032=attr/backgroundDimAmount\n01010033=attr/disabledAlpha\n01010034=attr/textAppearance\n01010035=attr/textAppearanceInverse\n01010036=attr/textColorPrimary\n01010037=attr/textColorPrimaryDisableOnly\n01010038=attr/textColorSecondary\n01010039=attr/textColorPrimaryInverse\n0101003a=attr/textColorSecondaryInverse\n0101003b=attr/textColorPrimaryNoDisable\n0101003c=attr/textColorSecondaryNoDisable\n0101003d=attr/textColorPrimaryInverseNoDisable\n0101003e=attr/textColorSecondaryInverseNoDisable\n0101003f=attr/textColorHintInverse\n01010040=attr/textAppearanceLarge\n01010041=attr/textAppearanceMedium\n01010042=attr/textAppearanceSmall\n01010043=attr/textAppearanceLargeInverse\n01010044=attr/textAppearanceMediumInverse\n01010045=attr/textAppearanceSmallInverse\n01010046=attr/textCheckMark\n01010047=attr/textCheckMarkInverse\n01010048=attr/buttonStyle\n01010049=attr/buttonStyleSmall\n0101004a=attr/buttonStyleInset\n0101004b=attr/buttonStyleToggle\n0101004c=attr/galleryItemBackground\n0101004d=attr/listPreferredItemHeight\n0101004e=attr/expandableListPreferredItemPaddingLeft\n0101004f=attr/expandableListPreferredChildPaddingLeft\n01010050=attr/expandableListPreferredItemIndicatorLeft\n01010051=attr/expandableListPreferredItemIndicatorRight\n01010052=attr/expandableListPreferredChildIndicatorLeft\n01010053=attr/expandableListPreferredChildIndicatorRight\n01010054=attr/windowBackground\n01010055=attr/windowFrame\n01010056=attr/windowNoTitle\n01010057=attr/windowIsFloating\n01010058=attr/windowIsTranslucent\n01010059=attr/windowContentOverlay\n0101005a=attr/windowTitleSize\n0101005b=attr/windowTitleStyle\n0101005c=attr/windowTitleBackgroundStyle\n0101005d=attr/alertDialogStyle\n0101005e=attr/panelBackground\n0101005f=attr/panelFullBackground\n01010060=attr/panelColorForeground\n01010061=attr/panelColorBackground\n01010062=attr/panelTextAppearance\n01010063=attr/scrollbarSize\n01010064=attr/scrollbarThumbHorizontal\n01010065=attr/scrollbarThumbVertical\n01010066=attr/scrollbarTrackHorizontal\n01010067=attr/scrollbarTrackVertical\n01010068=attr/scrollbarAlwaysDrawHorizontalTrack\n01010069=attr/scrollbarAlwaysDrawVerticalTrack\n0101006a=attr/absListViewStyle\n0101006b=attr/autoCompleteTextViewStyle\n0101006c=attr/checkboxStyle\n0101006d=attr/dropDownListViewStyle\n0101006e=attr/editTextStyle\n0101006f=attr/expandableListViewStyle\n01010070=attr/galleryStyle\n01010071=attr/gridViewStyle\n01010072=attr/imageButtonStyle\n01010073=attr/imageWellStyle\n01010074=attr/listViewStyle\n01010075=attr/listViewWhiteStyle\n01010076=attr/popupWindowStyle\n01010077=attr/progressBarStyle\n01010078=attr/progressBarStyleHorizontal\n01010079=attr/progressBarStyleSmall\n0101007a=attr/progressBarStyleLarge\n0101007b=attr/seekBarStyle\n0101007c=attr/ratingBarStyle\n0101007d=attr/ratingBarStyleSmall\n0101007e=attr/radioButtonStyle\n0101007f=attr/scrollbarStyle\n01010080=attr/scrollViewStyle\n01010081=attr/spinnerStyle\n01010082=attr/starStyle\n01010083=attr/tabWidgetStyle\n01010084=attr/textViewStyle\n01010085=attr/webViewStyle\n01010086=attr/dropDownItemStyle\n01010087=attr/spinnerDropDownItemStyle\n01010088=attr/dropDownHintAppearance\n01010089=attr/spinnerItemStyle\n0101008a=attr/mapViewStyle\n0101008b=attr/preferenceScreenStyle\n0101008c=attr/preferenceCategoryStyle\n0101008d=attr/preferenceInformationStyle\n0101008e=attr/preferenceStyle\n0101008f=attr/checkBoxPreferenceStyle\n01010090=attr/yesNoPreferenceStyle\n01010091=attr/dialogPreferenceStyle\n01010092=attr/editTextPreferenceStyle\n01010093=attr/ringtonePreferenceStyle\n01010094=attr/preferenceLayoutChild\n01010095=attr/textSize\n01010096=attr/typeface\n01010097=attr/textStyle\n01010098=attr/textColor\n01010099=attr/textColorHighlight\n0101009a=attr/textColorHint\n0101009b=attr/textColorLink\n0101009c=attr/state_focused\n0101009d=attr/state_window_focused\n0101009e=attr/state_enabled\n0101009f=attr/state_checkable\n010100a0=attr/state_checked\n010100a1=attr/state_selected\n010100a2=attr/state_active\n010100a3=attr/state_single\n010100a4=attr/state_first\n010100a5=attr/state_middle\n010100a6=attr/state_last\n010100a7=attr/state_pressed\n010100a8=attr/state_expanded\n010100a9=attr/state_empty\n010100aa=attr/state_above_anchor\n010100ab=attr/ellipsize\n010100ac=attr/x\n010100ad=attr/y\n010100ae=attr/windowAnimationStyle\n010100af=attr/gravity\n010100b0=attr/autoLink\n010100b1=attr/linksClickable\n010100b2=attr/entries\n010100b3=attr/layout_gravity\n010100b4=attr/windowEnterAnimation\n010100b5=attr/windowExitAnimation\n010100b6=attr/windowShowAnimation\n010100b7=attr/windowHideAnimation\n010100b8=attr/activityOpenEnterAnimation\n010100b9=attr/activityOpenExitAnimation\n010100ba=attr/activityCloseEnterAnimation\n010100bb=attr/activityCloseExitAnimation\n010100bc=attr/taskOpenEnterAnimation\n010100bd=attr/taskOpenExitAnimation\n010100be=attr/taskCloseEnterAnimation\n010100bf=attr/taskCloseExitAnimation\n010100c0=attr/taskToFrontEnterAnimation\n010100c1=attr/taskToFrontExitAnimation\n010100c2=attr/taskToBackEnterAnimation\n010100c3=attr/taskToBackExitAnimation\n010100c4=attr/orientation\n010100c5=attr/keycode\n010100c6=attr/fullDark\n010100c7=attr/topDark\n010100c8=attr/centerDark\n010100c9=attr/bottomDark\n010100ca=attr/fullBright\n010100cb=attr/topBright\n010100cc=attr/centerBright\n010100cd=attr/bottomBright\n010100ce=attr/bottomMedium\n010100cf=attr/centerMedium\n010100d0=attr/id\n010100d1=attr/tag\n010100d2=attr/scrollX\n010100d3=attr/scrollY\n010100d4=attr/background\n010100d5=attr/padding\n010100d6=attr/paddingLeft\n010100d7=attr/paddingTop\n010100d8=attr/paddingRight\n010100d9=attr/paddingBottom\n010100da=attr/focusable\n010100db=attr/focusableInTouchMode\n010100dc=attr/visibility\n010100dd=attr/fitsSystemWindows\n010100de=attr/scrollbars\n010100df=attr/fadingEdge\n010100e0=attr/fadingEdgeLength\n010100e1=attr/nextFocusLeft\n010100e2=attr/nextFocusRight\n010100e3=attr/nextFocusUp\n010100e4=attr/nextFocusDown\n010100e5=attr/clickable\n010100e6=attr/longClickable\n010100e7=attr/saveEnabled\n010100e8=attr/drawingCacheQuality\n010100e9=attr/duplicateParentState\n010100ea=attr/clipChildren\n010100eb=attr/clipToPadding\n010100ec=attr/layoutAnimation\n010100ed=attr/animationCache\n010100ee=attr/persistentDrawingCache\n010100ef=attr/alwaysDrawnWithCache\n010100f0=attr/addStatesFromChildren\n010100f1=attr/descendantFocusability\n010100f2=attr/layout\n010100f3=attr/inflatedId\n010100f4=attr/layout_width\n010100f5=attr/layout_height\n010100f6=attr/layout_margin\n010100f7=attr/layout_marginLeft\n010100f8=attr/layout_marginTop\n010100f9=attr/layout_marginRight\n010100fa=attr/layout_marginBottom\n010100fb=attr/listSelector\n010100fc=attr/drawSelectorOnTop\n010100fd=attr/stackFromBottom\n010100fe=attr/scrollingCache\n010100ff=attr/textFilterEnabled\n01010100=attr/transcriptMode\n01010101=attr/cacheColorHint\n01010102=attr/dial\n01010103=attr/hand_hour\n01010104=attr/hand_minute\n01010105=attr/format\n01010106=attr/checked\n01010107=attr/button\n01010108=attr/checkMark\n01010109=attr/foreground\n0101010a=attr/measureAllChildren\n0101010b=attr/groupIndicator\n0101010c=attr/childIndicator\n0101010d=attr/indicatorLeft\n0101010e=attr/indicatorRight\n0101010f=attr/childIndicatorLeft\n01010110=attr/childIndicatorRight\n01010111=attr/childDivider\n01010112=attr/animationDuration\n01010113=attr/spacing\n01010114=attr/horizontalSpacing\n01010115=attr/verticalSpacing\n01010116=attr/stretchMode\n01010117=attr/columnWidth\n01010118=attr/numColumns\n01010119=attr/src\n0101011a=attr/antialias\n0101011b=attr/filter\n0101011c=attr/dither\n0101011d=attr/scaleType\n0101011e=attr/adjustViewBounds\n0101011f=attr/maxWidth\n01010120=attr/maxHeight\n01010121=attr/tint\n01010122=attr/baselineAlignBottom\n01010123=attr/cropToPadding\n01010124=attr/textOn\n01010125=attr/textOff\n01010126=attr/baselineAligned\n01010127=attr/baselineAlignedChildIndex\n01010128=attr/weightSum\n01010129=attr/divider\n0101012a=attr/dividerHeight\n0101012b=attr/choiceMode\n0101012c=attr/itemTextAppearance\n0101012d=attr/horizontalDivider\n0101012e=attr/verticalDivider\n0101012f=attr/headerBackground\n01010130=attr/itemBackground\n01010131=attr/itemIconDisabledAlpha\n01010132=attr/rowHeight\n01010133=attr/maxRows\n01010134=attr/maxItemsPerRow\n01010135=attr/moreIcon\n01010136=attr/max\n01010137=attr/progress\n01010138=attr/secondaryProgress\n01010139=attr/indeterminate\n0101013a=attr/indeterminateOnly\n0101013b=attr/indeterminateDrawable\n0101013c=attr/progressDrawable\n0101013d=attr/indeterminateDuration\n0101013e=attr/indeterminateBehavior\n0101013f=attr/minWidth\n01010140=attr/minHeight\n01010141=attr/interpolator\n01010142=attr/thumb\n01010143=attr/thumbOffset\n01010144=attr/numStars\n01010145=attr/rating\n01010146=attr/stepSize\n01010147=attr/isIndicator\n01010148=attr/checkedButton\n01010149=attr/stretchColumns\n0101014a=attr/shrinkColumns\n0101014b=attr/collapseColumns\n0101014c=attr/layout_column\n0101014d=attr/layout_span\n0101014e=attr/bufferType\n0101014f=attr/text\n01010150=attr/hint\n01010151=attr/textScaleX\n01010152=attr/cursorVisible\n01010153=attr/maxLines\n01010154=attr/lines\n01010155=attr/height\n01010156=attr/minLines\n01010157=attr/maxEms\n01010158=attr/ems\n01010159=attr/width\n0101015a=attr/minEms\n0101015b=attr/scrollHorizontally\n0101015c=attr/password\n0101015d=attr/singleLine\n0101015e=attr/selectAllOnFocus\n0101015f=attr/includeFontPadding\n01010160=attr/maxLength\n01010161=attr/shadowColor\n01010162=attr/shadowDx\n01010163=attr/shadowDy\n01010164=attr/shadowRadius\n01010165=attr/numeric\n01010166=attr/digits\n01010167=attr/phoneNumber\n01010168=attr/inputMethod\n01010169=attr/capitalize\n0101016a=attr/autoText\n0101016b=attr/editable\n0101016c=attr/freezesText\n0101016d=attr/drawableTop\n0101016e=attr/drawableBottom\n0101016f=attr/drawableLeft\n01010170=attr/drawableRight\n01010171=attr/drawablePadding\n01010172=attr/completionHint\n01010173=attr/completionHintView\n01010174=attr/completionThreshold\n01010175=attr/dropDownSelector\n01010176=attr/popupBackground\n01010177=attr/inAnimation\n01010178=attr/outAnimation\n01010179=attr/flipInterval\n0101017a=attr/fillViewport\n0101017b=attr/prompt\n0101017c=attr/startYear\n0101017d=attr/endYear\n0101017e=attr/mode\n0101017f=attr/layout_x\n01010180=attr/layout_y\n01010181=attr/layout_weight\n01010182=attr/layout_toLeftOf\n01010183=attr/layout_toRightOf\n01010184=attr/layout_above\n01010185=attr/layout_below\n01010186=attr/layout_alignBaseline\n01010187=attr/layout_alignLeft\n01010188=attr/layout_alignTop\n01010189=attr/layout_alignRight\n0101018a=attr/layout_alignBottom\n0101018b=attr/layout_alignParentLeft\n0101018c=attr/layout_alignParentTop\n0101018d=attr/layout_alignParentRight\n0101018e=attr/layout_alignParentBottom\n0101018f=attr/layout_centerInParent\n01010190=attr/layout_centerHorizontal\n01010191=attr/layout_centerVertical\n01010192=attr/layout_alignWithParentIfMissing\n01010193=attr/layout_scale\n01010194=attr/visible\n01010195=attr/variablePadding\n01010196=attr/constantSize\n01010197=attr/oneshot\n01010198=attr/duration\n01010199=attr/drawable\n0101019a=attr/shape\n0101019b=attr/innerRadiusRatio\n0101019c=attr/thicknessRatio\n0101019d=attr/startColor\n0101019e=attr/endColor\n0101019f=attr/useLevel\n010101a0=attr/angle\n010101a1=attr/type\n010101a2=attr/centerX\n010101a3=attr/centerY\n010101a4=attr/gradientRadius\n010101a5=attr/color\n010101a6=attr/dashWidth\n010101a7=attr/dashGap\n010101a8=attr/radius\n010101a9=attr/topLeftRadius\n010101aa=attr/topRightRadius\n010101ab=attr/bottomLeftRadius\n010101ac=attr/bottomRightRadius\n010101ad=attr/left\n010101ae=attr/top\n010101af=attr/right\n010101b0=attr/bottom\n010101b1=attr/minLevel\n010101b2=attr/maxLevel\n010101b3=attr/fromDegrees\n010101b4=attr/toDegrees\n010101b5=attr/pivotX\n010101b6=attr/pivotY\n010101b7=attr/insetLeft\n010101b8=attr/insetRight\n010101b9=attr/insetTop\n010101ba=attr/insetBottom\n010101bb=attr/shareInterpolator\n010101bc=attr/fillBefore\n010101bd=attr/fillAfter\n010101be=attr/startOffset\n010101bf=attr/repeatCount\n010101c0=attr/repeatMode\n010101c1=attr/zAdjustment\n010101c2=attr/fromXScale\n010101c3=attr/toXScale\n010101c4=attr/fromYScale\n010101c5=attr/toYScale\n010101c6=attr/fromXDelta\n010101c7=attr/toXDelta\n010101c8=attr/fromYDelta\n010101c9=attr/toYDelta\n010101ca=attr/fromAlpha\n010101cb=attr/toAlpha\n010101cc=attr/delay\n010101cd=attr/animation\n010101ce=attr/animationOrder\n010101cf=attr/columnDelay\n010101d0=attr/rowDelay\n010101d1=attr/direction\n010101d2=attr/directionPriority\n010101d3=attr/factor\n010101d4=attr/cycles\n010101d5=attr/searchMode\n010101d6=attr/searchSuggestAuthority\n010101d7=attr/searchSuggestPath\n010101d8=attr/searchSuggestSelection\n010101d9=attr/searchSuggestIntentAction\n010101da=attr/searchSuggestIntentData\n010101db=attr/queryActionMsg\n010101dc=attr/suggestActionMsg\n010101dd=attr/suggestActionMsgColumn\n010101de=attr/menuCategory\n010101df=attr/orderInCategory\n010101e0=attr/checkableBehavior\n010101e1=attr/title\n010101e2=attr/titleCondensed\n010101e3=attr/alphabeticShortcut\n010101e4=attr/numericShortcut\n010101e5=attr/checkable\n010101e6=attr/selectable\n010101e7=attr/orderingFromXml\n010101e8=attr/key\n010101e9=attr/summary\n010101ea=attr/order\n010101eb=attr/widgetLayout\n010101ec=attr/dependency\n010101ed=attr/defaultValue\n010101ee=attr/shouldDisableView\n010101ef=attr/summaryOn\n010101f0=attr/summaryOff\n010101f1=attr/disableDependentsState\n010101f2=attr/dialogTitle\n010101f3=attr/dialogMessage\n010101f4=attr/dialogIcon\n010101f5=attr/positiveButtonText\n010101f6=attr/negativeButtonText\n010101f7=attr/dialogLayout\n010101f8=attr/entryValues\n010101f9=attr/ringtoneType\n010101fa=attr/showDefault\n010101fb=attr/showSilent\n010101fc=attr/scaleWidth\n010101fd=attr/scaleHeight\n010101fe=attr/scaleGravity\n010101ff=attr/ignoreGravity\n01010200=attr/foregroundGravity\n01010201=attr/tileMode\n01010202=attr/targetActivity\n01010203=attr/alwaysRetainTaskState\n01010204=attr/allowTaskReparenting\n01010205=attr/searchButtonText\n01010206=attr/colorForegroundInverse\n01010207=attr/textAppearanceButton\n01010208=attr/listSeparatorTextViewStyle\n01010209=attr/streamType\n0101020a=attr/clipOrientation\n0101020b=attr/centerColor\n0101020c=attr/minSdkVersion\n0101020d=attr/windowFullscreen\n0101020e=attr/unselectedAlpha\n0101020f=attr/progressBarStyleSmallTitle\n01010210=attr/ratingBarStyleIndicator\n01010211=attr/apiKey\n01010212=attr/textColorTertiary\n01010213=attr/textColorTertiaryInverse\n01010214=attr/listDivider\n01010215=attr/soundEffectsEnabled\n01010216=attr/keepScreenOn\n01010217=attr/lineSpacingExtra\n01010218=attr/lineSpacingMultiplier\n01010219=attr/listChoiceIndicatorSingle\n0101021a=attr/listChoiceIndicatorMultiple\n0101021b=attr/versionCode\n0101021c=attr/versionName\n0101021d=attr/marqueeRepeatLimit\n0101021e=attr/windowNoDisplay\n0101021f=attr/backgroundDimEnabled\n01010220=attr/inputType\n01010221=attr/isDefault\n01010222=attr/windowDisablePreview\n01010223=attr/privateImeOptions\n01010224=attr/editorExtras\n01010225=attr/settingsActivity\n01010226=attr/fastScrollEnabled\n01010227=attr/reqTouchScreen\n01010228=attr/reqKeyboardType\n01010229=attr/reqHardKeyboard\n0101022a=attr/reqNavigation\n0101022b=attr/windowSoftInputMode\n0101022c=attr/imeFullscreenBackground\n0101022d=attr/noHistory\n0101022e=attr/headerDividersEnabled\n0101022f=attr/footerDividersEnabled\n01010230=attr/candidatesTextStyleSpans\n01010231=attr/smoothScrollbar\n01010232=attr/reqFiveWayNav\n01010233=attr/keyBackground\n01010234=attr/keyTextSize\n01010235=attr/labelTextSize\n01010236=attr/keyTextColor\n01010237=attr/keyPreviewLayout\n01010238=attr/keyPreviewOffset\n01010239=attr/keyPreviewHeight\n0101023a=attr/verticalCorrection\n0101023b=attr/popupLayout\n0101023c=attr/state_long_pressable\n0101023d=attr/keyWidth\n0101023e=attr/keyHeight\n0101023f=attr/horizontalGap\n01010240=attr/verticalGap\n01010241=attr/rowEdgeFlags\n01010242=attr/codes\n01010243=attr/popupKeyboard\n01010244=attr/popupCharacters\n01010245=attr/keyEdgeFlags\n01010246=attr/isModifier\n01010247=attr/isSticky\n01010248=attr/isRepeatable\n01010249=attr/iconPreview\n0101024a=attr/keyOutputText\n0101024b=attr/keyLabel\n0101024c=attr/keyIcon\n0101024d=attr/keyboardMode\n0101024e=attr/isScrollContainer\n0101024f=attr/fillEnabled\n01010250=attr/updatePeriodMillis\n01010251=attr/initialLayout\n01010252=attr/voiceSearchMode\n01010253=attr/voiceLanguageModel\n01010254=attr/voicePromptText\n01010255=attr/voiceLanguage\n01010256=attr/voiceMaxResults\n01010257=attr/bottomOffset\n01010258=attr/topOffset\n01010259=attr/allowSingleTap\n0101025a=attr/handle\n0101025b=attr/content\n0101025c=attr/animateOnClick\n0101025d=attr/configure\n0101025e=attr/hapticFeedbackEnabled\n0101025f=attr/innerRadius\n01010260=attr/thickness\n01010261=attr/sharedUserLabel\n01010262=attr/dropDownWidth\n01010263=attr/dropDownAnchor\n01010264=attr/imeOptions\n01010265=attr/imeActionLabel\n01010266=attr/imeActionId\n01010267=attr/textColorPrimaryActivated\n01010268=attr/imeExtractEnterAnimation\n01010269=attr/imeExtractExitAnimation\n0101026a=attr/tension\n0101026b=attr/extraTension\n0101026c=attr/anyDensity\n0101026d=attr/searchSuggestThreshold\n0101026e=attr/includeInGlobalSearch\n0101026f=attr/onClick\n01010270=attr/targetSdkVersion\n01010271=attr/maxSdkVersion\n01010272=attr/testOnly\n01010273=attr/contentDescription\n01010274=attr/gestureStrokeWidth\n01010275=attr/gestureColor\n01010276=attr/uncertainGestureColor\n01010277=attr/fadeOffset\n01010278=attr/fadeDuration\n01010279=attr/gestureStrokeType\n0101027a=attr/gestureStrokeLengthThreshold\n0101027b=attr/gestureStrokeSquarenessThreshold\n0101027c=attr/gestureStrokeAngleThreshold\n0101027d=attr/eventsInterceptionEnabled\n0101027e=attr/fadeEnabled\n0101027f=attr/backupAgent\n01010280=attr/allowBackup\n01010281=attr/glEsVersion\n01010282=attr/queryAfterZeroResults\n01010283=attr/dropDownHeight\n01010284=attr/smallScreens\n01010285=attr/normalScreens\n01010286=attr/largeScreens\n01010287=attr/progressBarStyleInverse\n01010288=attr/progressBarStyleSmallInverse\n01010289=attr/progressBarStyleLargeInverse\n0101028a=attr/searchSettingsDescription\n0101028b=attr/textColorPrimaryInverseDisableOnly\n0101028c=attr/autoUrlDetect\n0101028d=attr/resizeable\n0101028e=attr/required\n0101028f=attr/accountType\n01010290=attr/contentAuthority\n01010291=attr/userVisible\n01010292=attr/windowShowWallpaper\n01010293=attr/wallpaperOpenEnterAnimation\n01010294=attr/wallpaperOpenExitAnimation\n01010295=attr/wallpaperCloseEnterAnimation\n01010296=attr/wallpaperCloseExitAnimation\n01010297=attr/wallpaperIntraOpenEnterAnimation\n01010298=attr/wallpaperIntraOpenExitAnimation\n01010299=attr/wallpaperIntraCloseEnterAnimation\n0101029a=attr/wallpaperIntraCloseExitAnimation\n0101029b=attr/supportsUploading\n0101029c=attr/killAfterRestore\n0101029d=attr/restoreNeedsApplication\n0101029e=attr/smallIcon\n0101029f=attr/accountPreferences\n010102a0=attr/textAppearanceSearchResultSubtitle\n010102a1=attr/textAppearanceSearchResultTitle\n010102a2=attr/summaryColumn\n010102a3=attr/detailColumn\n010102a4=attr/detailSocialSummary\n010102a5=attr/thumbnail\n010102a6=attr/detachWallpaper\n010102a7=attr/finishOnCloseSystemDialogs\n010102a8=attr/scrollbarFadeDuration\n010102a9=attr/scrollbarDefaultDelayBeforeFade\n010102aa=attr/fadeScrollbars\n010102ab=attr/colorBackgroundCacheHint\n010102ac=attr/dropDownHorizontalOffset\n010102ad=attr/dropDownVerticalOffset\n010102ae=attr/quickContactBadgeStyleWindowSmall\n010102af=attr/quickContactBadgeStyleWindowMedium\n010102b0=attr/quickContactBadgeStyleWindowLarge\n010102b1=attr/quickContactBadgeStyleSmallWindowSmall\n010102b2=attr/quickContactBadgeStyleSmallWindowMedium\n010102b3=attr/quickContactBadgeStyleSmallWindowLarge\n010102b4=attr/author\n010102b5=attr/autoStart\n010102b6=attr/expandableListViewWhiteStyle\n010102b7=attr/installLocation\n010102b8=attr/vmSafeMode\n010102b9=attr/webTextViewStyle\n010102ba=attr/restoreAnyVersion\n010102bb=attr/tabStripLeft\n010102bc=attr/tabStripRight\n010102bd=attr/tabStripEnabled\n010102be=attr/logo\n010102bf=attr/xlargeScreens\n010102c0=attr/immersive\n010102c1=attr/overScrollMode\n010102c2=attr/overScrollHeader\n010102c3=attr/overScrollFooter\n010102c4=attr/filterTouchesWhenObscured\n010102c5=attr/textSelectHandleLeft\n010102c6=attr/textSelectHandleRight\n010102c7=attr/textSelectHandle\n010102c8=attr/textSelectHandleWindowStyle\n010102c9=attr/popupAnimationStyle\n010102ca=attr/screenSize\n010102cb=attr/screenDensity\n010102cc=attr/allContactsName\n010102cd=attr/windowActionBar\n010102ce=attr/actionBarStyle\n010102cf=attr/navigationMode\n010102d0=attr/displayOptions\n010102d1=attr/subtitle\n010102d2=attr/customNavigationLayout\n010102d3=attr/hardwareAccelerated\n010102d4=attr/measureWithLargestChild\n010102d5=attr/animateFirstView\n010102d6=attr/dropDownSpinnerStyle\n010102d7=attr/actionDropDownStyle\n010102d8=attr/actionButtonStyle\n010102d9=attr/showAsAction\n010102da=attr/previewImage\n010102db=attr/actionModeBackground\n010102dc=attr/actionModeCloseDrawable\n010102dd=attr/windowActionModeOverlay\n010102de=attr/valueFrom\n010102df=attr/valueTo\n010102e0=attr/valueType\n010102e1=attr/propertyName\n010102e2=attr/ordering\n010102e3=attr/fragment\n010102e4=attr/windowActionBarOverlay\n010102e5=attr/fragmentOpenEnterAnimation\n010102e6=attr/fragmentOpenExitAnimation\n010102e7=attr/fragmentCloseEnterAnimation\n010102e8=attr/fragmentCloseExitAnimation\n010102e9=attr/fragmentFadeEnterAnimation\n010102ea=attr/fragmentFadeExitAnimation\n010102eb=attr/actionBarSize\n010102ec=attr/imeSubtypeLocale\n010102ed=attr/imeSubtypeMode\n010102ee=attr/imeSubtypeExtraValue\n010102ef=attr/splitMotionEvents\n010102f0=attr/listChoiceBackgroundIndicator\n010102f1=attr/spinnerMode\n010102f2=attr/animateLayoutChanges\n010102f3=attr/actionBarTabStyle\n010102f4=attr/actionBarTabBarStyle\n010102f5=attr/actionBarTabTextStyle\n010102f6=attr/actionOverflowButtonStyle\n010102f7=attr/actionModeCloseButtonStyle\n010102f8=attr/titleTextStyle\n010102f9=attr/subtitleTextStyle\n010102fa=attr/iconifiedByDefault\n010102fb=attr/actionLayout\n010102fc=attr/actionViewClass\n010102fd=attr/activatedBackgroundIndicator\n010102fe=attr/state_activated\n010102ff=attr/listPopupWindowStyle\n01010300=attr/popupMenuStyle\n01010301=attr/textAppearanceLargePopupMenu\n01010302=attr/textAppearanceSmallPopupMenu\n01010303=attr/breadCrumbTitle\n01010304=attr/breadCrumbShortTitle\n01010305=attr/listDividerAlertDialog\n01010306=attr/textColorAlertDialogListItem\n01010307=attr/loopViews\n01010308=attr/dialogTheme\n01010309=attr/alertDialogTheme\n0101030a=attr/dividerVertical\n0101030b=attr/homeAsUpIndicator\n0101030c=attr/enterFadeDuration\n0101030d=attr/exitFadeDuration\n0101030e=attr/selectableItemBackground\n0101030f=attr/autoAdvanceViewId\n01010310=attr/useIntrinsicSizeAsMinimum\n01010311=attr/actionModeCutDrawable\n01010312=attr/actionModeCopyDrawable\n01010313=attr/actionModePasteDrawable\n01010314=attr/textEditPasteWindowLayout\n01010315=attr/textEditNoPasteWindowLayout\n01010316=attr/textIsSelectable\n01010317=attr/windowEnableSplitTouch\n01010318=attr/indeterminateProgressStyle\n01010319=attr/progressBarPadding\n0101031a=attr/animationResolution\n0101031b=attr/state_accelerated\n0101031c=attr/baseline\n0101031d=attr/homeLayout\n0101031e=attr/opacity\n0101031f=attr/alpha\n01010320=attr/transformPivotX\n01010321=attr/transformPivotY\n01010322=attr/translationX\n01010323=attr/translationY\n01010324=attr/scaleX\n01010325=attr/scaleY\n01010326=attr/rotation\n01010327=attr/rotationX\n01010328=attr/rotationY\n01010329=attr/showDividers\n0101032a=attr/dividerPadding\n0101032b=attr/borderlessButtonStyle\n0101032c=attr/dividerHorizontal\n0101032d=attr/itemPadding\n0101032e=attr/buttonBarStyle\n0101032f=attr/buttonBarButtonStyle\n01010330=attr/segmentedButtonStyle\n01010331=attr/staticWallpaperPreview\n01010332=attr/allowParallelSyncs\n01010333=attr/isAlwaysSyncable\n01010334=attr/verticalScrollbarPosition\n01010335=attr/fastScrollAlwaysVisible\n01010336=attr/fastScrollThumbDrawable\n01010337=attr/fastScrollPreviewBackgroundLeft\n01010338=attr/fastScrollPreviewBackgroundRight\n01010339=attr/fastScrollTrackDrawable\n0101033a=attr/fastScrollOverlayPosition\n0101033b=attr/customTokens\n0101033c=attr/nextFocusForward\n0101033d=attr/firstDayOfWeek\n0101033e=attr/showWeekNumber\n0101033f=attr/minDate\n01010340=attr/maxDate\n01010341=attr/shownWeekCount\n01010342=attr/selectedWeekBackgroundColor\n01010343=attr/focusedMonthDateColor\n01010344=attr/unfocusedMonthDateColor\n01010345=attr/weekNumberColor\n01010346=attr/weekSeparatorLineColor\n01010347=attr/selectedDateVerticalBar\n01010348=attr/weekDayTextAppearance\n01010349=attr/dateTextAppearance\n0101034a=attr/solidColor\n0101034b=attr/spinnersShown\n0101034c=attr/calendarViewShown\n0101034d=attr/state_multiline\n0101034e=attr/detailsElementBackground\n0101034f=attr/textColorHighlightInverse\n01010350=attr/textColorLinkInverse\n01010351=attr/editTextColor\n01010352=attr/editTextBackground\n01010353=attr/horizontalScrollViewStyle\n01010354=attr/layerType\n01010355=attr/alertDialogIcon\n01010356=attr/windowMinWidthMajor\n01010357=attr/windowMinWidthMinor\n01010358=attr/queryHint\n01010359=attr/fastScrollTextColor\n0101035a=attr/largeHeap\n0101035b=attr/windowCloseOnTouchOutside\n0101035c=attr/datePickerStyle\n0101035d=attr/calendarViewStyle\n0101035e=attr/textEditSidePasteWindowLayout\n0101035f=attr/textEditSideNoPasteWindowLayout\n01010360=attr/actionMenuTextAppearance\n01010361=attr/actionMenuTextColor\n01010362=attr/textCursorDrawable\n01010363=attr/resizeMode\n01010364=attr/requiresSmallestWidthDp\n01010365=attr/compatibleWidthLimitDp\n01010366=attr/largestWidthLimitDp\n01010367=attr/state_hovered\n01010368=attr/state_drag_can_accept\n01010369=attr/state_drag_hovered\n0101036a=attr/stopWithTask\n0101036b=attr/switchTextOn\n0101036c=attr/switchTextOff\n0101036d=attr/switchPreferenceStyle\n0101036e=attr/switchTextAppearance\n0101036f=attr/track\n01010370=attr/switchMinWidth\n01010371=attr/switchPadding\n01010372=attr/thumbTextPadding\n01010373=attr/textSuggestionsWindowStyle\n01010374=attr/textEditSuggestionItemLayout\n01010375=attr/rowCount\n01010376=attr/rowOrderPreserved\n01010377=attr/columnCount\n01010378=attr/columnOrderPreserved\n01010379=attr/useDefaultMargins\n0101037a=attr/alignmentMode\n0101037b=attr/layout_row\n0101037c=attr/layout_rowSpan\n0101037d=attr/layout_columnSpan\n0101037e=attr/actionModeSelectAllDrawable\n0101037f=attr/isAuxiliary\n01010380=attr/accessibilityEventTypes\n01010381=attr/packageNames\n01010382=attr/accessibilityFeedbackType\n01010383=attr/notificationTimeout\n01010384=attr/accessibilityFlags\n01010385=attr/canRetrieveWindowContent\n01010386=attr/listPreferredItemHeightLarge\n01010387=attr/listPreferredItemHeightSmall\n01010388=attr/actionBarSplitStyle\n01010389=attr/actionProviderClass\n0101038a=attr/backgroundStacked\n0101038b=attr/backgroundSplit\n0101038c=attr/textAllCaps\n0101038d=attr/colorPressedHighlight\n0101038e=attr/colorLongPressedHighlight\n0101038f=attr/colorFocusedHighlight\n01010390=attr/colorActivatedHighlight\n01010391=attr/colorMultiSelectHighlight\n01010392=attr/drawableStart\n01010393=attr/drawableEnd\n01010394=attr/actionModeStyle\n01010395=attr/minResizeWidth\n01010396=attr/minResizeHeight\n01010397=attr/actionBarWidgetTheme\n01010398=attr/uiOptions\n01010399=attr/subtypeLocale\n0101039a=attr/subtypeExtraValue\n0101039b=attr/actionBarDivider\n0101039c=attr/actionBarItemBackground\n0101039d=attr/actionModeSplitBackground\n0101039e=attr/textAppearanceListItem\n0101039f=attr/textAppearanceListItemSmall\n010103a0=attr/targetDescriptions\n010103a1=attr/directionDescriptions\n010103a2=attr/overridesImplicitlyEnabledSubtype\n010103a3=attr/listPreferredItemPaddingLeft\n010103a4=attr/listPreferredItemPaddingRight\n010103a5=attr/requiresFadingEdge\n010103a6=attr/publicKey\n010103a7=attr/parentActivityName\n010103a8=attr/textColorSecondaryActivated\n010103a9=attr/isolatedProcess\n010103aa=attr/importantForAccessibility\n010103ab=attr/keyboardLayout\n010103ac=attr/fontFamily\n010103ad=attr/mediaRouteButtonStyle\n010103ae=attr/mediaRouteTypes\n010103af=attr/supportsRtl\n010103b0=attr/textDirection\n010103b1=attr/textAlignment\n010103b2=attr/layoutDirection\n010103b3=attr/paddingStart\n010103b4=attr/paddingEnd\n010103b5=attr/layout_marginStart\n010103b6=attr/layout_marginEnd\n010103b7=attr/layout_toStartOf\n010103b8=attr/layout_toEndOf\n010103b9=attr/layout_alignStart\n010103ba=attr/layout_alignEnd\n010103bb=attr/layout_alignParentStart\n010103bc=attr/layout_alignParentEnd\n010103bd=attr/listPreferredItemPaddingStart\n010103be=attr/listPreferredItemPaddingEnd\n010103bf=attr/singleUser\n010103c0=attr/presentationTheme\n010103c1=attr/subtypeId\n010103c2=attr/initialKeyguardLayout\n010103c3=attr/textColorSearchUrl\n010103c4=attr/widgetCategory\n010103c5=attr/permissionGroupFlags\n010103c6=attr/labelFor\n010103c7=attr/permissionFlags\n010103c8=attr/checkedTextViewStyle\n010103c9=attr/showOnLockScreen\n010103ca=attr/format12Hour\n010103cb=attr/format24Hour\n010103cc=attr/timeZone\n010103cd=attr/mipMap\n010103ce=attr/mirrorForRtl\n010103cf=attr/windowOverscan\n010103d0=attr/requiredForAllUsers\n010103d1=attr/indicatorStart\n010103d2=attr/indicatorEnd\n010103d3=attr/childIndicatorStart\n010103d4=attr/childIndicatorEnd\n010103d5=attr/restrictedAccountType\n010103d6=attr/requiredAccountType\n010103d7=attr/canRequestTouchExplorationMode\n010103d8=attr/canRequestEnhancedWebAccessibility\n010103d9=attr/canRequestFilterKeyEvents\n010103da=attr/layoutMode\n010103db=attr/keySet\n010103dc=attr/targetId\n010103dd=attr/fromScene\n010103de=attr/toScene\n010103df=attr/transition\n010103e0=attr/transitionOrdering\n010103e1=attr/fadingMode\n010103e2=attr/startDelay\n010103e3=attr/ssp\n010103e4=attr/sspPrefix\n010103e5=attr/sspPattern\n010103e6=attr/addPrintersActivity\n010103e7=attr/vendor\n010103e8=attr/category\n010103e9=attr/isAsciiCapable\n010103ea=attr/autoMirrored\n010103eb=attr/supportsSwitchingToNextInputMethod\n010103ec=attr/requireDeviceUnlock\n010103ed=attr/apduServiceBanner\n010103ee=attr/accessibilityLiveRegion\n010103ef=attr/windowTranslucentStatus\n010103f0=attr/windowTranslucentNavigation\n010103f1=attr/advancedPrintOptionsActivity\n010103f2=attr/banner\n010103f3=attr/windowSwipeToDismiss\n010103f4=attr/isGame\n010103f5=attr/allowEmbedded\n010103f6=attr/setupActivity\n010103f7=attr/fastScrollStyle\n010103f8=attr/windowContentTransitions\n010103f9=attr/windowContentTransitionManager\n010103fa=attr/translationZ\n010103fb=attr/tintMode\n010103fc=attr/controlX1\n010103fd=attr/controlY1\n010103fe=attr/controlX2\n010103ff=attr/controlY2\n01010400=attr/transitionName\n01010401=attr/transitionGroup\n01010402=attr/viewportWidth\n01010403=attr/viewportHeight\n01010404=attr/fillColor\n01010405=attr/pathData\n01010406=attr/strokeColor\n01010407=attr/strokeWidth\n01010408=attr/trimPathStart\n01010409=attr/trimPathEnd\n0101040a=attr/trimPathOffset\n0101040b=attr/strokeLineCap\n0101040c=attr/strokeLineJoin\n0101040d=attr/strokeMiterLimit\n0101040e=attr/searchWidgetCorpusItemBackground\n0101040f=attr/textAppearanceEasyCorrectSuggestion\n01010410=attr/textAppearanceMisspelledSuggestion\n01010411=attr/textAppearanceAutoCorrectionSuggestion\n01010412=attr/textUnderlineColor\n01010413=attr/textUnderlineThickness\n01010414=attr/errorMessageBackground\n01010415=attr/errorMessageAboveBackground\n01010416=attr/searchResultListItemHeight\n01010417=attr/dropdownListPreferredItemHeight\n01010418=attr/windowBackgroundFallback\n01010419=attr/windowActionBarFullscreenDecorLayout\n0101041a=attr/alertDialogButtonGroupStyle\n0101041b=attr/alertDialogCenterButtons\n0101041c=attr/panelMenuIsCompact\n0101041d=attr/panelMenuListWidth\n0101041e=attr/panelMenuListTheme\n0101041f=attr/gestureOverlayViewStyle\n01010420=attr/quickContactBadgeOverlay\n01010421=attr/fragmentBreadCrumbsStyle\n01010422=attr/numberPickerStyle\n01010423=attr/activityChooserViewStyle\n01010424=attr/actionModePopupWindowStyle\n01010425=attr/preferenceActivityStyle\n01010426=attr/preferenceFragmentStyle\n01010427=attr/preferencePanelStyle\n01010428=attr/preferenceHeaderPanelStyle\n01010429=attr/colorControlNormal\n0101042a=attr/colorControlActivated\n0101042b=attr/colorButtonNormal\n0101042c=attr/colorControlHighlight\n0101042d=attr/persistableMode\n0101042e=attr/titleTextAppearance\n0101042f=attr/subtitleTextAppearance\n01010430=attr/slideEdge\n01010431=attr/actionBarTheme\n01010432=attr/textAppearanceListItemSecondary\n01010433=attr/colorPrimary\n01010434=attr/colorPrimaryDark\n01010435=attr/colorAccent\n01010436=attr/nestedScrollingEnabled\n01010437=attr/windowEnterTransition\n01010438=attr/windowExitTransition\n01010439=attr/windowSharedElementEnterTransition\n0101043a=attr/windowSharedElementExitTransition\n0101043b=attr/windowAllowReturnTransitionOverlap\n0101043c=attr/windowAllowEnterTransitionOverlap\n0101043d=attr/sessionService\n0101043e=attr/stackViewStyle\n0101043f=attr/switchStyle\n01010440=attr/elevation\n01010441=attr/excludeId\n01010442=attr/excludeClass\n01010443=attr/hideOnContentScroll\n01010444=attr/actionOverflowMenuStyle\n01010445=attr/documentLaunchMode\n01010446=attr/maxRecents\n01010447=attr/autoRemoveFromRecents\n01010448=attr/stateListAnimator\n01010449=attr/toId\n0101044a=attr/fromId\n0101044b=attr/reversible\n0101044c=attr/splitTrack\n0101044d=attr/targetName\n0101044e=attr/excludeName\n0101044f=attr/matchOrder\n01010450=attr/windowDrawsSystemBarBackgrounds\n01010451=attr/statusBarColor\n01010452=attr/navigationBarColor\n01010453=attr/contentInsetStart\n01010454=attr/contentInsetEnd\n01010455=attr/contentInsetLeft\n01010456=attr/contentInsetRight\n01010457=attr/paddingMode\n01010458=attr/layout_rowWeight\n01010459=attr/layout_columnWeight\n0101045a=attr/translateX\n0101045b=attr/translateY\n0101045c=attr/selectableItemBackgroundBorderless\n0101045d=attr/elegantTextHeight\n0101045e=attr/searchKeyphraseId\n0101045f=attr/searchKeyphrase\n01010460=attr/searchKeyphraseSupportedLocales\n01010461=attr/windowTransitionBackgroundFadeDuration\n01010462=attr/overlapAnchor\n01010463=attr/progressTint\n01010464=attr/progressTintMode\n01010465=attr/progressBackgroundTint\n01010466=attr/progressBackgroundTintMode\n01010467=attr/secondaryProgressTint\n01010468=attr/secondaryProgressTintMode\n01010469=attr/indeterminateTint\n0101046a=attr/indeterminateTintMode\n0101046b=attr/backgroundTint\n0101046c=attr/backgroundTintMode\n0101046d=attr/foregroundTint\n0101046e=attr/foregroundTintMode\n0101046f=attr/buttonTint\n01010470=attr/buttonTintMode\n01010471=attr/thumbTint\n01010472=attr/thumbTintMode\n01010473=attr/fullBackupOnly\n01010474=attr/propertyXName\n01010475=attr/propertyYName\n01010476=attr/relinquishTaskIdentity\n01010477=attr/tileModeX\n01010478=attr/tileModeY\n01010479=attr/actionModeShareDrawable\n0101047a=attr/actionModeFindDrawable\n0101047b=attr/actionModeWebSearchDrawable\n0101047c=attr/transitionVisibilityMode\n0101047d=attr/minimumHorizontalAngle\n0101047e=attr/minimumVerticalAngle\n0101047f=attr/maximumAngle\n01010480=attr/searchViewStyle\n01010481=attr/closeIcon\n01010482=attr/goIcon\n01010483=attr/searchIcon\n01010484=attr/voiceIcon\n01010485=attr/commitIcon\n01010486=attr/suggestionRowLayout\n01010487=attr/queryBackground\n01010488=attr/submitBackground\n01010489=attr/buttonBarPositiveButtonStyle\n0101048a=attr/buttonBarNeutralButtonStyle\n0101048b=attr/buttonBarNegativeButtonStyle\n0101048c=attr/popupElevation\n0101048d=attr/actionBarPopupTheme\n0101048e=attr/multiArch\n0101048f=attr/touchscreenBlocksFocus\n01010490=attr/windowElevation\n01010491=attr/launchTaskBehindTargetAnimation\n01010492=attr/launchTaskBehindSourceAnimation\n01010493=attr/restrictionType\n01010494=attr/dayOfWeekBackground\n01010495=attr/dayOfWeekTextAppearance\n01010496=attr/headerMonthTextAppearance\n01010497=attr/headerDayOfMonthTextAppearance\n01010498=attr/headerYearTextAppearance\n01010499=attr/yearListItemTextAppearance\n0101049a=attr/yearListSelectorColor\n0101049b=attr/calendarTextColor\n0101049c=attr/recognitionService\n0101049d=attr/timePickerStyle\n0101049e=attr/timePickerDialogTheme\n0101049f=attr/headerTimeTextAppearance\n010104a0=attr/headerAmPmTextAppearance\n010104a1=attr/numbersTextColor\n010104a2=attr/numbersBackgroundColor\n010104a3=attr/numbersSelectorColor\n010104a4=attr/amPmTextColor\n010104a5=attr/amPmBackgroundColor\n010104a6=attr/searchKeyphraseRecognitionFlags\n010104a7=attr/checkMarkTint\n010104a8=attr/checkMarkTintMode\n010104a9=attr/popupTheme\n010104aa=attr/toolbarStyle\n010104ab=attr/windowClipToOutline\n010104ac=attr/datePickerDialogTheme\n010104ad=attr/showText\n010104ae=attr/windowReturnTransition\n010104af=attr/windowReenterTransition\n010104b0=attr/windowSharedElementReturnTransition\n010104b1=attr/windowSharedElementReenterTransition\n010104b2=attr/resumeWhilePausing\n010104b3=attr/datePickerMode\n010104b4=attr/timePickerMode\n010104b5=attr/inset\n010104b6=attr/letterSpacing\n010104b7=attr/fontFeatureSettings\n010104b8=attr/outlineProvider\n010104b9=attr/contentAgeHint\n010104ba=attr/country\n010104bb=attr/windowSharedElementsUseOverlay\n010104bc=attr/reparent\n010104bd=attr/reparentWithOverlay\n010104be=attr/ambientShadowAlpha\n010104bf=attr/spotShadowAlpha\n010104c0=attr/navigationIcon\n010104c1=attr/navigationContentDescription\n010104c2=attr/fragmentExitTransition\n010104c3=attr/fragmentEnterTransition\n010104c4=attr/fragmentSharedElementEnterTransition\n010104c5=attr/fragmentReturnTransition\n010104c6=attr/fragmentSharedElementReturnTransition\n010104c7=attr/fragmentReenterTransition\n010104c8=attr/fragmentAllowEnterTransitionOverlap\n010104c9=attr/fragmentAllowReturnTransitionOverlap\n010104ca=attr/patternPathData\n010104cb=attr/strokeAlpha\n010104cc=attr/fillAlpha\n010104cd=attr/windowActivityTransitions\n010104ce=attr/colorEdgeEffect\n010104cf=attr/resizeClip\n010104d0=attr/collapseContentDescription\n010104d1=attr/accessibilityTraversalBefore\n010104d2=attr/accessibilityTraversalAfter\n010104d3=attr/dialogPreferredPadding\n010104d4=attr/searchHintIcon\n010104d5=attr/revisionCode\n010104d6=attr/drawableTint\n010104d7=attr/drawableTintMode\n010104d8=attr/fraction\n010104d9=attr/trackTint\n010104da=attr/trackTintMode\n010104db=attr/start\n010104dc=attr/end\n010104dd=attr/breakStrategy\n010104de=attr/hyphenationFrequency\n010104df=attr/allowUndo\n010104e0=attr/windowLightStatusBar\n010104e1=attr/numbersInnerTextColor\n010104e2=attr/colorBackgroundFloating\n010104e3=attr/titleTextColor\n010104e4=attr/subtitleTextColor\n010104e5=attr/thumbPosition\n010104e6=attr/scrollIndicators\n010104e7=attr/contextClickable\n010104e8=attr/fingerprintAuthDrawable\n010104e9=attr/logoDescription\n010104ea=attr/extractNativeLibs\n010104eb=attr/fullBackupContent\n010104ec=attr/usesCleartextTraffic\n010104ed=attr/lockTaskMode\n010104ee=attr/autoVerify\n010104ef=attr/showForAllUsers\n010104f0=attr/supportsAssist\n010104f1=attr/supportsLaunchVoiceAssistFromKeyguard\n010104f2=attr/listMenuViewStyle\n010104f3=attr/subMenuArrow\n010104f4=attr/defaultWidth\n010104f5=attr/defaultHeight\n010104f6=attr/resizeableActivity\n010104f7=attr/supportsPictureInPicture\n010104f8=attr/titleMargin\n010104f9=attr/titleMarginStart\n010104fa=attr/titleMarginEnd\n010104fb=attr/titleMarginTop\n010104fc=attr/titleMarginBottom\n010104fd=attr/maxButtonHeight\n010104fe=attr/buttonGravity\n010104ff=attr/collapseIcon\n01010500=attr/level\n01010501=attr/contextPopupMenuStyle\n01010502=attr/textAppearancePopupMenuHeader\n01010503=attr/windowBackgroundFallback\n01010504=attr/defaultToDeviceProtectedStorage\n01010505=attr/directBootAware\n01010506=attr/preferenceFragmentStyle\n01010507=attr/canControlMagnification\n01010508=attr/languageTag\n01010509=attr/pointerIcon\n0101050a=attr/tickMark\n0101050b=attr/tickMarkTint\n0101050c=attr/tickMarkTintMode\n0101050d=attr/canPerformGestures\n0101050e=attr/externalService\n0101050f=attr/supportsLocalInteraction\n01010510=attr/startX\n01010511=attr/startY\n01010512=attr/endX\n01010513=attr/endY\n01010514=attr/offset\n01010515=attr/use32bitAbi\n01010516=attr/bitmap\n01010517=attr/hotSpotX\n01010518=attr/hotSpotY\n01010519=attr/version\n0101051a=attr/backupInForeground\n0101051b=attr/countDown\n0101051c=attr/canRecord\n0101051d=attr/tunerCount\n0101051e=attr/fillType\n0101051f=attr/popupEnterTransition\n01010520=attr/popupExitTransition\n01010521=attr/forceHasOverlappingRendering\n01010522=attr/contentInsetStartWithNavigation\n01010523=attr/contentInsetEndWithActions\n01010524=attr/numberPickerStyle\n01010525=attr/enableVrMode\n01010526=attr/hash\n01010527=attr/networkSecurityConfig\n01010528=attr/shortcutId\n01010529=attr/shortcutShortLabel\n0101052a=attr/shortcutLongLabel\n0101052b=attr/shortcutDisabledMessage\n0101052c=attr/roundIcon\n0101052d=attr/contextUri\n0101052e=attr/contextDescription\n0101052f=attr/showMetadataInPreview\n01010530=attr/colorSecondary\n01010531=attr/visibleToInstantApps\n01010532=attr/font\n01010533=attr/fontWeight\n01010534=attr/tooltipText\n01010535=attr/autoSizeTextType\n01010536=attr/autoSizeStepGranularity\n01010537=attr/autoSizePresetSizes\n01010538=attr/autoSizeMinTextSize\n01010539=attr/min\n0101053a=attr/rotationAnimation\n0101053b=attr/layout_marginHorizontal\n0101053c=attr/layout_marginVertical\n0101053d=attr/paddingHorizontal\n0101053e=attr/paddingVertical\n0101053f=attr/fontStyle\n01010540=attr/keyboardNavigationCluster\n01010541=attr/targetProcesses\n01010542=attr/nextClusterForward\n01010543=attr/colorError\n01010544=attr/focusedByDefault\n01010545=attr/appCategory\n01010546=attr/autoSizeMaxTextSize\n01010547=attr/recreateOnConfigChanges\n01010548=attr/certDigest\n01010549=attr/splitName\n0101054a=attr/colorMode\n0101054b=attr/isolatedSplits\n0101054c=attr/targetSandboxVersion\n0101054d=attr/canRequestFingerprintGestures\n0101054e=attr/alphabeticModifiers\n0101054f=attr/numericModifiers\n01010550=attr/fontProviderAuthority\n01010551=attr/fontProviderQuery\n01010552=attr/primaryContentAlpha\n01010553=attr/secondaryContentAlpha\n01010554=attr/requiredFeature\n01010555=attr/requiredNotFeature\n01010556=attr/autofillHints\n01010557=attr/fontProviderPackage\n01010558=attr/importantForAutofill\n01010559=attr/recycleEnabled\n0101055a=attr/isStatic\n0101055b=attr/isFeatureSplit\n0101055c=attr/singleLineTitle\n0101055d=attr/fontProviderCerts\n0101055e=attr/iconTint\n0101055f=attr/iconTintMode\n01010560=attr/maxAspectRatio\n01010561=attr/iconSpaceReserved\n01010562=attr/defaultFocusHighlightEnabled\n01010563=attr/persistentWhenFeatureAvailable\n01010564=attr/windowSplashscreenContent\n01010565=attr/requiredSystemPropertyName\n01010566=attr/requiredSystemPropertyValue\n01010567=attr/justificationMode\n01010568=attr/autofilledHighlight\n01010569=attr/showWhenLocked\n0101056a=attr/turnScreenOn\n0101056b=attr/classLoader\n0101056c=attr/windowLightNavigationBar\n0101056d=attr/navigationBarDividerColor\n0101056e=attr/cantSaveState\n0101056f=attr/ttcIndex\n01010570=attr/fontVariationSettings\n01010571=attr/dialogCornerRadius\n01010572=attr/compileSdkVersion\n01010573=attr/compileSdkVersionCodename\n01010574=attr/screenReaderFocusable\n01010575=attr/buttonCornerRadius\n01010576=attr/versionCodeMajor\n01010577=attr/versionMajor\n01010578=attr/isVrOnly\n01010579=attr/widgetFeatures\n0101057a=attr/appComponentFactory\n0101057b=attr/fallbackLineSpacing\n0101057c=attr/accessibilityPaneTitle\n0101057d=attr/firstBaselineToTopHeight\n0101057e=attr/lastBaselineToBottomHeight\n0101057f=attr/lineHeight\n01010580=attr/accessibilityHeading\n01010581=attr/outlineSpotShadowColor\n01010582=attr/outlineAmbientShadowColor\n01010583=attr/maxLongVersionCode\n01010584=attr/userRestriction\n01010585=attr/textFontWeight\n01010586=attr/windowLayoutInDisplayCutoutMode\n01010587=attr/packageType\n01010588=attr/opticalInsetLeft\n01010589=attr/opticalInsetTop\n0101058a=attr/opticalInsetRight\n0101058b=attr/opticalInsetBottom\n0101058c=attr/forceDarkAllowed\n0101058d=attr/supportsAmbientMode\n0101058e=attr/usesNonSdkApi\n0101058f=attr/nonInteractiveUiTimeout\n01010590=attr/isLightTheme\n01010591=attr/isSplitRequired\n01010592=attr/textLocale\n01010593=attr/settingsSliceUri\n01010594=attr/shell\n01010595=attr/interactiveUiTimeout\n01010596=attr/supportsMultipleDisplays\n01010597=attr/useAppZygote\n01010598=attr/selectionDividerHeight\n01010599=attr/foregroundServiceType\n0101059a=attr/hasFragileUserData\n0101059b=attr/minAspectRatio\n0101059c=attr/inheritShowWhenLocked\n0101059d=attr/zygotePreloadName\n0101059e=attr/useEmbeddedDex\n0101059f=attr/forceUriPermissions\n01010600=attr/allowClearUserDataOnFailedRestore\n01010601=attr/allowAudioPlaybackCapture\n01010602=attr/secureElementName\n01010603=attr/requestLegacyExternalStorage\n01010604=attr/enforceStatusBarContrast\n01010605=attr/enforceNavigationBarContrast\n01010606=attr/identifier\n01010607=attr/importantForContentCapture\n01010608=attr/forceQueryable\n01010609=attr/resourcesMap\n0101060a=attr/animatedImageDrawable\n0101060b=attr/htmlDescription\n0101060c=attr/preferMinimalPostProcessing\n0101060d=attr/supportsInlineSuggestions\n0101060e=attr/crossProfile\n0101060f=attr/canTakeScreenshot\n01010610=attr/sdkVersion\n01010611=attr/minExtensionVersion\n01010612=attr/allowNativeHeapPointerTagging\n01010613=attr/autoRevokePermissions\n01010614=attr/preserveLegacyExternalStorage\n01010615=attr/mimeGroup\n01010616=attr/gwpAsanMode\n01010617=attr/rollbackDataPolicy\n01010618=attr/allowClickWhenDisabled\n01010619=attr/windowLayoutAffinity\n0101061a=attr/canPauseRecording\n0101061b=attr/windowBlurBehindRadius\n0101061c=attr/windowBlurBehindEnabled\n0101061d=attr/requireDeviceScreenOn\n0101061e=attr/pathSuffix\n0101061f=attr/sspSuffix\n01010620=attr/pathAdvancedPattern\n01010621=attr/sspAdvancedPattern\n01010622=attr/fontProviderSystemFontFamily\n01010623=attr/hand_second\n01010624=attr/memtagMode\n01010625=attr/nativeHeapZeroInitialized\n01010626=attr/hotwordDetectionService\n01010627=attr/previewLayout\n01010628=attr/clipToOutline\n0101062a=attr/knownCerts\n0101062b=attr/windowBackgroundBlurRadius\n0101062c=attr/windowSplashScreenBackground\n0101062d=attr/windowSplashScreenAnimatedIcon\n0101062e=attr/windowSplashScreenAnimationDuration\n0101062f=attr/windowSplashScreenBrandingImage\n01010630=attr/windowSplashScreenIconBackgroundColor\n01010631=attr/splashScreenTheme\n01010632=attr/maxResizeWidth\n01010633=attr/maxResizeHeight\n01010634=attr/targetCellWidth\n01010635=attr/targetCellHeight\n01010636=attr/dialTint\n01010637=attr/dialTintMode\n01010638=attr/hand_hourTint\n01010639=attr/hand_hourTintMode\n0101063a=attr/hand_minuteTint\n0101063b=attr/hand_minuteTintMode\n0101063c=attr/hand_secondTint\n0101063d=attr/hand_secondTintMode\n0101063e=attr/dataExtractionRules\n0101063f=attr/passwordsActivity\n01010640=attr/selectableAsDefault\n01010641=attr/isAccessibilityTool\n01010642=attr/attributionTags\n01010643=attr/suppressesSpellChecker\n01010644=attr/usesPermissionFlags\n01010645=attr/requestRawExternalStorageAccess\n01010646=attr/playHomeTransitionSound\n01010647=attr/lStar\n01010648=attr/showInInputMethodPicker\n01010649=attr/effectColor\n0101064a=attr/requestForegroundServiceExemption\n0101064b=attr/attributionsAreUserVisible\n0101064c=attr/shouldUseDefaultUnfoldTransition\n0101064d=attr/sharedUserMaxSdkVersion\n0101064e=attr/requiredSplitTypes\n0101064f=attr/splitTypes\n01010650=attr/canDisplayOnRemoteDevices\n01010651=attr/supportedTypes\n01010652=attr/resetEnabledSettingsOnAppDataCleared\n01010653=attr/supportsStylusHandwriting\n01010654=attr/showClockAndComplications\n01010655=attr/gameSessionService\n01010656=attr/supportsBatteryGameMode\n01010657=attr/supportsPerformanceGameMode\n01010658=attr/allowGameAngleDriver\n01010659=attr/allowGameDownscaling\n0101065a=attr/allowGameFpsOverride\n0101065b=attr/localeConfig\n0101065c=attr/showBackdrop\n0101065d=attr/preferKeepClear\n0101065e=attr/autoHandwritingEnabled\n0101065f=attr/fromExtendLeft\n01010660=attr/fromExtendTop\n01010661=attr/fromExtendRight\n01010662=attr/fromExtendBottom\n01010663=attr/toExtendLeft\n01010664=attr/toExtendTop\n01010665=attr/toExtendRight\n01010666=attr/toExtendBottom\n01010667=attr/tileService\n01010668=attr/windowSplashScreenBehavior\n01010669=attr/allowUntrustedActivityEmbedding\n0101066a=attr/knownActivityEmbeddingCerts\n0101066b=attr/intro\n0101066c=attr/enableOnBackInvokedCallback\n0101066d=attr/supportsInlineSuggestionsWithTouchExploration\n0101066e=attr/lineBreakStyle\n0101066f=attr/lineBreakWordStyle\n01010670=attr/maxDrawableWidth\n01010671=attr/maxDrawableHeight\n01010672=attr/backdropColor\n01010673=attr/handwritingBoundsOffsetLeft\n01010674=attr/handwritingBoundsOffsetTop\n01010675=attr/handwritingBoundsOffsetRight\n01010676=attr/handwritingBoundsOffsetBottom\n01010677=attr/accessibilityDataSensitive\n01010678=attr/enableTextStylingShortcuts\n01010679=attr/requiredDisplayCategory\n0101067a=attr/visualQueryDetectionService\n0101067b=attr/physicalKeyboardHintLanguageTag\n0101067c=attr/physicalKeyboardHintLayoutType\n0101067d=attr/allowSharedIsolatedProcess\n0101067e=attr/keyboardLocale\n0101067f=attr/keyboardLayoutType\n01010680=attr/allowUpdateOwnership\n01010681=attr/isCredential\n01010682=attr/searchResultHighlightColor\n01010683=attr/focusedSearchResultHighlightColor\n01010684=attr/stylusHandwritingSettingsActivity\n01010685=attr/windowNoMoveAnimation\n01010686=attr/settingsSubtitle\n01010687=attr/capability\n01010688=attr/defaultLocale\n01010689=attr/isVirtualDeviceOnly\n0101068c=attr/featureFlag\n0101068d=attr/systemUserOnly\n0101068e=attr/allow\n0101068f=attr/query\n01010690=attr/queryPrefix\n01010691=attr/queryPattern\n01010692=attr/queryAdvancedPattern\n01010693=attr/querySuffix\n01010694=attr/fragmentPrefix\n01010695=attr/fragmentPattern\n01010696=attr/fragmentAdvancedPattern\n01010697=attr/fragmentSuffix\n01010698=attr/useBoundsForWidth\n01010699=attr/autoTransact\n0101069a=attr/windowOptOutEdgeToEdgeEnforcement\n0101069b=attr/requireContentUriPermissionFromCaller\n0101069d=attr/useLocalePreferredLineHeightForMinimum\n0101069e=attr/contentSensitivity\n0101069f=attr/supportsConnectionlessStylusHandwriting\n010106a0=attr/shouldDefaultToObserveMode\n010106a1=attr/allowCrossUidActivitySwitchFromBelow\n010106a2=attr/shiftDrawingOffsetForStartOverhang\n010106a3=attr/windowIsFrameRatePowerSavingsBalanced\n010106a4=attr/adServiceTypes\n010106a5=attr/languageSettingsActivity\n010106a6=attr/dreamCategory\n010106a7=attr/backgroundPermission\n010106a8=attr/supplementalDescription\n010106a9=attr/intentMatchingFlags\n010106aa=attr/layoutLabel\n010106ab=attr/pageSizeCompat\n010106ac=attr/wantsRoleHolderPriority\n01020000=id/background\n01020001=id/checkbox\n01020002=id/content\n01020003=id/edit\n01020004=id/empty\n01020005=id/hint\n01020006=id/icon\n01020007=id/icon1\n01020008=id/icon2\n01020009=id/input\n0102000a=id/list\n0102000b=id/message\n0102000c=id/primary\n0102000d=id/progress\n0102000e=id/selectedIcon\n0102000f=id/secondaryProgress\n01020010=id/summary\n01020011=id/tabcontent\n01020012=id/tabhost\n01020013=id/tabs\n01020014=id/text1\n01020015=id/text2\n01020016=id/title\n01020017=id/toggle\n01020018=id/widget_frame\n01020019=id/button1\n0102001a=id/button2\n0102001b=id/button3\n0102001c=id/extractArea\n0102001d=id/candidatesArea\n0102001e=id/inputArea\n0102001f=id/selectAll\n01020020=id/cut\n01020021=id/copy\n01020022=id/paste\n01020023=id/copyUrl\n01020024=id/switchInputMethod\n01020025=id/inputExtractEditText\n01020026=id/keyboardView\n01020027=id/closeButton\n01020028=id/startSelectingText\n01020029=id/stopSelectingText\n0102002a=id/addToDictionary\n0102002b=id/custom\n0102002c=id/home\n0102002d=id/selectTextMode\n0102002e=id/mask\n0102002f=id/statusBarBackground\n01020030=id/navigationBarBackground\n01020031=id/pasteAsPlainText\n01020032=id/undo\n01020033=id/redo\n01020034=id/replaceText\n01020035=id/shareText\n01020036=id/accessibilityActionShowOnScreen\n01020037=id/accessibilityActionScrollToPosition\n01020038=id/accessibilityActionScrollUp\n01020039=id/accessibilityActionScrollLeft\n0102003a=id/accessibilityActionScrollDown\n0102003b=id/accessibilityActionScrollRight\n0102003c=id/accessibilityActionContextClick\n0102003d=id/accessibilityActionSetProgress\n0102003e=id/icon_frame\n0102003f=id/list_container\n01020040=id/switch_widget\n01020041=id/textAssist\n01020042=id/accessibilityActionMoveWindow\n01020043=id/autofill\n01020044=id/accessibilityActionShowTooltip\n01020045=id/accessibilityActionHideTooltip\n01020046=id/accessibilityActionPageUp\n01020047=id/accessibilityActionPageDown\n01020048=id/accessibilityActionPageLeft\n01020049=id/accessibilityActionPageRight\n0102004a=id/accessibilityActionPressAndHold\n0102004b=id/accessibilitySystemActionBack\n0102004c=id/accessibilitySystemActionHome\n0102004d=id/accessibilitySystemActionRecents\n0102004e=id/accessibilitySystemActionNotifications\n0102004f=id/accessibilitySystemActionQuickSettings\n01020050=id/accessibilitySystemActionPowerDialog\n01020051=id/accessibilitySystemActionToggleSplitScreen\n01020052=id/accessibilitySystemActionLockScreen\n01020053=id/accessibilitySystemActionTakeScreenshot\n01020054=id/accessibilityActionImeEnter\n01020055=id/accessibilityActionDragStart\n01020056=id/accessibilityActionDragDrop\n01020057=id/accessibilityActionDragCancel\n01020058=id/accessibilityActionShowTextSuggestions\n01020059=id/inputExtractAction\n0102005a=id/inputExtractAccessories\n0102005b=id/bold\n0102005c=id/italic\n0102005d=id/underline\n0102005e=id/accessibilityActionScrollInDirection\n0102005f=id/ALT\n01020060=id/CAPS_LOCK\n01020061=id/CTRL\n01020062=id/FUNCTION\n01020063=id/KEYCODE_0\n01020064=id/KEYCODE_1\n01020065=id/KEYCODE_11\n01020066=id/KEYCODE_12\n01020067=id/KEYCODE_2\n01020068=id/KEYCODE_3\n01020069=id/KEYCODE_3D_MODE\n0102006a=id/KEYCODE_4\n0102006b=id/KEYCODE_5\n0102006c=id/KEYCODE_6\n0102006d=id/KEYCODE_7\n0102006e=id/KEYCODE_8\n0102006f=id/KEYCODE_9\n01020070=id/KEYCODE_A\n01020071=id/KEYCODE_ALL_APPS\n01020072=id/KEYCODE_ALT_LEFT\n01020073=id/KEYCODE_ALT_RIGHT\n01020074=id/KEYCODE_APOSTROPHE\n01020075=id/KEYCODE_APP_SWITCH\n01020076=id/KEYCODE_ASSIST\n01020077=id/KEYCODE_AT\n01020078=id/KEYCODE_AVR_INPUT\n01020079=id/KEYCODE_AVR_POWER\n0102007a=id/KEYCODE_B\n0102007b=id/KEYCODE_BACK\n0102007c=id/KEYCODE_BACKSLASH\n0102007d=id/KEYCODE_BOOKMARK\n0102007e=id/KEYCODE_BREAK\n0102007f=id/KEYCODE_BRIGHTNESS_DOWN\n01020080=id/KEYCODE_BRIGHTNESS_UP\n01020081=id/KEYCODE_BUTTON_1\n01020082=id/KEYCODE_BUTTON_10\n01020083=id/KEYCODE_BUTTON_11\n01020084=id/KEYCODE_BUTTON_12\n01020085=id/KEYCODE_BUTTON_13\n01020086=id/KEYCODE_BUTTON_14\n01020087=id/KEYCODE_BUTTON_15\n01020088=id/KEYCODE_BUTTON_16\n01020089=id/KEYCODE_BUTTON_2\n0102008a=id/KEYCODE_BUTTON_3\n0102008b=id/KEYCODE_BUTTON_4\n0102008c=id/KEYCODE_BUTTON_5\n0102008d=id/KEYCODE_BUTTON_6\n0102008e=id/KEYCODE_BUTTON_7\n0102008f=id/KEYCODE_BUTTON_8\n01020090=id/KEYCODE_BUTTON_9\n01020091=id/KEYCODE_BUTTON_A\n01020092=id/KEYCODE_BUTTON_B\n01020093=id/KEYCODE_BUTTON_C\n01020094=id/KEYCODE_BUTTON_L1\n01020095=id/KEYCODE_BUTTON_L2\n01020096=id/KEYCODE_BUTTON_MODE\n01020097=id/KEYCODE_BUTTON_R1\n01020098=id/KEYCODE_BUTTON_R2\n01020099=id/KEYCODE_BUTTON_SELECT\n0102009a=id/KEYCODE_BUTTON_START\n0102009b=id/KEYCODE_BUTTON_THUMBL\n0102009c=id/KEYCODE_BUTTON_THUMBR\n0102009d=id/KEYCODE_BUTTON_X\n0102009e=id/KEYCODE_BUTTON_Y\n0102009f=id/KEYCODE_BUTTON_Z\n010200a0=id/KEYCODE_C\n010200a1=id/KEYCODE_CALCULATOR\n010200a2=id/KEYCODE_CALENDAR\n010200a3=id/KEYCODE_CALL\n010200a4=id/KEYCODE_CAMERA\n010200a5=id/KEYCODE_CAPS_LOCK\n010200a6=id/KEYCODE_CAPTIONS\n010200a7=id/KEYCODE_CHANNEL_DOWN\n010200a8=id/KEYCODE_CHANNEL_UP\n010200a9=id/KEYCODE_CLEAR\n010200aa=id/KEYCODE_COMMA\n010200ab=id/KEYCODE_CONTACTS\n010200ac=id/KEYCODE_COPY\n010200ad=id/KEYCODE_CTRL_LEFT\n010200ae=id/KEYCODE_CTRL_RIGHT\n010200af=id/KEYCODE_CUT\n010200b0=id/KEYCODE_D\n010200b1=id/KEYCODE_DEL\n010200b2=id/KEYCODE_DEMO_APP_1\n010200b3=id/KEYCODE_DEMO_APP_2\n010200b4=id/KEYCODE_DEMO_APP_3\n010200b5=id/KEYCODE_DEMO_APP_4\n010200b6=id/KEYCODE_DPAD_CENTER\n010200b7=id/KEYCODE_DPAD_DOWN\n010200b8=id/KEYCODE_DPAD_DOWN_LEFT\n010200b9=id/KEYCODE_DPAD_DOWN_RIGHT\n010200ba=id/KEYCODE_DPAD_LEFT\n010200bb=id/KEYCODE_DPAD_RIGHT\n010200bc=id/KEYCODE_DPAD_UP\n010200bd=id/KEYCODE_DPAD_UP_LEFT\n010200be=id/KEYCODE_DPAD_UP_RIGHT\n010200bf=id/KEYCODE_DVR\n010200c0=id/KEYCODE_E\n010200c1=id/KEYCODE_EISU\n010200c2=id/KEYCODE_EMOJI_PICKER\n010200c3=id/KEYCODE_ENDCALL\n010200c4=id/KEYCODE_ENTER\n010200c5=id/KEYCODE_ENVELOPE\n010200c6=id/KEYCODE_EQUALS\n010200c7=id/KEYCODE_ESCAPE\n010200c8=id/KEYCODE_EXPLORER\n010200c9=id/KEYCODE_F\n010200ca=id/KEYCODE_F1\n010200cb=id/KEYCODE_F10\n010200cc=id/KEYCODE_F11\n010200cd=id/KEYCODE_F12\n010200ce=id/KEYCODE_F2\n010200cf=id/KEYCODE_F3\n010200d0=id/KEYCODE_F4\n010200d1=id/KEYCODE_F5\n010200d2=id/KEYCODE_F6\n010200d3=id/KEYCODE_F7\n010200d4=id/KEYCODE_F8\n010200d5=id/KEYCODE_F9\n010200d6=id/KEYCODE_FEATURED_APP_1\n010200d7=id/KEYCODE_FEATURED_APP_2\n010200d8=id/KEYCODE_FEATURED_APP_3\n010200d9=id/KEYCODE_FEATURED_APP_4\n010200da=id/KEYCODE_FOCUS\n010200db=id/KEYCODE_FORWARD\n010200dc=id/KEYCODE_FORWARD_DEL\n010200dd=id/KEYCODE_FUNCTION\n010200de=id/KEYCODE_G\n010200df=id/KEYCODE_GRAVE\n010200e0=id/KEYCODE_GUIDE\n010200e1=id/KEYCODE_H\n010200e2=id/KEYCODE_HEADSETHOOK\n010200e3=id/KEYCODE_HELP\n010200e4=id/KEYCODE_HENKAN\n010200e5=id/KEYCODE_HOME\n010200e6=id/KEYCODE_I\n010200e7=id/KEYCODE_INFO\n010200e8=id/KEYCODE_INSERT\n010200e9=id/KEYCODE_J\n010200ea=id/KEYCODE_K\n010200eb=id/KEYCODE_KANA\n010200ec=id/KEYCODE_KATAKANA_HIRAGANA\n010200ed=id/KEYCODE_KEYBOARD_BACKLIGHT_DOWN\n010200ee=id/KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE\n010200ef=id/KEYCODE_KEYBOARD_BACKLIGHT_UP\n010200f0=id/KEYCODE_L\n010200f1=id/KEYCODE_LANGUAGE_SWITCH\n010200f2=id/KEYCODE_LAST_CHANNEL\n010200f3=id/KEYCODE_LEFT_BRACKET\n010200f4=id/KEYCODE_M\n010200f5=id/KEYCODE_MACRO_1\n010200f6=id/KEYCODE_MACRO_2\n010200f7=id/KEYCODE_MACRO_3\n010200f8=id/KEYCODE_MACRO_4\n010200f9=id/KEYCODE_MANNER_MODE\n010200fa=id/KEYCODE_MEDIA_AUDIO_TRACK\n010200fb=id/KEYCODE_MEDIA_CLOSE\n010200fc=id/KEYCODE_MEDIA_EJECT\n010200fd=id/KEYCODE_MEDIA_FAST_FORWARD\n010200fe=id/KEYCODE_MEDIA_NEXT\n010200ff=id/KEYCODE_MEDIA_PAUSE\n01020100=id/KEYCODE_MEDIA_PLAY\n01020101=id/KEYCODE_MEDIA_PLAY_PAUSE\n01020102=id/KEYCODE_MEDIA_PREVIOUS\n01020103=id/KEYCODE_MEDIA_RECORD\n01020104=id/KEYCODE_MEDIA_REWIND\n01020105=id/KEYCODE_MEDIA_SKIP_BACKWARD\n01020106=id/KEYCODE_MEDIA_SKIP_FORWARD\n01020107=id/KEYCODE_MEDIA_SLEEP\n01020108=id/KEYCODE_MEDIA_STEP_BACKWARD\n01020109=id/KEYCODE_MEDIA_STEP_FORWARD\n0102010a=id/KEYCODE_MEDIA_STOP\n0102010b=id/KEYCODE_MEDIA_TOP_MENU\n0102010c=id/KEYCODE_MEDIA_WAKEUP\n0102010d=id/KEYCODE_MENU\n0102010e=id/KEYCODE_META_LEFT\n0102010f=id/KEYCODE_META_RIGHT\n01020110=id/KEYCODE_MINUS\n01020111=id/KEYCODE_MOVE_END\n01020112=id/KEYCODE_MOVE_HOME\n01020113=id/KEYCODE_MUHENKAN\n01020114=id/KEYCODE_MUSIC\n01020115=id/KEYCODE_MUTE\n01020116=id/KEYCODE_N\n01020117=id/KEYCODE_NAVIGATE_IN\n01020118=id/KEYCODE_NAVIGATE_NEXT\n01020119=id/KEYCODE_NAVIGATE_OUT\n0102011a=id/KEYCODE_NAVIGATE_PREVIOUS\n0102011b=id/KEYCODE_NOTIFICATION\n0102011c=id/KEYCODE_NUM\n0102011d=id/KEYCODE_NUMPAD_0\n0102011e=id/KEYCODE_NUMPAD_1\n0102011f=id/KEYCODE_NUMPAD_2\n01020120=id/KEYCODE_NUMPAD_3\n01020121=id/KEYCODE_NUMPAD_4\n01020122=id/KEYCODE_NUMPAD_5\n01020123=id/KEYCODE_NUMPAD_6\n01020124=id/KEYCODE_NUMPAD_7\n01020125=id/KEYCODE_NUMPAD_8\n01020126=id/KEYCODE_NUMPAD_9\n01020127=id/KEYCODE_NUMPAD_ADD\n01020128=id/KEYCODE_NUMPAD_COMMA\n01020129=id/KEYCODE_NUMPAD_DIVIDE\n0102012a=id/KEYCODE_NUMPAD_DOT\n0102012b=id/KEYCODE_NUMPAD_ENTER\n0102012c=id/KEYCODE_NUMPAD_EQUALS\n0102012d=id/KEYCODE_NUMPAD_LEFT_PAREN\n0102012e=id/KEYCODE_NUMPAD_MULTIPLY\n0102012f=id/KEYCODE_NUMPAD_RIGHT_PAREN\n01020130=id/KEYCODE_NUMPAD_SUBTRACT\n01020131=id/KEYCODE_NUM_LOCK\n01020132=id/KEYCODE_O\n01020133=id/KEYCODE_P\n01020134=id/KEYCODE_PAGE_DOWN\n01020135=id/KEYCODE_PAGE_UP\n01020136=id/KEYCODE_PAIRING\n01020137=id/KEYCODE_PASTE\n01020138=id/KEYCODE_PERIOD\n01020139=id/KEYCODE_PICTSYMBOLS\n0102013a=id/KEYCODE_PLUS\n0102013b=id/KEYCODE_POUND\n0102013c=id/KEYCODE_POWER\n0102013d=id/KEYCODE_PROFILE_SWITCH\n0102013e=id/KEYCODE_PROG_BLUE\n0102013f=id/KEYCODE_PROG_GRED\n01020140=id/KEYCODE_PROG_GREEN\n01020141=id/KEYCODE_PROG_YELLOW\n01020142=id/KEYCODE_Q\n01020143=id/KEYCODE_R\n01020144=id/KEYCODE_RECENT_APPS\n01020145=id/KEYCODE_REFRESH\n01020146=id/KEYCODE_RIGHT_BRACKET\n01020147=id/KEYCODE_RO\n01020148=id/KEYCODE_S\n01020149=id/KEYCODE_SCREENSHOT\n0102014a=id/KEYCODE_SCROLL_LOCK\n0102014b=id/KEYCODE_SEARCH\n0102014c=id/KEYCODE_SEMICOLON\n0102014d=id/KEYCODE_SETTINGS\n0102014e=id/KEYCODE_SHIFT_LEFT\n0102014f=id/KEYCODE_SHIFT_RIGHT\n01020150=id/KEYCODE_SLASH\n01020151=id/KEYCODE_SOFT_LEFT\n01020152=id/KEYCODE_SOFT_RIGHT\n01020153=id/KEYCODE_SOFT_SLEEP\n01020154=id/KEYCODE_SPACE\n01020155=id/KEYCODE_STAR\n01020156=id/KEYCODE_STB_INPUT\n01020157=id/KEYCODE_STB_POWER\n01020158=id/KEYCODE_STEM_1\n01020159=id/KEYCODE_STEM_2\n0102015a=id/KEYCODE_STEM_3\n0102015b=id/KEYCODE_STEM_PRIMARY\n0102015c=id/KEYCODE_STYLUS_BUTTON_PRIMARY\n0102015d=id/KEYCODE_STYLUS_BUTTON_SECONDARY\n0102015e=id/KEYCODE_STYLUS_BUTTON_TAIL\n0102015f=id/KEYCODE_STYLUS_BUTTON_TERTIARY\n01020160=id/KEYCODE_SWITCH_CHARSET\n01020161=id/KEYCODE_SYM\n01020162=id/KEYCODE_SYSRQ\n01020163=id/KEYCODE_SYSTEM_NAVIGATION_DOWN\n01020164=id/KEYCODE_SYSTEM_NAVIGATION_LEFT\n01020165=id/KEYCODE_SYSTEM_NAVIGATION_RIGHT\n01020166=id/KEYCODE_SYSTEM_NAVIGATION_UP\n01020167=id/KEYCODE_T\n01020168=id/KEYCODE_TAB\n01020169=id/KEYCODE_THUMBS_DOWN\n0102016a=id/KEYCODE_THUMBS_UP\n0102016b=id/KEYCODE_TV\n0102016c=id/KEYCODE_TV_ANTENNA_CABLE\n0102016d=id/KEYCODE_TV_AUDIO_DESCRIPTION\n0102016e=id/KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN\n0102016f=id/KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP\n01020170=id/KEYCODE_TV_CONTENTS_MENU\n01020171=id/KEYCODE_TV_DATA_SERVICE\n01020172=id/KEYCODE_TV_INPUT\n01020173=id/KEYCODE_TV_INPUT_COMPONENT_1\n01020174=id/KEYCODE_TV_INPUT_COMPONENT_2\n01020175=id/KEYCODE_TV_INPUT_COMPOSITE_1\n01020176=id/KEYCODE_TV_INPUT_COMPOSITE_2\n01020177=id/KEYCODE_TV_INPUT_HDMI_1\n01020178=id/KEYCODE_TV_INPUT_HDMI_2\n01020179=id/KEYCODE_TV_INPUT_HDMI_3\n0102017a=id/KEYCODE_TV_INPUT_HDMI_4\n0102017b=id/KEYCODE_TV_INPUT_VGA_1\n0102017c=id/KEYCODE_TV_MEDIA_CONTEXT_MENU\n0102017d=id/KEYCODE_TV_NETWORK\n0102017e=id/KEYCODE_TV_NUMBER_ENTRY\n0102017f=id/KEYCODE_TV_POWER\n01020180=id/KEYCODE_TV_RADIO_SERVICE\n01020181=id/KEYCODE_TV_SATELLITE\n01020182=id/KEYCODE_TV_SATELLITE_BS\n01020183=id/KEYCODE_TV_SATELLITE_CS\n01020184=id/KEYCODE_TV_SATELLITE_SERVICE\n01020185=id/KEYCODE_TV_TELETEXT\n01020186=id/KEYCODE_TV_TERRESTRIAL_ANALOG\n01020187=id/KEYCODE_TV_TERRESTRIAL_DIGITAL\n01020188=id/KEYCODE_TV_TIMER_PROGRAMMING\n01020189=id/KEYCODE_TV_ZOOM_MODE\n0102018a=id/KEYCODE_U\n0102018b=id/KEYCODE_UNKNOWN\n0102018c=id/KEYCODE_V\n0102018d=id/KEYCODE_VIDEO_APP_1\n0102018e=id/KEYCODE_VIDEO_APP_2\n0102018f=id/KEYCODE_VIDEO_APP_3\n01020190=id/KEYCODE_VIDEO_APP_4\n01020191=id/KEYCODE_VIDEO_APP_5\n01020192=id/KEYCODE_VIDEO_APP_6\n01020193=id/KEYCODE_VIDEO_APP_7\n01020194=id/KEYCODE_VIDEO_APP_8\n01020195=id/KEYCODE_VOICE_ASSIST\n01020196=id/KEYCODE_VOLUME_DOWN\n01020197=id/KEYCODE_VOLUME_MUTE\n01020198=id/KEYCODE_VOLUME_UP\n01020199=id/KEYCODE_W\n0102019a=id/KEYCODE_WINDOW\n0102019b=id/KEYCODE_X\n0102019c=id/KEYCODE_Y\n0102019d=id/KEYCODE_YEN\n0102019e=id/KEYCODE_Z\n0102019f=id/KEYCODE_ZENKAKU_HANKAKU\n010201a0=id/KEYCODE_ZOOM_IN\n010201a1=id/KEYCODE_ZOOM_OUT\n010201a2=id/META\n010201a3=id/NUM_LOCK\n010201a4=id/SCROLL_LOCK\n010201a5=id/SHIFT\n010201a6=id/SYM\n010201a7=id/aboveThumb\n010201a8=id/accessibility\n010201a9=id/accessibilityActionClickOnClickableSpan\n010201aa=id/accessibility_autoclick_button_group_container\n010201ab=id/accessibility_autoclick_double_click_button\n010201ac=id/accessibility_autoclick_double_click_layout\n010201ad=id/accessibility_autoclick_drag_button\n010201ae=id/accessibility_autoclick_drag_layout\n010201af=id/accessibility_autoclick_left_click_button\n010201b0=id/accessibility_autoclick_left_click_layout\n010201b1=id/accessibility_autoclick_pause_button\n010201b2=id/accessibility_autoclick_pause_layout\n010201b3=id/accessibility_autoclick_position_button\n010201b4=id/accessibility_autoclick_position_layout\n010201b5=id/accessibility_autoclick_right_click_button\n010201b6=id/accessibility_autoclick_right_click_layout\n010201b7=id/accessibility_autoclick_scroll_button\n010201b8=id/accessibility_autoclick_scroll_layout\n010201b9=id/accessibility_autoclick_scroll_panel\n010201ba=id/accessibility_autoclick_type_panel\n010201bb=id/accessibility_button_chooser_grid\n010201bc=id/accessibility_button_prompt\n010201bd=id/accessibility_button_prompt_prologue\n010201be=id/accessibility_button_target_icon\n010201bf=id/accessibility_button_target_label\n010201c0=id/accessibility_controlScreen_description\n010201c1=id/accessibility_controlScreen_icon\n010201c2=id/accessibility_controlScreen_title\n010201c3=id/accessibility_magnification_thumbnail_view\n010201c4=id/accessibility_performAction_description\n010201c5=id/accessibility_performAction_icon\n010201c6=id/accessibility_performAction_title\n010201c7=id/accessibility_permissionDialog_description\n010201c8=id/accessibility_permissionDialog_icon\n010201c9=id/accessibility_permissionDialog_title\n010201ca=id/accessibility_permission_enable_allow_button\n010201cb=id/accessibility_permission_enable_deny_button\n010201cc=id/accessibility_permission_enable_uninstall_button\n010201cd=id/accessibility_shortcut_target_checkbox\n010201ce=id/accessibility_shortcut_target_icon\n010201cf=id/accessibility_shortcut_target_label\n010201d0=id/accessibility_shortcut_target_status\n010201d1=id/accountPreferences\n010201d2=id/account_name\n010201d3=id/account_row_icon\n010201d4=id/account_row_text\n010201d5=id/account_type\n010201d6=id/action0\n010201d7=id/action1\n010201d8=id/action2\n010201d9=id/action3\n010201da=id/action4\n010201db=id/actionDone\n010201dc=id/actionGo\n010201dd=id/actionNext\n010201de=id/actionNone\n010201df=id/actionPrevious\n010201e0=id/actionSearch\n010201e1=id/actionSend\n010201e2=id/actionUnspecified\n010201e3=id/action_bar\n010201e4=id/action_bar_container\n010201e5=id/action_bar_spinner\n010201e6=id/action_bar_subtitle\n010201e7=id/action_bar_title\n010201e8=id/action_context_bar\n010201e9=id/action_divider\n010201ea=id/action_menu_divider\n010201eb=id/action_menu_presenter\n010201ec=id/action_mode_bar\n010201ed=id/action_mode_bar_stub\n010201ee=id/action_mode_close_button\n010201ef=id/actions\n010201f0=id/actions_container\n010201f1=id/actions_container_layout\n010201f2=id/activity_chooser_view_content\n010201f3=id/add\n010201f4=id/addToDictionaryButton\n010201f5=id/adjustNothing\n010201f6=id/adjustPan\n010201f7=id/adjustResize\n010201f8=id/adjustUnspecified\n010201f9=id/aerr_app_info\n010201fa=id/aerr_close\n010201fb=id/aerr_mute\n010201fc=id/aerr_report\n010201fd=id/aerr_restart\n010201fe=id/aerr_wait\n010201ff=id/afterDescendants\n01020200=id/alarm\n01020201=id/alertTitle\n01020202=id/alerted_icon\n01020203=id/alias\n01020204=id/alignBounds\n01020205=id/alignMargins\n01020206=id/all\n01020207=id/all_scroll\n01020208=id/allowNullAction\n01020209=id/allow_button\n0102020a=id/allowed\n0102020b=id/alternate_expand_target\n0102020c=id/alternative\n0102020d=id/always\n0102020e=id/alwaysScroll\n0102020f=id/alwaysUse\n01020210=id/amPm\n01020211=id/am_label\n01020212=id/am_pm_spinner\n01020213=id/ampm_layout\n01020214=id/animation\n01020215=id/animator\n01020216=id/anyRtl\n01020217=id/appPredictor\n01020218=id/app_name_divider\n01020219=id/app_name_text\n0102021a=id/app_ops\n0102021b=id/appop\n0102021c=id/arc\n0102021d=id/arrow\n0102021e=id/ask_checkbox\n0102021f=id/assertive\n01020220=id/assetsPaths\n01020221=id/async\n01020222=id/atThumb\n01020223=id/audio\n01020224=id/authtoken_type\n01020225=id/auto\n01020226=id/auto_fit\n01020227=id/autofill_button_bar_spacer\n01020228=id/autofill_dataset_footer\n01020229=id/autofill_dataset_header\n0102022a=id/autofill_dataset_icon\n0102022b=id/autofill_dataset_list\n0102022c=id/autofill_dataset_picker\n0102022d=id/autofill_dataset_title\n0102022e=id/autofill_dialog_container\n0102022f=id/autofill_dialog_header\n01020230=id/autofill_dialog_list\n01020231=id/autofill_dialog_no\n01020232=id/autofill_dialog_picker\n01020233=id/autofill_dialog_yes\n01020234=id/autofill_save\n01020235=id/autofill_save_button_bar\n01020236=id/autofill_save_custom_subtitle\n01020237=id/autofill_save_icon\n01020238=id/autofill_save_no\n01020239=id/autofill_save_title\n0102023a=id/autofill_save_yes\n0102023b=id/autofill_service_icon\n0102023c=id/autofill_sheet_divider\n0102023d=id/autofill_sheet_scroll_view\n0102023e=id/autofill_sheet_scroll_view_space\n0102023f=id/azerty\n01020240=id/back_button\n01020241=id/balanced\n01020242=id/beforeDescendants\n01020243=id/beginning\n01020244=id/behind\n01020245=id/bevel\n01020246=id/big_picture\n01020247=id/big_text\n01020248=id/blocksDescendants\n01020249=id/body\n0102024a=id/bool\n0102024b=id/bottom\n0102024c=id/bottom_to_top\n0102024d=id/bounds\n0102024e=id/breadcrumb_section\n0102024f=id/bubble_button\n01020250=id/bundle\n01020251=id/bundle_array\n01020252=id/butt\n01020253=id/button0\n01020254=id/button4\n01020255=id/button5\n01020256=id/button6\n01020257=id/button7\n01020258=id/buttonPanel\n01020259=id/button_always\n0102025a=id/button_bar\n0102025b=id/button_bar_container\n0102025c=id/button_once\n0102025d=id/button_open\n0102025e=id/buttons\n0102025f=id/by_common\n01020260=id/by_common_header\n01020261=id/by_org\n01020262=id/by_org_header\n01020263=id/by_org_unit\n01020264=id/by_org_unit_header\n01020265=id/calendar\n01020266=id/calendar_view\n01020267=id/camera\n01020268=id/cancel\n01020269=id/cell\n0102026a=id/center\n0102026b=id/centerCrop\n0102026c=id/centerInside\n0102026d=id/center_horizontal\n0102026e=id/center_vertical\n0102026f=id/challenge\n01020270=id/characterPicker\n01020271=id/characters\n01020272=id/check\n01020273=id/checked\n01020274=id/choice\n01020275=id/chooser_action_row\n01020276=id/chooser_copy_button\n01020277=id/chooser_edit_button\n01020278=id/chooser_header\n01020279=id/chooser_nearby_button\n0102027a=id/chooser_row\n0102027b=id/chooser_row_text_option\n0102027c=id/chronometer\n0102027d=id/clamp\n0102027e=id/clearDefaultHint\n0102027f=id/clipBounds\n01020280=id/clip_children_set_tag\n01020281=id/clip_children_tag\n01020282=id/clip_horizontal\n01020283=id/clip_to_padding_tag\n01020284=id/clip_vertical\n01020285=id/clock\n01020286=id/close_button\n01020287=id/close_button_pill_colorized_layer\n01020288=id/colemak\n01020289=id/collapseActionView\n0102028a=id/collapsing\n0102028b=id/colorMode\n0102028c=id/colorType\n0102028d=id/column\n0102028e=id/columnWidth\n0102028f=id/companion\n01020290=id/compat_checkbox\n01020291=id/configuration_optional\n01020292=id/configurator\n01020293=id/connectedDevice\n01020294=id/container\n01020295=id/contentPanel\n01020296=id/content_preview_container\n01020297=id/content_preview_file_area\n01020298=id/content_preview_file_icon\n01020299=id/content_preview_file_layout\n0102029a=id/content_preview_file_thumbnail\n0102029b=id/content_preview_filename\n0102029c=id/content_preview_image_1_large\n0102029d=id/content_preview_image_2_large\n0102029e=id/content_preview_image_2_small\n0102029f=id/content_preview_image_3_small\n010202a0=id/content_preview_image_area\n010202a1=id/content_preview_text\n010202a2=id/content_preview_text_area\n010202a3=id/content_preview_text_layout\n010202a4=id/content_preview_thumbnail\n010202a5=id/content_preview_title\n010202a6=id/content_preview_title_layout\n010202a7=id/context_menu\n010202a8=id/controlScreen_description\n010202a9=id/controlScreen_icon\n010202aa=id/controlScreen_title\n010202ab=id/conversation_face_pile\n010202ac=id/conversation_face_pile_bottom\n010202ad=id/conversation_face_pile_bottom_background\n010202ae=id/conversation_face_pile_top\n010202af=id/conversation_header\n010202b0=id/conversation_icon\n010202b1=id/conversation_icon_badge\n010202b2=id/conversation_icon_badge_bg\n010202b3=id/conversation_icon_badge_ring\n010202b4=id/conversation_icon_container\n010202b5=id/conversation_image_message_container\n010202b6=id/conversation_text\n010202b7=id/costsMoney\n010202b8=id/cross_task_transition\n010202b9=id/crossfade\n010202ba=id/crosshair\n010202bb=id/current_scene\n010202bc=id/customPanel\n010202bd=id/cycle\n010202be=id/dangerous\n010202bf=id/dataSync\n010202c0=id/date\n010202c1=id/datePicker\n010202c2=id/date_picker_day_picker\n010202c3=id/date_picker_header\n010202c4=id/date_picker_header_date\n010202c5=id/date_picker_header_year\n010202c6=id/date_picker_year_picker\n010202c7=id/datetime\n010202c8=id/day\n010202c9=id/day_names\n010202ca=id/day_picker_view_pager\n010202cb=id/decimal\n010202cc=id/decor_content_parent\n010202cd=id/decrement\n010202ce=id/default\n010202cf=id/defaultPosition\n010202d0=id/default_activity_button\n010202d1=id/default_loading_view\n010202d2=id/deleteButton\n010202d3=id/density\n010202d4=id/deny_button\n010202d5=id/description\n010202d6=id/development\n010202d7=id/dialog\n010202d8=id/dialog_item\n010202d9=id/disableHome\n010202da=id/disabled\n010202db=id/disallowed\n010202dc=id/discouraged\n010202dd=id/divider\n010202de=id/dpad\n010202df=id/drag\n010202e0=id/dropdown\n010202e1=id/dvorak\n010202e2=id/edit_query\n010202e3=id/editable\n010202e4=id/edittext_container\n010202e5=id/eight\n010202e6=id/email\n010202e7=id/enabled\n010202e8=id/end\n010202e9=id/enforceIntentFilter\n010202ea=id/evenOdd\n010202eb=id/exclude\n010202ec=id/excludeDescendants\n010202ed=id/expandChallengeHandle\n010202ee=id/expand_activities_button\n010202ef=id/expand_button\n010202f0=id/expand_button_a11y_container\n010202f1=id/expand_button_and_content_container\n010202f2=id/expand_button_container\n010202f3=id/expand_button_icon\n010202f4=id/expand_button_number\n010202f5=id/expand_button_pill\n010202f6=id/expand_button_pill_colorized_layer\n010202f7=id/expand_button_touch_container\n010202f8=id/expanded_menu\n010202f9=id/expires_on\n010202fa=id/expires_on_header\n010202fb=id/extended\n010202fc=id/fade_in\n010202fd=id/fade_in_out\n010202fe=id/fade_out\n010202ff=id/feedback\n01020300=id/feedbackAllMask\n01020301=id/feedbackAudible\n01020302=id/feedbackGeneric\n01020303=id/feedbackHaptic\n01020304=id/feedbackSpoken\n01020305=id/feedbackVisual\n01020306=id/ffwd\n01020307=id/fill\n01020308=id/fillInIntent\n01020309=id/fill_horizontal\n0102030a=id/fill_parent\n0102030b=id/fill_vertical\n0102030c=id/find\n0102030d=id/find_next\n0102030e=id/find_prev\n0102030f=id/finger\n01020310=id/fingerprints\n01020311=id/firstStrong\n01020312=id/firstStrongLtr\n01020313=id/firstStrongRtl\n01020314=id/fitCenter\n01020315=id/fitEnd\n01020316=id/fitStart\n01020317=id/fitXY\n01020318=id/five\n01020319=id/flagDefault\n0102031a=id/flagEnableAccessibilityVolume\n0102031b=id/flagForceAscii\n0102031c=id/flagIncludeNotImportantViews\n0102031d=id/flagInputMethodEditor\n0102031e=id/flagNavigateNext\n0102031f=id/flagNavigatePrevious\n01020320=id/flagNoAccessoryAction\n01020321=id/flagNoEnterAction\n01020322=id/flagNoExtractUi\n01020323=id/flagNoFullscreen\n01020324=id/flagNoPersonalizedLearning\n01020325=id/flagReportViewIds\n01020326=id/flagRequestAccessibilityButton\n01020327=id/flagRequestEnhancedWebAccessibility\n01020328=id/flagRequestFilterKeyEvents\n01020329=id/flagRequestFingerprintGestures\n0102032a=id/flagRequestMultiFingerGestures\n0102032b=id/flagRequestShortcutWarningDialogSpokenFeedback\n0102032c=id/flagRequestTouchExplorationMode\n0102032d=id/flagRetrieveInteractiveWindows\n0102032e=id/flagSendMotionEvents\n0102032f=id/flagServiceHandlesDoubleTap\n01020330=id/floatType\n01020331=id/floating\n01020332=id/floating_popup_container\n01020333=id/floating_toolbar_menu_item_image\n01020334=id/floating_toolbar_menu_item_image_button\n01020335=id/floating_toolbar_menu_item_text\n01020336=id/fontScale\n01020337=id/fontWeightAdjustment\n01020338=id/four\n01020339=id/frame\n0102033a=id/full\n0102033b=id/fullFast\n0102033c=id/fullSensor\n0102033d=id/fullUser\n0102033e=id/fullscreenArea\n0102033f=id/future\n01020340=id/game\n01020341=id/gone\n01020342=id/grab\n01020343=id/grabbing\n01020344=id/grammaticalGender\n01020345=id/grant_credentials_permission_message_footer\n01020346=id/grant_credentials_permission_message_header\n01020347=id/gravity\n01020348=id/group_divider\n01020349=id/group_message_container\n0102034a=id/hand\n0102034b=id/handwriting\n0102034c=id/hardRestricted\n0102034d=id/hard_keyboard_section\n0102034e=id/hard_keyboard_switch\n0102034f=id/hardware\n01020350=id/hdpi\n01020351=id/hdr\n01020352=id/header_text\n01020353=id/header_text_divider\n01020354=id/header_text_secondary\n01020355=id/header_text_secondary_divider\n01020356=id/headers\n01020357=id/health\n01020358=id/help\n01020359=id/hidden\n0102035a=id/hide_from_picker\n0102035b=id/high\n0102035c=id/high_quality\n0102035d=id/holo\n0102035e=id/homeAsUp\n0102035f=id/home_panel\n01020360=id/home_screen\n01020361=id/horizontal\n01020362=id/horizontal_double_arrow\n01020363=id/hour\n01020364=id/hour_label_holder\n01020365=id/hours\n01020366=id/icon_badge\n01020367=id/icon_menu\n01020368=id/icon_menu_presenter\n01020369=id/icon_preferred\n0102036a=id/ifContentScrolls\n0102036b=id/ifRoom\n0102036c=id/if_whitelisted\n0102036d=id/image\n0102036e=id/imageView\n0102036f=id/immersive_cling_description\n01020370=id/immersive_cling_icon\n01020371=id/immersive_cling_title\n01020372=id/immutablyRestricted\n01020373=id/inbox_text0\n01020374=id/inbox_text1\n01020375=id/inbox_text2\n01020376=id/inbox_text3\n01020377=id/inbox_text4\n01020378=id/inbox_text5\n01020379=id/inbox_text6\n0102037a=id/incidentReportApprover\n0102037b=id/include\n0102037c=id/increment\n0102037d=id/index\n0102037e=id/indicator\n0102037f=id/infinite\n01020380=id/inherit\n01020381=id/input_block\n01020382=id/input_header\n01020383=id/input_hour\n01020384=id/input_method_nav_back\n01020385=id/input_method_nav_buttons\n01020386=id/input_method_nav_center_group\n01020387=id/input_method_nav_ends_group\n01020388=id/input_method_nav_home_handle\n01020389=id/input_method_nav_horizontal\n0102038a=id/input_method_nav_ime_switcher\n0102038b=id/input_method_nav_inflater\n0102038c=id/input_method_navigation_bar_view\n0102038d=id/input_minute\n0102038e=id/input_mode\n0102038f=id/input_separator\n01020390=id/insertion_handle\n01020391=id/inside\n01020392=id/insideInset\n01020393=id/insideOverlay\n01020394=id/installer\n01020395=id/installerExemptIgnored\n01020396=id/instant\n01020397=id/intType\n01020398=id/integer\n01020399=id/inter_character\n0102039a=id/inter_word\n0102039b=id/internal\n0102039c=id/internalEmpty\n0102039d=id/internalOnly\n0102039e=id/intoExisting\n0102039f=id/invisible\n010203a0=id/issued_on\n010203a1=id/issued_on_header\n010203a2=id/issued_to_header\n010203a3=id/item\n010203a4=id/item_touch_helper_previous_elevation\n010203a5=id/jumpcut\n010203a6=id/keyboard\n010203a7=id/keyboardHidden\n010203a8=id/keyguard\n010203a9=id/keyguard_click_area\n010203aa=id/keyguard_message_area\n010203ab=id/knownSigner\n010203ac=id/label\n010203ad=id/label_error\n010203ae=id/label_hour\n010203af=id/label_minute\n010203b0=id/landscape\n010203b1=id/language_item\n010203b2=id/language_picker_header\n010203b3=id/language_picker_item\n010203b4=id/large\n010203b5=id/launchRecognizer\n010203b6=id/launchWebSearch\n010203b7=id/layoutDirection\n010203b8=id/ldpi\n010203b9=id/left\n010203ba=id/leftPanel\n010203bb=id/leftSpacer\n010203bc=id/left_icon\n010203bd=id/left_to_right\n010203be=id/line\n010203bf=id/line1\n010203c0=id/linear\n010203c1=id/listContainer\n010203c2=id/listMode\n010203c3=id/list_footer\n010203c4=id/list_item\n010203c5=id/list_menu_presenter\n010203c6=id/liveAudio\n010203c7=id/locale\n010203c8=id/locale_native\n010203c9=id/locale_search_menu\n010203ca=id/locale_secondary\n010203cb=id/location\n010203cc=id/lock_screen\n010203cd=id/locked\n010203ce=id/loose\n010203cf=id/low\n010203d0=id/low_light\n010203d1=id/ltr\n010203d2=id/map\n010203d3=id/maps\n010203d4=id/marquee\n010203d5=id/marquee_forever\n010203d6=id/match_parent\n010203d7=id/matches\n010203d8=id/material\n010203d9=id/matrix\n010203da=id/mcc\n010203db=id/mdpi\n010203dc=id/mediaPlayback\n010203dd=id/mediaProcessing\n010203de=id/mediaProjection\n010203df=id/media_actions\n010203e0=id/media_route_extended_settings_button\n010203e1=id/media_route_list\n010203e2=id/media_route_progress_bar\n010203e3=id/media_route_volume_layout\n010203e4=id/media_route_volume_slider\n010203e5=id/mediacontroller_progress\n010203e6=id/medium\n010203e7=id/menu\n010203e8=id/message_icon\n010203e9=id/message_icon_container\n010203ea=id/message_name\n010203eb=id/message_text\n010203ec=id/messaging_group_content_container\n010203ed=id/messaging_group_icon_container\n010203ee=id/messaging_group_sending_progress\n010203ef=id/messaging_group_sending_progress_container\n010203f0=id/mic\n010203f1=id/micro\n010203f2=id/microphone\n010203f3=id/middle\n010203f4=id/midpoint\n010203f5=id/miniresolver_info_section\n010203f6=id/miniresolver_info_section_icon\n010203f7=id/miniresolver_info_section_text\n010203f8=id/minute\n010203f9=id/minutes\n010203fa=id/mirror\n010203fb=id/miter\n010203fc=id/mnc\n010203fd=id/modeLarge\n010203fe=id/modeMedium\n010203ff=id/modeSmall\n01020400=id/mode_in\n01020401=id/mode_normal\n01020402=id/mode_out\n01020403=id/module\n01020404=id/monospace\n01020405=id/month\n01020406=id/month_name\n01020407=id/month_view\n01020408=id/multi-select\n01020409=id/multiple\n0102040a=id/multipleChoice\n0102040b=id/multipleChoiceModal\n0102040c=id/multiply\n0102040d=id/music\n0102040e=id/navigation\n0102040f=id/nest\n01020410=id/never\n01020411=id/neverForLocation\n01020412=id/new_app_action\n01020413=id/new_app_description\n01020414=id/new_app_icon\n01020415=id/news\n01020416=id/next\n01020417=id/next_button\n01020418=id/nine\n01020419=id/no\n0102041a=id/noExcludeDescendants\n0102041b=id/noHideDescendants\n0102041c=id/no_applications_message\n0102041d=id/no_drop\n0102041e=id/no_permissions\n0102041f=id/nokeys\n01020420=id/nonZero\n01020421=id/nonav\n01020422=id/none\n01020423=id/normal\n01020424=id/normalFast\n01020425=id/nosensor\n01020426=id/notSensitive\n01020427=id/not_keyguard\n01020428=id/notification\n01020429=id/notification_action_index_tag\n0102042a=id/notification_action_list_margin_target\n0102042b=id/notification_custom_view_index_tag\n0102042c=id/notification_header\n0102042d=id/notification_headerless_view_column\n0102042e=id/notification_headerless_view_row\n0102042f=id/notification_main_column\n01020430=id/notification_material_reply_container\n01020431=id/notification_material_reply_progress\n01020432=id/notification_material_reply_text_1\n01020433=id/notification_material_reply_text_1_container\n01020434=id/notification_material_reply_text_2\n01020435=id/notification_material_reply_text_3\n01020436=id/notification_media_content\n01020437=id/notification_messaging\n01020438=id/notification_progress_end_icon\n01020439=id/notification_progress_start_icon\n0102043a=id/notification_top_line\n0102043b=id/notouch\n0102043c=id/number\n0102043d=id/numberDecimal\n0102043e=id/numberPassword\n0102043f=id/numberSigned\n01020440=id/numberpicker_input\n01020441=id/oem\n01020442=id/off\n01020443=id/ok\n01020444=id/old_app_action\n01020445=id/old_app_icon\n01020446=id/on\n01020447=id/one\n01020448=id/oneLine\n01020449=id/opaque\n0102044a=id/open_cross_profile\n0102044b=id/opticalBounds\n0102044c=id/option1\n0102044d=id/option2\n0102044e=id/option3\n0102044f=id/orientation\n01020450=id/original_app_icon\n01020451=id/original_message\n01020452=id/outsideInset\n01020453=id/outsideOverlay\n01020454=id/oval\n01020455=id/overflow\n01020456=id/overflow_menu_presenter\n01020457=id/overlay\n01020458=id/overlay_display_window_texture\n01020459=id/overlay_display_window_title\n0102045a=id/package_icon\n0102045b=id/package_label\n0102045c=id/packages_list\n0102045d=id/paddedBounds\n0102045e=id/pageDeleteDropTarget\n0102045f=id/parentMatrix\n01020460=id/parentPanel\n01020461=id/past\n01020462=id/pathType\n01020463=id/pause\n01020464=id/pending_intent_tag\n01020465=id/performAction_description\n01020466=id/performAction_icon\n01020467=id/performAction_title\n01020468=id/perm_icon\n01020469=id/perm_money_icon\n0102046a=id/perm_money_label\n0102046b=id/perm_name\n0102046c=id/permissionDialog_description\n0102046d=id/permission_group\n0102046e=id/permission_icon\n0102046f=id/permission_list\n01020470=id/perms_list\n01020471=id/persistAcrossReboots\n01020472=id/persistNever\n01020473=id/persistRootOnly\n01020474=id/personalInfo\n01020475=id/phishing_alert\n01020476=id/phone\n01020477=id/phoneCall\n01020478=id/phrase\n01020479=id/pickers\n0102047a=id/pin_cancel_button\n0102047b=id/pin_confirm_text\n0102047c=id/pin_error_message\n0102047d=id/pin_message\n0102047e=id/pin_new_text\n0102047f=id/pin_ok_button\n01020480=id/pin_text\n01020481=id/placeholder\n01020482=id/pm_label\n01020483=id/polite\n01020484=id/popup_submenu_presenter\n01020485=id/portrait\n01020486=id/pre23\n01020487=id/preferExternal\n01020488=id/prefs\n01020489=id/prefs_container\n0102048a=id/prefs_frame\n0102048b=id/preinstalled\n0102048c=id/pressed\n0102048d=id/prev\n0102048e=id/privileged\n0102048f=id/productivity\n01020490=id/profile_badge\n01020491=id/profile_button\n01020492=id/profile_pager\n01020493=id/profile_tabhost\n01020494=id/progressContainer\n01020495=id/progress_circular\n01020496=id/progress_dialog_message\n01020497=id/progress_horizontal\n01020498=id/progress_number\n01020499=id/progress_percent\n0102049a=id/queryRewriteFromData\n0102049b=id/queryRewriteFromText\n0102049c=id/qwerty\n0102049d=id/qwertz\n0102049e=id/radial\n0102049f=id/radial_picker\n010204a0=id/radio\n010204a1=id/radio_power\n010204a2=id/random\n010204a3=id/read\n010204a4=id/readAndWrite\n010204a5=id/readOrWrite\n010204a6=id/reask_hint\n010204a7=id/recents\n010204a8=id/reconfigurable\n010204a9=id/rectangle\n010204aa=id/remoteMessaging\n010204ab=id/remoteViewsMetricsId\n010204ac=id/remote_checked_change_listener_tag\n010204ad=id/remote_input\n010204ae=id/remote_input_progress\n010204af=id/remote_input_send\n010204b0=id/remote_input_tag\n010204b1=id/remote_input_text\n010204b2=id/remote_views_next_child\n010204b3=id/remote_views_override_id\n010204b4=id/remote_views_stable_id\n010204b5=id/removed\n010204b6=id/repeat\n010204b7=id/replace_app_icon\n010204b8=id/replace_message\n010204b9=id/reply_action_container\n010204ba=id/resolver_button_bar_divider\n010204bb=id/resolver_empty_state\n010204bc=id/resolver_empty_state_button\n010204bd=id/resolver_empty_state_container\n010204be=id/resolver_empty_state_icon\n010204bf=id/resolver_empty_state_progress\n010204c0=id/resolver_empty_state_subtitle\n010204c1=id/resolver_empty_state_title\n010204c2=id/resolver_list\n010204c3=id/resolver_tab_divider\n010204c4=id/resourcesUnused\n010204c5=id/restart\n010204c6=id/restore\n010204c7=id/retailDemo\n010204c8=id/retain\n010204c9=id/reverse\n010204ca=id/reverseLandscape\n010204cb=id/reversePortrait\n010204cc=id/rew\n010204cd=id/right\n010204ce=id/rightSpacer\n010204cf=id/right_container\n010204d0=id/right_icon\n010204d1=id/right_to_left\n010204d2=id/ring\n010204d3=id/ringtone\n010204d4=id/role\n010204d5=id/rotate\n010204d6=id/round\n010204d7=id/row\n010204d8=id/rowTypeId\n010204d9=id/rtl\n010204da=id/runtime\n010204db=id/sans\n010204dc=id/scene_layoutid_cache\n010204dd=id/screen\n010204de=id/screenLayout\n010204df=id/screenSize\n010204e0=id/scrim\n010204e1=id/scrollView\n010204e2=id/scroll_down\n010204e3=id/scroll_down_layout\n010204e4=id/scroll_exit\n010204e5=id/scroll_exit_layout\n010204e6=id/scroll_left\n010204e7=id/scroll_left_layout\n010204e8=id/scroll_right\n010204e9=id/scroll_right_layout\n010204ea=id/scroll_up\n010204eb=id/scroll_up_layout\n010204ec=id/scrolling\n010204ed=id/seamless\n010204ee=id/search_app_icon\n010204ef=id/search_badge\n010204f0=id/search_bar\n010204f1=id/search_button\n010204f2=id/search_close_btn\n010204f3=id/search_edit_frame\n010204f4=id/search_go_btn\n010204f5=id/search_mag_icon\n010204f6=id/search_plate\n010204f7=id/search_src_text\n010204f8=id/search_view\n010204f9=id/search_voice_btn\n010204fa=id/searchbox\n010204fb=id/secondary\n010204fc=id/seekbar\n010204fd=id/select_all\n010204fe=id/select_dialog_listview\n010204ff=id/selection_end_handle\n01020500=id/selection_start_handle\n01020501=id/sensitive\n01020502=id/sensor\n01020503=id/sensorLandscape\n01020504=id/sensorPortrait\n01020505=id/sentences\n01020506=id/separator\n01020507=id/sequential\n01020508=id/sequentially\n01020509=id/serial_number\n0102050a=id/serial_number_header\n0102050b=id/serif\n0102050c=id/setup\n0102050d=id/seven\n0102050e=id/sha1_fingerprint\n0102050f=id/sha1_fingerprint_header\n01020510=id/sha256_fingerprint\n01020511=id/sha256_fingerprint_header\n01020512=id/share\n01020513=id/shortEdges\n01020514=id/shortService\n01020515=id/shortcut\n01020516=id/shortcuts_container\n01020517=id/shortest\n01020518=id/showCustom\n01020519=id/showHome\n0102051a=id/showSearchIconAsBadge\n0102051b=id/showSearchLabelAsBadge\n0102051c=id/showTitle\n0102051d=id/showVoiceSearchButton\n0102051e=id/signature\n0102051f=id/signatureOrSystem\n01020520=id/signed\n01020521=id/silent\n01020522=id/simple\n01020523=id/single\n01020524=id/singleChoice\n01020525=id/singleInstance\n01020526=id/singleInstancePerTask\n01020527=id/singleTask\n01020528=id/singleTop\n01020529=id/six\n0102052a=id/skip_button\n0102052b=id/small\n0102052c=id/smallIcon\n0102052d=id/smallestScreenSize\n0102052e=id/smart_reply_container\n0102052f=id/sms_short_code_coins_icon\n01020530=id/sms_short_code_confirm_message\n01020531=id/sms_short_code_detail_layout\n01020532=id/sms_short_code_detail_message\n01020533=id/sms_short_code_remember_choice_checkbox\n01020534=id/sms_short_code_remember_choice_text\n01020535=id/sms_short_code_remember_undo_instruction\n01020536=id/snooze_button\n01020537=id/social\n01020538=id/softRestricted\n01020539=id/software\n0102053a=id/spacer\n0102053b=id/spacingWidth\n0102053c=id/spacingWidthUniform\n0102053d=id/spannable\n0102053e=id/specialUse\n0102053f=id/spinner\n01020540=id/splashscreen\n01020541=id/splashscreen_branding_view\n01020542=id/splashscreen_icon_view\n01020543=id/splitActionBarWhenNarrow\n01020544=id/split_action_bar\n01020545=id/square\n01020546=id/src_atop\n01020547=id/src_in\n01020548=id/src_over\n01020549=id/stack\n0102054a=id/standard\n0102054b=id/start\n0102054c=id/stateAlwaysHidden\n0102054d=id/stateAlwaysVisible\n0102054e=id/stateHidden\n0102054f=id/stateUnchanged\n01020550=id/stateUnspecified\n01020551=id/stateVisible\n01020552=id/status\n01020553=id/status_bar_latest_event_content\n01020554=id/strict\n01020555=id/string\n01020556=id/stub\n01020557=id/stylus\n01020558=id/sub_color\n01020559=id/sub_name\n0102055a=id/sub_number\n0102055b=id/sub_short_number\n0102055c=id/submenuarrow\n0102055d=id/submit_area\n0102055e=id/suggestionContainer\n0102055f=id/suggestionWindowContainer\n01020560=id/surfaceView\n01020561=id/sweep\n01020562=id/switch_new\n01020563=id/switch_old\n01020564=id/sync\n01020565=id/system\n01020566=id/systemExempted\n01020567=id/system_language_view\n01020568=id/system_locale_subtitle\n01020569=id/tabMode\n0102056a=id/tabs_container\n0102056b=id/tag_alpha_animator\n0102056c=id/tag_is_first_layout\n0102056d=id/tag_keep_when_showing_left_icon\n0102056e=id/tag_layout_top\n0102056f=id/tag_margin_end_when_icon_gone\n01020570=id/tag_margin_end_when_icon_visible\n01020571=id/tag_top_animator\n01020572=id/tag_top_override\n01020573=id/tag_uses_right_icon_drawable\n01020574=id/text\n01020575=id/textAutoComplete\n01020576=id/textAutoCorrect\n01020577=id/textCapCharacters\n01020578=id/textCapSentences\n01020579=id/textCapWords\n0102057a=id/textClassifier\n0102057b=id/textEmailAddress\n0102057c=id/textEmailSubject\n0102057d=id/textEnableTextConversionSuggestions\n0102057e=id/textEnd\n0102057f=id/textFilter\n01020580=id/textImeMultiLine\n01020581=id/textLongMessage\n01020582=id/textMultiLine\n01020583=id/textNoSuggestions\n01020584=id/textPassword\n01020585=id/textPersonName\n01020586=id/textPhonetic\n01020587=id/textPostalAddress\n01020588=id/textShortMessage\n01020589=id/textSpacerNoButtons\n0102058a=id/textSpacerNoTitle\n0102058b=id/textStart\n0102058c=id/textUri\n0102058d=id/textVisiblePassword\n0102058e=id/textWebEditText\n0102058f=id/textWebEmailAddress\n01020590=id/textWebPassword\n01020591=id/text_layout\n01020592=id/textureView\n01020593=id/three\n01020594=id/time\n01020595=id/timePicker\n01020596=id/timePickerLayout\n01020597=id/time_current\n01020598=id/time_divider\n01020599=id/time_header\n0102059a=id/time_layout\n0102059b=id/titleDivider\n0102059c=id/titleDividerNoCustom\n0102059d=id/titleDividerTop\n0102059e=id/title_container\n0102059f=id/title_separator\n010205a0=id/title_template\n010205a1=id/to_common\n010205a2=id/to_common_header\n010205a3=id/to_org\n010205a4=id/to_org_header\n010205a5=id/to_org_unit\n010205a6=id/to_org_unit_header\n010205a7=id/together\n010205a8=id/toggle_mode\n010205a9=id/top\n010205aa=id/topPanel\n010205ab=id/top_label\n010205ac=id/top_left_diagonal_double_arrow\n010205ad=id/top_right_diagonal_double_arrow\n010205ae=id/top_to_bottom\n010205af=id/touchscreen\n010205b0=id/trackball\n010205b1=id/transitionPosition\n010205b2=id/transitionTransform\n010205b3=id/transition_overlay_view_tag\n010205b4=id/translucent\n010205b5=id/transparent\n010205b6=id/turkish_f\n010205b7=id/turkish_q\n010205b8=id/turn_off_screen\n010205b9=id/twelvekey\n010205ba=id/two\n010205bb=id/twoLine\n010205bc=id/typeAllMask\n010205bd=id/typeAnnouncement\n010205be=id/typeAssistReadingContext\n010205bf=id/typeContextClicked\n010205c0=id/typeGestureDetectionEnd\n010205c1=id/typeGestureDetectionStart\n010205c2=id/typeNotificationStateChanged\n010205c3=id/typeTouchExplorationGestureEnd\n010205c4=id/typeTouchExplorationGestureStart\n010205c5=id/typeTouchInteractionEnd\n010205c6=id/typeTouchInteractionStart\n010205c7=id/typeViewAccessibilityFocusCleared\n010205c8=id/typeViewAccessibilityFocused\n010205c9=id/typeViewClicked\n010205ca=id/typeViewFocused\n010205cb=id/typeViewHoverEnter\n010205cc=id/typeViewHoverExit\n010205cd=id/typeViewLongClicked\n010205ce=id/typeViewScrolled\n010205cf=id/typeViewSelected\n010205d0=id/typeViewTextChanged\n010205d1=id/typeViewTextSelectionChanged\n010205d2=id/typeViewTextTraversedAtMovementGranularity\n010205d3=id/typeWindowContentChanged\n010205d4=id/typeWindowStateChanged\n010205d5=id/typeWindowsChanged\n010205d6=id/uiMode\n010205d7=id/unchecked\n010205d8=id/undefined\n010205d9=id/uniform\n010205da=id/unpressed\n010205db=id/unspecified\n010205dc=id/up\n010205dd=id/useLogo\n010205de=id/use_same_profile_browser\n010205df=id/user\n010205e0=id/userIdentification\n010205e1=id/userLandscape\n010205e2=id/userPortrait\n010205e3=id/userSwitcher\n010205e4=id/validity_header\n010205e5=id/value\n010205e6=id/vendorPrivileged\n010205e7=id/verification_divider\n010205e8=id/verification_icon\n010205e9=id/verification_text\n010205ea=id/verifier\n010205eb=id/vertical\n010205ec=id/vertical_double_arrow\n010205ed=id/vertical_text\n010205ee=id/video\n010205ef=id/viewEnd\n010205f0=id/viewStart\n010205f1=id/visible\n010205f2=id/voice\n010205f3=id/voiceTrigger\n010205f4=id/wait\n010205f5=id/web\n010205f6=id/websearch\n010205f7=id/webview\n010205f8=id/wheel\n010205f9=id/wideColorGamut\n010205fa=id/widget\n010205fb=id/widgets\n010205fc=id/wipe\n010205fd=id/withText\n010205fe=id/words\n010205ff=id/work_widget_app_icon\n01020600=id/work_widget_badge_icon\n01020601=id/workman\n01020602=id/wrap_content\n01020603=id/write\n01020604=id/xhdpi\n01020605=id/xlarge\n01020606=id/xxhdpi\n01020607=id/xxxhdpi\n01020608=id/year\n01020609=id/yes\n0102060a=id/yesExcludeDescendants\n0102060b=id/zero\n0102060c=id/zoomControls\n0102060d=id/zoomIn\n0102060e=id/zoomMagnify\n0102060f=id/zoomOut\n01020610=id/zoom_fit_page\n01020611=id/zoom_in\n01020612=id/zoom_out\n01020613=id/zoom_page_overview\n01030000=style/Animation\n01030001=style/Animation.Activity\n01030002=style/Animation.Dialog\n01030003=style/Animation.Translucent\n01030004=style/Animation.Toast\n01030005=style/Theme\n01030006=style/Theme.NoTitleBar\n01030007=style/Theme.NoTitleBar.Fullscreen\n01030008=style/Theme.Black\n01030009=style/Theme.Black.NoTitleBar\n0103000a=style/Theme.Black.NoTitleBar.Fullscreen\n0103000b=style/Theme.Dialog\n0103000c=style/Theme.Light\n0103000d=style/Theme.Light.NoTitleBar\n0103000e=style/Theme.Light.NoTitleBar.Fullscreen\n0103000f=style/Theme.Translucent\n01030010=style/Theme.Translucent.NoTitleBar\n01030011=style/Theme.Translucent.NoTitleBar.Fullscreen\n01030012=style/Widget\n01030013=style/Widget.AbsListView\n01030014=style/Widget.Button\n01030015=style/Widget.Button.Inset\n01030016=style/Widget.Button.Small\n01030017=style/Widget.Button.Toggle\n01030018=style/Widget.CompoundButton\n01030019=style/Widget.CompoundButton.CheckBox\n0103001a=style/Widget.CompoundButton.RadioButton\n0103001b=style/Widget.CompoundButton.Star\n0103001c=style/Widget.ProgressBar\n0103001d=style/Widget.ProgressBar.Large\n0103001e=style/Widget.ProgressBar.Small\n0103001f=style/Widget.ProgressBar.Horizontal\n01030020=style/Widget.SeekBar\n01030021=style/Widget.RatingBar\n01030022=style/Widget.TextView\n01030023=style/Widget.EditText\n01030024=style/Widget.ExpandableListView\n01030025=style/Widget.ImageWell\n01030026=style/Widget.ImageButton\n01030027=style/Widget.AutoCompleteTextView\n01030028=style/Widget.Spinner\n01030029=style/Widget.TextView.PopupMenu\n0103002a=style/Widget.TextView.SpinnerItem\n0103002b=style/Widget.DropDownItem\n0103002c=style/Widget.DropDownItem.Spinner\n0103002d=style/Widget.ScrollView\n0103002e=style/Widget.ListView\n0103002f=style/Widget.ListView.White\n01030030=style/Widget.ListView.DropDown\n01030031=style/Widget.ListView.Menu\n01030032=style/Widget.GridView\n01030033=style/Widget.WebView\n01030034=style/Widget.TabWidget\n01030035=style/Widget.Gallery\n01030036=style/Widget.PopupWindow\n01030037=style/MediaButton\n01030038=style/MediaButton.Previous\n01030039=style/MediaButton.Next\n0103003a=style/MediaButton.Play\n0103003b=style/MediaButton.Ffwd\n0103003c=style/MediaButton.Rew\n0103003d=style/MediaButton.Pause\n0103003e=style/TextAppearance\n0103003f=style/TextAppearance.Inverse\n01030040=style/TextAppearance.Theme\n01030041=style/TextAppearance.DialogWindowTitle\n01030042=style/TextAppearance.Large\n01030043=style/TextAppearance.Large.Inverse\n01030044=style/TextAppearance.Medium\n01030045=style/TextAppearance.Medium.Inverse\n01030046=style/TextAppearance.Small\n01030047=style/TextAppearance.Small.Inverse\n01030048=style/TextAppearance.Theme.Dialog\n01030049=style/TextAppearance.Widget\n0103004a=style/TextAppearance.Widget.Button\n0103004b=style/TextAppearance.Widget.IconMenu.Item\n0103004c=style/TextAppearance.Widget.EditText\n0103004d=style/TextAppearance.Widget.TabWidget\n0103004e=style/TextAppearance.Widget.TextView\n0103004f=style/TextAppearance.Widget.TextView.PopupMenu\n01030050=style/TextAppearance.Widget.DropDownHint\n01030051=style/TextAppearance.Widget.DropDownItem\n01030052=style/TextAppearance.Widget.TextView.SpinnerItem\n01030053=style/TextAppearance.WindowTitle\n01030054=style/Theme.InputMethod\n01030055=style/Theme.NoDisplay\n01030056=style/Animation.InputMethod\n01030057=style/Widget.KeyboardView\n01030058=style/ButtonBar\n01030059=style/Theme.Panel\n0103005a=style/Theme.Light.Panel\n0103005b=style/Widget.ProgressBar.Inverse\n0103005c=style/Widget.ProgressBar.Large.Inverse\n0103005d=style/Widget.ProgressBar.Small.Inverse\n0103005e=style/Theme.Wallpaper\n0103005f=style/Theme.Wallpaper.NoTitleBar\n01030060=style/Theme.Wallpaper.NoTitleBar.Fullscreen\n01030061=style/Theme.WallpaperSettings\n01030062=style/Theme.Light.WallpaperSettings\n01030063=style/TextAppearance.SearchResult.Title\n01030064=style/TextAppearance.SearchResult.Subtitle\n01030065=style/TextAppearance.StatusBar.Title\n01030066=style/TextAppearance.StatusBar.Icon\n01030067=style/TextAppearance.StatusBar.EventContent\n01030068=style/TextAppearance.StatusBar.EventContent.Title\n01030069=style/Theme.WithActionBar\n0103006a=style/Theme.NoTitleBar.OverlayActionModes\n0103006b=style/Theme.Holo\n0103006c=style/Theme.Holo.NoActionBar\n0103006d=style/Theme.Holo.NoActionBar.Fullscreen\n0103006e=style/Theme.Holo.Light\n0103006f=style/Theme.Holo.Dialog\n01030070=style/Theme.Holo.Dialog.MinWidth\n01030071=style/Theme.Holo.Dialog.NoActionBar\n01030072=style/Theme.Holo.Dialog.NoActionBar.MinWidth\n01030073=style/Theme.Holo.Light.Dialog\n01030074=style/Theme.Holo.Light.Dialog.MinWidth\n01030075=style/Theme.Holo.Light.Dialog.NoActionBar\n01030076=style/Theme.Holo.Light.Dialog.NoActionBar.MinWidth\n01030077=style/Theme.Holo.DialogWhenLarge\n01030078=style/Theme.Holo.DialogWhenLarge.NoActionBar\n01030079=style/Theme.Holo.Light.DialogWhenLarge\n0103007a=style/Theme.Holo.Light.DialogWhenLarge.NoActionBar\n0103007b=style/Theme.Holo.Panel\n0103007c=style/Theme.Holo.Light.Panel\n0103007d=style/Theme.Holo.Wallpaper\n0103007e=style/Theme.Holo.Wallpaper.NoTitleBar\n0103007f=style/Theme.Holo.InputMethod\n01030080=style/TextAppearance.Widget.PopupMenu.Large\n01030081=style/TextAppearance.Widget.PopupMenu.Small\n01030082=style/Widget.ActionBar\n01030083=style/Widget.Spinner.DropDown\n01030084=style/Widget.ActionButton\n01030085=style/Widget.ListPopupWindow\n01030086=style/Widget.PopupMenu\n01030087=style/Widget.ActionButton.Overflow\n01030088=style/Widget.ActionButton.CloseMode\n01030089=style/Widget.FragmentBreadCrumbs\n0103008a=style/Widget.Holo\n0103008b=style/Widget.Holo.Button\n0103008c=style/Widget.Holo.Button.Small\n0103008d=style/Widget.Holo.Button.Inset\n0103008e=style/Widget.Holo.Button.Toggle\n0103008f=style/Widget.Holo.TextView\n01030090=style/Widget.Holo.AutoCompleteTextView\n01030091=style/Widget.Holo.CompoundButton.CheckBox\n01030092=style/Widget.Holo.ListView.DropDown\n01030093=style/Widget.Holo.EditText\n01030094=style/Widget.Holo.ExpandableListView\n01030095=style/Widget.Holo.GridView\n01030096=style/Widget.Holo.ImageButton\n01030097=style/Widget.Holo.ListView\n01030098=style/Widget.Holo.PopupWindow\n01030099=style/Widget.Holo.ProgressBar\n0103009a=style/Widget.Holo.ProgressBar.Horizontal\n0103009b=style/Widget.Holo.ProgressBar.Small\n0103009c=style/Widget.Holo.ProgressBar.Small.Title\n0103009d=style/Widget.Holo.ProgressBar.Large\n0103009e=style/Widget.Holo.SeekBar\n0103009f=style/Widget.Holo.RatingBar\n010300a0=style/Widget.Holo.RatingBar.Indicator\n010300a1=style/Widget.Holo.RatingBar.Small\n010300a2=style/Widget.Holo.CompoundButton.RadioButton\n010300a3=style/Widget.Holo.ScrollView\n010300a4=style/Widget.Holo.HorizontalScrollView\n010300a5=style/Widget.Holo.Spinner\n010300a6=style/Widget.Holo.CompoundButton.Star\n010300a7=style/Widget.Holo.TabWidget\n010300a8=style/Widget.Holo.WebTextView\n010300a9=style/Widget.Holo.WebView\n010300aa=style/Widget.Holo.DropDownItem\n010300ab=style/Widget.Holo.DropDownItem.Spinner\n010300ac=style/Widget.Holo.TextView.SpinnerItem\n010300ad=style/Widget.Holo.ListPopupWindow\n010300ae=style/Widget.Holo.PopupMenu\n010300af=style/Widget.Holo.ActionButton\n010300b0=style/Widget.Holo.ActionButton.Overflow\n010300b1=style/Widget.Holo.ActionButton.TextButton\n010300b2=style/Widget.Holo.ActionMode\n010300b3=style/Widget.Holo.ActionButton.CloseMode\n010300b4=style/Widget.Holo.ActionBar\n010300b5=style/Widget.Holo.Light\n010300b6=style/Widget.Holo.Light.Button\n010300b7=style/Widget.Holo.Light.Button.Small\n010300b8=style/Widget.Holo.Light.Button.Inset\n010300b9=style/Widget.Holo.Light.Button.Toggle\n010300ba=style/Widget.Holo.Light.TextView\n010300bb=style/Widget.Holo.Light.AutoCompleteTextView\n010300bc=style/Widget.Holo.Light.CompoundButton.CheckBox\n010300bd=style/Widget.Holo.Light.ListView.DropDown\n010300be=style/Widget.Holo.Light.EditText\n010300bf=style/Widget.Holo.Light.ExpandableListView\n010300c0=style/Widget.Holo.Light.GridView\n010300c1=style/Widget.Holo.Light.ImageButton\n010300c2=style/Widget.Holo.Light.ListView\n010300c3=style/Widget.Holo.Light.PopupWindow\n010300c4=style/Widget.Holo.Light.ProgressBar\n010300c5=style/Widget.Holo.Light.ProgressBar.Horizontal\n010300c6=style/Widget.Holo.Light.ProgressBar.Small\n010300c7=style/Widget.Holo.Light.ProgressBar.Small.Title\n010300c8=style/Widget.Holo.Light.ProgressBar.Large\n010300c9=style/Widget.Holo.Light.ProgressBar.Inverse\n010300ca=style/Widget.Holo.Light.ProgressBar.Small.Inverse\n010300cb=style/Widget.Holo.Light.ProgressBar.Large.Inverse\n010300cc=style/Widget.Holo.Light.SeekBar\n010300cd=style/Widget.Holo.Light.RatingBar\n010300ce=style/Widget.Holo.Light.RatingBar.Indicator\n010300cf=style/Widget.Holo.Light.RatingBar.Small\n010300d0=style/Widget.Holo.Light.CompoundButton.RadioButton\n010300d1=style/Widget.Holo.Light.ScrollView\n010300d2=style/Widget.Holo.Light.HorizontalScrollView\n010300d3=style/Widget.Holo.Light.Spinner\n010300d4=style/Widget.Holo.Light.CompoundButton.Star\n010300d5=style/Widget.Holo.Light.TabWidget\n010300d6=style/Widget.Holo.Light.WebTextView\n010300d7=style/Widget.Holo.Light.WebView\n010300d8=style/Widget.Holo.Light.DropDownItem\n010300d9=style/Widget.Holo.Light.DropDownItem.Spinner\n010300da=style/Widget.Holo.Light.TextView.SpinnerItem\n010300db=style/Widget.Holo.Light.ListPopupWindow\n010300dc=style/Widget.Holo.Light.PopupMenu\n010300dd=style/Widget.Holo.Light.ActionButton\n010300de=style/Widget.Holo.Light.ActionButton.Overflow\n010300df=style/Widget.Holo.Light.ActionMode\n010300e0=style/Widget.Holo.Light.ActionButton.CloseMode\n010300e1=style/Widget.Holo.Light.ActionBar\n010300e2=style/Widget.Holo.Button.Borderless\n010300e3=style/Widget.Holo.Tab\n010300e4=style/Widget.Holo.Light.Tab\n010300e5=style/Holo.ButtonBar\n010300e6=style/Holo.Light.ButtonBar\n010300e7=style/Holo.ButtonBar.AlertDialog\n010300e8=style/Holo.Light.ButtonBar.AlertDialog\n010300e9=style/Holo.SegmentedButton\n010300ea=style/Holo.Light.SegmentedButton\n010300eb=style/Widget.CalendarView\n010300ec=style/Widget.Holo.CalendarView\n010300ed=style/Widget.Holo.Light.CalendarView\n010300ee=style/Widget.DatePicker\n010300ef=style/Widget.Holo.DatePicker\n010300f0=style/Theme.Holo.Light.NoActionBar\n010300f1=style/Theme.Holo.Light.NoActionBar.Fullscreen\n010300f2=style/Widget.ActionBar.TabView\n010300f3=style/Widget.ActionBar.TabText\n010300f4=style/Widget.ActionBar.TabBar\n010300f5=style/Widget.Holo.ActionBar.TabView\n010300f6=style/Widget.Holo.ActionBar.TabText\n010300f7=style/Widget.Holo.ActionBar.TabBar\n010300f8=style/Widget.Holo.Light.ActionBar.TabView\n010300f9=style/Widget.Holo.Light.ActionBar.TabText\n010300fa=style/Widget.Holo.Light.ActionBar.TabBar\n010300fb=style/TextAppearance.Holo\n010300fc=style/TextAppearance.Holo.Inverse\n010300fd=style/TextAppearance.Holo.Large\n010300fe=style/TextAppearance.Holo.Large.Inverse\n010300ff=style/TextAppearance.Holo.Medium\n01030100=style/TextAppearance.Holo.Medium.Inverse\n01030101=style/TextAppearance.Holo.Small\n01030102=style/TextAppearance.Holo.Small.Inverse\n01030103=style/TextAppearance.Holo.SearchResult.Title\n01030104=style/TextAppearance.Holo.SearchResult.Subtitle\n01030105=style/TextAppearance.Holo.Widget\n01030106=style/TextAppearance.Holo.Widget.Button\n01030107=style/TextAppearance.Holo.Widget.IconMenu.Item\n01030108=style/TextAppearance.Holo.Widget.TabWidget\n01030109=style/TextAppearance.Holo.Widget.TextView\n0103010a=style/TextAppearance.Holo.Widget.TextView.PopupMenu\n0103010b=style/TextAppearance.Holo.Widget.DropDownHint\n0103010c=style/TextAppearance.Holo.Widget.DropDownItem\n0103010d=style/TextAppearance.Holo.Widget.TextView.SpinnerItem\n0103010e=style/TextAppearance.Holo.Widget.EditText\n0103010f=style/TextAppearance.Holo.Widget.PopupMenu\n01030110=style/TextAppearance.Holo.Widget.PopupMenu.Large\n01030111=style/TextAppearance.Holo.Widget.PopupMenu.Small\n01030112=style/TextAppearance.Holo.Widget.ActionBar.Title\n01030113=style/TextAppearance.Holo.Widget.ActionBar.Subtitle\n01030114=style/TextAppearance.Holo.Widget.ActionMode.Title\n01030115=style/TextAppearance.Holo.Widget.ActionMode.Subtitle\n01030116=style/TextAppearance.Holo.WindowTitle\n01030117=style/TextAppearance.Holo.DialogWindowTitle\n01030118=style/TextAppearance.SuggestionHighlight\n01030119=style/Theme.Holo.Light.DarkActionBar\n0103011a=style/Widget.Holo.Button.Borderless.Small\n0103011b=style/Widget.Holo.Light.Button.Borderless.Small\n0103011c=style/TextAppearance.Holo.Widget.ActionBar.Title.Inverse\n0103011d=style/TextAppearance.Holo.Widget.ActionBar.Subtitle.Inverse\n0103011e=style/TextAppearance.Holo.Widget.ActionMode.Title.Inverse\n0103011f=style/TextAppearance.Holo.Widget.ActionMode.Subtitle.Inverse\n01030120=style/TextAppearance.Holo.Widget.ActionBar.Menu\n01030121=style/Widget.Holo.ActionBar.Solid\n01030122=style/Widget.Holo.Light.ActionBar.Solid\n01030123=style/Widget.Holo.Light.ActionBar.Solid.Inverse\n01030124=style/Widget.Holo.Light.ActionBar.TabBar.Inverse\n01030125=style/Widget.Holo.Light.ActionBar.TabView.Inverse\n01030126=style/Widget.Holo.Light.ActionBar.TabText.Inverse\n01030127=style/Widget.Holo.Light.ActionMode.Inverse\n01030128=style/Theme.DeviceDefault\n01030129=style/Theme.DeviceDefault.NoActionBar\n0103012a=style/Theme.DeviceDefault.NoActionBar.Fullscreen\n0103012b=style/Theme.DeviceDefault.Light\n0103012c=style/Theme.DeviceDefault.Light.NoActionBar\n0103012d=style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen\n0103012e=style/Theme.DeviceDefault.Dialog\n0103012f=style/Theme.DeviceDefault.Dialog.MinWidth\n01030130=style/Theme.DeviceDefault.Dialog.NoActionBar\n01030131=style/Theme.DeviceDefault.Dialog.NoActionBar.MinWidth\n01030132=style/Theme.DeviceDefault.Light.Dialog\n01030133=style/Theme.DeviceDefault.Light.Dialog.MinWidth\n01030134=style/Theme.DeviceDefault.Light.Dialog.NoActionBar\n01030135=style/Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth\n01030136=style/Theme.DeviceDefault.DialogWhenLarge\n01030137=style/Theme.DeviceDefault.DialogWhenLarge.NoActionBar\n01030138=style/Theme.DeviceDefault.Light.DialogWhenLarge\n01030139=style/Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar\n0103013a=style/Theme.DeviceDefault.Panel\n0103013b=style/Theme.DeviceDefault.Light.Panel\n0103013c=style/Theme.DeviceDefault.Wallpaper\n0103013d=style/Theme.DeviceDefault.Wallpaper.NoTitleBar\n0103013e=style/Theme.DeviceDefault.InputMethod\n0103013f=style/Theme.DeviceDefault.Light.DarkActionBar\n01030140=style/Widget.DeviceDefault\n01030141=style/Widget.DeviceDefault.Button\n01030142=style/Widget.DeviceDefault.Button.Small\n01030143=style/Widget.DeviceDefault.Button.Inset\n01030144=style/Widget.DeviceDefault.Button.Toggle\n01030145=style/Widget.DeviceDefault.Button.Borderless.Small\n01030146=style/Widget.DeviceDefault.TextView\n01030147=style/Widget.DeviceDefault.AutoCompleteTextView\n01030148=style/Widget.DeviceDefault.CompoundButton.CheckBox\n01030149=style/Widget.DeviceDefault.ListView.DropDown\n0103014a=style/Widget.DeviceDefault.EditText\n0103014b=style/Widget.DeviceDefault.ExpandableListView\n0103014c=style/Widget.DeviceDefault.GridView\n0103014d=style/Widget.DeviceDefault.ImageButton\n0103014e=style/Widget.DeviceDefault.ListView\n0103014f=style/Widget.DeviceDefault.PopupWindow\n01030150=style/Widget.DeviceDefault.ProgressBar\n01030151=style/Widget.DeviceDefault.ProgressBar.Horizontal\n01030152=style/Widget.DeviceDefault.ProgressBar.Small\n01030153=style/Widget.DeviceDefault.ProgressBar.Small.Title\n01030154=style/Widget.DeviceDefault.ProgressBar.Large\n01030155=style/Widget.DeviceDefault.SeekBar\n01030156=style/Widget.DeviceDefault.RatingBar\n01030157=style/Widget.DeviceDefault.RatingBar.Indicator\n01030158=style/Widget.DeviceDefault.RatingBar.Small\n01030159=style/Widget.DeviceDefault.CompoundButton.RadioButton\n0103015a=style/Widget.DeviceDefault.ScrollView\n0103015b=style/Widget.DeviceDefault.HorizontalScrollView\n0103015c=style/Widget.DeviceDefault.Spinner\n0103015d=style/Widget.DeviceDefault.CompoundButton.Star\n0103015e=style/Widget.DeviceDefault.TabWidget\n0103015f=style/Widget.DeviceDefault.WebTextView\n01030160=style/Widget.DeviceDefault.WebView\n01030161=style/Widget.DeviceDefault.DropDownItem\n01030162=style/Widget.DeviceDefault.DropDownItem.Spinner\n01030163=style/Widget.DeviceDefault.TextView.SpinnerItem\n01030164=style/Widget.DeviceDefault.ListPopupWindow\n01030165=style/Widget.DeviceDefault.PopupMenu\n01030166=style/Widget.DeviceDefault.ActionButton\n01030167=style/Widget.DeviceDefault.ActionButton.Overflow\n01030168=style/Widget.DeviceDefault.ActionButton.TextButton\n01030169=style/Widget.DeviceDefault.ActionMode\n0103016a=style/Widget.DeviceDefault.ActionButton.CloseMode\n0103016b=style/Widget.DeviceDefault.ActionBar\n0103016c=style/Widget.DeviceDefault.Button.Borderless\n0103016d=style/Widget.DeviceDefault.Tab\n0103016e=style/Widget.DeviceDefault.CalendarView\n0103016f=style/Widget.DeviceDefault.DatePicker\n01030170=style/Widget.DeviceDefault.ActionBar.TabView\n01030171=style/Widget.DeviceDefault.ActionBar.TabText\n01030172=style/Widget.DeviceDefault.ActionBar.TabBar\n01030173=style/Widget.DeviceDefault.ActionBar.Solid\n01030174=style/Widget.DeviceDefault.Light\n01030175=style/Widget.DeviceDefault.Light.Button\n01030176=style/Widget.DeviceDefault.Light.Button.Small\n01030177=style/Widget.DeviceDefault.Light.Button.Inset\n01030178=style/Widget.DeviceDefault.Light.Button.Toggle\n01030179=style/Widget.DeviceDefault.Light.Button.Borderless.Small\n0103017a=style/Widget.DeviceDefault.Light.TextView\n0103017b=style/Widget.DeviceDefault.Light.AutoCompleteTextView\n0103017c=style/Widget.DeviceDefault.Light.CompoundButton.CheckBox\n0103017d=style/Widget.DeviceDefault.Light.ListView.DropDown\n0103017e=style/Widget.DeviceDefault.Light.EditText\n0103017f=style/Widget.DeviceDefault.Light.ExpandableListView\n01030180=style/Widget.DeviceDefault.Light.GridView\n01030181=style/Widget.DeviceDefault.Light.ImageButton\n01030182=style/Widget.DeviceDefault.Light.ListView\n01030183=style/Widget.DeviceDefault.Light.PopupWindow\n01030184=style/Widget.DeviceDefault.Light.ProgressBar\n01030185=style/Widget.DeviceDefault.Light.ProgressBar.Horizontal\n01030186=style/Widget.DeviceDefault.Light.ProgressBar.Small\n01030187=style/Widget.DeviceDefault.Light.ProgressBar.Small.Title\n01030188=style/Widget.DeviceDefault.Light.ProgressBar.Large\n01030189=style/Widget.DeviceDefault.Light.ProgressBar.Inverse\n0103018a=style/Widget.DeviceDefault.Light.ProgressBar.Small.Inverse\n0103018b=style/Widget.DeviceDefault.Light.ProgressBar.Large.Inverse\n0103018c=style/Widget.DeviceDefault.Light.SeekBar\n0103018d=style/Widget.DeviceDefault.Light.RatingBar\n0103018e=style/Widget.DeviceDefault.Light.RatingBar.Indicator\n0103018f=style/Widget.DeviceDefault.Light.RatingBar.Small\n01030190=style/Widget.DeviceDefault.Light.CompoundButton.RadioButton\n01030191=style/Widget.DeviceDefault.Light.ScrollView\n01030192=style/Widget.DeviceDefault.Light.HorizontalScrollView\n01030193=style/Widget.DeviceDefault.Light.Spinner\n01030194=style/Widget.DeviceDefault.Light.CompoundButton.Star\n01030195=style/Widget.DeviceDefault.Light.TabWidget\n01030196=style/Widget.DeviceDefault.Light.WebTextView\n01030197=style/Widget.DeviceDefault.Light.WebView\n01030198=style/Widget.DeviceDefault.Light.DropDownItem\n01030199=style/Widget.DeviceDefault.Light.DropDownItem.Spinner\n0103019a=style/Widget.DeviceDefault.Light.TextView.SpinnerItem\n0103019b=style/Widget.DeviceDefault.Light.ListPopupWindow\n0103019c=style/Widget.DeviceDefault.Light.PopupMenu\n0103019d=style/Widget.DeviceDefault.Light.Tab\n0103019e=style/Widget.DeviceDefault.Light.CalendarView\n0103019f=style/Widget.DeviceDefault.Light.ActionButton\n010301a0=style/Widget.DeviceDefault.Light.ActionButton.Overflow\n010301a1=style/Widget.DeviceDefault.Light.ActionMode\n010301a2=style/Widget.DeviceDefault.Light.ActionButton.CloseMode\n010301a3=style/Widget.DeviceDefault.Light.ActionBar\n010301a4=style/Widget.DeviceDefault.Light.ActionBar.TabView\n010301a5=style/Widget.DeviceDefault.Light.ActionBar.TabText\n010301a6=style/Widget.DeviceDefault.Light.ActionBar.TabBar\n010301a7=style/Widget.DeviceDefault.Light.ActionBar.Solid\n010301a8=style/Widget.DeviceDefault.Light.ActionBar.Solid.Inverse\n010301a9=style/Widget.DeviceDefault.Light.ActionBar.TabBar.Inverse\n010301aa=style/Widget.DeviceDefault.Light.ActionBar.TabView.Inverse\n010301ab=style/Widget.DeviceDefault.Light.ActionBar.TabText.Inverse\n010301ac=style/Widget.DeviceDefault.Light.ActionMode.Inverse\n010301ad=style/TextAppearance.DeviceDefault\n010301ae=style/TextAppearance.DeviceDefault.Inverse\n010301af=style/TextAppearance.DeviceDefault.Large\n010301b0=style/TextAppearance.DeviceDefault.Large.Inverse\n010301b1=style/TextAppearance.DeviceDefault.Medium\n010301b2=style/TextAppearance.DeviceDefault.Medium.Inverse\n010301b3=style/TextAppearance.DeviceDefault.Small\n010301b4=style/TextAppearance.DeviceDefault.Small.Inverse\n010301b5=style/TextAppearance.DeviceDefault.SearchResult.Title\n010301b6=style/TextAppearance.DeviceDefault.SearchResult.Subtitle\n010301b7=style/TextAppearance.DeviceDefault.WindowTitle\n010301b8=style/TextAppearance.DeviceDefault.DialogWindowTitle\n010301b9=style/TextAppearance.DeviceDefault.Widget\n010301ba=style/TextAppearance.DeviceDefault.Widget.Button\n010301bb=style/TextAppearance.DeviceDefault.Widget.IconMenu.Item\n010301bc=style/TextAppearance.DeviceDefault.Widget.TabWidget\n010301bd=style/TextAppearance.DeviceDefault.Widget.TextView\n010301be=style/TextAppearance.DeviceDefault.Widget.TextView.PopupMenu\n010301bf=style/TextAppearance.DeviceDefault.Widget.DropDownHint\n010301c0=style/TextAppearance.DeviceDefault.Widget.DropDownItem\n010301c1=style/TextAppearance.DeviceDefault.Widget.TextView.SpinnerItem\n010301c2=style/TextAppearance.DeviceDefault.Widget.EditText\n010301c3=style/TextAppearance.DeviceDefault.Widget.PopupMenu\n010301c4=style/TextAppearance.DeviceDefault.Widget.PopupMenu.Large\n010301c5=style/TextAppearance.DeviceDefault.Widget.PopupMenu.Small\n010301c6=style/TextAppearance.DeviceDefault.Widget.ActionBar.Title\n010301c7=style/TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle\n010301c8=style/TextAppearance.DeviceDefault.Widget.ActionMode.Title\n010301c9=style/TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle\n010301ca=style/TextAppearance.DeviceDefault.Widget.ActionBar.Title.Inverse\n010301cb=style/TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle.Inverse\n010301cc=style/TextAppearance.DeviceDefault.Widget.ActionMode.Title.Inverse\n010301cd=style/TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle.Inverse\n010301ce=style/TextAppearance.DeviceDefault.Widget.ActionBar.Menu\n010301cf=style/DeviceDefault.ButtonBar\n010301d0=style/DeviceDefault.ButtonBar.AlertDialog\n010301d1=style/DeviceDefault.SegmentedButton\n010301d2=style/DeviceDefault.Light.ButtonBar\n010301d3=style/DeviceDefault.Light.ButtonBar.AlertDialog\n010301d4=style/DeviceDefault.Light.SegmentedButton\n010301d5=style/Widget.Holo.MediaRouteButton\n010301d6=style/Widget.Holo.Light.MediaRouteButton\n010301d7=style/Widget.DeviceDefault.MediaRouteButton\n010301d8=style/Widget.DeviceDefault.Light.MediaRouteButton\n010301d9=style/Widget.Holo.CheckedTextView\n010301da=style/Widget.Holo.Light.CheckedTextView\n010301db=style/Widget.DeviceDefault.CheckedTextView\n010301dc=style/Widget.DeviceDefault.Light.CheckedTextView\n010301dd=style/Theme.Holo.NoActionBar.Overscan\n010301de=style/Theme.Holo.Light.NoActionBar.Overscan\n010301df=style/Theme.DeviceDefault.NoActionBar.Overscan\n010301e0=style/Theme.DeviceDefault.Light.NoActionBar.Overscan\n010301e1=style/Theme.Holo.NoActionBar.TranslucentDecor\n010301e2=style/Theme.Holo.Light.NoActionBar.TranslucentDecor\n010301e3=style/Theme.DeviceDefault.NoActionBar.TranslucentDecor\n010301e4=style/Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor\n010301e5=style/Widget.FastScroll\n010301e6=style/Widget.StackView\n010301e7=style/Widget.Toolbar\n010301e8=style/Widget.Toolbar.Button.Navigation\n010301e9=style/Widget.DeviceDefault.FastScroll\n010301ea=style/Widget.DeviceDefault.StackView\n010301eb=style/Widget.DeviceDefault.Light.FastScroll\n010301ec=style/Widget.DeviceDefault.Light.StackView\n010301ed=style/TextAppearance.Material\n010301ee=style/TextAppearance.Material.Button\n010301ef=style/TextAppearance.Material.Body2\n010301f0=style/TextAppearance.Material.Body1\n010301f1=style/TextAppearance.Material.Caption\n010301f2=style/TextAppearance.Material.DialogWindowTitle\n010301f3=style/TextAppearance.Material.Display4\n010301f4=style/TextAppearance.Material.Display3\n010301f5=style/TextAppearance.Material.Display2\n010301f6=style/TextAppearance.Material.Display1\n010301f7=style/TextAppearance.Material.Headline\n010301f8=style/TextAppearance.Material.Inverse\n010301f9=style/TextAppearance.Material.Large\n010301fa=style/TextAppearance.Material.Large.Inverse\n010301fb=style/TextAppearance.Material.Medium\n010301fc=style/TextAppearance.Material.Medium.Inverse\n010301fd=style/TextAppearance.Material.Menu\n010301fe=style/TextAppearance.Material.Notification\n010301ff=style/TextAppearance.Material.Notification.Emphasis\n01030200=style/TextAppearance.Material.Notification.Info\n01030201=style/TextAppearance.Material.Notification.Line2\n01030202=style/TextAppearance.Material.Notification.Time\n01030203=style/TextAppearance.Material.Notification.Title\n01030204=style/TextAppearance.Material.SearchResult.Subtitle\n01030205=style/TextAppearance.Material.SearchResult.Title\n01030206=style/TextAppearance.Material.Small\n01030207=style/TextAppearance.Material.Small.Inverse\n01030208=style/TextAppearance.Material.Subhead\n01030209=style/TextAppearance.Material.Title\n0103020a=style/TextAppearance.Material.WindowTitle\n0103020b=style/TextAppearance.Material.Widget\n0103020c=style/TextAppearance.Material.Widget.ActionBar.Menu\n0103020d=style/TextAppearance.Material.Widget.ActionBar.Subtitle\n0103020e=style/TextAppearance.Material.Widget.ActionBar.Subtitle.Inverse\n0103020f=style/TextAppearance.Material.Widget.ActionBar.Title\n01030210=style/TextAppearance.Material.Widget.ActionBar.Title.Inverse\n01030211=style/TextAppearance.Material.Widget.ActionMode.Subtitle\n01030212=style/TextAppearance.Material.Widget.ActionMode.Subtitle.Inverse\n01030213=style/TextAppearance.Material.Widget.ActionMode.Title\n01030214=style/TextAppearance.Material.Widget.ActionMode.Title.Inverse\n01030215=style/TextAppearance.Material.Widget.Button\n01030216=style/TextAppearance.Material.Widget.DropDownHint\n01030217=style/TextAppearance.Material.Widget.DropDownItem\n01030218=style/TextAppearance.Material.Widget.EditText\n01030219=style/TextAppearance.Material.Widget.IconMenu.Item\n0103021a=style/TextAppearance.Material.Widget.PopupMenu\n0103021b=style/TextAppearance.Material.Widget.PopupMenu.Large\n0103021c=style/TextAppearance.Material.Widget.PopupMenu.Small\n0103021d=style/TextAppearance.Material.Widget.TabWidget\n0103021e=style/TextAppearance.Material.Widget.TextView\n0103021f=style/TextAppearance.Material.Widget.TextView.PopupMenu\n01030220=style/TextAppearance.Material.Widget.TextView.SpinnerItem\n01030221=style/TextAppearance.Material.Widget.Toolbar.Subtitle\n01030222=style/TextAppearance.Material.Widget.Toolbar.Title\n01030223=style/Theme.DeviceDefault.Settings\n01030224=style/Theme.Material\n01030225=style/Theme.Material.Dialog\n01030226=style/Theme.Material.Dialog.Alert\n01030227=style/Theme.Material.Dialog.MinWidth\n01030228=style/Theme.Material.Dialog.NoActionBar\n01030229=style/Theme.Material.Dialog.NoActionBar.MinWidth\n0103022a=style/Theme.Material.Dialog.Presentation\n0103022b=style/Theme.Material.DialogWhenLarge\n0103022c=style/Theme.Material.DialogWhenLarge.NoActionBar\n0103022d=style/Theme.Material.InputMethod\n0103022e=style/Theme.Material.NoActionBar\n0103022f=style/Theme.Material.NoActionBar.Fullscreen\n01030230=style/Theme.Material.NoActionBar.Overscan\n01030231=style/Theme.Material.NoActionBar.TranslucentDecor\n01030232=style/Theme.Material.Panel\n01030233=style/Theme.Material.Settings\n01030234=style/Theme.Material.Voice\n01030235=style/Theme.Material.Wallpaper\n01030236=style/Theme.Material.Wallpaper.NoTitleBar\n01030237=style/Theme.Material.Light\n01030238=style/Theme.Material.Light.DarkActionBar\n01030239=style/Theme.Material.Light.Dialog\n0103023a=style/Theme.Material.Light.Dialog.Alert\n0103023b=style/Theme.Material.Light.Dialog.MinWidth\n0103023c=style/Theme.Material.Light.Dialog.NoActionBar\n0103023d=style/Theme.Material.Light.Dialog.NoActionBar.MinWidth\n0103023e=style/Theme.Material.Light.Dialog.Presentation\n0103023f=style/Theme.Material.Light.DialogWhenLarge\n01030240=style/Theme.Material.Light.DialogWhenLarge.NoActionBar\n01030241=style/Theme.Material.Light.NoActionBar\n01030242=style/Theme.Material.Light.NoActionBar.Fullscreen\n01030243=style/Theme.Material.Light.NoActionBar.Overscan\n01030244=style/Theme.Material.Light.NoActionBar.TranslucentDecor\n01030245=style/Theme.Material.Light.Panel\n01030246=style/Theme.Material.Light.Voice\n01030247=style/ThemeOverlay\n01030248=style/ThemeOverlay.Material\n01030249=style/ThemeOverlay.Material.ActionBar\n0103024a=style/ThemeOverlay.Material.Light\n0103024b=style/ThemeOverlay.Material.Dark\n0103024c=style/ThemeOverlay.Material.Dark.ActionBar\n0103024d=style/Widget.Material\n0103024e=style/Widget.Material.ActionBar\n0103024f=style/Widget.Material.ActionBar.Solid\n01030250=style/Widget.Material.ActionBar.TabBar\n01030251=style/Widget.Material.ActionBar.TabText\n01030252=style/Widget.Material.ActionBar.TabView\n01030253=style/Widget.Material.ActionButton\n01030254=style/Widget.Material.ActionButton.CloseMode\n01030255=style/Widget.Material.ActionButton.Overflow\n01030256=style/Widget.Material.ActionMode\n01030257=style/Widget.Material.AutoCompleteTextView\n01030258=style/Widget.Material.Button\n01030259=style/Widget.Material.Button.Borderless\n0103025a=style/Widget.Material.Button.Borderless.Colored\n0103025b=style/Widget.Material.Button.Borderless.Small\n0103025c=style/Widget.Material.Button.Inset\n0103025d=style/Widget.Material.Button.Small\n0103025e=style/Widget.Material.Button.Toggle\n0103025f=style/Widget.Material.ButtonBar\n01030260=style/Widget.Material.ButtonBar.AlertDialog\n01030261=style/Widget.Material.CalendarView\n01030262=style/Widget.Material.CheckedTextView\n01030263=style/Widget.Material.CompoundButton.CheckBox\n01030264=style/Widget.Material.CompoundButton.RadioButton\n01030265=style/Widget.Material.CompoundButton.Star\n01030266=style/Widget.Material.DatePicker\n01030267=style/Widget.Material.DropDownItem\n01030268=style/Widget.Material.DropDownItem.Spinner\n01030269=style/Widget.Material.EditText\n0103026a=style/Widget.Material.ExpandableListView\n0103026b=style/Widget.Material.FastScroll\n0103026c=style/Widget.Material.GridView\n0103026d=style/Widget.Material.HorizontalScrollView\n0103026e=style/Widget.Material.ImageButton\n0103026f=style/Widget.Material.ListPopupWindow\n01030270=style/Widget.Material.ListView\n01030271=style/Widget.Material.ListView.DropDown\n01030272=style/Widget.Material.MediaRouteButton\n01030273=style/Widget.Material.PopupMenu\n01030274=style/Widget.Material.PopupMenu.Overflow\n01030275=style/Widget.Material.PopupWindow\n01030276=style/Widget.Material.ProgressBar\n01030277=style/Widget.Material.ProgressBar.Horizontal\n01030278=style/Widget.Material.ProgressBar.Large\n01030279=style/Widget.Material.ProgressBar.Small\n0103027a=style/Widget.Material.ProgressBar.Small.Title\n0103027b=style/Widget.Material.RatingBar\n0103027c=style/Widget.Material.RatingBar.Indicator\n0103027d=style/Widget.Material.RatingBar.Small\n0103027e=style/Widget.Material.ScrollView\n0103027f=style/Widget.Material.SearchView\n01030280=style/Widget.Material.SeekBar\n01030281=style/Widget.Material.SegmentedButton\n01030282=style/Widget.Material.StackView\n01030283=style/Widget.Material.Spinner\n01030284=style/Widget.Material.Spinner.Underlined\n01030285=style/Widget.Material.Tab\n01030286=style/Widget.Material.TabWidget\n01030287=style/Widget.Material.TextView\n01030288=style/Widget.Material.TextView.SpinnerItem\n01030289=style/Widget.Material.TimePicker\n0103028a=style/Widget.Material.Toolbar\n0103028b=style/Widget.Material.Toolbar.Button.Navigation\n0103028c=style/Widget.Material.WebTextView\n0103028d=style/Widget.Material.WebView\n0103028e=style/Widget.Material.Light\n0103028f=style/Widget.Material.Light.ActionBar\n01030290=style/Widget.Material.Light.ActionBar.Solid\n01030291=style/Widget.Material.Light.ActionBar.TabBar\n01030292=style/Widget.Material.Light.ActionBar.TabText\n01030293=style/Widget.Material.Light.ActionBar.TabView\n01030294=style/Widget.Material.Light.ActionButton\n01030295=style/Widget.Material.Light.ActionButton.CloseMode\n01030296=style/Widget.Material.Light.ActionButton.Overflow\n01030297=style/Widget.Material.Light.ActionMode\n01030298=style/Widget.Material.Light.AutoCompleteTextView\n01030299=style/Widget.Material.Light.Button\n0103029a=style/Widget.Material.Light.Button.Borderless\n0103029b=style/Widget.Material.Light.Button.Borderless.Colored\n0103029c=style/Widget.Material.Light.Button.Borderless.Small\n0103029d=style/Widget.Material.Light.Button.Inset\n0103029e=style/Widget.Material.Light.Button.Small\n0103029f=style/Widget.Material.Light.Button.Toggle\n010302a0=style/Widget.Material.Light.ButtonBar\n010302a1=style/Widget.Material.Light.ButtonBar.AlertDialog\n010302a2=style/Widget.Material.Light.CalendarView\n010302a3=style/Widget.Material.Light.CheckedTextView\n010302a4=style/Widget.Material.Light.CompoundButton.CheckBox\n010302a5=style/Widget.Material.Light.CompoundButton.RadioButton\n010302a6=style/Widget.Material.Light.CompoundButton.Star\n010302a7=style/Widget.Material.Light.DatePicker\n010302a8=style/Widget.Material.Light.DropDownItem\n010302a9=style/Widget.Material.Light.DropDownItem.Spinner\n010302aa=style/Widget.Material.Light.EditText\n010302ab=style/Widget.Material.Light.ExpandableListView\n010302ac=style/Widget.Material.Light.FastScroll\n010302ad=style/Widget.Material.Light.GridView\n010302ae=style/Widget.Material.Light.HorizontalScrollView\n010302af=style/Widget.Material.Light.ImageButton\n010302b0=style/Widget.Material.Light.ListPopupWindow\n010302b1=style/Widget.Material.Light.ListView\n010302b2=style/Widget.Material.Light.ListView.DropDown\n010302b3=style/Widget.Material.Light.MediaRouteButton\n010302b4=style/Widget.Material.Light.PopupMenu\n010302b5=style/Widget.Material.Light.PopupMenu.Overflow\n010302b6=style/Widget.Material.Light.PopupWindow\n010302b7=style/Widget.Material.Light.ProgressBar\n010302b8=style/Widget.Material.Light.ProgressBar.Horizontal\n010302b9=style/Widget.Material.Light.ProgressBar.Inverse\n010302ba=style/Widget.Material.Light.ProgressBar.Large\n010302bb=style/Widget.Material.Light.ProgressBar.Large.Inverse\n010302bc=style/Widget.Material.Light.ProgressBar.Small\n010302bd=style/Widget.Material.Light.ProgressBar.Small.Inverse\n010302be=style/Widget.Material.Light.ProgressBar.Small.Title\n010302bf=style/Widget.Material.Light.RatingBar\n010302c0=style/Widget.Material.Light.RatingBar.Indicator\n010302c1=style/Widget.Material.Light.RatingBar.Small\n010302c2=style/Widget.Material.Light.ScrollView\n010302c3=style/Widget.Material.Light.SearchView\n010302c4=style/Widget.Material.Light.SeekBar\n010302c5=style/Widget.Material.Light.SegmentedButton\n010302c6=style/Widget.Material.Light.StackView\n010302c7=style/Widget.Material.Light.Spinner\n010302c8=style/Widget.Material.Light.Spinner.Underlined\n010302c9=style/Widget.Material.Light.Tab\n010302ca=style/Widget.Material.Light.TabWidget\n010302cb=style/Widget.Material.Light.TextView\n010302cc=style/Widget.Material.Light.TextView.SpinnerItem\n010302cd=style/Widget.Material.Light.TimePicker\n010302ce=style/Widget.Material.Light.WebTextView\n010302cf=style/Widget.Material.Light.WebView\n010302d0=style/Theme.Leanback.FormWizard\n010302d1=style/Theme.DeviceDefault.Dialog.Alert\n010302d2=style/Theme.DeviceDefault.Light.Dialog.Alert\n010302d3=style/Widget.Material.Button.Colored\n010302d4=style/TextAppearance.Material.Widget.Button.Inverse\n010302d5=style/Theme.Material.Light.LightStatusBar\n010302d6=style/ThemeOverlay.Material.Dialog\n010302d7=style/ThemeOverlay.Material.Dialog.Alert\n010302d8=style/Theme.Material.Light.DialogWhenLarge.DarkActionBar\n010302d9=style/Widget.Material.SeekBar.Discrete\n010302da=style/Widget.Material.CompoundButton.Switch\n010302db=style/Widget.Material.Light.CompoundButton.Switch\n010302dc=style/Widget.Material.NumberPicker\n010302dd=style/Widget.Material.Light.NumberPicker\n010302de=style/TextAppearance.Material.Widget.Button.Colored\n010302df=style/TextAppearance.Material.Widget.Button.Borderless.Colored\n010302e0=style/Widget.DeviceDefault.Button.Colored\n010302e1=style/Widget.DeviceDefault.Button.Borderless.Colored\n010302e2=style/Theme.DeviceDefault.DocumentsUI\n010302e3=style/Theme.DeviceDefault.DayNight\n010302e4=style/ThemeOverlay.DeviceDefault.Accent.DayNight\n010302e5=style/TextAppearance.DeviceDefault.Headline\n010302e6=style/AccessibilityAutoclickPanelButtonLayoutStyle\n010302e7=style/AccessibilityAutoclickPanelImageButtonStyle\n010302e8=style/AccessibilityAutoclickScrollPanelButtonLayoutStyle\n010302e9=style/AccessibilityAutoclickScrollPanelImageButtonStyle\n010302ea=style/AccessibilityButtonChooserDialog\n010302eb=style/AccessibilityDialog\n010302ec=style/AccessibilityDialogButton\n010302ed=style/AccessibilityDialogButtonBarSpace\n010302ee=style/AccessibilityDialogButtonList\n010302ef=style/AccessibilityDialogDescription\n010302f0=style/AccessibilityDialogIcon\n010302f1=style/AccessibilityDialogPermissionDescription\n010302f2=style/AccessibilityDialogPermissionTitle\n010302f3=style/AccessibilityDialogServiceIcon\n010302f4=style/AccessibilityDialogTitle\n010302f5=style/ActiveWallpaperSettings\n010302f6=style/AlertDialog\n010302f7=style/AlertDialog.DeviceDefault\n010302f8=style/AlertDialog.DeviceDefault.Light\n010302f9=style/AlertDialog.Holo\n010302fa=style/AlertDialog.Holo.Light\n010302fb=style/AlertDialog.Leanback\n010302fc=style/AlertDialog.Leanback.Light\n010302fd=style/AlertDialog.Material\n010302fe=style/AlertDialog.Material.Light\n010302ff=style/AlertDialog.Material3\n01030300=style/AlertDialogEmergencyButtonStyle\n01030301=style/AlertDialogWithEmergencyButton\n01030302=style/Animation.DeviceDefault.Activity\n01030303=style/Animation.DeviceDefault.Activity.Resolver\n01030304=style/Animation.DeviceDefault.Dialog\n01030305=style/Animation.DropDownDown\n01030306=style/Animation.DropDownUp\n01030307=style/Animation.Holo\n01030308=style/Animation.Holo.Activity\n01030309=style/Animation.Holo.Dialog\n0103030a=style/Animation.ImmersiveModeConfirmation\n0103030b=style/Animation.InputMethodFancy\n0103030c=style/Animation.LockScreen\n0103030d=style/Animation.Material\n0103030e=style/Animation.Material.Activity\n0103030f=style/Animation.Material.Dialog\n01030310=style/Animation.Material.Popup\n01030311=style/Animation.OptionsPanel\n01030312=style/Animation.PopupWindow\n01030313=style/Animation.PopupWindow.ActionMode\n01030314=style/Animation.RecentApplications\n01030315=style/Animation.SearchBar\n01030316=style/Animation.SubMenuPanel\n01030317=style/Animation.TextSelectHandle\n01030318=style/Animation.Tooltip\n01030319=style/Animation.TypingFilter\n0103031a=style/Animation.TypingFilterRestore\n0103031b=style/Animation.VoiceActivity\n0103031c=style/Animation.VoiceInteractionSession\n0103031d=style/Animation.VolumePanel\n0103031e=style/Animation.Wallpaper\n0103031f=style/Animation.ZoomButtons\n01030320=style/AutofillDatasetPicker\n01030321=style/AutofillHalfScreenAnimation\n01030322=style/AutofillHalfSheetButton\n01030323=style/AutofillHalfSheetDivider\n01030324=style/AutofillHalfSheetOutlinedButton\n01030325=style/AutofillHalfSheetTonalButton\n01030326=style/AutofillSaveAnimation\n01030327=style/AutofillSaveUiTitle\n01030328=style/BaseErrorDialog.DeviceDefault\n01030329=style/BasePreferenceFragment\n0103032a=style/CarDialogButtonText\n0103032b=style/CarDialogMessageText\n0103032c=style/DatePickerDialog.Material\n0103032d=style/DialogWindowTitle\n0103032e=style/DialogWindowTitle.DeviceDefault\n0103032f=style/DialogWindowTitle.DeviceDefault.Light\n01030330=style/DialogWindowTitle.Holo\n01030331=style/DialogWindowTitle.Holo.Light\n01030332=style/DialogWindowTitle.Material\n01030333=style/DialogWindowTitle.Material.Light\n01030334=style/DialogWindowTitleBackground\n01030335=style/DialogWindowTitleBackground.Material\n01030336=style/DialogWindowTitleBackground.Material.Light\n01030337=style/GrantCredentialsPermissionActivity\n01030338=style/Holo\n01030339=style/Holo.Light\n0103033a=style/InputMethodSwitchDialogStyle\n0103033b=style/InputMethodSwitchHardKeyboardSwitch\n0103033c=style/InputMethodSwitchHardKeyboardText\n0103033d=style/LargePointer\n0103033e=style/Material\n0103033f=style/Material.Light\n01030340=style/Notification.Header\n01030341=style/NotificationAction\n01030342=style/NotificationEmphasizedAction\n01030343=style/NotificationTombstoneAction\n01030344=style/Pointer\n01030345=style/PointerIconVectorStyleFillBlack\n01030346=style/PointerIconVectorStyleFillBlue\n01030347=style/PointerIconVectorStyleFillGreen\n01030348=style/PointerIconVectorStyleFillPink\n01030349=style/PointerIconVectorStyleFillPurple\n0103034a=style/PointerIconVectorStyleFillRed\n0103034b=style/PointerIconVectorStyleStrokeBlack\n0103034c=style/PointerIconVectorStyleStrokeNone\n0103034d=style/PointerIconVectorStyleStrokeWhite\n0103034e=style/Preference\n0103034f=style/Preference.Category\n01030350=style/Preference.CheckBoxPreference\n01030351=style/Preference.DeviceDefault\n01030352=style/Preference.DeviceDefault.Category\n01030353=style/Preference.DeviceDefault.CheckBoxPreference\n01030354=style/Preference.DeviceDefault.DialogPreference\n01030355=style/Preference.DeviceDefault.DialogPreference.EditTextPreference\n01030356=style/Preference.DeviceDefault.DialogPreference.YesNoPreference\n01030357=style/Preference.DeviceDefault.Information\n01030358=style/Preference.DeviceDefault.PreferenceScreen\n01030359=style/Preference.DeviceDefault.RingtonePreference\n0103035a=style/Preference.DeviceDefault.SeekBarPreference\n0103035b=style/Preference.DeviceDefault.SwitchPreference\n0103035c=style/Preference.DialogPreference\n0103035d=style/Preference.DialogPreference.EditTextPreference\n0103035e=style/Preference.DialogPreference.SeekBarPreference\n0103035f=style/Preference.DialogPreference.YesNoPreference\n01030360=style/Preference.Holo\n01030361=style/Preference.Holo.Category\n01030362=style/Preference.Holo.CheckBoxPreference\n01030363=style/Preference.Holo.DialogPreference\n01030364=style/Preference.Holo.DialogPreference.EditTextPreference\n01030365=style/Preference.Holo.DialogPreference.YesNoPreference\n01030366=style/Preference.Holo.Information\n01030367=style/Preference.Holo.PreferenceScreen\n01030368=style/Preference.Holo.RingtonePreference\n01030369=style/Preference.Holo.SeekBarPreference\n0103036a=style/Preference.Holo.SwitchPreference\n0103036b=style/Preference.Information\n0103036c=style/Preference.Material\n0103036d=style/Preference.Material.BasePreferenceScreen\n0103036e=style/Preference.Material.Category\n0103036f=style/Preference.Material.CheckBoxPreference\n01030370=style/Preference.Material.DialogPreference\n01030371=style/Preference.Material.DialogPreference.EditTextPreference\n01030372=style/Preference.Material.DialogPreference.SeekBarPreference\n01030373=style/Preference.Material.DialogPreference.YesNoPreference\n01030374=style/Preference.Material.Information\n01030375=style/Preference.Material.PreferenceScreen\n01030376=style/Preference.Material.RingtonePreference\n01030377=style/Preference.Material.SeekBarPreference\n01030378=style/Preference.Material.SwitchPreference\n01030379=style/Preference.PreferenceScreen\n0103037a=style/Preference.RingtonePreference\n0103037b=style/Preference.SeekBarPreference\n0103037c=style/Preference.SwitchPreference\n0103037d=style/PreferenceActivity\n0103037e=style/PreferenceActivity.Material\n0103037f=style/PreferenceFragment\n01030380=style/PreferenceFragment.Holo\n01030381=style/PreferenceFragment.Material\n01030382=style/PreferenceFragmentList\n01030383=style/PreferenceFragmentList.Material\n01030384=style/PreferenceHeaderList\n01030385=style/PreferenceHeaderList.Material\n01030386=style/PreferenceHeaderPanel\n01030387=style/PreferenceHeaderPanel.Material\n01030388=style/PreferencePanel\n01030389=style/PreferencePanel.Dialog\n0103038a=style/PreferencePanel.Material\n0103038b=style/PreferencePanel.Material.Dialog\n0103038c=style/PreviewWallpaperSettings\n0103038d=style/ProgressDialogMessage\n0103038e=style/SegmentedButton\n0103038f=style/TextAppearance.AlertDialog.Body1\n01030390=style/TextAppearance.AlertDialog.Title\n01030391=style/TextAppearance.AutoCorrectionSuggestion\n01030392=style/TextAppearance.DeviceDefault.Body1\n01030393=style/TextAppearance.DeviceDefault.Body2\n01030394=style/TextAppearance.DeviceDefault.Caption\n01030395=style/TextAppearance.DeviceDefault.Display1\n01030396=style/TextAppearance.DeviceDefault.ListItem\n01030397=style/TextAppearance.DeviceDefault.ListItemSecondary\n01030398=style/TextAppearance.DeviceDefault.Notification\n01030399=style/TextAppearance.DeviceDefault.Notification.BigTitle\n0103039a=style/TextAppearance.DeviceDefault.Notification.Info\n0103039b=style/TextAppearance.DeviceDefault.Notification.Reply\n0103039c=style/TextAppearance.DeviceDefault.Notification.Time\n0103039d=style/TextAppearance.DeviceDefault.Notification.Title\n0103039e=style/TextAppearance.DeviceDefault.Subhead\n0103039f=style/TextAppearance.DeviceDefault.Title\n010303a0=style/TextAppearance.DeviceDefault.Widget.Button.Borderless.Colored\n010303a1=style/TextAppearance.DeviceDefault.Widget.Switch\n010303a2=style/TextAppearance.DeviceDefault.Widget.Toolbar.Subtitle\n010303a3=style/TextAppearance.DeviceDefault.Widget.Toolbar.Title\n010303a4=style/TextAppearance.EasyCorrectSuggestion\n010303a5=style/TextAppearance.GrammarErrorSuggestion\n010303a6=style/TextAppearance.Holo.CalendarViewWeekDayView\n010303a7=style/TextAppearance.Holo.Light\n010303a8=style/TextAppearance.Holo.Light.CalendarViewWeekDayView\n010303a9=style/TextAppearance.Holo.Light.DialogWindowTitle\n010303aa=style/TextAppearance.Holo.Light.Inverse\n010303ab=style/TextAppearance.Holo.Light.Large\n010303ac=style/TextAppearance.Holo.Light.Large.Inverse\n010303ad=style/TextAppearance.Holo.Light.Medium\n010303ae=style/TextAppearance.Holo.Light.Medium.Inverse\n010303af=style/TextAppearance.Holo.Light.SearchResult\n010303b0=style/TextAppearance.Holo.Light.SearchResult.Subtitle\n010303b1=style/TextAppearance.Holo.Light.SearchResult.Title\n010303b2=style/TextAppearance.Holo.Light.Small\n010303b3=style/TextAppearance.Holo.Light.Small.Inverse\n010303b4=style/TextAppearance.Holo.Light.Widget\n010303b5=style/TextAppearance.Holo.Light.Widget.ActionMode.Subtitle\n010303b6=style/TextAppearance.Holo.Light.Widget.ActionMode.Title\n010303b7=style/TextAppearance.Holo.Light.Widget.Button\n010303b8=style/TextAppearance.Holo.Light.Widget.DropDownHint\n010303b9=style/TextAppearance.Holo.Light.Widget.EditText\n010303ba=style/TextAppearance.Holo.Light.Widget.PopupMenu\n010303bb=style/TextAppearance.Holo.Light.Widget.PopupMenu.Large\n010303bc=style/TextAppearance.Holo.Light.Widget.PopupMenu.Small\n010303bd=style/TextAppearance.Holo.Light.Widget.Switch\n010303be=style/TextAppearance.Holo.Light.WindowTitle\n010303bf=style/TextAppearance.Holo.SearchResult\n010303c0=style/TextAppearance.Holo.SuggestionHighlight\n010303c1=style/TextAppearance.Holo.Widget.ActionMode\n010303c2=style/TextAppearance.Holo.Widget.Switch\n010303c3=style/TextAppearance.Large.Inverse.NumberPickerInputText\n010303c4=style/TextAppearance.Leanback.FormWizard\n010303c5=style/TextAppearance.Leanback.FormWizard.Large\n010303c6=style/TextAppearance.Leanback.FormWizard.ListItem\n010303c7=style/TextAppearance.Leanback.FormWizard.Medium\n010303c8=style/TextAppearance.Leanback.FormWizard.Small\n010303c9=style/TextAppearance.Material.DatePicker.DateLabel\n010303ca=style/TextAppearance.Material.DatePicker.List.YearLabel\n010303cb=style/TextAppearance.Material.DatePicker.List.YearLabel.Activated\n010303cc=style/TextAppearance.Material.DatePicker.YearLabel\n010303cd=style/TextAppearance.Material.ListItem\n010303ce=style/TextAppearance.Material.ListItemSecondary\n010303cf=style/TextAppearance.Material.Menu.Inverse\n010303d0=style/TextAppearance.Material.Notification.BigTitle\n010303d1=style/TextAppearance.Material.Notification.Reply\n010303d2=style/TextAppearance.Material.NumberPicker\n010303d3=style/TextAppearance.Material.SearchResult\n010303d4=style/TextAppearance.Material.Subhead.Inverse\n010303d5=style/TextAppearance.Material.TextSuggestionHighlight\n010303d6=style/TextAppearance.Material.TimePicker.AmPmLabel\n010303d7=style/TextAppearance.Material.TimePicker.InputField\n010303d8=style/TextAppearance.Material.TimePicker.InputHeader\n010303d9=style/TextAppearance.Material.TimePicker.PromptLabel\n010303da=style/TextAppearance.Material.TimePicker.TimeLabel\n010303db=style/TextAppearance.Material.Title.Inverse\n010303dc=style/TextAppearance.Material.Widget.ActionBar.Menu.Inverse\n010303dd=style/TextAppearance.Material.Widget.ActionMode\n010303de=style/TextAppearance.Material.Widget.Calendar.Day\n010303df=style/TextAppearance.Material.Widget.Calendar.DayOfWeek\n010303e0=style/TextAppearance.Material.Widget.Calendar.Month\n010303e1=style/TextAppearance.Material.Widget.PopupMenu.Header\n010303e2=style/TextAppearance.Material.Widget.Switch\n010303e3=style/TextAppearance.MisspelledSuggestion\n010303e4=style/TextAppearance.SearchResult\n010303e5=style/TextAppearance.SlidingTabActive\n010303e6=style/TextAppearance.SlidingTabNormal\n010303e7=style/TextAppearance.Small.CalendarViewWeekDayView\n010303e8=style/TextAppearance.StatusBar\n010303e9=style/TextAppearance.StatusBar.EventContent.Emphasis\n010303ea=style/TextAppearance.StatusBar.EventContent.Info\n010303eb=style/TextAppearance.StatusBar.EventContent.Line2\n010303ec=style/TextAppearance.StatusBar.EventContent.Time\n010303ed=style/TextAppearance.StatusBar.Ticker\n010303ee=style/TextAppearance.Suggestion\n010303ef=style/TextAppearance.Toast\n010303f0=style/TextAppearance.Tooltip\n010303f1=style/TextAppearance.Watch\n010303f2=style/TextAppearance.Watch.AppErrorDialog\n010303f3=style/TextAppearance.Watch.AppErrorDialog.Item\n010303f4=style/TextAppearance.Watch.BaseErrorDialog\n010303f5=style/TextAppearance.Watch.BaseErrorDialog.Title\n010303f6=style/TextAppearance.Widget.ActionBar.Subtitle\n010303f7=style/TextAppearance.Widget.ActionBar.Title\n010303f8=style/TextAppearance.Widget.ActionMode.Subtitle\n010303f9=style/TextAppearance.Widget.ActionMode.Title\n010303fa=style/TextAppearance.Widget.Material3.Button\n010303fb=style/TextAppearance.Widget.Material3.Button.Filled\n010303fc=style/TextAppearance.Widget.PopupMenu\n010303fd=style/TextAppearance.Widget.Toolbar.Subtitle\n010303fe=style/TextAppearance.Widget.Toolbar.Title\n010303ff=style/Theme.DeviceDefault.Autofill\n01030400=style/Theme.DeviceDefault.Autofill.Light\n01030401=style/Theme.DeviceDefault.Autofill.Save\n01030402=style/Theme.DeviceDefault.AutofillHalfScreenDialogButton\n01030403=style/Theme.DeviceDefault.AutofillHalfScreenDialogList\n01030404=style/Theme.DeviceDefault.Chooser\n01030405=style/Theme.DeviceDefault.Dialog.Alert.DayNight\n01030406=style/Theme.DeviceDefault.Dialog.AppError\n01030407=style/Theme.DeviceDefault.Dialog.FixedSize\n01030408=style/Theme.DeviceDefault.Dialog.NoActionBar.FixedSize\n01030409=style/Theme.DeviceDefault.Dialog.NoFrame\n0103040a=style/Theme.DeviceDefault.Dialog.Presentation\n0103040b=style/Theme.DeviceDefault.InputMethodSwitcherDialog\n0103040c=style/Theme.DeviceDefault.Light.Autofill\n0103040d=style/Theme.DeviceDefault.Light.Autofill.Save\n0103040e=style/Theme.DeviceDefault.Light.Dialog.Alert.UserSwitchingDialog\n0103040f=style/Theme.DeviceDefault.Light.Dialog.FixedSize\n01030410=style/Theme.DeviceDefault.Light.Dialog.NoActionBar.FixedSize\n01030411=style/Theme.DeviceDefault.Light.Dialog.Presentation\n01030412=style/Theme.DeviceDefault.Light.SearchBar\n01030413=style/Theme.DeviceDefault.Light.Voice\n01030414=style/Theme.DeviceDefault.Notification\n01030415=style/Theme.DeviceDefault.Resolver\n01030416=style/Theme.DeviceDefault.ResolverCommon\n01030417=style/Theme.DeviceDefault.SearchBar\n01030418=style/Theme.DeviceDefault.Settings.BaseDialog\n01030419=style/Theme.DeviceDefault.Settings.CompactMenu\n0103041a=style/Theme.DeviceDefault.Settings.Dark.NoActionBar\n0103041b=style/Theme.DeviceDefault.Settings.Dialog\n0103041c=style/Theme.DeviceDefault.Settings.Dialog.Alert\n0103041d=style/Theme.DeviceDefault.Settings.Dialog.NoActionBar\n0103041e=style/Theme.DeviceDefault.Settings.Dialog.Presentation\n0103041f=style/Theme.DeviceDefault.Settings.DialogBase\n01030420=style/Theme.DeviceDefault.Settings.DialogWhenLarge\n01030421=style/Theme.DeviceDefault.Settings.DialogWhenLarge.NoActionBar\n01030422=style/Theme.DeviceDefault.Settings.NoActionBar\n01030423=style/Theme.DeviceDefault.Settings.SearchBar\n01030424=style/Theme.DeviceDefault.System\n01030425=style/Theme.DeviceDefault.System.Dialog\n01030426=style/Theme.DeviceDefault.System.Dialog.Alert\n01030427=style/Theme.DeviceDefault.SystemUI\n01030428=style/Theme.DeviceDefault.SystemUI.Dialog\n01030429=style/Theme.DeviceDefault.VoiceInteractionSession\n0103042a=style/Theme.DeviceDefaultBase\n0103042b=style/Theme.Dialog.Alert\n0103042c=style/Theme.Dialog.Confirmation\n0103042d=style/Theme.Dialog.NoFrame\n0103042e=style/Theme.Dialog.RecentApplications\n0103042f=style/Theme.Dream\n01030430=style/Theme.ExpandedMenu\n01030431=style/Theme.GameSessionTrampoline\n01030432=style/Theme.GlobalSearchBar\n01030433=style/Theme.Holo.CompactMenu\n01030434=style/Theme.Holo.Dialog.Alert\n01030435=style/Theme.Holo.Dialog.BaseAlert\n01030436=style/Theme.Holo.Dialog.FixedSize\n01030437=style/Theme.Holo.Dialog.NoActionBar.FixedSize\n01030438=style/Theme.Holo.Dialog.NoFrame\n01030439=style/Theme.Holo.Dialog.Presentation\n0103043a=style/Theme.Holo.Light.CompactMenu\n0103043b=style/Theme.Holo.Light.Dialog.Alert\n0103043c=style/Theme.Holo.Light.Dialog.BaseAlert\n0103043d=style/Theme.Holo.Light.Dialog.FixedSize\n0103043e=style/Theme.Holo.Light.Dialog.NoActionBar.FixedSize\n0103043f=style/Theme.Holo.Light.Dialog.Presentation\n01030440=style/Theme.Holo.Light.SearchBar\n01030441=style/Theme.Holo.SearchBar\n01030442=style/Theme.IconMenu\n01030443=style/Theme.Leanback.Dialog\n01030444=style/Theme.Leanback.Dialog.Alert\n01030445=style/Theme.Leanback.Dialog.AppError\n01030446=style/Theme.Leanback.Dialog.Confirmation\n01030447=style/Theme.Leanback.Resolver\n01030448=style/Theme.Leanback.Settings.Dialog\n01030449=style/Theme.Leanback.Settings.Dialog.Alert\n0103044a=style/Theme.Material.BaseDialog\n0103044b=style/Theme.Material.CompactMenu\n0103044c=style/Theme.Material.Dialog.BaseAlert\n0103044d=style/Theme.Material.Dialog.FixedSize\n0103044e=style/Theme.Material.Dialog.NoActionBar.FixedSize\n0103044f=style/Theme.Material.Dialog.NoFrame\n01030450=style/Theme.Material.Light.BaseDialog\n01030451=style/Theme.Material.Light.CompactMenu\n01030452=style/Theme.Material.Light.Dialog.BaseAlert\n01030453=style/Theme.Material.Light.Dialog.FixedSize\n01030454=style/Theme.Material.Light.Dialog.NoActionBar.FixedSize\n01030455=style/Theme.Material.Light.SearchBar\n01030456=style/Theme.Material.Notification\n01030457=style/Theme.Material.SearchBar\n01030458=style/Theme.Material.Settings.BaseDialog\n01030459=style/Theme.Material.Settings.CompactMenu\n0103045a=style/Theme.Material.Settings.Dialog\n0103045b=style/Theme.Material.Settings.Dialog.Alert\n0103045c=style/Theme.Material.Settings.Dialog.BaseAlert\n0103045d=style/Theme.Material.Settings.Dialog.Presentation\n0103045e=style/Theme.Material.Settings.DialogWhenLarge\n0103045f=style/Theme.Material.Settings.DialogWhenLarge.NoActionBar\n01030460=style/Theme.Material.Settings.NoActionBar\n01030461=style/Theme.Material.Settings.SearchBar\n01030462=style/Theme.Material.VoiceInteractionSession\n01030463=style/Theme.SearchBar\n01030464=style/Theme.Toast\n01030465=style/Theme.VoiceInteractionSession\n01030466=style/ThemeOverlay.DeviceDefault\n01030467=style/ThemeOverlay.DeviceDefault.Accent\n01030468=style/ThemeOverlay.DeviceDefault.Accent.Light\n01030469=style/ThemeOverlay.DeviceDefault.ActionBar\n0103046a=style/ThemeOverlay.DeviceDefault.Dark.ActionBar.Accent\n0103046b=style/ThemeOverlay.DeviceDefault.Popup.Light\n0103046c=style/ThemeOverlay.Material.BaseDialog\n0103046d=style/ThemeOverlay.Material.Dialog.DatePicker\n0103046e=style/ThemeOverlay.Material.Dialog.TimePicker\n0103046f=style/TimePickerDialog.Material\n01030470=style/VectorPointer\n01030471=style/Widget.ActionMode\n01030472=style/Widget.ActivityChooserView\n01030473=style/Widget.Button.Transparent\n01030474=style/Widget.CheckedTextView\n01030475=style/Widget.CompoundButton.Switch\n01030476=style/Widget.DeviceDefault.AbsListView\n01030477=style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog\n01030478=style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3\n01030479=style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3.Confirm\n0103047a=style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3.Negative\n0103047b=style/Widget.DeviceDefault.CompoundButton.Switch\n0103047c=style/Widget.DeviceDefault.ExpandableListView.White\n0103047d=style/Widget.DeviceDefault.FragmentBreadCrumbs\n0103047e=style/Widget.DeviceDefault.Gallery\n0103047f=style/Widget.DeviceDefault.GestureOverlayView\n01030480=style/Widget.DeviceDefault.ImageWell\n01030481=style/Widget.DeviceDefault.KeyboardView\n01030482=style/Widget.DeviceDefault.Light.AbsListView\n01030483=style/Widget.DeviceDefault.Light.Button.Borderless\n01030484=style/Widget.DeviceDefault.Light.DatePicker\n01030485=style/Widget.DeviceDefault.Light.ExpandableListView.White\n01030486=style/Widget.DeviceDefault.Light.FragmentBreadCrumbs\n01030487=style/Widget.DeviceDefault.Light.Gallery\n01030488=style/Widget.DeviceDefault.Light.GestureOverlayView\n01030489=style/Widget.DeviceDefault.Light.ImageWell\n0103048a=style/Widget.DeviceDefault.Light.ListView.White\n0103048b=style/Widget.DeviceDefault.Light.NumberPicker\n0103048c=style/Widget.DeviceDefault.Light.PopupWindow.ActionMode\n0103048d=style/Widget.DeviceDefault.Light.Spinner.DropDown\n0103048e=style/Widget.DeviceDefault.Light.Spinner.DropDown.ActionBar\n0103048f=style/Widget.DeviceDefault.Light.TextView.ListSeparator\n01030490=style/Widget.DeviceDefault.Light.TimePicker\n01030491=style/Widget.DeviceDefault.ListView.White\n01030492=style/Widget.DeviceDefault.Notification.MessagingName\n01030493=style/Widget.DeviceDefault.Notification.MessagingText\n01030494=style/Widget.DeviceDefault.Notification.Text\n01030495=style/Widget.DeviceDefault.NumberPicker\n01030496=style/Widget.DeviceDefault.PopupWindow.ActionMode\n01030497=style/Widget.DeviceDefault.PreferenceFrameLayout\n01030498=style/Widget.DeviceDefault.ProgressBar.Inverse\n01030499=style/Widget.DeviceDefault.ProgressBar.Large.Inverse\n0103049a=style/Widget.DeviceDefault.ProgressBar.Small.Inverse\n0103049b=style/Widget.DeviceDefault.QuickContactBadge.WindowLarge\n0103049c=style/Widget.DeviceDefault.QuickContactBadge.WindowMedium\n0103049d=style/Widget.DeviceDefault.QuickContactBadge.WindowSmall\n0103049e=style/Widget.DeviceDefault.QuickContactBadgeSmall.WindowLarge\n0103049f=style/Widget.DeviceDefault.QuickContactBadgeSmall.WindowMedium\n010304a0=style/Widget.DeviceDefault.QuickContactBadgeSmall.WindowSmall\n010304a1=style/Widget.DeviceDefault.Resolver.TabWidget\n010304a2=style/Widget.DeviceDefault.Spinner.DropDown\n010304a3=style/Widget.DeviceDefault.Spinner.DropDown.ActionBar\n010304a4=style/Widget.DeviceDefault.TextSelectHandle\n010304a5=style/Widget.DeviceDefault.TextView.ListSeparator\n010304a6=style/Widget.DeviceDefault.TimePicker\n010304a7=style/Widget.DeviceDefault.Toolbar\n010304a8=style/Widget.ExpandableListView.White\n010304a9=style/Widget.GenericQuickContactBadge\n010304aa=style/Widget.GestureOverlayView\n010304ab=style/Widget.GestureOverlayView.White\n010304ac=style/Widget.Holo.AbsListView\n010304ad=style/Widget.Holo.ActivityChooserView\n010304ae=style/Widget.Holo.ButtonBar\n010304af=style/Widget.Holo.ButtonBar.Button\n010304b0=style/Widget.Holo.CompoundButton\n010304b1=style/Widget.Holo.CompoundButton.Switch\n010304b2=style/Widget.Holo.ExpandableListView.White\n010304b3=style/Widget.Holo.FastScroll\n010304b4=style/Widget.Holo.FragmentBreadCrumbs\n010304b5=style/Widget.Holo.Gallery\n010304b6=style/Widget.Holo.GestureOverlayView\n010304b7=style/Widget.Holo.ImageWell\n010304b8=style/Widget.Holo.KeyboardView\n010304b9=style/Widget.Holo.Light.AbsListView\n010304ba=style/Widget.Holo.Light.ActivityChooserView\n010304bb=style/Widget.Holo.Light.Button.Borderless\n010304bc=style/Widget.Holo.Light.CompoundButton.Switch\n010304bd=style/Widget.Holo.Light.DatePicker\n010304be=style/Widget.Holo.Light.ExpandableListView.White\n010304bf=style/Widget.Holo.Light.FastScroll\n010304c0=style/Widget.Holo.Light.FragmentBreadCrumbs\n010304c1=style/Widget.Holo.Light.Gallery\n010304c2=style/Widget.Holo.Light.GestureOverlayView\n010304c3=style/Widget.Holo.Light.ImageWell\n010304c4=style/Widget.Holo.Light.KeyboardView\n010304c5=style/Widget.Holo.Light.ListView.White\n010304c6=style/Widget.Holo.Light.NumberPicker\n010304c7=style/Widget.Holo.Light.PopupWindow.ActionMode\n010304c8=style/Widget.Holo.Light.QuickContactBadge.WindowLarge\n010304c9=style/Widget.Holo.Light.QuickContactBadge.WindowMedium\n010304ca=style/Widget.Holo.Light.QuickContactBadge.WindowSmall\n010304cb=style/Widget.Holo.Light.QuickContactBadgeSmall.WindowLarge\n010304cc=style/Widget.Holo.Light.QuickContactBadgeSmall.WindowMedium\n010304cd=style/Widget.Holo.Light.QuickContactBadgeSmall.WindowSmall\n010304ce=style/Widget.Holo.Light.SearchView\n010304cf=style/Widget.Holo.Light.Spinner.DropDown\n010304d0=style/Widget.Holo.Light.Spinner.DropDown.ActionBar\n010304d1=style/Widget.Holo.Light.StackView\n010304d2=style/Widget.Holo.Light.TextSelectHandle\n010304d3=style/Widget.Holo.Light.TextView.ListSeparator\n010304d4=style/Widget.Holo.Light.TimePicker\n010304d5=style/Widget.Holo.ListView.White\n010304d6=style/Widget.Holo.NumberPicker\n010304d7=style/Widget.Holo.PopupWindow.ActionMode\n010304d8=style/Widget.Holo.PreferenceFrameLayout\n010304d9=style/Widget.Holo.ProgressBar.Inverse\n010304da=style/Widget.Holo.ProgressBar.Large.Inverse\n010304db=style/Widget.Holo.ProgressBar.Small.Inverse\n010304dc=style/Widget.Holo.QuickContactBadge.WindowLarge\n010304dd=style/Widget.Holo.QuickContactBadge.WindowMedium\n010304de=style/Widget.Holo.QuickContactBadge.WindowSmall\n010304df=style/Widget.Holo.QuickContactBadgeSmall.WindowLarge\n010304e0=style/Widget.Holo.QuickContactBadgeSmall.WindowMedium\n010304e1=style/Widget.Holo.QuickContactBadgeSmall.WindowSmall\n010304e2=style/Widget.Holo.SearchView\n010304e3=style/Widget.Holo.Spinner.DropDown\n010304e4=style/Widget.Holo.Spinner.DropDown.ActionBar\n010304e5=style/Widget.Holo.StackView\n010304e6=style/Widget.Holo.SuggestionButton\n010304e7=style/Widget.Holo.SuggestionItem\n010304e8=style/Widget.Holo.TabText\n010304e9=style/Widget.Holo.TextSelectHandle\n010304ea=style/Widget.Holo.TextView.ListSeparator\n010304eb=style/Widget.Holo.TimePicker\n010304ec=style/Widget.HorizontalScrollView\n010304ed=style/Widget.Leanback.Button\n010304ee=style/Widget.Leanback.Button.ButtonBar\n010304ef=style/Widget.Leanback.Button.ButtonBarGravityStart\n010304f0=style/Widget.Leanback.ButtonBar\n010304f1=style/Widget.Leanback.DatePicker\n010304f2=style/Widget.Leanback.NumberPicker\n010304f3=style/Widget.Leanback.TimePicker\n010304f4=style/Widget.LockPatternView\n010304f5=style/Widget.Magnifier\n010304f6=style/Widget.Material.AbsListView\n010304f7=style/Widget.Material.ActivityChooserView\n010304f8=style/Widget.Material.Button.ButtonBar.AlertDialog\n010304f9=style/Widget.Material.CompoundButton\n010304fa=style/Widget.Material.ContextPopupMenu\n010304fb=style/Widget.Material.ExpandableListView.White\n010304fc=style/Widget.Material.FragmentBreadCrumbs\n010304fd=style/Widget.Material.Gallery\n010304fe=style/Widget.Material.GestureOverlayView\n010304ff=style/Widget.Material.ImageWell\n01030500=style/Widget.Material.KeyboardView\n01030501=style/Widget.Material.Light.AbsListView\n01030502=style/Widget.Material.Light.ActivityChooserView\n01030503=style/Widget.Material.Light.Button.ButtonBar.AlertDialog\n01030504=style/Widget.Material.Light.CompoundButton\n01030505=style/Widget.Material.Light.ExpandableListView.White\n01030506=style/Widget.Material.Light.FragmentBreadCrumbs\n01030507=style/Widget.Material.Light.Gallery\n01030508=style/Widget.Material.Light.GestureOverlayView\n01030509=style/Widget.Material.Light.ImageWell\n0103050a=style/Widget.Material.Light.KeyboardView\n0103050b=style/Widget.Material.Light.ListView.White\n0103050c=style/Widget.Material.Light.PopupWindow.ActionMode\n0103050d=style/Widget.Material.Light.QuickContactBadge.WindowLarge\n0103050e=style/Widget.Material.Light.QuickContactBadge.WindowMedium\n0103050f=style/Widget.Material.Light.QuickContactBadge.WindowSmall\n01030510=style/Widget.Material.Light.QuickContactBadgeSmall.WindowLarge\n01030511=style/Widget.Material.Light.QuickContactBadgeSmall.WindowMedium\n01030512=style/Widget.Material.Light.QuickContactBadgeSmall.WindowSmall\n01030513=style/Widget.Material.Light.SearchView.ActionBar\n01030514=style/Widget.Material.Light.Spinner.DropDown\n01030515=style/Widget.Material.Light.Spinner.DropDown.ActionBar\n01030516=style/Widget.Material.Light.TextSelectHandle\n01030517=style/Widget.Material.Light.TextView.ListSeparator\n01030518=style/Widget.Material.ListMenuView\n01030519=style/Widget.Material.ListView.White\n0103051a=style/Widget.Material.Notification.MessagingName\n0103051b=style/Widget.Material.Notification.MessagingText\n0103051c=style/Widget.Material.Notification.NotificationProgressBar\n0103051d=style/Widget.Material.Notification.ProgressBar\n0103051e=style/Widget.Material.Notification.Text\n0103051f=style/Widget.Material.PopupWindow.ActionMode\n01030520=style/Widget.Material.PreferenceFrameLayout\n01030521=style/Widget.Material.ProgressBar.Inverse\n01030522=style/Widget.Material.ProgressBar.Large.Inverse\n01030523=style/Widget.Material.ProgressBar.Small.Inverse\n01030524=style/Widget.Material.QuickContactBadge.WindowLarge\n01030525=style/Widget.Material.QuickContactBadge.WindowMedium\n01030526=style/Widget.Material.QuickContactBadge.WindowSmall\n01030527=style/Widget.Material.QuickContactBadgeSmall.WindowLarge\n01030528=style/Widget.Material.QuickContactBadgeSmall.WindowMedium\n01030529=style/Widget.Material.QuickContactBadgeSmall.WindowSmall\n0103052a=style/Widget.Material.Resolver.Tab\n0103052b=style/Widget.Material.SearchView.ActionBar\n0103052c=style/Widget.Material.Spinner.DropDown\n0103052d=style/Widget.Material.Spinner.DropDown.ActionBar\n0103052e=style/Widget.Material.SuggestionButton\n0103052f=style/Widget.Material.SuggestionItem\n01030530=style/Widget.Material.TabText\n01030531=style/Widget.Material.TextSelectHandle\n01030532=style/Widget.Material.TextView.ListSeparator\n01030533=style/Widget.Material3\n01030534=style/Widget.Material3.Button\n01030535=style/Widget.Material3.Button.Filled\n01030536=style/Widget.Material3.Button.FilledTonal\n01030537=style/Widget.Material3.Button.Outlined\n01030538=style/Widget.Material3.Button.Text\n01030539=style/Widget.NumberPicker\n0103053a=style/Widget.PreferenceFrameLayout\n0103053b=style/Widget.ProgressBar.Small.Title\n0103053c=style/Widget.QuickContactBadge\n0103053d=style/Widget.QuickContactBadge.WindowLarge\n0103053e=style/Widget.QuickContactBadge.WindowMedium\n0103053f=style/Widget.QuickContactBadge.WindowSmall\n01030540=style/Widget.QuickContactBadgeSmall\n01030541=style/Widget.QuickContactBadgeSmall.WindowLarge\n01030542=style/Widget.QuickContactBadgeSmall.WindowMedium\n01030543=style/Widget.QuickContactBadgeSmall.WindowSmall\n01030544=style/Widget.RatingBar.Indicator\n01030545=style/Widget.RatingBar.Small\n01030546=style/Widget.TextSelectHandle\n01030547=style/Widget.TextView.ListSeparator\n01030548=style/Widget.TextView.ListSeparator.White\n01030549=style/Widget.TimePicker\n0103054a=style/Widget.WebTextView\n0103054b=style/WindowAnimationStyle.Leanback.Setup\n0103054c=style/WindowTitle\n0103054d=style/WindowTitle.DeviceDefault\n0103054e=style/WindowTitle.Holo\n0103054f=style/WindowTitle.Material\n01030550=style/WindowTitleBackground\n01030551=style/WindowTitleBackground.DeviceDefault\n01030552=style/WindowTitleBackground.Holo\n01030553=style/WindowTitleBackground.Material\n01030554=style/ZoomControls\n01030555=style/aerr_list_item\n01040000=string/cancel\n01040001=string/copy\n01040002=string/copyUrl\n01040003=string/cut\n01040004=string/defaultVoiceMailAlphaTag\n01040005=string/defaultMsisdnAlphaTag\n01040006=string/emptyPhoneNumber\n01040007=string/httpErrorBadUrl\n01040008=string/httpErrorUnsupportedScheme\n01040009=string/no\n0104000a=string/ok\n0104000b=string/paste\n0104000c=string/search_go\n0104000d=string/selectAll\n0104000e=string/unknownName\n0104000f=string/untitled\n01040010=string/VideoView_error_button\n01040011=string/VideoView_error_text_unknown\n01040012=string/VideoView_error_title\n01040013=string/yes\n01040014=string/dialog_alert_title\n01040015=string/VideoView_error_text_invalid_progressive_playback\n01040016=string/selectTextMode\n01040017=string/status_bar_notification_info_overflow\n01040018=string/fingerprint_icon_content_description\n01040019=string/paste_as_plain_text\n0104001a=string/autofill\n0104001b=string/config_helpPackageNameKey\n0104001c=string/config_helpPackageNameValue\n0104001d=string/config_helpIntentExtraKey\n0104001e=string/config_helpIntentNameKey\n0104001f=string/config_feedbackIntentExtraKey\n01040020=string/config_feedbackIntentNameKey\n01040021=string/config_defaultAssistant\n01040022=string/config_defaultBrowser\n01040023=string/config_defaultDialer\n01040024=string/config_defaultSms\n01040025=string/config_defaultCallRedirection\n01040026=string/config_defaultCallScreening\n01040027=string/config_systemGallery\n01040028=string/config_systemAutomotiveCluster\n01040029=string/config_systemAutomotiveProjection\n0104002a=string/config_systemShell\n0104002b=string/config_systemContacts\n0104002c=string/config_customMediaKeyDispatcher\n0104002d=string/config_customMediaSessionPolicyProvider\n0104002e=string/config_systemSpeechRecognizer\n0104002f=string/config_systemWifiCoexManager\n01040030=string/config_systemWellbeing\n01040031=string/config_systemTelevisionNotificationHandler\n01040032=string/config_systemUiIntelligence\n01040033=string/config_systemAmbientAudioIntelligence\n01040034=string/config_systemAudioIntelligence\n01040035=string/config_systemNotificationIntelligence\n01040036=string/config_systemTextIntelligence\n01040037=string/config_systemVisualIntelligence\n01040038=string/config_systemActivityRecognizer\n01040039=string/config_systemCompanionDeviceProvider\n0104003a=string/config_systemUi\n0104003b=string/config_defaultRingtoneVibrationSound\n0104003c=string/config_systemSupervision\n0104003d=string/config_devicePolicyManagement\n0104003e=string/config_systemAppProtectionService\n0104003f=string/config_systemAutomotiveCalendarSyncManager\n01040040=string/config_defaultAutomotiveNavigation\n01040041=string/safety_protection_display_text\n01040042=string/config_systemSettingsIntelligence\n01040043=string/config_systemBluetoothStack\n01040044=string/config_systemWearHealthService\n01040045=string/config_defaultNotes\n01040046=string/config_systemFinancedDeviceController\n01040047=string/config_systemCallStreaming\n01040048=string/config_defaultRetailDemo\n01040049=string/config_defaultWallet\n0104004a=string/config_systemDependencyInstaller\n0104004b=string/config_systemVendorIntelligence\n0104004c=string/BaMmi\n0104004d=string/CLIRDefaultOffNextCallOff\n0104004e=string/CLIRDefaultOffNextCallOn\n0104004f=string/CLIRDefaultOnNextCallOff\n01040050=string/CLIRDefaultOnNextCallOn\n01040051=string/CLIRPermanent\n01040052=string/CfMmi\n01040053=string/ClipMmi\n01040054=string/ClirMmi\n01040055=string/CndMmi\n01040056=string/CnipMmi\n01040057=string/CnirMmi\n01040058=string/ColpMmi\n01040059=string/ColrMmi\n0104005a=string/CwMmi\n0104005b=string/DndMmi\n0104005c=string/EmergencyCallWarningSummary\n0104005d=string/EmergencyCallWarningTitle\n0104005e=string/Midnight\n0104005f=string/NetworkPreferenceSwitchSummary\n01040060=string/NetworkPreferenceSwitchTitle\n01040061=string/Noon\n01040062=string/PERSOSUBSTATE_RUIM_CORPORATE_ENTRY\n01040063=string/PERSOSUBSTATE_RUIM_CORPORATE_ERROR\n01040064=string/PERSOSUBSTATE_RUIM_CORPORATE_IN_PROGRESS\n01040065=string/PERSOSUBSTATE_RUIM_CORPORATE_PUK_ENTRY\n01040066=string/PERSOSUBSTATE_RUIM_CORPORATE_PUK_ERROR\n01040067=string/PERSOSUBSTATE_RUIM_CORPORATE_PUK_IN_PROGRESS\n01040068=string/PERSOSUBSTATE_RUIM_CORPORATE_PUK_SUCCESS\n01040069=string/PERSOSUBSTATE_RUIM_CORPORATE_SUCCESS\n0104006a=string/PERSOSUBSTATE_RUIM_HRPD_ENTRY\n0104006b=string/PERSOSUBSTATE_RUIM_HRPD_ERROR\n0104006c=string/PERSOSUBSTATE_RUIM_HRPD_IN_PROGRESS\n0104006d=string/PERSOSUBSTATE_RUIM_HRPD_PUK_ENTRY\n0104006e=string/PERSOSUBSTATE_RUIM_HRPD_PUK_ERROR\n0104006f=string/PERSOSUBSTATE_RUIM_HRPD_PUK_IN_PROGRESS\n01040070=string/PERSOSUBSTATE_RUIM_HRPD_PUK_SUCCESS\n01040071=string/PERSOSUBSTATE_RUIM_HRPD_SUCCESS\n01040072=string/PERSOSUBSTATE_RUIM_NETWORK1_ENTRY\n01040073=string/PERSOSUBSTATE_RUIM_NETWORK1_ERROR\n01040074=string/PERSOSUBSTATE_RUIM_NETWORK1_IN_PROGRESS\n01040075=string/PERSOSUBSTATE_RUIM_NETWORK1_PUK_ENTRY\n01040076=string/PERSOSUBSTATE_RUIM_NETWORK1_PUK_ERROR\n01040077=string/PERSOSUBSTATE_RUIM_NETWORK1_PUK_IN_PROGRESS\n01040078=string/PERSOSUBSTATE_RUIM_NETWORK1_PUK_SUCCESS\n01040079=string/PERSOSUBSTATE_RUIM_NETWORK1_SUCCESS\n0104007a=string/PERSOSUBSTATE_RUIM_NETWORK2_ENTRY\n0104007b=string/PERSOSUBSTATE_RUIM_NETWORK2_ERROR\n0104007c=string/PERSOSUBSTATE_RUIM_NETWORK2_IN_PROGRESS\n0104007d=string/PERSOSUBSTATE_RUIM_NETWORK2_PUK_ENTRY\n0104007e=string/PERSOSUBSTATE_RUIM_NETWORK2_PUK_ERROR\n0104007f=string/PERSOSUBSTATE_RUIM_NETWORK2_PUK_IN_PROGRESS\n01040080=string/PERSOSUBSTATE_RUIM_NETWORK2_PUK_SUCCESS\n01040081=string/PERSOSUBSTATE_RUIM_NETWORK2_SUCCESS\n01040082=string/PERSOSUBSTATE_RUIM_RUIM_ENTRY\n01040083=string/PERSOSUBSTATE_RUIM_RUIM_ERROR\n01040084=string/PERSOSUBSTATE_RUIM_RUIM_IN_PROGRESS\n01040085=string/PERSOSUBSTATE_RUIM_RUIM_PUK_ENTRY\n01040086=string/PERSOSUBSTATE_RUIM_RUIM_PUK_ERROR\n01040087=string/PERSOSUBSTATE_RUIM_RUIM_PUK_IN_PROGRESS\n01040088=string/PERSOSUBSTATE_RUIM_RUIM_PUK_SUCCESS\n01040089=string/PERSOSUBSTATE_RUIM_RUIM_SUCCESS\n0104008a=string/PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ENTRY\n0104008b=string/PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ERROR\n0104008c=string/PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_IN_PROGRESS\n0104008d=string/PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_ENTRY\n0104008e=string/PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_ERROR\n0104008f=string/PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_IN_PROGRESS\n01040090=string/PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_SUCCESS\n01040091=string/PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_SUCCESS\n01040092=string/PERSOSUBSTATE_SIM_CORPORATE_ENTRY\n01040093=string/PERSOSUBSTATE_SIM_CORPORATE_ERROR\n01040094=string/PERSOSUBSTATE_SIM_CORPORATE_IN_PROGRESS\n01040095=string/PERSOSUBSTATE_SIM_CORPORATE_PUK_ENTRY\n01040096=string/PERSOSUBSTATE_SIM_CORPORATE_PUK_ERROR\n01040097=string/PERSOSUBSTATE_SIM_CORPORATE_PUK_IN_PROGRESS\n01040098=string/PERSOSUBSTATE_SIM_CORPORATE_PUK_SUCCESS\n01040099=string/PERSOSUBSTATE_SIM_CORPORATE_SUCCESS\n0104009a=string/PERSOSUBSTATE_SIM_ICCID_ENTRY\n0104009b=string/PERSOSUBSTATE_SIM_ICCID_ERROR\n0104009c=string/PERSOSUBSTATE_SIM_ICCID_IN_PROGRESS\n0104009d=string/PERSOSUBSTATE_SIM_ICCID_SUCCESS\n0104009e=string/PERSOSUBSTATE_SIM_IMPI_ENTRY\n0104009f=string/PERSOSUBSTATE_SIM_IMPI_ERROR\n010400a0=string/PERSOSUBSTATE_SIM_IMPI_IN_PROGRESS\n010400a1=string/PERSOSUBSTATE_SIM_IMPI_SUCCESS\n010400a2=string/PERSOSUBSTATE_SIM_NETWORK_ENTRY\n010400a3=string/PERSOSUBSTATE_SIM_NETWORK_ERROR\n010400a4=string/PERSOSUBSTATE_SIM_NETWORK_IN_PROGRESS\n010400a5=string/PERSOSUBSTATE_SIM_NETWORK_PUK_ENTRY\n010400a6=string/PERSOSUBSTATE_SIM_NETWORK_PUK_ERROR\n010400a7=string/PERSOSUBSTATE_SIM_NETWORK_PUK_IN_PROGRESS\n010400a8=string/PERSOSUBSTATE_SIM_NETWORK_PUK_SUCCESS\n010400a9=string/PERSOSUBSTATE_SIM_NETWORK_SUBSET_ENTRY\n010400aa=string/PERSOSUBSTATE_SIM_NETWORK_SUBSET_ERROR\n010400ab=string/PERSOSUBSTATE_SIM_NETWORK_SUBSET_IN_PROGRESS\n010400ac=string/PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ENTRY\n010400ad=string/PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ERROR\n010400ae=string/PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_IN_PROGRESS\n010400af=string/PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_SUCCESS\n010400b0=string/PERSOSUBSTATE_SIM_NETWORK_SUBSET_SUCCESS\n010400b1=string/PERSOSUBSTATE_SIM_NETWORK_SUCCESS\n010400b2=string/PERSOSUBSTATE_SIM_NS_SP_ENTRY\n010400b3=string/PERSOSUBSTATE_SIM_NS_SP_ERROR\n010400b4=string/PERSOSUBSTATE_SIM_NS_SP_IN_PROGRESS\n010400b5=string/PERSOSUBSTATE_SIM_NS_SP_SUCCESS\n010400b6=string/PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ENTRY\n010400b7=string/PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ERROR\n010400b8=string/PERSOSUBSTATE_SIM_SERVICE_PROVIDER_IN_PROGRESS\n010400b9=string/PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_ENTRY\n010400ba=string/PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_ERROR\n010400bb=string/PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_IN_PROGRESS\n010400bc=string/PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_SUCCESS\n010400bd=string/PERSOSUBSTATE_SIM_SERVICE_PROVIDER_SUCCESS\n010400be=string/PERSOSUBSTATE_SIM_SIM_ENTRY\n010400bf=string/PERSOSUBSTATE_SIM_SIM_ERROR\n010400c0=string/PERSOSUBSTATE_SIM_SIM_IN_PROGRESS\n010400c1=string/PERSOSUBSTATE_SIM_SIM_PUK_ENTRY\n010400c2=string/PERSOSUBSTATE_SIM_SIM_PUK_ERROR\n010400c3=string/PERSOSUBSTATE_SIM_SIM_PUK_IN_PROGRESS\n010400c4=string/PERSOSUBSTATE_SIM_SIM_PUK_SUCCESS\n010400c5=string/PERSOSUBSTATE_SIM_SIM_SUCCESS\n010400c6=string/PERSOSUBSTATE_SIM_SPN_ENTRY\n010400c7=string/PERSOSUBSTATE_SIM_SPN_ERROR\n010400c8=string/PERSOSUBSTATE_SIM_SPN_IN_PROGRESS\n010400c9=string/PERSOSUBSTATE_SIM_SPN_SUCCESS\n010400ca=string/PERSOSUBSTATE_SIM_SP_EHPLMN_ENTRY\n010400cb=string/PERSOSUBSTATE_SIM_SP_EHPLMN_ERROR\n010400cc=string/PERSOSUBSTATE_SIM_SP_EHPLMN_IN_PROGRESS\n010400cd=string/PERSOSUBSTATE_SIM_SP_EHPLMN_SUCCESS\n010400ce=string/PinMmi\n010400cf=string/PwdMmi\n010400d0=string/RestrictedOnAllVoiceTitle\n010400d1=string/RestrictedOnDataTitle\n010400d2=string/RestrictedOnEmergencyTitle\n010400d3=string/RestrictedOnNormalTitle\n010400d4=string/RestrictedStateContent\n010400d5=string/RestrictedStateContentMsimTemplate\n010400d6=string/RuacMmi\n010400d7=string/SetupCallDefault\n010400d8=string/ThreeWCMmi\n010400d9=string/accept\n010400da=string/accessibility_autoclick_double_click\n010400db=string/accessibility_autoclick_drag\n010400dc=string/accessibility_autoclick_left_click\n010400dd=string/accessibility_autoclick_pause\n010400de=string/accessibility_autoclick_position\n010400df=string/accessibility_autoclick_right_click\n010400e0=string/accessibility_autoclick_scroll\n010400e1=string/accessibility_autoclick_scroll_down\n010400e2=string/accessibility_autoclick_scroll_exit\n010400e3=string/accessibility_autoclick_scroll_left\n010400e4=string/accessibility_autoclick_scroll_panel_title\n010400e5=string/accessibility_autoclick_scroll_right\n010400e6=string/accessibility_autoclick_scroll_up\n010400e7=string/accessibility_autoclick_type_settings_panel_title\n010400e8=string/accessibility_binding_label\n010400e9=string/accessibility_button_instructional_text\n010400ea=string/accessibility_button_prompt_text\n010400eb=string/accessibility_dialog_button_allow\n010400ec=string/accessibility_dialog_button_deny\n010400ed=string/accessibility_dialog_button_uninstall\n010400ee=string/accessibility_dialog_touch_filtered_warning\n010400ef=string/accessibility_edit_shortcut_menu_button_title\n010400f0=string/accessibility_edit_shortcut_menu_volume_title\n010400f1=string/accessibility_enable_service_title\n010400f2=string/accessibility_gesture_3finger_instructional_text\n010400f3=string/accessibility_gesture_3finger_prompt_text\n010400f4=string/accessibility_gesture_instructional_text\n010400f5=string/accessibility_gesture_prompt_text\n010400f6=string/accessibility_label_clone_profile\n010400f7=string/accessibility_label_communal_profile\n010400f8=string/accessibility_label_managed_profile\n010400f9=string/accessibility_label_private_profile\n010400fa=string/accessibility_magnification_chooser_text\n010400fb=string/accessibility_select_shortcut_menu_title\n010400fc=string/accessibility_service_action_perform_description\n010400fd=string/accessibility_service_action_perform_title\n010400fe=string/accessibility_service_screen_control_description\n010400ff=string/accessibility_service_screen_control_title\n01040100=string/accessibility_service_warning_description\n01040101=string/accessibility_shortcut_disabling_service\n01040102=string/accessibility_shortcut_enabling_service\n01040103=string/accessibility_shortcut_menu_item_status_off\n01040104=string/accessibility_shortcut_menu_item_status_on\n01040105=string/accessibility_shortcut_multiple_service_list\n01040106=string/accessibility_shortcut_multiple_service_warning\n01040107=string/accessibility_shortcut_multiple_service_warning_title\n01040108=string/accessibility_shortcut_off\n01040109=string/accessibility_shortcut_on\n0104010a=string/accessibility_shortcut_single_service_warning\n0104010b=string/accessibility_shortcut_single_service_warning_title\n0104010c=string/accessibility_shortcut_spoken_feedback\n0104010d=string/accessibility_shortcut_toogle_warning\n0104010e=string/accessibility_shortcut_warning_dialog_title\n0104010f=string/accessibility_system_action_back_label\n01040110=string/accessibility_system_action_dismiss_notification_shade\n01040111=string/accessibility_system_action_dpad_center_label\n01040112=string/accessibility_system_action_dpad_down_label\n01040113=string/accessibility_system_action_dpad_left_label\n01040114=string/accessibility_system_action_dpad_right_label\n01040115=string/accessibility_system_action_dpad_up_label\n01040116=string/accessibility_system_action_hardware_a11y_shortcut_label\n01040117=string/accessibility_system_action_headset_hook_label\n01040118=string/accessibility_system_action_home_label\n01040119=string/accessibility_system_action_lock_screen_label\n0104011a=string/accessibility_system_action_media_play_pause_label\n0104011b=string/accessibility_system_action_menu_label\n0104011c=string/accessibility_system_action_notifications_label\n0104011d=string/accessibility_system_action_on_screen_a11y_shortcut_chooser_label\n0104011e=string/accessibility_system_action_on_screen_a11y_shortcut_label\n0104011f=string/accessibility_system_action_power_dialog_label\n01040120=string/accessibility_system_action_quick_settings_label\n01040121=string/accessibility_system_action_recents_label\n01040122=string/accessibility_system_action_screenshot_label\n01040123=string/accessibility_uncheck_legacy_item_warning\n01040124=string/action_bar_home_description\n01040125=string/action_bar_home_description_format\n01040126=string/action_bar_home_subtitle_description_format\n01040127=string/action_bar_up_description\n01040128=string/action_menu_overflow_description\n01040129=string/action_mode_done\n0104012a=string/activity_chooser_view_dialog_title_default\n0104012b=string/activity_chooser_view_see_all\n0104012c=string/activity_list_empty\n0104012d=string/activity_resolver_use_always\n0104012e=string/activity_resolver_use_once\n0104012f=string/activity_resolver_work_profiles_support\n01040130=string/activitychooserview_choose_application\n01040131=string/activitychooserview_choose_application_error\n01040132=string/adb_active_notification_message\n01040133=string/adb_active_notification_title\n01040134=string/adb_debugging_notification_channel_tv\n01040135=string/adbwifi_active_notification_message\n01040136=string/adbwifi_active_notification_title\n01040137=string/addToDictionary\n01040138=string/add_account_button_label\n01040139=string/add_account_label\n0104013a=string/aerr_application\n0104013b=string/aerr_application_repeated\n0104013c=string/aerr_close\n0104013d=string/aerr_close_app\n0104013e=string/aerr_mute\n0104013f=string/aerr_process\n01040140=string/aerr_process_repeated\n01040141=string/aerr_report\n01040142=string/aerr_restart\n01040143=string/aerr_wait\n01040144=string/alert_windows_notification_channel_group_name\n01040145=string/alert_windows_notification_channel_name\n01040146=string/alert_windows_notification_message\n01040147=string/alert_windows_notification_title\n01040148=string/alert_windows_notification_turn_off_action\n01040149=string/all_apps_group_a11y_title\n0104014a=string/allow\n0104014b=string/alternate_eri_file\n0104014c=string/alternative_face_setup_notification_content\n0104014d=string/alternative_fp_setup_notification_content\n0104014e=string/alternative_unlock_setup_notification_title\n0104014f=string/alwaysUse\n01040150=string/android_preparing_apk\n01040151=string/android_start_title\n01040152=string/android_system_label\n01040153=string/android_upgrading_complete\n01040154=string/android_upgrading_notification_title\n01040155=string/android_upgrading_starting_apps\n01040156=string/android_upgrading_title\n01040157=string/anr_activity_application\n01040158=string/anr_activity_process\n01040159=string/anr_application_process\n0104015a=string/anr_process\n0104015b=string/anr_title\n0104015c=string/app_blocked_message\n0104015d=string/app_blocked_title\n0104015e=string/app_category_accessibility\n0104015f=string/app_category_audio\n01040160=string/app_category_game\n01040161=string/app_category_image\n01040162=string/app_category_maps\n01040163=string/app_category_news\n01040164=string/app_category_productivity\n01040165=string/app_category_social\n01040166=string/app_category_video\n01040167=string/app_info\n01040168=string/app_not_found\n01040169=string/app_running_notification_text\n0104016a=string/app_running_notification_title\n0104016b=string/app_streaming_blocked_message\n0104016c=string/app_streaming_blocked_message_for_fingerprint_dialog\n0104016d=string/app_streaming_blocked_message_for_permission_request\n0104016e=string/app_streaming_blocked_message_for_settings_dialog\n0104016f=string/app_streaming_blocked_title\n01040170=string/app_streaming_blocked_title_for_camera_dialog\n01040171=string/app_streaming_blocked_title_for_fingerprint_dialog\n01040172=string/app_streaming_blocked_title_for_microphone_dialog\n01040173=string/app_streaming_blocked_title_for_permission_dialog\n01040174=string/app_streaming_blocked_title_for_playstore_dialog\n01040175=string/app_streaming_blocked_title_for_settings_dialog\n01040176=string/app_suspended_default_message\n01040177=string/app_suspended_more_details\n01040178=string/app_suspended_title\n01040179=string/app_suspended_unsuspend_message\n0104017a=string/app_upgrading_toast\n0104017b=string/as_app_forced_to_restricted_bucket\n0104017c=string/auto_data_switch_content\n0104017d=string/auto_data_switch_title\n0104017e=string/autoclick_feature_name\n0104017f=string/autofill_continue_yes\n01040180=string/autofill_error_cannot_autofill\n01040181=string/autofill_picker_accessibility_title\n01040182=string/autofill_picker_no_suggestions\n01040183=string/autofill_picker_some_suggestions\n01040184=string/autofill_save_accessibility_title\n01040185=string/autofill_save_never\n01040186=string/autofill_save_no\n01040187=string/autofill_save_notnow\n01040188=string/autofill_save_title\n01040189=string/autofill_save_title_with_2types\n0104018a=string/autofill_save_title_with_3types\n0104018b=string/autofill_save_title_with_type\n0104018c=string/autofill_save_type_address\n0104018d=string/autofill_save_type_credit_card\n0104018e=string/autofill_save_type_debit_card\n0104018f=string/autofill_save_type_email_address\n01040190=string/autofill_save_type_generic_card\n01040191=string/autofill_save_type_password\n01040192=string/autofill_save_type_payment_card\n01040193=string/autofill_save_type_username\n01040194=string/autofill_save_yes\n01040195=string/autofill_update_title\n01040196=string/autofill_update_title_with_2types\n01040197=string/autofill_update_title_with_3types\n01040198=string/autofill_update_title_with_type\n01040199=string/autofill_update_yes\n0104019a=string/autofill_window_title\n0104019b=string/back_button_label\n0104019c=string/badPin\n0104019d=string/badPuk\n0104019e=string/battery_saver_charged_notification_summary\n0104019f=string/battery_saver_description\n010401a0=string/battery_saver_description_with_learn_more\n010401a1=string/battery_saver_notification_channel_name\n010401a2=string/battery_saver_off_notification_title\n010401a3=string/beforeOneMonthDurationPast\n010401a4=string/bg_user_sound_notification_button_mute\n010401a5=string/bg_user_sound_notification_button_switch_user\n010401a6=string/bg_user_sound_notification_message\n010401a7=string/bg_user_sound_notification_title_alarm\n010401a8=string/biometric_app_setting_name\n010401a9=string/biometric_dangling_notification_action_not_now\n010401aa=string/biometric_dangling_notification_action_set_up\n010401ab=string/biometric_dialog_default_subtitle\n010401ac=string/biometric_dialog_default_title\n010401ad=string/biometric_error_canceled\n010401ae=string/biometric_error_device_not_secured\n010401af=string/biometric_error_generic\n010401b0=string/biometric_error_hw_unavailable\n010401b1=string/biometric_error_user_canceled\n010401b2=string/biometric_face_not_recognized\n010401b3=string/biometric_not_recognized\n010401b4=string/biometric_or_screen_lock_app_setting_name\n010401b5=string/biometric_or_screen_lock_dialog_default_subtitle\n010401b6=string/bluetooth_a2dp_audio_route_id\n010401b7=string/bluetooth_a2dp_audio_route_name\n010401b8=string/bluetooth_airplane_mode_toast\n010401b9=string/bugreport_countdown\n010401ba=string/bugreport_message\n010401bb=string/bugreport_option_full_summary\n010401bc=string/bugreport_option_full_title\n010401bd=string/bugreport_option_interactive_summary\n010401be=string/bugreport_option_interactive_title\n010401bf=string/bugreport_screenshot_failure_toast\n010401c0=string/bugreport_screenshot_success_toast\n010401c1=string/bugreport_status\n010401c2=string/bugreport_title\n010401c3=string/byteShort\n010401c4=string/call_notification_answer_action\n010401c5=string/call_notification_answer_video_action\n010401c6=string/call_notification_decline_action\n010401c7=string/call_notification_hang_up_action\n010401c8=string/call_notification_incoming_text\n010401c9=string/call_notification_ongoing_text\n010401ca=string/call_notification_screening_text\n010401cb=string/candidates_style\n010401cc=string/capability_desc_canCaptureFingerprintGestures\n010401cd=string/capability_desc_canControlMagnification\n010401ce=string/capability_desc_canPerformGestures\n010401cf=string/capability_desc_canRequestFilterKeyEvents\n010401d0=string/capability_desc_canRequestTouchExploration\n010401d1=string/capability_desc_canRetrieveWindowContent\n010401d2=string/capability_desc_canTakeScreenshot\n010401d3=string/capability_title_canCaptureFingerprintGestures\n010401d4=string/capability_title_canControlMagnification\n010401d5=string/capability_title_canPerformGestures\n010401d6=string/capability_title_canRequestFilterKeyEvents\n010401d7=string/capability_title_canRequestTouchExploration\n010401d8=string/capability_title_canRetrieveWindowContent\n010401d9=string/capability_title_canTakeScreenshot\n010401da=string/capital_off\n010401db=string/capital_on\n010401dc=string/car_loading_profile\n010401dd=string/car_mode_disable_notification_message\n010401de=string/car_mode_disable_notification_title\n010401df=string/carrier_app_notification_text\n010401e0=string/carrier_app_notification_title\n010401e1=string/cfTemplateForwarded\n010401e2=string/cfTemplateForwardedTime\n010401e3=string/cfTemplateNotForwarded\n010401e4=string/cfTemplateRegistered\n010401e5=string/cfTemplateRegisteredTime\n010401e6=string/checked\n010401e7=string/chooseActivity\n010401e8=string/chooseUsbActivity\n010401e9=string/choose_account_label\n010401ea=string/chooser_all_apps_button_label\n010401eb=string/chooser_no_direct_share_targets\n010401ec=string/chooser_wallpaper\n010401ed=string/clearDefaultHintMsg\n010401ee=string/clone_profile_label_badge\n010401ef=string/close_button_text\n010401f0=string/color_correction_feature_name\n010401f1=string/color_inversion_feature_name\n010401f2=string/common_last_name_prefixes\n010401f3=string/common_name\n010401f4=string/common_name_conjunctions\n010401f5=string/common_name_prefixes\n010401f6=string/common_name_suffixes\n010401f7=string/concurrent_display_notification_active_content\n010401f8=string/concurrent_display_notification_active_title\n010401f9=string/concurrent_display_notification_name\n010401fa=string/concurrent_display_notification_power_save_content\n010401fb=string/concurrent_display_notification_power_save_title\n010401fc=string/concurrent_display_notification_thermal_content\n010401fd=string/concurrent_display_notification_thermal_title\n010401fe=string/condition_provider_service_binding_label\n010401ff=string/conference_call\n01040200=string/config_UsbDeviceConnectionHandling_component\n01040201=string/config_accountTypeToKeepFirstAccount\n01040202=string/config_activityRecognitionHardwarePackageName\n01040203=string/config_ambientContextEventArrayExtraKey\n01040204=string/config_ambientContextPackageNameExtraKey\n01040205=string/config_appsAuthorizedForSharedAccounts\n01040206=string/config_appsNotReportingCrashes\n01040207=string/config_bandwidthEstimateSource\n01040208=string/config_batterySaverDeviceSpecificConfig\n01040209=string/config_batterySaverScheduleProvider\n0104020a=string/config_batterymeterBoltPath\n0104020b=string/config_batterymeterErrorPerimeterPath\n0104020c=string/config_batterymeterFillMask\n0104020d=string/config_batterymeterPerimeterPath\n0104020e=string/config_batterymeterPowersavePath\n0104020f=string/config_biometric_prompt_ui_package\n01040210=string/config_bodyFontFamily\n01040211=string/config_bodyFontFamilyMedium\n01040212=string/config_cameraLaunchGestureSensorStringType\n01040213=string/config_cameraLiftTriggerSensorStringType\n01040214=string/config_cameraShutterSound\n01040215=string/config_carrierAppInstallDialogComponent\n01040216=string/config_chooseAccountActivity\n01040217=string/config_chooseTypeAndAccountActivity\n01040218=string/config_chooserActivity\n01040219=string/config_clockFontFamily\n0104021a=string/config_companionDeviceManagerPackage\n0104021b=string/config_controlsPackage\n0104021c=string/config_credentialManagerReceiverComponent\n0104021d=string/config_customAdbPublicKeyConfirmationComponent\n0104021e=string/config_customAdbPublicKeyConfirmationSecondaryUserComponent\n0104021f=string/config_customAdbWifiNetworkConfirmationComponent\n01040220=string/config_customAdbWifiNetworkConfirmationSecondaryUserComponent\n01040221=string/config_customCountryDetector\n01040222=string/config_customResolverActivity\n01040223=string/config_customVpnAlwaysOnDisconnectedDialogComponent\n01040224=string/config_customVpnConfirmDialogComponent\n01040225=string/config_dataUsageSummaryComponent\n01040226=string/config_datause_iface\n01040227=string/config_defaultAccessibilityNotificationSound\n01040228=string/config_defaultAccessibilityService\n01040229=string/config_defaultAmbientContextConsentComponent\n0104022a=string/config_defaultAmbientContextDetectionService\n0104022b=string/config_defaultAppPredictionService\n0104022c=string/config_defaultAssistantAccessComponent\n0104022d=string/config_defaultAttentionService\n0104022e=string/config_defaultAugmentedAutofillService\n0104022f=string/config_defaultAutofillService\n01040230=string/config_defaultBugReportHandlerApp\n01040231=string/config_defaultCaptivePortalLoginPackageName\n01040232=string/config_defaultContentCaptureService\n01040233=string/config_defaultContentProtectionService\n01040234=string/config_defaultContentSuggestionsService\n01040235=string/config_defaultContextualSearchEnabled\n01040236=string/config_defaultContextualSearchKey\n01040237=string/config_defaultContextualSearchLegacyEnabled\n01040238=string/config_defaultContextualSearchPackageName\n01040239=string/config_defaultCredentialManagerAutofillService\n0104023a=string/config_defaultCredentialManagerHybridService\n0104023b=string/config_defaultDisplayCompatHostActivity\n0104023c=string/config_defaultDndAccessPackages\n0104023d=string/config_defaultDndDeniedPackages\n0104023e=string/config_defaultDockManagerPackageName\n0104023f=string/config_defaultFieldClassificationService\n01040240=string/config_defaultHealthConnectApp\n01040241=string/config_defaultLaunchOnPrivateDisplayRouterActivity\n01040242=string/config_defaultListenerAccessPackages\n01040243=string/config_defaultModuleMetadataProvider\n01040244=string/config_defaultMusicRecognitionService\n01040245=string/config_defaultNearbyFastPairSettingsDevicesComponent\n01040246=string/config_defaultNearbySharingComponent\n01040247=string/config_defaultNearbySharingSliceUri\n01040248=string/config_defaultNetworkRecommendationProviderPackage\n01040249=string/config_defaultNetworkScorerPackageName\n0104024a=string/config_defaultOnDeviceSpeechRecognitionService\n0104024b=string/config_defaultProfcollectReportUploaderAction\n0104024c=string/config_defaultProfcollectReportUploaderApp\n0104024d=string/config_defaultQrCodeComponent\n0104024e=string/config_defaultRotationResolverService\n0104024f=string/config_defaultSearchSelectorPackageName\n01040250=string/config_defaultSearchUiService\n01040251=string/config_defaultSelectToSpeakService\n01040252=string/config_defaultShutdownVibrationFile\n01040253=string/config_defaultSmartspaceService\n01040254=string/config_defaultSupervisionProfileOwnerComponent\n01040255=string/config_defaultSystemCaptionsManagerService\n01040256=string/config_defaultTextClassifierPackage\n01040257=string/config_defaultTranslationService\n01040258=string/config_defaultTrustAgent\n01040259=string/config_defaultWallpaperEffectsGenerationService\n0104025a=string/config_defaultWearableSensingConsentComponent\n0104025b=string/config_defaultWearableSensingService\n0104025c=string/config_defaultWellbeingPackage\n0104025d=string/config_default_dns_server\n0104025e=string/config_deviceConfiguratorPackageName\n0104025f=string/config_devicePolicyManagementUpdater\n01040260=string/config_deviceProvisioningPackage\n01040261=string/config_deviceSpecificAudioService\n01040262=string/config_deviceSpecificDevicePolicyManagerService\n01040263=string/config_deviceSpecificDeviceStatePolicyProvider\n01040264=string/config_deviceSpecificDisplayAreaPolicyProvider\n01040265=string/config_deviceSpecificInputMethodManagerService\n01040266=string/config_displayLightSensorType\n01040267=string/config_displayWhiteBalanceColorTemperatureSensorName\n01040268=string/config_display_features\n01040269=string/config_doublePressOnPowerTargetActivity\n0104026a=string/config_doubleTouchGestureEnableFile\n0104026b=string/config_dozeComponent\n0104026c=string/config_dozeDoubleTapSensorType\n0104026d=string/config_dozeLongPressSensorType\n0104026e=string/config_dozeTapSensorType\n0104026f=string/config_dozeUdfpsLongPressSensorType\n01040270=string/config_dreamsDefaultComponent\n01040271=string/config_emergency_call_number\n01040272=string/config_emergency_dialer_package\n01040273=string/config_ethernet_iface_regex\n01040274=string/config_ethernet_tcp_buffers\n01040275=string/config_extensionFallbackPackageName\n01040276=string/config_extensionFallbackServiceName\n01040277=string/config_factoryResetPackage\n01040278=string/config_fallbackCredentialManagerDialogComponent\n01040279=string/config_fingerprintFrrTargetComponent\n0104027a=string/config_foldedArea\n0104027b=string/config_forceVoiceInteractionServicePackage\n0104027c=string/config_fusedLocationProviderPackageName\n0104027d=string/config_geocoderProviderPackageName\n0104027e=string/config_geofenceProviderPackageName\n0104027f=string/config_globalAppSearchDataQuerierPackage\n01040280=string/config_gnssAssistanceProviderPackageName\n01040281=string/config_gnssLocationProviderPackageName\n01040282=string/config_hapticFeedbackCustomizationFile\n01040283=string/config_hdmiCecActiveSourceLostActivity\n01040284=string/config_hdmiCecSetMenuLanguageActivity\n01040285=string/config_headlineFontFamily\n01040286=string/config_headlineFontFamilyMedium\n01040287=string/config_headlineFontFeatureSettings\n01040288=string/config_healthConnectMigratorPackageName\n01040289=string/config_help_url_action_disabled_by_advanced_protection\n0104028a=string/config_iccHotswapPromptForRestartDialogComponent\n0104028b=string/config_icon_mask\n0104028c=string/config_inCallNotificationSound\n0104028d=string/config_incidentReportApproverPackage\n0104028e=string/config_inputEventCompatProcessorOverrideClassName\n0104028f=string/config_intrusionDetectionEventTransport\n01040290=string/config_isoImagePath\n01040291=string/config_keyguardComponent\n01040292=string/config_mainBuiltInDisplayCutout\n01040293=string/config_mainBuiltInDisplayCutoutRectApproximation\n01040294=string/config_mainDisplayShape\n01040295=string/config_managed_provisioning_package\n01040296=string/config_mediaProjectionPermissionDialogComponent\n01040297=string/config_misprovisionedBrandValue\n01040298=string/config_misprovisionedDeviceModel\n01040299=string/config_mms_user_agent\n0104029a=string/config_mms_user_agent_profile_url\n0104029b=string/config_mobile_hotspot_provision_app_no_ui\n0104029c=string/config_mobile_hotspot_provision_response\n0104029d=string/config_mt_sms_polling_text\n0104029e=string/config_networkLocationProviderPackageName\n0104029f=string/config_networkOverLimitComponent\n010402a0=string/config_notificationAccessConfirmationActivity\n010402a1=string/config_notificationHandlerPackage\n010402a2=string/config_oemCredentialManagerDialogComponent\n010402a3=string/config_oem_enabled_satellite_s2cell_file\n010402a4=string/config_oem_enabled_satellite_sos_handover_app\n010402a5=string/config_onDeviceIntelligenceModelLoadedBroadcastKey\n010402a6=string/config_onDeviceIntelligenceModelUnloadedBroadcastKey\n010402a7=string/config_overrideComponentUiPackage\n010402a8=string/config_packagedKeyboardName\n010402a9=string/config_pdp_reject_dialog_title\n010402aa=string/config_pdp_reject_multi_conn_to_same_pdn_not_allowed\n010402ab=string/config_pdp_reject_service_not_subscribed\n010402ac=string/config_pdp_reject_user_authentication_failed\n010402ad=string/config_persistentDataPackageName\n010402ae=string/config_platformVpnConfirmDialogComponent\n010402af=string/config_pluginsProviderJarPath\n010402b0=string/config_pointing_ui_class\n010402b1=string/config_pointing_ui_package\n010402b2=string/config_populationDensityProviderPackageName\n010402b3=string/config_powerSaveModeChangedListenerPackage\n010402b4=string/config_powerStatsThrottlePeriods\n010402b5=string/config_primaryLocationTimeZoneProviderPackageName\n010402b6=string/config_primaryShortPressTargetActivity\n010402b7=string/config_qualified_networks_service_class\n010402b8=string/config_qualified_networks_service_package\n010402b9=string/config_quickPickupSensorType\n010402ba=string/config_radio_access_family\n010402bb=string/config_rawContactsLocalAccountName\n010402bc=string/config_rawContactsLocalAccountType\n010402bd=string/config_rearDisplayPhysicalAddress\n010402be=string/config_recentsComponentName\n010402bf=string/config_retailDemoPackage\n010402c0=string/config_retailDemoPackageSignature\n010402c1=string/config_satellite_carrier_roaming_esos_provisioned_class\n010402c2=string/config_satellite_carrier_roaming_non_emergency_session_class\n010402c3=string/config_satellite_demo_mode_sos_intent_action\n010402c4=string/config_satellite_emergency_handover_intent_action\n010402c5=string/config_satellite_gateway_service_package\n010402c6=string/config_satellite_nidd_apn_name\n010402c7=string/config_satellite_service_package\n010402c8=string/config_satellite_sim_plmn_identifier\n010402c9=string/config_satellite_sim_spn_identifier\n010402ca=string/config_satellite_test_with_esp_replies_intent_action\n010402cb=string/config_screenshotAppClipsServiceComponent\n010402cc=string/config_screenshotErrorReceiverComponent\n010402cd=string/config_screenshotServiceComponent\n010402ce=string/config_searchKeyTargetActivity\n010402cf=string/config_secondaryBuiltInDisplayCutout\n010402d0=string/config_secondaryBuiltInDisplayCutoutRectApproximation\n010402d1=string/config_secondaryDisplayShape\n010402d2=string/config_secondaryHomePackage\n010402d3=string/config_secondaryLocationTimeZoneProviderPackageName\n010402d4=string/config_sensorStateChangedActivity\n010402d5=string/config_sensorUseStartedActivity\n010402d6=string/config_sensorUseStartedActivity_hwToggle\n010402d7=string/config_servicesExtensionPackage\n010402d8=string/config_sharedConnectivityServiceIntentAction\n010402d9=string/config_sharedConnectivityServicePackage\n010402da=string/config_signalAttributionPath\n010402db=string/config_slicePermissionComponent\n010402dc=string/config_somnambulatorComponent\n010402dd=string/config_supervisedUserCreationPackage\n010402de=string/config_systemGameService\n010402df=string/config_systemImageEditor\n010402e0=string/config_systemTelevisionRemoteService\n010402e1=string/config_systemUIServiceComponent\n010402e2=string/config_tcp_buffers\n010402e3=string/config_tvRemoteServicePackage\n010402e4=string/config_usbAccessoryUriActivity\n010402e5=string/config_usbConfirmActivity\n010402e6=string/config_usbContaminantActivity\n010402e7=string/config_usbPermissionActivity\n010402e8=string/config_usbResolverActivity\n010402e9=string/config_useragentprofile_url\n010402ea=string/config_vendorColorModesRestoreHint\n010402eb=string/config_wallpaperCropperPackage\n010402ec=string/config_wallpaperManagerServiceName\n010402ed=string/config_wearMediaControlsPackage\n010402ee=string/config_wearMediaSessionsPackage\n010402ef=string/config_wearRemoteIntentAction\n010402f0=string/config_wearServiceComponent\n010402f1=string/config_wearSysUiMainActivity\n010402f2=string/config_wearSysUiPackage\n010402f3=string/config_wearableAmbientContextEventArrayExtraKey\n010402f4=string/config_wearableAmbientContextPackageNameExtraKey\n010402f5=string/config_wifi_tether_enable\n010402f6=string/config_wimaxManagerClassname\n010402f7=string/config_wimaxNativeLibLocation\n010402f8=string/config_wimaxServiceClassname\n010402f9=string/config_wimaxServiceJarLocation\n010402fa=string/config_wimaxStateTrackerClassname\n010402fb=string/config_wlan_data_service_class\n010402fc=string/config_wlan_data_service_package\n010402fd=string/config_wlan_network_service_class\n010402fe=string/config_wlan_network_service_package\n010402ff=string/config_work_badge_path_24\n01040300=string/config_wwan_data_service_class\n01040301=string/config_wwan_data_service_package\n01040302=string/config_wwan_network_service_class\n01040303=string/config_wwan_network_service_package\n01040304=string/confirm_battery_saver\n01040305=string/connected_display_thermally_unavailable_notification_content\n01040306=string/connected_display_unavailable_notification_content\n01040307=string/connected_display_unavailable_notification_title\n01040308=string/console_running_notification_message\n01040309=string/console_running_notification_title\n0104030a=string/contentServiceSync\n0104030b=string/contentServiceSyncNotificationTitle\n0104030c=string/contentServiceTooManyDeletesNotificationDesc\n0104030d=string/content_description_collapsed\n0104030e=string/content_description_expanded\n0104030f=string/content_description_sliding_handle\n01040310=string/conversation_single_line_image_placeholder\n01040311=string/conversation_single_line_name_display\n01040312=string/conversation_title_fallback_group_chat\n01040313=string/conversation_title_fallback_one_to_one\n01040314=string/country_detector\n01040315=string/country_selection_title\n01040316=string/create_contact_using\n01040317=string/crossSimFormat_spn\n01040318=string/crossSimFormat_spn_cross_sim_calling\n01040319=string/csd_dose_reached_warning\n0104031a=string/csd_momentary_exposure_warning\n0104031b=string/data_saver_description\n0104031c=string/data_saver_enable_button\n0104031d=string/data_saver_enable_title\n0104031e=string/data_usage_limit_body\n0104031f=string/data_usage_limit_snoozed_body\n01040320=string/data_usage_mobile_limit_snoozed_title\n01040321=string/data_usage_mobile_limit_title\n01040322=string/data_usage_rapid_app_body\n01040323=string/data_usage_rapid_body\n01040324=string/data_usage_rapid_title\n01040325=string/data_usage_restricted_body\n01040326=string/data_usage_restricted_title\n01040327=string/data_usage_warning_body\n01040328=string/data_usage_warning_title\n01040329=string/data_usage_wifi_limit_snoozed_title\n0104032a=string/data_usage_wifi_limit_title\n0104032b=string/date_and_time\n0104032c=string/date_picker_day_of_week_typeface\n0104032d=string/date_picker_day_typeface\n0104032e=string/date_picker_decrement_day_button\n0104032f=string/date_picker_decrement_month_button\n01040330=string/date_picker_decrement_year_button\n01040331=string/date_picker_dialog_title\n01040332=string/date_picker_increment_day_button\n01040333=string/date_picker_increment_month_button\n01040334=string/date_picker_increment_year_button\n01040335=string/date_picker_mode\n01040336=string/date_picker_month_typeface\n01040337=string/date_picker_next_month_button\n01040338=string/date_picker_prev_month_button\n01040339=string/date_time\n0104033a=string/date_time_done\n0104033b=string/date_time_set\n0104033c=string/day\n0104033d=string/days\n0104033e=string/db_default_journal_mode\n0104033f=string/db_default_sync_mode\n01040340=string/db_wal_sync_mode\n01040341=string/decline\n01040342=string/decline_remote_bugreport_action\n01040343=string/default_audio_route_category_name\n01040344=string/default_audio_route_id\n01040345=string/default_audio_route_name\n01040346=string/default_audio_route_name_dock_speakers\n01040347=string/default_audio_route_name_external_device\n01040348=string/default_audio_route_name_headphones\n01040349=string/default_audio_route_name_usb\n0104034a=string/default_browser\n0104034b=string/default_card_name\n0104034c=string/default_notification_channel_label\n0104034d=string/default_sms_application\n0104034e=string/default_wallpaper_component\n0104034f=string/delete\n01040350=string/deleteText\n01040351=string/deleted_key\n01040352=string/demo_restarting_message\n01040353=string/demo_starting_message\n01040354=string/deny\n01040355=string/deprecated_abi_message\n01040356=string/deprecated_target_sdk_app_store\n01040357=string/deprecated_target_sdk_message\n01040358=string/description_target_unlock_tablet\n01040359=string/device_ownership_relinquished\n0104035a=string/device_policy_manager_service\n0104035b=string/device_state_notification_settings_button\n0104035c=string/device_state_notification_turn_off_button\n0104035d=string/device_storage_monitor_notification_channel\n0104035e=string/device_unlock_notification_name\n0104035f=string/dial_number_using\n01040360=string/disable_accessibility_shortcut\n01040361=string/dismiss_action\n01040362=string/display_manager_built_in_display_name\n01040363=string/display_manager_hdmi_display_name\n01040364=string/display_manager_overlay_display_name\n01040365=string/display_manager_overlay_display_secure_suffix\n01040366=string/display_manager_overlay_display_title\n01040367=string/display_rotation_camera_compat_toast_after_rotation\n01040368=string/display_rotation_camera_compat_toast_in_multi_window\n01040369=string/dlg_ok\n0104036a=string/done_accessibility_shortcut_menu_button\n0104036b=string/done_label\n0104036c=string/dream_accessibility_action_click\n0104036d=string/dream_preview_title\n0104036e=string/dump_heap_notification\n0104036f=string/dump_heap_notification_detail\n01040370=string/dump_heap_ready_notification\n01040371=string/dump_heap_ready_text\n01040372=string/dump_heap_system_text\n01040373=string/dump_heap_text\n01040374=string/dump_heap_title\n01040375=string/duration_days_medium\n01040376=string/duration_days_medium_future\n01040377=string/duration_days_medium_past\n01040378=string/duration_days_relative\n01040379=string/duration_days_relative_future\n0104037a=string/duration_days_shortest\n0104037b=string/duration_days_shortest_future\n0104037c=string/duration_days_shortest_past\n0104037d=string/duration_hours_medium\n0104037e=string/duration_hours_medium_future\n0104037f=string/duration_hours_medium_past\n01040380=string/duration_hours_relative\n01040381=string/duration_hours_relative_future\n01040382=string/duration_hours_shortest\n01040383=string/duration_hours_shortest_future\n01040384=string/duration_hours_shortest_past\n01040385=string/duration_minutes_medium\n01040386=string/duration_minutes_medium_future\n01040387=string/duration_minutes_medium_past\n01040388=string/duration_minutes_relative\n01040389=string/duration_minutes_relative_future\n0104038a=string/duration_minutes_shortest\n0104038b=string/duration_minutes_shortest_future\n0104038c=string/duration_minutes_shortest_past\n0104038d=string/duration_years_medium\n0104038e=string/duration_years_medium_future\n0104038f=string/duration_years_medium_past\n01040390=string/duration_years_relative\n01040391=string/duration_years_relative_future\n01040392=string/duration_years_shortest\n01040393=string/duration_years_shortest_future\n01040394=string/duration_years_shortest_past\n01040395=string/dynamic_mode_notification_channel_name\n01040396=string/dynamic_mode_notification_summary\n01040397=string/dynamic_mode_notification_summary_v2\n01040398=string/dynamic_mode_notification_title\n01040399=string/dynamic_mode_notification_title_v2\n0104039a=string/editTextMenuTitle\n0104039b=string/edit_accessibility_shortcut_menu_button\n0104039c=string/elapsed_time_short_format_h_mm_ss\n0104039d=string/elapsed_time_short_format_mm_ss\n0104039e=string/emailTypeCustom\n0104039f=string/emailTypeHome\n010403a0=string/emailTypeMobile\n010403a1=string/emailTypeOther\n010403a2=string/emailTypeWork\n010403a3=string/emergency_call_dialog_number_for_display\n010403a4=string/emergency_calling_do_not_show_again\n010403a5=string/emergency_calls_only\n010403a6=string/enablePin\n010403a7=string/enable_explore_by_touch_warning_message\n010403a8=string/enable_explore_by_touch_warning_title\n010403a9=string/error_handwriting_unsupported\n010403aa=string/error_handwriting_unsupported_password\n010403ab=string/error_message_change_not_allowed\n010403ac=string/error_message_title\n010403ad=string/etws_primary_default_message_earthquake\n010403ae=string/etws_primary_default_message_earthquake_and_tsunami\n010403af=string/etws_primary_default_message_others\n010403b0=string/etws_primary_default_message_test\n010403b1=string/etws_primary_default_message_tsunami\n010403b2=string/eventTypeAnniversary\n010403b3=string/eventTypeBirthday\n010403b4=string/eventTypeCustom\n010403b5=string/eventTypeOther\n010403b6=string/expand_action_accessibility\n010403b7=string/expand_button_content_description_collapsed\n010403b8=string/expand_button_content_description_expanded\n010403b9=string/expires_on\n010403ba=string/ext_media_badremoval_notification_message\n010403bb=string/ext_media_badremoval_notification_title\n010403bc=string/ext_media_browse_action\n010403bd=string/ext_media_checking_notification_message\n010403be=string/ext_media_checking_notification_title\n010403bf=string/ext_media_init_action\n010403c0=string/ext_media_missing_message\n010403c1=string/ext_media_missing_title\n010403c2=string/ext_media_move_failure_message\n010403c3=string/ext_media_move_failure_title\n010403c4=string/ext_media_move_specific_title\n010403c5=string/ext_media_move_success_message\n010403c6=string/ext_media_move_success_title\n010403c7=string/ext_media_move_title\n010403c8=string/ext_media_new_notification_message\n010403c9=string/ext_media_new_notification_title\n010403ca=string/ext_media_nomedia_notification_message\n010403cb=string/ext_media_nomedia_notification_title\n010403cc=string/ext_media_ready_notification_message\n010403cd=string/ext_media_seamless_action\n010403ce=string/ext_media_status_bad_removal\n010403cf=string/ext_media_status_checking\n010403d0=string/ext_media_status_ejecting\n010403d1=string/ext_media_status_formatting\n010403d2=string/ext_media_status_missing\n010403d3=string/ext_media_status_mounted\n010403d4=string/ext_media_status_mounted_ro\n010403d5=string/ext_media_status_removed\n010403d6=string/ext_media_status_unmountable\n010403d7=string/ext_media_status_unmounted\n010403d8=string/ext_media_status_unsupported\n010403d9=string/ext_media_unmount_action\n010403da=string/ext_media_unmountable_notification_message\n010403db=string/ext_media_unmountable_notification_title\n010403dc=string/ext_media_unmounting_notification_message\n010403dd=string/ext_media_unmounting_notification_title\n010403de=string/ext_media_unsupported_notification_message\n010403df=string/ext_media_unsupported_notification_title\n010403e0=string/extract_edit_menu_button\n010403e1=string/face_acquired_dark_glasses_detected\n010403e2=string/face_acquired_dark_glasses_detected_alt\n010403e3=string/face_acquired_insufficient\n010403e4=string/face_acquired_mouth_covering_detected\n010403e5=string/face_acquired_mouth_covering_detected_alt\n010403e6=string/face_acquired_not_detected\n010403e7=string/face_acquired_obscured\n010403e8=string/face_acquired_pan_too_extreme\n010403e9=string/face_acquired_poor_gaze\n010403ea=string/face_acquired_recalibrate\n010403eb=string/face_acquired_recalibrate_alt\n010403ec=string/face_acquired_roll_too_extreme\n010403ed=string/face_acquired_sensor_dirty\n010403ee=string/face_acquired_tilt_too_extreme\n010403ef=string/face_acquired_too_bright\n010403f0=string/face_acquired_too_close\n010403f1=string/face_acquired_too_dark\n010403f2=string/face_acquired_too_different\n010403f3=string/face_acquired_too_far\n010403f4=string/face_acquired_too_high\n010403f5=string/face_acquired_too_left\n010403f6=string/face_acquired_too_low\n010403f7=string/face_acquired_too_much_motion\n010403f8=string/face_acquired_too_right\n010403f9=string/face_acquired_too_similar\n010403fa=string/face_app_setting_name\n010403fb=string/face_authenticated_confirmation_required\n010403fc=string/face_authenticated_no_confirmation_required\n010403fd=string/face_dangling_notification_msg\n010403fe=string/face_dangling_notification_title\n010403ff=string/face_dialog_default_subtitle\n01040400=string/face_error_canceled\n01040401=string/face_error_hw_not_available\n01040402=string/face_error_hw_not_present\n01040403=string/face_error_lockout\n01040404=string/face_error_lockout_permanent\n01040405=string/face_error_lockout_screen_lock\n01040406=string/face_error_no_space\n01040407=string/face_error_not_enrolled\n01040408=string/face_error_security_update_required\n01040409=string/face_error_timeout\n0104040a=string/face_error_unable_to_process\n0104040b=string/face_error_user_canceled\n0104040c=string/face_error_vendor_unknown\n0104040d=string/face_icon_content_description\n0104040e=string/face_name_template\n0104040f=string/face_or_screen_lock_app_setting_name\n01040410=string/face_or_screen_lock_dialog_default_subtitle\n01040411=string/face_recalibrate_notification_content\n01040412=string/face_recalibrate_notification_name\n01040413=string/face_recalibrate_notification_title\n01040414=string/face_sensor_privacy_enabled\n01040415=string/faceunlock_multiple_failures\n01040416=string/factory_reset_message\n01040417=string/factory_reset_warning\n01040418=string/factorytest_failed\n01040419=string/factorytest_no_action\n0104041a=string/factorytest_not_system\n0104041b=string/factorytest_reboot\n0104041c=string/failed_to_copy_to_clipboard\n0104041d=string/fallback_wallpaper_component\n0104041e=string/fast_scroll_alphabet\n0104041f=string/fast_scroll_numeric_alphabet\n01040420=string/fcComplete\n01040421=string/fcError\n01040422=string/fileSizeSuffix\n01040423=string/file_count\n01040424=string/find\n01040425=string/find_next\n01040426=string/find_on_page\n01040427=string/find_previous\n01040428=string/fingerprint_acquired_already_enrolled\n01040429=string/fingerprint_acquired_imager_dirty\n0104042a=string/fingerprint_acquired_imager_dirty_alt\n0104042b=string/fingerprint_acquired_immobile\n0104042c=string/fingerprint_acquired_insufficient\n0104042d=string/fingerprint_acquired_partial\n0104042e=string/fingerprint_acquired_power_press\n0104042f=string/fingerprint_acquired_too_bright\n01040430=string/fingerprint_acquired_too_fast\n01040431=string/fingerprint_acquired_too_slow\n01040432=string/fingerprint_acquired_try_adjusting\n01040433=string/fingerprint_app_setting_name\n01040434=string/fingerprint_authenticated\n01040435=string/fingerprint_dangling_notification_msg_1\n01040436=string/fingerprint_dangling_notification_msg_2\n01040437=string/fingerprint_dangling_notification_msg_all_deleted_1\n01040438=string/fingerprint_dangling_notification_msg_all_deleted_2\n01040439=string/fingerprint_dangling_notification_title\n0104043a=string/fingerprint_dialog_default_subtitle\n0104043b=string/fingerprint_dialog_use_fingerprint_instead\n0104043c=string/fingerprint_error_bad_calibration\n0104043d=string/fingerprint_error_canceled\n0104043e=string/fingerprint_error_hw_not_available\n0104043f=string/fingerprint_error_hw_not_present\n01040440=string/fingerprint_error_lockout\n01040441=string/fingerprint_error_lockout_permanent\n01040442=string/fingerprint_error_no_fingerprints\n01040443=string/fingerprint_error_no_space\n01040444=string/fingerprint_error_not_match\n01040445=string/fingerprint_error_power_pressed\n01040446=string/fingerprint_error_security_update_required\n01040447=string/fingerprint_error_timeout\n01040448=string/fingerprint_error_unable_to_process\n01040449=string/fingerprint_error_user_canceled\n0104044a=string/fingerprint_error_vendor_unknown\n0104044b=string/fingerprint_frr_notification_msg\n0104044c=string/fingerprint_frr_notification_title\n0104044d=string/fingerprint_loe_notification_msg\n0104044e=string/fingerprint_name_template\n0104044f=string/fingerprint_or_screen_lock_app_setting_name\n01040450=string/fingerprint_or_screen_lock_dialog_default_subtitle\n01040451=string/fingerprint_recalibrate_notification_content\n01040452=string/fingerprint_recalibrate_notification_name\n01040453=string/fingerprint_recalibrate_notification_title\n01040454=string/fingerprint_udfps_error_not_match\n01040455=string/fingerprints\n01040456=string/floating_toolbar_close_overflow_description\n01040457=string/floating_toolbar_open_overflow_description\n01040458=string/font_family_body_1_material\n01040459=string/font_family_body_2_material\n0104045a=string/font_family_button_material\n0104045b=string/font_family_caption_material\n0104045c=string/font_family_display_1_material\n0104045d=string/font_family_display_2_material\n0104045e=string/font_family_display_3_material\n0104045f=string/font_family_display_4_material\n01040460=string/font_family_headline_material\n01040461=string/font_family_menu_material\n01040462=string/font_family_subhead_material\n01040463=string/font_family_title_material\n01040464=string/force_close\n01040465=string/foreground_service_app_in_background\n01040466=string/foreground_service_apps_in_background\n01040467=string/foreground_service_multiple_separator\n01040468=string/foreground_service_tap_for_details\n01040469=string/forward_intent_to_owner\n0104046a=string/forward_intent_to_work\n0104046b=string/fp_power_button_bp_message\n0104046c=string/fp_power_button_bp_negative_button\n0104046d=string/fp_power_button_bp_positive_button\n0104046e=string/fp_power_button_bp_title\n0104046f=string/fp_power_button_enrollment_button_text\n01040470=string/fp_power_button_enrollment_message\n01040471=string/fp_power_button_enrollment_title\n01040472=string/gadget_host_error_inflating\n01040473=string/geofencing_service\n01040474=string/global_action_assist\n01040475=string/global_action_bug_report\n01040476=string/global_action_emergency\n01040477=string/global_action_lock\n01040478=string/global_action_lockdown\n01040479=string/global_action_logout\n0104047a=string/global_action_power_off\n0104047b=string/global_action_power_options\n0104047c=string/global_action_restart\n0104047d=string/global_action_screenshot\n0104047e=string/global_action_settings\n0104047f=string/global_action_silent_mode_off_status\n01040480=string/global_action_silent_mode_on_status\n01040481=string/global_action_standby\n01040482=string/global_action_toggle_silent_mode\n01040483=string/global_action_voice_assist\n01040484=string/global_actions\n01040485=string/global_actions_airplane_mode_off_status\n01040486=string/global_actions_airplane_mode_on_status\n01040487=string/global_actions_toggle_airplane_mode\n01040488=string/gnss_service\n01040489=string/gnss_time_update_service\n0104048a=string/gpsNotifMessage\n0104048b=string/gpsNotifTicker\n0104048c=string/gpsNotifTitle\n0104048d=string/gpsVerifNo\n0104048e=string/gpsVerifYes\n0104048f=string/grant_credentials_permission_message_footer\n01040490=string/grant_credentials_permission_message_header\n01040491=string/grant_permissions_header_text\n01040492=string/granularity_label_character\n01040493=string/granularity_label_line\n01040494=string/granularity_label_link\n01040495=string/granularity_label_word\n01040496=string/gsm_alphabet_default_charset\n01040497=string/guest_name\n01040498=string/hardware\n01040499=string/harmful_app_warning_open_anyway\n0104049a=string/harmful_app_warning_title\n0104049b=string/harmful_app_warning_uninstall\n0104049c=string/hearing_aids_feature_name\n0104049d=string/hearing_device_notification_settings_button\n0104049e=string/hearing_device_notification_switch_button\n0104049f=string/hearing_device_status_active\n010404a0=string/hearing_device_status_connected\n010404a1=string/hearing_device_status_disconnected\n010404a2=string/hearing_device_status_loading\n010404a3=string/hearing_device_switch_hearing_mic_notification_text\n010404a4=string/hearing_device_switch_hearing_mic_notification_title\n010404a5=string/hearing_device_switch_phone_mic_notification_text\n010404a6=string/hearing_device_switch_phone_mic_notification_title\n010404a7=string/heavy_weight_notification\n010404a8=string/heavy_weight_notification_detail\n010404a9=string/heavy_weight_switcher_text\n010404aa=string/heavy_weight_switcher_title\n010404ab=string/hour\n010404ac=string/hour_picker_description\n010404ad=string/hours\n010404ae=string/httpError\n010404af=string/httpErrorAuth\n010404b0=string/httpErrorConnect\n010404b1=string/httpErrorFailedSslHandshake\n010404b2=string/httpErrorFile\n010404b3=string/httpErrorFileNotFound\n010404b4=string/httpErrorIO\n010404b5=string/httpErrorLookup\n010404b6=string/httpErrorOk\n010404b7=string/httpErrorProxyAuth\n010404b8=string/httpErrorRedirectLoop\n010404b9=string/httpErrorTimeout\n010404ba=string/httpErrorTooManyRequests\n010404bb=string/httpErrorUnsupportedAuthScheme\n010404bc=string/icu_abbrev_wday_month_day_no_year\n010404bd=string/identity_check_settings_action\n010404be=string/identity_check_settings_package_name\n010404bf=string/imProtocolAim\n010404c0=string/imProtocolCustom\n010404c1=string/imProtocolGoogleTalk\n010404c2=string/imProtocolIcq\n010404c3=string/imProtocolJabber\n010404c4=string/imProtocolMsn\n010404c5=string/imProtocolNetMeeting\n010404c6=string/imProtocolQq\n010404c7=string/imProtocolSkype\n010404c8=string/imProtocolYahoo\n010404c9=string/imTypeCustom\n010404ca=string/imTypeHome\n010404cb=string/imTypeOther\n010404cc=string/imTypeWork\n010404cd=string/image_wallpaper_component\n010404ce=string/ime_action_default\n010404cf=string/ime_action_done\n010404d0=string/ime_action_go\n010404d1=string/ime_action_next\n010404d2=string/ime_action_previous\n010404d3=string/ime_action_search\n010404d4=string/ime_action_send\n010404d5=string/imei\n010404d6=string/immersive_cling_description\n010404d7=string/immersive_cling_positive\n010404d8=string/immersive_cling_title\n010404d9=string/importance_from_person\n010404da=string/importance_from_user\n010404db=string/in_progress\n010404dc=string/indeterminate_progress_01\n010404dd=string/indeterminate_progress_02\n010404de=string/indeterminate_progress_03\n010404df=string/indeterminate_progress_04\n010404e0=string/indeterminate_progress_05\n010404e1=string/indeterminate_progress_06\n010404e2=string/indeterminate_progress_07\n010404e3=string/indeterminate_progress_08\n010404e4=string/indeterminate_progress_09\n010404e5=string/indeterminate_progress_10\n010404e6=string/indeterminate_progress_11\n010404e7=string/indeterminate_progress_12\n010404e8=string/indeterminate_progress_13\n010404e9=string/indeterminate_progress_14\n010404ea=string/indeterminate_progress_15\n010404eb=string/indeterminate_progress_16\n010404ec=string/indeterminate_progress_17\n010404ed=string/indeterminate_progress_18\n010404ee=string/indeterminate_progress_19\n010404ef=string/indeterminate_progress_20\n010404f0=string/indeterminate_progress_21\n010404f1=string/indeterminate_progress_22\n010404f2=string/indeterminate_progress_23\n010404f3=string/indeterminate_progress_24\n010404f4=string/indeterminate_progress_25\n010404f5=string/indeterminate_progress_26\n010404f6=string/indeterminate_progress_27\n010404f7=string/indeterminate_progress_28\n010404f8=string/indeterminate_progress_29\n010404f9=string/indeterminate_progress_30\n010404fa=string/indeterminate_progress_31\n010404fb=string/indeterminate_progress_32\n010404fc=string/indeterminate_progress_33\n010404fd=string/indeterminate_progress_34\n010404fe=string/indeterminate_progress_35\n010404ff=string/indeterminate_progress_36\n01040500=string/indeterminate_progress_37\n01040501=string/indeterminate_progress_38\n01040502=string/indeterminate_progress_39\n01040503=string/indeterminate_progress_40\n01040504=string/indeterminate_progress_41\n01040505=string/indeterminate_progress_42\n01040506=string/indeterminate_progress_43\n01040507=string/indeterminate_progress_44\n01040508=string/indeterminate_progress_45\n01040509=string/indeterminate_progress_46\n0104050a=string/indeterminate_progress_47\n0104050b=string/indeterminate_progress_48\n0104050c=string/indeterminate_progress_49\n0104050d=string/indeterminate_progress_50\n0104050e=string/indeterminate_progress_51\n0104050f=string/indeterminate_progress_52\n01040510=string/indeterminate_progress_53\n01040511=string/indeterminate_progress_54\n01040512=string/indeterminate_progress_55\n01040513=string/indeterminate_progress_56\n01040514=string/indeterminate_progress_57\n01040515=string/indeterminate_progress_58\n01040516=string/indeterminate_progress_59\n01040517=string/indeterminate_progress_60\n01040518=string/indeterminate_progress_background\n01040519=string/inputMethod\n0104051a=string/input_method_binding_label\n0104051b=string/input_method_ime_switch_button_desc\n0104051c=string/input_method_ime_switch_long_click_action_desc\n0104051d=string/input_method_language_settings\n0104051e=string/input_method_nav_back_button_desc\n0104051f=string/input_method_switcher_settings_button\n01040520=string/install_carrier_app_notification_button\n01040521=string/install_carrier_app_notification_text\n01040522=string/install_carrier_app_notification_text_app_name\n01040523=string/install_carrier_app_notification_title\n01040524=string/invalidPin\n01040525=string/invalidPuk\n01040526=string/issued_by\n01040527=string/issued_on\n01040528=string/issued_to\n01040529=string/js_dialog_before_unload\n0104052a=string/js_dialog_before_unload_negative_button\n0104052b=string/js_dialog_before_unload_positive_button\n0104052c=string/js_dialog_before_unload_title\n0104052d=string/js_dialog_title\n0104052e=string/js_dialog_title_default\n0104052f=string/key_warning_state\n01040530=string/keyboard_layout_notification_more_than_three_selected_message\n01040531=string/keyboard_layout_notification_multiple_selected_message\n01040532=string/keyboard_layout_notification_multiple_selected_title\n01040533=string/keyboard_layout_notification_one_selected_message\n01040534=string/keyboard_layout_notification_selected_title\n01040535=string/keyboard_layout_notification_three_selected_message\n01040536=string/keyboard_layout_notification_two_selected_message\n01040537=string/keyboard_shortcut_group_applications\n01040538=string/keyboard_shortcut_group_applications_browser\n01040539=string/keyboard_shortcut_group_applications_calculator\n0104053a=string/keyboard_shortcut_group_applications_calendar\n0104053b=string/keyboard_shortcut_group_applications_contacts\n0104053c=string/keyboard_shortcut_group_applications_email\n0104053d=string/keyboard_shortcut_group_applications_maps\n0104053e=string/keyboard_shortcut_group_applications_music\n0104053f=string/keyboard_shortcut_group_applications_sms\n01040540=string/keyboardview_keycode_alt\n01040541=string/keyboardview_keycode_cancel\n01040542=string/keyboardview_keycode_delete\n01040543=string/keyboardview_keycode_done\n01040544=string/keyboardview_keycode_enter\n01040545=string/keyboardview_keycode_mode_change\n01040546=string/keyboardview_keycode_shift\n01040547=string/keygaurd_accessibility_media_controls\n01040548=string/keyguard_accessibility_add_widget\n01040549=string/keyguard_accessibility_camera\n0104054a=string/keyguard_accessibility_expand_lock_area\n0104054b=string/keyguard_accessibility_face_unlock\n0104054c=string/keyguard_accessibility_password_unlock\n0104054d=string/keyguard_accessibility_pattern_area\n0104054e=string/keyguard_accessibility_pattern_unlock\n0104054f=string/keyguard_accessibility_pin_unlock\n01040550=string/keyguard_accessibility_sim_pin_unlock\n01040551=string/keyguard_accessibility_sim_puk_unlock\n01040552=string/keyguard_accessibility_slide_area\n01040553=string/keyguard_accessibility_slide_unlock\n01040554=string/keyguard_accessibility_status\n01040555=string/keyguard_accessibility_unlock_area_collapsed\n01040556=string/keyguard_accessibility_unlock_area_expanded\n01040557=string/keyguard_accessibility_user_selector\n01040558=string/keyguard_accessibility_widget\n01040559=string/keyguard_accessibility_widget_changed\n0104055a=string/keyguard_accessibility_widget_deleted\n0104055b=string/keyguard_accessibility_widget_empty_slot\n0104055c=string/keyguard_accessibility_widget_reorder_end\n0104055d=string/keyguard_accessibility_widget_reorder_start\n0104055e=string/keyguard_label_text\n0104055f=string/keyguard_password_enter_password_code\n01040560=string/keyguard_password_enter_pin_code\n01040561=string/keyguard_password_enter_pin_password_code\n01040562=string/keyguard_password_enter_pin_prompt\n01040563=string/keyguard_password_enter_puk_code\n01040564=string/keyguard_password_enter_puk_prompt\n01040565=string/keyguard_password_entry_touch_hint\n01040566=string/keyguard_password_wrong_pin_code\n01040567=string/kg_enter_confirm_pin_hint\n01040568=string/kg_failed_attempts_almost_at_login\n01040569=string/kg_failed_attempts_almost_at_wipe\n0104056a=string/kg_failed_attempts_now_wiping\n0104056b=string/kg_forgot_pattern_button_text\n0104056c=string/kg_invalid_confirm_pin_hint\n0104056d=string/kg_invalid_puk\n0104056e=string/kg_invalid_sim_pin_hint\n0104056f=string/kg_invalid_sim_puk_hint\n01040570=string/kg_login_account_recovery_hint\n01040571=string/kg_login_checking_password\n01040572=string/kg_login_instructions\n01040573=string/kg_login_invalid_input\n01040574=string/kg_login_password_hint\n01040575=string/kg_login_submit_button\n01040576=string/kg_login_too_many_attempts\n01040577=string/kg_login_username_hint\n01040578=string/kg_password_instructions\n01040579=string/kg_password_wrong_pin_code\n0104057a=string/kg_pattern_instructions\n0104057b=string/kg_pin_instructions\n0104057c=string/kg_puk_enter_pin_hint\n0104057d=string/kg_puk_enter_puk_hint\n0104057e=string/kg_reordering_delete_drop_target_text\n0104057f=string/kg_sim_pin_instructions\n01040580=string/kg_sim_unlock_progress_dialog_message\n01040581=string/kg_text_message_separator\n01040582=string/kg_too_many_failed_password_attempts_dialog_message\n01040583=string/kg_too_many_failed_pattern_attempts_dialog_message\n01040584=string/kg_too_many_failed_pin_attempts_dialog_message\n01040585=string/kg_wrong_password\n01040586=string/kg_wrong_pattern\n01040587=string/kg_wrong_pin\n01040588=string/language_picker_regions_section_suggested\n01040589=string/language_picker_section_all\n0104058a=string/language_picker_section_suggested\n0104058b=string/language_picker_section_suggested_bilingual\n0104058c=string/language_selection_title\n0104058d=string/last_month\n0104058e=string/last_num_days\n0104058f=string/launchBrowserDefault\n01040590=string/launch_warning_original\n01040591=string/launch_warning_replace\n01040592=string/launch_warning_title\n01040593=string/leave_accessibility_shortcut_on\n01040594=string/loading\n01040595=string/locale_replacement\n01040596=string/locale_search_menu\n01040597=string/location_changed_notification_text\n01040598=string/location_changed_notification_title\n01040599=string/location_service\n0104059a=string/lock_pattern_view_aspect\n0104059b=string/lock_to_app_unlock_password\n0104059c=string/lock_to_app_unlock_pattern\n0104059d=string/lock_to_app_unlock_pin\n0104059e=string/lockscreen_access_pattern_area\n0104059f=string/lockscreen_access_pattern_cell_added\n010405a0=string/lockscreen_access_pattern_cell_added_verbose\n010405a1=string/lockscreen_access_pattern_cleared\n010405a2=string/lockscreen_access_pattern_detected\n010405a3=string/lockscreen_access_pattern_start\n010405a4=string/lockscreen_carrier_default\n010405a5=string/lockscreen_emergency_call\n010405a6=string/lockscreen_failed_attempts_almost_at_wipe\n010405a7=string/lockscreen_failed_attempts_almost_glogin\n010405a8=string/lockscreen_failed_attempts_now_wiping\n010405a9=string/lockscreen_forgot_pattern_button_text\n010405aa=string/lockscreen_glogin_account_recovery_hint\n010405ab=string/lockscreen_glogin_checking_password\n010405ac=string/lockscreen_glogin_forgot_pattern\n010405ad=string/lockscreen_glogin_instructions\n010405ae=string/lockscreen_glogin_invalid_input\n010405af=string/lockscreen_glogin_password_hint\n010405b0=string/lockscreen_glogin_submit_button\n010405b1=string/lockscreen_glogin_too_many_attempts\n010405b2=string/lockscreen_glogin_username_hint\n010405b3=string/lockscreen_instructions_when_pattern_disabled\n010405b4=string/lockscreen_instructions_when_pattern_enabled\n010405b5=string/lockscreen_missing_sim_instructions\n010405b6=string/lockscreen_missing_sim_instructions_long\n010405b7=string/lockscreen_missing_sim_message\n010405b8=string/lockscreen_missing_sim_message_short\n010405b9=string/lockscreen_network_locked_message\n010405ba=string/lockscreen_password_wrong\n010405bb=string/lockscreen_pattern_correct\n010405bc=string/lockscreen_pattern_instructions\n010405bd=string/lockscreen_pattern_wrong\n010405be=string/lockscreen_permanent_disabled_sim_instructions\n010405bf=string/lockscreen_permanent_disabled_sim_message_short\n010405c0=string/lockscreen_return_to_call\n010405c1=string/lockscreen_screen_locked\n010405c2=string/lockscreen_sim_locked_message\n010405c3=string/lockscreen_sim_puk_locked_instructions\n010405c4=string/lockscreen_sim_puk_locked_message\n010405c5=string/lockscreen_sim_unlock_progress_dialog_message\n010405c6=string/lockscreen_sound_off_label\n010405c7=string/lockscreen_sound_on_label\n010405c8=string/lockscreen_storage_locked\n010405c9=string/lockscreen_too_many_failed_attempts_countdown\n010405ca=string/lockscreen_too_many_failed_attempts_dialog_message\n010405cb=string/lockscreen_too_many_failed_password_attempts_dialog_message\n010405cc=string/lockscreen_too_many_failed_pin_attempts_dialog_message\n010405cd=string/lockscreen_transport_ffw_description\n010405ce=string/lockscreen_transport_next_description\n010405cf=string/lockscreen_transport_pause_description\n010405d0=string/lockscreen_transport_play_description\n010405d1=string/lockscreen_transport_prev_description\n010405d2=string/lockscreen_transport_rew_description\n010405d3=string/lockscreen_transport_stop_description\n010405d4=string/lockscreen_unlock_label\n010405d5=string/low_internal_storage_view_text\n010405d6=string/low_internal_storage_view_text_no_boot\n010405d7=string/low_internal_storage_view_title\n010405d8=string/low_memory\n010405d9=string/managed_profile_app_label\n010405da=string/managed_profile_label\n010405db=string/managed_profile_label_badge\n010405dc=string/managed_profile_label_badge_2\n010405dd=string/managed_profile_label_badge_3\n010405de=string/matches_found\n010405df=string/maximize_button_text\n010405e0=string/me\n010405e1=string/media_route_button_content_description\n010405e2=string/media_route_chooser_extended_settings\n010405e3=string/media_route_chooser_searching\n010405e4=string/media_route_chooser_title\n010405e5=string/media_route_chooser_title_for_remote_display\n010405e6=string/media_route_controller_disconnect\n010405e7=string/media_route_status_available\n010405e8=string/media_route_status_connecting\n010405e9=string/media_route_status_in_use\n010405ea=string/media_route_status_not_available\n010405eb=string/media_route_status_scanning\n010405ec=string/mediasize_chinese_om_dai_pa_kai\n010405ed=string/mediasize_chinese_om_jurro_ku_kai\n010405ee=string/mediasize_chinese_om_pa_kai\n010405ef=string/mediasize_chinese_prc_1\n010405f0=string/mediasize_chinese_prc_10\n010405f1=string/mediasize_chinese_prc_16k\n010405f2=string/mediasize_chinese_prc_2\n010405f3=string/mediasize_chinese_prc_3\n010405f4=string/mediasize_chinese_prc_4\n010405f5=string/mediasize_chinese_prc_5\n010405f6=string/mediasize_chinese_prc_6\n010405f7=string/mediasize_chinese_prc_7\n010405f8=string/mediasize_chinese_prc_8\n010405f9=string/mediasize_chinese_prc_9\n010405fa=string/mediasize_chinese_roc_16k\n010405fb=string/mediasize_chinese_roc_8k\n010405fc=string/mediasize_iso_a0\n010405fd=string/mediasize_iso_a1\n010405fe=string/mediasize_iso_a10\n010405ff=string/mediasize_iso_a2\n01040600=string/mediasize_iso_a3\n01040601=string/mediasize_iso_a4\n01040602=string/mediasize_iso_a5\n01040603=string/mediasize_iso_a6\n01040604=string/mediasize_iso_a7\n01040605=string/mediasize_iso_a8\n01040606=string/mediasize_iso_a9\n01040607=string/mediasize_iso_b0\n01040608=string/mediasize_iso_b1\n01040609=string/mediasize_iso_b10\n0104060a=string/mediasize_iso_b2\n0104060b=string/mediasize_iso_b3\n0104060c=string/mediasize_iso_b4\n0104060d=string/mediasize_iso_b5\n0104060e=string/mediasize_iso_b6\n0104060f=string/mediasize_iso_b7\n01040610=string/mediasize_iso_b8\n01040611=string/mediasize_iso_b9\n01040612=string/mediasize_iso_c0\n01040613=string/mediasize_iso_c1\n01040614=string/mediasize_iso_c10\n01040615=string/mediasize_iso_c2\n01040616=string/mediasize_iso_c3\n01040617=string/mediasize_iso_c4\n01040618=string/mediasize_iso_c5\n01040619=string/mediasize_iso_c6\n0104061a=string/mediasize_iso_c7\n0104061b=string/mediasize_iso_c8\n0104061c=string/mediasize_iso_c9\n0104061d=string/mediasize_japanese_chou2\n0104061e=string/mediasize_japanese_chou3\n0104061f=string/mediasize_japanese_chou4\n01040620=string/mediasize_japanese_hagaki\n01040621=string/mediasize_japanese_jis_b0\n01040622=string/mediasize_japanese_jis_b1\n01040623=string/mediasize_japanese_jis_b10\n01040624=string/mediasize_japanese_jis_b2\n01040625=string/mediasize_japanese_jis_b3\n01040626=string/mediasize_japanese_jis_b4\n01040627=string/mediasize_japanese_jis_b5\n01040628=string/mediasize_japanese_jis_b6\n01040629=string/mediasize_japanese_jis_b7\n0104062a=string/mediasize_japanese_jis_b8\n0104062b=string/mediasize_japanese_jis_b9\n0104062c=string/mediasize_japanese_jis_exec\n0104062d=string/mediasize_japanese_kahu\n0104062e=string/mediasize_japanese_kaku2\n0104062f=string/mediasize_japanese_l\n01040630=string/mediasize_japanese_oufuku\n01040631=string/mediasize_japanese_you4\n01040632=string/mediasize_na_ansi_c\n01040633=string/mediasize_na_ansi_d\n01040634=string/mediasize_na_ansi_e\n01040635=string/mediasize_na_ansi_f\n01040636=string/mediasize_na_arch_a\n01040637=string/mediasize_na_arch_b\n01040638=string/mediasize_na_arch_c\n01040639=string/mediasize_na_arch_d\n0104063a=string/mediasize_na_arch_e\n0104063b=string/mediasize_na_arch_e1\n0104063c=string/mediasize_na_foolscap\n0104063d=string/mediasize_na_gvrnmt_letter\n0104063e=string/mediasize_na_index_3x5\n0104063f=string/mediasize_na_index_4x6\n01040640=string/mediasize_na_index_5x8\n01040641=string/mediasize_na_junior_legal\n01040642=string/mediasize_na_ledger\n01040643=string/mediasize_na_legal\n01040644=string/mediasize_na_letter\n01040645=string/mediasize_na_monarch\n01040646=string/mediasize_na_quarto\n01040647=string/mediasize_na_super_b\n01040648=string/mediasize_na_tabloid\n01040649=string/mediasize_unknown_landscape\n0104064a=string/mediasize_unknown_portrait\n0104064b=string/meid\n0104064c=string/menu_alt_shortcut_label\n0104064d=string/menu_ctrl_shortcut_label\n0104064e=string/menu_delete_shortcut_label\n0104064f=string/menu_enter_shortcut_label\n01040650=string/menu_function_shortcut_label\n01040651=string/menu_meta_shortcut_label\n01040652=string/menu_shift_shortcut_label\n01040653=string/menu_space_shortcut_label\n01040654=string/menu_sym_shortcut_label\n01040655=string/mic_access_off_toast\n01040656=string/mic_access_on_toast\n01040657=string/midnight\n01040658=string/mime_type_apk\n01040659=string/mime_type_audio\n0104065a=string/mime_type_audio_ext\n0104065b=string/mime_type_compressed\n0104065c=string/mime_type_compressed_ext\n0104065d=string/mime_type_document\n0104065e=string/mime_type_document_ext\n0104065f=string/mime_type_folder\n01040660=string/mime_type_generic\n01040661=string/mime_type_generic_ext\n01040662=string/mime_type_image\n01040663=string/mime_type_image_ext\n01040664=string/mime_type_presentation\n01040665=string/mime_type_presentation_ext\n01040666=string/mime_type_spreadsheet\n01040667=string/mime_type_spreadsheet_ext\n01040668=string/mime_type_video\n01040669=string/mime_type_video_ext\n0104066a=string/miniresolver_call\n0104066b=string/miniresolver_call_in_work\n0104066c=string/miniresolver_call_information\n0104066d=string/miniresolver_open_in_personal\n0104066e=string/miniresolver_open_in_work\n0104066f=string/miniresolver_open_work\n01040670=string/miniresolver_private_space_messages_information\n01040671=string/miniresolver_private_space_phone_information\n01040672=string/miniresolver_sms_information\n01040673=string/miniresolver_switch\n01040674=string/miniresolver_switch_to_work\n01040675=string/miniresolver_use_personal_browser\n01040676=string/miniresolver_use_work_browser\n01040677=string/minute\n01040678=string/minute_picker_description\n01040679=string/minutes\n0104067a=string/mismatchPin\n0104067b=string/mmcc_authentication_reject\n0104067c=string/mmcc_authentication_reject_msim_template\n0104067d=string/mmcc_illegal_me\n0104067e=string/mmcc_illegal_me_msim_template\n0104067f=string/mmcc_illegal_ms\n01040680=string/mmcc_illegal_ms_msim_template\n01040681=string/mmcc_imsi_unknown_in_hlr\n01040682=string/mmcc_imsi_unknown_in_hlr_msim_template\n01040683=string/mmiComplete\n01040684=string/mmiError\n01040685=string/mmiErrorNotSupported\n01040686=string/mmiErrorWhileRoaming\n01040687=string/mmiFdnError\n01040688=string/mobile_no_internet\n01040689=string/mobile_provisioning_apn\n0104068a=string/mobile_provisioning_url\n0104068b=string/month_day_year\n0104068c=string/more_item_label\n0104068d=string/mte_override_notification_message\n0104068e=string/mte_override_notification_title\n0104068f=string/music_recognition_manager_service\n01040690=string/muted_by\n01040691=string/nas_upgrade_notification_content\n01040692=string/nas_upgrade_notification_disable_action\n01040693=string/nas_upgrade_notification_enable_action\n01040694=string/nas_upgrade_notification_learn_more_action\n01040695=string/nas_upgrade_notification_learn_more_content\n01040696=string/nas_upgrade_notification_title\n01040697=string/needPuk\n01040698=string/needPuk2\n01040699=string/negative_duration\n0104069a=string/network_available_sign_in\n0104069b=string/network_available_sign_in_detailed\n0104069c=string/network_logging_notification_text\n0104069d=string/network_logging_notification_title\n0104069e=string/network_partial_connectivity\n0104069f=string/network_partial_connectivity_detailed\n010406a0=string/network_switch_metered\n010406a1=string/network_switch_metered_detail\n010406a2=string/network_switch_metered_toast\n010406a3=string/network_switch_type_name_unknown\n010406a4=string/new_app_action\n010406a5=string/new_app_description\n010406a6=string/new_sms_notification_content\n010406a7=string/new_sms_notification_title\n010406a8=string/news_notification_channel_label\n010406a9=string/next_button_label\n010406aa=string/noApplications\n010406ab=string/no_file_chosen\n010406ac=string/no_matches\n010406ad=string/no_permissions\n010406ae=string/no_recent_tasks\n010406af=string/noon\n010406b0=string/not_checked\n010406b1=string/not_selected\n010406b2=string/notification_action_check_bg_apps\n010406b3=string/notification_alerted_content_description\n010406b4=string/notification_app_name_settings\n010406b5=string/notification_app_name_system\n010406b6=string/notification_appops_camera_active\n010406b7=string/notification_appops_microphone_active\n010406b8=string/notification_appops_overlay_active\n010406b9=string/notification_channel_abusive_bg_apps\n010406ba=string/notification_channel_accessibility_hearing_device\n010406bb=string/notification_channel_accessibility_magnification\n010406bc=string/notification_channel_accessibility_security_policy\n010406bd=string/notification_channel_account\n010406be=string/notification_channel_alerts\n010406bf=string/notification_channel_call_forward\n010406c0=string/notification_channel_car_mode\n010406c1=string/notification_channel_developer\n010406c2=string/notification_channel_developer_important\n010406c3=string/notification_channel_device_admin\n010406c4=string/notification_channel_display\n010406c5=string/notification_channel_emergency_callback\n010406c6=string/notification_channel_foreground_service\n010406c7=string/notification_channel_heavy_weight_app\n010406c8=string/notification_channel_mobile_data_status\n010406c9=string/notification_channel_network_alert\n010406ca=string/notification_channel_network_alerts\n010406cb=string/notification_channel_network_available\n010406cc=string/notification_channel_network_status\n010406cd=string/notification_channel_physical_keyboard\n010406ce=string/notification_channel_retail_mode\n010406cf=string/notification_channel_security\n010406d0=string/notification_channel_sim\n010406d1=string/notification_channel_sim_high_prio\n010406d2=string/notification_channel_sms\n010406d3=string/notification_channel_system_changes\n010406d4=string/notification_channel_system_time\n010406d5=string/notification_channel_updates\n010406d6=string/notification_channel_usb\n010406d7=string/notification_channel_voice_mail\n010406d8=string/notification_channel_vpn\n010406d9=string/notification_channel_wfc\n010406da=string/notification_compact_heads_up_reply\n010406db=string/notification_content_abusive_bg_apps\n010406dc=string/notification_content_long_running_fgs\n010406dd=string/notification_feedback_indicator\n010406de=string/notification_feedback_indicator_alerted\n010406df=string/notification_feedback_indicator_demoted\n010406e0=string/notification_feedback_indicator_promoted\n010406e1=string/notification_feedback_indicator_silenced\n010406e2=string/notification_header_divider_symbol\n010406e3=string/notification_header_divider_symbol_with_spaces\n010406e4=string/notification_hidden_text\n010406e5=string/notification_history_title_placeholder\n010406e6=string/notification_inbox_ellipsis\n010406e7=string/notification_listener_binding_label\n010406e8=string/notification_messaging_title_template\n010406e9=string/notification_phishing_alert_content_description\n010406ea=string/notification_ranker_binding_label\n010406eb=string/notification_reply_button_accessibility\n010406ec=string/notification_title\n010406ed=string/notification_title_abusive_bg_apps\n010406ee=string/notification_title_long_running_fgs\n010406ef=string/notification_verified_content_description\n010406f0=string/notification_work_profile_content_description\n010406f1=string/now_string_shortest\n010406f2=string/number_picker_decrement_button\n010406f3=string/number_picker_increment_button\n010406f4=string/number_picker_increment_scroll_action\n010406f5=string/number_picker_increment_scroll_mode\n010406f6=string/old_app_action\n010406f7=string/older\n010406f8=string/oneMonthDurationPast\n010406f9=string/one_handed_mode_feature_name\n010406fa=string/orgTypeCustom\n010406fb=string/orgTypeOther\n010406fc=string/orgTypeWork\n010406fd=string/org_name\n010406fe=string/org_unit\n010406ff=string/other_networks_no_internet\n01040700=string/owner_name\n01040701=string/package_deleted_device_owner\n01040702=string/package_installed_device_owner\n01040703=string/package_updated_device_owner\n01040704=string/page_size_compat_apk_and_elf_warning\n01040705=string/page_size_compat_apk_warning\n01040706=string/page_size_compat_elf_warning\n01040707=string/passwordIncorrect\n01040708=string/password_keyboard_label_alpha_key\n01040709=string/password_keyboard_label_alt_key\n0104070a=string/password_keyboard_label_symbol_key\n0104070b=string/pasted_from_clipboard\n0104070c=string/peerTtyModeFull\n0104070d=string/peerTtyModeHco\n0104070e=string/peerTtyModeOff\n0104070f=string/peerTtyModeVco\n01040710=string/perm_costs_money\n01040711=string/permdesc_acceptHandovers\n01040712=string/permdesc_accessBackgroundLocation\n01040713=string/permdesc_accessCoarseLocation\n01040714=string/permdesc_accessDrmCertificates\n01040715=string/permdesc_accessFineLocation\n01040716=string/permdesc_accessHiddenProfile\n01040717=string/permdesc_accessImsCallService\n01040718=string/permdesc_accessLastKnownCellId\n01040719=string/permdesc_accessLocationExtraCommands\n0104071a=string/permdesc_accessNetworkConditions\n0104071b=string/permdesc_accessNetworkState\n0104071c=string/permdesc_accessNotifications\n0104071d=string/permdesc_accessWifiState\n0104071e=string/permdesc_accessWimaxState\n0104071f=string/permdesc_access_notification_policy\n01040720=string/permdesc_activityRecognition\n01040721=string/permdesc_addVoicemail\n01040722=string/permdesc_answerPhoneCalls\n01040723=string/permdesc_audioWrite\n01040724=string/permdesc_backgroundCamera\n01040725=string/permdesc_bindCarrierMessagingService\n01040726=string/permdesc_bindCarrierServices\n01040727=string/permdesc_bindCellBroadcastService\n01040728=string/permdesc_bindConditionProviderService\n01040729=string/permdesc_bindDreamService\n0104072a=string/permdesc_bindNotificationListenerService\n0104072b=string/permdesc_bind_connection_service\n0104072c=string/permdesc_bind_incall_service\n0104072d=string/permdesc_bluetooth\n0104072e=string/permdesc_bluetoothAdmin\n0104072f=string/permdesc_bluetooth_advertise\n01040730=string/permdesc_bluetooth_connect\n01040731=string/permdesc_bluetooth_scan\n01040732=string/permdesc_bodySensors\n01040733=string/permdesc_bodySensors_background\n01040734=string/permdesc_broadcastSticky\n01040735=string/permdesc_callCompanionApp\n01040736=string/permdesc_callPhone\n01040737=string/permdesc_camera\n01040738=string/permdesc_cameraHeadlessSystemUser\n01040739=string/permdesc_cameraOpenCloseListener\n0104073a=string/permdesc_changeNetworkState\n0104073b=string/permdesc_changeTetherState\n0104073c=string/permdesc_changeWifiMulticastState\n0104073d=string/permdesc_changeWifiState\n0104073e=string/permdesc_changeWimaxState\n0104073f=string/permdesc_companionProfileWatch\n01040740=string/permdesc_connection_manager\n01040741=string/permdesc_control_incall_experience\n01040742=string/permdesc_createNetworkSockets\n01040743=string/permdesc_deliverCompanionMessages\n01040744=string/permdesc_detectScreenCapture\n01040745=string/permdesc_disableKeyguard\n01040746=string/permdesc_enableCarMode\n01040747=string/permdesc_exemptFromAudioRecordRestrictions\n01040748=string/permdesc_expandStatusBar\n01040749=string/permdesc_eye_tracking_coarse\n0104074a=string/permdesc_eye_tracking_fine\n0104074b=string/permdesc_face_tracking\n0104074c=string/permdesc_foregroundService\n0104074d=string/permdesc_foregroundServiceCamera\n0104074e=string/permdesc_foregroundServiceConnectedDevice\n0104074f=string/permdesc_foregroundServiceDataSync\n01040750=string/permdesc_foregroundServiceFileManagement\n01040751=string/permdesc_foregroundServiceHealth\n01040752=string/permdesc_foregroundServiceLocation\n01040753=string/permdesc_foregroundServiceMediaPlayback\n01040754=string/permdesc_foregroundServiceMediaProcessing\n01040755=string/permdesc_foregroundServiceMediaProjection\n01040756=string/permdesc_foregroundServiceMicrophone\n01040757=string/permdesc_foregroundServicePhoneCall\n01040758=string/permdesc_foregroundServiceRemoteMessaging\n01040759=string/permdesc_foregroundServiceSpecialUse\n0104075a=string/permdesc_foregroundServiceSystemExempted\n0104075b=string/permdesc_fullScreenIntent\n0104075c=string/permdesc_getAccounts\n0104075d=string/permdesc_getPackageSize\n0104075e=string/permdesc_getTasks\n0104075f=string/permdesc_hand_tracking\n01040760=string/permdesc_handoverStatus\n01040761=string/permdesc_head_tracking\n01040762=string/permdesc_hideOverlayWindows\n01040763=string/permdesc_highSamplingRateSensors\n01040764=string/permdesc_imagesWrite\n01040765=string/permdesc_install_shortcut\n01040766=string/permdesc_invokeCarrierSetup\n01040767=string/permdesc_killBackgroundProcesses\n01040768=string/permdesc_manageFingerprint\n01040769=string/permdesc_manageNetworkPolicy\n0104076a=string/permdesc_manageOngoingCalls\n0104076b=string/permdesc_manageOwnCalls\n0104076c=string/permdesc_manageProfileAndDeviceOwners\n0104076d=string/permdesc_mediaLocation\n0104076e=string/permdesc_modifyAudioSettings\n0104076f=string/permdesc_modifyNetworkAccounting\n01040770=string/permdesc_nearby_wifi_devices\n01040771=string/permdesc_nfc\n01040772=string/permdesc_nfcTransactionEvent\n01040773=string/permdesc_observeCompanionDevicePresence\n01040774=string/permdesc_persistentActivity\n01040775=string/permdesc_postNotification\n01040776=string/permdesc_preferredPaymentInfo\n01040777=string/permdesc_processOutgoingCalls\n01040778=string/permdesc_queryAllPackages\n01040779=string/permdesc_ranging\n0104077a=string/permdesc_readBasicPhoneState\n0104077b=string/permdesc_readCalendar\n0104077c=string/permdesc_readCallLog\n0104077d=string/permdesc_readCellBroadcasts\n0104077e=string/permdesc_readContacts\n0104077f=string/permdesc_readInstallSessions\n01040780=string/permdesc_readMediaAudio\n01040781=string/permdesc_readMediaImages\n01040782=string/permdesc_readMediaVideo\n01040783=string/permdesc_readNetworkUsageHistory\n01040784=string/permdesc_readPhoneNumbers\n01040785=string/permdesc_readPhoneState\n01040786=string/permdesc_readSms\n01040787=string/permdesc_readSyncSettings\n01040788=string/permdesc_readSyncStats\n01040789=string/permdesc_readVisualUserSelect\n0104078a=string/permdesc_receiveBootCompleted\n0104078b=string/permdesc_receiveMms\n0104078c=string/permdesc_receiveSms\n0104078d=string/permdesc_receiveWapPush\n0104078e=string/permdesc_recordAudio\n0104078f=string/permdesc_recordBackgroundAudio\n01040790=string/permdesc_register_call_provider\n01040791=string/permdesc_register_sim_subscription\n01040792=string/permdesc_removeDrmCertificates\n01040793=string/permdesc_reorderTasks\n01040794=string/permdesc_requestDeletePackages\n01040795=string/permdesc_requestIgnoreBatteryOptimizations\n01040796=string/permdesc_requestInstallPackages\n01040797=string/permdesc_requestPasswordComplexity\n01040798=string/permdesc_route_media_output\n01040799=string/permdesc_runInBackground\n0104079a=string/permdesc_scene_understanding_coarse\n0104079b=string/permdesc_scene_understanding_fine\n0104079c=string/permdesc_schedule_exact_alarm\n0104079d=string/permdesc_sdcardRead\n0104079e=string/permdesc_sdcardWrite\n0104079f=string/permdesc_sendSms\n010407a0=string/permdesc_setAlarm\n010407a1=string/permdesc_setInputCalibration\n010407a2=string/permdesc_setTimeZone\n010407a3=string/permdesc_setWallpaper\n010407a4=string/permdesc_setWallpaperHints\n010407a5=string/permdesc_sim_communication\n010407a6=string/permdesc_startForegroundServicesFromBackground\n010407a7=string/permdesc_startReviewPermissionDecisions\n010407a8=string/permdesc_startViewAppFeatures\n010407a9=string/permdesc_startViewPermissionUsage\n010407aa=string/permdesc_statusBar\n010407ab=string/permdesc_statusBarService\n010407ac=string/permdesc_subscribedFeedsRead\n010407ad=string/permdesc_systemAlertWindow\n010407ae=string/permdesc_systemCamera\n010407af=string/permdesc_transmitIr\n010407b0=string/permdesc_turnScreenOn\n010407b1=string/permdesc_uninstall_shortcut\n010407b2=string/permdesc_updatePackagesWithoutUserAction\n010407b3=string/permdesc_useBiometric\n010407b4=string/permdesc_useDataInBackground\n010407b5=string/permdesc_useFingerprint\n010407b6=string/permdesc_use_exact_alarm\n010407b7=string/permdesc_use_sip\n010407b8=string/permdesc_uwb_ranging\n010407b9=string/permdesc_vibrate\n010407ba=string/permdesc_vibrator_state\n010407bb=string/permdesc_videoWrite\n010407bc=string/permdesc_wakeLock\n010407bd=string/permdesc_writeCalendar\n010407be=string/permdesc_writeCallLog\n010407bf=string/permdesc_writeContacts\n010407c0=string/permdesc_writeSettings\n010407c1=string/permdesc_writeSyncSettings\n010407c2=string/permdesc_writeVerificationStateE2eeContactKeys\n010407c3=string/permdesc_xr_tracking_in_background\n010407c4=string/permgroupdesc_activityRecognition\n010407c5=string/permgroupdesc_calendar\n010407c6=string/permgroupdesc_calllog\n010407c7=string/permgroupdesc_camera\n010407c8=string/permgroupdesc_contacts\n010407c9=string/permgroupdesc_location\n010407ca=string/permgroupdesc_microphone\n010407cb=string/permgroupdesc_nearby_devices\n010407cc=string/permgroupdesc_notifications\n010407cd=string/permgroupdesc_phone\n010407ce=string/permgroupdesc_readMediaAural\n010407cf=string/permgroupdesc_readMediaVisual\n010407d0=string/permgroupdesc_sensors\n010407d1=string/permgroupdesc_sms\n010407d2=string/permgroupdesc_storage\n010407d3=string/permgroupdesc_xr_tracking\n010407d4=string/permgroupdesc_xr_tracking_sensitive\n010407d5=string/permgrouplab_activityRecognition\n010407d6=string/permgrouplab_calendar\n010407d7=string/permgrouplab_calllog\n010407d8=string/permgrouplab_camera\n010407d9=string/permgrouplab_contacts\n010407da=string/permgrouplab_location\n010407db=string/permgrouplab_microphone\n010407dc=string/permgrouplab_nearby_devices\n010407dd=string/permgrouplab_notifications\n010407de=string/permgrouplab_phone\n010407df=string/permgrouplab_readMediaAural\n010407e0=string/permgrouplab_readMediaVisual\n010407e1=string/permgrouplab_sensors\n010407e2=string/permgrouplab_sms\n010407e3=string/permgrouplab_storage\n010407e4=string/permgrouplab_xr_tracking\n010407e5=string/permgrouplab_xr_tracking_sensitive\n010407e6=string/permission_request_notification_for_app_with_subtitle\n010407e7=string/permission_request_notification_title\n010407e8=string/permission_request_notification_with_subtitle\n010407e9=string/permlab_acceptHandover\n010407ea=string/permlab_accessBackgroundLocation\n010407eb=string/permlab_accessCoarseLocation\n010407ec=string/permlab_accessDrmCertificates\n010407ed=string/permlab_accessFineLocation\n010407ee=string/permlab_accessHiddenProfile\n010407ef=string/permlab_accessImsCallService\n010407f0=string/permlab_accessLastKnownCellId\n010407f1=string/permlab_accessLocationExtraCommands\n010407f2=string/permlab_accessNetworkConditions\n010407f3=string/permlab_accessNetworkState\n010407f4=string/permlab_accessNotifications\n010407f5=string/permlab_accessWifiState\n010407f6=string/permlab_accessWimaxState\n010407f7=string/permlab_access_notification_policy\n010407f8=string/permlab_activityRecognition\n010407f9=string/permlab_addVoicemail\n010407fa=string/permlab_answerPhoneCalls\n010407fb=string/permlab_audioWrite\n010407fc=string/permlab_backgroundCamera\n010407fd=string/permlab_bindCarrierMessagingService\n010407fe=string/permlab_bindCarrierServices\n010407ff=string/permlab_bindCellBroadcastService\n01040800=string/permlab_bindConditionProviderService\n01040801=string/permlab_bindDreamService\n01040802=string/permlab_bindNotificationListenerService\n01040803=string/permlab_bind_connection_service\n01040804=string/permlab_bind_incall_service\n01040805=string/permlab_bluetooth\n01040806=string/permlab_bluetoothAdmin\n01040807=string/permlab_bluetooth_advertise\n01040808=string/permlab_bluetooth_connect\n01040809=string/permlab_bluetooth_scan\n0104080a=string/permlab_bodySensors\n0104080b=string/permlab_bodySensors_background\n0104080c=string/permlab_broadcastSticky\n0104080d=string/permlab_callCompanionApp\n0104080e=string/permlab_callPhone\n0104080f=string/permlab_camera\n01040810=string/permlab_cameraHeadlessSystemUser\n01040811=string/permlab_cameraOpenCloseListener\n01040812=string/permlab_changeNetworkState\n01040813=string/permlab_changeTetherState\n01040814=string/permlab_changeWifiMulticastState\n01040815=string/permlab_changeWifiState\n01040816=string/permlab_changeWimaxState\n01040817=string/permlab_companionProfileWatch\n01040818=string/permlab_connection_manager\n01040819=string/permlab_control_incall_experience\n0104081a=string/permlab_createNetworkSockets\n0104081b=string/permlab_deliverCompanionMessages\n0104081c=string/permlab_detectScreenCapture\n0104081d=string/permlab_disableKeyguard\n0104081e=string/permlab_enableCarMode\n0104081f=string/permlab_exemptFromAudioRecordRestrictions\n01040820=string/permlab_expandStatusBar\n01040821=string/permlab_eye_tracking_coarse\n01040822=string/permlab_eye_tracking_fine\n01040823=string/permlab_face_tracking\n01040824=string/permlab_foregroundService\n01040825=string/permlab_foregroundServiceCamera\n01040826=string/permlab_foregroundServiceConnectedDevice\n01040827=string/permlab_foregroundServiceDataSync\n01040828=string/permlab_foregroundServiceFileManagement\n01040829=string/permlab_foregroundServiceHealth\n0104082a=string/permlab_foregroundServiceLocation\n0104082b=string/permlab_foregroundServiceMediaPlayback\n0104082c=string/permlab_foregroundServiceMediaProcessing\n0104082d=string/permlab_foregroundServiceMediaProjection\n0104082e=string/permlab_foregroundServiceMicrophone\n0104082f=string/permlab_foregroundServicePhoneCall\n01040830=string/permlab_foregroundServiceRemoteMessaging\n01040831=string/permlab_foregroundServiceSpecialUse\n01040832=string/permlab_foregroundServiceSystemExempted\n01040833=string/permlab_fullScreenIntent\n01040834=string/permlab_getAccounts\n01040835=string/permlab_getPackageSize\n01040836=string/permlab_getTasks\n01040837=string/permlab_hand_tracking\n01040838=string/permlab_handoverStatus\n01040839=string/permlab_head_tracking\n0104083a=string/permlab_hideOverlayWindows\n0104083b=string/permlab_highSamplingRateSensors\n0104083c=string/permlab_imagesWrite\n0104083d=string/permlab_install_shortcut\n0104083e=string/permlab_invokeCarrierSetup\n0104083f=string/permlab_killBackgroundProcesses\n01040840=string/permlab_manageFingerprint\n01040841=string/permlab_manageNetworkPolicy\n01040842=string/permlab_manageOngoingCalls\n01040843=string/permlab_manageOwnCalls\n01040844=string/permlab_manageProfileAndDeviceOwners\n01040845=string/permlab_mediaLocation\n01040846=string/permlab_modifyAudioSettings\n01040847=string/permlab_modifyNetworkAccounting\n01040848=string/permlab_nearby_wifi_devices\n01040849=string/permlab_nfc\n0104084a=string/permlab_nfcTransactionEvent\n0104084b=string/permlab_observeCompanionDevicePresence\n0104084c=string/permlab_persistentActivity\n0104084d=string/permlab_postNotification\n0104084e=string/permlab_preferredPaymentInfo\n0104084f=string/permlab_processOutgoingCalls\n01040850=string/permlab_queryAllPackages\n01040851=string/permlab_ranging\n01040852=string/permlab_readBasicPhoneState\n01040853=string/permlab_readCalendar\n01040854=string/permlab_readCallLog\n01040855=string/permlab_readCellBroadcasts\n01040856=string/permlab_readContacts\n01040857=string/permlab_readInstallSessions\n01040858=string/permlab_readMediaAudio\n01040859=string/permlab_readMediaImages\n0104085a=string/permlab_readMediaVideo\n0104085b=string/permlab_readNetworkUsageHistory\n0104085c=string/permlab_readPhoneNumbers\n0104085d=string/permlab_readPhoneState\n0104085e=string/permlab_readSms\n0104085f=string/permlab_readSyncSettings\n01040860=string/permlab_readSyncStats\n01040861=string/permlab_readVisualUserSelect\n01040862=string/permlab_receiveBootCompleted\n01040863=string/permlab_receiveMms\n01040864=string/permlab_receiveSms\n01040865=string/permlab_receiveWapPush\n01040866=string/permlab_recordAudio\n01040867=string/permlab_recordBackgroundAudio\n01040868=string/permlab_register_call_provider\n01040869=string/permlab_register_sim_subscription\n0104086a=string/permlab_removeDrmCertificates\n0104086b=string/permlab_reorderTasks\n0104086c=string/permlab_requestDeletePackages\n0104086d=string/permlab_requestIgnoreBatteryOptimizations\n0104086e=string/permlab_requestInstallPackages\n0104086f=string/permlab_requestPasswordComplexity\n01040870=string/permlab_route_media_output\n01040871=string/permlab_runInBackground\n01040872=string/permlab_scene_understanding_coarse\n01040873=string/permlab_scene_understanding_fine\n01040874=string/permlab_schedule_exact_alarm\n01040875=string/permlab_sdcardRead\n01040876=string/permlab_sdcardWrite\n01040877=string/permlab_sendSms\n01040878=string/permlab_setAlarm\n01040879=string/permlab_setInputCalibration\n0104087a=string/permlab_setTimeZone\n0104087b=string/permlab_setWallpaper\n0104087c=string/permlab_setWallpaperHints\n0104087d=string/permlab_sim_communication\n0104087e=string/permlab_startForegroundServicesFromBackground\n0104087f=string/permlab_startReviewPermissionDecisions\n01040880=string/permlab_startViewAppFeatures\n01040881=string/permlab_startViewPermissionUsage\n01040882=string/permlab_statusBar\n01040883=string/permlab_statusBarService\n01040884=string/permlab_subscribedFeedsRead\n01040885=string/permlab_systemAlertWindow\n01040886=string/permlab_systemCamera\n01040887=string/permlab_transmitIr\n01040888=string/permlab_turnScreenOn\n01040889=string/permlab_uninstall_shortcut\n0104088a=string/permlab_updatePackagesWithoutUserAction\n0104088b=string/permlab_useBiometric\n0104088c=string/permlab_useDataInBackground\n0104088d=string/permlab_useFingerprint\n0104088e=string/permlab_use_exact_alarm\n0104088f=string/permlab_use_sip\n01040890=string/permlab_uwb_ranging\n01040891=string/permlab_vibrate\n01040892=string/permlab_videoWrite\n01040893=string/permlab_wakeLock\n01040894=string/permlab_writeCalendar\n01040895=string/permlab_writeCallLog\n01040896=string/permlab_writeContacts\n01040897=string/permlab_writeSettings\n01040898=string/permlab_writeSyncSettings\n01040899=string/permlab_writeVerificationStateE2eeContactKeys\n0104089a=string/permlab_xr_tracking_in_background\n0104089b=string/perms_description_app\n0104089c=string/perms_new_perm_prefix\n0104089d=string/personal_apps_suspended_turn_profile_on\n0104089e=string/personal_apps_suspension_soon_text\n0104089f=string/personal_apps_suspension_text\n010408a0=string/personal_apps_suspension_title\n010408a1=string/phoneTypeAssistant\n010408a2=string/phoneTypeCallback\n010408a3=string/phoneTypeCar\n010408a4=string/phoneTypeCompanyMain\n010408a5=string/phoneTypeCustom\n010408a6=string/phoneTypeFaxHome\n010408a7=string/phoneTypeFaxWork\n010408a8=string/phoneTypeHome\n010408a9=string/phoneTypeIsdn\n010408aa=string/phoneTypeMain\n010408ab=string/phoneTypeMms\n010408ac=string/phoneTypeMobile\n010408ad=string/phoneTypeOther\n010408ae=string/phoneTypeOtherFax\n010408af=string/phoneTypePager\n010408b0=string/phoneTypeRadio\n010408b1=string/phoneTypeTelex\n010408b2=string/phoneTypeTtyTdd\n010408b3=string/phoneTypeWork\n010408b4=string/phoneTypeWorkMobile\n010408b5=string/phoneTypeWorkPager\n010408b6=string/pin_specific_target\n010408b7=string/pin_target\n010408b8=string/policydesc_disableCamera\n010408b9=string/policydesc_disableKeyguardFeatures\n010408ba=string/policydesc_encryptedStorage\n010408bb=string/policydesc_expirePassword\n010408bc=string/policydesc_forceLock\n010408bd=string/policydesc_limitPassword\n010408be=string/policydesc_resetPassword\n010408bf=string/policydesc_setGlobalProxy\n010408c0=string/policydesc_watchLogin\n010408c1=string/policydesc_watchLogin_secondaryUser\n010408c2=string/policydesc_wipeData\n010408c3=string/policydesc_wipeData_secondaryUser\n010408c4=string/policylab_disableCamera\n010408c5=string/policylab_disableKeyguardFeatures\n010408c6=string/policylab_encryptedStorage\n010408c7=string/policylab_expirePassword\n010408c8=string/policylab_forceLock\n010408c9=string/policylab_limitPassword\n010408ca=string/policylab_resetPassword\n010408cb=string/policylab_setGlobalProxy\n010408cc=string/policylab_watchLogin\n010408cd=string/policylab_wipeData\n010408ce=string/policylab_wipeData_secondaryUser\n010408cf=string/popup_window_default_title\n010408d0=string/postalTypeCustom\n010408d1=string/postalTypeHome\n010408d2=string/postalTypeOther\n010408d3=string/postalTypeWork\n010408d4=string/power_dialog\n010408d5=string/power_off\n010408d6=string/prefs_bugreport\n010408d7=string/prepend_shortcut_label\n010408d8=string/preposition_for_date\n010408d9=string/preposition_for_time\n010408da=string/preposition_for_year\n010408db=string/print_service_installed_message\n010408dc=string/print_service_installed_title\n010408dd=string/printing_disabled_by\n010408de=string/private_dns_broken_detailed\n010408df=string/private_profile_label_badge\n010408e0=string/private_space_biometric_prompt_title\n010408e1=string/private_space_deleted_by_admin\n010408e2=string/private_space_deleted_by_admin_details\n010408e3=string/private_space_set_up_screen_lock_for_reset\n010408e4=string/private_space_set_up_screen_lock_message\n010408e5=string/profile_encrypted_detail\n010408e6=string/profile_encrypted_message\n010408e7=string/profile_encrypted_title\n010408e8=string/profile_label_clone\n010408e9=string/profile_label_communal\n010408ea=string/profile_label_private\n010408eb=string/profile_label_supervising\n010408ec=string/profile_label_test\n010408ed=string/profile_label_work\n010408ee=string/profile_label_work_2\n010408ef=string/profile_label_work_3\n010408f0=string/progress_erasing\n010408f1=string/prohibit_manual_network_selection_in_gobal_mode\n010408f2=string/promotional_notification_channel_label\n010408f3=string/quick_contacts_not_available\n010408f4=string/radial_numbers_typeface\n010408f5=string/rating_label\n010408f6=string/reason_service_unavailable\n010408f7=string/reason_unknown\n010408f8=string/reboot_safemode_confirm\n010408f9=string/reboot_safemode_title\n010408fa=string/reboot_to_reset_message\n010408fb=string/reboot_to_reset_title\n010408fc=string/reboot_to_update_package\n010408fd=string/reboot_to_update_prepare\n010408fe=string/reboot_to_update_reboot\n010408ff=string/reboot_to_update_title\n01040900=string/recent_tasks_title\n01040901=string/recs_notification_channel_label\n01040902=string/redacted_notification_action_title\n01040903=string/redacted_notification_message\n01040904=string/redo\n01040905=string/reduce_bright_colors_feature_name\n01040906=string/region_picker_section_all\n01040907=string/region_picker_section_suggested_bilingual\n01040908=string/relationTypeAssistant\n01040909=string/relationTypeBrother\n0104090a=string/relationTypeChild\n0104090b=string/relationTypeCustom\n0104090c=string/relationTypeDomesticPartner\n0104090d=string/relationTypeFather\n0104090e=string/relationTypeFriend\n0104090f=string/relationTypeManager\n01040910=string/relationTypeMother\n01040911=string/relationTypeParent\n01040912=string/relationTypePartner\n01040913=string/relationTypeReferredBy\n01040914=string/relationTypeRelative\n01040915=string/relationTypeSister\n01040916=string/relationTypeSpouse\n01040917=string/relative_time\n01040918=string/replace\n01040919=string/report\n0104091a=string/reset\n0104091b=string/resolver_cant_access_personal_apps_explanation\n0104091c=string/resolver_cant_access_work_apps_explanation\n0104091d=string/resolver_cant_share_with_personal_apps_explanation\n0104091e=string/resolver_cant_share_with_work_apps_explanation\n0104091f=string/resolver_cross_profile_blocked\n01040920=string/resolver_no_personal_apps_available\n01040921=string/resolver_no_work_apps_available\n01040922=string/resolver_personal_tab\n01040923=string/resolver_personal_tab_accessibility\n01040924=string/resolver_switch_on_work\n01040925=string/resolver_turn_on_work_apps\n01040926=string/resolver_work_tab\n01040927=string/resolver_work_tab_accessibility\n01040928=string/restr_pin_confirm_pin\n01040929=string/restr_pin_create_pin\n0104092a=string/restr_pin_enter_admin_pin\n0104092b=string/restr_pin_enter_new_pin\n0104092c=string/restr_pin_enter_old_pin\n0104092d=string/restr_pin_enter_pin\n0104092e=string/restr_pin_error_doesnt_match\n0104092f=string/restr_pin_error_too_short\n01040930=string/restr_pin_incorrect\n01040931=string/restr_pin_try_later\n01040932=string/review_notification_settings_dismiss\n01040933=string/review_notification_settings_remind_me_action\n01040934=string/review_notification_settings_text\n01040935=string/review_notification_settings_title\n01040936=string/revoke\n01040937=string/ringtone_default\n01040938=string/ringtone_default_with_actual\n01040939=string/ringtone_picker_title\n0104093a=string/ringtone_picker_title_alarm\n0104093b=string/ringtone_picker_title_notification\n0104093c=string/ringtone_silent\n0104093d=string/ringtone_unknown\n0104093e=string/roamingText0\n0104093f=string/roamingText1\n01040940=string/roamingText10\n01040941=string/roamingText11\n01040942=string/roamingText12\n01040943=string/roamingText2\n01040944=string/roamingText3\n01040945=string/roamingText4\n01040946=string/roamingText5\n01040947=string/roamingText6\n01040948=string/roamingText7\n01040949=string/roamingText8\n0104094a=string/roamingText9\n0104094b=string/roamingTextSearching\n0104094c=string/safeMode\n0104094d=string/safe_media_volume_warning\n0104094e=string/sans_serif\n0104094f=string/satellite_access_config_file\n01040950=string/satellite_manual_selection_state_popup_cancel\n01040951=string/satellite_manual_selection_state_popup_message\n01040952=string/satellite_manual_selection_state_popup_ok\n01040953=string/satellite_manual_selection_state_popup_title\n01040954=string/satellite_messaging_available_notification_summary\n01040955=string/satellite_messaging_available_notification_title\n01040956=string/satellite_messaging_location_disabled_notification_summary\n01040957=string/satellite_messaging_location_disabled_notification_title\n01040958=string/satellite_messaging_not_in_allowed_region_notification_summary\n01040959=string/satellite_messaging_not_in_allowed_region_notification_title\n0104095a=string/satellite_messaging_not_provisioned_notification_summary\n0104095b=string/satellite_messaging_not_provisioned_notification_title\n0104095c=string/satellite_messaging_not_supported_notification_summary\n0104095d=string/satellite_messaging_not_supported_notification_title\n0104095e=string/satellite_messaging_unsupported_default_sms_app_notification_summary\n0104095f=string/satellite_messaging_unsupported_default_sms_app_notification_title\n01040960=string/satellite_notification_how_it_works\n01040961=string/satellite_notification_manual_summary\n01040962=string/satellite_notification_manual_title\n01040963=string/satellite_notification_open_message\n01040964=string/satellite_notification_summary\n01040965=string/satellite_notification_summary_with_data\n01040966=string/satellite_notification_title\n01040967=string/satellite_sos_available_notification_summary\n01040968=string/satellite_sos_available_notification_title\n01040969=string/satellite_sos_location_disabled_notification_summary\n0104096a=string/satellite_sos_location_disabled_notification_title\n0104096b=string/satellite_sos_not_in_allowed_region_notification_summary\n0104096c=string/satellite_sos_not_in_allowed_region_notification_title\n0104096d=string/satellite_sos_not_provisioned_notification_summary\n0104096e=string/satellite_sos_not_provisioned_notification_title\n0104096f=string/satellite_sos_not_supported_notification_summary\n01040970=string/satellite_sos_not_supported_notification_title\n01040971=string/satellite_sos_unsupported_default_sms_app_notification_summary\n01040972=string/satellite_sos_unsupported_default_sms_app_notification_title\n01040973=string/scCellularNetworkSecurityLearnMore\n01040974=string/scCellularNetworkSecuritySummary\n01040975=string/scCellularNetworkSecurityTitle\n01040976=string/scIdentifierDisclosureIssueSummary\n01040977=string/scIdentifierDisclosureIssueSummaryNotification\n01040978=string/scIdentifierDisclosureIssueTitle\n01040979=string/scNullCipherIssueActionGotIt\n0104097a=string/scNullCipherIssueActionLearnMore\n0104097b=string/scNullCipherIssueActionSettings\n0104097c=string/scNullCipherIssueEncryptedSummary\n0104097d=string/scNullCipherIssueEncryptedTitle\n0104097e=string/scNullCipherIssueNonEncryptedSummary\n0104097f=string/scNullCipherIssueNonEncryptedSummaryNotification\n01040980=string/scNullCipherIssueNonEncryptedTitle\n01040981=string/screen_compat_mode_hint\n01040982=string/screen_compat_mode_scale\n01040983=string/screen_compat_mode_show\n01040984=string/screen_lock\n01040985=string/screen_lock_app_setting_name\n01040986=string/screen_lock_dialog_default_subtitle\n01040987=string/screen_not_shared_sensitive_content\n01040988=string/screenshot_edit\n01040989=string/search_hint\n0104098a=string/search_language_hint\n0104098b=string/searchview_description_clear\n0104098c=string/searchview_description_query\n0104098d=string/searchview_description_search\n0104098e=string/searchview_description_submit\n0104098f=string/searchview_description_voice\n01040990=string/second\n01040991=string/seconds\n01040992=string/select_character\n01040993=string/select_day\n01040994=string/select_hours\n01040995=string/select_input_method\n01040996=string/select_keyboard_layout_notification_message\n01040997=string/select_keyboard_layout_notification_title\n01040998=string/select_minutes\n01040999=string/select_multiple_keyboards_layout_notification_title\n0104099a=string/select_year\n0104099b=string/selected\n0104099c=string/sendText\n0104099d=string/sending\n0104099e=string/sensor_notification_service\n0104099f=string/sensor_privacy_notification_channel_label\n010409a0=string/sensor_privacy_start_use_camera_notification_content_title\n010409a1=string/sensor_privacy_start_use_dialog_turn_on_button\n010409a2=string/sensor_privacy_start_use_mic_notification_content_title\n010409a3=string/sensor_privacy_start_use_notification_content_text\n010409a4=string/serial_number\n010409a5=string/serviceClassData\n010409a6=string/serviceClassDataAsync\n010409a7=string/serviceClassDataSync\n010409a8=string/serviceClassFAX\n010409a9=string/serviceClassPAD\n010409aa=string/serviceClassPacket\n010409ab=string/serviceClassSMS\n010409ac=string/serviceClassVoice\n010409ad=string/serviceDisabled\n010409ae=string/serviceEnabled\n010409af=string/serviceEnabledFor\n010409b0=string/serviceErased\n010409b1=string/serviceNotProvisioned\n010409b2=string/serviceRegistered\n010409b3=string/set_up_screen_lock_action_label\n010409b4=string/set_up_screen_lock_title\n010409b5=string/sha1_fingerprint\n010409b6=string/sha256_fingerprint\n010409b7=string/share\n010409b8=string/share_action_provider_share_with\n010409b9=string/share_remote_bugreport_action\n010409ba=string/share_remote_bugreport_notification_message_finished\n010409bb=string/share_remote_bugreport_notification_title\n010409bc=string/shareactionprovider_share_with\n010409bd=string/shareactionprovider_share_with_application\n010409be=string/sharing_remote_bugreport_notification_title\n010409bf=string/shortcut_disabled_reason_unknown\n010409c0=string/shortcut_group_a11y_title\n010409c1=string/shortcut_restore_not_supported\n010409c2=string/shortcut_restore_signature_mismatch\n010409c3=string/shortcut_restore_unknown_issue\n010409c4=string/shortcut_restored_on_lower_version\n010409c5=string/show_ime\n010409c6=string/shutdown_confirm\n010409c7=string/shutdown_confirm_question\n010409c8=string/shutdown_progress\n010409c9=string/silent_mode\n010409ca=string/silent_mode_ring\n010409cb=string/silent_mode_silent\n010409cc=string/silent_mode_vibrate\n010409cd=string/sim_added_message\n010409ce=string/sim_added_title\n010409cf=string/sim_done_button\n010409d0=string/sim_removed_message\n010409d1=string/sim_removed_title\n010409d2=string/sim_restart_button\n010409d3=string/sipAddressTypeCustom\n010409d4=string/sipAddressTypeHome\n010409d5=string/sipAddressTypeOther\n010409d6=string/sipAddressTypeWork\n010409d7=string/skip_button_label\n010409d8=string/slice_more_content\n010409d9=string/slices_permission_request\n010409da=string/sms_control_message\n010409db=string/sms_control_no\n010409dc=string/sms_control_title\n010409dd=string/sms_control_yes\n010409de=string/sms_premium_short_code_details\n010409df=string/sms_short_code_confirm_allow\n010409e0=string/sms_short_code_confirm_always_allow\n010409e1=string/sms_short_code_confirm_deny\n010409e2=string/sms_short_code_confirm_message\n010409e3=string/sms_short_code_confirm_never_allow\n010409e4=string/sms_short_code_details\n010409e5=string/sms_short_code_remember_choice\n010409e6=string/sms_short_code_remember_undo_instruction\n010409e7=string/smv_application\n010409e8=string/smv_process\n010409e9=string/social_notification_channel_label\n010409ea=string/splash_screen_view_branding_description\n010409eb=string/splash_screen_view_icon_description\n010409ec=string/ssl_ca_cert_noti_by_administrator\n010409ed=string/ssl_ca_cert_noti_by_unknown\n010409ee=string/ssl_ca_cert_noti_managed\n010409ef=string/ssl_ca_cert_warning\n010409f0=string/ssl_certificate\n010409f1=string/ssl_certificate_is_valid\n010409f2=string/status_bar_airplane\n010409f3=string/status_bar_alarm_clock\n010409f4=string/status_bar_battery\n010409f5=string/status_bar_bluetooth\n010409f6=string/status_bar_call_strength\n010409f7=string/status_bar_camera\n010409f8=string/status_bar_cast\n010409f9=string/status_bar_cdma_eri\n010409fa=string/status_bar_clock\n010409fb=string/status_bar_connected_display\n010409fc=string/status_bar_data_connection\n010409fd=string/status_bar_data_saver\n010409fe=string/status_bar_ethernet\n010409ff=string/status_bar_headset\n01040a00=string/status_bar_hotspot\n01040a01=string/status_bar_ime\n01040a02=string/status_bar_location\n01040a03=string/status_bar_managed_profile\n01040a04=string/status_bar_microphone\n01040a05=string/status_bar_mobile\n01040a06=string/status_bar_mute\n01040a07=string/status_bar_nfc\n01040a08=string/status_bar_no_calling\n01040a09=string/status_bar_oem_satellite\n01040a0a=string/status_bar_phone_evdo_signal\n01040a0b=string/status_bar_phone_signal\n01040a0c=string/status_bar_rotate\n01040a0d=string/status_bar_screen_record\n01040a0e=string/status_bar_secure\n01040a0f=string/status_bar_sensors_off\n01040a10=string/status_bar_speakerphone\n01040a11=string/status_bar_stacked_mobile\n01040a12=string/status_bar_sync_active\n01040a13=string/status_bar_sync_failing\n01040a14=string/status_bar_tty\n01040a15=string/status_bar_volume\n01040a16=string/status_bar_vpn\n01040a17=string/status_bar_wifi\n01040a18=string/status_bar_zen\n01040a19=string/stk_cc_ss_to_dial\n01040a1a=string/stk_cc_ss_to_dial_video\n01040a1b=string/stk_cc_ss_to_ss\n01040a1c=string/stk_cc_ss_to_ussd\n01040a1d=string/stk_cc_ussd_to_dial\n01040a1e=string/stk_cc_ussd_to_dial_video\n01040a1f=string/stk_cc_ussd_to_ss\n01040a20=string/stk_cc_ussd_to_ussd\n01040a21=string/storage_internal\n01040a22=string/storage_sd_card\n01040a23=string/storage_sd_card_label\n01040a24=string/storage_usb\n01040a25=string/storage_usb_drive\n01040a26=string/storage_usb_drive_label\n01040a27=string/submit\n01040a28=string/suggested_apps_group_a11y_title\n01040a29=string/supervised_user_creation_label\n01040a2a=string/suspended_widget_accessibility\n01040a2b=string/sync_binding_label\n01040a2c=string/sync_do_nothing\n01040a2d=string/sync_really_delete\n01040a2e=string/sync_too_many_deletes\n01040a2f=string/sync_too_many_deletes_desc\n01040a30=string/sync_undo_deletes\n01040a31=string/system_error_manufacturer\n01040a32=string/system_error_wipe_data\n01040a33=string/system_locale_title\n01040a34=string/system_ui_date_pattern\n01040a35=string/taking_remote_bugreport_notification_title\n01040a36=string/test_harness_mode_notification_message\n01040a37=string/test_harness_mode_notification_title\n01040a38=string/textSelectionCABTitle\n01040a39=string/time_of_day\n01040a3a=string/time_picker_decrement_hour_button\n01040a3b=string/time_picker_decrement_minute_button\n01040a3c=string/time_picker_decrement_set_am_button\n01040a3d=string/time_picker_dialog_title\n01040a3e=string/time_picker_header_text\n01040a3f=string/time_picker_hour_label\n01040a40=string/time_picker_increment_hour_button\n01040a41=string/time_picker_increment_minute_button\n01040a42=string/time_picker_increment_set_pm_button\n01040a43=string/time_picker_input_error\n01040a44=string/time_picker_minute_label\n01040a45=string/time_picker_mode\n01040a46=string/time_picker_prompt_label\n01040a47=string/time_picker_radial_mode_description\n01040a48=string/time_picker_text_input_mode_description\n01040a49=string/time_placeholder\n01040a4a=string/time_zone_change_notification_body\n01040a4b=string/time_zone_change_notification_title\n01040a4c=string/toolbar_collapse_description\n01040a4d=string/tooltip_popup_title\n01040a4e=string/turn_off_radio\n01040a4f=string/turn_on_magnification_settings_action\n01040a50=string/turn_on_radio\n01040a51=string/tutorial_double_tap_to_zoom_message_short\n01040a52=string/twilight_service\n01040a53=string/ui_translation_accessibility_translated_text\n01040a54=string/ui_translation_accessibility_translation_finished\n01040a55=string/unarchival_session_app_label\n01040a56=string/undo\n01040a57=string/unpin_specific_target\n01040a58=string/unpin_target\n01040a59=string/unread_convo_overflow\n01040a5a=string/unsupported_compile_sdk_check_update\n01040a5b=string/unsupported_compile_sdk_message\n01040a5c=string/unsupported_compile_sdk_show\n01040a5d=string/unsupported_display_size_message\n01040a5e=string/unsupported_display_size_show\n01040a5f=string/upload_file\n01040a60=string/usb_accessory_notification_title\n01040a61=string/usb_apm_usb_plugged_in_when_locked_notification_text\n01040a62=string/usb_apm_usb_plugged_in_when_locked_notification_title\n01040a63=string/usb_apm_usb_suspicious_activity_notification_text\n01040a64=string/usb_apm_usb_suspicious_activity_notification_title\n01040a65=string/usb_charging_notification_title\n01040a66=string/usb_contaminant_detected_message\n01040a67=string/usb_contaminant_detected_title\n01040a68=string/usb_contaminant_not_detected_message\n01040a69=string/usb_contaminant_not_detected_title\n01040a6a=string/usb_device_resolve_prompt_warn\n01040a6b=string/usb_midi_notification_title\n01040a6c=string/usb_midi_peripheral_manufacturer_name\n01040a6d=string/usb_midi_peripheral_name\n01040a6e=string/usb_midi_peripheral_product_name\n01040a6f=string/usb_mtp_launch_notification_description\n01040a70=string/usb_mtp_launch_notification_title\n01040a71=string/usb_mtp_notification_title\n01040a72=string/usb_notification_message\n01040a73=string/usb_power_notification_message\n01040a74=string/usb_ptp_notification_title\n01040a75=string/usb_supplying_notification_title\n01040a76=string/usb_tether_notification_title\n01040a77=string/usb_unsupported_audio_accessory_message\n01040a78=string/usb_unsupported_audio_accessory_title\n01040a79=string/usb_uvc_notification_title\n01040a7a=string/use_a_different_app\n01040a7b=string/user_creation_account_exists\n01040a7c=string/user_creation_adding\n01040a7d=string/user_logging_out_message\n01040a7e=string/user_owner_app_label\n01040a7f=string/user_owner_label\n01040a81=string/user_switching_message\n01040a82=string/validity_period\n01040a83=string/vdm_camera_access_denied\n01040a84=string/vdm_pip_blocked\n01040a85=string/vdm_secure_window\n01040a86=string/vendor_required_attestation_revocation_list_url\n01040a87=string/view_and_control_notification_content\n01040a88=string/view_and_control_notification_title\n01040a89=string/volume_alarm\n01040a8a=string/volume_bluetooth_call\n01040a8b=string/volume_call\n01040a8c=string/volume_dialog_ringer_guidance_silent\n01040a8d=string/volume_dialog_ringer_guidance_vibrate\n01040a8e=string/volume_icon_description_bluetooth\n01040a8f=string/volume_icon_description_incall\n01040a90=string/volume_icon_description_media\n01040a91=string/volume_icon_description_notification\n01040a92=string/volume_icon_description_ringer\n01040a93=string/volume_music\n01040a94=string/volume_music_hint_playing_through_bluetooth\n01040a95=string/volume_music_hint_silent_ringtone_selected\n01040a96=string/volume_notification\n01040a97=string/volume_ringtone\n01040a98=string/volume_unknown\n01040a99=string/vpn_lockdown_config\n01040a9a=string/vpn_lockdown_connected\n01040a9b=string/vpn_lockdown_connecting\n01040a9c=string/vpn_lockdown_disconnected\n01040a9d=string/vpn_lockdown_error\n01040a9e=string/vpn_text\n01040a9f=string/vpn_text_long\n01040aa0=string/vpn_title\n01040aa1=string/vpn_title_long\n01040aa2=string/vr_listener_binding_label\n01040aa3=string/wait\n01040aa4=string/wallpaper_binding_label\n01040aa5=string/webpage_unresponsive\n01040aa6=string/websearch\n01040aa7=string/week\n01040aa8=string/weeks\n01040aa9=string/wfcRegErrorTitle\n01040aaa=string/wfcSpnFormat\n01040aab=string/wfcSpnFormat_spn\n01040aac=string/wfcSpnFormat_spn_vowifi\n01040aad=string/wfcSpnFormat_spn_wifi\n01040aae=string/wfcSpnFormat_spn_wifi_calling\n01040aaf=string/wfcSpnFormat_spn_wifi_calling_vo_hyphen\n01040ab0=string/wfcSpnFormat_spn_wlan_call\n01040ab1=string/wfcSpnFormat_vowifi\n01040ab2=string/wfcSpnFormat_wifi\n01040ab3=string/wfcSpnFormat_wifi_call\n01040ab4=string/wfcSpnFormat_wifi_calling\n01040ab5=string/wfcSpnFormat_wifi_calling_bar_spn\n01040ab6=string/wfcSpnFormat_wifi_calling_wo_hyphen\n01040ab7=string/wfcSpnFormat_wlan_call\n01040ab8=string/wfc_mode_cellular_preferred_summary\n01040ab9=string/wfc_mode_wifi_only_summary\n01040aba=string/wfc_mode_wifi_preferred_summary\n01040abb=string/whichApplication\n01040abc=string/whichApplicationLabel\n01040abd=string/whichApplicationNamed\n01040abe=string/whichEditApplication\n01040abf=string/whichEditApplicationLabel\n01040ac0=string/whichEditApplicationNamed\n01040ac1=string/whichGiveAccessToApplicationLabel\n01040ac2=string/whichHomeApplication\n01040ac3=string/whichHomeApplicationLabel\n01040ac4=string/whichHomeApplicationNamed\n01040ac5=string/whichImageCaptureApplication\n01040ac6=string/whichImageCaptureApplicationLabel\n01040ac7=string/whichImageCaptureApplicationNamed\n01040ac8=string/whichOpenHostLinksWith\n01040ac9=string/whichOpenHostLinksWithApp\n01040aca=string/whichOpenLinksWith\n01040acb=string/whichOpenLinksWithApp\n01040acc=string/whichSendApplication\n01040acd=string/whichSendApplicationLabel\n01040ace=string/whichSendApplicationNamed\n01040acf=string/whichSendToApplication\n01040ad0=string/whichSendToApplicationLabel\n01040ad1=string/whichSendToApplicationNamed\n01040ad2=string/whichViewApplication\n01040ad3=string/whichViewApplicationLabel\n01040ad4=string/whichViewApplicationNamed\n01040ad5=string/widget_default_class_name\n01040ad6=string/widget_default_package_name\n01040ad7=string/wifi_available_sign_in\n01040ad8=string/wifi_calling_off_summary\n01040ad9=string/wifi_no_internet\n01040ada=string/wifi_no_internet_detailed\n01040adb=string/window_magnification_prompt_content\n01040adc=string/window_magnification_prompt_title\n01040add=string/wireless_display_route_description\n01040ade=string/work_mode_emergency_call_button\n01040adf=string/work_mode_off_title\n01040ae0=string/work_mode_turn_on\n01040ae1=string/work_profile_deleted\n01040ae2=string/work_profile_deleted_description_dpm_wipe\n01040ae3=string/work_profile_deleted_details\n01040ae4=string/work_profile_deleted_reason_maximum_password_failure\n01040ae5=string/work_profile_telephony_paused_text\n01040ae6=string/work_profile_telephony_paused_title\n01040ae7=string/work_profile_telephony_paused_turn_on_button\n01040ae8=string/write_fail_reason_cancelled\n01040ae9=string/write_fail_reason_cannot_write\n01040aea=string/wrong_hsum_configuration_notification_message\n01040aeb=string/wrong_hsum_configuration_notification_title\n01040aec=string/year\n01040aed=string/years\n01040aee=string/zen_mode_alarm\n01040aef=string/zen_mode_default_events_name\n01040af0=string/zen_mode_default_every_night_name\n01040af1=string/zen_mode_default_weekends_name\n01040af2=string/zen_mode_default_weeknights_name\n01040af3=string/zen_mode_downtime_feature_name\n01040af4=string/zen_mode_duration_hours\n01040af5=string/zen_mode_duration_hours_short\n01040af6=string/zen_mode_duration_hours_summary\n01040af7=string/zen_mode_duration_hours_summary_short\n01040af8=string/zen_mode_duration_minutes\n01040af9=string/zen_mode_duration_minutes_short\n01040afa=string/zen_mode_duration_minutes_summary\n01040afb=string/zen_mode_duration_minutes_summary_short\n01040afc=string/zen_mode_feature_name\n01040afd=string/zen_mode_forever\n01040afe=string/zen_mode_forever_dnd\n01040aff=string/zen_mode_implicit_activated\n01040b00=string/zen_mode_implicit_deactivated\n01040b01=string/zen_mode_implicit_name\n01040b02=string/zen_mode_implicit_trigger_description\n01040b03=string/zen_mode_rule_name_combination\n01040b04=string/zen_mode_trigger_event_calendar_any\n01040b05=string/zen_mode_trigger_summary_combined\n01040b06=string/zen_mode_trigger_summary_divider_text\n01040b07=string/zen_mode_trigger_summary_range_symbol_combination\n01040b08=string/zen_mode_trigger_summary_range_words\n01040b09=string/zen_mode_until\n01040b0a=string/zen_mode_until_next_day\n01050000=dimen/app_icon_size\n01050001=dimen/thumbnail_height\n01050002=dimen/thumbnail_width\n01050003=dimen/dialog_min_width_major\n01050004=dimen/dialog_min_width_minor\n01050005=dimen/notification_large_icon_width\n01050006=dimen/notification_large_icon_height\n01050007=dimen/config_restrictedIconSize\n01050008=dimen/system_app_widget_background_radius\n01050009=dimen/system_app_widget_inner_radius\n0105000a=dimen/config_viewConfigurationHandwritingGestureLineMargin\n0105000b=dimen/__removed_system_app_widget_internal_padding\n0105000c=dimen/accessibility_autoclick_scroll_panel_button_size\n0105000d=dimen/accessibility_autoclick_type_panel_button_size\n0105000e=dimen/accessibility_autoclick_type_panel_button_spacing\n0105000f=dimen/accessibility_autoclick_type_panel_divider_height\n01050010=dimen/accessibility_autoclick_type_panel_divider_width\n01050011=dimen/accessibility_focus_highlight_stroke_width\n01050012=dimen/accessibility_fullscreen_magnification_gesture_edge_slop\n01050013=dimen/accessibility_icon_foreground_padding_ratio\n01050014=dimen/accessibility_magnification_indicator_width\n01050015=dimen/accessibility_magnification_thumbnail_container_stroke_width\n01050016=dimen/accessibility_magnification_thumbnail_padding\n01050017=dimen/accessibility_touch_slop\n01050018=dimen/accessibility_window_magnifier_min_size\n01050019=dimen/action_bar_button_margin\n0105001a=dimen/action_bar_button_max_width\n0105001b=dimen/action_bar_content_inset_material\n0105001c=dimen/action_bar_content_inset_with_nav\n0105001d=dimen/action_bar_default_height\n0105001e=dimen/action_bar_default_height_material\n0105001f=dimen/action_bar_default_padding_end_material\n01050020=dimen/action_bar_default_padding_start_material\n01050021=dimen/action_bar_elevation_material\n01050022=dimen/action_bar_icon_vertical_padding\n01050023=dimen/action_bar_icon_vertical_padding_material\n01050024=dimen/action_bar_margin\n01050025=dimen/action_bar_margin_end\n01050026=dimen/action_bar_margin_start\n01050027=dimen/action_bar_overflow_padding_end_material\n01050028=dimen/action_bar_overflow_padding_start_material\n01050029=dimen/action_bar_stacked_max_height\n0105002a=dimen/action_bar_stacked_tab_max_width\n0105002b=dimen/action_bar_subtitle_bottom_margin\n0105002c=dimen/action_bar_subtitle_bottom_margin_material\n0105002d=dimen/action_bar_subtitle_text_size\n0105002e=dimen/action_bar_subtitle_top_margin\n0105002f=dimen/action_bar_subtitle_top_margin_material\n01050030=dimen/action_bar_title_text_size\n01050031=dimen/action_bar_toggle_internal_padding\n01050032=dimen/action_button_min_height_material\n01050033=dimen/action_button_min_width\n01050034=dimen/action_button_min_width_material\n01050035=dimen/action_button_min_width_overflow_material\n01050036=dimen/activity_chooser_popup_min_width\n01050037=dimen/activity_embedding_divider_handle_height\n01050038=dimen/activity_embedding_divider_handle_height_pressed\n01050039=dimen/activity_embedding_divider_handle_radius\n0105003a=dimen/activity_embedding_divider_handle_radius_pressed\n0105003b=dimen/activity_embedding_divider_handle_width\n0105003c=dimen/activity_embedding_divider_handle_width_pressed\n0105003d=dimen/activity_embedding_divider_touch_target_height\n0105003e=dimen/activity_embedding_divider_touch_target_width\n0105003f=dimen/aerr_list_item_height\n01050040=dimen/aerr_padding_list_bottom\n01050041=dimen/aerr_padding_list_top\n01050042=dimen/alertDialog_material_bottom_margin\n01050043=dimen/alertDialog_material_letter_spacing_body_1\n01050044=dimen/alertDialog_material_letter_spacing_title\n01050045=dimen/alertDialog_material_line_height_body_1\n01050046=dimen/alertDialog_material_line_height_title\n01050047=dimen/alertDialog_material_side_margin\n01050048=dimen/alertDialog_material_side_margin_body\n01050049=dimen/alertDialog_material_side_margin_title\n0105004a=dimen/alertDialog_material_text_size_body_1\n0105004b=dimen/alertDialog_material_text_size_title\n0105004c=dimen/alertDialog_material_top_margin\n0105004d=dimen/alert_dialog_button_bar_height\n0105004e=dimen/alert_dialog_button_bar_width\n0105004f=dimen/alert_dialog_round_padding\n01050050=dimen/alert_dialog_title_height\n01050051=dimen/ambient_shadow_alpha\n01050052=dimen/app_header_height\n01050053=dimen/autofill_button_bar_spacer_height\n01050054=dimen/autofill_button_bar_spacer_width\n01050055=dimen/autofill_dataset_picker_max_height\n01050056=dimen/autofill_dataset_picker_max_width\n01050057=dimen/autofill_dialog_corner_radius\n01050058=dimen/autofill_dialog_icon_max_height\n01050059=dimen/autofill_dialog_icon_size\n0105005a=dimen/autofill_dialog_max_width\n0105005b=dimen/autofill_dialog_offset\n0105005c=dimen/autofill_elevation\n0105005d=dimen/autofill_save_button_bar_padding\n0105005e=dimen/autofill_save_custom_subtitle_max_height\n0105005f=dimen/autofill_save_icon_max_height\n01050060=dimen/autofill_save_icon_size\n01050061=dimen/autofill_save_inner_padding\n01050062=dimen/autofill_save_outer_margin\n01050063=dimen/autofill_save_outer_top_padding\n01050064=dimen/autofill_save_scroll_view_top_margin\n01050065=dimen/autofill_save_title_start_padding\n01050066=dimen/back_progress_non_linear_factor\n01050067=dimen/base_error_dialog_bottom_padding\n01050068=dimen/base_error_dialog_contents_padding\n01050069=dimen/base_error_dialog_padding\n0105006a=dimen/base_error_dialog_top_padding\n0105006b=dimen/btn_drawable_padding\n0105006c=dimen/btn_horizontal_edge_padding\n0105006d=dimen/btn_lineHeight\n0105006e=dimen/btn_material_height\n0105006f=dimen/btn_material_width\n01050070=dimen/btn_textSize\n01050071=dimen/button_bar_layout_end_padding\n01050072=dimen/button_bar_layout_start_padding\n01050073=dimen/button_bar_layout_top_padding\n01050074=dimen/button_elevation_material\n01050075=dimen/button_end_margin\n01050076=dimen/button_inset_horizontal_material\n01050077=dimen/button_inset_vertical_material\n01050078=dimen/button_layout_height\n01050079=dimen/button_padding_horizontal_material\n0105007a=dimen/button_padding_vertical_material\n0105007b=dimen/button_pressed_z_material\n0105007c=dimen/call_notification_collapsible_indent\n0105007d=dimen/call_notification_system_action_min_width\n0105007e=dimen/car_action1_size\n0105007f=dimen/car_action2_size\n01050080=dimen/car_activity_resolver_corner_radius\n01050081=dimen/car_activity_resolver_list_item_height\n01050082=dimen/car_activity_resolver_list_max_height\n01050083=dimen/car_activity_resolver_width\n01050084=dimen/car_app_bar_height\n01050085=dimen/car_body1_size\n01050086=dimen/car_body2_size\n01050087=dimen/car_body3_size\n01050088=dimen/car_body4_size\n01050089=dimen/car_body5_size\n0105008a=dimen/car_borderless_button_horizontal_padding\n0105008b=dimen/car_button_height\n0105008c=dimen/car_button_horizontal_padding\n0105008d=dimen/car_button_min_width\n0105008e=dimen/car_button_radius\n0105008f=dimen/car_card_action_bar_height\n01050090=dimen/car_card_header_height\n01050091=dimen/car_dialog_action_bar_height\n01050092=dimen/car_double_line_list_item_height\n01050093=dimen/car_headline1_size\n01050094=dimen/car_headline2_size\n01050095=dimen/car_headline3_size\n01050096=dimen/car_headline4_size\n01050097=dimen/car_icon_size\n01050098=dimen/car_keyline_1\n01050099=dimen/car_keyline_1_keyline_3_diff\n0105009a=dimen/car_keyline_2\n0105009b=dimen/car_keyline_3\n0105009c=dimen/car_keyline_4\n0105009d=dimen/car_label1_size\n0105009e=dimen/car_label2_size\n0105009f=dimen/car_large_avatar_badge_size\n010500a0=dimen/car_large_avatar_size\n010500a1=dimen/car_list_divider_height\n010500a2=dimen/car_margin\n010500a3=dimen/car_padding_0\n010500a4=dimen/car_padding_1\n010500a5=dimen/car_padding_2\n010500a6=dimen/car_padding_3\n010500a7=dimen/car_padding_4\n010500a8=dimen/car_padding_5\n010500a9=dimen/car_padding_6\n010500aa=dimen/car_pill_button_size\n010500ab=dimen/car_preference_category_icon_size\n010500ac=dimen/car_preference_icon_size\n010500ad=dimen/car_preference_row_vertical_margin\n010500ae=dimen/car_primary_icon_size\n010500af=dimen/car_progress_bar_height\n010500b0=dimen/car_radius_1\n010500b1=dimen/car_radius_2\n010500b2=dimen/car_radius_3\n010500b3=dimen/car_radius_5\n010500b4=dimen/car_secondary_icon_size\n010500b5=dimen/car_seekbar_height\n010500b6=dimen/car_seekbar_padding\n010500b7=dimen/car_seekbar_text_overlap\n010500b8=dimen/car_seekbar_thumb_size\n010500b9=dimen/car_seekbar_thumb_stroke\n010500ba=dimen/car_single_line_list_item_height\n010500bb=dimen/car_switch_thumb_margin_size\n010500bc=dimen/car_switch_thumb_size\n010500bd=dimen/car_switch_track_margin_size\n010500be=dimen/car_textview_fading_edge_length\n010500bf=dimen/car_title2_size\n010500c0=dimen/car_title_size\n010500c1=dimen/car_touch_target_size\n010500c2=dimen/car_touch_target_size_minus_one\n010500c3=dimen/cascading_menus_min_smallest_width\n010500c4=dimen/chooser_action_button_icon_size\n010500c5=dimen/chooser_badge_size\n010500c6=dimen/chooser_corner_radius\n010500c7=dimen/chooser_direct_share_label_placeholder_max_width\n010500c8=dimen/chooser_edge_margin_normal\n010500c9=dimen/chooser_edge_margin_thin\n010500ca=dimen/chooser_grid_padding\n010500cb=dimen/chooser_header_scroll_elevation\n010500cc=dimen/chooser_icon_size\n010500cd=dimen/chooser_max_collapsed_height\n010500ce=dimen/chooser_preview_image_border\n010500cf=dimen/chooser_preview_image_font_size\n010500d0=dimen/chooser_preview_image_max_dimen\n010500d1=dimen/chooser_preview_width\n010500d2=dimen/chooser_row_text_option_translate\n010500d3=dimen/chooser_view_spacing\n010500d4=dimen/chooser_width\n010500d5=dimen/circular_display_mask_thickness\n010500d6=dimen/config_alertDialogSelectionScrollOffset\n010500d7=dimen/config_am_pssToRssThresholdModifier\n010500d8=dimen/config_ambiguousGestureMultiplier\n010500d9=dimen/config_appTransitionAnimationDurationScaleDefault\n010500da=dimen/config_autoKeyboardBrightnessSmoothingConstant\n010500db=dimen/config_backGestureInset\n010500dc=dimen/config_batterySaver_full_adjustBrightnessFactor\n010500dd=dimen/config_bottomDialogCornerRadius\n010500de=dimen/config_buttonCornerRadius\n010500df=dimen/config_closeToSquareDisplayMaxAspectRatio\n010500e0=dimen/config_defaultBinderHeavyHitterAutoSamplerThreshold\n010500e1=dimen/config_defaultBinderHeavyHitterWatcherThreshold\n010500e2=dimen/config_dialogCornerRadius\n010500e3=dimen/config_displayWhiteBalanceBrightnessFilterIntercept\n010500e4=dimen/config_displayWhiteBalanceColorTemperatureFilterIntercept\n010500e5=dimen/config_displayWhiteBalanceHighLightAmbientColorTemperature\n010500e6=dimen/config_displayWhiteBalanceHighLightAmbientColorTemperatureStrong\n010500e7=dimen/config_displayWhiteBalanceLowLightAmbientColorTemperature\n010500e8=dimen/config_displayWhiteBalanceLowLightAmbientColorTemperatureStrong\n010500e9=dimen/config_fixedOrientationLetterboxAspectRatio\n010500ea=dimen/config_hapticChannelMaxVibrationAmplitude\n010500eb=dimen/config_highResTaskSnapshotScale\n010500ec=dimen/config_horizontalScrollFactor\n010500ed=dimen/config_hoverTapSlop\n010500ee=dimen/config_inCallNotificationVolume\n010500ef=dimen/config_keyboardHapticFeedbackFixedAmplitude\n010500f0=dimen/config_letterboxBackgroundWallaperDarkScrimAlpha\n010500f1=dimen/config_letterboxBackgroundWallpaperBlurRadius\n010500f2=dimen/config_letterboxBookModePositionMultiplier\n010500f3=dimen/config_letterboxDefaultMinAspectRatioForUnresizableApps\n010500f4=dimen/config_letterboxHorizontalPositionMultiplier\n010500f5=dimen/config_letterboxTabletopModePositionMultiplier\n010500f6=dimen/config_letterboxThinLetterboxHeightDp\n010500f7=dimen/config_letterboxThinLetterboxWidthDp\n010500f8=dimen/config_letterboxVerticalPositionMultiplier\n010500f9=dimen/config_lowResTaskSnapshotScale\n010500fa=dimen/config_mediaMetadataBitmapMaxSize\n010500fb=dimen/config_minPercentageMultiWindowSupportHeight\n010500fc=dimen/config_minPercentageMultiWindowSupportWidth\n010500fd=dimen/config_minScalingSpan\n010500fe=dimen/config_minScalingTouchMajor\n010500ff=dimen/config_minScrollbarTouchTarget\n01050100=dimen/config_pictureInPictureExpandedHorizontalHeight\n01050101=dimen/config_pictureInPictureExpandedVerticalWidth\n01050102=dimen/config_pictureInPictureMaxAspectRatio\n01050103=dimen/config_pictureInPictureMinAspectRatio\n01050104=dimen/config_prefDialogWidth\n01050105=dimen/config_preferredHyphenationFrequency\n01050106=dimen/config_progressBarCornerRadius\n01050107=dimen/config_qsTileStrokeWidthActive\n01050108=dimen/config_qsTileStrokeWidthInactive\n01050109=dimen/config_resActivitySnapshotScale\n0105010a=dimen/config_rotaryEncoderAxisScrollTickInterval\n0105010b=dimen/config_screenBrightnessDimFloat\n0105010c=dimen/config_screenBrightnessDozeFloat\n0105010d=dimen/config_screenBrightnessMinimumDimAmountFloat\n0105010e=dimen/config_screenBrightnessSettingDefaultFloat\n0105010f=dimen/config_screenBrightnessSettingMaximumFloat\n01050110=dimen/config_screenBrightnessSettingMinimumFloat\n01050111=dimen/config_screen_magnification_scaling_threshold\n01050112=dimen/config_scrollFactor\n01050113=dimen/config_scrollFriction\n01050114=dimen/config_scrollbarSize\n01050115=dimen/config_signalCutoutHeightFraction\n01050116=dimen/config_signalCutoutWidthFraction\n01050117=dimen/config_verticalScrollFactor\n01050118=dimen/config_viewConfigurationHandwritingSlop\n01050119=dimen/config_viewConfigurationHoverSlop\n0105011a=dimen/config_viewConfigurationTouchSlop\n0105011b=dimen/config_viewMaxFlingVelocity\n0105011c=dimen/config_viewMaxRotaryEncoderFlingVelocity\n0105011d=dimen/config_viewMinFlingVelocity\n0105011e=dimen/config_viewMinRotaryEncoderFlingVelocity\n0105011f=dimen/config_wallpaperDimAmount\n01050120=dimen/config_wallpaperMaxScale\n01050121=dimen/config_wallpaperMinScale\n01050122=dimen/config_wearMaterial3_bottomDialogCornerRadius\n01050123=dimen/config_wearMaterial3_buttonCornerRadius\n01050124=dimen/config_windowManagerCameraCompatAspectRatio\n01050125=dimen/content_rect_bottom_clip_allowance\n01050126=dimen/control_corner_material\n01050127=dimen/control_inset_material\n01050128=dimen/control_padding_material\n01050129=dimen/controls_thumbnail_image_max_height\n0105012a=dimen/controls_thumbnail_image_max_width\n0105012b=dimen/conversation_avatar_size\n0105012c=dimen/conversation_avatar_size_group_expanded\n0105012d=dimen/conversation_badge_protrusion\n0105012e=dimen/conversation_badge_protrusion_group_expanded\n0105012f=dimen/conversation_badge_protrusion_group_expanded_face_pile\n01050130=dimen/conversation_compact_face_pile_avatar_size\n01050131=dimen/conversation_compact_face_pile_protection_width\n01050132=dimen/conversation_compact_face_pile_size\n01050133=dimen/conversation_content_start\n01050134=dimen/conversation_expand_button_height\n01050135=dimen/conversation_face_pile_avatar_size\n01050136=dimen/conversation_face_pile_avatar_size_group_expanded\n01050137=dimen/conversation_face_pile_protection_width\n01050138=dimen/conversation_face_pile_protection_width_expanded\n01050139=dimen/conversation_header_expanded_padding_end\n0105013a=dimen/conversation_icon_circle_start\n0105013b=dimen/conversation_icon_container_top_padding\n0105013c=dimen/conversation_icon_container_top_padding_small_avatar\n0105013d=dimen/conversation_icon_size_badged\n0105013e=dimen/conversation_image_start_margin\n0105013f=dimen/cross_profile_apps_thumbnail_size\n01050140=dimen/date_picker_date_label_size\n01050141=dimen/date_picker_day_height\n01050142=dimen/date_picker_day_of_week_height\n01050143=dimen/date_picker_day_of_week_text_size\n01050144=dimen/date_picker_day_selector_radius\n01050145=dimen/date_picker_day_text_size\n01050146=dimen/date_picker_day_width\n01050147=dimen/date_picker_month_height\n01050148=dimen/date_picker_month_text_size\n01050149=dimen/date_picker_year_label_size\n0105014a=dimen/datepicker_component_width\n0105014b=dimen/datepicker_dialog_width\n0105014c=dimen/datepicker_header_height\n0105014d=dimen/datepicker_header_text_size\n0105014e=dimen/datepicker_list_year_activated_label_size\n0105014f=dimen/datepicker_list_year_label_size\n01050150=dimen/datepicker_selected_date_day_size\n01050151=dimen/datepicker_selected_date_month_size\n01050152=dimen/datepicker_selected_date_year_size\n01050153=dimen/datepicker_view_animator_height\n01050154=dimen/datepicker_year_label_height\n01050155=dimen/day_picker_button_margin_top\n01050156=dimen/day_picker_padding_horizontal\n01050157=dimen/day_picker_padding_top\n01050158=dimen/default_app_widget_padding_bottom\n01050159=dimen/default_app_widget_padding_left\n0105015a=dimen/default_app_widget_padding_right\n0105015b=dimen/default_app_widget_padding_top\n0105015c=dimen/default_background_blur_radius\n0105015d=dimen/default_gap\n0105015e=dimen/default_magnifier_corner_radius\n0105015f=dimen/default_magnifier_elevation\n01050160=dimen/default_magnifier_height\n01050161=dimen/default_magnifier_horizontal_offset\n01050162=dimen/default_magnifier_vertical_offset\n01050163=dimen/default_magnifier_width\n01050164=dimen/default_magnifier_zoom\n01050165=dimen/default_minimal_size_resizable_task\n01050166=dimen/desktop_view_default_header_height\n01050167=dimen/dialog_btn_confirm_height\n01050168=dimen/dialog_btn_confirm_width\n01050169=dimen/dialog_btn_negative_height\n0105016a=dimen/dialog_btn_negative_width\n0105016b=dimen/dialog_corner_radius\n0105016c=dimen/dialog_fixed_height_major\n0105016d=dimen/dialog_fixed_height_minor\n0105016e=dimen/dialog_fixed_width_major\n0105016f=dimen/dialog_fixed_width_minor\n01050170=dimen/dialog_list_padding_bottom_no_buttons\n01050171=dimen/dialog_list_padding_top_no_title\n01050172=dimen/dialog_no_title_padding_top\n01050173=dimen/dialog_padding\n01050174=dimen/dialog_padding_material\n01050175=dimen/dialog_padding_top_material\n01050176=dimen/dialog_title_divider_material\n01050177=dimen/disabled_alpha_device_default\n01050178=dimen/disabled_alpha_leanback_formwizard\n01050179=dimen/disabled_alpha_material_dark\n0105017a=dimen/disabled_alpha_material_light\n0105017b=dimen/disabled_alpha_wear_material3\n0105017c=dimen/display_cutout_touchable_region_size\n0105017d=dimen/docked_stack_divider_insets\n0105017e=dimen/docked_stack_divider_thickness\n0105017f=dimen/docked_stack_minimize_thickness\n01050180=dimen/dropdownitem_icon_width\n01050181=dimen/dropdownitem_text_padding_left\n01050182=dimen/dropdownitem_text_padding_right\n01050183=dimen/edit_text_inset_bottom_material\n01050184=dimen/edit_text_inset_horizontal_material\n01050185=dimen/edit_text_inset_top_material\n01050186=dimen/emphasized_button_stroke_width\n01050187=dimen/expanded_group_conversation_message_padding\n01050188=dimen/face_unlock_height\n01050189=dimen/fast_scroller_minimum_touch_target\n0105018a=dimen/floating_toolbar_height\n0105018b=dimen/floating_toolbar_horizontal_margin\n0105018c=dimen/floating_toolbar_icon_text_spacing\n0105018d=dimen/floating_toolbar_maximum_overflow_height\n0105018e=dimen/floating_toolbar_menu_button_minimum_width\n0105018f=dimen/floating_toolbar_menu_button_side_padding\n01050190=dimen/floating_toolbar_menu_image_button_vertical_padding\n01050191=dimen/floating_toolbar_menu_image_button_width\n01050192=dimen/floating_toolbar_menu_image_width\n01050193=dimen/floating_toolbar_minimum_overflow_height\n01050194=dimen/floating_toolbar_overflow_image_button_width\n01050195=dimen/floating_toolbar_overflow_side_padding\n01050196=dimen/floating_toolbar_preferred_width\n01050197=dimen/floating_toolbar_text_size\n01050198=dimen/floating_toolbar_vertical_margin\n01050199=dimen/floating_window_margin_bottom\n0105019a=dimen/floating_window_margin_left\n0105019b=dimen/floating_window_margin_right\n0105019c=dimen/floating_window_margin_top\n0105019d=dimen/floating_window_z\n0105019e=dimen/glowpadview_target_placement_radius\n0105019f=dimen/handwriting_bounds_offset_bottom\n010501a0=dimen/handwriting_bounds_offset_left\n010501a1=dimen/handwriting_bounds_offset_right\n010501a2=dimen/handwriting_bounds_offset_top\n010501a3=dimen/harmful_app_icon_name_padding\n010501a4=dimen/harmful_app_icon_size\n010501a5=dimen/harmful_app_message_line_spacing_modifier\n010501a6=dimen/harmful_app_message_padding_bottom\n010501a7=dimen/harmful_app_message_padding_left\n010501a8=dimen/harmful_app_message_padding_right\n010501a9=dimen/harmful_app_name_padding_bottom\n010501aa=dimen/harmful_app_name_padding_left\n010501ab=dimen/harmful_app_name_padding_right\n010501ac=dimen/harmful_app_name_padding_top\n010501ad=dimen/harmful_app_padding_top\n010501ae=dimen/highlight_alpha_material_colored\n010501af=dimen/highlight_alpha_material_dark\n010501b0=dimen/highlight_alpha_material_light\n010501b1=dimen/image_margin_start\n010501b2=dimen/image_size\n010501b3=dimen/immersive_mode_cling_width\n010501b4=dimen/importance_ring_anim_max_stroke_width\n010501b5=dimen/importance_ring_size\n010501b6=dimen/importance_ring_stroke_width\n010501b7=dimen/indeterminate_progress_alpha_01\n010501b8=dimen/indeterminate_progress_alpha_02\n010501b9=dimen/indeterminate_progress_alpha_03\n010501ba=dimen/indeterminate_progress_alpha_04\n010501bb=dimen/indeterminate_progress_alpha_05\n010501bc=dimen/indeterminate_progress_alpha_06\n010501bd=dimen/indeterminate_progress_alpha_07\n010501be=dimen/indeterminate_progress_alpha_08\n010501bf=dimen/indeterminate_progress_alpha_09\n010501c0=dimen/indeterminate_progress_alpha_10\n010501c1=dimen/indeterminate_progress_alpha_11\n010501c2=dimen/indeterminate_progress_alpha_12\n010501c3=dimen/indeterminate_progress_alpha_13\n010501c4=dimen/indeterminate_progress_alpha_14\n010501c5=dimen/indeterminate_progress_alpha_15\n010501c6=dimen/indeterminate_progress_alpha_16\n010501c7=dimen/indeterminate_progress_alpha_17\n010501c8=dimen/indeterminate_progress_alpha_18\n010501c9=dimen/indeterminate_progress_alpha_19\n010501ca=dimen/indeterminate_progress_alpha_20\n010501cb=dimen/indeterminate_progress_alpha_21\n010501cc=dimen/indeterminate_progress_alpha_22\n010501cd=dimen/indeterminate_progress_alpha_23\n010501ce=dimen/indeterminate_progress_alpha_24\n010501cf=dimen/indeterminate_progress_alpha_25\n010501d0=dimen/indeterminate_progress_alpha_26\n010501d1=dimen/indeterminate_progress_alpha_27\n010501d2=dimen/indeterminate_progress_alpha_28\n010501d3=dimen/indeterminate_progress_alpha_29\n010501d4=dimen/indeterminate_progress_alpha_30\n010501d5=dimen/indeterminate_progress_alpha_31\n010501d6=dimen/indeterminate_progress_alpha_32\n010501d7=dimen/indeterminate_progress_alpha_33\n010501d8=dimen/indeterminate_progress_alpha_34\n010501d9=dimen/indeterminate_progress_alpha_35\n010501da=dimen/indeterminate_progress_alpha_36\n010501db=dimen/indeterminate_progress_alpha_37\n010501dc=dimen/indeterminate_progress_alpha_38\n010501dd=dimen/indeterminate_progress_alpha_39\n010501de=dimen/indeterminate_progress_alpha_40\n010501df=dimen/indeterminate_progress_alpha_41\n010501e0=dimen/indeterminate_progress_alpha_42\n010501e1=dimen/indeterminate_progress_alpha_43\n010501e2=dimen/indeterminate_progress_alpha_44\n010501e3=dimen/indeterminate_progress_alpha_45\n010501e4=dimen/indeterminate_progress_alpha_46\n010501e5=dimen/indeterminate_progress_alpha_47\n010501e6=dimen/indeterminate_progress_alpha_48\n010501e7=dimen/indeterminate_progress_alpha_49\n010501e8=dimen/indeterminate_progress_alpha_50\n010501e9=dimen/indeterminate_progress_alpha_51\n010501ea=dimen/indeterminate_progress_alpha_52\n010501eb=dimen/indeterminate_progress_alpha_53\n010501ec=dimen/indeterminate_progress_alpha_54\n010501ed=dimen/indeterminate_progress_alpha_55\n010501ee=dimen/indeterminate_progress_alpha_56\n010501ef=dimen/indeterminate_progress_alpha_57\n010501f0=dimen/indeterminate_progress_alpha_58\n010501f1=dimen/indeterminate_progress_alpha_59\n010501f2=dimen/indeterminate_progress_alpha_60\n010501f3=dimen/input_extract_action_button_height\n010501f4=dimen/input_extract_action_button_width\n010501f5=dimen/input_extract_action_icon_padding\n010501f6=dimen/input_method_divider_height\n010501f7=dimen/input_method_nav_content_padding\n010501f8=dimen/input_method_nav_key_button_ripple_max_width\n010501f9=dimen/input_method_navigation_key_padding\n010501fa=dimen/input_method_navigation_key_width\n010501fb=dimen/item_touch_helper_max_drag_scroll_per_frame\n010501fc=dimen/item_touch_helper_swipe_escape_max_velocity\n010501fd=dimen/item_touch_helper_swipe_escape_velocity\n010501fe=dimen/keyguard_avatar_frame_shadow_radius\n010501ff=dimen/keyguard_avatar_frame_stroke_width\n01050200=dimen/keyguard_avatar_name_size\n01050201=dimen/keyguard_avatar_size\n01050202=dimen/keyguard_lockscreen_clock_font_size\n01050203=dimen/keyguard_lockscreen_outerring_diameter\n01050204=dimen/keyguard_lockscreen_pin_margin_left\n01050205=dimen/keyguard_lockscreen_status_line_clockfont_bottom_margin\n01050206=dimen/keyguard_lockscreen_status_line_clockfont_top_margin\n01050207=dimen/keyguard_lockscreen_status_line_font_right_margin\n01050208=dimen/keyguard_lockscreen_status_line_font_size\n01050209=dimen/keyguard_muliuser_selector_margin\n0105020a=dimen/keyguard_pattern_unlock_clock_font_size\n0105020b=dimen/keyguard_pattern_unlock_status_line_font_size\n0105020c=dimen/kg_clock_top_margin\n0105020d=dimen/kg_edge_swipe_region_size\n0105020e=dimen/kg_emergency_button_shift\n0105020f=dimen/kg_key_horizontal_gap\n01050210=dimen/kg_key_vertical_gap\n01050211=dimen/kg_pin_key_height\n01050212=dimen/kg_runway_lights_height\n01050213=dimen/kg_runway_lights_top_margin\n01050214=dimen/kg_runway_lights_vertical_padding\n01050215=dimen/kg_secure_padding_height\n01050216=dimen/kg_security_panel_height\n01050217=dimen/kg_security_view_height\n01050218=dimen/kg_small_widget_height\n01050219=dimen/kg_squashed_layout_threshold\n0105021a=dimen/kg_status_clock_font_size\n0105021b=dimen/kg_status_date_font_size\n0105021c=dimen/kg_status_line_font_right_margin\n0105021d=dimen/kg_status_line_font_size\n0105021e=dimen/kg_widget_pager_bottom_padding\n0105021f=dimen/kg_widget_pager_horizontal_padding\n01050220=dimen/kg_widget_pager_top_padding\n01050221=dimen/kg_widget_view_height\n01050222=dimen/kg_widget_view_width\n01050223=dimen/leanback_button_height\n01050224=dimen/leanback_button_padding_horizontal\n01050225=dimen/leanback_button_padding_vertical\n01050226=dimen/leanback_button_radius\n01050227=dimen/leanback_dialog_corner_radius\n01050228=dimen/leanback_setup_alpha_activity_in_bkg_end\n01050229=dimen/leanback_setup_alpha_activity_in_bkg_start\n0105022a=dimen/leanback_setup_alpha_activity_out_bkg_end\n0105022b=dimen/leanback_setup_alpha_activity_out_bkg_start\n0105022c=dimen/leanback_setup_alpha_animiation_max_opacity\n0105022d=dimen/leanback_setup_alpha_animiation_min_opacity\n0105022e=dimen/leanback_setup_alpha_backward_in_content_end\n0105022f=dimen/leanback_setup_alpha_backward_in_content_start\n01050230=dimen/leanback_setup_alpha_backward_out_content_end\n01050231=dimen/leanback_setup_alpha_backward_out_content_start\n01050232=dimen/leanback_setup_alpha_forward_in_content_end\n01050233=dimen/leanback_setup_alpha_forward_in_content_start\n01050234=dimen/leanback_setup_alpha_forward_out_content_end\n01050235=dimen/leanback_setup_alpha_forward_out_content_start\n01050236=dimen/leanback_setup_translation_backward_out_content_end\n01050237=dimen/leanback_setup_translation_backward_out_content_end_v4\n01050238=dimen/leanback_setup_translation_backward_out_content_start\n01050239=dimen/leanback_setup_translation_backward_out_content_start_v4\n0105023a=dimen/leanback_setup_translation_content_cliff\n0105023b=dimen/leanback_setup_translation_content_resting_point\n0105023c=dimen/leanback_setup_translation_forward_in_content_end\n0105023d=dimen/leanback_setup_translation_forward_in_content_end_v4\n0105023e=dimen/leanback_setup_translation_forward_in_content_start\n0105023f=dimen/leanback_setup_translation_forward_in_content_start_v4\n01050240=dimen/light_radius\n01050241=dimen/light_y\n01050242=dimen/light_z\n01050243=dimen/list_item_padding_end_material\n01050244=dimen/list_item_padding_horizontal_material\n01050245=dimen/list_item_padding_start_material\n01050246=dimen/list_menu_item_icon_max_width\n01050247=dimen/loader_horizontal_min_height_watch\n01050248=dimen/loader_horizontal_min_width_watch\n01050249=dimen/lock_pattern_dot_hit_factor\n0105024a=dimen/lock_pattern_dot_line_width\n0105024b=dimen/lock_pattern_dot_size\n0105024c=dimen/lock_pattern_dot_size_activated\n0105024d=dimen/lock_pattern_fade_away_gradient_width\n0105024e=dimen/media_notification_action_button_size\n0105024f=dimen/media_notification_actions_padding_bottom\n01050250=dimen/media_notification_expanded_image_margin_bottom\n01050251=dimen/media_notification_expanded_image_max_size\n01050252=dimen/media_notification_header_height\n01050253=dimen/message_progress_dialog_bottom_padding\n01050254=dimen/message_progress_dialog_end_padding\n01050255=dimen/message_progress_dialog_letter_spacing\n01050256=dimen/message_progress_dialog_start_padding\n01050257=dimen/message_progress_dialog_text_size\n01050258=dimen/message_progress_dialog_top_padding\n01050259=dimen/messaging_avatar_size\n0105025a=dimen/messaging_group_sending_progress_size\n0105025b=dimen/messaging_group_singleline_sender_padding_end\n0105025c=dimen/messaging_image_extra_spacing\n0105025d=dimen/messaging_image_max_height\n0105025e=dimen/messaging_image_min_size\n0105025f=dimen/messaging_image_rounding\n01050260=dimen/messaging_layout_icon_padding_start\n01050261=dimen/min_xlarge_screen_width\n01050262=dimen/navigation_bar_frame_height\n01050263=dimen/navigation_bar_frame_height_landscape\n01050264=dimen/navigation_bar_gesture_height\n01050265=dimen/navigation_bar_gesture_larger_height\n01050266=dimen/navigation_bar_height\n01050267=dimen/navigation_bar_height_car_mode\n01050268=dimen/navigation_bar_height_landscape\n01050269=dimen/navigation_bar_height_landscape_car_mode\n0105026a=dimen/navigation_bar_height_portrait\n0105026b=dimen/navigation_bar_width\n0105026c=dimen/navigation_bar_width_car_mode\n0105026d=dimen/navigation_edge_action_progress_threshold\n0105026e=dimen/notification_2025_action_list_height\n0105026f=dimen/notification_2025_action_list_margin_bottom\n01050270=dimen/notification_2025_action_list_min_height\n01050271=dimen/notification_2025_action_text_size\n01050272=dimen/notification_2025_actions_icon_size\n01050273=dimen/notification_2025_actions_margin_start\n01050274=dimen/notification_2025_additional_margin\n01050275=dimen/notification_2025_badge_baseline\n01050276=dimen/notification_2025_badge_margin\n01050277=dimen/notification_2025_badge_size\n01050278=dimen/notification_2025_content_margin_start\n01050279=dimen/notification_2025_content_margin_top\n0105027a=dimen/notification_2025_content_min_height\n0105027b=dimen/notification_2025_conversation_icon_badge_padding\n0105027c=dimen/notification_2025_conversation_icon_badge_position\n0105027d=dimen/notification_2025_conversation_icon_badge_size\n0105027e=dimen/notification_2025_expand_button_horizontal_icon_padding\n0105027f=dimen/notification_2025_expand_button_icon_size\n01050280=dimen/notification_2025_expand_button_pill_height\n01050281=dimen/notification_2025_expand_button_pill_width\n01050282=dimen/notification_2025_expand_button_reduced_end_padding\n01050283=dimen/notification_2025_expand_button_right_icon_spacing\n01050284=dimen/notification_2025_expand_button_vertical_icon_padding\n01050285=dimen/notification_2025_face_pile_avatar_size\n01050286=dimen/notification_2025_header_height\n01050287=dimen/notification_2025_icon_circle_padding\n01050288=dimen/notification_2025_icon_circle_size\n01050289=dimen/notification_2025_left_icon_size\n0105028a=dimen/notification_2025_margin\n0105028b=dimen/notification_2025_media_actions_margin_start\n0105028c=dimen/notification_2025_messaging_spacing\n0105028d=dimen/notification_2025_min_height\n0105028e=dimen/notification_2025_reduced_margin\n0105028f=dimen/notification_2025_right_icon_content_margin\n01050290=dimen/notification_2025_right_icon_expanded_margin_end\n01050291=dimen/notification_2025_right_icon_vertical_margin\n01050292=dimen/notification_2025_smart_reply_container_margin\n01050293=dimen/notification_action_button_radius\n01050294=dimen/notification_action_disabled_alpha\n01050295=dimen/notification_action_disabled_container_alpha\n01050296=dimen/notification_action_disabled_content_alpha\n01050297=dimen/notification_action_emphasized_height\n01050298=dimen/notification_action_list_height\n01050299=dimen/notification_action_list_margin_top\n0105029a=dimen/notification_actions_collapsed_priority_width\n0105029b=dimen/notification_actions_icon_drawable_size\n0105029c=dimen/notification_actions_icon_size\n0105029d=dimen/notification_actions_padding_start\n0105029e=dimen/notification_alerted_size\n0105029f=dimen/notification_badge_size\n010502a0=dimen/notification_big_picture_max_height\n010502a1=dimen/notification_big_picture_max_height_low_ram\n010502a2=dimen/notification_big_picture_max_width\n010502a3=dimen/notification_big_picture_max_width_low_ram\n010502a4=dimen/notification_big_title_text_size\n010502a5=dimen/notification_close_button_padding\n010502a6=dimen/notification_close_button_size\n010502a7=dimen/notification_collapsed_height_with_summarization\n010502a8=dimen/notification_content_margin\n010502a9=dimen/notification_content_margin_end\n010502aa=dimen/notification_content_margin_start\n010502ab=dimen/notification_content_margin_top\n010502ac=dimen/notification_conversation_header_separating_margin\n010502ad=dimen/notification_custom_view_max_image_height\n010502ae=dimen/notification_custom_view_max_image_height_low_ram\n010502af=dimen/notification_custom_view_max_image_width\n010502b0=dimen/notification_custom_view_max_image_width_low_ram\n010502b1=dimen/notification_expand_button_icon_padding\n010502b2=dimen/notification_expand_button_pill_height\n010502b3=dimen/notification_feedback_size\n010502b4=dimen/notification_grayscale_icon_max_size\n010502b5=dimen/notification_header_app_name_margin_start\n010502b6=dimen/notification_header_background_height\n010502b7=dimen/notification_header_expand_icon_size\n010502b8=dimen/notification_header_height\n010502b9=dimen/notification_header_icon_size\n010502ba=dimen/notification_header_icon_size_ambient\n010502bb=dimen/notification_header_margin_bottom\n010502bc=dimen/notification_header_padding_bottom\n010502bd=dimen/notification_header_padding_top\n010502be=dimen/notification_header_separating_margin\n010502bf=dimen/notification_header_shrink_hide_width\n010502c0=dimen/notification_header_shrink_min_width\n010502c1=dimen/notification_header_touchable_height\n010502c2=dimen/notification_headerless_line_height\n010502c3=dimen/notification_headerless_margin_oneline\n010502c4=dimen/notification_headerless_margin_twoline\n010502c5=dimen/notification_headerless_min_height\n010502c6=dimen/notification_heading_margin_end\n010502c7=dimen/notification_icon_circle_padding\n010502c8=dimen/notification_icon_circle_size\n010502c9=dimen/notification_icon_circle_start\n010502ca=dimen/notification_inbox_item_top_padding\n010502cb=dimen/notification_left_icon_size\n010502cc=dimen/notification_left_icon_start\n010502cd=dimen/notification_messaging_spacing\n010502ce=dimen/notification_messaging_spacing_conversation_group\n010502cf=dimen/notification_min_height\n010502d0=dimen/notification_person_icon_max_size\n010502d1=dimen/notification_person_icon_max_size_low_ram\n010502d2=dimen/notification_phishing_alert_size\n010502d3=dimen/notification_progress_bar_height\n010502d4=dimen/notification_progress_icon_size\n010502d5=dimen/notification_progress_margin_horizontal\n010502d6=dimen/notification_progress_margin_top\n010502d7=dimen/notification_progress_points_corner_radius\n010502d8=dimen/notification_progress_points_inset\n010502d9=dimen/notification_progress_points_radius\n010502da=dimen/notification_progress_segPoint_gap\n010502db=dimen/notification_progress_segSeg_gap\n010502dc=dimen/notification_progress_segments_corner_radius\n010502dd=dimen/notification_progress_segments_faded_height\n010502de=dimen/notification_progress_segments_height\n010502df=dimen/notification_progress_segments_min_width\n010502e0=dimen/notification_progress_tracker_height\n010502e1=dimen/notification_progress_tracker_width\n010502e2=dimen/notification_right_icon_big_margin_top\n010502e3=dimen/notification_right_icon_content_margin\n010502e4=dimen/notification_right_icon_headerless_margin\n010502e5=dimen/notification_right_icon_size\n010502e6=dimen/notification_right_icon_size_low_ram\n010502e7=dimen/notification_secondary_text_disabled_alpha\n010502e8=dimen/notification_small_icon_size\n010502e9=dimen/notification_small_icon_size_low_ram\n010502ea=dimen/notification_subtext_size\n010502eb=dimen/notification_text_height\n010502ec=dimen/notification_text_margin_top\n010502ed=dimen/notification_text_size\n010502ee=dimen/notification_title_text_size\n010502ef=dimen/notification_top_pad\n010502f0=dimen/notification_top_pad_large_text\n010502f1=dimen/notification_top_pad_large_text_narrow\n010502f2=dimen/notification_top_pad_narrow\n010502f3=dimen/notification_verification_icon_size\n010502f4=dimen/password_keyboard_height\n010502f5=dimen/password_keyboard_horizontalGap\n010502f6=dimen/password_keyboard_key_height_alpha\n010502f7=dimen/password_keyboard_key_height_numeric\n010502f8=dimen/password_keyboard_spacebar_vertical_correction\n010502f9=dimen/password_keyboard_verticalGap\n010502fa=dimen/picker_bottom_margin\n010502fb=dimen/picker_top_margin\n010502fc=dimen/pip_minimized_visible_size\n010502fd=dimen/popup_enter_animation_from_y_delta\n010502fe=dimen/popup_exit_animation_to_y_delta\n010502ff=dimen/preference_breadcrumb_paddingLeft\n01050300=dimen/preference_breadcrumb_paddingRight\n01050301=dimen/preference_breadcrumbs_padding_end_material\n01050302=dimen/preference_breadcrumbs_padding_start_material\n01050303=dimen/preference_child_padding_side\n01050304=dimen/preference_fragment_padding_bottom\n01050305=dimen/preference_fragment_padding_side\n01050306=dimen/preference_fragment_padding_side_material\n01050307=dimen/preference_fragment_padding_vertical_material\n01050308=dimen/preference_icon_minWidth\n01050309=dimen/preference_item_padding_inner\n0105030a=dimen/preference_item_padding_side\n0105030b=dimen/preference_screen_bottom_margin\n0105030c=dimen/preference_screen_header_padding_side\n0105030d=dimen/preference_screen_header_padding_side_material\n0105030e=dimen/preference_screen_header_vertical_padding\n0105030f=dimen/preference_screen_header_vertical_padding_material\n01050310=dimen/preference_screen_side_margin\n01050311=dimen/preference_screen_side_margin_material\n01050312=dimen/preference_screen_side_margin_negative\n01050313=dimen/preference_screen_side_margin_negative_material\n01050314=dimen/preference_screen_top_margin\n01050315=dimen/preference_widget_width\n01050316=dimen/primary_content_alpha_device_default\n01050317=dimen/primary_content_alpha_material_dark\n01050318=dimen/primary_content_alpha_material_light\n01050319=dimen/primary_content_alpha_wear_material3\n0105031a=dimen/progress_bar_corner_material\n0105031b=dimen/progress_bar_height\n0105031c=dimen/progress_bar_height_material\n0105031d=dimen/progress_bar_size_large\n0105031e=dimen/progress_bar_size_medium\n0105031f=dimen/progress_bar_size_small\n01050320=dimen/progressbar_elevation\n01050321=dimen/progressbar_inner_radius_ratio\n01050322=dimen/progressbar_thickness\n01050323=dimen/quick_qs_offset_height\n01050324=dimen/resolver_badge_size\n01050325=dimen/resolver_button_bar_spacing\n01050326=dimen/resolver_edge_margin\n01050327=dimen/resolver_elevation\n01050328=dimen/resolver_empty_state_container_padding_bottom\n01050329=dimen/resolver_empty_state_container_padding_top\n0105032a=dimen/resolver_empty_state_height\n0105032b=dimen/resolver_empty_state_height_with_tabs\n0105032c=dimen/resolver_icon_margin\n0105032d=dimen/resolver_icon_size\n0105032e=dimen/resolver_max_collapsed_height\n0105032f=dimen/resolver_max_collapsed_height_with_default\n01050330=dimen/resolver_max_collapsed_height_with_default_with_tabs\n01050331=dimen/resolver_max_collapsed_height_with_tabs\n01050332=dimen/resolver_max_width\n01050333=dimen/resolver_profile_tab_margin\n01050334=dimen/resolver_small_margin\n01050335=dimen/resolver_tab_text_size\n01050336=dimen/resolver_title_padding_bottom\n01050337=dimen/restricted_icon_size_material\n01050338=dimen/round_scrollbar_width\n01050339=dimen/rounded_corner_content_padding\n0105033a=dimen/rounded_corner_radius\n0105033b=dimen/rounded_corner_radius_adjustment\n0105033c=dimen/rounded_corner_radius_bottom\n0105033d=dimen/rounded_corner_radius_bottom_adjustment\n0105033e=dimen/rounded_corner_radius_top\n0105033f=dimen/rounded_corner_radius_top_adjustment\n01050340=dimen/screen_percentage_0416\n01050341=dimen/screen_percentage_05\n01050342=dimen/screen_percentage_052\n01050343=dimen/screen_percentage_10\n01050344=dimen/screen_percentage_12\n01050345=dimen/screen_percentage_15\n01050346=dimen/screen_percentage_3646\n01050347=dimen/search_view_preferred_height\n01050348=dimen/search_view_preferred_width\n01050349=dimen/secondary_content_alpha_device_default\n0105034a=dimen/secondary_content_alpha_material_dark\n0105034b=dimen/secondary_content_alpha_material_light\n0105034c=dimen/secondary_rounded_corner_radius\n0105034d=dimen/secondary_rounded_corner_radius_adjustment\n0105034e=dimen/secondary_rounded_corner_radius_bottom\n0105034f=dimen/secondary_rounded_corner_radius_bottom_adjustment\n01050350=dimen/secondary_rounded_corner_radius_top\n01050351=dimen/secondary_rounded_corner_radius_top_adjustment\n01050352=dimen/secondary_waterfall_display_bottom_edge_size\n01050353=dimen/secondary_waterfall_display_left_edge_size\n01050354=dimen/secondary_waterfall_display_right_edge_size\n01050355=dimen/secondary_waterfall_display_top_edge_size\n01050356=dimen/seekbar_thumb_exclusion_max_size\n01050357=dimen/seekbar_track_background_height_material\n01050358=dimen/seekbar_track_progress_height_material\n01050359=dimen/select_dialog_drawable_padding_start_material\n0105035a=dimen/select_dialog_padding_start_material\n0105035b=dimen/slice_icon_size\n0105035c=dimen/slice_padding\n0105035d=dimen/slice_shortcut_size\n0105035e=dimen/snooze_and_bubble_gone_padding_end\n0105035f=dimen/spot_shadow_alpha\n01050360=dimen/starting_surface_default_icon_size\n01050361=dimen/starting_surface_icon_size\n01050362=dimen/status_bar_content_number_size\n01050363=dimen/status_bar_edge_ignore\n01050364=dimen/status_bar_height\n01050365=dimen/status_bar_height_default\n01050366=dimen/status_bar_height_landscape\n01050367=dimen/status_bar_height_portrait\n01050368=dimen/status_bar_icon_size\n01050369=dimen/status_bar_icon_size_sp\n0105036a=dimen/status_bar_system_icon_intrinsic_size\n0105036b=dimen/status_bar_system_icon_size\n0105036c=dimen/subtitle_corner_radius\n0105036d=dimen/subtitle_outline_width\n0105036e=dimen/subtitle_shadow_offset\n0105036f=dimen/subtitle_shadow_radius\n01050370=dimen/system_gestures_distance_threshold\n01050371=dimen/system_gestures_start_threshold\n01050372=dimen/task_height_of_minimized_mode\n01050373=dimen/taskbar_frame_height\n01050374=dimen/text_edit_floating_toolbar_elevation\n01050375=dimen/text_edit_floating_toolbar_margin\n01050376=dimen/text_handle_min_size\n01050377=dimen/text_line_spacing_multiplier_material\n01050378=dimen/text_size_body_1_material\n01050379=dimen/text_size_body_2_material\n0105037a=dimen/text_size_button_material\n0105037b=dimen/text_size_caption_material\n0105037c=dimen/text_size_display_1_material\n0105037d=dimen/text_size_display_2_material\n0105037e=dimen/text_size_display_3_material\n0105037f=dimen/text_size_display_4_material\n01050380=dimen/text_size_headline_material\n01050381=dimen/text_size_large_material\n01050382=dimen/text_size_medium_material\n01050383=dimen/text_size_menu_header_material\n01050384=dimen/text_size_menu_material\n01050385=dimen/text_size_small_material\n01050386=dimen/text_size_subhead_material\n01050387=dimen/text_size_subtitle_material_toolbar\n01050388=dimen/text_size_title_material\n01050389=dimen/text_size_title_material_toolbar\n0105038a=dimen/text_view_end_margin\n0105038b=dimen/text_view_start_margin\n0105038c=dimen/textview_error_popup_default_width\n0105038d=dimen/timepicker_am_top_padding\n0105038e=dimen/timepicker_ampm_horizontal_padding\n0105038f=dimen/timepicker_ampm_label_size\n01050390=dimen/timepicker_center_dot_radius\n01050391=dimen/timepicker_edit_text_size\n01050392=dimen/timepicker_header_height\n01050393=dimen/timepicker_left_side_width\n01050394=dimen/timepicker_pm_top_padding\n01050395=dimen/timepicker_radial_picker_dimen\n01050396=dimen/timepicker_radial_picker_horizontal_margin\n01050397=dimen/timepicker_radial_picker_top_margin\n01050398=dimen/timepicker_selector_dot_radius\n01050399=dimen/timepicker_selector_radius\n0105039a=dimen/timepicker_selector_stroke\n0105039b=dimen/timepicker_separator_padding\n0105039c=dimen/timepicker_text_inset_inner\n0105039d=dimen/timepicker_text_inset_normal\n0105039e=dimen/timepicker_text_size_inner\n0105039f=dimen/timepicker_text_size_normal\n010503a0=dimen/timepicker_time_label_size\n010503a1=dimen/toast_elevation\n010503a2=dimen/toast_text_size\n010503a3=dimen/toast_width\n010503a4=dimen/toast_y_offset\n010503a5=dimen/tooltip_corner_radius\n010503a6=dimen/tooltip_corner_radius_material\n010503a7=dimen/tooltip_font_size\n010503a8=dimen/tooltip_font_size_material\n010503a9=dimen/tooltip_horizontal_padding\n010503aa=dimen/tooltip_horizontal_padding_material\n010503ab=dimen/tooltip_margin\n010503ac=dimen/tooltip_precise_anchor_extra_offset\n010503ad=dimen/tooltip_precise_anchor_threshold\n010503ae=dimen/tooltip_vertical_padding\n010503af=dimen/tooltip_vertical_padding_material\n010503b0=dimen/tooltip_y_offset_non_touch\n010503b1=dimen/tooltip_y_offset_touch\n010503b2=dimen/user_icon_size\n010503b3=dimen/waterfall_display_bottom_edge_size\n010503b4=dimen/waterfall_display_left_edge_size\n010503b5=dimen/waterfall_display_right_edge_size\n010503b6=dimen/waterfall_display_top_edge_size\n01060000=color/darker_gray\n01060001=color/primary_text_dark\n01060002=color/primary_text_dark_nodisable\n01060003=color/primary_text_light\n01060004=color/primary_text_light_nodisable\n01060005=color/secondary_text_dark\n01060006=color/secondary_text_dark_nodisable\n01060007=color/secondary_text_light\n01060008=color/secondary_text_light_nodisable\n01060009=color/tab_indicator_text\n0106000a=color/widget_edittext_dark\n0106000b=color/white\n0106000c=color/black\n0106000d=color/transparent\n0106000e=color/background_dark\n0106000f=color/background_light\n01060010=color/tertiary_text_dark\n01060011=color/tertiary_text_light\n01060012=color/holo_blue_light\n01060013=color/holo_blue_dark\n01060014=color/holo_green_light\n01060015=color/holo_green_dark\n01060016=color/holo_red_light\n01060017=color/holo_red_dark\n01060018=color/holo_orange_light\n01060019=color/holo_orange_dark\n0106001a=color/holo_purple\n0106001b=color/holo_blue_bright\n0106001c=color/system_notification_accent_color\n0106001d=color/system_neutral1_0\n0106001e=color/system_neutral1_10\n0106001f=color/system_neutral1_50\n01060020=color/system_neutral1_100\n01060021=color/system_neutral1_200\n01060022=color/system_neutral1_300\n01060023=color/system_neutral1_400\n01060024=color/system_neutral1_500\n01060025=color/system_neutral1_600\n01060026=color/system_neutral1_700\n01060027=color/system_neutral1_800\n01060028=color/system_neutral1_900\n01060029=color/system_neutral1_1000\n0106002a=color/system_neutral2_0\n0106002b=color/system_neutral2_10\n0106002c=color/system_neutral2_50\n0106002d=color/system_neutral2_100\n0106002e=color/system_neutral2_200\n0106002f=color/system_neutral2_300\n01060030=color/system_neutral2_400\n01060031=color/system_neutral2_500\n01060032=color/system_neutral2_600\n01060033=color/system_neutral2_700\n01060034=color/system_neutral2_800\n01060035=color/system_neutral2_900\n01060036=color/system_neutral2_1000\n01060037=color/system_accent1_0\n01060038=color/system_accent1_10\n01060039=color/system_accent1_50\n0106003a=color/system_accent1_100\n0106003b=color/system_accent1_200\n0106003c=color/system_accent1_300\n0106003d=color/system_accent1_400\n0106003e=color/system_accent1_500\n0106003f=color/system_accent1_600\n01060040=color/system_accent1_700\n01060041=color/system_accent1_800\n01060042=color/system_accent1_900\n01060043=color/system_accent1_1000\n01060044=color/system_accent2_0\n01060045=color/system_accent2_10\n01060046=color/system_accent2_50\n01060047=color/system_accent2_100\n01060048=color/system_accent2_200\n01060049=color/system_accent2_300\n0106004a=color/system_accent2_400\n0106004b=color/system_accent2_500\n0106004c=color/system_accent2_600\n0106004d=color/system_accent2_700\n0106004e=color/system_accent2_800\n0106004f=color/system_accent2_900\n01060050=color/system_accent2_1000\n01060051=color/system_accent3_0\n01060052=color/system_accent3_10\n01060053=color/system_accent3_50\n01060054=color/system_accent3_100\n01060055=color/system_accent3_200\n01060056=color/system_accent3_300\n01060057=color/system_accent3_400\n01060058=color/system_accent3_500\n01060059=color/system_accent3_600\n0106005a=color/system_accent3_700\n0106005b=color/system_accent3_800\n0106005c=color/system_accent3_900\n0106005d=color/system_accent3_1000\n0106005e=color/system_primary_container_light\n0106005f=color/system_on_primary_container_light\n01060060=color/system_primary_light\n01060061=color/system_on_primary_light\n01060062=color/system_secondary_container_light\n01060063=color/system_on_secondary_container_light\n01060064=color/system_secondary_light\n01060065=color/system_on_secondary_light\n01060066=color/system_tertiary_container_light\n01060067=color/system_on_tertiary_container_light\n01060068=color/system_tertiary_light\n01060069=color/system_on_tertiary_light\n0106006a=color/system_background_light\n0106006b=color/system_on_background_light\n0106006c=color/system_surface_light\n0106006d=color/system_on_surface_light\n0106006e=color/system_surface_container_low_light\n0106006f=color/system_surface_container_lowest_light\n01060070=color/system_surface_container_light\n01060071=color/system_surface_container_high_light\n01060072=color/system_surface_container_highest_light\n01060073=color/system_surface_bright_light\n01060074=color/system_surface_dim_light\n01060075=color/system_surface_variant_light\n01060076=color/system_on_surface_variant_light\n01060077=color/system_outline_light\n01060078=color/system_error_light\n01060079=color/system_on_error_light\n0106007a=color/system_error_container_light\n0106007b=color/system_on_error_container_light\n0106007c=color/system_control_activated_light\n0106007d=color/system_control_normal_light\n0106007e=color/system_control_highlight_light\n0106007f=color/system_text_primary_inverse_light\n01060080=color/system_text_secondary_and_tertiary_inverse_light\n01060081=color/system_text_primary_inverse_disable_only_light\n01060082=color/system_text_secondary_and_tertiary_inverse_disabled_light\n01060083=color/system_text_hint_inverse_light\n01060084=color/system_palette_key_color_primary_light\n01060085=color/system_palette_key_color_secondary_light\n01060086=color/system_palette_key_color_tertiary_light\n01060087=color/system_palette_key_color_neutral_light\n01060088=color/system_palette_key_color_neutral_variant_light\n01060089=color/system_primary_container_dark\n0106008a=color/system_on_primary_container_dark\n0106008b=color/system_primary_dark\n0106008c=color/system_on_primary_dark\n0106008d=color/system_secondary_container_dark\n0106008e=color/system_on_secondary_container_dark\n0106008f=color/system_secondary_dark\n01060090=color/system_on_secondary_dark\n01060091=color/system_tertiary_container_dark\n01060092=color/system_on_tertiary_container_dark\n01060093=color/system_tertiary_dark\n01060094=color/system_on_tertiary_dark\n01060095=color/system_background_dark\n01060096=color/system_on_background_dark\n01060097=color/system_surface_dark\n01060098=color/system_on_surface_dark\n01060099=color/system_surface_container_low_dark\n0106009a=color/system_surface_container_lowest_dark\n0106009b=color/system_surface_container_dark\n0106009c=color/system_surface_container_high_dark\n0106009d=color/system_surface_container_highest_dark\n0106009e=color/system_surface_bright_dark\n0106009f=color/system_surface_dim_dark\n010600a0=color/system_surface_variant_dark\n010600a1=color/system_on_surface_variant_dark\n010600a2=color/system_outline_dark\n010600a3=color/system_error_dark\n010600a4=color/system_on_error_dark\n010600a5=color/system_error_container_dark\n010600a6=color/system_on_error_container_dark\n010600a7=color/system_control_activated_dark\n010600a8=color/system_control_normal_dark\n010600a9=color/system_control_highlight_dark\n010600aa=color/system_text_primary_inverse_dark\n010600ab=color/system_text_secondary_and_tertiary_inverse_dark\n010600ac=color/system_text_primary_inverse_disable_only_dark\n010600ad=color/system_text_secondary_and_tertiary_inverse_disabled_dark\n010600ae=color/system_text_hint_inverse_dark\n010600af=color/system_palette_key_color_primary_dark\n010600b0=color/system_palette_key_color_secondary_dark\n010600b1=color/system_palette_key_color_tertiary_dark\n010600b2=color/system_palette_key_color_neutral_dark\n010600b3=color/system_palette_key_color_neutral_variant_dark\n010600b4=color/system_primary_fixed\n010600b5=color/system_primary_fixed_dim\n010600b6=color/system_on_primary_fixed\n010600b7=color/system_on_primary_fixed_variant\n010600b8=color/system_secondary_fixed\n010600b9=color/system_secondary_fixed_dim\n010600ba=color/system_on_secondary_fixed\n010600bb=color/system_on_secondary_fixed_variant\n010600bc=color/system_tertiary_fixed\n010600bd=color/system_tertiary_fixed_dim\n010600be=color/system_on_tertiary_fixed\n010600bf=color/system_on_tertiary_fixed_variant\n010600c0=color/system_outline_variant_light\n010600c1=color/system_outline_variant_dark\n010600c2=color/system_surface_disabled\n010600c3=color/system_on_surface_disabled\n010600c4=color/system_outline_disabled\n010600c5=color/system_error_0\n010600c6=color/system_error_10\n010600c7=color/system_error_50\n010600c8=color/system_error_100\n010600c9=color/system_error_200\n010600ca=color/system_error_300\n010600cb=color/system_error_400\n010600cc=color/system_error_500\n010600cd=color/system_error_600\n010600ce=color/system_error_700\n010600cf=color/system_error_800\n010600d0=color/system_error_900\n010600d1=color/system_error_1000\n010600d2=color/GM2_grey_800\n010600d3=color/SIM_color_blue\n010600d4=color/SIM_color_cyan\n010600d5=color/SIM_color_green\n010600d6=color/SIM_color_orange\n010600d7=color/SIM_color_pink\n010600d8=color/SIM_color_purple\n010600d9=color/SIM_dark_mode_color_blue\n010600da=color/SIM_dark_mode_color_cyan\n010600db=color/SIM_dark_mode_color_green\n010600dc=color/SIM_dark_mode_color_orange\n010600dd=color/SIM_dark_mode_color_pink\n010600de=color/SIM_dark_mode_color_purple\n010600df=color/accent_device_default\n010600e0=color/accent_device_default_dark\n010600e1=color/accent_device_default_light\n010600e2=color/accent_material_dark\n010600e3=color/accent_material_light\n010600e4=color/accent_primary_device_default\n010600e5=color/accent_primary_variant_dark_device_default\n010600e6=color/accent_primary_variant_light_device_default\n010600e7=color/accent_secondary_device_default\n010600e8=color/accent_secondary_variant_dark_device_default\n010600e9=color/accent_secondary_variant_light_device_default\n010600ea=color/accent_tertiary_device_default\n010600eb=color/accent_tertiary_variant_dark_device_default\n010600ec=color/accent_tertiary_variant_light_device_default\n010600ed=color/accessibility_autoclick_background\n010600ee=color/accessibility_color_inversion_background\n010600ef=color/accessibility_daltonizer_background\n010600f0=color/accessibility_feature_background\n010600f1=color/accessibility_focus_highlight_color\n010600f2=color/accessibility_magnification_background\n010600f3=color/accessibility_magnification_thumbnail_background_color\n010600f4=color/accessibility_magnification_thumbnail_container_background_color\n010600f5=color/accessibility_magnification_thumbnail_container_stroke_color\n010600f6=color/accessibility_magnification_thumbnail_stroke_color\n010600f7=color/activity_embedding_divider_color\n010600f8=color/activity_embedding_divider_color_pressed\n010600f9=color/autofill_background_material_dark\n010600fa=color/autofill_background_material_light\n010600fb=color/autofilled_highlight\n010600fc=color/background_cache_hint_selector_device_default\n010600fd=color/background_cache_hint_selector_holo_dark\n010600fe=color/background_cache_hint_selector_holo_light\n010600ff=color/background_cache_hint_selector_material_dark\n01060100=color/background_cache_hint_selector_material_light\n01060101=color/background_device_default_dark\n01060102=color/background_device_default_light\n01060103=color/background_floating_device_default_dark\n01060104=color/background_floating_device_default_light\n01060105=color/background_floating_material_dark\n01060106=color/background_floating_material_light\n01060107=color/background_holo_dark\n01060108=color/background_holo_light\n01060109=color/background_leanback_dark\n0106010a=color/background_material_dark\n0106010b=color/background_material_light\n0106010c=color/bright_foreground_dark\n0106010d=color/bright_foreground_dark_disabled\n0106010e=color/bright_foreground_dark_inverse\n0106010f=color/bright_foreground_disabled_holo_dark\n01060110=color/bright_foreground_disabled_holo_light\n01060111=color/bright_foreground_holo_dark\n01060112=color/bright_foreground_holo_light\n01060113=color/bright_foreground_inverse_holo_dark\n01060114=color/bright_foreground_inverse_holo_light\n01060115=color/bright_foreground_light\n01060116=color/bright_foreground_light_disabled\n01060117=color/bright_foreground_light_inverse\n01060118=color/btn_colored_background_material\n01060119=color/btn_colored_borderless_text_material\n0106011a=color/btn_colored_text_material\n0106011b=color/btn_default_material_dark\n0106011c=color/btn_default_material_light\n0106011d=color/btn_leanback_color\n0106011e=color/btn_leanback_focused\n0106011f=color/btn_leanback_text_color\n01060120=color/btn_leanback_unfocused\n01060121=color/btn_material_filled_background_color_watch\n01060122=color/btn_material_filled_content_color_watch\n01060123=color/btn_material_filled_tonal_background_color_watch\n01060124=color/btn_material_filled_tonal_content_color_watch\n01060125=color/btn_material_outlined_background_color_watch\n01060126=color/btn_text_leanback_focused\n01060127=color/btn_text_leanback_unfocused\n01060128=color/btn_watch_default_dark\n01060129=color/button_material_dark\n0106012a=color/button_material_light\n0106012b=color/button_normal_device_default_dark\n0106012c=color/call_notification_answer_color\n0106012d=color/call_notification_decline_color\n0106012e=color/car_accent\n0106012f=color/car_accent_dark\n01060130=color/car_accent_light\n01060131=color/car_action1\n01060132=color/car_action1_dark\n01060133=color/car_action1_light\n01060134=color/car_background\n01060135=color/car_blue_100\n01060136=color/car_blue_200\n01060137=color/car_blue_300\n01060138=color/car_blue_400\n01060139=color/car_blue_50\n0106013a=color/car_blue_500\n0106013b=color/car_blue_600\n0106013c=color/car_blue_700\n0106013d=color/car_blue_800\n0106013e=color/car_blue_900\n0106013f=color/car_blue_grey_800\n01060140=color/car_blue_grey_900\n01060141=color/car_body1\n01060142=color/car_body1_dark\n01060143=color/car_body1_light\n01060144=color/car_body2\n01060145=color/car_body2_dark\n01060146=color/car_body2_light\n01060147=color/car_body3\n01060148=color/car_body3_dark\n01060149=color/car_body3_light\n0106014a=color/car_body4\n0106014b=color/car_body4_dark\n0106014c=color/car_body4_light\n0106014d=color/car_borderless_button_text_color\n0106014e=color/car_button_text_color\n0106014f=color/car_card\n01060150=color/car_card_dark\n01060151=color/car_card_light\n01060152=color/car_card_ripple_background\n01060153=color/car_card_ripple_background_dark\n01060154=color/car_card_ripple_background_inverse\n01060155=color/car_card_ripple_background_light\n01060156=color/car_colorPrimary\n01060157=color/car_colorPrimaryDark\n01060158=color/car_colorSecondary\n01060159=color/car_cyan_100\n0106015a=color/car_cyan_200\n0106015b=color/car_cyan_300\n0106015c=color/car_cyan_400\n0106015d=color/car_cyan_50\n0106015e=color/car_cyan_500\n0106015f=color/car_cyan_600\n01060160=color/car_cyan_700\n01060161=color/car_cyan_800\n01060162=color/car_cyan_900\n01060163=color/car_dark_blue_grey_1000\n01060164=color/car_dark_blue_grey_600\n01060165=color/car_dark_blue_grey_700\n01060166=color/car_dark_blue_grey_800\n01060167=color/car_dark_blue_grey_900\n01060168=color/car_green_100\n01060169=color/car_green_200\n0106016a=color/car_green_300\n0106016b=color/car_green_400\n0106016c=color/car_green_50\n0106016d=color/car_green_500\n0106016e=color/car_green_600\n0106016f=color/car_green_700\n01060170=color/car_green_800\n01060171=color/car_green_900\n01060172=color/car_grey_100\n01060173=color/car_grey_1000\n01060174=color/car_grey_200\n01060175=color/car_grey_300\n01060176=color/car_grey_400\n01060177=color/car_grey_50\n01060178=color/car_grey_500\n01060179=color/car_grey_600\n0106017a=color/car_grey_700\n0106017b=color/car_grey_746\n0106017c=color/car_grey_772\n0106017d=color/car_grey_800\n0106017e=color/car_grey_846\n0106017f=color/car_grey_868\n01060180=color/car_grey_900\n01060181=color/car_grey_928\n01060182=color/car_grey_958\n01060183=color/car_grey_972\n01060184=color/car_headline1\n01060185=color/car_headline1_dark\n01060186=color/car_headline1_light\n01060187=color/car_headline2\n01060188=color/car_headline2_dark\n01060189=color/car_headline2_light\n0106018a=color/car_headline3\n0106018b=color/car_headline3_dark\n0106018c=color/car_headline3_light\n0106018d=color/car_headline4\n0106018e=color/car_headline4_dark\n0106018f=color/car_headline4_light\n01060190=color/car_highlight\n01060191=color/car_highlight_dark\n01060192=color/car_highlight_light\n01060193=color/car_keyboard_divider_line\n01060194=color/car_keyboard_text_primary_color\n01060195=color/car_keyboard_text_secondary_color\n01060196=color/car_light_blue_300\n01060197=color/car_light_blue_500\n01060198=color/car_light_blue_600\n01060199=color/car_light_blue_700\n0106019a=color/car_light_blue_800\n0106019b=color/car_light_blue_900\n0106019c=color/car_list_divider\n0106019d=color/car_list_divider_dark\n0106019e=color/car_list_divider_light\n0106019f=color/car_orange_100\n010601a0=color/car_orange_200\n010601a1=color/car_orange_300\n010601a2=color/car_orange_400\n010601a3=color/car_orange_50\n010601a4=color/car_orange_500\n010601a5=color/car_orange_600\n010601a6=color/car_orange_700\n010601a7=color/car_orange_800\n010601a8=color/car_orange_900\n010601a9=color/car_pink_100\n010601aa=color/car_pink_200\n010601ab=color/car_pink_300\n010601ac=color/car_pink_400\n010601ad=color/car_pink_50\n010601ae=color/car_pink_500\n010601af=color/car_pink_600\n010601b0=color/car_pink_700\n010601b1=color/car_pink_800\n010601b2=color/car_pink_900\n010601b3=color/car_purple_100\n010601b4=color/car_purple_200\n010601b5=color/car_purple_300\n010601b6=color/car_purple_400\n010601b7=color/car_purple_50\n010601b8=color/car_purple_500\n010601b9=color/car_purple_600\n010601ba=color/car_purple_700\n010601bb=color/car_purple_800\n010601bc=color/car_purple_900\n010601bd=color/car_red_100\n010601be=color/car_red_200\n010601bf=color/car_red_300\n010601c0=color/car_red_400\n010601c1=color/car_red_50\n010601c2=color/car_red_500\n010601c3=color/car_red_500a\n010601c4=color/car_red_600\n010601c5=color/car_red_700\n010601c6=color/car_red_800\n010601c7=color/car_red_900\n010601c8=color/car_red_a700\n010601c9=color/car_scrollbar_thumb\n010601ca=color/car_scrollbar_thumb_dark\n010601cb=color/car_scrollbar_thumb_inverse\n010601cc=color/car_scrollbar_thumb_light\n010601cd=color/car_seekbar_track_background\n010601ce=color/car_seekbar_track_background_dark\n010601cf=color/car_seekbar_track_background_light\n010601d0=color/car_seekbar_track_secondary_progress\n010601d1=color/car_switch\n010601d2=color/car_switch_track\n010601d3=color/car_teal_100\n010601d4=color/car_teal_200\n010601d5=color/car_teal_300\n010601d6=color/car_teal_400\n010601d7=color/car_teal_50\n010601d8=color/car_teal_500\n010601d9=color/car_teal_600\n010601da=color/car_teal_700\n010601db=color/car_teal_800\n010601dc=color/car_teal_900\n010601dd=color/car_tint\n010601de=color/car_tint_dark\n010601df=color/car_tint_inverse\n010601e0=color/car_tint_light\n010601e1=color/car_title\n010601e2=color/car_title2\n010601e3=color/car_title2_dark\n010601e4=color/car_title2_light\n010601e5=color/car_title_dark\n010601e6=color/car_title_light\n010601e7=color/car_toast_background\n010601e8=color/car_user_switcher_user_image_bgcolor\n010601e9=color/car_user_switcher_user_image_fgcolor\n010601ea=color/car_white_1000\n010601eb=color/car_yellow_100\n010601ec=color/car_yellow_200\n010601ed=color/car_yellow_300\n010601ee=color/car_yellow_400\n010601ef=color/car_yellow_50\n010601f0=color/car_yellow_500\n010601f1=color/car_yellow_600\n010601f2=color/car_yellow_700\n010601f3=color/car_yellow_800\n010601f4=color/car_yellow_900\n010601f5=color/chooser_gradient_background\n010601f6=color/chooser_gradient_highlight\n010601f7=color/chooser_row_divider\n010601f8=color/config_defaultNotificationColor\n010601f9=color/config_letterboxBackgroundColor\n010601fa=color/config_progress_background_tint\n010601fb=color/control_checkable_material\n010601fc=color/control_default_material\n010601fd=color/control_highlight_material\n010601fe=color/conversation_important_highlight\n010601ff=color/customColorBrandA\n01060200=color/customColorBrandB\n01060201=color/customColorBrandC\n01060202=color/customColorBrandD\n01060203=color/customColorClockHour\n01060204=color/customColorClockMinute\n01060205=color/customColorClockSecond\n01060206=color/customColorOnShadeActive\n01060207=color/customColorOnShadeActiveVariant\n01060208=color/customColorOnShadeInactive\n01060209=color/customColorOnShadeInactiveVariant\n0106020a=color/customColorOnThemeApp\n0106020b=color/customColorOverviewBackground\n0106020c=color/customColorShadeActive\n0106020d=color/customColorShadeDisabled\n0106020e=color/customColorShadeInactive\n0106020f=color/customColorThemeApp\n01060210=color/customColorThemeAppRing\n01060211=color/customColorThemeNotif\n01060212=color/customColorUnderSurface\n01060213=color/customColorWeatherTemp\n01060214=color/customColorWidgetBackground\n01060215=color/datepicker_default_circle_background_color_material_dark\n01060216=color/datepicker_default_circle_background_color_material_light\n01060217=color/datepicker_default_disabled_text_color_material_dark\n01060218=color/datepicker_default_disabled_text_color_material_light\n01060219=color/datepicker_default_header_dayofweek_background_color_material_dark\n0106021a=color/datepicker_default_header_dayofweek_background_color_material_light\n0106021b=color/datepicker_default_header_selector_background_material_dark\n0106021c=color/datepicker_default_header_selector_background_material_light\n0106021d=color/datepicker_default_normal_text_color_material_dark\n0106021e=color/datepicker_default_normal_text_color_material_light\n0106021f=color/datepicker_default_pressed_text_color_material_dark\n01060220=color/datepicker_default_pressed_text_color_material_light\n01060221=color/datepicker_default_selected_text_color_material_dark\n01060222=color/datepicker_default_selected_text_color_material_light\n01060223=color/datepicker_default_view_animator_color_material_dark\n01060224=color/datepicker_default_view_animator_color_material_light\n01060225=color/decor_button_dark_color\n01060226=color/decor_button_light_color\n01060227=color/decor_view_status_guard\n01060228=color/decor_view_status_guard_light\n01060229=color/default_magnifier_color_overlay\n0106022a=color/dim_foreground_dark\n0106022b=color/dim_foreground_dark_disabled\n0106022c=color/dim_foreground_dark_inverse\n0106022d=color/dim_foreground_dark_inverse_disabled\n0106022e=color/dim_foreground_disabled_holo_dark\n0106022f=color/dim_foreground_disabled_holo_light\n01060230=color/dim_foreground_holo_dark\n01060231=color/dim_foreground_holo_light\n01060232=color/dim_foreground_inverse_disabled_holo_dark\n01060233=color/dim_foreground_inverse_disabled_holo_light\n01060234=color/dim_foreground_inverse_holo_dark\n01060235=color/dim_foreground_inverse_holo_light\n01060236=color/dim_foreground_light\n01060237=color/dim_foreground_light_disabled\n01060238=color/dim_foreground_light_inverse\n01060239=color/dim_foreground_light_inverse_disabled\n0106023a=color/edge_effect_device_default_dark\n0106023b=color/edge_effect_device_default_light\n0106023c=color/error_color_device_default_dark\n0106023d=color/error_color_device_default_light\n0106023e=color/error_color_material_dark\n0106023f=color/error_color_material_light\n01060240=color/facelock_spotlight_mask\n01060241=color/floating_popup_divider_dark\n01060242=color/floating_popup_divider_light\n01060243=color/foreground_device_default_dark\n01060244=color/foreground_device_default_light\n01060245=color/foreground_material_dark\n01060246=color/foreground_material_light\n01060247=color/global_actions_container_background\n01060248=color/group_button_dialog_focused_holo_dark\n01060249=color/group_button_dialog_focused_holo_light\n0106024a=color/group_button_dialog_pressed_holo_dark\n0106024b=color/group_button_dialog_pressed_holo_light\n0106024c=color/highlighted_text_dark\n0106024d=color/highlighted_text_holo_dark\n0106024e=color/highlighted_text_holo_light\n0106024f=color/highlighted_text_light\n01060250=color/highlighted_text_material\n01060251=color/hint_foreground_dark\n01060252=color/hint_foreground_holo_dark\n01060253=color/hint_foreground_holo_light\n01060254=color/hint_foreground_light\n01060255=color/hint_foreground_material_dark\n01060256=color/hint_foreground_material_light\n01060257=color/holo_button_normal\n01060258=color/holo_button_pressed\n01060259=color/holo_control_activated\n0106025a=color/holo_control_normal\n0106025b=color/holo_gray_bright\n0106025c=color/holo_gray_light\n0106025d=color/holo_light_button_normal\n0106025e=color/holo_light_button_pressed\n0106025f=color/holo_light_control_activated\n01060260=color/holo_light_control_normal\n01060261=color/holo_light_primary\n01060262=color/holo_light_primary_dark\n01060263=color/holo_primary\n01060264=color/holo_primary_dark\n01060265=color/input_method_switch_on_item\n01060266=color/instant_app_badge\n01060267=color/keyguard_avatar_frame_color\n01060268=color/keyguard_avatar_frame_pressed_color\n01060269=color/keyguard_avatar_frame_shadow_color\n0106026a=color/keyguard_avatar_nick_color\n0106026b=color/keyguard_text_color_decline\n0106026c=color/keyguard_text_color_normal\n0106026d=color/keyguard_text_color_soundoff\n0106026e=color/keyguard_text_color_soundon\n0106026f=color/keyguard_text_color_unlock\n01060270=color/kg_multi_user_text_active\n01060271=color/kg_multi_user_text_inactive\n01060272=color/kg_widget_pager_gradient\n01060273=color/language_picker_item_selected_bg\n01060274=color/language_picker_item_selected_indicator\n01060275=color/language_picker_item_selected_stroke\n01060276=color/language_picker_item_text_color\n01060277=color/language_picker_item_text_color_secondary\n01060278=color/language_picker_item_text_color_secondary_selected\n01060279=color/language_picker_item_text_color_selected\n0106027a=color/legacy_button_normal\n0106027b=color/legacy_button_pressed\n0106027c=color/legacy_control_activated\n0106027d=color/legacy_control_normal\n0106027e=color/legacy_green\n0106027f=color/legacy_light_button_normal\n01060280=color/legacy_light_button_pressed\n01060281=color/legacy_light_control_activated\n01060282=color/legacy_light_control_normal\n01060283=color/legacy_light_primary\n01060284=color/legacy_light_primary_dark\n01060285=color/legacy_long_pressed_highlight\n01060286=color/legacy_orange\n01060287=color/legacy_pressed_highlight\n01060288=color/legacy_primary\n01060289=color/legacy_primary_dark\n0106028a=color/legacy_selected_highlight\n0106028b=color/lighter_gray\n0106028c=color/link_text_dark\n0106028d=color/link_text_holo_dark\n0106028e=color/link_text_holo_light\n0106028f=color/link_text_light\n01060290=color/list_divider_color_dark\n01060291=color/list_divider_color_light\n01060292=color/list_divider_opacity_device_default_dark\n01060293=color/list_divider_opacity_device_default_light\n01060294=color/list_divider_opacity_material\n01060295=color/list_highlight_material\n01060296=color/loading_gradient_background_color_dark\n01060297=color/loading_gradient_background_color_light\n01060298=color/loading_gradient_highlight_color_dark\n01060299=color/loading_gradient_highlight_color_light\n0106029a=color/lock_pattern_view_regular_color\n0106029b=color/lock_pattern_view_success_color\n0106029c=color/lockscreen_clock_am_pm\n0106029d=color/lockscreen_clock_background\n0106029e=color/lockscreen_clock_foreground\n0106029f=color/lockscreen_owner_info\n010602a0=color/materialColorBackground\n010602a1=color/materialColorControlActivated\n010602a2=color/materialColorControlHighlight\n010602a3=color/materialColorControlNormal\n010602a4=color/materialColorError\n010602a5=color/materialColorErrorContainer\n010602a6=color/materialColorInverseOnSurface\n010602a7=color/materialColorInversePrimary\n010602a8=color/materialColorInverseSurface\n010602a9=color/materialColorOnBackground\n010602aa=color/materialColorOnError\n010602ab=color/materialColorOnErrorContainer\n010602ac=color/materialColorOnPrimary\n010602ad=color/materialColorOnPrimaryContainer\n010602ae=color/materialColorOnPrimaryFixed\n010602af=color/materialColorOnPrimaryFixedVariant\n010602b0=color/materialColorOnSecondary\n010602b1=color/materialColorOnSecondaryContainer\n010602b2=color/materialColorOnSecondaryFixed\n010602b3=color/materialColorOnSecondaryFixedVariant\n010602b4=color/materialColorOnSurface\n010602b5=color/materialColorOnSurfaceVariant\n010602b6=color/materialColorOnTertiary\n010602b7=color/materialColorOnTertiaryContainer\n010602b8=color/materialColorOnTertiaryFixed\n010602b9=color/materialColorOnTertiaryFixedVariant\n010602ba=color/materialColorOutline\n010602bb=color/materialColorOutlineVariant\n010602bc=color/materialColorPaletteKeyColorNeutral\n010602bd=color/materialColorPaletteKeyColorNeutralVariant\n010602be=color/materialColorPaletteKeyColorPrimary\n010602bf=color/materialColorPaletteKeyColorSecondary\n010602c0=color/materialColorPaletteKeyColorTertiary\n010602c1=color/materialColorPrimary\n010602c2=color/materialColorPrimaryContainer\n010602c3=color/materialColorPrimaryFixed\n010602c4=color/materialColorPrimaryFixedDim\n010602c5=color/materialColorScrim\n010602c6=color/materialColorSecondary\n010602c7=color/materialColorSecondaryContainer\n010602c8=color/materialColorSecondaryFixed\n010602c9=color/materialColorSecondaryFixedDim\n010602ca=color/materialColorShadow\n010602cb=color/materialColorSurface\n010602cc=color/materialColorSurfaceBright\n010602cd=color/materialColorSurfaceContainer\n010602ce=color/materialColorSurfaceContainerHigh\n010602cf=color/materialColorSurfaceContainerHighest\n010602d0=color/materialColorSurfaceContainerLow\n010602d1=color/materialColorSurfaceContainerLowest\n010602d2=color/materialColorSurfaceDim\n010602d3=color/materialColorSurfaceTint\n010602d4=color/materialColorSurfaceVariant\n010602d5=color/materialColorTertiary\n010602d6=color/materialColorTertiaryContainer\n010602d7=color/materialColorTertiaryFixed\n010602d8=color/materialColorTertiaryFixedDim\n010602d9=color/materialColorTextHintInverse\n010602da=color/materialColorTextPrimaryInverse\n010602db=color/materialColorTextPrimaryInverseDisableOnly\n010602dc=color/materialColorTextSecondaryAndTertiaryInverse\n010602dd=color/materialColorTextSecondaryAndTertiaryInverseDisabled\n010602de=color/material_blue_grey_200\n010602df=color/material_blue_grey_700\n010602e0=color/material_blue_grey_800\n010602e1=color/material_blue_grey_900\n010602e2=color/material_blue_grey_950\n010602e3=color/material_deep_teal_100\n010602e4=color/material_deep_teal_200\n010602e5=color/material_deep_teal_300\n010602e6=color/material_deep_teal_500\n010602e7=color/material_grey_100\n010602e8=color/material_grey_200\n010602e9=color/material_grey_300\n010602ea=color/material_grey_50\n010602eb=color/material_grey_600\n010602ec=color/material_grey_800\n010602ed=color/material_grey_850\n010602ee=color/material_grey_900\n010602ef=color/material_red_A100\n010602f0=color/material_red_A700\n010602f1=color/micro_text_light\n010602f2=color/navigation_bar_compatible\n010602f3=color/navigation_bar_default\n010602f4=color/navigation_bar_divider_device_default_settings\n010602f5=color/notification_action_button_text_color\n010602f6=color/notification_action_list\n010602f7=color/notification_action_list_background_color\n010602f8=color/notification_close_button_state_tint\n010602f9=color/notification_default_color\n010602fa=color/notification_default_color_current\n010602fb=color/notification_default_color_dark\n010602fc=color/notification_default_color_light\n010602fd=color/notification_expand_button_state_tint\n010602fe=color/notification_primary_text_color_current\n010602ff=color/notification_primary_text_color_dark\n01060300=color/notification_primary_text_color_light\n01060301=color/notification_progress_background_color\n01060302=color/notification_secondary_text_color_current\n01060303=color/notification_secondary_text_color_dark\n01060304=color/notification_secondary_text_color_light\n01060305=color/overview_background\n01060306=color/overview_background_dark\n01060307=color/perms_dangerous_grp_color\n01060308=color/perms_dangerous_perm_color\n01060309=color/personal_apps_suspension_notification_color\n0106030a=color/primary_dark_device_default_dark\n0106030b=color/primary_dark_device_default_light\n0106030c=color/primary_dark_device_default_settings\n0106030d=color/primary_dark_device_default_settings_light\n0106030e=color/primary_dark_material_dark\n0106030f=color/primary_dark_material_light\n01060310=color/primary_dark_material_light_light_status_bar\n01060311=color/primary_dark_material_settings\n01060312=color/primary_dark_material_settings_light\n01060313=color/primary_device_default_dark\n01060314=color/primary_device_default_light\n01060315=color/primary_device_default_settings\n01060316=color/primary_device_default_settings_light\n01060317=color/primary_material_dark\n01060318=color/primary_material_light\n01060319=color/primary_material_settings\n0106031a=color/primary_material_settings_light\n0106031b=color/primary_text_dark_disable_only\n0106031c=color/primary_text_dark_focused\n0106031d=color/primary_text_default_material_dark\n0106031e=color/primary_text_default_material_light\n0106031f=color/primary_text_disable_only_holo_dark\n01060320=color/primary_text_disable_only_holo_light\n01060321=color/primary_text_disable_only_material_dark\n01060322=color/primary_text_disable_only_material_light\n01060323=color/primary_text_focused_holo_dark\n01060324=color/primary_text_holo_dark\n01060325=color/primary_text_holo_light\n01060326=color/primary_text_inverse_when_activated_material\n01060327=color/primary_text_leanback_dark\n01060328=color/primary_text_leanback_formwizard_dark\n01060329=color/primary_text_leanback_formwizard_default_dark\n0106032a=color/primary_text_light_disable_only\n0106032b=color/primary_text_material_dark\n0106032c=color/primary_text_material_light\n0106032d=color/primary_text_nodisable_holo_dark\n0106032e=color/primary_text_nodisable_holo_light\n0106032f=color/primary_text_secondary_when_activated_material\n01060330=color/primary_text_secondary_when_activated_material_inverse\n01060331=color/profile_badge_1\n01060332=color/profile_badge_1_dark\n01060333=color/profile_badge_2\n01060334=color/profile_badge_2_dark\n01060335=color/profile_badge_3\n01060336=color/profile_badge_3_dark\n01060337=color/quaternary_device_default_settings\n01060338=color/quaternary_material_settings\n01060339=color/ratingbar_background_material\n0106033a=color/red\n0106033b=color/resolver_accent_ripple\n0106033c=color/resolver_button_text\n0106033d=color/resolver_profile_tab_selected_bg\n0106033e=color/resolver_profile_tab_text\n0106033f=color/resolver_text_color_secondary_dark\n01060340=color/ripple_material_dark\n01060341=color/ripple_material_light\n01060342=color/safe_mode_text\n01060343=color/search_url_text\n01060344=color/search_url_text_holo\n01060345=color/search_url_text_material_dark\n01060346=color/search_url_text_material_light\n01060347=color/search_url_text_normal\n01060348=color/search_url_text_pressed\n01060349=color/search_url_text_selected\n0106034a=color/search_widget_corpus_item_background\n0106034b=color/secondary_device_default_settings\n0106034c=color/secondary_device_default_settings_light\n0106034d=color/secondary_material_settings\n0106034e=color/secondary_material_settings_light\n0106034f=color/secondary_text_default_material_dark\n01060350=color/secondary_text_default_material_light\n01060351=color/secondary_text_holo_dark\n01060352=color/secondary_text_holo_light\n01060353=color/secondary_text_inverse_when_activated_material\n01060354=color/secondary_text_leanback_dark\n01060355=color/secondary_text_material_dark\n01060356=color/secondary_text_material_light\n01060357=color/secondary_text_nodisable_holo_dark\n01060358=color/secondary_text_nodisable_holo_light\n01060359=color/secondary_text_nofocus\n0106035a=color/seekbar_track_progress_material\n0106035b=color/shadow\n0106035c=color/side_fps_button_color\n0106035d=color/side_fps_text_color\n0106035e=color/side_fps_toast_background\n0106035f=color/sliding_tab_text_color_active\n01060360=color/sliding_tab_text_color_shadow\n01060361=color/suggestion_highlight_text\n01060362=color/surface_dark\n01060363=color/surface_effect_0\n01060364=color/surface_effect_0_color\n01060365=color/surface_effect_1\n01060366=color/surface_effect_1_color\n01060367=color/surface_effect_2\n01060368=color/surface_effect_2_color\n01060369=color/surface_effect_3\n0106036a=color/surface_effect_3_color\n0106036b=color/surface_header_dark\n0106036c=color/surface_header_light\n0106036d=color/surface_highlight_dark\n0106036e=color/surface_highlight_light\n0106036f=color/surface_light\n01060370=color/surface_variant_dark\n01060371=color/surface_variant_light\n01060372=color/switch_thumb_disabled_material_dark\n01060373=color/switch_thumb_disabled_material_light\n01060374=color/switch_thumb_material_dark\n01060375=color/switch_thumb_material_light\n01060376=color/switch_thumb_normal_material_dark\n01060377=color/switch_thumb_normal_material_light\n01060378=color/switch_thumb_watch_default_dark\n01060379=color/switch_track_material\n0106037a=color/switch_track_watch_default_dark\n0106037b=color/system_bar_background_semi_transparent\n0106037c=color/system_brand_a_dark\n0106037d=color/system_brand_a_light\n0106037e=color/system_brand_b_dark\n0106037f=color/system_brand_b_light\n01060380=color/system_brand_c_dark\n01060381=color/system_brand_c_light\n01060382=color/system_brand_d_dark\n01060383=color/system_brand_d_light\n01060384=color/system_clock_hour_dark\n01060385=color/system_clock_hour_light\n01060386=color/system_clock_minute_dark\n01060387=color/system_clock_minute_light\n01060388=color/system_clock_second_dark\n01060389=color/system_clock_second_light\n0106038a=color/system_on_shade_active_dark\n0106038b=color/system_on_shade_active_light\n0106038c=color/system_on_shade_active_variant_dark\n0106038d=color/system_on_shade_active_variant_light\n0106038e=color/system_on_shade_inactive_dark\n0106038f=color/system_on_shade_inactive_light\n01060390=color/system_on_shade_inactive_variant_dark\n01060391=color/system_on_shade_inactive_variant_light\n01060392=color/system_on_theme_app_dark\n01060393=color/system_on_theme_app_light\n01060394=color/system_overview_background_dark\n01060395=color/system_overview_background_light\n01060396=color/system_shade_active_dark\n01060397=color/system_shade_active_light\n01060398=color/system_shade_disabled_dark\n01060399=color/system_shade_disabled_light\n0106039a=color/system_shade_inactive_dark\n0106039b=color/system_shade_inactive_light\n0106039c=color/system_theme_app_dark\n0106039d=color/system_theme_app_light\n0106039e=color/system_theme_app_ring_dark\n0106039f=color/system_theme_app_ring_light\n010603a0=color/system_theme_notif_dark\n010603a1=color/system_theme_notif_light\n010603a2=color/system_under_surface_dark\n010603a3=color/system_under_surface_light\n010603a4=color/system_weather_temp_dark\n010603a5=color/system_weather_temp_light\n010603a6=color/system_widget_background_dark\n010603a7=color/system_widget_background_light\n010603a8=color/tab_highlight_material\n010603a9=color/tab_indicator_material\n010603aa=color/tab_indicator_text_material\n010603ab=color/tab_indicator_text_v4\n010603ac=color/tertiary_device_default_settings\n010603ad=color/tertiary_material_settings\n010603ae=color/tertiary_text_holo_dark\n010603af=color/tertiary_text_holo_light\n010603b0=color/text_color_on_accent_device_default\n010603b1=color/text_color_primary\n010603b2=color/text_color_primary_device_default_dark\n010603b3=color/text_color_primary_device_default_light\n010603b4=color/text_color_secondary\n010603b5=color/text_color_secondary_device_default_dark\n010603b6=color/text_color_secondary_device_default_light\n010603b7=color/text_color_tertiary_device_default_dark\n010603b8=color/text_color_tertiary_device_default_light\n010603b9=color/timepicker_default_ampm_selected_background_color_material\n010603ba=color/timepicker_default_ampm_unselected_background_color_material\n010603bb=color/timepicker_default_background_material\n010603bc=color/timepicker_default_numbers_background_color_material\n010603bd=color/timepicker_default_selector_color_material\n010603be=color/timepicker_default_text_color_material\n010603bf=color/tooltip_background_dark\n010603c0=color/tooltip_background_light\n010603c1=color/user_icon_1\n010603c2=color/user_icon_2\n010603c3=color/user_icon_3\n010603c4=color/user_icon_4\n010603c5=color/user_icon_5\n010603c6=color/user_icon_6\n010603c7=color/user_icon_7\n010603c8=color/user_icon_8\n010603c9=color/user_icon_default_gray\n010603ca=color/user_icon_default_white\n010603cb=color/white_disabled_material\n01070000=array/emailAddressTypes\n01070001=array/imProtocols\n01070002=array/organizationTypes\n01070003=array/phoneTypes\n01070004=array/postalAddressTypes\n01070005=array/config_keySystemUuidMapping\n01070006=array/config_optionalIpSecAlgorithms\n01070007=array/carrier_properties\n01070008=array/cloneable_apps\n01070009=array/common_nicknames\n0107000a=array/config_allowedGlobalInstantAppSettings\n0107000b=array/config_allowedSecureInstantAppSettings\n0107000c=array/config_allowedSystemInstantAppSettings\n0107000d=array/config_ambientBrighteningThresholds\n0107000e=array/config_ambientDarkeningThresholds\n0107000f=array/config_ambientThresholdLevels\n01070010=array/config_ambientThresholdsOfPeakRefreshRate\n01070011=array/config_angleAllowList\n01070012=array/config_autoBrightnessButtonBacklightValues\n01070013=array/config_autoBrightnessDisplayValuesNits\n01070014=array/config_autoBrightnessDisplayValuesNitsIdle\n01070015=array/config_autoBrightnessLcdBacklightValues\n01070016=array/config_autoBrightnessLcdBacklightValues_doze\n01070017=array/config_autoBrightnessLevels\n01070018=array/config_autoBrightnessLevelsIdle\n01070019=array/config_autoKeyboardBacklightBrightnessValues\n0107001a=array/config_autoKeyboardBacklightDecreaseLuxThreshold\n0107001b=array/config_autoKeyboardBacklightIncreaseLuxThreshold\n0107001c=array/config_autoRotationTiltTolerance\n0107001d=array/config_autoTimeSourcesPriority\n0107001e=array/config_availableColorModes\n0107001f=array/config_availableEMValueOptions\n01070020=array/config_backGestureInsetScales\n01070021=array/config_backupHealthConnectDataAndSettingsKnownSigners\n01070022=array/config_batteryPackageTypeService\n01070023=array/config_batteryPackageTypeSystem\n01070024=array/config_bg_current_drain_high_threshold_to_bg_restricted\n01070025=array/config_bg_current_drain_high_threshold_to_restricted_bucket\n01070026=array/config_bg_current_drain_threshold_to_bg_restricted\n01070027=array/config_bg_current_drain_threshold_to_restricted_bucket\n01070028=array/config_biometric_protected_package_names\n01070029=array/config_biometric_sensors\n0107002a=array/config_brightnessThresholdsOfPeakRefreshRate\n0107002b=array/config_builtInDisplayIsRoundArray\n0107002c=array/config_callBarringMMI\n0107002d=array/config_callBarringMMI_for_ims\n0107002e=array/config_cameraPrivacyLightAlsLuxThresholds\n0107002f=array/config_cameraPrivacyLightColors\n01070030=array/config_cdma_dun_supported_types\n01070031=array/config_cdma_home_system\n01070032=array/config_cdma_international_roaming_indicators\n01070033=array/config_cell_retries_per_error_code\n01070034=array/config_clockTickVibePattern\n01070035=array/config_companionDeviceCerts\n01070036=array/config_companionDevicePackages\n01070037=array/config_companionPermSyncEnabledCerts\n01070038=array/config_companionPermSyncEnabledPackages\n01070039=array/config_concurrentDisplayDeviceStates\n0107003a=array/config_convert_to_emergency_number_map\n0107003b=array/config_defaultAllowlistLaunchOnPrivateDisplayPackages\n0107003c=array/config_defaultAmbientContextServices\n0107003d=array/config_defaultCloudSearchServices\n0107003e=array/config_defaultFirstUserRestrictions\n0107003f=array/config_defaultImperceptibleKillingExemptionPkgs\n01070040=array/config_defaultImperceptibleKillingExemptionProcStates\n01070041=array/config_defaultNotificationVibePattern\n01070042=array/config_defaultNotificationVibeWaveform\n01070043=array/config_defaultPinnerServiceFiles\n01070044=array/config_default_vm_number\n01070045=array/config_deviceSpecificSystemServices\n01070046=array/config_deviceStatesAvailableForAppRequests\n01070047=array/config_deviceStatesOnWhichToSleep\n01070048=array/config_deviceStatesOnWhichToWakeUp\n01070049=array/config_deviceStatesToReverseDefaultDisplayRotationAroundZAxis\n0107004a=array/config_deviceTabletopRotations\n0107004b=array/config_device_state_postures\n0107004c=array/config_disabledDreamComponents\n0107004d=array/config_disabledUntilUsedPreinstalledImes\n0107004e=array/config_displayCompositionColorModes\n0107004f=array/config_displayCompositionColorSpaces\n01070050=array/config_displayCutoutApproximationRectArray\n01070051=array/config_displayCutoutPathArray\n01070052=array/config_displayCutoutSideOverrideArray\n01070053=array/config_displayShapeArray\n01070054=array/config_displayUniqueIdArray\n01070055=array/config_displayWhiteBalanceAmbientColorTemperatures\n01070056=array/config_displayWhiteBalanceBaseThresholds\n01070057=array/config_displayWhiteBalanceDecreaseThresholds\n01070058=array/config_displayWhiteBalanceDisplayColorTemperatures\n01070059=array/config_displayWhiteBalanceDisplayNominalWhite\n0107005a=array/config_displayWhiteBalanceDisplayPrimaries\n0107005b=array/config_displayWhiteBalanceDisplayRangeMinimums\n0107005c=array/config_displayWhiteBalanceDisplaySteps\n0107005d=array/config_displayWhiteBalanceHighLightAmbientBiases\n0107005e=array/config_displayWhiteBalanceHighLightAmbientBiasesStrong\n0107005f=array/config_displayWhiteBalanceHighLightAmbientBrightnesses\n01070060=array/config_displayWhiteBalanceHighLightAmbientBrightnessesStrong\n01070061=array/config_displayWhiteBalanceIncreaseThresholds\n01070062=array/config_displayWhiteBalanceLowLightAmbientBiases\n01070063=array/config_displayWhiteBalanceLowLightAmbientBiasesStrong\n01070064=array/config_displayWhiteBalanceLowLightAmbientBrightnesses\n01070065=array/config_displayWhiteBalanceLowLightAmbientBrightnessesStrong\n01070066=array/config_displayWhiteBalanceStrongAmbientColorTemperatures\n01070067=array/config_displayWhiteBalanceStrongDisplayColorTemperatures\n01070068=array/config_display_no_service_when_sim_unready\n01070069=array/config_dockExtconStateMapping\n0107006a=array/config_doubleClickVibePattern\n0107006b=array/config_dozeTapSensorPostureMapping\n0107006c=array/config_dropboxLowPriorityTags\n0107006d=array/config_emergency_iso_country_codes\n0107006e=array/config_enabledCredentialProviderService\n0107006f=array/config_ephemeralResolverPackage\n01070070=array/config_ethernet_interfaces\n01070071=array/config_face_acquire_biometricprompt_ignorelist\n01070072=array/config_face_acquire_enroll_ignorelist\n01070073=array/config_face_acquire_keyguard_ignorelist\n01070074=array/config_face_acquire_vendor_biometricprompt_ignorelist\n01070075=array/config_face_acquire_vendor_enroll_ignorelist\n01070076=array/config_face_acquire_vendor_keyguard_ignorelist\n01070077=array/config_fillBuiltInDisplayCutoutArray\n01070078=array/config_foldedDeviceStates\n01070079=array/config_fontManagerServiceCerts\n0107007a=array/config_forceQueryablePackages\n0107007b=array/config_forceSlowJpegModeList\n0107007c=array/config_force_cellular_transport_capabilities\n0107007d=array/config_globalActionsList\n0107007e=array/config_gnssParameters\n0107007f=array/config_halfFoldedDeviceStates\n01070080=array/config_healthConnectMigrationKnownSigners\n01070081=array/config_healthConnectRestoreKnownSigners\n01070082=array/config_hideWhenDisabled_packageNames\n01070083=array/config_highAmbientBrightnessThresholdsOfFixedRefreshRate\n01070084=array/config_highDisplayBrightnessThresholdsOfFixedRefreshRate\n01070085=array/config_highRefreshRateBlacklist\n01070086=array/config_integrityRuleProviderPackages\n01070087=array/config_keep_warming_services\n01070088=array/config_localNotStealTopFocusDisplayPorts\n01070089=array/config_localPrivateDisplayPorts\n0107008a=array/config_locationDriverAssistancePackageNames\n0107008b=array/config_locationExtraPackageNames\n0107008c=array/config_locationProviderPackageNames\n0107008d=array/config_loggable_dream_prefixes\n0107008e=array/config_longPressOnPowerDurationSettings\n0107008f=array/config_longPressVibePattern\n01070090=array/config_lteDbmThresholds\n01070091=array/config_mainBuiltInDisplayCutoutSideOverride\n01070092=array/config_mainBuiltInDisplayWaterfallCutout\n01070093=array/config_mappedColorModes\n01070094=array/config_maskBuiltInDisplayCutoutArray\n01070095=array/config_minimumBrightnessCurveLux\n01070096=array/config_minimumBrightnessCurveNits\n01070097=array/config_mobile_hotspot_provision_app\n01070098=array/config_mobile_tcp_buffers\n01070099=array/config_networkNotifySwitches\n0107009a=array/config_networkSupportedKeepaliveCount\n0107009b=array/config_network_type_tcp_buffers\n0107009c=array/config_nightDisplayColorTemperatureCoefficients\n0107009d=array/config_nightDisplayColorTemperatureCoefficientsNative\n0107009e=array/config_nonPreemptibleInputMethods\n0107009f=array/config_notificationDefaultUnsupportedAdjustments\n010700a0=array/config_notificationFallbackVibePattern\n010700a1=array/config_notificationFallbackVibeWaveform\n010700a2=array/config_notificationMsgPkgsAllowedAsConvos\n010700a3=array/config_notificationSignalExtractors\n010700a4=array/config_ntpServers\n010700a5=array/config_oemUsbModeOverride\n010700a6=array/config_oem_enabled_satellite_country_codes\n010700a7=array/config_openDeviceStates\n010700a8=array/config_packagesExemptFromSuspension\n010700a9=array/config_perDeviceStateRotationLockDefaults\n010700aa=array/config_primaryCredentialProviderService\n010700ab=array/config_priorityOnlyDndExemptPackages\n010700ac=array/config_profcollectOnCameraOpenedSkipPackages\n010700ad=array/config_protectedNetworks\n010700ae=array/config_rawContactsEligibleDefaultAccountTypes\n010700af=array/config_rearDisplayDeviceStates\n010700b0=array/config_reduceBrightColorsCoefficients\n010700b1=array/config_reduceBrightColorsCoefficientsNonlinear\n010700b2=array/config_requestVibrationParamsForUsages\n010700b3=array/config_restoreHealthConnectDataAndSettingsKnownSigners\n010700b4=array/config_restrictedImagesServices\n010700b5=array/config_ringtoneEffectUris\n010700b6=array/config_roundedCornerBottomRadiusAdjustmentArray\n010700b7=array/config_roundedCornerBottomRadiusArray\n010700b8=array/config_roundedCornerRadiusAdjustmentArray\n010700b9=array/config_roundedCornerRadiusArray\n010700ba=array/config_roundedCornerTopRadiusAdjustmentArray\n010700bb=array/config_roundedCornerTopRadiusArray\n010700bc=array/config_safeModeEnabledVibePattern\n010700bd=array/config_satellite_providers\n010700be=array/config_screenBrighteningThresholds\n010700bf=array/config_screenBrightnessBacklight\n010700c0=array/config_screenBrightnessNits\n010700c1=array/config_screenDarkeningThresholds\n010700c2=array/config_screenThresholdLevels\n010700c3=array/config_secondaryBuiltInDisplayCutoutSideOverride\n010700c4=array/config_secondaryBuiltInDisplayWaterfallCutout\n010700c5=array/config_securityStatePackages\n010700c6=array/config_serialPorts\n010700c7=array/config_serviceStateLocationAllowedPackages\n010700c8=array/config_setContactsDefaultAccountKnownSigners\n010700c9=array/config_sfps_enroll_stage_thresholds\n010700ca=array/config_sfps_sensor_props\n010700cb=array/config_sharedLibrariesLoadedAfterApp\n010700cc=array/config_smallAreaDetectionAllowlist\n010700cd=array/config_sms_enabled_locking_shift_tables\n010700ce=array/config_sms_enabled_single_shift_tables\n010700cf=array/config_statusBarIcons\n010700d0=array/config_supportedDreamComplications\n010700d1=array/config_supported_cellular_usage_settings\n010700d2=array/config_system_condition_providers\n010700d3=array/config_telephonyEuiccDeviceCapabilities\n010700d4=array/config_telephonyHardware\n010700d5=array/config_testLocationProviders\n010700d6=array/config_tether_bluetooth_regexs\n010700d7=array/config_tether_dhcp_range\n010700d8=array/config_tether_upstream_types\n010700d9=array/config_tether_usb_regexs\n010700da=array/config_tether_wifi_regexs\n010700db=array/config_toastCrossUserPackages\n010700dc=array/config_trustedAccessibilityServices\n010700dd=array/config_tvExternalInputLoggingDeviceBrandNames\n010700de=array/config_tvExternalInputLoggingDeviceOnScreenDisplayNames\n010700df=array/config_twoDigitNumberPattern\n010700e0=array/config_udfps_enroll_stage_thresholds\n010700e1=array/config_udfps_sensor_props\n010700e2=array/config_udfps_touch_detection_options\n010700e3=array/config_unsupported_network_capabilities\n010700e4=array/config_usbHostDenylist\n010700e5=array/config_verizon_satellite_enabled_tagids\n010700e6=array/config_virtualKeyVibePattern\n010700e7=array/config_vvmSmsFilterRegexes\n010700e8=array/config_waterfallCutoutArray\n010700e9=array/config_wearActivityModeRadios\n010700ea=array/crossSimSpnFormats\n010700eb=array/cross_profile_apps\n010700ec=array/default_wallpaper_component_per_device_color\n010700ed=array/demo_device_provisioning_known_signers\n010700ee=array/device_state_notification_active_contents\n010700ef=array/device_state_notification_active_titles\n010700f0=array/device_state_notification_names\n010700f1=array/device_state_notification_power_save_contents\n010700f2=array/device_state_notification_power_save_titles\n010700f3=array/device_state_notification_state_identifiers\n010700f4=array/device_state_notification_thermal_contents\n010700f5=array/device_state_notification_thermal_titles\n010700f6=array/dial_string_replace\n010700f7=array/disallowed_apps_managed_device\n010700f8=array/disallowed_apps_managed_profile\n010700f9=array/disallowed_apps_managed_user\n010700fa=array/face_acquired_vendor\n010700fb=array/face_error_vendor\n010700fc=array/fingerprint_acquired_vendor\n010700fd=array/fingerprint_error_vendor\n010700fe=array/imAddressTypes\n010700ff=array/maps_starting_lat_lng\n01070100=array/maps_starting_zoom\n01070101=array/networkAttributes\n01070102=array/network_switch_type_name\n01070103=array/networks_not_clear_data\n01070104=array/no_ems_support_sim_operators\n01070105=array/non_removable_euicc_slots\n01070106=array/pause_wallpaper_render_when_state_change\n01070107=array/policy_exempt_apps\n01070108=array/preloaded_color_state_lists\n01070109=array/preloaded_drawables\n0107010a=array/radioAttributes\n0107010b=array/required_apps_managed_device\n0107010c=array/required_apps_managed_profile\n0107010d=array/required_apps_managed_user\n0107010e=array/resolver_target_actions_pin\n0107010f=array/resolver_target_actions_unpin\n01070110=array/sim_colors\n01070111=array/special_locale_codes\n01070112=array/special_locale_names\n01070113=array/stoppable_fgs_system_apps\n01070114=array/supported_locales\n01070115=array/theming_defaults\n01070116=array/unloggable_phone_numbers\n01070117=array/vendor_cross_profile_apps\n01070118=array/vendor_disallowed_apps_managed_device\n01070119=array/vendor_disallowed_apps_managed_profile\n0107011a=array/vendor_disallowed_apps_managed_user\n0107011b=array/vendor_policy_exempt_apps\n0107011c=array/vendor_required_apps_managed_device\n0107011d=array/vendor_required_apps_managed_profile\n0107011e=array/vendor_required_apps_managed_user\n0107011f=array/vendor_required_attestation_certificates\n01070120=array/vendor_stoppable_fgs_system_apps\n01070121=array/wfcOperatorErrorAlertMessages\n01070122=array/wfcOperatorErrorNotificationMessages\n01070123=array/wfcSpnFormats\n01070124=array/wifi_known_signers\n01080000=drawable/alert_dark_frame\n01080001=drawable/alert_light_frame\n01080002=drawable/arrow_down_float\n01080003=drawable/arrow_up_float\n01080004=drawable/btn_default\n01080005=drawable/btn_default_small\n01080006=drawable/btn_dropdown\n01080007=drawable/btn_minus\n01080008=drawable/btn_plus\n01080009=drawable/btn_radio\n0108000a=drawable/btn_star\n0108000b=drawable/btn_star_big_off\n0108000c=drawable/btn_star_big_on\n0108000d=drawable/button_onoff_indicator_on\n0108000e=drawable/button_onoff_indicator_off\n0108000f=drawable/checkbox_off_background\n01080010=drawable/checkbox_on_background\n01080011=drawable/dialog_frame\n01080012=drawable/divider_horizontal_bright\n01080013=drawable/divider_horizontal_textfield\n01080014=drawable/divider_horizontal_dark\n01080015=drawable/divider_horizontal_dim_dark\n01080016=drawable/edit_text\n01080017=drawable/btn_dialog\n01080018=drawable/editbox_background\n01080019=drawable/editbox_background_normal\n0108001a=drawable/editbox_dropdown_dark_frame\n0108001b=drawable/editbox_dropdown_light_frame\n0108001c=drawable/gallery_thumb\n0108001d=drawable/ic_delete\n0108001e=drawable/ic_lock_idle_charging\n0108001f=drawable/ic_lock_idle_lock\n01080020=drawable/ic_lock_idle_low_battery\n01080021=drawable/ic_media_ff\n01080022=drawable/ic_media_next\n01080023=drawable/ic_media_pause\n01080024=drawable/ic_media_play\n01080025=drawable/ic_media_previous\n01080026=drawable/ic_media_rew\n01080027=drawable/ic_dialog_alert\n01080028=drawable/ic_dialog_dialer\n01080029=drawable/ic_dialog_email\n0108002a=drawable/ic_dialog_map\n0108002b=drawable/ic_input_add\n0108002c=drawable/ic_input_delete\n0108002d=drawable/ic_input_get\n0108002e=drawable/ic_lock_idle_alarm\n0108002f=drawable/ic_lock_lock\n01080030=drawable/ic_lock_power_off\n01080031=drawable/ic_lock_silent_mode\n01080032=drawable/ic_lock_silent_mode_off\n01080033=drawable/ic_menu_add\n01080034=drawable/ic_menu_agenda\n01080035=drawable/ic_menu_always_landscape_portrait\n01080036=drawable/ic_menu_call\n01080037=drawable/ic_menu_camera\n01080038=drawable/ic_menu_close_clear_cancel\n01080039=drawable/ic_menu_compass\n0108003a=drawable/ic_menu_crop\n0108003b=drawable/ic_menu_day\n0108003c=drawable/ic_menu_delete\n0108003d=drawable/ic_menu_directions\n0108003e=drawable/ic_menu_edit\n0108003f=drawable/ic_menu_gallery\n01080040=drawable/ic_menu_help\n01080041=drawable/ic_menu_info_details\n01080042=drawable/ic_menu_manage\n01080043=drawable/ic_menu_mapmode\n01080044=drawable/ic_menu_month\n01080045=drawable/ic_menu_more\n01080046=drawable/ic_menu_my_calendar\n01080047=drawable/ic_menu_mylocation\n01080048=drawable/ic_menu_myplaces\n01080049=drawable/ic_menu_preferences\n0108004a=drawable/ic_menu_recent_history\n0108004b=drawable/ic_menu_report_image\n0108004c=drawable/ic_menu_revert\n0108004d=drawable/ic_menu_rotate\n0108004e=drawable/ic_menu_save\n0108004f=drawable/ic_menu_search\n01080050=drawable/ic_menu_send\n01080051=drawable/ic_menu_set_as\n01080052=drawable/ic_menu_share\n01080053=drawable/ic_menu_slideshow\n01080054=drawable/ic_menu_today\n01080055=drawable/ic_menu_upload\n01080056=drawable/ic_menu_upload_you_tube\n01080057=drawable/ic_menu_view\n01080058=drawable/ic_menu_week\n01080059=drawable/ic_menu_zoom\n0108005a=drawable/ic_notification_clear_all\n0108005b=drawable/ic_notification_overlay\n0108005c=drawable/ic_partial_secure\n0108005d=drawable/ic_popup_disk_full\n0108005e=drawable/ic_popup_reminder\n0108005f=drawable/ic_popup_sync\n01080060=drawable/ic_search_category_default\n01080061=drawable/ic_secure\n01080062=drawable/list_selector_background\n01080063=drawable/menu_frame\n01080064=drawable/menu_full_frame\n01080065=drawable/menuitem_background\n01080066=drawable/picture_frame\n01080067=drawable/presence_away\n01080068=drawable/presence_busy\n01080069=drawable/presence_invisible\n0108006a=drawable/presence_offline\n0108006b=drawable/presence_online\n0108006c=drawable/progress_horizontal\n0108006d=drawable/progress_indeterminate_horizontal\n0108006e=drawable/radiobutton_off_background\n0108006f=drawable/radiobutton_on_background\n01080070=drawable/spinner_background\n01080071=drawable/spinner_dropdown_background\n01080072=drawable/star_big_on\n01080073=drawable/star_big_off\n01080074=drawable/star_on\n01080075=drawable/star_off\n01080076=drawable/stat_notify_call_mute\n01080077=drawable/stat_notify_chat\n01080078=drawable/stat_notify_error\n01080079=drawable/stat_notify_more\n0108007a=drawable/stat_notify_sdcard\n0108007b=drawable/stat_notify_sdcard_usb\n0108007c=drawable/stat_notify_sync\n0108007d=drawable/stat_notify_sync_noanim\n0108007e=drawable/stat_notify_voicemail\n0108007f=drawable/stat_notify_missed_call\n01080080=drawable/stat_sys_data_bluetooth\n01080081=drawable/stat_sys_download\n01080082=drawable/stat_sys_download_done\n01080083=drawable/stat_sys_headset\n01080084=drawable/stat_sys_phone_call\n01080085=drawable/stat_sys_phone_call_forward\n01080086=drawable/stat_sys_phone_call_on_hold\n01080087=drawable/stat_sys_speakerphone\n01080088=drawable/stat_sys_upload\n01080089=drawable/stat_sys_upload_done\n0108008a=drawable/stat_sys_warning\n0108008b=drawable/status_bar_item_app_background\n0108008c=drawable/status_bar_item_background\n0108008d=drawable/sym_action_call\n0108008e=drawable/sym_action_chat\n0108008f=drawable/sym_action_email\n01080090=drawable/sym_call_incoming\n01080091=drawable/sym_call_missed\n01080092=drawable/sym_call_outgoing\n01080093=drawable/sym_def_app_icon\n01080094=drawable/sym_contact_card\n01080095=drawable/title_bar\n01080096=drawable/toast_frame\n01080097=drawable/zoom_plate\n01080098=drawable/screen_background_dark\n01080099=drawable/screen_background_light\n0108009a=drawable/bottom_bar\n0108009b=drawable/ic_dialog_info\n0108009c=drawable/ic_menu_sort_alphabetically\n0108009d=drawable/ic_menu_sort_by_size\n0108009e=drawable/$$chooser_direct_share_icon_placeholder__0__0\n0108009f=drawable/$$chooser_direct_share_icon_placeholder__1__0\n010800a0=drawable/$$loader_horizontal_watch__1__0\n010800a1=drawable/$$loader_horizontal_watch__2__0\n010800a2=drawable/$$loader_horizontal_watch__3__0\n010800a3=drawable/$$loader_horizontal_watch__4__0\n010800a4=drawable/ic_btn_speak_now\n010800a5=drawable/dark_header\n010800a6=drawable/title_bar_tall\n010800a7=drawable/stat_sys_vp_phone_call\n010800a8=drawable/stat_sys_vp_phone_call_on_hold\n010800a9=drawable/screen_background_dark_transparent\n010800aa=drawable/screen_background_light_transparent\n010800ab=drawable/stat_notify_sdcard_prepare\n010800ac=drawable/presence_video_away\n010800ad=drawable/presence_video_busy\n010800ae=drawable/presence_video_online\n010800af=drawable/presence_audio_away\n010800b0=drawable/presence_audio_busy\n010800b1=drawable/presence_audio_online\n010800b2=drawable/dialog_holo_dark_frame\n010800b3=drawable/dialog_holo_light_frame\n010800b4=drawable/ic_info\n010800b5=drawable/ic_safety_protection\n010800b6=drawable/$$loader_horizontal_watch__5__0\n010800b7=drawable/$$loading_spinner__1__0\n010800b8=drawable/$$notification_progress_indeterminate_horizontal_material__0__0\n010800b9=drawable/$$notification_progress_indeterminate_horizontal_material__0__1\n010800ba=drawable/$$notification_progress_indeterminate_horizontal_material__0__2\n010800bb=drawable/$$notification_progress_indeterminate_horizontal_material__1__0\n010800bc=drawable/$$notification_progress_indeterminate_horizontal_material__1__1\n010800bd=drawable/$$notification_progress_indeterminate_horizontal_material__1__2\n010800be=drawable/$$notification_progress_indeterminate_horizontal_material__2__0\n010800bf=drawable/$$notification_progress_indeterminate_horizontal_material__2__1\n010800c0=drawable/$$notification_progress_indeterminate_horizontal_material__2__2\n010800c1=drawable/$$notification_progress_indeterminate_horizontal_material__3__0\n010800c2=drawable/$$notification_progress_indeterminate_horizontal_material__3__1\n010800c3=drawable/$$notification_progress_indeterminate_horizontal_material__3__2\n010800c4=drawable/$chooser_direct_share_icon_placeholder__0\n010800c5=drawable/$chooser_direct_share_icon_placeholder__1\n010800c6=drawable/$input_method_item_background_selected__0\n010800c7=drawable/$loader_horizontal_watch__0\n010800c8=drawable/$loader_horizontal_watch__1\n010800c9=drawable/$loader_horizontal_watch__2\n010800ca=drawable/$loader_horizontal_watch__3\n010800cb=drawable/$loader_horizontal_watch__4\n010800cc=drawable/$loader_horizontal_watch__5\n010800cd=drawable/$loader_horizontal_watch__6\n010800ce=drawable/$loader_horizontal_watch__7\n010800cf=drawable/$loading_spinner__0\n010800d0=drawable/$loading_spinner__1\n010800d1=drawable/$loading_spinner__2\n010800d2=drawable/$notification_progress_indeterminate_horizontal_material__0\n010800d3=drawable/$notification_progress_indeterminate_horizontal_material__1\n010800d4=drawable/$notification_progress_indeterminate_horizontal_material__2\n010800d5=drawable/$notification_progress_indeterminate_horizontal_material__3\n010800d6=drawable/$progress_indeterminate_horizontal_material__0\n010800d7=drawable/$progress_indeterminate_horizontal_material__1\n010800d8=drawable/$progress_indeterminate_horizontal_material__10\n010800d9=drawable/$progress_indeterminate_horizontal_material__11\n010800da=drawable/$progress_indeterminate_horizontal_material__12\n010800db=drawable/$progress_indeterminate_horizontal_material__13\n010800dc=drawable/$progress_indeterminate_horizontal_material__14\n010800dd=drawable/$progress_indeterminate_horizontal_material__15\n010800de=drawable/$progress_indeterminate_horizontal_material__16\n010800df=drawable/$progress_indeterminate_horizontal_material__17\n010800e0=drawable/$progress_indeterminate_horizontal_material__18\n010800e1=drawable/$progress_indeterminate_horizontal_material__19\n010800e2=drawable/$progress_indeterminate_horizontal_material__2\n010800e3=drawable/$progress_indeterminate_horizontal_material__20\n010800e4=drawable/$progress_indeterminate_horizontal_material__21\n010800e5=drawable/$progress_indeterminate_horizontal_material__22\n010800e6=drawable/$progress_indeterminate_horizontal_material__23\n010800e7=drawable/$progress_indeterminate_horizontal_material__24\n010800e8=drawable/$progress_indeterminate_horizontal_material__25\n010800e9=drawable/$progress_indeterminate_horizontal_material__26\n010800ea=drawable/$progress_indeterminate_horizontal_material__27\n010800eb=drawable/$progress_indeterminate_horizontal_material__28\n010800ec=drawable/$progress_indeterminate_horizontal_material__29\n010800ed=drawable/$progress_indeterminate_horizontal_material__3\n010800ee=drawable/$progress_indeterminate_horizontal_material__30\n010800ef=drawable/$progress_indeterminate_horizontal_material__31\n010800f0=drawable/$progress_indeterminate_horizontal_material__32\n010800f1=drawable/$progress_indeterminate_horizontal_material__33\n010800f2=drawable/$progress_indeterminate_horizontal_material__34\n010800f3=drawable/$progress_indeterminate_horizontal_material__35\n010800f4=drawable/$progress_indeterminate_horizontal_material__36\n010800f5=drawable/$progress_indeterminate_horizontal_material__37\n010800f6=drawable/$progress_indeterminate_horizontal_material__38\n010800f7=drawable/$progress_indeterminate_horizontal_material__39\n010800f8=drawable/$progress_indeterminate_horizontal_material__4\n010800f9=drawable/$progress_indeterminate_horizontal_material__40\n010800fa=drawable/$progress_indeterminate_horizontal_material__41\n010800fb=drawable/$progress_indeterminate_horizontal_material__42\n010800fc=drawable/$progress_indeterminate_horizontal_material__43\n010800fd=drawable/$progress_indeterminate_horizontal_material__44\n010800fe=drawable/$progress_indeterminate_horizontal_material__45\n010800ff=drawable/$progress_indeterminate_horizontal_material__46\n01080100=drawable/$progress_indeterminate_horizontal_material__47\n01080101=drawable/$progress_indeterminate_horizontal_material__48\n01080102=drawable/$progress_indeterminate_horizontal_material__49\n01080103=drawable/$progress_indeterminate_horizontal_material__5\n01080104=drawable/$progress_indeterminate_horizontal_material__50\n01080105=drawable/$progress_indeterminate_horizontal_material__51\n01080106=drawable/$progress_indeterminate_horizontal_material__52\n01080107=drawable/$progress_indeterminate_horizontal_material__53\n01080108=drawable/$progress_indeterminate_horizontal_material__54\n01080109=drawable/$progress_indeterminate_horizontal_material__55\n0108010a=drawable/$progress_indeterminate_horizontal_material__56\n0108010b=drawable/$progress_indeterminate_horizontal_material__57\n0108010c=drawable/$progress_indeterminate_horizontal_material__58\n0108010d=drawable/$progress_indeterminate_horizontal_material__59\n0108010e=drawable/$progress_indeterminate_horizontal_material__6\n0108010f=drawable/$progress_indeterminate_horizontal_material__7\n01080110=drawable/$progress_indeterminate_horizontal_material__8\n01080111=drawable/$progress_indeterminate_horizontal_material__9\n01080112=drawable/ab_bottom_solid_dark_holo\n01080113=drawable/ab_bottom_solid_inverse_holo\n01080114=drawable/ab_bottom_solid_light_holo\n01080115=drawable/ab_bottom_transparent_dark_holo\n01080116=drawable/ab_bottom_transparent_light_holo\n01080117=drawable/ab_share_pack_holo_dark\n01080118=drawable/ab_share_pack_holo_light\n01080119=drawable/ab_share_pack_material\n0108011a=drawable/ab_share_pack_mtrl_alpha\n0108011b=drawable/ab_solid_dark_holo\n0108011c=drawable/ab_solid_light_holo\n0108011d=drawable/ab_solid_shadow_holo\n0108011e=drawable/ab_solid_shadow_material\n0108011f=drawable/ab_solid_shadow_mtrl_alpha\n01080120=drawable/ab_stacked_solid_dark_holo\n01080121=drawable/ab_stacked_solid_inverse_holo\n01080122=drawable/ab_stacked_solid_light_holo\n01080123=drawable/ab_stacked_transparent_dark_holo\n01080124=drawable/ab_stacked_transparent_light_holo\n01080125=drawable/ab_transparent_dark_holo\n01080126=drawable/ab_transparent_light_holo\n01080127=drawable/accessibility_autoclick_button_group_rounded_background\n01080128=drawable/accessibility_autoclick_button_rounded_background\n01080129=drawable/accessibility_autoclick_double_click\n0108012a=drawable/accessibility_autoclick_drag\n0108012b=drawable/accessibility_autoclick_left_click\n0108012c=drawable/accessibility_autoclick_pause\n0108012d=drawable/accessibility_autoclick_position\n0108012e=drawable/accessibility_autoclick_resume\n0108012f=drawable/accessibility_autoclick_right_click\n01080130=drawable/accessibility_autoclick_scroll\n01080131=drawable/accessibility_autoclick_scroll_down\n01080132=drawable/accessibility_autoclick_scroll_exit\n01080133=drawable/accessibility_autoclick_scroll_left\n01080134=drawable/accessibility_autoclick_scroll_right\n01080135=drawable/accessibility_autoclick_scroll_up\n01080136=drawable/accessibility_autoclick_type_panel_rounded_background\n01080137=drawable/accessibility_magnification_thumbnail_background_bg\n01080138=drawable/accessibility_magnification_thumbnail_background_fg\n01080139=drawable/accessibility_magnification_thumbnail_bg\n0108013a=drawable/action_bar_background\n0108013b=drawable/action_bar_divider\n0108013c=drawable/action_bar_item_background_material\n0108013d=drawable/activated_background\n0108013e=drawable/activated_background_holo_dark\n0108013f=drawable/activated_background_holo_light\n01080140=drawable/activated_background_light\n01080141=drawable/activated_background_material\n01080142=drawable/activity_embedding_divider_handle\n01080143=drawable/activity_embedding_divider_handle_default\n01080144=drawable/activity_embedding_divider_handle_pressed\n01080145=drawable/activity_title_bar\n01080146=drawable/alert_window_layer\n01080147=drawable/android_logotype\n01080148=drawable/app_icon_background\n01080149=drawable/archived_app_cloud_overlay\n0108014a=drawable/autofill_bottomsheet_background\n0108014b=drawable/autofill_dataset_picker_background\n0108014c=drawable/autofill_half_sheet_divider\n0108014d=drawable/autofilled_highlight\n0108014e=drawable/background_holo_dark\n0108014f=drawable/background_holo_light\n01080150=drawable/background_leanback_setup\n01080151=drawable/battery_charge_background\n01080152=drawable/bilingual_language_item_selection_indicator\n01080153=drawable/blank_tile\n01080154=drawable/bottomsheet_background\n01080155=drawable/box\n01080156=drawable/btn_background_material_filled_tonal_watch\n01080157=drawable/btn_background_material_filled_watch\n01080158=drawable/btn_background_material_outlined_watch\n01080159=drawable/btn_background_material_text_watch\n0108015a=drawable/btn_borderless_material\n0108015b=drawable/btn_borderless_rect\n0108015c=drawable/btn_browser_zoom_fit_page\n0108015d=drawable/btn_browser_zoom_page_overview\n0108015e=drawable/btn_cab_done_default_holo_dark\n0108015f=drawable/btn_cab_done_default_holo_light\n01080160=drawable/btn_cab_done_focused_holo_dark\n01080161=drawable/btn_cab_done_focused_holo_light\n01080162=drawable/btn_cab_done_holo_dark\n01080163=drawable/btn_cab_done_holo_light\n01080164=drawable/btn_cab_done_pressed_holo_dark\n01080165=drawable/btn_cab_done_pressed_holo_light\n01080166=drawable/btn_check\n01080167=drawable/btn_check_buttonless_off\n01080168=drawable/btn_check_buttonless_on\n01080169=drawable/btn_check_holo_dark\n0108016a=drawable/btn_check_holo_light\n0108016b=drawable/btn_check_label_background\n0108016c=drawable/btn_check_material_anim\n0108016d=drawable/btn_check_off\n0108016e=drawable/btn_check_off_disable\n0108016f=drawable/btn_check_off_disable_focused\n01080170=drawable/btn_check_off_disable_focused_holo_dark\n01080171=drawable/btn_check_off_disable_focused_holo_light\n01080172=drawable/btn_check_off_disable_holo_dark\n01080173=drawable/btn_check_off_disable_holo_light\n01080174=drawable/btn_check_off_disabled_focused_holo_dark\n01080175=drawable/btn_check_off_disabled_focused_holo_light\n01080176=drawable/btn_check_off_disabled_holo_dark\n01080177=drawable/btn_check_off_disabled_holo_light\n01080178=drawable/btn_check_off_focused_holo_dark\n01080179=drawable/btn_check_off_focused_holo_light\n0108017a=drawable/btn_check_off_holo\n0108017b=drawable/btn_check_off_holo_dark\n0108017c=drawable/btn_check_off_holo_light\n0108017d=drawable/btn_check_off_normal_holo_dark\n0108017e=drawable/btn_check_off_normal_holo_light\n0108017f=drawable/btn_check_off_pressed\n01080180=drawable/btn_check_off_pressed_holo_dark\n01080181=drawable/btn_check_off_pressed_holo_light\n01080182=drawable/btn_check_off_selected\n01080183=drawable/btn_check_on\n01080184=drawable/btn_check_on_disable\n01080185=drawable/btn_check_on_disable_focused\n01080186=drawable/btn_check_on_disable_focused_holo_light\n01080187=drawable/btn_check_on_disable_holo_dark\n01080188=drawable/btn_check_on_disable_holo_light\n01080189=drawable/btn_check_on_disabled_focused_holo_dark\n0108018a=drawable/btn_check_on_disabled_focused_holo_light\n0108018b=drawable/btn_check_on_disabled_holo_dark\n0108018c=drawable/btn_check_on_disabled_holo_light\n0108018d=drawable/btn_check_on_focused_holo_dark\n0108018e=drawable/btn_check_on_focused_holo_light\n0108018f=drawable/btn_check_on_holo\n01080190=drawable/btn_check_on_holo_dark\n01080191=drawable/btn_check_on_holo_light\n01080192=drawable/btn_check_on_pressed\n01080193=drawable/btn_check_on_pressed_holo_dark\n01080194=drawable/btn_check_on_pressed_holo_light\n01080195=drawable/btn_check_on_selected\n01080196=drawable/btn_checkbox_checked_mtrl\n01080197=drawable/btn_checkbox_checked_to_unchecked_mtrl_animation\n01080198=drawable/btn_checkbox_unchecked_mtrl\n01080199=drawable/btn_checkbox_unchecked_to_checked_mtrl_animation\n0108019a=drawable/btn_circle\n0108019b=drawable/btn_circle_disable\n0108019c=drawable/btn_circle_disable_focused\n0108019d=drawable/btn_circle_normal\n0108019e=drawable/btn_circle_pressed\n0108019f=drawable/btn_circle_selected\n010801a0=drawable/btn_clock_material\n010801a1=drawable/btn_close\n010801a2=drawable/btn_close_normal\n010801a3=drawable/btn_close_pressed\n010801a4=drawable/btn_close_selected\n010801a5=drawable/btn_colored_material\n010801a6=drawable/btn_default_disabled_focused_holo_dark\n010801a7=drawable/btn_default_disabled_focused_holo_light\n010801a8=drawable/btn_default_disabled_holo\n010801a9=drawable/btn_default_disabled_holo_dark\n010801aa=drawable/btn_default_disabled_holo_light\n010801ab=drawable/btn_default_focused_holo\n010801ac=drawable/btn_default_focused_holo_dark\n010801ad=drawable/btn_default_focused_holo_light\n010801ae=drawable/btn_default_holo_dark\n010801af=drawable/btn_default_holo_light\n010801b0=drawable/btn_default_material\n010801b1=drawable/btn_default_mtrl_shape\n010801b2=drawable/btn_default_normal\n010801b3=drawable/btn_default_normal_disable\n010801b4=drawable/btn_default_normal_disable_focused\n010801b5=drawable/btn_default_normal_holo\n010801b6=drawable/btn_default_normal_holo_dark\n010801b7=drawable/btn_default_normal_holo_light\n010801b8=drawable/btn_default_pressed\n010801b9=drawable/btn_default_pressed_holo\n010801ba=drawable/btn_default_pressed_holo_dark\n010801bb=drawable/btn_default_pressed_holo_light\n010801bc=drawable/btn_default_selected\n010801bd=drawable/btn_default_small_normal\n010801be=drawable/btn_default_small_normal_disable\n010801bf=drawable/btn_default_small_normal_disable_focused\n010801c0=drawable/btn_default_small_pressed\n010801c1=drawable/btn_default_small_selected\n010801c2=drawable/btn_default_transparent\n010801c3=drawable/btn_default_transparent_normal\n010801c4=drawable/btn_dialog_disable\n010801c5=drawable/btn_dialog_normal\n010801c6=drawable/btn_dialog_pressed\n010801c7=drawable/btn_dialog_selected\n010801c8=drawable/btn_dropdown_disabled\n010801c9=drawable/btn_dropdown_disabled_focused\n010801ca=drawable/btn_dropdown_normal\n010801cb=drawable/btn_dropdown_pressed\n010801cc=drawable/btn_dropdown_selected\n010801cd=drawable/btn_erase_default\n010801ce=drawable/btn_erase_pressed\n010801cf=drawable/btn_erase_selected\n010801d0=drawable/btn_global_search\n010801d1=drawable/btn_global_search_normal\n010801d2=drawable/btn_group_disabled_holo_dark\n010801d3=drawable/btn_group_disabled_holo_light\n010801d4=drawable/btn_group_focused_holo_dark\n010801d5=drawable/btn_group_focused_holo_light\n010801d6=drawable/btn_group_holo_dark\n010801d7=drawable/btn_group_holo_light\n010801d8=drawable/btn_group_normal_holo_dark\n010801d9=drawable/btn_group_normal_holo_light\n010801da=drawable/btn_group_pressed_holo_dark\n010801db=drawable/btn_group_pressed_holo_light\n010801dc=drawable/btn_keyboard_key\n010801dd=drawable/btn_keyboard_key_dark_normal_holo\n010801de=drawable/btn_keyboard_key_dark_normal_off_holo\n010801df=drawable/btn_keyboard_key_dark_normal_on_holo\n010801e0=drawable/btn_keyboard_key_dark_pressed_holo\n010801e1=drawable/btn_keyboard_key_dark_pressed_off_holo\n010801e2=drawable/btn_keyboard_key_dark_pressed_on_holo\n010801e3=drawable/btn_keyboard_key_fulltrans\n010801e4=drawable/btn_keyboard_key_fulltrans_normal\n010801e5=drawable/btn_keyboard_key_fulltrans_normal_off\n010801e6=drawable/btn_keyboard_key_fulltrans_normal_on\n010801e7=drawable/btn_keyboard_key_fulltrans_pressed\n010801e8=drawable/btn_keyboard_key_fulltrans_pressed_off\n010801e9=drawable/btn_keyboard_key_fulltrans_pressed_on\n010801ea=drawable/btn_keyboard_key_ics\n010801eb=drawable/btn_keyboard_key_light_normal_holo\n010801ec=drawable/btn_keyboard_key_light_pressed_holo\n010801ed=drawable/btn_keyboard_key_material\n010801ee=drawable/btn_keyboard_key_normal\n010801ef=drawable/btn_keyboard_key_normal_off\n010801f0=drawable/btn_keyboard_key_normal_on\n010801f1=drawable/btn_keyboard_key_pressed\n010801f2=drawable/btn_keyboard_key_pressed_off\n010801f3=drawable/btn_keyboard_key_pressed_on\n010801f4=drawable/btn_keyboard_key_trans\n010801f5=drawable/btn_keyboard_key_trans_normal\n010801f6=drawable/btn_keyboard_key_trans_normal_off\n010801f7=drawable/btn_keyboard_key_trans_normal_on\n010801f8=drawable/btn_keyboard_key_trans_pressed\n010801f9=drawable/btn_keyboard_key_trans_pressed_off\n010801fa=drawable/btn_keyboard_key_trans_pressed_on\n010801fb=drawable/btn_keyboard_key_trans_selected\n010801fc=drawable/btn_leanback\n010801fd=drawable/btn_lock_normal\n010801fe=drawable/btn_media_player\n010801ff=drawable/btn_media_player_disabled\n01080200=drawable/btn_media_player_disabled_selected\n01080201=drawable/btn_media_player_pressed\n01080202=drawable/btn_media_player_selected\n01080203=drawable/btn_minus_default\n01080204=drawable/btn_minus_disable\n01080205=drawable/btn_minus_disable_focused\n01080206=drawable/btn_minus_pressed\n01080207=drawable/btn_minus_selected\n01080208=drawable/btn_notification_emphasized\n01080209=drawable/btn_outlined\n0108020a=drawable/btn_plus_default\n0108020b=drawable/btn_plus_disable\n0108020c=drawable/btn_plus_disable_focused\n0108020d=drawable/btn_plus_pressed\n0108020e=drawable/btn_plus_selected\n0108020f=drawable/btn_radio_holo_dark\n01080210=drawable/btn_radio_holo_light\n01080211=drawable/btn_radio_label_background\n01080212=drawable/btn_radio_material_anim\n01080213=drawable/btn_radio_off\n01080214=drawable/btn_radio_off_disabled_focused_holo_dark\n01080215=drawable/btn_radio_off_disabled_focused_holo_light\n01080216=drawable/btn_radio_off_disabled_holo_dark\n01080217=drawable/btn_radio_off_disabled_holo_light\n01080218=drawable/btn_radio_off_focused_holo_dark\n01080219=drawable/btn_radio_off_focused_holo_light\n0108021a=drawable/btn_radio_off_holo\n0108021b=drawable/btn_radio_off_holo_dark\n0108021c=drawable/btn_radio_off_holo_light\n0108021d=drawable/btn_radio_off_mtrl\n0108021e=drawable/btn_radio_off_pressed\n0108021f=drawable/btn_radio_off_pressed_holo_dark\n01080220=drawable/btn_radio_off_pressed_holo_light\n01080221=drawable/btn_radio_off_selected\n01080222=drawable/btn_radio_off_to_on_mtrl_animation\n01080223=drawable/btn_radio_on\n01080224=drawable/btn_radio_on_disabled_focused_holo_dark\n01080225=drawable/btn_radio_on_disabled_focused_holo_light\n01080226=drawable/btn_radio_on_disabled_holo_dark\n01080227=drawable/btn_radio_on_disabled_holo_light\n01080228=drawable/btn_radio_on_focused_holo_dark\n01080229=drawable/btn_radio_on_focused_holo_light\n0108022a=drawable/btn_radio_on_holo\n0108022b=drawable/btn_radio_on_holo_dark\n0108022c=drawable/btn_radio_on_holo_light\n0108022d=drawable/btn_radio_on_mtrl\n0108022e=drawable/btn_radio_on_mtrl_alpha\n0108022f=drawable/btn_radio_on_pressed\n01080230=drawable/btn_radio_on_pressed_holo_dark\n01080231=drawable/btn_radio_on_pressed_holo_light\n01080232=drawable/btn_radio_on_pressed_mtrl_alpha\n01080233=drawable/btn_radio_on_selected\n01080234=drawable/btn_radio_on_to_off_mtrl_animation\n01080235=drawable/btn_rating_star_off_disabled_focused_holo_dark\n01080236=drawable/btn_rating_star_off_disabled_focused_holo_light\n01080237=drawable/btn_rating_star_off_disabled_holo_dark\n01080238=drawable/btn_rating_star_off_disabled_holo_light\n01080239=drawable/btn_rating_star_off_focused_holo_dark\n0108023a=drawable/btn_rating_star_off_focused_holo_light\n0108023b=drawable/btn_rating_star_off_mtrl_alpha\n0108023c=drawable/btn_rating_star_off_normal\n0108023d=drawable/btn_rating_star_off_normal_holo_dark\n0108023e=drawable/btn_rating_star_off_normal_holo_light\n0108023f=drawable/btn_rating_star_off_pressed\n01080240=drawable/btn_rating_star_off_pressed_holo_dark\n01080241=drawable/btn_rating_star_off_pressed_holo_light\n01080242=drawable/btn_rating_star_off_selected\n01080243=drawable/btn_rating_star_on_disabled_focused_holo_dark\n01080244=drawable/btn_rating_star_on_disabled_focused_holo_light\n01080245=drawable/btn_rating_star_on_disabled_holo_dark\n01080246=drawable/btn_rating_star_on_disabled_holo_light\n01080247=drawable/btn_rating_star_on_focused_holo_dark\n01080248=drawable/btn_rating_star_on_focused_holo_light\n01080249=drawable/btn_rating_star_on_mtrl_alpha\n0108024a=drawable/btn_rating_star_on_normal\n0108024b=drawable/btn_rating_star_on_normal_holo_dark\n0108024c=drawable/btn_rating_star_on_normal_holo_light\n0108024d=drawable/btn_rating_star_on_pressed\n0108024e=drawable/btn_rating_star_on_pressed_holo_dark\n0108024f=drawable/btn_rating_star_on_pressed_holo_light\n01080250=drawable/btn_rating_star_on_selected\n01080251=drawable/btn_search_dialog\n01080252=drawable/btn_search_dialog_default\n01080253=drawable/btn_search_dialog_pressed\n01080254=drawable/btn_search_dialog_selected\n01080255=drawable/btn_search_dialog_voice\n01080256=drawable/btn_search_dialog_voice_default\n01080257=drawable/btn_search_dialog_voice_pressed\n01080258=drawable/btn_search_dialog_voice_selected\n01080259=drawable/btn_square_overlay\n0108025a=drawable/btn_square_overlay_disabled\n0108025b=drawable/btn_square_overlay_disabled_focused\n0108025c=drawable/btn_square_overlay_normal\n0108025d=drawable/btn_square_overlay_pressed\n0108025e=drawable/btn_square_overlay_selected\n0108025f=drawable/btn_star_big_off_disable\n01080260=drawable/btn_star_big_off_disable_focused\n01080261=drawable/btn_star_big_off_pressed\n01080262=drawable/btn_star_big_off_selected\n01080263=drawable/btn_star_big_on_disable\n01080264=drawable/btn_star_big_on_disable_focused\n01080265=drawable/btn_star_big_on_pressed\n01080266=drawable/btn_star_big_on_selected\n01080267=drawable/btn_star_holo_dark\n01080268=drawable/btn_star_holo_light\n01080269=drawable/btn_star_label_background\n0108026a=drawable/btn_star_material\n0108026b=drawable/btn_star_mtrl_alpha\n0108026c=drawable/btn_star_off_disabled_focused_holo_dark\n0108026d=drawable/btn_star_off_disabled_focused_holo_light\n0108026e=drawable/btn_star_off_disabled_holo_dark\n0108026f=drawable/btn_star_off_disabled_holo_light\n01080270=drawable/btn_star_off_focused_holo_dark\n01080271=drawable/btn_star_off_focused_holo_light\n01080272=drawable/btn_star_off_normal_holo_dark\n01080273=drawable/btn_star_off_normal_holo_light\n01080274=drawable/btn_star_off_pressed_holo_dark\n01080275=drawable/btn_star_off_pressed_holo_light\n01080276=drawable/btn_star_on_disabled_focused_holo_dark\n01080277=drawable/btn_star_on_disabled_focused_holo_light\n01080278=drawable/btn_star_on_disabled_holo_dark\n01080279=drawable/btn_star_on_disabled_holo_light\n0108027a=drawable/btn_star_on_focused_holo_dark\n0108027b=drawable/btn_star_on_focused_holo_light\n0108027c=drawable/btn_star_on_normal_holo_dark\n0108027d=drawable/btn_star_on_normal_holo_light\n0108027e=drawable/btn_star_on_pressed_holo_dark\n0108027f=drawable/btn_star_on_pressed_holo_light\n01080280=drawable/btn_switch_to_off_mtrl_00001\n01080281=drawable/btn_switch_to_off_mtrl_00002\n01080282=drawable/btn_switch_to_off_mtrl_00003\n01080283=drawable/btn_switch_to_off_mtrl_00004\n01080284=drawable/btn_switch_to_off_mtrl_00005\n01080285=drawable/btn_switch_to_off_mtrl_00006\n01080286=drawable/btn_switch_to_off_mtrl_00007\n01080287=drawable/btn_switch_to_off_mtrl_00008\n01080288=drawable/btn_switch_to_off_mtrl_00009\n01080289=drawable/btn_switch_to_off_mtrl_00010\n0108028a=drawable/btn_switch_to_off_mtrl_00011\n0108028b=drawable/btn_switch_to_off_mtrl_00012\n0108028c=drawable/btn_switch_to_on_mtrl_00001\n0108028d=drawable/btn_switch_to_on_mtrl_00002\n0108028e=drawable/btn_switch_to_on_mtrl_00003\n0108028f=drawable/btn_switch_to_on_mtrl_00004\n01080290=drawable/btn_switch_to_on_mtrl_00005\n01080291=drawable/btn_switch_to_on_mtrl_00006\n01080292=drawable/btn_switch_to_on_mtrl_00007\n01080293=drawable/btn_switch_to_on_mtrl_00008\n01080294=drawable/btn_switch_to_on_mtrl_00009\n01080295=drawable/btn_switch_to_on_mtrl_00010\n01080296=drawable/btn_switch_to_on_mtrl_00011\n01080297=drawable/btn_switch_to_on_mtrl_00012\n01080298=drawable/btn_toggle\n01080299=drawable/btn_toggle_bg\n0108029a=drawable/btn_toggle_holo_dark\n0108029b=drawable/btn_toggle_holo_light\n0108029c=drawable/btn_toggle_material\n0108029d=drawable/btn_toggle_off\n0108029e=drawable/btn_toggle_off_disabled_focused_holo_dark\n0108029f=drawable/btn_toggle_off_disabled_focused_holo_light\n010802a0=drawable/btn_toggle_off_disabled_holo_dark\n010802a1=drawable/btn_toggle_off_disabled_holo_light\n010802a2=drawable/btn_toggle_off_focused_holo_dark\n010802a3=drawable/btn_toggle_off_focused_holo_light\n010802a4=drawable/btn_toggle_off_normal_holo_dark\n010802a5=drawable/btn_toggle_off_normal_holo_light\n010802a6=drawable/btn_toggle_off_pressed_holo_dark\n010802a7=drawable/btn_toggle_off_pressed_holo_light\n010802a8=drawable/btn_toggle_on\n010802a9=drawable/btn_toggle_on_disabled_focused_holo_dark\n010802aa=drawable/btn_toggle_on_disabled_focused_holo_light\n010802ab=drawable/btn_toggle_on_disabled_holo_dark\n010802ac=drawable/btn_toggle_on_disabled_holo_light\n010802ad=drawable/btn_toggle_on_focused_holo_dark\n010802ae=drawable/btn_toggle_on_focused_holo_light\n010802af=drawable/btn_toggle_on_normal_holo_dark\n010802b0=drawable/btn_toggle_on_normal_holo_light\n010802b1=drawable/btn_toggle_on_pressed_holo_dark\n010802b2=drawable/btn_toggle_on_pressed_holo_light\n010802b3=drawable/btn_tonal\n010802b4=drawable/btn_zoom_down\n010802b5=drawable/btn_zoom_down_disabled\n010802b6=drawable/btn_zoom_down_disabled_focused\n010802b7=drawable/btn_zoom_down_normal\n010802b8=drawable/btn_zoom_down_pressed\n010802b9=drawable/btn_zoom_down_selected\n010802ba=drawable/btn_zoom_page\n010802bb=drawable/btn_zoom_page_normal\n010802bc=drawable/btn_zoom_page_press\n010802bd=drawable/btn_zoom_up\n010802be=drawable/btn_zoom_up_disabled\n010802bf=drawable/btn_zoom_up_disabled_focused\n010802c0=drawable/btn_zoom_up_normal\n010802c1=drawable/btn_zoom_up_pressed\n010802c2=drawable/btn_zoom_up_selected\n010802c3=drawable/button_inset\n010802c4=drawable/cab_background_bottom_holo_dark\n010802c5=drawable/cab_background_bottom_holo_light\n010802c6=drawable/cab_background_bottom_material\n010802c7=drawable/cab_background_bottom_mtrl_alpha\n010802c8=drawable/cab_background_top_holo_dark\n010802c9=drawable/cab_background_top_holo_light\n010802ca=drawable/cab_background_top_material\n010802cb=drawable/cab_background_top_mtrl_alpha\n010802cc=drawable/call_contact\n010802cd=drawable/car_button_background\n010802ce=drawable/car_dialog_button_background\n010802cf=drawable/car_seekbar_thumb\n010802d0=drawable/car_seekbar_thumb_dark\n010802d1=drawable/car_seekbar_thumb_light\n010802d2=drawable/car_seekbar_track\n010802d3=drawable/car_seekbar_track_dark\n010802d4=drawable/car_seekbar_track_light\n010802d5=drawable/car_switch_thumb\n010802d6=drawable/car_switch_track\n010802d7=drawable/chooser_action_button_bg\n010802d8=drawable/chooser_content_preview_rounded\n010802d9=drawable/chooser_dialog_background\n010802da=drawable/chooser_direct_share_icon_placeholder\n010802db=drawable/chooser_direct_share_label_placeholder\n010802dc=drawable/chooser_file_generic\n010802dd=drawable/chooser_group_background\n010802de=drawable/chooser_pinned_background\n010802df=drawable/chooser_row_layer_list\n010802e0=drawable/cling_arrow_up\n010802e1=drawable/cling_bg\n010802e2=drawable/cling_button\n010802e3=drawable/cling_button_normal\n010802e4=drawable/cling_button_pressed\n010802e5=drawable/clock_dial\n010802e6=drawable/clock_hand_hour\n010802e7=drawable/clock_hand_minute\n010802e8=drawable/close_button_bg\n010802e9=drawable/code_lock_bottom\n010802ea=drawable/code_lock_left\n010802eb=drawable/code_lock_top\n010802ec=drawable/combobox_disabled\n010802ed=drawable/combobox_nohighlight\n010802ee=drawable/compass_arrow\n010802ef=drawable/compass_base\n010802f0=drawable/config_scrollbarThumbVertical\n010802f1=drawable/config_scrollbarTrackVertical\n010802f2=drawable/contact_header_bg\n010802f3=drawable/control_background_32dp_material\n010802f4=drawable/control_background_40dp_material\n010802f5=drawable/conversation_badge_background\n010802f6=drawable/conversation_badge_ring\n010802f7=drawable/create_contact\n010802f8=drawable/dark_header_dither\n010802f9=drawable/day_picker_week_view_dayline_holo\n010802fa=drawable/default_lock_wallpaper\n010802fb=drawable/default_wallpaper\n010802fc=drawable/dialog_alert_button_background_negative_watch\n010802fd=drawable/dialog_alert_button_background_positive_watch\n010802fe=drawable/dialog_alert_button_negative_watch\n010802ff=drawable/dialog_alert_button_positive_watch\n01080300=drawable/dialog_background_material\n01080301=drawable/dialog_bottom_holo_dark\n01080302=drawable/dialog_bottom_holo_light\n01080303=drawable/dialog_divider_horizontal_holo_dark\n01080304=drawable/dialog_divider_horizontal_holo_light\n01080305=drawable/dialog_divider_horizontal_light\n01080306=drawable/dialog_full_holo_dark\n01080307=drawable/dialog_full_holo_light\n01080308=drawable/dialog_ic_close_focused_holo_dark\n01080309=drawable/dialog_ic_close_focused_holo_light\n0108030a=drawable/dialog_ic_close_normal_holo_dark\n0108030b=drawable/dialog_ic_close_normal_holo_light\n0108030c=drawable/dialog_ic_close_pressed_holo_dark\n0108030d=drawable/dialog_ic_close_pressed_holo_light\n0108030e=drawable/dialog_middle_holo\n0108030f=drawable/dialog_middle_holo_dark\n01080310=drawable/dialog_middle_holo_light\n01080311=drawable/dialog_top_holo_dark\n01080312=drawable/dialog_top_holo_light\n01080313=drawable/divider_horizontal_bright_opaque\n01080314=drawable/divider_horizontal_dark_opaque\n01080315=drawable/divider_horizontal_holo_dark\n01080316=drawable/divider_horizontal_holo_light\n01080317=drawable/divider_strong_holo\n01080318=drawable/divider_vertical_bright\n01080319=drawable/divider_vertical_bright_opaque\n0108031a=drawable/divider_vertical_dark\n0108031b=drawable/divider_vertical_dark_opaque\n0108031c=drawable/divider_vertical_holo_dark\n0108031d=drawable/divider_vertical_holo_light\n0108031e=drawable/dropdown_disabled_focused_holo_dark\n0108031f=drawable/dropdown_disabled_focused_holo_light\n01080320=drawable/dropdown_disabled_holo_dark\n01080321=drawable/dropdown_disabled_holo_light\n01080322=drawable/dropdown_focused_holo_dark\n01080323=drawable/dropdown_focused_holo_light\n01080324=drawable/dropdown_ic_arrow_disabled_focused_holo_dark\n01080325=drawable/dropdown_ic_arrow_disabled_focused_holo_light\n01080326=drawable/dropdown_ic_arrow_disabled_holo_dark\n01080327=drawable/dropdown_ic_arrow_disabled_holo_light\n01080328=drawable/dropdown_ic_arrow_focused_holo_dark\n01080329=drawable/dropdown_ic_arrow_focused_holo_light\n0108032a=drawable/dropdown_ic_arrow_normal_holo_dark\n0108032b=drawable/dropdown_ic_arrow_normal_holo_light\n0108032c=drawable/dropdown_ic_arrow_pressed_holo_dark\n0108032d=drawable/dropdown_ic_arrow_pressed_holo_light\n0108032e=drawable/dropdown_normal_holo_dark\n0108032f=drawable/dropdown_normal_holo_light\n01080330=drawable/dropdown_pressed_holo_dark\n01080331=drawable/dropdown_pressed_holo_light\n01080332=drawable/edit_query\n01080333=drawable/edit_query_background\n01080334=drawable/edit_query_background_normal\n01080335=drawable/edit_query_background_pressed\n01080336=drawable/edit_query_background_selected\n01080337=drawable/edit_text_holo_dark\n01080338=drawable/edit_text_holo_light\n01080339=drawable/edit_text_material\n0108033a=drawable/editbox_background_focus_yellow\n0108033b=drawable/editbox_dropdown_background\n0108033c=drawable/editbox_dropdown_background_dark\n0108033d=drawable/emergency_icon\n0108033e=drawable/emo_im_angel\n0108033f=drawable/emo_im_cool\n01080340=drawable/emo_im_crying\n01080341=drawable/emo_im_embarrassed\n01080342=drawable/emo_im_foot_in_mouth\n01080343=drawable/emo_im_happy\n01080344=drawable/emo_im_kissing\n01080345=drawable/emo_im_laughing\n01080346=drawable/emo_im_lips_are_sealed\n01080347=drawable/emo_im_money_mouth\n01080348=drawable/emo_im_sad\n01080349=drawable/emo_im_surprised\n0108034a=drawable/emo_im_tongue_sticking_out\n0108034b=drawable/emo_im_undecided\n0108034c=drawable/emo_im_winking\n0108034d=drawable/emo_im_wtf\n0108034e=drawable/emo_im_yelling\n0108034f=drawable/emulator_circular_window_overlay\n01080350=drawable/expand_button_pill_bg\n01080351=drawable/expander_close_holo_dark\n01080352=drawable/expander_close_holo_light\n01080353=drawable/expander_close_mtrl_alpha\n01080354=drawable/expander_group\n01080355=drawable/expander_group_holo_dark\n01080356=drawable/expander_group_holo_light\n01080357=drawable/expander_group_material\n01080358=drawable/expander_ic_maximized\n01080359=drawable/expander_ic_minimized\n0108035a=drawable/expander_open_holo_dark\n0108035b=drawable/expander_open_holo_light\n0108035c=drawable/expander_open_mtrl_alpha\n0108035d=drawable/fastscroll_label_left_holo_dark\n0108035e=drawable/fastscroll_label_left_holo_light\n0108035f=drawable/fastscroll_label_left_material\n01080360=drawable/fastscroll_label_right_holo_dark\n01080361=drawable/fastscroll_label_right_holo_light\n01080362=drawable/fastscroll_label_right_material\n01080363=drawable/fastscroll_thumb_default_holo\n01080364=drawable/fastscroll_thumb_holo\n01080365=drawable/fastscroll_thumb_material\n01080366=drawable/fastscroll_thumb_pressed_holo\n01080367=drawable/fastscroll_track_default_holo_dark\n01080368=drawable/fastscroll_track_default_holo_light\n01080369=drawable/fastscroll_track_holo_dark\n0108036a=drawable/fastscroll_track_holo_light\n0108036b=drawable/fastscroll_track_material\n0108036c=drawable/fastscroll_track_pressed_holo_dark\n0108036d=drawable/fastscroll_track_pressed_holo_light\n0108036e=drawable/floating_popup_background\n0108036f=drawable/focus_event_pressed_key_background\n01080370=drawable/focus_event_rotary_input_background\n01080371=drawable/focused_application_background_static\n01080372=drawable/frame_gallery_thumb\n01080373=drawable/frame_gallery_thumb_pressed\n01080374=drawable/frame_gallery_thumb_selected\n01080375=drawable/ft_avd_toarrow\n01080376=drawable/ft_avd_toarrow_animation\n01080377=drawable/ft_avd_tooverflow\n01080378=drawable/ft_avd_tooverflow_animation\n01080379=drawable/gallery_item_background\n0108037a=drawable/gallery_selected_default\n0108037b=drawable/gallery_selected_focused\n0108037c=drawable/gallery_selected_pressed\n0108037d=drawable/gallery_unselected_default\n0108037e=drawable/gallery_unselected_pressed\n0108037f=drawable/global_action_item_divider\n01080380=drawable/global_actions_item_grey_background\n01080381=drawable/global_actions_item_grey_background_shape\n01080382=drawable/global_actions_item_red_background\n01080383=drawable/global_actions_item_red_background_shape\n01080384=drawable/grid_selector_background\n01080385=drawable/grid_selector_background_focus\n01080386=drawable/grid_selector_background_pressed\n01080387=drawable/highlight_disabled\n01080388=drawable/highlight_pressed\n01080389=drawable/highlight_selected\n0108038a=drawable/ic_ab_back_holo_dark\n0108038b=drawable/ic_ab_back_holo_dark_am\n0108038c=drawable/ic_ab_back_holo_light\n0108038d=drawable/ic_ab_back_holo_light_am\n0108038e=drawable/ic_ab_back_material\n0108038f=drawable/ic_ab_back_material_dark\n01080390=drawable/ic_ab_back_material_light\n01080391=drawable/ic_ab_back_material_settings\n01080392=drawable/ic_accessibility_24dp\n01080393=drawable/ic_accessibility_autoclick\n01080394=drawable/ic_accessibility_autoclick_foreground\n01080395=drawable/ic_accessibility_color_correction\n01080396=drawable/ic_accessibility_color_correction_foreground\n01080397=drawable/ic_accessibility_color_inversion\n01080398=drawable/ic_accessibility_color_inversion_foreground\n01080399=drawable/ic_accessibility_generic\n0108039a=drawable/ic_accessibility_hearing_aid\n0108039b=drawable/ic_accessibility_hearing_aid_blue_dot\n0108039c=drawable/ic_accessibility_hearing_aid_disconnected\n0108039d=drawable/ic_accessibility_hearing_aid_disconnected_foreground\n0108039e=drawable/ic_accessibility_hearing_aid_foreground\n0108039f=drawable/ic_accessibility_hearing_aid_green_dot\n010803a0=drawable/ic_accessibility_magnification\n010803a1=drawable/ic_accessibility_magnification_foreground\n010803a2=drawable/ic_accessibility_one_handed\n010803a3=drawable/ic_accessibility_one_handed_foreground\n010803a4=drawable/ic_accessibility_reduce_bright_colors\n010803a5=drawable/ic_accessibility_reduce_bright_colors_foreground\n010803a6=drawable/ic_account_circle\n010803a7=drawable/ic_action_assist_focused\n010803a8=drawable/ic_action_open\n010803a9=drawable/ic_add_supervised_user\n010803aa=drawable/ic_aggregated\n010803ab=drawable/ic_alert_window_layer\n010803ac=drawable/ic_android_satellite_24px\n010803ad=drawable/ic_arrow_drop_right_black_24dp\n010803ae=drawable/ic_arrow_forward\n010803af=drawable/ic_audio_alarm\n010803b0=drawable/ic_audio_alarm_mute\n010803b1=drawable/ic_audio_media\n010803b2=drawable/ic_audio_media_mute\n010803b3=drawable/ic_audio_notification\n010803b4=drawable/ic_audio_notification_am_alpha\n010803b5=drawable/ic_audio_notification_mute\n010803b6=drawable/ic_audio_notification_mute_am_alpha\n010803b7=drawable/ic_audio_ring_notif\n010803b8=drawable/ic_audio_ring_notif_mute\n010803b9=drawable/ic_audio_ring_notif_vibrate\n010803ba=drawable/ic_audio_vol\n010803bb=drawable/ic_audio_vol_mute\n010803bc=drawable/ic_battery\n010803bd=drawable/ic_battery_80_24dp\n010803be=drawable/ic_bluetooth_transient_animation\n010803bf=drawable/ic_bluetooth_transient_animation_drawable\n010803c0=drawable/ic_bt_headphones_a2dp\n010803c1=drawable/ic_bt_headset_hfp\n010803c2=drawable/ic_bt_hearing_aid\n010803c3=drawable/ic_bt_laptop\n010803c4=drawable/ic_bt_misc_hid\n010803c5=drawable/ic_bt_network_pan\n010803c6=drawable/ic_bt_pointing_hid\n010803c7=drawable/ic_btn_round_more\n010803c8=drawable/ic_btn_round_more_disabled\n010803c9=drawable/ic_btn_round_more_normal\n010803ca=drawable/ic_btn_search_go\n010803cb=drawable/ic_btn_square_browser_zoom_fit_page\n010803cc=drawable/ic_btn_square_browser_zoom_fit_page_disabled\n010803cd=drawable/ic_btn_square_browser_zoom_fit_page_normal\n010803ce=drawable/ic_btn_square_browser_zoom_page_overview\n010803cf=drawable/ic_btn_square_browser_zoom_page_overview_disabled\n010803d0=drawable/ic_btn_square_browser_zoom_page_overview_normal\n010803d1=drawable/ic_bullet_key_permission\n010803d2=drawable/ic_cab_done_holo\n010803d3=drawable/ic_cab_done_holo_dark\n010803d4=drawable/ic_cab_done_holo_light\n010803d5=drawable/ic_cab_done_mtrl_alpha\n010803d6=drawable/ic_call_answer\n010803d7=drawable/ic_call_answer_video\n010803d8=drawable/ic_call_decline\n010803d9=drawable/ic_camera\n010803da=drawable/ic_camera_allowed\n010803db=drawable/ic_camera_blocked\n010803dc=drawable/ic_check_24dp\n010803dd=drawable/ic_check_circle_24px\n010803de=drawable/ic_check_watch\n010803df=drawable/ic_checkmark_holo_light\n010803e0=drawable/ic_chevron_end\n010803e1=drawable/ic_chevron_start\n010803e2=drawable/ic_chooser_group_arrow\n010803e3=drawable/ic_chooser_pin\n010803e4=drawable/ic_chooser_pin_dialog\n010803e5=drawable/ic_clear\n010803e6=drawable/ic_clear_disabled\n010803e7=drawable/ic_clear_holo_dark\n010803e8=drawable/ic_clear_holo_light\n010803e9=drawable/ic_clear_material\n010803ea=drawable/ic_clear_mtrl_alpha\n010803eb=drawable/ic_clear_normal\n010803ec=drawable/ic_clear_search_api_disabled_holo_dark\n010803ed=drawable/ic_clear_search_api_disabled_holo_light\n010803ee=drawable/ic_clear_search_api_holo_dark\n010803ef=drawable/ic_clear_search_api_holo_light\n010803f0=drawable/ic_clone_badge\n010803f1=drawable/ic_clone_icon_badge\n010803f2=drawable/ic_close\n010803f3=drawable/ic_close_watch\n010803f4=drawable/ic_coins_l\n010803f5=drawable/ic_coins_s\n010803f6=drawable/ic_collapse_bundle\n010803f7=drawable/ic_collapse_notification\n010803f8=drawable/ic_commit\n010803f9=drawable/ic_commit_search_api_holo_dark\n010803fa=drawable/ic_commit_search_api_holo_light\n010803fb=drawable/ic_commit_search_api_material\n010803fc=drawable/ic_commit_search_api_mtrl_alpha\n010803fd=drawable/ic_contact_picture\n010803fe=drawable/ic_contact_picture_180_holo_dark\n010803ff=drawable/ic_contact_picture_180_holo_light\n01080400=drawable/ic_contact_picture_2\n01080401=drawable/ic_contact_picture_3\n01080402=drawable/ic_contact_picture_holo_dark\n01080403=drawable/ic_contact_picture_holo_light\n01080404=drawable/ic_corp_badge\n01080405=drawable/ic_corp_badge_case\n01080406=drawable/ic_corp_badge_color\n01080407=drawable/ic_corp_badge_no_background\n01080408=drawable/ic_corp_badge_off\n01080409=drawable/ic_corp_icon\n0108040a=drawable/ic_corp_icon_badge_case\n0108040b=drawable/ic_corp_icon_badge_color\n0108040c=drawable/ic_corp_icon_badge_shadow\n0108040d=drawable/ic_corp_statusbar_icon\n0108040e=drawable/ic_corp_user_badge\n0108040f=drawable/ic_dialog_alert_holo_dark\n01080410=drawable/ic_dialog_alert_holo_light\n01080411=drawable/ic_dialog_alert_material\n01080412=drawable/ic_dialog_close_normal_holo\n01080413=drawable/ic_dialog_close_pressed_holo\n01080414=drawable/ic_dialog_focused_holo\n01080415=drawable/ic_dialog_time\n01080416=drawable/ic_dialog_usb\n01080417=drawable/ic_dnd_block_notifications\n01080418=drawable/ic_doc_apk\n01080419=drawable/ic_doc_audio\n0108041a=drawable/ic_doc_certificate\n0108041b=drawable/ic_doc_codes\n0108041c=drawable/ic_doc_compressed\n0108041d=drawable/ic_doc_contact\n0108041e=drawable/ic_doc_document\n0108041f=drawable/ic_doc_event\n01080420=drawable/ic_doc_excel\n01080421=drawable/ic_doc_folder\n01080422=drawable/ic_doc_font\n01080423=drawable/ic_doc_generic\n01080424=drawable/ic_doc_image\n01080425=drawable/ic_doc_pdf\n01080426=drawable/ic_doc_powerpoint\n01080427=drawable/ic_doc_presentation\n01080428=drawable/ic_doc_spreadsheet\n01080429=drawable/ic_doc_text\n0108042a=drawable/ic_doc_video\n0108042b=drawable/ic_doc_word\n0108042c=drawable/ic_drag_handle\n0108042d=drawable/ic_dual_screen\n0108042e=drawable/ic_eject_24dp\n0108042f=drawable/ic_emergency\n01080430=drawable/ic_expand_bundle\n01080431=drawable/ic_expand_more\n01080432=drawable/ic_expand_more_48dp\n01080433=drawable/ic_expand_notification\n01080434=drawable/ic_faster_emergency\n01080435=drawable/ic_feedback\n01080436=drawable/ic_feedback_alerted\n01080437=drawable/ic_feedback_downrank\n01080438=drawable/ic_feedback_indicator\n01080439=drawable/ic_feedback_silenced\n0108043a=drawable/ic_feedback_uprank\n0108043b=drawable/ic_file_copy\n0108043c=drawable/ic_find_next_holo_dark\n0108043d=drawable/ic_find_next_holo_light\n0108043e=drawable/ic_find_next_material\n0108043f=drawable/ic_find_next_mtrl_alpha\n01080440=drawable/ic_find_previous_holo_dark\n01080441=drawable/ic_find_previous_holo_light\n01080442=drawable/ic_find_previous_material\n01080443=drawable/ic_find_previous_mtrl_alpha\n01080444=drawable/ic_fingerprint\n01080445=drawable/ic_folder_24dp\n01080446=drawable/ic_go\n01080447=drawable/ic_go_search_api_holo_dark\n01080448=drawable/ic_go_search_api_holo_light\n01080449=drawable/ic_go_search_api_material\n0108044a=drawable/ic_hotspot_transient_animation\n0108044b=drawable/ic_hotspot_transient_animation_drawable\n0108044c=drawable/ic_ime_nav_back\n0108044d=drawable/ic_ime_switcher\n0108044e=drawable/ic_ime_switcher_new\n0108044f=drawable/ic_info_outline\n01080450=drawable/ic_info_outline_24\n01080451=drawable/ic_input_extract_action_done\n01080452=drawable/ic_input_extract_action_go\n01080453=drawable/ic_input_extract_action_next\n01080454=drawable/ic_input_extract_action_previous\n01080455=drawable/ic_input_extract_action_return\n01080456=drawable/ic_input_extract_action_search\n01080457=drawable/ic_input_extract_action_send\n01080458=drawable/ic_instant_icon_badge_bolt\n01080459=drawable/ic_jog_dial_answer\n0108045a=drawable/ic_jog_dial_answer_and_end\n0108045b=drawable/ic_jog_dial_answer_and_hold\n0108045c=drawable/ic_jog_dial_decline\n0108045d=drawable/ic_jog_dial_sound_off\n0108045e=drawable/ic_jog_dial_sound_on\n0108045f=drawable/ic_jog_dial_unlock\n01080460=drawable/ic_jog_dial_vibrate_on\n01080461=drawable/ic_launcher_android\n01080462=drawable/ic_lock\n01080463=drawable/ic_lock_airplane_mode\n01080464=drawable/ic_lock_airplane_mode_alpha\n01080465=drawable/ic_lock_airplane_mode_off\n01080466=drawable/ic_lock_airplane_mode_off_am_alpha\n01080467=drawable/ic_lock_bugreport\n01080468=drawable/ic_lock_idle_alarm_alpha\n01080469=drawable/ic_lock_lock_alpha\n0108046a=drawable/ic_lock_lockdown\n0108046b=drawable/ic_lock_open\n0108046c=drawable/ic_lock_open_wht_24dp\n0108046d=drawable/ic_lock_outline_wht_24dp\n0108046e=drawable/ic_lock_power_off_alpha\n0108046f=drawable/ic_lock_ringer_off_alpha\n01080470=drawable/ic_lock_ringer_on_alpha\n01080471=drawable/ic_lock_silent_mode_vibrate\n01080472=drawable/ic_lockscreen_alarm\n01080473=drawable/ic_lockscreen_answer_active\n01080474=drawable/ic_lockscreen_answer_focused\n01080475=drawable/ic_lockscreen_answer_normal\n01080476=drawable/ic_lockscreen_camera_activated\n01080477=drawable/ic_lockscreen_camera_normal\n01080478=drawable/ic_lockscreen_chevron_down\n01080479=drawable/ic_lockscreen_chevron_left\n0108047a=drawable/ic_lockscreen_chevron_right\n0108047b=drawable/ic_lockscreen_chevron_up\n0108047c=drawable/ic_lockscreen_decline_activated\n0108047d=drawable/ic_lockscreen_decline_focused\n0108047e=drawable/ic_lockscreen_decline_normal\n0108047f=drawable/ic_lockscreen_emergencycall_normal\n01080480=drawable/ic_lockscreen_emergencycall_pressed\n01080481=drawable/ic_lockscreen_forgotpassword_normal\n01080482=drawable/ic_lockscreen_forgotpassword_pressed\n01080483=drawable/ic_lockscreen_google_activated\n01080484=drawable/ic_lockscreen_google_focused\n01080485=drawable/ic_lockscreen_google_normal\n01080486=drawable/ic_lockscreen_handle_normal\n01080487=drawable/ic_lockscreen_handle_pressed\n01080488=drawable/ic_lockscreen_ime\n01080489=drawable/ic_lockscreen_outerring\n0108048a=drawable/ic_lockscreen_player_background\n0108048b=drawable/ic_lockscreen_puk\n0108048c=drawable/ic_lockscreen_silent_activated\n0108048d=drawable/ic_lockscreen_silent_focused\n0108048e=drawable/ic_lockscreen_silent_normal\n0108048f=drawable/ic_lockscreen_sim\n01080490=drawable/ic_lockscreen_soundon_activated\n01080491=drawable/ic_lockscreen_soundon_focused\n01080492=drawable/ic_lockscreen_soundon_normal\n01080493=drawable/ic_lockscreen_text_activated\n01080494=drawable/ic_lockscreen_text_focusde\n01080495=drawable/ic_lockscreen_text_normal\n01080496=drawable/ic_lockscreen_unlock_activated\n01080497=drawable/ic_lockscreen_unlock_normal\n01080498=drawable/ic_lockscreens_now_button\n01080499=drawable/ic_logout\n0108049a=drawable/ic_maps_indicator_current_position\n0108049b=drawable/ic_maps_indicator_current_position_anim\n0108049c=drawable/ic_maps_indicator_current_position_anim1\n0108049d=drawable/ic_maps_indicator_current_position_anim2\n0108049e=drawable/ic_maps_indicator_current_position_anim3\n0108049f=drawable/ic_media_embed_play\n010804a0=drawable/ic_media_fullscreen\n010804a1=drawable/ic_media_route_connected_dark_00_mtrl\n010804a2=drawable/ic_media_route_connected_dark_01_mtrl\n010804a3=drawable/ic_media_route_connected_dark_02_mtrl\n010804a4=drawable/ic_media_route_connected_dark_03_mtrl\n010804a5=drawable/ic_media_route_connected_dark_04_mtrl\n010804a6=drawable/ic_media_route_connected_dark_05_mtrl\n010804a7=drawable/ic_media_route_connected_dark_06_mtrl\n010804a8=drawable/ic_media_route_connected_dark_07_mtrl\n010804a9=drawable/ic_media_route_connected_dark_08_mtrl\n010804aa=drawable/ic_media_route_connected_dark_09_mtrl\n010804ab=drawable/ic_media_route_connected_dark_10_mtrl\n010804ac=drawable/ic_media_route_connected_dark_11_mtrl\n010804ad=drawable/ic_media_route_connected_dark_12_mtrl\n010804ae=drawable/ic_media_route_connected_dark_13_mtrl\n010804af=drawable/ic_media_route_connected_dark_14_mtrl\n010804b0=drawable/ic_media_route_connected_dark_15_mtrl\n010804b1=drawable/ic_media_route_connected_dark_16_mtrl\n010804b2=drawable/ic_media_route_connected_dark_17_mtrl\n010804b3=drawable/ic_media_route_connected_dark_18_mtrl\n010804b4=drawable/ic_media_route_connected_dark_19_mtrl\n010804b5=drawable/ic_media_route_connected_dark_20_mtrl\n010804b6=drawable/ic_media_route_connected_dark_21_mtrl\n010804b7=drawable/ic_media_route_connected_dark_22_mtrl\n010804b8=drawable/ic_media_route_connected_dark_23_mtrl\n010804b9=drawable/ic_media_route_connected_dark_24_mtrl\n010804ba=drawable/ic_media_route_connected_dark_25_mtrl\n010804bb=drawable/ic_media_route_connected_dark_26_mtrl\n010804bc=drawable/ic_media_route_connected_dark_27_mtrl\n010804bd=drawable/ic_media_route_connected_dark_28_mtrl\n010804be=drawable/ic_media_route_connected_dark_29_mtrl\n010804bf=drawable/ic_media_route_connected_dark_30_mtrl\n010804c0=drawable/ic_media_route_connected_dark_material\n010804c1=drawable/ic_media_route_connected_light_00_mtrl\n010804c2=drawable/ic_media_route_connected_light_01_mtrl\n010804c3=drawable/ic_media_route_connected_light_02_mtrl\n010804c4=drawable/ic_media_route_connected_light_03_mtrl\n010804c5=drawable/ic_media_route_connected_light_04_mtrl\n010804c6=drawable/ic_media_route_connected_light_05_mtrl\n010804c7=drawable/ic_media_route_connected_light_06_mtrl\n010804c8=drawable/ic_media_route_connected_light_07_mtrl\n010804c9=drawable/ic_media_route_connected_light_08_mtrl\n010804ca=drawable/ic_media_route_connected_light_09_mtrl\n010804cb=drawable/ic_media_route_connected_light_10_mtrl\n010804cc=drawable/ic_media_route_connected_light_11_mtrl\n010804cd=drawable/ic_media_route_connected_light_12_mtrl\n010804ce=drawable/ic_media_route_connected_light_13_mtrl\n010804cf=drawable/ic_media_route_connected_light_14_mtrl\n010804d0=drawable/ic_media_route_connected_light_15_mtrl\n010804d1=drawable/ic_media_route_connected_light_16_mtrl\n010804d2=drawable/ic_media_route_connected_light_17_mtrl\n010804d3=drawable/ic_media_route_connected_light_18_mtrl\n010804d4=drawable/ic_media_route_connected_light_19_mtrl\n010804d5=drawable/ic_media_route_connected_light_20_mtrl\n010804d6=drawable/ic_media_route_connected_light_21_mtrl\n010804d7=drawable/ic_media_route_connected_light_22_mtrl\n010804d8=drawable/ic_media_route_connected_light_23_mtrl\n010804d9=drawable/ic_media_route_connected_light_24_mtrl\n010804da=drawable/ic_media_route_connected_light_25_mtrl\n010804db=drawable/ic_media_route_connected_light_26_mtrl\n010804dc=drawable/ic_media_route_connected_light_27_mtrl\n010804dd=drawable/ic_media_route_connected_light_28_mtrl\n010804de=drawable/ic_media_route_connected_light_29_mtrl\n010804df=drawable/ic_media_route_connected_light_30_mtrl\n010804e0=drawable/ic_media_route_connected_light_material\n010804e1=drawable/ic_media_route_connecting_dark_00_mtrl\n010804e2=drawable/ic_media_route_connecting_dark_01_mtrl\n010804e3=drawable/ic_media_route_connecting_dark_02_mtrl\n010804e4=drawable/ic_media_route_connecting_dark_03_mtrl\n010804e5=drawable/ic_media_route_connecting_dark_04_mtrl\n010804e6=drawable/ic_media_route_connecting_dark_05_mtrl\n010804e7=drawable/ic_media_route_connecting_dark_06_mtrl\n010804e8=drawable/ic_media_route_connecting_dark_07_mtrl\n010804e9=drawable/ic_media_route_connecting_dark_08_mtrl\n010804ea=drawable/ic_media_route_connecting_dark_09_mtrl\n010804eb=drawable/ic_media_route_connecting_dark_10_mtrl\n010804ec=drawable/ic_media_route_connecting_dark_11_mtrl\n010804ed=drawable/ic_media_route_connecting_dark_12_mtrl\n010804ee=drawable/ic_media_route_connecting_dark_13_mtrl\n010804ef=drawable/ic_media_route_connecting_dark_14_mtrl\n010804f0=drawable/ic_media_route_connecting_dark_15_mtrl\n010804f1=drawable/ic_media_route_connecting_dark_16_mtrl\n010804f2=drawable/ic_media_route_connecting_dark_17_mtrl\n010804f3=drawable/ic_media_route_connecting_dark_18_mtrl\n010804f4=drawable/ic_media_route_connecting_dark_19_mtrl\n010804f5=drawable/ic_media_route_connecting_dark_20_mtrl\n010804f6=drawable/ic_media_route_connecting_dark_21_mtrl\n010804f7=drawable/ic_media_route_connecting_dark_22_mtrl\n010804f8=drawable/ic_media_route_connecting_dark_23_mtrl\n010804f9=drawable/ic_media_route_connecting_dark_24_mtrl\n010804fa=drawable/ic_media_route_connecting_dark_25_mtrl\n010804fb=drawable/ic_media_route_connecting_dark_26_mtrl\n010804fc=drawable/ic_media_route_connecting_dark_27_mtrl\n010804fd=drawable/ic_media_route_connecting_dark_28_mtrl\n010804fe=drawable/ic_media_route_connecting_dark_29_mtrl\n010804ff=drawable/ic_media_route_connecting_dark_30_mtrl\n01080500=drawable/ic_media_route_connecting_dark_material\n01080501=drawable/ic_media_route_connecting_holo_dark\n01080502=drawable/ic_media_route_connecting_holo_light\n01080503=drawable/ic_media_route_connecting_light_00_mtrl\n01080504=drawable/ic_media_route_connecting_light_01_mtrl\n01080505=drawable/ic_media_route_connecting_light_02_mtrl\n01080506=drawable/ic_media_route_connecting_light_03_mtrl\n01080507=drawable/ic_media_route_connecting_light_04_mtrl\n01080508=drawable/ic_media_route_connecting_light_05_mtrl\n01080509=drawable/ic_media_route_connecting_light_06_mtrl\n0108050a=drawable/ic_media_route_connecting_light_07_mtrl\n0108050b=drawable/ic_media_route_connecting_light_08_mtrl\n0108050c=drawable/ic_media_route_connecting_light_09_mtrl\n0108050d=drawable/ic_media_route_connecting_light_10_mtrl\n0108050e=drawable/ic_media_route_connecting_light_11_mtrl\n0108050f=drawable/ic_media_route_connecting_light_12_mtrl\n01080510=drawable/ic_media_route_connecting_light_13_mtrl\n01080511=drawable/ic_media_route_connecting_light_14_mtrl\n01080512=drawable/ic_media_route_connecting_light_15_mtrl\n01080513=drawable/ic_media_route_connecting_light_16_mtrl\n01080514=drawable/ic_media_route_connecting_light_17_mtrl\n01080515=drawable/ic_media_route_connecting_light_18_mtrl\n01080516=drawable/ic_media_route_connecting_light_19_mtrl\n01080517=drawable/ic_media_route_connecting_light_20_mtrl\n01080518=drawable/ic_media_route_connecting_light_21_mtrl\n01080519=drawable/ic_media_route_connecting_light_22_mtrl\n0108051a=drawable/ic_media_route_connecting_light_23_mtrl\n0108051b=drawable/ic_media_route_connecting_light_24_mtrl\n0108051c=drawable/ic_media_route_connecting_light_25_mtrl\n0108051d=drawable/ic_media_route_connecting_light_26_mtrl\n0108051e=drawable/ic_media_route_connecting_light_27_mtrl\n0108051f=drawable/ic_media_route_connecting_light_28_mtrl\n01080520=drawable/ic_media_route_connecting_light_29_mtrl\n01080521=drawable/ic_media_route_connecting_light_30_mtrl\n01080522=drawable/ic_media_route_connecting_light_material\n01080523=drawable/ic_media_route_dark_material\n01080524=drawable/ic_media_route_disabled_holo_dark\n01080525=drawable/ic_media_route_disabled_holo_light\n01080526=drawable/ic_media_route_disabled_mtrl_alpha\n01080527=drawable/ic_media_route_holo_dark\n01080528=drawable/ic_media_route_holo_light\n01080529=drawable/ic_media_route_light_material\n0108052a=drawable/ic_media_route_off_dark_mtrl\n0108052b=drawable/ic_media_route_off_holo_dark\n0108052c=drawable/ic_media_route_off_holo_light\n0108052d=drawable/ic_media_route_off_light_mtrl\n0108052e=drawable/ic_media_route_on_0_holo_dark\n0108052f=drawable/ic_media_route_on_0_holo_light\n01080530=drawable/ic_media_route_on_1_holo_dark\n01080531=drawable/ic_media_route_on_1_holo_light\n01080532=drawable/ic_media_route_on_2_holo_dark\n01080533=drawable/ic_media_route_on_2_holo_light\n01080534=drawable/ic_media_route_on_holo_dark\n01080535=drawable/ic_media_route_on_holo_light\n01080536=drawable/ic_media_seamless\n01080537=drawable/ic_media_stop\n01080538=drawable/ic_media_video_poster\n01080539=drawable/ic_menu\n0108053a=drawable/ic_menu_account_list\n0108053b=drawable/ic_menu_allfriends\n0108053c=drawable/ic_menu_archive\n0108053d=drawable/ic_menu_attachment\n0108053e=drawable/ic_menu_back\n0108053f=drawable/ic_menu_block\n01080540=drawable/ic_menu_blocked_user\n01080541=drawable/ic_menu_btn_add\n01080542=drawable/ic_menu_cc\n01080543=drawable/ic_menu_cc_am\n01080544=drawable/ic_menu_chat_dashboard\n01080545=drawable/ic_menu_clear_playlist\n01080546=drawable/ic_menu_compose\n01080547=drawable/ic_menu_copy\n01080548=drawable/ic_menu_copy_holo_dark\n01080549=drawable/ic_menu_copy_holo_light\n0108054a=drawable/ic_menu_copy_material\n0108054b=drawable/ic_menu_cut\n0108054c=drawable/ic_menu_cut_holo_dark\n0108054d=drawable/ic_menu_cut_holo_light\n0108054e=drawable/ic_menu_cut_material\n0108054f=drawable/ic_menu_emoticons\n01080550=drawable/ic_menu_end_conversation\n01080551=drawable/ic_menu_find\n01080552=drawable/ic_menu_find_holo_dark\n01080553=drawable/ic_menu_find_holo_light\n01080554=drawable/ic_menu_find_material\n01080555=drawable/ic_menu_find_mtrl_alpha\n01080556=drawable/ic_menu_forward\n01080557=drawable/ic_menu_friendslist\n01080558=drawable/ic_menu_goto\n01080559=drawable/ic_menu_help_holo_light\n0108055a=drawable/ic_menu_home\n0108055b=drawable/ic_menu_invite\n0108055c=drawable/ic_menu_login\n0108055d=drawable/ic_menu_mark\n0108055e=drawable/ic_menu_moreoverflow\n0108055f=drawable/ic_menu_moreoverflow_focused_holo_dark\n01080560=drawable/ic_menu_moreoverflow_focused_holo_light\n01080561=drawable/ic_menu_moreoverflow_holo_dark\n01080562=drawable/ic_menu_moreoverflow_holo_light\n01080563=drawable/ic_menu_moreoverflow_material\n01080564=drawable/ic_menu_moreoverflow_material_dark\n01080565=drawable/ic_menu_moreoverflow_material_light\n01080566=drawable/ic_menu_moreoverflow_normal_holo_dark\n01080567=drawable/ic_menu_moreoverflow_normal_holo_light\n01080568=drawable/ic_menu_notifications\n01080569=drawable/ic_menu_paste\n0108056a=drawable/ic_menu_paste_holo_dark\n0108056b=drawable/ic_menu_paste_holo_light\n0108056c=drawable/ic_menu_paste_material\n0108056d=drawable/ic_menu_play_clip\n0108056e=drawable/ic_menu_redo_material\n0108056f=drawable/ic_menu_refresh\n01080570=drawable/ic_menu_search_holo_dark\n01080571=drawable/ic_menu_search_holo_light\n01080572=drawable/ic_menu_search_material\n01080573=drawable/ic_menu_search_mtrl_alpha\n01080574=drawable/ic_menu_selectall_holo_dark\n01080575=drawable/ic_menu_selectall_holo_light\n01080576=drawable/ic_menu_selectall_material\n01080577=drawable/ic_menu_settings_holo_light\n01080578=drawable/ic_menu_share_holo_dark\n01080579=drawable/ic_menu_share_holo_light\n0108057a=drawable/ic_menu_share_material\n0108057b=drawable/ic_menu_star\n0108057c=drawable/ic_menu_start_conversation\n0108057d=drawable/ic_menu_stop\n0108057e=drawable/ic_menu_undo_material\n0108057f=drawable/ic_mic\n01080580=drawable/ic_mic_allowed\n01080581=drawable/ic_mic_blocked\n01080582=drawable/ic_minus\n01080583=drawable/ic_mode_edit\n01080584=drawable/ic_more_items\n01080585=drawable/ic_notification_2025_collapse\n01080586=drawable/ic_notification_2025_expand\n01080587=drawable/ic_notification_alert\n01080588=drawable/ic_notification_block\n01080589=drawable/ic_notification_cast_0\n0108058a=drawable/ic_notification_cast_1\n0108058b=drawable/ic_notification_cast_2\n0108058c=drawable/ic_notification_media_route\n0108058d=drawable/ic_notification_summarization\n0108058e=drawable/ic_notification_summary_auto\n0108058f=drawable/ic_notifications_alerted\n01080590=drawable/ic_number11\n01080591=drawable/ic_pan_tool\n01080592=drawable/ic_perm_device_info\n01080593=drawable/ic_perm_group_app_info\n01080594=drawable/ic_perm_group_audio_settings\n01080595=drawable/ic_perm_group_bluetooth\n01080596=drawable/ic_perm_group_bookmarks\n01080597=drawable/ic_perm_group_calendar\n01080598=drawable/ic_perm_group_camera\n01080599=drawable/ic_perm_group_device_alarms\n0108059a=drawable/ic_perm_group_display\n0108059b=drawable/ic_perm_group_effects_battery\n0108059c=drawable/ic_perm_group_location\n0108059d=drawable/ic_perm_group_messages\n0108059e=drawable/ic_perm_group_microphone\n0108059f=drawable/ic_perm_group_network\n010805a0=drawable/ic_perm_group_personal_info\n010805a1=drawable/ic_perm_group_phone_calls\n010805a2=drawable/ic_perm_group_screenlock\n010805a3=drawable/ic_perm_group_shortrange_network\n010805a4=drawable/ic_perm_group_social_info\n010805a5=drawable/ic_perm_group_status_bar\n010805a6=drawable/ic_perm_group_sync_settings\n010805a7=drawable/ic_perm_group_system_clock\n010805a8=drawable/ic_perm_group_system_tools\n010805a9=drawable/ic_perm_group_voicemail\n010805aa=drawable/ic_perm_group_wallpapewr\n010805ab=drawable/ic_permission\n010805ac=drawable/ic_phone\n010805ad=drawable/ic_phone_disabled\n010805ae=drawable/ic_plus\n010805af=drawable/ic_popup_sync_1\n010805b0=drawable/ic_popup_sync_2\n010805b1=drawable/ic_popup_sync_3\n010805b2=drawable/ic_popup_sync_4\n010805b3=drawable/ic_popup_sync_5\n010805b4=drawable/ic_popup_sync_6\n010805b5=drawable/ic_print\n010805b6=drawable/ic_print_error\n010805b7=drawable/ic_private_profile_badge\n010805b8=drawable/ic_private_profile_icon_badge\n010805b9=drawable/ic_qs_airplane\n010805ba=drawable/ic_qs_auto_rotate\n010805bb=drawable/ic_qs_battery_saver\n010805bc=drawable/ic_qs_bluetooth\n010805bd=drawable/ic_qs_dnd\n010805be=drawable/ic_qs_flashlight\n010805bf=drawable/ic_qs_night_display_on\n010805c0=drawable/ic_qs_one_handed_mode\n010805c1=drawable/ic_qs_ui_mode_night\n010805c2=drawable/ic_refresh\n010805c3=drawable/ic_restart\n010805c4=drawable/ic_satellite_alt_24px\n010805c5=drawable/ic_schedule\n010805c6=drawable/ic_screenshot\n010805c7=drawable/ic_screenshot_edit\n010805c8=drawable/ic_sd_card_48dp\n010805c9=drawable/ic_search\n010805ca=drawable/ic_search_api_holo_dark\n010805cb=drawable/ic_search_api_holo_light\n010805cc=drawable/ic_search_api_material\n010805cd=drawable/ic_settings\n010805ce=drawable/ic_settings_24dp\n010805cf=drawable/ic_settings_bluetooth\n010805d0=drawable/ic_settings_language\n010805d1=drawable/ic_settings_print\n010805d2=drawable/ic_signal_cellular\n010805d3=drawable/ic_signal_cellular_0_4_bar\n010805d4=drawable/ic_signal_cellular_0_5_bar\n010805d5=drawable/ic_signal_cellular_1_4_bar\n010805d6=drawable/ic_signal_cellular_1_5_bar\n010805d7=drawable/ic_signal_cellular_2_4_bar\n010805d8=drawable/ic_signal_cellular_2_5_bar\n010805d9=drawable/ic_signal_cellular_3_4_bar\n010805da=drawable/ic_signal_cellular_3_5_bar\n010805db=drawable/ic_signal_cellular_4_4_bar\n010805dc=drawable/ic_signal_cellular_4_5_bar\n010805dd=drawable/ic_signal_cellular_5_5_bar\n010805de=drawable/ic_signal_cellular_alt_24px\n010805df=drawable/ic_signal_location\n010805e0=drawable/ic_signal_wifi_transient_animation\n010805e1=drawable/ic_signal_wifi_transient_animation_drawable\n010805e2=drawable/ic_sim_card_multi_24px_clr\n010805e3=drawable/ic_sim_card_multi_48px_clr\n010805e4=drawable/ic_slice_send\n010805e5=drawable/ic_spinner_caret\n010805e6=drawable/ic_standby\n010805e7=drawable/ic_star_black_16dp\n010805e8=drawable/ic_star_black_36dp\n010805e9=drawable/ic_star_black_48dp\n010805ea=drawable/ic_star_half_black_16dp\n010805eb=drawable/ic_star_half_black_36dp\n010805ec=drawable/ic_star_half_black_48dp\n010805ed=drawable/ic_storage_48dp\n010805ee=drawable/ic_suggestions_add\n010805ef=drawable/ic_suggestions_delete\n010805f0=drawable/ic_swap_horiz\n010805f1=drawable/ic_swipe_down\n010805f2=drawable/ic_sysbar_quicksettings\n010805f3=drawable/ic_test_badge_experiment\n010805f4=drawable/ic_test_badge_no_background\n010805f5=drawable/ic_test_icon_badge_experiment\n010805f6=drawable/ic_text_dot\n010805f7=drawable/ic_thermostat\n010805f8=drawable/ic_thermostat_notification\n010805f9=drawable/ic_thread_network\n010805fa=drawable/ic_usb_48dp\n010805fb=drawable/ic_user_secure\n010805fc=drawable/ic_vibrate\n010805fd=drawable/ic_vibrate_small\n010805fe=drawable/ic_visibility\n010805ff=drawable/ic_voice_search\n01080600=drawable/ic_voice_search_api_holo_dark\n01080601=drawable/ic_voice_search_api_holo_light\n01080602=drawable/ic_voice_search_api_material\n01080603=drawable/ic_volume\n01080604=drawable/ic_volume_bluetooth_ad2p\n01080605=drawable/ic_volume_bluetooth_in_call\n01080606=drawable/ic_volume_off\n01080607=drawable/ic_volume_off_small\n01080608=drawable/ic_volume_small\n01080609=drawable/ic_wifi_signal_0\n0108060a=drawable/ic_wifi_signal_1\n0108060b=drawable/ic_wifi_signal_2\n0108060c=drawable/ic_wifi_signal_3\n0108060d=drawable/ic_wifi_signal_4\n0108060e=drawable/ic_zen_24dp\n0108060f=drawable/ic_zen_mode_icon_animal_paw\n01080610=drawable/ic_zen_mode_icon_apartment_building\n01080611=drawable/ic_zen_mode_icon_ball_sports\n01080612=drawable/ic_zen_mode_icon_beach\n01080613=drawable/ic_zen_mode_icon_book\n01080614=drawable/ic_zen_mode_icon_camping\n01080615=drawable/ic_zen_mode_icon_child\n01080616=drawable/ic_zen_mode_icon_classical_building\n01080617=drawable/ic_zen_mode_icon_croissant\n01080618=drawable/ic_zen_mode_icon_fork_and_knife\n01080619=drawable/ic_zen_mode_icon_gaming\n0108061a=drawable/ic_zen_mode_icon_golf\n0108061b=drawable/ic_zen_mode_icon_group_of_people\n0108061c=drawable/ic_zen_mode_icon_gym\n0108061d=drawable/ic_zen_mode_icon_headphones\n0108061e=drawable/ic_zen_mode_icon_heart\n0108061f=drawable/ic_zen_mode_icon_hiking\n01080620=drawable/ic_zen_mode_icon_house\n01080621=drawable/ic_zen_mode_icon_lightbulb\n01080622=drawable/ic_zen_mode_icon_lotus_flower\n01080623=drawable/ic_zen_mode_icon_martial_arts\n01080624=drawable/ic_zen_mode_icon_palette\n01080625=drawable/ic_zen_mode_icon_piano\n01080626=drawable/ic_zen_mode_icon_running\n01080627=drawable/ic_zen_mode_icon_shopping_cart\n01080628=drawable/ic_zen_mode_icon_snowflake\n01080629=drawable/ic_zen_mode_icon_speech_bubble\n0108062a=drawable/ic_zen_mode_icon_star_badge\n0108062b=drawable/ic_zen_mode_icon_swimming\n0108062c=drawable/ic_zen_mode_icon_train\n0108062d=drawable/ic_zen_mode_icon_tv\n0108062e=drawable/ic_zen_mode_icon_work\n0108062f=drawable/ic_zen_mode_icon_workshop\n01080630=drawable/ic_zen_mode_type_bedtime\n01080631=drawable/ic_zen_mode_type_driving\n01080632=drawable/ic_zen_mode_type_immersive\n01080633=drawable/ic_zen_mode_type_managed\n01080634=drawable/ic_zen_mode_type_other\n01080635=drawable/ic_zen_mode_type_schedule_calendar\n01080636=drawable/ic_zen_mode_type_schedule_time\n01080637=drawable/ic_zen_mode_type_special_dnd\n01080638=drawable/ic_zen_mode_type_theater\n01080639=drawable/ic_zen_priority_modes\n0108063a=drawable/icon_highlight_rectangle\n0108063b=drawable/icon_highlight_square\n0108063c=drawable/iconfactory_adaptive_icon_drawable_wrapper\n0108063d=drawable/ime_qwerty\n0108063e=drawable/immersive_cling_bg\n0108063f=drawable/immersive_cling_btn_bg\n01080640=drawable/indicator_check_mark_dark\n01080641=drawable/indicator_check_mark_light\n01080642=drawable/indicator_input_error\n01080643=drawable/input_extract_action_bg_material_dark\n01080644=drawable/input_extract_action_bg_normal_material_dark\n01080645=drawable/input_extract_action_bg_pressed_material_dark\n01080646=drawable/input_method_fullscreen_background\n01080647=drawable/input_method_fullscreen_background_holo\n01080648=drawable/input_method_item_background\n01080649=drawable/input_method_item_background_selected\n0108064a=drawable/input_method_item_background_selector\n0108064b=drawable/input_method_switch_button\n0108064c=drawable/input_method_switch_item_background\n0108064d=drawable/inset_resolver_profile_tab_bg\n0108064e=drawable/item_background\n0108064f=drawable/item_background_activated_holo_dark\n01080650=drawable/item_background_borderless_material\n01080651=drawable/item_background_borderless_material_dark\n01080652=drawable/item_background_borderless_material_light\n01080653=drawable/item_background_holo_dark\n01080654=drawable/item_background_holo_light\n01080655=drawable/item_background_material\n01080656=drawable/item_background_material_dark\n01080657=drawable/item_background_material_light\n01080658=drawable/jog_dial_arrow_long_left_green\n01080659=drawable/jog_dial_arrow_long_left_yellow\n0108065a=drawable/jog_dial_arrow_long_middle_yellow\n0108065b=drawable/jog_dial_arrow_long_right_red\n0108065c=drawable/jog_dial_arrow_long_right_yellow\n0108065d=drawable/jog_dial_arrow_short_left\n0108065e=drawable/jog_dial_arrow_short_left_and_right\n0108065f=drawable/jog_dial_arrow_short_right\n01080660=drawable/jog_dial_bg\n01080661=drawable/jog_dial_dimple\n01080662=drawable/jog_dial_dimple_dim\n01080663=drawable/jog_tab_bar_left_answer\n01080664=drawable/jog_tab_bar_left_end_confirm_gray\n01080665=drawable/jog_tab_bar_left_end_confirm_green\n01080666=drawable/jog_tab_bar_left_end_confirm_red\n01080667=drawable/jog_tab_bar_left_end_confirm_yellow\n01080668=drawable/jog_tab_bar_left_end_normal\n01080669=drawable/jog_tab_bar_left_end_pressed\n0108066a=drawable/jog_tab_bar_left_generic\n0108066b=drawable/jog_tab_bar_left_unlock\n0108066c=drawable/jog_tab_bar_right_decline\n0108066d=drawable/jog_tab_bar_right_end_confirm_gray\n0108066e=drawable/jog_tab_bar_right_end_confirm_green\n0108066f=drawable/jog_tab_bar_right_end_confirm_red\n01080670=drawable/jog_tab_bar_right_end_confirm_yellow\n01080671=drawable/jog_tab_bar_right_end_normal\n01080672=drawable/jog_tab_bar_right_end_pressed\n01080673=drawable/jog_tab_bar_right_generic\n01080674=drawable/jog_tab_bar_right_sound_off\n01080675=drawable/jog_tab_bar_right_sound_on\n01080676=drawable/jog_tab_left_answer\n01080677=drawable/jog_tab_left_confirm_gray\n01080678=drawable/jog_tab_left_confirm_green\n01080679=drawable/jog_tab_left_confirm_red\n0108067a=drawable/jog_tab_left_confirm_yellow\n0108067b=drawable/jog_tab_left_generic\n0108067c=drawable/jog_tab_left_normal\n0108067d=drawable/jog_tab_left_pressed\n0108067e=drawable/jog_tab_left_unlock\n0108067f=drawable/jog_tab_right_confirm_gray\n01080680=drawable/jog_tab_right_confirm_green\n01080681=drawable/jog_tab_right_confirm_red\n01080682=drawable/jog_tab_right_confirm_yellow\n01080683=drawable/jog_tab_right_decline\n01080684=drawable/jog_tab_right_generic\n01080685=drawable/jog_tab_right_normal\n01080686=drawable/jog_tab_right_pressed\n01080687=drawable/jog_tab_right_sound_off\n01080688=drawable/jog_tab_right_sound_on\n01080689=drawable/jog_tab_target_gray\n0108068a=drawable/jog_tab_target_green\n0108068b=drawable/jog_tab_target_red\n0108068c=drawable/jog_tab_target_yellow\n0108068d=drawable/keyboard_accessory_bg_landscape\n0108068e=drawable/keyboard_background\n0108068f=drawable/keyboard_key_feedback\n01080690=drawable/keyboard_key_feedback_background\n01080691=drawable/keyboard_key_feedback_more_background\n01080692=drawable/keyboard_popup_panel_background\n01080693=drawable/keyboard_popup_panel_trans_background\n01080694=drawable/language_picker_item_bg_selected\n01080695=drawable/language_picker_item_text_color2_selector\n01080696=drawable/language_picker_item_text_color_selector\n01080697=drawable/light_header\n01080698=drawable/light_header_dither\n01080699=drawable/list_activated_holo\n0108069a=drawable/list_choice_background_material\n0108069b=drawable/list_divider_holo_dark\n0108069c=drawable/list_divider_holo_light\n0108069d=drawable/list_divider_horizontal_holo_dark\n0108069e=drawable/list_divider_material\n0108069f=drawable/list_focused_holo\n010806a0=drawable/list_highlight\n010806a1=drawable/list_highlight_active\n010806a2=drawable/list_highlight_inactive\n010806a3=drawable/list_longpressed_holo\n010806a4=drawable/list_longpressed_holo_dark\n010806a5=drawable/list_longpressed_holo_light\n010806a6=drawable/list_pressed_holo_dark\n010806a7=drawable/list_pressed_holo_light\n010806a8=drawable/list_section_divider_holo_dark\n010806a9=drawable/list_section_divider_holo_light\n010806aa=drawable/list_section_divider_material\n010806ab=drawable/list_section_divider_mtrl_alpha\n010806ac=drawable/list_section_header_holo_dark\n010806ad=drawable/list_section_header_holo_light\n010806ae=drawable/list_selected_background\n010806af=drawable/list_selected_background_light\n010806b0=drawable/list_selected_holo_dark\n010806b1=drawable/list_selected_holo_light\n010806b2=drawable/list_selector_activated_holo_dark\n010806b3=drawable/list_selector_activated_holo_light\n010806b4=drawable/list_selector_background_default\n010806b5=drawable/list_selector_background_default_light\n010806b6=drawable/list_selector_background_disabled\n010806b7=drawable/list_selector_background_disabled_light\n010806b8=drawable/list_selector_background_focus\n010806b9=drawable/list_selector_background_focused\n010806ba=drawable/list_selector_background_focused_light\n010806bb=drawable/list_selector_background_focused_selected\n010806bc=drawable/list_selector_background_light\n010806bd=drawable/list_selector_background_longpress\n010806be=drawable/list_selector_background_longpress_light\n010806bf=drawable/list_selector_background_pressed\n010806c0=drawable/list_selector_background_pressed_light\n010806c1=drawable/list_selector_background_selected\n010806c2=drawable/list_selector_background_selected_light\n010806c3=drawable/list_selector_background_transition\n010806c4=drawable/list_selector_background_transition_holo_dark\n010806c5=drawable/list_selector_background_transition_holo_light\n010806c6=drawable/list_selector_background_transition_light\n010806c7=drawable/list_selector_disabled_holo_dark\n010806c8=drawable/list_selector_disabled_holo_light\n010806c9=drawable/list_selector_focused_holo_dark\n010806ca=drawable/list_selector_focused_holo_light\n010806cb=drawable/list_selector_holo_dark\n010806cc=drawable/list_selector_holo_light\n010806cd=drawable/list_selector_multiselect_holo_dark\n010806ce=drawable/list_selector_multiselect_holo_light\n010806cf=drawable/list_selector_pressed_holo_dark\n010806d0=drawable/list_selector_pressed_holo_light\n010806d1=drawable/load_average_background\n010806d2=drawable/loader_horizontal_watch\n010806d3=drawable/loading_spinner\n010806d4=drawable/loading_tile\n010806d5=drawable/loading_tile_android\n010806d6=drawable/lockscreen_notselected\n010806d7=drawable/lockscreen_protection_pattern\n010806d8=drawable/lockscreen_selected\n010806d9=drawable/magnified_region_frame\n010806da=drawable/maps_google_logo\n010806db=drawable/media_button_background\n010806dc=drawable/media_seamless_background\n010806dd=drawable/menu_background\n010806de=drawable/menu_background_fill_parent_width\n010806df=drawable/menu_dropdown_panel_holo_dark\n010806e0=drawable/menu_dropdown_panel_holo_light\n010806e1=drawable/menu_hardkey_panel_holo_dark\n010806e2=drawable/menu_hardkey_panel_holo_light\n010806e3=drawable/menu_panel_holo_dark\n010806e4=drawable/menu_panel_holo_light\n010806e5=drawable/menu_popup_panel_holo_dark\n010806e6=drawable/menu_popup_panel_holo_light\n010806e7=drawable/menu_selector\n010806e8=drawable/menu_separator\n010806e9=drawable/menu_submenu_background\n010806ea=drawable/menuitem_background_focus\n010806eb=drawable/menuitem_background_pressed\n010806ec=drawable/menuitem_background_solid\n010806ed=drawable/menuitem_background_solid_focused\n010806ee=drawable/menuitem_background_solid_pressed\n010806ef=drawable/menuitem_checkbox\n010806f0=drawable/menuitem_checkbox_on\n010806f1=drawable/messaging_user\n010806f2=drawable/minitab_lt\n010806f3=drawable/minitab_lt_focus\n010806f4=drawable/minitab_lt_press\n010806f5=drawable/minitab_lt_selected\n010806f6=drawable/minitab_lt_unselected\n010806f7=drawable/minitab_lt_unselected_press\n010806f8=drawable/no_tile_128\n010806f9=drawable/no_tile_256\n010806fa=drawable/notification_2025_expand_button_pill_bg\n010806fb=drawable/notification_big_picture_outline\n010806fc=drawable/notification_close_button_icon\n010806fd=drawable/notification_icon_circle\n010806fe=drawable/notification_large_icon_outline\n010806ff=drawable/notification_material_action_background\n01080700=drawable/notification_material_media_action_background\n01080701=drawable/notification_progress\n01080702=drawable/notification_progress_icon_background\n01080703=drawable/notification_progress_indeterminate_horizontal_material\n01080704=drawable/notification_template_divider\n01080705=drawable/notification_template_divider_media\n01080706=drawable/notification_template_icon_bg\n01080707=drawable/notification_template_icon_low_bg\n01080708=drawable/number_picker_divider_material\n01080709=drawable/numberpicker_down_btn\n0108070a=drawable/numberpicker_down_disabled\n0108070b=drawable/numberpicker_down_disabled_focused\n0108070c=drawable/numberpicker_down_disabled_focused_holo_dark\n0108070d=drawable/numberpicker_down_disabled_focused_holo_light\n0108070e=drawable/numberpicker_down_disabled_holo_dark\n0108070f=drawable/numberpicker_down_disabled_holo_light\n01080710=drawable/numberpicker_down_focused_holo_dark\n01080711=drawable/numberpicker_down_focused_holo_light\n01080712=drawable/numberpicker_down_longpressed_holo_dark\n01080713=drawable/numberpicker_down_longpressed_holo_light\n01080714=drawable/numberpicker_down_normal\n01080715=drawable/numberpicker_down_normal_holo_dark\n01080716=drawable/numberpicker_down_normal_holo_light\n01080717=drawable/numberpicker_down_pressed\n01080718=drawable/numberpicker_down_pressed_holo_dark\n01080719=drawable/numberpicker_down_pressed_holo_light\n0108071a=drawable/numberpicker_down_selected\n0108071b=drawable/numberpicker_input\n0108071c=drawable/numberpicker_input_disabled\n0108071d=drawable/numberpicker_input_normal\n0108071e=drawable/numberpicker_input_pressed\n0108071f=drawable/numberpicker_input_selected\n01080720=drawable/numberpicker_selection_divider\n01080721=drawable/numberpicker_up_btn\n01080722=drawable/numberpicker_up_disabled\n01080723=drawable/numberpicker_up_disabled_focused\n01080724=drawable/numberpicker_up_disabled_focused_holo_dark\n01080725=drawable/numberpicker_up_disabled_focused_holo_light\n01080726=drawable/numberpicker_up_disabled_holo_dark\n01080727=drawable/numberpicker_up_disabled_holo_light\n01080728=drawable/numberpicker_up_focused_holo_dark\n01080729=drawable/numberpicker_up_focused_holo_light\n0108072a=drawable/numberpicker_up_longpressed_holo_dark\n0108072b=drawable/numberpicker_up_longpressed_holo_light\n0108072c=drawable/numberpicker_up_normal\n0108072d=drawable/numberpicker_up_normal_holo_dark\n0108072e=drawable/numberpicker_up_normal_holo_light\n0108072f=drawable/numberpicker_up_pressed\n01080730=drawable/numberpicker_up_pressed_holo_dark\n01080731=drawable/numberpicker_up_pressed_holo_light\n01080732=drawable/numberpicker_up_selected\n01080733=drawable/panel_background\n01080734=drawable/panel_bg_holo_dark\n01080735=drawable/panel_bg_holo_light\n01080736=drawable/panel_picture_frame_background\n01080737=drawable/panel_picture_frame_bg_focus_blue\n01080738=drawable/panel_picture_frame_bg_normal\n01080739=drawable/panel_picture_frame_bg_pressed_blue\n0108073a=drawable/password_field_default\n0108073b=drawable/password_keyboard_background_holo\n0108073c=drawable/perm_group_accessibility_features\n0108073d=drawable/perm_group_activity_recognition\n0108073e=drawable/perm_group_affects_battery\n0108073f=drawable/perm_group_app_info\n01080740=drawable/perm_group_audio_settings\n01080741=drawable/perm_group_aural\n01080742=drawable/perm_group_bluetooth\n01080743=drawable/perm_group_bookmarks\n01080744=drawable/perm_group_calendar\n01080745=drawable/perm_group_call_log\n01080746=drawable/perm_group_camera\n01080747=drawable/perm_group_contacts\n01080748=drawable/perm_group_device_alarms\n01080749=drawable/perm_group_display\n0108074a=drawable/perm_group_location\n0108074b=drawable/perm_group_microphone\n0108074c=drawable/perm_group_nearby_devices\n0108074d=drawable/perm_group_network\n0108074e=drawable/perm_group_personal_info\n0108074f=drawable/perm_group_phone_calls\n01080750=drawable/perm_group_read_media_aural\n01080751=drawable/perm_group_read_media_visual\n01080752=drawable/perm_group_screenlock\n01080753=drawable/perm_group_sensors\n01080754=drawable/perm_group_shortrange_network\n01080755=drawable/perm_group_sms\n01080756=drawable/perm_group_status_bar\n01080757=drawable/perm_group_storage\n01080758=drawable/perm_group_sync_settings\n01080759=drawable/perm_group_system_clock\n0108075a=drawable/perm_group_system_tools\n0108075b=drawable/perm_group_visual\n0108075c=drawable/perm_group_voicemail\n0108075d=drawable/perm_group_wallpaper\n0108075e=drawable/picture_emergency\n0108075f=drawable/platlogo\n01080760=drawable/platlogo_m\n01080761=drawable/pointer_alias\n01080762=drawable/pointer_alias_icon\n01080763=drawable/pointer_alias_large\n01080764=drawable/pointer_alias_large_icon\n01080765=drawable/pointer_alias_vector\n01080766=drawable/pointer_alias_vector_icon\n01080767=drawable/pointer_all_scroll\n01080768=drawable/pointer_all_scroll_icon\n01080769=drawable/pointer_all_scroll_large\n0108076a=drawable/pointer_all_scroll_large_icon\n0108076b=drawable/pointer_all_scroll_vector\n0108076c=drawable/pointer_all_scroll_vector_icon\n0108076d=drawable/pointer_arrow\n0108076e=drawable/pointer_arrow_icon\n0108076f=drawable/pointer_arrow_large\n01080770=drawable/pointer_arrow_large_icon\n01080771=drawable/pointer_arrow_vector\n01080772=drawable/pointer_arrow_vector_icon\n01080773=drawable/pointer_cell\n01080774=drawable/pointer_cell_icon\n01080775=drawable/pointer_cell_large\n01080776=drawable/pointer_cell_large_icon\n01080777=drawable/pointer_cell_vector\n01080778=drawable/pointer_cell_vector_icon\n01080779=drawable/pointer_context_menu\n0108077a=drawable/pointer_context_menu_icon\n0108077b=drawable/pointer_context_menu_large\n0108077c=drawable/pointer_context_menu_large_icon\n0108077d=drawable/pointer_context_menu_vector\n0108077e=drawable/pointer_context_menu_vector_icon\n0108077f=drawable/pointer_copy\n01080780=drawable/pointer_copy_icon\n01080781=drawable/pointer_copy_large\n01080782=drawable/pointer_copy_large_icon\n01080783=drawable/pointer_copy_vector\n01080784=drawable/pointer_copy_vector_icon\n01080785=drawable/pointer_crosshair\n01080786=drawable/pointer_crosshair_icon\n01080787=drawable/pointer_crosshair_large\n01080788=drawable/pointer_crosshair_large_icon\n01080789=drawable/pointer_crosshair_vector\n0108078a=drawable/pointer_crosshair_vector_icon\n0108078b=drawable/pointer_grab\n0108078c=drawable/pointer_grab_icon\n0108078d=drawable/pointer_grab_large\n0108078e=drawable/pointer_grab_large_icon\n0108078f=drawable/pointer_grab_vector\n01080790=drawable/pointer_grab_vector_icon\n01080791=drawable/pointer_grabbing\n01080792=drawable/pointer_grabbing_icon\n01080793=drawable/pointer_grabbing_large\n01080794=drawable/pointer_grabbing_large_icon\n01080795=drawable/pointer_grabbing_vector\n01080796=drawable/pointer_grabbing_vector_icon\n01080797=drawable/pointer_hand\n01080798=drawable/pointer_hand_icon\n01080799=drawable/pointer_hand_large\n0108079a=drawable/pointer_hand_large_icon\n0108079b=drawable/pointer_hand_vector\n0108079c=drawable/pointer_hand_vector_icon\n0108079d=drawable/pointer_handwriting\n0108079e=drawable/pointer_handwriting_icon\n0108079f=drawable/pointer_handwriting_vector\n010807a0=drawable/pointer_handwriting_vector_icon\n010807a1=drawable/pointer_help\n010807a2=drawable/pointer_help_icon\n010807a3=drawable/pointer_help_large\n010807a4=drawable/pointer_help_large_icon\n010807a5=drawable/pointer_help_vector\n010807a6=drawable/pointer_help_vector_icon\n010807a7=drawable/pointer_horizontal_double_arrow\n010807a8=drawable/pointer_horizontal_double_arrow_icon\n010807a9=drawable/pointer_horizontal_double_arrow_large\n010807aa=drawable/pointer_horizontal_double_arrow_large_icon\n010807ab=drawable/pointer_horizontal_double_arrow_vector\n010807ac=drawable/pointer_horizontal_double_arrow_vector_icon\n010807ad=drawable/pointer_nodrop\n010807ae=drawable/pointer_nodrop_icon\n010807af=drawable/pointer_nodrop_large\n010807b0=drawable/pointer_nodrop_large_icon\n010807b1=drawable/pointer_nodrop_vector\n010807b2=drawable/pointer_nodrop_vector_icon\n010807b3=drawable/pointer_spot_anchor\n010807b4=drawable/pointer_spot_anchor_icon\n010807b5=drawable/pointer_spot_anchor_vector\n010807b6=drawable/pointer_spot_anchor_vector_icon\n010807b7=drawable/pointer_spot_hover\n010807b8=drawable/pointer_spot_hover_icon\n010807b9=drawable/pointer_spot_hover_vector\n010807ba=drawable/pointer_spot_hover_vector_icon\n010807bb=drawable/pointer_spot_touch\n010807bc=drawable/pointer_spot_touch_icon\n010807bd=drawable/pointer_spot_touch_vector\n010807be=drawable/pointer_spot_touch_vector_icon\n010807bf=drawable/pointer_text\n010807c0=drawable/pointer_text_icon\n010807c1=drawable/pointer_text_large\n010807c2=drawable/pointer_text_large_icon\n010807c3=drawable/pointer_text_vector\n010807c4=drawable/pointer_text_vector_icon\n010807c5=drawable/pointer_top_left_diagonal_double_arrow\n010807c6=drawable/pointer_top_left_diagonal_double_arrow_icon\n010807c7=drawable/pointer_top_left_diagonal_double_arrow_large\n010807c8=drawable/pointer_top_left_diagonal_double_arrow_large_icon\n010807c9=drawable/pointer_top_left_diagonal_double_arrow_vector\n010807ca=drawable/pointer_top_left_diagonal_double_arrow_vector_icon\n010807cb=drawable/pointer_top_right_diagonal_double_arrow\n010807cc=drawable/pointer_top_right_diagonal_double_arrow_icon\n010807cd=drawable/pointer_top_right_diagonal_double_arrow_large\n010807ce=drawable/pointer_top_right_diagonal_double_arrow_large_icon\n010807cf=drawable/pointer_top_right_diagonal_double_arrow_vector\n010807d0=drawable/pointer_top_right_diagonal_double_arrow_vector_icon\n010807d1=drawable/pointer_vertical_double_arrow\n010807d2=drawable/pointer_vertical_double_arrow_icon\n010807d3=drawable/pointer_vertical_double_arrow_large\n010807d4=drawable/pointer_vertical_double_arrow_large_icon\n010807d5=drawable/pointer_vertical_double_arrow_vector\n010807d6=drawable/pointer_vertical_double_arrow_vector_icon\n010807d7=drawable/pointer_vertical_text\n010807d8=drawable/pointer_vertical_text_icon\n010807d9=drawable/pointer_vertical_text_large\n010807da=drawable/pointer_vertical_text_large_icon\n010807db=drawable/pointer_vertical_text_vector\n010807dc=drawable/pointer_vertical_text_vector_icon\n010807dd=drawable/pointer_wait\n010807de=drawable/pointer_wait_0\n010807df=drawable/pointer_wait_1\n010807e0=drawable/pointer_wait_10\n010807e1=drawable/pointer_wait_11\n010807e2=drawable/pointer_wait_12\n010807e3=drawable/pointer_wait_13\n010807e4=drawable/pointer_wait_14\n010807e5=drawable/pointer_wait_15\n010807e6=drawable/pointer_wait_16\n010807e7=drawable/pointer_wait_17\n010807e8=drawable/pointer_wait_18\n010807e9=drawable/pointer_wait_19\n010807ea=drawable/pointer_wait_2\n010807eb=drawable/pointer_wait_20\n010807ec=drawable/pointer_wait_21\n010807ed=drawable/pointer_wait_22\n010807ee=drawable/pointer_wait_23\n010807ef=drawable/pointer_wait_24\n010807f0=drawable/pointer_wait_25\n010807f1=drawable/pointer_wait_26\n010807f2=drawable/pointer_wait_27\n010807f3=drawable/pointer_wait_28\n010807f4=drawable/pointer_wait_29\n010807f5=drawable/pointer_wait_3\n010807f6=drawable/pointer_wait_30\n010807f7=drawable/pointer_wait_31\n010807f8=drawable/pointer_wait_32\n010807f9=drawable/pointer_wait_33\n010807fa=drawable/pointer_wait_34\n010807fb=drawable/pointer_wait_35\n010807fc=drawable/pointer_wait_36\n010807fd=drawable/pointer_wait_37\n010807fe=drawable/pointer_wait_38\n010807ff=drawable/pointer_wait_39\n01080800=drawable/pointer_wait_4\n01080801=drawable/pointer_wait_40\n01080802=drawable/pointer_wait_41\n01080803=drawable/pointer_wait_42\n01080804=drawable/pointer_wait_43\n01080805=drawable/pointer_wait_44\n01080806=drawable/pointer_wait_45\n01080807=drawable/pointer_wait_46\n01080808=drawable/pointer_wait_47\n01080809=drawable/pointer_wait_48\n0108080a=drawable/pointer_wait_49\n0108080b=drawable/pointer_wait_5\n0108080c=drawable/pointer_wait_50\n0108080d=drawable/pointer_wait_51\n0108080e=drawable/pointer_wait_52\n0108080f=drawable/pointer_wait_53\n01080810=drawable/pointer_wait_54\n01080811=drawable/pointer_wait_55\n01080812=drawable/pointer_wait_56\n01080813=drawable/pointer_wait_57\n01080814=drawable/pointer_wait_58\n01080815=drawable/pointer_wait_59\n01080816=drawable/pointer_wait_6\n01080817=drawable/pointer_wait_60\n01080818=drawable/pointer_wait_61\n01080819=drawable/pointer_wait_62\n0108081a=drawable/pointer_wait_63\n0108081b=drawable/pointer_wait_64\n0108081c=drawable/pointer_wait_65\n0108081d=drawable/pointer_wait_66\n0108081e=drawable/pointer_wait_67\n0108081f=drawable/pointer_wait_68\n01080820=drawable/pointer_wait_69\n01080821=drawable/pointer_wait_7\n01080822=drawable/pointer_wait_70\n01080823=drawable/pointer_wait_71\n01080824=drawable/pointer_wait_72\n01080825=drawable/pointer_wait_73\n01080826=drawable/pointer_wait_74\n01080827=drawable/pointer_wait_75\n01080828=drawable/pointer_wait_76\n01080829=drawable/pointer_wait_77\n0108082a=drawable/pointer_wait_78\n0108082b=drawable/pointer_wait_79\n0108082c=drawable/pointer_wait_8\n0108082d=drawable/pointer_wait_80\n0108082e=drawable/pointer_wait_9\n0108082f=drawable/pointer_wait_icon\n01080830=drawable/pointer_wait_vector\n01080831=drawable/pointer_wait_vector_0\n01080832=drawable/pointer_wait_vector_1\n01080833=drawable/pointer_wait_vector_10\n01080834=drawable/pointer_wait_vector_11\n01080835=drawable/pointer_wait_vector_12\n01080836=drawable/pointer_wait_vector_13\n01080837=drawable/pointer_wait_vector_14\n01080838=drawable/pointer_wait_vector_15\n01080839=drawable/pointer_wait_vector_16\n0108083a=drawable/pointer_wait_vector_17\n0108083b=drawable/pointer_wait_vector_18\n0108083c=drawable/pointer_wait_vector_19\n0108083d=drawable/pointer_wait_vector_2\n0108083e=drawable/pointer_wait_vector_20\n0108083f=drawable/pointer_wait_vector_21\n01080840=drawable/pointer_wait_vector_22\n01080841=drawable/pointer_wait_vector_23\n01080842=drawable/pointer_wait_vector_24\n01080843=drawable/pointer_wait_vector_25\n01080844=drawable/pointer_wait_vector_26\n01080845=drawable/pointer_wait_vector_27\n01080846=drawable/pointer_wait_vector_28\n01080847=drawable/pointer_wait_vector_29\n01080848=drawable/pointer_wait_vector_3\n01080849=drawable/pointer_wait_vector_30\n0108084a=drawable/pointer_wait_vector_31\n0108084b=drawable/pointer_wait_vector_32\n0108084c=drawable/pointer_wait_vector_33\n0108084d=drawable/pointer_wait_vector_34\n0108084e=drawable/pointer_wait_vector_35\n0108084f=drawable/pointer_wait_vector_36\n01080850=drawable/pointer_wait_vector_37\n01080851=drawable/pointer_wait_vector_38\n01080852=drawable/pointer_wait_vector_39\n01080853=drawable/pointer_wait_vector_4\n01080854=drawable/pointer_wait_vector_40\n01080855=drawable/pointer_wait_vector_41\n01080856=drawable/pointer_wait_vector_42\n01080857=drawable/pointer_wait_vector_43\n01080858=drawable/pointer_wait_vector_44\n01080859=drawable/pointer_wait_vector_45\n0108085a=drawable/pointer_wait_vector_46\n0108085b=drawable/pointer_wait_vector_47\n0108085c=drawable/pointer_wait_vector_48\n0108085d=drawable/pointer_wait_vector_49\n0108085e=drawable/pointer_wait_vector_5\n0108085f=drawable/pointer_wait_vector_50\n01080860=drawable/pointer_wait_vector_51\n01080861=drawable/pointer_wait_vector_52\n01080862=drawable/pointer_wait_vector_53\n01080863=drawable/pointer_wait_vector_54\n01080864=drawable/pointer_wait_vector_55\n01080865=drawable/pointer_wait_vector_56\n01080866=drawable/pointer_wait_vector_57\n01080867=drawable/pointer_wait_vector_58\n01080868=drawable/pointer_wait_vector_59\n01080869=drawable/pointer_wait_vector_6\n0108086a=drawable/pointer_wait_vector_60\n0108086b=drawable/pointer_wait_vector_61\n0108086c=drawable/pointer_wait_vector_62\n0108086d=drawable/pointer_wait_vector_63\n0108086e=drawable/pointer_wait_vector_64\n0108086f=drawable/pointer_wait_vector_65\n01080870=drawable/pointer_wait_vector_66\n01080871=drawable/pointer_wait_vector_67\n01080872=drawable/pointer_wait_vector_68\n01080873=drawable/pointer_wait_vector_69\n01080874=drawable/pointer_wait_vector_7\n01080875=drawable/pointer_wait_vector_70\n01080876=drawable/pointer_wait_vector_71\n01080877=drawable/pointer_wait_vector_72\n01080878=drawable/pointer_wait_vector_73\n01080879=drawable/pointer_wait_vector_74\n0108087a=drawable/pointer_wait_vector_75\n0108087b=drawable/pointer_wait_vector_76\n0108087c=drawable/pointer_wait_vector_77\n0108087d=drawable/pointer_wait_vector_78\n0108087e=drawable/pointer_wait_vector_79\n0108087f=drawable/pointer_wait_vector_8\n01080880=drawable/pointer_wait_vector_80\n01080881=drawable/pointer_wait_vector_81\n01080882=drawable/pointer_wait_vector_82\n01080883=drawable/pointer_wait_vector_83\n01080884=drawable/pointer_wait_vector_84\n01080885=drawable/pointer_wait_vector_85\n01080886=drawable/pointer_wait_vector_86\n01080887=drawable/pointer_wait_vector_87\n01080888=drawable/pointer_wait_vector_9\n01080889=drawable/pointer_wait_vector_icon\n0108088a=drawable/pointer_zoom_in\n0108088b=drawable/pointer_zoom_in_icon\n0108088c=drawable/pointer_zoom_in_large\n0108088d=drawable/pointer_zoom_in_large_icon\n0108088e=drawable/pointer_zoom_in_vector\n0108088f=drawable/pointer_zoom_in_vector_icon\n01080890=drawable/pointer_zoom_out\n01080891=drawable/pointer_zoom_out_icon\n01080892=drawable/pointer_zoom_out_large\n01080893=drawable/pointer_zoom_out_large_icon\n01080894=drawable/pointer_zoom_out_vector\n01080895=drawable/pointer_zoom_out_vector_icon\n01080896=drawable/popup_background_material\n01080897=drawable/popup_background_mtrl_mult\n01080898=drawable/popup_bottom_bright\n01080899=drawable/popup_bottom_dark\n0108089a=drawable/popup_bottom_medium\n0108089b=drawable/popup_center_bright\n0108089c=drawable/popup_center_dark\n0108089d=drawable/popup_center_medium\n0108089e=drawable/popup_full_bright\n0108089f=drawable/popup_full_dark\n010808a0=drawable/popup_inline_error\n010808a1=drawable/popup_inline_error_above\n010808a2=drawable/popup_inline_error_above_am\n010808a3=drawable/popup_inline_error_above_holo_dark\n010808a4=drawable/popup_inline_error_above_holo_dark_am\n010808a5=drawable/popup_inline_error_above_holo_light\n010808a6=drawable/popup_inline_error_above_holo_light_am\n010808a7=drawable/popup_inline_error_am\n010808a8=drawable/popup_inline_error_holo_dark\n010808a9=drawable/popup_inline_error_holo_dark_am\n010808aa=drawable/popup_inline_error_holo_light\n010808ab=drawable/popup_inline_error_holo_light_am\n010808ac=drawable/popup_top_bright\n010808ad=drawable/popup_top_dark\n010808ae=drawable/pressed_application_background_static\n010808af=drawable/progress_bg_holo_dark\n010808b0=drawable/progress_bg_holo_light\n010808b1=drawable/progress_horizontal_holo_dark\n010808b2=drawable/progress_horizontal_holo_light\n010808b3=drawable/progress_horizontal_material\n010808b4=drawable/progress_indeterminate_anim_large_material\n010808b5=drawable/progress_indeterminate_anim_medium_material\n010808b6=drawable/progress_indeterminate_horizontal_holo\n010808b7=drawable/progress_indeterminate_horizontal_material\n010808b8=drawable/progress_large\n010808b9=drawable/progress_large_holo\n010808ba=drawable/progress_large_material\n010808bb=drawable/progress_large_white\n010808bc=drawable/progress_medium\n010808bd=drawable/progress_medium_holo\n010808be=drawable/progress_medium_material\n010808bf=drawable/progress_medium_white\n010808c0=drawable/progress_primary_holo_dark\n010808c1=drawable/progress_primary_holo_light\n010808c2=drawable/progress_ring_watch\n010808c3=drawable/progress_secondary_holo_dark\n010808c4=drawable/progress_secondary_holo_light\n010808c5=drawable/progress_small\n010808c6=drawable/progress_small_holo\n010808c7=drawable/progress_small_material\n010808c8=drawable/progress_small_titlebar\n010808c9=drawable/progress_small_white\n010808ca=drawable/progress_static_material\n010808cb=drawable/progressbar_indeterminate1\n010808cc=drawable/progressbar_indeterminate2\n010808cd=drawable/progressbar_indeterminate3\n010808ce=drawable/progressbar_indeterminate_holo1\n010808cf=drawable/progressbar_indeterminate_holo2\n010808d0=drawable/progressbar_indeterminate_holo3\n010808d1=drawable/progressbar_indeterminate_holo4\n010808d2=drawable/progressbar_indeterminate_holo5\n010808d3=drawable/progressbar_indeterminate_holo6\n010808d4=drawable/progressbar_indeterminate_holo7\n010808d5=drawable/progressbar_indeterminate_holo8\n010808d6=drawable/quickactions_arrowdown_left_holo_dark\n010808d7=drawable/quickactions_arrowdown_left_holo_light\n010808d8=drawable/quickactions_arrowdown_right_holo_dark\n010808d9=drawable/quickactions_arrowdown_right_holo_light\n010808da=drawable/quickactions_arrowup_left_holo_dark\n010808db=drawable/quickactions_arrowup_left_holo_light\n010808dc=drawable/quickactions_arrowup_left_right_holo_dark\n010808dd=drawable/quickactions_arrowup_right_holo_light\n010808de=drawable/quickcontact_badge_overlay_dark\n010808df=drawable/quickcontact_badge_overlay_focused_dark\n010808e0=drawable/quickcontact_badge_overlay_focused_dark_am\n010808e1=drawable/quickcontact_badge_overlay_focused_light\n010808e2=drawable/quickcontact_badge_overlay_focused_light_am\n010808e3=drawable/quickcontact_badge_overlay_light\n010808e4=drawable/quickcontact_badge_overlay_normal_dark\n010808e5=drawable/quickcontact_badge_overlay_normal_dark_am\n010808e6=drawable/quickcontact_badge_overlay_normal_light\n010808e7=drawable/quickcontact_badge_overlay_normal_light_am\n010808e8=drawable/quickcontact_badge_overlay_pressed_dark\n010808e9=drawable/quickcontact_badge_overlay_pressed_dark_am\n010808ea=drawable/quickcontact_badge_overlay_pressed_light\n010808eb=drawable/quickcontact_badge_overlay_pressed_light_am\n010808ec=drawable/rate_star_big_half\n010808ed=drawable/rate_star_big_half_holo_dark\n010808ee=drawable/rate_star_big_half_holo_light\n010808ef=drawable/rate_star_big_off\n010808f0=drawable/rate_star_big_off_holo_dark\n010808f1=drawable/rate_star_big_off_holo_light\n010808f2=drawable/rate_star_big_on\n010808f3=drawable/rate_star_big_on_holo_dark\n010808f4=drawable/rate_star_big_on_holo_light\n010808f5=drawable/rate_star_med_half\n010808f6=drawable/rate_star_med_half_holo_dark\n010808f7=drawable/rate_star_med_half_holo_light\n010808f8=drawable/rate_star_med_off\n010808f9=drawable/rate_star_med_off_holo_dark\n010808fa=drawable/rate_star_med_off_holo_light\n010808fb=drawable/rate_star_med_on\n010808fc=drawable/rate_star_med_on_holo_dark\n010808fd=drawable/rate_star_med_on_holo_light\n010808fe=drawable/rate_star_small_half\n010808ff=drawable/rate_star_small_half_holo_dark\n01080900=drawable/rate_star_small_half_holo_light\n01080901=drawable/rate_star_small_off\n01080902=drawable/rate_star_small_off_holo_dark\n01080903=drawable/rate_star_small_off_holo_light\n01080904=drawable/rate_star_small_on\n01080905=drawable/rate_star_small_on_holo_dark\n01080906=drawable/rate_star_small_on_holo_light\n01080907=drawable/ratingbar\n01080908=drawable/ratingbar_full\n01080909=drawable/ratingbar_full_empty\n0108090a=drawable/ratingbar_full_empty_holo_dark\n0108090b=drawable/ratingbar_full_empty_holo_light\n0108090c=drawable/ratingbar_full_empty_material\n0108090d=drawable/ratingbar_full_filled\n0108090e=drawable/ratingbar_full_filled_holo_dark\n0108090f=drawable/ratingbar_full_filled_holo_light\n01080910=drawable/ratingbar_full_filled_material\n01080911=drawable/ratingbar_full_half_material\n01080912=drawable/ratingbar_full_holo_dark\n01080913=drawable/ratingbar_full_holo_light\n01080914=drawable/ratingbar_holo_dark\n01080915=drawable/ratingbar_holo_light\n01080916=drawable/ratingbar_indicator_material\n01080917=drawable/ratingbar_material\n01080918=drawable/ratingbar_small\n01080919=drawable/ratingbar_small_holo_dark\n0108091a=drawable/ratingbar_small_holo_light\n0108091b=drawable/ratingbar_small_material\n0108091c=drawable/recent_dialog_background\n0108091d=drawable/red_shield\n0108091e=drawable/resolver_button_bg\n0108091f=drawable/resolver_icon_placeholder\n01080920=drawable/resolver_outlined_button_bg\n01080921=drawable/resolver_profile_tab_bg\n01080922=drawable/resolver_turn_on_work_button_ripple_background\n01080923=drawable/reticle\n01080924=drawable/safe_mode_background\n01080925=drawable/screen_background_holo_dark\n01080926=drawable/screen_background_holo_light\n01080927=drawable/screen_background_selector_dark\n01080928=drawable/screen_background_selector_light\n01080929=drawable/scroll_indicator_material\n0108092a=drawable/scrollbar_handle_accelerated_anim2\n0108092b=drawable/scrollbar_handle_holo_dark\n0108092c=drawable/scrollbar_handle_holo_light\n0108092d=drawable/scrollbar_handle_horizontal\n0108092e=drawable/scrollbar_handle_material\n0108092f=drawable/scrollbar_handle_vertical\n01080930=drawable/scrollbar_vertical_thumb\n01080931=drawable/scrollbar_vertical_track\n01080932=drawable/scrubber_control_disabled_holo\n01080933=drawable/scrubber_control_focused_holo\n01080934=drawable/scrubber_control_normal_holo\n01080935=drawable/scrubber_control_on_mtrl_alpha\n01080936=drawable/scrubber_control_on_pressed_mtrl_alpha\n01080937=drawable/scrubber_control_pressed_holo\n01080938=drawable/scrubber_control_selector_holo\n01080939=drawable/scrubber_primary_holo\n0108093a=drawable/scrubber_primary_mtrl_alpha\n0108093b=drawable/scrubber_progress_horizontal_holo_dark\n0108093c=drawable/scrubber_progress_horizontal_holo_light\n0108093d=drawable/scrubber_secondary_holo\n0108093e=drawable/scrubber_track_holo_dark\n0108093f=drawable/scrubber_track_holo_light\n01080940=drawable/scrubber_track_mtrl_alpha\n01080941=drawable/search_bar_default_color\n01080942=drawable/search_dropdown_background\n01080943=drawable/search_dropdown_dark\n01080944=drawable/search_dropdown_light\n01080945=drawable/search_plate\n01080946=drawable/search_plate_global\n01080947=drawable/search_spinner\n01080948=drawable/seek_thumb\n01080949=drawable/seek_thumb_normal\n0108094a=drawable/seek_thumb_pressed\n0108094b=drawable/seek_thumb_selected\n0108094c=drawable/seekbar_thumb_material_anim\n0108094d=drawable/seekbar_thumb_pressed_to_unpressed\n0108094e=drawable/seekbar_thumb_pressed_to_unpressed_animation\n0108094f=drawable/seekbar_thumb_unpressed_to_pressed\n01080950=drawable/seekbar_thumb_unpressed_to_pressed_animation\n01080951=drawable/seekbar_tick_mark_material\n01080952=drawable/seekbar_track_material\n01080953=drawable/selected_day_background\n01080954=drawable/settings_header\n01080955=drawable/settings_header_raw\n01080956=drawable/silent_mode_indicator\n01080957=drawable/sim_dark_blue\n01080958=drawable/sim_dark_green\n01080959=drawable/sim_dark_orange\n0108095a=drawable/sim_dark_purple\n0108095b=drawable/sim_light_blue\n0108095c=drawable/sim_light_green\n0108095d=drawable/sim_light_orange\n0108095e=drawable/sim_light_purple\n0108095f=drawable/slice_remote_input_bg\n01080960=drawable/slice_ripple_drawable\n01080961=drawable/spinner_16_inner_holo\n01080962=drawable/spinner_16_outer_holo\n01080963=drawable/spinner_48_inner_holo\n01080964=drawable/spinner_48_outer_holo\n01080965=drawable/spinner_76_inner_holo\n01080966=drawable/spinner_76_outer_holo\n01080967=drawable/spinner_ab_activated_holo_dark\n01080968=drawable/spinner_ab_activated_holo_light\n01080969=drawable/spinner_ab_default_holo_dark\n0108096a=drawable/spinner_ab_default_holo_dark_am\n0108096b=drawable/spinner_ab_default_holo_light\n0108096c=drawable/spinner_ab_default_holo_light_am\n0108096d=drawable/spinner_ab_disabled_holo_dark\n0108096e=drawable/spinner_ab_disabled_holo_dark_am\n0108096f=drawable/spinner_ab_disabled_holo_light\n01080970=drawable/spinner_ab_disabled_holo_light_am\n01080971=drawable/spinner_ab_focused_holo_dark\n01080972=drawable/spinner_ab_focused_holo_dark_am\n01080973=drawable/spinner_ab_focused_holo_light\n01080974=drawable/spinner_ab_focused_holo_light_am\n01080975=drawable/spinner_ab_holo_dark\n01080976=drawable/spinner_ab_holo_light\n01080977=drawable/spinner_ab_pressed_holo_dark\n01080978=drawable/spinner_ab_pressed_holo_dark_am\n01080979=drawable/spinner_ab_pressed_holo_light\n0108097a=drawable/spinner_ab_pressed_holo_light_am\n0108097b=drawable/spinner_activated_holo_dark\n0108097c=drawable/spinner_activated_holo_light\n0108097d=drawable/spinner_background_holo_dark\n0108097e=drawable/spinner_background_holo_light\n0108097f=drawable/spinner_background_material\n01080980=drawable/spinner_black_16\n01080981=drawable/spinner_black_20\n01080982=drawable/spinner_black_48\n01080983=drawable/spinner_black_76\n01080984=drawable/spinner_default_holo_dark\n01080985=drawable/spinner_default_holo_dark_am\n01080986=drawable/spinner_default_holo_light\n01080987=drawable/spinner_default_holo_light_am\n01080988=drawable/spinner_disabled_holo\n01080989=drawable/spinner_disabled_holo_dark\n0108098a=drawable/spinner_disabled_holo_dark_am\n0108098b=drawable/spinner_disabled_holo_light\n0108098c=drawable/spinner_disabled_holo_light_am\n0108098d=drawable/spinner_dropdown_background_down\n0108098e=drawable/spinner_dropdown_background_up\n0108098f=drawable/spinner_focused_holo_dark\n01080990=drawable/spinner_focused_holo_dark_am\n01080991=drawable/spinner_focused_holo_light\n01080992=drawable/spinner_focused_holo_light_am\n01080993=drawable/spinner_normal\n01080994=drawable/spinner_normal_holo\n01080995=drawable/spinner_press\n01080996=drawable/spinner_pressed_holo_dark\n01080997=drawable/spinner_pressed_holo_dark_am\n01080998=drawable/spinner_pressed_holo_light\n01080999=drawable/spinner_pressed_holo_light_am\n0108099a=drawable/spinner_select\n0108099b=drawable/spinner_textfield_background_material\n0108099c=drawable/spinner_white_16\n0108099d=drawable/spinner_white_48\n0108099e=drawable/spinner_white_76\n0108099f=drawable/stat_ecb_mode\n010809a0=drawable/stat_notify_car_mode\n010809a1=drawable/stat_notify_disabled_data\n010809a2=drawable/stat_notify_disk_full\n010809a3=drawable/stat_notify_email_generic\n010809a4=drawable/stat_notify_gmail\n010809a5=drawable/stat_notify_mmcc_indication_icn\n010809a6=drawable/stat_notify_rssi_in_range\n010809a7=drawable/stat_notify_sim_toolkit\n010809a8=drawable/stat_notify_sync_anim0\n010809a9=drawable/stat_notify_sync_error\n010809aa=drawable/stat_notify_wifi_in_range\n010809ab=drawable/stat_sys_adb\n010809ac=drawable/stat_sys_battery\n010809ad=drawable/stat_sys_battery_0\n010809ae=drawable/stat_sys_battery_10\n010809af=drawable/stat_sys_battery_100\n010809b0=drawable/stat_sys_battery_15\n010809b1=drawable/stat_sys_battery_20\n010809b2=drawable/stat_sys_battery_28\n010809b3=drawable/stat_sys_battery_40\n010809b4=drawable/stat_sys_battery_43\n010809b5=drawable/stat_sys_battery_57\n010809b6=drawable/stat_sys_battery_60\n010809b7=drawable/stat_sys_battery_71\n010809b8=drawable/stat_sys_battery_80\n010809b9=drawable/stat_sys_battery_85\n010809ba=drawable/stat_sys_battery_charge\n010809bb=drawable/stat_sys_battery_charge_anim0\n010809bc=drawable/stat_sys_battery_charge_anim1\n010809bd=drawable/stat_sys_battery_charge_anim100\n010809be=drawable/stat_sys_battery_charge_anim15\n010809bf=drawable/stat_sys_battery_charge_anim2\n010809c0=drawable/stat_sys_battery_charge_anim28\n010809c1=drawable/stat_sys_battery_charge_anim3\n010809c2=drawable/stat_sys_battery_charge_anim4\n010809c3=drawable/stat_sys_battery_charge_anim43\n010809c4=drawable/stat_sys_battery_charge_anim5\n010809c5=drawable/stat_sys_battery_charge_anim57\n010809c6=drawable/stat_sys_battery_charge_anim71\n010809c7=drawable/stat_sys_battery_charge_anim85\n010809c8=drawable/stat_sys_battery_unknown\n010809c9=drawable/stat_sys_certificate_info\n010809ca=drawable/stat_sys_data_usb\n010809cb=drawable/stat_sys_data_wimax_signal_3_fully\n010809cc=drawable/stat_sys_data_wimax_signal_disconnected\n010809cd=drawable/stat_sys_download_anim0\n010809ce=drawable/stat_sys_download_anim1\n010809cf=drawable/stat_sys_download_anim2\n010809d0=drawable/stat_sys_download_anim3\n010809d1=drawable/stat_sys_download_anim4\n010809d2=drawable/stat_sys_download_anim5\n010809d3=drawable/stat_sys_download_done_static\n010809d4=drawable/stat_sys_gps_on\n010809d5=drawable/stat_sys_managed_profile_status\n010809d6=drawable/stat_sys_private_profile_status\n010809d7=drawable/stat_sys_r_signal_0_cdma\n010809d8=drawable/stat_sys_r_signal_1_cdma\n010809d9=drawable/stat_sys_r_signal_2_cdma\n010809da=drawable/stat_sys_r_signal_3_cdma\n010809db=drawable/stat_sys_r_signal_4_cdma\n010809dc=drawable/stat_sys_ra_signal_0_cdma\n010809dd=drawable/stat_sys_ra_signal_1_cdma\n010809de=drawable/stat_sys_ra_signal_2_cdma\n010809df=drawable/stat_sys_ra_signal_3_cdma\n010809e0=drawable/stat_sys_ra_signal_4_cdma\n010809e1=drawable/stat_sys_signal_0_cdma\n010809e2=drawable/stat_sys_signal_1_cdma\n010809e3=drawable/stat_sys_signal_2_cdma\n010809e4=drawable/stat_sys_signal_3_cdma\n010809e5=drawable/stat_sys_signal_4_cdma\n010809e6=drawable/stat_sys_signal_evdo_0\n010809e7=drawable/stat_sys_signal_evdo_1\n010809e8=drawable/stat_sys_signal_evdo_2\n010809e9=drawable/stat_sys_signal_evdo_3\n010809ea=drawable/stat_sys_signal_evdo_4\n010809eb=drawable/stat_sys_tether_wifi\n010809ec=drawable/stat_sys_throttled\n010809ed=drawable/stat_sys_upload_anim0\n010809ee=drawable/stat_sys_upload_anim1\n010809ef=drawable/stat_sys_upload_anim2\n010809f0=drawable/stat_sys_upload_anim3\n010809f1=drawable/stat_sys_upload_anim4\n010809f2=drawable/stat_sys_upload_anim5\n010809f3=drawable/stat_sys_vitals\n010809f4=drawable/status_bar_background\n010809f5=drawable/status_bar_closed_default_background\n010809f6=drawable/status_bar_header_background\n010809f7=drawable/status_bar_item_app_background_normal\n010809f8=drawable/status_bar_item_background_focus\n010809f9=drawable/status_bar_item_background_normal\n010809fa=drawable/status_bar_item_background_pressed\n010809fb=drawable/status_bar_opened_default_background\n010809fc=drawable/statusbar_background\n010809fd=drawable/submenu_arrow\n010809fe=drawable/submenu_arrow_nofocus\n010809ff=drawable/switch_bg_disabled_holo_dark\n01080a00=drawable/switch_bg_disabled_holo_light\n01080a01=drawable/switch_bg_focused_holo_dark\n01080a02=drawable/switch_bg_focused_holo_light\n01080a03=drawable/switch_bg_holo_dark\n01080a04=drawable/switch_bg_holo_light\n01080a05=drawable/switch_inner_holo_dark\n01080a06=drawable/switch_inner_holo_light\n01080a07=drawable/switch_thumb_activated_holo_dark\n01080a08=drawable/switch_thumb_activated_holo_light\n01080a09=drawable/switch_thumb_disabled_holo_dark\n01080a0a=drawable/switch_thumb_disabled_holo_light\n01080a0b=drawable/switch_thumb_holo_dark\n01080a0c=drawable/switch_thumb_holo_light\n01080a0d=drawable/switch_thumb_holo_light_v2\n01080a0e=drawable/switch_thumb_material_anim\n01080a0f=drawable/switch_thumb_pressed_holo_dark\n01080a10=drawable/switch_thumb_pressed_holo_light\n01080a11=drawable/switch_thumb_watch_default_dark_anim\n01080a12=drawable/switch_track_holo_dark\n01080a13=drawable/switch_track_holo_light\n01080a14=drawable/switch_track_material\n01080a15=drawable/sym_action_add\n01080a16=drawable/sym_app_on_sd_unavailable_icon\n01080a17=drawable/sym_def_app_icon_background\n01080a18=drawable/sym_keyboard_delete\n01080a19=drawable/sym_keyboard_delete_dim\n01080a1a=drawable/sym_keyboard_delete_holo\n01080a1b=drawable/sym_keyboard_enter\n01080a1c=drawable/sym_keyboard_feedback_delete\n01080a1d=drawable/sym_keyboard_feedback_ok\n01080a1e=drawable/sym_keyboard_feedback_return\n01080a1f=drawable/sym_keyboard_feedback_shift\n01080a20=drawable/sym_keyboard_feedback_shift_locked\n01080a21=drawable/sym_keyboard_feedback_space\n01080a22=drawable/sym_keyboard_num0_no_plus\n01080a23=drawable/sym_keyboard_num1\n01080a24=drawable/sym_keyboard_num2\n01080a25=drawable/sym_keyboard_num3\n01080a26=drawable/sym_keyboard_num4\n01080a27=drawable/sym_keyboard_num5\n01080a28=drawable/sym_keyboard_num6\n01080a29=drawable/sym_keyboard_num7\n01080a2a=drawable/sym_keyboard_num8\n01080a2b=drawable/sym_keyboard_num9\n01080a2c=drawable/sym_keyboard_ok\n01080a2d=drawable/sym_keyboard_ok_dim\n01080a2e=drawable/sym_keyboard_return\n01080a2f=drawable/sym_keyboard_return_holo\n01080a30=drawable/sym_keyboard_shift\n01080a31=drawable/sym_keyboard_shift_locked\n01080a32=drawable/sym_keyboard_space\n01080a33=drawable/tab_bottom_holo\n01080a34=drawable/tab_bottom_left\n01080a35=drawable/tab_bottom_left_v4\n01080a36=drawable/tab_bottom_right\n01080a37=drawable/tab_bottom_right_v4\n01080a38=drawable/tab_focus\n01080a39=drawable/tab_focus_bar_left\n01080a3a=drawable/tab_focus_bar_right\n01080a3b=drawable/tab_indicator\n01080a3c=drawable/tab_indicator_ab_holo\n01080a3d=drawable/tab_indicator_holo\n01080a3e=drawable/tab_indicator_material\n01080a3f=drawable/tab_indicator_mtrl_alpha\n01080a40=drawable/tab_indicator_resolver\n01080a41=drawable/tab_indicator_v4\n01080a42=drawable/tab_press\n01080a43=drawable/tab_press_bar_left\n01080a44=drawable/tab_press_bar_right\n01080a45=drawable/tab_pressed_holo\n01080a46=drawable/tab_selected\n01080a47=drawable/tab_selected_bar_left\n01080a48=drawable/tab_selected_bar_left_v4\n01080a49=drawable/tab_selected_bar_right\n01080a4a=drawable/tab_selected_bar_right_v4\n01080a4b=drawable/tab_selected_focused_holo\n01080a4c=drawable/tab_selected_holo\n01080a4d=drawable/tab_selected_pressed_holo\n01080a4e=drawable/tab_selected_v4\n01080a4f=drawable/tab_unselected\n01080a50=drawable/tab_unselected_focused_holo\n01080a51=drawable/tab_unselected_holo\n01080a52=drawable/tab_unselected_pressed_holo\n01080a53=drawable/tab_unselected_v4\n01080a54=drawable/text_cursor_holo_dark\n01080a55=drawable/text_cursor_holo_light\n01080a56=drawable/text_cursor_material\n01080a57=drawable/text_edit_paste_window\n01080a58=drawable/text_edit_side_paste_window\n01080a59=drawable/text_edit_suggestions_window\n01080a5a=drawable/text_select_handle_left_material\n01080a5b=drawable/text_select_handle_left_mtrl_alpha\n01080a5c=drawable/text_select_handle_middle_material\n01080a5d=drawable/text_select_handle_middle_mtrl_alpha\n01080a5e=drawable/text_select_handle_right_material\n01080a5f=drawable/text_select_handle_right_mtrl_alpha\n01080a60=drawable/textfield_activated_holo_dark\n01080a61=drawable/textfield_activated_holo_light\n01080a62=drawable/textfield_activated_mtrl_alpha\n01080a63=drawable/textfield_bg_activated_holo_dark\n01080a64=drawable/textfield_bg_default_holo_dark\n01080a65=drawable/textfield_bg_disabled_focused_holo_dark\n01080a66=drawable/textfield_bg_disabled_holo_dark\n01080a67=drawable/textfield_bg_focused_holo_dark\n01080a68=drawable/textfield_default\n01080a69=drawable/textfield_default_holo_dark\n01080a6a=drawable/textfield_default_holo_light\n01080a6b=drawable/textfield_default_mtrl_alpha\n01080a6c=drawable/textfield_disabled\n01080a6d=drawable/textfield_disabled_focused_holo_dark\n01080a6e=drawable/textfield_disabled_focused_holo_light\n01080a6f=drawable/textfield_disabled_holo_dark\n01080a70=drawable/textfield_disabled_holo_light\n01080a71=drawable/textfield_disabled_selected\n01080a72=drawable/textfield_focused_holo_dark\n01080a73=drawable/textfield_focused_holo_light\n01080a74=drawable/textfield_longpress_holo\n01080a75=drawable/textfield_multiline_activated_holo_dark\n01080a76=drawable/textfield_multiline_activated_holo_light\n01080a77=drawable/textfield_multiline_default_holo_dark\n01080a78=drawable/textfield_multiline_default_holo_light\n01080a79=drawable/textfield_multiline_disabled_focused_holo_dark\n01080a7a=drawable/textfield_multiline_disabled_focused_holo_light\n01080a7b=drawable/textfield_multiline_disabled_holo_dark\n01080a7c=drawable/textfield_multiline_disabled_holo_light\n01080a7d=drawable/textfield_multiline_focused_holo_dark\n01080a7e=drawable/textfield_multiline_focused_holo_light\n01080a7f=drawable/textfield_pressed_holo\n01080a80=drawable/textfield_search\n01080a81=drawable/textfield_search_activated_mtrl_alpha\n01080a82=drawable/textfield_search_default\n01080a83=drawable/textfield_search_default_holo_dark\n01080a84=drawable/textfield_search_default_holo_light\n01080a85=drawable/textfield_search_default_mtrl_alpha\n01080a86=drawable/textfield_search_empty\n01080a87=drawable/textfield_search_empty_default\n01080a88=drawable/textfield_search_empty_pressed\n01080a89=drawable/textfield_search_empty_selected\n01080a8a=drawable/textfield_search_material\n01080a8b=drawable/textfield_search_pressed\n01080a8c=drawable/textfield_search_right_default_holo_dark\n01080a8d=drawable/textfield_search_right_default_holo_light\n01080a8e=drawable/textfield_search_right_selected_holo_dark\n01080a8f=drawable/textfield_search_right_selected_holo_light\n01080a90=drawable/textfield_search_selected\n01080a91=drawable/textfield_search_selected_holo_dark\n01080a92=drawable/textfield_search_selected_holo_light\n01080a93=drawable/textfield_searchview_holo_dark\n01080a94=drawable/textfield_searchview_holo_light\n01080a95=drawable/textfield_searchview_right_holo_dark\n01080a96=drawable/textfield_searchview_right_holo_light\n01080a97=drawable/textfield_selected\n01080a98=drawable/time_picker_editable_background\n01080a99=drawable/title_bar_medium\n01080a9a=drawable/title_bar_portrait\n01080a9b=drawable/title_bar_shadow\n01080a9c=drawable/tooltip_frame\n01080a9d=drawable/transportcontrol_bg\n01080a9e=drawable/unknown_image\n01080a9f=drawable/unlock_default\n01080aa0=drawable/unlock_halo\n01080aa1=drawable/unlock_ring\n01080aa2=drawable/unlock_wave\n01080aa3=drawable/usb_cable_unknown_issue\n01080aa4=drawable/vector_drawable_progress_bar_large\n01080aa5=drawable/vector_drawable_progress_bar_medium\n01080aa6=drawable/vector_drawable_progress_bar_small\n01080aa7=drawable/vector_drawable_progress_indeterminate_horizontal\n01080aa8=drawable/vector_notification_progress_indeterminate_horizontal\n01080aa9=drawable/view_accessibility_focused\n01080aaa=drawable/vpn_connected\n01080aab=drawable/vpn_disconnected\n01080aac=drawable/watch_switch_thumb_mtrl_14w\n01080aad=drawable/watch_switch_thumb_mtrl_15w\n01080aae=drawable/watch_switch_thumb_mtrl_16w\n01080aaf=drawable/watch_switch_thumb_mtrl_17w\n01080ab0=drawable/watch_switch_thumb_mtrl_18w\n01080ab1=drawable/watch_switch_track_mtrl\n01080ab2=drawable/work_mode_emergency_button_background\n01080ab3=drawable/work_widget_mask_view_background\n01090000=layout/activity_list_item\n01090001=layout/expandable_list_content\n01090002=layout/preference_category\n01090003=layout/simple_list_item_1\n01090004=layout/simple_list_item_2\n01090005=layout/simple_list_item_checked\n01090006=layout/simple_expandable_list_item_1\n01090007=layout/simple_expandable_list_item_2\n01090008=layout/simple_spinner_item\n01090009=layout/simple_spinner_dropdown_item\n0109000a=layout/simple_dropdown_item_1line\n0109000b=layout/simple_gallery_item\n0109000c=layout/test_list_item\n0109000d=layout/two_line_list_item\n0109000e=layout/browser_link_context_header\n0109000f=layout/simple_list_item_single_choice\n01090010=layout/simple_list_item_multiple_choice\n01090011=layout/select_dialog_item\n01090012=layout/select_dialog_singlechoice\n01090013=layout/select_dialog_multichoice\n01090014=layout/list_content\n01090015=layout/simple_selectable_list_item\n01090016=layout/simple_list_item_activated_1\n01090017=layout/simple_list_item_activated_2\n01090018=layout/accessibility_autoclick_scroll_panel\n01090019=layout/accessibility_autoclick_type_panel\n0109001a=layout/accessibility_button_chooser\n0109001b=layout/accessibility_button_chooser_item\n0109001c=layout/accessibility_enable_service_warning\n0109001d=layout/accessibility_service_warning\n0109001e=layout/accessibility_shortcut_chooser_item\n0109001f=layout/action_bar_home\n01090020=layout/action_bar_home_material\n01090021=layout/action_bar_title_item\n01090022=layout/action_bar_up_container\n01090023=layout/action_menu_item_layout\n01090024=layout/action_menu_layout\n01090025=layout/action_mode_bar\n01090026=layout/action_mode_close_item\n01090027=layout/action_mode_close_item_material\n01090028=layout/activity_chooser_view\n01090029=layout/activity_chooser_view_list_item\n0109002a=layout/activity_list\n0109002b=layout/activity_list_item_2\n0109002c=layout/adaptive_notification_wrapper\n0109002d=layout/alert_dialog\n0109002e=layout/alert_dialog_button_bar_leanback\n0109002f=layout/alert_dialog_button_bar_material\n01090030=layout/alert_dialog_holo\n01090031=layout/alert_dialog_icon_button_watch\n01090032=layout/alert_dialog_leanback\n01090033=layout/alert_dialog_leanback_button_panel_side\n01090034=layout/alert_dialog_material\n01090035=layout/alert_dialog_progress\n01090036=layout/alert_dialog_progress_holo\n01090037=layout/alert_dialog_progress_material\n01090038=layout/alert_dialog_title_material\n01090039=layout/alert_dialog_title_watch\n0109003a=layout/alert_dialog_watch\n0109003b=layout/always_use_checkbox\n0109003c=layout/am_compat_mode_dialog\n0109003d=layout/app_anr_dialog\n0109003e=layout/app_error_dialog\n0109003f=layout/app_language_picker_locale_item\n01090040=layout/app_language_picker_system_current\n01090041=layout/app_language_picker_system_default\n01090042=layout/app_not_authorized\n01090043=layout/app_permission_item\n01090044=layout/app_permission_item_money\n01090045=layout/app_permission_item_old\n01090046=layout/app_perms_summary\n01090047=layout/auto_complete_list\n01090048=layout/autofill_dataset_picker\n01090049=layout/autofill_dataset_picker_fullscreen\n0109004a=layout/autofill_dataset_picker_header_footer\n0109004b=layout/autofill_fill_dialog\n0109004c=layout/autofill_save\n0109004d=layout/breadcrumbs_in_fragment\n0109004e=layout/breadcrumbs_in_fragment_material\n0109004f=layout/calendar_view\n01090050=layout/car_alert_dialog\n01090051=layout/car_alert_dialog_button_bar\n01090052=layout/car_alert_dialog_title\n01090053=layout/car_preference\n01090054=layout/car_preference_category\n01090055=layout/cascading_menu_item_layout\n01090056=layout/cascading_menu_item_layout_material\n01090057=layout/character_picker\n01090058=layout/character_picker_button\n01090059=layout/choose_account\n0109005a=layout/choose_account_row\n0109005b=layout/choose_account_type\n0109005c=layout/choose_type_and_account\n0109005d=layout/chooser_action_button\n0109005e=layout/chooser_action_row\n0109005f=layout/chooser_az_label_row\n01090060=layout/chooser_dialog\n01090061=layout/chooser_dialog_item\n01090062=layout/chooser_grid\n01090063=layout/chooser_grid_preview_file\n01090064=layout/chooser_grid_preview_image\n01090065=layout/chooser_grid_preview_text\n01090066=layout/chooser_list_per_profile\n01090067=layout/chooser_profile_row\n01090068=layout/chooser_row\n01090069=layout/chooser_row_direct_share\n0109006a=layout/common_tab_settings\n0109006b=layout/conversation_face_pile_layout\n0109006c=layout/date_picker_dialog\n0109006d=layout/date_picker_header_material\n0109006e=layout/date_picker_legacy\n0109006f=layout/date_picker_legacy_holo\n01090070=layout/date_picker_material\n01090071=layout/date_picker_month_item_material\n01090072=layout/date_picker_view_animator_material\n01090073=layout/day_picker_content_material\n01090074=layout/default_navigation\n01090075=layout/dialog_custom_title\n01090076=layout/dialog_custom_title_holo\n01090077=layout/dialog_custom_title_material\n01090078=layout/dialog_title\n01090079=layout/dialog_title_holo\n0109007a=layout/dialog_title_icons\n0109007b=layout/dialog_title_icons_holo\n0109007c=layout/dialog_title_icons_material\n0109007d=layout/dialog_title_material\n0109007e=layout/expanded_menu_layout\n0109007f=layout/floating_popup_close_overflow_button\n01090080=layout/floating_popup_container\n01090081=layout/floating_popup_menu_button\n01090082=layout/floating_popup_open_overflow_button\n01090083=layout/floating_popup_overflow_button\n01090084=layout/fragment_bread_crumb_item\n01090085=layout/fragment_bread_crumb_item_material\n01090086=layout/fragment_bread_crumbs\n01090087=layout/global_actions\n01090088=layout/global_actions_item\n01090089=layout/global_actions_silent_mode\n0109008a=layout/grant_credentials_permission\n0109008b=layout/harmful_app_warning_dialog\n0109008c=layout/heavy_weight_switcher\n0109008d=layout/icon_menu_item_layout\n0109008e=layout/icon_menu_layout\n0109008f=layout/immersive_mode_cling\n01090090=layout/input_method\n01090091=layout/input_method_extract_view\n01090092=layout/input_method_nav_back\n01090093=layout/input_method_nav_home_handle\n01090094=layout/input_method_nav_ime_switcher\n01090095=layout/input_method_navigation_bar\n01090096=layout/input_method_navigation_layout\n01090097=layout/input_method_switch_dialog_new\n01090098=layout/input_method_switch_dialog_title\n01090099=layout/input_method_switch_item\n0109009a=layout/input_method_switch_item_divider\n0109009b=layout/input_method_switch_item_header\n0109009c=layout/input_method_switch_item_new\n0109009d=layout/input_method_switcher_list_layout\n0109009e=layout/js_prompt\n0109009f=layout/keyboard_key_preview\n010900a0=layout/keyboard_popup_keyboard\n010900a1=layout/keyguard\n010900a2=layout/language_picker_bilingual_item\n010900a3=layout/language_picker_bilingual_section_header\n010900a4=layout/language_picker_item\n010900a5=layout/language_picker_section_header\n010900a6=layout/launch_warning\n010900a7=layout/list_content_simple\n010900a8=layout/list_gestures_overlay\n010900a9=layout/list_menu_item_checkbox\n010900aa=layout/list_menu_item_fixed_size_icon\n010900ab=layout/list_menu_item_icon\n010900ac=layout/list_menu_item_layout\n010900ad=layout/list_menu_item_radio\n010900ae=layout/locale_picker_item\n010900af=layout/media_controller\n010900b0=layout/media_route_chooser_dialog\n010900b1=layout/media_route_controller_dialog\n010900b2=layout/media_route_list_item\n010900b3=layout/menu_item\n010900b4=layout/miniresolver\n010900b5=layout/notification_2025_action_list\n010900b6=layout/notification_2025_conversation_face_pile_layout\n010900b7=layout/notification_2025_conversation_header\n010900b8=layout/notification_2025_conversation_icon_container\n010900b9=layout/notification_2025_expand_button\n010900ba=layout/notification_2025_messaging_group\n010900bb=layout/notification_2025_reply_history_container\n010900bc=layout/notification_2025_right_icon\n010900bd=layout/notification_2025_template_collapsed_base\n010900be=layout/notification_2025_template_collapsed_call\n010900bf=layout/notification_2025_template_collapsed_conversation\n010900c0=layout/notification_2025_template_collapsed_media\n010900c1=layout/notification_2025_template_collapsed_messaging\n010900c2=layout/notification_2025_template_compact_heads_up_base\n010900c3=layout/notification_2025_template_compact_heads_up_messaging\n010900c4=layout/notification_2025_template_expanded_base\n010900c5=layout/notification_2025_template_expanded_big_picture\n010900c6=layout/notification_2025_template_expanded_big_text\n010900c7=layout/notification_2025_template_expanded_call\n010900c8=layout/notification_2025_template_expanded_conversation\n010900c9=layout/notification_2025_template_expanded_inbox\n010900ca=layout/notification_2025_template_expanded_media\n010900cb=layout/notification_2025_template_expanded_messaging\n010900cc=layout/notification_2025_template_expanded_progress\n010900cd=layout/notification_2025_template_header\n010900ce=layout/notification_2025_template_heads_up_base\n010900cf=layout/notification_2025_text\n010900d0=layout/notification_2025_title\n010900d1=layout/notification_2025_top_line_views\n010900d2=layout/notification_close_button\n010900d3=layout/notification_expand_button\n010900d4=layout/notification_intruder_content\n010900d5=layout/notification_material_action\n010900d6=layout/notification_material_action_emphasized\n010900d7=layout/notification_material_action_emphasized_tombstone\n010900d8=layout/notification_material_action_list\n010900d9=layout/notification_material_action_tombstone\n010900da=layout/notification_material_media_action\n010900db=layout/notification_material_reply_text\n010900dc=layout/notification_template_conversation_header\n010900dd=layout/notification_template_conversation_icon_container\n010900de=layout/notification_template_header\n010900df=layout/notification_template_material_base\n010900e0=layout/notification_template_material_big_base\n010900e1=layout/notification_template_material_big_call\n010900e2=layout/notification_template_material_big_media\n010900e3=layout/notification_template_material_big_messaging\n010900e4=layout/notification_template_material_big_picture\n010900e5=layout/notification_template_material_big_text\n010900e6=layout/notification_template_material_call\n010900e7=layout/notification_template_material_compact_heads_up_base\n010900e8=layout/notification_template_material_conversation\n010900e9=layout/notification_template_material_heads_up_base\n010900ea=layout/notification_template_material_inbox\n010900eb=layout/notification_template_material_media\n010900ec=layout/notification_template_material_messaging\n010900ed=layout/notification_template_material_messaging_compact_heads_up\n010900ee=layout/notification_template_material_progress\n010900ef=layout/notification_template_messaging_group\n010900f0=layout/notification_template_messaging_image_message\n010900f1=layout/notification_template_messaging_text_message\n010900f2=layout/notification_template_notification_progress_bar\n010900f3=layout/notification_template_part_chronometer\n010900f4=layout/notification_template_part_line1\n010900f5=layout/notification_template_progress\n010900f6=layout/notification_template_progressbar\n010900f7=layout/notification_template_right_icon\n010900f8=layout/notification_template_smart_reply_container\n010900f9=layout/notification_template_text\n010900fa=layout/notification_template_text_multiline\n010900fb=layout/notification_top_line_views\n010900fc=layout/number_picker\n010900fd=layout/number_picker_material\n010900fe=layout/number_picker_with_selector_wheel\n010900ff=layout/overlay_display_window\n01090100=layout/permissions_account_and_authtokentype\n01090101=layout/permissions_package_list_item\n01090102=layout/platlogo_layout\n01090103=layout/popup_menu_header_item_layout\n01090104=layout/popup_menu_item_layout\n01090105=layout/popup_menu_item_layout_material\n01090106=layout/power_dialog\n01090107=layout/preference\n01090108=layout/preference_category_holo\n01090109=layout/preference_category_material\n0109010a=layout/preference_child\n0109010b=layout/preference_child_holo\n0109010c=layout/preference_child_material\n0109010d=layout/preference_dialog_edittext\n0109010e=layout/preference_dialog_edittext_material\n0109010f=layout/preference_dialog_seekbar\n01090110=layout/preference_dialog_seekbar_material\n01090111=layout/preference_header_item\n01090112=layout/preference_header_item_material\n01090113=layout/preference_holo\n01090114=layout/preference_information\n01090115=layout/preference_information_holo\n01090116=layout/preference_information_material\n01090117=layout/preference_list_content\n01090118=layout/preference_list_content_material\n01090119=layout/preference_list_content_single\n0109011a=layout/preference_list_fragment\n0109011b=layout/preference_list_fragment_material\n0109011c=layout/preference_material\n0109011d=layout/preference_widget_checkbox\n0109011e=layout/preference_widget_seekbar\n0109011f=layout/preference_widget_seekbar_material\n01090120=layout/preference_widget_switch\n01090121=layout/preferences\n01090122=layout/progress_dialog\n01090123=layout/progress_dialog_holo\n01090124=layout/progress_dialog_material\n01090125=layout/recent_apps_dialog\n01090126=layout/recent_apps_icon\n01090127=layout/remote_views_adapter_default_loading_view\n01090128=layout/resolve_grid_item\n01090129=layout/resolve_list_item\n0109012a=layout/resolver_different_item_header\n0109012b=layout/resolver_empty_states\n0109012c=layout/resolver_list\n0109012d=layout/resolver_list_per_profile\n0109012e=layout/resolver_list_with_default\n0109012f=layout/resolver_profile_tab_button\n01090130=layout/restrictions_pin_challenge\n01090131=layout/restrictions_pin_setup\n01090132=layout/safe_mode\n01090133=layout/screen\n01090134=layout/screen_action_bar\n01090135=layout/screen_custom_title\n01090136=layout/screen_progress\n01090137=layout/screen_simple\n01090138=layout/screen_simple_overlay_action_mode\n01090139=layout/screen_title\n0109013a=layout/screen_title_icons\n0109013b=layout/screen_toolbar\n0109013c=layout/search_bar\n0109013d=layout/search_dropdown_item_icons_2line\n0109013e=layout/search_view\n0109013f=layout/select_dialog\n01090140=layout/select_dialog_holo\n01090141=layout/select_dialog_item_holo\n01090142=layout/select_dialog_item_material\n01090143=layout/select_dialog_material\n01090144=layout/select_dialog_multichoice_holo\n01090145=layout/select_dialog_multichoice_material\n01090146=layout/select_dialog_singlechoice_holo\n01090147=layout/select_dialog_singlechoice_material\n01090148=layout/shutdown_dialog\n01090149=layout/side_fps_toast\n0109014a=layout/simple_account_item\n0109014b=layout/simple_dropdown_hint\n0109014c=layout/simple_dropdown_item_2line\n0109014d=layout/simple_list_item_2_single_choice\n0109014e=layout/slice_grid\n0109014f=layout/slice_message\n01090150=layout/slice_message_local\n01090151=layout/slice_remote_input\n01090152=layout/slice_secondary_text\n01090153=layout/slice_small_template\n01090154=layout/slice_title\n01090155=layout/sms_short_code_confirmation_dialog\n01090156=layout/splash_screen_view\n01090157=layout/ssl_certificate\n01090158=layout/status_bar_latest_event_content\n01090159=layout/subscription_item_layout\n0109015a=layout/system_user_home\n0109015b=layout/tab_content\n0109015c=layout/tab_indicator\n0109015d=layout/tab_indicator_holo\n0109015e=layout/tab_indicator_material\n0109015f=layout/tab_indicator_resolver\n01090160=layout/text_drag_thumbnail\n01090161=layout/text_edit_action_popup_text\n01090162=layout/text_edit_no_paste_window\n01090163=layout/text_edit_paste_window\n01090164=layout/text_edit_side_no_paste_window\n01090165=layout/text_edit_side_paste_window\n01090166=layout/text_edit_suggestion_container\n01090167=layout/text_edit_suggestion_container_material\n01090168=layout/text_edit_suggestion_item\n01090169=layout/text_edit_suggestion_item_material\n0109016a=layout/text_edit_suggestions_window\n0109016b=layout/textview_hint\n0109016c=layout/thumbnail_background_view\n0109016d=layout/time_picker_dialog\n0109016e=layout/time_picker_header_material\n0109016f=layout/time_picker_legacy\n01090170=layout/time_picker_legacy_material\n01090171=layout/time_picker_material\n01090172=layout/time_picker_text_input_material\n01090173=layout/tooltip\n01090174=layout/transient_notification\n01090175=layout/transient_notification_with_icon\n01090176=layout/twelve_key_entry\n01090177=layout/typing_filter\n01090178=layout/unsupported_compile_sdk_dialog_content\n01090179=layout/unsupported_display_size_dialog_content\n0109017a=layout/user_switching_dialog\n0109017b=layout/voice_interaction_session\n0109017c=layout/watch_base_error_dialog\n0109017d=layout/watch_base_error_dialog_title\n0109017e=layout/web_runtime\n0109017f=layout/web_text_view_dropdown\n01090180=layout/webview_find\n01090181=layout/webview_select_singlechoice\n01090182=layout/work_widget_mask_view\n01090183=layout/year_label_text_view\n01090184=layout/zoom_browser_accessory_buttons\n01090185=layout/zoom_container\n01090186=layout/zoom_controls\n01090187=layout/zoom_magnify\n010a0000=anim/fade_in\n010a0001=anim/fade_out\n010a0002=anim/slide_in_left\n010a0003=anim/slide_out_right\n010a0004=anim/accelerate_decelerate_interpolator\n010a0005=anim/accelerate_interpolator\n010a0006=anim/decelerate_interpolator\n010a0007=anim/anticipate_interpolator\n010a0008=anim/overshoot_interpolator\n010a0009=anim/anticipate_overshoot_interpolator\n010a000a=anim/bounce_interpolator\n010a000b=anim/linear_interpolator\n010a000c=anim/cycle_interpolator\n010a000d=anim/activity_close_enter\n010a000e=anim/activity_close_exit\n010a000f=anim/activity_open_enter\n010a0010=anim/activity_open_exit\n010a0011=anim/activity_translucent_close_exit\n010a0012=anim/activity_translucent_open_enter\n010a0013=anim/app_starting_exit\n010a0014=anim/btn_checkbox_to_checked_box_inner_merged_animation\n010a0015=anim/btn_checkbox_to_checked_box_outer_merged_animation\n010a0016=anim/btn_checkbox_to_checked_icon_null_animation\n010a0017=anim/btn_checkbox_to_unchecked_box_inner_merged_animation\n010a0018=anim/btn_checkbox_to_unchecked_check_path_merged_animation\n010a0019=anim/btn_checkbox_to_unchecked_icon_null_animation\n010a001a=anim/btn_radio_to_off_mtrl_dot_group_animation\n010a001b=anim/btn_radio_to_off_mtrl_ring_outer_animation\n010a001c=anim/btn_radio_to_off_mtrl_ring_outer_path_animation\n010a001d=anim/btn_radio_to_on_mtrl_dot_group_animation\n010a001e=anim/btn_radio_to_on_mtrl_ring_outer_animation\n010a001f=anim/btn_radio_to_on_mtrl_ring_outer_path_animation\n010a0020=anim/button_state_list_anim_material\n010a0021=anim/cross_profile_apps_thumbnail_enter\n010a0022=anim/date_picker_fade_in_material\n010a0023=anim/date_picker_fade_out_material\n010a0024=anim/dialog_enter\n010a0025=anim/dialog_exit\n010a0026=anim/dream_activity_close_exit\n010a0027=anim/dream_activity_open_enter\n010a0028=anim/dream_activity_open_exit\n010a0029=anim/fast_fade_in\n010a002a=anim/fast_fade_out\n010a002b=anim/flat_button_state_list_anim_material\n010a002c=anim/ft_avd_toarrow_rectangle_1_animation\n010a002d=anim/ft_avd_toarrow_rectangle_1_pivot_0_animation\n010a002e=anim/ft_avd_toarrow_rectangle_1_pivot_animation\n010a002f=anim/ft_avd_toarrow_rectangle_2_animation\n010a0030=anim/ft_avd_toarrow_rectangle_2_pivot_0_animation\n010a0031=anim/ft_avd_toarrow_rectangle_2_pivot_animation\n010a0032=anim/ft_avd_toarrow_rectangle_3_animation\n010a0033=anim/ft_avd_toarrow_rectangle_3_pivot_0_animation\n010a0034=anim/ft_avd_toarrow_rectangle_3_pivot_animation\n010a0035=anim/ft_avd_toarrow_rectangle_4_animation\n010a0036=anim/ft_avd_toarrow_rectangle_5_animation\n010a0037=anim/ft_avd_toarrow_rectangle_6_animation\n010a0038=anim/ft_avd_toarrow_rectangle_path_1_animation\n010a0039=anim/ft_avd_toarrow_rectangle_path_2_animation\n010a003a=anim/ft_avd_toarrow_rectangle_path_3_animation\n010a003b=anim/ft_avd_toarrow_rectangle_path_4_animation\n010a003c=anim/ft_avd_toarrow_rectangle_path_5_animation\n010a003d=anim/ft_avd_toarrow_rectangle_path_6_animation\n010a003e=anim/ft_avd_tooverflow_rectangle_1_animation\n010a003f=anim/ft_avd_tooverflow_rectangle_1_pivot_animation\n010a0040=anim/ft_avd_tooverflow_rectangle_2_animation\n010a0041=anim/ft_avd_tooverflow_rectangle_2_pivot_animation\n010a0042=anim/ft_avd_tooverflow_rectangle_3_animation\n010a0043=anim/ft_avd_tooverflow_rectangle_3_pivot_animation\n010a0044=anim/ft_avd_tooverflow_rectangle_path_1_animation\n010a0045=anim/ft_avd_tooverflow_rectangle_path_2_animation\n010a0046=anim/ft_avd_tooverflow_rectangle_path_3_animation\n010a0047=anim/grow_fade_in\n010a0048=anim/grow_fade_in_center\n010a0049=anim/grow_fade_in_from_bottom\n010a004a=anim/ic_bluetooth_transient_animation_0\n010a004b=anim/ic_bluetooth_transient_animation_1\n010a004c=anim/ic_bluetooth_transient_animation_2\n010a004d=anim/ic_hotspot_transient_animation_0\n010a004e=anim/ic_hotspot_transient_animation_1\n010a004f=anim/ic_hotspot_transient_animation_2\n010a0050=anim/ic_hotspot_transient_animation_3\n010a0051=anim/ic_signal_wifi_transient_animation_0\n010a0052=anim/ic_signal_wifi_transient_animation_1\n010a0053=anim/ic_signal_wifi_transient_animation_2\n010a0054=anim/ic_signal_wifi_transient_animation_3\n010a0055=anim/ic_signal_wifi_transient_animation_4\n010a0056=anim/ic_signal_wifi_transient_animation_5\n010a0057=anim/ic_signal_wifi_transient_animation_6\n010a0058=anim/ic_signal_wifi_transient_animation_7\n010a0059=anim/ic_signal_wifi_transient_animation_8\n010a005a=anim/input_method_enter\n010a005b=anim/input_method_exit\n010a005c=anim/input_method_extract_enter\n010a005d=anim/input_method_extract_exit\n010a005e=anim/input_method_fancy_enter\n010a005f=anim/input_method_fancy_exit\n010a0060=anim/launch_task_behind_source\n010a0061=anim/launch_task_behind_target\n010a0062=anim/lock_screen_behind_enter\n010a0063=anim/lock_screen_behind_enter_fade_in\n010a0064=anim/lock_screen_behind_enter_subtle\n010a0065=anim/lock_screen_behind_enter_wallpaper\n010a0066=anim/lock_screen_enter\n010a0067=anim/lock_screen_exit\n010a0068=anim/lock_screen_wallpaper_exit\n010a0069=anim/options_panel_enter\n010a006a=anim/options_panel_exit\n010a006b=anim/overlay_task_fragment_change\n010a006c=anim/overlay_task_fragment_close_to_bottom\n010a006d=anim/overlay_task_fragment_close_to_left\n010a006e=anim/overlay_task_fragment_close_to_right\n010a006f=anim/overlay_task_fragment_close_to_top\n010a0070=anim/overlay_task_fragment_open_from_bottom\n010a0071=anim/overlay_task_fragment_open_from_left\n010a0072=anim/overlay_task_fragment_open_from_right\n010a0073=anim/overlay_task_fragment_open_from_top\n010a0074=anim/popup_enter_material\n010a0075=anim/popup_exit_material\n010a0076=anim/progress_indeterminate_horizontal_rect1\n010a0077=anim/progress_indeterminate_horizontal_rect2\n010a0078=anim/progress_indeterminate_material\n010a0079=anim/progress_indeterminate_rotation_material\n010a007a=anim/push_down_in\n010a007b=anim/push_down_in_no_alpha\n010a007c=anim/push_down_out\n010a007d=anim/push_down_out_no_alpha\n010a007e=anim/push_up_in\n010a007f=anim/push_up_out\n010a0080=anim/recent_enter\n010a0081=anim/recent_exit\n010a0082=anim/recents_fade_in\n010a0083=anim/recents_fade_out\n010a0084=anim/resolver_close_anim\n010a0085=anim/resolver_launch_anim\n010a0086=anim/rotation_animation_enter\n010a0087=anim/rotation_animation_jump_exit\n010a0088=anim/rotation_animation_xfade_exit\n010a0089=anim/rounded_window_enter\n010a008a=anim/rounded_window_exit\n010a008b=anim/screen_rotate_0_enter\n010a008c=anim/screen_rotate_0_exit\n010a008d=anim/screen_rotate_180_enter\n010a008e=anim/screen_rotate_180_exit\n010a008f=anim/screen_rotate_180_frame\n010a0090=anim/screen_rotate_alpha\n010a0091=anim/screen_rotate_finish_enter\n010a0092=anim/screen_rotate_finish_exit\n010a0093=anim/screen_rotate_finish_frame\n010a0094=anim/screen_rotate_minus_90_enter\n010a0095=anim/screen_rotate_minus_90_exit\n010a0096=anim/screen_rotate_plus_90_enter\n010a0097=anim/screen_rotate_plus_90_exit\n010a0098=anim/screen_rotate_start_enter\n010a0099=anim/screen_rotate_start_exit\n010a009a=anim/screen_rotate_start_frame\n010a009b=anim/screen_user_enter\n010a009c=anim/screen_user_exit\n010a009d=anim/search_bar_enter\n010a009e=anim/search_bar_exit\n010a009f=anim/seekbar_thumb_pressed_to_unpressed_thumb_animation\n010a00a0=anim/seekbar_thumb_unpressed_to_pressed_thumb_0_animation\n010a00a1=anim/shrink_fade_out\n010a00a2=anim/shrink_fade_out_center\n010a00a3=anim/shrink_fade_out_from_bottom\n010a00a4=anim/slide_in_child_bottom\n010a00a5=anim/slide_in_enter_micro\n010a00a6=anim/slide_in_exit_micro\n010a00a7=anim/slide_in_right\n010a00a8=anim/slide_in_up\n010a00a9=anim/slide_out_down\n010a00aa=anim/slide_out_left\n010a00ab=anim/slide_out_micro\n010a00ac=anim/slow_fade_in\n010a00ad=anim/submenu_enter\n010a00ae=anim/submenu_exit\n010a00af=anim/swipe_window_enter\n010a00b0=anim/swipe_window_exit\n010a00b1=anim/task_close_enter\n010a00b2=anim/task_close_exit\n010a00b3=anim/task_fragment_clear_top_close_enter\n010a00b4=anim/task_fragment_clear_top_close_exit\n010a00b5=anim/task_fragment_clear_top_open_enter\n010a00b6=anim/task_fragment_clear_top_open_exit\n010a00b7=anim/task_fragment_close_enter\n010a00b8=anim/task_fragment_close_exit\n010a00b9=anim/task_fragment_open_enter\n010a00ba=anim/task_fragment_open_exit\n010a00bb=anim/task_open_enter\n010a00bc=anim/task_open_enter_cross_profile_apps\n010a00bd=anim/task_open_exit\n010a00be=anim/toast_enter\n010a00bf=anim/toast_exit\n010a00c0=anim/tooltip_enter\n010a00c1=anim/tooltip_exit\n010a00c2=anim/translucent_enter\n010a00c3=anim/translucent_exit\n010a00c4=anim/voice_activity_close_enter\n010a00c5=anim/voice_activity_close_exit\n010a00c6=anim/voice_activity_open_enter\n010a00c7=anim/voice_activity_open_exit\n010a00c8=anim/voice_layer_enter\n010a00c9=anim/voice_layer_exit\n010a00ca=anim/wallpaper_close_enter\n010a00cb=anim/wallpaper_close_exit\n010a00cc=anim/wallpaper_enter\n010a00cd=anim/wallpaper_exit\n010a00ce=anim/wallpaper_intra_close_enter\n010a00cf=anim/wallpaper_intra_close_exit\n010a00d0=anim/wallpaper_intra_open_enter\n010a00d1=anim/wallpaper_intra_open_exit\n010a00d2=anim/wallpaper_open_enter\n010a00d3=anim/wallpaper_open_exit\n010a00d4=anim/window_move_from_decor\n010a00d5=anim/grow_fade_in_center\n010a00d6=anim/grow_fade_in_from_bottom\n010a00d7=anim/ic_bluetooth_transient_animation_0\n010a00d8=anim/ic_bluetooth_transient_animation_1\n010a00d9=anim/ic_bluetooth_transient_animation_2\n010a00da=anim/ic_hotspot_transient_animation_0\n010a00db=anim/ic_hotspot_transient_animation_1\n010a00dc=anim/ic_hotspot_transient_animation_2\n010a00dd=anim/ic_hotspot_transient_animation_3\n010a00de=anim/ic_signal_wifi_transient_animation_0\n010a00df=anim/ic_signal_wifi_transient_animation_1\n010a00e0=anim/ic_signal_wifi_transient_animation_2\n010a00e1=anim/ic_signal_wifi_transient_animation_3\n010a00e2=anim/ic_signal_wifi_transient_animation_4\n010a00e3=anim/ic_signal_wifi_transient_animation_5\n010a00e4=anim/ic_signal_wifi_transient_animation_6\n010a00e5=anim/ic_signal_wifi_transient_animation_7\n010a00e6=anim/ic_signal_wifi_transient_animation_8\n010a00e7=anim/input_method_enter\n010a00e8=anim/input_method_exit\n010a00e9=anim/input_method_extract_enter\n010a00ea=anim/input_method_extract_exit\n010a00eb=anim/input_method_fancy_enter\n010a00ec=anim/input_method_fancy_exit\n010a00ed=anim/launch_task_behind_source\n010a00ee=anim/launch_task_behind_target\n010a00ef=anim/lock_in\n010a00f0=anim/lock_lock\n010a00f1=anim/lock_scanning\n010a00f2=anim/lock_screen_behind_enter\n010a00f3=anim/lock_screen_behind_enter_fade_in\n010a00f4=anim/lock_screen_behind_enter_wallpaper\n010a00f5=anim/lock_screen_enter\n010a00f6=anim/lock_screen_exit\n010a00f7=anim/lock_screen_wallpaper_exit\n010a00f8=anim/lock_to_error\n010a00f9=anim/lock_unlock\n010a00fa=anim/options_panel_enter\n010a00fb=anim/options_panel_exit\n010a00fc=anim/popup_enter_material\n010a00fd=anim/popup_exit_material\n010a00fe=anim/progress_indeterminate_horizontal_rect1\n010a00ff=anim/progress_indeterminate_horizontal_rect2\n010a0100=anim/progress_indeterminate_material\n010a0101=anim/progress_indeterminate_rotation_material\n010a0102=anim/push_down_in\n010a0103=anim/push_down_in_no_alpha\n010a0104=anim/push_down_out\n010a0105=anim/push_down_out_no_alpha\n010a0106=anim/push_up_in\n010a0107=anim/push_up_out\n010a0108=anim/recent_enter\n010a0109=anim/recent_exit\n010a010a=anim/recents_fade_in\n010a010b=anim/recents_fade_out\n010a010c=anim/resolver_close_anim\n010a010d=anim/resolver_launch_anim\n010a010e=anim/rotation_animation_enter\n010a010f=anim/rotation_animation_jump_exit\n010a0110=anim/rotation_animation_xfade_exit\n010a0111=anim/screen_rotate_0_enter\n010a0112=anim/screen_rotate_0_exit\n010a0113=anim/screen_rotate_0_frame\n010a0114=anim/screen_rotate_180_enter\n010a0115=anim/screen_rotate_180_exit\n010a0116=anim/screen_rotate_180_frame\n010a0117=anim/screen_rotate_finish_enter\n010a0118=anim/screen_rotate_finish_exit\n010a0119=anim/screen_rotate_finish_frame\n010a011a=anim/screen_rotate_minus_90_enter\n010a011b=anim/screen_rotate_minus_90_exit\n010a011c=anim/screen_rotate_minus_90_frame\n010a011d=anim/screen_rotate_plus_90_enter\n010a011e=anim/screen_rotate_plus_90_exit\n010a011f=anim/screen_rotate_plus_90_frame\n010a0120=anim/screen_rotate_start_enter\n010a0121=anim/screen_rotate_start_exit\n010a0122=anim/screen_rotate_start_frame\n010a0123=anim/screen_user_enter\n010a0124=anim/screen_user_exit\n010a0125=anim/search_bar_enter\n010a0126=anim/search_bar_exit\n010a0127=anim/seekbar_thumb_pressed_to_unpressed_thumb_animation\n010a0128=anim/seekbar_thumb_unpressed_to_pressed_thumb_0_animation\n010a0129=anim/shrink_fade_out\n010a012a=anim/shrink_fade_out_center\n010a012b=anim/shrink_fade_out_from_bottom\n010a012c=anim/slide_in_child_bottom\n010a012d=anim/slide_in_enter_micro\n010a012e=anim/slide_in_exit_micro\n010a012f=anim/slide_in_right\n010a0130=anim/slide_in_up\n010a0131=anim/slide_out_down\n010a0132=anim/slide_out_left\n010a0133=anim/slide_out_micro\n010a0134=anim/slow_fade_in\n010a0135=anim/submenu_enter\n010a0136=anim/submenu_exit\n010a0137=anim/swipe_window_enter\n010a0138=anim/swipe_window_exit\n010a0139=anim/task_close_enter\n010a013a=anim/task_close_exit\n010a013b=anim/task_open_enter\n010a013c=anim/task_open_enter_cross_profile_apps\n010a013d=anim/task_open_exit\n010a013e=anim/toast_enter\n010a013f=anim/toast_exit\n010a0140=anim/tooltip_enter\n010a0141=anim/tooltip_exit\n010a0142=anim/translucent_enter\n010a0143=anim/translucent_exit\n010a0144=anim/voice_activity_close_enter\n010a0145=anim/voice_activity_close_exit\n010a0146=anim/voice_activity_open_enter\n010a0147=anim/voice_activity_open_exit\n010a0148=anim/voice_layer_enter\n010a0149=anim/voice_layer_exit\n010a014a=anim/wallpaper_close_enter\n010a014b=anim/wallpaper_close_exit\n010a014c=anim/wallpaper_enter\n010a014d=anim/wallpaper_exit\n010a014e=anim/wallpaper_intra_close_enter\n010a014f=anim/wallpaper_intra_close_exit\n010a0150=anim/wallpaper_intra_open_enter\n010a0151=anim/wallpaper_intra_open_exit\n010a0152=anim/wallpaper_open_enter\n010a0153=anim/wallpaper_open_exit\n010a0154=anim/window_move_from_decor\n010b0000=animator/fade_in\n010b0001=animator/fade_out\n010b0002=animator/fragment_close_enter\n010b0003=animator/fragment_close_exit\n010b0004=animator/fragment_fade_enter\n010b0005=animator/fragment_fade_exit\n010b0006=animator/fragment_open_enter\n010b0007=animator/fragment_open_exit\n010b0008=animator/leanback_setup_fragment_close_enter\n010b0009=animator/leanback_setup_fragment_close_exit\n010b000a=animator/leanback_setup_fragment_open_enter\n010b000b=animator/leanback_setup_fragment_open_exit\n010b000c=xml/time_zones_by_country\n010c0000=interpolator/accelerate_quad\n010c0001=interpolator/decelerate_quad\n010c0002=interpolator/accelerate_cubic\n010c0003=interpolator/decelerate_cubic\n010c0004=interpolator/accelerate_quint\n010c0005=interpolator/decelerate_quint\n010c0006=interpolator/accelerate_decelerate\n010c0007=interpolator/anticipate\n010c0008=interpolator/overshoot\n010c0009=interpolator/anticipate_overshoot\n010c000a=interpolator/bounce\n010c000b=interpolator/linear\n010c000c=interpolator/cycle\n010c000d=interpolator/fast_out_slow_in\n010c000e=interpolator/linear_out_slow_in\n010c000f=interpolator/fast_out_linear_in\n010c0010=interpolator/accelerate_quart\n010c0011=interpolator/activity_close_dim\n010c0012=interpolator/aggressive_ease\n010c0013=interpolator/btn_checkbox_checked_mtrl_animation_interpolator_0\n010c0014=interpolator/btn_checkbox_checked_mtrl_animation_interpolator_1\n010c0015=interpolator/btn_checkbox_unchecked_mtrl_animation_interpolator_0\n010c0016=interpolator/btn_checkbox_unchecked_mtrl_animation_interpolator_1\n010c0017=interpolator/btn_radio_to_off_mtrl_animation_interpolator_0\n010c0018=interpolator/btn_radio_to_on_mtrl_animation_interpolator_0\n010c0019=interpolator/decelerate_quart\n010c001a=interpolator/fast_out_extra_slow_in\n010c001b=interpolator/emphasized\n010c001c=interpolator/emphasized_accelerate\n010c001d=interpolator/emphasized_decelerate\n010c001e=interpolator/ft_avd_toarrow_animation_interpolator_0\n010c001f=interpolator/ft_avd_toarrow_animation_interpolator_1\n010c0020=interpolator/ft_avd_toarrow_animation_interpolator_2\n010c0021=interpolator/ft_avd_toarrow_animation_interpolator_3\n010c0022=interpolator/ft_avd_toarrow_animation_interpolator_4\n010c0023=interpolator/ft_avd_toarrow_animation_interpolator_5\n010c0024=interpolator/ft_avd_toarrow_animation_interpolator_6\n010c0025=interpolator/launch_task_behind_source_scale_1\n010c0026=interpolator/launch_task_behind_source_scale_2\n010c0027=interpolator/launch_task_behind_target_ydelta\n010c0028=interpolator/launch_task_micro_alpha\n010c0029=interpolator/launch_task_micro_ydelta\n010c002a=interpolator/progress_indeterminate_horizontal_rect1_scalex\n010c002b=interpolator/progress_indeterminate_horizontal_rect1_translatex\n010c002c=interpolator/progress_indeterminate_horizontal_rect2_scalex\n010c002d=interpolator/progress_indeterminate_horizontal_rect2_translatex\n010c002e=interpolator/progress_indeterminate_rotation_interpolator\n010c002f=interpolator/rounded_window_interpolator\n010c0030=interpolator/screen_rotation\n010c0031=interpolator/screen_rotation_alpha_in\n010c0032=interpolator/screen_rotation_alpha_out\n010c0033=interpolator/standard\n010c0034=interpolator/standard_accelerate\n010c0035=interpolator/standard_decelerate\n010c0036=interpolator/transient_interpolator\n010c0037=interpolator/trim_end_interpolator\n010c0038=interpolator/trim_offset_interpolator\n010c0039=interpolator/trim_start_interpolator\n010d0000=mipmap/sym_def_app_icon\n010d0001=mipmap/sym_def_app_icon_foreground\n010d0002=mipmap/sym_def_app_icon_maskable\n010d0003=mipmap/sym_def_app_icon_maskable\n010d0004=bool/config_bypass_keyguard_if_slider_open\n010d0005=bool/config_automatic_brightness_available\n010d0006=bool/config_annoy_dianne\n010d0007=bool/config_unplugTurnsOnScreen\n010d0008=bool/config_animateScreenLights\n010d0009=bool/config_deskDockEnablesAccelerometer\n010d000a=bool/config_carDockEnablesAccelerometer\n010d000b=bool/config_batterySdCardAccessibility\n010d000c=bool/config_use_strict_phone_number_comparation\n010d000d=bool/config_disableMenuKeyInLockScreen\n010d000e=bool/config_swipeDisambiguation\n010d000f=bool/config_filterTouchEvents\n010d0010=bool/config_filterJumpyTouchEvents\n010d0011=bool/config_bluetooth_sco_off_call\n010d0012=bool/config_sip_wifi_only\n010d0013=bool/skip_restoring_network_selection\n010d0014=bool/lockscreen_isPortrait\n010e0000=integer/config_shortAnimTime\n010e0001=integer/config_mediumAnimTime\n010e0002=integer/config_longAnimTime\n010e0003=integer/status_bar_notification_info_maxnum\n010e0004=integer/auto_data_switch_availability_stability_time_threshold_millis\n010e0005=integer/auto_data_switch_availability_switchback_stability_time_threshold_millis\n010e0006=integer/auto_data_switch_performance_stability_time_threshold_millis\n010e0007=integer/auto_data_switch_score_tolerance\n010e0008=integer/auto_data_switch_validation_max_retry\n010e0009=integer/autofill_max_visible_datasets\n010e000a=integer/bugreport_state_hide\n010e000b=integer/bugreport_state_show\n010e000c=integer/bugreport_state_unknown\n010e000d=integer/button_pressed_animation_delay\n010e000e=integer/button_pressed_animation_duration\n010e000f=integer/config_MaxConcurrentDownloadsAllowed\n010e0010=integer/config_accessibilityColorMode\n010e0011=integer/config_accumulatedBatteryUsageStatsSpanSize\n010e0012=integer/config_activeTaskDurationHours\n010e0013=integer/config_activityDefaultDur\n010e0014=integer/config_activityShortDur\n010e0015=integer/config_aggregatedPowerStatsSpanDuration\n010e0016=integer/config_alertDialogController\n010e0017=integer/config_allowedUnprivilegedKeepalivePerUid\n010e0018=integer/config_am_tieredCachedAdjUiTierSize\n010e0019=integer/config_app_exit_info_history_list_size\n010e001a=integer/config_attentionMaximumExtension\n010e001b=integer/config_attentiveTimeout\n010e001c=integer/config_attentiveWarningDuration\n010e001d=integer/config_audio_alarm_min_vol\n010e001e=integer/config_audio_notif_vol_default\n010e001f=integer/config_audio_notif_vol_steps\n010e0020=integer/config_audio_ring_vol_default\n010e0021=integer/config_audio_ring_vol_steps\n010e0022=integer/config_autoBrightnessBrighteningLightDebounce\n010e0023=integer/config_autoBrightnessDarkeningLightDebounce\n010e0024=integer/config_autoBrightnessInitialLightSensorRate\n010e0025=integer/config_autoBrightnessLightSensorRate\n010e0026=integer/config_autoBrightnessShortTermModelTimeout\n010e0027=integer/config_autoGroupAtCount\n010e0028=integer/config_autoPowerModeAnyMotionSensor\n010e0029=integer/config_autoPowerModeThresholdAngle\n010e002a=integer/config_backgroundUserScheduledStopTimeSecs\n010e002b=integer/config_batteryHistoryStorageSize\n010e002c=integer/config_batterySaver_full_locationMode\n010e002d=integer/config_batterySaver_full_soundTriggerMode\n010e002e=integer/config_bg_current_drain_exempted_types\n010e002f=integer/config_bg_current_drain_location_min_duration\n010e0030=integer/config_bg_current_drain_media_playback_min_duration\n010e0031=integer/config_bg_current_drain_power_components\n010e0032=integer/config_bg_current_drain_types_to_bg_restricted\n010e0033=integer/config_bg_current_drain_types_to_restricted_bucket\n010e0034=integer/config_bg_current_drain_window\n010e0035=integer/config_bluetooth_idle_cur_ma\n010e0036=integer/config_bluetooth_operating_voltage_mv\n010e0037=integer/config_bluetooth_rx_cur_ma\n010e0038=integer/config_bluetooth_tx_cur_ma\n010e0039=integer/config_brightness_ramp_rate_fast\n010e003a=integer/config_brightness_ramp_rate_slow\n010e003b=integer/config_burnInProtectionMaxHorizontalOffset\n010e003c=integer/config_burnInProtectionMaxRadius\n010e003d=integer/config_burnInProtectionMaxVerticalOffset\n010e003e=integer/config_burnInProtectionMinHorizontalOffset\n010e003f=integer/config_burnInProtectionMinVerticalOffset\n010e0040=integer/config_cameraLaunchGestureSensorType\n010e0041=integer/config_cameraLiftTriggerSensorType\n010e0042=integer/config_cameraPrivacyLightAlsAveragingIntervalMillis\n010e0043=integer/config_carDockKeepsScreenOn\n010e0044=integer/config_carDockRotation\n010e0045=integer/config_cdma_3waycall_flash_delay\n010e0046=integer/config_chooser_max_targets_per_row\n010e0047=integer/config_criticalBatteryWarningLevel\n010e0048=integer/config_cursorWindowSize\n010e0049=integer/config_customizedMaxCachedProcesses\n010e004a=integer/config_datagram_wait_for_connected_state_for_last_message_timeout_millis\n010e004b=integer/config_datagram_wait_for_connected_state_timeout_millis\n010e004c=integer/config_datause_notification_type\n010e004d=integer/config_datause_polling_period_sec\n010e004e=integer/config_datause_threshold_bytes\n010e004f=integer/config_datause_throttle_kbitsps\n010e0050=integer/config_debugSystemServerPssThresholdBytes\n010e0051=integer/config_defaultActionModeHideDurationMillis\n010e0052=integer/config_defaultAlarmVibrationIntensity\n010e0053=integer/config_defaultAnalogClockSecondsHandFps\n010e0054=integer/config_defaultBinderHeavyHitterAutoSamplerBatchSize\n010e0055=integer/config_defaultBinderHeavyHitterWatcherBatchSize\n010e0056=integer/config_defaultDisplayDefaultColorMode\n010e0057=integer/config_defaultHapticFeedbackIntensity\n010e0058=integer/config_defaultKeyboardVibrationIntensity\n010e0059=integer/config_defaultMaxDurationBetweenUndimsMillis\n010e005a=integer/config_defaultMediaVibrationIntensity\n010e005b=integer/config_defaultMinEmergencyGestureTapDurationMillis\n010e005c=integer/config_defaultNightDisplayAutoMode\n010e005d=integer/config_defaultNightDisplayCustomEndTime\n010e005e=integer/config_defaultNightDisplayCustomStartTime\n010e005f=integer/config_defaultNightMode\n010e0060=integer/config_defaultNotificationLedOff\n010e0061=integer/config_defaultNotificationLedOn\n010e0062=integer/config_defaultNotificationVibrationIntensity\n010e0063=integer/config_defaultPeakRefreshRate\n010e0064=integer/config_defaultPictureInPictureGravity\n010e0065=integer/config_defaultPreventScreenTimeoutForMillis\n010e0066=integer/config_defaultRefreshRate\n010e0067=integer/config_defaultRefreshRateInHbmHdr\n010e0068=integer/config_defaultRefreshRateInHbmSunlight\n010e0069=integer/config_defaultRefreshRateInZone\n010e006a=integer/config_defaultRingVibrationIntensity\n010e006b=integer/config_defaultUiModeType\n010e006c=integer/config_defaultUndimsRequired\n010e006d=integer/config_defaultVibrationAmplitude\n010e006e=integer/config_default_cellular_usage_setting\n010e006f=integer/config_delay_for_ims_dereg_millis\n010e0070=integer/config_demo_pointing_aligned_duration_millis\n010e0071=integer/config_demo_pointing_not_aligned_duration_millis\n010e0072=integer/config_deskDockKeepsScreenOn\n010e0073=integer/config_deskDockRotation\n010e0074=integer/config_deviceStateConcurrentRearDisplay\n010e0075=integer/config_deviceStateRearDisplay\n010e0076=integer/config_displayWhiteBalanceBrightnessFilterHorizon\n010e0077=integer/config_displayWhiteBalanceBrightnessSensorRate\n010e0078=integer/config_displayWhiteBalanceColorTemperatureDefault\n010e0079=integer/config_displayWhiteBalanceColorTemperatureFilterHorizon\n010e007a=integer/config_displayWhiteBalanceColorTemperatureMax\n010e007b=integer/config_displayWhiteBalanceColorTemperatureMin\n010e007c=integer/config_displayWhiteBalanceColorTemperatureSensorRate\n010e007d=integer/config_displayWhiteBalanceDecreaseDebounce\n010e007e=integer/config_displayWhiteBalanceDisplayNominalWhiteCct\n010e007f=integer/config_displayWhiteBalanceIncreaseDebounce\n010e0080=integer/config_displayWhiteBalanceTransitionTime\n010e0081=integer/config_displayWhiteBalanceTransitionTimeDecrease\n010e0082=integer/config_displayWhiteBalanceTransitionTimeIncrease\n010e0083=integer/config_dockedStackDividerSnapMode\n010e0084=integer/config_doublePressOnPowerBehavior\n010e0085=integer/config_doublePressOnStemPrimaryBehavior\n010e0086=integer/config_doubleTapMinTimeMillis\n010e0087=integer/config_doubleTapOnHomeBehavior\n010e0088=integer/config_doubleTapPowerGestureMode\n010e0089=integer/config_doubleTapPowerGestureMultiTargetDefaultAction\n010e008a=integer/config_doubleTapTimeoutMillis\n010e008b=integer/config_doublelineClockDefault\n010e008c=integer/config_downloadDataDirLowSpaceThreshold\n010e008d=integer/config_downloadDataDirSize\n010e008e=integer/config_dozeWakeLockScreenDebounce\n010e008f=integer/config_drawLockTimeoutMillis\n010e0090=integer/config_dreamCloseAnimationDuration\n010e0091=integer/config_dreamOpenAnimationDuration\n010e0092=integer/config_dreamsBatteryLevelDrainCutoff\n010e0093=integer/config_dreamsBatteryLevelMinimumWhenNotPowered\n010e0094=integer/config_dreamsBatteryLevelMinimumWhenPowered\n010e0095=integer/config_dropboxLowPriorityBroadcastRateLimitPeriod\n010e0096=integer/config_dynamicPowerSavingsDefaultDisableThreshold\n010e0097=integer/config_emergency_call_wait_for_connection_timeout_millis\n010e0098=integer/config_esim_bootstrap_data_limit_bytes\n010e0099=integer/config_externalDisplayPeakHeight\n010e009a=integer/config_externalDisplayPeakRefreshRate\n010e009b=integer/config_externalDisplayPeakWidth\n010e009c=integer/config_extraFreeKbytesAbsolute\n010e009d=integer/config_extraFreeKbytesAdjust\n010e009e=integer/config_faceMaxTemplatesPerUser\n010e009f=integer/config_fingerprintMaxTemplatesPerUser\n010e00a0=integer/config_fixedRefreshRateInHighZone\n010e00a1=integer/config_flipToScreenOffMaxLatencyMicros\n010e00a2=integer/config_globalActionsKeyTimeout\n010e00a3=integer/config_hotwordDetectedResultMaxBundleSize\n010e00a4=integer/config_hoverTapTimeoutMillis\n010e00a5=integer/config_hsumBootStrategy\n010e00a6=integer/config_immersive_mode_confirmation_panic\n010e00a7=integer/config_jobSchedulerBackgroundJobsDelay\n010e00a8=integer/config_jobSchedulerIdleWindowSlop\n010e00a9=integer/config_jobSchedulerInactivityIdleThreshold\n010e00aa=integer/config_jobSchedulerInactivityIdleThresholdOnStablePower\n010e00ab=integer/config_jobSchedulerUserGracePeriod\n010e00ac=integer/config_jumpTapTimeoutMillis\n010e00ad=integer/config_keepPreloadsMinDays\n010e00ae=integer/config_keyChordPowerVolumeUp\n010e00af=integer/config_keyboardBacklightTimeoutMs\n010e00b0=integer/config_keyguardDrawnTimeout\n010e00b1=integer/config_letterboxActivityCornersRadius\n010e00b2=integer/config_letterboxBackgroundType\n010e00b3=integer/config_letterboxDefaultPositionForBookModeReachability\n010e00b4=integer/config_letterboxDefaultPositionForHorizontalReachability\n010e00b5=integer/config_letterboxDefaultPositionForTabletopModeReachability\n010e00b6=integer/config_letterboxDefaultPositionForVerticalReachability\n010e00b7=integer/config_lidKeyboardAccessibility\n010e00b8=integer/config_lidNavigationAccessibility\n010e00b9=integer/config_lidOpenRotation\n010e00ba=integer/config_lightSensorWarmupTime\n010e00bb=integer/config_lockSoundVolumeDb\n010e00bc=integer/config_longPressOnBackBehavior\n010e00bd=integer/config_longPressOnHomeBehavior\n010e00be=integer/config_longPressOnPowerBehavior\n010e00bf=integer/config_longPressOnPowerDurationMs\n010e00c0=integer/config_longPressOnStemPrimaryBehavior\n010e00c1=integer/config_lowBatteryAutoTriggerDefaultLevel\n010e00c2=integer/config_lowBatteryCloseWarningBump\n010e00c3=integer/config_lowBatteryWarningLevel\n010e00c4=integer/config_lowMemoryKillerMinFreeKbytesAbsolute\n010e00c5=integer/config_lowMemoryKillerMinFreeKbytesAdjust\n010e00c6=integer/config_lowPowerStandbyNonInteractiveTimeout\n010e00c7=integer/config_maxDesktopWindowingActiveTasks\n010e00c8=integer/config_maxNumVisibleRecentTasks\n010e00c9=integer/config_maxNumVisibleRecentTasks_lowRam\n010e00ca=integer/config_maxResolverActivityColumns\n010e00cb=integer/config_maxScanTasksForHomeVisibility\n010e00cc=integer/config_maxShortcutTargetsPerApp\n010e00cd=integer/config_maxUiWidth\n010e00ce=integer/config_maxWearableSensingServiceConcurrentConnections\n010e00cf=integer/config_maximumCallLogEntriesPerSim\n010e00d0=integer/config_maximumScreenDimDuration\n010e00d1=integer/config_mdc_initial_max_retry\n010e00d2=integer/config_mediaOutputSwitchDialogVersion\n010e00d3=integer/config_mediaRouter_builtInSpeakerSuitability\n010e00d4=integer/config_metrics_pull_cooldown_millis\n010e00d5=integer/config_minMillisBetweenInputUserActivityEvents\n010e00d6=integer/config_minNumVisibleRecentTasks\n010e00d7=integer/config_minNumVisibleRecentTasks_lowRam\n010e00d8=integer/config_minimumScreenOffTimeout\n010e00d9=integer/config_mobile_hotspot_provision_check_period\n010e00da=integer/config_mobile_mtu\n010e00db=integer/config_motionPredictionOffsetNanos\n010e00dc=integer/config_mt_sms_polling_throttle_millis\n010e00dd=integer/config_multiuserMaxRunningUsers\n010e00de=integer/config_multiuserMaximumUsers\n010e00df=integer/config_navBarInteractionMode\n010e00e0=integer/config_navBarOpacityMode\n010e00e1=integer/config_networkAvoidBadWifi\n010e00e2=integer/config_networkDefaultDailyMultipathQuotaBytes\n010e00e3=integer/config_networkMeteredMultipathPreference\n010e00e4=integer/config_networkNotifySwitchType\n010e00e5=integer/config_networkPolicyDefaultWarning\n010e00e6=integer/config_networkWakeupPacketMark\n010e00e7=integer/config_networkWakeupPacketMask\n010e00e8=integer/config_nightDisplayColorTemperatureDefault\n010e00e9=integer/config_nightDisplayColorTemperatureMax\n010e00ea=integer/config_nightDisplayColorTemperatureMin\n010e00eb=integer/config_notificationLongTextMaxLineCount\n010e00ec=integer/config_notificationServiceArchiveSize\n010e00ed=integer/config_notificationStripRemoteViewSizeBytes\n010e00ee=integer/config_notificationWarnRemoteViewSizeBytes\n010e00ef=integer/config_notificationsBatteryFullARGB\n010e00f0=integer/config_notificationsBatteryLedOff\n010e00f1=integer/config_notificationsBatteryLedOn\n010e00f2=integer/config_notificationsBatteryLowARGB\n010e00f3=integer/config_notificationsBatteryLowBehavior\n010e00f4=integer/config_notificationsBatteryMediumARGB\n010e00f5=integer/config_notificationsBatteryNearlyFullLevel\n010e00f6=integer/config_ntpPollingInterval\n010e00f7=integer/config_ntpPollingIntervalShorter\n010e00f8=integer/config_ntpRetry\n010e00f9=integer/config_ntpTimeout\n010e00fa=integer/config_num_physical_slots\n010e00fb=integer/config_oem_enabled_satellite_location_fresh_duration\n010e00fc=integer/config_overrideHasPermanentMenuKey\n010e00fd=integer/config_pauseRotationWhenUnfolding_displaySwitchTimeout\n010e00fe=integer/config_pauseRotationWhenUnfolding_hingeEventTimeout\n010e00ff=integer/config_pauseRotationWhenUnfolding_maxHingeAngle\n010e0100=integer/config_pdp_reject_retry_delay_ms\n010e0101=integer/config_phonenumber_compare_min_match\n010e0102=integer/config_pictureInPictureMaxNumberOfActions\n010e0103=integer/config_pinnerAssistantPinBytes\n010e0104=integer/config_pinnerCameraPinBytes\n010e0105=integer/config_pinnerHomePinBytes\n010e0106=integer/config_pinnerMaxPinnedMemoryPercentage\n010e0107=integer/config_pinnerWebviewPinBytes\n010e0108=integer/config_powerStatsAggregationPeriod\n010e0109=integer/config_pressedStateDurationMillis\n010e010a=integer/config_previousVibrationsDumpAggregationTimeMillisLimit\n010e010b=integer/config_previousVibrationsDumpSizeLimit\n010e010c=integer/config_progressTimeoutFallbackHome\n010e010d=integer/config_radioScanningTimeout\n010e010e=integer/config_recentVibrationsDumpSizeLimit\n010e010f=integer/config_reduceBrightColorsStrengthDefault\n010e0110=integer/config_reduceBrightColorsStrengthMax\n010e0111=integer/config_reduceBrightColorsStrengthMin\n010e0112=integer/config_reevaluate_bootstrap_sim_data_usage_millis\n010e0113=integer/config_requestVibrationParamsTimeout\n010e0114=integer/config_reservedPrivilegedKeepaliveSlots\n010e0115=integer/config_respectsActivityMinWidthHeightMultiWindow\n010e0116=integer/config_safe_media_volume_index\n010e0117=integer/config_safe_media_volume_usb_mB\n010e0118=integer/config_satellite_delay_minutes_before_retry_validating_possible_change_in_allowed_region\n010e0119=integer/config_satellite_demo_mode_nb_iot_inactivity_timeout_millis\n010e011a=integer/config_satellite_emergency_mode_duration\n010e011b=integer/config_satellite_location_query_throttle_interval_minutes\n010e011c=integer/config_satellite_max_retry_count_for_validating_possible_change_in_allowed_region\n010e011d=integer/config_satellite_modem_image_switching_duration_millis\n010e011e=integer/config_satellite_nb_iot_inactivity_timeout_millis\n010e011f=integer/config_satellite_stay_at_listening_from_receiving_millis\n010e0120=integer/config_satellite_stay_at_listening_from_sending_millis\n010e0121=integer/config_satellite_wait_for_cellular_modem_off_timeout_millis\n010e0122=integer/config_screenBrightnessCapForWearBedtimeMode\n010e0123=integer/config_screenBrightnessDark\n010e0124=integer/config_screenBrightnessDim\n010e0125=integer/config_screenBrightnessDoze\n010e0126=integer/config_screenBrightnessSettingDefault\n010e0127=integer/config_screenBrightnessSettingMaximum\n010e0128=integer/config_screenBrightnessSettingMinimum\n010e0129=integer/config_screenTimeoutOverride\n010e012a=integer/config_screen_magnification_multi_tap_adjustment\n010e012b=integer/config_screen_rotation_color_transition\n010e012c=integer/config_screen_rotation_fade_in\n010e012d=integer/config_screen_rotation_fade_in_delay\n010e012e=integer/config_screen_rotation_fade_out\n010e012f=integer/config_screen_rotation_total_180\n010e0130=integer/config_screen_rotation_total_90\n010e0131=integer/config_screenshotChordKeyTimeout\n010e0132=integer/config_searchKeyBehavior\n010e0133=integer/config_selected_udfps_touch_detection\n010e0134=integer/config_settingsKeyBehavior\n010e0135=integer/config_shortPressOnPowerBehavior\n010e0136=integer/config_shortPressOnSleepBehavior\n010e0137=integer/config_shortPressOnStemPrimaryBehavior\n010e0138=integer/config_showOperatorNameDefault\n010e0139=integer/config_shutdownBatteryTemperature\n010e013a=integer/config_sideFpsToastTimeout\n010e013b=integer/config_sidefpsBpPowerPressWindow\n010e013c=integer/config_sidefpsKeyguardPowerPressWindow\n010e013d=integer/config_sidefpsPostAuthDowntime\n010e013e=integer/config_sidefpsSkipWaitForPowerAcquireMessage\n010e013f=integer/config_sidefpsSkipWaitForPowerVendorAcquireMessage\n010e0140=integer/config_smartSelectionInitializedTimeoutMillis\n010e0141=integer/config_smartSelectionInitializingTimeoutMillis\n010e0142=integer/config_soundEffectVolumeDb\n010e0143=integer/config_stableDeviceDisplayHeight\n010e0144=integer/config_stableDeviceDisplayWidth\n010e0145=integer/config_storageManagerDaystoRetainDefault\n010e0146=integer/config_supportsNonResizableMultiWindow\n010e0147=integer/config_tapTimeoutMillis\n010e0148=integer/config_timeDetectorAutoUpdateDiffMillis\n010e0149=integer/config_timeout_to_receive_delivered_ack_millis\n010e014a=integer/config_toastDefaultGravity\n010e014b=integer/config_tooltipAnimTime\n010e014c=integer/config_triplePressOnPowerBehavior\n010e014d=integer/config_triplePressOnStemPrimaryBehavior\n010e014e=integer/config_undockedHdmiRotation\n010e014f=integer/config_unfoldTransitionHalfFoldedTimeout\n010e0150=integer/config_userTypePackageWhitelistMode\n010e0151=integer/config_valid_wappush_index\n010e0152=integer/config_veryLongPressOnPowerBehavior\n010e0153=integer/config_veryLongPressTimeout\n010e0154=integer/config_vibrationPipelineMaxDuration\n010e0155=integer/config_vibrationWaveformRampDownDuration\n010e0156=integer/config_vibrationWaveformRampStepDuration\n010e0157=integer/config_vibratorControlServiceDumpAggregationTimeMillisLimit\n010e0158=integer/config_vibratorControlServiceDumpSizeLimit\n010e0159=integer/config_virtualDisplayLimit\n010e015a=integer/config_virtualDisplayLimitPerPackage\n010e015b=integer/config_virtualKeyQuietTimeMillis\n010e015c=integer/config_volte_replacement_rat\n010e015d=integer/config_wait_for_datagram_sending_response_for_last_message_timeout_millis\n010e015e=integer/config_wait_for_datagram_sending_response_timeout_millis\n010e015f=integer/config_wait_for_satellite_enabling_response_timeout_millis\n010e0160=integer/config_wakeUpToLastStateTimeoutMillis\n010e0161=integer/config_wallpaperFrameRateCompatibility\n010e0162=integer/config_whenToStartHubModeDefault\n010e0163=integer/config_windowOutsetBottom\n010e0164=integer/config_zen_repeat_callers_threshold\n010e0165=integer/config_zoomControlsTimeoutMillis\n010e0166=integer/date_picker_header_max_lines_material\n010e0167=integer/date_picker_mode\n010e0168=integer/date_picker_mode_material\n010e0169=integer/db_connection_pool_size\n010e016a=integer/db_default_idle_connection_timeout\n010e016b=integer/db_journal_size_limit\n010e016c=integer/db_wal_autocheckpoint\n010e016d=integer/db_wal_truncate_size\n010e016e=integer/default_data_warning_level_mb\n010e016f=integer/default_reserved_data_coding_scheme\n010e0170=integer/device_idle_flex_time_short_ms\n010e0171=integer/device_idle_idle_after_inactive_to_ms\n010e0172=integer/device_idle_idle_factor\n010e0173=integer/device_idle_idle_pending_factor\n010e0174=integer/device_idle_idle_pending_to_ms\n010e0175=integer/device_idle_idle_to_ms\n010e0176=integer/device_idle_inactive_to_ms\n010e0177=integer/device_idle_light_after_inactive_to_ms\n010e0178=integer/device_idle_light_idle_factor\n010e0179=integer/device_idle_light_idle_flex_linear_increase_factor_ms\n010e017a=integer/device_idle_light_idle_linear_increase_factor_ms\n010e017b=integer/device_idle_light_idle_maintenance_max_budget_ms\n010e017c=integer/device_idle_light_idle_maintenance_min_budget_ms\n010e017d=integer/device_idle_light_idle_to_init_flex_ms\n010e017e=integer/device_idle_light_idle_to_max_flex_ms\n010e017f=integer/device_idle_light_idle_to_ms\n010e0180=integer/device_idle_light_max_idle_to_ms\n010e0181=integer/device_idle_locating_to_ms\n010e0182=integer/device_idle_location_accuracy\n010e0183=integer/device_idle_max_idle_pending_to_ms\n010e0184=integer/device_idle_max_idle_to_ms\n010e0185=integer/device_idle_max_temp_app_allowlist_duration_ms\n010e0186=integer/device_idle_min_deep_maintenance_time_ms\n010e0187=integer/device_idle_min_light_maintenance_time_ms\n010e0188=integer/device_idle_min_time_to_alarm_ms\n010e0189=integer/device_idle_mms_temp_app_allowlist_duration_ms\n010e018a=integer/device_idle_motion_inactive_to_flex_ms\n010e018b=integer/device_idle_motion_inactive_to_ms\n010e018c=integer/device_idle_notification_allowlist_duration_ms\n010e018d=integer/device_idle_quick_doze_delay_to_ms\n010e018e=integer/device_idle_sensing_to_ms\n010e018f=integer/device_idle_sms_temp_app_allowlist_duration_ms\n010e0190=integer/disabled_alpha_animation_duration\n010e0191=integer/dock_enter_exit_duration\n010e0192=integer/kg_carousel_angle\n010e0193=integer/kg_glowpad_rotation_offset\n010e0194=integer/kg_security_flipper_weight\n010e0195=integer/kg_selector_gravity\n010e0196=integer/kg_widget_region_weight\n010e0197=integer/leanback_setup_alpha_activity_in_bkg_delay\n010e0198=integer/leanback_setup_alpha_activity_in_bkg_duration\n010e0199=integer/leanback_setup_alpha_activity_out_bkg_delay\n010e019a=integer/leanback_setup_alpha_activity_out_bkg_duration\n010e019b=integer/leanback_setup_alpha_backward_in_content_delay\n010e019c=integer/leanback_setup_alpha_backward_in_content_duration\n010e019d=integer/leanback_setup_alpha_backward_out_content_delay\n010e019e=integer/leanback_setup_alpha_backward_out_content_duration\n010e019f=integer/leanback_setup_alpha_forward_in_content_delay\n010e01a0=integer/leanback_setup_alpha_forward_in_content_duration\n010e01a1=integer/leanback_setup_alpha_forward_out_content_delay\n010e01a2=integer/leanback_setup_alpha_forward_out_content_duration\n010e01a3=integer/leanback_setup_base_animation_duration\n010e01a4=integer/leanback_setup_translation_backward_out_content_delay\n010e01a5=integer/leanback_setup_translation_backward_out_content_duration\n010e01a6=integer/leanback_setup_translation_content_cliff_v4\n010e01a7=integer/leanback_setup_translation_content_resting_point_v4\n010e01a8=integer/leanback_setup_translation_forward_in_content_delay\n010e01a9=integer/leanback_setup_translation_forward_in_content_duration\n010e01aa=integer/lock_pattern_fade_pattern_delay\n010e01ab=integer/lock_pattern_fade_pattern_duration\n010e01ac=integer/lock_pattern_line_fade_out_delay\n010e01ad=integer/lock_pattern_line_fade_out_duration\n010e01ae=integer/preference_fragment_scrollbarStyle\n010e01af=integer/preference_screen_header_scrollbarStyle\n010e01b0=integer/preferences_left_pane_weight\n010e01b1=integer/preferences_right_pane_weight\n010e01b2=integer/thumbnail_width_tv\n010e01b3=integer/time_picker_mode\n010e01b4=integer/time_picker_mode_material\n010e01b5=integer/timepicker_title_visibility\n010f0000=transition/no_transition\n010f0001=transition/move\n010f0002=transition/fade\n010f0003=transition/explode\n010f0004=transition/slide_bottom\n010f0005=transition/slide_top\n010f0006=transition/slide_right\n010f0007=transition/slide_left\n010f0008=transition/popup_window_enter\n010f0009=transition/popup_window_exit\n010f000a=xml/password_kbd_qwerty_shifted\n010f000b=xml/password_kbd_symbols\n010f000c=xml/password_kbd_symbols_shift\n010f000d=xml/power_profile\n010f000e=xml/preferred_time_zones\n010f000f=xml/sms_short_codes\n010f0010=xml/storage_list\n010f0011=xml/time_zones_by_country\n010f0012=plurals/wifi_available_detailed\n01100000=raw/loaderror\n01100001=raw/nodomain\n01100002=raw/color_fade_frag\n01100003=raw/color_fade_vert\n01100004=raw/default_ringtone_vibration_effect\n01100005=raw/fallback_categories\n01100006=raw/fallbackring\n01100007=raw/remote_views_color_resources\n01110000=bool/config_sendPackageName\n01110001=bool/config_showDefaultAssistant\n01110002=bool/config_showDefaultEmergency\n01110003=bool/config_showDefaultHome\n01110004=bool/config_perDisplayFocusEnabled\n01110005=bool/config_assistantOnTopOfDream\n01110006=bool/config_remoteInsetsControllerControlsSystemBars\n01110007=bool/config_preventImeStartupUnlessTextEditor\n01110008=bool/config_enableQrCodeScannerOnLockScreen\n01110009=bool/config_safetyProtectionEnabled\n0111000a=bool/config_enableDefaultNotes\n0111000b=bool/config_enableDefaultNotesForWorkProfile\n0111000c=bool/ImsConnectedDefaultValue\n0111000d=bool/action_bar_embed_tabs\n0111000e=bool/action_bar_expanded_action_views_exclusive\n0111000f=bool/allow_clear_initial_attach_data_profile\n01110010=bool/allow_test_udfps\n01110011=bool/auto_data_switch_ping_test_before_switch\n01110012=bool/autofill_dialog_horizontal_space_included\n01110013=bool/config_LTE_eri_for_network_name\n01110014=bool/config_actionMenuItemAllCaps\n01110015=bool/config_adaptive_sleep_available\n01110016=bool/config_allow3rdPartyAppOnInternal\n01110017=bool/config_allowAlarmsOnStoppedUsers\n01110018=bool/config_allowAllRotations\n01110019=bool/config_allowAnimationsInLowPowerMode\n0111001a=bool/config_allowAutoBrightnessWhileDozing\n0111001b=bool/config_allowChangeUserSwitcherEnabled\n0111001c=bool/config_allowDisablingAssistDisclosure\n0111001d=bool/config_allowDockBeforeProvision\n0111001e=bool/config_allowEscrowTokenForTrustAgent\n0111001f=bool/config_allowFloatingWindowsFillScreen\n01110020=bool/config_allowNormalBrightnessForDozePolicy\n01110021=bool/config_allowPriorityVibrationsInLowPowerMode\n01110022=bool/config_allowRotationResolver\n01110023=bool/config_allowSeamlessRotationDespiteNavBarMoving\n01110024=bool/config_allowStartActivityForLongPressOnPowerInSetup\n01110025=bool/config_allowTheaterModeWakeFromCameraLens\n01110026=bool/config_allowTheaterModeWakeFromDock\n01110027=bool/config_allowTheaterModeWakeFromGesture\n01110028=bool/config_allowTheaterModeWakeFromKey\n01110029=bool/config_allowTheaterModeWakeFromLidSwitch\n0111002a=bool/config_allowTheaterModeWakeFromMotion\n0111002b=bool/config_allowTheaterModeWakeFromMotionWhenNotDreaming\n0111002c=bool/config_allowTheaterModeWakeFromPowerKey\n0111002d=bool/config_allowTheaterModeWakeFromUnplug\n0111002e=bool/config_allowTheaterModeWakeFromWindowLayout\n0111002f=bool/config_allow_pin_storage_for_unattended_reboot\n01110030=bool/config_allow_ussd_over_ims\n01110031=bool/config_alwaysScaleWallpaper\n01110032=bool/config_alwaysUseCdmaRssi\n01110033=bool/config_am_disablePssProfiling\n01110034=bool/config_animateScreenLights\n01110035=bool/config_annoy_dianne\n01110036=bool/config_appCompatUserAppAspectRatioFullscreenIsEnabled\n01110037=bool/config_appCompatUserAppAspectRatioSettingsIsEnabled\n01110038=bool/config_assistLongPressHomeEnabledDefault\n01110039=bool/config_assistTouchGestureEnabledDefault\n0111003a=bool/config_attachNavBarToAppDuringTransition\n0111003b=bool/config_audio_ringer_mode_affects_alarm_stream\n0111003c=bool/config_autoBrightnessResetAmbientLuxAfterWarmUp\n0111003d=bool/config_autoPowerModePreferWristTilt\n0111003e=bool/config_autoPowerModePrefetchLocation\n0111003f=bool/config_autoPowerModeUseMotionSensor\n01110040=bool/config_autoResetAirplaneMode\n01110041=bool/config_auto_attach_data_on_creation\n01110042=bool/config_automatic_brightness_available\n01110043=bool/config_avoidGfxAccel\n01110044=bool/config_awareSettingAvailable\n01110045=bool/config_batterySaverStickyBehaviourDisabled\n01110046=bool/config_batterySaverSupported\n01110047=bool/config_batterySaverTurnedOffNotificationEnabled\n01110048=bool/config_batterySaver_full_deferFullBackup\n01110049=bool/config_batterySaver_full_deferKeyValueBackup\n0111004a=bool/config_batterySaver_full_disableAnimation\n0111004b=bool/config_batterySaver_full_disableAod\n0111004c=bool/config_batterySaver_full_disableLaunchBoost\n0111004d=bool/config_batterySaver_full_disableOptionalSensors\n0111004e=bool/config_batterySaver_full_disableVibration\n0111004f=bool/config_batterySaver_full_enableAdjustBrightness\n01110050=bool/config_batterySaver_full_enableDataSaver\n01110051=bool/config_batterySaver_full_enableFirewall\n01110052=bool/config_batterySaver_full_enableNightMode\n01110053=bool/config_batterySaver_full_enableQuickDoze\n01110054=bool/config_batterySaver_full_forceAllAppsStandby\n01110055=bool/config_batterySaver_full_forceBackgroundCheck\n01110056=bool/config_batterySdCardAccessibility\n01110057=bool/config_batteryStatsResetOnUnplugAfterSignificantCharge\n01110058=bool/config_batteryStatsResetOnUnplugHighBatteryLevel\n01110059=bool/config_battery_percentage_setting_available\n0111005a=bool/config_batterymeterDualTone\n0111005b=bool/config_bg_current_drain_auto_restrict_abusive_apps\n0111005c=bool/config_bg_current_drain_event_duration_based_threshold_enabled\n0111005d=bool/config_bg_current_drain_high_threshold_by_bg_location\n0111005e=bool/config_bg_current_drain_monitor_enabled\n0111005f=bool/config_bg_prompt_abusive_apps_to_bg_restricted\n01110060=bool/config_bg_prompt_fgs_with_noti_to_bg_restricted\n01110061=bool/config_biometricFrrNotificationEnabled\n01110062=bool/config_bluetooth_address_validation\n01110063=bool/config_bluetooth_sco_off_call\n01110064=bool/config_brightWhenDozing\n01110065=bool/config_bugReportHandlerEnabled\n01110066=bool/config_built_in_sip_phone\n01110067=bool/config_buttonTextAllCaps\n01110068=bool/config_callNotificationActionColorsRequireColorized\n01110069=bool/config_cameraDoubleTapPowerGestureEnabled\n0111006a=bool/config_camera_autorotate\n0111006b=bool/config_camera_sound_forced\n0111006c=bool/config_canInternalDisplayHostDesktops\n0111006d=bool/config_canRemoveFirstAccount\n0111006e=bool/config_canSwitchToHeadlessSystemUser\n0111006f=bool/config_carDockEnablesAccelerometer\n01110070=bool/config_carrier_volte_available\n01110071=bool/config_carrier_volte_tty_supported\n01110072=bool/config_carrier_vt_available\n01110073=bool/config_carrier_wfc_ims_available\n01110074=bool/config_cbrs_supported\n01110075=bool/config_cecHdmiCecControlDisabled_allowed\n01110076=bool/config_cecHdmiCecControlDisabled_default\n01110077=bool/config_cecHdmiCecControlEnabled_allowed\n01110078=bool/config_cecHdmiCecControlEnabled_default\n01110079=bool/config_cecHdmiCecEnabled_userConfigurable\n0111007a=bool/config_cecHdmiCecVersion14b_allowed\n0111007b=bool/config_cecHdmiCecVersion14b_default\n0111007c=bool/config_cecHdmiCecVersion20_allowed\n0111007d=bool/config_cecHdmiCecVersion20_default\n0111007e=bool/config_cecHdmiCecVersion_userConfigurable\n0111007f=bool/config_cecPowerControlModeBroadcast_allowed\n01110080=bool/config_cecPowerControlModeBroadcast_default\n01110081=bool/config_cecPowerControlModeNone_allowed\n01110082=bool/config_cecPowerControlModeNone_default\n01110083=bool/config_cecPowerControlModeTvAndAudioSystem_allowed\n01110084=bool/config_cecPowerControlModeTvAndAudioSystem_default\n01110085=bool/config_cecPowerControlModeTv_allowed\n01110086=bool/config_cecPowerControlModeTv_default\n01110087=bool/config_cecPowerControlMode_userConfigurable\n01110088=bool/config_cecPowerStateChangeOnActiveSourceLostNone_allowed\n01110089=bool/config_cecPowerStateChangeOnActiveSourceLostNone_default\n0111008a=bool/config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed\n0111008b=bool/config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default\n0111008c=bool/config_cecPowerStateChangeOnActiveSourceLost_userConfigurable\n0111008d=bool/config_cecQuerySadAacDisabled_allowed\n0111008e=bool/config_cecQuerySadAacDisabled_default\n0111008f=bool/config_cecQuerySadAacEnabled_allowed\n01110090=bool/config_cecQuerySadAacEnabled_default\n01110091=bool/config_cecQuerySadAac_userConfigurable\n01110092=bool/config_cecQuerySadAtracDisabled_allowed\n01110093=bool/config_cecQuerySadAtracDisabled_default\n01110094=bool/config_cecQuerySadAtracEnabled_allowed\n01110095=bool/config_cecQuerySadAtracEnabled_default\n01110096=bool/config_cecQuerySadAtrac_userConfigurable\n01110097=bool/config_cecQuerySadDdDisabled_allowed\n01110098=bool/config_cecQuerySadDdDisabled_default\n01110099=bool/config_cecQuerySadDdEnabled_allowed\n0111009a=bool/config_cecQuerySadDdEnabled_default\n0111009b=bool/config_cecQuerySadDd_userConfigurable\n0111009c=bool/config_cecQuerySadDdpDisabled_allowed\n0111009d=bool/config_cecQuerySadDdpDisabled_default\n0111009e=bool/config_cecQuerySadDdpEnabled_allowed\n0111009f=bool/config_cecQuerySadDdpEnabled_default\n011100a0=bool/config_cecQuerySadDdp_userConfigurable\n011100a1=bool/config_cecQuerySadDstDisabled_allowed\n011100a2=bool/config_cecQuerySadDstDisabled_default\n011100a3=bool/config_cecQuerySadDstEnabled_allowed\n011100a4=bool/config_cecQuerySadDstEnabled_default\n011100a5=bool/config_cecQuerySadDst_userConfigurable\n011100a6=bool/config_cecQuerySadDtsDisabled_allowed\n011100a7=bool/config_cecQuerySadDtsDisabled_default\n011100a8=bool/config_cecQuerySadDtsEnabled_allowed\n011100a9=bool/config_cecQuerySadDtsEnabled_default\n011100aa=bool/config_cecQuerySadDts_userConfigurable\n011100ab=bool/config_cecQuerySadDtshdDisabled_allowed\n011100ac=bool/config_cecQuerySadDtshdDisabled_default\n011100ad=bool/config_cecQuerySadDtshdEnabled_allowed\n011100ae=bool/config_cecQuerySadDtshdEnabled_default\n011100af=bool/config_cecQuerySadDtshd_userConfigurable\n011100b0=bool/config_cecQuerySadLpcmDisabled_allowed\n011100b1=bool/config_cecQuerySadLpcmDisabled_default\n011100b2=bool/config_cecQuerySadLpcmEnabled_allowed\n011100b3=bool/config_cecQuerySadLpcmEnabled_default\n011100b4=bool/config_cecQuerySadLpcm_userConfigurable\n011100b5=bool/config_cecQuerySadMaxDisabled_allowed\n011100b6=bool/config_cecQuerySadMaxDisabled_default\n011100b7=bool/config_cecQuerySadMaxEnabled_allowed\n011100b8=bool/config_cecQuerySadMaxEnabled_default\n011100b9=bool/config_cecQuerySadMax_userConfigurable\n011100ba=bool/config_cecQuerySadMp3Disabled_allowed\n011100bb=bool/config_cecQuerySadMp3Disabled_default\n011100bc=bool/config_cecQuerySadMp3Enabled_allowed\n011100bd=bool/config_cecQuerySadMp3Enabled_default\n011100be=bool/config_cecQuerySadMp3_userConfigurable\n011100bf=bool/config_cecQuerySadMpeg1Disabled_allowed\n011100c0=bool/config_cecQuerySadMpeg1Disabled_default\n011100c1=bool/config_cecQuerySadMpeg1Enabled_allowed\n011100c2=bool/config_cecQuerySadMpeg1Enabled_default\n011100c3=bool/config_cecQuerySadMpeg1_userConfigurable\n011100c4=bool/config_cecQuerySadMpeg2Disabled_allowed\n011100c5=bool/config_cecQuerySadMpeg2Disabled_default\n011100c6=bool/config_cecQuerySadMpeg2Enabled_allowed\n011100c7=bool/config_cecQuerySadMpeg2Enabled_default\n011100c8=bool/config_cecQuerySadMpeg2_userConfigurable\n011100c9=bool/config_cecQuerySadOnebitaudioDisabled_allowed\n011100ca=bool/config_cecQuerySadOnebitaudioDisabled_default\n011100cb=bool/config_cecQuerySadOnebitaudioEnabled_allowed\n011100cc=bool/config_cecQuerySadOnebitaudioEnabled_default\n011100cd=bool/config_cecQuerySadOnebitaudio_userConfigurable\n011100ce=bool/config_cecQuerySadTruehdDisabled_allowed\n011100cf=bool/config_cecQuerySadTruehdDisabled_default\n011100d0=bool/config_cecQuerySadTruehdEnabled_allowed\n011100d1=bool/config_cecQuerySadTruehdEnabled_default\n011100d2=bool/config_cecQuerySadTruehd_userConfigurable\n011100d3=bool/config_cecQuerySadWmaproDisabled_allowed\n011100d4=bool/config_cecQuerySadWmaproDisabled_default\n011100d5=bool/config_cecQuerySadWmaproEnabled_allowed\n011100d6=bool/config_cecQuerySadWmaproEnabled_default\n011100d7=bool/config_cecQuerySadWmapro_userConfigurable\n011100d8=bool/config_cecRcProfileSourceContentsMenuHandled_allowed\n011100d9=bool/config_cecRcProfileSourceContentsMenuHandled_default\n011100da=bool/config_cecRcProfileSourceContentsMenuNotHandled_allowed\n011100db=bool/config_cecRcProfileSourceContentsMenuNotHandled_default\n011100dc=bool/config_cecRcProfileSourceContentsMenu_userConfigurable\n011100dd=bool/config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed\n011100de=bool/config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default\n011100df=bool/config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed\n011100e0=bool/config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default\n011100e1=bool/config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable\n011100e2=bool/config_cecRcProfileSourceRootMenuHandled_allowed\n011100e3=bool/config_cecRcProfileSourceRootMenuHandled_default\n011100e4=bool/config_cecRcProfileSourceRootMenuNotHandled_allowed\n011100e5=bool/config_cecRcProfileSourceRootMenuNotHandled_default\n011100e6=bool/config_cecRcProfileSourceRootMenu_userConfigurable\n011100e7=bool/config_cecRcProfileSourceSetupMenuHandled_allowed\n011100e8=bool/config_cecRcProfileSourceSetupMenuHandled_default\n011100e9=bool/config_cecRcProfileSourceSetupMenuNotHandled_allowed\n011100ea=bool/config_cecRcProfileSourceSetupMenuNotHandled_default\n011100eb=bool/config_cecRcProfileSourceSetupMenu_userConfigurable\n011100ec=bool/config_cecRcProfileSourceTopMenuHandled_allowed\n011100ed=bool/config_cecRcProfileSourceTopMenuHandled_default\n011100ee=bool/config_cecRcProfileSourceTopMenuNotHandled_allowed\n011100ef=bool/config_cecRcProfileSourceTopMenuNotHandled_default\n011100f0=bool/config_cecRcProfileSourceTopMenu_userConfigurable\n011100f1=bool/config_cecRcProfileTvFour_allowed\n011100f2=bool/config_cecRcProfileTvFour_default\n011100f3=bool/config_cecRcProfileTvNone_allowed\n011100f4=bool/config_cecRcProfileTvNone_default\n011100f5=bool/config_cecRcProfileTvOne_allowed\n011100f6=bool/config_cecRcProfileTvOne_default\n011100f7=bool/config_cecRcProfileTvThree_allowed\n011100f8=bool/config_cecRcProfileTvThree_default\n011100f9=bool/config_cecRcProfileTvTwo_allowed\n011100fa=bool/config_cecRcProfileTvTwo_default\n011100fb=bool/config_cecRcProfileTv_userConfigurable\n011100fc=bool/config_cecRoutingControlDisabled_allowed\n011100fd=bool/config_cecRoutingControlDisabled_default\n011100fe=bool/config_cecRoutingControlEnabled_allowed\n011100ff=bool/config_cecRoutingControlEnabled_default\n01110100=bool/config_cecRoutingControl_userConfigurable\n01110101=bool/config_cecSetMenuLanguageDisabled_allowed\n01110102=bool/config_cecSetMenuLanguageDisabled_default\n01110103=bool/config_cecSetMenuLanguageEnabled_allowed\n01110104=bool/config_cecSetMenuLanguageEnabled_default\n01110105=bool/config_cecSetMenuLanguage_userConfigurable\n01110106=bool/config_cecSoundbarModeDisabled_allowed\n01110107=bool/config_cecSoundbarModeDisabled_default\n01110108=bool/config_cecSoundbarModeEnabled_allowed\n01110109=bool/config_cecSoundbarModeEnabled_default\n0111010a=bool/config_cecSoundbarMode_userConfigurable\n0111010b=bool/config_cecSystemAudioControlDisabled_allowed\n0111010c=bool/config_cecSystemAudioControlDisabled_default\n0111010d=bool/config_cecSystemAudioControlEnabled_allowed\n0111010e=bool/config_cecSystemAudioControlEnabled_default\n0111010f=bool/config_cecSystemAudioControl_userConfigurable\n01110110=bool/config_cecSystemAudioModeMutingDisabled_allowed\n01110111=bool/config_cecSystemAudioModeMutingDisabled_default\n01110112=bool/config_cecSystemAudioModeMutingEnabled_allowed\n01110113=bool/config_cecSystemAudioModeMutingEnabled_default\n01110114=bool/config_cecSystemAudioModeMuting_userConfigurable\n01110115=bool/config_cecTvSendStandbyOnSleepDisabled_allowed\n01110116=bool/config_cecTvSendStandbyOnSleepDisabled_default\n01110117=bool/config_cecTvSendStandbyOnSleepEnabled_allowed\n01110118=bool/config_cecTvSendStandbyOnSleepEnabled_default\n01110119=bool/config_cecTvSendStandbyOnSleep_userConfigurable\n0111011a=bool/config_cecTvWakeOnOneTouchPlayDisabled_allowed\n0111011b=bool/config_cecTvWakeOnOneTouchPlayDisabled_default\n0111011c=bool/config_cecTvWakeOnOneTouchPlayEnabled_allowed\n0111011d=bool/config_cecTvWakeOnOneTouchPlayEnabled_default\n0111011e=bool/config_cecTvWakeOnOneTouchPlay_userConfigurable\n0111011f=bool/config_cecVolumeControlModeDisabled_allowed\n01110120=bool/config_cecVolumeControlModeDisabled_default\n01110121=bool/config_cecVolumeControlModeEnabled_allowed\n01110122=bool/config_cecVolumeControlModeEnabled_default\n01110123=bool/config_cecVolumeControlMode_userConfigurable\n01110124=bool/config_cellBroadcastAppLinks\n01110125=bool/config_checkWallpaperAtBoot\n01110126=bool/config_closeDialogWhenTouchOutside\n01110127=bool/config_customBugreport\n01110128=bool/config_customUserSwitchUi\n01110129=bool/config_debugEnableAutomaticSystemServerHeapDumps\n0111012a=bool/config_decoupleStatusBarAndDisplayCutoutFromScreenSize\n0111012b=bool/config_defaultAdasGnssLocationEnabled\n0111012c=bool/config_defaultBatteryPercentageSetting\n0111012d=bool/config_defaultBinderHeavyHitterAutoSamplerEnabled\n0111012e=bool/config_defaultBinderHeavyHitterWatcherEnabled\n0111012f=bool/config_defaultEmergencyGestureEnabled\n01110130=bool/config_defaultEmergencyGestureSoundEnabled\n01110131=bool/config_defaultInTouchMode\n01110132=bool/config_defaultPreventScreenTimeoutEnabled\n01110133=bool/config_defaultRingtonePickerEnabled\n01110134=bool/config_defaultWindowFeatureContextMenu\n01110135=bool/config_defaultWindowFeatureOptionsPanel\n01110136=bool/config_deskDockEnablesAccelerometer\n01110137=bool/config_deviceSupportsHighPerfTransitions\n01110138=bool/config_deviceSupportsWifiUsd\n01110139=bool/config_device_respects_hold_carrier_config\n0111013a=bool/config_device_volte_available\n0111013b=bool/config_device_vt_available\n0111013c=bool/config_device_wfc_ims_available\n0111013d=bool/config_disableLockscreenByDefault\n0111013e=bool/config_disableMenuKeyInLockScreen\n0111013f=bool/config_disableShutdownVibrationInZen\n01110140=bool/config_disableTaskSnapshots\n01110141=bool/config_disableTransitionAnimation\n01110142=bool/config_disableUsbPermissionDialogs\n01110143=bool/config_disableWeaverOnUnsecuredUsers\n01110144=bool/config_disable_all_cb_messages\n01110145=bool/config_dismissDreamOnActivityStart\n01110146=bool/config_displayBlanksAfterDoze\n01110147=bool/config_displayBrightnessBucketsInDoze\n01110148=bool/config_displayColorFadeDisabled\n01110149=bool/config_displayWhiteBalanceAvailable\n0111014a=bool/config_displayWhiteBalanceEnabledDefault\n0111014b=bool/config_displayWhiteBalanceLightModeAllowed\n0111014c=bool/config_dockBigOverlayWindows\n0111014d=bool/config_dockedStackDividerFreeSnapMode\n0111014e=bool/config_dontPreferApn\n0111014f=bool/config_dozeAfterScreenOffByDefault\n01110150=bool/config_dozeAlwaysOnDisplayAvailable\n01110151=bool/config_dozeAlwaysOnEnabled\n01110152=bool/config_dozePickupGestureEnabled\n01110153=bool/config_dozePulsePickup\n01110154=bool/config_dozeSupportsAodWallpaper\n01110155=bool/config_dozeWakeLockScreenSensorAvailable\n01110156=bool/config_dragToMaximizeInDesktopMode\n01110157=bool/config_dreamsActivatedOnDockByDefault\n01110158=bool/config_dreamsActivatedOnPosturedByDefault\n01110159=bool/config_dreamsActivatedOnSleepByDefault\n0111015a=bool/config_dreamsDisabledByAmbientModeSuppressionConfig\n0111015b=bool/config_dreamsEnabledByDefault\n0111015c=bool/config_dreamsEnabledOnBattery\n0111015d=bool/config_dreamsOnlyEnabledForDockUser\n0111015e=bool/config_dreamsSupported\n0111015f=bool/config_dropboxmanager_persistent_logging_enabled\n01110160=bool/config_duplicate_port_omadm_wappush\n01110161=bool/config_eap_sim_based_auth_supported\n01110162=bool/config_earcEnabled_userConfigurable\n01110163=bool/config_earcFeatureDisabled_allowed\n01110164=bool/config_earcFeatureDisabled_default\n01110165=bool/config_earcFeatureEnabled_allowed\n01110166=bool/config_earcFeatureEnabled_default\n01110167=bool/config_emergencyGestureEnabled\n01110168=bool/config_enableActivityRecognitionHardwareOverlay\n01110169=bool/config_enableAppCloningBuildingBlocks\n0111016a=bool/config_enableAppWidgetService\n0111016b=bool/config_enableAutoPowerModes\n0111016c=bool/config_enableBackSound\n0111016d=bool/config_enableBurnInProtection\n0111016e=bool/config_enableCarDockHomeLaunch\n0111016f=bool/config_enableContextSyncInCall\n01110170=bool/config_enableCredentialFactoryResetProtection\n01110171=bool/config_enableCrossTaskScaleUpAnimation\n01110172=bool/config_enableDefaultHdrConversionPassthrough\n01110173=bool/config_enableFusedLocationOverlay\n01110174=bool/config_enableGaiaEducationInPrivateSpace\n01110175=bool/config_enableGeocoderOverlay\n01110176=bool/config_enableGeofenceOverlay\n01110177=bool/config_enableGeolocationTimeZoneDetection\n01110178=bool/config_enableGnssAssistanceOverlay\n01110179=bool/config_enableGnssLocationOverlay\n0111017a=bool/config_enableGnssTimeUpdateService\n0111017b=bool/config_enableHapticTextHandle\n0111017c=bool/config_enableIdleScreenBrightnessMode\n0111017d=bool/config_enableLockBeforeUnlockScreen\n0111017e=bool/config_enableLockScreenRotation\n0111017f=bool/config_enableMotionPrediction\n01110180=bool/config_enableMultiUserUI\n01110181=bool/config_enableMultipleAdmins\n01110182=bool/config_enableNetworkLocationOverlay\n01110183=bool/config_enableNewAutoSelectNetworkUI\n01110184=bool/config_enableNightMode\n01110185=bool/config_enableNotificationAccessibilityEvents\n01110186=bool/config_enablePopulationDensityProviderOverlay\n01110187=bool/config_enablePrimaryLocationTimeZoneProvider\n01110188=bool/config_enableProximityService\n01110189=bool/config_enableSafetyCenter\n0111018a=bool/config_enableScreenshotChord\n0111018b=bool/config_enableSearchTileHideIllustrationInPrivateSpace\n0111018c=bool/config_enableSecondaryLocationTimeZoneProvider\n0111018d=bool/config_enableServerNotificationEffectsForAutomotive\n0111018e=bool/config_enableStylusPointerIcon\n0111018f=bool/config_enableTelephonyTimeZoneDetection\n01110190=bool/config_enableTimeZoneManualChangeTrackingSupported\n01110191=bool/config_enableTimeZoneNotificationsSupported\n01110192=bool/config_enableTimeZoneNotificationsTrackingSupported\n01110193=bool/config_enableTimeoutToDockUserWhenDocked\n01110194=bool/config_enableUdcSysfsUsbStateUpdate\n01110195=bool/config_enableUserSwitcherUponUserCreation\n01110196=bool/config_enableViewGroupScalingFading\n01110197=bool/config_enableVirtualDeviceManager\n01110198=bool/config_enableWallpaperService\n01110199=bool/config_enableWcgMode\n0111019a=bool/config_enableWifiDisplay\n0111019b=bool/config_enable_a11y_fullscreen_magnification_overscroll_handler\n0111019c=bool/config_enable_a11y_magnification_single_panning\n0111019d=bool/config_enable_cellular_on_boot_default\n0111019e=bool/config_enable_emergency_call_while_sim_locked\n0111019f=bool/config_enable_iwlan_handover_policy\n011101a0=bool/config_enable_puk_unlock_screen\n011101a1=bool/config_enabled_mt_sms_polling\n011101a2=bool/config_enhancedConfirmationModeEnabled\n011101a3=bool/config_enhanced_iwlan_handover_check\n011101a4=bool/config_enterDesktopByDefaultOnFreeformDisplay\n011101a5=bool/config_evenDimmerEnabled\n011101a6=bool/config_expandLockScreenUserSwitcher\n011101a7=bool/config_faceAuthDismissesKeyguard\n011101a8=bool/config_faceAuthSupportsSelfIllumination\n011101a9=bool/config_fillMainBuiltInDisplayCutout\n011101aa=bool/config_fillSecondaryBuiltInDisplayCutout\n011101ab=bool/config_fingerprintSupportsGestures\n011101ac=bool/config_flexibleSplitRatios\n011101ad=bool/config_flipToScreenOffEnabled\n011101ae=bool/config_focusScrollContainersInTouchMode\n011101af=bool/config_fold_lock_behavior\n011101b0=bool/config_forceOrientationListenerEnabledWhileDreaming\n011101b1=bool/config_forceSystemPackagesQueryable\n011101b2=bool/config_forceWindowDrawsStatusBarBackground\n011101b3=bool/config_force_phone_globals_creation\n011101b4=bool/config_fusedLocationOverlayUnstableFallback\n011101b5=bool/config_glanceableHubEnabled\n011101b6=bool/config_gnssLocationOverlayUnstableFallback\n011101b7=bool/config_goToSleepOnButtonPressTheaterMode\n011101b8=bool/config_guestUserAllowEphemeralStateChange\n011101b9=bool/config_guestUserAutoCreated\n011101ba=bool/config_guestUserEphemeral\n011101bb=bool/config_handleVolumeAliasesUsingVolumeGroups\n011101bc=bool/config_handleVolumeKeysInWindowManager\n011101bd=bool/config_hasPermanentDpad\n011101be=bool/config_hasRecents\n011101bf=bool/config_hibernationDeletesOatArtifactsEnabled\n011101c0=bool/config_hideDisplayCutoutWithDisplayArea\n011101c1=bool/config_hideNavBarForKeyboard\n011101c2=bool/config_honor_data_retry_timer_for_emergency_network\n011101c3=bool/config_hotspotNetworksEnabledForService\n011101c4=bool/config_hotswapCapable\n011101c5=bool/config_ignoreUdfpsVote\n011101c6=bool/config_ignoreVibrationsOnWirelessCharger\n011101c7=bool/config_imeDrawsImeNavBar\n011101c8=bool/config_intrusiveNotificationLed\n011101c9=bool/config_isCompatFakeFocusEnabled\n011101ca=bool/config_isDesktopModeDevOptionSupported\n011101cb=bool/config_isDesktopModeSupported\n011101cc=bool/config_isDisplayHingeAlwaysSeparating\n011101cd=bool/config_isMainUserPermanentAdmin\n011101ce=bool/config_isPreApprovalRequestAvailable\n011101cf=bool/config_isWindowManagerCameraCompatSplitScreenAspectRatioEnabled\n011101d0=bool/config_isWindowManagerCameraCompatTreatmentEnabled\n011101d1=bool/config_is_powerbutton_fps\n011101d2=bool/config_jobSchedulerRestrictBackgroundUser\n011101d3=bool/config_keepDreamingWhenUnplugging\n011101d4=bool/config_keepRestrictedProfilesInBackground\n011101d5=bool/config_keyboardVibrationSettingsSupported\n011101d6=bool/config_keyguardUserSwitcher\n011101d7=bool/config_knownNetworksEnabledForService\n011101d8=bool/config_launchCameraOnCameraLensCoverToggle\n011101d9=bool/config_leftRightSplitInPortrait\n011101da=bool/config_letterboxIsAutomaticReachabilityInBookModeEnabled\n011101db=bool/config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled\n011101dc=bool/config_letterboxIsDisplayRotationImmersiveAppCompatPolicyEnabled\n011101dd=bool/config_letterboxIsEducationEnabled\n011101de=bool/config_letterboxIsEnabledForTranslucentActivities\n011101df=bool/config_letterboxIsHorizontalReachabilityEnabled\n011101e0=bool/config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled\n011101e1=bool/config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled\n011101e2=bool/config_letterboxIsVerticalReachabilityEnabled\n011101e3=bool/config_lidControlsScreenLock\n011101e4=bool/config_lidControlsSleep\n011101e5=bool/config_localDisplaysMirrorContent\n011101e6=bool/config_lockDayNightMode\n011101e7=bool/config_lockUiMode\n011101e8=bool/config_longPressOnPowerForAssistantSettingAvailable\n011101e9=bool/config_lowPowerStandbyEnabledByDefault\n011101ea=bool/config_lowPowerStandbySupported\n011101eb=bool/config_magnification_always_on_enabled\n011101ec=bool/config_magnification_area\n011101ed=bool/config_magnification_keep_zoom_level_when_context_changed\n011101ee=bool/config_mainBuiltInDisplayIsRound\n011101ef=bool/config_maskMainBuiltInDisplayCutout\n011101f0=bool/config_maskSecondaryBuiltInDisplayCutout\n011101f1=bool/config_matchSecondaryInternalDisplaysOrientationToReverseDefaultDisplay\n011101f2=bool/config_mms_content_disposition_support\n011101f3=bool/config_mobile_data_capable\n011101f4=bool/config_multiuserDelayUserDataLocking\n011101f5=bool/config_multiuserVisibleBackgroundUsers\n011101f6=bool/config_multiuserVisibleBackgroundUsersOnDefaultDisplay\n011101f7=bool/config_navBarAlwaysShowOnSideEdgeGesture\n011101f8=bool/config_navBarCanMove\n011101f9=bool/config_navBarDefaultTransparent\n011101fa=bool/config_navBarNeedsScrim\n011101fb=bool/config_navBarTapThrough\n011101fc=bool/config_networkSamplingWakesDevice\n011101fd=bool/config_nightDisplayAvailable\n011101fe=bool/config_noHomeScreen\n011101ff=bool/config_notificationBadging\n01110200=bool/config_notificationCloseButtonSupported\n01110201=bool/config_notificationHeaderClickableForExpand\n01110202=bool/config_notificationReviewPermissions\n01110203=bool/config_oem_enabled_satellite_access_allow\n01110204=bool/config_offsetWallpaperToCenterOfLargestDisplay\n01110205=bool/config_omnipresentCommunalUser\n01110206=bool/config_orderUnlockAndWake\n01110207=bool/config_overrideRemoteViewsActivityTransition\n01110208=bool/config_pauseWallpaperRenderWhenStateChangeEnabled\n01110209=bool/config_pdp_reject_enable_retry\n0111020a=bool/config_performantAuthDefault\n0111020b=bool/config_permissionsIndividuallyControlled\n0111020c=bool/config_persistBrightnessNitsForDefaultDisplay\n0111020d=bool/config_pinnerAssistantApp\n0111020e=bool/config_pinnerCameraApp\n0111020f=bool/config_powerDecoupleAutoSuspendModeFromDisplay\n01110210=bool/config_powerDecoupleInteractiveModeFromDisplay\n01110211=bool/config_predictShowStartingSurface\n01110212=bool/config_preferKeepClearForFocus\n01110213=bool/config_preferenceFragmentClipToPadding\n01110214=bool/config_profcollectReportUploaderEnabled\n01110215=bool/config_pulseOnNotificationsAvailable\n01110216=bool/config_quickSettingsShowMediaPlayer\n01110217=bool/config_quickSettingsSupported\n01110218=bool/config_rawContactsAccountRestrictionEnabled\n01110219=bool/config_reduceBrightColorsAvailable\n0111021a=bool/config_refreshRateSynchronizationEnabled\n0111021b=bool/config_remoteInsetsControllerSystemBarsCanBeShownByUserAction\n0111021c=bool/config_repairModeSupported\n0111021d=bool/config_requireCallCapableAccountForHandle\n0111021e=bool/config_resetScreenTimeoutOnUnexpectedDreamExit\n0111021f=bool/config_restartRadioAfterProvisioning\n01110220=bool/config_restart_radio_on_pdp_fail_regular_deactivation\n01110221=bool/config_reverseDefaultRotation\n01110222=bool/config_ringtoneVibrationSettingsSupported\n01110223=bool/config_safe_media_disable_on_volume_up\n01110224=bool/config_safe_media_volume_enabled\n01110225=bool/config_safe_sound_dosage_enabled\n01110226=bool/config_satellite_allow_check_message_in_not_connected\n01110227=bool/config_satellite_allow_tn_scanning_during_satellite_session\n01110228=bool/config_satellite_modem_support_concurrent_tn_scanning\n01110229=bool/config_satellite_should_notify_availability\n0111022a=bool/config_screen_off_udfps_default_on\n0111022b=bool/config_screen_off_udfps_enabled\n0111022c=bool/config_searchAllEntrypointsEnabledDefault\n0111022d=bool/config_secondaryBuiltInDisplayIsRound\n0111022e=bool/config_sendAudioBecomingNoisy\n0111022f=bool/config_send_satellite_datagram_to_modem_in_demo_mode\n01110230=bool/config_sensorPrivacyRequiresAuthentication\n01110231=bool/config_setColorTransformAccelerated\n01110232=bool/config_setColorTransformAcceleratedPerLayer\n01110233=bool/config_settingsHelpLinksEnabled\n01110234=bool/config_sf_limitedAlpha\n01110235=bool/config_sf_slowBlur\n01110236=bool/config_shortPressEarlyOnPower\n01110237=bool/config_shortPressEarlyOnStemPrimary\n01110238=bool/config_showAreaUpdateInfoSettings\n01110239=bool/config_showBuiltinWirelessChargingAnim\n0111023a=bool/config_showGesturalNavigationHints\n0111023b=bool/config_showMenuShortcutsWhenKeyboardPresent\n0111023c=bool/config_showNavigationBar\n0111023d=bool/config_showNotificationForBackgroundUserAlarms\n0111023e=bool/config_showPercentageTextDuringRebootToUpdate\n0111023f=bool/config_showSysuiShutdown\n01110240=bool/config_showUserSwitcherByDefault\n01110241=bool/config_shutdownIfNoPower\n01110242=bool/config_silenceRingerOnSleepKey\n01110243=bool/config_silenceSensorAvailable\n01110244=bool/config_single_volume\n01110245=bool/config_sip_wifi_only\n01110246=bool/config_skipActivityRelaunchWhenDocking\n01110247=bool/config_skipScreenOffTransition\n01110248=bool/config_skipScreenOnBrightnessRamp\n01110249=bool/config_skipSensorAvailable\n0111024a=bool/config_smart_battery_available\n0111024b=bool/config_smma_notification_supported_over_ims\n0111024c=bool/config_smppsim_response_via_ims\n0111024d=bool/config_sms_ask_every_time_support\n0111024e=bool/config_sms_capable\n0111024f=bool/config_sms_decode_gsm_8bit_data\n01110250=bool/config_sms_force_7bit_encoding\n01110251=bool/config_sms_utf8_support\n01110252=bool/config_spatial_audio_head_tracking_enabled_default\n01110253=bool/config_speed_up_audio_on_mt_calls\n01110254=bool/config_startDreamImmediatelyOnDock\n01110255=bool/config_stkNoAlphaUsrCnf\n01110256=bool/config_stk_sms_send_support\n01110257=bool/config_stopSystemPackagesByDefault\n01110258=bool/config_strongAuthRequiredOnBoot\n01110259=bool/config_subscription_database_async_update\n0111025a=bool/config_supportAudioSourceUnprocessed\n0111025b=bool/config_supportAutoRotation\n0111025c=bool/config_supportDoubleTapSleep\n0111025d=bool/config_supportDoubleTapWake\n0111025e=bool/config_supportLongPressPowerWhenNonInteractive\n0111025f=bool/config_supportMicNearUltrasound\n01110260=bool/config_supportPreRebootSecurityLogs\n01110261=bool/config_supportProfilesOnNonMainUser\n01110262=bool/config_supportShortPressPowerWhenDefaultDisplayOn\n01110263=bool/config_supportSpeakerNearUltrasound\n01110264=bool/config_supportSystemNavigationKeys\n01110265=bool/config_supportTelephonyTimeZoneFallback\n01110266=bool/config_support_disable_satellite_while_enable_in_progress\n01110267=bool/config_supportsBubble\n01110268=bool/config_supportsCamToggle\n01110269=bool/config_supportsConcurrentInternalDisplays\n0111026a=bool/config_supportsHardwareCamToggle\n0111026b=bool/config_supportsHardwareMicToggle\n0111026c=bool/config_supportsInsecureLockScreen\n0111026d=bool/config_supportsMicToggle\n0111026e=bool/config_supportsMultiDisplay\n0111026f=bool/config_supportsMultiWindow\n01110270=bool/config_supportsRoundedCornersOnWindows\n01110271=bool/config_supportsSeamlessRefreshRateSwitching\n01110272=bool/config_supportsSplitScreenMultiWindow\n01110273=bool/config_supportsSystemDecorsOnSecondaryDisplays\n01110274=bool/config_suspendWhenScreenOffDueToProximity\n01110275=bool/config_sustainedPerformanceModeSupported\n01110276=bool/config_swipeDisambiguation\n01110277=bool/config_swipe_up_gesture_setting_available\n01110278=bool/config_switch_phone_on_voice_reg_state_change\n01110279=bool/config_syncstorageengine_masterSyncAutomatically\n0111027a=bool/config_systemCaptionsServiceCallsEnabled\n0111027b=bool/config_telephony5gNonStandalone\n0111027c=bool/config_telephony5gStandalone\n0111027d=bool/config_textShareSupported\n0111027e=bool/config_tintNotificationActionButtons\n0111027f=bool/config_trackerAppNeedsPermissions\n01110280=bool/config_tvExternalInputLoggingDisplayNameFilterEnabled\n01110281=bool/config_ui_enableFadingMarquee\n01110282=bool/config_unfoldTransitionEnabled\n01110283=bool/config_unfoldTransitionHapticsEnabled\n01110284=bool/config_unfoldTransitionHingeAngle\n01110285=bool/config_unplugTurnsOnScreen\n01110286=bool/config_usbChargingMessage\n01110287=bool/config_use16BitTaskSnapshotPixelFormat\n01110288=bool/config_useAssistantVolume\n01110289=bool/config_useAttentionLight\n0111028a=bool/config_useAutoSuspend\n0111028b=bool/config_useCurrentRotationOnRotationLockChange\n0111028c=bool/config_useDefaultFocusHighlight\n0111028d=bool/config_useDevInputEventForAudioJack\n0111028e=bool/config_useFixedVolume\n0111028f=bool/config_useGnssHardwareProvider\n01110290=bool/config_useLegacySplit\n01110291=bool/config_useRoundIcon\n01110292=bool/config_useSmsAppService\n01110293=bool/config_useSystemProvidedLauncherForSecondary\n01110294=bool/config_useVideoPauseWorkaround\n01110295=bool/config_useVolumeKeySounds\n01110296=bool/config_use_sim_language_file\n01110297=bool/config_use_strict_phone_number_comparation\n01110298=bool/config_use_strict_phone_number_comparation_for_kazakhstan\n01110299=bool/config_use_strict_phone_number_comparation_for_russia\n0111029a=bool/config_use_voip_mode_for_ims\n0111029b=bool/config_user_notification_of_restrictied_mobile_access\n0111029c=bool/config_vehicleInternalNetworkAlwaysRequested\n0111029d=bool/config_viewBasedRotaryEncoderHapticsEnabled\n0111029e=bool/config_viewRotaryEncoderHapticScrollFedbackEnabled\n0111029f=bool/config_viewTouchScreenHapticScrollFeedbackEnabled\n011102a0=bool/config_voice_capable\n011102a1=bool/config_voice_data_sms_auto_fallback\n011102a2=bool/config_volumeHushGestureEnabled\n011102a3=bool/config_volume_down_to_enter_silent\n011102a4=bool/config_volume_up_to_exit_silent\n011102a5=bool/config_wait_for_device_alignment_in_demo_datagram\n011102a6=bool/config_wakeOnAssistKeyPress\n011102a7=bool/config_wakeOnBackKeyPress\n011102a8=bool/config_wakeOnDpadKeyPress\n011102a9=bool/config_wallpaperTopApp\n011102aa=bool/config_watchlistUseFileHashesCache\n011102ab=bool/config_wifiDisplaySupportsProtectedBuffers\n011102ac=bool/config_wimaxEnabled\n011102ad=bool/config_windowActionBarSupported\n011102ae=bool/config_windowEnableCircularEmulatorDisplayOverlay\n011102af=bool/config_windowIsRound\n011102b0=bool/config_windowManagerHalfFoldAutoRotateOverride\n011102b1=bool/config_windowManagerPauseRotationWhenUnfolding\n011102b2=bool/config_windowNoTitleDefault\n011102b3=bool/config_windowOverscanByDefault\n011102b4=bool/config_windowShowCircularMask\n011102b5=bool/config_wirelessConsentRequired\n011102b6=bool/config_wlan_data_service_conn_persistence_on_restart\n011102b7=bool/config_zramWriteback\n011102b8=bool/device_idle_light_idle_increase_linearly\n011102b9=bool/device_idle_use_mode_manager\n011102ba=bool/device_idle_use_window_alarms\n011102bb=bool/device_idle_wait_for_unlock\n011102bc=bool/editable_voicemailnumber\n011102bd=bool/euicc_optimize_apdu_sender\n011102be=bool/ignore_emergency_number_routing_from_db\n011102bf=bool/ignore_modem_config_emergency_numbers\n011102c0=bool/imsServiceAllowTurnOff\n011102c1=bool/kg_center_small_widgets_vertically\n011102c2=bool/kg_enable_camera_default_widget\n011102c3=bool/kg_share_status_area\n011102c4=bool/kg_sim_puk_account_full_screen\n011102c5=bool/kg_top_align_page_shrink_on_bouncer_visible\n011102c6=bool/lockscreen_isPortrait\n011102c7=bool/preferences_prefer_dual_pane\n011102c8=bool/reset_geo_fencing_check_after_boot_or_apm\n011102c9=bool/resolver_landscape_phone\n011102ca=bool/show_ongoing_ime_switcher\n011102cb=bool/skipHoldBeforeMerge\n011102cc=bool/skip_restoring_network_selection\n011102cd=bool/split_action_bar_is_narrow\n011102ce=bool/use_lock_pattern_drawable\n01120000=^attr-private/__removed0\n01120001=^attr-private/__removed1\n01120002=^attr-private/__removed2\n01120003=^attr-private/__removed3\n01120004=^attr-private/__removed4\n01120005=^attr-private/__removed5\n01120006=^attr-private/__removed6\n01120007=^attr-private/accessibilityFocusedDrawable\n01120008=^attr-private/actionModePopupWindowStyle\n01120009=^attr-private/actionModeRedoDrawable\n0112000a=^attr-private/actionModeUndoDrawable\n0112000b=^attr-private/activityChooserViewStyle\n0112000c=^attr-private/activityOpenRemoteViewsEnterAnimation\n0112000d=^attr-private/adjustable\n0112000e=^attr-private/alertDialogButtonGroupStyle\n0112000f=^attr-private/alertDialogCenterButtons\n01120010=^attr-private/allowAutoRevokePermissionsExemption\n01120011=^attr-private/allowMassStorage\n01120012=^attr-private/allowStacking\n01120013=^attr-private/alwaysFocusable\n01120014=^attr-private/aspect\n01120015=^attr-private/autofillDatasetPickerMaxHeight\n01120016=^attr-private/autofillDatasetPickerMaxWidth\n01120017=^attr-private/autofillSaveCustomSubtitleMaxHeight\n01120018=^attr-private/backgroundLeft\n01120019=^attr-private/backgroundRequest\n0112001a=^attr-private/backgroundRequestDetail\n0112001b=^attr-private/backgroundRight\n0112001c=^attr-private/borderBottom\n0112001d=^attr-private/borderLeft\n0112001e=^attr-private/borderRight\n0112001f=^attr-private/borderTop\n01120020=^attr-private/buttonPanelSideLayout\n01120021=^attr-private/calendarViewMode\n01120022=^attr-private/checkMarkGravity\n01120023=^attr-private/clickColor\n01120024=^attr-private/closeItemLayout\n01120025=^attr-private/colorAccentPrimary\n01120026=^attr-private/colorAccentPrimaryVariant\n01120027=^attr-private/colorAccentSecondary\n01120028=^attr-private/colorAccentSecondaryVariant\n01120029=^attr-private/colorAccentTertiary\n0112002a=^attr-private/colorAccentTertiaryVariant\n0112002b=^attr-private/colorListDivider\n0112002c=^attr-private/colorPopupBackground\n0112002d=^attr-private/colorProgressBackgroundNormal\n0112002e=^attr-private/colorSurface\n0112002f=^attr-private/colorSurfaceHeader\n01120030=^attr-private/colorSurfaceHighlight\n01120031=^attr-private/colorSurfaceVariant\n01120032=^attr-private/colorSwitchThumbNormal\n01120033=^attr-private/controllerType\n01120034=^attr-private/cornerRadius\n01120035=^attr-private/dayHighlightColor\n01120036=^attr-private/daySelectorColor\n01120037=^attr-private/defaultQueryHint\n01120038=^attr-private/dialogCustomTitleDecorLayout\n01120039=^attr-private/dialogMode\n0112003a=^attr-private/dialogTitleDecorLayout\n0112003b=^attr-private/dialogTitleIconsDecorLayout\n0112003c=^attr-private/disableChildrenWhenDisabled\n0112003d=^attr-private/dotActivatedColor\n0112003e=^attr-private/dotColor\n0112003f=^attr-private/dotSize\n01120040=^attr-private/drawableAlpha\n01120041=^attr-private/dreamActivityCloseExitAnimation\n01120042=^attr-private/dreamActivityOpenEnterAnimation\n01120043=^attr-private/dreamActivityOpenExitAnimation\n01120044=^attr-private/dropdownListPreferredItemHeight\n01120045=^attr-private/emergencyInstaller\n01120046=^attr-private/emulated\n01120047=^attr-private/enableControlView\n01120048=^attr-private/enableSubtitle\n01120049=^attr-private/enlargeVertexEntryArea\n0112004a=^attr-private/errorColor\n0112004b=^attr-private/errorMessageAboveBackground\n0112004c=^attr-private/errorMessageBackground\n0112004d=^attr-private/expandActivityOverflowButtonDrawable\n0112004e=^attr-private/externalRouteEnabledDrawable\n0112004f=^attr-private/fadedHeight\n01120050=^attr-private/findOnPageNextDrawable\n01120051=^attr-private/findOnPagePreviousDrawable\n01120052=^attr-private/floatingToolbarCloseDrawable\n01120053=^attr-private/floatingToolbarDividerColor\n01120054=^attr-private/floatingToolbarItemBackgroundBorderlessDrawable\n01120055=^attr-private/floatingToolbarItemBackgroundDrawable\n01120056=^attr-private/floatingToolbarOpenDrawable\n01120057=^attr-private/foregroundInsidePadding\n01120058=^attr-private/fragmentBreadCrumbsStyle\n01120059=^attr-private/frameDuration\n0112005a=^attr-private/framesCount\n0112005b=^attr-private/fromBottom\n0112005c=^attr-private/fromLeft\n0112005d=^attr-private/fromRight\n0112005e=^attr-private/fromTop\n0112005f=^attr-private/gestureOverlayViewStyle\n01120060=^attr-private/glowDot\n01120061=^attr-private/glyphDrawable\n01120062=^attr-private/glyphMap\n01120063=^attr-private/hasRoundedCorners\n01120064=^attr-private/headerLayout\n01120065=^attr-private/headerRemoveIconIfEmpty\n01120066=^attr-private/headerTextColor\n01120067=^attr-private/hideWheelUntilFocused\n01120068=^attr-private/horizontalProgressLayout\n01120069=^attr-private/iconfactoryBadgeSize\n0112006a=^attr-private/iconfactoryIconSize\n0112006b=^attr-private/ignoreOffsetTopLimit\n0112006c=^attr-private/initialActivityCount\n0112006d=^attr-private/internalLayout\n0112006e=^attr-private/internalMaxHeight\n0112006f=^attr-private/internalMaxWidth\n01120070=^attr-private/internalMinHeight\n01120071=^attr-private/internalMinWidth\n01120072=^attr-private/interpolatorX\n01120073=^attr-private/interpolatorY\n01120074=^attr-private/interpolatorZ\n01120075=^attr-private/itemColor\n01120076=^attr-private/itemLayout\n01120077=^attr-private/keepDotActivated\n01120078=^attr-private/keyboardViewStyle\n01120079=^attr-private/layoutManager\n0112007a=^attr-private/layout_alwaysShow\n0112007b=^attr-private/layout_childType\n0112007c=^attr-private/layout_hasNestedScrollIndicator\n0112007d=^attr-private/layout_ignoreOffset\n0112007e=^attr-private/layout_maxHeight\n0112007f=^attr-private/layout_removeBorders\n01120080=^attr-private/leftToRight\n01120081=^attr-private/legacyLayout\n01120082=^attr-private/lightRadius\n01120083=^attr-private/lightY\n01120084=^attr-private/lightZ\n01120085=^attr-private/listItemLayout\n01120086=^attr-private/listLayout\n01120087=^attr-private/lockPatternStyle\n01120088=^attr-private/magnifierColorOverlay\n01120089=^attr-private/magnifierElevation\n0112008a=^attr-private/magnifierHeight\n0112008b=^attr-private/magnifierHorizontalOffset\n0112008c=^attr-private/magnifierStyle\n0112008d=^attr-private/magnifierVerticalOffset\n0112008e=^attr-private/magnifierWidth\n0112008f=^attr-private/magnifierZoom\n01120090=^attr-private/majorWeightMax\n01120091=^attr-private/majorWeightMin\n01120092=^attr-private/maxCollapsedHeight\n01120093=^attr-private/maxCollapsedHeightSmall\n01120094=^attr-private/maxFileSize\n01120095=^attr-private/maxItems\n01120096=^attr-private/minorWeightMax\n01120097=^attr-private/minorWeightMin\n01120098=^attr-private/modifier\n01120099=^attr-private/modifierState\n0112009a=^attr-private/monthTextAppearance\n0112009b=^attr-private/mountPoint\n0112009c=^attr-private/mtpReserve\n0112009d=^attr-private/multiChoiceItemLayout\n0112009e=^attr-private/navigationButtonStyle\n0112009f=^attr-private/needsDefaultBackgrounds\n011200a0=^attr-private/notificationHeaderAppNameVisibility\n011200a1=^attr-private/notificationHeaderIconSize\n011200a2=^attr-private/notificationHeaderStyle\n011200a3=^attr-private/notificationHeaderTextAppearance\n011200a4=^attr-private/numDots\n011200a5=^attr-private/opacityListDivider\n011200a6=^attr-private/outKeycode\n011200a7=^attr-private/paddingBottomNoButtons\n011200a8=^attr-private/paddingTopNoTitle\n011200a9=^attr-private/pageSpacing\n011200aa=^attr-private/panelMenuIsCompact\n011200ab=^attr-private/panelMenuListTheme\n011200ac=^attr-private/panelMenuListWidth\n011200ad=^attr-private/pathColor\n011200ae=^attr-private/pointerIconAlias\n011200af=^attr-private/pointerIconAllScroll\n011200b0=^attr-private/pointerIconArrow\n011200b1=^attr-private/pointerIconCell\n011200b2=^attr-private/pointerIconContextMenu\n011200b3=^attr-private/pointerIconCopy\n011200b4=^attr-private/pointerIconCrosshair\n011200b5=^attr-private/pointerIconGrab\n011200b6=^attr-private/pointerIconGrabbing\n011200b7=^attr-private/pointerIconHand\n011200b8=^attr-private/pointerIconHandwriting\n011200b9=^attr-private/pointerIconHelp\n011200ba=^attr-private/pointerIconHorizontalDoubleArrow\n011200bb=^attr-private/pointerIconNodrop\n011200bc=^attr-private/pointerIconSpotAnchor\n011200bd=^attr-private/pointerIconSpotHover\n011200be=^attr-private/pointerIconSpotTouch\n011200bf=^attr-private/pointerIconText\n011200c0=^attr-private/pointerIconTopLeftDiagonalDoubleArrow\n011200c1=^attr-private/pointerIconTopRightDiagonalDoubleArrow\n011200c2=^attr-private/pointerIconVectorFill\n011200c3=^attr-private/pointerIconVectorFillInverse\n011200c4=^attr-private/pointerIconVectorStroke\n011200c5=^attr-private/pointerIconVectorStrokeInverse\n011200c6=^attr-private/pointerIconVerticalDoubleArrow\n011200c7=^attr-private/pointerIconVerticalText\n011200c8=^attr-private/pointerIconWait\n011200c9=^attr-private/pointerIconZoomIn\n011200ca=^attr-private/pointerIconZoomOut\n011200cb=^attr-private/popupPromptView\n011200cc=^attr-private/position\n011200cd=^attr-private/preferenceActivityStyle\n011200ce=^attr-private/preferenceFragmentListStyle\n011200cf=^attr-private/preferenceFragmentPaddingSide\n011200d0=^attr-private/preferenceFrameLayoutStyle\n011200d1=^attr-private/preferenceHeaderPanelStyle\n011200d2=^attr-private/preferenceListStyle\n011200d3=^attr-private/preferencePanelStyle\n011200d4=^attr-private/preserveIconSpacing\n011200d5=^attr-private/primary\n011200d6=^attr-private/productId\n011200d7=^attr-private/progressBarCornerRadius\n011200d8=^attr-private/progressLayout\n011200d9=^attr-private/quickContactBadgeOverlay\n011200da=^attr-private/quickContactWindowSize\n011200db=^attr-private/regularColor\n011200dc=^attr-private/relativeTimeDisambiguationText\n011200dd=^attr-private/relativeTimeUnitDisplayLength\n011200de=^attr-private/removable\n011200df=^attr-private/removeBeforeMRelease\n011200e0=^attr-private/request\n011200e1=^attr-private/requestDetail\n011200e2=^attr-private/resOutColor\n011200e3=^attr-private/reverseLayout\n011200e4=^attr-private/screenLayout\n011200e5=^attr-private/scrollCaptureHint\n011200e6=^attr-private/scrollIndicatorPaddingLeft\n011200e7=^attr-private/scrollIndicatorPaddingRight\n011200e8=^attr-private/searchDialogTheme\n011200e9=^attr-private/searchResultListItemHeight\n011200ea=^attr-private/searchWidgetCorpusItemBackground\n011200eb=^attr-private/seekBarDialogPreferenceStyle\n011200ec=^attr-private/seekBarPreferenceStyle\n011200ed=^attr-private/segMinWidth\n011200ee=^attr-private/segPointGap\n011200ef=^attr-private/segSegGap\n011200f0=^attr-private/selectionDivider\n011200f1=^attr-private/selectionDividersDistance\n011200f2=^attr-private/selectionScrollOffset\n011200f3=^attr-private/showAtTop\n011200f4=^attr-private/showRelative\n011200f5=^attr-private/showSeekBarValue\n011200f6=^attr-private/showTitle\n011200f7=^attr-private/showWallpaper\n011200f8=^attr-private/singleChoiceItemLayout\n011200f9=^attr-private/spanCount\n011200fa=^attr-private/stackFromEnd\n011200fb=^attr-private/state_accessibility_focused\n011200fc=^attr-private/state_ux_restricted\n011200fd=^attr-private/storageDescription\n011200fe=^attr-private/strokeCap\n011200ff=^attr-private/successColor\n01120100=^attr-private/tabLayout\n01120101=^attr-private/textAppearanceAutoCorrectionSuggestion\n01120102=^attr-private/textAppearanceEasyCorrectSuggestion\n01120103=^attr-private/textAppearanceGrammarErrorSuggestion\n01120104=^attr-private/textAppearanceMisspelledSuggestion\n01120105=^attr-private/textColorOnAccent\n01120106=^attr-private/textColorPrimaryActivated\n01120107=^attr-private/textColorSearchUrl\n01120108=^attr-private/textColorSecondaryActivated\n01120109=^attr-private/textEditSuggestionContainerLayout\n0112010a=^attr-private/textEditSuggestionHighlightStyle\n0112010b=^attr-private/textUnderlineColor\n0112010c=^attr-private/textUnderlineThickness\n0112010d=^attr-private/thumbDrawable\n0112010e=^attr-private/thumbMinHeight\n0112010f=^attr-private/thumbMinWidth\n01120110=^attr-private/toBottom\n01120111=^attr-private/toLeft\n01120112=^attr-private/toRight\n01120113=^attr-private/toTop\n01120114=^attr-private/toastFrameBackground\n01120115=^attr-private/tooltipBackgroundColor\n01120116=^attr-private/tooltipCornerRadius\n01120117=^attr-private/tooltipFontSize\n01120118=^attr-private/tooltipForegroundColor\n01120119=^attr-private/tooltipFrameBackground\n0112011a=^attr-private/tooltipHorizontalPadding\n0112011b=^attr-private/tooltipVerticalPadding\n0112011c=^attr-private/trackDrawable\n0112011d=^attr-private/tracker\n0112011e=^attr-private/trackerHeight\n0112011f=^attr-private/unlockProfile\n01120120=^attr-private/updatableSystem\n01120121=^attr-private/useDisabledAlpha\n01120122=^attr-private/vendorId\n01120123=^attr-private/viewType\n01120124=^attr-private/virtualButtonPressedDrawable\n01120125=^attr-private/windowActionBarFullscreenDecorLayout\n01120126=^attr-private/windowFixedHeightMajor\n01120127=^attr-private/windowFixedHeightMinor\n01120128=^attr-private/windowFixedWidthMajor\n01120129=^attr-private/windowFixedWidthMinor\n0112012a=^attr-private/windowOutsetBottom\n0112012b=^attr-private/yearListItemActivatedTextAppearance\n0112012c=^attr-private/showWallpaper\n0112012d=^attr-private/singleChoiceItemLayout\n0112012e=^attr-private/spanCount\n0112012f=^attr-private/stackFromEnd\n01120130=^attr-private/state_accessibility_focused\n01120131=^attr-private/state_ux_restricted\n01120132=^attr-private/storageDescription\n01120133=^attr-private/successColor\n01120134=^attr-private/tabLayout\n01120135=^attr-private/textAppearanceAutoCorrectionSuggestion\n01120136=^attr-private/textAppearanceEasyCorrectSuggestion\n01120137=^attr-private/textAppearanceGrammarErrorSuggestion\n01120138=^attr-private/textAppearanceMisspelledSuggestion\n01120139=^attr-private/textColorOnAccent\n0112013a=^attr-private/textColorPrimaryActivated\n0112013b=^attr-private/textColorSearchUrl\n0112013c=^attr-private/textColorSecondaryActivated\n0112013d=^attr-private/textEditSuggestionContainerLayout\n0112013e=^attr-private/textEditSuggestionHighlightStyle\n0112013f=^attr-private/textUnderlineColor\n01120140=^attr-private/textUnderlineThickness\n01120141=^attr-private/thumbDrawable\n01120142=^attr-private/thumbMinHeight\n01120143=^attr-private/thumbMinWidth\n01120144=^attr-private/toBottom\n01120145=^attr-private/toLeft\n01120146=^attr-private/toRight\n01120147=^attr-private/toTop\n01120148=^attr-private/toastFrameBackground\n01120149=^attr-private/tooltipBackgroundColor\n0112014a=^attr-private/tooltipForegroundColor\n0112014b=^attr-private/tooltipFrameBackground\n0112014c=^attr-private/trackDrawable\n0112014d=^attr-private/unlockProfile\n0112014e=^attr-private/updatableSystem\n0112014f=^attr-private/useDisabledAlpha\n01120150=^attr-private/vendorId\n01120151=^attr-private/viewType\n01120152=^attr-private/virtualButtonPressedDrawable\n01120153=^attr-private/windowActionBarFullscreenDecorLayout\n01120154=^attr-private/windowFixedHeightMajor\n01120155=^attr-private/windowFixedHeightMinor\n01120156=^attr-private/windowFixedWidthMajor\n01120157=^attr-private/windowFixedWidthMinor\n01120158=^attr-private/windowOutsetBottom\n01120159=^attr-private/yearListItemActivatedTextAppearance\n01130000=fraction/config_autoBrightnessAdjustmentMaxGamma\n01130001=fraction/config_biometricNotificationFrrThreshold\n01130002=fraction/config_dimBehindFadeDuration\n01130003=fraction/config_maximumScreenDimRatio\n01130004=fraction/config_prescaleAbsoluteVolume_index1\n01130005=fraction/config_prescaleAbsoluteVolume_index2\n01130006=fraction/config_prescaleAbsoluteVolume_index3\n01130007=fraction/config_screenAutoBrightnessDozeScaleFactor\n01130008=fraction/docked_stack_divider_fixed_ratio\n01130009=fraction/global_actions_horizontal_padding_percentage\n0113000a=fraction/global_actions_vertical_padding_percentage\n0113000b=fraction/input_extract_action_margin_bottom\n0113000c=fraction/input_extract_layout_height\n0113000d=fraction/input_extract_layout_padding_left\n0113000e=fraction/input_extract_layout_padding_left_no_action\n0113000f=fraction/input_extract_layout_padding_right\n01130010=fraction/input_extract_text_margin_bottom\n01130011=fraction/thumbnail_fullscreen_scale\n01130012=plurals/duration_seconds\n01130013=plurals/duration_minutes\n01130014=plurals/duration_hours\n01130015=plurals/wifi_available\n01130016=plurals/wifi_available_detailed\n01130017=plurals/matches_found\n01130018=plurals/restr_pin_countdown\n01140000=menu/language_selection_list\n01140001=menu/webview_copy\n01140002=menu/webview_find\n01140003=plurals/last_num_days\n01140004=plurals/duration_seconds\n01140005=plurals/duration_minutes\n01140006=plurals/duration_hours\n01140007=plurals/duration_minutes_shortest\n01140008=plurals/duration_hours_shortest\n01140009=plurals/duration_days_shortest\n0114000a=plurals/duration_years_shortest\n0114000b=plurals/duration_minutes_shortest_future\n0114000c=plurals/duration_hours_shortest_future\n0114000d=plurals/duration_days_shortest_future\n0114000e=plurals/duration_years_shortest_future\n0114000f=plurals/duration_minutes_relative\n01140010=plurals/duration_hours_relative\n01140011=plurals/duration_days_relative\n01140012=plurals/duration_years_relative\n01140013=plurals/duration_minutes_relative_future\n01140014=plurals/duration_hours_relative_future\n01140015=plurals/duration_days_relative_future\n01140016=plurals/duration_years_relative_future\n01140017=plurals/wifi_available\n01140018=plurals/wifi_available_detailed\n01140019=plurals/matches_found\n0114001a=plurals/restr_pin_countdown\n0114001b=plurals/zen_mode_duration_minutes_summary\n0114001c=plurals/zen_mode_duration_minutes_summary_short\n0114001d=plurals/zen_mode_duration_hours_summary\n0114001e=plurals/zen_mode_duration_hours_summary_short\n0114001f=plurals/zen_mode_duration_minutes\n01140020=plurals/zen_mode_duration_minutes_short\n01140021=plurals/zen_mode_duration_hours\n01140022=plurals/zen_mode_duration_hours_short\n01140023=plurals/selected_count\n01150000=plurals/pinpuk_attempts\n01150001=plurals/bugreport_countdown\n01150002=plurals/duration_days_relative\n01150003=plurals/duration_days_relative_future\n01150004=plurals/duration_days_shortest\n01150005=plurals/duration_days_shortest_future\n01150006=plurals/duration_hours_relative\n01150007=plurals/duration_hours_relative_future\n01150008=plurals/duration_hours_shortest\n01150009=plurals/duration_hours_shortest_future\n0115000a=plurals/duration_minutes_relative\n0115000b=plurals/duration_minutes_relative_future\n0115000c=plurals/duration_minutes_shortest\n0115000d=plurals/duration_minutes_shortest_future\n0115000e=plurals/duration_years_relative\n0115000f=plurals/duration_years_relative_future\n01150010=plurals/duration_years_shortest\n01150011=plurals/duration_years_shortest_future\n01150012=plurals/file_count\n01150013=plurals/kg_too_many_failed_attempts_countdown\n01150014=plurals/last_num_days\n01150015=plurals/matches_found\n01150016=plurals/pinpuk_attempts\n01150017=plurals/restr_pin_countdown\n01150018=plurals/selected_count\n01150019=plurals/ssl_ca_cert_warning\n0115001a=plurals/zen_mode_duration_hours\n0115001b=plurals/zen_mode_duration_hours_short\n0115001c=plurals/zen_mode_duration_hours_summary\n0115001d=plurals/zen_mode_duration_hours_summary_short\n0115001e=plurals/zen_mode_duration_minutes\n0115001f=plurals/zen_mode_duration_minutes_short\n01150020=plurals/zen_mode_duration_minutes_summary\n01150021=plurals/zen_mode_duration_minutes_summary_short\n01150022=plurals/zen_mode_duration_minutes_summary\n01150023=plurals/zen_mode_duration_minutes_summary_short\n01160000=^attr-private/isLightTheme\n01160001=^attr-private/textColorPrimaryActivated\n01160002=^attr-private/textColorSecondaryActivated\n01160003=^attr-private/textColorSearchUrl\n01160004=^attr-private/searchWidgetCorpusItemBackground\n01160005=^attr-private/textAppearanceEasyCorrectSuggestion\n01160006=^attr-private/textAppearanceMisspelledSuggestion\n01160007=^attr-private/textAppearanceAutoCorrectionSuggestion\n01160008=^attr-private/textUnderlineColor\n01160009=^attr-private/textUnderlineThickness\n0116000a=^attr-private/errorMessageBackground\n0116000b=^attr-private/errorMessageAboveBackground\n0116000c=^attr-private/searchResultListItemHeight\n0116000d=^attr-private/dropdownListPreferredItemHeight\n0116000e=^attr-private/windowActionBarFullscreenDecorLayout\n0116000f=^attr-private/floatingToolbarCloseDrawable\n01160010=^attr-private/floatingToolbarForegroundColor\n01160011=^attr-private/floatingToolbarItemBackgroundBorderlessDrawable\n01160012=^attr-private/floatingToolbarItemBackgroundDrawable\n01160013=^attr-private/floatingToolbarOpenDrawable\n01160014=^attr-private/floatingToolbarPopupBackgroundDrawable\n01160015=^attr-private/alertDialogButtonGroupStyle\n01160016=^attr-private/alertDialogCenterButtons\n01160017=^attr-private/panelMenuIsCompact\n01160018=^attr-private/panelMenuListWidth\n01160019=^attr-private/panelMenuListTheme\n0116001a=^attr-private/gestureOverlayViewStyle\n0116001b=^attr-private/quickContactBadgeOverlay\n0116001c=^attr-private/fragmentBreadCrumbsStyle\n0116001d=^attr-private/activityChooserViewStyle\n0116001e=^attr-private/actionModePopupWindowStyle\n0116001f=^attr-private/preferenceActivityStyle\n01160020=^attr-private/seekBarDialogPreferenceStyle\n01160021=^attr-private/preferencePanelStyle\n01160022=^attr-private/preferenceHeaderPanelStyle\n01160023=^attr-private/preferenceListStyle\n01160024=^attr-private/preferenceFragmentListStyle\n01160025=^attr-private/preferenceFragmentPaddingSide\n01160026=^attr-private/seekBarPreferenceStyle\n01160027=^attr-private/textEditSuggestionContainerLayout\n01160028=^attr-private/textEditSuggestionHighlightStyle\n01160029=^attr-private/dialogTitleIconsDecorLayout\n0116002a=^attr-private/dialogCustomTitleDecorLayout\n0116002b=^attr-private/dialogTitleDecorLayout\n0116002c=^attr-private/toastFrameBackground\n0116002d=^attr-private/searchDialogTheme\n0116002e=^attr-private/preferenceFrameLayoutStyle\n0116002f=^attr-private/accessibilityFocusedDrawable\n01160030=^attr-private/findOnPageNextDrawable\n01160031=^attr-private/findOnPagePreviousDrawable\n01160032=^attr-private/colorSwitchThumbNormal\n01160033=^attr-private/lightY\n01160034=^attr-private/lightZ\n01160035=^attr-private/lightRadius\n01160036=^attr-private/windowFixedWidthMajor\n01160037=^attr-private/windowFixedHeightMinor\n01160038=^attr-private/windowFixedWidthMinor\n01160039=^attr-private/windowFixedHeightMajor\n0116003a=^attr-private/windowOutsetBottom\n0116003b=^attr-private/buttonPanelSideLayout\n0116003c=^attr-private/listLayout\n0116003d=^attr-private/multiChoiceItemLayout\n0116003e=^attr-private/singleChoiceItemLayout\n0116003f=^attr-private/listItemLayout\n01160040=^attr-private/progressLayout\n01160041=^attr-private/horizontalProgressLayout\n01160042=^attr-private/showTitle\n01160043=^attr-private/needsDefaultBackgrounds\n01160044=^attr-private/controllerType\n01160045=^attr-private/allowStacking\n01160046=^attr-private/activityOpenRemoteViewsEnterAnimation\n01160047=^attr-private/foregroundInsidePadding\n01160048=^attr-private/paddingBottomNoButtons\n01160049=^attr-private/paddingTopNoTitle\n0116004a=^attr-private/checkMarkGravity\n0116004b=^attr-private/thumbDrawable\n0116004c=^attr-private/thumbMinWidth\n0116004d=^attr-private/thumbMinHeight\n0116004e=^attr-private/trackDrawable\n0116004f=^attr-private/backgroundRight\n01160050=^attr-private/backgroundLeft\n01160051=^attr-private/position\n01160052=^attr-private/drawableAlpha\n01160053=^attr-private/borderTop\n01160054=^attr-private/borderBottom\n01160055=^attr-private/borderLeft\n01160056=^attr-private/borderRight\n01160057=^attr-private/layout_removeBorders\n01160058=^attr-private/preserveIconSpacing\n01160059=^attr-private/maxItems\n0116005a=^attr-private/useDisabledAlpha\n0116005b=^attr-private/resOutColor\n0116005c=^attr-private/clickColor\n0116005d=^attr-private/tabLayout\n0116005e=^attr-private/popupPromptView\n0116005f=^attr-private/disableChildrenWhenDisabled\n01160060=^attr-private/internalLayout\n01160061=^attr-private/headerTextColor\n01160062=^attr-private/yearListItemActivatedTextAppearance\n01160063=^attr-private/dialogMode\n01160064=^attr-private/quickContactWindowSize\n01160065=^attr-private/majorWeightMin\n01160066=^attr-private/minorWeightMin\n01160067=^attr-private/majorWeightMax\n01160068=^attr-private/minorWeightMax\n01160069=^attr-private/monthTextAppearance\n0116006a=^attr-private/daySelectorColor\n0116006b=^attr-private/dayHighlightColor\n0116006c=^attr-private/calendarViewMode\n0116006d=^attr-private/selectionDivider\n0116006e=^attr-private/selectionDividerHeight\n0116006f=^attr-private/selectionDividersDistance\n01160070=^attr-private/internalMinHeight\n01160071=^attr-private/internalMaxHeight\n01160072=^attr-private/internalMinWidth\n01160073=^attr-private/internalMaxWidth\n01160074=^attr-private/virtualButtonPressedDrawable\n01160075=^attr-private/hideWheelUntilFocused\n01160076=^attr-private/legacyLayout\n01160077=^attr-private/frameDuration\n01160078=^attr-private/framesCount\n01160079=^attr-private/opticalInsetLeft\n0116007a=^attr-private/opticalInsetTop\n0116007b=^attr-private/opticalInsetRight\n0116007c=^attr-private/opticalInsetBottom\n0116007d=^attr-private/interpolatorX\n0116007e=^attr-private/interpolatorY\n0116007f=^attr-private/interpolatorZ\n01160080=^attr-private/removeBeforeMRelease\n01160081=^attr-private/state_accessibility_focused\n01160082=^attr-private/initialActivityCount\n01160083=^attr-private/expandActivityOverflowButtonDrawable\n01160084=^attr-private/keyboardViewStyle\n01160085=^attr-private/aspect\n01160086=^attr-private/pathColor\n01160087=^attr-private/regularColor\n01160088=^attr-private/errorColor\n01160089=^attr-private/successColor\n0116008a=^attr-private/closeItemLayout\n0116008b=^attr-private/defaultQueryHint\n0116008c=^attr-private/pointerIconArrow\n0116008d=^attr-private/pointerIconSpotHover\n0116008e=^attr-private/pointerIconSpotTouch\n0116008f=^attr-private/pointerIconSpotAnchor\n01160090=^attr-private/pointerIconContextMenu\n01160091=^attr-private/pointerIconHand\n01160092=^attr-private/pointerIconHelp\n01160093=^attr-private/pointerIconWait\n01160094=^attr-private/pointerIconCell\n01160095=^attr-private/pointerIconCrosshair\n01160096=^attr-private/pointerIconText\n01160097=^attr-private/pointerIconVerticalText\n01160098=^attr-private/pointerIconAlias\n01160099=^attr-private/pointerIconCopy\n0116009a=^attr-private/pointerIconNodrop\n0116009b=^attr-private/pointerIconAllScroll\n0116009c=^attr-private/pointerIconHorizontalDoubleArrow\n0116009d=^attr-private/pointerIconVerticalDoubleArrow\n0116009e=^attr-private/pointerIconTopRightDiagonalDoubleArrow\n0116009f=^attr-private/pointerIconTopLeftDiagonalDoubleArrow\n011600a0=^attr-private/pointerIconZoomIn\n011600a1=^attr-private/pointerIconZoomOut\n011600a2=^attr-private/pointerIconGrab\n011600a3=^attr-private/pointerIconGrabbing\n011600a4=^attr-private/mountPoint\n011600a5=^attr-private/storageDescription\n011600a6=^attr-private/primary\n011600a7=^attr-private/removable\n011600a8=^attr-private/emulated\n011600a9=^attr-private/mtpReserve\n011600aa=^attr-private/allowMassStorage\n011600ab=^attr-private/maxFileSize\n011600ac=^attr-private/screenLayout\n011600ad=^attr-private/headerLayout\n011600ae=^attr-private/headerRemoveIconIfEmpty\n011600af=^attr-private/locale\n011600b0=^attr-private/vendorId\n011600b1=^attr-private/productId\n011600b2=^attr-private/externalRouteEnabledDrawable\n011600b3=^attr-private/pageSpacing\n011600b4=^attr-private/scrollIndicatorPaddingLeft\n011600b5=^attr-private/scrollIndicatorPaddingRight\n011600b6=^attr-private/dotSize\n011600b7=^attr-private/numDots\n011600b8=^attr-private/glowDot\n011600b9=^attr-private/leftToRight\n011600ba=^attr-private/layout_childType\n011600bb=^attr-private/itemLayout\n011600bc=^attr-private/itemColor\n011600bd=^attr-private/navigationButtonStyle\n011600be=^attr-private/maxCollapsedHeight\n011600bf=^attr-private/maxCollapsedHeightSmall\n011600c0=^attr-private/showRelative\n011600c1=^attr-private/layout_alwaysShow\n011600c2=^attr-private/layout_ignoreOffset\n011600c3=^attr-private/layout_hasNestedScrollIndicator\n011600c4=^attr-private/cantSaveState\n011600c5=^attr-private/systemUserOnly\n011600c6=^attr-private/alwaysFocusable\n011600c7=^attr-private/minimalWidth\n011600c8=^attr-private/minimalHeight\n01170000=xml/apns\n01170001=xml/audio_assets\n01170002=xml/autofill_compat_accessibility_service\n01170003=xml/autotext\n01170004=xml/bookmarks\n01170005=xml/color_extraction\n01170006=xml/config_user_types\n01170007=xml/config_webview_packages\n01170008=xml/default_zen_mode_config\n01170009=xml/global_keys\n0117000a=xml/haptic_feedback_customization\n0117000b=xml/haptic_feedback_customization_source_rotary_encoder\n0117000c=xml/haptic_feedback_customization_source_touchscreen\n0117000d=xml/irq_device_map\n0117000e=xml/kg_password_kbd_numeric\n0117000f=xml/password_kbd_extension\n01170010=xml/password_kbd_numeric\n01170011=xml/password_kbd_popup_template\n01170012=xml/password_kbd_qwerty\n01170013=xml/password_kbd_qwerty_shifted\n01170014=xml/password_kbd_symbols\n01170015=xml/password_kbd_symbols_shift\n01170016=xml/power_profile\n01170017=xml/power_profile_test\n01170018=xml/sms_7bit_translation_table\n01170019=xml/sms_short_codes\n0117001a=xml/storage_list\n01a60000=integer/config_motionStandardFastSpatialStiffness\n01a60001=integer/config_motionStandardFastEffectStiffness\n01a60002=integer/config_motionStandardDefaultSpatialStiffness\n01a60003=integer/config_motionStandardDefaultEffectStiffness\n01a60004=integer/config_motionStandardSlowSpatialStiffness\n01a60005=integer/config_motionStandardSlowEffectStiffness\n01a60006=integer/config_motionExpressiveFastSpatialStiffness\n01a60007=integer/config_motionExpressiveFastEffectStiffness\n01a60008=integer/config_motionExpressiveDefaultSpatialStiffness\n01a60009=integer/config_motionExpressiveDefaultEffectStiffness\n01a6000a=integer/config_motionExpressiveSlowSpatialStiffness\n01a6000b=integer/config_motionExpressiveSlowEffectStiffness\n01ae0000=color/system_inverse_on_surface_light\n01ae0001=color/system_inverse_primary_light\n01ae0002=color/system_inverse_surface_light\n01ae0003=color/system_scrim_light\n01ae0004=color/system_shadow_light\n01ae0005=color/system_surface_tint_light\n01ae0006=color/system_inverse_on_surface_dark\n01ae0007=color/system_inverse_primary_dark\n01ae0008=color/system_inverse_surface_dark\n01ae0009=color/system_scrim_dark\n01ae000a=color/system_shadow_dark\n01ae000b=color/system_surface_tint_dark\n01af0000=dimen/config_motionStandardFastSpatialDamping\n01af0001=dimen/config_motionStandardFastEffectDamping\n01af0002=dimen/config_motionStandardDefaultSpatialDamping\n01af0003=dimen/config_motionStandardDefaultEffectDamping\n01af0004=dimen/config_motionStandardSlowSpatialDamping\n01af0005=dimen/config_motionStandardSlowEffectDamping\n01af0006=dimen/config_motionExpressiveFastSpatialDamping\n01af0007=dimen/config_motionExpressiveFastEffectDamping\n01af0008=dimen/config_motionExpressiveDefaultSpatialDamping\n01af0009=dimen/config_motionExpressiveDefaultEffectDamping\n01af000a=dimen/config_motionExpressiveSlowSpatialDamping\n01af000b=dimen/config_motionExpressiveSlowEffectDamping\n01af000c=dimen/config_shapeCornerRadiusXsmall\n01af000d=dimen/config_shapeCornerRadiusSmall\n01af000e=dimen/config_shapeCornerRadiusMedium\n01af000f=dimen/config_shapeCornerRadiusLarge\n01af0010=dimen/config_shapeCornerRadiusXlarge\n01b00000=string/config_defaultOnDeviceIntelligenceService\n01b00001=string/config_defaultOnDeviceSandboxedInferenceService\n01b00002=string/config_defaultOnDeviceIntelligenceDeviceConfigNamespace\n01b20001=id/accessibilityActionSetExtendedSelection\n01b30000=attr/optional\n01b30001=attr/alternateLauncherIcons\n01b30002=attr/alternateLauncherLabels\n01b40000=string/config_systemDependencyInstaller\n01b40002=string/config_systemVendorIntelligence\n01b70000=attr/optional\n01b70001=attr/adServiceTypes\n01b70002=attr/languageSettingsActivity\n01b70003=attr/dreamCategory\n01b70004=attr/backgroundPermission\n01b70005=attr/supplementalDescription\n01b70006=attr/intentMatchingFlags\n01b70007=attr/layoutLabel\n01b7000a=attr/pageSizeCompat\n01b7000b=attr/wantsRoleHolderPriority\n01b80000=color/system_surface_disabled\n01b80001=color/system_on_surface_disabled\n01b80002=color/system_outline_disabled\n01b80003=color/system_error_0\n01b80004=color/system_error_10\n01b80005=color/system_error_50\n01b80006=color/system_error_100\n01b80007=color/system_error_200\n01b80008=color/system_error_300\n01b80009=color/system_error_400\n01b8000a=color/system_error_500\n01b8000b=color/system_error_600\n01b8000c=color/system_error_700\n01b8000d=color/system_error_800\n01b8000e=color/system_error_900\n01b8000f=color/system_error_1000\n01ba0000=string/config_defaultRetailDemo\n01ba0001=string/config_defaultWallet\n01bd0000=attr/defaultLocale\n01bd0001=attr/isVirtualDeviceOnly\n01bd0004=attr/featureFlag\n01bd0005=attr/systemUserOnly\n01bd0006=attr/allow\n01bd0007=attr/query\n01bd0008=attr/queryPrefix\n01bd0009=attr/queryPattern\n01bd000a=attr/queryAdvancedPattern\n01bd000b=attr/querySuffix\n01bd000c=attr/fragmentPrefix\n01bd000d=attr/fragmentPattern\n01bd000e=attr/fragmentAdvancedPattern\n01bd000f=attr/fragmentSuffix\n01bd0010=attr/useBoundsForWidth\n01bd0011=attr/autoTransact\n01bd0012=attr/windowOptOutEdgeToEdgeEnforcement\n01bd0013=attr/requireContentUriPermissionFromCaller\n01bd0015=attr/useLocalePreferredLineHeightForMinimum\n01bd0016=attr/contentSensitivity\n01bd0017=attr/supportsConnectionlessStylusHandwriting\n01bd0018=attr/shouldDefaultToObserveMode\n01bd0019=attr/allowCrossUidActivitySwitchFromBelow\n01bd001a=attr/shiftDrawingOffsetForStartOverhang\n01bd001b=attr/windowIsFrameRatePowerSavingsBalanced\n01be0000=bool/config_safetyProtectionEnabled\n01be0001=bool/config_enableDefaultNotes\n01be0002=bool/config_enableDefaultNotesForWorkProfile\n01c90000=color/system_primary_container_light\n01c90001=color/system_on_primary_container_light\n01c90002=color/system_primary_light\n01c90003=color/system_on_primary_light\n01c90004=color/system_secondary_container_light\n01c90005=color/system_on_secondary_container_light\n01c90006=color/system_secondary_light\n01c90007=color/system_on_secondary_light\n01c90008=color/system_tertiary_container_light\n01c90009=color/system_on_tertiary_container_light\n01c9000a=color/system_tertiary_light\n01c9000b=color/system_on_tertiary_light\n01c9000c=color/system_background_light\n01c9000d=color/system_on_background_light\n01c9000e=color/system_surface_light\n01c9000f=color/system_on_surface_light\n01c90010=color/system_surface_container_low_light\n01c90011=color/system_surface_container_lowest_light\n01c90012=color/system_surface_container_light\n01c90013=color/system_surface_container_high_light\n01c90014=color/system_surface_container_highest_light\n01c90015=color/system_surface_bright_light\n01c90016=color/system_surface_dim_light\n01c90017=color/system_surface_variant_light\n01c90018=color/system_on_surface_variant_light\n01c90019=color/system_outline_light\n01c9001a=color/system_error_light\n01c9001b=color/system_on_error_light\n01c9001c=color/system_error_container_light\n01c9001d=color/system_on_error_container_light\n01c9002a=color/system_control_activated_light\n01c9002b=color/system_control_normal_light\n01c9002c=color/system_control_highlight_light\n01c9002d=color/system_text_primary_inverse_light\n01c9002e=color/system_text_secondary_and_tertiary_inverse_light\n01c9002f=color/system_text_primary_inverse_disable_only_light\n01c90030=color/system_text_secondary_and_tertiary_inverse_disabled_light\n01c90031=color/system_text_hint_inverse_light\n01c90032=color/system_palette_key_color_primary_light\n01c90033=color/system_palette_key_color_secondary_light\n01c90034=color/system_palette_key_color_tertiary_light\n01c90035=color/system_palette_key_color_neutral_light\n01c90036=color/system_palette_key_color_neutral_variant_light\n01c90037=color/system_primary_container_dark\n01c90038=color/system_on_primary_container_dark\n01c90039=color/system_primary_dark\n01c9003a=color/system_on_primary_dark\n01c9003b=color/system_secondary_container_dark\n01c9003c=color/system_on_secondary_container_dark\n01c9003d=color/system_secondary_dark\n01c9003e=color/system_on_secondary_dark\n01c9003f=color/system_tertiary_container_dark\n01c90040=color/system_on_tertiary_container_dark\n01c90041=color/system_tertiary_dark\n01c90042=color/system_on_tertiary_dark\n01c90043=color/system_background_dark\n01c90044=color/system_on_background_dark\n01c90045=color/system_surface_dark\n01c90046=color/system_on_surface_dark\n01c90047=color/system_surface_container_low_dark\n01c90048=color/system_surface_container_lowest_dark\n01c90049=color/system_surface_container_dark\n01c9004a=color/system_surface_container_high_dark\n01c9004b=color/system_surface_container_highest_dark\n01c9004c=color/system_surface_bright_dark\n01c9004d=color/system_surface_dim_dark\n01c9004e=color/system_surface_variant_dark\n01c9004f=color/system_on_surface_variant_dark\n01c90050=color/system_outline_dark\n01c90051=color/system_error_dark\n01c90052=color/system_on_error_dark\n01c90053=color/system_error_container_dark\n01c90054=color/system_on_error_container_dark\n01c90061=color/system_control_activated_dark\n01c90062=color/system_control_normal_dark\n01c90063=color/system_control_highlight_dark\n01c90064=color/system_text_primary_inverse_dark\n01c90065=color/system_text_secondary_and_tertiary_inverse_dark\n01c90066=color/system_text_primary_inverse_disable_only_dark\n01c90067=color/system_text_secondary_and_tertiary_inverse_disabled_dark\n01c90068=color/system_text_hint_inverse_dark\n01c90069=color/system_palette_key_color_primary_dark\n01c9006a=color/system_palette_key_color_secondary_dark\n01c9006b=color/system_palette_key_color_tertiary_dark\n01c9006c=color/system_palette_key_color_neutral_dark\n01c9006d=color/system_palette_key_color_neutral_variant_dark\n01c9006e=color/system_primary_fixed\n01c9006f=color/system_primary_fixed_dim\n01c90070=color/system_on_primary_fixed\n01c90071=color/system_on_primary_fixed_variant\n01c90072=color/system_secondary_fixed\n01c90073=color/system_secondary_fixed_dim\n01c90074=color/system_on_secondary_fixed\n01c90075=color/system_on_secondary_fixed_variant\n01c90076=color/system_tertiary_fixed\n01c90077=color/system_tertiary_fixed_dim\n01c90078=color/system_on_tertiary_fixed\n01c90079=color/system_on_tertiary_fixed_variant\n01c9007a=color/system_outline_variant_light\n01c9007b=color/system_outline_variant_dark\n01ca0000=dimen/config_viewConfigurationHandwritingGestureLineMargin\n01cb0000=string/config_systemWearHealthService\n01cb0001=string/config_defaultNotes\n01cb0002=string/config_systemFinancedDeviceController\n01cb0003=string/config_systemCallStreaming\n01cd0000=id/bold\n01cd0001=id/italic\n01cd0002=id/underline\n01cd0003=id/accessibilityActionScrollInDirection\n01ce0000=attr/handwritingBoundsOffsetLeft\n01ce0001=attr/handwritingBoundsOffsetTop\n01ce0002=attr/handwritingBoundsOffsetRight\n01ce0003=attr/handwritingBoundsOffsetBottom\n01ce0004=attr/accessibilityDataSensitive\n01ce0005=attr/enableTextStylingShortcuts\n01ce0006=attr/requiredDisplayCategory\n01ce0008=attr/visualQueryDetectionService\n01ce0009=attr/physicalKeyboardHintLanguageTag\n01ce000a=attr/physicalKeyboardHintLayoutType\n01ce000b=attr/allowSharedIsolatedProcess\n01ce000c=attr/keyboardLocale\n01ce000d=attr/keyboardLayoutType\n01ce000e=attr/allowUpdateOwnership\n01ce000f=attr/isCredential\n01ce0010=attr/searchResultHighlightColor\n01ce0011=attr/focusedSearchResultHighlightColor\n01ce0012=attr/stylusHandwritingSettingsActivity\n01ce0013=attr/windowNoMoveAnimation\n01ce0014=attr/settingsSubtitle\n01ce0015=attr/capability\n01cf0000=bool/config_preventImeStartupUnlessTextEditor\n01cf0001=bool/config_enableQrCodeScannerOnLockScreen\n01d80000=drawable/ic_safety_protection\n01d90000=array/config_optionalIpSecAlgorithms\n01dc0000=string/config_systemSupervision\n01dc0001=string/config_devicePolicyManagement\n01dc0002=string/config_systemAppProtectionService\n01dc0003=string/config_systemAutomotiveCalendarSyncManager\n01dc0004=string/config_defaultAutomotiveNavigation\n01dc0005=string/safety_protection_display_text\n01dc0006=string/config_systemSettingsIntelligence\n01dc0007=string/config_systemBluetoothStack\n01dd0000=style/TextAppearance.DeviceDefault.Headline\n01de0004=id/accessibilityActionShowTextSuggestions\n01de0005=id/inputExtractAction\n01de0006=id/inputExtractAccessories\n01df0000=attr/sharedUserMaxSdkVersion\n01df0001=attr/requiredSplitTypes\n01df0002=attr/splitTypes\n01df0003=attr/canDisplayOnRemoteDevices\n01df0004=attr/supportedTypes\n01df0005=attr/resetEnabledSettingsOnAppDataCleared\n01df0006=attr/supportsStylusHandwriting\n01df0007=attr/showClockAndComplications\n01df0008=attr/gameSessionService\n01df0009=attr/supportsBatteryGameMode\n01df000a=attr/supportsPerformanceGameMode\n01df000b=attr/allowGameAngleDriver\n01df000c=attr/allowGameDownscaling\n01df000d=attr/allowGameFpsOverride\n01df000e=attr/localeConfig\n01df000f=attr/showBackdrop\n01df0012=attr/preferKeepClear\n01df0013=attr/autoHandwritingEnabled\n01df0014=attr/fromExtendLeft\n01df0015=attr/fromExtendTop\n01df0016=attr/fromExtendRight\n01df0017=attr/fromExtendBottom\n01df0018=attr/toExtendLeft\n01df0019=attr/toExtendTop\n01df001a=attr/toExtendRight\n01df001b=attr/toExtendBottom\n01df001c=attr/tileService\n01df001d=attr/windowSplashScreenBehavior\n01df001e=attr/allowUntrustedActivityEmbedding\n01df001f=attr/knownActivityEmbeddingCerts\n01df0020=attr/intro\n01df0021=attr/enableOnBackInvokedCallback\n01df0022=attr/supportsInlineSuggestionsWithTouchExploration\n01df0023=attr/lineBreakStyle\n01df0024=attr/lineBreakWordStyle\n01df0025=attr/maxDrawableWidth\n01df0026=attr/maxDrawableHeight\n01df0027=attr/backdropColor\n01fe0000=id/accessibilityActionDragStart\n01fe0001=id/accessibilityActionDragDrop\n01fe0002=id/accessibilityActionDragCancel\n01ff0000=attr/shouldUseDefaultUnfoldTransition\n"
  },
  {
    "path": "jadx-core/src/main/resources/export/android/app.build.gradle.tmpl",
    "content": "plugins {\n    id 'com.android.application'\n}\n\nandroid {\n\tnamespace \"{{applicationId}}\"\n    compileSdkVersion {{compileSdkVersion}}\n\n    defaultConfig {\n        applicationId '{{applicationId}}'\n        minSdkVersion {{minSdkVersion}}\n        //noinspection ExpiringTargetSdkVersion,ExpiredTargetSdkVersion\n        targetSdkVersion {{targetSdkVersion}}\n        versionCode {{versionCode}}\n        versionName \"{{versionName}}\"\n{{additionalOptions}}\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    lintOptions {\n        abortOnError false\n    }\n\n    buildFeatures {\n        buildConfig = false\n    }\n}\n\ndependencies {\n    // TODO: dependencies\n}\n"
  },
  {
    "path": "jadx-core/src/main/resources/export/android/build.gradle.tmpl",
    "content": "buildscript {\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:8.10.1'\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "jadx-core/src/main/resources/export/android/lib.build.gradle.tmpl",
    "content": "plugins {\n    id 'com.android.library'\n}\n\nandroid {\n    namespace '{{packageId}}'\n    compileSdk {{compileSdkVersion}}\n\n    defaultConfig {\n        minSdk {{minSdkVersion}}\n\n{{additionalOptions}}\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n        }\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    lintOptions {\n        abortOnError false\n    }\n\n    buildFeatures {\n        buildConfig = false\n    }\n}\n\ndependencies {\n\t// TODO: dependencies\n}\n"
  },
  {
    "path": "jadx-core/src/main/resources/export/android/settings.gradle.tmpl",
    "content": "rootProject.name = '{{projectName}}'\n\ninclude '{{mainModuleName}}'\n"
  },
  {
    "path": "jadx-core/src/main/resources/export/java/build.gradle.kts.tmpl",
    "content": "plugins {\n\tjava\n}\n\nrepositories {\n\tgoogle()\n\tmavenCentral()\n}\n\ndependencies {\n\t// some dependencies\n}\n"
  },
  {
    "path": "jadx-core/src/main/resources/export/java/settings.gradle.kts.tmpl",
    "content": "rootProject.name = \"{{projectName}}\"\n\ninclude(\"app\")\n"
  },
  {
    "path": "jadx-core/src/main/resources/jadx/core/deobf/conditions/tlds.txt",
    "content": "# All tld domains\n# Source: https://data.iana.org/TLD/tlds-alpha-by-domain.txt\n# Version 2024102400, Last Updated Thu Oct 24 07:07:01 2024 UTC\n\n# ASCII domains\naaa\naarp\nabb\nabbott\nabbvie\nabc\nable\nabogado\nabudhabi\nac\nacademy\naccenture\naccountant\naccountants\naco\nactor\nad\nads\nadult\nae\naeg\naero\naetna\naf\nafl\nafrica\nag\nagakhan\nagency\nai\naig\nairbus\nairforce\nairtel\nakdn\nal\nalibaba\nalipay\nallfinanz\nallstate\nally\nalsace\nalstom\nam\namazon\namericanexpress\namericanfamily\namex\namfam\namica\namsterdam\nanalytics\nandroid\nanquan\nanz\nao\naol\napartments\napp\napple\naq\naquarelle\nar\narab\naramco\narchi\narmy\narpa\nart\narte\nas\nasda\nasia\nassociates\nat\nathleta\nattorney\nau\nauction\naudi\naudible\naudio\nauspost\nauthor\nauto\nautos\naw\naws\nax\naxa\naz\nazure\nba\nbaby\nbaidu\nbanamex\nband\nbank\nbar\nbarcelona\nbarclaycard\nbarclays\nbarefoot\nbargains\nbaseball\nbasketball\nbauhaus\nbayern\nbb\nbbc\nbbt\nbbva\nbcg\nbcn\nbd\nbe\nbeats\nbeauty\nbeer\nbentley\nberlin\nbest\nbestbuy\nbet\nbf\nbg\nbh\nbharti\nbi\nbible\nbid\nbike\nbing\nbingo\nbio\nbiz\nbj\nblack\nblackfriday\nblockbuster\nblog\nbloomberg\nblue\nbm\nbms\nbmw\nbn\nbnpparibas\nbo\nboats\nboehringer\nbofa\nbom\nbond\nboo\nbook\nbooking\nbosch\nbostik\nboston\nbot\nboutique\nbox\nbr\nbradesco\nbridgestone\nbroadway\nbroker\nbrother\nbrussels\nbs\nbt\nbuild\nbuilders\nbusiness\nbuy\nbuzz\nbv\nbw\nby\nbz\nbzh\nca\ncab\ncafe\ncal\ncall\ncalvinklein\ncam\ncamera\ncamp\ncanon\ncapetown\ncapital\ncapitalone\ncar\ncaravan\ncards\ncare\ncareer\ncareers\ncars\ncasa\ncase\ncash\ncasino\ncat\ncatering\ncatholic\ncba\ncbn\ncbre\ncc\ncd\ncenter\nceo\ncern\ncf\ncfa\ncfd\ncg\nch\nchanel\nchannel\ncharity\nchase\nchat\ncheap\nchintai\nchristmas\nchrome\nchurch\nci\ncipriani\ncircle\ncisco\ncitadel\nciti\ncitic\ncity\nck\ncl\nclaims\ncleaning\nclick\nclinic\nclinique\nclothing\ncloud\nclub\nclubmed\ncm\ncn\nco\ncoach\ncodes\ncoffee\ncollege\ncologne\ncom\ncommbank\ncommunity\ncompany\ncompare\ncomputer\ncomsec\ncondos\nconstruction\nconsulting\ncontact\ncontractors\ncooking\ncool\ncoop\ncorsica\ncountry\ncoupon\ncoupons\ncourses\ncpa\ncr\ncredit\ncreditcard\ncreditunion\ncricket\ncrown\ncrs\ncruise\ncruises\ncu\ncuisinella\ncv\ncw\ncx\ncy\ncymru\ncyou\ncz\ndad\ndance\ndata\ndate\ndating\ndatsun\nday\ndclk\ndds\nde\ndeal\ndealer\ndeals\ndegree\ndelivery\ndell\ndeloitte\ndelta\ndemocrat\ndental\ndentist\ndesi\ndesign\ndev\ndhl\ndiamonds\ndiet\ndigital\ndirect\ndirectory\ndiscount\ndiscover\ndish\ndiy\ndj\ndk\ndm\ndnp\ndo\ndocs\ndoctor\ndog\ndomains\ndot\ndownload\ndrive\ndtv\ndubai\ndunlop\ndupont\ndurban\ndvag\ndvr\ndz\nearth\neat\nec\neco\nedeka\nedu\neducation\nee\neg\nemail\nemerck\nenergy\nengineer\nengineering\nenterprises\nepson\nequipment\ner\nericsson\nerni\nes\nesq\nestate\net\neu\neurovision\neus\nevents\nexchange\nexpert\nexposed\nexpress\nextraspace\nfage\nfail\nfairwinds\nfaith\nfamily\nfan\nfans\nfarm\nfarmers\nfashion\nfast\nfedex\nfeedback\nferrari\nferrero\nfi\nfidelity\nfido\nfilm\nfinal\nfinance\nfinancial\nfire\nfirestone\nfirmdale\nfish\nfishing\nfit\nfitness\nfj\nfk\nflickr\nflights\nflir\nflorist\nflowers\nfly\nfm\nfo\nfoo\nfood\nfootball\nford\nforex\nforsale\nforum\nfoundation\nfox\nfr\nfree\nfresenius\nfrl\nfrogans\nfrontier\nftr\nfujitsu\nfun\nfund\nfurniture\nfutbol\nfyi\nga\ngal\ngallery\ngallo\ngallup\ngame\ngames\ngap\ngarden\ngay\ngb\ngbiz\ngd\ngdn\nge\ngea\ngent\ngenting\ngeorge\ngf\ngg\nggee\ngh\ngi\ngift\ngifts\ngives\ngiving\ngl\nglass\ngle\nglobal\nglobo\ngm\ngmail\ngmbh\ngmo\ngmx\ngn\ngodaddy\ngold\ngoldpoint\ngolf\ngoo\ngoodyear\ngoog\ngoogle\ngop\ngot\ngov\ngp\ngq\ngr\ngrainger\ngraphics\ngratis\ngreen\ngripe\ngrocery\ngroup\ngs\ngt\ngu\ngucci\nguge\nguide\nguitars\nguru\ngw\ngy\nhair\nhamburg\nhangout\nhaus\nhbo\nhdfc\nhdfcbank\nhealth\nhealthcare\nhelp\nhelsinki\nhere\nhermes\nhiphop\nhisamitsu\nhitachi\nhiv\nhk\nhkt\nhm\nhn\nhockey\nholdings\nholiday\nhomedepot\nhomegoods\nhomes\nhomesense\nhonda\nhorse\nhospital\nhost\nhosting\nhot\nhotels\nhotmail\nhouse\nhow\nhr\nhsbc\nht\nhu\nhughes\nhyatt\nhyundai\nibm\nicbc\nice\nicu\nid\nie\nieee\nifm\nikano\nil\nim\nimamat\nimdb\nimmo\nimmobilien\nin\ninc\nindustries\ninfiniti\ninfo\ning\nink\ninstitute\ninsurance\ninsure\nint\ninternational\nintuit\ninvestments\nio\nipiranga\niq\nir\nirish\nis\nismaili\nist\nistanbul\nit\nitau\nitv\njaguar\njava\njcb\nje\njeep\njetzt\njewelry\njio\njll\njm\njmp\njnj\njo\njobs\njoburg\njot\njoy\njp\njpmorgan\njprs\njuegos\njuniper\nkaufen\nkddi\nke\nkerryhotels\nkerrylogistics\nkerryproperties\nkfh\nkg\nkh\nki\nkia\nkids\nkim\nkindle\nkitchen\nkiwi\nkm\nkn\nkoeln\nkomatsu\nkosher\nkp\nkpmg\nkpn\nkr\nkrd\nkred\nkuokgroup\nkw\nky\nkyoto\nkz\nla\nlacaixa\nlamborghini\nlamer\nlancaster\nland\nlandrover\nlanxess\nlasalle\nlat\nlatino\nlatrobe\nlaw\nlawyer\nlb\nlc\nlds\nlease\nleclerc\nlefrak\nlegal\nlego\nlexus\nlgbt\nli\nlidl\nlife\nlifeinsurance\nlifestyle\nlighting\nlike\nlilly\nlimited\nlimo\nlincoln\nlink\nlipsy\nlive\nliving\nlk\nllc\nllp\nloan\nloans\nlocker\nlocus\nlol\nlondon\nlotte\nlotto\nlove\nlpl\nlplfinancial\nlr\nls\nlt\nltd\nltda\nlu\nlundbeck\nluxe\nluxury\nlv\nly\nma\nmadrid\nmaif\nmaison\nmakeup\nman\nmanagement\nmango\nmap\nmarket\nmarketing\nmarkets\nmarriott\nmarshalls\nmattel\nmba\nmc\nmckinsey\nmd\nme\nmed\nmedia\nmeet\nmelbourne\nmeme\nmemorial\nmen\nmenu\nmerckmsd\nmg\nmh\nmiami\nmicrosoft\nmil\nmini\nmint\nmit\nmitsubishi\nmk\nml\nmlb\nmls\nmm\nmma\nmn\nmo\nmobi\nmobile\nmoda\nmoe\nmoi\nmom\nmonash\nmoney\nmonster\nmormon\nmortgage\nmoscow\nmoto\nmotorcycles\nmov\nmovie\nmp\nmq\nmr\nms\nmsd\nmt\nmtn\nmtr\nmu\nmuseum\nmusic\nmv\nmw\nmx\nmy\nmz\nna\nnab\nnagoya\nname\nnavy\nnba\nnc\nne\nnec\nnet\nnetbank\nnetflix\nnetwork\nneustar\nnew\nnews\nnext\nnextdirect\nnexus\nnf\nnfl\nng\nngo\nnhk\nni\nnico\nnike\nnikon\nninja\nnissan\nnissay\nnl\nno\nnokia\nnorton\nnow\nnowruz\nnowtv\nnp\nnr\nnra\nnrw\nntt\nnu\nnyc\nnz\nobi\nobserver\noffice\nokinawa\nolayan\nolayangroup\nollo\nom\nomega\none\nong\nonl\nonline\nooo\nopen\noracle\norange\norg\norganic\norigins\nosaka\notsuka\nott\novh\npa\npage\npanasonic\nparis\npars\npartners\nparts\nparty\npay\npccw\npe\npet\npf\npfizer\npg\nph\npharmacy\nphd\nphilips\nphone\nphoto\nphotography\nphotos\nphysio\npics\npictet\npictures\npid\npin\nping\npink\npioneer\npizza\npk\npl\nplace\nplay\nplaystation\nplumbing\nplus\npm\npn\npnc\npohl\npoker\npolitie\nporn\npost\npr\npramerica\npraxi\npress\nprime\npro\nprod\nproductions\nprof\nprogressive\npromo\nproperties\nproperty\nprotection\npru\nprudential\nps\npt\npub\npw\npwc\npy\nqa\nqpon\nquebec\nquest\nracing\nradio\nre\nread\nrealestate\nrealtor\nrealty\nrecipes\nred\nredstone\nredumbrella\nrehab\nreise\nreisen\nreit\nreliance\nren\nrent\nrentals\nrepair\nreport\nrepublican\nrest\nrestaurant\nreview\nreviews\nrexroth\nrich\nrichardli\nricoh\nril\nrio\nrip\nro\nrocks\nrodeo\nrogers\nroom\nrs\nrsvp\nru\nrugby\nruhr\nrun\nrw\nrwe\nryukyu\nsa\nsaarland\nsafe\nsafety\nsakura\nsale\nsalon\nsamsclub\nsamsung\nsandvik\nsandvikcoromant\nsanofi\nsap\nsarl\nsas\nsave\nsaxo\nsb\nsbi\nsbs\nsc\nscb\nschaeffler\nschmidt\nscholarships\nschool\nschule\nschwarz\nscience\nscot\nsd\nse\nsearch\nseat\nsecure\nsecurity\nseek\nselect\nsener\nservices\nseven\nsew\nsex\nsexy\nsfr\nsg\nsh\nshangrila\nsharp\nshell\nshia\nshiksha\nshoes\nshop\nshopping\nshouji\nshow\nsi\nsilk\nsina\nsingles\nsite\nsj\nsk\nski\nskin\nsky\nskype\nsl\nsling\nsm\nsmart\nsmile\nsn\nsncf\nso\nsoccer\nsocial\nsoftbank\nsoftware\nsohu\nsolar\nsolutions\nsong\nsony\nsoy\nspa\nspace\nsport\nspot\nsr\nsrl\nss\nst\nstada\nstaples\nstar\nstatebank\nstatefarm\nstc\nstcgroup\nstockholm\nstorage\nstore\nstream\nstudio\nstudy\nstyle\nsu\nsucks\nsupplies\nsupply\nsupport\nsurf\nsurgery\nsuzuki\nsv\nswatch\nswiss\nsx\nsy\nsydney\nsystems\nsz\ntab\ntaipei\ntalk\ntaobao\ntarget\ntatamotors\ntatar\ntattoo\ntax\ntaxi\ntc\ntci\ntd\ntdk\nteam\ntech\ntechnology\ntel\ntemasek\ntennis\nteva\ntf\ntg\nth\nthd\ntheater\ntheatre\ntiaa\ntickets\ntienda\ntips\ntires\ntirol\ntj\ntjmaxx\ntjx\ntk\ntkmaxx\ntl\ntm\ntmall\ntn\nto\ntoday\ntokyo\ntools\ntop\ntoray\ntoshiba\ntotal\ntours\ntown\ntoyota\ntoys\ntr\ntrade\ntrading\ntraining\ntravel\ntravelers\ntravelersinsurance\ntrust\ntrv\ntt\ntube\ntui\ntunes\ntushu\ntv\ntvs\ntw\ntz\nua\nubank\nubs\nug\nuk\nunicom\nuniversity\nuno\nuol\nups\nus\nuy\nuz\nva\nvacations\nvana\nvanguard\nvc\nve\nvegas\nventures\nverisign\nversicherung\nvet\nvg\nvi\nviajes\nvideo\nvig\nviking\nvillas\nvin\nvip\nvirgin\nvisa\nvision\nviva\nvivo\nvlaanderen\nvn\nvodka\nvolvo\nvote\nvoting\nvoto\nvoyage\nvu\nwales\nwalmart\nwalter\nwang\nwanggou\nwatch\nwatches\nweather\nweatherchannel\nwebcam\nweber\nwebsite\nwed\nwedding\nweibo\nweir\nwf\nwhoswho\nwien\nwiki\nwilliamhill\nwin\nwindows\nwine\nwinners\nwme\nwolterskluwer\nwoodside\nwork\nworks\nworld\nwow\nws\nwtc\nwtf\nxbox\nxerox\nxihuan\nxin\nxxx\nxyz\nyachts\nyahoo\nyamaxun\nyandex\nye\nyodobashi\nyoga\nyokohama\nyou\nyoutube\nyt\nyun\nza\nzappos\nzara\nzero\nzip\nzm\nzone\nzuerich\nzw\n\n# Non-ASCII domains\nकॉम\nセール\n佛山\nಭಾರತ\n慈善\n集团\n在线\n한국\nଭାରତ\n点看\nคอม\nভাৰত\nভারত\n八卦\nישראל\nموقع\nবাংলা\n公益\n公司\n香格里拉\n网站\n移动\n我爱你\nмосква\nқаз\nкатолик\nонлайн\nсайт\n联通\nсрб\nбг\nбел\nקום\n时尚\n微博\n淡马锡\nファッション\nорг\nनेट\nストア\nアマゾン\n삼성\nசிங்கப்பூர்\n商标\n商店\n商城\nдети\nмкд\nею\nポイント\n新闻\n家電\nكوم\n中文网\n中信\n中国\n中國\n娱乐\n谷歌\nభారత్\nලංකා\n電訊盈科\n购物\nクラウド\nભારત\n通販\nभारतम्\nभारत\nभारोत\n网店\nसंगठन\n餐厅\n网络\nком\nукр\n香港\n亚马逊\n食品\n飞利浦\n台湾\n台灣\n手机\nмон\nالجزائر\nعمان\nارامكو\nایران\nالعليان\nامارات\nبازار\nموريتانيا\nپاکستان\nالاردن\nبارت\nبھارت\nالمغرب\nابوظبي\nالبحرين\nالسعودية\nڀارت\nكاثوليك\nسودان\nهمراه\nعراق\nمليسيا\n澳門\n닷컴\n政府\nشبكة\nبيتك\nعرب\nგე\n机构\n组织机构\n健康\nไทย\nسورية\n招聘\nрус\nрф\nتونس\n大拿\nລາວ\nみんな\nグーグル\nευ\nελ\n世界\n書籍\nഭാരതം\nਭਾਰਤ\n网址\n닷넷\nコム\n天主教\n游戏\nvermögensberater\nvermögensberatung\n企业\n信息\n嘉里大酒店\n嘉里\nمصر\nقطر\n广东\nஇலங்கை\nஇந்தியா\nհայ\n新加坡\nفلسطين\n政务\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/NotYetImplemented.java",
    "content": "package jadx;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Indicates a test which is known to fail.\n *\n * <p>\n * This would cause a failure to be considered as success and a success as failure,\n * with the benefit of updating the related issue when it has been resolved even unintentionally.\n * </p>\n *\n * <p>\n * To have an effect, the test class must be annotated with:\n *\n * <code>\n * &#064;ExtendWith(NotYetImplementedExtension.class)\n * </code>\n * </p>\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.TYPE, ElementType.METHOD })\npublic @interface NotYetImplemented {\n\tString value() default \"\";\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/NotYetImplementedExtension.java",
    "content": "package jadx;\n\nimport java.lang.reflect.Method;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.extension.AfterTestExecutionCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestExecutionExceptionHandler;\n\npublic class NotYetImplementedExtension implements AfterTestExecutionCallback, TestExecutionExceptionHandler {\n\n\tprivate final Set<Method> knownFailedMethods = new HashSet<>();\n\n\t@Override\n\tpublic void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {\n\t\tif (!isNotYetImplemented(context)) {\n\t\t\tthrow throwable;\n\t\t}\n\t\tknownFailedMethods.add(context.getTestMethod().get());\n\t}\n\n\t@Override\n\tpublic void afterTestExecution(ExtensionContext context) throws Exception {\n\t\tif (!knownFailedMethods.contains(context.getTestMethod().get())\n\t\t\t\t&& isNotYetImplemented(context)\n\t\t\t\t&& context.getExecutionException().isEmpty()) {\n\t\t\tthrow new AssertionError(\"Test \"\n\t\t\t\t\t+ context.getTestClass().get().getName() + '.' + context.getTestMethod().get().getName()\n\t\t\t\t\t+ \" is marked as @NotYetImplemented, but passes!\");\n\t\t}\n\t}\n\n\tprivate static boolean isNotYetImplemented(ExtensionContext context) {\n\t\treturn context.getTestMethod().get().getAnnotation(NotYetImplemented.class) != null\n\t\t\t\t|| context.getTestClass().get().getAnnotation(NotYetImplemented.class) != null;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/api/JadxArgsValidatorOutDirsTest.java",
    "content": "package jadx.api;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport static jadx.core.utils.files.FileUtils.toFile;\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class JadxArgsValidatorOutDirsTest {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxArgsValidatorOutDirsTest.class);\n\n\tpublic JadxArgs args;\n\n\t@TempDir\n\tPath testDir;\n\n\t@Test\n\tpublic void checkAllSet() {\n\t\tsetOutDirs(\"r\", \"s\", \"r\");\n\t\tcheckOutDirs(\"r\", \"s\", \"r\");\n\t}\n\n\t@Test\n\tpublic void checkRootOnly() {\n\t\tsetOutDirs(\"out\", null, null);\n\t\tcheckOutDirs(\"out\", \"out/\" + JadxArgs.DEFAULT_SRC_DIR, \"out/\" + JadxArgs.DEFAULT_RES_DIR);\n\t}\n\n\t@Test\n\tpublic void checkSrcOnly() {\n\t\tsetOutDirs(null, \"src\", null);\n\t\tcheckOutDirs(\"src\", \"src\", \"src/\" + JadxArgs.DEFAULT_RES_DIR);\n\t}\n\n\t@Test\n\tpublic void checkResOnly() {\n\t\tsetOutDirs(null, null, \"res\");\n\t\tcheckOutDirs(\"res\", \"res/\" + JadxArgs.DEFAULT_SRC_DIR, \"res\");\n\t}\n\n\t@Test\n\tpublic void checkNone() {\n\t\tsetOutDirs(null, null, null);\n\t\tString inputFileBase = args.getInputFiles().get(0).getName().replace(\".apk\", \"\");\n\t\tcheckOutDirs(inputFileBase,\n\t\t\t\tinputFileBase + '/' + JadxArgs.DEFAULT_SRC_DIR,\n\t\t\t\tinputFileBase + '/' + JadxArgs.DEFAULT_RES_DIR);\n\t}\n\n\tprivate void setOutDirs(String outDir, String srcDir, String resDir) {\n\t\targs = makeArgs();\n\t\targs.setOutDir(toFile(outDir));\n\t\targs.setOutDirSrc(toFile(srcDir));\n\t\targs.setOutDirRes(toFile(resDir));\n\t\tLOG.debug(\"Set dirs: out={}, src={}, res={}\", outDir, srcDir, resDir);\n\t}\n\n\tprivate void checkOutDirs(String outDir, String srcDir, String resDir) {\n\t\tJadxArgsValidator.validate(new JadxDecompiler(args));\n\t\tLOG.debug(\"Got dirs: out={}, src={}, res={}\", args.getOutDir(), args.getOutDirSrc(), args.getOutDirRes());\n\t\tassertThat(args.getOutDir()).isEqualTo(toFile(outDir));\n\t\tassertThat(args.getOutDirSrc()).isEqualTo(toFile(srcDir));\n\t\tassertThat(args.getOutDirRes()).isEqualTo(toFile(resDir));\n\t}\n\n\tprivate JadxArgs makeArgs() {\n\t\ttry {\n\t\t\tJadxArgs args = new JadxArgs();\n\t\t\targs.getInputFiles().add(Files.createTempFile(testDir, \"test-\", \".apk\").toFile());\n\t\t\treturn args;\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/api/JadxDecompilerTest.java",
    "content": "package jadx.api;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\n\nimport jadx.core.xmlgen.ResContainer;\nimport jadx.plugins.input.dex.DexInputPlugin;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class JadxDecompilerTest {\n\n\t@TempDir\n\tFile testDir;\n\n\t@Test\n\tpublic void testExampleUsage() {\n\t\tFile sampleApk = getFileFromSampleDir(\"app-with-fake-dex.apk\");\n\n\t\t// test simple apk loading\n\t\tJadxArgs args = new JadxArgs();\n\t\targs.getInputFiles().add(sampleApk);\n\t\targs.setOutDir(testDir);\n\n\t\ttry (JadxDecompiler jadx = new JadxDecompiler(args)) {\n\t\t\tjadx.load();\n\t\t\tjadx.save();\n\t\t\tjadx.printErrorsReport();\n\n\t\t\t// test class print\n\t\t\tfor (JavaClass cls : jadx.getClasses()) {\n\t\t\t\tSystem.out.println(cls.getCode());\n\t\t\t}\n\n\t\t\tassertThat(jadx.getClasses()).hasSize(3);\n\t\t\tassertThat(jadx.getErrorsCount()).isEqualTo(0);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testDirectDexInput() throws IOException {\n\t\ttry (JadxDecompiler jadx = new JadxDecompiler();\n\t\t\t\tInputStream in = new FileInputStream(getFileFromSampleDir(\"hello.dex\"))) {\n\t\t\tjadx.addCustomCodeLoader(new DexInputPlugin().loadDexFromInputStream(in, \"input\"));\n\t\t\tjadx.load();\n\t\t\tfor (JavaClass cls : jadx.getClasses()) {\n\t\t\t\tSystem.out.println(cls.getCode());\n\t\t\t}\n\t\t\tassertThat(jadx.getClasses()).hasSize(1);\n\t\t\tassertThat(jadx.getErrorsCount()).isEqualTo(0);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testResourcesLoad() {\n\t\tFile sampleApk = getFileFromSampleDir(\"app-with-fake-dex.apk\");\n\n\t\tJadxArgs args = new JadxArgs();\n\t\targs.getInputFiles().add(sampleApk);\n\t\targs.setOutDir(testDir);\n\t\targs.setSkipSources(true);\n\t\ttry (JadxDecompiler jadx = new JadxDecompiler(args)) {\n\t\t\tjadx.load();\n\t\t\tList<ResourceFile> resources = jadx.getResources();\n\t\t\tassertThat(resources).hasSize(8);\n\t\t\tResourceFile arsc = resources.stream()\n\t\t\t\t\t.filter(r -> r.getType() == ResourceType.ARSC)\n\t\t\t\t\t.findFirst().orElseThrow();\n\t\t\tResContainer resContainer = arsc.loadContent();\n\t\t\tResContainer xmlRes = resContainer.getSubFiles().stream()\n\t\t\t\t\t.filter(r -> r.getName().equals(\"res/values/colors.xml\"))\n\t\t\t\t\t.findFirst().orElseThrow();\n\t\t\tassertThat(xmlRes.getText())\n\t\t\t\t\t.code()\n\t\t\t\t\t.containsOne(\"<color name=\\\"colorPrimary\\\">#008577</color>\");\n\t\t}\n\t}\n\n\tprivate static final String TEST_SAMPLES_DIR = \"test-samples/\";\n\n\tpublic static File getFileFromSampleDir(String fileName) {\n\t\tURL resource = JadxDecompilerTest.class.getClassLoader().getResource(TEST_SAMPLES_DIR + fileName);\n\t\tassertThat(resource).isNotNull();\n\t\tString pathStr = resource.getFile();\n\t\treturn new File(pathStr);\n\t}\n\n\t// TODO add more tests\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/api/JadxInternalAccess.java",
    "content": "package jadx.api;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\n\npublic class JadxInternalAccess {\n\n\tpublic static RootNode getRoot(JadxDecompiler d) {\n\t\treturn d.getRoot();\n\t}\n\n\tpublic static JavaClass convertClassNode(JadxDecompiler d, ClassNode clsNode) {\n\t\treturn d.convertClassNode(clsNode);\n\t}\n\n\tpublic static JavaMethod convertMethodNode(JadxDecompiler d, MethodNode mthNode) {\n\t\treturn d.convertMethodNode(mthNode);\n\t}\n\n\tpublic static JavaField convertFieldNode(JadxDecompiler d, FieldNode fldNode) {\n\t\treturn d.convertFieldNode(fldNode);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/deobf/NameMapperTest.java",
    "content": "package jadx.core.deobf;\n\nimport org.junit.jupiter.api.Test;\n\nimport static jadx.core.deobf.NameMapper.isValidIdentifier;\nimport static jadx.core.deobf.NameMapper.removeInvalidChars;\nimport static jadx.core.deobf.NameMapper.removeInvalidCharsMiddle;\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class NameMapperTest {\n\n\t@Test\n\tpublic void validIdentifiers() {\n\t\tassertThat(isValidIdentifier(\"ACls\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void notValidIdentifiers() {\n\t\tassertThat(isValidIdentifier(\"1cls\")).isFalse();\n\t\tassertThat(isValidIdentifier(\"-cls\")).isFalse();\n\t\tassertThat(isValidIdentifier(\"A-cls\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void testRemoveInvalidCharsMiddle() {\n\t\tassertThat(removeInvalidCharsMiddle(\"1cls\")).isEqualTo(\"1cls\");\n\t\tassertThat(removeInvalidCharsMiddle(\"-cls\")).isEqualTo(\"cls\");\n\t\tassertThat(removeInvalidCharsMiddle(\"A-cls\")).isEqualTo(\"Acls\");\n\t}\n\n\t@Test\n\tpublic void testRemoveInvalidChars() {\n\t\tassertThat(removeInvalidChars(\"1cls\", \"C\")).isEqualTo(\"C1cls\");\n\t\tassertThat(removeInvalidChars(\"-cls\", \"C\")).isEqualTo(\"cls\");\n\t\tassertThat(removeInvalidChars(\"A-cls\", \"C\")).isEqualTo(\"Acls\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/dex/info/AccessInfoTest.java",
    "content": "package jadx.core.dex.info;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.core.dex.info.AccessInfo.AFType;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class AccessInfoTest {\n\n\t@Test\n\tpublic void changeVisibility() {\n\t\tAccessInfo accessInfo = new AccessInfo(AccessFlags.PROTECTED | AccessFlags.STATIC, AFType.METHOD);\n\t\tAccessInfo result = accessInfo.changeVisibility(AccessFlags.PUBLIC);\n\n\t\tassertThat(result.isPublic()).isTrue();\n\t\tassertThat(result.isPrivate()).isFalse();\n\t\tassertThat(result.isProtected()).isFalse();\n\n\t\tassertThat(result.isStatic()).isTrue();\n\t}\n\n\t@Test\n\tpublic void changeVisibilityNoOp() {\n\t\tAccessInfo accessInfo = new AccessInfo(AccessFlags.PUBLIC, AFType.METHOD);\n\t\tAccessInfo result = accessInfo.changeVisibility(AccessFlags.PUBLIC);\n\t\tassertThat(result).isSameAs(accessInfo);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/dex/instructions/args/ArgTypeTest.java",
    "content": "package jadx.core.dex.instructions.args;\n\nimport org.junit.jupiter.api.Test;\n\nimport static jadx.core.dex.instructions.args.ArgType.WildcardBound.SUPER;\nimport static jadx.core.dex.instructions.args.ArgType.generic;\nimport static jadx.core.dex.instructions.args.ArgType.genericType;\nimport static jadx.core.dex.instructions.args.ArgType.wildcard;\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\nclass ArgTypeTest {\n\n\t@Test\n\tpublic void testEqualsOfGenericTypes() {\n\t\tArgType first = ArgType.generic(\"java.lang.List\", ArgType.STRING);\n\t\tArgType second = ArgType.generic(\"Ljava/lang/List;\", ArgType.STRING);\n\n\t\tassertThat(first).isEqualTo(second);\n\t}\n\n\t@Test\n\tvoid testContainsGenericType() {\n\t\tArgType wildcard = wildcard(genericType(\"T\"), SUPER);\n\t\tassertThat(wildcard.containsTypeVariable()).isTrue();\n\n\t\tArgType type = generic(\"java.lang.List\", wildcard);\n\t\tassertThat(type.containsTypeVariable()).isTrue();\n\t}\n\n\t@Test\n\tvoid testInnerGeneric() {\n\t\tArgType[] genericTypes = new ArgType[] { ArgType.genericType(\"K\"), ArgType.genericType(\"V\") };\n\t\tArgType base = ArgType.generic(\"java.util.Map\", genericTypes);\n\n\t\tArgType genericInner = ArgType.outerGeneric(base, ArgType.generic(\"Entry\", genericTypes));\n\t\tassertThat(genericInner.toString()).isEqualTo(\"java.util.Map<K, V>$Entry<K, V>\");\n\t\tassertThat(genericInner.containsTypeVariable()).isTrue();\n\n\t\tArgType genericInner2 = ArgType.outerGeneric(base, ArgType.object(\"Entry\"));\n\t\tassertThat(genericInner2.toString()).isEqualTo(\"java.util.Map<K, V>$Entry\");\n\t\tassertThat(genericInner2.containsTypeVariable()).isTrue();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/dex/nodes/utils/SelectFromDuplicatesTest.java",
    "content": "package jadx.core.dex.nodes.utils;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.stream.Collectors;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.JadxDecompiler;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.tests.api.utils.TestUtils;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\nclass SelectFromDuplicatesTest {\n\n\tprivate RootNode root;\n\n\t@BeforeEach\n\tpublic void init() {\n\t\tJadxArgs args = new JadxArgs();\n\t\targs.addInputFile(TestUtils.getFileForSample(\"test-samples/hello.dex\"));\n\t\tJadxDecompiler decompiler = new JadxDecompiler(args);\n\t\tdecompiler.load();\n\t\troot = decompiler.getRoot();\n\t}\n\n\t@Test\n\tvoid testSelectBySource() {\n\t\tselectBySources(0, false, \"classes.dex\", \"classes2.dex\");\n\t\tselectBySources(2, false, \"classes10.dex\", \"classes20.dex\", \"classes2.dex\");\n\t}\n\n\t@RepeatedTest(10)\n\tvoid testSelectBySourceShuffled() {\n\t\tselectFirstByShuffleSources(\"classes.dex\", \"classes2.dex\", \"classes4.dex\");\n\t\tselectFirstByShuffleSources(\"classes2.dex\", \"classes10.dex\", \"classes20.dex\");\n\t\tselectFirstByShuffleSources(\"classes10.dex\", \"classes1.dex\", \"classes01.dex\", \"classes000.dex\", \"classes02.dex\");\n\t}\n\n\tprivate void selectFirstByShuffleSources(String... sources) {\n\t\tselectBySources(0, true, sources);\n\t}\n\n\tprivate void selectBySources(int selectedPos, boolean shuffle, String... sources) {\n\t\tList<ClassNode> clsList = Arrays.stream(sources)\n\t\t\t\t.map(this::buildClassNodeBySource)\n\t\t\t\t.collect(Collectors.toList());\n\t\tClassNode expected = clsList.get(selectedPos);\n\t\tif (shuffle) {\n\t\t\tCollections.shuffle(clsList, new Random(System.currentTimeMillis() + System.nanoTime()));\n\t\t}\n\t\tClassNode selectedCls = SelectFromDuplicates.process(clsList);\n\t\tassertThat(selectedCls)\n\t\t\t\t.describedAs(\"Expect %s, but got %s from list: %s\", expected, selectedCls, clsList)\n\t\t\t\t.isSameAs(expected);\n\t}\n\n\tprivate ClassNode buildClassNodeBySource(String clsSource) {\n\t\tClassInfo clsInfo = ClassInfo.fromName(root, \"ClassFromSource:\" + clsSource);\n\t\tClassNode cls = ClassNode.addSyntheticClass(root, clsInfo, 0);\n\t\tcls.setInputFileName(clsSource);\n\t\treturn cls;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/dex/nodes/utils/TypeUtilsTest.java",
    "content": "package jadx.core.dex.nodes.utils;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.JadxArgs;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.RootNode;\n\nimport static jadx.core.dex.instructions.args.ArgType.EXCEPTION;\nimport static jadx.core.dex.instructions.args.ArgType.STRING;\nimport static jadx.core.dex.instructions.args.ArgType.array;\nimport static jadx.core.dex.instructions.args.ArgType.generic;\nimport static jadx.core.dex.instructions.args.ArgType.genericType;\nimport static jadx.core.dex.instructions.args.ArgType.object;\nimport static jadx.core.dex.instructions.args.ArgType.outerGeneric;\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\nclass TypeUtilsTest {\n\tprivate TypeUtils typeUtils;\n\n\t@BeforeEach\n\tpublic void init() {\n\t\ttypeUtils = new TypeUtils(new RootNode(new JadxArgs()));\n\t}\n\n\t@Test\n\tvoid replaceTypeVariablesUsingMap() {\n\t\tArgType typeVar = genericType(\"T\");\n\t\tArgType listCls = object(\"java.util.List\");\n\t\tMap<ArgType, ArgType> typeMap = Collections.singletonMap(typeVar, STRING);\n\n\t\treplaceTypeVar(typeVar, typeMap, STRING);\n\t\treplaceTypeVar(generic(listCls, typeVar), typeMap, generic(listCls, STRING));\n\t\treplaceTypeVar(array(typeVar), typeMap, array(STRING));\n\t}\n\n\t@Test\n\tvoid replaceTypeVariablesUsingMap2() {\n\t\tArgType kVar = genericType(\"K\");\n\t\tArgType vVar = genericType(\"V\");\n\t\tArgType mapCls = object(\"java.util.Map\");\n\t\tArgType entryCls = object(\"Entry\");\n\t\tArgType typedMap = generic(mapCls, kVar, vVar);\n\t\tArgType typedEntry = generic(entryCls, kVar, vVar);\n\n\t\tMap<ArgType, ArgType> typeMap = new HashMap<>();\n\t\ttypeMap.put(kVar, STRING);\n\t\ttypeMap.put(vVar, EXCEPTION);\n\n\t\tArgType replacedMap = typeUtils.replaceTypeVariablesUsingMap(typedMap, typeMap);\n\t\tArgType replacedEntry = typeUtils.replaceTypeVariablesUsingMap(typedEntry, typeMap);\n\n\t\treplaceTypeVar(outerGeneric(typedMap, entryCls), typeMap, outerGeneric(replacedMap, entryCls));\n\t\treplaceTypeVar(outerGeneric(typedMap, typedEntry), typeMap, outerGeneric(replacedMap, replacedEntry));\n\t}\n\n\tprivate void replaceTypeVar(ArgType typeVar, Map<ArgType, ArgType> typeMap, ArgType expected) {\n\t\tArgType resultType = typeUtils.replaceTypeVariablesUsingMap(typeVar, typeMap);\n\t\tassertThat(resultType)\n\t\t\t\t.as(\"Replace %s using map %s\", typeVar, typeMap)\n\t\t\t\t.isEqualTo(expected);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/dex/trycatch/TryCatchBlockAttrTest.java",
    "content": "package jadx.core.dex.trycatch;\n\nimport org.junit.jupiter.api.Nested;\n\npublic class TryCatchBlockAttrTest {\n\t@Nested\n\tpublic class TryCatchBlockAttrIntegration {\n\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/dex/visitors/typeinference/PrimitiveConversionsTests.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport jadx.api.JadxArgs;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.RootNode;\n\nimport static jadx.core.dex.instructions.args.ArgType.BOOLEAN;\nimport static jadx.core.dex.instructions.args.ArgType.BYTE;\nimport static jadx.core.dex.instructions.args.ArgType.CHAR;\nimport static jadx.core.dex.instructions.args.ArgType.DOUBLE;\nimport static jadx.core.dex.instructions.args.ArgType.FLOAT;\nimport static jadx.core.dex.instructions.args.ArgType.INT;\nimport static jadx.core.dex.instructions.args.ArgType.LONG;\nimport static jadx.core.dex.instructions.args.ArgType.SHORT;\nimport static jadx.core.dex.instructions.args.ArgType.VOID;\nimport static jadx.core.dex.visitors.typeinference.TypeCompareEnum.CONFLICT;\nimport static jadx.core.dex.visitors.typeinference.TypeCompareEnum.EQUAL;\nimport static jadx.core.dex.visitors.typeinference.TypeCompareEnum.NARROW;\nimport static jadx.core.dex.visitors.typeinference.TypeCompareEnum.WIDER;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class PrimitiveConversionsTests {\n\n\tprivate static TypeCompare comparator;\n\n\t@BeforeAll\n\tstatic void before() {\n\t\tJadxArgs args = new JadxArgs();\n\t\tRootNode root = new RootNode(args);\n\t\tcomparator = new TypeCompare(root);\n\t}\n\n\t@DisplayName(\"Check conversion of numeric types\")\n\t@ParameterizedTest(name = \"{0} -> {1} (should be {2})\")\n\t@MethodSource(\"provideArgsForNumericConversionsTest\")\n\tvoid testNumericConversions(ArgType firstType, ArgType secondType, TypeCompareEnum expectedResult) {\n\t\tassertThat(comparator.compareTypes(firstType, secondType)).isEqualTo(expectedResult);\n\t}\n\n\t@DisplayName(\"Ensure that `boolean` is not convertible to other primitive types\")\n\t@ParameterizedTest(name = \"{0} <-> boolean\")\n\t@MethodSource(\"providePrimitiveTypesWithVoid\")\n\tvoid testBooleanConversions(ArgType type) {\n\t\tfinal var expectedResult = type.equals(BOOLEAN) ? EQUAL : CONFLICT;\n\t\tassertThat(comparator.compareTypes(type, BOOLEAN)).isEqualTo(expectedResult);\n\t\tassertThat(comparator.compareTypes(BOOLEAN, type)).isEqualTo(expectedResult);\n\t}\n\n\t@DisplayName(\"Ensure that `void` is not convertible to other primitive types\")\n\t@ParameterizedTest(name = \"{0} <-> void\")\n\t@MethodSource(\"providePrimitiveTypesWithVoid\")\n\tvoid testVoidConversions(ArgType type) {\n\t\tfinal var expectedResult = type.equals(VOID) ? EQUAL : CONFLICT;\n\t\tassertThat(comparator.compareTypes(type, VOID)).isEqualTo(expectedResult);\n\t\tassertThat(comparator.compareTypes(VOID, type)).isEqualTo(expectedResult);\n\t}\n\n\tprivate static Stream<Arguments> provideArgsForNumericConversionsTest() {\n\t\treturn Stream.of(\n\t\t\t\tArguments.of(BYTE, BYTE, EQUAL),\n\t\t\t\tArguments.of(BYTE, SHORT, NARROW),\n\t\t\t\tArguments.of(BYTE, CHAR, WIDER),\n\t\t\t\tArguments.of(BYTE, INT, NARROW),\n\t\t\t\tArguments.of(BYTE, LONG, NARROW),\n\t\t\t\tArguments.of(BYTE, FLOAT, NARROW),\n\t\t\t\tArguments.of(BYTE, DOUBLE, NARROW),\n\n\t\t\t\tArguments.of(SHORT, BYTE, WIDER),\n\t\t\t\tArguments.of(SHORT, SHORT, EQUAL),\n\t\t\t\tArguments.of(SHORT, CHAR, WIDER),\n\t\t\t\tArguments.of(SHORT, INT, NARROW),\n\t\t\t\tArguments.of(SHORT, LONG, NARROW),\n\t\t\t\tArguments.of(SHORT, FLOAT, NARROW),\n\t\t\t\tArguments.of(SHORT, DOUBLE, NARROW),\n\n\t\t\t\tArguments.of(CHAR, BYTE, WIDER),\n\t\t\t\tArguments.of(CHAR, SHORT, WIDER),\n\t\t\t\tArguments.of(CHAR, CHAR, EQUAL),\n\t\t\t\tArguments.of(CHAR, INT, NARROW),\n\t\t\t\tArguments.of(CHAR, LONG, NARROW),\n\t\t\t\tArguments.of(CHAR, FLOAT, NARROW),\n\t\t\t\tArguments.of(CHAR, DOUBLE, NARROW),\n\n\t\t\t\tArguments.of(INT, BYTE, WIDER),\n\t\t\t\tArguments.of(INT, SHORT, WIDER),\n\t\t\t\tArguments.of(INT, CHAR, WIDER),\n\t\t\t\tArguments.of(INT, INT, EQUAL),\n\t\t\t\tArguments.of(INT, LONG, NARROW),\n\t\t\t\tArguments.of(INT, FLOAT, NARROW),\n\t\t\t\tArguments.of(INT, DOUBLE, NARROW),\n\n\t\t\t\tArguments.of(LONG, BYTE, WIDER),\n\t\t\t\tArguments.of(LONG, SHORT, WIDER),\n\t\t\t\tArguments.of(LONG, CHAR, WIDER),\n\t\t\t\tArguments.of(LONG, INT, WIDER),\n\t\t\t\tArguments.of(LONG, LONG, EQUAL),\n\t\t\t\tArguments.of(LONG, FLOAT, NARROW),\n\t\t\t\tArguments.of(LONG, DOUBLE, NARROW),\n\n\t\t\t\tArguments.of(FLOAT, BYTE, WIDER),\n\t\t\t\tArguments.of(FLOAT, SHORT, WIDER),\n\t\t\t\tArguments.of(FLOAT, CHAR, WIDER),\n\t\t\t\tArguments.of(FLOAT, INT, WIDER),\n\t\t\t\tArguments.of(FLOAT, LONG, WIDER),\n\t\t\t\tArguments.of(FLOAT, FLOAT, EQUAL),\n\t\t\t\tArguments.of(FLOAT, DOUBLE, NARROW),\n\n\t\t\t\tArguments.of(DOUBLE, BYTE, WIDER),\n\t\t\t\tArguments.of(DOUBLE, SHORT, WIDER),\n\t\t\t\tArguments.of(DOUBLE, CHAR, WIDER),\n\t\t\t\tArguments.of(DOUBLE, INT, WIDER),\n\t\t\t\tArguments.of(DOUBLE, LONG, WIDER),\n\t\t\t\tArguments.of(DOUBLE, FLOAT, WIDER),\n\t\t\t\tArguments.of(DOUBLE, DOUBLE, EQUAL));\n\t}\n\n\tprivate static Stream<ArgType> providePrimitiveTypesWithVoid() {\n\t\treturn Stream.of(BYTE, SHORT, CHAR, INT, LONG, FLOAT, DOUBLE, BOOLEAN, VOID);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/dex/visitors/typeinference/TypeCompareTest.java",
    "content": "package jadx.core.dex.visitors.typeinference;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.NotYetImplementedExtension;\nimport jadx.api.JadxArgs;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.ArgType.WildcardBound;\nimport jadx.core.dex.nodes.RootNode;\n\nimport static jadx.core.dex.instructions.args.ArgType.BYTE;\nimport static jadx.core.dex.instructions.args.ArgType.CHAR;\nimport static jadx.core.dex.instructions.args.ArgType.CLASS;\nimport static jadx.core.dex.instructions.args.ArgType.EXCEPTION;\nimport static jadx.core.dex.instructions.args.ArgType.INT;\nimport static jadx.core.dex.instructions.args.ArgType.NARROW;\nimport static jadx.core.dex.instructions.args.ArgType.NARROW_INTEGRAL;\nimport static jadx.core.dex.instructions.args.ArgType.OBJECT;\nimport static jadx.core.dex.instructions.args.ArgType.STRING;\nimport static jadx.core.dex.instructions.args.ArgType.THROWABLE;\nimport static jadx.core.dex.instructions.args.ArgType.UNKNOWN;\nimport static jadx.core.dex.instructions.args.ArgType.UNKNOWN_ARRAY;\nimport static jadx.core.dex.instructions.args.ArgType.UNKNOWN_OBJECT;\nimport static jadx.core.dex.instructions.args.ArgType.array;\nimport static jadx.core.dex.instructions.args.ArgType.generic;\nimport static jadx.core.dex.instructions.args.ArgType.genericType;\nimport static jadx.core.dex.instructions.args.ArgType.object;\nimport static jadx.core.dex.instructions.args.ArgType.wildcard;\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@ExtendWith(NotYetImplementedExtension.class)\npublic class TypeCompareTest {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(TypeCompareTest.class);\n\n\tprivate TypeCompare compare;\n\n\t@BeforeEach\n\tpublic void init() {\n\t\tJadxArgs args = new JadxArgs();\n\t\tRootNode root = new RootNode(args);\n\t\troot.loadClasses(Collections.emptyList());\n\t\troot.initClassPath();\n\t\tcompare = new TypeCompare(root);\n\t}\n\n\t@Test\n\tpublic void compareTypes() {\n\t\tcheck(INT, UNKNOWN_OBJECT, TypeCompareEnum.CONFLICT);\n\t\tcheck(INT, OBJECT, TypeCompareEnum.CONFLICT);\n\n\t\tfirstIsNarrow(INT, UNKNOWN);\n\t\tfirstIsNarrow(CHAR, NARROW_INTEGRAL);\n\n\t\tfirstIsNarrow(array(UNKNOWN), UNKNOWN);\n\t\tfirstIsNarrow(array(UNKNOWN), NARROW);\n\t\tfirstIsNarrow(array(CHAR), UNKNOWN_OBJECT);\n\t}\n\n\t@Test\n\tpublic void compareArrays() {\n\t\tfirstIsNarrow(array(CHAR), OBJECT);\n\t\tfirstIsNarrow(array(CHAR), array(UNKNOWN));\n\n\t\tfirstIsNarrow(array(OBJECT), OBJECT);\n\t\tfirstIsNarrow(array(OBJECT), array(UNKNOWN_OBJECT));\n\t\tfirstIsNarrow(array(STRING), array(UNKNOWN_OBJECT));\n\t\tfirstIsNarrow(array(STRING), array(OBJECT));\n\n\t\tfirstIsNarrow(UNKNOWN_ARRAY, OBJECT);\n\n\t\tfirstIsNarrow(array(BYTE), OBJECT);\n\t\tfirstIsNarrow(array(array(BYTE)), array(OBJECT));\n\n\t\tcheck(array(OBJECT), array(INT), TypeCompareEnum.CONFLICT);\n\n\t\tArgType integerType = object(\"java.lang.Integer\");\n\t\tcheck(array(OBJECT), array(integerType), TypeCompareEnum.WIDER);\n\t\tcheck(array(INT), array(integerType), TypeCompareEnum.CONFLICT);\n\t\tcheck(array(INT), array(INT), TypeCompareEnum.EQUAL);\n\n\t\tArgType wildClass = generic(CLASS, wildcard());\n\t\tcheck(array(wildClass), array(CLASS), TypeCompareEnum.NARROW_BY_GENERIC);\n\t\tcheck(array(CLASS), array(wildClass), TypeCompareEnum.WIDER_BY_GENERIC);\n\t}\n\n\t@Test\n\tpublic void compareGenerics() {\n\t\tArgType mapCls = object(\"java.util.Map\");\n\t\tArgType setCls = object(\"java.util.Set\");\n\n\t\tArgType keyType = genericType(\"K\");\n\t\tArgType valueType = genericType(\"V\");\n\t\tArgType mapGeneric = ArgType.generic(mapCls.getObject(), keyType, valueType);\n\n\t\tcheck(mapCls, mapGeneric, TypeCompareEnum.WIDER_BY_GENERIC);\n\t\tcheck(mapCls, setCls, TypeCompareEnum.CONFLICT);\n\n\t\tArgType setGeneric = ArgType.generic(setCls.getObject(), valueType);\n\t\tArgType setWildcard = ArgType.generic(setCls.getObject(), ArgType.wildcard());\n\n\t\tcheck(setWildcard, setGeneric, TypeCompareEnum.CONFLICT);\n\t\tcheck(setWildcard, setCls, TypeCompareEnum.NARROW_BY_GENERIC);\n\t\t// TODO implement compare for wildcard with bounds\n\t}\n\n\t@Test\n\tpublic void compareWildCards() {\n\t\tArgType clsWildcard = generic(CLASS.getObject(), wildcard());\n\t\tcheck(clsWildcard, CLASS, TypeCompareEnum.NARROW_BY_GENERIC);\n\n\t\tArgType clsExtendedWildcard = generic(CLASS.getObject(), wildcard(STRING, WildcardBound.EXTENDS));\n\t\tcheck(clsWildcard, clsExtendedWildcard, TypeCompareEnum.WIDER);\n\n\t\tArgType listWildcard = generic(CLASS.getObject(), wildcard(object(\"java.util.List\"), WildcardBound.EXTENDS));\n\t\tArgType collWildcard = generic(CLASS.getObject(), wildcard(object(\"java.util.Collection\"), WildcardBound.EXTENDS));\n\t\tcheck(listWildcard, collWildcard, TypeCompareEnum.NARROW);\n\n\t\tArgType collSuperWildcard = generic(CLASS.getObject(), wildcard(object(\"java.util.Collection\"), WildcardBound.SUPER));\n\t\tcheck(collSuperWildcard, listWildcard, TypeCompareEnum.CONFLICT);\n\t}\n\n\t@Test\n\tpublic void compareGenericWildCards() {\n\t\t// 'java.util.List<T>' and 'java.util.List<? extends T>'\n\t\tArgType listCls = object(\"java.util.List\");\n\t\tArgType genericType = genericType(\"T\");\n\t\tArgType genericList = generic(listCls, genericType);\n\t\tArgType genericExtendedList = generic(listCls, wildcard(genericType, WildcardBound.EXTENDS));\n\t\tcheck(genericList, genericExtendedList, TypeCompareEnum.CONFLICT_BY_GENERIC);\n\t}\n\n\t@Test\n\tpublic void compareGenericTypes() {\n\t\tArgType vType = genericType(\"V\");\n\t\tcheck(vType, OBJECT, TypeCompareEnum.NARROW);\n\t\tcheck(vType, STRING, TypeCompareEnum.CONFLICT);\n\n\t\tArgType rType = genericType(\"R\");\n\t\tcheck(vType, rType, TypeCompareEnum.CONFLICT);\n\t\tcheck(vType, vType, TypeCompareEnum.EQUAL);\n\n\t\tArgType tType = genericType(\"T\");\n\t\tArgType tStringType = genericType(\"T\", STRING);\n\n\t\tcheck(tStringType, STRING, TypeCompareEnum.NARROW);\n\t\tcheck(tStringType, OBJECT, TypeCompareEnum.NARROW);\n\t\tcheck(tStringType, tType, TypeCompareEnum.NARROW);\n\n\t\tArgType tObjType = genericType(\"T\", OBJECT);\n\n\t\tcheck(tObjType, OBJECT, TypeCompareEnum.NARROW);\n\t\tcheck(tObjType, tType, TypeCompareEnum.EQUAL);\n\n\t\tcheck(tStringType, tObjType, TypeCompareEnum.NARROW);\n\t}\n\n\t@Test\n\tpublic void compareGenericTypes2() {\n\t\tArgType npeType = object(\"java.lang.NullPointerException\");\n\n\t\t// check clsp graph\n\t\tcheck(npeType, THROWABLE, TypeCompareEnum.NARROW);\n\t\tcheck(npeType, EXCEPTION, TypeCompareEnum.NARROW);\n\t\tcheck(EXCEPTION, THROWABLE, TypeCompareEnum.NARROW);\n\n\t\tArgType typeVar = genericType(\"T\", EXCEPTION); // T extends Exception\n\n\t\t// target checks\n\t\tcheck(THROWABLE, typeVar, TypeCompareEnum.WIDER);\n\t\tcheck(EXCEPTION, typeVar, TypeCompareEnum.WIDER);\n\t\tcheck(npeType, typeVar, TypeCompareEnum.NARROW);\n\t}\n\n\t@Test\n\tpublic void compareOuterGenerics() {\n\t\tArgType hashMapType = object(\"java.util.HashMap\");\n\t\tArgType innerEntrySetType = object(\"EntrySet\");\n\t\tArgType firstInstance = ArgType.outerGeneric(generic(hashMapType, STRING, STRING), innerEntrySetType);\n\t\tArgType secondInstance = ArgType.outerGeneric(generic(hashMapType, OBJECT, OBJECT), innerEntrySetType);\n\n\t\tcheck(firstInstance, secondInstance, TypeCompareEnum.NARROW);\n\t}\n\n\tprivate void firstIsNarrow(ArgType first, ArgType second) {\n\t\tcheck(first, second, TypeCompareEnum.NARROW);\n\t}\n\n\tprivate void check(ArgType first, ArgType second, TypeCompareEnum expectedResult) {\n\t\tLOG.debug(\"Compare: '{}' and '{}', expect: '{}'\", first, second, expectedResult);\n\n\t\tassertThat(compare.compareTypes(first, second))\n\t\t\t\t.as(\"Compare '%s' and '%s'\", first, second)\n\t\t\t\t.isEqualTo(expectedResult);\n\n\t\tassertThat(compare.compareTypes(second, first))\n\t\t\t\t.as(\"Compare '%s' and '%s'\", second, first)\n\t\t\t\t.isEqualTo(expectedResult.invert());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/plugins/versions/VerifyRequiredVersionTest.java",
    "content": "package jadx.core.plugins.versions;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass VerifyRequiredVersionTest {\n\t@Test\n\tpublic void test() {\n\t\tisCompatible(\"1.5.0, r2000\", \"1.5.1\", true);\n\t\tisCompatible(\"1.5.1, r3000\", \"1.5.1\", true);\n\t\tisCompatible(\"1.5.1, r3000\", \"1.6.0\", true);\n\t\tisCompatible(\"1.5.1, r3000\", \"1.5.0\", false);\n\n\t\tisCompatible(\"1.5.1, r3000\", \"r3001.417bb7a\", true);\n\t\tisCompatible(\"1.5.1, r3000\", \"r4000\", true);\n\t\tisCompatible(\"1.5.1, r3000\", \"r3000\", true);\n\t\tisCompatible(\"1.5.1, r3000\", \"r2000\", false);\n\t}\n\n\tprivate static void isCompatible(String requiredVersion, String jadxVersion, boolean result) {\n\t\tassertThat(new VerifyRequiredVersion(jadxVersion).isCompatible(requiredVersion))\n\t\t\t\t.as(\"Expect plugin with required version %s is%s compatible with jadx %s\",\n\t\t\t\t\t\trequiredVersion, result ? \"\" : \" not\", jadxVersion)\n\t\t\t\t.isEqualTo(result);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/plugins/versions/VersionComparatorTest.java",
    "content": "package jadx.core.plugins.versions;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass VersionComparatorTest {\n\n\t@Test\n\tpublic void testCompare() {\n\t\tcheckCompare(\"\", \"\", 0);\n\t\tcheckCompare(\"1\", \"1\", 0);\n\t\tcheckCompare(\"1\", \"2\", -1);\n\t\tcheckCompare(\"1.1\", \"1.1\", 0);\n\t\tcheckCompare(\"0.5\", \"0.5\", 0);\n\t\tcheckCompare(\"0.5\", \"0.5.0\", 0);\n\t\tcheckCompare(\"0.5\", \"0.5.00\", 0);\n\t\tcheckCompare(\"0.5\", \"0.5.0.0\", 0);\n\t\tcheckCompare(\"0.5\", \"0.5.0.1\", -1);\n\t\tcheckCompare(\"0.5.0\", \"0.5.0\", 0);\n\t\tcheckCompare(\"0.5.0\", \"0.5.1\", -1);\n\t\tcheckCompare(\"0.5\", \"0.5.1\", -1);\n\t\tcheckCompare(\"0.4.8\", \"0.5\", -1);\n\t\tcheckCompare(\"0.4.8\", \"0.5.0\", -1);\n\t\tcheckCompare(\"0.4.8\", \"0.6\", -1);\n\t\tcheckCompare(\"1.3.3.1\", \"1.3.3\", 1);\n\t\tcheckCompare(\"1.3.3-1\", \"1.3.3\", 1);\n\t\tcheckCompare(\"1.3.3.1-1\", \"1.3.3\", 1);\n\t}\n\n\t@Test\n\tpublic void testCompareUnstable() {\n\t\tcheckCompare(\"r2190.ce527ed\", \"jadx-r2299.742d30d\", -1);\n\t}\n\n\tprivate static void checkCompare(String a, String b, int result) {\n\t\tassertThat(VersionComparator.checkAndCompare(a, b))\n\t\t\t\t.as(\"Compare %s and %s expect %d\", a, b, result)\n\t\t\t\t.isEqualTo(result);\n\t\tassertThat(VersionComparator.checkAndCompare(b, a))\n\t\t\t\t.as(\"Compare %s and %s expect %d\", b, a, -result)\n\t\t\t\t.isEqualTo(-result);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/utils/PassMergeTest.java",
    "content": "package jadx.core.utils;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.impl.passes.DecompilePassWrapper;\nimport jadx.api.plugins.pass.JadxPass;\nimport jadx.api.plugins.pass.JadxPassInfo;\nimport jadx.api.plugins.pass.impl.OrderedJadxPassInfo;\nimport jadx.api.plugins.pass.impl.SimpleJadxPassInfo;\nimport jadx.api.plugins.pass.types.JadxDecompilePass;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.dex.visitors.AbstractVisitor;\nimport jadx.core.dex.visitors.IDexTreeVisitor;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static java.util.Arrays.asList;\nimport static java.util.Collections.emptyList;\nimport static java.util.Collections.singletonList;\nimport static org.assertj.core.api.Assertions.catchThrowable;\n\nclass PassMergeTest {\n\n\t@Test\n\tpublic void testSimple() {\n\t\tList<String> base = asList(\"a\", \"b\", \"c\");\n\t\tcheck(base, mockPass(\"x\"), asList(\"a\", \"b\", \"c\", \"x\"));\n\t\tcheck(base, mockPass(mockInfo(\"x\").after(JadxPassInfo.START)), asList(\"x\", \"a\", \"b\", \"c\"));\n\t\tcheck(base, mockPass(mockInfo(\"x\").before(JadxPassInfo.END)), asList(\"a\", \"b\", \"c\", \"x\"));\n\t}\n\n\t@Test\n\tpublic void testSingle() {\n\t\tList<String> base = asList(\"a\", \"b\", \"c\");\n\t\tcheck(base, mockPass(mockInfo(\"x\").after(\"a\")), asList(\"a\", \"x\", \"b\", \"c\"));\n\t\tcheck(base, mockPass(mockInfo(\"x\").before(\"c\")), asList(\"a\", \"b\", \"x\", \"c\"));\n\t\tcheck(base, mockPass(mockInfo(\"x\").before(\"a\")), asList(\"x\", \"a\", \"b\", \"c\"));\n\t\tcheck(base, mockPass(mockInfo(\"x\").after(\"c\")), asList(\"a\", \"b\", \"c\", \"x\"));\n\t}\n\n\t@Test\n\tpublic void testMulti() {\n\t\tList<String> base = asList(\"a\", \"b\", \"c\");\n\t\tJadxPass x = mockPass(mockInfo(\"x\").after(\"a\"));\n\t\tJadxPass y = mockPass(mockInfo(\"y\").after(\"a\"));\n\t\tJadxPass z = mockPass(mockInfo(\"z\").before(\"b\"));\n\t\tcheck(base, asList(x, y, z), asList(\"a\", \"y\", \"x\", \"z\", \"b\", \"c\"));\n\t}\n\n\t@Test\n\tpublic void testMultiWithDeps() {\n\t\tList<String> base = asList(\"a\", \"b\", \"c\");\n\t\tJadxPass x = mockPass(mockInfo(\"x\").after(\"a\"));\n\t\tJadxPass y = mockPass(mockInfo(\"y\").after(\"x\"));\n\t\tJadxPass z = mockPass(mockInfo(\"z\").before(\"b\").after(\"y\"));\n\t\tcheck(base, asList(x, y, z), asList(\"a\", \"x\", \"y\", \"z\", \"b\", \"c\"));\n\t}\n\n\t@Test\n\tpublic void testMultiWithDeps2() {\n\t\tList<String> base = asList(\"a\", \"b\", \"c\");\n\t\tJadxPass x = mockPass(mockInfo(\"x\").before(\"y\"));\n\t\tJadxPass y = mockPass(mockInfo(\"y\").before(\"b\"));\n\t\tJadxPass z = mockPass(mockInfo(\"z\").after(\"y\"));\n\t\tcheck(base, asList(x, y, z), asList(\"a\", \"x\", \"y\", \"z\", \"b\", \"c\"));\n\t}\n\n\t@Test\n\tpublic void testMultiWithDeps3() {\n\t\tList<String> base = asList(\"a\", \"b\", \"c\");\n\t\tJadxPass x = mockPass(mockInfo(\"x\"));\n\t\tJadxPass y = mockPass(mockInfo(\"y\").after(\"x\").before(\"b\"));\n\t\tcheck(base, asList(x, y), asList(\"a\", \"x\", \"y\", \"b\", \"c\"));\n\t}\n\n\t@Test\n\tpublic void testLoop() {\n\t\tList<String> base = asList(\"a\", \"b\", \"c\");\n\t\tJadxPass x = mockPass(mockInfo(\"x\").before(\"y\"));\n\t\tJadxPass y = mockPass(mockInfo(\"y\").before(\"x\"));\n\t\tThrowable thrown = catchThrowable(() -> check(base, asList(x, y), emptyList()));\n\t\tassertThat(thrown).isInstanceOf(JadxRuntimeException.class);\n\t}\n\n\tprivate void check(List<String> visitorNames, JadxPass pass, List<String> result) {\n\t\tcheck(visitorNames, singletonList(pass), result);\n\t}\n\n\tprivate void check(List<String> visitorNames, List<JadxPass> passes, List<String> result) {\n\t\tList<IDexTreeVisitor> visitors = ListUtils.map(visitorNames, PassMergeTest::mockVisitor);\n\t\tnew PassMerge(visitors).merge(passes, p -> new DecompilePassWrapper((JadxDecompilePass) p));\n\t\tList<String> resultVisitors = ListUtils.map(visitors, IDexTreeVisitor::getName);\n\t\tassertThat(resultVisitors).isEqualTo(result);\n\t}\n\n\tprivate static IDexTreeVisitor mockVisitor(String name) {\n\t\treturn new AbstractVisitor() {\n\t\t\t@Override\n\t\t\tpublic String getName() {\n\t\t\t\treturn name;\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate JadxPass mockPass(String name) {\n\t\treturn mockPass(new SimpleJadxPassInfo(name));\n\t}\n\n\tprivate OrderedJadxPassInfo mockInfo(String name) {\n\t\treturn new OrderedJadxPassInfo(name, name);\n\t}\n\n\tprivate JadxPass mockPass(JadxPassInfo info) {\n\t\treturn new JadxDecompilePass() {\n\t\t\t@Override\n\t\t\tpublic void init(RootNode root) {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean visit(ClassNode cls) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void visit(MethodNode mth) {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic JadxPassInfo getInfo() {\n\t\t\t\treturn info;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String toString() {\n\t\t\t\treturn info.getName();\n\t\t\t}\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/utils/TestBetterName.java",
    "content": "package jadx.core.utils;\n\nimport org.junit.jupiter.api.Test;\n\nimport static jadx.core.utils.BetterName.calcRating;\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestBetterName {\n\n\t@Deprecated\n\t@Test\n\tpublic void test() {\n\t\texpectFirst(\"color_main\", \"t0\");\n\t\texpectFirst(\"done\", \"oOo0oO0o\");\n\t}\n\n\t@Deprecated\n\tprivate void expectFirst(String first, String second) {\n\t\tString best = BetterName.compareAndGet(first, second);\n\t\tassertThat(best)\n\t\t\t\t.as(() -> String.format(\"'%s'=%d, '%s'=%d\", first, calcRating(first), second, calcRating(second)))\n\t\t\t\t.isEqualTo(first);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/utils/TestGetBetterClassName.java",
    "content": "package jadx.core.utils;\n\nimport org.assertj.core.api.AbstractStringAssert;\nimport org.junit.jupiter.api.Test;\n\nimport static jadx.core.utils.BetterName.getBetterClassName;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestGetBetterClassName {\n\n\t@Test\n\tpublic void testGoodNamesVsGeneratedAliases() {\n\t\tassertThatBetterClassName(\"AppCompatButton\", \"C2404e2\").isEqualTo(\"AppCompatButton\");\n\t\tassertThatBetterClassName(\"ContextThemeWrapper\", \"C2106b1\").isEqualTo(\"ContextThemeWrapper\");\n\t\tassertThatBetterClassName(\"ListPopupWindow\", \"C2344a3\").isEqualTo(\"ListPopupWindow\");\n\t}\n\n\t@Test\n\tpublic void testShortGoodNamesVsGeneratedAliases() {\n\t\tassertThatBetterClassName(\"Room\", \"C2937kh\").isEqualTo(\"Room\");\n\t\tassertThatBetterClassName(\"Fade\", \"C1428qi\").isEqualTo(\"Fade\");\n\t\tassertThatBetterClassName(\"Scene\", \"C4063yi\").isEqualTo(\"Scene\");\n\t}\n\n\t@Test\n\tpublic void testGoodNamesVsGeneratedAliasesWithPrefix() {\n\t\tassertThatBetterClassName(\"AppCompatActivity\", \"ActivityC2646h0\").isEqualTo(\"AppCompatActivity\");\n\t\tassertThatBetterClassName(\"PagerAdapter\", \"AbstractC3038lk\").isEqualTo(\"PagerAdapter\");\n\t\tassertThatBetterClassName(\"Lazy\", \"InterfaceC6434a\").isEqualTo(\"Lazy\");\n\t\tassertThatBetterClassName(\"MembersInjector\", \"InterfaceC6435b\").isEqualTo(\"MembersInjector\");\n\t\tassertThatBetterClassName(\"Subscriber\", \"InterfaceC6439c\").isEqualTo(\"Subscriber\");\n\t}\n\n\t@Test\n\tpublic void testGoodNamesWithDigitsVsGeneratedAliases() {\n\t\tassertThatBetterClassName(\"ISO8061Formatter\", \"C1121uq4\").isEqualTo(\"ISO8061Formatter\");\n\t\tassertThatBetterClassName(\"Jdk9Platform\", \"C1189rn6\").isEqualTo(\"Jdk9Platform\");\n\t\tassertThatBetterClassName(\"WrappedDrawableApi14\", \"C2847i9\").isEqualTo(\"WrappedDrawableApi14\");\n\t\tassertThatBetterClassName(\"WrappedDrawableApi21\", \"C2888j9\").isEqualTo(\"WrappedDrawableApi21\");\n\t}\n\n\t@Test\n\tpublic void testShortNamesVsLongNames() {\n\t\tassertThatBetterClassName(\"az\", \"Observer\").isEqualTo(\"Observer\");\n\t\tassertThatBetterClassName(\"bb\", \"RenderEvent\").isEqualTo(\"RenderEvent\");\n\t\tassertThatBetterClassName(\"aaaa\", \"FontUtils\").isEqualTo(\"FontUtils\");\n\t}\n\n\t/**\n\t * Tests {@link BetterName#getBetterClassName(String, String)} on equally good names.\n\t * In this case, according to the documentation, the method should return the first argument.\n\t *\n\t * @see BetterName#getBetterClassName(String, String)\n\t */\n\t@Test\n\tpublic void testEquallyGoodNames() {\n\t\tassertThatBetterClassName(\"AAAA\", \"BBBB\").isEqualTo(\"AAAA\");\n\t\tassertThatBetterClassName(\"BBBB\", \"AAAA\").isEqualTo(\"BBBB\");\n\n\t\tassertThatBetterClassName(\"XYXYXY\", \"YZYZYZ\").isEqualTo(\"XYXYXY\");\n\t\tassertThatBetterClassName(\"YZYZYZ\", \"XYXYXY\").isEqualTo(\"YZYZYZ\");\n\t}\n\n\tprivate AbstractStringAssert<?> assertThatBetterClassName(String firstName, String secondName) {\n\t\treturn assertThat(getBetterClassName(firstName, secondName));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/utils/TestGetBetterResourceName.java",
    "content": "package jadx.core.utils;\n\nimport org.assertj.core.api.AbstractStringAssert;\nimport org.junit.jupiter.api.Test;\n\nimport static jadx.core.utils.BetterName.getBetterResourceName;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestGetBetterResourceName {\n\n\t@Test\n\tpublic void testGoodNamesVsSyntheticNames() {\n\t\tassertThatBetterResourceName(\"color_main\", \"t0\").isEqualTo(\"color_main\");\n\t\tassertThatBetterResourceName(\"done\", \"oOo0oO0o\").isEqualTo(\"done\");\n\t}\n\n\t/**\n\t * Tests {@link BetterName#getBetterResourceName(String, String)} on equally good names.\n\t * In this case, according to the documentation, the method should return the first argument.\n\t *\n\t * @see BetterName#getBetterResourceName(String, String)\n\t */\n\t@Test\n\tpublic void testEquallyGoodNames() {\n\t\tassertThatBetterResourceName(\"AAAA\", \"BBBB\").isEqualTo(\"AAAA\");\n\t\tassertThatBetterResourceName(\"BBBB\", \"AAAA\").isEqualTo(\"BBBB\");\n\n\t\tassertThatBetterResourceName(\"Theme.AppCompat.Light\", \"Theme_AppCompat_Light\")\n\t\t\t\t.isEqualTo(\"Theme.AppCompat.Light\");\n\t\tassertThatBetterResourceName(\"Theme_AppCompat_Light\", \"Theme.AppCompat.Light\")\n\t\t\t\t.isEqualTo(\"Theme_AppCompat_Light\");\n\t}\n\n\tprivate AbstractStringAssert<?> assertThatBetterResourceName(String firstName, String secondName) {\n\t\treturn assertThat(getBetterResourceName(firstName, secondName));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/utils/TypeUtilsTest.java",
    "content": "package jadx.core.utils;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxArgs;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.RootNode;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\nclass TypeUtilsTest {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(TypeUtilsTest.class);\n\n\tprivate static RootNode root;\n\n\t@BeforeAll\n\tpublic static void init() {\n\t\troot = new RootNode(new JadxArgs());\n\t\troot.initClassPath();\n\t}\n\n\t@Test\n\tpublic void testReplaceGenericsWithWildcards() {\n\t\t// check classpath graph\n\t\tList<ArgType> classGenerics = root.getTypeUtils().getClassGenerics(ArgType.object(\"java.util.ArrayList\"));\n\t\tassertThat(classGenerics).hasSize(1);\n\t\tArgType genericInfo = classGenerics.get(0);\n\t\tassertThat(genericInfo.getObject()).isEqualTo(\"E\");\n\t\tassertThat(genericInfo.getExtendTypes()).hasSize(0);\n\n\t\t// prepare input\n\t\tArgType instanceType = ArgType.generic(\"java.util.ArrayList\", ArgType.OBJECT);\n\t\tLOG.debug(\"instanceType: {}\", instanceType);\n\n\t\tArgType generic = ArgType.generic(\"java.util.List\", ArgType.wildcard(ArgType.genericType(\"E\"), ArgType.WildcardBound.SUPER));\n\t\tLOG.debug(\"generic: {}\", generic);\n\n\t\t// replace\n\t\tArgType result = root.getTypeUtils().replaceClassGenerics(instanceType, generic);\n\t\tLOG.debug(\"result: {}\", result);\n\n\t\tArgType expected = ArgType.generic(\"java.util.List\", ArgType.wildcard(ArgType.OBJECT, ArgType.WildcardBound.SUPER));\n\t\tassertThat(result).isEqualTo(expected);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/utils/log/LogUtilsTest.java",
    "content": "package jadx.core.utils.log;\n\nimport org.junit.jupiter.api.Test;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\nclass LogUtilsTest {\n\n\t@Test\n\tvoid escape() {\n\t\tString src = \"a.b,c:d;e disallowed\\\"a'b#c*d\\te\\rf\\ng\";\n\t\tString out = \"a.b,c:d;e disallowed.a.b.c.d.e.f.g\";\n\t\tassertThat(LogUtils.escape(src)).isEqualTo(out);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/xmlgen/ResNameUtilsTest.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass ResNameUtilsTest {\n\n\t@DisplayName(\"Check sanitizeAsResourceName(name, postfix, allowNonPrintable)\")\n\t@ParameterizedTest(name = \"({0}, {1}, {2}) -> {3}\")\n\t@MethodSource(\"provideArgsForSanitizeAsResourceNameTest\")\n\tvoid testSanitizeAsResourceName(String name, String postfix, boolean allowNonPrintable, String expectedResult) {\n\t\tassertThat(ResNameUtils.sanitizeAsResourceName(name, postfix, allowNonPrintable)).isEqualTo(expectedResult);\n\t}\n\n\t@DisplayName(\"Check convertToRFieldName(resourceName)\")\n\t@ParameterizedTest(name = \"{0} -> {1}\")\n\t@MethodSource(\"provideArgsForConvertToRFieldNameTest\")\n\tvoid testConvertToRFieldName(String resourceName, String expectedResult) {\n\t\tassertThat(ResNameUtils.convertToRFieldName(resourceName)).isEqualTo(expectedResult);\n\t}\n\n\tprivate static Stream<Arguments> provideArgsForSanitizeAsResourceNameTest() {\n\t\treturn Stream.of(\n\t\t\t\tArguments.of(\"name\", \"_postfix\", false, \"name\"),\n\n\t\t\t\tArguments.of(\"/name\", \"_postfix\", true, \"_name_postfix\"),\n\t\t\t\tArguments.of(\"na/me\", \"_postfix\", true, \"na_me_postfix\"),\n\t\t\t\tArguments.of(\"name/\", \"_postfix\", true, \"name__postfix\"),\n\n\t\t\t\tArguments.of(\"$name\", \"_postfix\", true, \"_name_postfix\"),\n\t\t\t\tArguments.of(\"na$me\", \"_postfix\", true, \"na_me_postfix\"),\n\t\t\t\tArguments.of(\"name$\", \"_postfix\", true, \"name__postfix\"),\n\n\t\t\t\tArguments.of(\".name\", \"_postfix\", true, \"_.name_postfix\"),\n\t\t\t\tArguments.of(\"na.me\", \"_postfix\", true, \"na.me\"),\n\t\t\t\tArguments.of(\"name.\", \"_postfix\", true, \"name.\"),\n\n\t\t\t\tArguments.of(\"0name\", \"_postfix\", true, \"_0name_postfix\"),\n\t\t\t\tArguments.of(\"na0me\", \"_postfix\", true, \"na0me\"),\n\t\t\t\tArguments.of(\"name0\", \"_postfix\", true, \"name0\"),\n\n\t\t\t\tArguments.of(\"-name\", \"_postfix\", true, \"_name_postfix\"),\n\t\t\t\tArguments.of(\"na-me\", \"_postfix\", true, \"na_me_postfix\"),\n\t\t\t\tArguments.of(\"name-\", \"_postfix\", true, \"name__postfix\"),\n\n\t\t\t\tArguments.of(\"Ĉname\", \"_postfix\", false, \"_name_postfix\"),\n\t\t\t\tArguments.of(\"naĈme\", \"_postfix\", false, \"na_me_postfix\"),\n\t\t\t\tArguments.of(\"nameĈ\", \"_postfix\", false, \"name__postfix\"),\n\n\t\t\t\tArguments.of(\"Ĉname\", \"_postfix\", true, \"Ĉname\"),\n\t\t\t\tArguments.of(\"naĈme\", \"_postfix\", true, \"naĈme\"),\n\t\t\t\tArguments.of(\"nameĈ\", \"_postfix\", true, \"nameĈ\"),\n\n\t\t\t\t// Uncomment this when XID_Start and XID_Continue characters are correctly determined.\n\t\t\t\t// Arguments.of(\"Жname\", \"_postfix\", true, \"Жname\"),\n\t\t\t\t// Arguments.of(\"naЖme\", \"_postfix\", true, \"naЖme\"),\n\t\t\t\t// Arguments.of(\"nameЖ\", \"_postfix\", true, \"nameЖ\"),\n\t\t\t\t//\n\t\t\t\t// Arguments.of(\"€name\", \"_postfix\", true, \"_name_postfix\"),\n\t\t\t\t// Arguments.of(\"na€me\", \"_postfix\", true, \"na_me_postfix\"),\n\t\t\t\t// Arguments.of(\"name€\", \"_postfix\", true, \"name__postfix\"),\n\n\t\t\t\tArguments.of(\"\", \"_postfix\", true, \"_postfix\"),\n\n\t\t\t\tArguments.of(\"if\", \"_postfix\", true, \"if_postfix\"),\n\t\t\t\tArguments.of(\"default\", \"_postfix\", true, \"default_postfix\"),\n\t\t\t\tArguments.of(\"true\", \"_postfix\", true, \"true_postfix\"),\n\t\t\t\tArguments.of(\"_\", \"_postfix\", true, \"__postfix\"));\n\t}\n\n\tprivate static Stream<Arguments> provideArgsForConvertToRFieldNameTest() {\n\t\treturn Stream.of(\n\t\t\t\tArguments.of(\"ThemeDesign\", \"ThemeDesign\"),\n\t\t\t\tArguments.of(\"Theme.Design\", \"Theme_Design\"),\n\n\t\t\t\tArguments.of(\"Ĉ_ThemeDesign_Ĉ\", \"Ĉ_ThemeDesign_Ĉ\"),\n\t\t\t\tArguments.of(\"Ĉ_Theme.Design_Ĉ\", \"Ĉ_Theme_Design_Ĉ\"),\n\n\t\t\t\t// The function must return a plausible result even though the resource name is invalid.\n\t\t\t\tArguments.of(\"/_ThemeDesign_/\", \"/_ThemeDesign_/\"),\n\t\t\t\tArguments.of(\"/_Theme.Design_/\", \"/_Theme_Design_/\"));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/xmlgen/ResXmlGenTest.java",
    "content": "package jadx.core.xmlgen;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.assertj.core.util.Lists;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.security.IJadxSecurity;\nimport jadx.api.security.JadxSecurityFlag;\nimport jadx.api.security.impl.JadxSecurity;\nimport jadx.core.xmlgen.entry.RawNamedValue;\nimport jadx.core.xmlgen.entry.RawValue;\nimport jadx.core.xmlgen.entry.ResourceEntry;\nimport jadx.core.xmlgen.entry.ValuesParser;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass ResXmlGenTest {\n\tprivate final JadxArgs args = new JadxArgs();\n\tprivate final IJadxSecurity security = new JadxSecurity(JadxSecurityFlag.all());\n\tprivate final ManifestAttributes manifestAttributes = new ManifestAttributes(security);\n\n\t@BeforeEach\n\tvoid init() {\n\t\targs.setCodeNewLineStr(\"\\n\");\n\t}\n\n\t@Test\n\tvoid testSimpleAttr() {\n\t\tResourceStorage resStorage = new ResourceStorage(security);\n\t\tResourceEntry re = new ResourceEntry(2130903103, \"jadx.gui.app\", \"attr\", \"size\", \"\");\n\t\tre.setNamedValues(Lists.list(new RawNamedValue(16777216, new RawValue(16, 64))));\n\t\tresStorage.add(re);\n\n\t\tValuesParser vp = new ValuesParser(null, resStorage.getResourcesNames());\n\t\tResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);\n\t\tList<ResContainer> files = resXmlGen.makeResourcesXml(args);\n\n\t\tassertThat(files).hasSize(1);\n\t\tassertThat(files.get(0).getName()).isEqualTo(\"res/values/attrs.xml\");\n\t\tString input = files.get(0).getText().toString();\n\t\tassertThat(input).isEqualTo(\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\\n\"\n\t\t\t\t+ \"<resources>\\n\"\n\t\t\t\t+ \"    <attr name=\\\"size\\\" format=\\\"dimension\\\">\\n\"\n\t\t\t\t+ \"    </attr>\\n\"\n\t\t\t\t+ \"</resources>\");\n\t}\n\n\t@Test\n\tvoid testAttrEnum() {\n\t\tResourceStorage resStorage = new ResourceStorage(security);\n\t\tResourceEntry re = new ResourceEntry(2130903103, \"jadx.gui.app\", \"attr\", \"size\", \"\");\n\t\tre.setNamedValues(\n\t\t\t\tLists.list(\n\t\t\t\t\t\tnew RawNamedValue(0x01000000, new RawValue(16, 65536)),\n\t\t\t\t\t\tnew RawNamedValue(0x01040000, new RawValue(16, 1))));\n\t\tresStorage.add(re);\n\n\t\tValuesParser vp = new ValuesParser(null, resStorage.getResourcesNames());\n\t\tResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);\n\t\tList<ResContainer> files = resXmlGen.makeResourcesXml(args);\n\n\t\tassertThat(files).hasSize(1);\n\t\tassertThat(files.get(0).getName()).isEqualTo(\"res/values/attrs.xml\");\n\t\tString input = files.get(0).getText().toString();\n\t\tassertThat(input).isEqualTo(\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\\n\"\n\t\t\t\t+ \"<resources>\\n\"\n\t\t\t\t+ \"    <attr name=\\\"size\\\">\\n\"\n\t\t\t\t+ \"        <enum name=\\\"android:string.cancel\\\" value=\\\"1\\\" />\\n\"\n\t\t\t\t+ \"    </attr>\\n\"\n\t\t\t\t+ \"</resources>\");\n\t}\n\n\t@Test\n\tvoid testAttrFlag() {\n\t\tResourceStorage resStorage = new ResourceStorage(security);\n\t\tResourceEntry re = new ResourceEntry(2130903103, \"jadx.gui.app\", \"attr\", \"size\", \"\");\n\t\tre.setNamedValues(\n\t\t\t\tLists.list(\n\t\t\t\t\t\tnew RawNamedValue(0x01000000, new RawValue(16, 131072)),\n\t\t\t\t\t\tnew RawNamedValue(0x01040000, new RawValue(16, 1))));\n\t\tresStorage.add(re);\n\n\t\tValuesParser vp = new ValuesParser(null, resStorage.getResourcesNames());\n\t\tResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);\n\t\tList<ResContainer> files = resXmlGen.makeResourcesXml(args);\n\n\t\tassertThat(files).hasSize(1);\n\t\tassertThat(files.get(0).getName()).isEqualTo(\"res/values/attrs.xml\");\n\t\tString input = files.get(0).getText().toString();\n\t\tassertThat(input).isEqualTo(\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\\n\"\n\t\t\t\t+ \"<resources>\\n\"\n\t\t\t\t+ \"    <attr name=\\\"size\\\">\\n\"\n\t\t\t\t+ \"        <flag name=\\\"android:string.cancel\\\" value=\\\"1\\\" />\\n\"\n\t\t\t\t+ \"    </attr>\\n\"\n\t\t\t\t+ \"</resources>\");\n\t}\n\n\t@Test\n\tvoid testAttrMin() {\n\t\tResourceStorage resStorage = new ResourceStorage(security);\n\t\tResourceEntry re = new ResourceEntry(2130903103, \"jadx.gui.app\", \"attr\", \"size\", \"\");\n\t\tre.setNamedValues(\n\t\t\t\tLists.list(new RawNamedValue(16777216, new RawValue(16, 4)), new RawNamedValue(16777217, new RawValue(16, 1))));\n\t\tresStorage.add(re);\n\n\t\tValuesParser vp = new ValuesParser(null, resStorage.getResourcesNames());\n\t\tResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);\n\t\tList<ResContainer> files = resXmlGen.makeResourcesXml(args);\n\n\t\tassertThat(files).hasSize(1);\n\t\tassertThat(files.get(0).getName()).isEqualTo(\"res/values/attrs.xml\");\n\t\tString input = files.get(0).getText().toString();\n\t\tassertThat(input).isEqualTo(\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\\n\"\n\t\t\t\t+ \"<resources>\\n\"\n\t\t\t\t+ \"    <attr name=\\\"size\\\" format=\\\"integer\\\" min=\\\"1\\\">\\n\"\n\t\t\t\t+ \"    </attr>\\n\"\n\t\t\t\t+ \"</resources>\");\n\t}\n\n\t@Test\n\tvoid testStyle() {\n\t\tResourceStorage resStorage = new ResourceStorage(security);\n\t\tResourceEntry re = new ResourceEntry(2130903103, \"jadx.gui.app\", \"style\", \"JadxGui\", \"\");\n\t\tre.setNamedValues(Lists.list(new RawNamedValue(16842836, new RawValue(1, 17170445))));\n\t\tresStorage.add(re);\n\n\t\tre = new ResourceEntry(2130903104, \"jadx.gui.app\", \"style\", \"JadxGui.Dialog\", \"\");\n\t\tre.setParentRef(2130903103);\n\t\tre.setNamedValues(new ArrayList<>());\n\t\tresStorage.add(re);\n\t\tValuesParser vp = new ValuesParser(null, resStorage.getResourcesNames());\n\t\tResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);\n\t\tList<ResContainer> files = resXmlGen.makeResourcesXml(args);\n\n\t\tassertThat(files).hasSize(1);\n\t\tassertThat(files.get(0).getName()).isEqualTo(\"res/values/styles.xml\");\n\t\tString input = files.get(0).getText().toString();\n\t\tassertThat(input).isEqualTo(\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\\n\"\n\t\t\t\t+ \"<resources>\\n\"\n\t\t\t\t+ \"    <style name=\\\"JadxGui\\\" parent=\\\"\\\">\\n\"\n\t\t\t\t+ \"        <item name=\\\"android:windowBackground\\\">@android:color/transparent</item>\\n\"\n\t\t\t\t+ \"    </style>\\n\"\n\t\t\t\t+ \"    <style name=\\\"JadxGui.Dialog\\\" parent=\\\"@style/JadxGui\\\">\\n\"\n\t\t\t\t+ \"    </style>\\n\"\n\t\t\t\t+ \"</resources>\");\n\t}\n\n\t@Test\n\tvoid testString() {\n\t\tResourceStorage resStorage = new ResourceStorage(security);\n\t\tResourceEntry re = new ResourceEntry(2130903103, \"jadx.gui.app\", \"string\", \"app_name\", \"\");\n\t\tre.setSimpleValue(new RawValue(3, 0));\n\t\tre.setNamedValues(Lists.list());\n\t\tresStorage.add(re);\n\n\t\tBinaryXMLStrings strings = new BinaryXMLStrings();\n\t\tstrings.put(0, \"Jadx Decompiler App\");\n\t\tValuesParser vp = new ValuesParser(strings, resStorage.getResourcesNames());\n\t\tResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);\n\t\tList<ResContainer> files = resXmlGen.makeResourcesXml(args);\n\n\t\tassertThat(files).hasSize(1);\n\t\tassertThat(files.get(0).getName()).isEqualTo(\"res/values/strings.xml\");\n\t\tString input = files.get(0).getText().toString();\n\t\tassertThat(input).isEqualTo(\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\\n\"\n\t\t\t\t+ \"<resources>\\n\"\n\t\t\t\t+ \"    <string name=\\\"app_name\\\">Jadx Decompiler App</string>\\n\"\n\t\t\t\t+ \"</resources>\");\n\t}\n\n\t@Test\n\tvoid testStringFormattedFalse() {\n\t\tResourceStorage resStorage = new ResourceStorage(security);\n\t\tResourceEntry re = new ResourceEntry(2130903103, \"jadx.gui.app\", \"string\", \"app_name\", \"\");\n\t\tre.setSimpleValue(new RawValue(3, 0));\n\t\tre.setNamedValues(Lists.list());\n\t\tresStorage.add(re);\n\n\t\tBinaryXMLStrings strings = new BinaryXMLStrings();\n\t\tstrings.put(0, \"%s at %s\");\n\t\tValuesParser vp = new ValuesParser(strings, resStorage.getResourcesNames());\n\t\tResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);\n\t\tList<ResContainer> files = resXmlGen.makeResourcesXml(args);\n\n\t\tassertThat(files).hasSize(1);\n\t\tassertThat(files.get(0).getName()).isEqualTo(\"res/values/strings.xml\");\n\t\tString input = files.get(0).getText().toString();\n\t\tassertThat(input).isEqualTo(\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\\n\"\n\t\t\t\t+ \"<resources>\\n\"\n\t\t\t\t+ \"    <string name=\\\"app_name\\\" formatted=\\\"false\\\">%s at %s</string>\\n\"\n\t\t\t\t+ \"</resources>\");\n\t}\n\n\t@Test\n\tvoid testArrayEscape() {\n\t\tResourceStorage resStorage = new ResourceStorage(security);\n\t\tResourceEntry re = new ResourceEntry(2130903103, \"jadx.gui.app\", \"array\", \"single_quote_escape_sample\", \"\");\n\t\tre.setNamedValues(\n\t\t\t\tLists.list(new RawNamedValue(16777216, new RawValue(3, 0))));\n\t\tresStorage.add(re);\n\n\t\tBinaryXMLStrings strings = new BinaryXMLStrings();\n\t\tstrings.put(0, \"Let's go\");\n\t\tValuesParser vp = new ValuesParser(strings, resStorage.getResourcesNames());\n\t\tResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);\n\t\tList<ResContainer> files = resXmlGen.makeResourcesXml(args);\n\n\t\tassertThat(files).hasSize(1);\n\t\tassertThat(files.get(0).getName()).isEqualTo(\"res/values/arrays.xml\");\n\t\tString input = files.get(0).getText().toString();\n\t\tassertThat(input).isEqualTo(\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\\n\"\n\t\t\t\t+ \"<resources>\\n\"\n\t\t\t\t+ \"    <array name=\\\"single_quote_escape_sample\\\">\\n\"\n\t\t\t\t+ \"        <item>Let\\\\'s go</item>\\n\"\n\t\t\t\t+ \"    </array>\\n\"\n\t\t\t\t+ \"</resources>\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/core/xmlgen/entry/ValuesParserTest.java",
    "content": "package jadx.core.xmlgen.entry;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.utils.android.AndroidResourcesMap;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\nclass ValuesParserTest {\n\n\t@Test\n\tvoid testResMapLoad() {\n\t\tMap<Integer, String> androidResMap = AndroidResourcesMap.getMap();\n\t\tassertThat(androidResMap).isNotNull().isNotEmpty();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/ExportGradleTest.java",
    "content": "package jadx.tests.api;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.util.List;\n\nimport org.junit.jupiter.api.io.TempDir;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.JadxArgs;\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourceFileContainer;\nimport jadx.api.ResourceFileContent;\nimport jadx.api.ResourceType;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.export.ExportGradle;\nimport jadx.core.export.ExportGradleType;\nimport jadx.core.export.OutDirs;\nimport jadx.core.xmlgen.ResContainer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\npublic abstract class ExportGradleTest {\n\tprivate static final String MANIFEST_TESTS_DIR = \"manifest\";\n\n\tprivate final RootNode root = new RootNode(new JadxArgs());\n\n\t@TempDir\n\tprivate File exportDir;\n\n\tprotected ICodeInfo loadResource(String filename) {\n\t\treturn new SimpleCodeInfo(loadResourceContent(MANIFEST_TESTS_DIR, filename));\n\t}\n\n\tprivate static String loadFileContent(File filePath) {\n\t\ttry {\n\t\t\treturn Files.readString(filePath.toPath());\n\t\t} catch (IOException e) {\n\t\t\tfail(\"Loading file failed\", e);\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tprivate String loadResourceContent(String dir, String filename) {\n\t\tString resPath = dir + '/' + filename;\n\t\ttry (InputStream in = getClass().getClassLoader().getResourceAsStream(resPath)) {\n\t\t\tif (in == null) {\n\t\t\t\tfail(\"Resource not found: \" + resPath);\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t\treturn new String(in.readAllBytes(), StandardCharsets.UTF_8);\n\t\t} catch (Exception e) {\n\t\t\tfail(\"Loading file failed: \" + resPath, e);\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tprotected RootNode getRootNode() {\n\t\treturn root;\n\t}\n\n\tprotected void exportGradle(String manifestFilename, String stringsFileName) {\n\t\tResourceFile androidManifest =\n\t\t\t\tnew ResourceFileContent(\"AndroidManifest.xml\", ResourceType.MANIFEST, loadResource(manifestFilename));\n\t\tResContainer strings = ResContainer.textResource(stringsFileName, loadResource(stringsFileName));\n\t\tResContainer arsc = ResContainer.resourceTable(\"resources.arsc\", List.of(strings), new SimpleCodeInfo(\"empty\"));\n\t\tResourceFile arscFile = new ResourceFileContainer(\"resources.arsc\", ResourceType.ARSC, arsc);\n\t\tList<ResourceFile> resources = List.of(androidManifest, arscFile);\n\n\t\troot.getArgs().setExportGradleType(ExportGradleType.ANDROID_APP);\n\t\tExportGradle export = new ExportGradle(root, exportDir, resources);\n\t\tOutDirs outDirs = export.init();\n\t\tassertThat(outDirs.getSrcOutDir()).exists();\n\t\tassertThat(outDirs.getResOutDir()).exists();\n\t\texport.generateGradleFiles();\n\t}\n\n\tprotected String getAppGradleBuild() {\n\t\treturn loadFileContent(new File(exportDir, \"app/build.gradle\"));\n\t}\n\n\tprotected String getSettingsGradle() {\n\t\treturn loadFileContent(new File(exportDir, \"settings.gradle\"));\n\t}\n\n\tprotected File getGradleProperiesFile() {\n\t\treturn new File(exportDir, \"gradle.properties\");\n\t}\n\n\tprotected String getGradleProperties() {\n\t\treturn loadFileContent(getGradleProperiesFile());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java",
    "content": "package jadx.tests.api;\n\nimport java.io.Closeable;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.net.URL;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarOutputStream;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Assumptions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.CommentsLevel;\nimport jadx.api.DecompilationMode;\nimport jadx.api.ICodeInfo;\nimport jadx.api.JadxArgs;\nimport jadx.api.JadxDecompiler;\nimport jadx.api.JadxInternalAccess;\nimport jadx.api.JavaClass;\nimport jadx.api.JavaMethod;\nimport jadx.api.JavaVariable;\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourceType;\nimport jadx.api.ResourcesLoader;\nimport jadx.api.args.GeneratedRenamesMappingFileMode;\nimport jadx.api.data.IJavaNodeRef;\nimport jadx.api.data.impl.JadxCodeData;\nimport jadx.api.data.impl.JadxCodeRename;\nimport jadx.api.data.impl.JadxNodeRef;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.api.metadata.ICodeMetadata;\nimport jadx.api.metadata.annotations.InsnCodeOffset;\nimport jadx.api.metadata.annotations.VarNode;\nimport jadx.api.plugins.CustomResourcesLoader;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.core.xmlgen.BinaryXMLStrings;\nimport jadx.core.xmlgen.IResTableParser;\nimport jadx.core.xmlgen.ResContainer;\nimport jadx.core.xmlgen.ResourceStorage;\nimport jadx.core.xmlgen.entry.ResourceEntry;\nimport jadx.tests.api.compiler.CompilerOptions;\nimport jadx.tests.api.compiler.JavaUtils;\nimport jadx.tests.api.compiler.TestCompiler;\nimport jadx.tests.api.utils.TestFilesGetter;\nimport jadx.tests.api.utils.TestUtils;\n\nimport static org.apache.commons.lang3.StringUtils.leftPad;\nimport static org.apache.commons.lang3.StringUtils.rightPad;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\npublic abstract class IntegrationTest extends TestUtils {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(IntegrationTest.class);\n\n\tprivate static final String TEST_DIRECTORY = \"src/test/java\";\n\tprivate static final String TEST_DIRECTORY2 = \"jadx-core/\" + TEST_DIRECTORY;\n\n\tprivate static final String DEFAULT_INPUT_PLUGIN = \"dx\";\n\t/**\n\t * Set 'TEST_INPUT_PLUGIN' env variable to use 'java' or 'dx' input in tests\n\t */\n\tstatic final boolean USE_JAVA_INPUT = Utils.getOrElse(System.getenv(\"TEST_INPUT_PLUGIN\"), DEFAULT_INPUT_PLUGIN).equals(\"java\");\n\n\t/**\n\t * Run auto check method if defined:\n\t *\n\t * <pre>\n\t * public void check() {\n\t * }\n\t * </pre>\n\t */\n\tprivate static final String CHECK_METHOD_NAME = \"check\";\n\n\tprotected JadxArgs args;\n\n\tprotected boolean compile;\n\tprivate CompilerOptions compilerOptions;\n\n\tprivate boolean saveTestJar = false;\n\n\tprotected Map<Integer, String> resMap = Collections.emptyMap();\n\n\tprivate boolean allowWarnInCode;\n\tprivate boolean printLineNumbers;\n\tprivate boolean printOffsets;\n\tprivate boolean printDisassemble;\n\tprivate @Nullable Boolean useJavaInput;\n\tprivate boolean removeParentClassOnInput;\n\n\tprivate @Nullable TestCompiler sourceCompiler;\n\tprivate @Nullable TestCompiler decompiledCompiler;\n\n\t/**\n\t * Run check method on decompiled code even if source check method not found.\n\t * Useful for smali test if check method added to smali code\n\t */\n\tprivate boolean forceDecompiledCheck = false;\n\n\tprotected JadxDecompiler jadxDecompiler;\n\n\t@TempDir\n\tPath testDir;\n\n\t@BeforeEach\n\tpublic void init() {\n\t\tthis.compile = true;\n\t\tthis.compilerOptions = new CompilerOptions();\n\t\tthis.resMap = Collections.emptyMap();\n\t\tthis.removeParentClassOnInput = true;\n\t\tthis.useJavaInput = null;\n\n\t\targs = new JadxArgs();\n\t\targs.setOutDir(testDir.toFile());\n\t\targs.setShowInconsistentCode(true);\n\t\targs.setThreadsCount(1);\n\t\targs.setSkipResources(true);\n\t\targs.setCommentsLevel(CommentsLevel.DEBUG);\n\t\targs.setDeobfuscationOn(false);\n\t\targs.setGeneratedRenamesMappingFileMode(GeneratedRenamesMappingFileMode.IGNORE);\n\t\targs.setRunDebugChecks(true);\n\t\targs.setFilesGetter(new TestFilesGetter(testDir));\n\n\t\t// use the same values on all systems\n\t\targs.setFsCaseSensitive(false);\n\t\targs.setCodeNewLineStr(\"\\n\");\n\t\targs.setCodeIndentStr(JadxArgs.DEFAULT_INDENT_STR);\n\t}\n\n\t@AfterEach\n\tpublic void after() throws IOException {\n\t\tclose(jadxDecompiler);\n\t\tclose(sourceCompiler);\n\t\tclose(decompiledCompiler);\n\t}\n\n\tprivate void close(Closeable closeable) throws IOException {\n\t\tif (closeable != null) {\n\t\t\tcloseable.close();\n\t\t}\n\t}\n\n\tpublic void setOutDirSuffix(String suffix) {\n\t\targs.setOutDir(new File(testDir.toFile(), suffix));\n\t}\n\n\tpublic String getTestName() {\n\t\treturn this.getClass().getSimpleName();\n\t}\n\n\tpublic String getTestPkg() {\n\t\treturn this.getClass().getPackage().getName().replace(\"jadx.tests.integration.\", \"\");\n\t}\n\n\tpublic ClassNode getClassNode(Class<?> clazz) {\n\t\ttry {\n\t\t\tList<File> files = compileClass(clazz);\n\t\t\tassertThat(files).as(\"File list is empty\").isNotEmpty();\n\t\t\treturn getClassNodeFromFiles(files, clazz.getName());\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to get class node\", e);\n\t\t\tfail(e.getMessage());\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic List<ClassNode> getClassNodes(Class<?>... classes) {\n\t\ttry {\n\t\t\tassertThat(classes).as(\"Class list is empty\").isNotEmpty();\n\t\t\tList<File> srcFiles = Stream.of(classes).map(this::getSourceFileForClass).collect(Collectors.toList());\n\t\t\tList<File> clsFiles = compileSourceFiles(srcFiles);\n\t\t\tassertThat(clsFiles).as(\"Class files list is empty\").isNotEmpty();\n\t\t\treturn decompileFiles(clsFiles);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to get class node\", e);\n\t\t\tfail(e.getMessage());\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic ClassNode getClassNodeFromFiles(List<File> files, String clsName) {\n\t\tjadxDecompiler = loadFiles(files);\n\t\tRootNode root = JadxInternalAccess.getRoot(jadxDecompiler);\n\n\t\tClassNode cls = root.resolveClass(clsName);\n\t\tassertThat(cls).as(\"Class not found: \" + clsName).isNotNull();\n\t\tif (removeParentClassOnInput) {\n\t\t\tassertThat(clsName).isEqualTo(cls.getClassInfo().getFullName());\n\t\t} else {\n\t\t\tLOG.info(\"Convert back to top level: {}\", cls);\n\t\t\tcls.getTopParentClass().decompile(); // keep correct process order\n\t\t\tcls.notInner();\n\t\t}\n\t\tdecompileAndCheck(cls);\n\t\treturn cls;\n\t}\n\n\tpublic List<ClassNode> decompileFiles(List<File> files) {\n\t\tjadxDecompiler = loadFiles(files);\n\t\tList<ClassNode> sortedClsNodes = jadxDecompiler.getDecompileScheduler()\n\t\t\t\t.buildBatches(jadxDecompiler.getClasses())\n\t\t\t\t.stream()\n\t\t\t\t.flatMap(Collection::stream)\n\t\t\t\t.map(JavaClass::getClassNode)\n\t\t\t\t.collect(Collectors.toList());\n\t\tdecompileAndCheck(sortedClsNodes);\n\t\treturn sortedClsNodes;\n\t}\n\n\t@NotNull\n\tpublic ClassNode searchTestCls(List<ClassNode> list, String shortClsName) {\n\t\treturn searchCls(list, getTestPkg() + '.' + shortClsName);\n\t}\n\n\t@NotNull\n\tpublic ClassNode searchCls(List<ClassNode> list, String clsName) {\n\t\tfor (ClassNode cls : list) {\n\t\t\tif (cls.getClassInfo().getFullName().equals(clsName)) {\n\t\t\t\treturn cls;\n\t\t\t}\n\t\t}\n\t\tfor (ClassNode cls : list) {\n\t\t\tif (cls.getClassInfo().getShortName().equals(clsName)) {\n\t\t\t\treturn cls;\n\t\t\t}\n\t\t}\n\t\tfail(\"Class not found by name \" + clsName + \" in list: \" + list);\n\t\treturn null;\n\t}\n\n\tprotected JadxDecompiler loadFiles(List<File> inputFiles) {\n\t\targs.setInputFiles(inputFiles);\n\t\tboolean useDx = !isJavaInput();\n\t\tLOG.info(useDx ? \"Using dex input\" : \"Using java input\");\n\t\targs.setUseDxInput(useDx);\n\n\t\tJadxDecompiler d = new JadxDecompiler(args);\n\t\ttry {\n\t\t\tinsertResources(d);\n\t\t\td.load();\n\t\t\treturn d;\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Load failed\", e);\n\t\t\td.close();\n\t\t\tfail(e.getMessage());\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected void decompileAndCheck(ClassNode cls) {\n\t\tdecompileAndCheck(Collections.singletonList(cls));\n\t}\n\n\tprotected void decompileAndCheck(List<ClassNode> clsList) {\n\t\tclsList.forEach(cls -> cls.add(AFlag.DONT_UNLOAD_CLASS)); // keep error and warning attributes\n\t\tclsList.forEach(ClassNode::decompile);\n\n\t\tfor (ClassNode cls : clsList) {\n\t\t\tSystem.out.println(\"-----------------------------------------------------------\");\n\t\t\tICodeInfo code = cls.getCode();\n\t\t\tif (printLineNumbers) {\n\t\t\t\tprintCodeWithLineNumbers(code);\n\t\t\t} else if (printOffsets) {\n\t\t\t\tprintCodeWithOffsets(code);\n\t\t\t} else {\n\t\t\t\tSystem.out.println(code);\n\t\t\t}\n\t\t}\n\t\tSystem.out.println(\"-----------------------------------------------------------\");\n\t\tif (printDisassemble) {\n\t\t\tclsList.forEach(this::printDisasm);\n\t\t}\n\t\trunChecks(clsList);\n\t}\n\n\tpublic void runChecks(ClassNode cls) {\n\t\trunChecks(Collections.singletonList(cls));\n\t}\n\n\tprotected void runChecks(List<ClassNode> clsList) {\n\t\tclsList.forEach(cls -> checkCode(cls, allowWarnInCode));\n\t\tcompileClassNode(clsList);\n\t\tclsList.forEach(this::runAutoCheck);\n\t}\n\n\tprivate void printDisasm(ClassNode cls) {\n\t\tSystem.out.println(\"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\");\n\t\tSystem.out.println(cls.getDisassembledCode());\n\t\tSystem.out.println(\"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\");\n\t}\n\n\tprivate void printCodeWithLineNumbers(ICodeInfo code) {\n\t\tString codeStr = code.getCodeStr();\n\t\tMap<Integer, Integer> lineMapping = code.getCodeMetadata().getLineMapping();\n\t\tString[] lines = codeStr.split(\"\\\\R\");\n\t\tfor (int i = 0; i < lines.length; i++) {\n\t\t\tString line = lines[i];\n\t\t\tint curLine = i + 1;\n\t\t\tString lineNumStr = \"/* \" + leftPad(String.valueOf(curLine), 3) + \" */\";\n\t\t\tInteger sourceLine = lineMapping.get(curLine);\n\t\t\tif (sourceLine != null) {\n\t\t\t\tlineNumStr += \" /* \" + sourceLine + \" */\";\n\t\t\t}\n\t\t\tSystem.out.println(rightPad(lineNumStr, 20) + line);\n\t\t}\n\t}\n\n\tprivate void printCodeWithOffsets(ICodeInfo code) {\n\t\tString codeStr = code.getCodeStr();\n\t\tICodeMetadata metadata = code.getCodeMetadata();\n\t\tint lineStartPos = 0;\n\t\tString newLineStr = args.getCodeNewLineStr();\n\t\tint newLineLen = newLineStr.length();\n\t\tfor (String line : codeStr.split(newLineStr)) {\n\t\t\tObject ann = metadata.getAt(lineStartPos);\n\t\t\tString offsetStr = \"\";\n\t\t\tif (ann instanceof InsnCodeOffset) {\n\t\t\t\tint offset = ((InsnCodeOffset) ann).getOffset();\n\t\t\t\toffsetStr = \"/* \" + leftPad(String.valueOf(offset), 5) + \" */\";\n\t\t\t}\n\t\t\tSystem.out.println(rightPad(offsetStr, 12) + line);\n\t\t\tlineStartPos += line.length() + newLineLen;\n\t\t}\n\t}\n\n\t/**\n\t * Insert mock resource table data ('.arsc' file)\n\t */\n\tprivate void insertResources(JadxDecompiler decompiler) throws IOException {\n\t\tif (resMap.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tString resTableName = \"test-res-table\";\n\t\tPath resTablePath = testDir.resolve(resTableName);\n\t\tFile resTableFile = resTablePath.toFile().getAbsoluteFile();\n\t\tFileUtils.makeDirsForFile(resTableFile);\n\t\tFiles.writeString(resTablePath, resTableName);\n\t\tJadxArgs jadxArgs = decompiler.getArgs();\n\t\tjadxArgs.getInputFiles().add(resTableFile);\n\n\t\t// load mock file as 'arsc'\n\t\tdecompiler.addCustomResourcesLoader(new CustomResourcesLoader() {\n\t\t\t@Override\n\t\t\tpublic boolean load(ResourcesLoader loader, List<ResourceFile> list, File file) {\n\t\t\t\tif (file == resTableFile) {\n\t\t\t\t\tlist.add(ResourceFile.createResourceFile(decompiler, resTableFile, ResourceType.ARSC));\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void close() {\n\t\t\t}\n\t\t});\n\n\t\t// convert resources map to resource storage object\n\t\tResourceStorage resStorage = new ResourceStorage(jadxArgs.getSecurity());\n\t\tfor (Map.Entry<Integer, String> entry : resMap.entrySet()) {\n\t\t\tInteger id = entry.getKey();\n\t\t\tString name = entry.getValue();\n\t\t\tString[] parts = name.split(\"\\\\.\");\n\t\t\tresStorage.add(new ResourceEntry(id, \"\", parts[0], parts[1], \"\"));\n\t\t}\n\n\t\t// mock res table parser to just return resource storage\n\t\tIResTableParser resTableParser = new IResTableParser() {\n\t\t\t@Override\n\t\t\tpublic void decode(InputStream inputStream) {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic ResourceStorage getResStorage() {\n\t\t\t\treturn resStorage;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic ResContainer decodeFiles() {\n\t\t\t\treturn ResContainer.textResource(resTableName, new SimpleCodeInfo(resTableName));\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic BinaryXMLStrings getStrings() {\n\t\t\t\treturn new BinaryXMLStrings();\n\t\t\t}\n\t\t};\n\n\t\t// directly return generated resource storage instead parsing for mock res file\n\t\tdecompiler.getResourcesLoader().addResTableParserProvider(resFile -> {\n\t\t\tif (resFile.getOriginalName().equals(resTableFile.getAbsolutePath())) {\n\t\t\t\treturn resTableParser;\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t}\n\n\tprivate void runAutoCheck(ClassNode cls) {\n\t\tString clsName = cls.getClassInfo().getRawName().replace('/', '.');\n\t\ttry {\n\t\t\t// run 'check' method from original class\n\t\t\tboolean sourceCheckFound = runSourceAutoCheck(clsName);\n\n\t\t\t// run 'check' method from decompiled class\n\t\t\tif (compile && (sourceCheckFound || forceDecompiledCheck)) {\n\t\t\t\trunDecompiledAutoCheck(cls);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Auto check failed\", e);\n\t\t\tfail(\"Auto check exception: \" + e.getMessage());\n\t\t}\n\t}\n\n\tprivate boolean runSourceAutoCheck(String clsName) {\n\t\tif (sourceCompiler == null) {\n\t\t\tSystem.out.println(\"Source check: no code\");\n\t\t\treturn false;\n\t\t}\n\t\tClass<?> origCls;\n\t\ttry {\n\t\t\torigCls = sourceCompiler.getClass(clsName);\n\t\t} catch (ClassNotFoundException e) {\n\t\t\trethrow(\"Missing class: \" + clsName, e);\n\t\t\treturn false;\n\t\t}\n\t\tMethod checkMth;\n\t\ttry {\n\t\t\tcheckMth = sourceCompiler.getMethod(origCls, CHECK_METHOD_NAME, new Class[] {});\n\t\t} catch (NoSuchMethodException e) {\n\t\t\t// ignore\n\t\t\treturn false;\n\t\t}\n\t\tif (!checkMth.getReturnType().equals(void.class)\n\t\t\t\t|| !Modifier.isPublic(checkMth.getModifiers())\n\t\t\t\t|| Modifier.isStatic(checkMth.getModifiers())) {\n\t\t\tfail(\"Wrong 'check' method\");\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tlimitExecTime(() -> checkMth.invoke(origCls.getConstructor().newInstance()));\n\t\t\tSystem.out.println(\"Source check: PASSED\");\n\t\t\treturn true;\n\t\t} catch (Throwable e) {\n\t\t\tthrow new JadxRuntimeException(\"Source check failed\", e);\n\t\t}\n\t}\n\n\tpublic void runDecompiledAutoCheck(ClassNode cls) {\n\t\ttry {\n\t\t\tlimitExecTime(() -> invoke(decompiledCompiler, cls.getFullName(), CHECK_METHOD_NAME));\n\t\t\tSystem.out.println(\"Decompiled check: PASSED\");\n\t\t} catch (Throwable e) {\n\t\t\trethrow(\"Decompiled check failed\", e);\n\t\t}\n\t}\n\n\tprivate <T> T limitExecTime(Callable<T> call) {\n\t\tExecutorService executor = Executors.newSingleThreadExecutor();\n\t\tFuture<T> future = executor.submit(call);\n\t\ttry {\n\t\t\treturn future.get(5, TimeUnit.SECONDS);\n\t\t} catch (TimeoutException ex) {\n\t\t\tfuture.cancel(true);\n\t\t\trethrow(\"Execution timeout\", ex);\n\t\t} catch (Throwable ex) {\n\t\t\trethrow(ex.getMessage(), ex);\n\t\t} finally {\n\t\t\texecutor.shutdownNow();\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static void rethrow(String msg, Throwable e) {\n\t\tif (e instanceof InvocationTargetException) {\n\t\t\trethrow(msg, e.getCause());\n\t\t} else if (e instanceof ExecutionException) {\n\t\t\trethrow(msg, e.getCause());\n\t\t} else if (e instanceof AssertionError) {\n\t\t\tSystem.err.println(msg);\n\t\t\tthrow (AssertionError) e;\n\t\t} else {\n\t\t\tthrow new RuntimeException(msg, e);\n\t\t}\n\t}\n\n\tprotected MethodNode getMethod(ClassNode cls, String methodName) {\n\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\tif (mth.getName().equals(methodName)) {\n\t\t\t\treturn mth;\n\t\t\t}\n\t\t}\n\t\tfail(\"Method not found \" + methodName + \" in class \" + cls);\n\t\treturn null;\n\t}\n\n\tprotected FieldNode getField(ClassNode cls, String fieldName) {\n\t\tfor (FieldNode fld : cls.getFields()) {\n\t\t\tif (fld.getName().equals(fieldName)) {\n\t\t\t\treturn fld;\n\t\t\t}\n\t\t}\n\t\tfail(\"Field not found \" + fieldName + \" in class \" + cls);\n\t\treturn null;\n\t}\n\n\tvoid compileClassNode(List<ClassNode> clsList) {\n\t\tif (!compile) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\t// TODO: eclipse uses files or compilation units providers added in Java 9\n\t\t\tcompilerOptions.setUseEclipseCompiler(false);\n\t\t\tdecompiledCompiler = new TestCompiler(compilerOptions);\n\t\t\tdecompiledCompiler.compileNodes(clsList);\n\t\t\tSystem.out.println(\"Compilation: PASSED\");\n\t\t} catch (Exception e) {\n\t\t\tfail(e);\n\t\t}\n\t}\n\n\tpublic Object invoke(TestCompiler compiler, String clsFullName, String method) throws Exception {\n\t\tassertThat(compiler).as(\"compiler not ready\").isNotNull();\n\t\treturn compiler.invoke(clsFullName, method, new Class<?>[] {}, new Object[] {});\n\t}\n\n\tprivate List<File> compileClass(Class<?> cls) throws IOException {\n\t\tFile sourceFile = getSourceFileForClass(cls);\n\t\tList<File> clsFiles = compileSourceFiles(Collections.singletonList(sourceFile));\n\t\tif (removeParentClassOnInput) {\n\t\t\t// remove classes which are parents for test class\n\t\t\tString clsFullName = cls.getName();\n\t\t\tString clsName = clsFullName.substring(clsFullName.lastIndexOf('.') + 1);\n\t\t\tclsFiles.removeIf(next -> !next.getName().contains(clsName));\n\t\t}\n\t\treturn clsFiles;\n\t}\n\n\tprivate File getSourceFileForClass(Class<?> cls) {\n\t\tString clsFullName = cls.getName();\n\t\tint innerEnd = clsFullName.indexOf('$');\n\t\tString rootClsName = innerEnd == -1 ? clsFullName : clsFullName.substring(0, innerEnd);\n\t\tString javaFileName = rootClsName.replace('.', '/') + \".java\";\n\t\tFile file = new File(TEST_DIRECTORY, javaFileName);\n\t\tif (file.exists()) {\n\t\t\treturn file;\n\t\t}\n\t\tFile file2 = new File(TEST_DIRECTORY2, javaFileName);\n\t\tif (file2.exists()) {\n\t\t\treturn file2;\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Test source not found for class: \" + clsFullName);\n\t}\n\n\tprivate List<File> compileSourceFiles(List<File> compileFileList) throws IOException {\n\t\tPath outTmp = Files.createTempDirectory(testDir, \"jadx-tmp-classes\");\n\t\tsourceCompiler = new TestCompiler(compilerOptions);\n\t\tList<File> files = sourceCompiler.compileFiles(compileFileList, outTmp);\n\t\tif (saveTestJar) {\n\t\t\tsaveToJar(files, outTmp);\n\t\t}\n\t\treturn files;\n\t}\n\n\tprivate void saveToJar(List<File> files, Path baseDir) throws IOException {\n\t\tPath jarFile = Files.createTempFile(\"tests-\" + getTestName() + '-', \".jar\");\n\t\ttry (JarOutputStream jar = new JarOutputStream(Files.newOutputStream(jarFile))) {\n\t\t\tfor (File file : files) {\n\t\t\t\tPath fullPath = file.toPath();\n\t\t\t\tPath relativePath = baseDir.relativize(fullPath);\n\t\t\t\tJarEntry entry = new JarEntry(relativePath.toString());\n\t\t\t\tjar.putNextEntry(entry);\n\t\t\t\tjar.write(Files.readAllBytes(fullPath));\n\t\t\t\tjar.closeEntry();\n\t\t\t}\n\t\t}\n\t\tLOG.info(\"Test jar saved to: {}\", jarFile.toAbsolutePath());\n\t}\n\n\tpublic JadxArgs getArgs() {\n\t\treturn args;\n\t}\n\n\tpublic CompilerOptions getCompilerOptions() {\n\t\treturn compilerOptions;\n\t}\n\n\tpublic void setArgs(JadxArgs args) {\n\t\tthis.args = args;\n\t}\n\n\tpublic void setResMap(Map<Integer, String> resMap) {\n\t\tthis.resMap = resMap;\n\t}\n\n\tprotected void noDebugInfo() {\n\t\tthis.compilerOptions.setIncludeDebugInfo(false);\n\t}\n\n\tpublic void useEclipseCompiler() {\n\t\tAssumptions.assumeTrue(JavaUtils.checkJavaVersion(11), \"eclipse compiler library using Java 11\");\n\t\tthis.compilerOptions.setUseEclipseCompiler(true);\n\t}\n\n\tpublic void useTargetJavaVersion(int version) {\n\t\tAssumptions.assumeTrue(JavaUtils.checkJavaVersion(version), \"skip test for higher java version\");\n\t\tthis.compilerOptions.setJavaVersion(version);\n\t}\n\n\tprotected void setFallback() {\n\t\tdisableCompilation();\n\t\tthis.args.setDecompilationMode(DecompilationMode.FALLBACK);\n\t}\n\n\tprotected void disableCompilation() {\n\t\tthis.compile = false;\n\t}\n\n\tprotected void forceDecompiledCheck() {\n\t\tthis.forceDecompiledCheck = true;\n\t}\n\n\tprotected void enableDeobfuscation() {\n\t\targs.setDeobfuscationOn(true);\n\t\targs.setGeneratedRenamesMappingFileMode(GeneratedRenamesMappingFileMode.IGNORE);\n\t\targs.setDeobfuscationMinLength(2);\n\t\targs.setDeobfuscationMaxLength(64);\n\t}\n\n\tprotected void allowWarnInCode() {\n\t\tallowWarnInCode = true;\n\t}\n\n\tprotected void printLineNumbers() {\n\t\tprintLineNumbers = true;\n\t}\n\n\tprotected void printOffsets() {\n\t\tprintOffsets = true;\n\t}\n\n\tpublic void useJavaInput() {\n\t\tthis.useJavaInput = true;\n\t}\n\n\tpublic void useDexInput() {\n\t\tAssumptions.assumeFalse(USE_JAVA_INPUT, \"skip dex input tests\");\n\t\tthis.useJavaInput = false;\n\t}\n\n\tpublic void useDexInput(String mode) {\n\t\tuseDexInput();\n\t\tthis.getArgs().getPluginOptions().put(\"java-convert.mode\", mode);\n\t}\n\n\tprotected boolean isJavaInput() {\n\t\treturn Utils.getOrElse(useJavaInput, USE_JAVA_INPUT);\n\t}\n\n\tpublic void keepParentClassOnInput() {\n\t\tthis.removeParentClassOnInput = false;\n\t}\n\n\t// Use only for debug purpose\n\tprotected void printDisassemble() {\n\t\tthis.printDisassemble = true;\n\t}\n\n\t// Use only for debug purpose\n\tprotected void saveTestJar() {\n\t\tthis.saveTestJar = true;\n\t}\n\n\tprotected void addClsRename(String fullClsName, String newName) {\n\t\tJadxNodeRef clsRef = JadxNodeRef.forCls(fullClsName);\n\t\tgetCodeData().getRenames().add(new JadxCodeRename(clsRef, newName));\n\t}\n\n\tprotected void addMthRename(String fullClsName, String mthSignature, String newName) {\n\t\tJadxNodeRef mthRef = new JadxNodeRef(IJavaNodeRef.RefType.METHOD, fullClsName, mthSignature);\n\t\tgetCodeData().getRenames().add(new JadxCodeRename(mthRef, newName));\n\t}\n\n\tprotected void addFldRename(String fullClsName, String fldSignature, String newName) {\n\t\tJadxNodeRef fldRef = new JadxNodeRef(IJavaNodeRef.RefType.FIELD, fullClsName, fldSignature);\n\t\tgetCodeData().getRenames().add(new JadxCodeRename(fldRef, newName));\n\t}\n\n\tprotected JadxCodeData getCodeData() {\n\t\tJadxCodeData codeData = (JadxCodeData) getArgs().getCodeData();\n\t\tif (codeData == null) {\n\t\t\tcodeData = new JadxCodeData();\n\t\t\tcodeData.setRenames(new ArrayList<>());\n\t\t\tcodeData.setComments(new ArrayList<>());\n\t\t\tgetArgs().setCodeData(codeData);\n\t\t}\n\t\treturn codeData;\n\t}\n\n\tprotected JavaClass toJavaClass(ClassNode cls) {\n\t\tJavaClass javaClass = JadxInternalAccess.convertClassNode(jadxDecompiler, cls);\n\t\tassertThat(javaClass).isNotNull();\n\t\treturn javaClass;\n\t}\n\n\tprotected JavaMethod toJavaMethod(MethodNode mth) {\n\t\tJavaMethod javaMethod = JadxInternalAccess.convertMethodNode(jadxDecompiler, mth);\n\t\tassertThat(javaMethod).isNotNull();\n\t\treturn javaMethod;\n\t}\n\n\tprotected JavaVariable toJavaVariable(VarNode varNode) {\n\t\tJavaVariable javaVariable = (JavaVariable) jadxDecompiler.getJavaNodeByCodeAnnotation(null, varNode);\n\t\tassertThat(javaVariable).isNotNull();\n\t\treturn javaVariable;\n\t}\n\n\tpublic File getResourceFile(String filePath) {\n\t\tURL resource = getClass().getClassLoader().getResource(filePath);\n\t\tassertThat(resource).as(\"Resource not found: %s\", filePath).isNotNull();\n\t\tString resPath = resource.getFile();\n\t\treturn new File(resPath);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/RaungTest.java",
    "content": "package jadx.tests.api;\n\nimport java.io.File;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeEach;\n\nimport jadx.api.JadxInternalAccess;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.RootNode;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic abstract class RaungTest extends IntegrationTest {\n\n\tprivate static final String RAUNG_TESTS_PROJECT = \"jadx-core\";\n\tprivate static final String RAUNG_TESTS_DIR = \"src/test/raung\";\n\tprivate static final String RAUNG_TESTS_EXT = \".raung\";\n\n\t@BeforeEach\n\tpublic void init() {\n\t\tsuper.init();\n\t\tthis.useJavaInput();\n\t}\n\n\t/**\n\t * Preferred method for one file raung test\n\t */\n\tprotected ClassNode getClassNodeFromRaung() {\n\t\tString pkg = getTestPkg();\n\t\tString clsName = getTestName();\n\t\treturn getClassNodeFromRaung(pkg + File.separatorChar + clsName, pkg + '.' + clsName);\n\t}\n\n\tprotected ClassNode getClassNodeFromRaung(String file, String clsName) {\n\t\tFile raungFile = getRaungFile(file);\n\t\treturn getClassNodeFromFiles(Collections.singletonList(raungFile), clsName);\n\t}\n\n\tprotected List<ClassNode> loadFromRaungFiles() {\n\t\tjadxDecompiler = loadFiles(collectRaungFiles(getTestPkg(), getTestName()));\n\t\tRootNode root = JadxInternalAccess.getRoot(jadxDecompiler);\n\t\tList<ClassNode> classes = root.getClasses(false);\n\t\tdecompileAndCheck(classes);\n\t\treturn classes;\n\t}\n\n\tprivate List<File> collectRaungFiles(String pkg, String testDir) {\n\t\tString raungFilesDir = pkg + File.separatorChar + testDir + File.separatorChar;\n\t\tFile raungDir = getRaungDir(raungFilesDir);\n\t\tString[] raungFileNames = raungDir.list((dir, name) -> name.endsWith(\".raung\"));\n\t\tassertThat(raungFileNames).as(\"Raung files not found in \" + raungDir).isNotNull();\n\t\treturn Stream.of(raungFileNames)\n\t\t\t\t.map(file -> new File(raungDir, file))\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tprivate static File getRaungFile(String baseName) {\n\t\tFile raungFile = new File(RAUNG_TESTS_DIR, baseName + RAUNG_TESTS_EXT);\n\t\tif (raungFile.exists()) {\n\t\t\treturn raungFile;\n\t\t}\n\t\tFile pathFromRoot = new File(RAUNG_TESTS_PROJECT, raungFile.getPath());\n\t\tif (pathFromRoot.exists()) {\n\t\t\treturn pathFromRoot;\n\t\t}\n\t\tthrow new AssertionError(\"Raung file not found: \" + raungFile.getPath());\n\t}\n\n\tprivate static File getRaungDir(String baseName) {\n\t\tFile raungDir = new File(RAUNG_TESTS_DIR, baseName);\n\t\tif (raungDir.exists()) {\n\t\t\treturn raungDir;\n\t\t}\n\t\tFile pathFromRoot = new File(RAUNG_TESTS_PROJECT, raungDir.getPath());\n\t\tif (pathFromRoot.exists()) {\n\t\t\treturn pathFromRoot;\n\t\t}\n\t\tthrow new AssertionError(\"Raung dir not found: \" + raungDir.getPath());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/SmaliTest.java",
    "content": "package jadx.tests.api;\n\nimport java.io.File;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.junit.jupiter.api.Assumptions;\nimport org.junit.jupiter.api.BeforeEach;\n\nimport jadx.api.JadxInternalAccess;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.RootNode;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic abstract class SmaliTest extends IntegrationTest {\n\n\tprivate static final String SMALI_TESTS_DIR = \"src/test/smali\";\n\tprivate static final String SMALI_TESTS_EXT = \".smali\";\n\n\tprivate String currentProject = \"jadx-core\";\n\n\tpublic void setCurrentProject(String currentProject) {\n\t\tthis.currentProject = currentProject;\n\t}\n\n\t@BeforeEach\n\tpublic void init() {\n\t\tAssumptions.assumeFalse(USE_JAVA_INPUT, \"skip smali test for java input tests\");\n\t\tsuper.init();\n\t\tthis.useDexInput();\n\t}\n\n\tprotected ClassNode getClassNodeFromSmali(String file, String clsName) {\n\t\tFile smaliFile = getSmaliFile(file);\n\t\treturn getClassNodeFromFiles(Collections.singletonList(smaliFile), clsName);\n\t}\n\n\t/**\n\t * Preferred method for one file smali test\n\t */\n\tprotected ClassNode getClassNodeFromSmali() {\n\t\treturn getClassNodeFromSmaliWithPkg(getTestPkg(), getTestName());\n\t}\n\n\tprotected ClassNode getClassNodeFromSmaliWithClsName(String fullClsName) {\n\t\treturn getClassNodeFromSmali(getTestPkg() + File.separatorChar + getTestName(), fullClsName);\n\t}\n\n\tprotected ClassNode getClassNodeFromSmaliWithPath(String path, String clsName) {\n\t\treturn getClassNodeFromSmali(path + File.separatorChar + clsName, clsName);\n\t}\n\n\tprotected ClassNode getClassNodeFromSmaliWithPkg(String pkg, String clsName) {\n\t\treturn getClassNodeFromSmali(pkg + File.separatorChar + clsName, pkg + '.' + clsName);\n\t}\n\n\tprotected ClassNode getClassNodeFromSmaliFiles(String pkg, String testName, String clsName) {\n\t\treturn getClassNodeFromFiles(collectSmaliFiles(pkg, testName), pkg + '.' + clsName);\n\t}\n\n\tprotected ClassNode getClassNodeFromSmaliFiles(String clsName) {\n\t\treturn searchCls(loadFromSmaliFiles(), getTestPkg() + '.' + clsName);\n\t}\n\n\tprotected ClassNode getClassNodeFromSmaliFiles() {\n\t\treturn searchCls(loadFromSmaliFiles(), getTestPkg() + '.' + getTestName());\n\t}\n\n\tprotected List<ClassNode> loadFromSmaliFiles() {\n\t\tjadxDecompiler = loadFiles(collectSmaliFiles(getTestPkg(), getTestName()));\n\t\tRootNode root = JadxInternalAccess.getRoot(jadxDecompiler);\n\t\tList<ClassNode> classes = root.getClasses(false);\n\t\tdecompileAndCheck(classes);\n\t\treturn classes;\n\t}\n\n\tprivate List<File> collectSmaliFiles(String pkg, @Nullable String testDir) {\n\t\tString smaliFilesDir;\n\t\tif (testDir == null) {\n\t\t\tsmaliFilesDir = pkg + File.separatorChar;\n\t\t} else {\n\t\t\tsmaliFilesDir = pkg + File.separatorChar + testDir + File.separatorChar;\n\t\t}\n\t\tFile smaliDir = getSmaliDir(smaliFilesDir);\n\t\tString[] smaliFileNames = smaliDir.list((dir, name) -> name.endsWith(\".smali\"));\n\t\tassertThat(smaliFileNames).as(\"Smali files not found in \" + smaliDir).isNotNull();\n\t\treturn Stream.of(smaliFileNames)\n\t\t\t\t.map(file -> new File(smaliDir, file))\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tprivate File getSmaliFile(String baseName) {\n\t\tFile smaliFile = new File(SMALI_TESTS_DIR, baseName + SMALI_TESTS_EXT);\n\t\tif (smaliFile.exists()) {\n\t\t\treturn smaliFile;\n\t\t}\n\t\tFile pathFromRoot = new File(currentProject, smaliFile.getPath());\n\t\tif (pathFromRoot.exists()) {\n\t\t\treturn pathFromRoot;\n\t\t}\n\t\tthrow new AssertionError(\"Smali file not found: \" + smaliFile.getPath());\n\t}\n\n\tprivate File getSmaliDir(String baseName) {\n\t\tFile smaliDir = new File(SMALI_TESTS_DIR, baseName);\n\t\tif (smaliDir.exists()) {\n\t\t\treturn smaliDir;\n\t\t}\n\t\tFile pathFromRoot = new File(currentProject, smaliDir.getPath());\n\t\tif (pathFromRoot.exists()) {\n\t\t\treturn pathFromRoot;\n\t\t}\n\t\tthrow new AssertionError(\"Smali dir not found: \" + smaliDir.getPath());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/compiler/ClassFileManager.java",
    "content": "package jadx.tests.api.compiler;\n\nimport java.io.Closeable;\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.tools.FileObject;\nimport javax.tools.ForwardingJavaFileManager;\nimport javax.tools.JavaFileObject;\nimport javax.tools.StandardJavaFileManager;\n\nimport static javax.tools.JavaFileObject.Kind;\n\npublic class ClassFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> implements Closeable {\n\n\tprivate final DynamicClassLoader classLoader;\n\n\tpublic ClassFileManager(StandardJavaFileManager standardManager) {\n\t\tsuper(standardManager);\n\t\tclassLoader = new DynamicClassLoader();\n\t}\n\n\tpublic List<JavaFileObject> getJavaFileObjectsFromFiles(List<File> sourceFiles) {\n\t\tList<JavaFileObject> list = new ArrayList<>();\n\t\tfor (JavaFileObject javaFileObject : fileManager.getJavaFileObjectsFromFiles(sourceFiles)) {\n\t\t\tlist.add(javaFileObject);\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) {\n\t\tJavaClassObject clsObject = new JavaClassObject(className, kind);\n\t\tclassLoader.add(className, clsObject);\n\t\treturn clsObject;\n\t}\n\n\t@Override\n\tpublic ClassLoader getClassLoader(Location location) {\n\t\treturn classLoader;\n\t}\n\n\tpublic DynamicClassLoader getClassLoader() {\n\t\treturn classLoader;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/compiler/CompilerOptions.java",
    "content": "package jadx.tests.api.compiler;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class CompilerOptions {\n\tprivate boolean includeDebugInfo = true;\n\tprivate boolean useEclipseCompiler = false;\n\tprivate int javaVersion = 8;\n\n\tList<String> arguments = Collections.emptyList();\n\n\tpublic boolean isIncludeDebugInfo() {\n\t\treturn includeDebugInfo;\n\t}\n\n\tpublic void setIncludeDebugInfo(boolean includeDebugInfo) {\n\t\tthis.includeDebugInfo = includeDebugInfo;\n\t}\n\n\tpublic boolean isUseEclipseCompiler() {\n\t\treturn useEclipseCompiler;\n\t}\n\n\tpublic void setUseEclipseCompiler(boolean useEclipseCompiler) {\n\t\tthis.useEclipseCompiler = useEclipseCompiler;\n\t}\n\n\tpublic int getJavaVersion() {\n\t\treturn javaVersion;\n\t}\n\n\tpublic void setJavaVersion(int javaVersion) {\n\t\tthis.javaVersion = javaVersion;\n\t}\n\n\tpublic List<String> getArguments() {\n\t\treturn Collections.unmodifiableList(arguments);\n\t}\n\n\tpublic void addArgument(String argName) {\n\t\tif (arguments.isEmpty()) {\n\t\t\targuments = new ArrayList<>();\n\t\t}\n\t\targuments.add(argName);\n\t}\n\n\tpublic void addArgument(String argName, String argValue) {\n\t\tif (arguments.isEmpty()) {\n\t\t\targuments = new ArrayList<>();\n\t\t}\n\t\targuments.add(argName);\n\t\targuments.add(argValue);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/compiler/DynamicClassLoader.java",
    "content": "package jadx.tests.api.compiler;\n\nimport java.security.SecureClassLoader;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic class DynamicClassLoader extends SecureClassLoader {\n\tprivate final Map<String, JavaClassObject> clsMap = new ConcurrentHashMap<>();\n\tprivate final Map<String, Class<?>> clsCache = new ConcurrentHashMap<>();\n\n\tpublic void add(String className, JavaClassObject clsObject) {\n\t\tthis.clsMap.put(className, clsObject);\n\t}\n\n\t@Override\n\tpublic Class<?> findClass(String name) throws ClassNotFoundException {\n\t\tClass<?> cls = replaceClass(name);\n\t\tif (cls != null) {\n\t\t\treturn cls;\n\t\t}\n\t\treturn super.findClass(name);\n\t}\n\n\tpublic Class<?> loadClass(String name) throws ClassNotFoundException {\n\t\tClass<?> cls = replaceClass(name);\n\t\tif (cls != null) {\n\t\t\treturn cls;\n\t\t}\n\t\treturn super.loadClass(name);\n\t}\n\n\t@Nullable\n\tpublic Class<?> replaceClass(String name) {\n\t\tClass<?> cacheCls = clsCache.get(name);\n\t\tif (cacheCls != null) {\n\t\t\treturn cacheCls;\n\t\t}\n\t\tJavaClassObject clsObject = clsMap.get(name);\n\t\tif (clsObject == null) {\n\t\t\treturn null;\n\t\t}\n\t\tbyte[] clsBytes = clsObject.getBytes();\n\t\tClass<?> cls = super.defineClass(name, clsBytes, 0, clsBytes.length);\n\t\tclsCache.put(name, cls);\n\t\treturn cls;\n\t}\n\n\tpublic Collection<? extends JavaClassObject> getClassObjects() {\n\t\treturn clsMap.values();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/compiler/EclipseCompilerUtils.java",
    "content": "package jadx.tests.api.compiler;\n\nimport javax.tools.JavaCompiler;\n\npublic class EclipseCompilerUtils {\n\n\tpublic static JavaCompiler newInstance() {\n\t\tif (!JavaUtils.checkJavaVersion(11)) {\n\t\t\tthrow new IllegalArgumentException(\"Eclipse compiler build with Java 11\");\n\t\t}\n\t\ttry {\n\t\t\tClass<?> ecjCls = Class.forName(\"org.eclipse.jdt.internal.compiler.tool.EclipseCompiler\");\n\t\t\treturn (JavaCompiler) ecjCls.getConstructor().newInstance();\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to init Eclipse compiler\", e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/compiler/JavaClassObject.java",
    "content": "package jadx.tests.api.compiler;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.OutputStream;\nimport java.net.URI;\n\nimport javax.tools.SimpleJavaFileObject;\n\npublic class JavaClassObject extends SimpleJavaFileObject {\n\n\tprivate final String name;\n\tprivate final ByteArrayOutputStream bos = new ByteArrayOutputStream();\n\n\tpublic JavaClassObject(String name, Kind kind) {\n\t\tsuper(URI.create(\"string:///\" + name.replace('.', '/') + kind.extension), kind);\n\t\tthis.name = name;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic byte[] getBytes() {\n\t\treturn bos.toByteArray();\n\t}\n\n\t@Override\n\tpublic OutputStream openOutputStream() {\n\t\treturn bos;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/compiler/JavaUtils.java",
    "content": "package jadx.tests.api.compiler;\n\nimport org.apache.commons.lang3.SystemUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class JavaUtils {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JavaUtils.class);\n\n\tpublic static final int JAVA_VERSION_INT = getJavaVersionInt();\n\n\tpublic static boolean checkJavaVersion(int requiredVersion) {\n\t\treturn JAVA_VERSION_INT >= requiredVersion;\n\t}\n\n\tprivate static int getJavaVersionInt() {\n\t\tString javaSpecVerStr = SystemUtils.JAVA_SPECIFICATION_VERSION;\n\t\tif (javaSpecVerStr == null) {\n\t\t\tLOG.warn(\"Unknown current java specification version, use 8 as fallback\");\n\t\t\treturn 8; // fallback version\n\t\t}\n\t\tif (javaSpecVerStr.startsWith(\"1.\")) {\n\t\t\treturn Integer.parseInt(javaSpecVerStr.substring(2));\n\t\t}\n\t\treturn Integer.parseInt(javaSpecVerStr);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/compiler/StringJavaFileObject.java",
    "content": "package jadx.tests.api.compiler;\n\nimport java.net.URI;\n\nimport javax.tools.SimpleJavaFileObject;\n\npublic class StringJavaFileObject extends SimpleJavaFileObject {\n\n\tprivate final String content;\n\n\tpublic StringJavaFileObject(String className, String content) {\n\t\tsuper(URI.create(\"string:///\" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);\n\t\tthis.content = content;\n\t}\n\n\t@Override\n\tpublic CharSequence getCharContent(boolean ignoreEncodingErrors) {\n\t\treturn content;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/compiler/TestCompiler.java",
    "content": "package jadx.tests.api.compiler;\n\nimport java.io.Closeable;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.io.Writer;\nimport java.lang.reflect.Method;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport javax.tools.DiagnosticListener;\nimport javax.tools.JavaCompiler;\nimport javax.tools.JavaCompiler.CompilationTask;\nimport javax.tools.JavaFileObject;\nimport javax.tools.ToolProvider;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.api.impl.SimpleCodeWriter;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.tests.api.IntegrationTest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestCompiler implements Closeable {\n\tprivate final CompilerOptions options;\n\tprivate final JavaCompiler compiler;\n\tprivate final ClassFileManager fileManager;\n\n\tpublic TestCompiler(CompilerOptions options) {\n\t\tthis.options = options;\n\t\tint javaVersion = options.getJavaVersion();\n\t\tif (!JavaUtils.checkJavaVersion(javaVersion)) {\n\t\t\tthrow new IllegalArgumentException(\"Current java version not meet requirement: \"\n\t\t\t\t\t+ \"current: \" + JavaUtils.JAVA_VERSION_INT + \", required: \" + javaVersion);\n\t\t}\n\t\tif (options.isUseEclipseCompiler()) {\n\t\t\tcompiler = EclipseCompilerUtils.newInstance();\n\t\t} else {\n\t\t\tcompiler = ToolProvider.getSystemJavaCompiler();\n\t\t\tif (compiler == null) {\n\t\t\t\tthrow new IllegalStateException(\"Can not find compiler, please use JDK instead\");\n\t\t\t}\n\t\t}\n\t\tfileManager = new ClassFileManager(compiler.getStandardFileManager(null, null, null));\n\t}\n\n\tpublic List<File> compileFiles(List<File> sourceFiles, Path outTmp) throws IOException {\n\t\tcompile(fileManager.getJavaFileObjectsFromFiles(sourceFiles));\n\t\tList<File> files = new ArrayList<>();\n\t\tfor (JavaClassObject classObject : fileManager.getClassLoader().getClassObjects()) {\n\t\t\tPath path = outTmp.resolve(classObject.getName().replace('.', '/') + \".class\");\n\t\t\tFileUtils.makeDirsForFile(path);\n\t\t\tFiles.write(path, classObject.getBytes());\n\t\t\tfiles.add(path.toFile());\n\t\t}\n\t\treturn files;\n\t}\n\n\tpublic void compileNodes(List<ClassNode> clsNodeList) {\n\t\tList<JavaFileObject> jfObjects = new ArrayList<>(clsNodeList.size());\n\t\tfor (ClassNode clsNode : clsNodeList) {\n\t\t\tjfObjects.add(new StringJavaFileObject(clsNode.getFullName(), clsNode.getCode().getCodeStr()));\n\t\t}\n\t\tcompile(jfObjects);\n\t}\n\n\tprivate void compile(List<JavaFileObject> jfObjects) {\n\t\tList<String> arguments = new ArrayList<>();\n\t\targuments.add(options.isIncludeDebugInfo() ? \"-g\" : \"-g:none\");\n\t\tint javaVersion = options.getJavaVersion();\n\t\tString javaVerStr = javaVersion <= 8 ? \"1.\" + javaVersion : Integer.toString(javaVersion);\n\t\targuments.add(\"-source\");\n\t\targuments.add(javaVerStr);\n\t\targuments.add(\"-target\");\n\t\targuments.add(javaVerStr);\n\t\targuments.addAll(options.getArguments());\n\n\t\tSimpleCodeWriter output = new SimpleCodeWriter();\n\t\tDiagnosticListener<JavaFileObject> diagnostic = diagObj -> {\n\t\t\tString msg = \"Compiler diagnostic: \" + diagObj;\n\t\t\toutput.startLine(msg);\n\t\t\tSystem.out.println(msg);\n\t\t};\n\t\tWriter out = new PrintWriter(System.out);\n\t\tCompilationTask compilerTask = compiler.getTask(out, fileManager, diagnostic, arguments, null, jfObjects);\n\t\tif (Boolean.FALSE.equals(compilerTask.call())) {\n\t\t\tthrow new RuntimeException(\"Compilation failed: \" + output);\n\t\t}\n\t}\n\n\tprivate ClassLoader getClassLoader() {\n\t\treturn fileManager.getClassLoader();\n\t}\n\n\tpublic Class<?> getClass(String clsFullName) throws ClassNotFoundException {\n\t\treturn getClassLoader().loadClass(clsFullName);\n\t}\n\n\t@NotNull\n\tpublic Method getMethod(Class<?> cls, String methodName, Class<?>[] types) throws NoSuchMethodException {\n\t\treturn cls.getMethod(methodName, types);\n\t}\n\n\tpublic Object invoke(String clsFullName, String methodName, Class<?>[] types, Object[] args) {\n\t\ttry {\n\t\t\tfor (Class<?> type : types) {\n\t\t\t\tcheckType(type);\n\t\t\t}\n\t\t\tClass<?> cls = getClass(clsFullName);\n\t\t\tMethod mth = getMethod(cls, methodName, types);\n\t\t\tObject inst = cls.getConstructor().newInstance();\n\t\t\tassertThat(mth).as(\"Failed to get method \" + methodName + '(' + Arrays.toString(types) + ')').isNotNull();\n\t\t\treturn mth.invoke(inst, args);\n\t\t} catch (Throwable e) {\n\t\t\tIntegrationTest.rethrow(\"Invoke error for method: \" + methodName, e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate Class<?> checkType(Class<?> type) throws ClassNotFoundException {\n\t\tif (type.isPrimitive()) {\n\t\t\treturn type;\n\t\t}\n\t\tif (type.isArray()) {\n\t\t\treturn checkType(type.getComponentType());\n\t\t}\n\t\tClass<?> cls = getClassLoader().loadClass(type.getName());\n\t\tif (type != cls) {\n\t\t\tthrow new IllegalArgumentException(\"Internal test class cannot be used in method invoke\");\n\t\t}\n\t\treturn cls;\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\tfileManager.close();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/extensions/profiles/JadxTestProfilesExtension.java",
    "content": "package jadx.tests.api.extensions.profiles;\n\nimport java.lang.reflect.Method;\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.BeforeTestExecutionCallback;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContext;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;\nimport org.junit.platform.commons.util.AnnotationUtils;\nimport org.junit.platform.commons.util.Preconditions;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static org.junit.platform.commons.util.AnnotationUtils.isAnnotated;\n\npublic class JadxTestProfilesExtension implements TestTemplateInvocationContextProvider {\n\n\t@Override\n\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\treturn isAnnotated(context.getTestMethod(), TestWithProfiles.class);\n\t}\n\n\t@Override\n\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\tPreconditions.condition(IntegrationTest.class.isAssignableFrom(context.getRequiredTestClass()),\n\t\t\t\t\"@TestWithProfiles should be used only in IntegrationTest subclasses\");\n\n\t\tMethod testMethod = context.getRequiredTestMethod();\n\t\tboolean testAnnAdded = AnnotationUtils.findAnnotation(testMethod, Test.class).isPresent();\n\t\tPreconditions.condition(!testAnnAdded, \"@Test annotation should be removed\");\n\n\t\tTestWithProfiles profilesAnn = AnnotationUtils.findAnnotation(testMethod, TestWithProfiles.class).get();\n\t\tEnumSet<TestProfile> profilesSet = EnumSet.noneOf(TestProfile.class);\n\t\tCollections.addAll(profilesSet, profilesAnn.value());\n\t\tif (profilesSet.contains(TestProfile.ALL)) {\n\t\t\tCollections.addAll(profilesSet, TestProfile.values());\n\t\t}\n\t\tprofilesSet.remove(TestProfile.ALL);\n\t\treturn profilesSet.stream()\n\t\t\t\t.sorted()\n\t\t\t\t.map(RunWithProfile::new);\n\t}\n\n\tprivate static class RunWithProfile implements TestTemplateInvocationContext {\n\t\tprivate final TestProfile testProfile;\n\n\t\tpublic RunWithProfile(TestProfile testProfile) {\n\t\t\tthis.testProfile = testProfile;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getDisplayName(int invocationIndex) {\n\t\t\treturn testProfile.getDescription();\n\t\t}\n\n\t\t@Override\n\t\tpublic List<Extension> getAdditionalExtensions() {\n\t\t\treturn Collections.singletonList(beforeTest());\n\t\t}\n\n\t\tprivate BeforeTestExecutionCallback beforeTest() {\n\t\t\treturn execContext -> testProfile.accept((IntegrationTest) execContext.getRequiredTestInstance());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/extensions/profiles/TestProfile.java",
    "content": "package jadx.tests.api.extensions.profiles;\n\nimport java.util.function.Consumer;\n\nimport jadx.tests.api.IntegrationTest;\n\npublic enum TestProfile implements Consumer<IntegrationTest> {\n\tDX_J8(\"dx-j8\", test -> {\n\t\ttest.useTargetJavaVersion(8);\n\t\ttest.useDexInput(\"dx\");\n\t}),\n\tD8_J8(\"d8-j8\", test -> {\n\t\ttest.useTargetJavaVersion(8);\n\t\ttest.useDexInput(\"d8\");\n\t}),\n\tD8_J11(\"d8-j11\", test -> {\n\t\ttest.useTargetJavaVersion(11);\n\t\ttest.useDexInput(\"d8\");\n\t}),\n\tD8_J11_DESUGAR(\"d8-j11-desugar\", test -> {\n\t\ttest.useTargetJavaVersion(11);\n\t\ttest.useDexInput(\"d8\");\n\t\ttest.keepParentClassOnInput();\n\t\ttest.getArgs().getPluginOptions().put(\"java-convert.d8-desugar\", \"yes\");\n\t}),\n\tJAVA8(\"java-8\", test -> {\n\t\ttest.useTargetJavaVersion(8);\n\t\ttest.useJavaInput();\n\t}),\n\tJAVA11(\"java-11\", test -> {\n\t\ttest.useTargetJavaVersion(11);\n\t\ttest.useJavaInput();\n\t}),\n\tJAVA17(\"java-17\", test -> {\n\t\ttest.useTargetJavaVersion(17);\n\t\ttest.useJavaInput();\n\t}),\n\tECJ_DX_J8(\"ecj-dx-j8\", test -> {\n\t\ttest.useEclipseCompiler();\n\t\ttest.useTargetJavaVersion(8);\n\t\ttest.useDexInput();\n\t}),\n\tECJ_J8(\"ecj-j8\", test -> {\n\t\ttest.useEclipseCompiler();\n\t\ttest.useTargetJavaVersion(8);\n\t\ttest.useJavaInput();\n\t}),\n\tALL(\"all\", null);\n\n\tprivate final String description;\n\tprivate final Consumer<IntegrationTest> setup;\n\n\tTestProfile(String description, Consumer<IntegrationTest> setup) {\n\t\tthis.description = description;\n\t\tthis.setup = setup;\n\t}\n\n\t@Override\n\tpublic void accept(IntegrationTest test) {\n\t\tthis.setup.accept(test);\n\t\ttest.setOutDirSuffix(description);\n\t}\n\n\tpublic String getDescription() {\n\t\treturn description;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/extensions/profiles/TestWithProfiles.java",
    "content": "package jadx.tests.api.extensions.profiles;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.TestTemplate;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n@TestTemplate\n@ExtendWith(JadxTestProfilesExtension.class)\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.METHOD)\npublic @interface TestWithProfiles {\n\n\tTestProfile[] value();\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/utils/TestFilesGetter.java",
    "content": "package jadx.tests.api.utils;\n\nimport java.nio.file.Path;\n\nimport jadx.core.plugins.files.IJadxFilesGetter;\nimport jadx.core.utils.files.FileUtils;\n\npublic class TestFilesGetter implements IJadxFilesGetter {\n\tprivate final Path testDir;\n\n\tpublic TestFilesGetter(Path testDir) {\n\t\tthis.testDir = testDir;\n\t}\n\n\t@Override\n\tpublic Path getConfigDir() {\n\t\treturn makeSubDir(\"config\");\n\t}\n\n\t@Override\n\tpublic Path getCacheDir() {\n\t\treturn makeSubDir(\"cache\");\n\t}\n\n\t@Override\n\tpublic Path getTempDir() {\n\t\treturn testDir;\n\t}\n\n\tprivate Path makeSubDir(String config) {\n\t\tPath dir = testDir.resolve(config);\n\t\tFileUtils.makeDirs(dir);\n\t\treturn dir;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/utils/TestUtils.java",
    "content": "package jadx.tests.api.utils;\n\nimport java.io.File;\n\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport jadx.NotYetImplementedExtension;\nimport jadx.api.CommentsLevel;\nimport jadx.api.JadxArgs;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.IAttributeNode;\nimport jadx.core.dex.attributes.nodes.JadxCommentsAttr;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\n@ExtendWith(NotYetImplementedExtension.class)\npublic class TestUtils {\n\n\tpublic static String indent() {\n\t\treturn JadxArgs.DEFAULT_INDENT_STR;\n\t}\n\n\tpublic static String indent(int indent) {\n\t\tif (indent == 1) {\n\t\t\treturn JadxArgs.DEFAULT_INDENT_STR;\n\t\t}\n\t\treturn Utils.strRepeat(JadxArgs.DEFAULT_INDENT_STR, indent);\n\t}\n\n\tpublic static int count(String string, String substring) {\n\t\tif (substring == null || substring.isEmpty()) {\n\t\t\tthrow new IllegalArgumentException(\"Substring can't be null or empty\");\n\t\t}\n\t\tint count = 0;\n\t\tint idx = 0;\n\t\twhile ((idx = string.indexOf(substring, idx)) != -1) {\n\t\t\tidx++;\n\t\t\tcount++;\n\t\t}\n\t\treturn count;\n\t}\n\n\tprotected static void checkCode(ClassNode cls, boolean allowWarnInCode) {\n\t\tassertThat(hasErrors(cls, allowWarnInCode)).as(\"Inconsistent cls: \" + cls).isFalse();\n\t\tfor (MethodNode mthNode : cls.getMethods()) {\n\t\t\tif (hasErrors(mthNode, allowWarnInCode)) {\n\t\t\t\tfail(\"Method with problems: \" + mthNode\n\t\t\t\t\t\t+ \"\\n \" + Utils.listToString(mthNode.getAttributesStringsList(), \"\\n \"));\n\t\t\t}\n\t\t}\n\t\tif (!cls.contains(AFlag.DONT_GENERATE)) {\n\t\t\tassertThat(cls)\n\t\t\t\t\t.code()\n\t\t\t\t\t.doesNotContain(\"inconsistent\")\n\t\t\t\t\t.doesNotContain(\"JADX ERROR\");\n\t\t}\n\t}\n\n\tprotected static boolean hasErrors(IAttributeNode node, boolean allowWarnInCode) {\n\t\tif (node.contains(AFlag.INCONSISTENT_CODE) || node.contains(AType.JADX_ERROR)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!allowWarnInCode) {\n\t\t\tJadxCommentsAttr commentsAttr = node.get(AType.JADX_COMMENTS);\n\t\t\tif (commentsAttr != null) {\n\t\t\t\treturn commentsAttr.getComments().get(CommentsLevel.WARN) != null;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static File getFileForSample(String resPath) {\n\t\ttry {\n\t\t\treturn new File(ClassLoader.getSystemResource(resPath).toURI().getRawPath());\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Resource load failed: \" + resPath, e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxAssertions.java",
    "content": "package jadx.tests.api.utils.assertj;\n\nimport org.assertj.core.api.Assertions;\n\nimport jadx.api.ICodeInfo;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic class JadxAssertions extends Assertions {\n\n\tpublic static JadxClassNodeAssertions assertThat(ClassNode cls) {\n\t\tAssertions.assertThat(cls).isNotNull();\n\t\treturn new JadxClassNodeAssertions(cls);\n\t}\n\n\tpublic static JadxMethodNodeAssertions assertThat(MethodNode mth) {\n\t\tAssertions.assertThat(mth).isNotNull();\n\t\treturn new JadxMethodNodeAssertions(mth);\n\t}\n\n\tpublic static JadxCodeInfoAssertions assertThat(ICodeInfo codeInfo) {\n\t\tAssertions.assertThat(codeInfo).isNotNull();\n\t\treturn new JadxCodeInfoAssertions(codeInfo);\n\t}\n\n\tpublic static JadxCodeAssertions assertThat(String code) {\n\t\treturn new JadxCodeAssertions(code);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxClassNodeAssertions.java",
    "content": "package jadx.tests.api.utils.assertj;\n\nimport java.util.Map;\n\nimport org.assertj.core.api.AbstractObjectAssert;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\npublic class JadxClassNodeAssertions extends AbstractObjectAssert<JadxClassNodeAssertions, ClassNode> {\n\tpublic JadxClassNodeAssertions(ClassNode cls) {\n\t\tsuper(cls, JadxClassNodeAssertions.class);\n\t}\n\n\tpublic JadxCodeInfoAssertions decompile() {\n\t\tisNotNull();\n\t\tICodeInfo codeInfo = actual.getCode();\n\t\tassertThat(codeInfo).isNotNull();\n\t\treturn new JadxCodeInfoAssertions(codeInfo);\n\t}\n\n\tpublic JadxCodeAssertions code() {\n\t\tisNotNull();\n\t\tICodeInfo code = actual.getCode();\n\t\tassertThat(code).isNotNull();\n\t\tString codeStr = code.getCodeStr();\n\t\tassertThat(codeStr).isNotBlank();\n\t\treturn new JadxCodeAssertions(codeStr);\n\t}\n\n\tpublic JadxCodeAssertions disasmCode() {\n\t\tisNotNull();\n\t\tString disasmCode = actual.getDisassembledCode();\n\t\tassertThat(disasmCode).isNotNull().isNotBlank();\n\t\treturn new JadxCodeAssertions(disasmCode);\n\t}\n\n\tpublic JadxCodeAssertions reloadCode(IntegrationTest testInstance) {\n\t\tisNotNull();\n\t\tICodeInfo code = actual.reloadCode();\n\t\tassertThat(code).isNotNull();\n\t\tString codeStr = code.getCodeStr();\n\t\tassertThat(codeStr).isNotBlank();\n\n\t\tJadxCodeAssertions codeAssertions = new JadxCodeAssertions(codeStr);\n\t\tcodeAssertions.print();\n\t\ttestInstance.runChecks(actual);\n\t\treturn codeAssertions;\n\t}\n\n\t/**\n\t * Force running auto check on decompiled code.\n\t * Useful for smali tests.\n\t */\n\tpublic JadxClassNodeAssertions runDecompiledAutoCheck(IntegrationTest testInstance) {\n\t\tisNotNull();\n\t\ttestInstance.runDecompiledAutoCheck(actual);\n\t\treturn this;\n\t}\n\n\tpublic JadxClassNodeAssertions checkCodeAnnotationFor(String refStr, ICodeAnnotation node) {\n\t\tcheckCodeAnnotationFor(refStr, 0, node);\n\t\treturn this;\n\t}\n\n\tpublic JadxClassNodeAssertions checkCodeAnnotationFor(String refStr, int refOffset, ICodeAnnotation node) {\n\t\tICodeInfo code = actual.getCode();\n\t\tint codePos = code.getCodeStr().indexOf(refStr);\n\t\tassertThat(codePos).describedAs(\"String '%s' not found\", refStr).isNotEqualTo(-1);\n\t\tint refPos = codePos + refOffset;\n\t\tfor (Map.Entry<Integer, ICodeAnnotation> entry : code.getCodeMetadata().getAsMap().entrySet()) {\n\t\t\tif (entry.getKey() == refPos) {\n\t\t\t\tassertThat(entry.getValue()).isEqualTo(node);\n\t\t\t\treturn this;\n\t\t\t}\n\t\t}\n\t\tfail(\"Annotation for reference string: '%s' at position %d not found\", refStr, refPos);\n\t\treturn this;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxCodeAssertions.java",
    "content": "package jadx.tests.api.utils.assertj;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.function.Function;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\nimport org.assertj.core.api.AbstractStringAssert;\nimport org.assertj.core.internal.Failures;\n\nimport jadx.tests.api.utils.TestUtils;\n\nimport static org.assertj.core.error.ShouldNotContainSubsequence.shouldNotContainSubsequence;\n\npublic class JadxCodeAssertions extends AbstractStringAssert<JadxCodeAssertions> {\n\n\tprivate Failures failures = Failures.instance();\n\n\tpublic JadxCodeAssertions(String code) {\n\t\tsuper(code, JadxCodeAssertions.class);\n\t}\n\n\tpublic JadxCodeAssertions containsOne(String substring) {\n\t\treturn countString(1, substring);\n\t}\n\n\tpublic JadxCodeAssertions countString(int count, String substring) {\n\t\tisNotNull();\n\t\tint actualCount = TestUtils.count(actual, substring);\n\t\tif (actualCount != count) {\n\t\t\tfailWithMessage(\"Expected a substring <%s> count <%d> but was <%d>\", substring, count, actualCount);\n\t\t}\n\t\treturn this;\n\t}\n\n\tpublic JadxCodeAssertions notContainsLine(int indent, String line) {\n\t\treturn countLine(0, indent, line);\n\t}\n\n\tpublic JadxCodeAssertions containsLine(int indent, String line) {\n\t\treturn countLine(1, indent, line);\n\t}\n\n\tprivate JadxCodeAssertions countLine(int count, int indent, String line) {\n\t\tString indentStr = TestUtils.indent(indent);\n\t\treturn countString(count, indentStr + line);\n\t}\n\n\tpublic JadxCodeAssertions containsLines(String... lines) {\n\t\treturn containsLines(0, lines);\n\t}\n\n\tpublic JadxCodeAssertions containsLines(int commonIndent, String... lines) {\n\t\tif (lines.length == 1) {\n\t\t\treturn containsLine(commonIndent, lines[0]);\n\t\t}\n\t\tString indent = TestUtils.indent(commonIndent);\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (String line : lines) {\n\t\t\tsb.append('\\n');\n\t\t\tif (line.isEmpty()) {\n\t\t\t\t// don't add common indent to empty lines\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString searchLine = indent + line;\n\t\t\tsb.append(searchLine);\n\t\t\t// check every line for easier debugging\n\t\t\tcontains(searchLine);\n\t\t}\n\t\treturn containsOnlyOnce(sb.substring(1));\n\t}\n\n\tpublic JadxCodeAssertions doesNotContainSubsequence(CharSequence... values) {\n\t\tfinal var regex = Arrays.stream(values)\n\t\t\t\t.map(value -> Pattern.quote(value.toString()))\n\t\t\t\t.collect(Collectors.joining(\".*\"));\n\n\t\tfinal var pattern = Pattern.compile(regex, Pattern.DOTALL);\n\n\t\tfinal var matcher = pattern.matcher(actual);\n\t\tif (matcher.find()) {\n\t\t\tthrow failures.failure(info, shouldNotContainSubsequence(actual, values, matcher.start()));\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tpublic JadxCodeAssertions removeBlockComments() {\n\t\tString code = actual.replaceAll(\"/\\\\*.*\\\\*/\", \"\");\n\t\tJadxCodeAssertions newCode = new JadxCodeAssertions(code);\n\t\tnewCode.print();\n\t\treturn newCode;\n\t}\n\n\tpublic JadxCodeAssertions removeLineComments() {\n\t\tString code = actual.replaceAll(\"//.*(?!$)\", \"\");\n\t\tJadxCodeAssertions newCode = new JadxCodeAssertions(code);\n\t\tnewCode.print();\n\t\treturn newCode;\n\t}\n\n\tpublic JadxCodeAssertions print() {\n\t\tSystem.out.println(\"-----------------------------------------------------------\");\n\t\tSystem.out.println(actual);\n\t\tSystem.out.println(\"-----------------------------------------------------------\");\n\t\treturn this;\n\t}\n\n\tpublic JadxCodeAssertions containsOneOf(String... substringArr) {\n\t\tint matches = 0;\n\t\tfor (String substring : substringArr) {\n\t\t\tmatches += TestUtils.count(actual, substring);\n\t\t}\n\t\tif (matches != 1) {\n\t\t\tfailWithMessage(\"Expected only one match from <%s> but was <%d>\", Arrays.toString(substringArr), matches);\n\t\t}\n\t\treturn this;\n\t}\n\n\t@SuppressWarnings(\"UnusedReturnValue\")\n\t@SafeVarargs\n\tpublic final JadxCodeAssertions oneOf(Function<JadxCodeAssertions, JadxCodeAssertions>... checks) {\n\t\tint passed = 0;\n\t\tList<Throwable> failed = new ArrayList<>();\n\t\tfor (Function<JadxCodeAssertions, JadxCodeAssertions> check : checks) {\n\t\t\ttry {\n\t\t\t\tcheck.apply(this);\n\t\t\t\tpassed++;\n\t\t\t} catch (Throwable e) {\n\t\t\t\tfailed.add(e);\n\t\t\t}\n\t\t}\n\t\tif (passed != 1) {\n\t\t\tfailWithMessage(\"Expected only one match but passed: <%d>, failed: <%d>, details:\\n<%s>\",\n\t\t\t\t\tpassed, failed.size(),\n\t\t\t\t\tfailed.stream().map(Throwable::getMessage).collect(Collectors.joining(\"\\nFailed check:\\n \")));\n\t\t}\n\t\treturn this;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxCodeInfoAssertions.java",
    "content": "package jadx.tests.api.utils.assertj;\n\nimport java.util.stream.Collectors;\n\nimport org.assertj.core.api.AbstractObjectAssert;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.metadata.annotations.InsnCodeOffset;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class JadxCodeInfoAssertions extends AbstractObjectAssert<JadxCodeInfoAssertions, ICodeInfo> {\n\tpublic JadxCodeInfoAssertions(ICodeInfo cls) {\n\t\tsuper(cls, JadxCodeInfoAssertions.class);\n\t}\n\n\tpublic JadxCodeAssertions code() {\n\t\tisNotNull();\n\t\tString codeStr = actual.getCodeStr();\n\t\tassertThat(codeStr).isNotBlank();\n\t\treturn new JadxCodeAssertions(codeStr);\n\t}\n\n\tpublic JadxCodeInfoAssertions checkCodeOffsets() {\n\t\tlong dupOffsetCount = actual.getCodeMetadata().getAsMap().values().stream()\n\t\t\t\t.filter(InsnCodeOffset.class::isInstance)\n\t\t\t\t.collect(Collectors.groupingBy(o -> ((InsnCodeOffset) o).getOffset(), Collectors.toList()))\n\t\t\t\t.values().stream()\n\t\t\t\t.filter(list -> list.size() > 1)\n\t\t\t\t.count();\n\t\tassertThat(dupOffsetCount)\n\t\t\t\t.describedAs(\"Found duplicated code offsets\")\n\t\t\t\t.isEqualTo(0);\n\t\treturn this;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxMethodNodeAssertions.java",
    "content": "package jadx.tests.api.utils.assertj;\n\nimport org.assertj.core.api.AbstractObjectAssert;\n\nimport jadx.core.dex.nodes.MethodNode;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class JadxMethodNodeAssertions extends AbstractObjectAssert<JadxMethodNodeAssertions, MethodNode> {\n\tpublic JadxMethodNodeAssertions(MethodNode mth) {\n\t\tsuper(mth, JadxMethodNodeAssertions.class);\n\t}\n\n\tpublic JadxCodeAssertions code() {\n\t\tisNotNull();\n\t\tString codeStr = actual.getCodeStr();\n\t\tassertThat(codeStr).isNotBlank();\n\t\treturn new JadxCodeAssertions(codeStr);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/export/IllegalCharsForGradleWrapper.java",
    "content": "package jadx.tests.export;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.ExportGradleTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\nclass IllegalCharsForGradleWrapper extends ExportGradleTest {\n\n\t@Test\n\tvoid test() {\n\t\texportGradle(\"IllegalCharsForGradleWrapper.xml\", \"strings.xml\");\n\n\t\tassertThat(getSettingsGradle()).contains(\"'JadxTestApp'\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/export/OptionalTargetSdkVersion.java",
    "content": "package jadx.tests.export;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.ExportGradleTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class OptionalTargetSdkVersion extends ExportGradleTest {\n\n\t@Test\n\tvoid test() {\n\t\texportGradle(\"OptionalTargetSdkVersion.xml\", \"strings.xml\");\n\n\t\tassertThat(getAppGradleBuild()).contains(\"targetSdkVersion 14\").doesNotContain(\"        vectorDrawables.useSupportLibrary = true\");\n\t}\n\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/export/TestApacheHttpClient.java",
    "content": "package jadx.tests.export;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.export.GradleInfoStorage;\nimport jadx.tests.api.ExportGradleTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestApacheHttpClient extends ExportGradleTest {\n\n\t@Test\n\tvoid test() {\n\t\tGradleInfoStorage gradleInfo = getRootNode().getGradleInfoStorage();\n\t\tgradleInfo.setUseApacheHttpLegacy(true);\n\t\texportGradle(\"OptionalTargetSdkVersion.xml\", \"strings.xml\");\n\t\tassertThat(getAppGradleBuild()).contains(\"        useLibrary 'org.apache.http.legacy'\");\n\n\t\tgradleInfo.setUseApacheHttpLegacy(false);\n\t\texportGradle(\"OptionalTargetSdkVersion.xml\", \"strings.xml\");\n\t\tassertThat(getAppGradleBuild()).doesNotContain(\"        useLibrary 'org.apache.http.legacy'\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/export/TestNonFinalResIds.java",
    "content": "package jadx.tests.export;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.export.GradleInfoStorage;\nimport jadx.tests.api.ExportGradleTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestNonFinalResIds extends ExportGradleTest {\n\n\t@Test\n\tvoid test() {\n\t\tGradleInfoStorage gradleInfo = getRootNode().getGradleInfoStorage();\n\t\tgradleInfo.setNonFinalResIds(false);\n\t\texportGradle(\"OptionalTargetSdkVersion.xml\", \"strings.xml\");\n\t\tAssertions.assertFalse(getGradleProperiesFile().exists());\n\n\t\tgradleInfo.setNonFinalResIds(true);\n\t\texportGradle(\"OptionalTargetSdkVersion.xml\", \"strings.xml\");\n\t\tassertThat(getGradleProperties()).containsOne(\"android.nonFinalResIds=false\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/export/VectorDrawablesUseSupportLibrary.java",
    "content": "package jadx.tests.export;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.export.GradleInfoStorage;\nimport jadx.tests.api.ExportGradleTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class VectorDrawablesUseSupportLibrary extends ExportGradleTest {\n\n\t@Test\n\tvoid test() {\n\t\tGradleInfoStorage gradleInfo = getRootNode().getGradleInfoStorage();\n\t\tgradleInfo.setVectorFillType(true);\n\t\texportGradle(\"OptionalTargetSdkVersion.xml\", \"strings.xml\");\n\t\tassertThat(getAppGradleBuild()).contains(\"        vectorDrawables.useSupportLibrary = true\");\n\n\t\tgradleInfo.setVectorFillType(false);\n\t\tgradleInfo.setVectorPathData(true);\n\t\texportGradle(\"OptionalTargetSdkVersion.xml\", \"strings.xml\");\n\t\tassertThat(getAppGradleBuild()).contains(\"        vectorDrawables.useSupportLibrary = true\");\n\n\t\texportGradle(\"MinSdkVersion25.xml\", \"strings.xml\");\n\t\tassertThat(getAppGradleBuild()).doesNotContain(\"        vectorDrawables.useSupportLibrary = true\");\n\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/external/BaseExternalTest.java",
    "content": "package jadx.tests.external;\n\nimport java.io.File;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.CommentsLevel;\nimport jadx.api.ICodeInfo;\nimport jadx.api.JadxArgs;\nimport jadx.api.JadxDecompiler;\nimport jadx.api.JadxInternalAccess;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.tests.api.utils.TestUtils;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic abstract class BaseExternalTest extends TestUtils {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(BaseExternalTest.class);\n\n\tprotected JadxDecompiler decompiler;\n\n\tprotected abstract String getSamplesDir();\n\n\tprotected JadxArgs prepare(String inputFile) {\n\t\treturn prepare(new File(getSamplesDir(), inputFile));\n\t}\n\n\tprotected JadxArgs prepare(File input) {\n\t\tJadxArgs args = new JadxArgs();\n\t\targs.getInputFiles().add(input);\n\t\targs.setOutDir(new File(\"../jadx-external-tests-tmp\"));\n\t\targs.setSkipFilesSave(true);\n\t\targs.setSkipResources(true);\n\t\targs.setShowInconsistentCode(true);\n\t\targs.setCommentsLevel(CommentsLevel.DEBUG);\n\t\treturn args;\n\t}\n\n\tprotected JadxDecompiler decompile(JadxArgs jadxArgs) {\n\t\treturn decompile(jadxArgs, null, null);\n\t}\n\n\tprotected JadxDecompiler decompile(JadxArgs jadxArgs, String clsPatternStr) {\n\t\treturn decompile(jadxArgs, clsPatternStr, null);\n\t}\n\n\tprotected JadxDecompiler decompile(JadxArgs jadxArgs, @Nullable String clsPatternStr, @Nullable String mthPatternStr) {\n\t\tdecompiler = new JadxDecompiler(jadxArgs);\n\t\tdecompiler.load();\n\n\t\tif (clsPatternStr == null) {\n\t\t\tdecompiler.save();\n\t\t} else {\n\t\t\tprocessByPatterns(decompiler, clsPatternStr, mthPatternStr);\n\t\t}\n\t\tprintErrorReport(decompiler);\n\t\treturn decompiler;\n\t}\n\n\tprivate void processByPatterns(JadxDecompiler jadx, String clsPattern, @Nullable String mthPattern) {\n\t\tRootNode root = JadxInternalAccess.getRoot(jadx);\n\t\tint processed = 0;\n\t\tfor (ClassNode classNode : root.getClasses(true)) {\n\t\t\tString clsFullName = classNode.getClassInfo().getFullName();\n\t\t\tif (clsFullName.equals(clsPattern)) {\n\t\t\t\tif (processCls(mthPattern, classNode)) {\n\t\t\t\t\tprocessed++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tassertThat(processed).as(\"No classes processed\").isGreaterThan(0);\n\t}\n\n\tprivate boolean processCls(@Nullable String mthPattern, ClassNode classNode) {\n\t\tclassNode.load();\n\t\tboolean decompile = false;\n\t\tif (mthPattern == null) {\n\t\t\tdecompile = true;\n\t\t} else {\n\t\t\tfor (MethodNode mth : classNode.getMethods()) {\n\t\t\t\tif (isMthMatch(mth, mthPattern)) {\n\t\t\t\t\tdecompile = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!decompile) {\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tclassNode.decompile();\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Class process failed\", e);\n\t\t}\n\t\tLOG.info(\"----------------------------------------------------------------\");\n\t\tLOG.info(\"Print class: {} from: {}\", classNode.getFullName(), classNode.getInputFileName());\n\t\tif (mthPattern != null) {\n\t\t\tprintMethods(classNode, mthPattern);\n\t\t} else {\n\t\t\tLOG.info(\"Code: \\n{}\", classNode.getCode());\n\t\t}\n\t\tcheckCode(classNode, false);\n\t\treturn true;\n\t}\n\n\tprivate boolean isMthMatch(MethodNode mth, String mthPattern) {\n\t\tString shortId = mth.getMethodInfo().getShortId();\n\t\treturn isMatch(shortId, mthPattern);\n\t}\n\n\tprivate boolean isMatch(String str, String pattern) {\n\t\tif (str.equals(pattern)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn str.startsWith(pattern);\n\t}\n\n\tprivate void printMethods(ClassNode classNode, @NotNull String mthPattern) {\n\t\tICodeInfo codeInfo = classNode.getCode();\n\t\tString code = codeInfo.getCodeStr();\n\t\tif (code == null) {\n\t\t\treturn;\n\t\t}\n\t\tString dashLine = \"======================================================================================\";\n\t\tfor (MethodNode mth : classNode.getMethods()) {\n\t\t\tif (isMthMatch(mth, mthPattern)) {\n\t\t\t\tLOG.info(\"Print method: {}\\n{}\\n{}\\n{}\",\n\t\t\t\t\t\tmth.getMethodInfo().getRawFullId(),\n\t\t\t\t\t\tdashLine,\n\t\t\t\t\t\tmth.getCodeStr(),\n\t\t\t\t\t\tdashLine);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void printErrorReport(JadxDecompiler jadx) {\n\t\tjadx.printErrorsReport();\n\t\tassertThat(jadx.getErrorsCount()).isEqualTo(0);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/functional/AttributeStorageTest.java",
    "content": "package jadx.tests.functional;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.AttributeStorage;\n\nimport static jadx.core.dex.attributes.AFlag.SYNTHETIC;\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class AttributeStorageTest {\n\tprivate AttributeStorage storage;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tstorage = new AttributeStorage();\n\t}\n\n\t@Test\n\tpublic void testAdd() {\n\t\tstorage.add(SYNTHETIC);\n\t\tassertThat(storage.contains(SYNTHETIC)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testRemove() {\n\t\tstorage.add(SYNTHETIC);\n\t\tstorage.remove(SYNTHETIC);\n\t\tassertThat(storage.contains(SYNTHETIC)).isFalse();\n\t}\n\n\tpublic static final AType<TestAttr> TEST = new AType<>();\n\n\tpublic static class TestAttr implements IJadxAttribute {\n\t\t@Override\n\t\tpublic AType<TestAttr> getAttrType() {\n\t\t\treturn TEST;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testAddAttribute() {\n\t\tTestAttr attr = new TestAttr();\n\t\tstorage.add(attr);\n\n\t\tassertThat(storage.contains(TEST)).isTrue();\n\t\tassertThat(storage.get(TEST)).isEqualTo(attr);\n\t}\n\n\t@Test\n\tpublic void testRemoveAttribute() {\n\t\tTestAttr attr = new TestAttr();\n\t\tstorage.add(attr);\n\t\tstorage.remove(attr);\n\n\t\tassertThat(storage.contains(TEST)).isFalse();\n\t\tassertThat(storage.get(TEST)).isNull();\n\t}\n\n\t@Test\n\tpublic void testRemoveOtherAttribute() {\n\t\tTestAttr attr = new TestAttr();\n\t\tstorage.add(attr);\n\t\tstorage.remove(new TestAttr());\n\n\t\tassertThat(storage.contains(TEST)).isTrue();\n\t\tassertThat(storage.get(TEST)).isEqualTo(attr);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/functional/JadxClasspathTest.java",
    "content": "package jadx.tests.functional;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.JadxArgs;\nimport jadx.core.clsp.ClspGraph;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.RootNode;\n\nimport static jadx.core.dex.instructions.args.ArgType.OBJECT;\nimport static jadx.core.dex.instructions.args.ArgType.STRING;\nimport static jadx.core.dex.instructions.args.ArgType.isCastNeeded;\nimport static jadx.core.dex.instructions.args.ArgType.object;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class JadxClasspathTest {\n\n\tprivate static final String JAVA_LANG_EXCEPTION = \"java.lang.Exception\";\n\tprivate static final String JAVA_LANG_THROWABLE = \"java.lang.Throwable\";\n\n\tprivate RootNode root;\n\tprivate ClspGraph clsp;\n\n\t@BeforeEach\n\tpublic void initClsp() {\n\t\tthis.root = new RootNode(new JadxArgs());\n\t\tthis.root.loadClasses(Collections.emptyList());\n\t\tthis.root.initClassPath();\n\t\tthis.clsp = root.getClsp();\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tArgType objExc = object(JAVA_LANG_EXCEPTION);\n\t\tArgType objThr = object(JAVA_LANG_THROWABLE);\n\n\t\tassertThat(clsp.isImplements(JAVA_LANG_EXCEPTION, JAVA_LANG_THROWABLE)).isTrue();\n\t\tassertThat(clsp.isImplements(JAVA_LANG_THROWABLE, JAVA_LANG_EXCEPTION)).isFalse();\n\n\t\tassertThat(isCastNeeded(root, objExc, objThr)).isFalse();\n\t\tassertThat(isCastNeeded(root, objThr, objExc)).isTrue();\n\n\t\tassertThat(isCastNeeded(root, OBJECT, STRING)).isTrue();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/functional/JadxVisitorsOrderTest.java",
    "content": "package jadx.tests.functional;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxArgs;\nimport jadx.core.Jadx;\nimport jadx.core.dex.visitors.IDexTreeVisitor;\nimport jadx.core.dex.visitors.JadxVisitor;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class JadxVisitorsOrderTest {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxVisitorsOrderTest.class);\n\n\t@Test\n\tpublic void testOrder() {\n\t\tcheckPassList(Jadx.getPassesList(new JadxArgs()));\n\t\tcheckPassList(Jadx.getPreDecompilePassesList());\n\t\tcheckPassList(Jadx.getFallbackPassesList());\n\t}\n\n\tprivate void checkPassList(List<IDexTreeVisitor> passes) {\n\t\tList<String> errors = check(passes);\n\t\tfor (String str : errors) {\n\t\t\tLOG.error(str);\n\t\t}\n\t\tassertThat(errors).isEmpty();\n\t}\n\n\tprivate static List<String> check(List<IDexTreeVisitor> passes) {\n\t\tList<Class<?>> classList = new ArrayList<>(passes.size());\n\t\tfor (IDexTreeVisitor pass : passes) {\n\t\t\tclassList.add(pass.getClass());\n\t\t}\n\t\tList<String> errors = new ArrayList<>();\n\n\t\tSet<String> names = new HashSet<>();\n\t\tSet<Class<?>> passClsSet = new HashSet<>();\n\t\tfor (int i = 0; i < passes.size(); i++) {\n\t\t\tIDexTreeVisitor pass = passes.get(i);\n\t\t\tClass<? extends IDexTreeVisitor> passClass = pass.getClass();\n\t\t\tJadxVisitor info = passClass.getAnnotation(JadxVisitor.class);\n\t\t\tif (info == null) {\n\t\t\t\tLOG.warn(\"No JadxVisitor annotation for visitor: {}\", passClass.getName());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tboolean firstOccurrence = passClsSet.add(passClass);\n\t\t\tString passName = passClass.getSimpleName();\n\t\t\tif (firstOccurrence && !names.add(passName)) {\n\t\t\t\terrors.add(\"Visitor name conflict: \" + passName + \", class: \" + passClass.getName());\n\t\t\t}\n\t\t\tfor (Class<? extends IDexTreeVisitor> cls : info.runBefore()) {\n\t\t\t\tint beforeIndex = classList.indexOf(cls);\n\t\t\t\tif (beforeIndex != -1 && beforeIndex < i) {\n\t\t\t\t\terrors.add(\"Pass \" + passName + \" must be before \" + cls.getSimpleName());\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (Class<? extends IDexTreeVisitor> cls : info.runAfter()) {\n\t\t\t\tif (classList.indexOf(cls) > i) {\n\t\t\t\t\terrors.add(\"Pass \" + passName + \" must be after \" + cls.getSimpleName());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn errors;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/functional/NameMapperTest.java",
    "content": "package jadx.tests.functional;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.deobf.NameMapper;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class NameMapperTest {\n\n\t@Test\n\tpublic void testValidFullIdentifiers() {\n\t\tString[] validNames = {\n\t\t\t\t\"C\",\n\t\t\t\t\"Cc\",\n\t\t\t\t\"b.C\",\n\t\t\t\t\"b.Cc\",\n\t\t\t\t\"aAa.b.Cc\",\n\t\t\t\t\"a.b.Cc\",\n\t\t\t\t\"a.b.C_c\",\n\t\t\t\t\"a.b.C$c\",\n\t\t\t\t\"a.b.C9\"\n\t\t};\n\t\tfor (String validName : validNames) {\n\t\t\tassertThat(NameMapper.isValidFullIdentifier(validName)).isTrue();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testInvalidFullIdentifiers() {\n\t\tString[] invalidNames = {\n\t\t\t\t\"\",\n\t\t\t\t\"5\",\n\t\t\t\t\"7A\",\n\t\t\t\t\".C\",\n\t\t\t\t\"b.9C\",\n\t\t\t\t\"b..C\",\n\t\t};\n\t\tfor (String invalidName : invalidNames) {\n\t\t\tassertThat(NameMapper.isValidFullIdentifier(invalidName)).isFalse();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/functional/SignatureParserTest.java",
    "content": "package jadx.tests.functional;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.ArgType.WildcardBound;\nimport jadx.core.dex.nodes.parser.SignatureParser;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.core.dex.instructions.args.ArgType.INT;\nimport static jadx.core.dex.instructions.args.ArgType.OBJECT;\nimport static jadx.core.dex.instructions.args.ArgType.array;\nimport static jadx.core.dex.instructions.args.ArgType.generic;\nimport static jadx.core.dex.instructions.args.ArgType.genericType;\nimport static jadx.core.dex.instructions.args.ArgType.object;\nimport static jadx.core.dex.instructions.args.ArgType.outerGeneric;\nimport static jadx.core.dex.instructions.args.ArgType.wildcard;\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static java.util.Collections.emptyList;\nimport static java.util.Collections.singletonList;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\nclass SignatureParserTest {\n\n\t@Test\n\tpublic void testSimpleTypes() {\n\t\tcheckType(\"\", null);\n\t\tcheckType(\"I\", INT);\n\t\tcheckType(\"[I\", array(INT));\n\t\tcheckType(\"Ljava/lang/Object;\", OBJECT);\n\t\tcheckType(\"[Ljava/lang/Object;\", array(OBJECT));\n\t\tcheckType(\"[[I\", array(array(INT)));\n\t}\n\n\tprivate static void checkType(String str, ArgType type) {\n\t\tassertThat(new SignatureParser(str).consumeType()).isEqualTo(type);\n\t}\n\n\t@Test\n\tpublic void testGenerics() {\n\t\tcheckType(\"TD;\", genericType(\"D\"));\n\t\tcheckType(\"La<TV;Lb;>;\", generic(\"La;\", genericType(\"V\"), object(\"b\")));\n\t\tcheckType(\"La<Lb<Lc;>;>;\", generic(\"La;\", generic(\"Lb;\", object(\"Lc;\"))));\n\t\tcheckType(\"La/b/C<Ld/E<Lf/G;>;>;\", generic(\"La/b/C;\", generic(\"Ld/E;\", object(\"Lf/G;\"))));\n\t\tcheckType(\"La<TD;>.c;\", outerGeneric(generic(\"La;\", genericType(\"D\")), ArgType.object(\"c\")));\n\t\tcheckType(\"La<TD;>.c/d;\", outerGeneric(generic(\"La;\", genericType(\"D\")), ArgType.object(\"c.d\")));\n\t\tcheckType(\"La<Lb;>.c<TV;>;\", outerGeneric(generic(\"La;\", object(\"Lb;\")), ArgType.generic(\"c\", genericType(\"V\"))));\n\t}\n\n\t@Test\n\tpublic void testInnerGeneric() {\n\t\tString signature = \"La<TV;>.LinkedHashIterator<Lb$c<Ls;TV;>;>;\";\n\t\tString objectStr = new SignatureParser(signature).consumeType().getObject();\n\t\tassertThat(objectStr).isEqualTo(\"a$LinkedHashIterator\");\n\t}\n\n\t@Test\n\tpublic void testNestedInnerGeneric() {\n\t\tString signature = \"La<TV;>.I.X;\";\n\t\tArgType result = new SignatureParser(signature).consumeType();\n\t\tassertThat(result.getObject()).isEqualTo(\"a$I$X\");\n\t\t// nested 'outerGeneric' objects\n\t\tArgType obj = generic(\"La;\", genericType(\"V\"));\n\t\tassertThat(result).isEqualTo(outerGeneric(outerGeneric(obj, object(\"I\")), object(\"X\")));\n\t}\n\n\t@Test\n\tpublic void testNestedInnerGeneric2() {\n\t\t// full name in inner class\n\t\tString signature = \"Lsome/long/pkg/ba<Lsome/pkg/s;>.some/long/pkg/bb<Lsome/pkg/p;Lsome/pkg/n;>;\";\n\t\tArgType result = new SignatureParser(signature).consumeType();\n\t\tSystem.out.println(result);\n\t\tassertThat(result.getObject()).isEqualTo(\"some.long.pkg.ba$some.long.pkg.bb\");\n\t\tArgType baseObj = generic(\"Lsome/long/pkg/ba;\", object(\"Lsome/pkg/s;\"));\n\t\tArgType innerObj = generic(\"Lsome/long/pkg/bb;\", object(\"Lsome/pkg/p;\"), object(\"Lsome/pkg/n;\"));\n\t\tArgType obj = outerGeneric(baseObj, innerObj);\n\t\tassertThat(result).isEqualTo(obj);\n\t}\n\n\t@Test\n\tpublic void testWildcards() {\n\t\tcheckWildcards(\"*\", wildcard());\n\t\tcheckWildcards(\"+Lb;\", wildcard(object(\"b\"), WildcardBound.EXTENDS));\n\t\tcheckWildcards(\"-Lb;\", wildcard(object(\"b\"), WildcardBound.SUPER));\n\t\tcheckWildcards(\"+TV;\", wildcard(genericType(\"V\"), WildcardBound.EXTENDS));\n\t\tcheckWildcards(\"-TV;\", wildcard(genericType(\"V\"), WildcardBound.SUPER));\n\n\t\tcheckWildcards(\"**\", wildcard(), wildcard());\n\t\tcheckWildcards(\"*Lb;\", wildcard(), object(\"b\"));\n\t\tcheckWildcards(\"*TV;\", wildcard(), genericType(\"V\"));\n\t\tcheckWildcards(\"TV;*\", genericType(\"V\"), wildcard());\n\t\tcheckWildcards(\"Lb;*\", object(\"b\"), wildcard());\n\n\t\tcheckWildcards(\"***\", wildcard(), wildcard(), wildcard());\n\t\tcheckWildcards(\"*Lb;*\", wildcard(), object(\"b\"), wildcard());\n\t}\n\n\tprivate static void checkWildcards(String w, ArgType... types) {\n\t\tArgType parsedType = new SignatureParser(\"La<\" + w + \">;\").consumeType();\n\t\tArgType expectedType = generic(\"La;\", types);\n\t\tassertThat(parsedType).isEqualTo(expectedType);\n\t}\n\n\t@Test\n\tpublic void testGenericMap() {\n\t\tcheckGenerics(\"\");\n\t\tcheckGenerics(\"<T:Ljava/lang/Object;>\", \"T\", emptyList());\n\t\tcheckGenerics(\"<K:Ljava/lang/Object;LongType:Ljava/lang/Object;>\", \"K\", emptyList(), \"LongType\", emptyList());\n\t\tcheckGenerics(\"<ResultT:Ljava/lang/Exception;:Ljava/lang/Object;>\", \"ResultT\", singletonList(object(\"java.lang.Exception\")));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static void checkGenerics(String g, Object... objs) {\n\t\tList<ArgType> genericsList = new SignatureParser(g).consumeGenericTypeParameters();\n\t\tList<ArgType> expectedList = new ArrayList<>();\n\t\tfor (int i = 0; i < objs.length; i += 2) {\n\t\t\tString typeVar = (String) objs[i];\n\t\t\tList<ArgType> list = (List<ArgType>) objs[i + 1];\n\t\t\texpectedList.add(ArgType.genericType(typeVar, list));\n\t\t}\n\t\tassertThat(genericsList).isEqualTo(expectedList);\n\t}\n\n\t@Test\n\tpublic void testMethodArgs() {\n\t\tList<ArgType> argTypes = new SignatureParser(\"(Ljava/util/List<*>;)V\").consumeMethodArgs(1);\n\n\t\tassertThat(argTypes).hasSize(1);\n\t\tassertThat(argTypes.get(0)).isEqualTo(generic(\"Ljava/util/List;\", wildcard()));\n\t}\n\n\t@Test\n\tpublic void testMethodArgs2() {\n\t\tList<ArgType> argTypes = new SignatureParser(\"(La/b/C<TT;>.d/E;)V\").consumeMethodArgs(1);\n\n\t\tassertThat(argTypes).hasSize(1);\n\t\tArgType argType = argTypes.get(0);\n\t\tassertThat(argType.getObject().indexOf('/')).isEqualTo(-1);\n\t\tassertThat(argType).isEqualTo(outerGeneric(generic(\"La/b/C;\", genericType(\"T\")), object(\"d.E\")));\n\t}\n\n\t@Test\n\tpublic void testBadGenericMap() {\n\t\tassertThatExceptionOfType(JadxRuntimeException.class)\n\t\t\t\t.isThrownBy(() -> new SignatureParser(\"<A:Ljava/lang/Object;B\").consumeGenericTypeParameters());\n\t}\n\n\t@Test\n\tpublic void testBadArgs() {\n\t\tassertThatExceptionOfType(JadxRuntimeException.class)\n\t\t\t\t.isThrownBy(() -> new SignatureParser(\"(TCONTENT)Lpkg/Cls;\").consumeMethodArgs(1));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/functional/StringUtilsTest.java",
    "content": "package jadx.tests.functional;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.JadxArgs;\nimport jadx.core.utils.StringUtils;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\nclass StringUtilsTest {\n\n\tprivate StringUtils stringUtils;\n\n\t@Test\n\t@SuppressWarnings(\"AvoidEscapedUnicodeCharacters\")\n\tpublic void testStringUnescape() {\n\t\tJadxArgs args = new JadxArgs();\n\t\targs.setEscapeUnicode(true);\n\t\tstringUtils = new StringUtils(args);\n\n\t\tcheckStringUnescape(\"\", \"\");\n\t\tcheckStringUnescape(\"'\", \"'\");\n\t\tcheckStringUnescape(\"a\", \"a\");\n\t\tcheckStringUnescape(\"\\n\", \"\\\\n\");\n\t\tcheckStringUnescape(\"\\t\", \"\\\\t\");\n\t\tcheckStringUnescape(\"\\r\", \"\\\\r\");\n\t\tcheckStringUnescape(\"\\b\", \"\\\\b\");\n\t\tcheckStringUnescape(\"\\f\", \"\\\\f\");\n\t\tcheckStringUnescape(\"\\\\\", \"\\\\\\\\\");\n\t\tcheckStringUnescape(\"\\\"\", \"\\\\\\\"\");\n\t\tcheckStringUnescape(\"\\u1234\", \"\\\\u1234\");\n\t}\n\n\tprivate void checkStringUnescape(String input, String result) {\n\t\tassertThat(stringUtils.unescapeString(input)).isEqualTo('\"' + result + '\"');\n\t}\n\n\t@Test\n\tpublic void testCharUnescape() {\n\t\tstringUtils = new StringUtils(new JadxArgs());\n\n\t\tcheckCharUnescape('a', \"a\");\n\t\tcheckCharUnescape(' ', \" \");\n\t\tcheckCharUnescape('\\n', \"\\\\n\");\n\t\tcheckCharUnescape('\\'', \"\\\\'\");\n\n\t\tassertThat(stringUtils.unescapeChar('\\0')).isEqualTo(\"0\");\n\t}\n\n\tprivate void checkCharUnescape(char input, String result) {\n\t\tassertThat(stringUtils.unescapeChar(input)).isEqualTo('\\'' + result + '\\'');\n\t}\n\n\t@Test\n\tpublic void testResStrValueEscape() {\n\t\tcheckResStrValueEscape(\"line\\nnew line\", \"line\\\\nnew line\");\n\t\tcheckResStrValueEscape(\"can't\", \"can\\\\'t\");\n\t\tcheckResStrValueEscape(\"quote\\\"end\", \"quote\\\\\\\"end\");\n\t}\n\n\tprivate void checkResStrValueEscape(String input, String result) {\n\t\tassertThat(StringUtils.escapeResStrValue(input)).isEqualTo(result);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/functional/TemplateFileTest.java",
    "content": "package jadx.tests.functional;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.export.TemplateFile;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TemplateFileTest {\n\n\t@Test\n\tpublic void testBuildGradle() throws Exception {\n\t\tTemplateFile tmpl = TemplateFile.fromResources(\"/export/android/app.build.gradle.tmpl\");\n\t\ttmpl.add(\"applicationId\", \"SOME_ID\");\n\t\ttmpl.add(\"minSdkVersion\", 1);\n\t\ttmpl.add(\"targetSdkVersion\", 2);\n\t\ttmpl.add(\"versionCode\", 3);\n\t\ttmpl.add(\"versionName\", \"1.2.3\");\n\t\ttmpl.add(\"additionalOptions\", \"useLibrary 'org.apache.http.legacy'\");\n\t\ttmpl.add(\"compileSdkVersion\", 4);\n\t\tString res = tmpl.build();\n\t\tSystem.out.println(res);\n\n\t\tassertThat(res).contains(\"applicationId 'SOME_ID'\");\n\t\tassertThat(res).contains(\"targetSdkVersion 2\");\n\t\tassertThat(res).contains(\"versionCode 3\");\n\t\tassertThat(res).contains(\"versionName \\\"1.2.3\\\"\");\n\t\tassertThat(res).contains(\"compileSdkVersion 4\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/functional/TestIfCondition.java",
    "content": "package jadx.tests.functional;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.instructions.IfNode;\nimport jadx.core.dex.instructions.IfOp;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.LiteralArg;\nimport jadx.core.dex.regions.conditions.Compare;\nimport jadx.core.dex.regions.conditions.IfCondition;\n\nimport static jadx.core.dex.regions.conditions.IfCondition.Mode;\nimport static jadx.core.dex.regions.conditions.IfCondition.Mode.AND;\nimport static jadx.core.dex.regions.conditions.IfCondition.Mode.COMPARE;\nimport static jadx.core.dex.regions.conditions.IfCondition.Mode.NOT;\nimport static jadx.core.dex.regions.conditions.IfCondition.Mode.OR;\nimport static jadx.core.dex.regions.conditions.IfCondition.merge;\nimport static jadx.core.dex.regions.conditions.IfCondition.not;\nimport static jadx.core.dex.regions.conditions.IfCondition.simplify;\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestIfCondition {\n\n\tprivate static IfCondition makeCondition(IfOp op, InsnArg a, InsnArg b) {\n\t\treturn IfCondition.fromIfNode(new IfNode(op, -1, a, b));\n\t}\n\n\tprivate static IfCondition makeSimpleCondition() {\n\t\treturn makeCondition(IfOp.EQ, mockArg(), LiteralArg.litTrue());\n\t}\n\n\tprivate static IfCondition makeNegCondition() {\n\t\treturn makeCondition(IfOp.NE, mockArg(), LiteralArg.litTrue());\n\t}\n\n\tprivate static InsnArg mockArg() {\n\t\treturn InsnArg.reg(0, ArgType.INT);\n\t}\n\n\t@Test\n\tpublic void testNormalize() {\n\t\t// 'a != false' => 'a == true'\n\t\tInsnArg a = mockArg();\n\t\tIfCondition c = makeCondition(IfOp.NE, a, LiteralArg.litFalse());\n\t\tIfCondition simp = simplify(c);\n\n\t\tassertThat(simp.getMode()).isEqualTo(COMPARE);\n\t\tCompare compare = simp.getCompare();\n\t\tassertThat(compare.getA()).isEqualTo(a);\n\t\tassertThat(compare.getB()).isEqualTo(LiteralArg.litTrue());\n\t}\n\n\t@Test\n\tpublic void testMerge() {\n\t\tIfCondition a = makeSimpleCondition();\n\t\tIfCondition b = makeSimpleCondition();\n\t\tIfCondition c = merge(Mode.OR, a, b);\n\n\t\tassertThat(c.getMode()).isEqualTo(OR);\n\t\tassertThat(c.first()).isEqualTo(a);\n\t\tassertThat(c.second()).isEqualTo(b);\n\t}\n\n\t@Test\n\tpublic void testSimplifyNot() {\n\t\t// !(!a) => a\n\t\tIfCondition a = not(not(makeSimpleCondition()));\n\t\tassertThat(simplify(a)).isEqualTo(a);\n\t}\n\n\t@Test\n\tpublic void testSimplifyNot2() {\n\t\t// !(!a) => a\n\t\tIfCondition a = not(makeNegCondition());\n\t\tassertThat(simplify(a)).isEqualTo(a);\n\t}\n\n\t@Test\n\tpublic void testSimplify() {\n\t\t// '!(!a || !b)' => 'a && b'\n\t\tIfCondition a = makeSimpleCondition();\n\t\tIfCondition b = makeSimpleCondition();\n\t\tIfCondition c = not(merge(Mode.OR, not(a), not(b)));\n\t\tIfCondition simp = simplify(c);\n\n\t\tassertThat(simp.getMode()).isEqualTo(AND);\n\t\tassertThat(simp.first()).isEqualTo(a);\n\t\tassertThat(simp.second()).isEqualTo(b);\n\t}\n\n\t@Test\n\tpublic void testSimplify2() {\n\t\t// '(!a || !b) && !c' => '!((a && b) || c)'\n\t\tIfCondition a = makeSimpleCondition();\n\t\tIfCondition b = makeSimpleCondition();\n\t\tIfCondition c = makeSimpleCondition();\n\t\tIfCondition cond = merge(Mode.AND, merge(Mode.OR, not(a), not(b)), not(c));\n\t\tIfCondition simp = simplify(cond);\n\n\t\tassertThat(simp.getMode()).isEqualTo(NOT);\n\t\tIfCondition f = simp.first();\n\t\tassertThat(f.getMode()).isEqualTo(OR);\n\t\tassertThat(f.first().getMode()).isEqualTo(AND);\n\t\tassertThat(f.first().first()).isEqualTo(a);\n\t\tassertThat(f.first().second()).isEqualTo(b);\n\t\tassertThat(f.second()).isEqualTo(c);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/android/TestRFieldAccess.java",
    "content": "package jadx.tests.integration.android;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"TypeName\")\npublic class TestRFieldAccess extends IntegrationTest {\n\n\tpublic static class R {\n\t\tpublic static final class id {\n\t\t\tpublic static final int BUTTON_01 = 2131230730;\n\t\t}\n\t}\n\n\tpublic static class TestR {\n\t\tpublic int test() {\n\t\t\treturn R.id.BUTTON_01;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestRFieldAccess.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"return R.id.BUTTON_01;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/android/TestRFieldRestore.java",
    "content": "package jadx.tests.integration.android;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.api.plugins.input.data.attributes.JadxAttrType.CONSTANT_VALUE;\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestRFieldRestore extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int test() {\n\t\t\treturn 2131230730;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\t// unknown R class\n\t\tdisableCompilation();\n\n\t\tMap<Integer, String> map = new HashMap<>();\n\t\tint buttonConstValue = 2131230730;\n\t\tmap.put(buttonConstValue, \"id.Button\");\n\t\tsetResMap(map);\n\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls).code()\n\t\t\t\t.containsOne(\"return R.id.Button;\")\n\t\t\t\t.doesNotContain(\"import R;\");\n\n\t\t// check 'R' class\n\t\tClassNode rCls = cls.root().searchClassByFullAlias(\"R\");\n\t\tassertThat(rCls).isNotNull();\n\n\t\t// check inner 'id' class\n\t\tList<ClassNode> innerClasses = rCls.getInnerClasses();\n\t\tassertThat(innerClasses).hasSize(1);\n\t\tClassNode idCls = innerClasses.get(0);\n\t\tassertThat(idCls.getShortName()).isEqualTo(\"id\");\n\n\t\t// check 'Button' field\n\t\tFieldNode buttonField = idCls.searchFieldByName(\"Button\");\n\t\tassertThat(buttonField).isNotNull();\n\t\tEncodedValue constVal = buttonField.get(CONSTANT_VALUE);\n\t\tInteger buttonValue = (Integer) constVal.getValue();\n\t\tassertThat(buttonValue).isEqualTo(buttonConstValue);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/android/TestRFieldRestore2.java",
    "content": "package jadx.tests.integration.android;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestRFieldRestore2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static class R {\n\t\t}\n\n\t\tpublic int test() {\n\t\t\treturn 2131230730;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tMap<Integer, String> map = new HashMap<>();\n\t\tmap.put(2131230730, \"id.Button\");\n\t\tsetResMap(map);\n\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"R.id.Button;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/android/TestRFieldRestore3.java",
    "content": "package jadx.tests.integration.android;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestRFieldRestore3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\t@T(2131230730)\n\t\tpublic static class A {\n\t\t\t@F(2131230730)\n\t\t\tprivate int f;\n\n\t\t\t@M(bind = 2137373737)\n\t\t\tprivate void mth() {\n\t\t\t}\n\n\t\t\t@T(2137373737)\n\t\t\tprivate class D {\n\t\t\t}\n\t\t}\n\n\t\t@Retention(RetentionPolicy.RUNTIME)\n\t\t@Target(ElementType.TYPE)\n\t\t@interface T {\n\t\t\tint value();\n\t\t}\n\n\t\t@Retention(RetentionPolicy.RUNTIME)\n\t\t@Target({ ElementType.FIELD })\n\t\t@interface F {\n\t\t\tint value();\n\t\t}\n\n\t\t@Retention(RetentionPolicy.RUNTIME)\n\t\t@Target({ ElementType.METHOD })\n\t\t@interface M {\n\t\t\tint bind();\n\t\t}\n\n\t\tpublic static class R {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tMap<Integer, String> map = new HashMap<>();\n\t\tmap.put(2131230730, \"id.Button\");\n\t\tmap.put(2137373737, \"id.MyId\");\n\t\tsetResMap(map);\n\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOnlyOnce(\"@T(R.id.Button)\")\n\t\t\t\t.containsOnlyOnce(\"@T(R.id.MyId)\")\n\t\t\t\t.containsOnlyOnce(\"@F(R.id.Button)\")\n\t\t\t\t.containsOnlyOnce(\"@M(bind = R.id.MyId)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/android/TestResConstReplace.java",
    "content": "package jadx.tests.integration.android;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestResConstReplace extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int test() {\n\t\t\treturn 0x0101013f; // android.R.attr.minWidth\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"import android.R;\")\n\t\t\t\t.containsOne(\"return R.attr.minWidth;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/android/TestResConstReplace2.java",
    "content": "package jadx.tests.integration.android;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestResConstReplace2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int test(int i) {\n\t\t\tswitch (i) {\n\t\t\t\tcase 0x0101013f: // android.R.attr.minWidth\n\t\t\t\t\treturn 1;\n\t\t\t\tcase 0x01010140: // android.R.attr.minHeight\n\t\t\t\t\treturn 2;\n\t\t\t\tdefault:\n\t\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"import android.R;\")\n\t\t\t\t.containsOne(\"case R.attr.minWidth:\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/android/TestResConstReplace3.java",
    "content": "package jadx.tests.integration.android;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestResConstReplace3 extends IntegrationTest {\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\tpublic @interface UsesAndroidResource {\n\t\tint value() default 0;\n\t}\n\n\t@UsesAndroidResource(17039370 /* android.R.string.ok */)\n\tpublic static class TestCls {\n\t\tpublic void test(@UsesAndroidResource(17039370 /* android.R.string.ok */) int i) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"import android.R;\")\n\t\t\t\t.countString(2, \"@TestResConstReplace3.UsesAndroidResource(R.string.ok)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/annotations/TestAnnotations.java",
    "content": "package jadx.tests.integration.annotations;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnnotations extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate @interface A {\n\t\t\tint a();\n\t\t}\n\n\t\t@A(a = -1)\n\t\tpublic void methodA1() {\n\t\t}\n\n\t\t@A(a = -253)\n\t\tpublic void methodA2() {\n\t\t}\n\n\t\t@A(a = -11253)\n\t\tpublic void methodA3() {\n\t\t}\n\n\t\tprivate @interface V {\n\t\t\tboolean value();\n\t\t}\n\n\t\t@V(false)\n\t\tpublic void methodV() {\n\t\t}\n\n\t\tprivate @interface D {\n\t\t\tfloat value() default 1.1f;\n\t\t}\n\n\t\t@D\n\t\tpublic void methodD() {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class)).code()\n\t\t\t\t.doesNotContain(\"@A(a = 255)\")\n\t\t\t\t.containsOne(\"@A(a = -1)\")\n\t\t\t\t.containsOne(\"@A(a = -253)\")\n\t\t\t\t.containsOne(\"@A(a = -11253)\")\n\t\t\t\t.containsOne(\"@V(false)\")\n\t\t\t\t.doesNotContain(\"@D()\")\n\t\t\t\t.containsOne(\"@D\")\n\t\t\t\t.containsOne(\"int a();\")\n\t\t\t\t.containsOne(\"float value() default 1.1f;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/annotations/TestAnnotations2.java",
    "content": "package jadx.tests.integration.annotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestAnnotations2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\t@Target(ElementType.TYPE)\n\t\t@Retention(RetentionPolicy.RUNTIME)\n\t\tpublic @interface A {\n\t\t\tint i();\n\n\t\t\tfloat f();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"@Target({ElementType.TYPE})\")\n\t\t\t\t.contains(\"@Retention(RetentionPolicy.RUNTIME)\")\n\t\t\t\t.contains(\"public @interface A {\")\n\t\t\t\t.contains(\"float f();\")\n\t\t\t\t.contains(\"int i();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/annotations/TestAnnotationsMix.java",
    "content": "package jadx.tests.integration.annotations;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static java.lang.Thread.State.TERMINATED;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestAnnotationsMix extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic boolean test() throws Exception {\n\t\t\tClass<?> cls = TestCls.class;\n\t\t\tnew Thread();\n\n\t\t\tMethod err = cls.getMethod(\"error\");\n\t\t\tassertThat(err.getExceptionTypes().length > 0).isTrue();\n\t\t\tassertThat(err.getExceptionTypes()[0]).isSameAs(Exception.class);\n\n\t\t\tMethod d = cls.getMethod(\"depr\", String[].class);\n\t\t\tassertThat(d.getAnnotations().length > 0).isTrue();\n\t\t\tassertThat(d.getAnnotations()[0].annotationType()).isSameAs(Deprecated.class);\n\n\t\t\tMethod ma = cls.getMethod(\"test\", String[].class);\n\t\t\tassertThat(ma.getAnnotations().length > 0).isTrue();\n\t\t\tMyAnnotation a = (MyAnnotation) ma.getAnnotations()[0];\n\t\t\tassertThat(a.num()).isEqualTo(7);\n\t\t\tassertThat(a.state()).isSameAs(TERMINATED);\n\t\t\treturn true;\n\t\t}\n\n\t\t@Deprecated\n\t\tpublic int a;\n\n\t\tpublic void error() throws Exception {\n\t\t\tthrow new Exception(\"error\");\n\t\t}\n\n\t\t@Deprecated\n\t\tpublic static Object depr(String[] a) {\n\t\t\treturn Arrays.asList(a);\n\t\t}\n\n\t\t@MyAnnotation(\n\t\t\t\tname = \"b\",\n\t\t\t\tnum = 7,\n\t\t\t\tcls = Exception.class,\n\t\t\t\tdoubles = { 0.0, 1.1 },\n\t\t\t\tvalue = 9.87f,\n\t\t\t\tsimple = @SimpleAnnotation(false)\n\t\t)\n\t\tpublic static Object test(String[] a) {\n\t\t\treturn Arrays.asList(a);\n\t\t}\n\n\t\t@Documented\n\t\t@Retention(RetentionPolicy.RUNTIME)\n\t\t@Target(ElementType.METHOD)\n\t\tpublic @interface MyAnnotation {\n\t\t\tString name() default \"a\";\n\n\t\t\tString str() default \"str\";\n\n\t\t\tint num();\n\n\t\t\tfloat value();\n\n\t\t\tdouble[] doubles();\n\n\t\t\tClass<?> cls();\n\n\t\t\tSimpleAnnotation simple();\n\n\t\t\tThread.State state() default Thread.State.TERMINATED;\n\t\t}\n\n\t\tpublic @interface SimpleAnnotation {\n\t\t\tboolean value();\n\t\t}\n\n\t\tpublic void check() throws Exception {\n\t\t\ttest();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\t// useDexInput();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"int i = false;\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n\n\t@Test\n\tpublic void testDeclaration() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"Thread thread = new Thread();\")\n\t\t\t\t.contains(\"new Thread();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/annotations/TestAnnotationsRename.java",
    "content": "package jadx.tests.integration.annotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnnotationsRename extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\t@Target(ElementType.METHOD)\n\t\t@Retention(RetentionPolicy.RUNTIME)\n\t\tpublic @interface A {\n\t\t\tint x();\n\t\t}\n\n\t\t@A(x = 5)\n\t\tvoid test() {\n\t\t}\n\n\t\tpublic void check() throws NoSuchMethodException {\n\t\t\tMethod test = TestCls.class.getDeclaredMethod(\"test\");\n\t\t\tA annotation = test.getAnnotation(A.class);\n\t\t\tassertThat(annotation.x()).isEqualTo(5);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tenableDeobfuscation();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"public @interface \")\n\t\t\t\t.doesNotContain(\"(x = 5)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/annotations/TestAnnotationsRenameDef.java",
    "content": "package jadx.tests.integration.annotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestAnnotationsRenameDef extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\t@Target(ElementType.METHOD)\n\t\t@Retention(RetentionPolicy.RUNTIME)\n\t\tpublic @interface A {\n\t\t\tint value();\n\t\t}\n\n\t\t@A(5)\n\t\tvoid test() {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tenableDeobfuscation();\n\t\t// force rename 'value' method\n\t\targs.setDeobfuscationMinLength(20);\n\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"public @interface \")\n\t\t\t\t.doesNotContain(\"int value();\")\n\t\t\t\t.doesNotContain(\"(5)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/annotations/TestAnnotationsUsage.java",
    "content": "package jadx.tests.integration.annotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnnotationsUsage extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\t@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })\n\t\t@Retention(RetentionPolicy.RUNTIME)\n\t\tpublic @interface A {\n\t\t\tClass<?> c();\n\t\t}\n\n\t\t@A(c = TestCls.class)\n\t\tpublic static class B {\n\t\t}\n\n\t\tpublic static class C {\n\t\t\t@A(c = B.class)\n\t\t\tpublic String field;\n\t\t}\n\n\t\t@A(c = B.class)\n\t\tvoid test() {\n\t\t}\n\n\t\tvoid test2(@A(c = B.class) Integer value) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tClassNode annCls = searchCls(cls.getInnerClasses(), \"A\");\n\t\tClassNode bCls = searchCls(cls.getInnerClasses(), \"B\");\n\t\tClassNode cCls = searchCls(cls.getInnerClasses(), \"C\");\n\t\tMethodNode testMth = getMethod(cls, \"test\");\n\t\tMethodNode testMth2 = getMethod(cls, \"test2\");\n\n\t\tassertThat(annCls.getUseIn()).contains(cls, bCls, cCls);\n\t\tassertThat(annCls.getUseInMth()).contains(testMth, testMth2);\n\n\t\tassertThat(bCls.getUseIn()).contains(cCls);\n\t\tassertThat(bCls.getUseInMth()).contains(testMth, testMth2);\n\n\t\tassertThat(cls)\n\t\t\t\t.code()\n\t\t\t\t.countString(3, \"@A(c = B.class)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/annotations/TestParamAnnotations.java",
    "content": "package jadx.tests.integration.annotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestParamAnnotations extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\t@Target(ElementType.PARAMETER)\n\t\t@Retention(RetentionPolicy.RUNTIME)\n\t\tpublic static @interface A {\n\t\t\tint i() default 7;\n\t\t}\n\n\t\tvoid test1(@A int i) {\n\t\t}\n\n\t\tvoid test2(int i, @A int j) {\n\t\t}\n\n\t\tvoid test3(@A(i = 5) int i) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"void test1(@A int i) {\")\n\t\t\t\t.contains(\"void test2(int i, @A int j) {\")\n\t\t\t\t.contains(\"void test3(@A(i = 5) int i) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arith/TestArith.java",
    "content": "package jadx.tests.integration.arith;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArith extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static final int F = 7;\n\n\t\tpublic int test(int a) {\n\t\t\ta += 2;\n\t\t\tuse(a);\n\t\t\treturn a;\n\t\t}\n\n\t\tpublic int test2(int a) {\n\t\t\ta++;\n\t\t\tuse(a);\n\t\t\treturn a;\n\t\t}\n\n\t\tprivate static void use(int i) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code();\n\t}\n\n\t@Test\n\t@NotYetImplemented\n\tpublic void test2() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"a += 2;\")\n\t\t\t\t.contains(\"a++;\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code();\n\t}\n\n\t@Test\n\t@NotYetImplemented\n\tpublic void testNoDebug2() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"i += 2;\")\n\t\t\t\t.contains(\"i++;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arith/TestArith2.java",
    "content": "package jadx.tests.integration.arith;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestArith2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic int test1(int a) {\n\t\t\treturn (a + 2) * 3;\n\t\t}\n\n\t\tpublic int test2(int a, int b, int c) {\n\t\t\treturn a + b + c;\n\t\t}\n\n\t\tpublic boolean test3(boolean a, boolean b, boolean c) {\n\t\t\treturn a | b | c;\n\t\t}\n\n\t\tpublic boolean test4(boolean a, boolean b, boolean c) {\n\t\t\treturn a & b & c;\n\t\t}\n\n\t\tpublic int substract(int a, int b, int c) {\n\t\t\treturn a - (b - c);\n\t\t}\n\n\t\tpublic int divide(int a, int b, int c) {\n\t\t\treturn a / (b / c);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return (a + 2) * 3;\")\n\t\t\t\t.doesNotContain(\"a + 2 * 3\")\n\t\t\t\t.contains(\"return a + b + c;\")\n\t\t\t\t.doesNotContain(\"return (a + b) + c;\")\n\t\t\t\t.contains(\"return a | b | c;\")\n\t\t\t\t.doesNotContain(\"return (a | b) | c;\")\n\t\t\t\t.contains(\"return a & b & c;\")\n\t\t\t\t.doesNotContain(\"return (a & b) & c;\")\n\t\t\t\t.contains(\"return a - (b - c);\")\n\t\t\t\t.doesNotContain(\"return a - b - c;\")\n\t\t\t\t.contains(\"return a / (b / c);\")\n\t\t\t\t.doesNotContain(\"return a / b / c;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arith/TestArith3.java",
    "content": "package jadx.tests.integration.arith;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestArith3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int vp;\n\n\t\tpublic void test(byte[] buffer) {\n\t\t\tint n = ((buffer[3] & 255) + 4) + ((buffer[2] & 15) << 8);\n\t\t\twhile (n + 4 < buffer.length) {\n\t\t\t\tint p = (buffer[n + 2] & 255) + ((buffer[n + 1] & 31) << 8);\n\t\t\t\tint len = (buffer[n + 4] & 255) + ((buffer[n + 3] & 15) << 8);\n\t\t\t\tint c = buffer[n] & 255;\n\t\t\t\tswitch (c) {\n\t\t\t\t\tcase 27:\n\t\t\t\t\t\tthis.vp = p;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tn += len + 5;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"while (n + 4 < buffer.length) {\")\n\t\t\t\t.containsOne(indent() + \"n += len + 5;\")\n\t\t\t\t.doesNotContain(\"; n += len + 5) {\")\n\t\t\t\t.doesNotContain(\"default:\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"while (\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arith/TestArith4.java",
    "content": "package jadx.tests.integration.arith;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArith4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static byte test(byte b) {\n\t\t\tint k = b & 7;\n\t\t\treturn (byte) (((b & 255) >>> (8 - k)) | (b << k));\n\t\t}\n\n\t\tpublic static int test2(String str) {\n\t\t\tint k = 'a' | str.charAt(0);\n\t\t\treturn (1 - k) & (1 + k);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"int k = b & 7;\")\n\t\t\t\t.containsOne(\"& 255\")\n\t\t\t\t.containsOneOf(\"return (1 - k) & (1 + k);\", \"return (1 - k) & (k + 1);\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"& 255\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arith/TestArithConst.java",
    "content": "package jadx.tests.integration.arith;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArithConst extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNodeFromSmaliWithPath(\"arith\", \"TestArithConst\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return i + CONST_INT;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arith/TestArithNot.java",
    "content": "package jadx.tests.integration.arith;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArithNot extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t  Smali Code equivalent:\n\t\tpublic static class TestCls {\n\t\t\tpublic int test1(int a) {\n\t\t\t\treturn ~a;\n\t\t\t}\n\n\t\t\tpublic long test2(long b) {\n\t\t\t\treturn ~b;\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliWithPath(\"arith\", \"TestArithNot\"))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return ~a;\")\n\t\t\t\t.contains(\"return ~b;\")\n\t\t\t\t.doesNotContain(\"^\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arith/TestFieldIncrement.java",
    "content": "package jadx.tests.integration.arith;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestFieldIncrement extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int instanceField = 1;\n\t\tpublic static int staticField = 1;\n\t\tpublic static String result = \"\";\n\n\t\tpublic void method() {\n\t\t\tinstanceField++;\n\t\t}\n\n\t\tpublic void method2() {\n\t\t\tstaticField--;\n\t\t}\n\n\t\tpublic void method3(String s) {\n\t\t\tresult += s + '_';\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"instanceField++;\")\n\t\t\t\t.contains(\"staticField--;\")\n\t\t\t\t.contains(\"result += s + '_';\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arith/TestFieldIncrement2.java",
    "content": "package jadx.tests.integration.arith;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestFieldIncrement2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static class A {\n\t\t\tint f = 5;\n\t\t}\n\n\t\tpublic A a;\n\n\t\tpublic void test1(int n) {\n\t\t\tthis.a.f = this.a.f + n;\n\t\t}\n\n\t\tpublic void test2(int n) {\n\t\t\tthis.a.f *= n;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"this.a.f += n;\")\n\t\t\t\t.contains(\"this.a.f *= n;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arith/TestFieldIncrement3.java",
    "content": "package jadx.tests.integration.arith;\n\nimport java.util.Random;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestFieldIncrement3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tstatic int tileX;\n\t\tstatic int tileY;\n\t\tstatic Vector2 targetPos = new Vector2();\n\t\tstatic Vector2 directVect = new Vector2();\n\t\tstatic Vector2 newPos = new Vector2();\n\n\t\tpublic static void test() {\n\t\t\tRandom rd = new Random();\n\t\t\tint direction = rd.nextInt(7);\n\t\t\tswitch (direction) {\n\t\t\t\tcase 0:\n\t\t\t\t\ttargetPos.x = ((tileX + 1) * 55) + 55;\n\t\t\t\t\ttargetPos.y = ((tileY + 1) * 35) + 35;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\ttargetPos.x = ((tileX + 1) * 55) + 55;\n\t\t\t\t\ttargetPos.y = ((tileY - 1) * 35) + 35;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\ttargetPos.x = ((tileX - 1) * 55) + 55;\n\t\t\t\t\ttargetPos.y = ((tileY - 1) * 35) + 35;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 6:\n\t\t\t\t\ttargetPos.x = ((tileX - 1) * 55) + 55;\n\t\t\t\t\ttargetPos.y = ((tileY + 1) * 35) + 35;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdirectVect.x = targetPos.x - newPos.x;\n\t\t\tdirectVect.y = targetPos.y - newPos.y;\n\n\t\t\tfloat hPos = (float) Math.sqrt((directVect.x * directVect.x) + (directVect.y * directVect.y));\n\t\t\tdirectVect.x /= hPos;\n\t\t\tdirectVect.y /= hPos;\n\t\t}\n\n\t\tstatic class Vector2 {\n\t\t\tpublic float x;\n\t\t\tpublic float y;\n\n\t\t\tpublic Vector2() {\n\t\t\t\tthis.x = 0.0f;\n\t\t\t\tthis.y = 0.0f;\n\t\t\t}\n\n\t\t\tpublic boolean equals(Vector2 other) {\n\t\t\t\treturn (this.x == other.x && this.y == other.y);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"directVect.x = targetPos.x - newPos.x;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arith/TestNumbersFormat.java",
    "content": "package jadx.tests.integration.arith;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.args.IntegerFormat;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestNumbersFormat extends IntegrationTest {\n\n\t@SuppressWarnings({ \"FieldCanBeLocal\", \"UnusedAssignment\", \"unused\" })\n\tpublic static class TestCls {\n\t\tprivate Object obj;\n\n\t\tpublic void test() {\n\t\t\tobj = new byte[] { 0, -1, -0xA, (byte) 0xff, Byte.MIN_VALUE, Byte.MAX_VALUE };\n\t\t\tobj = new short[] { 0, -1, -0xA, (short) 0xffff, Short.MIN_VALUE, Short.MAX_VALUE };\n\t\t\tobj = new int[] { 0, -1, -0xA, 0xffff_ffff, Integer.MIN_VALUE, Integer.MAX_VALUE };\n\t\t\tobj = new long[] { 0, -1, -0xA, 0xffff_ffff_ffff_ffffL, Long.MIN_VALUE, Long.MAX_VALUE };\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tgetArgs().setIntegerFormat(IntegerFormat.AUTO);\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"new byte[]{0, -1, -10, -1, -128, 127}\")\n\t\t\t\t.containsOne(\"new short[]{0, -1, -10, -1, Short.MIN_VALUE, Short.MAX_VALUE}\")\n\t\t\t\t.containsOne(\"new int[]{0, -1, -10, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}\")\n\t\t\t\t.containsOne(\"new long[]{0, -1, -10, -1, Long.MIN_VALUE, Long.MAX_VALUE}\");\n\t}\n\n\t@Test\n\tpublic void testDecimalFormat() {\n\t\tgetArgs().setIntegerFormat(IntegerFormat.DECIMAL);\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"new byte[]{0, -1, -10, -1, -128, 127}\")\n\t\t\t\t.containsOne(\"new short[]{0, -1, -10, -1, -32768, 32767}\")\n\t\t\t\t.containsOne(\"new int[]{0, -1, -10, -1, -2147483648, 2147483647}\")\n\t\t\t\t.containsOne(\"new long[]{0, -1, -10, -1, -9223372036854775808L, 9223372036854775807L}\");\n\t}\n\n\t@Test\n\tpublic void testHexFormat() {\n\t\tgetArgs().setIntegerFormat(IntegerFormat.HEXADECIMAL);\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"new byte[]{0x0, (byte) 0xff, (byte) 0xf6, (byte) 0xff, (byte) 0x80, 0x7f}\")\n\t\t\t\t.containsOne(\"new short[]{0x0, (short) 0xffff, (short) 0xfff6, (short) 0xffff, (short) 0x8000, 0x7fff}\")\n\t\t\t\t.containsOne(\"new int[]{0x0, (int) 0xffffffff, (int) 0xfffffff6, (int) 0xffffffff, (int) 0x80000000, 0x7fffffff}\")\n\t\t\t\t.containsOne(\n\t\t\t\t\t\t\"new long[]{0x0, 0xffffffffffffffffL, 0xfffffffffffffff6L, 0xffffffffffffffffL, 0x8000000000000000L, 0x7fffffffffffffffL}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arith/TestPrimitivesNegate.java",
    "content": "package jadx.tests.integration.arith;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestPrimitivesNegate extends IntegrationTest {\n\n\t@SuppressWarnings(\"UnnecessaryUnaryMinus\")\n\tpublic static class TestCls {\n\t\tpublic double test() {\n\t\t\tdouble[] arr = new double[5];\n\t\t\tarr[0] = -20;\n\t\t\tarr[0] += -79;\n\t\t\treturn arr[0];\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test()).isEqualTo(-99);\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.JAVA8 })\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"dArr[0] = -20.0d;\")\n\t\t\t\t.containsOne(\"dArr[0] = dArr[0] - 79.0d;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arith/TestSpecialValues.java",
    "content": "package jadx.tests.integration.arith;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSpecialValues extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test() {\n\t\t\tshorts(Short.MIN_VALUE, Short.MAX_VALUE);\n\t\t\tints(Integer.MIN_VALUE, Integer.MAX_VALUE);\n\t\t\tlongs(Long.MIN_VALUE, Long.MAX_VALUE);\n\n\t\t\tfloats(Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY,\n\t\t\t\t\tFloat.MIN_VALUE, Float.MAX_VALUE, Float.MIN_NORMAL);\n\n\t\t\tdoubles(Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,\n\t\t\t\t\tDouble.MIN_VALUE, Double.MAX_VALUE, Double.MIN_NORMAL);\n\t\t}\n\n\t\tprivate void shorts(short... v) {\n\t\t}\n\n\t\tprivate void ints(int... v) {\n\t\t}\n\n\t\tprivate void longs(long... v) {\n\t\t}\n\n\t\tprivate void floats(float... v) {\n\t\t}\n\n\t\tprivate void doubles(double... v) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\n\t\t\t\t\t\t\"Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Float.MIN_VALUE, Float.MAX_VALUE, Float.MIN_NORMAL\")\n\t\t\t\t.containsOne(\"Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, \"\n\t\t\t\t\t\t+ \"Double.MIN_VALUE, Double.MAX_VALUE, Double.MIN_NORMAL\")\n\t\t\t\t.containsOne(\"Short.MIN_VALUE, Short.MAX_VALUE\")\n\t\t\t\t.containsOne(\"Integer.MIN_VALUE, Integer.MAX_VALUE\")\n\t\t\t\t.containsOne(\"Long.MIN_VALUE, Long.MAX_VALUE\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arith/TestSpecialValues2.java",
    "content": "package jadx.tests.integration.arith;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSpecialValues2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static int compareUnsigned(final int x, final int y) {\n\t\t\treturn Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"Integer.MIN_VALUE\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arith/TestXor.java",
    "content": "package jadx.tests.integration.arith;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestXor extends SmaliTest {\n\n\t@SuppressWarnings(\"PointlessBooleanExpression\")\n\tpublic static class TestCls {\n\t\tpublic boolean test1() {\n\t\t\treturn test() ^ true;\n\t\t}\n\n\t\tpublic boolean test2(boolean v) {\n\t\t\treturn v ^ true;\n\t\t}\n\n\t\tpublic boolean test() {\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test1()).isFalse();\n\t\t\tassertThat(test2(true)).isFalse();\n\t\t\tassertThat(test2(false)).isTrue();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return !test();\")\n\t\t\t\t.containsOne(\"return !v;\");\n\t}\n\n\t@Test\n\tpublic void smali() {\n\t\t// @formatter:off\n\t\t/*\n\t\t\tpublic boolean test1() {\n\t\t\t\treturn test() ^ true;\n\t\t\t}\n\n\t\t\tpublic boolean test2() {\n\t\t\t\treturn test() ^ false;\n\t\t\t}\n\n\t\t\tpublic boolean test() {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t */\n\t\t// @formatter:on\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return !test();\")\n\t\t\t\t.containsOne(\"return test();\");\n\t}\n\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arrays/TestArrayFill.java",
    "content": "package jadx.tests.integration.arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestArrayFill extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic String[] method() {\n\t\t\treturn new String[] { \"1\", \"2\", \"3\" };\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return new String[]{\\\"1\\\", \\\"2\\\", \\\"3\\\"};\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arrays/TestArrayFill2.java",
    "content": "package jadx.tests.integration.arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestArrayFill2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic int[] test(int a) {\n\t\t\treturn new int[] { 1, a + 1, 2 };\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return new int[]{1, a + 1, 2};\");\n\t}\n\n\tpublic static class TestCls2 {\n\n\t\tpublic int[] test2(int a) {\n\t\t\treturn new int[] { 1, a++, a * 2 };\n\t\t}\n\t}\n\n\t@Test\n\t@NotYetImplemented\n\tpublic void test2() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls2.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return new int[]{1, a++, a * 2};\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arrays/TestArrayFill3.java",
    "content": "package jadx.tests.integration.arrays;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArrayFill3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic byte[] test() {\n\t\t\treturn new byte[] { 0, 1, 2 };\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.ECJ_J8, TestProfile.ECJ_DX_J8 })\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return new byte[]{0, 1, 2}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arrays/TestArrayFill4.java",
    "content": "package jadx.tests.integration.arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArrayFill4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\t// replaced constant break filled array creation\n\t\tprivate static final int ARRAY_SIZE = 4;\n\n\t\tpublic long[] test() {\n\t\t\treturn new long[] { 0, 1, Long.MAX_VALUE, Long.MIN_VALUE + 1 };\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"new long[ARRAY_SIZE];\")\n\t\t\t\t.containsOne(\"return new long[]{0, 1, \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arrays/TestArrayFillConstReplace.java",
    "content": "package jadx.tests.integration.arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArrayFillConstReplace extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static final int CONST_INT = 0xffff;\n\n\t\tpublic int[] test() {\n\t\t\treturn new int[] { 127, 129, CONST_INT };\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\" int CONST_INT = 65535;\")\n\t\t\t\t.containsOne(\"return new int[]{127, 129, CONST_INT};\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arrays/TestArrayFillNegative.java",
    "content": "package jadx.tests.integration.arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArrayFillNegative extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int[] test() {\n\t\t\tint[] arr = new int[3];\n\t\t\tarr[0] = 1;\n\t\t\tarr[1] = arr[0] + 1;\n\t\t\tarr[2] = arr[1] + 1;\n\t\t\treturn arr;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test()).isEqualTo(new int[] { 1, 2, 3 });\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"int[] arr = {1, \")\n\t\t\t\t.containsOne(\"int[] arr = new int[3];\")\n\t\t\t\t.containsOne(\"arr[1] = arr[0] + 1;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arrays/TestArrayFillWithMove.java",
    "content": "package jadx.tests.integration.arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArrayFillWithMove extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliFiles(\"TestCls\"))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"// fill-array-data instruction\")\n\t\t\t\t.doesNotContain(\"arr[0] = 0;\")\n\t\t\t\t.contains(\"return new long[]{0, 1}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arrays/TestArrayInit.java",
    "content": "package jadx.tests.integration.arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestArrayInit extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tbyte[] bytes;\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tpublic void test() {\n\t\t\tbyte[] arr = new byte[] { 10, 20, 30 };\n\t\t}\n\n\t\tpublic void test2() {\n\t\t\tbytes = new byte[] { 10, 20, 30 };\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"= {10, 20, 30};\")\n\t\t\t\t.contains(\"this.bytes = new byte[]{10, 20, 30};\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arrays/TestArrayInitField.java",
    "content": "package jadx.tests.integration.arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArrayInitField extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tstatic byte[] a = new byte[] { 10, 20, 30 };\n\t\tbyte[] b = new byte[] { 40, 50, 60 };\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"static byte[] a = {10, 20, 30};\")\n\t\t\t\t.containsOne(\"byte[] b = {40, 50, 60};\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arrays/TestArrayInitField2.java",
    "content": "package jadx.tests.integration.arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArrayInitField2 extends SmaliTest {\n\t@Test\n\tpublic void test() {\n\t\tforceDecompiledCheck();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"static long[] myArr = {1282979400, 0, 0};\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arrays/TestArrays.java",
    "content": "package jadx.tests.integration.arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestArrays extends IntegrationTest {\n\tpublic static class TestCls {\n\n\t\tpublic int test1(int i) {\n\t\t\tint[] a = new int[] { 1, 2, 3, 5 };\n\t\t\treturn a[i];\n\t\t}\n\n\t\tpublic int test2(int i) {\n\t\t\tint[][] a = new int[i][i + 1];\n\t\t\treturn a.length;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return new int[]{1, 2, 3, 5}[i];\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arrays/TestArrays2.java",
    "content": "package jadx.tests.integration.arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArrays2 extends IntegrationTest {\n\tpublic static class TestCls {\n\n\t\tprivate static Object test4(int type) {\n\t\t\tif (type == 1) {\n\t\t\t\treturn new int[] { 1, 2 };\n\t\t\t} else if (type == 2) {\n\t\t\t\treturn new float[] { 1, 2 };\n\t\t\t} else if (type == 3) {\n\t\t\t\treturn new short[] { 1, 2 };\n\t\t\t} else if (type == 4) {\n\t\t\t\treturn new byte[] { 1, 2 };\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test4(4)).isInstanceOf(byte[].class);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"new int[]{1, 2}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arrays/TestArrays3.java",
    "content": "package jadx.tests.integration.arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArrays3 extends IntegrationTest {\n\tpublic static class TestCls {\n\n\t\tprivate Object test(byte[] bArr) {\n\t\t\treturn new Object[] { bArr };\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tbyte[] inputArr = { 1, 2 };\n\t\t\tObject result = test(inputArr);\n\t\t\tassertThat(result).isInstanceOf(Object[].class);\n\t\t\tassertThat(((Object[]) result)[0]).isEqualTo(inputArr);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return new Object[]{bArr};\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return new Object[]{bArr};\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arrays/TestArrays4.java",
    "content": "package jadx.tests.integration.arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestArrays4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tchar[] payload;\n\n\t\tpublic TestCls(byte[] bytes) {\n\t\t\tchar[] a = toChars(bytes);\n\t\t\tthis.payload = new char[a.length];\n\t\t\tSystem.arraycopy(a, 0, this.payload, 0, bytes.length);\n\t\t}\n\n\t\tprivate static char[] toChars(byte[] bArr) {\n\t\t\treturn new char[bArr.length];\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testArrayTypeInference() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"char[] chars = toChars(bArr);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arrays/TestFillArrayData.java",
    "content": "package jadx.tests.integration.arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestFillArrayData extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliFiles(\"TestCls\"))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"jArr[0] = 1;\")\n\t\t\t\t.contains(\"jArr[1] = 2;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/arrays/TestMultiDimArrayFill.java",
    "content": "package jadx.tests.integration.arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestMultiDimArrayFill extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static Obj test(int a, int b) {\n\t\t\treturn new Obj(\n\t\t\t\t\tnew int[][] { { 1 }, { 2 }, { 3 }, { 4, 5 }, new int[0] },\n\t\t\t\t\tnew int[] { a, a, a, a, b });\n\t\t}\n\n\t\tprivate static class Obj {\n\t\t\tpublic Obj(int[][] ints, int[] ints2) {\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return new Obj(\"\n\t\t\t\t\t\t+ \"new int[][]{new int[]{1}, new int[]{2}, new int[]{3}, new int[]{4, 5}, new int[0]}, \"\n\t\t\t\t\t\t+ \"new int[]{a, a, a, a, b});\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/code/TestArrayAccessReorder.java",
    "content": "package jadx.tests.integration.code;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArrayAccessReorder extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int[] test(int[] arr) {\n\t\t\tint len = arr.length;\n\t\t\tint[] result = new int[len];\n\t\t\tint i = 0;\n\t\t\tint k = len;\n\t\t\twhile (k != 0) {\n\t\t\t\tint v = arr[i];\n\t\t\t\tk--;\n\t\t\t\tint t = -v;\n\t\t\t\ti++;\n\t\t\t\tresult[k] = t * 5;\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(new int[] { 1, 2, 3 })).isEqualTo(new int[] { -15, -10, -5 });\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"i++\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/code/TestCodeCommentStyle.java",
    "content": "package jadx.tests.integration.code;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.data.CommentStyle;\nimport jadx.api.data.IJavaNodeRef;\nimport jadx.api.data.impl.JadxCodeComment;\nimport jadx.api.data.impl.JadxNodeRef;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestCodeCommentStyle extends IntegrationTest {\n\n\t@SuppressWarnings(\"unused\")\n\tpublic static class TestCls {\n\t\tpublic int aSingleLine;\n\t\tpublic int aMultiLine;\n\n\t\tpublic int block;\n\t\tpublic int blockMulti;\n\t\tpublic int blockCondensed;\n\t\tpublic int blockCondensedMulti;\n\n\t\tpublic int javaDoc;\n\t\tpublic int javaDocMulti;\n\t\tpublic int javaDocCondensed;\n\t\tpublic int javaDocCondensedMulti;\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\taddFldComment(\"aSingleLine\", \"Test line comment\", CommentStyle.LINE);\n\t\taddFldComment(\"aMultiLine\", \"Test multi\\nline comment\", CommentStyle.LINE);\n\n\t\taddFldComment(\"block\", \"Test block comment\", CommentStyle.BLOCK);\n\t\taddFldComment(\"blockMulti\", \"Test multi\\nline block comment\", CommentStyle.BLOCK);\n\t\taddFldComment(\"blockCondensed\", \"Test condensed block comment\", CommentStyle.BLOCK_CONDENSED);\n\t\taddFldComment(\"blockCondensedMulti\", \"Test condensed multi\\nline block comment\", CommentStyle.BLOCK_CONDENSED);\n\n\t\taddFldComment(\"javaDoc\", \"Test javaDoc comment\", CommentStyle.JAVADOC);\n\t\taddFldComment(\"javaDocMulti\", \"Test multi\\nline javaDoc comment\", CommentStyle.JAVADOC);\n\t\taddFldComment(\"javaDocCondensed\", \"Test condensed javaDoc comment\", CommentStyle.JAVADOC_CONDENSED);\n\t\taddFldComment(\"javaDocCondensedMulti\", \"Test condensed multi\\nline javaDoc comment\", CommentStyle.JAVADOC_CONDENSED);\n\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"// Test line comment\")\n\t\t\t\t.containsOne(\"/* Test condensed block comment */\");\n\t}\n\n\tprivate void addFldComment(String fldName, String comment, CommentStyle style) {\n\t\tString clsName = \"jadx.tests.integration.code.TestCodeCommentStyle$TestCls\";\n\t\tJadxNodeRef fldRef = new JadxNodeRef(IJavaNodeRef.RefType.FIELD, clsName, fldName + \":I\");\n\t\tgetCodeData().getComments().add(new JadxCodeComment(fldRef, comment, style));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestBitwiseAnd.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\n@SuppressWarnings({ \"PointlessBooleanExpression\", \"unused\" })\npublic class TestBitwiseAnd extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate boolean a;\n\t\tprivate boolean b;\n\n\t\tpublic void test() {\n\t\t\tif ((a & b) != false) {\n\t\t\t\ttest();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (this.a && this.b) {\");\n\t}\n\n\tpublic static class TestCls2 {\n\t\tprivate boolean a;\n\t\tprivate boolean b;\n\n\t\tpublic void test() {\n\t\t\tif ((a & b) != true) {\n\t\t\t\ttest();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls2.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (!this.a || !this.b) {\");\n\t}\n\n\tpublic static class TestCls3 {\n\t\tprivate boolean a;\n\t\tprivate boolean b;\n\n\t\tpublic void test() {\n\t\t\tif ((a & b) == false) {\n\t\t\t\ttest();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test3() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls3.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (!this.a || !this.b) {\");\n\t}\n\n\tpublic static class TestCls4 {\n\t\tprivate boolean a;\n\t\tprivate boolean b;\n\n\t\tpublic void test() {\n\t\t\tif ((a & b) == true) {\n\t\t\t\ttest();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test4() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls4.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (this.a && this.b) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestBitwiseOr.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestBitwiseOr extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate boolean a;\n\t\tprivate boolean b;\n\n\t\tpublic void test() {\n\t\t\tif ((a | b) != false) {\n\t\t\t\ttest();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (this.a || this.b) {\");\n\t}\n\n\tpublic static class TestCls2 {\n\t\tprivate boolean a;\n\t\tprivate boolean b;\n\n\t\tpublic void test() {\n\t\t\tif ((a | b) != true) {\n\t\t\t\ttest();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls2.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (!this.a && !this.b) {\");\n\t}\n\n\tpublic static class TestCls3 {\n\t\tprivate boolean a;\n\t\tprivate boolean b;\n\n\t\tpublic void test() {\n\t\t\tif ((a | b) == false) {\n\t\t\t\ttest();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test3() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls3.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (!this.a && !this.b) {\");\n\t}\n\n\tpublic static class TestCls4 {\n\t\tprivate boolean a;\n\t\tprivate boolean b;\n\n\t\tpublic void test() {\n\t\t\tif ((a | b) == true) {\n\t\t\t\ttest();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test4() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls4.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (this.a || this.b) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestBooleanToByte.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestBooleanToByte extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tprivate boolean showConsent;\n\n\t\tpublic void write(byte b) {\n\t\t}\n\n\t\tpublic void writeToParcel(TestBooleanToByte testBooleanToByte) {\n\t\t\ttestBooleanToByte.write(this.showConsent ? (byte) 1 : 0);\n\t\t}\n\t*/\n\t// @formatter:on\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"write(this.showConsent ? (byte) 1 : (byte) 0);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestBooleanToChar.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestBooleanToChar extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tprivate boolean showConsent;\n\n\t\tpublic void write(char b) {\n\t\t}\n\n\t\tpublic void writeToParcel(TestBooleanToChar testBooleanToChar) {\n\t\t\ttestBooleanToChar.write(this.showConsent ? (char) 1 : 0);\n\t\t}\n\t*/\n\t// @formatter:on\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"write(this.showConsent ? (char) 1 : (char) 0);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestBooleanToDouble.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestBooleanToDouble extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tprivate boolean showConsent;\n\n\t\tpublic void write(double d) {\n\t\t}\n\n\t\tpublic void writeToParcel(TestBooleanToDouble testBooleanToDouble) {\n\t\t\ttestBooleanToDouble.write(this.showConsent ? 1 : 0);\n\t\t}\n\t*/\n\t// @formatter:on\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"write(this.showConsent ? 1.0d : 0.0d);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestBooleanToFloat.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestBooleanToFloat extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tprivate boolean showConsent;\n\n\t\tpublic void write(float f) {\n\t\t}\n\n\t\tpublic void writeToParcel(TestBooleanToFloat testBooleanToFloat) {\n\t\t\ttestBooleanToFloat.write(this.showConsent ? 1 : 0);\n\t\t}\n\t*/\n\t// @formatter:on\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"write(this.showConsent ? 1.0f : 0.0f);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestBooleanToInt.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"CommentedOutCode\")\npublic class TestBooleanToInt extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tprivate boolean showConsent;\n\n\t\tpublic void write(int b) {\n\t\t}\n\n\t\tpublic void writeToParcel(TestBooleanToInt testBooleanToInt) {\n\t\t\ttestBooleanToInt.write(this.showConsent ? 1 : 0);\n\t\t}\n\t*/\n\t// @formatter:on\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"write(this.showConsent ? 1 : 0);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestBooleanToInt2.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"CommentedOutCode\")\npublic class TestBooleanToInt2 extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic static class TestCls {\n\t\t\tpublic void test() {\n\t\t\t\tboolean v = getValue();\n\t\t\t\tuse1(Integer.valueOf(v));\n\t\t\t\tuse2(v);\n\t\t\t}\n\n\t\t\tprivate boolean getValue() {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tprivate void use1(Integer v) {\n\t\t\t}\n\n\t\t\tprivate void use2(int v) {\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"use1(Integer.valueOf(value ? 1 : 0));\")\n\t\t\t\t.containsOne(\"use2(value ? 1 : 0);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestBooleanToLong.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestBooleanToLong extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tprivate boolean showConsent;\n\n\t\tpublic void write(long j) {\n\t\t}\n\n\t\tpublic void writeToParcel(TestBooleanToLong testBooleanToLong) {\n\t\t\ttestBooleanToLong.write(this.showConsent ? 1 : 0);\n\t\t}\n\t*/\n\t// @formatter:on\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"write(this.showConsent ? 1L : 0L);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestBooleanToShort.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestBooleanToShort extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tprivate boolean showConsent;\n\n\t\tpublic void write(short b) {\n\t\t}\n\n\t\tpublic void writeToParcel(TestBooleanToShort testBooleanToShort) {\n\t\t\ttestBooleanToShort.write(this.showConsent ? (short) 1 : 0);\n\t\t}\n\t*/\n\t// @formatter:on\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"write(this.showConsent ? (short) 1 : (short) 0);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestCast.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestCast extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tbyte myByte;\n\t\tshort myShort;\n\n\t\tpublic void test1(boolean a) {\n\t\t\twrite(a ? (byte) 0 : 1);\n\t\t}\n\n\t\tpublic void test2(boolean a) {\n\t\t\twrite(a ? 0 : myByte);\n\t\t}\n\n\t\tpublic void test3(boolean a) {\n\t\t\twrite(a ? 0 : (byte) 127);\n\t\t}\n\n\t\tpublic void test4(boolean a) {\n\t\t\twrite(a ? (short) 0 : 1);\n\t\t}\n\n\t\tpublic void test5(boolean a) {\n\t\t\twrite(a ? myShort : 0);\n\t\t}\n\n\t\tpublic void test6(boolean a) {\n\t\t\twrite(a ? Short.MIN_VALUE : 0);\n\t\t}\n\n\t\tpublic void write(byte b) {\n\t\t}\n\n\t\tpublic void write(short b) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"write(a ? (byte) 0 : (byte) 1);\")\n\t\t\t\t.contains(\"write(a ? (byte) 0 : this.myByte);\")\n\t\t\t\t.contains(\"write(a ? (byte) 0 : (byte) 127);\")\n\t\t\t\t.contains(\"write(a ? (short) 0 : (short) 1);\")\n\t\t\t\t.contains(\"write(a ? this.myShort : (short) 0);\")\n\t\t\t\t.contains(\"write(a ? Short.MIN_VALUE : (short) 0);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestCmpOp.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestCmpOp extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic boolean testGT(float a) {\n\t\t\treturn a > 3.0f;\n\t\t}\n\n\t\tpublic boolean testLT(float b) {\n\t\t\treturn b < 2.0f;\n\t\t}\n\n\t\tpublic boolean testEQ(float c) {\n\t\t\treturn c == 1.0f;\n\t\t}\n\n\t\tpublic boolean testNE(float d) {\n\t\t\treturn d != 0.0f;\n\t\t}\n\n\t\tpublic boolean testGE(float e) {\n\t\t\treturn e >= -1.0f;\n\t\t}\n\n\t\tpublic boolean testLE(float f) {\n\t\t\treturn f <= -2.0f;\n\t\t}\n\n\t\tpublic boolean testGT2(float g) {\n\t\t\treturn 4.0f > g;\n\t\t}\n\n\t\tpublic boolean testLT2(long h) {\n\t\t\treturn 5 < h;\n\t\t}\n\n\t\tpublic boolean testGE2(double i) {\n\t\t\treturn 6.5d < i;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return a > 3.0f;\")\n\t\t\t\t.contains(\"return b < 2.0f;\")\n\t\t\t\t.contains(\"return c == 1.0f;\")\n\t\t\t\t.contains(\"return d != 0.0f;\")\n\t\t\t\t.contains(\"return e >= -1.0f;\")\n\t\t\t\t.contains(\"return f <= -2.0f;\")\n\t\t\t\t.contains(\"return 4.0f > g;\")\n\t\t\t\t.contains(\"return 5 < h;\").contains(\"return 6.5d < i;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestCmpOp2.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestCmpOp2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic boolean testGT(float a, float b) {\n\t\t\treturn a > b;\n\t\t}\n\n\t\tpublic boolean testLT(float c, double d) {\n\t\t\treturn c < d;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return a > b;\")\n\t\t\t\t.contains(\"return ((double) c) < d;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestComplexIf.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestComplexIf extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic final class TestComplexIf {\n\t\t\tprivate String a;\n\t\t\tprivate int b;\n\t\t\tprivate float c;\n\n\t\t\tpublic final boolean test() {\n\t\t\t\tif (this.a.equals(\"GT-P6200\") || this.a.equals(\"GT-P6210\") || ... ) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tif (this.a.equals(\"SM-T810\") || this.a.equals(\"SM-T813\") || ...) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn this.c > 160.0f ? true : this.c <= 0.0f && ((this.b & 15) == 4 ? 1 : null) != null;\n\t\t\t}\n\t\t}\n\t */\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliWithPkg(\"conditions\", \"TestComplexIf\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (this.a.equals(\\\"GT-P6200\\\") || this.a.equals(\\\"GT-P6210\\\") || this.a.equals(\\\"A100\\\") \"\n\t\t\t\t\t\t+ \"|| this.a.equals(\\\"A101\\\") || this.a.equals(\\\"LIFETAB_S786X\\\") || this.a.equals(\\\"VS890 4G\\\")) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestComplexIf2.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestComplexIf2 extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic void test() {\n\t\t\tif (this.isSaved) {\n\t\t\t\tthrow new RuntimeException(\"Error\");\n\t\t\t}\n\t\t\tif (LoaderUtils.isContextLoaderAvailable()) {\n\t\t\t\tthis.savedContextLoader = LoaderUtils.getContextClassLoader();\n\t\t\t\tClassLoader loader = this;\n\t\t\t\tif (this.project != null && \"simple\".equals(this.project)) {\n\t\t\t\t\tloader = getClass().getClassLoader();\n\t\t\t\t}\n\t\t\t\tLoaderUtils.setContextClassLoader(loader);\n\t\t\t\tthis.isSaved = true;\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmaliWithPkg(\"conditions\", \"TestComplexIf2\")).code()\n\t\t\t\t.containsOne(\"if (this.project != null && \\\"simple\\\".equals(this.project)) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestComplexIf3.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestComplexIf3 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.countString(1, \"iArr = null;\")\n\t\t\t\t.countString(2, \"z = false;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestComplexIf4.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestComplexIf4 extends SmaliTest {\n\t@Test\n\tvoid test() {\n\t\tdisableCompilation();\n\t\tallowWarnInCode(); // this is just to allow a harmless duplicated region warning\n\t\tassertThat(getClassNodeFromSmali()).code().contains(\"if (0 >= 0) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditionInLoop.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestConditionInLoop extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static int test(int a, int b) {\n\t\t\tint c = a + b;\n\t\t\tfor (int i = a; i < b; i++) {\n\t\t\t\tif (i == 7) {\n\t\t\t\t\tc += 2;\n\t\t\t\t} else {\n\t\t\t\t\tc *= 2;\n\t\t\t\t}\n\t\t\t}\n\t\t\tc--;\n\t\t\treturn c;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(5, 9)).isEqualTo(115);\n\t\t\tassertThat(test(8, 23)).isEqualTo(1015807);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"for (int i = a; i < b; i++) {\")\n\t\t\t\t.containsOne(\"c += 2;\")\n\t\t\t\t.containsOne(\"c *= 2;\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"while\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestConditions extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic boolean test(boolean a, boolean b, boolean c) {\n\t\t\treturn (a && b) || c;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"(!a || !b) && !c\")\n\t\t\t\t.contains(\"return (a && b) || c;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions10.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestConditions10 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test(boolean a, int b) {\n\t\t\tif (a || b > 2) {\n\t\t\t\tb++;\n\t\t\t}\n\t\t\tif (!a || (b >= 0 && b <= 11)) {\n\t\t\t\tSystem.out.println(\"1\");\n\t\t\t} else {\n\t\t\t\tSystem.out.println(\"2\");\n\t\t\t}\n\t\t\tSystem.out.println(\"3\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"return\")\n\t\t\t\t.containsOne(\"if (a || b > 2) {\")\n\t\t\t\t.containsOne(\"b++;\")\n\t\t\t\t.containsOne(\"if (!a || (b >= 0 && b <= 11)) {\")\n\t\t\t\t.containsOne(\"System.out.println(\\\"1\\\");\")\n\t\t\t\t.containsOne(\"} else {\")\n\t\t\t\t.containsOne(\"System.out.println(\\\"2\\\");\")\n\t\t\t\t.containsOne(\"System.out.println(\\\"3\\\");\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions11.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestConditions11 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test(boolean a, int b) {\n\t\t\tif (a || b > 2) {\n\t\t\t\tf();\n\t\t\t}\n\t\t}\n\n\t\tprivate void f() {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (a || b > 2) {\")\n\t\t\t\t.containsOne(\"f();\")\n\t\t\t\t.doesNotContain(\"return\")\n\t\t\t\t.doesNotContain(\"else\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions12.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestConditions12 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tstatic boolean autoStop = true;\n\t\tstatic boolean qualityReading = false;\n\t\tstatic int lastValidRaw = -1;\n\n\t\tpublic static void main(String[] args) throws Exception {\n\t\t\tint a = 5;\n\t\t\tint b = 30;\n\t\t\tdataProcess(a, b);\n\t\t}\n\n\t\tpublic static void dataProcess(int raw, int quality) {\n\t\t\tif (quality >= 10 && raw != 0) {\n\t\t\t\tSystem.out.println(\"OK\" + raw);\n\t\t\t\tqualityReading = false;\n\t\t\t} else if (raw == 0 || quality < 6 || !qualityReading) {\n\t\t\t\tSystem.out.println(\"Not OK\" + raw);\n\t\t\t} else {\n\t\t\t\tSystem.out.println(\"Quit OK\" + raw);\n\t\t\t}\n\t\t\tif (quality < 30) {\n\t\t\t\tint timeLeft = 30 - quality;\n\t\t\t\tif (quality >= 10) {\n\t\t\t\t\tSystem.out.println(\"Processing\" + timeLeft);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tSystem.out.println(\"Finish Processing\");\n\t\t\t\tif (raw > 0) {\n\t\t\t\t\tlastValidRaw = raw;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (quality >= 30 && autoStop) {\n\t\t\t\tSystem.out.println(\"Finished\");\n\t\t\t}\n\t\t\tif (!autoStop && lastValidRaw > -1 && quality < 10) {\n\t\t\t\tSystem.out.println(\"Finished\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (quality >= 10 && raw != 0) {\")\n\t\t\t\t.containsOne(\"} else if (raw == 0 || quality < 6 || !qualityReading) {\")\n\t\t\t\t.containsOne(\"if (quality < 30) {\")\n\t\t\t\t.containsOne(\"if (quality >= 10) {\")\n\t\t\t\t.containsOne(\"if (raw > 0) {\")\n\t\t\t\t.containsOne(\"if (quality >= 30 && autoStop) {\")\n\t\t\t\t.containsOne(\"if (!autoStop && lastValidRaw > -1 && quality < 10) {\")\n\t\t\t\t.doesNotContain(\"return\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions13.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestConditions13 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tstatic boolean qualityReading;\n\n\t\tpublic static void dataProcess(int raw, int quality) {\n\t\t\tif (quality >= 10 && raw != 0) {\n\t\t\t\tSystem.out.println(\"OK\" + raw);\n\t\t\t\tqualityReading = false;\n\t\t\t} else if (raw == 0 || quality < 6 || !qualityReading) {\n\t\t\t\tSystem.out.println(\"Not OK\" + raw);\n\t\t\t} else {\n\t\t\t\tSystem.out.println(\"Quit OK\" + raw);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (quality >= 10 && raw != 0) {\")\n\t\t\t\t.containsOne(\"System.out.println(\\\"OK\\\" + raw);\")\n\t\t\t\t.containsOne(\"qualityReading = false;\")\n\t\t\t\t.containsOne(\"} else if (raw == 0 || quality < 6 || !qualityReading) {\")\n\t\t\t\t.doesNotContain(\"return\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions14.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestConditions14 extends IntegrationTest {\n\n\t@SuppressWarnings({ \"EqualsReplaceableByObjectsCall\", \"ConstantConditions\" })\n\tpublic static class TestCls {\n\n\t\tpublic static boolean test(Object a, Object b) {\n\t\t\tboolean r = a == null ? b != null : !a.equals(b);\n\t\t\tif (r) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tSystem.out.println(\"r=\" + r);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"boolean r = a == null ? b != null : !a.equals(b);\")\n\t\t\t\t.containsOne(\"if (r) {\")\n\t\t\t\t.containsOne(\"System.out.println(\\\"r=\\\" + r);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions15.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestConditions15 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static boolean test(final String name) {\n\t\t\tif (isEmpty(name)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (\"1\".equals(name)\n\t\t\t\t\t|| \"2\".equals(name)\n\t\t\t\t\t|| \"3\".equals(name)\n\t\t\t\t\t|| \"4\".equals(name)\n\t\t\t\t\t|| \"5\".equals(name)\n\t\t\t\t\t|| \"6\".equals(name)\n\t\t\t\t\t|| \"7\".equals(name)\n\t\t\t\t\t|| \"8\".equals(name)\n\t\t\t\t\t|| \"9\".equals(name)\n\t\t\t\t\t|| \"10\".equals(name)\n\t\t\t\t\t|| \"11\".equals(name)\n\t\t\t\t\t|| \"12\".equals(name)\n\t\t\t\t\t|| \"13\".equals(name)\n\t\t\t\t\t|| \"14\".equals(name)\n\t\t\t\t\t|| \"15\".equals(name)\n\t\t\t\t\t|| \"16\".equals(name)\n\t\t\t\t\t|| \"17\".equals(name)\n\t\t\t\t\t|| \"18\".equals(name)\n\t\t\t\t\t|| \"19\".equals(name)\n\t\t\t\t\t|| \"20\".equals(name)\n\t\t\t\t\t|| \"22\".equals(name)\n\t\t\t\t\t|| \"22\".equals(name)\n\t\t\t\t\t|| \"23\".equals(name)\n\t\t\t\t\t|| \"24\".equals(name)\n\t\t\t\t\t|| \"25\".equals(name)\n\t\t\t\t\t|| \"26\".equals(name)\n\t\t\t\t\t|| \"27\".equals(name)\n\t\t\t\t\t|| \"28\".equals(name)\n\t\t\t\t\t|| \"29\".equals(name)\n\t\t\t\t\t|| \"30\".equals(name)) {\n\t\t\t\treturn false;\n\t\t\t} else {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tprivate static boolean isEmpty(String name) {\n\t\t\treturn name.isEmpty();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"\\\"1\\\".equals(name)\")\n\t\t\t\t.containsOne(\"\\\"30\\\".equals(name)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions16.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestConditions16 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static boolean test(int a, int b) {\n\t\t\treturn a < 0 || b % 2 != 0 && a > 28 || b < 0;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(-1, 1)).isTrue();\n\t\t\tassertThat(test(1, -1)).isTrue();\n\t\t\tassertThat(test(29, 3)).isTrue();\n\t\t\tassertThat(test(2, 2)).isFalse();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return a < 0 || (b % 2 != 0 && a > 28) || b < 0;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions17.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestConditions17 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static final int SOMETHING = 2;\n\n\t\tpublic static void test(int a) {\n\t\t\tif ((a & SOMETHING) != 0) {\n\t\t\t\tprint(1);\n\t\t\t}\n\t\t\tprint(2);\n\t\t}\n\n\t\tpublic static void print(Object o) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\" & \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions18.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"CommentedOutCode\")\npublic class TestConditions18 extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic static class TestConditions18 {\n\t\t\tprivate Map map;\n\n\t\t\tpublic boolean test(Object obj) {\n\t\t\t\treturn this == obj || ((obj instanceof TestConditions18) && st(this.map, ((TestConditions18) obj).map));\n\t\t\t}\n\n\t\t\tprivate static boolean st(Object obj, Object obj2) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"if (this != obj) {\",\n\t\t\t\t\t\tindent() + \"return (obj instanceof TestConditions18) && st(this.map, ((TestConditions18) obj).map);\",\n\t\t\t\t\t\t\"}\",\n\t\t\t\t\t\t\"return true;\");\n\t}\n\n\t@Test\n\t@NotYetImplemented\n\tpublic void testNYI() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return this == obj || ((obj instanceof TestConditions18) && st(this.map, ((TestConditions18) obj).map));\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions2.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\npublic class TestConditions2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tint c;\n\t\tString d;\n\t\tString f;\n\n\t\tpublic void testComplexIf(String a, int b) {\n\t\t\tif (d == null || (c == 0 && b != -1 && d.length() == 0)) {\n\t\t\t\tc = a.codePointAt(c);\n\t\t\t} else {\n\t\t\t\tif (a.hashCode() != 0xCDE) {\n\t\t\t\t\tc = f.compareTo(a);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions21.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestConditions21 extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic boolean check(Object obj) {\n\t\t\tif (this == obj) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (obj instanceof List) {\n\t\t\t\tList list = (List) obj;\n\t\t\t\tif (!list.isEmpty() && list.contains(this)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali()).code()\n\t\t\t\t.containsOne(\"!list.isEmpty() && list.contains(this)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions3.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport java.util.List;\nimport java.util.regex.Pattern;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestConditions3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static final Pattern PATTERN = Pattern.compile(\"[a-f0-9]{20}\");\n\n\t\tpublic static Object test(final A a) {\n\t\t\tList<String> list = a.getList();\n\t\t\tif (list == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (list.size() != 1) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tString s = list.get(0);\n\t\t\tif (isEmpty(s)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (isDigitsOnly(s)) {\n\t\t\t\treturn new A().set(s);\n\t\t\t}\n\t\t\tif (PATTERN.matcher(s).matches()) {\n\t\t\t\treturn new A().set(s);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate static boolean isDigitsOnly(String s) {\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate static boolean isEmpty(String s) {\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate static class A {\n\t\t\tpublic Object set(String s) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tpublic List<String> getList() {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return null;\")\n\t\t\t\t.doesNotContain(\"else\")\n\t\t\t\t.doesNotContain(\"AnonymousClass_1\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions4.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestConditions4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int test(int num) {\n\t\t\tboolean inRange = (num >= 59 && num <= 66);\n\t\t\treturn inRange ? num + 1 : num;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"num >= 59 && num <= 66\")\n\t\t\t\t.contains(\"? num + 1 : num;\").doesNotContain(\"else\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions5.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestConditions5 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static void test(Object a1, Object a2) {\n\t\t\tif (a1 == null) {\n\t\t\t\tif (a2 != null) {\n\t\t\t\t\tthrow new AssertionError(a1 + \" != \" + a2);\n\t\t\t\t}\n\t\t\t} else if (!a1.equals(a2)) {\n\t\t\t\tthrow new AssertionError(a1 + \" != \" + a2);\n\t\t\t}\n\t\t}\n\n\t\tpublic static void test2(Object a1, Object a2) {\n\t\t\tif (a1 != null) {\n\t\t\t\tif (!a1.equals(a2)) {\n\t\t\t\t\tthrow new AssertionError(a1 + \" != \" + a2);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (a2 != null) {\n\t\t\t\t\tthrow new AssertionError(a1 + \" != \" + a2);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"if (a1 == null) {\")\n\t\t\t\t.contains(\"if (a2 != null) {\")\n\t\t\t\t.contains(\"throw new AssertionError(a1 + \\\" != \\\" + a2);\")\n\t\t\t\t.doesNotContain(\"if (a1.equals(a2)) {\")\n\t\t\t\t.contains(\"} else if (!a1.equals(a2)) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions6.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestConditions6 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic boolean test(List<String> l1, List<String> l2) {\n\t\t\tif (l2.size() > 0) {\n\t\t\t\tl1.clear();\n\t\t\t}\n\t\t\treturn l1.size() == 0;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return l1.size() == 0;\")\n\t\t\t\t.doesNotContain(\"else\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions7.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestConditions7 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test(int[] a, int i) {\n\t\t\tif (i >= 0 && i < a.length) {\n\t\t\t\ta[i]++;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"if (i >= 0 && i < a.length) {\")\n\t\t\t\t.doesNotContain(\"||\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions8.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestConditions8 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate TestCls pager;\n\t\tprivate TestCls listView;\n\n\t\tpublic void test(TestCls view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {\n\t\t\tif (!isUsable()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!pager.hasMore()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (getLoaderManager().hasRunningLoaders()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (listView != null\n\t\t\t\t\t&& listView.getLastVisiblePosition() >= pager.size()) {\n\t\t\t\tshowMore();\n\t\t\t}\n\t\t}\n\n\t\tprivate void showMore() {\n\t\t}\n\n\t\tprivate int size() {\n\t\t\treturn 0;\n\t\t}\n\n\t\tprivate int getLastVisiblePosition() {\n\t\t\treturn 0;\n\t\t}\n\n\t\tprivate boolean hasRunningLoaders() {\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate TestCls getLoaderManager() {\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate boolean hasMore() {\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate boolean isUsable() {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"showMore();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions9.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestConditions9 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test(boolean a, int b) throws Exception {\n\t\t\tif (!a || (b >= 0 && b <= 11)) {\n\t\t\t\tSystem.out.println('1');\n\t\t\t} else {\n\t\t\t\tSystem.out.println('2');\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (!a || (b >= 0 && b <= 11)) {\")\n\t\t\t\t.containsOne(\"System.out.println('1');\")\n\t\t\t\t.containsOne(\"} else {\")\n\t\t\t\t.containsOne(\"System.out.println('2');\")\n\t\t\t\t.doesNotContain(\"return;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestElseIf.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"IfCanBeSwitch\")\npublic class TestElseIf extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int testIfElse(String str) {\n\t\t\tint r;\n\t\t\tif (str.equals(\"a\")) {\n\t\t\t\tr = 1;\n\t\t\t} else if (str.equals(\"b\")) {\n\t\t\t\tr = 2;\n\t\t\t} else if (str.equals(\"3\")) {\n\t\t\t\tr = 3;\n\t\t\t} else if (str.equals(\"$\")) {\n\t\t\t\tr = 4;\n\t\t\t} else {\n\t\t\t\tr = -1;\n\t\t\t\tSystem.out.println();\n\t\t\t}\n\t\t\tr = r * 10;\n\t\t\treturn Math.abs(r);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"} else if (str.equals(\\\"b\\\")) {\")\n\t\t\t\t.containsOne(\"} else {\")\n\t\t\t\t.containsOne(\"int r;\")\n\t\t\t\t.containsOne(\"r = 1;\")\n\t\t\t\t.containsOne(\"r = -1;\")\n\t\t\t\t.doesNotContain(\" ? \")\n\t\t\t\t.doesNotContain(\" : \"); // no ternary operator\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestElseIfCodeStyle.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestElseIfCodeStyle extends IntegrationTest {\n\n\t@SuppressWarnings(\"unused\")\n\tpublic static class TestCls {\n\n\t\tpublic void test(String str) {\n\t\t\tif (\"a\".equals(str)) {\n\t\t\t\tcall(1);\n\t\t\t} else if (\"b\".equals(str)) {\n\t\t\t\tcall(2);\n\t\t\t} else if (\"c\".equals(str)) {\n\t\t\t\tcall(3);\n\t\t\t}\n\t\t}\n\n\t\tprivate void call(int i) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"!\\\"c\\\".equals(str)\")\n\t\t\t\t.doesNotContain(\"{\\n\" + indent(2) + \"} else {\"); // no empty `then` block\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestIfAndSwitch.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestIfAndSwitch extends SmaliTest {\n\n\t/* @formatter:off\n\t\tprivate final static int C = 0;\n\n\t\tprivate static int i;\n\t\tprivate static final Random rd = new Random();\n\t\tprivate static final int ACTION_MOVE = 2;\n\n\t\tpublic static boolean ifAndSwitch() {\n\t\t\tboolean update = false;\n\t\t\tif (rd.nextInt() == ACTION_MOVE) {\n\t\t\t\tswitch (i) {\n\t\t\t\t\tcase C:\n\t\t\t\t\t\tupdate = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (update) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t@formatter:on */\n\n\t@Test\n\tpublic void test() {\n\t\tallowWarnInCode();\n\t\tJadxAssertions.assertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.countString(1, \"if (rd.nextInt() == ACTION_MOVE) {\")\n\t\t\t\t.countString(1, \"switch (\")\n\t\t\t\t.countString(1, \"else {\");\n\n\t}\n\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestIfCodeStyle.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Issue #1455\n */\npublic class TestIfCodeStyle extends SmaliTest {\n\n\t@SuppressWarnings({ \"ConstantConditions\", \"FieldCanBeLocal\", \"unused\" })\n\tpublic static class TestCls {\n\n\t\tprivate String moduleName;\n\t\tprivate String modulePath;\n\t\tprivate String preinstalledModulePath;\n\t\tprivate long versionCode;\n\t\tprivate String versionName;\n\t\tprivate boolean isFactory;\n\t\tprivate boolean isActive;\n\n\t\tpublic void test(Parcel parcel) {\n\t\t\tint startPos = parcel.dataPosition();\n\t\t\tint size = parcel.readInt();\n\t\t\tif (size < 0) {\n\t\t\t\tif (startPos > Integer.MAX_VALUE - size) {\n\t\t\t\t\tthrow new RuntimeException(\"Overflow in the size of parcelable\");\n\t\t\t\t}\n\t\t\t\tparcel.setDataPosition(startPos + size);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tif (parcel.dataPosition() - startPos >= size) {\n\t\t\t\t\tif (startPos > Integer.MAX_VALUE - size) {\n\t\t\t\t\t\tthrow new RuntimeException(\"Overflow in the size of parcelable\");\n\t\t\t\t\t}\n\t\t\t\t\tparcel.setDataPosition(startPos + size);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.moduleName = parcel.readString();\n\t\t\t\tif (parcel.dataPosition() - startPos >= size) {\n\t\t\t\t\tif (startPos > Integer.MAX_VALUE - size) {\n\t\t\t\t\t\tthrow new RuntimeException(\"Overflow in the size of parcelable\");\n\t\t\t\t\t}\n\t\t\t\t\tparcel.setDataPosition(startPos + size);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.modulePath = parcel.readString();\n\t\t\t\tif (parcel.dataPosition() - startPos >= size) {\n\t\t\t\t\tif (startPos > Integer.MAX_VALUE - size) {\n\t\t\t\t\t\tthrow new RuntimeException(\"Overflow in the size of parcelable\");\n\t\t\t\t\t}\n\t\t\t\t\tparcel.setDataPosition(startPos + size);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.preinstalledModulePath = parcel.readString();\n\t\t\t\tif (parcel.dataPosition() - startPos >= size) {\n\t\t\t\t\tif (startPos > Integer.MAX_VALUE - size) {\n\t\t\t\t\t\tthrow new RuntimeException(\"Overflow in the size of parcelable\");\n\t\t\t\t\t}\n\t\t\t\t\tparcel.setDataPosition(startPos + size);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.versionCode = parcel.readLong();\n\t\t\t\tif (parcel.dataPosition() - startPos >= size) {\n\t\t\t\t\tif (startPos > Integer.MAX_VALUE - size) {\n\t\t\t\t\t\tthrow new RuntimeException(\"Overflow in the size of parcelable\");\n\t\t\t\t\t}\n\t\t\t\t\tparcel.setDataPosition(startPos + size);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.versionName = parcel.readString();\n\t\t\t\tif (parcel.dataPosition() - startPos >= size) {\n\t\t\t\t\tif (startPos > Integer.MAX_VALUE - size) {\n\t\t\t\t\t\tthrow new RuntimeException(\"Overflow in the size of parcelable\");\n\t\t\t\t\t}\n\t\t\t\t\tparcel.setDataPosition(startPos + size);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.isFactory = parcel.readInt() != 0;\n\t\t\t\tif (parcel.dataPosition() - startPos >= size) {\n\t\t\t\t\tif (startPos > Integer.MAX_VALUE - size) {\n\t\t\t\t\t\tthrow new RuntimeException(\"Overflow in the size of parcelable\");\n\t\t\t\t\t}\n\t\t\t\t\tparcel.setDataPosition(startPos + size);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.isActive = parcel.readInt() != 0;\n\t\t\t\tif (startPos > Integer.MAX_VALUE - size) {\n\t\t\t\t\tthrow new RuntimeException(\"Overflow in the size of parcelable\");\n\t\t\t\t}\n\t\t\t\tparcel.setDataPosition(startPos + size);\n\t\t\t} catch (Throwable e) {\n\t\t\t\tif (startPos <= Integer.MAX_VALUE - size) {\n\t\t\t\t\tparcel.setDataPosition(startPos + size);\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t\tthrow new RuntimeException(\"Overflow in the size of parcelable\");\n\t\t\t}\n\t\t}\n\n\t\tprivate static class Parcel {\n\t\t\tpublic void setDataPosition(int i) {\n\t\t\t}\n\n\t\t\tpublic int dataPosition() {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tpublic int readInt() {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tpublic String readString() {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tpublic long readLong() {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t// allow one last 'else'\n\t\t\t\t.oneOf(c -> c.doesNotContain(\"else\").countString(8, \"return;\"),\n\t\t\t\t\t\tc -> c.countString(1, \"else\").countString(7, \"return;\"))\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"if (i < 0) {\",\n\t\t\t\t\t\tindent() + \"if (iDataPosition > Integer.MAX_VALUE - i) {\",\n\t\t\t\t\t\tindent(2) + \"throw new RuntimeException(\\\"Overflow in the size of parcelable\\\");\",\n\t\t\t\t\t\tindent() + \"}\",\n\t\t\t\t\t\tindent() + \"parcel.setDataPosition(iDataPosition + i);\",\n\t\t\t\t\t\tindent() + \"return;\",\n\t\t\t\t\t\t\"}\");\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t// allow one last 'else'\n\t\t\t\t.oneOf(c -> c.doesNotContain(\"else\").countString(8, \"return;\"),\n\t\t\t\t\t\tc -> c.countString(1, \"else\").countString(7, \"return;\"))\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"if (_aidl_parcelable_size < 0) {\",\n\t\t\t\t\t\tindent() + \"if (_aidl_start_pos > Integer.MAX_VALUE - _aidl_parcelable_size) {\",\n\t\t\t\t\t\tindent(2) + \"throw new RuntimeException(\\\"Overflow in the size of parcelable\\\");\",\n\t\t\t\t\t\tindent() + \"}\",\n\t\t\t\t\t\tindent() + \"_aidl_parcel.setDataPosition(_aidl_start_pos + _aidl_parcelable_size);\",\n\t\t\t\t\t\tindent() + \"return;\",\n\t\t\t\t\t\t\"}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestIfCodeStyle2.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Issue #2052\n */\npublic class TestIfCodeStyle2 extends SmaliTest {\n\n\t@Test\n\tpublic void testSmali() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.countString(1, \"} else if (\")\n\t\t\t\t.countString(1, \"} else {\")\n\t\t\t\t.countString(19, \"return \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestIfElseAndConditionIntermediateInstruction.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\n// Here there are two IF blocks for each part of the IF predicate.\n// In some cases with optimised dex, two IF blocks cannot merged into the same IF region.\n// This happens where there are intermediate instructions between the two blocks which cannot be\n// inlined.\n// Both IF blocks share the same ELSE block which is then added to both resultant regions.\n// The resultant code does not reproduce the single if-else statement but it is better than failing\n// to decompile.\npublic class TestIfElseAndConditionIntermediateInstruction extends SmaliTest {\n\n\t/* @formatter:off\n\t\tprivate boolean bool;\n\t\tprivate float num;\n\t\tprivate static final float CONST = 342;\n\n\t\tpublic void function() {\n\t\t\tif (bool && num < 1) {\n\t\t\t\tnum += CONST;\n\t\t\t} else {\n\t\t\t\tnothing2();\n\t\t\t}\n\t\t\tnothing1();\n\t\t}\n\n\t\tprivate void nothing1() {\n\n\t\t}\n\n\t\tprivate void nothing2() {\n\n\t\t}\n\t@formatter:on */\n\n\t@Test\n\tpublic void test() {\n\t\tallowWarnInCode();\n\t\tJadxAssertions.assertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"else\")\n\t\t\t\t.countString(2, \"nothing2();\")\n\t\t\t\t.countString(1, \"nothing1();\")\n\t\t\t\t.countString(1, \"if (this.bool)\")\n\t\t\t\t.countString(1, \"if (f < 1.0f)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestInnerAssign.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInnerAssign extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate String result;\n\n\t\t@SuppressWarnings(\"checkstyle:InnerAssignment\")\n\t\tpublic void test(String str) {\n\t\t\tint len;\n\t\t\tif (str.isEmpty() || (len = str.length()) > 5) {\n\t\t\t\tresult += \"bad\";\n\t\t\t} else {\n\t\t\t\tresult += \"good, len: \" + len;\n\t\t\t}\n\t\t\tresult += \", str: \" + str;\n\t\t\tSystem.out.println(\"done\");\n\t\t}\n\n\t\tprivate String runTest(String str) {\n\t\t\tresult = \"\";\n\t\t\ttest(str);\n\t\t\treturn result;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(runTest(\"\")).isEqualTo(\"bad, str: \");\n\t\t\tassertThat(runTest(\"1234\")).isEqualTo(\"good, len: 4, str: 1234\");\n\t\t\tassertThat(runTest(\"1234567\")).isEqualTo(\"bad, str: 1234567\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"str.length()\")\n\t\t\t\t.containsOne(\"System.out.println(\\\"done\\\");\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestInnerAssign2.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInnerAssign2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate String field;\n\t\tprivate String swapField;\n\n\t\t@SuppressWarnings(\"checkstyle:InnerAssignment\")\n\t\tpublic boolean test(String str) {\n\t\t\tString sub;\n\t\t\treturn call(str) || ((sub = this.field) != null && sub.isEmpty());\n\t\t}\n\n\t\tprivate boolean call(String str) {\n\t\t\tthis.field = swapField;\n\t\t\treturn str.isEmpty();\n\t\t}\n\n\t\tpublic boolean testWrap(String str, String fieldValue) {\n\t\t\tthis.field = null;\n\t\t\tthis.swapField = fieldValue;\n\t\t\treturn test(str);\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(testWrap(\"\", null)).isTrue();\n\t\t\tassertThat(testWrap(\"a\", \"\")).isTrue();\n\t\t\tassertThat(testWrap(\"b\", null)).isFalse();\n\t\t\tassertThat(testWrap(\"c\", \"d\")).isFalse();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"sub = this.field\")\n\t\t\t\t.containsOne(\"return call(str) || ((sub = this.field) != null && sub.isEmpty());\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestInnerAssign3.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Issue #820\n */\npublic class TestInnerAssign3 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"(testClass2TestMethod = (testClass1 = null).testMethod()) == null\")\n\t\t\t\t.containsOne(\"testClass1.testField != null\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestNestedIf.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestNestedIf extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate boolean a0 = false;\n\t\tprivate int a1 = 1;\n\t\tprivate int a2 = 2;\n\t\tprivate int a3 = 1;\n\t\tprivate int a4 = 2;\n\n\t\tpublic boolean test1() {\n\t\t\tif (a0) {\n\t\t\t\tif (a1 == 0 || a2 == 0) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} else if (a3 == 0 || a4 == 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ttest1();\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (this.a0) {\")\n\t\t\t\t.containsOne(\"if (this.a1 == 0 || this.a2 == 0) {\")\n\t\t\t\t.containsOne(\"} else if (this.a3 == 0 || this.a4 == 0) {\")\n\t\t\t\t.countString(2, \"return false;\")\n\t\t\t\t.containsOne(\"test1();\")\n\t\t\t\t.containsOne(\"return true;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestNestedIf2.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestNestedIf2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tstatic int executedCount = 0;\n\t\tstatic boolean finished = false;\n\t\tstatic int repeatCount = 2;\n\n\t\tstatic boolean test(float delta, Object object) {\n\t\t\tif (executedCount != repeatCount && isRun(delta, object)) {\n\t\t\t\tif (finished) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tif (repeatCount == -1) {\n\t\t\t\t\t++executedCount;\n\t\t\t\t\taction();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t++executedCount;\n\t\t\t\tif (executedCount >= repeatCount) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\taction();\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic static void action() {\n\t\t}\n\n\t\tpublic static boolean isRun(float delta, Object object) {\n\t\t\treturn delta == 0;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (executedCount != repeatCount && isRun(delta, object)) {\")\n\t\t\t\t.containsOne(\"if (finished) {\")\n\t\t\t\t.doesNotContain(\"else\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestOutBlock.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Issue #2384\n */\npublic class TestOutBlock extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tallowWarnInCode();\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"setContentView\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestSimpleConditions.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestSimpleConditions extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic boolean test1(boolean[] a) {\n\t\t\treturn (a[0] && a[1] && a[2]) || (a[3] && a[4]);\n\t\t}\n\n\t\tpublic boolean test2(boolean[] a) {\n\t\t\treturn a[0] || a[1] || a[2] || a[3];\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return (a[0] && a[1] && a[2]) || (a[3] && a[4]);\")\n\t\t\t\t.contains(\"return a[0] || a[1] || a[2] || a[3];\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestTernary.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTernary extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic boolean test1(int a) {\n\t\t\treturn a != 2;\n\t\t}\n\n\t\tpublic void test2(int a) {\n\t\t\tcheckTrue(a == 3);\n\t\t}\n\n\t\tpublic int test3(int a) {\n\t\t\treturn a > 0 ? a : (a + 2) * 3;\n\t\t}\n\n\t\tprivate static void checkTrue(boolean v) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"else\")\n\t\t\t\t.contains(\"return a != 2;\")\n\t\t\t\t.contains(\"checkTrue(a == 3)\")\n\t\t\t\t.contains(\"return a > 0 ? a : (a + 2) * 3;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestTernary2.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTernary2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test() {\n\t\t\tcheckFalse(f(1, 0) == 0);\n\t\t}\n\n\t\tprivate int f(int a, int b) {\n\t\t\treturn a + b;\n\t\t}\n\n\t\tprivate void checkFalse(boolean b) {\n\t\t\tif (b) {\n\t\t\t\tthrow new AssertionError(\"Must be false\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"f(1, 0)\");\n\t}\n\n\t@Test\n\t@NotYetImplemented\n\tpublic void test2() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"assertTrue(f(1, 0) == 0);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestTernary3.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.Named;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTernary3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic boolean isNameEquals(InsnArg arg) {\n\t\t\tString n = getName(arg);\n\t\t\tif (n == null || !(arg instanceof Named)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn n.equals(((Named) arg).getName());\n\t\t}\n\n\t\tprivate String getName(InsnArg arg) {\n\t\t\tif (arg instanceof RegisterArg) {\n\t\t\t\treturn \"r\";\n\t\t\t}\n\t\t\tif (arg instanceof Named) {\n\t\t\t\treturn \"n\";\n\t\t\t}\n\t\t\treturn arg.toString();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (n == null || !(arg instanceof Named)) {\")\n\t\t\t\t.containsOne(\"return n.equals(((Named) arg).getName());\")\n\t\t\t\t.doesNotContain(\"if ((arg instanceof RegisterArg)) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestTernary4.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"CommentedOutCode\")\npublic class TestTernary4 extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tprivate Set test(HashMap<String, Object> hashMap) {\n\t\t\tboolean z;\n\t\t\tHashSet hashSet = new HashSet();\n\t\t\tsynchronized (this.defaultValuesByPath) {\n\t\t\t\tfor (String next : this.defaultValuesByPath.keySet()) {\n\t\t\t\t\tObject obj = hashMap.get(next);\n\t\t\t\t\tif (obj != null) {\n\t\t\t\t\t\tz = !getValueObject(next).equals(obj);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tz = this.valuesByPath.get(next) != null;;\n\t\t\t\t\t}\n\t\t\t\t\tif (z) {\n\t\t\t\t\t\thashSet.add(next);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn hashSet;\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.removeBlockComments()\n\t\t\t\t.doesNotContain(\"5\")\n\t\t\t\t.doesNotContain(\"try\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestTernaryInIf.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTernaryInIf extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic boolean test1(boolean a, boolean b, boolean c) {\n\t\t\treturn a ? b : c;\n\t\t}\n\n\t\tpublic int test2(boolean a, boolean b, boolean c) {\n\t\t\treturn (!a ? c : b) ? 1 : 2;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"if\")\n\t\t\t\t.doesNotContain(\"else\")\n\t\t\t\t.containsOne(\"return a ? b : c;\")\n\t\t\t\t.containsOneOf(\n\t\t\t\t\t\t\"return (a ? b : c) ? 1 : 2;\",\n\t\t\t\t\t\t\"return (a ? !b : !c) ? 2 : 1;\" // TODO: simplify this\n\t\t\t\t);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestTernaryInIf2.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTernaryInIf2 extends SmaliTest {\n\n\tpublic static class TestCls {\n\t\tprivate String a = \"a\";\n\t\tprivate String b = \"b\";\n\n\t\tpublic boolean equals(TestCls other) {\n\t\t\tif (this.a == null ? other.a == null : this.a.equals(other.a)) {\n\t\t\t\tif (this.b == null ? other.b == null : this.b.equals(other.b)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tTestCls other = new TestCls();\n\t\t\tother.a = \"a\";\n\t\t\tother.b = \"b\";\n\t\t\tassertThat(this.equals(other)).isTrue();\n\n\t\t\tother.b = \"not-b\";\n\t\t\tassertThat(this.equals(other)).isFalse();\n\n\t\t\tother.b = null;\n\t\t\tassertThat(this.equals(other)).isFalse();\n\n\t\t\tthis.b = null;\n\t\t\tassertThat(this.equals(other)).isTrue();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLines(2, \"if (this.a != null ? this.a.equals(other.a) : other.a == null) {\");\n\t\t// .containsLines(3, \"if (this.b != null ? this.b.equals(other.b) : other.b == null) {\")\n\t\t// .containsLines(4, \"return true;\")\n\t\t// .containsLines(2, \"return false;\")\n\t}\n\n\t@Test\n\t@NotYetImplemented\n\tpublic void testNYI() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLines(2, \"return (this.a != null ? this.a.equals(other.a) : other.a == null) \"\n\t\t\t\t\t\t+ \"&& (this.b == null ? other.b == null : this.b.equals(other.b));\");\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tgetClassNodeFromSmaliWithPath(\"conditions\", \"TestTernaryInIf2\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestTernaryInIf3.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\npublic class TestTernaryInIf3 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tgetClassNodeFromSmali();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestTernaryOneBranchInConstructor.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTernaryOneBranchInConstructor extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic TestCls(String str, int i) {\n\t\t\tthis(str == null ? 0 : i);\n\t\t}\n\n\t\tpublic TestCls(int i) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"this(str == null ? 0 : i);\")\n\t\t\t\t.doesNotContain(\"//\")\n\t\t\t\t.doesNotContain(\"call moved to the top of the method\");\n\t}\n\n\tpublic static class TestCls2 {\n\t\tpublic TestCls2(String str, int i) {\n\t\t\tthis(i == 1 ? str : \"\", i == 0 ? \"\" : str);\n\t\t}\n\n\t\tpublic TestCls2(String a, String b) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls2.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"this(i == 1 ? str : \\\"\\\", i == 0 ? \\\"\\\" : str);\")\n\t\t\t\t.doesNotContain(\"//\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/conditions/TestTernaryOneBranchInConstructor2.java",
    "content": "package jadx.tests.integration.conditions;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTernaryOneBranchInConstructor2 extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic class A {\n\t\t\tpublic A(String str, String str2, String str3, boolean z) {}\n\n\t\t\tpublic A(String str, String str2, String str3, boolean z, int i, int i2) {\n\t\t\t\tthis(str, (i & 2) != 0 ? \"\" : str2, (i & 4) != 0 ? \"\" : str3, (i & 8) != 0 ? false : z);\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"this(str, (i & 2) != 0 ? \\\"\\\" : str2, (i & 4) != 0 ? \\\"\\\" : str3, (i & 8) != 0 ? false : z);\")\n\t\t\t\t.doesNotContain(\"//\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/debuginfo/TestLineNumbers.java",
    "content": "package jadx.tests.integration.debuginfo;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.utils.CodeUtils;\nimport jadx.core.dex.attributes.nodes.LineAttrNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLineNumbers extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tint field;\n\n\t\tpublic void func() {\n\t\t}\n\n\t\tpublic static class Inner {\n\t\t\tint innerField;\n\n\t\t\tpublic void innerFunc() {\n\t\t\t}\n\n\t\t\tpublic void innerFunc2() {\n\t\t\t\tnew Runnable() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t}\n\t\t\t\t}.run();\n\t\t\t}\n\n\t\t\tpublic void innerFunc3() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tprintLineNumbers();\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tString code = cls.getCode().toString();\n\n\t\tFieldNode field = cls.searchFieldByName(\"field\");\n\t\tMethodNode func = cls.searchMethodByShortId(\"func()V\");\n\t\tClassNode inner = cls.getInnerClasses().get(0);\n\t\tMethodNode innerFunc = inner.searchMethodByShortId(\"innerFunc()V\");\n\t\tMethodNode innerFunc2 = inner.searchMethodByShortId(\"innerFunc2()V\");\n\t\tMethodNode innerFunc3 = inner.searchMethodByShortId(\"innerFunc3()V\");\n\t\tFieldNode innerField = inner.searchFieldByName(\"innerField\");\n\n\t\t// check source lines (available only for instructions and methods)\n\t\tint testClassLine = 16;\n\t\tassertThat(testClassLine + 3).isEqualTo(func.getSourceLine());\n\t\tassertThat(testClassLine + 9).isEqualTo(innerFunc.getSourceLine());\n\t\tassertThat(testClassLine + 12).isEqualTo(innerFunc2.getSourceLine());\n\t\tassertThat(testClassLine + 20).isEqualTo(innerFunc3.getSourceLine());\n\n\t\t// check decompiled lines\n\t\tcheckLine(code, field, \"int field;\");\n\t\tcheckLine(code, func, \"public void func() {\");\n\t\tcheckLine(code, inner, \"public static class Inner {\");\n\t\tcheckLine(code, innerField, \"int innerField;\");\n\t\tcheckLine(code, innerFunc, \"public void innerFunc() {\");\n\t\tcheckLine(code, innerFunc2, \"public void innerFunc2() {\");\n\t\tcheckLine(code, innerFunc3, \"public void innerFunc3() {\");\n\t}\n\n\tprivate static void checkLine(String code, LineAttrNode node, String str) {\n\t\tString line = CodeUtils.getLineForPos(code, node.getDefPosition());\n\t\tassertThat(line).contains(str);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/debuginfo/TestLineNumbers2.java",
    "content": "package jadx.tests.integration.debuginfo;\n\nimport java.lang.ref.WeakReference;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestLineNumbers2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate WeakReference<TestCls> f;\n\n\t\t// keep constructor at line 18\n\t\tpublic TestCls(TestCls s) {\n\t\t}\n\n\t\tpublic TestCls test(TestCls s) {\n\t\t\tTestCls store = f != null ? f.get() : null;\n\t\t\tif (store == null) {\n\t\t\t\tstore = new TestCls(s);\n\t\t\t\tf = new WeakReference<>(store);\n\t\t\t}\n\t\t\treturn store;\n\t\t}\n\n\t\tpublic Object test2() {\n\t\t\treturn new Object();\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.JAVA8 })\n\tpublic void test() {\n\t\tprintLineNumbers();\n\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tString linesMapStr = cls.getCode().getCodeMetadata().getLineMapping().toString();\n\t\tif (isJavaInput()) {\n\t\t\tassertThat(linesMapStr).isEqualTo(\"{6=16, 9=17, 12=21, 13=22, 14=23, 15=24, 16=25, 18=27, 21=30, 22=31}\");\n\t\t} else {\n\t\t\tassertThat(linesMapStr).isEqualTo(\"{6=16, 9=17, 12=21, 13=22, 14=23, 15=24, 16=25, 17=27, 19=27, 22=30, 23=31}\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/debuginfo/TestLineNumbers3.java",
    "content": "package jadx.tests.integration.debuginfo;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLineNumbers3 extends IntegrationTest {\n\n\tpublic static class TestCls extends Exception {\n\n\t\tpublic TestCls(final Object message) {\n\t\t\tsuper((message == null) ? \"\" : message.toString());\n\t\t\t/*\n\t\t\t * comment to increase line number in return instruction\n\t\t\t * -\n\t\t\t * -\n\t\t\t * -\n\t\t\t * -\n\t\t\t * -\n\t\t\t * -\n\t\t\t * -\n\t\t\t * -\n\t\t\t * -\n\t\t\t * -\n\t\t\t */\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.JAVA8 })\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls).code().containsOne(\"super(message == null ? \\\"\\\" : message.toString());\");\n\t\tString linesMapStr = cls.getCode().getCodeMetadata().getLineMapping().toString();\n\t\tassertThat(linesMapStr).isEqualTo(\"{4=13, 5=14, 6=15}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/debuginfo/TestReturnSourceLine.java",
    "content": "package jadx.tests.integration.debuginfo;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.utils.CodeUtils;\nimport jadx.core.dex.attributes.nodes.LineAttrNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestReturnSourceLine extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int test1(boolean v) {\n\t\t\tif (v) {\n\t\t\t\tf();\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tf();\n\t\t\treturn 0;\n\t\t}\n\n\t\tpublic int test2(int v) {\n\t\t\tif (v == 0) {\n\t\t\t\tf();\n\t\t\t\treturn v - 1;\n\t\t\t}\n\t\t\tf();\n\t\t\treturn v + 1;\n\t\t}\n\n\t\tpublic int test3(int v) {\n\t\t\tif (v == 0) {\n\t\t\t\tf();\n\t\t\t\treturn v;\n\t\t\t}\n\t\t\tf();\n\t\t\treturn v + 1;\n\t\t}\n\n\t\tprivate void f() {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tprintLineNumbers();\n\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tICodeInfo codeInfo = cls.getCode();\n\t\tString[] lines = codeInfo.getCodeStr().split(\"\\\\R\");\n\n\t\tMethodNode test1 = cls.searchMethodByShortId(\"test1(Z)I\");\n\t\tcheckLine(lines, codeInfo, test1, 3, \"return 1;\");\n\n\t\tMethodNode test2 = cls.searchMethodByShortId(\"test2(I)I\");\n\t\tcheckLine(lines, codeInfo, test2, 3, \"return v - 1;\");\n\t\tcheckLine(lines, codeInfo, test2, 6, \"return v + 1;\");\n\n\t\tMethodNode test3 = cls.searchMethodByShortId(\"test3(I)I\");\n\t\tif (isJavaInput()) { // dx lost line number for this return\n\t\t\tcheckLine(lines, codeInfo, test3, 3, \"return v;\");\n\t\t}\n\t\tcheckLine(lines, codeInfo, test3, 6, \"return v + 1;\");\n\t}\n\n\tprivate static void checkLine(String[] lines, ICodeInfo cw, LineAttrNode node, int offset, String str) {\n\t\tint nodeDefLine = CodeUtils.getLineNumForPos(cw.getCodeStr(), node.getDefPosition(), \"\\n\");\n\t\tint decompiledLine = nodeDefLine + offset;\n\t\tassertThat(lines[decompiledLine - 1]).containsOne(str);\n\t\tInteger sourceLine = cw.getCodeMetadata().getLineMapping().get(decompiledLine);\n\t\tassertThat(sourceLine).isNotNull();\n\t\tassertThat((int) sourceLine).isEqualTo(node.getSourceLine() + offset);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/debuginfo/TestVariablesNames.java",
    "content": "package jadx.tests.integration.debuginfo;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestVariablesNames extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic static class TestCls {\n\n\t\t\tpublic void test(String s, int k) {\n\t\t\t\tf1(s);\n\t\t\t\tint i = k + 3;\n\t\t\t\tString s2 = \"i\" + i;\n\t\t\t\tf2(i, s2);\n\t\t\t\tdouble d = i * 5;\n\t\t\t\tString s3 = \"d\" + d;\n\t\t\t\tf3(d, s3);\n\t\t\t}\n\n\t\t\tprivate void f1(String s) {\n\t\t\t}\n\n\t\t\tprivate void f2(int i, String i2) {\n\t\t\t}\n\n\t\t\tprivate void f3(double d, String d2) {\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t/**\n\t * Parameter register reused in variables assign with different types and names\n\t * No variables names in debug info\n\t */\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliWithPath(\"debuginfo\", \"TestVariablesNames\"))\n\t\t\t\t.code()\n\t\t\t\t// TODO: don't use current variables naming in tests\n\t\t\t\t.containsOne(\"f1(str);\")\n\t\t\t\t.containsOne(\"f2(i2, \\\"i\\\" + i2);\")\n\t\t\t\t.containsOne(\"f3(d, \\\"d\\\" + d);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/deobf/TestDontRenameClspOverriddenMethod.java",
    "content": "package jadx.tests.integration.deobf;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestDontRenameClspOverriddenMethod extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static class A implements Runnable {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t}\n\t\t}\n\n\t\tpublic static class B extends A {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tenableDeobfuscation();\n\t\targs.setDeobfuscationMinLength(100); // rename everything\n\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"public void run() {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/deobf/TestFieldFromInnerClass.java",
    "content": "package jadx.tests.integration.deobf;\n\nimport java.util.List;\nimport java.util.Queue;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestFieldFromInnerClass extends IntegrationTest {\n\n\tpublic static class TestCls<T> {\n\t\tTestCls<T>.I f;\n\n\t\tpublic class I {\n\t\t\tQueue<T> a;\n\n\t\t\tQueue<TestCls<T>.I> b;\n\n\t\t\tpublic class X {\n\t\t\t\tList<TestCls<T>.I.X> c;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tenableDeobfuscation();\n\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls)\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"class I {\")\n\t\t\t\t.doesNotContain(\".I \")\n\t\t\t\t.doesNotContain(\".I.X>\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/deobf/TestInheritedMethodRename.java",
    "content": "package jadx.tests.integration.deobf;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInheritedMethodRename extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static class A extends B {\n\t\t}\n\n\t\tpublic static class B {\n\t\t\tpublic void call() {\n\t\t\t\tSystem.out.println(\"call\");\n\t\t\t}\n\t\t}\n\n\t\tpublic void test(A a) {\n\t\t\t// reference to A.call() not renamed,\n\t\t\t// should be resolved to B.call() and use alias\n\t\t\ta.call();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tenableDeobfuscation();\n\t\tgetArgs().setDeobfuscationMinLength(99);\n\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public void m1call() {\")\n\t\t\t\t.doesNotContain(\".call();\")\n\t\t\t\t.containsOne(\".m1call();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/deobf/TestMthRename.java",
    "content": "package jadx.tests.integration.deobf;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestMthRename extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic abstract static class TestAbstractCls {\n\t\t\tpublic abstract void a();\n\t\t}\n\n\t\tpublic void test(TestAbstractCls a) {\n\t\t\ta.a();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tenableDeobfuscation();\n\t\tassertThat(getClassNode(TestCls.class)).code()\n\t\t\t\t.doesNotContain(\"public abstract void a();\")\n\t\t\t\t.doesNotContain(\".a();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/deobf/TestRenameOverriddenMethod.java",
    "content": "package jadx.tests.integration.deobf;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestRenameOverriddenMethod extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic interface I {\n\t\t\tvoid m();\n\t\t}\n\n\t\tpublic static class A implements I {\n\t\t\t@Override\n\t\t\tpublic void m() {\n\t\t\t}\n\t\t}\n\n\t\tpublic static class B extends A {\n\t\t\t@Override\n\t\t\tpublic void m() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tenableDeobfuscation();\n\t\targs.setDeobfuscationMinLength(100); // rename everything\n\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"@Override\")\n\t\t\t\t.countString(3, \"renamed from: m\")\n\t\t\t\t.containsOne(\"void mo0m();\")\n\t\t\t\t.countString(2, \"public void mo0m() {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/deobf/TestRenameOverriddenMethod2.java",
    "content": "package jadx.tests.integration.deobf;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestRenameOverriddenMethod2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic interface I {\n\t\t\tint call();\n\t\t}\n\n\t\tpublic static class A implements I {\n\t\t\t@Override\n\t\t\tpublic int call() {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\n\t\tpublic static class B implements I {\n\t\t\t@Override\n\t\t\tpublic int call() {\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tenableDeobfuscation();\n\t\targs.setDeobfuscationMinLength(100); // rename everything\n\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls)\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"@Override\")\n\t\t\t\t.countString(3, \"int mo0call()\");\n\n\t\tassertThat(searchCls(cls.getInnerClasses(), \"I\")).isNotNull()\n\t\t\t\t.extracting(c -> c.searchMethodByShortName(\"call\")).isNotNull()\n\t\t\t\t.extracting(m -> m.get(AType.METHOD_OVERRIDE)).isNotNull()\n\t\t\t\t.satisfies(ovrdAttr -> {\n\t\t\t\t\tassertThat(ovrdAttr.getRelatedMthNodes()).hasSize(3);\n\t\t\t\t\tassertThat(ovrdAttr.getOverrideList()).isEmpty();\n\t\t\t\t});\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/deobf/TestRenameOverriddenMethod3.java",
    "content": "package jadx.tests.integration.deobf;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestRenameOverriddenMethod3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic abstract static class A {\n\t\t\tpublic abstract int call();\n\t\t}\n\n\t\tpublic static class B extends A {\n\t\t\t@Override\n\t\t\tpublic final int call() {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\taddMthRename(TestCls.class.getName() + \"$A\", \"call()I\", \"callRenamed\");\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(1, \"@Override\")\n\t\t\t\t.countString(2, \"int callRenamed()\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/deobf/a/TestNegativeRenameCondition.java",
    "content": "package jadx.tests.integration.deobf.a;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestNegativeRenameCondition extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\t@SuppressWarnings(\"checkstyle:TypeName\")\n\t\tpublic interface a {\n\n\t\t\t@SuppressWarnings(\"checkstyle:MethodName\")\n\t\t\tvoid a();\n\t\t}\n\n\t\tpublic void test(a a) {\n\t\t\ta.a();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tenableDeobfuscation();\n\t\t// disable rename by length\n\t\targs.setDeobfuscationMinLength(0);\n\t\targs.setDeobfuscationMaxLength(999);\n\t\t// disable all renaming options\n\t\targs.setRenameFlags(Collections.emptySet());\n\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"renamed from\")\n\t\t\t\t.containsOne(\"package jadx.tests.integration.deobf.a;\")\n\t\t\t\t.containsOne(\"public interface a {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumKotlinEntries.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Test for Kotlin 1.9+ enum $ENTRIES pattern.\n */\npublic class TestEnumKotlinEntries extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation(); // kotlin.enums.EnumEntries not on test classpath\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsLines(1, \"ALPHA,\", \"BETA,\", \"GAMMA;\")\n\t\t\t\t.containsOne(\"EnumEntries $ENTRIES = EnumEntriesKt.enumEntries(values());\")\n\t\t\t\t.doesNotContain(\"$VALUES\")\n\t\t\t\t.doesNotContain(\"Failed to restore enum\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumObfuscated.java",
    "content": "package jadx.tests.integration.enums;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.CommentsLevel;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEnumObfuscated extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic enum TestEnumObfuscated {\n\t\t\tprivate static final synthetic TestEnumObfuscated[] $VLS = {ONE, TWO};\n\t\t\tpublic static final TestEnumObfuscated ONE = new TestEnumObfuscated(\"ONE\", 0, 1);\n\t\t\tpublic static final TestEnumObfuscated TWO = new TestEnumObfuscated(\"TWO\", 1, 2);\n\t\t\tprivate final int num;\n\n\t\t\tprivate TestEnumObfuscated(String str, int i, int i2) {\n\t\t\t\tsuper(str, i);\n\t\t\t\tthis.num = i2;\n\t\t\t}\n\n\t\t\tpublic static TestEnumObfuscated vo(String str) {\n\t\t\t\treturn (TestEnumObfuscated) Enum.valueOf(TestEnumObfuscated.class, str);\n\t\t\t}\n\n\t\t\tpublic static TestEnumObfuscated[] vs() {\n\t\t\t\treturn (TestEnumObfuscated[]) $VLS.clone();\n\t\t\t}\n\n\t\t\tpublic synthetic int getNum() {\n\t\t\t\treturn this.num;\n\t\t\t}\n\n\t\t\t// custom values method\n\t\t\t// should be kept and renamed to avoid collision to enum 'values()' method\n\t\t\tpublic static int values() {\n\t\t\t\treturn new TestEnumObfuscated[0];\n\t\t\t}\n\n\t\t\t// usage of renamed 'values()' method, should be renamed back to 'values'\n\t\t\tpublic static int valuesCount() {\n\t\t\t\treturn vs().length;\n\t\t\t}\n\n\t\t\t// usage of renamed '$VALUES' field, should be replaced with 'values()' method call\n\t\t\tpublic static int valuesFieldUse() {\n\t\t\t\treturn $VLS.length;\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tgetArgs().setCommentsLevel(CommentsLevel.WARN);\n\t\tgetArgs().setRenameFlags(Collections.emptySet());\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"$VLS\")\n\t\t\t\t.doesNotContain(\"vo(\")\n\t\t\t\t.doesNotContain(\"vs(\")\n\t\t\t\t.containsOne(\"int getNum() {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumUsesOtherEnum.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEnumUsesOtherEnum extends SmaliTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic enum VType {\n\t\t\tINT(1),\n\t\t\tOTHER_INT(INT);\n\n\t\t\tprivate final int type;\n\n\t\t\tVType(int type) {\n\t\t\t\tthis.type = type;\n\t\t\t}\n\n\t\t\tVType(VType refType) {\n\t\t\t\tthis(refType.type);\n\t\t\t}\n\t\t}\n\t}\n\n\t@TestWithProfiles(TestProfile.D8_J11)\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"OTHER_INT(INT);\")\n\t\t\t\t.doesNotContain(\"\\n        \\n\"); // no indentation for empty string\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public enum TestEnumUsesOtherEnum {\")\n\t\t\t\t.doesNotContain(\"static {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumWithConstInlining.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestEnumWithConstInlining extends SmaliTest {\n\tenum TestCls {\n\t\tE0,\n\t\tE1,\n\t\tE2,\n\t\tE3,\n\t\tE4,\n\t\tE5,\n\t\tE6,\n\t\tE7,\n\t\tE8,\n\t\tE9,\n\t\tE10,\n\t\tE11,\n\t\tE12,\n\t\tE13,\n\t\tE14,\n\t\tE15,\n\t\tE16,\n\t\tE17,\n\t\tE18,\n\t\tE19,\n\t\tE20,\n\t\tE21,\n\t\tE22,\n\t\tE23,\n\t\tE24,\n\t\tE25,\n\t\tE26,\n\t\tE27,\n\t\tE28,\n\t\tE29,\n\t\tE30,\n\t\tE31,\n\t\tE32,\n\t\tE33,\n\t\tE34,\n\t\tE35,\n\t\tE36,\n\t\tE37,\n\t\tE38,\n\t\tE39,\n\t\tE40,\n\t\tE41,\n\t\tE42,\n\t\tE43,\n\t\tE44,\n\t\tE45,\n\t\tE46,\n\t\tE47,\n\t\tE48,\n\t\tE49,\n\t\tE50,\n\t\tE51,\n\t\tE52,\n\t\tE53,\n\t\tE54,\n\t\tE55,\n\t\tE56,\n\t\tE57,\n\t\tE58,\n\t\tE59,\n\t\tE60,\n\t\tE61,\n\t\tE62,\n\t\tE63,\n\t\tE64,\n\t\tE65,\n\t\tE66,\n\t\tE67,\n\t\tE68,\n\t\tE69,\n\t\tE70,\n\t\tE71,\n\t\tE72,\n\t\tE73,\n\t\tE74,\n\t\tE75,\n\t\tE76,\n\t\tE77,\n\t\tE78,\n\t\tE79,\n\t\tE80,\n\t\tE81,\n\t\tE82,\n\t\tE83,\n\t\tE84,\n\t\tE85,\n\t\tE86,\n\t\tE87,\n\t\tE88,\n\t\tE89,\n\t\tE90,\n\t\tE91,\n\t\tE92,\n\t\tE93,\n\t\tE94,\n\t\tE95,\n\t\tE96,\n\t\tE97,\n\t\tE98,\n\t\tE99,\n\t\tE100;\n\n\t\t/**\n\t\t * Match the length of the $VALUES array.\n\t\t */\n\t\tpublic static final int CONST = 101;\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"E42,\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumWithFields.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestEnumWithFields extends SmaliTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic enum SearchTimeout {\n\t\t\tDISABLED(0), TWO_SECONDS(2), FIVE_SECONDS(5);\n\n\t\t\tpublic static final SearchTimeout DEFAULT = DISABLED;\n\t\t\tpublic static final SearchTimeout MAX = FIVE_SECONDS;\n\n\t\t\tpublic final int sec;\n\n\t\t\tSearchTimeout(int val) {\n\t\t\t\tthis.sec = val;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(SearchTimeout.DISABLED.sec).isEqualTo(0);\n\t\t\tassertThat(SearchTimeout.DEFAULT.sec).isEqualTo(0);\n\t\t\tassertThat(SearchTimeout.TWO_SECONDS.sec).isEqualTo(2);\n\t\t\tassertThat(SearchTimeout.MAX.sec).isEqualTo(5);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code();\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEnums extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic enum EmptyEnum {\n\t\t}\n\n\t\t@SuppressWarnings(\"NoWhitespaceBefore\")\n\t\tpublic enum EmptyEnum2 {\n\t\t\t;\n\n\t\t\tpublic static void mth() {\n\t\t\t}\n\t\t}\n\n\t\tpublic enum Direction {\n\t\t\tNORTH,\n\t\t\tSOUTH,\n\t\t\tEAST,\n\t\t\tWEST\n\t\t}\n\n\t\tpublic enum Singleton {\n\t\t\tINSTANCE;\n\n\t\t\tpublic String test() {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLines(1, \"public enum EmptyEnum {\", \"}\")\n\t\t\t\t.containsLines(1,\n\t\t\t\t\t\t\"public enum EmptyEnum2 {\",\n\t\t\t\t\t\tindent(1) + ';',\n\t\t\t\t\t\t\"\",\n\t\t\t\t\t\tindent(1) + \"public static void mth() {\",\n\t\t\t\t\t\tindent(1) + '}',\n\t\t\t\t\t\t\"}\")\n\t\t\t\t.containsLines(1, \"public enum Direction {\",\n\t\t\t\t\t\tindent(1) + \"NORTH,\",\n\t\t\t\t\t\tindent(1) + \"SOUTH,\",\n\t\t\t\t\t\tindent(1) + \"EAST,\",\n\t\t\t\t\t\tindent(1) + \"WEST\",\n\t\t\t\t\t\t\"}\")\n\t\t\t\t.containsLines(1, \"public enum Singleton {\",\n\t\t\t\t\t\tindent(1) + \"INSTANCE;\",\n\t\t\t\t\t\t\"\",\n\t\t\t\t\t\tindent(1) + \"public String test() {\",\n\t\t\t\t\t\tindent(2) + \"return \\\"\\\";\",\n\t\t\t\t\t\tindent(1) + '}',\n\t\t\t\t\t\t\"}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums10.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Some enum field was removed, but still exist in values array\n */\npublic class TestEnums10 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"Failed to restore enum class\")\n\t\t\t\t.containsOne(\"enum TestEnums10 {\")\n\t\t\t\t.countString(4, \"Fake field\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums11.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.RaungTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEnums11 extends RaungTest {\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromRaung())\n\t\t\t\t.code()\n\t\t\t\t.containsLines(\"public enum TestEnums11 {\", indent(1) + \"UNKNOWN;\")\n\t\t\t\t.containsOne(\"public final int a = -99;\")\n\t\t\t\t.doesNotContain(\"TestEnums11() {\");\n\t}\n\n\t@Test\n\tpublic void testDisableEnumRestore() {\n\t\t// constructor method incorrectly removed\n\t\tgetArgs().getDisabledPasses().add(\"EnumVisitor\");\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromRaung())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public TestEnums11() {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums2.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.CommentsLevel;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEnums2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic enum Operation {\n\t\t\tPLUS {\n\t\t\t\t@Override\n\t\t\t\tpublic int apply(int x, int y) {\n\t\t\t\t\treturn x + y;\n\t\t\t\t}\n\t\t\t},\n\t\t\tMINUS {\n\t\t\t\t@Override\n\t\t\t\tpublic int apply(int x, int y) {\n\t\t\t\t\treturn x - y;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tpublic abstract int apply(int x, int y);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tgetArgs().setCommentsLevel(CommentsLevel.WARN);\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLines(1,\n\t\t\t\t\t\t\"public enum Operation {\",\n\t\t\t\t\t\tindent(1) + \"PLUS {\",\n\t\t\t\t\t\tindent(2) + \"@Override\",\n\t\t\t\t\t\tindent(2) + \"public int apply(int x, int y) {\",\n\t\t\t\t\t\tindent(3) + \"return x + y;\",\n\t\t\t\t\t\tindent(2) + '}',\n\t\t\t\t\t\tindent(1) + \"},\",\n\t\t\t\t\t\tindent(1) + \"MINUS {\",\n\t\t\t\t\t\tindent(2) + \"@Override\",\n\t\t\t\t\t\tindent(2) + \"public int apply(int x, int y) {\",\n\t\t\t\t\t\tindent(3) + \"return x - y;\",\n\t\t\t\t\t\tindent(2) + '}',\n\t\t\t\t\t\tindent(1) + \"};\",\n\t\t\t\t\t\t\"\",\n\t\t\t\t\t\tindent(1) + \"public abstract int apply(int i, int i2);\",\n\t\t\t\t\t\t\"}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums2a.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static jadx.tests.integration.enums.TestEnums2a.TestCls.DoubleOperations.DIVIDE;\nimport static jadx.tests.integration.enums.TestEnums2a.TestCls.DoubleOperations.TIMES;\n\npublic class TestEnums2a extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic interface IOps {\n\t\t\tdouble apply(double x, double y);\n\t\t}\n\n\t\tpublic enum DoubleOperations implements IOps {\n\t\t\tTIMES(\"*\") {\n\t\t\t\t@Override\n\t\t\t\tpublic double apply(double x, double y) {\n\t\t\t\t\treturn x * y;\n\t\t\t\t}\n\t\t\t},\n\t\t\tDIVIDE(\"/\") {\n\t\t\t\t@Override\n\t\t\t\tpublic double apply(double x, double y) {\n\t\t\t\t\treturn x / y;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tprivate final String op;\n\n\t\t\tDoubleOperations(String op) {\n\t\t\t\tthis.op = op;\n\t\t\t}\n\n\t\t\tpublic String getOp() {\n\t\t\t\treturn op;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(TIMES.getOp()).isEqualTo(\"*\");\n\t\t\tassertThat(DIVIDE.getOp()).isEqualTo(\"/\");\n\n\t\t\tassertThat(TIMES.apply(2, 3)).isEqualTo(6);\n\t\t\tassertThat(DIVIDE.apply(10, 5)).isEqualTo(2);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"TIMES(\\\"*\\\") {\")\n\t\t\t\t.containsOne(\"DIVIDE(\\\"/\\\")\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums3.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestEnums3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tprivate static int three = 3;\n\n\t\tpublic enum Numbers {\n\t\t\tONE(1), TWO(2), THREE(three), FOUR(three + 1);\n\n\t\t\tprivate final int num;\n\n\t\t\tNumbers(int n) {\n\t\t\t\tthis.num = n;\n\t\t\t}\n\n\t\t\tpublic int getNum() {\n\t\t\t\treturn num;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(Numbers.ONE.getNum()).isEqualTo(1);\n\t\t\tassertThat(Numbers.THREE.getNum()).isEqualTo(3);\n\t\t\tassertThat(Numbers.FOUR.getNum()).isEqualTo(4);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"ONE(1)\")\n\t\t\t\t.containsOne(\"Numbers(int n) {\");\n\t\t// assertThat(code, containsOne(\"THREE(three)\"));\n\t\t// assertThat(code, containsOne(\"assertTrue(Numbers.ONE.getNum() == 1);\"));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums4.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEnums4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic enum ResType {\n\t\t\tCODE(\".dex\", \".class\"),\n\t\t\tMANIFEST(\"AndroidManifest.xml\"),\n\t\t\tXML(\".xml\"),\n\t\t\tARSC(\".arsc\"),\n\t\t\tFONT(\".ttf\"),\n\t\t\tIMG(\".png\", \".gif\", \".jpg\"),\n\t\t\tLIB(\".so\"),\n\t\t\tUNKNOWN;\n\n\t\t\tprivate final String[] exts;\n\n\t\t\tResType(String... extensions) {\n\t\t\t\tthis.exts = extensions;\n\t\t\t}\n\n\t\t\tpublic String[] getExts() {\n\t\t\t\treturn exts;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(ResType.CODE.getExts()).containsExactly(new String[] { \".dex\", \".class\" });\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"CODE(\\\".dex\\\", \\\".class\\\"),\")\n\t\t\t\t.containsOne(\"ResType(String... extensions) {\");\n\t\t// assertThat(code, not(containsString(\"private ResType\")));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums5.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEnums5 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliWithClsName(\"kotlin.collections.State\"))\n\t\t\t\t.code()\n\t\t\t\t.containsLines(\n\t\t\t\t\t\t\"enum State {\",\n\t\t\t\t\t\tindent() + \"Ready,\",\n\t\t\t\t\t\tindent() + \"NotReady,\",\n\t\t\t\t\t\tindent() + \"Done,\",\n\t\t\t\t\t\tindent() + \"Failed\",\n\t\t\t\t\t\t\"}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums6.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestEnums6 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic enum Numbers {\n\t\t\tZERO,\n\t\t\tONE(1);\n\n\t\t\tprivate final int n;\n\n\t\t\tNumbers() {\n\t\t\t\tthis(0);\n\t\t\t}\n\n\t\t\tNumbers(int n) {\n\t\t\t\tthis.n = n;\n\t\t\t}\n\n\t\t\tpublic int getN() {\n\t\t\t\treturn n;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(TestCls.Numbers.ZERO.getN()).isEqualTo(0);\n\t\t\tassertThat(TestCls.Numbers.ONE.getN()).isEqualTo(1);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"ZERO,\")\n\t\t\t\t.containsOne(\"Numbers() {\")\n\t\t\t\t.containsOne(\"ONE(1);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums7.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEnums7 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic enum Numbers {\n\t\t\tZERO,\n\t\t\tONE;\n\n\t\t\tprivate final int n;\n\n\t\t\tNumbers() {\n\t\t\t\tthis.n = this.name().equals(\"ZERO\") ? 0 : 1;\n\t\t\t}\n\n\t\t\tpublic int getN() {\n\t\t\t\treturn n;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(Numbers.ZERO.getN()).isEqualTo(0);\n\t\t\tassertThat(Numbers.ONE.getN()).isEqualTo(1);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"ZERO,\")\n\t\t\t\t.containsOne(\"ONE;\")\n\t\t\t\t.containsOne(\"Numbers() {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums8.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEnums8 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"enum TestEnums8\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums9.java",
    "content": "package jadx.tests.integration.enums;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEnums9 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic enum Types {\n\t\t\tINT,\n\t\t\tFLOAT,\n\t\t\tLONG,\n\t\t\tDOUBLE,\n\t\t\tOBJECT,\n\t\t\tARRAY;\n\n\t\t\tprivate static Set<Types> primitives = EnumSet.of(INT, FLOAT, LONG, DOUBLE);\n\t\t\tpublic static List<Types> references = new ArrayList<>();\n\n\t\t\tstatic {\n\t\t\t\treferences.add(OBJECT);\n\t\t\t\treferences.add(ARRAY);\n\t\t\t}\n\n\t\t\tpublic static Set<Types> getPrimitives() {\n\t\t\t\treturn primitives;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(Types.getPrimitives()).contains(Types.INT);\n\t\t\tassertThat(Types.references).hasSize(2);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"EnumSet.of((Enum) INT,\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsInterface.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.CommentsLevel;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEnumsInterface extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic enum Operation implements IOperation {\n\t\t\tPLUS {\n\t\t\t\t@Override\n\t\t\t\tpublic int apply(int x, int y) {\n\t\t\t\t\treturn x + y;\n\t\t\t\t}\n\t\t\t},\n\t\t\tMINUS {\n\t\t\t\t@Override\n\t\t\t\tpublic int apply(int x, int y) {\n\t\t\t\t\treturn x - y;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic interface IOperation {\n\t\t\tint apply(int x, int y);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tgetArgs().setCommentsLevel(CommentsLevel.WARN);\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLines(1,\n\t\t\t\t\t\t\"public enum Operation implements IOperation {\",\n\t\t\t\t\t\tindent(1) + \"PLUS {\",\n\t\t\t\t\t\tindent(2) + \"@Override\",\n\t\t\t\t\t\tindent(2) + \"public int apply(int x, int y) {\",\n\t\t\t\t\t\tindent(3) + \"return x + y;\",\n\t\t\t\t\t\tindent(2) + '}',\n\t\t\t\t\t\tindent(1) + \"},\",\n\t\t\t\t\t\tindent(1) + \"MINUS {\",\n\t\t\t\t\t\tindent(2) + \"@Override\",\n\t\t\t\t\t\tindent(2) + \"public int apply(int x, int y) {\",\n\t\t\t\t\t\tindent(3) + \"return x - y;\",\n\t\t\t\t\t\tindent(2) + '}',\n\t\t\t\t\t\tindent(1) + '}',\n\t\t\t\t\t\t\"}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsWithAssert.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEnumsWithAssert extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic enum Numbers {\n\t\t\tONE(1), TWO(2), THREE(3);\n\n\t\t\tprivate final int num;\n\n\t\t\tNumbers(int n) {\n\t\t\t\tthis.num = n;\n\t\t\t}\n\n\t\t\tpublic int getNum() {\n\t\t\t\tassert num > 0;\n\t\t\t\treturn num;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class)).code()\n\t\t\t\t.containsOne(\"ONE(1)\")\n\t\t\t\t.doesNotContain(\"Failed to restore enum class\");\n\t}\n\n\t@NotYetImplemented(\"handle java assert\")\n\t@Test\n\tpublic void testNYI() {\n\t\tassertThat(getClassNode(TestCls.class)).code()\n\t\t\t\t.containsOne(\"assert num > 0;\")\n\t\t\t\t.doesNotContain(\"$assertionsDisabled\")\n\t\t\t\t.doesNotContain(\"throw new AssertionError()\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsWithConsts.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEnumsWithConsts extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static final int C1 = 1;\n\t\tpublic static final int C2 = 2;\n\t\tpublic static final int C4 = 4;\n\n\t\tpublic static final String S = \"NORTH\";\n\n\t\tpublic enum Direction {\n\t\t\tNORTH,\n\t\t\tSOUTH,\n\t\t\tEAST,\n\t\t\tWEST\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLines(1, \"public enum Direction {\",\n\t\t\t\t\t\tindent(1) + \"NORTH,\",\n\t\t\t\t\t\tindent(1) + \"SOUTH,\",\n\t\t\t\t\t\tindent(1) + \"EAST,\",\n\t\t\t\t\t\tindent(1) + \"WEST\",\n\t\t\t\t\t\t\"}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsWithCustomInit.java",
    "content": "package jadx.tests.integration.enums;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEnumsWithCustomInit extends IntegrationTest {\n\n\tpublic enum TestCls {\n\t\tONE(\"I\"),\n\t\tTWO(\"II\"),\n\t\tTHREE(\"III\");\n\n\t\tpublic static final Map<String, TestCls> MAP = new HashMap<>();\n\n\t\tstatic {\n\t\t\tfor (TestCls value : values()) {\n\t\t\t\tMAP.put(value.toString(), value);\n\t\t\t}\n\t\t}\n\n\t\tprivate final String str;\n\n\t\tTestCls(String str) {\n\t\t\tthis.str = str;\n\t\t}\n\n\t\tpublic String toString() {\n\t\t\treturn str;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"ONE(\\\"I\\\"),\")\n\t\t\t\t.doesNotContain(\"new TestEnumsWithCustomInit$TestCls(\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsWithStaticFields.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEnumsWithStaticFields extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOnlyOnce(\"INSTANCE;\")\n\t\t\t\t.containsOnlyOnce(\"private static c sB\")\n\t\t\t\t.doesNotContain(\" sA\")\n\t\t\t\t.doesNotContain(\" sC\")\n\t\t\t\t.doesNotContain(\"private TestEnumsWithStaticFields(String str) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsWithTernary.java",
    "content": "package jadx.tests.integration.enums;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEnumsWithTernary extends IntegrationTest {\n\n\tpublic enum TestCls {\n\t\tFIRST(useNumber() ? \"1\" : \"A\"),\n\t\tSECOND(useNumber() ? \"2\" : \"B\"),\n\t\tANY(useNumber() ? \"1\" : \"2\");\n\n\t\tprivate final String str;\n\n\t\tTestCls(String str) {\n\t\t\tthis.str = str;\n\t\t}\n\n\t\tpublic String getStr() {\n\t\t\treturn str;\n\t\t}\n\n\t\tpublic static boolean useNumber() {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.D8_J8 })\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"ANY(useNumber() ? \\\"1\\\" : \\\"2\\\");\")\n\t\t\t\t.doesNotContain(\"static {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestInnerEnums.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestInnerEnums extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic enum Numbers {\n\t\t\tONE((byte) 1, NumString.ONE), TWO((byte) 2, NumString.TWO);\n\n\t\t\tprivate final byte num;\n\t\t\tprivate final NumString str;\n\n\t\t\tpublic enum NumString {\n\t\t\t\tONE(\"one\"), TWO(\"two\");\n\n\t\t\t\tprivate final String name;\n\n\t\t\t\tNumString(String name) {\n\t\t\t\t\tthis.name = name;\n\t\t\t\t}\n\n\t\t\t\tpublic String getName() {\n\t\t\t\t\treturn name;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tNumbers(byte n, NumString str) {\n\t\t\t\tthis.num = n;\n\t\t\t\tthis.str = str;\n\t\t\t}\n\n\t\t\tpublic int getNum() {\n\t\t\t\treturn num;\n\t\t\t}\n\n\t\t\tpublic NumString getNumStr() {\n\t\t\t\treturn str;\n\t\t\t}\n\n\t\t\tpublic String getName() {\n\t\t\t\treturn str.getName();\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(Numbers.ONE.getNum()).isEqualTo(1);\n\t\t\tassertThat(Numbers.ONE.getNumStr()).isEqualTo(Numbers.NumString.ONE);\n\t\t\tassertThat(Numbers.ONE.getName()).isEqualTo(\"one\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"ONE((byte) 1, NumString.ONE)\")\n\t\t\t\t.containsOne(\"ONE(\\\"one\\\")\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestSwitchOverEnum.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchOverEnum extends SmaliTest {\n\n\tpublic enum Count {\n\t\tONE, TWO, THREE\n\t}\n\n\tpublic int testEnum(Count c) {\n\t\tswitch (c) {\n\t\t\tcase ONE:\n\t\t\t\treturn 1;\n\t\t\tcase TWO:\n\t\t\t\treturn 2;\n\t\t}\n\t\treturn 0;\n\t}\n\n\tpublic void check() {\n\t\tassertThat(testEnum(Count.ONE)).isEqualTo(1);\n\t\tassertThat(testEnum(Count.TWO)).isEqualTo(2);\n\t\tassertThat(testEnum(Count.THREE)).isEqualTo(0);\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\t// remapping array placed in top class, place test also in top class\n\t\tassertThat(getClassNode(TestSwitchOverEnum.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(1, \"synthetic\")\n\t\t\t\t.countString(2, \"switch (c) {\")\n\t\t\t\t.countString(3, \"case ONE:\");\n\t}\n\n\t/**\n\t * Java 21 compiler can omit a remapping array and use switch over ordinal directly\n\t */\n\t@Test\n\tpublic void testSmaliDirect() {\n\t\tassertThat(getClassNodeFromSmaliFiles())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"switch (v) {\")\n\t\t\t\t.containsOne(\"case ONE:\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/enums/TestSwitchOverEnum2.java",
    "content": "package jadx.tests.integration.enums;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestSwitchOverEnum2 extends IntegrationTest {\n\n\tpublic enum Count {\n\t\tONE, TWO, THREE\n\t}\n\n\tpublic enum Animal {\n\t\tCAT, DOG\n\t}\n\n\tpublic int testEnum(Count c, Animal a) {\n\t\tint result = 0;\n\t\tswitch (c) {\n\t\t\tcase ONE:\n\t\t\t\tresult = 1;\n\t\t\t\tbreak;\n\t\t\tcase TWO:\n\t\t\t\tresult = 2;\n\t\t\t\tbreak;\n\t\t}\n\t\tswitch (a) {\n\t\t\tcase CAT:\n\t\t\t\tresult += 10;\n\t\t\t\tbreak;\n\t\t\tcase DOG:\n\t\t\t\tresult += 20;\n\t\t\t\tbreak;\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic void check() {\n\t\tassertThat(testEnum(Count.ONE, Animal.DOG)).isEqualTo(21);\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestSwitchOverEnum2.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(1, \"synthetic\")\n\t\t\t\t.countString(2, \"switch (c) {\")\n\t\t\t\t.countString(2, \"case ONE:\")\n\t\t\t\t.countString(2, \"case DOG:\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/fallback/TestFallbackManyNops.java",
    "content": "package jadx.tests.integration.fallback;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestFallbackManyNops extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tsetFallback();\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.contains(\"public static void test() {\")\n\t\t\t\t.containsOne(\"return\")\n\t\t\t\t.doesNotContain(\"Method dump skipped\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/fallback/TestFallbackMode.java",
    "content": "package jadx.tests.integration.fallback;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestFallbackMode extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic int test(int a) {\n\t\t\twhile (a < 10) {\n\t\t\t\ta++;\n\t\t\t}\n\t\t\treturn a;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tuseDexInput();\n\t\tsetFallback();\n\t\tdisableCompilation();\n\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"public int test(int r2) {\")\n\t\t\t\t.containsOne(\"r1 = this;\")\n\t\t\t\t.containsOne(\"L0:\")\n\t\t\t\t.containsOne(\"L7:\")\n\t\t\t\t.containsOne(\"int r2 = r2 + 1\")\n\t\t\t\t.doesNotContain(\"throw new UnsupportedOperationException\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestClassSignature.java",
    "content": "package jadx.tests.integration.generics;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestClassSignature extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tIncorrect class signature, super class is equals to this class: <T:Ljava/lang/Object;>Lgenerics/TestClassSignature<TT;>;\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tallowWarnInCode();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"Incorrect class signature\")\n\t\t\t\t.doesNotContain(\"StackOverflowError\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestConstructorGenerics.java",
    "content": "package jadx.tests.integration.generics;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestConstructorGenerics extends IntegrationTest {\n\n\t@SuppressWarnings({ \"MismatchedQueryAndUpdateOfCollection\", \"RedundantOperationOnEmptyContainer\" })\n\tpublic static class TestCls {\n\t\tpublic String test() {\n\t\t\tMap<String, String> map = new HashMap<>();\n\t\t\treturn map.get(\"test\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"Map<String, String> map = new HashMap<>();\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return (String) new HashMap().get(\\\"test\\\");\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestGeneric8.java",
    "content": "package jadx.tests.integration.generics;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestGeneric8 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\t\tpublic class TestNumber<T extends Integer> {\n\t\t\tprivate final T n;\n\n\t\t\tpublic TestNumber(T n) {\n\t\t\t\tthis.n = n;\n\t\t\t}\n\n\t\t\tpublic boolean isEven() {\n\t\t\t\treturn n.intValue() % 2 == 0;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public TestNumber(T n\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestGenericFields.java",
    "content": "package jadx.tests.integration.generics;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestGenericFields extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static class Summary {\n\t\t\tValue<Amount> price;\n\t\t}\n\n\t\tpublic static class Value<T> {\n\t\t\tT value;\n\t\t}\n\n\t\tpublic static class Amount {\n\t\t\tString cur;\n\t\t\tint val;\n\t\t}\n\n\t\tpublic String test(Summary summary) {\n\t\t\tAmount amount = summary.price.value;\n\t\t\treturn amount.val + \" \" + amount.cur;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"T t = \")\n\t\t\t\t.containsOne(\"Amount amount =\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestGenerics.java",
    "content": "package jadx.tests.integration.generics;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestGenerics extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tclass A {\n\t\t}\n\n\t\tpublic static void mthWildcard(List<?> list) {\n\t\t}\n\n\t\tpublic static void mthExtends(List<? extends A> list) {\n\t\t}\n\n\t\tpublic static void mthSuper(List<? super A> list) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"mthWildcard(List<?> list)\")\n\t\t\t\t.contains(\"mthExtends(List<? extends A> list)\")\n\t\t\t\t.contains(\"mthSuper(List<? super A> list)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestGenerics2.java",
    "content": "package jadx.tests.integration.generics;\n\nimport java.lang.ref.ReferenceQueue;\nimport java.lang.ref.WeakReference;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestGenerics2 extends IntegrationTest {\n\n\t@SuppressWarnings(\"MismatchedQueryAndUpdateOfCollection\")\n\tpublic static class TestCls {\n\t\tpublic static class ItemReference<V> extends WeakReference<V> {\n\t\t\tpublic Object id;\n\n\t\t\tpublic ItemReference(V item, Object objId, ReferenceQueue<? super V> queue) {\n\t\t\t\tsuper(item, queue);\n\t\t\t\tthis.id = objId;\n\t\t\t}\n\t\t}\n\n\t\tpublic static class ItemReferences<V> {\n\t\t\tprivate Map<Object, ItemReference<V>> items;\n\n\t\t\tpublic V get(Object id) {\n\t\t\t\tWeakReference<V> ref = this.items.get(id);\n\t\t\t\tif (ref != null) {\n\t\t\t\t\treturn ref.get();\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public ItemReference(V item, Object objId, ReferenceQueue<? super V> queue) {\")\n\t\t\t\t.containsOne(\"public V get(Object id) {\")\n\t\t\t\t.containsOne(\"WeakReference<V> ref = \")\n\t\t\t\t.containsOne(\"return ref.get();\");\n\t}\n\n\t@Test\n\tpublic void testDebug() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"ItemReference<V> itemReference = this.items.get(obj);\")\n\t\t\t\t.containsOne(\"return itemReference.get();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestGenerics3.java",
    "content": "package jadx.tests.integration.generics;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestGenerics3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static void mthExtendsArray(List<? extends byte[]> list) {\n\t\t}\n\n\t\tpublic static void mthSuperArray(List<? super int[]> list) {\n\t\t}\n\n\t\tpublic static void mthSuperInteger(List<? super Integer> list) {\n\t\t}\n\n\t\tpublic static void mthExtendsString(List<? super String> list) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"mthExtendsArray(List<? extends byte[]> list)\")\n\t\t\t\t.contains(\"mthSuperArray(List<? super int[]> list)\")\n\t\t\t\t.contains(\"mthSuperInteger(List<? super Integer> list)\")\n\t\t\t\t.contains(\"mthExtendsString(List<? super String> list)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestGenerics4.java",
    "content": "package jadx.tests.integration.generics;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestGenerics4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static Class<?> method(int i) {\n\t\t\tClass<?>[] a = new Class<?>[0];\n\t\t\treturn a[a.length - i];\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"Class<?>[] a =\")\n\t\t\t\t.doesNotContain(\"Class[] a =\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestGenerics6.java",
    "content": "package jadx.tests.integration.generics;\n\nimport java.util.Collection;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestGenerics6 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test1(Collection<? extends A> as) {\n\t\t\tfor (A a : as) {\n\t\t\t\ta.f();\n\t\t\t}\n\t\t}\n\n\t\tpublic void test2(Collection<? extends A> is) {\n\t\t\tfor (I i : is) {\n\t\t\t\ti.f();\n\t\t\t}\n\t\t}\n\n\t\tprivate interface I {\n\t\t\tvoid f();\n\t\t}\n\n\t\tprivate class A implements I {\n\t\t\tpublic void f() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"for (A a : as) {\")\n\t\t\t\t.containsOne(\"for (I i : is) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestGenerics7.java",
    "content": "package jadx.tests.integration.generics;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestGenerics7 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test() {\n\t\t\tdeclare(String.class);\n\t\t}\n\n\t\tpublic <T> T declare(Class<T> cls) {\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic void declare(Object cls) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"declare(String.class);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestGenerics8.java",
    "content": "package jadx.tests.integration.generics;\n\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestGenerics8 extends IntegrationTest {\n\n\t@SuppressWarnings(\"IllegalType\")\n\tpublic static class TestCls<I> extends LinkedHashMap<I, Integer> implements Iterable<I> {\n\t\t@Override\n\t\tpublic Iterator<I> iterator() {\n\t\t\treturn keySet().iterator();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return keySet().iterator();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestGenericsInArgs.java",
    "content": "package jadx.tests.integration.generics;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestGenericsInArgs extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static <T> void test(List<? super T> genericList, Set<T> set) {\n\t\t\tif (genericList == null) {\n\t\t\t\tthrow new RuntimeException(\"list is null\");\n\t\t\t}\n\t\t\tif (set == null) {\n\t\t\t\tthrow new RuntimeException(\"set is null\");\n\t\t\t}\n\t\t\tgenericList.clear();\n\t\t\tuse(genericList);\n\t\t\tset.clear();\n\t\t}\n\n\t\tprivate static void use(List<?> l) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"public static <T> void test(List<? super T> genericList, Set<T> set) {\")\n\t\t\t\t.contains(\"if (genericList == null) {\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"public static <T> void test(List<? super T> list, Set<T> set) {\")\n\t\t\t\t.contains(\"if (list == null) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestGenericsMthOverride.java",
    "content": "package jadx.tests.integration.generics;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestGenericsMthOverride extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic interface I<X, Y> {\n\t\t\tY method(X x);\n\t\t}\n\n\t\tpublic static class A<X, Y> implements I<X, Y> {\n\t\t\t@Override\n\t\t\tpublic Y method(X x) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tpublic static class B<X, Y> implements I<X, Y> {\n\t\t\t@Override\n\t\t\tpublic Y method(Object x) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tpublic static class C<X extends Exception, Y> implements I<X, Y> {\n\t\t\t@Override\n\t\t\tpublic Y method(Exception x) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic static class D<X, Y> implements I<X, Y> {\n\t\t\t@Override\n\t\t\tpublic Object method(Object x) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public Y method(X x) {\")\n\t\t\t\t.containsOne(\"public Y method(Object x) {\")\n\t\t\t\t.containsOne(\"public Y method(Exception x) {\")\n\t\t\t\t.containsOne(\"public Object method(Object x) {\")\n\t\t\t\t.countString(4, \"@Override\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestImportGenericMap.java",
    "content": "package jadx.tests.integration.generics;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.integration.generics.TestImportGenericMap.SuperClass.NotToImport;\nimport static jadx.tests.integration.generics.TestImportGenericMap.SuperClass.ToImport;\n\npublic class TestImportGenericMap extends IntegrationTest {\n\n\tpublic static class SuperClass<O extends SuperClass.ToImport> {\n\n\t\tinterface ToImport {\n\t\t}\n\n\t\tinterface NotToImport {\n\t\t}\n\n\t\tstatic final class Class1<C extends NotToImport> {\n\t\t}\n\n\t\tpublic <C extends NotToImport> SuperClass(Class1<C> zzf) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(SuperClass.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"import \" + ToImport.class.getName().replace(\"$ToImport\", \".ToImport\") + ';')\n\t\t\t\t.doesNotContain(\"import \" + NotToImport.class.getName().replace(\"NotToImport\", \".NotToImport\") + ';');\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestMethodOverride.java",
    "content": "package jadx.tests.integration.generics;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestMethodOverride extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"String createFromParcel(Parcel parcel) {\")\n\t\t\t\t.containsOne(\"String[] newArray(int i) {\")\n\t\t\t\t.countString(2, \"@Override\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestMissingGenericsTypes2.java",
    "content": "package jadx.tests.integration.generics;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestMissingGenericsTypes2 extends SmaliTest {\n\t// @formatter:off\n\t/*\n\tpackage generics;\n\n\timport java.util.Iterator;\n\n\tpublic class TestMissingGenericsTypes2<T> implements Iterable<T> {\n\n\t\t@Override\n\t\tpublic Iterator<T> iterator() {\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic void test(TestMissingGenericsTypes2<String> l) {\n\t\t\tIterator<String> i = l.iterator(); // <-- This generics type was removed in smali\n\t\t\twhile (i.hasNext()) {\n\t\t\t\tString s = i.next();\n\t\t\t\tdoSomething(s);\n\t\t\t}\n\t\t}\n\n\t\tprivate void doSomething(String s) {\n\t\t}\n\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"Iterator i\")\n\t\t\t\t.containsOne(\"for (String s : l) {\");\n\t}\n\n\t@Test\n\tpublic void testTypes() {\n\t\t// prevent loop from converting to 'for-each' to keep iterator variable type in code\n\t\tgetArgs().getDisabledPasses().add(\"LoopRegionVisitor\");\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"Iterator i\")\n\t\t\t\t.containsOne(\"Iterator<String> it = \"); // variable name reject along with type\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestOuterGeneric.java",
    "content": "package jadx.tests.integration.generics;\n\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestOuterGeneric extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static class A<T> {\n\t\t\tpublic class B<V> {\n\t\t\t}\n\n\t\t\tpublic class C {\n\t\t\t}\n\t\t}\n\n\t\tpublic static class D {\n\t\t\tpublic class E {\n\t\t\t}\n\t\t}\n\n\t\tpublic void test1() {\n\t\t\tA<String> a = new A<>();\n\t\t\tuse(a);\n\t\t\tA<String>.B<Exception> b = a.new B<Exception>();\n\t\t\tuse(b);\n\t\t\tuse(b);\n\t\t\tA<String>.C c = a.new C();\n\t\t\tuse(c);\n\t\t\tuse(c);\n\n\t\t\tuse(new A<Set<String>>().new C());\n\t\t}\n\n\t\tpublic void test2() {\n\t\t\tD d = new D();\n\t\t\tD.E e = d.new E();\n\t\t\tuse(e);\n\t\t\tuse(e);\n\t\t}\n\n\t\tpublic void test3() {\n\t\t\tuse(A.class);\n\t\t\tuse(A.B.class);\n\t\t\tuse(A.C.class);\n\t\t}\n\n\t\tprivate void use(Object obj) {\n\t\t}\n\t}\n\n\t@NotYetImplemented(\"Instance constructor for inner classes\")\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"A<String> a = new A<>();\")\n\t\t\t\t.containsOne(\"A<String>.B<Exception> b = a.new B<Exception>();\")\n\t\t\t\t.containsOne(\"A<String>.C c = a.new C();\")\n\t\t\t\t.containsOne(\"use(new A<Set<String>>().new C());\")\n\t\t\t\t.containsOne(\"D.E e = d.new E();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestSyntheticOverride.java",
    "content": "package jadx.tests.integration.generics;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSyntheticOverride extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tfinal class TestSyntheticOverride extends Lambda implements Function1<String, Unit> {\n\n\t\t\t// fixing method types to match interface (i.e Unit invoke(String str))\n\t\t\t// make duplicate methods signatures\n\t\t\tpublic bridge synthetic Object invoke(Object str) {\n\t\t\t\tinvoke(str);\n\t\t\t\treturn Unit.INSTANCE;\n\t\t\t}\n\n\t\t\tpublic final void invoke(String str) {\n\t\t\t\t...\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tallowWarnInCode();\n\t\tdisableCompilation();\n\t\tList<ClassNode> classNodes = loadFromSmaliFiles();\n\t\tassertThat(searchCls(classNodes, \"TestSyntheticOverride\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"invoke(String str)\")\n\t\t\t\t.containsOne(\"invoke2(String str)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestTypeVarsFromOuterClass.java",
    "content": "package jadx.tests.integration.generics;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeVarsFromOuterClass extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic interface I<X> {\n\t\t\tMap.Entry<X, X> entry();\n\t\t}\n\n\t\tpublic static class Outer<Y> {\n\t\t\tpublic class Inner implements I<Y> {\n\t\t\t\t@Override\n\t\t\t\tpublic Map.Entry<Y, Y> entry() {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic Inner getInner() {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tprivate Outer<String> outer;\n\n\t\tpublic void test() {\n\t\t\tOuter<String>.Inner inner = this.outer.getInner();\n\t\t\tuse(inner, inner);\n\t\t\tMap.Entry<String, String> entry = inner.entry();\n\t\t\tuse(entry.getKey(), entry.getValue());\n\t\t}\n\n\t\tpublic void test2() {\n\t\t\t// force interface virtual call\n\t\t\tI<String> base = this.outer.getInner();\n\t\t\tuse(base, base);\n\t\t\tMap.Entry<String, String> entry = base.entry();\n\t\t\tuse(entry.getKey(), entry.getValue());\n\t\t}\n\n\t\tpublic void use(Object a, Object b) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"Outer<Y>.Inner inner\")\n\t\t\t\t.doesNotContain(\"Object entry = \")\n\t\t\t\t.countString(2, \"Outer<String>.Inner inner = this.outer.getInner();\")\n\t\t\t\t.countString(2, \"Map.Entry<String, String> entry = \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestTypeVarsFromSuperClass.java",
    "content": "package jadx.tests.integration.generics;\n\nimport java.util.Objects;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeVarsFromSuperClass extends IntegrationTest {\n\n\t@SuppressWarnings(\"ResultOfMethodCallIgnored\")\n\tpublic static class TestCls {\n\n\t\tpublic static class C1<A> {\n\t\t}\n\n\t\tpublic static class C2<B> extends C1<B> {\n\t\t\tpublic B call() {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tpublic static class C3<C> extends C2<C> {\n\t\t}\n\n\t\tpublic static class C4 extends C3<String> {\n\t\t\tpublic Object test() {\n\t\t\t\tString str = call();\n\t\t\t\tObjects.nonNull(str);\n\t\t\t\treturn str;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"= call();\")\n\t\t\t\t.doesNotContain(\"(String)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/generics/TestUsageInGenerics.java",
    "content": "package jadx.tests.integration.generics;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestUsageInGenerics extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static class A {\n\t\t}\n\n\t\tpublic static class B<T extends A> {\n\t\t}\n\n\t\tpublic static class C {\n\t\t\tpublic List<? extends A> list;\n\t\t}\n\n\t\tpublic <T extends A> T test() {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tClassNode testCls = searchCls(cls.getInnerClasses(), \"A\");\n\t\tClassNode bCls = searchCls(cls.getInnerClasses(), \"B\");\n\t\tClassNode cCls = searchCls(cls.getInnerClasses(), \"C\");\n\t\tMethodNode testMth = getMethod(cls, \"test\");\n\n\t\tassertThat(testCls.getUseIn()).contains(cls, bCls, cCls);\n\t\tassertThat(testCls.getUseInMth()).contains(testMth);\n\n\t\tassertThat(cls)\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public <T extends A> T test() {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestConstInline.java",
    "content": "package jadx.tests.integration.inline;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestConstInline extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic boolean test() {\n\t\t\ttry {\n\t\t\t\treturn f(0);\n\t\t\t} catch (Exception e) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tpublic boolean f(int i) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return f(0);\")\n\t\t\t\t.containsOne(\"return false;\")\n\t\t\t\t.doesNotContain(\" = \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestGetterInlineNegative.java",
    "content": "package jadx.tests.integration.inline;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestGetterInlineNegative extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic class TestGetterInlineNegative {\n\t\t\tpublic static final String field = \"some string\";\n\n\t\t\tpublic static synthetic String getter() {\n\t\t\t\treturn field;\n\t\t\t}\n\n\t\t\tpublic void test() {\n\t\t\t\tgetter(); // inline will produce 'field;' and fail to compile with 'not a statement' error\n\t\t\t}\n\n\t\t\tpublic String test2() {\n\t\t\t\treturn getter();\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(indent() + \"field;\")\n\t\t\t\t.containsOne(\"return field;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestInline.java",
    "content": "package jadx.tests.integration.inline;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestInline extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static void main(String[] args) throws Exception {\n\t\t\tSystem.out.println(\"Test: \" + new TestCls().testRun());\n\t\t}\n\n\t\tprivate boolean testRun() {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"System.out.println(\\\"Test: \\\" + new TestInline$TestCls().testRun());\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestInline2.java",
    "content": "package jadx.tests.integration.inline;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestInline2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int test() throws InterruptedException {\n\t\t\tint[] a = new int[] { 1, 2, 4, 6, 8 };\n\t\t\tint b = 0;\n\t\t\tfor (int i = 0; i < a.length; i += 2) {\n\t\t\t\tb += a[i];\n\t\t\t}\n\t\t\tfor (long i = b; i > 0; i--) {\n\t\t\t\tb += i;\n\t\t\t}\n\t\t\treturn b;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"int[] a = {1, 2, 4, 6, 8};\")\n\t\t\t\t.containsOne(\"for (int i = 0; i < a.length; i += 2) {\")\n\t\t\t\t.containsOne(\"for (long i2 = b; i2 > 0; i2--) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestInline3.java",
    "content": "package jadx.tests.integration.inline;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestInline3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic TestCls(int b1, int b2) {\n\t\t\tthis(b1, b2, 0, 0, 0);\n\t\t}\n\n\t\tpublic TestCls(int a1, int a2, int a3, int a4, int a5) {\n\t\t}\n\n\t\tpublic class A extends TestCls {\n\t\t\tpublic A(int a) {\n\t\t\t\tsuper(a, a);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"this(b1, b2, 0, 0, 0);\")\n\t\t\t\t.contains(\"super(a, a);\")\n\t\t\t\t.doesNotContain(\"super(a, a).this$0\")\n\t\t\t\t.contains(\"public class A extends TestInline3$TestCls {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestInline6.java",
    "content": "package jadx.tests.integration.inline;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestInline6 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void f() {\n\t\t}\n\n\t\tpublic void test(int a, int b) {\n\t\t\tlong start = System.nanoTime();\n\t\t\tf();\n\t\t\tSystem.out.println(System.nanoTime() - start);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"System.out.println(System.nanoTime() - start);\")\n\t\t\t\t.doesNotContain(\"System.out.println(System.nanoTime() - System.nanoTime());\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestInline7.java",
    "content": "package jadx.tests.integration.inline;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInline7 extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic void onViewCreated(View view, @Nullable Bundle bundle) {\n\t\t\tsuper.onViewCreated(view, bundle);\n\t\t\tview.findViewById(R.id.done_button_early_release_failure).setOnClickListener(new SafeClickListener(this));\n\t\t\tBundle arguments = getArguments();\n\t\t\tif (arguments != null) {\n\t\t\t\t((TextView) view.findViewById(R.id.summary_content_early_release_failure)).setText(\n\t\t\t\t\tgetString(R.string.withdraw_id_capture_failure_content,\n\t\t\t\t\t\tnew Object[]{arguments.getString(\"withdrawAmount\"), arguments.getString (\"withdrawHoldTime\")})\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmaliWithPkg(\"inline\", \"TestInline7\"))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"Bundle arguments;\")\n\t\t\t\t.containsOne(\"Bundle arguments = getArguments();\")\n\t\t\t\t.containsOne(\"@Nullable Bundle bundle\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestInstanceLambda.java",
    "content": "package jadx.tests.integration.inline;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.visitors.ProcessAnonymous;\nimport jadx.core.utils.ListUtils;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInstanceLambda extends SmaliTest {\n\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\", \"SameParameterValue\" })\n\tpublic static class TestCls {\n\n\t\tpublic <T> Map<T, T> test(List<? extends T> list) {\n\t\t\treturn toMap(list, Lambda$1.INSTANCE);\n\t\t}\n\n\t\t/**\n\t\t * Smali test missing 'T' definition in 'Lambda<T>'\n\t\t * Note: use '$1' so class looks like generated by compiler and pass check in\n\t\t * {@link ProcessAnonymous#canBeAnonymous(ClassNode)}\n\t\t */\n\t\t@SuppressWarnings({ \"CheckStyle\", \"checkstyle:TypeName\" })\n\t\tprivate static class Lambda$1<T> implements Function<T, T> {\n\t\t\tpublic static final Lambda$1 INSTANCE = new Lambda$1();\n\n\t\t\t@Override\n\t\t\tpublic T apply(T t) {\n\t\t\t\treturn t;\n\t\t\t}\n\t\t}\n\n\t\tprivate static <T> Map<T, T> toMap(List<? extends T> list, Function<T, T> valueMap) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tuseJavaInput();\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code();\n\t}\n\n\t@Test\n\tpublic void testSmaliDisableInline() {\n\t\targs.setInlineAnonymousClasses(false);\n\t\tList<ClassNode> classNodes = loadFromSmaliFiles();\n\t\tassertThat(searchTestCls(classNodes, \"Lambda$1\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"class Lambda$1<T> implements Function<T, T> {\");\n\t\tassertThat(searchTestCls(classNodes, \"TestCls\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"Lambda$1.INSTANCE\");\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tList<ClassNode> classNodes = loadFromSmaliFiles();\n\t\tassertThat(ListUtils.filter(classNodes, c -> !c.contains(AFlag.DONT_GENERATE)))\n\t\t\t\t.describedAs(\"Expect lambda to be inlined\")\n\t\t\t\t.hasSize(1);\n\t\tassertThat(searchTestCls(classNodes, \"TestCls\"))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"Lambda$1.INSTANCE\")\n\t\t\t\t.containsOne(\"toMap(list, new Function<T, T>() {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestIssue86.java",
    "content": "package jadx.tests.integration.inline;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\n@SuppressWarnings(\"checkstyle:printstacktrace\")\npublic class TestIssue86 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static final String SERVER_ERR = \"server-err\";\n\t\tprivate static final String NOT_FOUND = \"not-found\";\n\t\tprivate static final String LIST_TAG = \"list-tag\";\n\t\tprivate static final String TEMP_TAG = \"temp-tag\";\n\t\tprivate static final String MIN_TAG = \"min-tag\";\n\t\tprivate static final String MAX_TAG = \"max-tag\";\n\t\tprivate static final String MILLIS_TAG = \"millis-tag\";\n\t\tprivate static final String WEATHER_TAG = \"weather-tag\";\n\t\tprivate static final String DESC_TAG = \"desc-tag\";\n\n\t\tpublic List<Day> test(String response) {\n\t\t\tList<Day> reportList = new ArrayList<>();\n\t\t\ttry {\n\t\t\t\tSystem.out.println(response);\n\t\t\t\tif (response != null\n\t\t\t\t\t\t&& (response.startsWith(SERVER_ERR)\n\t\t\t\t\t\t\t\t|| response.startsWith(NOT_FOUND))) {\n\t\t\t\t\treturn reportList;\n\t\t\t\t}\n\t\t\t\tJSONObject jsonObj = new JSONObject(response);\n\t\t\t\tJSONArray days = jsonObj.getJSONArray(LIST_TAG);\n\t\t\t\tfor (int i = 0; i < days.length(); i++) {\n\t\t\t\t\tJSONObject c = days.getJSONObject(i);\n\t\t\t\t\tlong millis = c.getLong(MILLIS_TAG);\n\t\t\t\t\tJSONObject temp = c.getJSONObject(TEMP_TAG);\n\t\t\t\t\tString max = temp.getString(MAX_TAG);\n\t\t\t\t\tString min = temp.getString(MIN_TAG);\n\t\t\t\t\tJSONArray weather = c.getJSONArray(WEATHER_TAG);\n\t\t\t\t\tString weatherDesc = weather.getJSONObject(0).getString(DESC_TAG);\n\t\t\t\t\tDay d = new Day();\n\t\t\t\t\td.setMilis(millis);\n\t\t\t\t\td.setMinTmp(min);\n\t\t\t\t\td.setMaxTmp(max);\n\t\t\t\t\td.setWeatherDesc(weatherDesc);\n\t\t\t\t\treportList.add(d);\n\t\t\t\t}\n\t\t\t} catch (JSONException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\treturn reportList;\n\t\t}\n\n\t\tprivate static class Day {\n\t\t\tpublic void setMilis(long milis) {\n\t\t\t}\n\n\t\t\tpublic void setMinTmp(String min) {\n\t\t\t}\n\n\t\t\tpublic void setMaxTmp(String max) {\n\t\t\t}\n\n\t\t\tpublic void setWeatherDesc(String weatherDesc) {\n\t\t\t}\n\t\t}\n\n\t\tprivate static class JSONObject {\n\t\t\tpublic JSONObject(String response) {\n\t\t\t}\n\n\t\t\tpublic JSONArray getJSONArray(String tag) throws JSONException {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tpublic JSONObject getJSONObject(String tag) throws JSONException {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tpublic String getString(String tag) throws JSONException {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tpublic long getLong(String tag) throws JSONException {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\n\t\tprivate class JSONArray {\n\t\t\tpublic JSONObject getJSONObject(int i) throws JSONException {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tpublic int length() {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\n\t\tprivate class JSONException extends Exception {\n\t\t\tprivate static final long serialVersionUID = -4358405506584551910L;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"response.startsWith(NOT_FOUND)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestMethodInline.java",
    "content": "package jadx.tests.integration.inline;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestMethodInline extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpackage inline;\n\n\t\tpublic class A {\n\t\t\tpublic static void useMth() {\n\t\t\t\tinline.other.B.bridgeMth(); // after inline 'inline.other.C.test()' is not accessible\n\t\t\t}\n\t\t}\n\t\t-----------------------------------------------------------\n\t\tpackage inline.other;\n\n\t\tpublic class B {\n\t\t\tpublic static bridge synthetic void bridgeMth() {\n\t\t\t\tinline.other.C.test();\n\t\t\t}\n\t\t}\n\t\t----------------------------------------------------------\n\t\tpackage inline.other;\n\n\t\tclass C {\n\t\t\tpublic static void test() {\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tList<ClassNode> classes = loadFromSmaliFiles();\n\t\tClassNode aCls = searchCls(classes, \"inline.A\");\n\t\tClassNode bCls = searchCls(classes, \"inline.other.B\");\n\t\tClassNode cCls = searchCls(classes, \"inline.other.C\");\n\n\t\tassertThat(bCls).code().doesNotContain(\"bridgeMth()\");\n\t\tassertThat(aCls).code().containsOne(\"C.test()\");\n\t\tassertThat(cCls).code().containsOne(\"public class C {\");\n\n\t\t// TODO: update dependencies?\n\t\t// assertThat(aCls.getDependencies()).contains(cCls);\n\t\t// assertThat(cCls.getUsedIn()).contains(aCls);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestOverlapSyntheticMethods.java",
    "content": "package jadx.tests.integration.inline;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"CommentedOutCode\")\npublic class TestOverlapSyntheticMethods extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic String test(int i) {\n\t\t\treturn a(i) + \"|\" + a(i);\n\t\t}\n\n\t\tpublic int a(int i) {\n\t\t\treturn i;\n\t\t}\n\n\t\tpublic String a(int i) {\n\t\t\treturn \"i:\" + i;\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void testSmali() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"int a(int i) {\")\n\t\t\t\t.containsOne(\"String m0a(int i) {\");\n\t}\n\n\t@Test\n\tpublic void testSmaliNoRename() {\n\t\tgetArgs().setRenameFlags(Collections.emptySet());\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"int a(int i) {\")\n\t\t\t\t.containsOne(\"String a(int i) {\")\n\t\t\t\t.containsOne(\"return a(i) + \\\"|\\\" + a(i);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestOverrideBridgeMerge.java",
    "content": "package jadx.tests.integration.inline;\n\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.annotations.NodeDeclareRef;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestOverrideBridgeMerge extends SmaliTest {\n\n\tpublic static class TestCls implements Function<String, Integer> {\n\t\t@Override\n\t\tpublic /* bridge */ /* synthetic */ Integer apply(String str) {\n\t\t\treturn test(str);\n\t\t}\n\n\t\tpublic Integer test(String str) {\n\t\t\treturn str.length();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"Integer test(String str) {\"); // not inlined\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tClassNode cls = getClassNodeFromSmali();\n\t\tICodeAnnotation mthDef = new NodeDeclareRef(getMethod(cls, \"apply\"));\n\t\tassertThat(cls)\n\t\t\t\t.checkCodeAnnotationFor(\"apply(String str) {\", mthDef)\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"@Override\")\n\t\t\t\t.containsOne(\"public Integer apply(String str) {\")\n\t\t\t\t.doesNotContain(\"test(String str)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestSyntheticBridgeRename.java",
    "content": "package jadx.tests.integration.inline;\n\nimport org.assertj.core.api.Condition;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSyntheticBridgeRename extends IntegrationTest {\n\n\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\tpublic static class TestCls {\n\t\tprivate abstract class Inner<V> {\n\t\t\tpublic abstract V get(String value);\n\t\t}\n\n\t\tpublic class IntInner extends Inner<Integer> {\n\t\t\tpublic Integer get(String value) {\n\t\t\t\treturn value.length();\n\t\t\t}\n\t\t}\n\n\t\tpublic void test() {\n\t\t\tIntInner inner = new IntInner();\n\t\t\tcall(inner.get(\"a\"));\n\t\t}\n\n\t\tprivate static void call(Integer value) {\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.JAVA8 })\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(searchCls(cls.getInnerClasses(), \"IntInner\").getMethods())\n\t\t\t\t.as(\"check that bridge method was generated by compiler\")\n\t\t\t\t.haveAtLeastOne(new Condition<>(mth -> mth.getAccessFlags().isBridge(), \"bridge\"));\n\n\t\tassertThat(cls)\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"mo0get\")\n\t\t\t\t.containsOne(\"call(inner.get(\\\"a\\\"));\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestSyntheticClassInline.java",
    "content": "package jadx.tests.integration.inline;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSyntheticClassInline extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tList<ClassNode> classes = loadFromSmaliFiles();\n\t\tassertThat(searchCls(classes, \"inline.A\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"static Supplier<Long> test(final long x1, final long x2) {\")\n\t\t\t\t.containsOne(\"return new Supplier() {\")\n\t\t\t\t.containsOne(\"return A.lambda$test$0(x1, x2);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestSyntheticInline.java",
    "content": "package jadx.tests.integration.inline;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSyntheticInline extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate int f;\n\n\t\tprivate int func() {\n\t\t\treturn -1;\n\t\t}\n\n\t\tpublic class A {\n\t\t\tpublic int getF() {\n\t\t\t\treturn f;\n\t\t\t}\n\n\t\t\tpublic void setF(int v) {\n\t\t\t\tf = v;\n\t\t\t}\n\n\t\t\tpublic int callFunc() {\n\t\t\t\treturn func();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"synthetic\")\n\t\t\t\t.doesNotContain(\"access$\")\n\t\t\t\t.doesNotContain(\"x0\")\n\t\t\t\t.contains(\"f = v;\")\n\t\t\t\t.contains(\"return TestSyntheticInline$TestCls.this.f;\")\n\n\t\t\t\t// .contains(\"return f;\");\n\t\t\t\t// .contains(\"return func();\");\n\t\t\t\t// Temporary solution\n\t\t\t\t.contains(\"return TestSyntheticInline$TestCls.this.func();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestSyntheticInline2.java",
    "content": "package jadx.tests.integration.inline;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSyntheticInline2 extends IntegrationTest {\n\n\tpublic static class Base {\n\t\tprotected void call() {\n\t\t\tSystem.out.println(\"base call\");\n\t\t}\n\t}\n\n\tpublic static class TestCls extends Base {\n\t\tpublic class A {\n\t\t\tpublic void invokeCall() {\n\t\t\t\tTestCls.this.call();\n\t\t\t}\n\n\t\t\tpublic void invokeSuperCall() {\n\t\t\t\tTestCls.super.call();\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void call() {\n\t\t\tSystem.out.println(\"TestCls call\");\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tA a = new A();\n\t\t\ta.invokeSuperCall();\n\t\t\ta.invokeCall();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation(); // strange java compiler bug\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"synthetic\")\n\t\t\t\t.doesNotContain(\"access$\").contains(\"TestSyntheticInline2$TestCls.this.call();\")\n\t\t\t\t.contains(\"TestSyntheticInline2$TestCls.super.call();\");\n\t}\n\n\t@Test\n\tpublic void testTopClass() {\n\t\tJadxAssertions.assertThat(getClassNode(TestSyntheticInline2.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(indent(1) + \"TestCls.super.call();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestSyntheticInline3.java",
    "content": "package jadx.tests.integration.inline;\n\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSyntheticInline3 extends SmaliTest {\n\n\t@SuppressWarnings({ \"Convert2Lambda\", \"TrivialFunctionalExpressionUsage\" })\n\tpublic static class TestCls {\n\t\tprivate String strField;\n\n\t\tprivate String str() {\n\t\t\treturn \"a\";\n\t\t}\n\n\t\tprivate void test() {\n\t\t\tnew Function<String, Void>() {\n\t\t\t\t@Override\n\t\t\t\tpublic Void apply(String s) {\n\t\t\t\t\tSystem.out.println(s + strField + str());\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}.apply(\"c\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code();\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tallowWarnInCode();\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmaliFiles())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\".access$getDialog$p(\")\n\t\t\t\t.doesNotContain(\".access$getChooserIntent(\")\n\t\t\t\t.doesNotContain(\"= r1;\")\n\t\t\t\t.doesNotContain(\"this$0\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inline/TestTernaryCast.java",
    "content": "package jadx.tests.integration.inline;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTernaryCast extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic String test(boolean b, Object obj, CharSequence cs) {\n\t\t\treturn (String) (b ? obj : cs);\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(true, \"a\", \"b\")).isEqualTo(\"a\");\n\t\t\tassertThat(test(false, \"a\", \"b\")).isEqualTo(\"b\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return (String) (b ? obj : cs);\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass.java",
    "content": "package jadx.tests.integration.inner;\n\nimport java.io.File;\nimport java.io.FilenameFilter;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnonymousClass extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic int test() {\n\t\t\tString[] files = new File(\"a\").list(new FilenameFilter() {\n\t\t\t\t@Override\n\t\t\t\tpublic boolean accept(File dir, String name) {\n\t\t\t\t\treturn name.equals(\"a\");\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn files.length;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"new File(\\\"a\\\").list(new FilenameFilter()\")\n\t\t\t\t.doesNotContain(\"synthetic\")\n\t\t\t\t.doesNotContain(\"this\")\n\t\t\t\t.doesNotContain(\"null\")\n\t\t\t\t.doesNotContain(\"AnonymousClass_\")\n\t\t\t\t.doesNotContain(\"class AnonymousClass\");\n\t}\n\n\t@Test\n\tpublic void testNoInline() {\n\t\tgetArgs().setInlineAnonymousClasses(false);\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"class AnonymousClass1 implements FilenameFilter {\")\n\t\t\t\t.containsOne(\"new AnonymousClass1()\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass10.java",
    "content": "package jadx.tests.integration.inner;\n\nimport java.util.Random;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestAnonymousClass10 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic A test() {\n\t\t\tRandom random = new Random();\n\t\t\tint a2 = random.nextInt();\n\t\t\treturn new A(this, a2, a2 + 3, 4, 5, random.nextDouble()) {\n\t\t\t\t@Override\n\t\t\t\tpublic void m() {\n\t\t\t\t\tSystem.out.println(1);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tpublic abstract class A {\n\t\t\tpublic A(TestCls a1, int a2, int a3, int a4, int a5, double a6) {\n\t\t\t}\n\n\t\t\tpublic abstract void m();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return new A(this, a2, a2 + 3, 4, 5, random.nextDouble()) {\")\n\t\t\t\t.doesNotContain(\"synthetic\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass11.java",
    "content": "package jadx.tests.integration.inner;\n\nimport java.util.Random;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestAnonymousClass11 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test() {\n\t\t\tfinal int a = new Random().nextInt();\n\t\t\tfinal long l = new Random().nextLong();\n\t\t\tfunc(new A(l) {\n\t\t\t\t@Override\n\t\t\t\tpublic void m() {\n\t\t\t\t\tSystem.out.println(a);\n\t\t\t\t}\n\t\t\t});\n\t\t\tSystem.out.println(\"a\" + a);\n\t\t\tprint(a);\n\t\t\tprint2(1, a);\n\t\t\tprint3(1, l);\n\t\t}\n\n\t\tpublic abstract class A {\n\t\t\tpublic A(long l) {\n\t\t\t}\n\n\t\t\tpublic abstract void m();\n\t\t}\n\n\t\tprivate void func(A a) {\n\t\t}\n\n\t\tprivate void print(int a) {\n\t\t}\n\n\t\tprivate void print2(int i, int a) {\n\t\t}\n\n\t\tprivate void print3(int i, long l) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"System.out.println(\\\"a\\\" + a);\")\n\t\t\t\t.containsOne(\"print(a);\")\n\t\t\t\t.doesNotContain(\"synthetic\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass12.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestAnonymousClass12 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic abstract static class BasicAbstract {\n\t\t\tpublic abstract void doSomething();\n\t\t}\n\n\t\tpublic BasicAbstract outer;\n\t\tpublic BasicAbstract inner;\n\n\t\tpublic void test() {\n\t\t\touter = new BasicAbstract() {\n\t\t\t\t@Override\n\t\t\t\tpublic void doSomething() {\n\t\t\t\t\tinner = new BasicAbstract() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void doSomething() {\n\t\t\t\t\t\t\tinner = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"outer = new BasicAbstract() {\")\n\t\t\t\t.containsOne(\"inner = new BasicAbstract() {\")\n\t\t\t\t.containsOne(\"inner = null;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass13.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\npublic class TestAnonymousClass13 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test() {\n\t\t\tnew TestCls() {\n\t\t\t};\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass14.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.CommentsLevel;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.ListUtils;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnonymousClass14 extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic class OuterCls implements Runnable {\n\n\t\t\tclass TestCls {\n\t\t\t\tprivate TestCls() {\n\t\t\t\t\tnew ArrayList();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic void makeAnonymousCls() {\n\t\t\t\tuse(new Thread(this) {\n\t\t\t\t/ * class inner.OuterCls.AnonymousClass1 * /\n\n\t\t\t\t\tpublic void someMethod() {\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tpublic void makeTestCls() {\n\t\t\t\tnew TestCls();\n\t\t\t}\n\n\t\t\tpublic void run() {\n\t\t\t}\n\n\t\t\tpublic void use(Thread thread) {\n\t\t\t}\n\t\t}\n\t */\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tgetArgs().setCommentsLevel(CommentsLevel.WARN);\n\t\tClassNode outerCls = getClassNodeFromSmaliFiles(\"OuterCls\");\n\t\tassertThat(outerCls).code()\n\t\t\t\t.doesNotContain(\"synthetic\", \"AnonymousClass1\")\n\t\t\t\t.describedAs(\"only one constructor\").containsOne(\"private TestCls(\")\n\t\t\t\t.describedAs(\"constructor without args\").containsOne(\"private TestCls() {\");\n\n\t\tMethodNode makeTestClsMth = outerCls.searchMethodByShortName(\"makeTestCls\");\n\t\tassertThat(makeTestClsMth).isNotNull();\n\n\t\tClassNode testCls = searchCls(outerCls.getInnerClasses(), \"TestCls\");\n\t\tMethodNode ctrMth = ListUtils.filterOnlyOne(testCls.getMethods(),\n\t\t\t\tm -> m.isConstructor() && !m.getAccessFlags().isSynthetic());\n\t\tassertThat(ctrMth).isNotNull();\n\t\tassertThat(ctrMth.getUseIn()).hasSize(1);\n\t\tassertThat(ctrMth.getUseIn().get(0)).isEqualTo(makeTestClsMth);\n\n\t\tassertThat(outerCls).checkCodeAnnotationFor(\"new TestCls();\", 4, ctrMth);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass15.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnonymousClass15 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic Thread test(Runnable run) {\n\t\t\treturn new Thread(run) {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tSystem.out.println(\"run\");\n\t\t\t\t\tsuper.run();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tpublic Thread test2(Runnable run) {\n\t\t\treturn new Thread(run) {\n\t\t\t\t{\n\t\t\t\t\tsetName(\"run\");\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"return new Thread(run) {\")\n\t\t\t\t.containsOne(\"setName(\\\"run\\\");\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass16.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.CommentsLevel;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnonymousClass16 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic Something test() {\n\t\t\tSomething a = new Something() {\n\t\t\t\t{\n\t\t\t\t\tput(\"a\", \"b\");\n\t\t\t\t}\n\t\t\t};\n\t\t\ta.put(\"c\", \"d\");\n\t\t\treturn a;\n\t\t}\n\n\t\tpublic class Something {\n\t\t\tpublic void put(Object o, Object o2) {\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tgetArgs().setCommentsLevel(CommentsLevel.NONE);\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"r0\")\n\t\t\t\t.doesNotContain(\"AnonymousClass1 r0 = \")\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"Something something = new Something() {\",\n\t\t\t\t\t\tindent() + \"{\",\n\t\t\t\t\t\tindent(2) + \"put(\\\"a\\\", \\\"b\\\");\",\n\t\t\t\t\t\tindent() + \"}\",\n\t\t\t\t\t\t\"};\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass17.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnonymousClass17 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\t@SuppressWarnings({ \"checkstyle:InnerAssignment\", \"Convert2Lambda\" })\n\t\tpublic void test(boolean a, boolean b) {\n\t\t\tString v;\n\t\t\tif (a && (v = get(b)) != null) {\n\t\t\t\tuse(new Runnable() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tSystem.out.println(v);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tpublic String get(boolean a) {\n\t\t\treturn a ? \"str\" : null;\n\t\t}\n\n\t\tpublic void use(Runnable r) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (a && (v = get(b)) != null) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass18.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.CommentsLevel;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnonymousClass18 extends IntegrationTest {\n\n\t@SuppressWarnings({ \"Convert2Lambda\", \"Anonymous2MethodRef\", \"unused\" })\n\tpublic static class TestCls {\n\n\t\tpublic interface Job {\n\t\t\tvoid executeJob();\n\t\t}\n\n\t\tpublic void start() {\n\t\t\trunJob(new Job() {\n\t\t\t\t@Override\n\t\t\t\tpublic void executeJob() {\n\t\t\t\t\trunJob(new Job() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void executeJob() {\n\t\t\t\t\t\t\tdoSomething();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tprivate void doSomething() {\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tpublic static void runJob(Job job) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tgetArgs().setCommentsLevel(CommentsLevel.WARN);\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"AnonymousClass1.this\")\n\t\t\t\t.doesNotContain(\"class AnonymousClass1\")\n\t\t\t\t// .doesNotContain(\"TestAnonymousClass18$TestCls.runJob(\") // TODO: ???\n\t\t\t\t.containsOne(indent() + \"doSomething();\");\n\t}\n\n\t@Test\n\tpublic void testNoInline() {\n\t\tgetArgs().setInlineAnonymousClasses(false);\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"class AnonymousClass1 implements Job {\")\n\t\t\t\t.containsOne(\"class C00001 implements Job {\")\n\t\t\t\t.containsOne(\"AnonymousClass1.this.doSomething();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass19.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnonymousClass19 extends SmaliTest {\n\n\t@SuppressWarnings({ \"Convert2Lambda\", \"unused\" })\n\tpublic static class TestCls {\n\n\t\tpublic void test(boolean a, boolean b) {\n\t\t\tboolean c = a && b;\n\t\t\tuse(new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tSystem.out.println(a + \" && \" + b + \" = \" + c);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tpublic void use(Runnable r) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"System.out.println(a + \\\" && \\\" + b + \\\" = \\\" + c);\");\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tassertThat(getClassNodeFromSmaliFiles(\"ATestCls\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"System.out.println(a + \\\" && \\\" + b + \\\" = \\\" + c);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass2.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnonymousClass2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static class Inner {\n\t\t\tpublic int f;\n\n\t\t\tpublic Runnable test() {\n\t\t\t\treturn new Runnable() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tf = 1;\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tpublic Runnable test2() {\n\t\t\t\treturn new Runnable() {\n\t\t\t\t\t@Override\n\t\t\t\t\t@SuppressWarnings(\"unused\")\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tObject obj = Inner.this;\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tpublic Runnable test3() {\n\t\t\t\tfinal int i = f + 2;\n\t\t\t\treturn new Runnable() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tf = i;\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"synthetic\")\n\t\t\t\t.doesNotContain(\"AnonymousClass_\")\n\t\t\t\t.contains(\"f = 1;\")\n\t\t\t\t.contains(\"f = i;\")\n\t\t\t\t.doesNotContain(\"Inner obj = ;\")\n\t\t\t\t.contains(\"Inner.this;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass20.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.JadxInternalAccess;\nimport jadx.api.JavaClass;\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnonymousClass20 extends IntegrationTest {\n\n\t@SuppressWarnings({ \"unused\", \"checkstyle:TypeName\", \"Convert2Lambda\", \"Anonymous2MethodRef\" })\n\tpublic static class Test$Cls {\n\t\tpublic Runnable test() {\n\t\t\treturn new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tnew Test$Cls();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(Test$Cls.class);\n\t\tassertThat(cls.get(AType.ANONYMOUS_CLASS)).isNull();\n\n\t\tJavaClass javaClass = JadxInternalAccess.convertClassNode(jadxDecompiler, cls);\n\t\tassertThat(javaClass.getTopParentClass()).isEqualTo(javaClass);\n\n\t\tassertThat(cls)\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"new TestAnonymousClass20$Test$Cls();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass21.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Don't inline const string into anonymous class constructor\n */\npublic class TestAnonymousClass21 extends IntegrationTest {\n\n\t@SuppressWarnings(\"Convert2Lambda\")\n\tpublic static class TestCls {\n\t\tpublic void test() {\n\t\t\tString str = \"str\";\n\t\t\tnew Thread(new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tSystem.out.println(str);\n\t\t\t\t}\n\t\t\t}).start();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"String str = \\\"str\\\";\")\n\t\t\t\t.containsOne(\"System.out.println(str);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass22.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnonymousClass22 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static class Parent {\n\t\t\tpublic static Parent test(Class<?> cls) {\n\t\t\t\tfinal AnotherClass another = new AnotherClass();\n\t\t\t\treturn new Parent() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic String func() {\n\t\t\t\t\t\treturn another.toString();\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tpublic String func() {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t}\n\n\t\tpublic static class AnotherClass {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return another.toString();\")\n\t\t\t\t.doesNotContain(\"AnotherClass.this\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass3.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnonymousClass3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static class Inner {\n\t\t\tprivate int f;\n\t\t\tpublic double d;\n\n\t\t\tpublic void test() {\n\t\t\t\tnew Thread() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tint a = f--;\n\t\t\t\t\t\tp(a);\n\n\t\t\t\t\t\tf += 2;\n\t\t\t\t\t\tf *= 2;\n\n\t\t\t\t\t\ta = ++f;\n\t\t\t\t\t\tp(a);\n\n\t\t\t\t\t\td /= 3;\n\t\t\t\t\t}\n\n\t\t\t\t\tpublic void p(int a) {\n\t\t\t\t\t}\n\t\t\t\t}.start();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(indent(4) + \"public void run() {\")\n\t\t\t\t.contains(indent(3) + \"}.start();\")\n\t\t\t\t.doesNotContain(\"AnonymousClass_\");\n\t}\n\n\t@Test\n\t@NotYetImplemented\n\tpublic void test2() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"synthetic\")\n\t\t\t\t.contains(\"a = f--;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass3a.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.api.CommentsLevel;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnonymousClass3a extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static class Inner {\n\t\t\tprivate int f;\n\t\t\tprivate int r;\n\n\t\t\tpublic void test() {\n\t\t\t\tnew Runnable() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tint a = --Inner.this.f;\n\t\t\t\t\t\tp(a);\n\t\t\t\t\t}\n\n\t\t\t\t\tpublic void p(int a) {\n\t\t\t\t\t\tInner.this.r = a;\n\t\t\t\t\t}\n\t\t\t\t}.run();\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tInner inner = new Inner();\n\t\t\tinner.f = 2;\n\t\t\tinner.test();\n\t\t\tassertThat(inner.f).isEqualTo(1);\n\t\t\tassertThat(inner.r).isEqualTo(1);\n\t\t}\n\t}\n\n\t@Test\n\t@NotYetImplemented\n\tpublic void test() {\n\t\tgetArgs().setCommentsLevel(CommentsLevel.NONE);\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"synthetic\")\n\t\t\t\t.doesNotContain(\"access$00\")\n\t\t\t\t.doesNotContain(\"AnonymousClass_\")\n\t\t\t\t.doesNotContain(\"unused = \")\n\t\t\t\t.containsLine(4, \"public void run() {\")\n\t\t\t\t.containsLine(3, \"}.run();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass4.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestAnonymousClass4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\t@SuppressWarnings(\"unused\")\n\t\tpublic static class Inner {\n\t\t\tprivate int f;\n\t\t\tprivate double d;\n\n\t\t\tpublic void test() {\n\t\t\t\tnew Thread() {\n\t\t\t\t\t{\n\t\t\t\t\t\tf = 1;\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\td = 7.5;\n\t\t\t\t\t}\n\t\t\t\t}.start();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(indent(3) + \"new Thread() {\")\n\t\t\t\t.containsOne(indent(4) + '{')\n\t\t\t\t.containsOne(\"f = 1;\")\n\t\t\t\t.countString(2, indent(4) + '}')\n\t\t\t\t.containsOne(indent(4) + \"public void run() {\")\n\t\t\t\t.containsOne(\"d = 7.5\")\n\t\t\t\t.containsOne(indent(3) + \"}.start();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass5.java",
    "content": "package jadx.tests.integration.inner;\n\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnonymousClass5 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate final Map<String, TestCls> map = new HashMap<>();\n\t\tprivate int a;\n\n\t\tpublic Iterable<TestCls> test(String name) {\n\t\t\tfinal TestCls cls = map.get(name);\n\t\t\tif (cls == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tfinal int listSize = cls.size();\n\t\t\tfinal Iterator<TestCls> iterator = new Iterator<TestCls>() {\n\t\t\t\tint counter = 0;\n\n\t\t\t\t@Override\n\t\t\t\tpublic TestCls next() {\n\t\t\t\t\tcls.a++;\n\t\t\t\t\tcounter++;\n\t\t\t\t\treturn cls;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic boolean hasNext() {\n\t\t\t\t\treturn counter < listSize;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void remove() {\n\t\t\t\t\tthrow new UnsupportedOperationException();\n\t\t\t\t}\n\t\t\t};\n\t\t\treturn new Iterable<TestCls>() {\n\t\t\t\t@Override\n\t\t\t\tpublic Iterator<TestCls> iterator() {\n\t\t\t\t\treturn iterator;\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tprivate int size() {\n\t\t\treturn 7;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tTestCls v = new TestCls();\n\t\t\tv.a = 3;\n\t\t\tmap.put(\"a\", v);\n\t\t\tIterable<TestCls> it = test(\"a\");\n\t\t\tTestCls next = it.iterator().next();\n\t\t\tassertThat(next).isSameAs(v);\n\t\t\tassertThat(next.a).isEqualTo(4);\n\t\t}\n\t}\n\n\t@Test\n\t@NotYetImplemented\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"map.get(name);\")\n\t\t\t\t.doesNotContain(\"access$008\")\n\t\t\t\t.doesNotContain(\"synthetic\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass6.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestAnonymousClass6 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic Runnable test(final double d) {\n\t\t\treturn new Runnable() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tSystem.out.println(d);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public Runnable test(final double d) {\")\n\t\t\t\t.containsOne(\"return new Runnable() {\")\n\t\t\t\t.containsOne(\"public void run() {\")\n\t\t\t\t.containsOne(\"System.out.println(d);\")\n\t\t\t\t.doesNotContain(\"synthetic\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass7.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestAnonymousClass7 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static Runnable test(final double d) {\n\t\t\treturn new Runnable() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tSystem.out.println(d);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public static Runnable test(final double d) {\")\n\t\t\t\t.containsOne(\"return new Runnable() {\")\n\t\t\t\t.containsOne(\"public void run() {\")\n\t\t\t\t.containsOne(\"System.out.println(d);\")\n\t\t\t\t.doesNotContain(\"synthetic\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass8.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestAnonymousClass8 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic final double d = Math.abs(4);\n\n\t\tpublic Runnable test() {\n\t\t\treturn new Runnable() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tSystem.out.println(d);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public Runnable test() {\")\n\t\t\t\t.containsOne(\"return new Runnable() {\")\n\t\t\t\t.containsOne(\"public void run() {\")\n\t\t\t\t.containsOne(\"this.d);\")\n\t\t\t\t.doesNotContain(\"synthetic\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestAnonymousClass9.java",
    "content": "package jadx.tests.integration.inner;\n\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.FutureTask;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestAnonymousClass9 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic Callable<String> c = new Callable<String>() {\n\t\t\t@Override\n\t\t\tpublic String call() throws Exception {\n\t\t\t\treturn \"str\";\n\t\t\t}\n\t\t};\n\n\t\tpublic Runnable test() {\n\t\t\treturn new FutureTask<String>(this.c) {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tSystem.out.println(6);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"c = new Callable<String>() {\")\n\t\t\t\t.containsOne(\"return new FutureTask<String>(this.c) {\")\n\t\t\t\t.doesNotContain(\"synthetic\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestIncorrectAnonymousClass.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestIncorrectAnonymousClass extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic static class TestCls {\n\t\t\tpublic final class 1 {\n\t\t\t\tpublic void invoke() {\n\t\t\t\t\tnew 1(); // cause infinite self inline\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic void test() {\n\t\t\t\tnew 1();\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliFiles(\"TestCls\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public final class AnonymousClass1 {\")\n\t\t\t\t.countString(2, \"new AnonymousClass1();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestInner2Samples.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestInner2Samples extends IntegrationTest {\n\n\tpublic static class TestInner2 {\n\t\tprivate String a;\n\n\t\tpublic class A {\n\t\t\tpublic A() {\n\t\t\t\ta = \"a\";\n\t\t\t}\n\n\t\t\tpublic String a() {\n\t\t\t\treturn a;\n\t\t\t}\n\t\t}\n\n\t\tprivate static String b;\n\n\t\tpublic static class B {\n\t\t\tpublic B() {\n\t\t\t\tb = \"b\";\n\t\t\t}\n\n\t\t\tpublic String b() {\n\t\t\t\treturn b;\n\t\t\t}\n\t\t}\n\n\t\tprivate String c;\n\n\t\tprivate void setC(String c) {\n\t\t\tthis.c = c;\n\t\t}\n\n\t\tpublic class C {\n\t\t\tpublic String c() {\n\t\t\t\tsetC(\"c\");\n\t\t\t\treturn c;\n\t\t\t}\n\t\t}\n\n\t\tprivate static String d;\n\n\t\tprivate static void setD(String s) {\n\t\t\td = s;\n\t\t}\n\n\t\tpublic static class D {\n\t\t\tpublic String d() {\n\t\t\t\tsetD(\"d\");\n\t\t\t\treturn d;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestInner2.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"setD(\\\"d\\\");\")\n\t\t\t\t.doesNotContain(\"synthetic\")\n\t\t\t\t.doesNotContain(\"access$\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClass.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestInnerClass extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic class Inner {\n\t\t\tpublic class Inner2 extends Thread {\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"Inner {\")\n\t\t\t\t.contains(\"Inner2 extends Thread {\")\n\t\t\t\t.doesNotContain(\"super();\")\n\t\t\t\t.doesNotContain(\"this$\")\n\t\t\t\t.doesNotContain(\"/* synthetic */\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClass2.java",
    "content": "package jadx.tests.integration.inner;\n\nimport java.util.Timer;\nimport java.util.TimerTask;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestInnerClass2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static class TerminateTask extends TimerTask {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tSystem.err.println(\"Test timed out\");\n\t\t\t}\n\t\t}\n\n\t\tpublic void test() {\n\t\t\tnew Timer().schedule(new TerminateTask(), 1000L);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"new Timer().schedule(new TerminateTask(), 1000L);\")\n\t\t\t\t.doesNotContain(\"synthetic\")\n\t\t\t\t.doesNotContain(\"this\")\n\t\t\t\t.doesNotContain(\"null\")\n\t\t\t\t.doesNotContain(\"AnonymousClass\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClass3.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInnerClass3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate String c;\n\n\t\tprivate void setC(String c) {\n\t\t\tthis.c = c;\n\t\t}\n\n\t\tpublic class C {\n\t\t\tpublic String c() {\n\t\t\t\tsetC(\"c\");\n\t\t\t\treturn c;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"synthetic\")\n\t\t\t\t.doesNotContain(\"access$\")\n\t\t\t\t.doesNotContain(\"x0\")\n\t\t\t\t.contains(\"setC(\\\"c\\\");\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClass4.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestInnerClass4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic class C {\n\t\t\tpublic String c;\n\n\t\t\tprivate C() {\n\t\t\t\tthis.c = \"c\";\n\t\t\t}\n\t\t}\n\n\t\tpublic String test() {\n\t\t\treturn new C().c;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return new C().c;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClass5.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestInnerClass5 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tprivate String i0;\n\n\t\tpublic class A {\n\n\t\t\tprotected String a;\n\n\t\t\tpublic A() {\n\t\t\t\ta = \"\";\n\t\t\t}\n\n\t\t\tpublic String a() {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t}\n\n\t\tpublic class I0 {\n\t\t\tprivate String i0;\n\t\t\tprivate String i1;\n\n\t\t\tpublic class I1 {\n\t\t\t\tprivate String i0;\n\t\t\t\tprivate String i1;\n\t\t\t\tprivate String i2;\n\n\t\t\t\tpublic I1() {\n\t\t\t\t\tTestCls.this.i0 = \"i0\";\n\t\t\t\t\tI0.this.i0 = \"i1\";\n\t\t\t\t\tI0.this.i1 = \"i2\";\n\n\t\t\t\t\ti0 = \"i0\";\n\t\t\t\t\ti1 = \"i1\";\n\t\t\t\t\ti2 = \"i2\";\n\t\t\t\t}\n\n\t\t\t\tpublic String i() {\n\n\t\t\t\t\tString result = TestCls.this.i0 + I0.this.i0 + I0.this.i1 + i0 + i1 + i2;\n\n\t\t\t\t\tA a = new A() {\n\n\t\t\t\t\t\tpublic String a() {\n\t\t\t\t\t\t\tTestCls.this.i0 = \"i1\";\n\t\t\t\t\t\t\tI0.this.i0 = \"i2\";\n\t\t\t\t\t\t\tI0.this.i1 = \"i3\";\n\t\t\t\t\t\t\tI1.this.i0 = \"i1\";\n\t\t\t\t\t\t\tI1.this.i1 = \"i2\";\n\t\t\t\t\t\t\tI1.this.i2 = \"i3\";\n\t\t\t\t\t\t\ta = \"a\";\n\n\t\t\t\t\t\t\treturn TestCls.this.i0 + I0.this.i0 + I0.this.i1 + I1.this.i0 + I1.this.i1 + I1.this.i2 + a;\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\n\t\t\t\t\treturn result + a.a();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic I0() {\n\t\t\t\tTestCls.this.i0 = \"i-\";\n\t\t\t\ti0 = \"i0\";\n\t\t\t\ti1 = \"i1\";\n\t\t\t}\n\n\t\t\tpublic String i() {\n\t\t\t\tString result = TestCls.this.i0 + i0 + i1;\n\t\t\t\treturn result + new I1().i();\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() throws Exception {\n\t\t\tassertThat(new I0().i()).isEqualTo(\"i-i0i1i0i1i2i0i1i2i1i2i3i1i2i3a\");\n\t\t\tassertThat(i0).isEqualTo(\"i1\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public class I0 {\")\n\t\t\t\t.containsOne(\"public class I1 {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClassFakeSyntheticConstructor.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInnerClassFakeSyntheticConstructor extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic class TestCls {\n\t\t\tpublic synthetic TestCls(String a) {\n\t\t\t\tthis(a, true);\n\t\t\t}\n\n\t\t\tpublic TestCls(String a, boolean b) {\n\t\t\t}\n\n\t\t\tpublic static TestCls build(String str) {\n\t\t\t\treturn new TestCls(str);\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali(\"inner/TestInnerClassFakeSyntheticConstructor\", \"jadx.tests.inner.TestCls\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"TestCls(String a) {\");\n\t\t// and must compile\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClassSyntheticConstructor.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\npublic class TestInnerClassSyntheticConstructor extends IntegrationTest {\n\n\tprivate class TestCls {\n\t\tprivate int mth() {\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tpublic int call() {\n\t\treturn new TestCls().mth();\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tgetClassNode(TestInnerClassSyntheticConstructor.class);\n\t\t// must compile, no usage of removed synthetic empty class\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClassSyntheticRename.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Issue: #336\n */\n@SuppressWarnings(\"CommentedOutCode\")\npublic class TestInnerClassSyntheticRename extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tprivate class TestCls extends AsyncTask<Uri, Uri, List<Uri>> {\n\t\t\t@Override\n\t\t\tprotected List<Uri> doInBackground(Uri... uris) {\n\t\t\t\tLog.i(\"MyAsync\", \"doInBackground\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onPostExecute(List<Uri> uris) {\n\t\t\t\tLog.i(\"MyAsync\", \"onPostExecute\");\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"List<Uri> doInBackground(Uri... uriArr) {\")\n\t\t\t\t.containsOne(\"void onPostExecute(List<Uri> list) {\")\n\t\t\t\t.doesNotContain(\"synthetic\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerConstructorCall.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInnerConstructorCall extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\t\tpublic class A {\n\t\t\tpublic class AA {\n\t\t\t\tpublic void test() {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic void test() {\n\t\t\tA a = new A();\n\t\t\tA.AA aa = a.new AA();\n\t\t\taa.test();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"A.AA aa = a.new AA();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestNestedAnonymousClass.java",
    "content": "package jadx.tests.integration.inner;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.Callable;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestNestedAnonymousClass extends SmaliTest {\n\n\t@SuppressWarnings(\"Convert2Lambda\")\n\tpublic static class TestCls {\n\t\tpublic void test() {\n\t\t\tuse(new Callable<Runnable>() {\n\t\t\t\t@Override\n\t\t\t\tpublic Runnable call() {\n\t\t\t\t\treturn new Runnable() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\tSystem.out.println(\"run\");\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tpublic void testLambda() {\n\t\t\tuse(() -> () -> System.out.println(\"lambda\"));\n\t\t}\n\n\t\tpublic void use(Callable<Runnable> r) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"use(new Callable<Runnable>() {\")\n\t\t\t\t.containsOne(\"return new Runnable() {\");\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tgetArgs().setRenameFlags(Collections.emptySet());\n\t\tList<ClassNode> classes = loadFromSmaliFiles();\n\t\tassertThat(searchCls(classes, \"A\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"use(new Callable<Runnable>() {\")\n\t\t\t\t.containsOne(\"return new Runnable() {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestOuterConstructorCall.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.CommentsLevel;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestOuterConstructorCall extends IntegrationTest {\n\n\t@SuppressWarnings({ \"InnerClassMayBeStatic\", \"unused\" })\n\tpublic static class TestCls {\n\t\tprivate TestCls(Inner inner) {\n\t\t\tSystem.out.println(inner);\n\t\t}\n\n\t\tprivate class Inner {\n\t\t\tprivate TestCls test() {\n\t\t\t\treturn new TestCls(this);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tgetArgs().setCommentsLevel(CommentsLevel.WARN);\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"class Inner {\")\n\t\t\t\t.containsOne(\"return new TestOuterConstructorCall$TestCls(this);\")\n\t\t\t\t.doesNotContain(\"synthetic\", \"this$0\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestReplaceConstsInAnnotations.java",
    "content": "package jadx.tests.integration.inner;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestReplaceConstsInAnnotations extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\t@Target(ElementType.TYPE)\n\t\t@Retention(RetentionPolicy.RUNTIME)\n\t\tpublic @interface A {\n\t\t\tint i();\n\n\t\t\tfloat f();\n\t\t}\n\n\t\t@A(i = -1, f = C.FLOAT_CONST)\n\t\tpublic static class C {\n\t\t\tpublic static final float FLOAT_CONST = 3.14f;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOnlyOnce(\"f = C.FLOAT_CONST\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestReplaceConstsInAnnotations2.java",
    "content": "package jadx.tests.integration.inner;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestReplaceConstsInAnnotations2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\t@Target(ElementType.TYPE)\n\t\t@Retention(RetentionPolicy.RUNTIME)\n\t\tpublic @interface A {\n\t\t\tint[] value();\n\t\t}\n\n\t\t@A(C.INT_CONST)\n\t\tpublic static class C {\n\t\t\tpublic static final int INT_CONST = 23412342;\n\t\t}\n\n\t\t@A({ C.INT_CONST, C2.INT_CONST })\n\t\tpublic static class C2 {\n\t\t\tpublic static final int INT_CONST = 34563456;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t// .containsOne(\"@A(C.INT_CONST)\") // TODO: remove brackets for single element\n\t\t\t\t.containsOne(\"@A({C.INT_CONST}\")\n\t\t\t\t.containsOne(\"@A({C.INT_CONST, C2.INT_CONST})\")\n\t\t\t\t.containsOne(\"23412342\")\n\t\t\t\t.containsOne(\"34563456\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/inner/TestSyntheticMthRename.java",
    "content": "package jadx.tests.integration.inner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Issue: https://github.com/skylot/jadx/issues/397\n */\npublic class TestSyntheticMthRename extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic class TestCls {\n\t\t\tpublic interface I<R, P> {\n\t\t\t\tR call(P... p);\n\t\t\t}\n\n\t\t\tpublic static final class A implements I<String, Runnable> {\n\t\t\t\tpublic synthetic virtual Object call(Object[] objArr) {\n\t\t\t\t\treturn renamedCall((Runnable[]) objArr);\n\t\t\t\t}\n\n\t\t\t\tprivate varargs direct String renamedCall(Runnable... p) {\n\t\t\t\t\treturn \"str\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliFiles(\"inner\", \"TestSyntheticMthRename\", \"TestCls\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public String call(Runnable... p) {\")\n\t\t\t\t.doesNotContain(\"synthetic\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestCastInOverloadedAccessor.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestCastInOverloadedAccessor extends SmaliTest {\n\tstatic class X {\n\t\tvoid test() {\n\t\t\tnew Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\touterMethod(\"\");\n\t\t\t\t\touterMethod(\"\", \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tprivate void outerMethod(String s) {\n\t\t}\n\n\t\tprivate void outerMethod(String s, String t) {\n\t\t}\n\n\t\tprivate void outerMethod(int a) {\n\t\t}\n\n\t\tprivate void outerMethod(int a, int b) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(X.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"outerMethod(\\\"\\\")\")\n\t\t\t\t.containsOne(\"outerMethod(\\\"\\\", \\\"\\\")\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestCastInOverloadedInvoke.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestCastInOverloadedInvoke extends IntegrationTest {\n\n\t@SuppressWarnings(\"IllegalType\")\n\tpublic static class TestCls {\n\t\tint c = 0;\n\n\t\tpublic void test() {\n\t\t\tcall(new ArrayList<>());\n\t\t\tcall((List<String>) new ArrayList<String>());\n\t\t}\n\n\t\tpublic void test2(Object obj) {\n\t\t\tif (obj instanceof String) {\n\t\t\t\tcall((String) obj);\n\t\t\t}\n\t\t}\n\n\t\tpublic void test3() {\n\t\t\tcall((String) null);\n\t\t\tcall((List<String>) null);\n\t\t\tcall((ArrayList<String>) null);\n\t\t}\n\n\t\tpublic void call(String str) {\n\t\t\tc += 1;\n\t\t}\n\n\t\tpublic void call(List<String> list) {\n\t\t\tc += 10;\n\t\t}\n\n\t\tpublic void call(ArrayList<String> list) {\n\t\t\tc += 100;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\ttest();\n\t\t\tassertThat(c).isEqualTo(10 + 100);\n\t\t\tc = 0;\n\t\t\ttest2(\"str\");\n\t\t\tassertThat(c).isEqualTo(1);\n\t\t\tc = 0;\n\t\t\ttest3();\n\t\t\tassertThat(c).isEqualTo(111);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"call(new ArrayList<>());\")\n\t\t\t\t.containsOne(\"call((List<String>) new ArrayList());\")\n\t\t\t\t.containsOne(\"call((String) obj);\");\n\t}\n\n\t@NotYetImplemented\n\t@Test\n\tpublic void testNYI() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"call((List<String>) new ArrayList<String>());\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestCastInOverloadedInvoke2.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestCastInOverloadedInvoke2 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"new Intent().putExtra(\\\"param\\\", (Parcelable) null);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestCastInOverloadedInvoke3.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Test cast for 'unknown' but overloaded method\n */\npublic class TestCastInOverloadedInvoke3 extends IntegrationTest {\n\n\tpublic static class OuterCls {\n\t\tstatic int c = 0;\n\n\t\tpublic static void call(String str) {\n\t\t\tc = 1;\n\t\t}\n\n\t\tpublic static void call(List<String> list) {\n\t\t\tc = 10;\n\t\t}\n\t}\n\n\tpublic static class TestCls {\n\t\tpublic void test() {\n\t\t\tOuterCls.call((String) null);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"OuterCls.call((String) null);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestCastInOverloadedInvoke4.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestCastInOverloadedInvoke4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic String test(String str) {\n\t\t\treturn str.replace('\\n', ' ');\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return str.replace('\\\\n', ' ');\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestConstructorWithMoves.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestConstructorWithMoves extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic boolean test() {\n\t\t\t\tjava.lang.Boolean r5 = new java.lang.Boolean\n\t\t\t\tr8 = r5\n\t\t\t\tr5 = r8\n\t\t\t\tr6 = r8\n\t\t\t\tjava.lang.String r7 = \"test\"\n\t\t\t\tr6.<init>(r7)\n\t\t\t\tjava.lang.Boolean r5 = (java.lang.Boolean) r5\n\t\t\t\tboolean r5 = r5.booleanValue()\n\t\t\t\tr3 = r5\n\t\t\t\treturn r3\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return new Boolean(\\\"test\\\").booleanValue();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestHierarchyOverloadedInvoke.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestHierarchyOverloadedInvoke extends IntegrationTest {\n\n\t@SuppressWarnings(\"IllegalType\")\n\tpublic static class TestCls {\n\t\tstatic int c = 0;\n\t\tB b = new B();\n\n\t\tpublic interface I {\n\t\t\tdefault void call(String str) {\n\t\t\t\tc += 1;\n\t\t\t}\n\t\t}\n\n\t\tpublic static class A implements I {\n\t\t\tpublic void call(List<String> list) {\n\t\t\t\tc += 10;\n\t\t\t}\n\t\t}\n\n\t\tpublic static class B extends A {\n\t\t\tpublic void call(ArrayList<String> list) {\n\t\t\t\tc += 100;\n\t\t\t}\n\t\t}\n\n\t\tpublic void test() {\n\t\t\tb.call(new ArrayList<>());\n\t\t\tb.call((List<String>) new ArrayList<String>());\n\t\t}\n\n\t\tpublic void test2(Object obj) {\n\t\t\tif (obj instanceof String) {\n\t\t\t\tb.call((String) obj);\n\t\t\t}\n\t\t}\n\n\t\tpublic void test3() {\n\t\t\tb.call((String) null);\n\t\t\tb.call((List<String>) null);\n\t\t\tb.call((ArrayList<String>) null);\n\t\t}\n\n\t\tpublic void test4() {\n\t\t\t((I) b).call(null);\n\t\t\t((A) b).call((String) null);\n\t\t\t((A) b).call((List<String>) null);\n\t\t}\n\n\t\tpublic void check() {\n\t\t\ttest();\n\t\t\tassertThat(c).isEqualTo(10 + 100);\n\n\t\t\tc = 0;\n\t\t\ttest2(\"str\");\n\t\t\tassertThat(c).isEqualTo(1);\n\n\t\t\tc = 0;\n\t\t\ttest3();\n\t\t\tassertThat(c).isEqualTo(111);\n\n\t\t\tc = 0;\n\t\t\ttest4();\n\t\t\tassertThat(c).isEqualTo(12);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"b.call(new ArrayList<>());\")\n\t\t\t\t.containsOne(\"b.call((List<String>) new ArrayList());\")\n\t\t\t\t.containsOne(\"b.call((String) obj);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestInheritedStaticInvoke.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestInheritedStaticInvoke extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static class A {\n\t\t\tpublic static int a() {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\n\t\tpublic static class B extends A {\n\t\t}\n\n\t\tpublic int test() {\n\t\t\treturn B.a(); // not A.a()\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return B.a();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestInvoke1.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport java.io.IOException;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestInvoke1 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tprivate A is;\n\n\t\tpublic C test(int start) throws IOException {\n\t\t\tint id = is.readInt32();\n\t\t\tString name = is.readString16Fixed(128);\n\n\t\t\tlong typeStringsOffset = start + is.readInt32();\n\t\t\tlong keyStringsOffset = start + is.readInt32();\n\n\t\t\tString[] types = null;\n\t\t\tif (typeStringsOffset != 0) {\n\t\t\t\ttypes = strs();\n\t\t\t}\n\t\t\tString[] keys = null;\n\t\t\tif (keyStringsOffset != 0) {\n\t\t\t\tkeys = strs();\n\t\t\t}\n\n\t\t\tC pkg = new C(id, name, types, keys);\n\t\t\tif (id == 0x7F) {\n\t\t\t\tis.readInt32();\n\t\t\t}\n\t\t\treturn pkg;\n\t\t}\n\n\t\tprivate String[] strs() {\n\t\t\treturn new String[0];\n\t\t}\n\n\t\tprivate static final class C {\n\t\t\tpublic C(int id, String name, String[] types, String[] keys) {\n\t\t\t}\n\t\t}\n\n\t\tprivate final class A {\n\t\t\tpublic int readInt32() {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tpublic String readString16Fixed(int i) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"C pkg = new C(id, name, types, keys);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestInvokeInCatch.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport java.io.IOException;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInvokeInCatch extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static final String TAG = \"TAG\";\n\n\t\tpublic void test(int[] a, int b) {\n\t\t\ttry {\n\t\t\t\texc();\n\t\t\t} catch (IOException e) {\n\t\t\t\tif (b == 1) {\n\t\t\t\t\tlog(TAG, \"Error: {}\", e.getMessage());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate static void log(String tag, String str, String... args) {\n\t\t}\n\n\t\tprivate void exc() throws IOException {\n\t\t\tthrow new IOException();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"try {\")\n\t\t\t\t.containsOne(\"exc();\")\n\t\t\t\t.doesNotContain(\"return;\")\n\t\t\t\t.containsOne(\"} catch (IOException e) {\")\n\t\t\t\t.containsOne(\"if (b == 1) {\")\n\t\t\t\t.containsOne(\"log(TAG, \\\"Error: {}\\\", e.getMessage());\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestInvokeWithWideVars.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInvokeWithWideVars extends IntegrationTest {\n\n\t@SuppressWarnings(\"SameParameterValue\")\n\tpublic static class TestCls {\n\n\t\tpublic long test1() {\n\t\t\treturn call(1, 2L);\n\t\t}\n\n\t\tpublic long test2() {\n\t\t\treturn rangeCall(1L, 2, 3.0d, (byte) 4);\n\t\t}\n\n\t\tprivate long call(int a, long b) {\n\t\t\treturn 0L;\n\t\t}\n\n\t\tprivate long rangeCall(long a, int b, double c, byte d) {\n\t\t\treturn 0L;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return call(1, 2L);\")\n\t\t\t\t.containsOne(\"return rangeCall(1L, 2, 3.0d, (byte) 4);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestOverloadedInvoke.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestOverloadedInvoke extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static final int N = 10;\n\n\t\tpublic void test() {\n\t\t\tint[][][] arr = new int[N][N][N];\n\t\t\tuse(arr, -1);\n\t\t\tuse(arr[0], -2);\n\t\t}\n\n\t\tpublic void use(Object[][] arr, Object obj) {\n\t\t}\n\n\t\tpublic void use(int[][] arr, int i) {\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.D8_J11, TestProfile.JAVA8 })\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"use(iArr[0], -2);\")\n\t\t\t\t.containsOne(\"use((Object[][]) iArr, (Object) (-1));\");\n\t\t// TODO: don't add unnecessary casts\n\t\t// .containsOne(\"use(iArr, -1);\");\n\t\t// TODO: replace call `Array.newInstance` with new array creation: `new int[N][N][N]`\n\t\t// .containsOne(\"new int[10][10][10];\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestOverloadedMethodInvoke.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestOverloadedMethodInvoke extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tint c;\n\n\t\tpublic void method(Throwable th, int a) {\n\t\t\tc++;\n\t\t\tif (th != null) {\n\t\t\t\tc += 100;\n\t\t\t}\n\t\t\tc += a;\n\t\t}\n\n\t\tpublic void method(Exception e, int a) {\n\t\t\tc += 1000;\n\t\t\tif (e != null) {\n\t\t\t\tc += 10000;\n\t\t\t}\n\t\t\tc += a;\n\t\t}\n\n\t\tpublic void test(Throwable th, Exception e) {\n\t\t\tmethod(e, 10);\n\t\t\tmethod(th, 100);\n\t\t\tmethod((Throwable) e, 1000);\n\t\t\tmethod((Exception) th, 10000);\n\t\t}\n\n\t\tpublic void check() {\n\t\t\ttest(null, new Exception());\n\t\t\tassertThat(c).isEqualTo(23212);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public void test(Throwable th, Exception e) {\")\n\t\t\t\t.containsOne(\"method(e, 10);\")\n\t\t\t\t.containsOne(\"method(th, 100);\")\n\t\t\t\t.containsOne(\"method((Throwable) e, 1000);\")\n\t\t\t\t.containsOne(\"method((Exception) th, 10000);\")\n\t\t\t\t.doesNotContain(\"(Exception) e\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestOverloadedMethodInvoke2.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestOverloadedMethodInvoke2 extends IntegrationTest {\n\n\tpublic static class AbstractItem {\n\n\t\tpublic void doSomething(Container c, Item i) {\n\t\t\tc.add(i);\n\t\t}\n\n\t\tpublic static class Container {\n\n\t\t\tpublic <T extends AbstractItem> int add(T t) {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tpublic void add(AbstractItem... item) {\n\t\t\t}\n\t\t}\n\n\t\tpublic static class Item extends AbstractItem {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestOverloadedMethodInvoke2.AbstractItem.class))\n\t\t\t\t.code().containsOne(\"c.add(i);\")\n\t\t\t\t.doesNotContain(\"(Container)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestPolymorphicInvoke.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport java.lang.invoke.MethodHandle;\nimport java.lang.invoke.MethodHandles;\nimport java.lang.invoke.MethodType;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\npublic class TestPolymorphicInvoke extends SmaliTest {\n\n\tpublic static class TestCls {\n\t\tpublic String func(int a, int c) {\n\t\t\treturn String.valueOf(a + c);\n\t\t}\n\n\t\tpublic String test() {\n\t\t\ttry {\n\t\t\t\tMethodType methodType = MethodType.methodType(String.class, Integer.TYPE, Integer.TYPE);\n\t\t\t\tMethodHandle methodHandle = MethodHandles.lookup().findVirtual(TestCls.class, \"func\", methodType);\n\t\t\t\treturn (String) methodHandle.invoke(this, 1, 2);\n\t\t\t} catch (Throwable e) {\n\t\t\t\tfail(\"\", e);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test()).isEqualTo(\"3\");\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.D8_J11 })\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls).code()\n\t\t\t\t.containsOne(\"return (String) methodHandle.invoke(this, 1, 2);\");\n\t\tassertThat(cls).disasmCode()\n\t\t\t\t.containsOne(\"invoke-polymorphic\");\n\t}\n\n\t@TestWithProfiles({ TestProfile.JAVA8, TestProfile.JAVA11 })\n\tpublic void testJava() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return (String) methodHandle.invoke(this, 1, 2);\");\n\t\t// java uses 'invokevirtual'\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"String ret = (String) methodHandle.invoke(this, 10, 20);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestPolymorphicRangeInvoke.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport java.lang.invoke.MethodHandle;\nimport java.lang.invoke.MethodHandles;\nimport java.lang.invoke.MethodType;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\npublic class TestPolymorphicRangeInvoke extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic String func2(int a, int b, int c, int d, int e, int f) {\n\t\t\treturn String.valueOf(a + b + c + d + e + f);\n\t\t}\n\n\t\tpublic String test() {\n\t\t\ttry {\n\t\t\t\tMethodHandles.Lookup lookup = MethodHandles.lookup();\n\t\t\t\tMethodType methodType = MethodType.methodType(String.class, Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE,\n\t\t\t\t\t\tInteger.TYPE, Integer.TYPE);\n\t\t\t\tMethodHandle methodHandle = lookup.findVirtual(TestCls.class, \"func2\", methodType);\n\t\t\t\treturn (String) methodHandle.invoke(this, 10, 20, 30, 40, 50, 60);\n\t\t\t} catch (Throwable e) {\n\t\t\t\tfail(\"\", e);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test()).isEqualTo(\"210\");\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8 })\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls).code()\n\t\t\t\t.containsOne(\"return (String) methodHandle.invoke(this, 10, 20, 30, 40, 50, 60);\");\n\t\tassertThat(cls).disasmCode()\n\t\t\t\t.containsOne(\"invoke-polymorphic/range\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestRawCustomInvoke.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport java.lang.invoke.CallSite;\nimport java.lang.invoke.ConstantCallSite;\nimport java.lang.invoke.MethodHandles;\nimport java.lang.invoke.MethodType;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\npublic class TestRawCustomInvoke extends SmaliTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static String func(int a, double b) {\n\t\t\treturn String.valueOf(a + b);\n\t\t}\n\n\t\tprivate static CallSite staticBootstrap(MethodHandles.Lookup lookup, String name, MethodType type) {\n\t\t\ttry {\n\t\t\t\treturn new ConstantCallSite(lookup.findStatic(lookup.lookupClass(), name, type));\n\t\t\t} catch (NoSuchMethodException | IllegalAccessException e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\n\t\tpublic String test() {\n\t\t\ttry {\n\t\t\t\treturn (String) staticBootstrap(MethodHandles.lookup(), \"func\",\n\t\t\t\t\t\tMethodType.methodType(String.class, Integer.TYPE, Double.TYPE))\n\t\t\t\t\t\t\t\t.dynamicInvoker().invoke(1, 2.0d);\n\t\t\t} catch (Throwable e) {\n\t\t\t\tfail(\"\", e);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test()).isEqualTo(\"3.0\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\t// this code does not contain `invoke-custom` instruction\n\t\t// only check if equivalent polymorphic call is correct\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\n\t\t\t\t\t\t\"return (String) staticBootstrap(MethodHandles.lookup(), \\\"func\\\", MethodType.methodType(String.class, Integer.TYPE, Double.TYPE)).dynamicInvoker().invoke(1, 2.0d);\");\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tforceDecompiledCheck();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\n\t\t\t\t\t\t\"return (String) staticBootstrap(MethodHandles.lookup(), \\\"func\\\", MethodType.methodType(String.class, Integer.TYPE, Double.TYPE)).dynamicInvoker().invoke(1, 2.0d) /* invoke-custom */;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestSuperInvoke.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestSuperInvoke extends IntegrationTest {\n\n\tpublic class A {\n\t\tpublic int a() {\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tpublic class B extends A {\n\t\t@Override\n\t\tpublic int a() {\n\t\t\treturn super.a() + 2;\n\t\t}\n\n\t\tpublic int test() {\n\t\t\treturn a();\n\t\t}\n\t}\n\n\tpublic void check() {\n\t\tassertThat(new B().test()).isEqualTo(3);\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestSuperInvoke.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"return super.a() + 2;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestSuperInvoke2.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSuperInvoke2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn super.toString();\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(new TestCls().toString()).containsOne(\"@\");\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.JAVA8 })\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return super.toString();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestSuperInvokeUnknown.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSuperInvokeUnknown extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static class BaseClass {\n\t\t\tpublic int doSomething() {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\n\t\tpublic static class NestedClass extends BaseClass {\n\t\t\t@Override\n\t\t\tpublic int doSomething() {\n\t\t\t\treturn super.doSomething();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.NestedClass.class)) // BaseClass unknown\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return super.doSomething();\");\n\t}\n\n\t@Test\n\tpublic void testTopCls() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return super.doSomething();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestSuperInvokeWithGenerics.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestSuperInvokeWithGenerics extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static class A<T extends Exception, V> {\n\t\t\tpublic A(T t) {\n\t\t\t\tSystem.out.println(\"t\" + t);\n\t\t\t}\n\n\t\t\tpublic A(V v) {\n\t\t\t\tSystem.out.println(\"v\" + v);\n\t\t\t}\n\t\t}\n\n\t\tpublic static class B extends A<Exception, String> {\n\t\t\tpublic B(String s) {\n\t\t\t\tsuper(s);\n\t\t\t}\n\n\t\t\tpublic B(Exception e) {\n\t\t\t\tsuper(e);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"super(e);\")\n\t\t\t\t.containsOne(\"super(s);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestVarArg.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestVarArg extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test1(int... a) {\n\t\t}\n\n\t\tpublic void test2(int i, Object... a) {\n\t\t}\n\n\t\tpublic void test3(int[] a) {\n\t\t}\n\n\t\tpublic void call() {\n\t\t\ttest1(1, 2);\n\t\t\ttest2(3, \"1\", 7);\n\t\t\ttest3(new int[] { 5, 8 });\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"void test1(int... a) {\")\n\t\t\t\t.contains(\"void test2(int i, Object... a) {\")\n\t\t\t\t.contains(\"test1(1, 2);\")\n\t\t\t\t.contains(\"test2(3, \\\"1\\\", 7);\")\n\t\t\t\t// negative case\n\t\t\t\t.contains(\"void test3(int[] a) {\")\n\t\t\t\t.contains(\"test3(new int[]{5, 8});\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/invoke/TestVarArg2.java",
    "content": "package jadx.tests.integration.invoke;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestVarArg2 extends IntegrationTest {\n\n\t@SuppressWarnings(\"ConstantConditions\")\n\tpublic static class TestCls {\n\t\tprotected static boolean b1;\n\t\tprotected static final boolean IS_VALID = b1 && isValid(\"test\");\n\n\t\tprivate static boolean isValid(String... string) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"isValid(\\\"test\\\")\"); // TODO: .containsOne(\"b1 && isValid(\\\"test\\\")\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaArgs.java",
    "content": "package jadx.tests.integration.java8;\n\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLambdaArgs extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test1() {\n\t\t\tcall1(a -> -a);\n\t\t}\n\n\t\tpublic void test2() {\n\t\t\tcall2((a, b) -> a - b);\n\t\t}\n\n\t\tprivate void call1(Function<Integer, Integer> func) {\n\t\t}\n\n\t\tprivate void call2(BiFunction<Integer, Integer, Integer> func) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"call1(a ->\")\n\t\t\t\t.containsOne(\"call2((a, b) ->\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaConstructor.java",
    "content": "package jadx.tests.integration.java8;\n\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLambdaConstructor extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic Supplier<Exception> test() {\n\t\t\treturn RuntimeException::new;\n\t\t}\n\n\t\tpublic void check() throws Exception {\n\t\t\tassertThat(test().get()).isInstanceOf(RuntimeException.class);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return RuntimeException::new;\");\n\t}\n\n\t@Test\n\tpublic void testFallback() {\n\t\tsetFallback();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"r0 = java.lang.RuntimeException::new\")\n\t\t\t\t.containsOne(\"return r0\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaExtVar.java",
    "content": "package jadx.tests.integration.java8;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLambdaExtVar extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test(List<String> list, String str) {\n\t\t\tlist.removeIf(s -> s.equals(str));\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tList<String> list = new ArrayList<>(Arrays.asList(\"a\", \"str\", \"b\"));\n\t\t\ttest(list, \"str\");\n\t\t\tassertThat(list).isEqualTo(Arrays.asList(\"a\", \"b\"));\n\t\t}\n\t}\n\n\t@TestWithProfiles(TestProfile.DX_J8)\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls)\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"lambda$\")\n\t\t\t\t.containsOne(\"return s.equals(str);\"); // TODO: simplify to expression\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaExtVar2.java",
    "content": "package jadx.tests.integration.java8;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLambdaExtVar2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test(List<String> list) {\n\t\t\tString space = \" \";\n\t\t\tlist.removeIf(s -> s.equals(space) || s.contains(space));\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tList<String> list = new ArrayList<>(Arrays.asList(\"a\", \" \", \"b\", \"r \"));\n\t\t\ttest(list);\n\t\t\tassertThat(list).isEqualTo(Arrays.asList(\"a\", \"b\"));\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.D8_J11, TestProfile.JAVA11 })\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"lambda$\")\n\t\t\t\t.containsOne(\"String space = \\\" \\\";\")\n\t\t\t\t.containsOne(\"s.equals(space) || s.contains(space)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaInArray.java",
    "content": "package jadx.tests.integration.java8;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLambdaInArray extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic List<Function<String, Integer>> test() {\n\t\t\treturn Arrays.asList(this::call1, this::call2);\n\t\t}\n\n\t\tprivate Integer call1(String s) {\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate Integer call2(String s) {\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic void check() throws Exception {\n\t\t\tassertThat(test()).hasSize(2);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return Arrays.asList(this::call1, this::call2);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaInstance.java",
    "content": "package jadx.tests.integration.java8;\n\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLambdaInstance extends IntegrationTest {\n\n\t@SuppressWarnings(\"Convert2MethodRef\")\n\tpublic static class TestCls {\n\n\t\tpublic Function<String, Integer> test() {\n\t\t\treturn str -> this.call(str);\n\t\t}\n\n\t\tpublic Function<String, Integer> testMthRef() {\n\t\t\treturn this::call;\n\t\t}\n\n\t\tpublic Integer call(String str) {\n\t\t\treturn Integer.parseInt(str);\n\t\t}\n\n\t\tpublic Function<Integer, String> test2() {\n\t\t\treturn num -> num.toString();\n\t\t}\n\n\t\tpublic Function<Integer, String> testMthRef2() {\n\t\t\treturn Object::toString;\n\t\t}\n\n\t\tpublic void check() throws Exception {\n\t\t\tassertThat(test().apply(\"11\")).isEqualTo(11);\n\t\t\tassertThat(testMthRef().apply(\"7\")).isEqualTo(7);\n\n\t\t\tassertThat(test2().apply(15)).isEqualTo(\"15\");\n\t\t\tassertThat(testMthRef2().apply(13)).isEqualTo(\"13\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"lambda$\")\n\t\t\t\t.doesNotContain(\"renamed\")\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"return str -> {\",\n\t\t\t\t\t\tindent() + \"return call(str);\",\n\t\t\t\t\t\t\"};\")\n\t\t\t\t// .containsOne(\"return Object::toString;\") // TODO\n\t\t\t\t.containsOne(\"return this::call;\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n\n\t@Test\n\tpublic void testFallback() {\n\t\tsetFallback();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaInstance2.java",
    "content": "package jadx.tests.integration.java8;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLambdaInstance2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate String field;\n\n\t\tpublic Runnable test(String str, int i) {\n\t\t\treturn () -> call(str, i);\n\t\t}\n\n\t\tpublic void call(String str, int i) {\n\t\t\tfield = str + '=' + i;\n\t\t}\n\n\t\tpublic void check() throws Exception {\n\t\t\tfield = \"\";\n\t\t\ttest(\"num\", 7).run();\n\t\t\tassertThat(field).isEqualTo(\"num=7\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"lambda$\")\n\t\t\t\t.containsOne(\"call(str, i)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaInstance3.java",
    "content": "package jadx.tests.integration.java8;\n\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.RaungTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"DataFlowIssue\")\npublic class TestLambdaInstance3 extends RaungTest {\n\n\tpublic interface TestCls<R> extends Supplier<R> {\n\t\tdefault TestCls<R> test() {\n\t\t\treturn (TestCls<R> & Memoized) Lazy.of(this)::get;\n\t\t}\n\t}\n\n\tpublic static final class Lazy<T> implements Supplier<T> {\n\t\tpublic static <T> Lazy<T> of(Supplier<? extends T> supplier) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic T get() {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tinterface Memoized {\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\t// some java versions failed to compile usage of interface with '$' in name\n\t\taddClsRename(\"jadx.tests.integration.java8.TestLambdaInstance3$TestCls\", \"java8.TestCls\");\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"this::get\")\n\t\t\t\t.containsOne(\"return (TestCls) lazyOf::get;\");\n\t\t// TODO: type inference set type for 'lazyOf' to Memoized and cast incorrectly removed\n\t\t// .containsOne(\"Memoized)\");\n\t}\n\n\t@Test\n\tpublic void testRaung() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromRaung())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"this::get\")\n\t\t\t\t.containsOne(\" lazyOf::get\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaResugar.java",
    "content": "package jadx.tests.integration.java8;\n\nimport java.util.function.Function;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLambdaResugar extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate String field;\n\n\t\tpublic void test() {\n\t\t\tcall(s -> {\n\t\t\t\tthis.field = s;\n\t\t\t\treturn s.length();\n\t\t\t});\n\t\t}\n\n\t\tpublic void call(Function<String, Integer> func) {\n\t\t}\n\t}\n\n\t@NotYetImplemented(\"Inline lambda methods\")\n\t@TestWithProfiles(TestProfile.D8_J11_DESUGAR)\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"lambda$\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaReturn.java",
    "content": "package jadx.tests.integration.java8;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLambdaReturn extends IntegrationTest {\n\n\t@SuppressWarnings(\"unused\")\n\tpublic static class TestCls {\n\t\tinterface Function0<R> {\n\t\t\tR apply();\n\t\t}\n\n\t\tpublic static class T2 {\n\t\t\tpublic long l;\n\n\t\t\tpublic T2(long l) {\n\t\t\t\tthis.l = l;\n\t\t\t}\n\n\t\t\tpublic void w() {\n\t\t\t}\n\t\t}\n\n\t\tpublic Byte test(Byte b1) {\n\t\t\tFunction0<Void> f1 = () -> {\n\t\t\t\tnew T2(94L).w();\n\t\t\t\treturn null;\n\t\t\t};\n\t\t\tf1.apply();\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@TestWithProfiles(TestProfile.DX_J8)\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"Function0<Void> f1 = () -> {\",\n\t\t\t\t\t\tindent() + \"new T2(94L).w();\",\n\t\t\t\t\t\tindent() + \"return null;\",\n\t\t\t\t\t\t\"};\");\n\t}\n\n\t@TestWithProfiles(TestProfile.D8_J11_DESUGAR)\n\tpublic void testLambda() {\n\t\tgetClassNode(TestCls.class);\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaStatic.java",
    "content": "package jadx.tests.integration.java8;\n\nimport java.util.concurrent.Callable;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLambdaStatic extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic Callable<String> test1() {\n\t\t\treturn () -> \"test\";\n\t\t}\n\n\t\tpublic Callable<String> test2(String str) {\n\t\t\treturn () -> str;\n\t\t}\n\n\t\tpublic Function<String, Integer> test3(String a) {\n\t\t\treturn (b) -> Integer.parseInt(a) - Integer.parseInt(b);\n\t\t}\n\n\t\tpublic Function<String, Integer> test4() {\n\t\t\treturn Integer::parseInt;\n\t\t}\n\n\t\t@SuppressWarnings(\"Convert2MethodRef\")\n\t\tpublic Function<String, Integer> test4a() {\n\t\t\treturn s -> Integer.parseInt(s);\n\t\t}\n\n\t\tpublic Function<String, Integer> test5() {\n\t\t\tString str = Integer.toString(3);\n\t\t\treturn (s) -> Integer.parseInt(str) - Integer.parseInt(s);\n\t\t}\n\n\t\tpublic void check() throws Exception {\n\t\t\tassertThat(test1().call()).isEqualTo(\"test\");\n\t\t\tassertThat(test2(\"a\").call()).isEqualTo(\"a\");\n\t\t\tassertThat(test3(\"3\").apply(\"1\")).isEqualTo(2);\n\t\t\tassertThat(test4().apply(\"5\")).isEqualTo(5);\n\t\t\tassertThat(test4a().apply(\"7\")).isEqualTo(7);\n\t\t\tassertThat(test5().apply(\"1\")).isEqualTo(2);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"lambda$\")\n\t\t\t\t.doesNotContain(\"renamed\")\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"return () -> {\",\n\t\t\t\t\t\tindent() + \"return \\\"test\\\";\",\n\t\t\t\t\t\t\"};\")\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"return () -> {\",\n\t\t\t\t\t\tindent() + \"return str;\",\n\t\t\t\t\t\t\"};\")\n\t\t\t\t.containsOne(\"return Integer::parseInt;\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n\n\t@Test\n\tpublic void testFallback() {\n\t\tsetFallback();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/jbc/TestDup2x1.java",
    "content": "package jadx.tests.integration.jbc;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestDup2x1 extends IntegrationTest {\n\n\t@SuppressWarnings({ \"FieldCanBeLocal\", \"checkstyle:InnerAssignment\", \"unused\" })\n\tpublic static class TestCls {\n\t\tprivate long value;\n\n\t\tpublic long setValue(long v) {\n\t\t\treturn this.value = v;\n\t\t}\n\t}\n\n\t@TestWithProfiles(TestProfile.JAVA11)\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"this.value = v;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/jbc/TestStackConvert.java",
    "content": "package jadx.tests.integration.jbc;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.RaungTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestStackConvert extends RaungTest {\n\n\t@SuppressWarnings({ \"UnnecessaryLocalVariable\", \"CallToPrintStackTrace\", \"printstacktrace\" })\n\tpublic static class TestCls {\n\t\tpublic int parseIntDefault(String num, int defaultNum) {\n\t\t\ttry {\n\t\t\t\tint defaultNum2 = Integer.parseInt(num);\n\t\t\t\treturn defaultNum2;\n\t\t\t} catch (NumberFormatException e) {\n\t\t\t\tSystem.out.println(\"Before println\");\n\t\t\t\te.printStackTrace();\n\t\t\t\treturn defaultNum;\n\t\t\t}\n\t\t}\n\t}\n\n\t@TestWithProfiles(TestProfile.JAVA11)\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"Integer.parseInt(num)\");\n\t}\n\n\t@Test\n\tpublic void testRaung() {\n\t\tassertThat(getClassNodeFromRaung())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"Integer.parseInt(num)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestArrayForEach.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArrayForEach extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic int test(int[] a) {\n\t\t\tint sum = 0;\n\t\t\tfor (int n : a) {\n\t\t\t\tsum += n;\n\t\t\t}\n\t\t\treturn sum;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"int sum = 0;\",\n\t\t\t\t\t\t\"for (int n : a) {\",\n\t\t\t\t\t\tindent() + \"sum += n;\",\n\t\t\t\t\t\t\"}\",\n\t\t\t\t\t\t\"return sum;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestArrayForEach2.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArrayForEach2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test(String str) {\n\t\t\tfor (String s : str.split(\"\\n\")) {\n\t\t\t\tString t = s.trim();\n\t\t\t\tif (t.length() > 0) {\n\t\t\t\t\tSystem.out.println(t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"int \")\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"for (String s : str.split(\\\"\\\\n\\\")) {\",\n\t\t\t\t\t\tindent(1) + \"String t = s.trim();\",\n\t\t\t\t\t\tindent(1) + \"if (t.length() > 0) {\",\n\t\t\t\t\t\tindent(2) + \"System.out.println(t);\",\n\t\t\t\t\t\tindent(1) + '}',\n\t\t\t\t\t\t\"}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestArrayForEach3.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\n/**\n * Issue #977\n */\npublic class TestArrayForEach3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test(String[] arr) {\n\t\t\tfor (String s : arr) {\n\t\t\t\tif (s.length() > 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\"All strings are empty\");\n\t\t}\n\n\t\tpublic void check() {\n\t\t\ttest(new String[] { \"\", \"a\" }); // no exception\n\t\t\ttry {\n\t\t\t\ttest(new String[] { \"\", \"\" });\n\t\t\t\tfail(\"IllegalArgumentException expected\");\n\t\t\t} catch (IllegalArgumentException e) {\n\t\t\t\t// expected\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"while\")\n\t\t\t\t.containsOne(\"for (String str : strArr) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestArrayForEachNegative.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.CommentsLevel;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestArrayForEachNegative extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic int test(int[] a, int[] b) {\n\t\t\tint sum = 0;\n\t\t\tfor (int i = 0; i < a.length; i += 2) {\n\t\t\t\tsum += a[i];\n\t\t\t}\n\t\t\tfor (int i = 1; i < a.length; i++) {\n\t\t\t\tsum += a[i];\n\t\t\t}\n\t\t\tfor (int i = 0; i < a.length; i--) {\n\t\t\t\tsum += a[i];\n\t\t\t}\n\t\t\tfor (int i = 0; i <= a.length; i++) {\n\t\t\t\tsum += a[i];\n\t\t\t}\n\t\t\tfor (int i = 0; i + 1 < a.length; i++) {\n\t\t\t\tsum += a[i];\n\t\t\t}\n\t\t\tfor (int i = 0; i < a.length; i++) {\n\t\t\t\tsum += a[i - 1];\n\t\t\t}\n\t\t\tfor (int i = 0; i < b.length; i++) {\n\t\t\t\tsum += a[i];\n\t\t\t}\n\t\t\tint j = 0;\n\t\t\tfor (int i = 0; i < a.length; j++) {\n\t\t\t\tsum += a[j];\n\t\t\t}\n\t\t\treturn sum;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\t// Remove all comments - as the comment created by CodeGenUtils.addInputFileInfo always contains a\n\t\t// colon\n\t\tgetArgs().setCommentsLevel(CommentsLevel.NONE);\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\":\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestBreakInComplexIf.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestBreakInComplexIf extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tprivate int test(Map<String, Point> map, int mapX) {\n\t\t\tint length = 1;\n\t\t\tfor (int x = mapX + 1; x < 100; x++) {\n\t\t\t\tPoint tile = map.get(x + \"\");\n\t\t\t\tif (tile == null || tile.y != 100) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tlength++;\n\t\t\t}\n\t\t\treturn length;\n\t\t}\n\n\t\tclass Point {\n\t\t\tpublic final int x;\n\t\t\tpublic final int y;\n\n\t\t\tPoint(int x, int y) {\n\t\t\t\tthis.x = x;\n\t\t\t\tthis.y = y;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tMap<String, Point> first = new HashMap<>();\n\t\t\tfirst.put(\"3\", new Point(100, 100));\n\t\t\tfirst.put(\"4\", new Point(60, 100));\n\t\t\tassertThat(test(first, 2)).isEqualTo(3);\n\n\t\t\tMap<String, Point> second = new HashMap<>();\n\t\t\tsecond.put(\"3\", new Point(100, 100));\n\t\t\tsecond.put(\"4\", new Point(60, 0)); // check break\n\t\t\tsecond.put(\"5\", new Point(60, 100));\n\t\t\tassertThat(test(second, 2)).isEqualTo(2);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (tile == null || tile.y != 100) {\")\n\t\t\t\t.containsOne(\"break;\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"break;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestBreakInComplexIf2.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestBreakInComplexIf2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tprivate int test(List<String> list) {\n\t\t\tint length = 0;\n\t\t\tfor (String str : list) {\n\t\t\t\tif (str.isEmpty() || str.length() > 4) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (str.equals(\"skip\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (str.equals(\"a\")) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tlength++;\n\t\t\t}\n\t\t\treturn length;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(Arrays.asList(\"x\", \"y\", \"skip\", \"z\", \"a\"))).isEqualTo(3);\n\t\t\tassertThat(test(Arrays.asList(\"x\", \"skip\", \"\"))).isEqualTo(1);\n\t\t\tassertThat(test(Arrays.asList(\"skip\", \"y\", \"12345\"))).isEqualTo(1);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"break;\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"break;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestBreakInLoop.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestBreakInLoop extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int f;\n\n\t\tpublic void test(int[] a, int b) {\n\t\t\tfor (int i = 0; i < a.length; i++) {\n\t\t\t\ta[i]++;\n\t\t\t\tif (i < b) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.f++;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"for (int i = 0; i < a.length; i++) {\")\n\t\t\t\t.containsOne(\"if (i < b) {\")\n\t\t\t\t.containsOne(\"break;\")\n\t\t\t\t.containsOne(\"this.f++;\")\n\t\t\t\t// .containsOne(\"a[i]++;\")\n\t\t\t\t.countString(0, \"else\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestBreakInLoop2.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestBreakInLoop2 extends IntegrationTest {\n\n\t@SuppressWarnings({ \"BusyWait\", \"ResultOfMethodCallIgnored\" })\n\tpublic static class TestCls {\n\t\tpublic void test(List<Integer> data) throws Exception {\n\t\t\tfor (;;) {\n\t\t\t\ttry {\n\t\t\t\t\tfuncB(data);\n\t\t\t\t\tbreak;\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tif (funcC()) {\n\t\t\t\t\t\tthrow ex;\n\t\t\t\t\t}\n\t\t\t\t\tdata.clear();\n\t\t\t\t}\n\t\t\t\tThread.sleep(100L);\n\t\t\t}\n\t\t}\n\n\t\tprivate boolean funcB(List<Integer> data) {\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate boolean funcC() {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"while (true) {\")\n\t\t\t\t.containsOneOf(\"break;\", \"return;\")\n\t\t\t\t.containsOne(\"throw ex;\")\n\t\t\t\t.containsOne(\"data.clear();\")\n\t\t\t\t.containsOne(\"Thread.sleep(100L);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestBreakInLoop3.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestBreakInLoop3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tprivate StringBuilder builder = new StringBuilder();\n\n\t\tpublic void writeMore(String fid) {\n\t\t\tboolean tryMkdir = true;\n\t\t\tFile ff = new File(fid);\n\t\t\twhile (true) {\n\t\t\t\tprt(\"1\");\n\t\t\t\ttry {\n\t\t\t\t\tnew FileOutputStream(fid).close();\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tif (tryMkdir) { // On first error, try creating the base dirs.\n\t\t\t\t\t\ttryMkdir = false;\n\t\t\t\t\t\tprt(\"2\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tprt(\"3\");\n\t\t\t\t\tif (ff.exists()) {\n\t\t\t\t\t\tprt(\"4\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tprt(\"5\");\n\t\t\t\t\t}\n\t\t\t\t\tprt(\"6\");\n\t\t\t\t}\n\t\t\t\tprt(\"7\");\n\t\t\t\tbreak;\n\t\t\t} // end of while true, loop to allow retry of fos.write after mkdir\n\t\t\tprt(\"8\");\n\t\t} // end of writeMore\n\n\t\tprivate void prt(String s) {\n\t\t\tbuilder.append(s);\n\t\t}\n\n\t\tpublic void check() {\n\t\t\twriteMore(\"\");\n\t\t\tassertThat(builder).hasToString(\"12135678\");\n\t\t}\n\t}\n\n\t// @Test\n\t@NotYetImplemented\n\tpublic void test43() throws Exception {\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestBreakInLoop4.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestBreakInLoop4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic double test(char c) {\n\t\t\tdouble m = 1.0;\n\t\t\tfor (int i = 0; i < 5; i++) {\n\t\t\t\tif (c != '.') {\n\t\t\t\t\tif (c == 'a' || c == 'b') {\n\t\t\t\t\t\tm = 1024.0;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn m;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test('.')).isEqualTo(1.0);\n\t\t\tassertThat(test('a')).isEqualTo(1024.0);\n\t\t\tassertThat(test('b')).isEqualTo(1024.0);\n\t\t\tassertThat(test('c')).isEqualTo(1.0);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"while\")\n\t\t\t\t.containsOne(\"for\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"while\")\n\t\t\t\t.containsOne(\"for\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestBreakInLoop5.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestBreakInLoop5 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic long test(String spaceStr) {\n\t\t\ttry {\n\t\t\t\tdouble multiplier = 1.0; // Often the compiler will move this line to each of the loop exits creating complexity\n\t\t\t\tchar c;\n\t\t\t\tStringBuffer sb = new StringBuffer();\n\t\t\t\tfor (int i = 0; i < spaceStr.length(); i++) {\n\t\t\t\t\tc = spaceStr.charAt(i);\n\t\t\t\t\tif (!Character.isDigit(c) && c != '.') {\n\t\t\t\t\t\tif (c == 'm' || c == 'M') {\n\t\t\t\t\t\t\tmultiplier = 1024.0;\n\t\t\t\t\t\t} else if (c == 'g' || c == 'G') {\n\t\t\t\t\t\t\tmultiplier = 1024.0 * 1024.0;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tsb.append(spaceStr.charAt(i));\n\t\t\t\t}\n\t\t\t\treturn (long) Math.ceil(Double.valueOf(sb.toString()) * multiplier);\n\t\t\t} catch (Exception e) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(\"1.2\")).isEqualTo(2);\n\t\t\tassertThat(test(\"12am\")).isEqualTo(12);\n\t\t\tassertThat(test(\"13m\")).isEqualTo(13 * 1024);\n\t\t\tassertThat(test(\"1G4\")).isEqualTo(1 * 1024 * 1024);\n\t\t\tassertThat(test(\"\")).isEqualTo(-1);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code();\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestBreakInLoop6.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Test that a continue is not erroneously inserted where a break edge instruction already exists,\n * leading to the continue being lost and causing a decompile failure.\n */\npublic class TestBreakInLoop6 extends SmaliTest {\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali()).code().containsOne(\"break\");\n\t}\n\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestBreakWithLabel.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestBreakWithLabel extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic boolean test(int[][] arr, int b) {\n\t\t\tboolean found = false;\n\t\t\tloop0: for (int i = 0; i < arr.length; i++) {\n\t\t\t\tfor (int j = 0; j < arr[i].length; j++) {\n\t\t\t\t\tif (arr[i][j] == b) {\n\t\t\t\t\t\tfound = true;\n\t\t\t\t\t\tbreak loop0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tSystem.out.println(\"found: \" + found);\n\t\t\treturn found;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tint[][] testArray = { { 1, 2 }, { 3, 4 } };\n\t\t\tassertThat(test(testArray, 3)).isTrue();\n\t\t\tassertThat(test(testArray, 5)).isFalse();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"loop0:\")\n\t\t\t\t.containsOne(\"break loop0;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestComplexWhileLoop.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestComplexWhileLoop extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static String test(String[] arr) {\n\t\t\tint index = 0;\n\t\t\tint length = arr.length;\n\t\t\tString str;\n\t\t\twhile ((str = arr[index]) != null) {\n\t\t\t\tif (str.length() == 1) {\n\t\t\t\t\treturn str.trim();\n\t\t\t\t}\n\t\t\t\tif (++index >= length) {\n\t\t\t\t\tindex = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tSystem.out.println(\"loop end\");\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"for (int at = 0; at < len; at = endAt) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestContinueInLoop.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestContinueInLoop extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int f;\n\n\t\tpublic void test(int[] a, int b) {\n\t\t\tfor (int i = 0; i < a.length; i++) {\n\t\t\t\tint v = a[i];\n\t\t\t\tif (v < b) {\n\t\t\t\t\ta[i]++;\n\t\t\t\t} else if (v > b) {\n\t\t\t\t\ta[i]--;\n\t\t\t\t} else {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (i < b) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.f++;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"for (int i = 0; i < a.length; i++) {\")\n\t\t\t\t.containsOne(\"if (i < b) {\")\n\t\t\t\t.containsOne(\"continue;\")\n\t\t\t\t.containsOne(\"break;\")\n\t\t\t\t.containsOne(\"this.f++;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestDoWhileBreak.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestDoWhileBreak extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic int test(int k) throws InterruptedException {\n\t\t\tint i = 3;\n\t\t\tdo {\n\t\t\t\tif (k > 9) {\n\t\t\t\t\ti = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t} while (i < 5);\n\n\t\t\treturn i;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"while (\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestDoWhileBreak2.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.util.Arrays;\nimport java.util.Iterator;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestDoWhileBreak2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tIterator<String> it;\n\n\t\t@SuppressWarnings(\"ConstantConditions\")\n\t\tpublic Object test() {\n\t\t\tString obj;\n\t\t\tdo {\n\t\t\t\tobj = this.it.next();\n\t\t\t\tif (obj == null) {\n\t\t\t\t\treturn obj; // 'return null' or 'break' also fine\n\t\t\t\t}\n\t\t\t} while (this.it.hasNext());\n\t\t\treturn obj;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tthis.it = Arrays.asList(\"a\", \"b\").iterator();\n\t\t\tassertThat(test()).isEqualTo(\"b\");\n\n\t\t\tthis.it = Arrays.asList(\"a\", \"b\", null).iterator();\n\t\t\tassertThat(test()).isEqualTo(null);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLine(2, \"do {\")\n\t\t\t\t.containsLine(3, \"obj = this.it.next();\")\n\t\t\t\t.containsLine(3, \"if (obj == null) {\")\n\t\t\t\t.containsLine(2, \"} while (this.it.hasNext());\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestDoWhileBreak3.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.util.Iterator;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestDoWhileBreak3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tIterator<String> it;\n\n\t\tpublic void test() {\n\t\t\tdo {\n\t\t\t\tif (!it.hasNext()) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} while (it.next() != null);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"while\")\n\t\t\t\t.containsLines(2, \"while (this.it.hasNext() && this.it.next() != null) {\", \"}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestEndlessLoop.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestEndlessLoop extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tvoid test1() {\n\t\t\twhile (this == this) {\n\t\t\t}\n\t\t}\n\n\t\tvoid test2() {\n\t\t\tdo {\n\t\t\t} while (this == this);\n\t\t}\n\n\t\tvoid test3() {\n\t\t\twhile (true) {\n\t\t\t\tif (this != this) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"while (this == this)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestEndlessLoop2.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Empty endless loop, issue #1611\n */\npublic class TestEndlessLoop2 extends SmaliTest {\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"while (true) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestIfInLoop2.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestIfInLoop2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static void test(String str) {\n\t\t\tint len = str.length();\n\t\t\tint at = 0;\n\t\t\twhile (at < len) {\n\t\t\t\tchar c = str.charAt(at);\n\t\t\t\tint endAt = at + 1;\n\t\t\t\tif (c == 'A') {\n\t\t\t\t\twhile (endAt < len) {\n\t\t\t\t\t\tc = str.charAt(endAt);\n\t\t\t\t\t\tif (c == 'B') {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tendAt++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tat = endAt;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"for (int at = 0; at < len; at = endAt) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestIfInLoop3.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestIfInLoop3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tstatic boolean[][] occupied = new boolean[70][70];\n\t\tstatic boolean placingStone = true;\n\n\t\tprivate static boolean test(int xx, int yy) {\n\t\t\tint[] extraArray = new int[] { 10, 45, 50, 50, 20, 20 };\n\t\t\tif (extraArray != null && placingStone) {\n\t\t\t\tfor (int i = 0; i < extraArray.length; i += 2) {\n\t\t\t\t\tint tX;\n\t\t\t\t\tint tY;\n\t\t\t\t\tif (yy % 2 == 0) {\n\t\t\t\t\t\tif (extraArray[i + 1] % 2 == 0) {\n\t\t\t\t\t\t\ttX = xx + extraArray[i];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttX = extraArray[i] + xx - 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttY = yy + extraArray[i + 1];\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttX = xx + extraArray[i];\n\t\t\t\t\t\ttY = yy + extraArray[i + 1];\n\t\t\t\t\t}\n\t\t\t\t\tif (tX < 0 || tY < 0 || tY % 2 != 0 && tX > 28 || tY > 70\n\t\t\t\t\t\t\t|| occupied[tX][tY]) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(14, 2)).isTrue();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"for (int i = 0; i < extraArray.length; i += 2) {\")\n\t\t\t\t.containsOne(\"if (extraArray != null && placingStone) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestIfInLoop4.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestIfInLoop4 extends SmaliTest {\n\n\t/*\n\t * Test handling of edge instructions when generated from a loop near an if statement with no else.\n\t * They should not be added to an else region, since the if statement has no else.\n\t * The actual condition here is less important than if decompilation succeeds at all.\n\t */\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmaliWithPath(\"loops\", \"TestIfInLoop4\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return true;\");\n\t}\n\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestIndexForLoop.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestIndexForLoop extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tprivate int test(int[] a, int b) {\n\t\t\tint sum = 0;\n\t\t\tfor (int i = 0; i < b; i++) {\n\t\t\t\tsum += a[i];\n\t\t\t}\n\t\t\treturn sum;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tint[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n\t\t\tassertThat(test(array, 0)).isEqualTo(0);\n\t\t\tassertThat(test(array, 3)).isEqualTo(6);\n\t\t\tassertThat(test(array, 8)).isEqualTo(36);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"int sum = 0;\",\n\t\t\t\t\t\t\"for (int i = 0; i < b; i++) {\",\n\t\t\t\t\t\tindent(1) + \"sum += a[i];\",\n\t\t\t\t\t\t\"}\",\n\t\t\t\t\t\t\"return sum;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestIndexedLoop.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.io.File;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestIndexedLoop extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic File test(File[] files) {\n\t\t\tFile file = null;\n\t\t\tif (files != null) {\n\t\t\t\tint length = files.length;\n\t\t\t\tif (length == 0) {\n\t\t\t\t\tfile = null;\n\t\t\t\t} else {\n\t\t\t\t\tfor (int i = 0; i < length; i++) {\n\t\t\t\t\t\tfile = files[i];\n\t\t\t\t\t\tif (file.getName().equals(\"f\")) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfile = null;\n\t\t\t}\n\t\t\tif (file != null) {\n\t\t\t\tfile.deleteOnExit();\n\t\t\t}\n\t\t\treturn file;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(null)).isNull();\n\t\t\tassertThat(test(new File[] {})).isNull();\n\n\t\t\tFile file = new File(\"f\");\n\t\t\tassertThat(test(new File[] { new File(\"a\"), file })).isEqualTo(file);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"for (File file :\")\n\t\t\t\t.containsOne(\"for (int i = 0; i < length; i++) {\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"for (File file :\")\n\t\t\t\t.containsOne(\"for (int i = 0; i < length; i++) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestIterableForEach.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestIterableForEach extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic String test(Iterable<String> a) {\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tfor (String s : a) {\n\t\t\t\tsb.append(s);\n\t\t\t}\n\t\t\treturn sb.toString();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"StringBuilder sb = new StringBuilder();\",\n\t\t\t\t\t\t\"for (String s : a) {\",\n\t\t\t\t\t\tindent(1) + \"sb.append(s);\",\n\t\t\t\t\t\t\"}\",\n\t\t\t\t\t\t\"return sb.toString();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestIterableForEach2.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestIterableForEach2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static String test(final Service service) throws IOException {\n\t\t\tfor (Authorization auth : service.getAuthorizations()) {\n\t\t\t\tif (isValid(auth)) {\n\t\t\t\t\treturn auth.getToken();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate static boolean isValid(Authorization auth) {\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate static class Service {\n\t\t\tpublic List<Authorization> getAuthorizations() {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tprivate static class Authorization {\n\t\t\tpublic String getToken() {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"for (Authorization auth : service.getAuthorizations()) {\")\n\t\t\t\t.containsOne(\"if (isValid(auth)) {\")\n\t\t\t\t.containsOne(\"return auth.getToken();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestIterableForEach3.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestIterableForEach3 extends IntegrationTest {\n\n\tpublic static class TestCls<T extends String> {\n\t\tprivate Set<T> a;\n\t\tprivate Set<T> b;\n\n\t\tpublic void test(T str) {\n\t\t\tSet<T> set = str.length() == 1 ? a : b;\n\t\t\tfor (T s : set) {\n\t\t\t\tif (s.length() == str.length()) {\n\t\t\t\t\tif (str.length() == 0) {\n\t\t\t\t\t\tset.remove(s);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tset.add(str);\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"for (T s : set) {\")\n\t\t\t\t.containsOne(\"if (str.length() == 0) {\");\n\t\t// TODO move return outside 'if'\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestIterableForEach4.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestIterableForEach4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test(List<Object> objects) {\n\t\t\tfor (Object o : objects) {\n\t\t\t\tif (o.hashCode() != 42 || o.hashCode() != 1) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"while (\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopCondition.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestLoopCondition extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test(List<String> list) {\n\t\t\tfor (int i = 0; i != 16 && i < 255; i++) {\n\t\t\t\tlist.set(i, \"ABC\");\n\t\t\t\tif (i == 128) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlist.set(i, \"DEF\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"list.set(i, \\\"ABC\\\")\")\n\t\t\t\t.containsOne(\"list.set(i, \\\"DEF\\\")\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopCondition2.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestLoopCondition2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic int test(boolean a) {\n\t\t\tint i = 0;\n\t\t\twhile (a && i < 10) {\n\t\t\t\ti++;\n\t\t\t}\n\t\t\treturn i;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"int i = 0;\")\n\t\t\t\t.containsOne(\"while (a && i < 10) {\")\n\t\t\t\t.containsOne(\"i++;\")\n\t\t\t\t.containsOne(\"return i;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopCondition3.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestLoopCondition3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static void test(int a, int b, int c) {\n\t\t\twhile (a < 12) {\n\t\t\t\tif (b + a < 9 && b < 8) {\n\t\t\t\t\tif (b >= 2 && a > -1 && b < 6) {\n\t\t\t\t\t\tSystem.out.println(\"OK\");\n\t\t\t\t\t\tc = b + 1;\n\t\t\t\t\t}\n\t\t\t\t\tb = a;\n\t\t\t\t}\n\t\t\t\tc = b;\n\t\t\t\tb++;\n\t\t\t\tb = c;\n\t\t\t\ta++;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"while (a < 12) {\")\n\t\t\t\t.containsOne(\"if (b + a < 9 && b < 8) {\")\n\t\t\t\t.containsOne(\"if (b >= 2 && a > -1 && b < 6) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopCondition4.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestLoopCondition4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static void test() {\n\t\t\tint n = -1;\n\t\t\twhile (n < 0) {\n\t\t\t\tn += 12;\n\t\t\t}\n\t\t\twhile (n > 11) {\n\t\t\t\tn -= 12;\n\t\t\t}\n\t\t\tSystem.out.println(n);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"int n = -1;\")\n\t\t\t\t.containsOne(\"while (n < 0) {\")\n\t\t\t\t.containsOne(\"n += 12;\")\n\t\t\t\t.containsOne(\"while (n > 11) {\")\n\t\t\t\t.containsOne(\"n -= 12;\")\n\t\t\t\t.containsOne(\"System.out.println(n);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopCondition5.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLoopCondition5 extends SmaliTest {\n\n\tpublic static class TestCls {\n\t\tpublic static int lastIndexOf(int[] array, int target, int start, int end) {\n\t\t\tfor (int i = end - 1; i >= start; i--) {\n\t\t\t\tif (array[i] == target) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test0() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"for (\")\n\t\t\t\t.containsOne(\"return -1;\")\n\t\t\t\t.countString(2, \"return \");\n\t}\n\n\t@Test\n\tpublic void test1() {\n\t\tassertThat(getClassNodeFromSmaliWithPath(\"loops\", \"TestLoopCondition5\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOneOf(\"for (\", \"while (true) {\", \"} while (iArr[i3] != i);\")\n\t\t\t\t.containsOne(\"return -1;\")\n\t\t\t\t.countString(2, \"return \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopConditionInvoke.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestLoopConditionInvoke extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static final char STOP_CHAR = 0;\n\t\tprivate int pos;\n\n\t\tpublic boolean test(char lastChar) {\n\t\t\tint startPos = pos;\n\t\t\tchar ch;\n\t\t\twhile ((ch = next()) != STOP_CHAR) {\n\t\t\t\tif (ch == lastChar) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tpos = startPos;\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate char next() {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"do {\")\n\t\t\t\t.containsOne(\"if (ch == 0) {\")\n\t\t\t\t.containsOne(\"this.pos = startPos;\")\n\t\t\t\t.containsOne(\"return false;\")\n\t\t\t\t.containsOne(\"} while (ch != lastChar);\")\n\t\t\t\t.containsOne(\"return true;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopDetection.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLoopDetection extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test(int[] a, int b) {\n\t\t\tint i = 0;\n\t\t\twhile (i < a.length && i < b) {\n\t\t\t\ta[i]++;\n\t\t\t\ti++;\n\t\t\t}\n\t\t\twhile (i < a.length) {\n\t\t\t\ta[i]--;\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tint[] a = { 1, 1, 1, 1, 1 };\n\t\t\ttest(a, 3);\n\t\t\tassertThat(a).containsExactly(new int[] { 2, 2, 2, 0, 0 });\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"while (i < a.length && i < b) {\")\n\t\t\t\t.contains(\"while (i < a.length) {\")\n\t\t\t\t.contains(\"int i = 0;\")\n\t\t\t\t.doesNotContain(\"i_2\")\n\t\t\t\t.contains(\"i++;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopDetection2.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestLoopDetection2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic int test(int a, int b) {\n\t\t\tint c = a + b;\n\t\t\tfor (int i = a; i < b; i++) {\n\t\t\t\tif (i == 7) {\n\t\t\t\t\tc += 2;\n\t\t\t\t} else {\n\t\t\t\t\tc *= 2;\n\t\t\t\t}\n\t\t\t}\n\t\t\tc--;\n\t\t\treturn c;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"int c = a + b;\")\n\t\t\t\t.containsOne(\"for (int i = a; i < b; i++) {\")\n\t\t\t\t.doesNotContain(\"c_2\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopDetection3.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestLoopDetection3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test(TestCls parent, int pos) {\n\t\t\tObject item;\n\t\t\twhile (--pos >= 0) {\n\t\t\t\titem = parent.get(pos);\n\t\t\t\tif (item instanceof String) {\n\t\t\t\t\tfunc((String) item);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate Object get(int pos) {\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate void func(String item) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"while\");\n\t}\n\n\t@Test\n\t@NotYetImplemented\n\tpublic void test2() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"while (--pos >= 0) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopDetection4.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.util.Iterator;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestLoopDetection4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate Iterator<String> iterator;\n\t\tprivate SomeCls filter;\n\n\t\tpublic String test() {\n\t\t\twhile (iterator.hasNext()) {\n\t\t\t\tString next = iterator.next();\n\t\t\t\tString filtered = filter.filter(next);\n\t\t\t\tif (filtered != null) {\n\t\t\t\t\treturn filtered;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate class SomeCls {\n\t\t\tpublic String filter(String str) {\n\t\t\t\treturn str;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"while (this.iterator.hasNext()) {\")\n\t\t\t\t.containsOne(\"if (filtered != null) {\")\n\t\t\t\t.containsOne(\"return filtered;\")\n\t\t\t\t.containsOne(\"return null;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopDetection5.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLoopDetection5 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic String test(String str) {\n\t\t\tIterator<String> it = getStrings().iterator();\n\t\t\tString otherStr = null;\n\t\t\twhile (it.hasNext()) {\n\t\t\t\totherStr = it.next();\n\t\t\t\tif (otherStr.equalsIgnoreCase(str)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn otherStr;\n\t\t}\n\n\t\tprivate List<String> getStrings() {\n\t\t\treturn Arrays.asList(\"str\", \"otherStr\", \"STR\", \"OTHERSTR\");\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(\"OTHERSTR\")).isEqualTo(\"otherStr\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"for (\")\n\t\t\t\t.containsOne(\"it.next();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopRestore.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLoopRestore extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"try {\")\n\t\t\t\t.containsOne(\"for (byte b : bArrDigest) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopRestore2.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.RaungTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLoopRestore2 extends RaungTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation(); // unreachable statement\n\t\tassertThat(getClassNodeFromRaung())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"while (1 == 0) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopRestore3.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLoopRestore3 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.countString(3, \"while (\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestMultiEntryLoop.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestMultiEntryLoop extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"while (true) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestMultiEntryLoop2.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestMultiEntryLoop2 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"while (true) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestNestedLoops.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestNestedLoops extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test(List<String> l1, List<String> l2) {\n\t\t\tfor (String s1 : l1) {\n\t\t\t\tfor (String s2 : l2) {\n\t\t\t\t\tif (s1.equals(s2)) {\n\t\t\t\t\t\tif (s1.length() == 5) {\n\t\t\t\t\t\t\tl2.add(s1);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tl1.remove(s2);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (l2.size() > 0) {\n\t\t\t\tl1.clear();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"for (String s1 : l1) {\")\n\t\t\t\t.containsOne(\"for (String s2 : l2) {\")\n\t\t\t\t.containsOne(\"if (s1.equals(s2)) {\")\n\t\t\t\t.containsOne(\"l2.add(s1);\")\n\t\t\t\t.containsOne(\"l1.remove(s2);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestNestedLoops2.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestNestedLoops2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic boolean test(List<String> list) {\n\t\t\tint j = 0;\n\t\t\tfor (int i = 0; i < list.size(); i++) {\n\t\t\t\tString s = list.get(i);\n\t\t\t\twhile (j < s.length()) {\n\t\t\t\t\tj++;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn j > 10;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"for (int i = 0; i < list.size(); i++) {\")\n\t\t\t\t.containsOne(\"while (j < s.length()) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestNestedLoops3.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestNestedLoops3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tint c = 0;\n\n\t\tpublic int test(int b) {\n\t\t\tint i;\n\t\t\tloop0: while (true) {\n\t\t\t\tf1();\n\t\t\t\ti = 0;\n\t\t\t\twhile (true) {\n\t\t\t\t\tf2();\n\t\t\t\t\tif (i != 0) {\n\t\t\t\t\t\tbreak loop0;\n\t\t\t\t\t}\n\t\t\t\t\ti += 3;\n\t\t\t\t\tif (b >= 16) {\n\t\t\t\t\t\tbreak loop0;\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\texc();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t// ignore\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn i;\n\t\t}\n\n\t\tprivate void exc() throws Exception {\n\t\t\tif (c > 200) {\n\t\t\t\tthrow new Exception();\n\t\t\t}\n\t\t}\n\n\t\tprivate void f1() {\n\t\t\tc += 1;\n\t\t}\n\n\t\tprivate void f2() {\n\t\t\tc += 100;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\ttest(1);\n\t\t\tassertThat(c).isEqualTo(302);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"} catch (Exception e) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestNestedLoops4.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestNestedLoops4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic int testFor() {\n\t\t\tint tmp = 1;\n\t\t\tfor (int i = 10; i > -1; i--) {\n\t\t\t\tif (i > tmp) {\n\t\t\t\t\tfor (int j = 0; j < 54; j += 4) {\n\t\t\t\t\t\tif (i < j) {\n\t\t\t\t\t\t\tfor (int k = j; k < j + 4; k++) {\n\t\t\t\t\t\t\t\tif (tmp > k) {\n\t\t\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttmp++;\n\t\t\t}\n\t\t\treturn tmp;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(testFor()).isEqualTo(12);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"break;\")\n\t\t\t\t.containsOne(\"return 0;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestNestedLoops5.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestNestedLoops5 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic int testFor() {\n\t\t\tint tmp = 1;\n\t\t\tfor (int i = 5; i > -1; i--) {\n\t\t\t\tif (i > tmp) {\n\t\t\t\t\tfor (int j = 1; j < 5; j++) {\n\t\t\t\t\t\tif (tmp > j * 100) {\n\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttmp++;\n\t\t\t}\n\t\t\treturn tmp;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(testFor()).isEqualTo(7);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"continue;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestNotIndexedLoop.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.io.File;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestNotIndexedLoop extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic File test(File[] files) {\n\t\t\tFile file;\n\t\t\tif (files != null) {\n\t\t\t\tint length = files.length;\n\t\t\t\tif (length == 0) {\n\t\t\t\t\tfile = null;\n\t\t\t\t} else {\n\t\t\t\t\tint i = 0;\n\t\t\t\t\twhile (true) {\n\t\t\t\t\t\tif (i >= length) {\n\t\t\t\t\t\t\tfile = new File(\"h\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfile = files[i];\n\t\t\t\t\t\tif (file.getName().equals(\"f\")) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfile = null;\n\t\t\t}\n\t\t\tif (file != null) {\n\t\t\t\tfile.deleteOnExit();\n\t\t\t}\n\t\t\treturn file;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(null)).isNull();\n\t\t\tassertThat(test(new File[] {})).isNull();\n\n\t\t\tFile file = new File(\"f\");\n\t\t\tassertThat(test(new File[] { new File(\"a\"), file })).isEqualTo(file);\n\n\t\t\tassertThat(test(new File[] { new File(\"a\") }).getName()).isEqualTo(\"h\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"for (\")\n\t\t\t\t.containsOne(\"while (true) {\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"for (\")\n\t\t\t\t.containsOne(\"while (true) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestSequentialLoops.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSequentialLoops extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int test(int a, int b) {\n\t\t\tint c = b;\n\t\t\tint z;\n\n\t\t\twhile (true) {\n\t\t\t\tz = c + a;\n\t\t\t\tif (z >= 7) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tc = z;\n\t\t\t}\n\n\t\t\twhile ((z = c + a) >= 7) {\n\t\t\t\tc = z;\n\t\t\t}\n\t\t\treturn c;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"while (\")\n\t\t\t\t.containsOne(\"break;\")\n\t\t\t\t.containsOne(\"return c;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestSequentialLoops2.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSequentialLoops2 extends IntegrationTest {\n\n\t@SuppressWarnings({ \"unused\", \"FieldMayBeFinal\" })\n\tpublic static class TestCls {\n\t\tprivate static char[] lowercases = new char[] { 'a' };\n\n\t\tpublic static String asciiToLowerCase(String s) {\n\t\t\tchar[] c = null;\n\t\t\tint i = s.length();\n\t\t\twhile (i-- > 0) {\n\t\t\t\tchar c1 = s.charAt(i);\n\t\t\t\tif (c1 <= 127) {\n\t\t\t\t\tchar c2 = lowercases[c1];\n\t\t\t\t\tif (c1 != c2) {\n\t\t\t\t\t\tc = s.toCharArray();\n\t\t\t\t\t\tc[i] = c2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\twhile (i-- > 0) {\n\t\t\t\tif (c[i] <= 127) {\n\t\t\t\t\tc[i] = lowercases[c[i]];\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn c == null ? s : new String(c);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"while (\")\n\t\t\t\t.contains(\"break;\")\n\t\t\t\t.containsOne(\"return c\")\n\t\t\t\t.countString(2, \"<= 127\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"while (\")\n\t\t\t\t.contains(\"break;\")\n\t\t\t\t.countString(2, \"<= 127\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestSynchronizedInEndlessLoop.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestSynchronizedInEndlessLoop extends IntegrationTest {\n\n\t@SuppressWarnings(\"BusyWait\")\n\tpublic static class TestCls {\n\t\tint f = 5;\n\n\t\tint test() {\n\t\t\twhile (true) {\n\t\t\t\tsynchronized (this) {\n\t\t\t\t\tif (f > 7) {\n\t\t\t\t\t\treturn 7;\n\t\t\t\t\t}\n\t\t\t\t\tif (f < 3) {\n\t\t\t\t\t\treturn 3;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tf++;\n\t\t\t\t\tThread.sleep(100L);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"synchronized (this) {\")\n\t\t\t\t.containsOne(\"try {\")\n\t\t\t\t.containsOne(\"f++;\")\n\t\t\t\t.containsOne(\"Thread.sleep(100L);\")\n\t\t\t\t.containsOne(\"} catch (Exception e) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestTryCatchInLoop.java",
    "content": "package jadx.tests.integration.loops;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestTryCatchInLoop extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tint c = 0;\n\n\t\tpublic int test() {\n\t\t\twhile (true) {\n\t\t\t\ttry {\n\t\t\t\t\texc();\n\t\t\t\t\tbreak;\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t//\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (c == 5) {\n\t\t\t\tSystem.out.println(c);\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tprivate void exc() throws Exception {\n\t\t\tc++;\n\t\t\tif (c < 3) {\n\t\t\t\tthrow new Exception();\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\ttest();\n\t\t\tassertThat(c).isEqualTo(3);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"} catch (Exception e) {\")\n\t\t\t\t.containsOne(\"break;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/loops/TestTryCatchInLoop2.java",
    "content": "package jadx.tests.integration.loops;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTryCatchInLoop2 extends IntegrationTest {\n\n\tpublic static class TestCls<T extends String> {\n\t\tprivate static class MyItem {\n\t\t\tint idx;\n\t\t\tString name;\n\t\t}\n\n\t\tprivate final Map<Integer, MyItem> mCache = new HashMap<>();\n\n\t\tvoid test(MyItem[] items) {\n\t\t\tsynchronized (this.mCache) {\n\t\t\t\tfor (int i = 0; i < items.length; ++i) {\n\t\t\t\t\tMyItem existingItem = mCache.get(items[i].idx);\n\t\t\t\t\tif (null == existingItem) {\n\t\t\t\t\t\tmCache.put(items[i].idx, items[i]);\n\t\t\t\t\t} else {\n\t\t\t\t\t\texistingItem.name = items[i].name;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"synchronized (this.mCache) {\")\n\t\t\t\t.containsOne(\"for (int i = 0; i < items.length; i++) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/TestCaseSensitiveChecks.java",
    "content": "package jadx.tests.integration.names;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.Consts;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestCaseSensitiveChecks extends SmaliTest {\n\t/*\n\t * public class A {}\n\t * public class a {}\n\t */\n\n\t@Test\n\tpublic void test() {\n\t\targs.setFsCaseSensitive(false);\n\n\t\tList<ClassNode> classes = loadFromSmaliFiles();\n\t\tfor (ClassNode cls : classes) {\n\t\t\tassertThat(cls.getPackage()).isEqualTo(Consts.DEFAULT_PACKAGE_NAME);\n\t\t}\n\t\tlong namesCount = classes.stream().map(cls -> cls.getAlias().toLowerCase()).distinct().count();\n\t\tassertThat(namesCount).isEqualTo(2L);\n\t}\n\n\t@Test\n\tpublic void testCaseSensitiveFS() {\n\t\targs.setFsCaseSensitive(true);\n\n\t\tList<ClassNode> classes = loadFromSmaliFiles();\n\t\tfor (ClassNode cls : classes) {\n\t\t\tassertThat(cls.getPackage()).isEqualTo(Consts.DEFAULT_PACKAGE_NAME);\n\t\t}\n\t\tList<String> names = classes.stream().map(ClassNode::getAlias).collect(Collectors.toList());\n\t\tassertThat(names).containsExactlyInAnyOrder(\"A\", \"a\");\n\t}\n\n\t@Test\n\tpublic void testWithDeobfuscation() {\n\t\tenableDeobfuscation();\n\n\t\tList<ClassNode> classes = loadFromSmaliFiles();\n\t\tfor (ClassNode cls : classes) {\n\t\t\tassertThat(cls.getPackage()).isNotEmpty();\n\t\t\tassertThat(cls.getPackage()).isNotEqualTo(Consts.DEFAULT_PACKAGE_NAME);\n\t\t}\n\t\tlong namesCount = classes.stream().map(cls -> cls.getAlias().toLowerCase()).distinct().count();\n\t\tassertThat(namesCount).isEqualTo(2L);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/TestClassNameWithInvalidChar.java",
    "content": "package jadx.tests.integration.names;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\npublic class TestClassNameWithInvalidChar extends SmaliTest {\n\t/*\n\t * public class do- {}\n\t * public class i-f {}\n\t */\n\n\t@Test\n\tpublic void test() {\n\t\tloadFromSmaliFiles();\n\t}\n\n\t@Test\n\tpublic void testWithDeobfuscation() {\n\t\tenableDeobfuscation();\n\t\tloadFromSmaliFiles();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/TestClassNamesCollision.java",
    "content": "package jadx.tests.integration.names;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.CommentsLevel;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.integration.names.pkg.a;\nimport jadx.tests.integration.names.pkg.b;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestClassNamesCollision extends IntegrationTest {\n\n\t@Test\n\tpublic void test() {\n\t\tgetArgs().setCommentsLevel(CommentsLevel.WARN);\n\t\tList<ClassNode> classNodes = getClassNodes(a.class, b.class);\n\n\t\tassertThat(searchCls(classNodes, \"a\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public class a {\")\n\t\t\t\t.containsOne(\"public static a a() {\");\n\n\t\tassertThat(searchCls(classNodes, \"b\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"class a {\")\n\t\t\t\t.containsOne(\"jadx.tests.integration.names.pkg.a a = jadx.tests.integration.names.pkg.a.a();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/TestClassNamesCollision2.java",
    "content": "package jadx.tests.integration.names;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.CommentsLevel;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestClassNamesCollision2 extends IntegrationTest {\n\n\t@SuppressWarnings(\"rawtypes\")\n\tpublic static class TestCls {\n\t\tstatic class List {\n\t\t\tpublic static List getList() {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tprotected List list = List.getList();\n\n\t\tprotected void clearList(java.util.List l) {\n\t\t\tl.clear();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tgetArgs().setCommentsLevel(CommentsLevel.WARN);\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"static class List {\")\n\t\t\t\t.containsOne(\"protected void clearList(java.util.List l) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/TestCollisionWithJavaLangClasses.java",
    "content": "package jadx.tests.integration.names;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestCollisionWithJavaLangClasses extends IntegrationTest {\n\n\tpublic static class TestCls1 {\n\t\tpublic static class System {\n\t\t\tpublic static void main(String[] args) {\n\t\t\t\tjava.lang.System.out.println(\"Hello world\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test1() {\n\t\tassertThat(getClassNode(TestCls1.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"java.lang.System.out.println\");\n\t}\n\n\tpublic static class TestCls2 {\n\t\tpublic void doSomething() {\n\t\t\tSystem.doSomething();\n\t\t\tjava.lang.System.out.println(\"Hello World\");\n\t\t}\n\n\t\tpublic static class System {\n\t\t\tpublic static void doSomething() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tassertThat(getClassNode(TestCls2.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLine(2, \"System.doSomething();\")\n\t\t\t\t.containsOne(\"java.lang.System.out.println\");\n\t}\n\n\t@Test\n\tpublic void test3() {\n\t\tList<ClassNode> classes = getClassNodes(\n\t\t\t\tjadx.tests.integration.names.pkg2.System.class,\n\t\t\t\tjadx.tests.integration.names.pkg2.TestCls.class);\n\t\tassertThat(searchCls(classes, \"TestCls\"))\n\t\t\t\t.code()\n\t\t\t\t.containsLine(2, \"System.doSomething();\")\n\t\t\t\t.containsOne(\"java.lang.System.out.println\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/TestConstructorArgNames.java",
    "content": "package jadx.tests.integration.names;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestConstructorArgNames extends IntegrationTest {\n\n\t@SuppressWarnings({ \"FieldCanBeLocal\", \"FieldMayBeFinal\", \"StaticVariableName\", \"ParameterName\" })\n\tpublic static class TestCls {\n\t\tprivate static String STR = \"static field\";\n\t\tprivate final String str;\n\t\tprivate final String store;\n\n\t\tpublic TestCls(String str, String STR) {\n\t\t\tthis.str = str;\n\t\t\tthis.store = STR;\n\t\t}\n\n\t\tpublic TestCls() {\n\t\t\tthis.str = \"a\";\n\t\t\tthis.store = STR;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(new TestCls(\"a\", \"b\").store).isEqualTo(\"b\");\n\t\t\tassertThat(new TestCls().store).isEqualTo(STR);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"this.str = str;\")\n\t\t\t\t.containsOne(\"this.store = STR2;\")\n\t\t\t\t.containsOne(\"this.store = STR;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/TestDefPkgRename.java",
    "content": "package jadx.tests.integration.names;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestDefPkgRename extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tList<ClassNode> clsList = loadFromSmaliFiles();\n\t\t// class A moved to 'defpackage'\n\t\tassertThat(searchCls(clsList, \"A\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"package defpackage;\");\n\t\tassertThat(searchCls(clsList, \"pkg.B\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"import defpackage.A;\")\n\t\t\t\t.containsOne(\"public A a;\");\n\t}\n\n\t@Test\n\tpublic void testNoImports() {\n\t\targs.setUseImports(false);\n\t\tList<ClassNode> clsList = loadFromSmaliFiles();\n\t\t// class A moved to 'defpackage', but use full names\n\t\tassertThat(searchCls(clsList, \"A\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"package defpackage;\");\n\t\tassertThat(searchCls(clsList, \"pkg.B\"))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"import\")\n\t\t\t\t.containsOne(\"public defpackage.A a;\");\n\t}\n\n\t@Test\n\tpublic void testDeobf() {\n\t\tenableDeobfuscation();\n\t\tList<ClassNode> clsList = loadFromSmaliFiles();\n\t\t// package for class A deobfuscated\n\t\tassertThat(searchCls(clsList, \"pkg.B\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"import p000.C0000A;\")\n\t\t\t\t.containsOne(\"public C0000A f0a;\");\n\t}\n\n\t@Test\n\tpublic void testRenameDisabled() {\n\t\tdisableCompilation();\n\t\targs.setRenameFlags(Collections.emptySet());\n\t\tList<ClassNode> clsList = loadFromSmaliFiles();\n\t\t// no renaming, code will not compile\n\t\tassertThat(searchCls(clsList, \"A\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"// default package\");\n\t\tassertThat(searchCls(clsList, \"pkg.B\"))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"import\") // omit import\n\t\t\t\t.containsOne(\"public A a;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/TestDuplicateVarNames.java",
    "content": "package jadx.tests.integration.names;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestDuplicateVarNames extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static class A {\n\t\t\tpublic String mth(A a) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String toString() {\n\t\t\t\treturn \"1\";\n\t\t\t}\n\t\t}\n\n\t\tpublic A test(A a) {\n\t\t\treturn new A() {\n\t\t\t\t@Override\n\t\t\t\tpublic String mth(A innerA) {\n\t\t\t\t\treturn a + \".\" + innerA;\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tString str = test(new A()).mth(new A() {\n\t\t\t\t@Override\n\t\t\t\tpublic String toString() {\n\t\t\t\t\treturn \"2\";\n\t\t\t\t}\n\t\t\t});\n\t\t\tassertThat(str).isEqualTo(\"1.2\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tClassNode cls = getClassNode(TestCls.class);\n\n\t\tassertThat(cls)\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"return a + \\\".\\\" + a;\")\n\t\t\t\t.doesNotContain(\"AnonymousClass1\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/TestDuplicatedNames.java",
    "content": "package jadx.tests.integration.names;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestDuplicatedNames extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic static class TestCls {\n\n\t\t\tpublic Object fieldName;\n\t\t\tpublic String fieldName;\n\n\t\t\tpublic Object run() {\n\t\t\t\treturn this.fieldName;\n\t\t\t}\n\n\t\t\tpublic String run() {\n\t\t\t\treturn this.fieldName;\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tcommonChecks();\n\t}\n\n\t@Test\n\tpublic void testWithDeobf() {\n\t\tenableDeobfuscation();\n\t\tcommonChecks();\n\t}\n\n\tprivate void commonChecks() {\n\t\tassertThat(getClassNodeFromSmaliWithPath(\"names\", \"TestDuplicatedNames\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"Object fieldName;\")\n\t\t\t\t.containsOne(\"String f0fieldName\")\n\t\t\t\t.containsOne(\"this.fieldName\")\n\t\t\t\t.containsOne(\"this.f0fieldName\")\n\t\t\t\t.containsOne(\"public Object run() {\")\n\t\t\t\t.containsOne(\"public String m0run() {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/TestFieldCollideWithPackage.java",
    "content": "package jadx.tests.integration.names;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestFieldCollideWithPackage extends SmaliTest {\n\t//@formatter:off\n\t/*\n\t\t-----------------------------------------------------------\n\t\tpackage first;\n\n\t\tpublic class A {\n\t\t\tpublic A first;\n\t\t\tpublic second.A second;\n\n\t\t\tpublic String test() {\n\t\t\t\treturn second.A.call(); // compiler treat 'second' as field name\n\t\t\t}\n\t\t}\n\t\t-----------------------------------------------------------\n\t\tpackage second;\n\n\t\tpublic class A {\n\t\t\tpublic static String call() {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\t-----------------------------------------------------------\n\t*/\n\t//@formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tList<ClassNode> clsList = loadFromSmaliFiles();\n\t\tClassNode firstA = searchCls(clsList, \"first.A\");\n\t\tString code = firstA.getCode().toString();\n\n\t\tassertThat(code)\n\t\t\t\t.contains(\"second.A\")\n\t\t\t\t.doesNotContain(\"public second.A second;\");\n\t\t// expect field to be renamed\n\t}\n\n\t@Test\n\tpublic void testWithoutImports() {\n\t\tgetArgs().setUseImports(false);\n\t\tloadFromSmaliFiles();\n\t}\n\n\t@Test\n\tpublic void testWithDeobfuscation() {\n\t\tenableDeobfuscation();\n\t\tloadFromSmaliFiles();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/TestLocalVarCollideWithPackage.java",
    "content": "package jadx.tests.integration.names;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLocalVarCollideWithPackage extends SmaliTest {\n\t//@formatter:off\n\t/*\n\t\t-----------------------------------------------------------\n\t\tpackage first;\n\n\t\timport pkg.Second;\n\n\t\tpublic class A {\n\t\t\tpublic String test() {\n\t\t\t\tSecond second = new Second();\n\t\t\t\tsecond.A.call(); // collision\n\t\t\t\treturn second.str;\n\t\t\t}\n\t\t}\n\t\t-----------------------------------------------------------\n\t\tpackage pkg;\n\n\t\tpublic class Second {\n\t\t\tpublic String str;\n\t\t}\n\t\t-----------------------------------------------------------\n\t\tpackage second;\n\n\t\tpublic class A {\n\t\t}\n\t\t-----------------------------------------------------------\n\t*/\n\t//@formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tList<ClassNode> clsList = loadFromSmaliFiles();\n\t\tClassNode firstA = searchCls(clsList, \"first.A\");\n\t\tString code = firstA.getCode().toString();\n\n\t\tassertThat(code)\n\t\t\t\t.contains(\"second.A.call();\")\n\t\t\t\t.doesNotContain(\"Second second = new Second();\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tloadFromSmaliFiles();\n\t}\n\n\t@Test\n\tpublic void testWithoutImports() {\n\t\tgetArgs().setUseImports(false);\n\t\tloadFromSmaliFiles();\n\t}\n\n\t@Test\n\tpublic void testWithDeobfuscation() {\n\t\tenableDeobfuscation();\n\t\tloadFromSmaliFiles();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/TestNameAssign2.java",
    "content": "package jadx.tests.integration.names;\n\nimport java.util.ArrayDeque;\nimport java.util.BitSet;\nimport java.util.Deque;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.visitors.ssa.LiveVarAnalysis;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestNameAssign2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static void test(MethodNode mth, int regNum, LiveVarAnalysis la) {\n\t\t\tList<BlockNode> blocks = mth.getBasicBlocks();\n\t\t\tint blocksCount = blocks.size();\n\t\t\tBitSet hasPhi = new BitSet(blocksCount);\n\t\t\tBitSet processed = new BitSet(blocksCount);\n\t\t\tDeque<BlockNode> workList = new ArrayDeque<>();\n\n\t\t\tBitSet assignBlocks = la.getAssignBlocks(regNum);\n\t\t\tfor (int id = assignBlocks.nextSetBit(0); id >= 0; id = assignBlocks.nextSetBit(id + 1)) {\n\t\t\t\tprocessed.set(id);\n\t\t\t\tworkList.add(blocks.get(id));\n\t\t\t}\n\t\t\twhile (!workList.isEmpty()) {\n\t\t\t\tBlockNode block = workList.pop();\n\t\t\t\tBitSet domFrontier = block.getDomFrontier();\n\t\t\t\tfor (int id = domFrontier.nextSetBit(0); id >= 0; id = domFrontier.nextSetBit(id + 1)) {\n\t\t\t\t\tif (!hasPhi.get(id) && la.isLive(id, regNum)) {\n\t\t\t\t\t\tBlockNode df = blocks.get(id);\n\t\t\t\t\t\taddPhi(df, regNum);\n\t\t\t\t\t\thasPhi.set(id);\n\t\t\t\t\t\tif (!processed.get(id)) {\n\t\t\t\t\t\t\tprocessed.set(id);\n\t\t\t\t\t\t\tworkList.add(df);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate static void addPhi(BlockNode df, int regNum) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"int id;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/TestReservedClassNames.java",
    "content": "package jadx.tests.integration.names;\n\nimport java.io.File;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestReservedClassNames extends SmaliTest {\n\t/*\n\t * public class do {\n\t * }\n\t */\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali(\"names\" + File.separatorChar + \"TestReservedClassNames\", \"do\"))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"public class do\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/TestReservedNames.java",
    "content": "package jadx.tests.integration.names;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestReservedNames extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic static class TestCls {\n\n\t\t\tpublic String do; // reserved name\n\t\t\tpublic String 0f; // invalid identifier\n\n\t\t\tpublic String try() {\n\t\t\t\treturn this.do;\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"public String do;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/TestReservedPackageNames.java",
    "content": "package jadx.tests.integration.names;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestReservedPackageNames extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpackage do.if;\n\n\t\tpublic class A {}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tList<ClassNode> clsList = loadFromSmaliFiles();\n\t\tfor (ClassNode cls : clsList) {\n\t\t\tassertThat(cls).code().doesNotContain(\"package do.if;\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testDeobf() {\n\t\tenableDeobfuscation();\n\t\tList<ClassNode> clsList = loadFromSmaliFiles();\n\t\tfor (ClassNode cls : clsList) {\n\t\t\tassertThat(cls).code().doesNotContain(\"package do.if;\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testRenameDisabled() {\n\t\tdisableCompilation();\n\t\targs.setRenameFlags(Collections.emptySet());\n\t\tfor (ClassNode cls : loadFromSmaliFiles()) {\n\t\t\tif (cls.getAlias().equals(\"A\")) {\n\t\t\t\tassertThat(cls).code().contains(\"package do.if;\");\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/TestSameMethodsNames.java",
    "content": "package jadx.tests.integration.names;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestSameMethodsNames extends IntegrationTest {\n\n\tpublic static class TestCls<V> {\n\n\t\tpublic static void test() {\n\t\t\tnew Bug().Bug();\n\t\t}\n\n\t\tpublic static class Bug {\n\t\t\tpublic Bug() {\n\t\t\t\tSystem.out.println(\"constructor\");\n\t\t\t}\n\n\t\t\t@SuppressWarnings({ \"MethodName\", \"MethodNameSameAsClassName\" })\n\t\t\tvoid Bug() {\n\t\t\t\tSystem.out.println(\"Bug\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"new Bug().Bug();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/pkg/a.java",
    "content": "package jadx.tests.integration.names.pkg;\n\n@SuppressWarnings({ \"TypeName\", \"MethodName\" })\npublic class a {\n\tpublic static a a() {\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/pkg/b.java",
    "content": "package jadx.tests.integration.names.pkg;\n\n@SuppressWarnings(\"TypeName\")\npublic class b {\n\tclass a {\n\t}\n\n\tprivate jadx.tests.integration.names.pkg.a a = jadx.tests.integration.names.pkg.a.a();\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/pkg2/System.java",
    "content": "package jadx.tests.integration.names.pkg2;\n\npublic class System {\n\tpublic static void doSomething() {\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/names/pkg2/TestCls.java",
    "content": "package jadx.tests.integration.names.pkg2;\n\npublic class TestCls {\n\tpublic void doSomething() {\n\t\tSystem.doSomething();\n\t\tjava.lang.System.out.println(\"Hello World\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestAllNops.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAllNops extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsLines(1, \"private boolean test() {\", \"}\")\n\t\t\t\t.containsLines(1, \"private boolean testWithTryCatch() {\", \"}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestArgInline.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestArgInline extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test(int a) {\n\t\t\twhile (a < 10) {\n\t\t\t\tint b = a + 1;\n\t\t\t\ta = b;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"i++;\")\n\t\t\t\t.doesNotContain(\"i = i + 1;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestBadClassAccessModifiers.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestBadClassAccessModifiers extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\t// class others.A\n\t\tpublic class A {\n\t\t\tpublic void call() {\n\t\t\t\tB.BB.BBB.test();\n\t\t\t}\n\t\t}\n\n\t\t// class others.B\n\t\tpublic class B {\n\t\t\tprivate static class BB {\n\t\t\t\tpublic static class BBB {\n\t\t\t\t\tpublic static void test() {\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t*/\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliFiles(\"A\"))\n\t\t\t\t.code();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestBadMethodAccessModifiers.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestBadMethodAccessModifiers extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic static class TestCls {\n\n\t\t\tpublic abstract class A {\n\t\t\t\tpublic abstract void test();\n\t\t\t}\n\n\t\t\tpublic class B extends A {\n\t\t\t\tprotected void test() {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliFiles(\"others\", \"TestBadMethodAccessModifiers\", \"TestCls\"))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"protected void test() {\")\n\t\t\t\t.containsOne(\"public void test() {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestCastOfNull.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"unused\")\npublic class TestCastOfNull extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test() {\n\t\t\tm((long[]) null);\n\t\t\tm((String) null);\n\t\t\tm((List<String>) null);\n\t\t}\n\n\t\tpublic void m(long[] a) {\n\t\t}\n\n\t\tpublic void m(String s) {\n\t\t}\n\n\t\tpublic void m(List<String> list) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"m((long[]) null);\")\n\t\t\t\t.containsOne(\"m((String) null);\")\n\t\t\t\t.containsOne(\"m((List<String>) null);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestClassGen.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestClassGen extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic interface I {\n\t\t\tint test();\n\n\t\t\tpublic int test3();\n\t\t}\n\n\t\tpublic abstract static class A {\n\t\t\tpublic abstract int test2();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"public interface I {\")\n\t\t\t\t.contains(indent(2) + \"int test();\")\n\t\t\t\t.doesNotContain(\"public int test();\")\n\t\t\t\t.contains(indent(2) + \"int test3();\")\n\t\t\t\t.contains(\"public static abstract class A {\")\n\t\t\t\t.contains(indent(2) + \"public abstract int test2();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestClassImplementsSignature.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.RaungTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestClassImplementsSignature extends RaungTest {\n\n\tpublic static class TestCls {\n\t\tpublic abstract static class A<T> implements Comparable<A<T>> {\n\t\t\tT value;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public static abstract class A<T> implements Comparable<A<T>> {\");\n\t}\n\n\t@Test\n\tpublic void testRaung() {\n\t\tallowWarnInCode();\n\t\tassertThat(getClassNodeFromRaung())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public class TestClassImplementsSignature<T> {\")\n\t\t\t\t.containsOne(\"Unexpected interfaces in signature\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestClassReGen.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestClassReGen extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate int intField = 5;\n\n\t\tpublic static class A {\n\t\t}\n\n\t\tpublic int test() {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls)\n\t\t\t\t.code()\n\t\t\t\t.containsOnlyOnce(\"private int intField = 5;\")\n\t\t\t\t.containsOnlyOnce(\"public static class A {\")\n\t\t\t\t.containsOnlyOnce(\"public int test() {\");\n\n\t\tcls.getInnerClasses().get(0).getClassInfo().changeShortName(\"ARenamed\");\n\t\tcls.searchMethodByShortName(\"test\").getMethodInfo().setAlias(\"testRenamed\");\n\t\tcls.searchFieldByName(\"intField\").getFieldInfo().setAlias(\"intFieldRenamed\");\n\n\t\tassertThat(cls)\n\t\t\t\t.reloadCode(this)\n\t\t\t\t.containsOnlyOnce(\"private int intFieldRenamed = 5;\")\n\t\t\t\t.containsOnlyOnce(\"public static class ARenamed {\")\n\t\t\t\t.containsOnlyOnce(\"public int testRenamed() {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestCodeComments.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.data.ICodeComment;\nimport jadx.api.data.IJavaCodeRef;\nimport jadx.api.data.IJavaNodeRef.RefType;\nimport jadx.api.data.impl.JadxCodeComment;\nimport jadx.api.data.impl.JadxCodeData;\nimport jadx.api.data.impl.JadxCodeRef;\nimport jadx.api.data.impl.JadxNodeRef;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestCodeComments extends IntegrationTest {\n\n\t@SuppressWarnings(\"FieldCanBeLocal\")\n\tpublic static class TestCls {\n\t\tprivate int intField = 5;\n\n\t\tpublic static class A {\n\t\t}\n\n\t\tpublic int test() {\n\t\t\tSystem.out.println(\"Hello\");\n\t\t\tSystem.out.println(\"comment\");\n\t\t\treturn intField;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tString baseClsId = TestCls.class.getName();\n\t\tICodeComment clsComment = new JadxCodeComment(JadxNodeRef.forCls(baseClsId), \"class comment\");\n\t\tICodeComment innerClsComment = new JadxCodeComment(JadxNodeRef.forCls(baseClsId + \"$A\"), \"inner class comment\");\n\t\tICodeComment fldComment = new JadxCodeComment(new JadxNodeRef(RefType.FIELD, baseClsId, \"intField:I\"), \"field comment\");\n\t\tJadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId, \"test()I\");\n\t\tICodeComment mthComment = new JadxCodeComment(mthRef, \"method comment\");\n\t\tIJavaCodeRef insnRef = JadxCodeRef.forInsn(isJavaInput() ? 13 : 11);\n\t\tICodeComment insnComment = new JadxCodeComment(mthRef, insnRef, \"insn comment\");\n\n\t\tJadxCodeData codeData = new JadxCodeData();\n\t\tgetArgs().setCodeData(codeData);\n\t\tcodeData.setComments(Arrays.asList(clsComment, innerClsComment, fldComment, mthComment, insnComment));\n\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls)\n\t\t\t\t.decompile()\n\t\t\t\t.checkCodeOffsets()\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"// class comment\")\n\t\t\t\t.containsOne(\"// inner class comment\")\n\t\t\t\t.containsOne(\"// field comment\")\n\t\t\t\t.containsOne(\"// method comment\")\n\t\t\t\t.containsOne(\"System.out.println(\\\"comment\\\"); // insn comment\");\n\n\t\tString code = cls.getCode().getCodeStr();\n\t\tassertThat(cls)\n\t\t\t\t.reloadCode(this)\n\t\t\t\t.isEqualTo(code);\n\n\t\tICodeComment updInsnComment = new JadxCodeComment(mthRef, insnRef, \"updated insn comment\");\n\t\tcodeData.setComments(Collections.singletonList(updInsnComment));\n\t\tjadxDecompiler.reloadCodeData();\n\t\tassertThat(cls)\n\t\t\t\t.reloadCode(this)\n\t\t\t\t.containsOne(\"System.out.println(\\\"comment\\\"); // updated insn comment\")\n\t\t\t\t.doesNotContain(\"class comment\")\n\t\t\t\t.containsOne(\" comment\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestCodeComments2.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.data.ICodeComment;\nimport jadx.api.data.IJavaCodeRef;\nimport jadx.api.data.IJavaNodeRef.RefType;\nimport jadx.api.data.impl.JadxCodeComment;\nimport jadx.api.data.impl.JadxCodeData;\nimport jadx.api.data.impl.JadxCodeRef;\nimport jadx.api.data.impl.JadxNodeRef;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestCodeComments2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int test(boolean z) {\n\t\t\tif (z) {\n\t\t\t\tSystem.out.println(\"z\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\treturn 3;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tprintOffsets();\n\n\t\tString baseClsId = TestCls.class.getName();\n\t\tJadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId, \"test(Z)I\");\n\t\tIJavaCodeRef insnRef = JadxCodeRef.forInsn(isJavaInput() ? 13 : 10);\n\t\tICodeComment insnComment = new JadxCodeComment(mthRef, insnRef, \"return comment\");\n\t\tIJavaCodeRef insnRef2 = JadxCodeRef.forInsn(isJavaInput() ? 15 : 11);\n\t\tICodeComment insnComment2 = new JadxCodeComment(mthRef, insnRef2, \"another return comment\");\n\n\t\tJadxCodeData codeData = new JadxCodeData();\n\t\tcodeData.setComments(Arrays.asList(insnComment, insnComment2));\n\t\tgetArgs().setCodeData(codeData);\n\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.decompile()\n\t\t\t\t.checkCodeOffsets()\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return 1; // \" + insnComment.getComment())\n\t\t\t\t.containsOne(\"return 3; // \" + insnComment2.getComment());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestCodeComments2a.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.util.Arrays;\nimport java.util.Random;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.data.ICodeComment;\nimport jadx.api.data.IJavaCodeRef;\nimport jadx.api.data.IJavaNodeRef.RefType;\nimport jadx.api.data.impl.JadxCodeComment;\nimport jadx.api.data.impl.JadxCodeData;\nimport jadx.api.data.impl.JadxCodeRef;\nimport jadx.api.data.impl.JadxNodeRef;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestCodeComments2a extends IntegrationTest {\n\n\t@SuppressWarnings(\"unused\")\n\tpublic static class TestCls {\n\t\tprivate int f;\n\n\t\tpublic int test(boolean z) {\n\t\t\tif (z) {\n\t\t\t\tSystem.out.println(\"z\");\n\t\t\t\treturn new Random().nextInt();\n\t\t\t}\n\t\t\treturn f;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tprintOffsets();\n\n\t\tString baseClsId = TestCls.class.getName();\n\t\tJadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId, \"test(Z)I\");\n\t\tIJavaCodeRef insnRef = JadxCodeRef.forInsn(isJavaInput() ? 22 : 18);\n\t\tICodeComment insnComment = new JadxCodeComment(mthRef, insnRef, \"return comment\");\n\t\tIJavaCodeRef insnRef2 = JadxCodeRef.forInsn(isJavaInput() ? 27 : 19);\n\t\tICodeComment insnComment2 = new JadxCodeComment(mthRef, insnRef2, \"another return comment\");\n\n\t\tJadxCodeData codeData = new JadxCodeData();\n\t\tcodeData.setComments(Arrays.asList(insnComment, insnComment2));\n\t\tgetArgs().setCodeData(codeData);\n\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.decompile()\n\t\t\t\t.checkCodeOffsets()\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"// \" + insnComment.getComment())\n\t\t\t\t.containsOne(\"// \" + insnComment2.getComment());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestCodeCommentsMultiline.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.data.ICodeComment;\nimport jadx.api.data.IJavaCodeRef;\nimport jadx.api.data.IJavaNodeRef.RefType;\nimport jadx.api.data.impl.JadxCodeComment;\nimport jadx.api.data.impl.JadxCodeData;\nimport jadx.api.data.impl.JadxCodeRef;\nimport jadx.api.data.impl.JadxNodeRef;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestCodeCommentsMultiline extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int test(boolean z) {\n\t\t\tif (z) {\n\t\t\t\tSystem.out.println(\"z\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\treturn 3;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tprintOffsets();\n\n\t\tString baseClsId = TestCls.class.getName();\n\t\tJadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId, \"test(Z)I\");\n\t\tIJavaCodeRef insnRef = JadxCodeRef.forInsn(isJavaInput() ? 15 : 11);\n\t\tICodeComment insnComment = new JadxCodeComment(mthRef, insnRef, \"multi\\nline\\ncomment\");\n\n\t\tJadxCodeData codeData = new JadxCodeData();\n\t\tcodeData.setComments(Collections.singletonList(insnComment));\n\t\tgetArgs().setCodeData(codeData);\n\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"// multi\")\n\t\t\t\t.containsOne(\"// line\")\n\t\t\t\t.containsOne(\"// comment\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestCodeCommentsOverride.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.data.ICodeComment;\nimport jadx.api.data.IJavaNodeRef.RefType;\nimport jadx.api.data.impl.JadxCodeComment;\nimport jadx.api.data.impl.JadxCodeData;\nimport jadx.api.data.impl.JadxNodeRef;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestCodeCommentsOverride extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic interface I {\n\t\t\tvoid mth();\n\t\t}\n\n\t\tpublic static class A implements I {\n\t\t\t@Override\n\t\t\tpublic void mth() {\n\t\t\t\tSystem.out.println(\"mth\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tString baseClsId = TestCls.class.getName();\n\t\tJadxNodeRef iMthRef = new JadxNodeRef(RefType.METHOD, baseClsId + \"$I\", \"mth()V\");\n\t\tICodeComment iMthComment = new JadxCodeComment(iMthRef, \"interface mth comment\");\n\n\t\tJadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId + \"$A\", \"mth()V\");\n\t\tICodeComment mthComment = new JadxCodeComment(mthRef, \"mth comment\");\n\n\t\tJadxCodeData codeData = new JadxCodeData();\n\t\tcodeData.setComments(Arrays.asList(iMthComment, mthComment));\n\t\tgetArgs().setCodeData(codeData);\n\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls)\n\t\t\t\t.decompile()\n\t\t\t\t.checkCodeOffsets()\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"@Override\")\n\t\t\t\t.containsOne(\"// \" + iMthComment.getComment())\n\t\t\t\t.containsOne(\"// \" + mthComment.getComment());\n\n\t\tassertThat(cls)\n\t\t\t\t.reloadCode(this)\n\t\t\t\t.containsOne(\"@Override\")\n\t\t\t\t.containsOne(\"// \" + iMthComment.getComment())\n\t\t\t\t.containsOne(\"// \" + mthComment.getComment());\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestCodeMetadata.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.JadxInternalAccess;\nimport jadx.api.JavaClass;\nimport jadx.api.JavaMethod;\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeAnnotation.AnnType;\nimport jadx.api.metadata.ICodeMetadata;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestCodeMetadata extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static class A {\n\t\t\tpublic String str;\n\t\t}\n\n\t\tpublic String test() {\n\t\t\tA a = new A();\n\t\t\ta.str = call();\n\t\t\treturn a.str;\n\t\t}\n\n\t\tpublic static String call() {\n\t\t\treturn \"str\";\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls).code().containsOne(\"return a.str;\");\n\n\t\tMethodNode testMth = getMethod(cls, \"test\");\n\t\tMethodNode callMth = getMethod(cls, \"call\");\n\n\t\tint callDefPos = callMth.getDefPosition();\n\t\tassertThat(callDefPos).isNotZero();\n\n\t\tJavaClass javaClass = JadxInternalAccess.convertClassNode(jadxDecompiler, cls);\n\t\tJavaMethod callJavaMethod = JadxInternalAccess.convertMethodNode(jadxDecompiler, callMth);\n\t\tList<Integer> callUsePlaces = javaClass.getUsePlacesFor(javaClass.getCodeInfo(), callJavaMethod);\n\t\tassertThat(callUsePlaces).hasSize(1);\n\t\tint callUse = callUsePlaces.get(0);\n\n\t\tICodeMetadata metadata = cls.getCode().getCodeMetadata();\n\t\tSystem.out.println(metadata);\n\t\tICodeNodeRef callDef = metadata.getNodeAt(callUse);\n\t\tassertThat(callDef).isSameAs(testMth);\n\n\t\tAtomicInteger endPos = new AtomicInteger();\n\t\tICodeAnnotation testEnd = metadata.searchUp(callDefPos, (pos, ann) -> {\n\t\t\tif (ann.getAnnType() == AnnType.END) {\n\t\t\t\tendPos.set(pos);\n\t\t\t\treturn ann;\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t\tassertThat(testEnd).isNotNull();\n\t\tint testEndPos = endPos.get();\n\n\t\tICodeAnnotation closest = metadata.getClosestUp(testEndPos);\n\t\tassertThat(closest).isInstanceOf(FieldNode.class); // field reference from 'return a.str;'\n\n\t\tICodeNodeRef nodeBelow = metadata.getNodeBelow(testEndPos);\n\t\tassertThat(nodeBelow).isSameAs(callMth);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestCodeMetadata2.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.JavaClass;\nimport jadx.api.JavaMethod;\nimport jadx.api.metadata.ICodeMetadata;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.api.JadxInternalAccess.convertClassNode;\nimport static jadx.api.JadxInternalAccess.convertMethodNode;\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestCodeMetadata2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\t@SuppressWarnings(\"Convert2Lambda\")\n\t\tpublic Runnable test(boolean a) {\n\t\t\tif (a) {\n\t\t\t\treturn new Runnable() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tSystem.out.println(\"test\");\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t\tSystem.out.println(\"another\");\n\t\t\treturn empty();\n\t\t}\n\n\t\tpublic static Runnable empty() {\n\t\t\treturn new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\t// empty\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls).code().containsOne(\"return empty();\");\n\n\t\tMethodNode testMth = getMethod(cls, \"test\");\n\t\tMethodNode emptyMth = getMethod(cls, \"empty\");\n\n\t\tJavaClass javaClass = convertClassNode(jadxDecompiler, cls);\n\t\tJavaMethod emptyJavaMethod = convertMethodNode(jadxDecompiler, emptyMth);\n\t\tList<Integer> emptyUsePlaces = javaClass.getUsePlacesFor(javaClass.getCodeInfo(), emptyJavaMethod);\n\t\tassertThat(emptyUsePlaces).hasSize(1);\n\t\tint callUse = emptyUsePlaces.get(0);\n\n\t\tICodeMetadata metadata = cls.getCode().getCodeMetadata();\n\t\tassertThat(metadata.getNodeAt(callUse)).isSameAs(testMth);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestCodeMetadata3.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.JavaClass;\nimport jadx.api.JavaVariable;\nimport jadx.api.metadata.annotations.VarNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Test variables refs in code metadata\n */\npublic class TestCodeMetadata3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic String test(String str) {\n\t\t\tint k = str.length();\n\t\t\tk++;\n\t\t\treturn str + ':' + k;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tICodeInfo codeInfo = cls.getCode();\n\t\tSystem.out.println(codeInfo.getCodeMetadata());\n\n\t\tMethodNode testMth = getMethod(cls, \"test\");\n\t\tJavaClass javaClass = toJavaClass(cls);\n\t\tList<VarNode> varNodes = testMth.collectArgNodes();\n\t\tassertThat(varNodes).hasSize(1);\n\t\tVarNode strVar = varNodes.get(0);\n\t\tJavaVariable strJavaVar = toJavaVariable(strVar);\n\t\tassertThat(strJavaVar.getName()).isEqualTo(\"str\");\n\n\t\tList<Integer> strUsePlaces = javaClass.getUsePlacesFor(codeInfo, strJavaVar);\n\t\tassertThat(strUsePlaces).hasSize(2);\n\t\tassertThat(codeInfo).code().countString(3, \"str\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestConstReplace.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestConstReplace extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static final String CONST_VALUE = \"string\";\n\n\t\tpublic String test() {\n\t\t\treturn CONST_VALUE;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tMethodNode testMth = cls.searchMethodByShortName(\"test\");\n\t\tassertThat(testMth)\n\t\t\t\t.code()\n\t\t\t\t.print()\n\t\t\t\t.containsOne(\"return CONST_VALUE;\");\n\n\t\tFieldNode constField = cls.searchFieldByName(\"CONST_VALUE\");\n\t\tassertThat(constField).isNotNull();\n\t\tassertThat(constField.getUseIn()).containsExactly(testMth);\n\t}\n\n\t@Test\n\tpublic void testWithoutReplace() {\n\t\tgetArgs().setReplaceConsts(false);\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls).code().containsOne(\"return \\\"string\\\";\");\n\n\t\tFieldNode constField = cls.searchFieldByName(\"CONST_VALUE\");\n\t\tassertThat(constField).isNotNull();\n\t\tassertThat(constField.getUseIn()).isEmpty();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestConstStringConcat.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestConstStringConcat extends IntegrationTest {\n\n\t@SuppressWarnings(\"StringBufferReplaceableByString\")\n\tpublic static class TestCls {\n\t\tpublic String test1(int value) {\n\t\t\treturn new StringBuilder().append(\"Value\").append(\" equals \").append(value).toString();\n\t\t}\n\n\t\tpublic String test2() {\n\t\t\treturn new StringBuilder().append(\"App \").append(\"version: \").append(1).append('.').append(2).toString();\n\t\t}\n\n\t\tpublic String test3(String name, int value) {\n\t\t\treturn \"value \" + name + \" = \" + value;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test1(7)).isEqualTo(\"Value equals 7\");\n\t\t\tassertThat(test2()).isEqualTo(\"App version: 1.2\");\n\t\t\tassertThat(test3(\"v\", 4)).isEqualTo(\"value v = 4\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return \\\"Value equals \\\" + \")\n\t\t\t\t.containsOne(\"return \\\"App version: 1.2\\\";\")\n\t\t\t\t.containsOne(\"return \\\"value \\\" + str + \\\" = \\\" + i;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestConstructor.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestConstructor extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tprivate SomeObject test(double r23, double r25, SomeObject r27) {\n\t\t\tSomeObject r17 = new SomeObject\n\t\t\tr0 = r17\n\t\t\tr1 = r27\n\t\t\tr0.<init>(r1)\n\t\t\treturn r17\n\t\t}\n\t */\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"new SomeObject(arg3);\")\n\t\t\t\t.doesNotContain(\"= someObject\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestConstructor2.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Constructor called on object instance is from Object not instance type\n */\npublic class TestConstructor2 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliFiles())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"A a = new A();\")\n\t\t\t\t.doesNotContain(\"return\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestConstructorBranched.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"CommentedOutCode\")\npublic class TestConstructorBranched extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic Set<String> test(Collection<String> collection) {\n\t\t\tSet<String> set;\n\t\t\tif (collection == null) {\n\t\t\t\tset = new HashSet<>();\n\t\t\t} else {\n\t\t\t\tset = new HashSet<>(collection);\n\t\t\t}\n\t\t\tset.add(\"end\");\n\t\t\treturn set;\n\t\t}\n\t */\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"new HashSet()\")\n\t\t\t\t.containsOne(\"new HashSet(collection)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestConstructorBranched2.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestConstructorBranched2 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.countString(3, \"new StringBuilder()\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestConstructorBranched3.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestConstructorBranched3 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"return new f(\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestDeadBlockReferencesStart.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestDeadBlockReferencesStart extends SmaliTest {\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.countString(0, \"throw\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestDeboxing.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestDeboxing extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic Object testInt() {\n\t\t\treturn 1;\n\t\t}\n\n\t\tpublic Object testBoolean() {\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic Object testByte() {\n\t\t\treturn (byte) 2;\n\t\t}\n\n\t\tpublic Short testShort() {\n\t\t\treturn 3;\n\t\t}\n\n\t\tpublic Character testChar() {\n\t\t\treturn 'c';\n\t\t}\n\n\t\tpublic Long testLong() {\n\t\t\treturn 4L;\n\t\t}\n\n\t\tpublic void testConstInline() {\n\t\t\tBoolean v = true;\n\t\t\tuse(v);\n\t\t\tuse(v);\n\t\t}\n\n\t\tprivate void use(Boolean v) {\n\t\t}\n\n\t\tpublic void check() {\n\t\t\t// don't mind weird comparisons\n\t\t\t// need to get primitive without using boxing or literal\n\t\t\t// otherwise will get same result after decompilation\n\t\t\tassertThat(testInt()).isEqualTo(Integer.sum(0, 1));\n\t\t\tassertThat(testBoolean()).isEqualTo(Boolean.TRUE);\n\t\t\tassertThat(testByte()).isEqualTo(Byte.parseByte(\"2\"));\n\t\t\tassertThat(testShort()).isEqualTo(Short.parseShort(\"3\"));\n\t\t\tassertThat(testChar()).isEqualTo(\"c\".charAt(0));\n\t\t\tassertThat(testLong()).isEqualTo(Long.valueOf(\"4\"));\n\t\t\ttestConstInline();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return 1;\")\n\t\t\t\t.containsOne(\"return true;\")\n\t\t\t\t.containsOne(\"return (byte) 2;\")\n\t\t\t\t.containsOne(\"return (short) 3;\")\n\t\t\t\t.containsOne(\"return 'c';\")\n\t\t\t\t.containsOne(\"return 4L;\")\n\t\t\t\t.countString(2, \"use(true);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestDeboxing2.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestDeboxing2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic long test(Long l) {\n\t\t\tif (l == null) {\n\t\t\t\tl = 0L;\n\t\t\t}\n\t\t\treturn l;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(null)).isEqualTo(0L);\n\t\t\tassertThat(test(0L)).isEqualTo(0L);\n\t\t\tassertThat(test(7L)).isEqualTo(7L);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"long test(Long l)\")\n\t\t\t\t.containsOne(\"if (l == null) {\")\n\t\t\t\t.containsOne(\"l = 0L;\")\n\t\t\t\t.containsOne(\"test(null)\")\n\t\t\t\t.containsOne(\"test(0L)\")\n\t\t\t\t// checks for 'check' method\n\t\t\t\t.countString(2, \"isEqualTo(0L)\")\n\t\t\t\t.containsOne(\"test(7L)\")\n\t\t\t\t.containsOne(\"isEqualTo(7L)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestDeboxing3.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestDeboxing3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static class Pair<F, S> {\n\t\t\tpublic F first;\n\t\t\tpublic S second;\n\t\t}\n\n\t\tprivate Map<String, Pair<Long, String>> cache = new HashMap<>();\n\n\t\tpublic boolean test(String id, Long l) {\n\t\t\tif (l == null) {\n\t\t\t\tl = 900000L;\n\t\t\t}\n\t\t\tPair<Long, String> pair = this.cache.get(id);\n\t\t\tif (pair == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn pair.first + l > System.currentTimeMillis();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"l = 900000L;\");\n\t}\n\n\t@Test\n\t@NotYetImplemented(\"Full deboxing and generics propagation\")\n\tpublic void testFull() {\n\t\tnoDebugInfo();\n\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"Pair<Long, String> pair = this.cache.get(id);\")\n\t\t\t\t.containsOne(\"return pair.first + l > System.currentTimeMillis();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestDeboxing4.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestDeboxing4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic boolean test(Integer i) {\n\t\t\treturn ((Integer) 1).equals(i);\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(null)).isFalse();\n\t\t\tassertThat(test(0)).isFalse();\n\t\t\tassertThat(test(1)).isTrue();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"return 1.equals(num);\");\n\t}\n\n\t@Test\n\t@NotYetImplemented(\"Inline boxed types\")\n\tpublic void testInline() {\n\t\tnoDebugInfo();\n\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return ((Integer) 1).equals(i);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestDeboxing5.java",
    "content": "package jadx.tests.integration.others;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestDeboxing5 extends IntegrationTest {\n\n\t@SuppressWarnings(\"WrapperTypeMayBePrimitive\")\n\tpublic static class TestCls {\n\t\tprivate static String type;\n\n\t\tpublic static void test(String[] args) {\n\t\t\tFloat f = (float) -47.99;\n\t\t\tBoolean b = args.length == 0;\n\t\t\tObject o = ((b) ? false : f);\n\t\t\tcall(o);\n\t\t}\n\n\t\tpublic static void call(Object o) {\n\t\t\tif (o instanceof Boolean) {\n\t\t\t\ttype = \"Boolean\";\n\t\t\t}\n\t\t\tif (o instanceof Float) {\n\t\t\t\ttype = \"Float\";\n\t\t\t}\n\t\t}\n\n\t\tprivate static void verify(String[] arr, String str) {\n\t\t\ttype = null;\n\t\t\ttest(arr);\n\t\t\tassertThat(type).isEqualTo(str);\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tverify(new String[0], \"Boolean\");\n\t\t\tverify(new String[] { \"1\" }, \"Float\");\n\t\t}\n\t}\n\n\t@TestWithProfiles(TestProfile.D8_J11)\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"boolean valueOf\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestDefConstructorNotRemoved.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestDefConstructorNotRemoved extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tstatic {\n\t\t\t// empty\n\t\t}\n\n\t\tpublic static class A {\n\t\t\tpublic final String s;\n\n\t\t\tpublic A() {\n\t\t\t\ts = \"a\";\n\t\t\t}\n\n\t\t\tpublic A(String str) {\n\t\t\t\ts = str;\n\t\t\t}\n\t\t}\n\n\t\tpublic static class B extends A {\n\t\t\tpublic B() {\n\t\t\t\tsuper();\n\t\t\t}\n\n\t\t\tpublic B(String s) {\n\t\t\t\tsuper(s);\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tnew A();\n\t\t\tnew A(\"a\");\n\t\t\tnew B();\n\t\t\tnew B(\"b\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"super();\")\n\t\t\t\t.doesNotContain(\"static {\")\n\t\t\t\t.containsOne(\"public B() {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestDefConstructorWithAnnotation.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestDefConstructorWithAnnotation extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\t@AnnotationTest\n\t\tpublic TestCls() {\n\t\t}\n\n\t\t@Target(ElementType.CONSTRUCTOR)\n\t\t@Retention(RetentionPolicy.RUNTIME)\n\t\tpublic @interface AnnotationTest {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"@AnnotationTest\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestDuplicateCast.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.instructions.args.InsnWrapArg;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.core.dex.instructions.InsnType.CHECK_CAST;\nimport static jadx.core.dex.instructions.InsnType.RETURN;\nimport static jadx.core.utils.BlockUtils.collectAllInsns;\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Test duplicate 'check-cast' instruction produced because of bug in javac:\n * http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6246854\n */\npublic class TestDuplicateCast extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int[] method(Object o) {\n\t\t\treturn (int[]) o;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tMethodNode mth = getMethod(cls, \"method\");\n\n\t\tassertThat(cls)\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return (int[]) o;\");\n\n\t\tList<InsnNode> insns = collectAllInsns(mth.getBasicBlocks());\n\t\tassertThat(insns).hasSize(1);\n\t\tInsnNode insnNode = insns.get(0);\n\t\tassertThat(insnNode.getType()).isEqualTo(RETURN);\n\t\tassertThat(insnNode.getArg(0).isInsnWrap()).isTrue();\n\t\tInsnNode wrapInsn = ((InsnWrapArg) insnNode.getArg(0)).getWrapInsn();\n\t\tassertThat(wrapInsn.getType()).isEqualTo(CHECK_CAST);\n\t\tassertThat(wrapInsn.getArg(0).isInsnWrap()).isFalse();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestExplicitOverride.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestExplicitOverride extends SmaliTest {\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.countString(1, \"@Override\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestFieldAccessReorder.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestFieldAccessReorder extends IntegrationTest {\n\tpublic static class TestCls {\n\t\tprivate long field = 10;\n\n\t\tpublic final boolean test() {\n\t\t\tlong value = longCall();\n\t\t\tlong diff = value - this.field;\n\t\t\tthis.field = value;\n\t\t\treturn diff > 250;\n\t\t}\n\n\t\tpublic static long longCall() {\n\t\t\treturn 261L;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test()).isTrue();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t\t// auto check should pass\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestFieldInit2.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestFieldInit2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic interface BasicAbstract {\n\t\t\tvoid doSomething();\n\t\t}\n\n\t\tpublic BasicAbstract x = new BasicAbstract() {\n\t\t\t@Override\n\t\t\tpublic void doSomething() {\n\t\t\t\ty = 1;\n\t\t\t}\n\t\t};\n\t\tpublic int y = 0;\n\n\t\tpublic TestCls() {\n\t\t}\n\n\t\tpublic TestCls(int z) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"x = new BasicAbstract() {\")\n\t\t\t\t.containsOne(\"y = 0;\")\n\t\t\t\t.containsLines(1, \"public TestFieldInit2$TestCls(int z) {\", \"}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestFieldInit3.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestFieldInit3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic abstract static class A {\n\t\t\tpublic int field = 4;\n\t\t}\n\n\t\tpublic static final class B extends A {\n\t\t\tpublic B() {\n\t\t\t\t// IPUT for A.field\n\t\t\t\tsuper.field = 7;\n\t\t\t}\n\t\t}\n\n\t\tpublic static final class C extends A {\n\t\t\tpublic int other = 11;\n\n\t\t\tpublic C() {\n\t\t\t\t// IPUT for C.field not A.field !!!\n\t\t\t\tthis.field = 9;\n\t\t\t}\n\t\t}\n\n\t\tpublic static final class D extends A {\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(new B().field).isEqualTo(7);\n\t\t\tassertThat(new C().field).isEqualTo(9);\n\t\t\tassertThat(new C().other).isEqualTo(11);\n\t\t\tassertThat(new D().field).isEqualTo(4);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public int field = 4;\")\n\t\t\t\t.containsOne(\"field = 7;\")\n\t\t\t\t.containsOne(\"field = 9;\")\n\t\t\t\t.containsOne(\"public int other = 11;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestFieldInitInTryCatch.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.net.MalformedURLException;\nimport java.net.URL;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestFieldInitInTryCatch extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static final URL A;\n\n\t\tstatic {\n\t\t\ttry {\n\t\t\t\tA = new URL(\"http://www.example.com/\");\n\t\t\t} catch (MalformedURLException e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static class TestCls2 {\n\t\tpublic static final URL[] A;\n\n\t\tstatic {\n\t\t\ttry {\n\t\t\t\tA = new URL[] { new URL(\"http://www.example.com/\") };\n\t\t\t} catch (MalformedURLException e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static class TestCls3 {\n\t\tpublic static final String[] A;\n\n\t\tstatic {\n\t\t\ttry {\n\t\t\t\tA = new String[] { \"a\" };\n\t\t\t\t// Note: follow code will not be extracted:\n\t\t\t\t// a = new String[]{new String(\"a\")};\n\t\t\t\tnew URL(\"http://www.example.com/\");\n\t\t\t} catch (MalformedURLException e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public static final URL A;\")\n\t\t\t\t.containsOne(\"A = new URL(\\\"http://www.example.com/\\\");\")\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"try {\",\n\t\t\t\t\t\tindent(1) + \"A = new URL(\\\"http://www.example.com/\\\");\",\n\t\t\t\t\t\t\"} catch (MalformedURLException e) {\");\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tassertThat(getClassNode(TestCls2.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"try {\",\n\t\t\t\t\t\tindent(1) + \"A = new URL[]{new URL(\\\"http://www.example.com/\\\")};\",\n\t\t\t\t\t\t\"} catch (MalformedURLException e) {\");\n\t}\n\n\t@Test\n\tpublic void test3() {\n\t\tassertThat(getClassNode(TestCls3.class))\n\t\t\t\t.code()\n\t\t\t\t// don't move code from try/catch\n\t\t\t\t.containsOne(\"public static final String[] A;\")\n\t\t\t\t.containsOne(\"A = new String[]{\\\"a\\\"};\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestFieldInitNegative.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Negative case for field initialization move (#1599).\n * Can't reorder with other instance methods.\n */\npublic class TestFieldInitNegative extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tStringBuilder sb;\n\t\tint field;\n\n\t\tpublic TestCls() {\n\t\t\tinitBuilder(new StringBuilder(\"sb\"));\n\t\t\tthis.field = initField();\n\t\t\tthis.sb.append(this.field);\n\t\t}\n\n\t\tprivate void initBuilder(StringBuilder sb) {\n\t\t\tthis.sb = sb;\n\t\t}\n\n\t\tprivate int initField() {\n\t\t\treturn sb.length();\n\t\t}\n\n\t\tpublic String getStr() {\n\t\t\treturn sb.toString();\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(new TestCls().getStr()).isEqualTo(\"sb2\"); // no NPE\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"int field = initField();\")\n\t\t\t\t.containsOne(\"this.field = initField();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestFieldInitOrder.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestFieldInitOrder extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate final StringBuilder sb = new StringBuilder();\n\t\tprivate final String a = sb.append(\"a\").toString();\n\t\tprivate final String b = sb.append(\"b\").toString();\n\t\tprivate final String c = sb.append(\"c\").toString();\n\t\tprivate final String result = sb.toString();\n\n\t\tpublic void check() {\n\t\t\tassertThat(result).isEqualTo(\"abc\");\n\t\t\tassertThat(a).isEqualTo(\"a\");\n\t\t\tassertThat(b).isEqualTo(\"ab\");\n\t\t\tassertThat(c).isEqualTo(\"abc\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"TestCls() {\") // constructor removed\n\t\t\t\t.doesNotContain(\"String result;\")\n\t\t\t\t.containsOne(\"String result = this.sb.toString();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestFieldInitOrder2.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestFieldInitOrder2 extends SmaliTest {\n\n\t@SuppressWarnings({ \"SpellCheckingInspection\", \"StaticVariableName\" })\n\tpublic static class TestCls {\n\t\tstatic String ZPREFIX = \"SOME_\";\n\t\tprivate static final String VALUE = ZPREFIX + \"VALUE\";\n\n\t\tpublic void check() {\n\t\t\tassertThat(VALUE).isEqualTo(\"SOME_VALUE\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"private static final String VALUE = ZPREFIX + \\\"VALUE\\\";\");\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.runDecompiledAutoCheck(this)\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"private static final String VALUE = ZPREFIX + \\\"VALUE\\\";\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestFieldInitOrderStatic.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestFieldInitOrderStatic extends IntegrationTest {\n\n\t@SuppressWarnings(\"ConstantName\")\n\tpublic static class TestCls {\n\t\tprivate static final StringBuilder sb = new StringBuilder();\n\t\tprivate static final String a = sb.append(\"a\").toString();\n\t\tprivate static final String b = sb.append(\"b\").toString();\n\t\tprivate static final String c = sb.append(\"c\").toString();\n\t\tprivate static final String result = sb.toString();\n\n\t\tpublic void check() {\n\t\t\tassertThat(result).isEqualTo(\"abc\");\n\t\t\tassertThat(a).isEqualTo(\"a\");\n\t\t\tassertThat(b).isEqualTo(\"ab\");\n\t\t\tassertThat(c).isEqualTo(\"abc\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"static {\")\n\t\t\t\t.doesNotContain(\"String result;\")\n\t\t\t\t.containsOne(\"String result = sb.toString();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestFieldUsageMove.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestFieldUsageMove extends SmaliTest {\n\n\tpublic static class TestCls {\n\t\tpublic static void test(Object obj) {\n\t\t\tif (obj instanceof Boolean) {\n\t\t\t\tSystem.out.println(\"Boolean: \" + obj);\n\t\t\t}\n\t\t\tif (obj instanceof Float) {\n\t\t\t\tSystem.out.println(\"Float: \" + obj);\n\t\t\t}\n\t\t}\n\t}\n\n\t@TestWithProfiles(TestProfile.D8_J11)\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"System.out.println(\\\"Boolean: \\\" +\")\n\t\t\t\t.containsOne(\"System.out.println(\\\"Float: \\\" +\");\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"System.out.println(\\\"Boolean: \\\" +\")\n\t\t\t\t.containsOne(\"System.out.println(\\\"Float: \\\" +\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestFixClassAccessModifiers.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestFixClassAccessModifiers extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\t// class others.TestCls\n\t\tpublic Cls.InnerCls field;\n\n\t\t// class others.Cls\n\t\tpublic static class Cls {\n\t\t\tprivate static class InnerCls {\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tList<ClassNode> classes = loadFromSmaliFiles();\n\t\tassertThat(searchCls(classes, \"others.Cls\"))\n\t\t\t\t.code();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestFloatValue.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.within;\n\npublic class TestFloatValue extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic float[] test() {\n\t\t\tfloat[] fa = { 0.55f };\n\t\t\tfa[0] /= 2;\n\t\t\treturn fa;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test()[0]).isCloseTo(0.275f, within(0.0001f));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"1073741824\")\n\t\t\t\t.containsOne(\"0.55f\")\n\t\t\t\t.containsOne(\"fa[0] = fa[0] / 2.0f;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestIfInTry.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestIfInTry extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic File dir;\n\n\t\tpublic int test() {\n\t\t\ttry {\n\t\t\t\tint a = f();\n\t\t\t\tif (a != 0) {\n\t\t\t\t\treturn a;\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\t// skip\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tf();\n\t\t\t\treturn 1;\n\t\t\t} catch (IOException e) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\n\t\tprivate int f() throws IOException {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (a != 0) {\")\n\t\t\t\t.containsOne(\"} catch (Exception e) {\")\n\t\t\t\t.countString(2, \"try {\")\n\t\t\t\t.countString(3, \"f()\")\n\t\t\t\t.containsOne(\"return 1;\")\n\t\t\t\t.containsOne(\"} catch (IOException e\")\n\t\t\t\t.containsOne(\"return -1;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestIfTryInCatch.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestIfTryInCatch extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic Exception exception;\n\t\tprivate java.lang.Object data;\n\n\t\tpublic java.lang.Object test(final Object obj) {\n\t\t\texception = null;\n\t\t\ttry {\n\t\t\t\treturn f();\n\t\t\t} catch (Exception e) {\n\t\t\t\tif (a(e) && b(obj)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn f();\n\t\t\t\t\t} catch (Exception exc) {\n\t\t\t\t\t\te = exc;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tSystem.out.println(\"Exception\" + e);\n\t\t\t\texception = e;\n\t\t\t\treturn data;\n\t\t\t}\n\t\t}\n\n\t\tprivate static boolean b(Object obj) {\n\t\t\treturn obj == null;\n\t\t}\n\n\t\tprivate static boolean a(Exception e) {\n\t\t\treturn e instanceof RuntimeException;\n\t\t}\n\n\t\tprivate Object f() {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"try {\")\n\t\t\t\t.containsOne(\"if (\")\n\t\t\t\t.countString(2, \"return f();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestIncorrectFieldSignature.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestIncorrectFieldSignature extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public static boolean A;\")\n\t\t\t\t.containsOne(\"public static Boolean B;\")\n\t\t\t\t.countString(2, \"/* JADX INFO: Incorrect field signature:\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestIncorrectMethodSignature.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Issue #858.\n * Incorrect method signature change argument type and shift register numbers\n */\npublic class TestIncorrectMethodSignature extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tallowWarnInCode();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public TestIncorrectMethodSignature(String str) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestInlineVarArg.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInlineVarArg extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"f(\\\"a\\\", \\\"b\\\", \\\"c\\\");\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestInsnsBeforeSuper.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInsnsBeforeSuper extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic class A {\n\t\t\tpublic A(String s) {\n\t\t\t}\n\t\t}\n\n\t\tpublic class B extends A {\n\t\t\tpublic B(String str) {\n\t\t\t\tcheckNull(str);\n\t\t\t\tsuper(str);\n\t\t\t}\n\n\t\t\tpublic void checkNull(Object o) {\n\t\t\t\tif (o == null) {\n\t\t\t\t\tthrow new NullPointerException();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tallowWarnInCode();\n\t\tassertThat(getClassNodeFromSmaliFiles(\"B\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"checkNull(str);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestInsnsBeforeSuper2.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInsnsBeforeSuper2 extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic class TestInsnsBeforeSuper2 extends java.lang.Exception {\n\t\t\tprivate int mErrorType;\n\n\t\t\tpublic TestInsnsBeforeSuper2(java.lang.String r9, int r10) {\n\t\t\t\tr8 = this;\n\t\t\t\tr0 = r8\n\t\t\t\tr1 = r9\n\t\t\t\tr2 = r10\n\t\t\t\tr3 = r0\n\t\t\t\tr4 = r1\n\t\t\t\tr5 = r2\n\t\t\t\tr6 = r1\n\t\t\t\tr0.<init>(r6)\n\t\t\t\tr7 = 0\n\t\t\t\tr0.mErrorType = r7\n\t\t\t\tr0.mErrorType = r2\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"super(message);\")\n\t\t\t\t.containsOne(\"this.mErrorType = errorType;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestInsnsBeforeThis.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInsnsBeforeThis extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic class A {\n\t\t\tpublic A(String str) {\n\t\t\t\tcheckNull(str);\n\t\t\t\tthis(str.length());\n\t\t\t}\n\n\t\t\tpublic A(int i) {\n\t\t\t}\n\n\t\t\tpublic void checkNull(Object o) {\n\t\t\t\tif (o == null) {\n\t\t\t\t\tthrow new NullPointerException();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tallowWarnInCode();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"checkNull(str);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestInterfaceDefaultMethod.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInterfaceDefaultMethod extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\t@SuppressWarnings(\"UnnecessaryInterfaceModifier\")\n\t\tpublic interface ITest {\n\t\t\tvoid test1();\n\n\t\t\tdefault void test2() {\n\t\t\t}\n\n\t\t\tstatic void test3() {\n\t\t\t}\n\n\t\t\tabstract void test4();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"static default\")\n\t\t\t\t.doesNotContain(\"abstract\")\n\t\t\t\t.containsOne(\"void test1();\")\n\t\t\t\t.containsOne(\"default void test2() {\")\n\t\t\t\t.containsOne(\"static void test3() {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestInvalidExceptions.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\nclass TestInvalidExceptions extends SmaliTest {\n\n\t@Test\n\tvoid test() {\n\t\tallowWarnInCode();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"invalidException() throws FileNotFoundException {\")\n\t\t\t\t.containsOne(\"Byte code manipulation detected: skipped illegal throws declaration\")\n\t\t\t\t.removeBlockComments()\n\t\t\t\t.doesNotContain(\"String\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestInvalidExceptions2.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\nclass TestInvalidExceptions2 extends SmaliTest {\n\n\t@Test\n\tvoid test() {\n\t\tallowWarnInCode();\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"throwPossibleExceptionType() throws UnknownTypeHierarchyException {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestIssue13a.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.lang.reflect.Field;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestIssue13a extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static final String TAG = \"Parcel\";\n\t\tprivate static final Map<ClassLoader, Map<String, Parcelable.Creator<?>>> M_CREATORS = new HashMap<>();\n\n\t\t@SuppressWarnings({ \"unchecked\", \"ConstantConditions\", \"Java8MapApi\", \"rawtypes\" })\n\t\tpublic final <T extends Parcelable> T test(ClassLoader loader) {\n\t\t\tString name = readString();\n\t\t\tif (name == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tParcelable.Creator<T> creator;\n\t\t\tsynchronized (M_CREATORS) {\n\t\t\t\tMap<String, Parcelable.Creator<?>> map = M_CREATORS.get(loader);\n\t\t\t\tif (map == null) {\n\t\t\t\t\tmap = new HashMap<>();\n\t\t\t\t\tM_CREATORS.put(loader, map);\n\t\t\t\t}\n\t\t\t\tcreator = (Parcelable.Creator<T>) map.get(name);\n\t\t\t\tif (creator == null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tClass<?> c = loader == null ? Class.forName(name) : Class.forName(name, true, loader);\n\t\t\t\t\t\tField f = c.getField(\"CREATOR\");\n\t\t\t\t\t\tcreator = (Parcelable.Creator) f.get(null);\n\t\t\t\t\t} catch (IllegalAccessException e) {\n\t\t\t\t\t\tLog.e(TAG, '1' + name + \", e: \" + e);\n\t\t\t\t\t\tthrow new RuntimeException('2' + name);\n\t\t\t\t\t} catch (ClassNotFoundException e) {\n\t\t\t\t\t\tLog.e(TAG, '3' + name + \", e: \" + e);\n\t\t\t\t\t\tthrow new RuntimeException('4' + name);\n\t\t\t\t\t} catch (ClassCastException e) {\n\t\t\t\t\t\tthrow new RuntimeException('5' + name);\n\t\t\t\t\t} catch (NoSuchFieldException e) {\n\t\t\t\t\t\tthrow new RuntimeException('6' + name);\n\t\t\t\t\t}\n\t\t\t\t\tif (creator == null) {\n\t\t\t\t\t\tthrow new RuntimeException('7' + name);\n\t\t\t\t\t}\n\t\t\t\t\tmap.put(name, creator);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (creator instanceof Parcelable.ClassLoaderCreator<?>) {\n\t\t\t\treturn ((Parcelable.ClassLoaderCreator<T>) creator).createFromParcel(this, loader);\n\t\t\t}\n\t\t\treturn creator.createFromParcel(this);\n\t\t}\n\n\t\tprivate String readString() {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tprivate class Parcelable {\n\t\t\tpublic class Creator<T> {\n\t\t\t\tpublic T createFromParcel(TestCls testCls) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic class ClassLoaderCreator<T> extends Creator<T> {\n\t\t\t\tpublic T createFromParcel(TestCls testCls, ClassLoader loader) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate static class Log {\n\t\t\tpublic static void e(String tag, String s) {\n\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tString code = cls.getCode().toString();\n\n\t\tfor (int i = 1; i <= 7; i++) {\n\t\t\tassertThat(code).containsOne(\"'\" + i + '\\'');\n\t\t}\n\n\t\t// TODO: add additional checks\n\t\tassertThat(code).doesNotContain(\"Throwable\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestIssue13b.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.util.Properties;\nimport java.util.concurrent.CountDownLatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestIssue13b extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static final String PROPERTIES_FILE = \"\";\n\t\tprivate static final String TAG = \"\";\n\t\tprivate final CountDownLatch mInitializedLatch = new CountDownLatch(1);\n\t\tpublic int mC2KServerPort = 0;\n\t\tprivate String mSuplServerHost = \"\";\n\t\tpublic int mSuplServerPort = 0;\n\t\tprivate String mC2KServerHost = \"\";\n\n\t\tpublic TestCls() {\n\t\t\tProperties mProperties = new Properties();\n\t\t\ttry {\n\t\t\t\tFile file = new File(PROPERTIES_FILE);\n\t\t\t\tFileInputStream stream = new FileInputStream(file);\n\t\t\t\tmProperties.load(stream);\n\t\t\t\tstream.close();\n\n\t\t\t\tmSuplServerHost = mProperties.getProperty(\"SUPL_HOST\");\n\t\t\t\tString portString = mProperties.getProperty(\"SUPL_PORT\");\n\t\t\t\tif (mSuplServerHost != null && portString != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tmSuplServerPort = Integer.parseInt(portString);\n\t\t\t\t\t} catch (NumberFormatException e) {\n\t\t\t\t\t\tLog.e(TAG, \"unable to parse SUPL_PORT: \" + portString);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tmC2KServerHost = mProperties.getProperty(\"C2K_HOST\");\n\t\t\t\tportString = mProperties.getProperty(\"C2K_PORT\");\n\t\t\t\tif (mC2KServerHost != null && portString != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tmC2KServerPort = Integer.parseInt(portString);\n\t\t\t\t\t} catch (NumberFormatException e) {\n\t\t\t\t\t\tLog.e(TAG, \"unable to parse C2K_PORT: \" + portString);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (IOException e) {\n\t\t\t\tLog.e(TAG, \"Could not open GPS configuration file \" + PROPERTIES_FILE);\n\t\t\t}\n\n\t\t\tThread mThread = new Thread();\n\t\t\tmThread.start();\n\t\t\twhile (true) {\n\t\t\t\ttry {\n\t\t\t\t\tmInitializedLatch.await();\n\t\t\t\t\tbreak;\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\tThread.currentThread().interrupt();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate static class Log {\n\t\t\tpublic static void e(String tag, String s) {\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(4, \"} catch (\")\n\t\t\t\t.countString(3, \"Log.e(\")\n\t\t\t\t.containsOne(\"Thread.currentThread().interrupt();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestJavaDup2x2.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.RaungTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestJavaDup2x2 extends RaungTest {\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromRaung())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"dArr[0] = 127.5d;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestJavaDupInsn.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.BlockNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestJavaDupInsn extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate MethodNode mth;\n\t\tprivate BlockNode block;\n\t\tprivate SSAVar[] vars;\n\t\tprivate int[] versions;\n\n\t\tpublic SSAVar test(RegisterArg regArg) {\n\t\t\tint regNum = regArg.getRegNum();\n\t\t\tint version = versions[regNum]++;\n\t\t\tSSAVar ssaVar = mth.makeNewSVar(regNum, version, regArg);\n\t\t\tvars[regNum] = ssaVar;\n\t\t\treturn ssaVar;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestJavaJSR.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.RaungTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestJavaJSR extends RaungTest {\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromRaung())\n\t\t\t\t.code()\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"InputStream in = url.openStream();\",\n\t\t\t\t\t\t\"try {\",\n\t\t\t\t\t\tindent() + \"return call(in);\",\n\t\t\t\t\t\t\"} finally {\",\n\t\t\t\t\t\tindent() + \"in.close();\",\n\t\t\t\t\t\t\"}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestJavaSwap.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.RaungTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestJavaSwap extends RaungTest {\n\n\t@SuppressWarnings(\"StringBufferReplaceableByString\")\n\tpublic static class TestCls {\n\t\tprivate Iterable<String> field;\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tString string = String.valueOf(this.field);\n\t\t\treturn new StringBuilder(8 + String.valueOf(string).length())\n\t\t\t\t\t.append(\"concat(\").append(string).append(\")\")\n\t\t\t\t\t.toString();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testJava() {\n\t\tuseJavaInput();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code();\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tuseJavaInput();\n\t\tassertThat(getClassNodeFromRaung())\n\t\t\t\t.code();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestJsonOutput.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.api.JadxArgs.OutputFormatEnum.JSON;\n\npublic class TestJsonOutput extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate final String prefix = \"list: \";\n\n\t\tstatic {\n\t\t\tSystem.out.println(\"test\");\n\t\t}\n\n\t\tpublic void test(boolean b, List<String> list) {\n\t\t\tif (b) {\n\t\t\t\tSystem.out.println(prefix + list);\n\t\t\t}\n\t\t}\n\n\t\tpublic static class Inner implements Runnable {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tSystem.out.println(\"run\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\targs.setOutputFormat(JSON);\n\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"\\\"offset\\\": \\\"0x\")\n\t\t\t\t.containsOne(\"public static class Inner implements Runnable\");\n\t}\n\n\t@Test\n\tpublic void testFallback() {\n\t\tdisableCompilation();\n\t\tsetFallback();\n\t\targs.setOutputFormat(JSON);\n\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"\\\"offset\\\": \\\"0x\")\n\t\t\t\t.containsOne(\"public static class Inner implements java.lang.Runnable\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestLoopInTry.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestLoopInTry extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static boolean b = true;\n\n\t\tpublic int test() {\n\t\t\ttry {\n\t\t\t\tif (b) {\n\t\t\t\t\tthrow new Exception();\n\t\t\t\t}\n\t\t\t\twhile (f()) {\n\t\t\t\t\ts();\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tSystem.out.println(\"exception\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tprivate static void s() {\n\t\t}\n\n\t\tprivate static boolean f() {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"try {\")\n\t\t\t\t.containsOne(\"if (b) {\")\n\t\t\t\t.containsOne(\"throw new Exception();\")\n\t\t\t\t.containsOne(\"while (f()) {\")\n\t\t\t\t.containsOne(\"s();\")\n\t\t\t\t.containsOne(\"} catch (Exception e) {\")\n\t\t\t\t.containsOne(\"return 1;\")\n\t\t\t\t.containsOne(\"return 0;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestMethodParametersAttribute.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\nimport java.util.Arrays;\nimport java.util.stream.Collectors;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestMethodParametersAttribute extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic String test(String paramStr, final int number) {\n\t\t\treturn paramStr + number;\n\t\t}\n\n\t\tpublic String paramNames() throws NoSuchMethodException {\n\t\t\tMethod testMethod = TestCls.class.getMethod(\"test\", String.class, int.class);\n\t\t\treturn Arrays.stream(testMethod.getParameters())\n\t\t\t\t\t.map(Parameter::getName)\n\t\t\t\t\t.collect(Collectors.joining(\", \"));\n\t\t}\n\n\t\tpublic void check() throws NoSuchMethodException {\n\t\t\tassertThat(paramNames()).isEqualTo(\"paramStr, number\");\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.JAVA8, TestProfile.D8_J11 })\n\tpublic void test() {\n\t\tgetCompilerOptions().addArgument(\"-parameters\");\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public String test(String paramStr, final int number) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestMissingExceptions.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\nclass TestMissingExceptions extends SmaliTest {\n\n\t@Test\n\tvoid test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.countString(6, \"FileNotFoundException\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestMoveInline.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"CommentedOutCode\")\npublic class TestMoveInline extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic final void Y(int i) throws k {\n\t\t\tint i2 = 0;\n\t\t\twhile ((i & (-128)) != 0) {\n\t\t\t\tthis.h[i2] = (byte) ((i & 127) | 128);\n\t\t\t\ti >>>= 7;\n\t\t\t\ti2++;\n\t\t\t}\n\t\t\tbyte[] bArr = this.h;\n\t\t\tbArr[i2] = (byte) i;\n\t\t\tthis.a.k(bArr, 0, i2 + 1);\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t// check operations order\n\t\t\t\t.containsLines(3,\n\t\t\t\t\t\t\"i >>>= 7;\",\n\t\t\t\t\t\t\"i2++;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestMultipleNOPs.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\npublic class TestMultipleNOPs extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\n\t\t// expected no errors\n\t\tloadFromSmaliFiles();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestN21.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestN21 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"while (\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestNullInline.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestNullInline extends IntegrationTest {\n\n\t@SuppressWarnings({ \"RedundantCast\", \"DataFlowIssue\", \"unused\" })\n\tpublic static class TestCls {\n\t\tpublic static Long test(Double d1) {\n\t\t\tT1<T2, Byte> t1 = (T1<T2, Byte>) null;\n\t\t\treturn t1.t2.l;\n\t\t}\n\n\t\tstatic class T2 {\n\t\t\tpublic long l;\n\t\t}\n\n\t\tstatic class T1<H, P extends Byte> {\n\t\t\tpublic T2 t2;\n\n\t\t\tpublic T1(T2 t2) {\n\t\t\t\tthis.t2 = t2;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"Long.valueOf(t1.t2.l);\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestOverridePackagePrivateMethod.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestOverridePackagePrivateMethod extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\t-----------------------------------------------------------\n\t\tpackage test;\n\n\t\tpublic class A {\n\t\t\tvoid a() { // package-private\n\t\t\t}\n\t\t}\n\t\t-----------------------------------------------------------\n\t\tpackage test;\n\n\t\tpublic class B extends A {\n\t\t\t@Override // test.A\n\t\t\tpublic void a() {\n\t\t\t}\n\t\t}\n\t\t-----------------------------------------------------------\n\t\tpackage other;\n\n\t\timport test.A;\n\n\t\tpublic class C extends A {\n\t\t\t// No @Override here\n\t\t\tpublic void a() {\n\t\t\t}\n\t\t}\n\t\t-----------------------------------------------------------\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tcommonChecks();\n\t}\n\n\t@Test\n\tpublic void testDontChangeAccFlags() {\n\t\tgetArgs().setRespectBytecodeAccModifiers(true);\n\t\tcommonChecks();\n\t}\n\n\tprivate void commonChecks() {\n\t\tList<ClassNode> classes = loadFromSmaliFiles();\n\t\tassertThat(searchCls(classes, \"test.A\"))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"/* access modifiers changed\")\n\t\t\t\t.containsLine(1, \"void a() {\");\n\n\t\tassertThat(searchCls(classes, \"test.B\")).code().containsOne(\"@Override\");\n\t\tassertThat(searchCls(classes, \"other.C\")).code().doesNotContain(\"@Override\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestOverridePrivateMethod.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestOverridePrivateMethod extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static class BaseClass {\n\t\t\tprivate int a() {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\n\t\tpublic static class MyClass extends BaseClass {\n\t\t\tpublic int a() {\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(new MyClass().a()).isEqualTo(2);\n\t\t\tassertThat(new BaseClass().a()).isEqualTo(1);\n\t\t\t// TODO: assertThat(((BaseClass) new MyClass()).a()).isEqualTo(1);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"@Override\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestOverrideStaticMethod.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestOverrideStaticMethod extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static class BaseClass {\n\t\t\tpublic static int a() {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\n\t\tpublic static class MyClass extends BaseClass {\n\t\t\tpublic static int a() {\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(BaseClass.a()).isEqualTo(1);\n\t\t\tassertThat(MyClass.a()).isEqualTo(2);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"@Override\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestOverrideWithSameName.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"CommentedOutCode\")\npublic class TestOverrideWithSameName extends SmaliTest {\n\n\t//@formatter:off\n\t/*\n\t\tinterface A {\n\t\t\tB a();\n\t\t\tC a();\n\t\t}\n\n\t\tabstract class B implements A {\n\t\t\t@Override\n\t\t\tpublic C a() {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tpublic class C extends B {\n\t\t\t@Override\n\t\t\tpublic B a() {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t*/\n\t//@formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tList<ClassNode> clsNodes = loadFromSmaliFiles();\n\t\tassertThat(searchCls(clsNodes, \"test.A\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"C mo0a();\") // assume second method was renamed\n\t\t\t\t.doesNotContain(\"@Override\");\n\n\t\tClassNode bCls = searchCls(clsNodes, \"test.B\");\n\t\tassertThat(bCls)\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"C mo0a() {\")\n\t\t\t\t.containsOne(\"@Override\");\n\n\t\tassertThat(getMethod(bCls, \"a\").get(AType.METHOD_OVERRIDE).getOverrideList())\n\t\t\t\t.singleElement()\n\t\t\t\t.satisfies(mth -> assertThat(mth.getMethodInfo().getDeclClass().getShortName()).isEqualTo(\"A\"));\n\n\t\tClassNode cCls = searchCls(clsNodes, \"test.C\");\n\t\tassertThat(cCls)\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"B a() {\")\n\t\t\t\t.containsOne(\"@Override\");\n\n\t\tassertThat(getMethod(cCls, \"a\").get(AType.METHOD_OVERRIDE).getOverrideList())\n\t\t\t\t.singleElement()\n\t\t\t\t.satisfies(mth -> assertThat(mth.getMethodInfo().getDeclClass().getShortName()).isEqualTo(\"A\"));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestOverrideWithTwoBases.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestOverrideWithTwoBases extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic abstract static class BaseClass {\n\t\t\tpublic abstract int a();\n\t\t}\n\n\t\tpublic interface I {\n\t\t\tint a();\n\t\t}\n\n\t\tpublic static class Cls extends BaseClass implements I {\n\t\t\t@Override\n\t\t\tpublic int a() {\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"@Override\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestOverrideWithTwoBases2.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestOverrideWithTwoBases2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic interface I {\n\t\t\tint a();\n\t\t}\n\n\t\tpublic abstract static class BaseCls implements I {\n\t\t}\n\n\t\tpublic static class Cls extends BaseCls implements I {\n\t\t\t@Override\n\t\t\tpublic int a() {\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"@Override\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestPrimitiveCasts.java",
    "content": "package jadx.tests.integration.others;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestPrimitiveCasts extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test() {\n\t\t\tuseShort((short) 0);\n\t\t\tuseShort((short) getInt());\n\t\t\tuseByte((byte) 0);\n\t\t\tuseByte((byte) getInt());\n\t\t\tuseChar((char) 0);\n\t\t\tuseChar((char) getInt());\n\n\t\t\tuseShort((short) 0L);\n\t\t\tuseShort((short) getLong());\n\t\t\tuseByte((byte) 0L);\n\t\t\tuseByte((byte) getLong());\n\t\t\tuseChar((char) 0L);\n\t\t\tuseChar((char) getLong());\n\n\t\t\tuseShort((short) ' ');\n\t\t\tuseShort((short) getChar());\n\t\t\tuseByte((byte) ' ');\n\t\t\tuseByte((byte) getChar());\n\n\t\t\tuseInt((byte) 7);\n\t\t\tuseInt((char) ' ');\n\t\t\tuseInt(getChar());\n\t\t\tuseInt((int) 2L);\n\t\t\tuseInt((int) getLong());\n\t\t}\n\n\t\tprivate long getLong() {\n\t\t\treturn 1L;\n\t\t}\n\n\t\tprivate char getChar() {\n\t\t\treturn ' ';\n\t\t}\n\n\t\tprivate int getInt() {\n\t\t\treturn 1;\n\t\t}\n\n\t\tprivate void useChar(char c) {\n\t\t}\n\n\t\tprivate void useByte(byte b) {\n\t\t}\n\n\t\tprivate void useShort(short s) {\n\t\t}\n\n\t\tprivate void useInt(int i) {\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.JAVA8 })\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"(0)\")\n\t\t\t\t.doesNotContain(\") ((int) getLong())\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestPrimitiveCasts2.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n// Source: https://github.com/skylot/jadx/issues/1620\npublic class TestPrimitiveCasts2 extends IntegrationTest {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\tpublic static class TestCls {\n\t\tlong instanceCount;\n\n\t\t{\n\t\t\tfloat f = 50.231F;\n\t\t\tinstanceCount &= (long) f;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestRedundantBrackets.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestRedundantBrackets extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic boolean method(String str) {\n\t\t\treturn str.indexOf('a') != -1;\n\t\t}\n\n\t\tpublic int method2(Object obj) {\n\t\t\tif (obj instanceof String) {\n\t\t\t\treturn ((String) obj).length();\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tpublic int method3(int a, int b) {\n\t\t\tif (a + b < 10) {\n\t\t\t\treturn a;\n\t\t\t}\n\t\t\tif ((a & b) != 0) {\n\t\t\t\treturn a * b;\n\t\t\t}\n\t\t\treturn b;\n\t\t}\n\n\t\tpublic void method4(int num) {\n\t\t\tif (num == 4 || num == 6 || num == 8 || num == 10) {\n\t\t\t\tmethod2(null);\n\t\t\t}\n\t\t}\n\n\t\tpublic void method5(int[] a, int n) {\n\t\t\ta[1] = n * 2;\n\t\t\ta[n - 1] = 1;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"(-1)\")\n\t\t\t\t.doesNotContain(\"return;\")\n\t\t\t\t.contains(\"if (obj instanceof String) {\")\n\t\t\t\t.contains(\"return ((String) obj).length();\")\n\t\t\t\t.contains(\"a + b < 10\")\n\t\t\t\t.contains(\"(a & b) != 0\")\n\t\t\t\t.contains(\"if (num == 4 || num == 6 || num == 8 || num == 10)\")\n\t\t\t\t.contains(\"a[1] = n * 2;\")\n\t\t\t\t.contains(\"a[n - 1] = 1;\")\n\t\t\t\t.contains(\"public int method2(Object obj) {\")\n\t\t\t\t// argument type isn't changed to String\n\t\t\t\t// cast not eliminated\n\t\t\t\t.contains(\"((String) obj).length()\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestRedundantReturn.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\npublic class TestRedundantReturn extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test(int num) {\n\t\t\tif (num == 4) {\n\t\t\t\tfail(\"\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"return;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestReturnWrapping.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestReturnWrapping extends IntegrationTest {\n\tpublic static class TestCls {\n\n\t\tpublic static int f1(int arg0) {\n\t\t\tswitch (arg0) {\n\t\t\t\tcase 1:\n\t\t\t\t\treturn 255;\n\t\t\t}\n\t\t\treturn arg0 + 1;\n\t\t}\n\n\t\tpublic static Object f2(Object arg0, int arg1) {\n\t\t\tObject ret = null;\n\t\t\tint i = arg1;\n\t\t\tif (arg0 == null) {\n\t\t\t\treturn ret + Integer.toHexString(i);\n\t\t\t}\n\t\t\ti++;\n\t\t\ttry {\n\t\t\t\tret = new Object().getClass();\n\t\t\t} catch (Exception e) {\n\t\t\t\tret = \"Qwerty\";\n\t\t\t}\n\t\t\treturn i > 128 ? arg0.toString() + ret.toString() : i;\n\t\t}\n\n\t\tpublic static int f3(int arg0) {\n\t\t\twhile (arg0 > 10) {\n\t\t\t\tint abc = 951;\n\t\t\t\tif (arg0 == 255) {\n\t\t\t\t\treturn arg0 + 2;\n\t\t\t\t}\n\t\t\t\targ0 -= abc;\n\t\t\t}\n\t\t\treturn arg0;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return 255;\")\n\t\t\t\t.contains(\"return arg0 + 1;\").contains(\"return i > 128 ? arg0.toString() + ret.toString() : Integer.valueOf(i);\")\n\t\t\t\t.contains(\"return arg0 + 2;\")\n\t\t\t\t.contains(\"arg0 -= 951;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestShadowingSuperMember.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestShadowingSuperMember extends IntegrationTest {\n\tpublic static class TestCls {\n\t\tpublic static class C {\n\t\t\tpublic C(String s) {\n\t\t\t}\n\t\t}\n\n\t\tpublic static class A {\n\t\t\tpublic int a00;\n\n\t\t\tpublic A(String s) {\n\t\t\t}\n\t\t}\n\n\t\tpublic static class B extends A {\n\t\t\tpublic C a00;\n\n\t\t\tpublic B(String str) {\n\t\t\t\tsuper(str);\n\t\t\t}\n\n\t\t\tpublic int add(int b) {\n\t\t\t\treturn super.a00 + b;\n\t\t\t}\n\n\t\t\tpublic int sub(int b) {\n\t\t\t\treturn ((A) this).a00 - b;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tB b = new B(\"\");\n\t\t\t((A) b).a00 = 2;\n\t\t\tassertThat(b.add(3)).isEqualTo(5);\n\t\t\tassertThat(b.sub(3)).isEqualTo(-1);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return super.a00 + b;\")\n\t\t\t\t.containsOne(\"return super.a00 - b;\")\n\t\t\t\t.containsOne(\"((A) b).a00 = 2;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestStaticFieldsInit.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestStaticFieldsInit extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static final String S1 = \"1\";\n\t\tpublic static final String S2 = \"12\".substring(1);\n\t\tpublic static final String S3 = null;\n\t\tpublic static final String S4;\n\t\tpublic static final String S5 = \"5\";\n\t\tpublic static String s6 = \"6\";\n\n\t\tstatic {\n\t\t\tif (S5.equals(\"?\")) {\n\t\t\t\tS4 = \"?\";\n\t\t\t} else {\n\t\t\t\tS4 = \"4\";\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"public static final String S2 = null;\")\n\t\t\t\t.contains(\"public static final String S3 = null;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestStaticMethod.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestStaticMethod extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tstatic {\n\t\t\tf();\n\t\t}\n\n\t\tprivate static void f() {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"static {\")\n\t\t\t\t.contains(\"private static void f() {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestStringBuilderElimination.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestStringBuilderElimination extends IntegrationTest {\n\n\tpublic static class MyException extends Exception {\n\t\tprivate static final long serialVersionUID = 4245254480662372757L;\n\n\t\tpublic MyException(String str, Exception e) {\n\t\t\tsuper(\"msg:\" + str, e);\n\t\t}\n\n\t\tpublic void method(int k) {\n\t\t\tSystem.out.println(\"k=\" + k);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(MyException.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"MyException(String str, Exception e) {\")\n\t\t\t\t.contains(\"super(\\\"msg:\\\" + str, e);\")\n\t\t\t\t.doesNotContain(\"new StringBuilder\")\n\t\t\t\t.contains(\"System.out.println(\\\"k=\\\" + k);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestStringBuilderElimination2.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.visitors.SimplifyVisitor;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\n/**\n * Test the StringBuilder simplification part of {@link SimplifyVisitor}\n *\n * @author Jan Peter Stotz\n */\n@SuppressWarnings(\"StringBufferReplaceableByString\")\npublic class TestStringBuilderElimination2 extends IntegrationTest {\n\n\tpublic static class TestCls1 {\n\t\tpublic String test() {\n\t\t\treturn new StringBuilder(\"[init]\").append(\"a1\").append('c').append(2).append(0L).append(1.0f).append(2.0d).append(true)\n\t\t\t\t\t.toString();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test1() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls1.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return \\\"[init]a1c201.02.0true\\\";\");\n\t}\n\n\tpublic static class TestCls2 {\n\t\tpublic String test() {\n\t\t\t// A chain with non-final variables\n\t\t\tString sInit = \"[init]\";\n\t\t\tString s = \"a1\";\n\t\t\tchar c = 'c';\n\t\t\tint i = 1;\n\t\t\tlong l = 2;\n\t\t\tfloat f = 1.0f;\n\t\t\tdouble d = 2.0d;\n\t\t\tboolean b = true;\n\t\t\treturn new StringBuilder(sInit).append(s).append(c).append(i).append(l).append(f).append(d).append(b).toString();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls2.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return \\\"[init]a1c121.02.0true\\\";\");\n\t}\n\n\tpublic static class TestClsStringUtilsReverse {\n\n\t\t/**\n\t\t * Simplified version of org.apache.commons.lang3.StringUtils.reverse()\n\t\t */\n\t\tpublic static String reverse(final String str) {\n\t\t\treturn new StringBuilder(str).reverse().toString();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test3() {\n\t\tJadxAssertions.assertThat(getClassNode(TestClsStringUtilsReverse.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return new StringBuilder(str).reverse().toString();\");\n\t}\n\n\tpublic static class TestClsChainWithDelete {\n\t\tpublic String test() {\n\t\t\t// a chain we can't simplify\n\t\t\treturn new StringBuilder(\"[init]\").append(\"a1\").delete(1, 2).toString();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testChainWithDelete() {\n\t\tJadxAssertions.assertThat(getClassNode(TestClsChainWithDelete.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return new StringBuilder(\\\"[init]\\\").append(\\\"a1\\\").delete(1, 2).toString();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestStringBuilderElimination3.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestStringBuilderElimination3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static String test(String a) {\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tsb.append(\"result = \");\n\t\t\tsb.append(a);\n\t\t\treturn sb.toString();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return \\\"result = \\\" + a;\")\n\t\t\t\t.doesNotContain(\"new StringBuilder()\");\n\t}\n\n\tpublic static class TestClsNegative {\n\t\tprivate String f = \"first\";\n\n\t\tpublic String test() {\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tsb.append(\"before = \");\n\t\t\tsb.append(this.f);\n\t\t\tupdateF();\n\t\t\tsb.append(\", after = \");\n\t\t\tsb.append(this.f);\n\t\t\treturn sb.toString();\n\t\t}\n\n\t\tprivate void updateF() {\n\t\t\tthis.f = \"second\";\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test()).isEqualTo(\"before = first, after = second\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testNegative() {\n\t\tJadxAssertions.assertThat(getClassNode(TestClsNegative.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"return sb.toString();\")\n\t\t\t\t.contains(\"new StringBuilder()\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestStringBuilderElimination4Neg.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestStringBuilderElimination4Neg extends IntegrationTest {\n\n\tpublic static class TestCls<K, V> {\n\t\tprivate K k;\n\t\tprivate V v;\n\n\t\tpublic String test() {\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tsb.append(k);\n\t\t\tsb.append('=');\n\t\t\tsb.append(v);\n\t\t\treturn sb.toString();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"sb.append('=');\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestStringBuilderElimination5.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestStringBuilderElimination5 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\t@SuppressWarnings(\"StringConcatenationInLoop\")\n\t\tpublic static String test(long[] a) {\n\t\t\tString s = \"\";\n\t\t\tfinal char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };\n\t\t\tfor (int i = a.length - 1; i >= 0; i--) {\n\t\t\t\ts += hexChars[(int) (a[i] >>> 60) & 0x0f];\n\t\t\t\ts += hexChars[(int) (a[i] >>> 56) & 0x0f];\n\t\t\t\ts += hexChars[(int) (a[i] >>> 52) & 0x0f];\n\t\t\t\ts += hexChars[(int) (a[i] >>> 48) & 0x0f];\n\t\t\t\ts += hexChars[(int) (a[i] >>> 44) & 0x0f];\n\t\t\t\ts += hexChars[(int) (a[i] >>> 40) & 0x0f];\n\t\t\t\ts += hexChars[(int) (a[i] >>> 36) & 0x0f];\n\t\t\t\ts += hexChars[(int) (a[i] >>> 32) & 0x0f];\n\t\t\t\ts += hexChars[(int) (a[i] >>> 28) & 0x0f];\n\t\t\t\ts += hexChars[(int) (a[i] >>> 24) & 0x0f];\n\t\t\t\ts += hexChars[(int) (a[i] >>> 20) & 0x0f];\n\t\t\t\ts += hexChars[(int) (a[i] >>> 16) & 0x0f];\n\t\t\t\ts += hexChars[(int) (a[i] >>> 12) & 0x0f];\n\t\t\t\ts += hexChars[(int) (a[i] >>> 8) & 0x0f];\n\t\t\t\ts += hexChars[(int) (a[i] >>> 4) & 0x0f];\n\t\t\t\ts += hexChars[(int) (a[i]) & 0x0f];\n\t\t\t\ts += \" \";\n\t\t\t}\n\t\t\treturn s;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\".append(\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestStringConcatJava11.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.RaungTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestStringConcatJava11 extends RaungTest {\n\n\tpublic static class TestCls {\n\t\tpublic String test(final String s) {\n\t\t\treturn s + \"test\";\n\t\t}\n\n\t\t//@formatter:off\n\t\t/* Dynamic call looks like this:\n\t\tpublic String test(final String s) {\n\t\t\treturn java.lang.invoke.StringConcatFactory.makeConcatWithConstants(\n\t\t\t\t\tjava.lang.invoke.MethodHandles.lookup(),\n\t\t\t\t\t\"makeConcatWithConstants\",\n\t\t\t\t\tjava.lang.invoke.MethodType.fromMethodDescriptorString(\"(Ljava/lang/String;)Ljava/lang/String;\", this.getClass().getClassLoader()),\n\t\t\t\t\t\"\\u0001test\"\n\t\t\t).dynamicInvoker().invoke(s);\n\t\t}\n\t\t*/\n\t\t//@formatter:on\n\n\t\tpublic String test2(final String s) {\n\t\t\treturn s + \"test\" + s + 7;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromRaung())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return str + \\\"test\\\";\")\n\t\t\t\t.containsOne(\"return str + \\\"test\\\" + str + 7;\");\n\t}\n\n\t@Test\n\tpublic void testJava8() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return str + \\\"test\\\";\")\n\t\t\t\t.containsOneOf(\n\t\t\t\t\t\t\"return str + \\\"test\\\" + str + 7;\",\n\t\t\t\t\t\t\"return str + \\\"test\\\" + str + \\\"7\\\";\"); // dynamic concat add const to string recipe\n\t}\n\n\t@TestWithProfiles({ TestProfile.D8_J11, TestProfile.JAVA11 })\n\tpublic void testJava11() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return str + \\\"test\\\";\")\n\t\t\t\t.containsOne(\"return str + \\\"test\\\" + str + \\\"7\\\";\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestStringConcatWithoutResult.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestStringConcatWithoutResult extends IntegrationTest {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(TestStringConcatWithoutResult.class);\n\n\tpublic static class TestCls {\n\t\tpublic static final boolean LOG_DEBUG = false;\n\n\t\tpublic void test(int i) {\n\t\t\tString msg = \"Input arg value: \" + i;\n\t\t\tif (LOG_DEBUG) {\n\t\t\t\tLOG.debug(msg);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\" = \\\"Input arg value: \\\" + i;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestStringConstructor.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.nio.charset.StandardCharsets;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestStringConstructor extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic String tag = new String(new byte[] { 'a', 'b', 'c' });\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"abc\");\n\t}\n\n\tpublic static class TestCls2 {\n\t\tpublic String tag = new String(new byte[] { 'a', 'b', 'c' }, StandardCharsets.UTF_8);\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls2.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"new String(\\\"abc\\\".getBytes(), StandardCharsets.UTF_8)\");\n\t}\n\n\tpublic static class TestCls3 {\n\t\tpublic String tag = new String(new byte[] { 1, 2, 3, 'a', 'b', 'c' });\n\t}\n\n\t@Test\n\tpublic void test3() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls3.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"\\\\u0001\\\\u0002\\\\u0003abc\");\n\t}\n\n\tpublic static class TestCls4 {\n\t\tpublic String tag = new String(new char[] { 1, 2, 3, 'a', 'b', 'c' });\n\t}\n\n\t@Test\n\tpublic void test4() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls4.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"\\\\u0001\\\\u0002\\\\u0003abc\");\n\t}\n\n\tpublic static class TestCls5 {\n\t\tpublic String tag = new String(new char[] { 1, 2, 3, 'a', 'b' });\n\t}\n\n\t@Test\n\tpublic void test5() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls5.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"{1, 2, 3, 'a', 'b'}\");\n\t}\n\n\tpublic static class TestClsNegative {\n\t\tpublic String tag = new String();\n\t}\n\n\t@Test\n\tpublic void testNegative() {\n\t\tJadxAssertions.assertThat(getClassNode(TestClsNegative.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"tag = new String();\");\n\t}\n\n\tpublic static class TestClsNegative2 {\n\t\tpublic byte b = 32;\n\t\tpublic String tag = new String(new byte[] { 31, b });\n\t}\n\n\t@Test\n\tpublic void testNegative2() {\n\t\tJadxAssertions.assertThat(getClassNode(TestClsNegative2.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"tag = new String(new byte[]{31, this.b});\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestSuperLoop.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"CommentedOutCode\")\npublic class TestSuperLoop extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic class A extends B {\n\t\t\tpublic int a;\n\t\t}\n\n\t\tpublic class B extends A {\n\t\t\tpublic int b;\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tallowWarnInCode();\n\t\tdisableCompilation();\n\n\t\tList<ClassNode> clsList = loadFromSmaliFiles();\n\t\tassertThat(searchCls(clsList, \"A\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public class A extends B {\");\n\n\t\tassertThat(searchCls(clsList, \"B\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public class B extends A {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestSyntheticConstructor.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSyntheticConstructor extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic class Test {\n\t\t\tstatic {\n\t\t\t\tnew BuggyConstructor();\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmaliFiles(\"Test\"))\n\t\t\t\t.code()\n\t\t\t\t.containsLine(2, \"new BuggyConstructor();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestThrows.java",
    "content": "package jadx.tests.integration.others;\n\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestThrows extends IntegrationTest {\n\n\tpublic static class MissingThrowsTest extends Exception {\n\n\t\tprivate void throwCustomException() throws MissingThrowsTest {\n\t\t\tthrow new MissingThrowsTest();\n\t\t}\n\n\t\tprivate void throwException() throws Exception {\n\t\t\tthrow new Exception();\n\t\t}\n\n\t\tprivate void throwRuntimeException1() {\n\t\t\tthrow new RuntimeException();\n\t\t}\n\n\t\tprivate void throwRuntimeException2() {\n\t\t\tthrow new NullPointerException();\n\t\t}\n\n\t\tprivate void throwError() {\n\t\t\tthrow new Error();\n\t\t}\n\n\t\tprivate void throwError2() {\n\t\t\tthrow new OutOfMemoryError();\n\t\t}\n\n\t\t@SuppressWarnings(\"checkstyle:illegalThrows\")\n\t\tprivate void throwThrowable() throws Throwable {\n\t\t\tthrow new Throwable();\n\t\t}\n\n\t\tprivate void exceptionSource() throws FileNotFoundException {\n\t\t\tthrow new FileNotFoundException(\"\");\n\t\t}\n\n\t\tpublic void mergeThrownExceptions() throws IOException {\n\t\t\texceptionSource();\n\t\t}\n\n\t\tpublic void rethrowThrowable() {\n\t\t\ttry {\n\t\t\t} catch (Throwable t) {\n\t\t\t\tthrow t;\n\t\t\t}\n\t\t}\n\n\t\tpublic void doSomething1(int i) throws FileNotFoundException {\n\t\t\tif (i == 1) {\n\t\t\t\tdoSomething2(i);\n\t\t\t} else {\n\t\t\t\tdoSomething1(i);\n\t\t\t}\n\t\t}\n\n\t\tpublic void doSomething2(int i) throws FileNotFoundException {\n\t\t\tif (i == 1) {\n\t\t\t\texceptionSource();\n\t\t\t} else {\n\t\t\t\tdoSomething1(i);\n\t\t\t}\n\t\t}\n\n\t\tpublic int doSomething3(int i) throws IllegalArgumentException {\n\t\t\tif (i < 0) {\n\t\t\t\tthrow new IllegalArgumentException();\n\t\t\t}\n\t\t\treturn 1;\n\t\t}\n\n\t\tpublic void noThrownExceptions1(InputStream i1) {\n\t\t\ttry {\n\t\t\t\ti1.close();\n\t\t\t} catch (IOException ignore) {\n\t\t\t}\n\t\t}\n\n\t\tpublic void noThrownExceptions2() {\n\t\t\ttry {\n\t\t\t\tthrow new FileNotFoundException(\"\");\n\t\t\t} catch (IOException ignore) {\n\t\t\t}\n\t\t}\n\n\t\tpublic void noThrownExceptions3() {\n\t\t\tint i = doSomething3(0);\n\t\t\tSystem.out.print(i);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(MissingThrowsTest.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"throwCustomException() throws TestThrows$MissingThrowsTest {\")\n\t\t\t\t.containsOne(\"throwException() throws Exception {\")\n\t\t\t\t.containsOne(\"throwRuntimeException1() {\")\n\t\t\t\t.containsOne(\"throwRuntimeException2() {\")\n\t\t\t\t.containsOne(\"throwError() {\")\n\t\t\t\t.containsOne(\"throwError2() {\")\n\t\t\t\t.containsOne(\"throwThrowable() throws Throwable {\")\n\t\t\t\t.containsOne(\"exceptionSource() throws FileNotFoundException {\")\n\t\t\t\t.containsOne(\"mergeThrownExceptions() throws IOException {\")\n\t\t\t\t.containsOne(\"rethrowThrowable() {\")\n\t\t\t\t.containsOne(\"noThrownExceptions1(InputStream i1) {\")\n\t\t\t\t.containsOne(\"noThrownExceptions2() {\")\n\t\t\t\t.containsOne(\"noThrownExceptions3() {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestUsageApacheHttpClient.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestUsageApacheHttpClient extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpackage others;\n\t\timport org.apache.http.client.HttpClient;\n\n\t\tpublic class HttpClientTest {\n\t\t\tprivate HttpClient httpClient;\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tClassNode cls = getClassNodeFromSmali();\n\t\tassertThat(cls.root().getGradleInfoStorage().isUseApacheHttpLegacy()).isTrue();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestWrongCode.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestWrongCode extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\t@SuppressWarnings(\"null\")\n\t\tpublic int test() {\n\t\t\tint[] a = null;\n\t\t\treturn a.length;\n\t\t}\n\n\t\tpublic int test2(int a) {\n\t\t\tif (a == 0) {\n\t\t\t}\n\t\t\treturn a;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"return false.length;\")\n\t\t\t\t.containsOne(\"int[] a = null;\")\n\t\t\t\t.containsOne(\"return a.length;\")\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"if (a == 0) {\",\n\t\t\t\t\t\t\"}\",\n\t\t\t\t\t\t\"return a;\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/others/TestWrongCode2.java",
    "content": "package jadx.tests.integration.others;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestWrongCode2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\t@SuppressWarnings(\"ConstantConditions\")\n\t\tpublic String test() {\n\t\t\tA a = null;\n\t\t\ta.str = \"\";\n\t\t\treturn a.str;\n\t\t}\n\n\t\t@SuppressWarnings(\"ConstantConditions\")\n\t\tpublic int test2() {\n\t\t\tint[] a = null;\n\t\t\ta[1] = 2;\n\t\t\treturn a[0];\n\t\t}\n\n\t\t@SuppressWarnings({ \"ConstantConditions\", \"SynchronizationOnLocalVariableOrMethodParameter\" })\n\t\tpublic boolean test3() {\n\t\t\tA a = null;\n\t\t\tsynchronized (a) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tpublic boolean test4() {\n\t\t\treturn null instanceof A;\n\t\t}\n\n\t\t// everything is 'A' :)\n\t\t@SuppressWarnings({ \"MethodName\", \"LocalVariableName\" }) // ignore checkstyle\n\t\tpublic A A() {\n\t\t\tA A = A();\n\t\t\tA.A = A;\n\t\t\treturn A;\n\t\t}\n\n\t\t@SuppressWarnings(\"MemberName\")\n\t\tpublic static class A {\n\t\t\tpublic String str;\n\t\t\tpublic A A;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return a.str;\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/rename/TestAnonymousInline.java",
    "content": "package jadx.tests.integration.rename;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestAnonymousInline extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic Runnable test() {\n\t\t\treturn new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tSystem.out.println(\"run\");\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls).code()\n\t\t\t\t.containsOnlyOnce(\"return new Runnable() {\");\n\n\t\tassertThat(cls).reloadCode(this)\n\t\t\t\t.removeBlockComments() // remove comment about inlined class\n\t\t\t\t.containsOnlyOnce(\"return new Runnable() {\")\n\t\t\t\t.doesNotContain(\"AnonymousClass1\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/rename/TestConstReplace.java",
    "content": "package jadx.tests.integration.rename;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestConstReplace extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static final String CONST = \"SOME_CONST\";\n\n\t\tpublic String test() {\n\t\t\treturn CONST;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls).code()\n\t\t\t\t.containsOnlyOnce(\"return CONST;\");\n\n\t\tassertThat(cls).reloadCode(this)\n\t\t\t\t.containsOnlyOnce(\"return CONST;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/rename/TestFieldRenameFormat.java",
    "content": "package jadx.tests.integration.rename;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport com.google.gson.annotations.SerializedName;\n\nimport jadx.api.data.ICodeRename;\nimport jadx.api.data.IJavaNodeRef.RefType;\nimport jadx.api.data.impl.JadxCodeData;\nimport jadx.api.data.impl.JadxCodeRename;\nimport jadx.api.data.impl.JadxNodeRef;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestFieldRenameFormat extends IntegrationTest {\n\n\t@SuppressWarnings({ \"unused\", \"NonSerializableClassWithSerialVersionUID\" })\n\tpublic static class TestCls {\n\t\tprivate static final long serialVersionUID = -2619335455376089892L;\n\t\t@SerializedName(\"id\")\n\t\tprivate int b;\n\t\t@SerializedName(\"title\")\n\t\tprivate String c;\n\t\t@SerializedName(\"images\")\n\t\tprivate List<String> d;\n\t\t@SerializedName(\"authors\")\n\t\tprivate List<String> e;\n\t\t@SerializedName(\"description\")\n\t\tprivate String f;\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\n\t\tString baseClsId = TestCls.class.getName();\n\t\tList<ICodeRename> renames = Arrays.asList(\n\t\t\t\tfieldRename(baseClsId, \"b:I\", \"id\"),\n\t\t\t\tfieldRename(baseClsId, \"c:Ljava/lang/String;\", \"title\"),\n\t\t\t\tfieldRename(baseClsId, \"e:Ljava/util/List;\", \"authors\"));\n\n\t\tJadxCodeData codeData = new JadxCodeData();\n\t\tcodeData.setRenames(renames);\n\t\tgetArgs().setCodeData(codeData);\n\t\tgetArgs().setDeobfuscationOn(false);\n\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"private int id;\")\n\t\t\t\t.containsOne(\"private List<String> authors;\")\n\t\t\t\t.containsLines(1,\n\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\"/* JADX INFO: renamed from: c */\",\n\t\t\t\t\t\t\"@SerializedName(\\\"title\\\")\",\n\t\t\t\t\t\t\"private String title;\",\n\t\t\t\t\t\t\"\");\n\t}\n\n\tprivate static JadxCodeRename fieldRename(String baseClsId, String shortId, String id) {\n\t\treturn new JadxCodeRename(new JadxNodeRef(RefType.FIELD, baseClsId, shortId), id);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/rename/TestFieldWithGenericRename.java",
    "content": "package jadx.tests.integration.rename;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestFieldWithGenericRename extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tList<String> list;\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls).code()\n\t\t\t\t.containsOnlyOnce(\"List<String> list;\");\n\n\t\tcls.searchFieldByName(\"list\").getFieldInfo().setAlias(\"listFieldRenamed\");\n\n\t\tassertThat(cls).reloadCode(this)\n\t\t\t\t.containsOnlyOnce(\"List<String> listFieldRenamed;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/rename/TestRenameEnum.java",
    "content": "package jadx.tests.integration.rename;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestRenameEnum extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic enum A implements Runnable {\n\t\t\tONE {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tSystem.out.println(\"ONE\");\n\t\t\t\t}\n\t\t\t},\n\t\t\tTWO {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tSystem.out.println(\"TWO\");\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls).code()\n\t\t\t\t.containsOnlyOnce(\"public enum A \")\n\t\t\t\t.containsOnlyOnce(\"ONE {\");\n\n\t\tcls.getInnerClasses().get(0).getClassInfo().changeShortName(\"ARenamed\");\n\n\t\tassertThat(cls).reloadCode(this)\n\t\t\t\t.containsOnlyOnce(\"public enum ARenamed \")\n\t\t\t\t.containsOnlyOnce(\"ONE {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/rename/TestUserRenames.java",
    "content": "package jadx.tests.integration.rename;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.data.CodeRefType;\nimport jadx.api.data.ICodeRename;\nimport jadx.api.data.IJavaCodeRef;\nimport jadx.api.data.IJavaNodeRef.RefType;\nimport jadx.api.data.impl.JadxCodeData;\nimport jadx.api.data.impl.JadxCodeRef;\nimport jadx.api.data.impl.JadxCodeRename;\nimport jadx.api.data.impl.JadxNodeRef;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestUserRenames extends IntegrationTest {\n\n\t@SuppressWarnings({ \"FieldCanBeLocal\", \"FieldMayBeFinal\" })\n\tpublic static class TestCls {\n\t\tprivate int intField = 5;\n\n\t\tpublic static class A {\n\t\t}\n\n\t\tpublic int test(int x) {\n\t\t\tint y = x + \"test\".length();\n\t\t\tSystem.out.println(y);\n\t\t\tint z = y + 1;\n\t\t\tSystem.out.println(z);\n\t\t\treturn z;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tgetArgs().setDeobfuscationOn(false);\n\n\t\tList<ICodeRename> renames = new ArrayList<>();\n\t\tString baseClsId = TestCls.class.getName();\n\t\trenames.add(new JadxCodeRename(JadxNodeRef.forPkg(\"jadx.tests\"), \"renamedPkgTests\"));\n\t\trenames.add(new JadxCodeRename(JadxNodeRef.forPkg(\"jadx.tests.integration.rename\"), \"renamedPkgRename\"));\n\t\trenames.add(new JadxCodeRename(JadxNodeRef.forCls(baseClsId), \"RenamedTestCls\"));\n\t\trenames.add(new JadxCodeRename(JadxNodeRef.forCls(baseClsId + \"$A\"), \"RenamedInnerCls\"));\n\t\trenames.add(new JadxCodeRename(new JadxNodeRef(RefType.FIELD, baseClsId, \"intField:I\"), \"renamedField\"));\n\t\tJadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId, \"test(I)I\");\n\t\trenames.add(new JadxCodeRename(mthRef, \"renamedTestMth\"));\n\t\trenames.add(new JadxCodeRename(mthRef, new JadxCodeRef(CodeRefType.MTH_ARG, 0), \"renamedX\"));\n\t\tJadxCodeRef varDeclareRef = isJavaInput() ? JadxCodeRef.forVar(0, 1) : JadxCodeRef.forVar(0, 0);\n\t\trenames.add(new JadxCodeRename(mthRef, varDeclareRef, \"renamedY\"));\n\t\tIJavaCodeRef varUseRef = isJavaInput() ? JadxCodeRef.forVar(0, 4) : JadxCodeRef.forVar(1, 0);\n\t\trenames.add(new JadxCodeRename(mthRef, varUseRef, \"renamedZ\"));\n\n\t\tJadxCodeData codeData = new JadxCodeData();\n\t\tcodeData.setRenames(renames);\n\t\tgetArgs().setCodeData(codeData);\n\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls)\n\t\t\t\t.decompile()\n\t\t\t\t.checkCodeOffsets()\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"package jadx.renamedPkgTests.integration.renamedPkgRename;\")\n\t\t\t\t.containsOne(\"public class RenamedTestCls {\")\n\t\t\t\t.containsOne(\"private int renamedField\")\n\t\t\t\t.containsOne(\"public static class RenamedInnerCls {\")\n\t\t\t\t.containsOne(\"public int renamedTestMth(int renamedX) {\")\n\t\t\t\t.containsOne(\"int renamedY = renamedX + \\\"test\\\".length();\")\n\t\t\t\t.containsOne(\"int renamedZ = renamedY + 1;\")\n\t\t\t\t.containsOne(\"return renamedZ;\");\n\n\t\tString code = cls.getCode().getCodeStr();\n\t\tassertThat(cls)\n\t\t\t\t.reloadCode(this)\n\t\t\t\t.isEqualTo(code);\n\n\t\tICodeRename updVarRename = new JadxCodeRename(mthRef, varUseRef, \"anotherZ\");\n\t\tcodeData.setRenames(Collections.singletonList(updVarRename));\n\t\tjadxDecompiler.reloadCodeData();\n\t\tassertThat(cls)\n\t\t\t\t.reloadCode(this)\n\t\t\t\t.containsOne(\"int anotherZ = y + 1;\")\n\t\t\t\t.doesNotContain(\"int z\")\n\t\t\t\t.doesNotContain(\"int renamedZ\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/rename/TestUsingSourceFileName.java",
    "content": "package jadx.tests.integration.rename;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.args.UseSourceNameAsClassNameAlias;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestUsingSourceFileName extends SmaliTest {\n\n\t@Test\n\tpublic void testNeverUseSourceName() {\n\t\targs.setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias.NEVER);\n\t\tassertThat(searchCls(loadFromSmaliFiles(), \"b\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"class b {\");\n\t}\n\n\t@Test\n\tpublic void testIfBetterUseSourceName() {\n\t\targs.setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias.IF_BETTER);\n\t\tassertThat(searchCls(loadFromSmaliFiles(), \"b\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"class a {\");\n\t}\n\n\t@Test\n\tpublic void testAlwaysUseSourceName() {\n\t\targs.setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias.ALWAYS);\n\t\tassertThat(searchCls(loadFromSmaliFiles(), \"b\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"class a {\");\n\t}\n\n\t@Test\n\tpublic void testNeverUseSourceNameWithDeobf() {\n\t\targs.setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias.NEVER);\n\t\tenableDeobfuscation();\n\t\targs.setDeobfuscationMinLength(100); // rename everything\n\t\tassertThat(searchCls(loadFromSmaliFiles(), \"b\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"class C0000b {\")\n\t\t\t\t.containsOne(\"compiled from: a.java\");\n\t}\n\n\t@Test\n\tpublic void testIfBetterUseSourceNameWithDeobf() {\n\t\targs.setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias.IF_BETTER);\n\t\tenableDeobfuscation();\n\t\targs.setDeobfuscationMinLength(100); // rename everything\n\t\tassertThat(searchCls(loadFromSmaliFiles(), \"b\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"class a {\")\n\t\t\t\t.containsOne(\"compiled from: a.java\");\n\t}\n\n\t@Test\n\tpublic void testAlwaysUseSourceNameWithDeobf() {\n\t\targs.setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias.ALWAYS);\n\t\tenableDeobfuscation();\n\t\targs.setDeobfuscationMinLength(100); // rename everything\n\t\tassertThat(searchCls(loadFromSmaliFiles(), \"b\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"class a {\")\n\t\t\t\t.containsOne(\"compiled from: a.java\");\n\t}\n\n\t@Test\n\tpublic void testDeprecatedDontUseSourceName() {\n\t\t// noinspection deprecation\n\t\targs.setUseSourceNameAsClassAlias(false);\n\t\tassertThat(searchCls(loadFromSmaliFiles(), \"b\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"class b {\");\n\t}\n\n\t@Test\n\tpublic void testDeprecatedUseSourceName() {\n\t\t// noinspection deprecation\n\t\targs.setUseSourceNameAsClassAlias(true);\n\t\tassertThat(searchCls(loadFromSmaliFiles(), \"b\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"class a {\");\n\t}\n\n\t@Test\n\tpublic void testDeprecatedDontUseSourceNameWithDeobf() {\n\t\t// noinspection deprecation\n\t\targs.setUseSourceNameAsClassAlias(false);\n\t\tenableDeobfuscation();\n\t\targs.setDeobfuscationMinLength(100); // rename everything\n\t\tassertThat(searchCls(loadFromSmaliFiles(), \"b\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"class C0000b {\")\n\t\t\t\t.containsOne(\"compiled from: a.java\");\n\t}\n\n\t@Test\n\tpublic void testDeprecatedUseSourceNameWithDeobf() {\n\t\t// noinspection deprecation\n\t\targs.setUseSourceNameAsClassAlias(true);\n\t\tenableDeobfuscation();\n\t\targs.setDeobfuscationMinLength(100); // rename everything\n\t\tassertThat(searchCls(loadFromSmaliFiles(), \"b\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"class a {\")\n\t\t\t\t.containsOne(\"compiled from: a.java\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/special/TestPackageInfoSupport.java",
    "content": "package jadx.tests.integration.special;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestPackageInfoSupport extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tList<ClassNode> classes = loadFromSmaliFiles();\n\t\tassertThat(searchCls(classes, \"special.pkg1.package-info\"))\n\t\t\t\t.satisfies(cls -> assertThat(cls.getAlias()).isEqualTo(\"package-info\")) // shouldn't be renamed\n\t\t\t\t.code()\n\t\t\t\t.containsLines(\n\t\t\t\t\t\t\"@Deprecated\",\n\t\t\t\t\t\t\"package special.pkg1;\");\n\t\tassertThat(searchCls(classes, \"special.pkg2.package-info\"))\n\t\t\t\t.code()\n\t\t\t\t.containsLines(\n\t\t\t\t\t\t\"@ApiStatus.Internal\",\n\t\t\t\t\t\t\"package special.pkg2;\",\n\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\"import org.jetbrains.annotations.ApiStatus;\");\n\t\tassertThat(searchCls(classes, \"special.pkg3.package-info\"))\n\t\t\t\t.code().isEqualTo(\"\\npackage special.pkg3;\\n\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitch.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitch extends IntegrationTest {\n\tpublic static class TestCls {\n\t\tpublic String test(String str) {\n\t\t\tint len = str.length();\n\t\t\tStringBuilder sb = new StringBuilder(len);\n\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\tchar c = str.charAt(i);\n\t\t\t\tswitch (c) {\n\t\t\t\t\tcase '.':\n\t\t\t\t\tcase '/':\n\t\t\t\t\t\tsb.append('_');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase ']':\n\t\t\t\t\t\tsb.append('A');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase '?':\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tsb.append(c);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn sb.toString();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"case '/':\")\n\t\t\t\t.contains(indent(5) + \"break;\")\n\t\t\t\t.contains(indent(4) + \"default:\")\n\t\t\t\t.containsOne(\"i++\")\n\t\t\t\t.countString(4, \"break;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitch2.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitch2 extends IntegrationTest {\n\tpublic static class TestCls {\n\t\tboolean isLongtouchable;\n\t\tboolean isMultiTouchZoom;\n\t\tboolean isCanZoomIn;\n\t\tboolean isCanZoomOut;\n\t\tboolean isScrolling;\n\t\tfloat multiTouchZoomOldDist;\n\n\t\tpublic void test(int action) {\n\t\t\tswitch (action & 255) {\n\t\t\t\tcase 0:\n\t\t\t\t\tthis.isLongtouchable = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\tcase 6:\n\t\t\t\t\tif (this.isMultiTouchZoom) {\n\t\t\t\t\t\tthis.isMultiTouchZoom = false;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tif (this.isMultiTouchZoom) {\n\t\t\t\t\t\tfloat dist = multiTouchZoomOldDist;\n\t\t\t\t\t\tif (Math.abs(dist - this.multiTouchZoomOldDist) > 10.0f) {\n\t\t\t\t\t\t\tfloat scale = dist / this.multiTouchZoomOldDist;\n\t\t\t\t\t\t\tif ((scale > 1.0f && this.isCanZoomIn) || (scale < 1.0f && this.isCanZoomOut)) {\n\t\t\t\t\t\t\t\tthis.multiTouchZoomOldDist = dist;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 5:\n\t\t\t\t\tthis.multiTouchZoomOldDist = action;\n\t\t\t\t\tif (this.multiTouchZoomOldDist > 10.0f) {\n\t\t\t\t\t\tthis.isMultiTouchZoom = true;\n\t\t\t\t\t\tthis.isLongtouchable = false;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (this.isScrolling && action == 1) {\n\t\t\t\tthis.isScrolling = false;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(4, \"break;\")\n\t\t\t\t// .countString(2, \"return;\")\n\t\t\t\t// TODO: remove redundant returns\n\t\t\t\t.countString(4, \"return;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitch3.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitch3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate int i;\n\n\t\tvoid test(int a) {\n\t\t\tswitch (a) {\n\t\t\t\tcase 1:\n\t\t\t\t\ti = 1;\n\t\t\t\t\treturn;\n\t\t\t\tcase 2:\n\t\t\t\tcase 3:\n\t\t\t\t\ti = 2;\n\t\t\t\t\treturn;\n\t\t\t\tdefault:\n\t\t\t\t\ti = 4;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\ti = 5;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\ttest(1);\n\t\t\tassertThat(i).isEqualTo(1);\n\t\t\ttest(2);\n\t\t\tassertThat(i).isEqualTo(2);\n\t\t\ttest(3);\n\t\t\tassertThat(i).isEqualTo(2);\n\t\t\ttest(4);\n\t\t\tassertThat(i).isEqualTo(5);\n\t\t\ttest(10);\n\t\t\tassertThat(i).isEqualTo(5);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(3, \"break;\")\n\t\t\t\t.countString(0, \"return;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitch4.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitch4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\t@SuppressWarnings({ \"FallThrough\", \"unused\" })\n\t\tprivate static int parse(char[] ch, int off, int len) {\n\t\t\tint num = ch[off + len - 1] - '0';\n\t\t\tswitch (len) {\n\t\t\t\tcase 4:\n\t\t\t\t\tnum += (ch[off++] - '0') * 1000;\n\t\t\t\tcase 3:\n\t\t\t\t\tnum += (ch[off++] - '0') * 100;\n\t\t\t\tcase 2:\n\t\t\t\t\tnum += (ch[off] - '0') * 10;\n\t\t\t}\n\t\t\treturn num;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(parse(\"123\".toCharArray(), 0, 3)).isEqualTo(123);\n\t\t\tassertThat(parse(\"a=1234\".toCharArray(), 2, 4)).isEqualTo(1234);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"switch (\")\n\t\t\t\t.countString(3, \"case \")\n\t\t\t\t.doesNotContain(\"break\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchBreak.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchBreak extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic String test(int a) {\n\t\t\tString s = \"\";\n\t\t\tloop: while (a > 0) {\n\t\t\t\tswitch (a % 4) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\ts += \"1\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\ts += \"4\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 5:\n\t\t\t\t\t\ts += \"+\";\n\t\t\t\t\t\tbreak loop;\n\t\t\t\t}\n\t\t\t\ts += \"-\";\n\t\t\t\ta--;\n\t\t\t}\n\t\t\treturn s;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(9)).isEqualTo(\"1--4--1--4--1-\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"switch (a % 4) {\")\n\t\t\t\t.countString(4, \"case \")\n\t\t\t\t.countString(2, \"break;\")\n\t\t\t\t.doesNotContain(\"default:\")\n\t\t\t\t// TODO finish break with label from switch\n\t\t\t\t.containsOne(\"return s + \\\"+\\\";\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchBreak2.java",
    "content": "package jadx.tests.integration.switches;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchBreak2 extends IntegrationTest {\n\n\t@SuppressWarnings(\"SwitchStatementWithTooFewBranches\")\n\tpublic static class TestCls {\n\t\tprivate int value;\n\n\t\tpublic void test(int i, boolean b1, boolean b2) {\n\t\t\tsetValue(-1);\n\t\t\tswitch (i) {\n\t\t\t\tcase 0:\n\t\t\t\t\tif (b1 && b2) {\n\t\t\t\t\t\tsetValue(1);\n\t\t\t\t\t\t// no break here;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsetValue(2);\n\t\t\t\t\t\t// no break here;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tsetValue(0);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tprivate void setValue(int value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\ttest(0, true, true);\n\t\t\tassertThat(value).isEqualTo(1);\n\t\t\ttest(0, true, false);\n\t\t\tassertThat(value).isEqualTo(2);\n\t\t\ttest(1, true, true);\n\t\t\tassertThat(value).isEqualTo(0);\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.JAVA11, TestProfile.D8_J11 })\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"break;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchBreak3.java",
    "content": "package jadx.tests.integration.switches;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchBreak3 extends IntegrationTest {\n\n\t@SuppressWarnings(\"SwitchStatementWithTooFewBranches\")\n\tpublic static class TestCls {\n\t\tprivate int value;\n\n\t\tpublic void test(int i, boolean b1, boolean b2, boolean b3) {\n\t\t\tsetValue(-1);\n\t\t\tswitch (i) {\n\t\t\t\tcase 0:\n\t\t\t\t\tif (b1 == b2) {\n\t\t\t\t\t\tsetValue(1);\n\t\t\t\t\t\t// no break here;\n\t\t\t\t\t} else if (b1 == b3) {\n\t\t\t\t\t\tsetValue(2);\n\t\t\t\t\t\t// no break here;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tsetValue(0);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tprivate void setValue(int value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\ttest(0, true, true, true);\n\t\t\tassertThat(value).isEqualTo(1);\n\t\t\ttest(0, true, false, true);\n\t\t\tassertThat(value).isEqualTo(2);\n\t\t\ttest(1, true, true, true);\n\t\t\tassertThat(value).isEqualTo(0);\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.JAVA11, TestProfile.D8_J11 })\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"break;\")\n\t\t\t\t.containsOne(\"} else if (\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchBreak4.java",
    "content": "package jadx.tests.integration.switches;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchBreak4 extends IntegrationTest {\n\n\t@SuppressWarnings(\"SwitchStatementWithTooFewBranches\")\n\tpublic static class TestCls {\n\t\tprivate int value;\n\n\t\tpublic void test(int i, boolean b1, boolean b2, boolean b3) {\n\t\t\tsetValue(-1);\n\t\t\tswitch (i) {\n\t\t\t\tcase 0:\n\t\t\t\t\tif (b1 == b2) {\n\t\t\t\t\t\tsetValue(1);\n\t\t\t\t\t} else if (b1 == b3) {\n\t\t\t\t\t\tsetValue(2);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsetValue(3);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tsetValue(0);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tprivate void setValue(int value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\ttest(0, true, true, true);\n\t\t\tassertThat(value).isEqualTo(1);\n\t\t\ttest(0, true, false, true);\n\t\t\tassertThat(value).isEqualTo(2);\n\t\t\ttest(0, true, false, false);\n\t\t\tassertThat(value).isEqualTo(3);\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.JAVA11, TestProfile.D8_J11 })\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"break;\")\n\t\t\t\t.containsOne(\"} else if (\")\n\t\t\t\t.containsOne(\"} else {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchContinue.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchContinue extends IntegrationTest {\n\n\t@SuppressWarnings({ \"StringConcatenationInLoop\", \"DataFlowIssue\" })\n\tpublic static class TestCls {\n\t\tpublic String test(int a) {\n\t\t\tString s = \"\";\n\t\t\twhile (a > 0) {\n\t\t\t\tswitch (a % 4) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\ts += \"1\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\ts += \"4\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 5:\n\t\t\t\t\t\ta -= 2;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ts += \"-\";\n\t\t\t\ta--;\n\t\t\t}\n\t\t\treturn s;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"switch (a % 4) {\")\n\t\t\t\t.countString(4, \"case \")\n\t\t\t\t.countString(2, \"break;\")\n\t\t\t\t.containsOne(\"a -= 2;\")\n\t\t\t\t.containsOne(\"continue;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchFallThrough.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchFallThrough extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int r;\n\n\t\t@SuppressWarnings(\"fallthrough\")\n\t\tpublic void test(int a) {\n\t\t\tint i = 10;\n\t\t\tswitch (a) {\n\t\t\t\tcase 1:\n\t\t\t\t\ti = 1000;\n\t\t\t\t\t// fallthrough\n\t\t\t\tcase 2:\n\t\t\t\t\tr = i;\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tr = -1;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tr *= 2;\n\t\t\tSystem.out.println(\"in: \" + a + \", out: \" + r);\n\t\t}\n\n\t\tpublic int testWrap(int a) {\n\t\t\tr = 0;\n\t\t\ttest(a);\n\t\t\treturn r;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(testWrap(1)).isEqualTo(2000);\n\t\t\tassertThat(testWrap(2)).isEqualTo(20);\n\t\t\tassertThat(testWrap(0)).isEqualTo(-2);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"switch (a) {\")\n\t\t\t\t.containsOne(\"r = i;\")\n\t\t\t\t.containsOne(\"r = -1;\")\n\t\t\t\t.countString(2, \"break;\");\n\t\t// code correctness checks done in 'check' method\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchInLoop.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestSwitchInLoop extends IntegrationTest {\n\tpublic static class TestCls {\n\t\tpublic int test(int k) {\n\t\t\tint a = 0;\n\t\t\twhile (true) {\n\t\t\t\tswitch (k) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn a;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\ta++;\n\t\t\t\t\t\tk >>= 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(1)).isEqualTo(1);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"switch (k) {\")\n\t\t\t\t.containsOne(\"case 0:\")\n\t\t\t\t.containsOne(\"return a;\")\n\t\t\t\t.containsOne(\"default:\")\n\t\t\t\t.containsOne(\"a++;\")\n\t\t\t\t.containsOne(\"k >>= 1;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchInLoop2.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchInLoop2 extends IntegrationTest {\n\tpublic static class TestCls {\n\t\tpublic boolean test() {\n\t\t\twhile (true) {\n\t\t\t\tswitch (call()) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate int call() {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"while (true) {\")\n\t\t\t\t.containsOne(\"switch (call()) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchInLoop3.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchInLoop3 extends IntegrationTest {\n\n\t@SuppressWarnings(\"SwitchStatementWithTooFewBranches\")\n\tpublic static class TestCls {\n\t\tpublic int test(int k) {\n\t\t\tint a = 0;\n\t\t\twhile (true) {\n\t\t\t\tint x = 0; // keep this: force to generate the necessary CFG\n\t\t\t\tswitch (k) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn a;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\ta++;\n\t\t\t\t\t\tk >>= 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(1)).isEqualTo(1);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLines(3,\n\t\t\t\t\t\t\"switch (k) {\",\n\t\t\t\t\t\tindent() + \"case 0:\",\n\t\t\t\t\t\tindent(2) + \"return a;\",\n\t\t\t\t\t\tindent() + \"default:\",\n\t\t\t\t\t\tindent(2) + \"a++;\",\n\t\t\t\t\t\tindent(2) + \"k >>= 1;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchInLoop4.java",
    "content": "package jadx.tests.integration.switches;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchInLoop4 extends IntegrationTest {\n\n\t@SuppressWarnings(\"SwitchStatementWithTooFewBranches\")\n\tpublic static class TestCls {\n\t\tprivate static boolean test(String s, int start) {\n\t\t\tboolean foundSeparator = false;\n\t\t\tfor (int i = start; i < s.length(); i++) {\n\t\t\t\tchar c = s.charAt(i);\n\t\t\t\tswitch (c) {\n\t\t\t\t\tcase '.':\n\t\t\t\t\t\tfoundSeparator = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (foundSeparator) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn foundSeparator;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(\"a.b\", 0)).isTrue();\n\t\t\tassertThat(test(\"abc\", 1)).isFalse();\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.D8_J11, TestProfile.JAVA11 })\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"switch (c) {\")\n\t\t\t\t.containsOne(\"break;\"); // allow replacing second 'break' with 'return'\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchInLoop5.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchInLoop5 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static int test(int r) {\n\t\t\tint i;\n\t\t\twhile (true) {\n\t\t\t\tswitch (r) {\n\t\t\t\t\tcase 42:\n\t\t\t\t\t\ti = 32;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 52:\n\t\t\t\t\t\ti = 42;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tSystem.out.println(\"Default switch case\");\n\t\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tr = i;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"default:\")\n\t\t\t\t.containsOne(\"System.out.println(\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchInLoop6.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchInLoop6 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate void test() throws Exception {\n\t\t\twhile (true) {\n\t\t\t\tint n = getN();\n\t\t\t\tswitch (n) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\tn1();\n\t\t\t\t\t\treturn;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\tn2();\n\t\t\t\t\t\tif (getN() == 3) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\tn3();\n\t\t\t\t\t\treturn;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\tn4();\n\t\t\t\t\t\treturn;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow new Exception();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Output below:\n\t\t// @formatter:off\n\t\t/*\n\t\t\tpublic void function() throws Exception {\n\t\t\t\tdo {\n\t\t\t\t\tswitch (getN()) {\n\t\t\t\t\t\tcase 1:\n\t\t\t\t\t\t\tn1();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\tcase 2:\n\t\t\t\t\t\t\tn2();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 3:\n\t\t\t\t\t\t\tn3();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\tcase 4:\n\t\t\t\t\t\t\tn4();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tthrow new Exception();\n\t\t\t\t\t}\n\t\t\t\t} while (getN() != 3);\n\t\t\t}\n\t\t*/\n\t\t// @formatter:on\n\n\t\tvoid n1() {\n\t\t}\n\n\t\tvoid n2() {\n\t\t}\n\n\t\tvoid n3() {\n\t\t}\n\n\t\tvoid n4() {\n\t\t}\n\n\t\tprivate int getN() {\n\t\t\tdouble i = Math.random();\n\t\t\tif (i < 0.25) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif (i < 0.5) {\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t\tif (i < 0.75) {\n\t\t\t\treturn 3;\n\t\t\t}\n\t\t\tif (i < 1.0) {\n\t\t\t\treturn 4;\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tallowWarnInCode();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"switch (n) {\")\n\t\t\t\t.containsOne(\"case 1:\")\n\t\t\t\t.containsOne(\"case 2:\")\n\t\t\t\t.containsOne(\"case 3:\")\n\t\t\t\t.containsOne(\"case 4:\")\n\t\t\t\t.containsOne(\"do {\")\n\t\t\t\t.containsOne(\"while (getN() != 3)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchInLoop7.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchInLoop7 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate void test() {\n\t\t\tint i = 0;\n\t\t\tint n = getN();\n\t\t\twhile (true) {\n\t\t\t\tif (i > n) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t\tif (n == 5) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tswitch (n) {\n\t\t\t\t\tcase 0: {\n\t\t\t\t\t\tif (n != 1) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\ti++;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Output below:\n\t\t// @formatter:off\n\t\t/*\n\t\t\tpublic void function() {\n\t\t\t\tint i = 0;\n\t\t\t\tint n = getN();\n\t\t\t\twhile (i <= n) {\n\t\t\t\t\ti++;\n\t\t\t\t\tif (n != 5) {\n\t\t\t\t\t\tswitch (n) {\n\t\t\t\t\t\t\tcase 0:\n\t\t\t\t\t\t\t\tif (n == 1) {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase 1:\n\t\t\t\t\t\t\t\ti++;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t*/\n\t\t// @formatter:on\n\n\t\tprivate int getN() {\n\t\t\tdouble i = Math.random();\n\t\t\tif (i < 0.25) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif (i < 0.5) {\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t\tif (i < 0.75) {\n\t\t\t\treturn 3;\n\t\t\t}\n\t\t\tif (i < 1.0) {\n\t\t\t\treturn 4;\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\t// Checks that the redundant default continue case is not recovered\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"switch (n) {\")\n\t\t\t\t.containsOne(\"case 0:\")\n\t\t\t\t.containsOne(\"case 1:\")\n\t\t\t\t.containsOne(\"while (\")\n\t\t\t\t.doesNotContain(\"default\")\n\t\t\t\t.doesNotContain(\"contine\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchInLoop8.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchInLoop8 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate void test() {\n\t\t\tint i = 0;\n\t\t\tint n = getN();\n\t\t\twhile (true) {\n\t\t\t\tif (i > n) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t\tif (n == 5) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tswitch (n) {\n\t\t\t\t\tcase 0: {\n\t\t\t\t\t\tif (n != 1) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\ti++;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (i < 2) {\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tprivate int getN() {\n\t\t\tdouble i = Math.random();\n\t\t\tif (i < 0.25) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif (i < 0.5) {\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t\tif (i < 0.75) {\n\t\t\t\treturn 3;\n\t\t\t}\n\t\t\tif (i < 1.0) {\n\t\t\t\treturn 4;\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\t// Checks that the default continue case is not removed\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"switch (n) {\")\n\t\t\t\t.containsOne(\"case 0:\")\n\t\t\t\t.containsOne(\"case 1:\")\n\t\t\t\t.containsOne(\"while (\")\n\t\t\t\t.containsOne(\"default\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchInLoop9.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchInLoop9 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate void test() {\n\t\t\tint i = 0;\n\t\t\tint n = getN();\n\t\t\twhile (true) {\n\t\t\t\tif (i > n) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t\tif (n == 5) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tswitch (n) {\n\t\t\t\t\tcase 0: {\n\t\t\t\t\t\tif (n != 1) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\ti++;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (i < 2) {\n\t\t\t\t\ti += 327;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tprivate int getN() {\n\t\t\tdouble i = Math.random();\n\t\t\tif (i < 0.25) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif (i < 0.5) {\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t\tif (i < 0.75) {\n\t\t\t\treturn 3;\n\t\t\t}\n\t\t\tif (i < 1.0) {\n\t\t\t\treturn 4;\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\t// Checks that the work after the switch is recovered only once\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"switch (n) {\")\n\t\t\t\t.containsOne(\"case 0:\")\n\t\t\t\t.containsOne(\"case 1:\")\n\t\t\t\t.containsOne(\"while (\")\n\t\t\t\t.containsOne(\"default\")\n\t\t\t\t.containsOne(\"327\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchLabels.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchLabels extends IntegrationTest {\n\tpublic static class TestCls {\n\t\tpublic static final int CONST_ABC = 0xABC;\n\t\tpublic static final int CONST_CDE = 0xCDE;\n\n\t\tpublic static class Inner {\n\t\t\tprivate static final int CONST_CDE_PRIVATE = 0xCDE;\n\n\t\t\tpublic int f1(int arg0) {\n\t\t\t\tswitch (arg0) {\n\t\t\t\t\tcase CONST_CDE_PRIVATE:\n\t\t\t\t\t\treturn CONST_ABC;\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\n\t\tpublic static int f1(int arg0) {\n\t\t\tswitch (arg0) {\n\t\t\t\tcase CONST_ABC:\n\t\t\t\t\treturn CONST_CDE;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"case CONST_ABC\")\n\t\t\t\t.contains(\"return CONST_CDE;\")\n\t\t\t\t.doesNotContain(\"case CONST_CDE_PRIVATE\")\n\t\t\t\t.contains(\".CONST_ABC;\");\n\t}\n\n\t@Test\n\tpublic void testWithDisabledConstReplace() {\n\t\tgetArgs().setReplaceConsts(false);\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"case CONST_ABC\")\n\t\t\t\t.contains(\"case 2748\")\n\t\t\t\t.doesNotContain(\"return CONST_CDE;\")\n\t\t\t\t.contains(\"return 3294;\")\n\t\t\t\t.doesNotContain(\"case CONST_CDE_PRIVATE\")\n\t\t\t\t.doesNotContain(\".CONST_ABC;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchNoDefault.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchNoDefault extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test(int a) {\n\t\t\tString s = null;\n\t\t\tswitch (a) {\n\t\t\t\tcase 1:\n\t\t\t\t\ts = \"1\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\ts = \"2\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\ts = \"3\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\ts = \"4\";\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tSystem.out.println(s);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(4, \"break;\")\n\t\t\t\t.containsOne(\"System.out.println(s);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchOverStrings.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchOverStrings extends IntegrationTest {\n\n\t/**\n\t * Strings 'frewhyh', 'phgafkp' and 'ucguedt' have same hash code.\n\t */\n\tpublic static class TestCls {\n\n\t\tpublic int test(String str) {\n\t\t\tswitch (str) {\n\t\t\t\tcase \"frewhyh\":\n\t\t\t\t\treturn 1;\n\t\t\t\tcase \"phgafkp\":\n\t\t\t\t\treturn 2;\n\t\t\t\tcase \"test\":\n\t\t\t\tcase \"test2\":\n\t\t\t\t\treturn 3;\n\t\t\t\tcase \"other\":\n\t\t\t\t\treturn 4;\n\t\t\t\tdefault:\n\t\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(\"frewhyh\")).isEqualTo(1);\n\t\t\tassertThat(test(\"phgafkp\")).isEqualTo(2);\n\t\t\tassertThat(test(\"test\")).isEqualTo(3);\n\t\t\tassertThat(test(\"test2\")).isEqualTo(3);\n\t\t\tassertThat(test(\"other\")).isEqualTo(4);\n\t\t\tassertThat(test(\"unknown\")).isEqualTo(0);\n\t\t\tassertThat(test(\"ucguedt\")).isEqualTo(0);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"case -603257287:\")\n\t\t\t\t.doesNotContain(\"c = \")\n\t\t\t\t.doesNotContainSubsequence(\"default:\", \"case \")\n\t\t\t\t.containsOne(\"case \\\"frewhyh\\\":\")\n\t\t\t\t.countString(5, \"return \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchOverStrings2.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchOverStrings2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic int test(String str) {\n\t\t\tswitch (str) {\n\t\t\t\tcase \"branch1\":\n\t\t\t\tcase \"branch2\":\n\t\t\t\t\treturn 1;\n\t\t\t\tcase \"branch3\":\n\t\t\t\tcase \"branch4\":\n\t\t\t\tdefault:\n\t\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(\"branch1\")).isEqualTo(1);\n\t\t\tassertThat(test(\"branch2\")).isEqualTo(1);\n\t\t\tassertThat(test(\"branch3\")).isEqualTo(0);\n\t\t\tassertThat(test(\"branch4\")).isEqualTo(0);\n\t\t\tassertThat(test(\"other\")).isEqualTo(0);\n\t\t\tassertThat(test(\"other2\")).isEqualTo(0);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(4, \"case \")\n\t\t\t\t.countString(1, \"default:\")\n\t\t\t\t.countString(2, \"return \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchOverStrings3.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchOverStrings3 extends IntegrationTest {\n\n\t@SuppressWarnings(\"SwitchStatementWithTooFewBranches\")\n\tpublic static class TestCls {\n\t\tpublic int test(String v) {\n\t\t\tswitch (v) {\n\t\t\t\tcase \"a\":\n\t\t\t\t\treturn 1;\n\t\t\t\tdefault:\n\t\t\t\t\tswitch (v) {\n\t\t\t\t\t\tcase \"b\":\n\t\t\t\t\t\t\treturn 2;\n\t\t\t\t\t\tcase \"c\":\n\t\t\t\t\t\t\treturn 3;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn 4;\n\t\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(\"a\")).isEqualTo(1);\n\t\t\tassertThat(test(\"b\")).isEqualTo(2);\n\t\t\tassertThat(test(\"c\")).isEqualTo(3);\n\t\t\tassertThat(test(\"d\")).isEqualTo(4);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(3, \"case \")\n\t\t\t\t.countString(2, \"default:\")\n\t\t\t\t.countString(4, \"return \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchReturnFromCase.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestSwitchReturnFromCase extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test(int a) {\n\t\t\tif (a > 1000) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString s = null;\n\t\t\tswitch (a % 10) {\n\t\t\t\tcase 1:\n\t\t\t\t\ts = \"1\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\ts = \"2\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\tcase 4:\n\t\t\t\t\ts = \"4\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 5:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 6:\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (s == null) {\n\t\t\t\ts = \"5\";\n\t\t\t}\n\t\t\tSystem.out.println(s);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"switch (a % 10) {\")\n\t\t\t\t// case 5: removed\n\t\t\t\t.countString(5, \"case \")\n\t\t\t\t.countString(3, \"break;\")\n\t\t\t\t.containsOne(\"s = \\\"1\\\";\")\n\t\t\t\t.containsOne(\"s = \\\"2\\\";\")\n\t\t\t\t.containsOne(\"s = \\\"4\\\";\")\n\t\t\t\t.containsOne(\"s = \\\"5\\\";\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchReturnFromCase2.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestSwitchReturnFromCase2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic boolean test(int a) {\n\t\t\tswitch (a % 4) {\n\t\t\t\tcase 2:\n\t\t\t\tcase 3:\n\t\t\t\t\tif (a == 2) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(2)).isTrue();\n\t\t\tassertThat(test(3)).isTrue();\n\t\t\tassertThat(test(15)).isTrue();\n\t\t\tassertThat(test(1)).isFalse();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"switch (a % 4) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchSimple.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSwitchSimple extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test(int a) {\n\t\t\tString s = null;\n\t\t\tswitch (a % 4) {\n\t\t\t\tcase 1:\n\t\t\t\t\ts = \"1\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\ts = \"2\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\ts = \"3\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\ts = \"4\";\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tSystem.out.println(\"Not Reach\");\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tSystem.out.println(s);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(5, \"break;\")\n\t\t\t\t.containsOne(\"System.out.println(s);\")\n\t\t\t\t.containsOne(\"System.out.println(\\\"Not Reach\\\");\")\n\t\t\t\t.doesNotContain(\"switch ((a % 4)) {\")\n\t\t\t\t.contains(\"switch (a % 4) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchWithFallThroughCase.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestSwitchWithFallThroughCase extends IntegrationTest {\n\n\t@SuppressWarnings(\"fallthrough\")\n\tpublic static class TestCls {\n\t\tpublic String test(int a, boolean b, boolean c) {\n\t\t\tString str = \"\";\n\t\t\tswitch (a % 4) {\n\t\t\t\tcase 1:\n\t\t\t\t\tstr += \">\";\n\t\t\t\t\tif (a == 5 && b) {\n\t\t\t\t\t\tif (c) {\n\t\t\t\t\t\t\tstr += \"1\";\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tstr += \"!c\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t// fallthrough\n\t\t\t\tcase 2:\n\t\t\t\t\tif (b) {\n\t\t\t\t\t\tstr += \"2\";\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tstr += \"default\";\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tstr += \";\";\n\t\t\treturn str;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(5, true, true)).isEqualTo(\">1;\");\n\t\t\tassertThat(test(1, true, true)).isEqualTo(\">2;\");\n\t\t\tassertThat(test(3, true, true)).isEqualTo(\";\");\n\t\t\tassertThat(test(0, true, true)).isEqualTo(\"default;\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"switch (a % 4) {\")\n\t\t\t\t.containsOne(\"if (a == 5 && b) {\")\n\t\t\t\t.containsOne(\"if (b) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchWithFallThroughCase2.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestSwitchWithFallThroughCase2 extends IntegrationTest {\n\n\t@SuppressWarnings(\"fallthrough\")\n\tpublic static class TestCls {\n\t\tpublic String test(int a, boolean b, boolean c) {\n\t\t\tString str = \"\";\n\t\t\tif (a > 0) {\n\t\t\t\tswitch (a % 4) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\tstr += \">\";\n\t\t\t\t\t\tif (a == 5 && b) {\n\t\t\t\t\t\t\tif (c) {\n\t\t\t\t\t\t\t\tstr += \"1\";\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tstr += \"!c\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\tif (b) {\n\t\t\t\t\t\t\tstr += \"2\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tstr += \"default\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tstr += \"+\";\n\t\t\t}\n\t\t\tif (b && c) {\n\t\t\t\tstr += \"-\";\n\t\t\t}\n\t\t\treturn str;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(5, true, true)).isEqualTo(\">1+-\");\n\t\t\tassertThat(test(1, true, true)).isEqualTo(\">2+-\");\n\t\t\tassertThat(test(3, true, true)).isEqualTo(\"+-\");\n\t\t\tassertThat(test(16, true, true)).isEqualTo(\"default+-\");\n\t\t\tassertThat(test(-1, true, true)).isEqualTo(\"-\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"switch (a % 4) {\")\n\t\t\t\t.containsOne(\"if (a == 5 && b) {\")\n\t\t\t\t.containsOne(\"if (b) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchWithThrow.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.catchThrowable;\n\npublic class TestSwitchWithThrow extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int test(int i) {\n\t\t\tif (i != 0) {\n\t\t\t\tswitch (i % 4) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\tthrow new IllegalStateException(\"1\");\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\tthrow new IllegalStateException(\"2\");\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow new IllegalStateException(\"Other\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tSystem.out.println(\"0\");\n\t\t\treturn -1;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(0)).isEqualTo(-1);\n\t\t\tassertThat(catchThrowable(() -> test(1)))\n\t\t\t\t\t.isInstanceOf(IllegalStateException.class)\n\t\t\t\t\t.hasMessageContaining(\"1\");\n\t\t\tassertThat(catchThrowable(() -> test(3)))\n\t\t\t\t\t.isInstanceOf(IllegalStateException.class)\n\t\t\t\t\t.hasMessageContaining(\"Other\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"throw new IllegalStateException(\\\"1\\\");\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchWithTryCatch.java",
    "content": "package jadx.tests.integration.switches;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"checkstyle:printstacktrace\")\npublic class TestSwitchWithTryCatch extends IntegrationTest {\n\tpublic static class TestCls {\n\t\tvoid test(int a) {\n\t\t\tswitch (a) {\n\t\t\t\tcase 0:\n\t\t\t\t\ttry {\n\t\t\t\t\t\texc();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t// no break;\n\n\t\t\t\tcase 1:\n\t\t\t\t\ttry {\n\t\t\t\t\t\texc();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 2:\n\t\t\t\t\ttry {\n\t\t\t\t\t\texc();\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 3:\n\t\t\t\t\ttry {\n\t\t\t\t\t\texc();\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (a == 10) {\n\t\t\t\tSystem.out.println(a);\n\t\t\t}\n\t\t}\n\n\t\tprivate void exc() throws Exception {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(3, \"break;\")\n\t\t\t\t.countString(4, \"return;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/synchronize/TestNestedSynchronize.java",
    "content": "package jadx.tests.integration.synchronize;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestNestedSynchronize extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic final void test() {\n\t\t\tObject obj = null;\n\t\t\tObject obj2 = null;\n\t\t\tsynchronized (obj) {\n\t\t\t\tsynchronized (obj2) {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"synchronized\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/synchronize/TestSynchronized.java",
    "content": "package jadx.tests.integration.synchronize;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestSynchronized extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic boolean f = false;\n\t\tpublic final Object o = new Object();\n\t\tpublic int i = 7;\n\n\t\tpublic synchronized boolean test1() {\n\t\t\treturn this.f;\n\t\t}\n\n\t\tpublic int test2() {\n\t\t\tsynchronized (this.o) {\n\t\t\t\treturn this.i;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"synchronized (this) {\")\n\t\t\t\t.containsOne(\"public synchronized boolean test1() {\")\n\t\t\t\t.containsOne(\"return this.f\")\n\t\t\t\t.containsOne(\"synchronized (this.o) {\")\n\t\t\t\t.doesNotContain(indent(3) + ';')\n\t\t\t\t.doesNotContain(\"try {\")\n\t\t\t\t.doesNotContain(\"} catch (Throwable th) {\")\n\t\t\t\t.doesNotContain(\"throw th;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/synchronize/TestSynchronized2.java",
    "content": "package jadx.tests.integration.synchronize;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSynchronized2 extends IntegrationTest {\n\n\t@SuppressWarnings(\"unused\")\n\tpublic static class TestCls {\n\t\tprivate static synchronized boolean test(Object obj) {\n\t\t\treturn obj.toString() != null;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"private static synchronized boolean test(Object obj) {\")\n\t\t\t\t.doesNotContain(\"synchronized (\")\n\t\t\t\t.contains(\"obj.toString() != null;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/synchronize/TestSynchronized3.java",
    "content": "package jadx.tests.integration.synchronize;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSynchronized3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate int x;\n\n\t\tpublic void f() {\n\t\t}\n\n\t\tpublic void test() {\n\t\t\twhile (true) {\n\t\t\t\tsynchronized (this) {\n\t\t\t\t\tif (x == 0) {\n\t\t\t\t\t\tthrow new IllegalStateException();\n\t\t\t\t\t}\n\t\t\t\t\tx++;\n\t\t\t\t\tif (x == 10) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.x++;\n\t\t\t\tf();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLines(3, \"}\", \"this.x++;\", \"f();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/synchronize/TestSynchronized4.java",
    "content": "package jadx.tests.integration.synchronize;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSynchronized4 extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic boolean test(int i) {\n\t\t\tsynchronized (this.obj) {\n\t\t\t\tif (isZero(i)) {\n\t\t\t\t\treturn call(obj, i);\n\t\t\t\t}\n\t\t\t\tSystem.out.println();\n\t\t\t\treturn getField() == null;\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"synchronized (this.obj) {\")\n\t\t\t\t.containsOne(\"return call(this.obj, i);\")\n\t\t\t\t.containsOne(\"return getField() == null;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/synchronize/TestSynchronized5.java",
    "content": "package jadx.tests.integration.synchronize;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSynchronized5 extends SmaliTest {\n\t@Test\n\tpublic void test() {\n\t\tallowWarnInCode();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.contains(\"1 != 0\")\n\t\t\t\t.contains(\"System.gc();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/synchronize/TestSynchronized6.java",
    "content": "package jadx.tests.integration.synchronize;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestSynchronized6 extends SmaliTest {\n\n\tpublic static class TestCls {\n\t\tprivate final Object lock = new Object();\n\n\t\tprivate boolean test(Object obj) {\n\t\t\tsynchronized (this.lock) {\n\t\t\t\treturn isA(obj) || isB(obj);\n\t\t\t}\n\t\t}\n\n\t\tprivate boolean isA(Object obj) {\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate boolean isB(Object obj) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"synchronized (this.lock) {\")\n\t\t\t\t.containsOne(\"isA(obj) || isB(obj);\"); // TODO: \"return isA(obj) || isB(obj);\"\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"synchronized (this.lock) {\");\n\t\t// TODO: .containsOne(\"return isA(obj) || isB(obj);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestEmptyCatch.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Empty catch blocks in enum switch remap building\n */\npublic class TestEmptyCatch extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.countString(5, \"try {\")\n\t\t\t\t.countString(5, \"} catch (NoSuchFieldError unused\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestEmptyFinally.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.io.FileInputStream;\nimport java.io.IOException;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestEmptyFinally extends IntegrationTest {\n\n\t@SuppressWarnings(\"EmptyFinallyBlock\")\n\tpublic static class TestCls {\n\t\tpublic void test(FileInputStream f1) {\n\t\t\ttry {\n\t\t\t\tf1.close();\n\t\t\t} catch (IOException e) {\n\t\t\t\t// do nothing\n\t\t\t} finally {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"} catch (IOException e) {\")\n\t\t\t\t.doesNotContain(\"} finally {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestFinally.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestFinally extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static final String DISPLAY_NAME = \"name\";\n\n\t\tString test(Context context, Object uri) {\n\t\t\tCursor cursor = null;\n\t\t\ttry {\n\t\t\t\tString[] projection = { DISPLAY_NAME };\n\t\t\t\tcursor = context.query(uri, projection);\n\t\t\t\tint columnIndex = cursor.getColumnIndexOrThrow(DISPLAY_NAME);\n\t\t\t\tcursor.moveToFirst();\n\t\t\t\treturn cursor.getString(columnIndex);\n\t\t\t} finally {\n\t\t\t\tif (cursor != null) {\n\t\t\t\t\tcursor.close();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate class Context {\n\t\t\tpublic Cursor query(Object o, String[] s) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tprivate class Cursor {\n\t\t\tpublic void close() {\n\t\t\t}\n\n\t\t\tpublic void moveToFirst() {\n\t\t\t}\n\n\t\t\tpublic int getColumnIndexOrThrow(String s) {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tpublic String getString(int i) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"} finally {\")\n\t\t\t\t.containsOne(\"cursor.getString(columnIndex);\")\n\t\t\t\t.doesNotContain(\"String str = true;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestFinally2.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestFinally2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic Result test(byte[] data) throws IOException {\n\t\t\tInputStream inputStream = null;\n\t\t\ttry {\n\t\t\t\tinputStream = getInputStream(data);\n\t\t\t\tdecode(inputStream);\n\t\t\t\treturn new Result(400);\n\t\t\t} finally {\n\t\t\t\tcloseQuietly(inputStream);\n\t\t\t}\n\t\t}\n\n\t\tpublic static final class Result {\n\t\t\tprivate final int mCode;\n\n\t\t\tpublic Result(int code) {\n\t\t\t\tmCode = code;\n\t\t\t}\n\n\t\t\tpublic int getCode() {\n\t\t\t\treturn mCode;\n\t\t\t}\n\t\t}\n\n\t\tprivate InputStream getInputStream(byte[] data) throws IOException {\n\t\t\treturn new ByteArrayInputStream(data);\n\t\t}\n\n\t\tprivate int decode(InputStream inputStream) throws IOException {\n\t\t\treturn inputStream.available();\n\t\t}\n\n\t\tprivate void closeQuietly(InputStream is) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"decode(inputStream);\")\n\t\t\t\t.containsOne(\"return new Result(400);\")\n\t\t\t\t.doesNotContain(\"result =\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestFinally3.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestFinally3 extends SmaliTest {\n\n\t@SuppressWarnings({ \"RedundantThrows\", \"unused\" })\n\tpublic static class TestCls {\n\t\tpublic byte[] bytes;\n\n\t\tpublic byte[] test() throws Exception {\n\t\t\tInputStream inputStream = null;\n\t\t\ttry {\n\t\t\t\tif (bytes == null) {\n\t\t\t\t\tif (!validate()) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tinputStream = getInputStream();\n\t\t\t\t\tbytes = read(inputStream);\n\t\t\t\t}\n\t\t\t\treturn convert(bytes);\n\t\t\t} finally {\n\t\t\t\tclose(inputStream);\n\t\t\t}\n\t\t}\n\n\t\tprivate byte[] convert(byte[] bytes) throws Exception {\n\t\t\treturn new byte[0];\n\t\t}\n\n\t\tprivate boolean validate() throws Exception {\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate InputStream getInputStream() throws Exception {\n\t\t\treturn new ByteArrayInputStream(new byte[] {});\n\t\t}\n\n\t\tprivate byte[] read(InputStream in) throws Exception {\n\t\t\treturn new byte[] {};\n\t\t}\n\n\t\tprivate static void close(InputStream is) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"} finally {\")\n\t\t\t\t.doesNotContain(\"close(null);\")\n\t\t\t\t.containsOne(\"close(inputStream);\");\n\t}\n\n\t@NotYetImplemented(\"Finally extract failed\")\n\t@Test\n\tpublic void test2NoDebug() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"} finally {\")\n\t\t\t\t.containsOne(indent() + \"close(\");\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"Type inference failed\")\n\t\t\t\t.containsOne(\"} finally {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestFinallyExtract.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestFinallyExtract extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate int result = 0;\n\n\t\tpublic String test() {\n\t\t\tboolean success = false;\n\t\t\ttry {\n\t\t\t\tString value = call();\n\t\t\t\tresult++;\n\t\t\t\tsuccess = true;\n\t\t\t\treturn value;\n\t\t\t} finally {\n\t\t\t\tif (!success) {\n\t\t\t\t\tresult -= 2;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate String call() {\n\t\t\treturn \"call\";\n\t\t}\n\n\t\tpublic void check() {\n\t\t\ttest();\n\t\t\tassertThat(result).isEqualTo(1);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"} finally {\")\n\t\t\t\t.doesNotContain(\"if (0 == 0) {\")\n\n\t\t\t\t.containsOne(\"boolean success = false;\")\n\t\t\t\t.containsOne(\"try {\")\n\t\t\t\t.containsOne(\"success = true;\")\n\t\t\t\t.containsOne(\"return value;\")\n\t\t\t\t.containsOne(\"if (!success) {\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"this.result++;\")\n\t\t\t\t.containsOne(\"} catch (Throwable th) {\")\n\t\t\t\t.containsOne(\"this.result -= 2;\")\n\t\t\t\t.containsOne(\"throw th;\");\n\n\t\t// java compiler optimization: 'success' variable completely removed and no code duplication:\n\t\t// @formatter:off\n\t\t/*\n\t\t    public String test() {\n\t\t        try {\n\t\t            String call = call();\n\t\t            this.result++;\n\t\t            return call;\n\t\t        } catch (Throwable th) {\n\t\t            this.result -= 2;\n\t\t            throw th;\n\t\t        }\n\t\t    }\n\t\t*/\n\t\t// @formatter:on\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestIfInTryCatch.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestIfInTryCatch extends IntegrationTest {\n\tpublic static class TestCls {\n\t\tprivate void test() {\n\t\t\t/*\n\t\t\t * 1. if in try\n\t\t\t * 2. then branch is return\n\t\t\t * 3. after if, there's more blocks inside try\n\t\t\t * 4. after try, there's more blocks\n\t\t\t * this will result in if block and below moved out of try\n\t\t\t */\n\t\t\ttry {\n\t\t\t\tif (getDouble() > 0.5) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tSystem.out.println(\"after if\");\n\t\t\t} catch (Exception e) {\n\t\t\t\tSystem.out.println(\"exception\");\n\t\t\t}\n\t\t\tSystem.out.println(\"after try\");\n\t\t}\n\n\t\tprivate static double getDouble() throws InterruptedException {\n\t\t\tThread.sleep(50L);\n\t\t\treturn Math.random();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\t// if ifBlock is moved out of try, there will be uncaught exception\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestInlineInCatch.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.io.File;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestInlineInCatch extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate File dir;\n\n\t\tpublic int test() {\n\t\t\tFile output = null;\n\t\t\ttry {\n\t\t\t\toutput = File.createTempFile(\"f\", \"a\", dir);\n\t\t\t\tif (!output.exists()) {\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t} catch (Exception e) {\n\t\t\t\tif (output != null) {\n\t\t\t\t\toutput.delete();\n\t\t\t\t}\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"File output = null;\")\n\t\t\t\t.containsOne(\"output = File.createTempFile(\\\"f\\\", \\\"a\\\", \")\n\t\t\t\t.containsOne(\"return 0;\")\n\t\t\t\t.containsOne(\"} catch (Exception e) {\")\n\t\t\t\t.containsOne(\"if (output != null) {\")\n\t\t\t\t.containsOne(\"output.delete();\")\n\t\t\t\t.containsOne(\"return 2;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestLoopInTryCatch.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestLoopInTryCatch extends SmaliTest {\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.oneOf(c -> c.containsLines(2,\n\t\t\t\t\t\t\"int i;\",\n\t\t\t\t\t\t\"while (true) {\",\n\t\t\t\t\t\t\"    try {\",\n\t\t\t\t\t\t\"        i = getI();\",\n\t\t\t\t\t\t\"    } catch (RuntimeException unused) {\",\n\t\t\t\t\t\t\"        return;\",\n\t\t\t\t\t\t\"    }\",\n\t\t\t\t\t\t\"    if (i == 1 || i == 2) {\",\n\t\t\t\t\t\t\"        break;\",\n\t\t\t\t\t\t\"    }\",\n\t\t\t\t\t\t\"}\",\n\t\t\t\t\t\t\"if (i != 1) {\",\n\t\t\t\t\t\t\"    getI();\",\n\t\t\t\t\t\t\"}\"),\n\t\t\t\t\t\tc -> c.containsLines(2,\n\t\t\t\t\t\t\t\t\"int i;\",\n\t\t\t\t\t\t\t\t\"while (true) {\",\n\t\t\t\t\t\t\t\t\"    try {\",\n\t\t\t\t\t\t\t\t\"        i = getI();\",\n\t\t\t\t\t\t\t\t\"        if (i == 1 || i == 2) {\",\n\t\t\t\t\t\t\t\t\"            break;\",\n\t\t\t\t\t\t\t\t\"        }\",\n\t\t\t\t\t\t\t\t\"    } catch (RuntimeException unused) {\",\n\t\t\t\t\t\t\t\t\"        return;\",\n\t\t\t\t\t\t\t\t\"    }\",\n\t\t\t\t\t\t\t\t\"}\",\n\t\t\t\t\t\t\t\t\"if (i != 1) {\",\n\t\t\t\t\t\t\t\t\"    getI();\",\n\t\t\t\t\t\t\t\t\"}\"),\n\t\t\t\t\t\t// TODO: weird result but correct, better to not use do-while if not really needed\n\t\t\t\t\t\tc -> c.containsLines(2,\n\t\t\t\t\t\t\t\t\"int i;\",\n\t\t\t\t\t\t\t\t\"do {\",\n\t\t\t\t\t\t\t\t\"    try {\",\n\t\t\t\t\t\t\t\t\"        i = getI();\",\n\t\t\t\t\t\t\t\t\"        if (i == 1) {\",\n\t\t\t\t\t\t\t\t\"            break;\",\n\t\t\t\t\t\t\t\t\"        }\",\n\t\t\t\t\t\t\t\t\"    } catch (RuntimeException unused) {\",\n\t\t\t\t\t\t\t\t\"        return;\",\n\t\t\t\t\t\t\t\t\"    }\",\n\t\t\t\t\t\t\t\t\"} while (i != 2);\",\n\t\t\t\t\t\t\t\t\"if (i != 1) {\",\n\t\t\t\t\t\t\t\t\"    getI();\",\n\t\t\t\t\t\t\t\t\"}\"));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestMultiExceptionCatch.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.security.ProviderException;\nimport java.time.DateTimeException;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestMultiExceptionCatch extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test() {\n\t\t\ttry {\n\t\t\t\tSystem.out.println(\"Test\");\n\t\t\t} catch (ProviderException | DateTimeException e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"try {\")\n\t\t\t\t.containsOne(\"} catch (ProviderException | DateTimeException e) {\")\n\t\t\t\t.containsOne(\"throw new RuntimeException(e);\")\n\t\t\t\t.doesNotContain(\"RuntimeException e;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestMultiExceptionCatch2.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationTargetException;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\n@SuppressWarnings(\"checkstyle:printstacktrace\")\npublic class TestMultiExceptionCatch2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test(Constructor<?> constructor) {\n\t\t\ttry {\n\t\t\t\tconstructor.newInstance();\n\t\t\t} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tcommonChecks();\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tcommonChecks();\n\t}\n\n\tprivate void commonChecks() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"try {\")\n\t\t\t\t.containsOne(\"} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {\")\n\t\t\t\t.containsOne(\"e.printStackTrace();\");\n\n\t\t// TODO: store vararg attribute for methods from classpath\n\t\t// assertThat(code, containsOne(\"constructor.newInstance();\"));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestMultiExceptionCatchSameJump.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"CommentedOutCode\")\npublic class TestMultiExceptionCatchSameJump extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic static class TestCls {\n\t\t\tpublic void test() {\n\t\t\t\ttry {\n\t\t\t\t\tSystem.out.println(\"Test\");\n\t\t\t\t} catch (ProviderException | DateTimeException e) {\n\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliWithPkg(\"trycatch\", \"TestMultiExceptionCatchSameJump\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"try {\")\n\t\t\t\t.containsOne(\"} catch (ProviderException | DateTimeException e) {\")\n\t\t\t\t.containsOne(\"throw new RuntimeException(e);\")\n\t\t\t\t.doesNotContain(\"RuntimeException e;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestNestedTryCatch.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestNestedTryCatch extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test() {\n\t\t\ttry {\n\t\t\t\tThread.sleep(1L);\n\t\t\t\ttry {\n\t\t\t\t\tThread.sleep(2L);\n\t\t\t\t} catch (InterruptedException ignored) {\n\t\t\t\t\tSystem.out.println(2);\n\t\t\t\t}\n\t\t\t} catch (Exception ignored) {\n\t\t\t\tSystem.out.println(1);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"try {\")\n\t\t\t\t.contains(\"Thread.sleep(1L);\")\n\t\t\t\t.contains(\"Thread.sleep(2L);\")\n\t\t\t\t.contains(\"} catch (InterruptedException e) {\")\n\t\t\t\t.contains(\"} catch (Exception e2) {\")\n\t\t\t\t.doesNotContain(\"return\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestNestedTryCatch2.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestNestedTryCatch2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test() {\n\t\t\ttry {\n\t\t\t\ttry {\n\t\t\t\t\tcall();\n\t\t\t\t\tcall();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\texc(e);\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\texc(e);\n\t\t\t}\n\t\t}\n\n\t\tprivate void call() {\n\t\t}\n\n\t\tprivate void exc(Exception e) {\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.JAVA8, TestProfile.DX_J8 })\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"} catch (Exception \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestNestedTryCatch3.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestNestedTryCatch3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic I test() {\n\t\t\ttry {\n\t\t\t\ttry {\n\t\t\t\t\treturn new A();\n\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\treturn new B();\n\t\t\t\t}\n\t\t\t} catch (Throwable e) {\n\t\t\t\treturn new C();\n\t\t\t}\n\t\t}\n\n\t\tprivate interface I {\n\t\t}\n\n\t\tprivate static class A implements I {\n\t\t}\n\n\t\tprivate static class B implements I {\n\t\t}\n\n\t\tprivate static class C implements I {\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.JAVA8, TestProfile.DX_J8 })\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return new A();\")\n\t\t\t\t.containsOne(\"return new B();\")\n\t\t\t\t.containsOne(\"return new C();\")\n\t\t\t\t.countString(2, \"} catch (Throwable \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestNestedTryCatch4.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestNestedTryCatch4 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"?? \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestNestedTryCatch5.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestNestedTryCatch5 extends SmaliTest {\n\n\t@Test\n\t@NotYetImplemented(\"Extracting finally on loop advancement\")\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"?? \")\n\t\t\t\t.containsOne(\"} finally\")\n\t\t\t\t.containsOne(\"endTransaction\")\n\t\t\t\t.countString(1, \"throw \"); // 1 real throws, 1 implicit throw on finally handler and 1 implicit throw on empty ALL handler\n\t}\n\n\t@Test\n\tpublic void testNoFinally() {\n\t\targs.setExtractFinally(false);\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"?? \")\n\t\t\t\t.countString(3, \"throw \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryAfterDeclaration.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTryAfterDeclaration extends IntegrationTest {\n\n\tstatic class TestClass {\n\t\tpublic static void consume() throws IOException {\n\t\t\tInputStream bis = null;\n\t\t\ttry {\n\t\t\t\tbis = new FileInputStream(\"1.txt\");\n\t\t\t\twhile (bis != null) {\n\t\t\t\t\tSystem.out.println(\"c\");\n\t\t\t\t}\n\t\t\t} catch (final IOException ignore) {\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Issue #62.\n\t */\n\t@Test\n\t@NotYetImplemented\n\tpublic void test62() {\n\t\tJadxAssertions.assertThat(getClassNode(TestClass.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"try {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatch.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTryCatch extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void f() {\n\t\t\ttry {\n\t\t\t\tThread.sleep(50L);\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"try {\")\n\t\t\t\t.contains(\"Thread.sleep(50L);\")\n\t\t\t\t.contains(\"} catch (InterruptedException e) {\")\n\t\t\t\t.doesNotContain(\"return\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatch10.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatch10 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.countString(3, \"return false;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatch11.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n// Ensure that we can still merge if conditions even in the case where\n// the BOTTOM_SPLITTER occurs before the final IF block.\npublic class TestTryCatch11 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static class Cursor implements AutoCloseable {\n\t\t\t@Override\n\t\t\tpublic void close() {\n\t\t\t\tSystem.out.println(\"Closed AutoCloseableResources_First\");\n\t\t\t}\n\n\t\t\tpublic String getString() {\n\t\t\t\treturn \"jfdkelapgfureiqop[]\";\n\t\t\t}\n\t\t}\n\n\t\tpublic static String test() {\n\t\t\ttry (Cursor cursor = new Cursor()) {\n\t\t\t\tString value = cursor.getString();\n\t\t\t\tif (value.startsWith(\"content://\") || !value.startsWith(\"/\") && !value.startsWith(\"file://\")) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\treturn value;\n\t\t\t} catch (Exception ignore) {\n\t\t\t\tSystem.out.println(\"catch\");\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.JAVA8 })\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"value.startsWith(\\\"content://\\\") || (!value.startsWith(\\\"/\\\") && !value.startsWith(\\\"file://\\\"))\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatch2.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTryCatch2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static final Object OBJ = new Object();\n\n\t\tpublic static boolean test() {\n\t\t\ttry {\n\t\t\t\tsynchronized (OBJ) {\n\t\t\t\t\tOBJ.wait(5L);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"try {\")\n\t\t\t\t.contains(\"synchronized (OBJ) {\")\n\t\t\t\t.contains(\"OBJ.wait(5L);\")\n\t\t\t\t.contains(\"return true;\")\n\t\t\t\t.contains(\"} catch (InterruptedException e) {\")\n\t\t\t\t.contains(\"return false;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatch6.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.io.IOException;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatch6 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static boolean test(Object obj) {\n\t\t\tboolean res = false;\n\t\t\twhile (true) {\n\t\t\t\ttry {\n\t\t\t\t\tres = exc(obj);\n\t\t\t\t\treturn res;\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tres = true;\n\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\tif (obj == null) {\n\t\t\t\t\t\tobj = new Object();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate static boolean exc(Object obj) throws IOException {\n\t\t\tif (obj == null) {\n\t\t\t\tthrow new IOException();\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(new Object())).isTrue();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"try {\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"try {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatch7.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"checkstyle:printstacktrace\")\npublic class TestTryCatch7 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic Exception test() {\n\t\t\tException e = new Exception();\n\t\t\ttry {\n\t\t\t\tThread.sleep(50);\n\t\t\t} catch (Exception ex) {\n\t\t\t\te = ex;\n\t\t\t}\n\t\t\te.printStackTrace();\n\t\t\treturn e;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tString code = cls.getCode().toString();\n\t\tcheck(code, \"e\", \"ex\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tString code = cls.getCode().toString();\n\t\tcheck(code, \"e\", \"e2\");\n\t}\n\n\tprivate void check(String code, String excVarName, String catchExcVarName) {\n\t\tassertThat(code).containsOne(\"Exception \" + excVarName + \" = new Exception();\")\n\t\t\t\t.containsOne(\"} catch (Exception \" + catchExcVarName + \") {\")\n\t\t\t\t.containsOne(excVarName + \" = \" + catchExcVarName + ';')\n\t\t\t\t.containsOne(excVarName + \".printStackTrace();\")\n\t\t\t\t.containsOne(\"return \" + excVarName + ';');\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatch8.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatch8 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tstatic class MyException extends Exception {\n\t\t\tprivate static final long serialVersionUID = 7963400419047287279L;\n\n\t\t\tMyException() {\n\t\t\t}\n\n\t\t\tMyException(String msg, Throwable cause) {\n\t\t\t\tsuper(msg, cause);\n\t\t\t}\n\t\t}\n\n\t\tMyException e = null;\n\n\t\tpublic void test() {\n\t\t\tsynchronized (this) {\n\t\t\t\ttry {\n\t\t\t\t\tthrow new MyException();\n\t\t\t\t} catch (MyException myExc) {\n\t\t\t\t\tthis.e = myExc;\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tthis.e = new MyException(\"MyExc\", ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\ttest();\n\t\t\tassertThat(e).isInstanceOf(MyException.class);\n\t\t\tassertThat(e.getMessage()).isNull();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"synchronized (this) {\")\n\t\t\t\t.containsOne(\"throw new MyException();\")\n\t\t\t\t.containsOne(\"} catch (MyException myExc) {\")\n\t\t\t\t.containsOne(\"this.e = myExc;\")\n\t\t\t\t.containsOne(\"} catch (Exception ex) {\")\n\t\t\t\t.containsOne(\"this.e = new MyException(\\\"MyExc\\\", ex);\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"synchronized (this) {\")\n\t\t\t\t.containsOne(\"throw new MyException();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatch9.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatch9 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic Integer test(final Integer i) {\n\t\t\tif (i == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tInteger res = null;\n\t\t\ttry {\n\t\t\t\tif (i == 5) {\n\t\t\t\t\tres = 4;\n\t\t\t\t} else {\n\t\t\t\t\tres = 9;\n\t\t\t\t}\n\t\t\t} catch (final Exception ex) {\n\t\t\t\tlogError(ex);\n\t\t\t}\n\t\t\treturn res;\n\t\t}\n\n\t\tprivate void logError(Exception ex) {\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(5)).isEqualTo(4);\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.JAVA8 })\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"logError(ex);\")\n\t\t\t\t.containsOne(\"Integer res\")\n\t\t\t\t.contains(\"res = null;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"checkstyle:printstacktrace\")\npublic class TestTryCatchFinally extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic boolean f;\n\n\t\t@SuppressWarnings(\"ConstantConditions\")\n\t\tprivate boolean test(Object obj) {\n\t\t\tthis.f = false;\n\t\t\ttry {\n\t\t\t\texc(obj);\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t} finally {\n\t\t\t\tthis.f = true;\n\t\t\t}\n\t\t\treturn this.f;\n\t\t}\n\n\t\tprivate static boolean exc(Object obj) throws Exception {\n\t\t\tif (obj == null) {\n\t\t\t\tthrow new Exception(\"test\");\n\t\t\t}\n\t\t\treturn (obj instanceof String);\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(\"a\")).isTrue();\n\t\t\tassertThat(test(null)).isTrue();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"this.f = false;\")\n\t\t\t\t.containsOne(\"exc(obj);\")\n\t\t\t\t.containsOne(\"} catch (Exception e) {\")\n\t\t\t\t.containsOne(\"e.printStackTrace();\")\n\t\t\t\t.containsOne(\"} finally {\")\n\t\t\t\t.containsOne(\"this.f = true;\")\n\t\t\t\t.containsOne(\"return this.f;\")\n\t\t\t\t.doesNotContain(\"boolean z\");\n\t}\n\n\t@Test\n\tpublic void testWithoutFinally() {\n\t\targs.setExtractFinally(false);\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"exc(obj);\")\n\t\t\t\t.containsOne(indent(3) + \"} catch (Exception e) {\")\n\t\t\t\t.containsOne(indent(2) + \"} catch (Throwable th) {\")\n\t\t\t\t.containsOne(\"this.f = false;\")\n\t\t\t\t.countString(3, \"this.f = true;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally10.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatchFinally10 extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic static String test(Context context, int i) {\n\t\t\tCommonContracts.requireNonNull(context);\n\t\t\tInputStream inputStream = null;\n\t\t\ttry {\n\t\t\t\tinputStream = context.getResources().openRawResource(i);\n\t\t\t\tScanner useDelimiter = new Scanner(inputStream).useDelimiter(\"\\\\A\");\n\t\t\t\treturn useDelimiter.hasNext() ? useDelimiter.next() : \"\";\n\t\t\t} finally {\n\t\t\t\tif (inputStream != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tinputStream.close();\n\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\tl.logException(LogLevel.ERROR, e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"boolean z = null;\")\n\t\t\t\t.doesNotContain(\"} catch (Throwable\")\n\t\t\t\t.containsOne(\"} finally {\")\n\t\t\t\t.containsOne(\".close();\")\n\t\t\t\t.containsOne(\"} catch (IOException e\")\n\t\t\t\t.containsOne(\".logException(\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally11.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatchFinally11 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate int count = 0;\n\n\t\tpublic void test(List<Object> list) {\n\t\t\ttry {\n\t\t\t\tcall1();\n\t\t\t} finally {\n\t\t\t\tfor (Object item : list) {\n\t\t\t\t\tcall2(item);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void call1() {\n\t\t\tcount += 100;\n\t\t}\n\n\t\tprivate void call2(Object item) {\n\t\t\tcount++;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tTestCls t = new TestCls();\n\t\t\tt.test(Arrays.asList(\"1\", \"2\"));\n\t\t\tassertThat(t.count).isEqualTo(102);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"} finally {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally12.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatchFinally12 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate StringBuilder sb;\n\n\t\tpublic void test1(int excType) {\n\t\t\ttry {\n\t\t\t\ttry {\n\t\t\t\t\tcall(excType);\n\t\t\t\t} catch (NullPointerException e) {\n\t\t\t\t\tsb.append(\"-catch\");\n\t\t\t\t}\n\t\t\t\tsb.append(\"-out\");\n\t\t\t} finally {\n\t\t\t\tsb.append(\"-finally\");\n\t\t\t}\n\t\t}\n\n\t\tpublic void test2(int excType) {\n\t\t\ttry {\n\t\t\t\ttry {\n\t\t\t\t\tcall(excType);\n\t\t\t\t} catch (NullPointerException e) {\n\t\t\t\t\tsb.append(\"-catch\");\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tsb.append(\"-finally\");\n\t\t\t}\n\t\t}\n\n\t\tpublic void test3(int excType) {\n\t\t\ttry {\n\t\t\t\tcall(excType);\n\t\t\t} catch (NullPointerException e) {\n\t\t\t\tsb.append(\"-catch\");\n\t\t\t} finally {\n\t\t\t\tsb.append(\"-finally\");\n\t\t\t}\n\t\t}\n\n\t\tpublic void call(int excType) {\n\t\t\tsb.append(\"call\");\n\t\t\tswitch (excType) {\n\t\t\t\tcase 1:\n\t\t\t\t\tsb.append(\"-npe\");\n\t\t\t\t\tthrow new NullPointerException();\n\t\t\t\tcase 2:\n\t\t\t\t\tsb.append(\"-iae\");\n\t\t\t\t\tthrow new IllegalArgumentException();\n\t\t\t}\n\t\t}\n\n\t\tpublic String runTest(int testNumber, int excType) {\n\t\t\tsb = new StringBuilder();\n\t\t\ttry {\n\t\t\t\tswitch (testNumber) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\ttest1(excType);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\ttest2(excType);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\ttest3(excType);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} catch (IllegalArgumentException e) {\n\t\t\t\tassertThat(excType).isEqualTo(2);\n\t\t\t}\n\t\t\treturn sb.toString();\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(runTest(1, 0)).isEqualTo(\"call-out-finally\");\n\t\t\tassertThat(runTest(1, 1)).isEqualTo(\"call-npe-catch-out-finally\");\n\t\t\tassertThat(runTest(1, 2)).isEqualTo(\"call-iae-finally\");\n\n\t\t\tassertThat(runTest(2, 0)).isEqualTo(\"call-finally\");\n\t\t\tassertThat(runTest(2, 1)).isEqualTo(\"call-npe-catch-finally\");\n\t\t\tassertThat(runTest(2, 2)).isEqualTo(\"call-iae-finally\");\n\n\t\t\tassertThat(runTest(3, 0)).isEqualTo(\"call-finally\");\n\t\t\tassertThat(runTest(3, 1)).isEqualTo(\"call-npe-catch-finally\");\n\t\t\tassertThat(runTest(3, 2)).isEqualTo(\"call-iae-finally\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(3, \"} finally {\");\n\t}\n\n\t@Test\n\tpublic void testWithoutFinally() {\n\t\tgetArgs().setExtractFinally(false);\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"} finally {\")\n\t\t\t\t.countString(2 + 2 + 3, \"sb.append(\\\"-finally\\\");\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally13.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatchFinally13 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test(int i) {\n\t\t\ttry {\n\t\t\t\tdoSomething1();\n\t\t\t\tif (i == -12) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (i > 10) {\n\t\t\t\t\tdoSomething2();\n\t\t\t\t} else if (i == -1) {\n\t\t\t\t\tdoSomething3();\n\t\t\t\t}\n\t\t\t} catch (Exception ex) {\n\t\t\t\tlogError();\n\t\t\t} finally {\n\t\t\t\tdoSomething4();\n\t\t\t}\n\t\t}\n\n\t\tprivate void logError() {\n\t\t}\n\n\t\tprivate void doSomething1() {\n\t\t}\n\n\t\tprivate void doSomething2() {\n\t\t}\n\n\t\tprivate void doSomething3() {\n\t\t}\n\n\t\tprivate void doSomething4() {\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.JAVA8 })\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"} finally {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally14.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatchFinally14 extends IntegrationTest {\n\n\t@SuppressWarnings(\"unused\")\n\tpublic static class TestCls {\n\t\tprivate TCls t;\n\n\t\tpublic void test() {\n\t\t\ttry {\n\t\t\t\tif (t != null) {\n\t\t\t\t\tt.doSomething();\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tif (t != null) {\n\t\t\t\t\tt.doFinally();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate static class TCls {\n\t\t\tpublic void doSomething() {\n\t\t\t}\n\n\t\t\tpublic void doFinally() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.D8_J11, TestProfile.JAVA8 })\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\".doSomething();\")\n\t\t\t\t.containsOne(\"} finally {\")\n\t\t\t\t.containsOne(\".doFinally();\")\n\t\t\t\t.countString(2, \"!= null) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally15.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Negative test case for finally extract (issue 1592).\n * Different registers incorrectly merged into one.\n */\n@SuppressWarnings({ \"CommentedOutCode\", \"GrazieInspection\" })\npublic class TestTryCatchFinally15 extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tprotected final Parcel test(int i, Parcel parcel) throws RemoteException {\n\t\t\tParcel obtain = Parcel.obtain();\n\t\t\ttry {\n\t\t\t\ttry {\n\t\t\t\t\tthis.zza.transact(i, parcel, obtain, 0);\n\t\t\t\t\tobtain.readException();\n\t\t\t\t\treturn obtain;\n\t\t\t\t} catch (RuntimeException e) {\n\t\t\t\t\tobtain.recycle();\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tparcel.recycle();\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"parcel = Parcel.obtain();\")\n\t\t\t\t.containsOne(\"this.zza.transact(i, parcel, parcelObtain, 0);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally16.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatchFinally16 extends SmaliTest {\n\n\t@SuppressWarnings(\"unused\")\n\tpublic static class TestCls {\n\t\tpublic void test() {\n\t\t\ttry {\n\t\t\t\tTCls.doSomething();\n\t\t\t} catch (Exception e) {\n\t\t\t\t// do nothing\n\t\t\t} finally {\n\t\t\t\tTCls.doFinally();\n\t\t\t}\n\t\t}\n\n\t\tprivate static class TCls {\n\t\t\tpublic static void doSomething() {\n\t\t\t}\n\n\t\t\tpublic static void doFinally() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.D8_J11, TestProfile.JAVA8 })\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tClassNode node = getClassNode(TestCls.class);\n\t\tassertThat(node)\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"TCls.doSomething()\")\n\t\t\t\t.containsOne(\"TCls.doFinally()\")\n\t\t\t\t.containsOne(\"finally\")\n\t\t\t\t.containsOne(\"} catch\")\n\t\t\t\t.contains(\"catch (Exception e)\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally17.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatchFinally17 extends SmaliTest {\n\n\t@SuppressWarnings(\"unused\")\n\tpublic static class TestCls {\n\t\tpublic int test() {\n\t\t\ttry {\n\t\t\t\tTCls.doSomething();\n\t\t\t} catch (UnsupportedOperationException e) {\n\t\t\t\t// do nothing\n\t\t\t} catch (NullPointerException e) {\n\t\t\t\treturn 1;\n\t\t\t} finally {\n\t\t\t\tTCls.doFinally();\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tprivate static class TCls {\n\t\t\tpublic static void doSomething() {\n\t\t\t}\n\n\t\t\tpublic static void doFinally() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.D8_J11, TestProfile.JAVA8 })\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tClassNode node = getClassNode(TestCls.class);\n\t\tassertThat(node)\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"TCls.doSomething()\")\n\t\t\t\t.containsOne(\"TCls.doFinally()\")\n\t\t\t\t.containsOne(\"} finally\")\n\t\t\t\t.containsOne(\"catch (NullPointerException \")\n\t\t\t\t.containsOne(\"catch (UnsupportedOperationException \")\n\t\t\t\t.doesNotContain(\"catch (Throwable\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally18.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport jadx.NotYetImplemented;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatchFinally18 extends SmaliTest {\n\n\t@SuppressWarnings(\"unused\")\n\tpublic static class TestCls {\n\t\tpublic int test3() {\n\t\t\tint val;\n\t\t\ttry {\n\t\t\t\tval = TCls.doSomething();\n\t\t\t} catch (UnsupportedOperationException e) {\n\t\t\t\treturn -1;\n\t\t\t} catch (NullPointerException e) {\n\t\t\t\tval = 0;\n\t\t\t} finally {\n\t\t\t\tTCls.dispose();\n\t\t\t}\n\t\t\tval += 4;\n\t\t\tif (val < 10) {\n\t\t\t\tTCls.log(\"less than 10\");\n\t\t\t}\n\t\t\treturn val;\n\t\t}\n\n\t\tprivate static class TCls {\n\t\t\tpublic static int doSomething() {\n\t\t\t\treturn 14;\n\t\t\t}\n\n\t\t\tpublic static void dispose() {\n\t\t\t}\n\n\t\t\tpublic static void log(String msg) {\n\n\t\t\t}\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.D8_J11 })\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tClassNode node = getClassNode(TestCls.class);\n\t\tassertThat(node)\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"TCls.doSomething()\")\n\t\t\t\t.containsOne(\"TCls.dispose()\")\n\t\t\t\t.containsOne(\"} finally\")\n\t\t\t\t.containsOne(\"catch (NullPointerException \")\n\t\t\t\t.containsOne(\"catch (UnsupportedOperationException \")\n\t\t\t\t.doesNotContain(\"catch (Throwable\");\n\t}\n\n\t@NotYetImplemented(\"To be investigated why J8 does not work\")\n\t@TestWithProfiles({ TestProfile.JAVA8 })\n\tpublic void testJ8() {\n\t\tdisableCompilation();\n\t\tClassNode node = getClassNode(TestCls.class);\n\t\tassertThat(node)\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"TCls.doSomething()\")\n\t\t\t\t.containsOne(\"TCls.dispose()\")\n\t\t\t\t.containsOne(\"} finally\")\n\t\t\t\t.containsOne(\"catch (NullPointerException \")\n\t\t\t\t.containsOne(\"catch (UnsupportedOperationException \")\n\t\t\t\t.doesNotContain(\"catch (Throwable\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally19.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport jadx.NotYetImplemented;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * A class which tests finally extraction for cases where the all handler does not rethrow an\n * exception.\n */\npublic class TestTryCatchFinally19 extends SmaliTest {\n\n\t@SuppressWarnings(\"unused\")\n\tpublic static class TestCls {\n\t\tpublic Integer test() {\n\t\t\tInteger val;\n\t\t\ttry {\n\t\t\t\treturn TCls.doSomething();\n\t\t\t} catch (Throwable t) {\n\t\t\t\treturn null;\n\t\t\t} finally {\n\t\t\t\tTCls.dispose();\n\t\t\t}\n\t\t}\n\n\t\tprivate static class TCls {\n\t\t\tpublic static int doSomething() {\n\t\t\t\treturn 14;\n\t\t\t}\n\n\t\t\tpublic static void dispose() {\n\t\t\t}\n\n\t\t\tpublic static void log(String msg) {\n\n\t\t\t}\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.D8_J11 })\n\t@NotYetImplemented(\"Currently only processing finally blocks if the all handler throws.\")\n\tpublic void testDXJ8() {\n\t\tdisableCompilation();\n\t\tClassNode node = getClassNode(TestCls.class);\n\t\tassertThat(node)\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"TCls.doSomething()\")\n\t\t\t\t.containsOne(\"TCls.dispose()\")\n\t\t\t\t.containsOne(\"} finally\")\n\t\t\t\t.containsOne(\"catch (Throwable \")\n\t\t\t\t.containsOne(\"return null\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally2.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.io.DataOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.clsp.ClspClass;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTryCatchFinally2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate ClspClass[] classes;\n\n\t\tpublic void test(OutputStream output) throws IOException {\n\t\t\tDataOutputStream out = new DataOutputStream(output);\n\t\t\ttry {\n\t\t\t\tout.writeByte(1);\n\t\t\t\tout.writeInt(classes.length);\n\t\t\t\tfor (ClspClass cls : classes) {\n\t\t\t\t\twriteString(out, cls.getName());\n\t\t\t\t}\n\t\t\t\tfor (ClspClass cls : classes) {\n\t\t\t\t\tArgType[] parents = cls.getParents();\n\t\t\t\t\tout.writeByte(parents.length);\n\t\t\t\t\tfor (ArgType parent : parents) {\n\t\t\t\t\t\tout.writeInt(parent.getObject().hashCode());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tout.close();\n\t\t\t}\n\t\t}\n\n\t\tprivate void writeString(DataOutputStream out, String name) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"} finally {\")\n\t\t\t\t.containsOne(\"out.close();\")\n\t\t\t\t.containsOne(\"for (ArgType parent : parents) {\")\n\t\t\t\t.containsOne(\"for (ClspClass cls : this.classes) {\")\n\t\t\t\t.containsOne(\"for (ClspClass cls2 : this.classes) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally3.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.visitors.DepthTraversal;\nimport jadx.core.dex.visitors.IDexTreeVisitor;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTryCatchFinally3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static final Logger LOG = LoggerFactory.getLogger(TestCls.class);\n\n\t\tpublic static void test(ClassNode cls, List<IDexTreeVisitor> passes) {\n\t\t\ttry {\n\t\t\t\tcls.load();\n\t\t\t\tfor (IDexTreeVisitor visitor : passes) {\n\t\t\t\t\tDepthTraversal.visit(visitor, cls);\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Class process exception: {}\", cls, e);\n\t\t\t} finally {\n\t\t\t\tcls.unload();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"for (IDexTreeVisitor visitor : passes) {\")\n\t\t\t\t.containsOne(\"} catch (Exception e) {\")\n\t\t\t\t.containsOne(\"LOG.error(\\\"Class process exception: {}\\\", cls, e);\")\n\t\t\t\t.containsOne(\"} finally {\")\n\t\t\t\t.containsOne(\"cls.unload();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally4.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTryCatchFinally4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test() throws IOException {\n\t\t\tFile file = File.createTempFile(\"test\", \"txt\");\n\t\t\tOutputStream outputStream = new FileOutputStream(file);\n\t\t\ttry {\n\t\t\t\toutputStream.write(1);\n\t\t\t} finally {\n\t\t\t\ttry {\n\t\t\t\t\toutputStream.close();\n\t\t\t\t\tfile.delete();\n\t\t\t\t} catch (IOException ignored) {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"File file = File.createTempFile(\\\"test\\\", \\\"txt\\\");\")\n\t\t\t\t.containsOne(\"OutputStream outputStream = new FileOutputStream(file);\")\n\t\t\t\t.containsOne(\"outputStream.write(1);\")\n\t\t\t\t.containsOne(\"} finally {\")\n\t\t\t\t.containsOne(\"outputStream.close();\")\n\t\t\t\t.containsOne(\"} catch (IOException e) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally5.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatchFinally5 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic <E> List<E> test(A a, B<E> b) {\n\t\t\tC c = p(a);\n\t\t\tif (c == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tD d = b.f(c);\n\t\t\ttry {\n\t\t\t\tif (!d.first()) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tList<E> list = new ArrayList<>();\n\t\t\t\tdo {\n\t\t\t\t\tlist.add(b.load(d));\n\t\t\t\t} while (d.toNext());\n\t\t\t\treturn list;\n\t\t\t} finally {\n\t\t\t\td.close();\n\t\t\t}\n\t\t}\n\n\t\tprivate C p(A a) {\n\t\t\treturn (C) a;\n\t\t}\n\n\t\tprivate interface A {\n\t\t}\n\n\t\tprivate interface B<T> {\n\t\t\tD f(C c);\n\n\t\t\tT load(D d);\n\t\t}\n\n\t\tprivate interface C {\n\t\t}\n\n\t\tprivate interface D {\n\t\t\tboolean first();\n\n\t\t\tboolean toNext();\n\n\t\t\tvoid close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"} finally {\")\n\t\t\t\t.containsOne(\"d.close();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally6.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatchFinally6 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static void test() throws IOException {\n\t\t\tInputStream is = null;\n\t\t\ttry {\n\t\t\t\tcall();\n\t\t\t\tis = new FileInputStream(\"1.txt\");\n\t\t\t} finally {\n\t\t\t\tif (is != null) {\n\t\t\t\t\tis.close();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate static void call() {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"InputStream is = null;\",\n\t\t\t\t\t\t\"try {\",\n\t\t\t\t\t\tindent(1) + \"call();\",\n\t\t\t\t\t\tindent(1) + \"is = new FileInputStream(\\\"1.txt\\\");\",\n\t\t\t\t\t\t\"} finally {\",\n\t\t\t\t\t\tindent(1) + \"if (is != null) {\",\n\t\t\t\t\t\tindent(2) + \"is.close();\",\n\t\t\t\t\t\tindent(1) + '}',\n\t\t\t\t\t\t\"}\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (0 != 0) {\");\n\n\t\t// impossible to prove that variables should be merged, so can't restore finally block here\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally7.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestTryCatchFinally7 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate int f = 0;\n\n\t\tprivate boolean test(Object obj) {\n\t\t\tboolean res;\n\t\t\ttry {\n\t\t\t\tres = exc(obj);\n\t\t\t} catch (Exception e) {\n\t\t\t\tres = false;\n\t\t\t} finally {\n\t\t\t\tf++;\n\t\t\t}\n\t\t\treturn res;\n\t\t}\n\n\t\tprivate boolean exc(Object obj) throws Exception {\n\t\t\tif (\"r\".equals(obj)) {\n\t\t\t\tthrow new AssertionError();\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tf = 0;\n\t\t\tassertThat(test(null)).isTrue();\n\t\t\tassertThat(f).isEqualTo(1);\n\n\t\t\tf = 0;\n\t\t\ttry {\n\t\t\t\ttest(\"r\");\n\t\t\t} catch (AssertionError e) {\n\t\t\t\t// pass\n\t\t\t}\n\t\t\tassertThat(f).isEqualTo(1);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"try {\")\n\t\t\t\t.contains(\"exc(obj);\")\n\t\t\t\t.contains(\"} catch (Exception e) {\")\n\t\t\t\t.doesNotContain(\"throw th;\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"throw th;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally8.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTryCatchFinally8 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic Object test(Object obj) {\n\t\t\tFile file = new File(\"r\");\n\t\t\tFileOutputStream output = null;\n\t\t\ttry {\n\t\t\t\toutput = new FileOutputStream(file);\n\t\t\t\tif (obj.equals(\"a\")) {\n\t\t\t\t\treturn new Object();\n\t\t\t\t} else {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t} catch (IOException e) {\n\t\t\t\tSystem.out.println(\"Exception\");\n\t\t\t\treturn null;\n\t\t\t} finally {\n\t\t\t\tif (output != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\toutput.close();\n\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\t// Ignored\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfile.delete();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\t@NotYetImplemented(\"Fix merged catch blocks (shared code between catches)\")\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"try {\")\n\t\t\t\t.contains(\"} catch (IOException e) {\")\n\t\t\t\t.contains(\"} finally {\")\n\t\t\t\t.contains(\"file.delete();\");\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tdisableCompilation();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"output = new FileOutputStream(file);\")\n\t\t\t\t.contains(\"} catch (IOException e) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally9.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Scanner;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTryCatchFinally9 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic String test() throws IOException {\n\t\t\tInputStream input = null;\n\t\t\ttry {\n\t\t\t\tinput = this.getClass().getResourceAsStream(\"resource\");\n\t\t\t\tScanner scanner = new Scanner(input).useDelimiter(\"\\\\A\");\n\t\t\t\treturn scanner.hasNext() ? scanner.next() : \"\";\n\t\t\t} finally {\n\t\t\t\tif (input != null) {\n\t\t\t\t\tinput.close();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"JADX INFO: finally extract failed\")\n\t\t\t\t.doesNotContain(indent() + \"throw \")\n\t\t\t\t.containsOne(\"} finally {\")\n\t\t\t\t.containsOne(\"if (input != null) {\")\n\t\t\t\t.containsOne(\"input.close();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchInIf.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestTryCatchInIf extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tprivate String test(String name, String value) {\n\t\t\tif (value != null) {\n\t\t\t\ttry {\n\t\t\t\t\tint key;\n\t\t\t\t\tif (value.startsWith(\"0x\")) {\n\t\t\t\t\t\tvalue = value.substring(2);\n\t\t\t\t\t\tkey = Integer.parseInt(value, 16);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tkey = Integer.parseInt(value);\n\t\t\t\t\t}\n\t\t\t\t\treturn name + '=' + key;\n\t\t\t\t} catch (NumberFormatException e) {\n\t\t\t\t\treturn \"Failed to parse number\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tSystem.out.println(\"?\");\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(\"n\", null)).isNull();\n\t\t\tassertThat(test(\"n\", \"7\")).isEqualTo(\"n=7\");\n\t\t\tassertThat(test(\"n\", \"0x\" + Integer.toHexString(77))).isEqualTo(\"n=77\");\n\t\t\tassertThat(test(\"n\", \"abc\")).isEqualTo(\"Failed to parse number\");\n\t\t\tassertThat(test(\"n\", \"0xabX\")).isEqualTo(\"Failed to parse number\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"try {\")\n\t\t\t\t.containsOne(\"} catch (NumberFormatException e) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchInIf2.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\n// #2384\npublic class TestTryCatchInIf2 extends IntegrationTest {\n\tpublic static class TestCls {\n\t\tpublic void test(Class<?> cls) {\n\t\t\tObject obj = null;\n\t\t\tif (cls != null) {\n\t\t\t\ttry {\n\t\t\t\t\tobj = cls.getDeclaredConstructor().newInstance();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tSystem.out.println(\"error\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tSystem.out.println(\"obj = \" + obj);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\t// happens only without debug info and java version >= 10\n\t\tnoDebugInfo();\n\t\tuseTargetJavaVersion(10);\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code();\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchLastInsn.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatchLastInsn extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic Exception test() {\n\t\t\t? r1 = \"result\"; // String\n\t\t\ttry {\n\t\t\t\tr1 = call(); // Exception\n\t\t\t} catch (Exception e) {\n\t\t\t\tSystem.out.println(r1); // String\n\t\t\t\tr1 = e;\n\t\t\t}\n\t\t\treturn r1;\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return call();\")\n\t\t\t\t.containsOne(\"} catch (Exception e) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchMultiException.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.security.ProviderException;\nimport java.time.DateTimeException;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatchMultiException extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test() {\n\t\t\ttry {\n\t\t\t\tSystem.out.println(\"Test\");\n\t\t\t} catch (ProviderException | DateTimeException e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tString catchExcVarName = \"e\";\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"} catch (ProviderException | DateTimeException \" + catchExcVarName + \") {\")\n\t\t\t\t.containsOne(\"throw new RuntimeException(\" + catchExcVarName + \");\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchMultiException2.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"CommentedOutCode\")\npublic class TestTryCatchMultiException2 extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n    public static boolean test() {\n        try {\n            Class<?> cls = Class.forName(\"c\");\n            return ((Boolean) cls.getMethod(\"b\", new Class[0]).invoke(cls, new Object[0])).booleanValue();\n        } catch (ClassNotFoundException | NoSuchMethodException | Exception | Throwable unused) {\n        \t// java compiler don't allow shadow subclasses in multi-catch\n        \t// in this case leave only Throwable\n            return false;\n        }\n    }\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"} catch (Throwable unused) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchNoMoveExc.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatchNoMoveExc extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tprivate static void test(AutoCloseable closeable) {\n\t\t\tif (closeable != null) {\n\t\t\t\ttry {\n\t\t\t\t\tcloseable.close();\n\t\t\t\t} catch (Exception ignored) {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliWithPkg(\"trycatch\", \"TestTryCatchNoMoveExc\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (autoCloseable != null) {\")\n\t\t\t\t.containsOne(\"try {\")\n\t\t\t\t.containsOne(\"autoCloseable.close();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchNoMoveExc2.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Issue: https://github.com/skylot/jadx/issues/395\n */\npublic class TestTryCatchNoMoveExc2 extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tprivate static void test(AutoCloseable closeable) {\n\t\t\tif (closeable != null) {\n\t\t\t\ttry {\n\t\t\t\t\tcloseable.close();\n\t\t\t\t} catch (Exception unused) {\n\t\t\t\t}\n\t\t\t\tSystem.nanoTime();\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliWithPkg(\"trycatch\", \"TestTryCatchNoMoveExc2\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"try {\")\n\t\t\t\t.containsLines(2,\n\t\t\t\t\t\t\"} catch (Exception unused) {\",\n\t\t\t\t\t\t\"}\",\n\t\t\t\t\t\t\"System.nanoTime();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchStartOnMove.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryCatchStartOnMove extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tprivate static void test(String s) {\n\t\t\ttry {\n\t\t\t\tcall(s);\n\t\t\t} catch (Exception unused) {\n\t\t\t\tSystem.out.println(\"Failed call for \" + s);\n\t\t\t}\n\t\t}\n\n\t\tprivate static void call(String s) {}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliWithPkg(\"trycatch\", \"TestTryCatchStartOnMove\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"try {\")\n\t\t\t\t.containsOne(\"} catch (Exception e) {\")\n\t\t\t\t.containsOne(\"System.out.println(\\\"Failed call for \\\" + str\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryWithEmptyCatch.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.util.Properties;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTryWithEmptyCatch extends IntegrationTest {\n\n\tpublic static class TestCls extends Exception {\n\t\tprivate static final long serialVersionUID = -5723049816464070603L;\n\t\tprivate Properties field;\n\n\t\tpublic TestCls(String str) {\n\t\t\tsuper(str);\n\t\t\tProperties properties = null;\n\t\t\ttry {\n\t\t\t\tif (str.contains(\"properties\")) {\n\t\t\t\t\tproperties = new Properties();\n\t\t\t\t}\n\t\t\t} catch (Exception unused) {\n\t\t\t\t// empty\n\t\t\t}\n\t\t\tthis.field = properties;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"try {\")\n\t\t\t\t.containsOne(\"if (\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryWithEmptyCatchTriple.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTryWithEmptyCatchTriple extends SmaliTest {\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t// all catches are empty\n\t\t\t\t.containsLines(2, \"} catch (Error unused) {\", \"}\")\n\t\t\t\t.containsLines(2, \"} catch (Error unused2) {\", \"}\")\n\t\t\t\t.containsLines(2, \"} catch (Error unused3) {\", \"}\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryWithResources.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.SmaliTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTryWithResources extends SmaliTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static void writeFully(File file, byte[] data) throws IOException {\n\t\t\ttry (OutputStream out = new FileOutputStream(file)) {\n\t\t\t\tout.write(data);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\t@NotYetImplemented\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"try (\")\n\t\t\t\t.doesNotContain(\"close()\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestUnreachableCatch.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n@SuppressWarnings(\"CommentedOutCode\")\npublic class TestUnreachableCatch extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tprivate static Map<Uri, ByteBuffer> prepareFontData(Context context, FontInfo[] fonts,\n\t\t\tCancellationSignal cancellationSignal) {\n\t\tfinal HashMap<Uri, ByteBuffer> out = new HashMap<>();\n\t\tfinal ContentResolver resolver = context.getContentResolver();\n\n\t\tfor (FontInfo font : fonts) {\n\t\t\tif (font.getResultCode() != Columns.RESULT_CODE_OK) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfinal Uri uri = font.getUri();\n\t\t\tif (out.containsKey(uri)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tByteBuffer buffer = null;\n\t\t\ttry (final ParcelFileDescriptor pfd =\n\t\t\t\t\tresolver.openFileDescriptor(uri, \"r\", cancellationSignal)) {\n\t\t\t\tif (pfd != null) {\n\t\t\t\t\ttry (final FileInputStream fis =\n\t\t\t\t\t\t\tnew FileInputStream(pfd.getFileDescriptor())) {\n\t\t\t\t\t\tfinal FileChannel fileChannel = fis.getChannel();\n\t\t\t\t\t\tfinal long size = fileChannel.size();\n\t\t\t\t\t\tbuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);\n\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\t// ignore\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (IOException e) {\n\t\t\t\t// ignore\n\t\t\t}\n\n\t\t\t// TODO: try other approach?, e.g. read all contents instead of mmap.\n\n\t\t\tout.put(uri, buffer);\n\t\t}\n\t\treturn Collections.unmodifiableMap(out);\n\t}\n\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tallowWarnInCode();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.contains(\"IOException\")\n\t\t\t\t.contains(\"Collections.unmodifiableMap\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/trycatch/TestUnreachableCatch2.java",
    "content": "package jadx.tests.integration.trycatch;\n\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.FileChannel;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestUnreachableCatch2 extends SmaliTest {\n\n\t@SuppressWarnings({ \"unused\", \"DataFlowIssue\" })\n\tpublic static class UnusedExceptionHandlers1 implements AutoCloseable {\n\t\tpublic static void test(final Object unused1, final Object[] array, final Object o1,\n\t\t\t\tfinal Object o2, final Object unused2) {\n\t\t\tfor (final Object item : array) {\n\t\t\t\tByteBuffer buffer = null;\n\t\t\t\ttry (final UnusedExceptionHandlers1 u = doSomething2(o1, \"\", o2)) {\n\t\t\t\t\ttry (final FileInputStream fis = new FileInputStream(u.getFilename())) {\n\t\t\t\t\t\tfinal FileChannel fileChannel = fis.getChannel();\n\t\t\t\t\t\tbuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, 42);\n\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\t// ignore\n\t\t\t\t\t}\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate String getFilename() {\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate static UnusedExceptionHandlers1 doSomething2(final Object o1, final String s,\n\t\t\t\tfinal Object o2) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() throws IOException {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\t// TODO: result code not compilable because 'try' block split into 2 block and 'fis' var become\n\t\t// uninitialized\n\t\tdisableCompilation();\n\t\tassertThat(getClassNode(UnusedExceptionHandlers1.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"break;\")\n\t\t\t\t.countString(2, \"} catch (IOException e\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestArrayTypes.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestArrayTypes extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic void test() {\n\t\t\tException e = new Exception();\n\t\t\tSystem.out.println(e);\n\t\t\tuse(new Object[] { e });\n\t\t}\n\n\t\tpublic void use(Object[] arr) {\n\t\t}\n\n\t\tpublic void check() {\n\t\t\ttest();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"use(new Object[]{e});\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"use(new Object[]{exc});\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestConstInline.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestConstInline extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tprivate static String test(boolean b) {\n\t\t\tList<String> list;\n\t\t\tString str;\n\t\t\tif (b) {\n\t\t\t\tlist = Collections.emptyList();\n\t\t\t\tstr = \"1\";\n\t\t\t} else {\n\t\t\t\tlist = null;\n\t\t\t\tstr = list; // not correct assign in java but bytecode allow it\n\t\t\t}\n\t\t\treturn use(list, str);\n\t\t}\n\n\t\tprivate static String use(List<String> list, String str) {\n\t\t\treturn list + str;\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliWithPkg(\"types\", \"TestConstInline\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"list = null;\")\n\t\t\t\t.containsOne(\"str = null;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestConstTypeInference.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestConstTypeInference extends IntegrationTest {\n\n\t@SuppressWarnings({ \"overrides\", \"EqualsHashCode\" })\n\tpublic static class TestCls {\n\t\tprivate final int a;\n\n\t\tpublic TestCls() {\n\t\t\tthis(0);\n\t\t}\n\n\t\tpublic TestCls(int a) {\n\t\t\tthis.a = a;\n\t\t}\n\n\t\tpublic boolean equals(Object obj) {\n\t\t\tif (obj == this) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (obj != null) {\n\t\t\t\tif (getClass() == obj.getClass()) {\n\t\t\t\t\tTestCls other = (TestCls) obj;\n\t\t\t\t\treturn this.a == other.a;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tTestCls seven = new TestCls(7);\n\t\t\tassertThat(seven).isEqualTo(seven);\n\t\t\tassertThat(seven).isNotEqualTo(null);\n\n\t\t\tTestCls six = new TestCls(6);\n\t\t\tassertThat(six).isNotEqualTo(seven);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"obj == this\")\n\t\t\t\t.containsOneOf(\"obj == null\", \"obj != null\");\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestFieldAccess.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestFieldAccess extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate String field;\n\n\t\tstatic <T extends TestCls> T testPut(T t) {\n\t\t\t((TestCls) t).field = \"\";\n\t\t\treturn t;\n\t\t}\n\n\t\tstatic <T extends TestCls> T testGet(T t) {\n\t\t\tSystem.out.println(((TestCls) t).field);\n\t\t\treturn t;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"t.field\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestFieldCast.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Issue #962\n */\npublic class TestFieldCast extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static class A {\n\t\t\tpublic boolean publicField;\n\t\t\tboolean packagePrivateField;\n\t\t\tprotected boolean protectedField;\n\t\t\tprivate boolean privateField;\n\t\t}\n\n\t\tpublic static class B extends A {\n\t\t\tpublic void test() {\n\t\t\t\t((A) this).publicField = false;\n\t\t\t\t((A) this).protectedField = false;\n\t\t\t\t((A) this).packagePrivateField = false;\n\t\t\t\t((A) this).privateField = false; // cast to 'A' needed only here\n\t\t\t}\n\t\t}\n\n\t\tpublic static class C {\n\t\t\tpublic void test(B b) {\n\t\t\t\t((A) b).publicField = false;\n\t\t\t\t((A) b).protectedField = false;\n\t\t\t\t((A) b).packagePrivateField = false;\n\t\t\t\t((A) b).privateField = false; // cast to 'A' needed only here\n\t\t\t}\n\t\t}\n\n\t\tprivate static class D {\n\t\t\tpublic <T extends B> void test(T t) {\n\t\t\t\t((A) t).publicField = false;\n\t\t\t\t((A) t).protectedField = false;\n\t\t\t\t((A) t).packagePrivateField = false;\n\t\t\t\t((A) t).privateField = false; // cast to 'A' needed only here\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"((A) this)\")\n\t\t\t\t.containsOne(\"((A) b)\")\n\t\t\t\t.containsOne(\"((A) t)\")\n\t\t\t\t.doesNotContain(\"unused =\")\n\t\t\t\t.doesNotContain(\"access modifiers changed\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestGenerics extends IntegrationTest {\n\n\tpublic static class TestCls<T> {\n\t\tpublic T data;\n\n\t\tpublic TestCls<T> data(T t) {\n\t\t\tthis.data = t;\n\t\t\treturn this;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"TestCls<T> data(T t) {\");\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"TestCls<T> data(T t) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics2.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestGenerics2 extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic void test() {\n\t\t\tMap<Integer, String> map = this.field;\n\t\t\tuseInt(map.size());\n\t\t\tfor (Map.Entry<Integer, String> entry : map.entrySet()) {\n\t\t\t\tuseInt(entry.getKey().intValue());\n\t\t\t\tentry.getValue().trim();\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"for (Map.Entry<Integer, String> entry : map.entrySet()) {\")\n\t\t\t\t.containsOne(\"useInt(entry.getKey().intValue());\") // no Integer cast\n\t\t\t\t.containsOne(\"entry.getValue().trim();\"); // no String cast\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics3.java",
    "content": "package jadx.tests.integration.types;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestGenerics3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static void test() {\n\t\t\tList<String> classes = getClasses();\n\t\t\tCollections.sort(classes);\n\t\t\tint passed = 0;\n\t\t\tfor (String cls : classes) {\n\t\t\t\tif (runTest(cls)) {\n\t\t\t\t\tpassed++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tint failed = classes.size() - passed;\n\t\t\tSystem.out.println(\"failed: \" + failed);\n\t\t}\n\n\t\tprivate static boolean runTest(String clsName) {\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate static List<String> getClasses() {\n\t\t\treturn new ArrayList<>();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"List<String> classes\")\n\t\t\t\t.containsOne(\"for (String cls : classes) {\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"List<String> classes\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics4.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestGenerics4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static class Inner<T> {\n\t\t\tpublic void overload(IList<? super T> list) {\n\t\t\t}\n\n\t\t\tpublic void overload(T t) {\n\t\t\t}\n\t\t}\n\n\t\tpublic interface IList<T> {\n\t\t\tvoid list(T t);\n\t\t}\n\n\t\tpublic static class ObjIList implements IList<Object> {\n\t\t\t@Override\n\t\t\tpublic void list(Object o) {\n\t\t\t}\n\t\t}\n\n\t\tpublic Inner<Object> test() {\n\t\t\tInner<Object> inner = new Inner<>();\n\t\t\tinner.overload(new ObjIList());\n\t\t\treturn inner;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public static class ObjIList implements IList<Object> {\")\n\t\t\t\t.containsOne(\"Inner<Object> inner = new Inner<>();\")\n\t\t\t\t.containsOne(\"inner.overload((IList<? super Object>) new ObjIList());\");\n\t}\n\n\t@NotYetImplemented\n\t@Test\n\tpublic void testOmitCast() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"inner.overload(new ObjIList());\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics5.java",
    "content": "package jadx.tests.integration.types;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestGenerics5 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate InheritableThreadLocal<Map<String, String>> inheritableThreadLocal;\n\n\t\tpublic void test(String key, String val) {\n\t\t\tif (key == null) {\n\t\t\t\tthrow new IllegalArgumentException(\"key cannot be null\");\n\t\t\t}\n\t\t\tMap<String, String> map = this.inheritableThreadLocal.get();\n\t\t\tif (map == null) {\n\t\t\t\tmap = new HashMap<>();\n\t\t\t\tthis.inheritableThreadLocal.set(map);\n\t\t\t}\n\t\t\tmap.put(key, val);\n\t\t}\n\n\t\tpublic void remove(String key) {\n\t\t\tMap<String, String> map = this.inheritableThreadLocal.get();\n\t\t\tif (map != null) {\n\t\t\t\tmap.remove(key);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.countString(2, \"Map<String, String> map = this.inheritableThreadLocal.get();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics6.java",
    "content": "package jadx.tests.integration.types;\n\nimport java.util.Iterator;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestGenerics6 extends IntegrationTest {\n\n\tpublic static class TestCls<K, V> implements Iterable<Map.Entry<K, V>> {\n\t\tpublic V test(K key, V v) {\n\t\t\tEntry<K, V> entry = get(key);\n\t\t\tif (entry != null) {\n\t\t\t\treturn entry.mValue;\n\t\t\t}\n\t\t\tput(key, v);\n\t\t\treturn null;\n\t\t}\n\n\t\tprotected Entry<K, V> get(K k) {\n\t\t\treturn null;\n\t\t}\n\n\t\tprotected Entry<K, V> put(K key, V v) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Iterator<Map.Entry<K, V>> iterator() {\n\t\t\treturn null;\n\t\t}\n\n\t\tstatic class Entry<K, V> implements Map.Entry<K, V> {\n\t\t\tfinal V mValue;\n\n\t\t\tEntry(K key, V value) {\n\t\t\t\tthis.mValue = value;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic K getKey() {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic V getValue() {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic V setValue(V value) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"Entry entry = get(k);\")\n\t\t\t\t.containsOne(\"Entry<K, V> entry = get(k);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics7.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Issue https://github.com/skylot/jadx/issues/956\n */\npublic class TestGenerics7 extends IntegrationTest {\n\n\tpublic static class TestCls<T> {\n\t\tprivate Object[] elements = new Object[1];\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic final T test(int i) {\n\t\t\tObject[] arr = this.elements;\n\t\t\tT obj = (T) arr[i];\n\t\t\tarr[i] = null;\n\t\t\tif (obj == null) {\n\t\t\t\tthrow new NullPointerException();\n\t\t\t}\n\t\t\treturn obj;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tthis.elements = new Object[] { 1, \"\" };\n\t\t\tassertThat(test(1)).isEqualTo(\"\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"T t = (T) objArr[i];\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics8.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestGenerics8 extends IntegrationTest {\n\n\tpublic static class TestCls<T> {\n\n\t\tpublic abstract static class Class2<S extends I1 & I2> extends Parent2<S> {\n\t\t\tpublic void test() {\n\t\t\t\tS s = get();\n\t\t\t\ts.i1();\n\t\t\t\ts.i2();\n\t\t\t}\n\t\t}\n\n\t\tstatic class Parent2<T extends I1> {\n\t\t\tT t;\n\n\t\t\tprotected T get() {\n\t\t\t\treturn t;\n\t\t\t}\n\t\t}\n\n\t\tinterface I1 {\n\t\t\tvoid i1();\n\t\t}\n\n\t\tinterface I2 {\n\t\t\tvoid i2();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"S s = get();\")\n\t\t\t\t.containsOne(\"s.i1();\")\n\t\t\t\t.containsOne(\"s.i2();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestGenericsInFullInnerCls.java",
    "content": "package jadx.tests.integration.types;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.CommentsLevel;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestGenericsInFullInnerCls extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tgetArgs().setCommentsLevel(CommentsLevel.WARN);\n\t\tList<ClassNode> classNodes = loadFromSmaliFiles();\n\n\t\tassertThat(searchCls(classNodes, \"types.FieldCls\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"private ba<n>.bb<n, n> a;\");\n\n\t\tassertThat(searchCls(classNodes, \"types.test.ba\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public final class ba<S> {\")\n\t\t\t\t.containsOne(\"public final class bb<T, V extends n> {\")\n\t\t\t\t.containsOne(\"private ba<S> b;\")\n\t\t\t\t.containsOne(\"private ba<S>.bb<T, V>.bc<T, V> c;\")\n\t\t\t\t.containsOne(\"public final class bc<T, V extends n> {\")\n\t\t\t\t.containsOne(\"private ba<S> a;\");\n\t}\n\n\t@Test\n\tpublic void testWithDeobf() {\n\t\tenableDeobfuscation();\n\t\targs.setDeobfuscationMinLength(100); // rename everything\n\n\t\tgetArgs().setCommentsLevel(CommentsLevel.WARN);\n\t\tloadFromSmaliFiles();\n\t\t// compilation should pass\n\t}\n\n\t@Test\n\tpublic void testWithFullNames() {\n\t\tgetArgs().setUseImports(false);\n\t\tgetArgs().setCommentsLevel(CommentsLevel.WARN);\n\t\tloadFromSmaliFiles();\n\t\t// compilation should pass\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestInterfacesCast.java",
    "content": "package jadx.tests.integration.types;\n\nimport java.io.Closeable;\nimport java.io.IOException;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInterfacesCast extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic Runnable test(Closeable obj) throws IOException {\n\t\t\treturn (Runnable) obj;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return (Runnable) closeable;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestLongCast.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestLongCast extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic long test(char c) {\n\t\t\treturn (long) c << 32;\n\t\t}\n\n\t\tpublic int test2(long l) {\n\t\t\treturn (int) l >> 2;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test((char) 22)).isEqualTo(94489280512L);\n\t\t\tassertThat(test2((1L << 32) + 8)).isEqualTo(2);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOneOf(\n\t\t\t\t\t\t\"return (long) c << 32;\",\n\t\t\t\t\t\t\"return ((long) c) << 32;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestPrimitiveConversion.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestPrimitiveConversion extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic void test(long j, boolean z) {\n\t\t\tputByte(j, z ? (byte) 1 : (byte) 0);\n\t\t}\n\n\t\tprivate static void putByte(long j, byte z) {\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"putByte(j, z);\")\n\t\t\t\t.containsOne(\"putByte(j, z ? (byte) 1 : (byte) 0);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestPrimitiveConversion2.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestPrimitiveConversion2 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"boolean z2 = !convertedPrice2.code.equals(itemCurrency.code);\")\n\t\t\t\t.doesNotContain(\"z2 == 0\")\n\t\t\t\t.doesNotContain(\"z2 | 2\")\n\t\t\t\t.containsOne(\"(z2 ? 1 : 0) | 2\")\n\t\t\t\t.containsOne(\"if (z2 && currency != null) {\")\n\t\t\t\t.containsOne(\"i = 1;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestPrimitivesInIf.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestPrimitivesInIf extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic boolean test(String str) {\n\t\t\tshort sh = Short.parseShort(str);\n\t\t\tint i = Integer.parseInt(str);\n\t\t\tSystem.out.println(sh + \" vs \" + i);\n\t\t\treturn sh == i;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(test(\"1\")).isTrue();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"short sh = Short.parseShort(str);\")\n\t\t\t\t.containsOne(\"int i = Integer.parseInt(str);\")\n\t\t\t\t.containsOne(\"return sh == i;\");\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"short s = Short.parseShort(str);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeInheritance.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTypeInheritance extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic interface IRoot {\n\t\t}\n\n\t\tpublic interface IBase extends IRoot {\n\t\t}\n\n\t\tpublic static class A implements IBase {\n\t\t}\n\n\t\tpublic static class B implements IBase {\n\t\t\tpublic void b() {\n\t\t\t}\n\t\t}\n\n\t\tpublic static void test(boolean z) {\n\t\t\tIBase impl;\n\t\t\tif (z) {\n\t\t\t\timpl = new A();\n\t\t\t} else {\n\t\t\t\tB b = new B();\n\t\t\t\tb.b();\n\t\t\t\timpl = b; // this move is removed in no-debug byte-code\n\t\t\t}\n\t\t\tuseBase(impl);\n\t\t\tuseRoot(impl);\n\t\t}\n\n\t\tprivate static void useRoot(IRoot root) {\n\t\t}\n\n\t\tprivate static void useBase(IBase base) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"IBase impl;\")\n\t\t\t\t.containsOne(\"impl = new A();\")\n\t\t\t\t.containsOne(\"B b = new B();\")\n\t\t\t\t.containsOne(\"impl = b;\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTypeResolver extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic TestCls(int b1, int b2) {\n\t\t\t// test 'this' move and constructor invocation on moved register\n\t\t\tthis(b1, b2, 0, 0, 0);\n\t\t}\n\n\t\tpublic TestCls(int a1, int a2, int a3, int a4, int a5) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"this(b1, b2, 0, 0, 0);\")\n\t\t\t\t.doesNotContain(\"= this;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver10.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver10 extends SmaliTest {\n\n\t/*\n\t * Method argument assigned with different types in separate branches\n\t */\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code().containsOne(\"Object test(String str, String str2)\")\n\t\t\t\t.doesNotContain(\"Object obj2 = 0;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver11.java",
    "content": "package jadx.tests.integration.types;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver11 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic Void test(Object... objects) {\n\t\t\tint val = (Integer) objects[0];\n\t\t\tString str = (String) objects[1];\n\t\t\tcall(str, str, val, val);\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate void call(String a, String b, int... val) {\n\t\t}\n\n\t\tprivate boolean test2(String s1, String... args) {\n\t\t\tString str = Arrays.toString(args);\n\t\t\treturn s1.length() + str.length() > 0;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\ttest(1, \"str\");\n\t\t\tassertThat(test2(\"1\", \"2\", \"34\")).isTrue();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"(Integer) objects[0]\")\n\t\t\t\t.containsOne(\"String str = (String) objects[1];\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"(Integer) objArr[0]\")\n\t\t\t\t.containsOne(\"String str = (String) objArr[1];\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver12.java",
    "content": "package jadx.tests.integration.types;\n\nimport java.lang.ref.WeakReference;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver12 extends IntegrationTest {\n\n\tpublic abstract static class TestCls<T> {\n\t\tprivate WeakReference<T> ref;\n\n\t\tpublic void test(String str) {\n\t\t\tT obj = this.ref.get();\n\t\t\tif (obj != null) {\n\t\t\t\tcall(obj, str);\n\t\t\t}\n\t\t}\n\n\t\tpublic abstract void call(T t, String str);\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"T obj = this.ref.get();\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"Object obj\")\n\t\t\t\t.containsOne(\"T t = this.ref.get();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver13.java",
    "content": "package jadx.tests.integration.types;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver13 extends IntegrationTest {\n\n\t@SuppressWarnings(\"MismatchedQueryAndUpdateOfCollection\")\n\tpublic static class TestCls {\n\t\tprivate static final Set<?> CONST = new HashSet<>();\n\t\tprivate Map<Set<?>, List<?>> map = new HashMap<>();\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic <T> List<T> test(Set<T> type) {\n\t\t\tList<?> obj = this.map.get(type == null ? CONST : type);\n\t\t\tif (obj != null) {\n\t\t\t\treturn (List<T>) obj;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@NotYetImplemented(\"additional cast for generic types\")\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"public <T> List<T> test(Set<T> type) {\")\n\t\t\t\t.containsOne(\"return (List<T>) obj;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver14.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver14 extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic Date test() throws Exception {\n\t\t\tDate date = null;\n\t\t\tLong l = null;\n\t\t\tCursor query = DBUtil.query(false, (CancellationSignal) null);\n\t\t\ttry {\n\t\t\t\tif (query.moveToFirst()) {\n\t\t\t\t\tif (!query.isNull(0)) {\n\t\t\t\t\t\tl = Long.valueOf(query.getLong(0));\n\t\t\t\t\t}\n\t\t\t\t\tdate = this.this$0.toDate(l);\n\t\t\t\t}\n\t\t\t\treturn date;\n\t\t\t} finally {\n\t\t\t\tquery.close();\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"? r2\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver15.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Issue 921 (second case)\n */\npublic class TestTypeResolver15 extends SmaliTest {\n\n\tpublic static class TestCls {\n\t\tprivate void test(boolean z) {\n\t\t\tuseInt(z ? 0 : 8);\n\t\t\tuseInt(!z ? 1 : 0); // replaced with xor in smali test\n\t\t}\n\n\t\tprivate void useInt(int i) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t// .containsOne(\"useInt(!z ? 1 : 0);\") // TODO: convert to ternary\n\t\t\t\t.containsOne(\"useInt(z ? 0 : 8);\");\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"useInt(z ? 0 : 8);\")\n\t\t\t\t.containsOne(\"useInt(!z ? 1 : 0);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver16.java",
    "content": "package jadx.tests.integration.types;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\nimport static java.util.Collections.emptyList;\n\n/**\n * Issue 1002\n * Insertion of additional cast (at use place) needed for successful type inference\n */\npublic class TestTypeResolver16 extends SmaliTest {\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static class TestCls {\n\n\t\tpublic final <T, K> List<T> test(List<? extends T> list,\n\t\t\t\tSet<? extends T> set, Function<? super T, ? extends K> function) {\n\t\t\tif (set != null) {\n\t\t\t\tList<? extends T> union = list != null ? union(list, set, function) : null;\n\t\t\t\tif (union != null) {\n\t\t\t\t\tlist = union;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn list != null ? (List<T>) list : emptyList();\n\t\t}\n\n\t\tpublic static <T, K> List<T> union(\n\t\t\t\tCollection<? extends T> collection,\n\t\t\t\tIterable<? extends T> iterable,\n\t\t\t\tFunction<? super T, ? extends K> function) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"(List<T>) listUnion\");\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"(List<T>) listUnion\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver17.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Issue 1197\n */\npublic class TestTypeResolver17 extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tprivate static String test(Context context, Uri uri, String str, String str2) {\n\t\t\tCursor cursor = null;\n\t\t\ttry {\n\t\t\t\tcursor = context.getContentResolver().query(uri, new String[]{str}, null, null, null);\n\t\t\t\tif (cursor.moveToFirst() && !cursor.isNull(0)) {\n\t\t\t\t\treturn cursor.getString(0);\n\t\t\t\t}\n\t\t\t\tcloseQuietly(cursor);\n\t\t\t\treturn str2;\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.w(\"DocumentFile\", \"Failed query: \" + e);\n\t\t\t\treturn str2;\n\t\t\t} finally {\n\t\t\t\tcloseQuietly(cursor);\n\t\t\t}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"Cursor cursorQuery = null;\")\n\t\t\t\t.doesNotContain(\"(AutoCloseable autoCloseable = \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver18.java",
    "content": "package jadx.tests.integration.types;\n\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver18 extends IntegrationTest {\n\n\tpublic static class TestCls<T> {\n\t\tprivate final AtomicReference<T> reference = new AtomicReference<>();\n\n\t\tpublic void test() {\n\t\t\tT t = this.reference.get();\n\t\t\tif (t instanceof Closeable) {\n\t\t\t\ttry {\n\t\t\t\t\t((Closeable) t).close();\n\t\t\t\t} catch (IOException unused) {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.reference.set(null);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"((Closeable) t).close();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver19.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Issue 1407\n */\npublic class TestTypeResolver19 extends SmaliTest {\n\n\tpublic static class TestCls {\n\t\tpublic static int[] test(byte[] bArr) {\n\t\t\tint[] iArr = new int[bArr.length];\n\t\t\tfor (int i = 0; i < bArr.length; i++) {\n\t\t\t\tiArr[i] = bArr[i];\n\t\t\t}\n\t\t\treturn iArr;\n\t\t}\n\n\t\tpublic static int[] test2(byte[] bArr) {\n\t\t\tint[] iArr = new int[bArr.length];\n\t\t\tfor (int i = 0; i < bArr.length; i++) {\n\t\t\t\tint i2 = bArr[i];\n\t\t\t\tif (i2 < 0) {\n\t\t\t\t\ti2 = (int) ((long) i2 & 0xFFFF_FFFFL);\n\t\t\t\t}\n\t\t\t\tiArr[i] = i2;\n\t\t\t}\n\t\t\treturn iArr;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"iArr[i] = bArr[i];\")\n\t\t\t\t.containsOne(\"iArr[i] = i2;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver2.java",
    "content": "package jadx.tests.integration.types;\n\nimport java.io.IOException;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTypeResolver2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static boolean test(Object obj) throws IOException {\n\t\t\tif (obj != null) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tthrow new IOException();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"if (obj != null) {\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver20.java",
    "content": "package jadx.tests.integration.types;\n\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Issue 1238\n */\npublic class TestTypeResolver20 extends SmaliTest {\n\n\tpublic static class TestCls {\n\t\tpublic interface Sequence<T> {\n\t\t\tIterator<T> iterator();\n\t\t}\n\n\t\tpublic static <T extends Comparable<? super T>> T max(Sequence<? extends T> seq) {\n\t\t\tIterator<? extends T> it = seq.iterator();\n\t\t\tif (!it.hasNext()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tT t = it.next();\n\t\t\twhile (it.hasNext()) {\n\t\t\t\tT next = it.next();\n\t\t\t\tif (t.compareTo(next) < 0) {\n\t\t\t\t\tt = next;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn t;\n\t\t}\n\n\t\tprivate static class ArraySeq<T> implements Sequence<T> {\n\t\t\tprivate final List<T> list;\n\n\t\t\t@SafeVarargs\n\t\t\tpublic ArraySeq(T... arr) {\n\t\t\t\tthis.list = Arrays.asList(arr);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Iterator<T> iterator() {\n\t\t\t\treturn list.iterator();\n\t\t\t}\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tassertThat(max(new ArraySeq<>(2, 5, 3, 4))).isEqualTo(5);\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.JAVA8 })\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"next = next;\")\n\t\t\t\t.containsOne(\"T next = it.next();\");\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tassertThat(getClassNodeFromSmaliFiles())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"T next = it.next();\")\n\t\t\t\t.containsOne(\"T next2 = it.next();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver21.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\n/**\n * Issue 1527\n */\n@SuppressWarnings(\"CommentedOutCode\")\npublic class TestTypeResolver21 extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic Number test(Object objectArray) {\n\t\t\tObject[] arr = (Object[]) objectArray;\n\t\t\treturn (Number) arr[0];\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"Object[] arr = (Object[]) objectArray;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver22.java",
    "content": "package jadx.tests.integration.types;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver22 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test(InputStream input, long count) throws IOException {\n\t\t\tlong pos = input.skip(count);\n\t\t\twhile (pos < count) {\n\t\t\t\tpos += input.skip(count - pos);\n\t\t\t}\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.JAVA8, TestProfile.DX_J8 })\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"long pos = \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver23.java",
    "content": "package jadx.tests.integration.types;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver23 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic long test(int a) {\n\t\t\tlong v = 1L;\n\t\t\tif (a == 2) {\n\t\t\t\tv = 2L;\n\t\t\t} else if (a == 3) {\n\t\t\t\tv = 3L;\n\t\t\t}\n\t\t\tSystem.out.println(v);\n\t\t\treturn v;\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.JAVA8, TestProfile.DX_J8 })\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"long v\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver24.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver24 extends SmaliTest {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\tpublic static class TestCls {\n\t\tpublic void test() {\n\t\t\t((T1) null).foo1();\n\t\t\t((T2) null).foo2();\n\t\t}\n\n\t\tstatic class T1 {\n\t\t\tpublic void foo1() {\n\t\t\t}\n\t\t}\n\n\t\tstatic class T2 {\n\t\t\tpublic void foo2() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.JAVA8 })\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"((T1) null).foo1();\")\n\t\t\t\t.containsOne(\"((T2) null).foo2();\");\n\t}\n\n\t@Test\n\tpublic void testSmali() {\n\t\tassertThat(searchCls(loadFromSmaliFiles(), \"Test1\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"((T1) null).foo1();\")\n\t\t\t\t.containsOne(\"((T2) null).foo2();\")\n\t\t\t\t.doesNotContain(\"T1 \")\n\t\t\t\t.doesNotContain(\"T2 \");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver25.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver25 extends SmaliTest {\n\n\t@Test\n\tpublic void testSmali() {\n\t\t// TODO: type inference error not yet resolved\n\t\t// Check that no stack overflow in type inference for now\n\t\tallowWarnInCode();\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.oneOf(c -> c.containsOne(\"t = obj;\"),\n\t\t\t\t\t\tc -> c.containsOne(\"t = (T) obj;\"));\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver26.java",
    "content": "package jadx.tests.integration.types;\n\nimport java.util.ArrayList;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver26 extends IntegrationTest {\n\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\", \"checkstyle:IllegalType\" })\n\tpublic static class TestCls {\n\t\tfinal ArrayList<String> target = new ArrayList<>();\n\t\tfinal ArrayList source = new ArrayList();\n\n\t\tpublic void test() {\n\t\t\t((ArrayList) target).add(source.get(0)); // cast removed in bytecode\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"type inference failed\")\n\t\t\t\t.containsOne(\"this.target.add((String) this.source.get(0));\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver3.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver3 extends IntegrationTest {\n\n\t@SuppressWarnings(\"UseCompareMethod\")\n\tpublic static class TestCls {\n\n\t\tpublic int test(String s1, String s2) {\n\t\t\tint cmp = s2.compareTo(s1);\n\t\t\tif (cmp != 0) {\n\t\t\t\treturn cmp;\n\t\t\t}\n\t\t\treturn s1.length() == s2.length() ? 0 : s1.length() < s2.length() ? -1 : 1;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tuseJavaInput();\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOneOf(\n\t\t\t\t\t\t\"return s1.length() == s2.length() ? 0 : s1.length() < s2.length() ? -1 : 1;\",\n\t\t\t\t\t\t\"return s1.length() < s2.length() ? -1 : 1;\");\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver4.java",
    "content": "package jadx.tests.integration.types;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tprivate static String test(byte[] strArray, int offset) {\n\t\t\tint len = strArray.length;\n\t\t\tint start = offset + f(strArray, offset);\n\t\t\tint end = start;\n\t\t\twhile (end + 1 < len && (strArray[end] != 0 || strArray[end + 1] != 0)) {\n\t\t\t\tend += 2;\n\t\t\t}\n\t\t\tbyte[] arr = Arrays.copyOfRange(strArray, start, end);\n\t\t\treturn new String(arr);\n\t\t}\n\n\t\tprivate static int f(byte[] strArray, int offset) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tString test = test((\"1234\" + \"utfstr\\0\\0\" + \"4567\").getBytes(), 4);\n\t\t\tassertThat(test).isEqualTo(\"utfstr\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"(strArray[end] != 0 || strArray[end + 1] != 0)\");\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver5.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver5 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"Object string2\")\n\t\t\t\t.doesNotContain(\"r1v2\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver6.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTypeResolver6 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic final Object obj;\n\n\t\tpublic TestCls(boolean b) {\n\t\t\tthis.obj = b ? this : makeObj();\n\t\t}\n\n\t\tpublic Object makeObj() {\n\t\t\treturn new Object();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"this.obj = b ? this : makeObj();\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver6a.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestTypeResolver6a extends IntegrationTest {\n\n\tpublic static class TestCls implements Runnable {\n\t\tpublic final Runnable runnable;\n\n\t\tpublic TestCls(boolean b) {\n\t\t\tthis.runnable = b ? this : makeRunnable();\n\t\t}\n\n\t\tpublic Runnable makeRunnable() {\n\t\t\treturn new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\t// do nothing\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\t// do nothing\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"this.runnable = b ? this : makeRunnable();\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver7.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver7 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic void test(boolean a, boolean b) {\n\t\t\tObject obj = null;\n\t\t\tif (a) {\n\t\t\t\tuse(b ? (Exception) getObj() : (Exception) obj);\n\t\t\t} else {\n\t\t\t\tRunnable r = (Runnable) obj;\n\t\t\t\tif (b) {\n\t\t\t\t\tr = (Runnable) getObj();\n\t\t\t\t}\n\t\t\t\tuse(r);\n\t\t\t}\n\t\t}\n\n\t\tprivate Object getObj() {\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate void use(Exception e) {\n\t\t}\n\n\t\tprivate void use(Runnable r) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.oneOf(c -> c.containsOne(\"use(b ? (Exception) getObj() : null);\"),\n\t\t\t\t\t\tc -> c.containsOne(\"use(b ? (Exception) getObj() : (Exception) null);\"));\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver8.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.NotYetImplemented;\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver8 extends SmaliTest {\n\n\t// @formatter:off\n\t/*\n\t\tpublic class A {}\n\n\t\tpublic class B {\n\t\t\tpublic B(A a) {\n\t\t\t}\n\t\t}\n\n\t\tpublic static class TestCls {\n\t\t\tprivate A f;\n\n\t\t\tpublic void test() {\n\t\t\t\tA x = this.f;\n\t\t\t\tif (x != null) {\n\t\t\t\t\tx = new B(x); // different types, type of 'x' can't be resolved\n\t\t\t\t}\n\t\t\t\tuse(x);\n\t\t\t}\n\n\t\t\tprivate void use(B b) {}\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\t@NotYetImplemented\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmaliFiles(\"types\", \"TestTypeResolver8\", \"TestCls\"))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"use(a != null ? new B(a) : null);\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver9.java",
    "content": "package jadx.tests.integration.types;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestTypeResolver9 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int test(byte b) {\n\t\t\treturn 16777216 * b;\n\t\t}\n\n\t\tpublic int test2(byte[] array, int offset) {\n\t\t\treturn array[offset] * 128 + (array[offset + 1] & 0xFF);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return 16777216 * b;\")\n\t\t\t\t.doesNotContain(\"Byte.MIN_VALUE\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/usethis/TestDontInlineThis.java",
    "content": "package jadx.tests.integration.usethis;\n\nimport java.util.Random;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestDontInlineThis extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int field = new Random().nextInt();\n\n\t\tpublic TestCls test() {\n\t\t\tTestCls res;\n\t\t\tif (field == 7) {\n\t\t\t\tres = this;\n\t\t\t\tSystem.out.println();\n\t\t\t} else {\n\t\t\t\tres = new TestCls();\n\t\t\t}\n\t\t\tres.method();\n\t\t\treturn res;\n\t\t}\n\n\t\tprivate void method() {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"TestDontInlineThis$TestCls res\")\n\t\t\t\t.containsOne(\"res = this;\")\n\t\t\t\t.containsOne(\"res = new TestDontInlineThis$TestCls();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/usethis/TestInlineThis.java",
    "content": "package jadx.tests.integration.usethis;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestInlineThis extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int field;\n\n\t\tpublic void test() {\n\t\t\tTestCls something = this;\n\t\t\tsomething.method();\n\t\t\tsomething.field = 123;\n\t\t}\n\n\t\tprivate void method() {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"something\")\n\t\t\t\t.doesNotContain(\"something.method()\")\n\t\t\t\t.doesNotContain(\"something.field\")\n\t\t\t\t.doesNotContain(\"= this\")\n\t\t\t\t.containsOne(\"this.field = 123;\")\n\t\t\t\t.containsOne(\"method();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/usethis/TestInlineThis2.java",
    "content": "package jadx.tests.integration.usethis;\n\nimport java.util.Objects;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestInlineThis2 extends IntegrationTest {\n\n\t@SuppressWarnings(\"ConstantValue\")\n\tpublic static class TestCls {\n\t\tpublic int field;\n\n\t\tpublic void test() {\n\t\t\tTestCls thisVar = this;\n\t\t\tif (Objects.isNull(thisVar)) {\n\t\t\t\tSystem.out.println(\"null\");\n\t\t\t}\n\t\t\tthisVar.method();\n\t\t\tthisVar.field = 123;\n\t\t}\n\n\t\tprivate void method() {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"thisVar\")\n\t\t\t\t.doesNotContain(\"thisVar.method()\")\n\t\t\t\t.doesNotContain(\"thisVar.field\")\n\t\t\t\t.doesNotContain(\"= this\")\n\t\t\t\t.containsOne(\"if (Objects.isNull(this)) {\")\n\t\t\t\t.containsOne(\"this.field = 123;\")\n\t\t\t\t.containsOne(\"method();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/usethis/TestRedundantThis.java",
    "content": "package jadx.tests.integration.usethis;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestRedundantThis extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic int field1 = 1;\n\t\tpublic int field2 = 2;\n\n\t\tpublic boolean f1() {\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic int method() {\n\t\t\tf1();\n\t\t\treturn field1;\n\t\t}\n\n\t\tpublic void method2(int field2) {\n\t\t\tthis.field2 = field2;\n\t\t}\n\t}\n\n\t// @Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"this.f1();\")\n\t\t\t\t.doesNotContain(\"return this.field1;\")\n\t\t\t\t.contains(\"this.field2 = field2;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/variables/TestThisBranchDup.java",
    "content": "package jadx.tests.integration.variables;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestThisBranchDup extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"this(\") // constructor type correctly detected\n\t\t\t\t.countString(6, \"(i & \"); // ternary used and inlined\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/variables/TestVariables2.java",
    "content": "package jadx.tests.integration.variables;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestVariables2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic Object test(Object s) {\n\t\t\tObject store = s != null ? s : null;\n\t\t\tif (store == null) {\n\t\t\t\tstore = new Object();\n\t\t\t\ts = store;\n\t\t\t}\n\t\t\treturn store;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"Object store = s != null ? s : null;\");\n\t}\n\n\t@Test\n\tpublic void testNoDebug() {\n\t\tnoDebugInfo();\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"Object obj2 = obj != null ? obj : null;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/variables/TestVariables3.java",
    "content": "package jadx.tests.integration.variables;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestVariables3 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tString test(Object s) {\n\t\t\tint i;\n\t\t\tif (s == null) {\n\t\t\t\ti = 2;\n\t\t\t} else {\n\t\t\t\ti = 3;\n\t\t\t\ts = null;\n\t\t\t}\n\t\t\treturn s + \" \" + i;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"int i;\")\n\t\t\t\t.contains(\"i = 2;\")\n\t\t\t\t.contains(\"i = 3;\")\n\t\t\t\t.contains(\"s = null;\")\n\t\t\t\t.contains(\"return s + \\\" \\\" + i;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/variables/TestVariables4.java",
    "content": "package jadx.tests.integration.variables;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\n@SuppressWarnings(\"checkstyle:printstacktrace\")\npublic class TestVariables4 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic static boolean runTest(String clsName) {\n\t\t\ttry {\n\t\t\t\tboolean pass = false;\n\t\t\t\tString msg = null;\n\t\t\t\tThrowable exc = null;\n\n\t\t\t\tClass<?> cls = Class.forName(clsName);\n\t\t\t\tif (cls.getSuperclass() == AbstractTest.class) {\n\t\t\t\t\tMethod mth = cls.getMethod(\"testRun\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\tAbstractTest test = (AbstractTest) cls.getConstructor().newInstance();\n\t\t\t\t\t\tpass = (Boolean) mth.invoke(test);\n\t\t\t\t\t} catch (InvocationTargetException e) {\n\t\t\t\t\t\tpass = false;\n\t\t\t\t\t\texc = e.getCause();\n\t\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\t\tpass = false;\n\t\t\t\t\t\texc = e;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tmsg = \"not extends AbstractTest\";\n\t\t\t\t}\n\t\t\t\tSystem.err.println(\">> \"\n\t\t\t\t\t\t+ (pass ? \"PASS\" : \"FAIL\") + '\\t'\n\t\t\t\t\t\t+ clsName\n\t\t\t\t\t\t+ (msg == null ? \"\" : \"\\t - \" + msg));\n\t\t\t\tif (exc != null) {\n\t\t\t\t\texc.printStackTrace();\n\t\t\t\t}\n\t\t\t\treturn pass;\n\t\t\t} catch (ClassNotFoundException e) {\n\t\t\t\tSystem.err.println(\"Class '\" + clsName + \"' not found\");\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate static class AbstractTest {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.contains(\"} catch (InvocationTargetException e) {\")\n\t\t\t\t.contains(\"pass = false;\")\n\t\t\t\t.contains(\"exc = e.getCause();\")\n\t\t\t\t.contains(\"System.err.println(\\\"Class '\\\" + clsName + \\\"' not found\\\");\")\n\t\t\t\t.contains(\"return pass;\");\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tnoDebugInfo();\n\t\tgetClassNode(TestCls.class);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/variables/TestVariables5.java",
    "content": "package jadx.tests.integration.variables;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestVariables5 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic String f = \"str//ing\";\n\t\tprivate boolean enabled;\n\n\t\tprivate void testIfInLoop() {\n\t\t\tint i = 0;\n\t\t\tfor (int i2 = 0; i2 < f.length(); i2++) {\n\t\t\t\tchar ch = f.charAt(i2);\n\t\t\t\tif (ch == '/') {\n\t\t\t\t\ti++;\n\t\t\t\t\tif (i == 2) {\n\t\t\t\t\t\tsetEnabled(true);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tsetEnabled(false);\n\t\t}\n\n\t\tprivate void setEnabled(boolean b) {\n\t\t\tthis.enabled = b;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\tsetEnabled(false);\n\t\t\ttestIfInLoop();\n\t\t\tassertThat(enabled).isTrue();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls)\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"int i2++;\")\n\t\t\t\t.containsOne(\"int i = 0;\")\n\t\t\t\t.containsOneOf(\"i++;\", \"&& (i = i + 1) == 2\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/variables/TestVariables6.java",
    "content": "package jadx.tests.integration.variables;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestVariables6 extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmaliWithPath(\"variables\", \"TestVariables6\"))\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"r4\")\n\t\t\t\t.doesNotContain(\"r1v1\")\n\t\t\t\t.contains(\"DateStringParser dateStringParser\")\n\t\t\t\t.contains(\"FinancialInstrumentMetadataAttribute startYear =\"\n\t\t\t\t\t\t+ \" this.mFinancialInstrumentMetadataDefinition.getStartYear();\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/variables/TestVariablesDeclAnnotation.java",
    "content": "package jadx.tests.integration.variables;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.metadata.annotations.NodeDeclareRef;\nimport jadx.api.metadata.annotations.VarNode;\nimport jadx.api.utils.CodeUtils;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestVariablesDeclAnnotation extends IntegrationTest {\n\n\tpublic abstract static class TestCls {\n\t\tpublic int test(String str, int i) {\n\t\t\treturn i;\n\t\t}\n\n\t\tpublic abstract int test2(String str);\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tnoDebugInfo();\n\t\tClassNode cls = getClassNode(TestCls.class);\n\t\tassertThat(cls).code()\n\t\t\t\t.containsOne(\"public int test(String str, int i) {\")\n\t\t\t\t.containsOne(\"public abstract int test2(String str);\");\n\n\t\tcheckArgNamesInMethod(cls, \"test\", \"[str, i]\");\n\t\tcheckArgNamesInMethod(cls, \"test2\", \"[str]\");\n\t}\n\n\tprivate static void checkArgNamesInMethod(ClassNode cls, String mthName, String expectedVars) {\n\t\tMethodNode testMth = cls.searchMethodByShortName(mthName);\n\t\tassertThat(testMth).isNotNull();\n\n\t\tICodeInfo codeInfo = cls.getCode();\n\t\tint mthDefPos = testMth.getDefPosition();\n\t\tint lineEndPos = CodeUtils.getLineEndForPos(codeInfo.getCodeStr(), mthDefPos);\n\t\tList<String> argNames2 = new ArrayList<>();\n\t\tcodeInfo.getCodeMetadata().searchDown(mthDefPos, (pos, ann) -> {\n\t\t\tif (pos > lineEndPos) {\n\t\t\t\treturn Boolean.TRUE; // stop at line end\n\t\t\t}\n\t\t\tif (ann instanceof NodeDeclareRef) {\n\t\t\t\tICodeNodeRef declRef = ((NodeDeclareRef) ann).getNode();\n\t\t\t\tif (declRef instanceof VarNode) {\n\t\t\t\t\tVarNode varNode = (VarNode) declRef;\n\t\t\t\t\tif (varNode.getMth().equals(testMth)) {\n\t\t\t\t\t\targNames2.add(varNode.getName());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\n\t\tassertThat(argNames2).doesNotContainNull();\n\t\tassertThat(argNames2.toString()).isEqualTo(expectedVars);\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/variables/TestVariablesDefinitions.java",
    "content": "package jadx.tests.integration.variables;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.visitors.DepthTraversal;\nimport jadx.core.dex.visitors.IDexTreeVisitor;\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestVariablesDefinitions extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tprivate static Logger log;\n\t\tprivate ClassNode cls;\n\t\tprivate List<IDexTreeVisitor> passes;\n\n\t\tpublic void test() {\n\t\t\ttry {\n\t\t\t\tcls.load();\n\t\t\t\tfor (IDexTreeVisitor pass : this.passes) {\n\t\t\t\t\tDepthTraversal.visit(pass, cls);\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.error(\"Decode exception: {}\", cls, e);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(indent(3) + \"for (IDexTreeVisitor pass : this.passes) {\")\n\t\t\t\t.doesNotContain(\"iterator;\")\n\t\t\t\t.doesNotContain(\"Iterator\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/variables/TestVariablesDefinitions2.java",
    "content": "package jadx.tests.integration.variables;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\npublic class TestVariablesDefinitions2 extends IntegrationTest {\n\n\tpublic static class TestCls {\n\n\t\tpublic static int test(List<String> list) {\n\t\t\tint i = 0;\n\t\t\tif (list != null) {\n\t\t\t\tfor (String str : list) {\n\t\t\t\t\tif (str.isEmpty()) {\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn i;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"int i = 0;\")\n\t\t\t\t.containsOne(\"i++;\")\n\t\t\t\t.containsOne(\"return i;\")\n\t\t\t\t.doesNotContain(\"i2;\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/variables/TestVariablesGeneric.java",
    "content": "package jadx.tests.integration.variables;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestVariablesGeneric extends SmaliTest {\n\t// @formatter:off\n\t/*\n\t\tpublic static <T> j a(i<? super T> iVar, c<T> cVar) {\n\t\t\tif (iVar == null) {\n\t\t\t\tthrow new IllegalArgumentException(\"subscriber can not be null\");\n\t\t\t}\n\t\t\tif (cVar.a == null) {\n\t\t\t\tthrow new IllegalStateException(\"onSubscribe function can not be null.\");\n\t\t\t}\n\t\t\t...\n\t\t}\n\t*/\n\t// @formatter:on\n\n\t@Test\n\tpublic void test() {\n\t\tdisableCompilation();\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.doesNotContain(\"iVar2\")\n\t\t\t\t.containsOne(\"public static <T> j a(i<? super T> iVar, c<T> cVar) throws OnErrorFailedException {\")\n\t\t\t\t.containsOne(\"if (iVar == null) {\")\n\t\t\t\t.countString(2, \"} catch (Throwable th\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/variables/TestVariablesIfElseChain.java",
    "content": "package jadx.tests.integration.variables;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.utils.assertj.JadxAssertions;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestVariablesIfElseChain extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tString used;\n\n\t\tpublic String test(int a) {\n\t\t\tif (a == 0) {\n\t\t\t\tuse(\"zero\");\n\t\t\t} else if (a == 1) {\n\t\t\t\tString r = m(a);\n\t\t\t\tif (r != null) {\n\t\t\t\t\tuse(r);\n\t\t\t\t}\n\t\t\t} else if (a == 2) {\n\t\t\t\tString r = m(a);\n\t\t\t\tif (r != null) {\n\t\t\t\t\tuse(r);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn \"miss\";\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic String m(int a) {\n\t\t\treturn \"hit\" + a;\n\t\t}\n\n\t\tpublic void use(String s) {\n\t\t\tused = s;\n\t\t}\n\n\t\tpublic void check() {\n\t\t\ttest(0);\n\t\t\tassertThat(used).isEqualTo(\"zero\");\n\t\t\ttest(1);\n\t\t\tassertThat(used).isEqualTo(\"hit1\");\n\t\t\ttest(2);\n\t\t\tassertThat(used).isEqualTo(\"hit2\");\n\t\t\tassertThat(test(5)).isEqualTo(\"miss\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tJadxAssertions.assertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"return \\\"miss\\\";\");\n\t\t// and compilable\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/variables/TestVariablesInInlinedAssign.java",
    "content": "package jadx.tests.integration.variables;\n\nimport jadx.tests.api.IntegrationTest;\nimport jadx.tests.api.extensions.profiles.TestProfile;\nimport jadx.tests.api.extensions.profiles.TestWithProfiles;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestVariablesInInlinedAssign extends IntegrationTest {\n\n\tpublic static class TestCls {\n\t\tpublic final int test(final char[] s) {\n\t\t\tint i;\n\t\t\tfor (i = 0; i < s.length; i++) {\n\t\t\t\tfinal char c = s[i];\n\t\t\t\tif (c != 'a' && c != 'b') {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn i;\n\t\t}\n\t}\n\n\t@TestWithProfiles({ TestProfile.DX_J8, TestProfile.JAVA8 })\n\tpublic void test() {\n\t\tassertThat(getClassNode(TestCls.class))\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"char c\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/variables/TestVariablesInLoop.java",
    "content": "package jadx.tests.integration.variables;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.SmaliTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestVariablesInLoop extends SmaliTest {\n\n\t@Test\n\tpublic void test() {\n\t\tassertThat(getClassNodeFromSmali())\n\t\t\t\t.code()\n\t\t\t\t.containsOne(\"int iMth;\")\n\t\t\t\t.countString(2, \"iMth = 0;\")\n\t\t\t\t.doesNotContain(\"i2\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/java/jadx/tests/integration/variables/TestVariablesUsageWithLoops.java",
    "content": "package jadx.tests.integration.variables;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.tests.api.IntegrationTest;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class TestVariablesUsageWithLoops extends IntegrationTest {\n\n\tpublic static class TestEnhancedFor {\n\t\tpublic void test() {\n\t\t\tList<Object> list;\n\t\t\tsynchronized (this) {\n\t\t\t\tlist = new ArrayList<>();\n\t\t\t}\n\t\t\tfor (Object o : list) {\n\t\t\t\tSystem.out.println(o);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testEnhancedFor() {\n\t\tassertThat(getClassNode(TestEnhancedFor.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLine(2, \"synchronized (this) {\")\n\t\t\t\t.containsLine(3, \"list = new ArrayList<>\");\n\t}\n\n\tpublic static class TestForLoop {\n\t\t@SuppressWarnings(\"rawtypes\")\n\t\tpublic void test() {\n\t\t\tList<Object> list;\n\t\t\tsynchronized (this) {\n\t\t\t\tlist = new ArrayList<>();\n\t\t\t}\n\t\t\tfor (int i = 0; i < list.size(); i++) {\n\t\t\t\tSystem.out.println(i);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testForLoop() {\n\t\tassertThat(getClassNode(TestEnhancedFor.class))\n\t\t\t\t.code()\n\t\t\t\t.containsLine(2, \"synchronized (this) {\")\n\t\t\t\t.containsLine(3, \"list = new ArrayList<>\");\n\t}\n}\n"
  },
  {
    "path": "jadx-core/src/test/raung/enums/TestEnums11.raung",
    "content": ".version 52  # Java 8\n.class public final enum enums/TestEnums11\n.super java/lang/Enum\n.signature Ljava/lang/Enum<Lenums/TestEnums11;>;\n.source \"SourceFile\"\n\n.field public static final enum A Lenums/TestEnums11;\n.field public static final synthetic b [Lenums/TestEnums11;\n.field public final a I\n\n.method public static values()[Lenums/TestEnums11;\n    .max stack 1\n    .max locals 1\n\n    getstatic enums/TestEnums11 b [Lenums/TestEnums11;\n    invokevirtual [Lenums/TestEnums11; clone ()Ljava/lang/Object;\n    checkcast [Lenums/TestEnums11;\n    areturn\n.end method\n\n.method public static valueOf(Ljava/lang/String;)Lenums/TestEnums11;\n    .max stack 2\n    .max locals 1\n\n    ldc Lenums/TestEnums11;\n    aload 0\n    invokestatic java/lang/Enum valueOf (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;\n    checkcast enums/TestEnums11\n    areturn\n.end method\n\n.method public <init>()V\n    .signature (I)V\n    .max stack 4\n    .max locals 1\n\n    aload 0\n    dup\n    ldc \"UNKNOWN\"\n    iconst_0\n    invokespecial java/lang/Enum <init> (Ljava/lang/String;I)V\n    bipush -99\n    putfield enums/TestEnums11 a I\n    return\n.end method\n\n.method public static <clinit>()V\n    .max stack 4\n    .max locals 1\n\n    new enums/TestEnums11\n    dup\n    dup\n    astore 0\n    invokespecial enums/TestEnums11 <init> ()V\n    putstatic enums/TestEnums11 A Lenums/TestEnums11;\n    iconst_1\n    anewarray enums/TestEnums11\n    dup\n    iconst_0\n    aload 0\n    aastore\n    putstatic enums/TestEnums11 b [Lenums/TestEnums11;\n    return\n.end method\n\n.method public getErrorCode()I\n    .max stack 1\n    .max locals 1\n\n    aload 0\n    getfield enums/TestEnums11 a I\n    ireturn\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/raung/java8/TestLambdaInstance3.raung",
    "content": ".version 52  # Java 8\n.class public interface abstract java8/TestLambdaInstance3\n.implements java/io/Serializable\n.implements java/util/function/Supplier\n.signature <R:Ljava/lang/Object;>Ljava/lang/Object;Ljava/io/Serializable;Ljava/util/function/Supplier<TR;>;\n.source \"Function0.java\"\n.annotation runtime Ljava/lang/FunctionalInterface;\n.end annotation\n\n\n.method public memoized()Ljava8/TestLambdaInstance3;\n    .signature ()Ljava8/TestLambdaInstance3<TR;>;\n    .max stack 2\n    .max locals 1\n\n    .local 0 \"this\" Ljava8/TestLambdaInstance3; Ljava8/TestLambdaInstance3<TR;>;\n    .line 197\n    aload 0\n    invokeinterface java8/TestLambdaInstance3 isMemoized ()Z\n    ifeq :L1\n    .line 198\n    aload 0\n    areturn\n  :L1\n    .line 200\n    .stack same\n    aload 0\n    invokestatic test/Lazy of (Ljava/util/function/Supplier;)Ltest/Lazy;\n    dup\n    invokevirtual java/lang/Object getClass ()Ljava/lang/Class;\n    pop\n    invokedynamic apply (Ltest/Lazy;)Ljava8/TestLambdaInstance3;\n        .handle invoke-static java/lang/invoke/LambdaMetafactory altMetafactory (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n        .arg 0 .methodtype ()Ljava/lang/Object;\n        .arg 1 .handle invoke-virtual test/Lazy get ()Ljava/lang/Object;\n        .arg 2 .methodtype ()Ljava/lang/Object;\n        .arg 3 7\n        .arg 4 1\n        .arg 5 Ltest/Memoized;\n        .arg 6 0\n    .end invokedynamic\n    checkcast test/Memoized\n    checkcast java8/TestLambdaInstance3\n    areturn\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/raung/jbc/TestStackConvert.raung",
    "content": ".version 52  # Java 8\n.class public super jbc/TestStackConvert\n\n.method public parseIntDefault(Ljava/lang/String;I)I\n    .max stack 3\n    .max locals 5\n\n  :L0\n    .local 0 \"this\" Ljbc/TestStackConvert;\n    .local 1 \"num\" Ljava/lang/String;\n    .local 2 \"defaultNum\" I\n    .line 13\n    aload 1\n    invokestatic java/lang/Integer parseInt (Ljava/lang/String;)I\n  :L1\n    .catch java/lang/NumberFormatException :L0 .. :L1 goto :L2\n    ireturn\n  :L3\n    .line 14\n    .stack full\n        stack 0 java/lang/NumberFormatException\n        local 0 jbc/TestStackConvert\n        local 1 java/lang/String\n        local 2 int\n        local 3 Top\n        local 4 java/lang/NumberFormatException\n    .end stack\n    astore 3\n    .local 3 \"e\" Ljava/lang/NumberFormatException;\n    .line 15\n    aload 3\n    invokevirtual java/lang/NumberFormatException printStackTrace ()V\n    .line 17\n    iload 2\n    .end local 3 # \"e\"\n    ireturn\n  :L2\n    .stack full\n        stack 0 java/lang/NumberFormatException\n        local 0 jbc/TestStackConvert\n        local 1 java/lang/String\n        local 2 int\n    .end stack\n    .end local 0 # \"this\"\n    .end local 1 # \"num\"\n    .end local 2 # \"defaultNum\"\n    astore 4\n    getstatic java/lang/System out Ljava/io/PrintStream;\n    ldc \"Before println\"\n    invokevirtual java/io/PrintStream println (Ljava/lang/String;)V\n    aload 4\n    goto :L3\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/raung/loops/TestLoopRestore2.raung",
    "content": ".version 45.3  # Java 1.1\n.class public super loops/TestLoopRestore2\n\n.method public test([I[I)V\n    .max stack 5\n    .max locals 6\n\n    iconst_0\n    istore 3\n    sipush 0\n    ifeq :L0\n    goto :L1\n  :L1\n    sipush 1\n    ifeq :L1\n    goto :L0\n  :L0\n    iinc 3 1\n    return\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/raung/others/TestClassImplementsSignature.raung",
    "content": ".version 52  # Java 8\n.class public super others/TestClassImplementsSignature\n.signature <T:Ljava/lang/Object;>Ljava/lang/Object;Ljava/lang/Comparable<Lothers/TestClassImplementsSignature<LT;>;>;\n\n.field value Ljava/lang/Object;\n  .signature TT;\n.end field\n\n.method public <init>()V\n    .max stack 1\n    .max locals 1\n\n    aload 0\n    invokespecial java/lang/Object <init> ()V\n    return\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/raung/others/TestJavaDup2x2.raung",
    "content": ".version 45.3\n.class others/TestJavaDup2x2\n\n.auto frames\n\n.method public static test([D)V\n    aload 0\n    iconst_0\n    aload 0\n    iconst_1\n    aload 0\n    iconst_2\n    aload 0\n    iconst_3\n    ldc2_w 127.5\n    dup2_x2\n    dastore\n    dup2_x2\n    dastore\n    dup2_x2\n    dastore\n    dastore\n\treturn\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/raung/others/TestJavaJSR.raung",
    "content": ".version 45.3\n.class others/TestJavaJSR\n\n.method public test(Ljava/net/URL;)Ljava/lang/String;\n    .throw java/io/IOException\n    .max stack 2\n    .max locals 6\n\n    .local 0 \"this\" Lothers/TestJavaJSR;\n    .local 1 \"url\" Ljava/net/URL;\n    .line 88\n    aload 1\n    invokevirtual java/net/URL openStream ()Ljava/io/InputStream;\n    astore 2\n  :L0\n    .local 2 \"in\" Ljava/io/InputStream;\n    .line 89\n    .line 90\n    aload 0\n    aload 2\n    invokevirtual others/TestJavaJSR call (Ljava/io/InputStream;)Ljava/lang/String;\n    astore 3\n    jsr :L3\n    aload 3\n    areturn\n  :L1\n    .catch all :L0 .. :L1 goto :L1\n    .line 89\n    astore 4\n    jsr :L3\n    aload 4\n    athrow\n  :L3\n    astore 5\n    .line 92\n    aload 2\n    invokevirtual java/io/InputStream close ()V\n    .line 89\n    ret 5\n.end method\n\n.method public call(Ljava/io/InputStream;)Ljava/lang/String;\n    .throw java/io/IOException\n    .max stack 1\n    .max locals 2\n\n    ldc \"\"\n    areturn\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/raung/others/TestJavaSwap.raung",
    "content": ".version 52\n.class others/TestJavaSwap\n.auto frames\n\n.field private field Ljava/lang/Iterable;\n\n.method public toString()Ljava/lang/String;\n    aload 0\n    getfield others/TestJavaSwap field Ljava/lang/Iterable;\n    invokestatic java/lang/String valueOf (Ljava/lang/Object;)Ljava/lang/String;\n    astore 1\n    bipush 8\n    aload 1\n    invokestatic java/lang/String valueOf (Ljava/lang/Object;)Ljava/lang/String;\n    invokevirtual java/lang/String length ()I\n    iadd\n    new java/lang/StringBuilder\n    dup_x1\n    swap\n    invokespecial java/lang/StringBuilder <init> (I)V\n    ldc \"concat(\"\n    invokevirtual java/lang/StringBuilder append (Ljava/lang/String;)Ljava/lang/StringBuilder;\n    aload 1\n    invokevirtual java/lang/StringBuilder append (Ljava/lang/String;)Ljava/lang/StringBuilder;\n    ldc \")\"\n    invokevirtual java/lang/StringBuilder append (Ljava/lang/String;)Ljava/lang/StringBuilder;\n    invokevirtual java/lang/StringBuilder toString ()Ljava/lang/String;\n    areturn\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/raung/others/TestStringConcatJava11.raung",
    "content": ".version 55\n.class public others/TestStringConcatJava11\n\n.method public test(Ljava/lang/String;)Ljava/lang/String;\n    .max stack 1\n    .max locals 2\n\n    aload 1\n    invokedynamic makeConcatWithConstants (Ljava/lang/String;)Ljava/lang/String;\n        .handle invoke-static java/lang/invoke/StringConcatFactory makeConcatWithConstants (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n        .arg 0 \"\\u0001test\"\n    .end invokedynamic\n    areturn\n.end method\n\n.method public test2(Ljava/lang/String;)Ljava/lang/String;\n    .max stack 2\n    .max locals 2\n\n    aload 1\n    aload 1\n    invokedynamic makeConcatWithConstants (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;\n        .handle invoke-static java/lang/invoke/StringConcatFactory makeConcatWithConstants (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n        .arg 0 \"\\u0001test\\u0001\\u0002\"\n        .arg 1 7  # synthetic usage, compiler adds const values into recipe string\n    .end invokedynamic\n    areturn\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/resources/logback.xml",
    "content": "<configuration>\n\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t\t<encoder>\n\t\t\t<pattern>%-5level - %msg%n</pattern>\n\t\t</encoder>\n\t</appender>\n\n\t<root level=\"DEBUG\">\n\t\t<appender-ref ref=\"STDOUT\"/>\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "jadx-core/src/test/resources/manifest/IllegalCharsForGradleWrapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" android:versionCode=\"1\" android:versionName=\"1.0\" android:compileSdkVersion=\"33\" android:compileSdkVersionCodename=\"13\" package=\"jadx.test.app\" platformBuildVersionCode=\"33\" platformBuildVersionName=\"13\">\n    <uses-sdk android:minSdkVersion=\"21\" android:targetSdkVersion=\"32\"/>\n    <application android:label=\"JadxTestApp/\\:?*|\">\n    </application>\n</manifest>\n"
  },
  {
    "path": "jadx-core/src/test/resources/manifest/MinSdkVersion25.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" android:versionCode=\"1\" android:versionName=\"1.0\" android:compileSdkVersion=\"33\" android:compileSdkVersionCodename=\"13\" package=\"jadx.test.app\" platformBuildVersionCode=\"33\" platformBuildVersionName=\"13\">\n    <uses-sdk android:minSdkVersion=\"25\" android:targetSdkVersion=\"32\"/>\n    <application android:label=\"JadxTestApp\">\n    </application>\n</manifest>\n"
  },
  {
    "path": "jadx-core/src/test/resources/manifest/OptionalTargetSdkVersion.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" android:versionCode=\"1\" android:versionName=\"1.0\" package=\"jadx.test.app\">\n    <uses-sdk android:minSdkVersion=\"14\"/>\n    <application />\n</manifest>\n"
  },
  {
    "path": "jadx-core/src/test/resources/manifest/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <string name=\"app_name\">JadxTestApp</string>\n</resources>\n"
  },
  {
    "path": "jadx-core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker",
    "content": "mock-maker-inline"
  },
  {
    "path": "jadx-core/src/test/smali/arith/TestArithConst.smali",
    "content": ".class public LTestArithConst;\n.super Ljava/lang/Object;\n\n.field public static final CONST_INT:I = 0xff\n\n.method private test(I)I\n    .registers 2\n\n    add-int/lit16 v0, p1, 0xff\n\n    return v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/arith/TestArithNot.smali",
    "content": ".class public LTestArithNot;\n.super Ljava/lang/Object;\n\n.method private test1(I)I\n    .registers 2\n    .param p1, \"a\"\n\n    not-int v0, p1\n\n    return v0\n.end method\n\n.method private test2(J)J\n    .registers 4\n    .param p1, \"b\"\n\n    not-long v0, p1\n\n    return v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/arith/TestXor.smali",
    "content": ".class public Larith/TestXor;\n.super Ljava/lang/Object;\n\n\n.method public test()Z\n    .locals 1\n\n    .line 20\n    const/4 v0, 0x1\n\n    return v0\n.end method\n\n.method public test1()Z\n    .locals 1\n\n    .line 12\n    invoke-virtual {p0}, Larith/TestXor;->test()Z\n\n    move-result v0\n\n    xor-int/lit8 v0, v0, 0x1\n\n    return v0\n.end method\n\n.method public test2()Z\n    .locals 1\n\n    .line 16\n    invoke-virtual {p0}, Larith/TestXor;->test()Z\n\n    move-result v0\n\n    xor-int/lit8 v0, v0, 0x0\n\n    return v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/arrays/TestArrayFillWithMove/TestCls.smali",
    "content": ".class public Larrays/TestCls;\n.super Ljava/lang/Object;\n\n.method public test()[J\n    .registers 4\n\n    const/16 v3, 0x2\n\n    move/from16 v0, v3\n\n    new-array v0, v0, [J\n\n    move-object/from16 v1, v0\n\n    fill-array-data v1, :array_0\n\n    return v1\n\n    :array_0\n    .array-data 8\n        0x0\n        0x1\n    .end array-data\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/arrays/TestArrayInitField2.smali",
    "content": ".class public Larrays/TestArrayInitField2;\n.super Ljava/lang/Object;\n\n.field static myArr:[J\n\n.method static constructor <clinit>()V\n    .locals 4\n\n    const v0, 0x3\n    new-array v0, v0, [J\n    sput-object v0, Larrays/TestArrayInitField2;->myArr:[J\n    const/4 v1, 0x0\n    const-wide/32 v2, 0x4c78b648\n    aput-wide v2, v0, v1\n    return-void\n    nop\n\n.end method\n\n.method public check()V\n    .registers 5\n    sget-object v0, Larrays/TestArrayInitField2;->myArr:[J\n    invoke-static {v0}, Lorg/assertj/core/api/Assertions;->assertThat([J)Lorg/assertj/core/api/AbstractLongArrayAssert;\n    move-result-object v0\n    const/4 v1, 0x3\n    invoke-virtual {v0, v1}, Lorg/assertj/core/api/AbstractLongArrayAssert;->hasSize(I)Lorg/assertj/core/api/AbstractLongArrayAssert;\n\n    sget-object v0, Larrays/TestArrayInitField2;->myArr:[J\n    const/4 v1, 0x0\n    aget-wide v0, v0, v1\n    invoke-static {v0, v1}, Lorg/assertj/core/api/Assertions;->assertThat(J)Lorg/assertj/core/api/AbstractLongAssert;\n    move-result-object v0\n    const-wide/32 v2, 0x4c78b648\n    invoke-virtual {v0, v2, v3}, Lorg/assertj/core/api/AbstractLongAssert;->isEqualTo(J)Lorg/assertj/core/api/AbstractLongAssert;\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/arrays/TestFillArrayData/TestCls.smali",
    "content": ".class public Larrays/TestCls;\n.super Ljava/lang/Object;\n\n.method public test([J)V\n    .registers 2\n\n    fill-array-data p1, :array_0\n\n    return-void\n\n    :array_0\n    .array-data 8\n        0x1\n        0x2\n    .end array-data\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestBooleanToByte.smali",
    "content": ".class public Lconditions/TestBooleanToByte;\n.super Ljava/lang/Object;\n\n.field private showConsent:Z\n\n.method public writeToParcel(Lconditions/TestBooleanToByte;)V\n    .locals 0\n\n    iget-boolean p1, p0, Lconditions/TestBooleanToByte;->showConsent:Z\n\n    int-to-byte p1, p1\n\n    invoke-virtual {p0, p1}, Lconditions/TestBooleanToByte;->write(B)V\n\n    return-void\n.end method\n\n.method public write(B)V\n    .locals 0\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestBooleanToChar.smali",
    "content": ".class public Lconditions/TestBooleanToChar;\n.super Ljava/lang/Object;\n\n.field private showConsent:Z\n\n.method public writeToParcel(Lconditions/TestBooleanToChar;)V\n    .locals 0\n\n    iget-boolean p1, p0, Lconditions/TestBooleanToChar;->showConsent:Z\n\n    int-to-char p1, p1\n\n    invoke-virtual {p0, p1}, Lconditions/TestBooleanToChar;->write(C)V\n\n    return-void\n.end method\n\n.method public write(C)V\n    .locals 0\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestBooleanToDouble.smali",
    "content": ".class public Lconditions/TestBooleanToDouble;\n.super Ljava/lang/Object;\n\n.field private showConsent:Z\n\n.method public writeToParcel(Lconditions/TestBooleanToDouble;)V\n    .locals 0\n\n    iget-boolean p1, p0, Lconditions/TestBooleanToDouble;->showConsent:Z\n\n    int-to-double p1, p1\n\n    invoke-virtual {p0, p1}, Lconditions/TestBooleanToDouble;->write(D)V\n\n    return-void\n.end method\n\n.method public write(D)V\n    .locals 0\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestBooleanToFloat.smali",
    "content": ".class public Lconditions/TestBooleanToFloat;\n.super Ljava/lang/Object;\n\n.field private showConsent:Z\n\n.method public writeToParcel(Lconditions/TestBooleanToFloat;)V\n    .locals 0\n\n    iget-boolean p1, p0, Lconditions/TestBooleanToFloat;->showConsent:Z\n\n    int-to-float p1, p1\n\n    invoke-virtual {p0, p1}, Lconditions/TestBooleanToFloat;->write(F)V\n\n    return-void\n.end method\n\n.method public write(F)V\n    .locals 0\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestBooleanToInt.smali",
    "content": ".class public Lconditions/TestBooleanToInt;\n.super Ljava/lang/Object;\n\n.field private showConsent:Z\n\n.method public writeToParcel(Lconditions/TestBooleanToInt;)V\n    .locals 0\n\n    iget-boolean p1, p0, Lconditions/TestBooleanToInt;->showConsent:Z\n\n    invoke-virtual {p0, p1}, Lconditions/TestBooleanToInt;->write(I)V\n\n    return-void\n.end method\n\n.method public write(I)V\n    .locals 0\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestBooleanToInt2.smali",
    "content": ".class public Lconditions/TestBooleanToInt2;\n.super Ljava/lang/Object;\n\n.method public test()V\n    .registers 3\n    invoke-direct {p0}, Lconditions/TestBooleanToInt2;->getValue()Z\n    move-result v0\n    invoke-static {v0}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;\n    move-result-object v1\n    invoke-direct {p0, v1}, Lconditions/TestBooleanToInt2;->use1(Ljava/lang/Integer;)V\n    invoke-direct {p0, v0}, Lconditions/TestBooleanToInt2;->use2(I)V\n    return-void\n.end method\n\n.method private getValue()Z\n    .registers 2\n    const/4 v0, 0x0\n    return v0\n.end method\n\n.method private use1(Ljava/lang/Integer;)V\n    .registers 2\n    return-void\n.end method\n\n.method private use2(I)V\n    .registers 2\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestBooleanToLong.smali",
    "content": ".class public Lconditions/TestBooleanToLong;\n.super Ljava/lang/Object;\n\n.field private showConsent:Z\n\n.method public writeToParcel(Lconditions/TestBooleanToLong;)V\n    .locals 0\n\n    iget-boolean p1, p0, Lconditions/TestBooleanToLong;->showConsent:Z\n\n    int-to-long p1, p1\n\n    invoke-virtual {p0, p1}, Lconditions/TestBooleanToLong;->write(J)V\n\n    return-void\n.end method\n\n.method public write(J)V\n    .locals 0\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestBooleanToShort.smali",
    "content": ".class public Lconditions/TestBooleanToShort;\n.super Ljava/lang/Object;\n\n.field private showConsent:Z\n\n.method public writeToParcel(Lconditions/TestBooleanToShort;)V\n    .locals 0\n\n    iget-boolean p1, p0, Lconditions/TestBooleanToShort;->showConsent:Z\n\n    int-to-short p1, p1\n\n    invoke-virtual {p0, p1}, Lconditions/TestBooleanToShort;->write(S)V\n\n    return-void\n.end method\n\n.method public write(S)V\n    .locals 0\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestComplexIf.smali",
    "content": ".class public final Lconditions/TestComplexIf;\n.super Ljava/lang/Object;\n\n\n# instance fields\n.field private a:Ljava/lang/String;\n\n.field private b:I\n\n.field private c:F\n\n\n# direct methods\n.method public constructor <init>()V\n    .locals 1\n    return-void\n.end method\n\n.method public final test()Z\n    .locals 5\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v1, \"GT-P6200\"\n\n    invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    const/4 v1, 0x1\n\n    if-nez v0, :cond_b\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v2, \"GT-P6210\"\n\n    invoke-virtual {v0, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_b\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v2, \"A100\"\n\n    invoke-virtual {v0, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_b\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v2, \"A101\"\n\n    invoke-virtual {v0, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_b\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v2, \"LIFETAB_S786X\"\n\n    invoke-virtual {v0, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-eqz v0, :cond_0\n\n    goto/16 :goto_2\n\n    :cond_0\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v2, \"VS890 4G\"\n\n    invoke-virtual {v0, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-eqz v0, :cond_1\n\n    return v1\n\n    :cond_1\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v2, \"SM-T810\"\n\n    invoke-virtual {v0, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    const/4 v2, 0x0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T813\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T815\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T815N0\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T815Y\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T820\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T825\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-P585\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-P585N0\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T561\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T567V\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T320\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T321\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T325\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T700\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T705\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T705M\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T705Y\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SC-03G\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"GT-N5100\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"GT-N5105\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"GT-N5110\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"GT-N5120\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SHW-M500W\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T310\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T311\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T315\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T330\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T330NU\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T331\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T335\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T337V\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T710\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T715\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T715N0\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SM-T719\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"GT-P6800\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"SC-01E\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-eqz v0, :cond_2\n\n    goto/16 :goto_1\n\n    :cond_2\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"LG-V500\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"LG-V930\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-eqz v0, :cond_3\n\n    goto/16 :goto_1\n\n    :cond_3\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"P01T_1\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"P01MA\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"Nexus 9\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"ASUS_P00I\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-eqz v0, :cond_4\n\n    goto :goto_1\n\n    :cond_4\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"Lenovo YT3-X90X\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_a\n\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"Lenovo YT-X703F\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-eqz v0, :cond_5\n\n    goto :goto_1\n\n    :cond_5\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"PMT3408_4G\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-eqz v0, :cond_6\n\n    return v2\n\n    :cond_6\n    iget-object v0, p0, Lconditions/TestComplexIf;->a:Ljava/lang/String;\n\n    const-string v3, \"MediaPad T2 10.0 Pro\"\n\n    invoke-virtual {v0, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-eqz v0, :cond_7\n\n    return v2\n\n    :cond_7\n    iget-object v0, p0, Lconditions/TestComplexIf;->b:I\n\n    and-int/lit8 v0, v0, 0xf\n\n    const/4 v3, 0x4\n\n    if-ne v0, v3, :cond_8\n\n    const/4 v0, 0x1\n\n    goto :goto_0\n\n    :cond_8\n    const/4 v0, 0x0\n\n    :goto_0\n    iget v3, p0, Lconditions/TestComplexIf;->c:F\n\n    const/high16 v4, 0x43200000    # 160.0f\n\n    cmpl-float v3, v3, v4\n\n    if-lez v3, :cond_9\n\n    return v1\n\n    :cond_9\n    iget v3, p0, Lconditions/TestComplexIf;->c:F\n\n    const/4 v4, 0x0\n\n    cmpg-float v3, v3, v4\n\n    if-gtz v3, :cond_a\n\n    if-eqz v0, :cond_a\n\n    return v1\n\n    :cond_a\n    :goto_1\n    return v2\n\n    :cond_b\n    :goto_2\n    return v1\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestComplexIf2.smali",
    "content": ".class public final Lconditions/TestComplexIf2;\n.super Ljava/lang/ClassLoader;\n\n\n# instance fields\n.field private isSaved:Z\n\n.field private project:Ljava/lang/String;\n\n\n.method public test()V\n    .locals 4\n\n    .line 415\n    iget-boolean v0, p0, Lconditions/TestComplexIf2;->isSaved:Z\n\n    if-eqz v0, :cond_0\n\n    .line 416\n    new-instance v0, Ljava/lang/RuntimeException;\n\n    const-string v1, \"Error\"\n\n    invoke-direct {v0, v1}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/String;)V\n\n    throw v0\n\n    .line 418\n    :cond_0\n    invoke-static {}, Lorg/apache/tools/ant/util/LoaderUtils;->isContextLoaderAvailable()Z\n\n    move-result v0\n\n    if-eqz v0, :cond_2\n\n    .line 419\n    invoke-static {}, Lorg/apache/tools/ant/util/LoaderUtils;->getContextClassLoader()Ljava/lang/ClassLoader;\n\n    move-result-object v0\n\n    iput-object v0, p0, Lconditions/TestComplexIf2;->savedContextLoader:Ljava/lang/ClassLoader;\n\n    .line 420\n    move-object v0, p0\n\n    .line 421\n    .local v0, \"loader\":Ljava/lang/ClassLoader;\n    iget-object v1, p0, Lconditions/TestComplexIf2;->project:Ljava/lang/String;\n\n    if-eqz v1, :cond_1\n\n    const-string v1, \"simple\"\n\n    iget-object v2, p0, Lconditions/TestComplexIf2;->project:Ljava/lang/String;\n\n    invoke-virtual {v1, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v1\n\n    if-eqz v1, :cond_1\n\n    .line 423\n    invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class;\n\n    move-result-object v1\n\n    invoke-virtual {v1}, Ljava/lang/Class;->getClassLoader()Ljava/lang/ClassLoader;\n\n    move-result-object v0\n\n    .line 425\n    :cond_1\n    invoke-static {v0}, Lorg/apache/tools/ant/util/LoaderUtils;->setContextClassLoader(Ljava/lang/ClassLoader;)V\n\n    .line 426\n    const/4 v1, 0x1\n\n    iput-boolean v1, p0, Lconditions/TestComplexIf2;->isSaved:Z\n\n    .line 428\n    .end local v0    # \"loader\":Ljava/lang/ClassLoader;\n    :cond_2\n    return-void\n.end method\n\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestComplexIf3.smali",
    "content": ".class public final Lconditions/TestComplexIf3;\n.super Ljava/lang/Object;\n\n.method public test(Landroid/os/Message;)V\n    .registers 10\n\n    .prologue\n    const/16 v7, 0xf\n\n    const/4 v6, 0x4\n\n    const/4 v0, 0x0\n\n    const/4 v1, 0x1\n\n    const/4 v2, 0x0\n\n    .line 3307\n    const-string/jumbo v3, \"Service\"\n\n    new-instance v4, Ljava/lang/StringBuilder;\n\n    invoke-direct {v4}, Ljava/lang/StringBuilder;-><init>()V\n\n    const-string/jumbo v5, \"handle: \"\n\n    invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v4\n\n    iget v5, p1, Landroid/os/Message;->what:I\n\n    invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;\n\n    move-result-object v4\n\n    invoke-virtual {v4}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v4\n\n    invoke-static {v3, v4}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 3308\n    iget v3, p1, Landroid/os/Message;->what:I\n\n    sparse-switch v3, :sswitch_data_2d0\n\n    .line 3516\n    :cond_27\n    :goto_27\n    return-void\n\n    .line 3312\n    :sswitch_28\n    invoke-virtual {p1}, Landroid/os/Message;->getData()Landroid/os/Bundle;\n\n    move-result-object v0\n\n    .line 3313\n    iget-object v1, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v1, v0}, Lconditions/TestComplexIf3;->-wrap27(Lconditions/TestComplexIf3;Landroid/os/Bundle;)V\n\n    goto :goto_27\n\n    .line 3318\n    :sswitch_32\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0}, Lconditions/TestComplexIf3;->-wrap26(Lconditions/TestComplexIf3;)V\n\n    goto :goto_27\n\n    .line 3323\n    :sswitch_38\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0}, Lconditions/TestComplexIf3;->-get18(Lconditions/TestComplexIf3;)Z\n\n    move-result v0\n\n    if-eqz v0, :cond_45\n\n    .line 3324\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0, v2}, Lconditions/TestComplexIf3;->-wrap33(Lconditions/TestComplexIf3;Z)V\n\n    .line 3326\n    :cond_45\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0}, Lconditions/TestComplexIf3;->-wrap9(Lconditions/TestComplexIf3;)Z\n\n    .line 3327\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0}, Lconditions/TestComplexIf3;->-wrap0(Lconditions/TestComplexIf3;)Z\n\n    .line 3329\n    new-instance v0, Landroid/os/Bundle;\n\n    invoke-direct {v0, v1}, Landroid/os/Bundle;-><init>(I)V\n\n    .line 3330\n    const-string/jumbo v1, \"flag\"\n\n    const/16 v2, 0xb\n\n    invoke-virtual {v0, v1, v2}, Landroid/os/Bundle;->putInt(Ljava/lang/String;I)V\n\n    .line 3331\n    iget-object v1, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v1, v0}, Lconditions/TestComplexIf3;->-wrap28(Lconditions/TestComplexIf3;Landroid/os/Bundle;)V\n\n    .line 3333\n    invoke-static {}, Lconditions/TestComplexIf3;->-get28()Lconditions/TestComplexIf3$OnExitListener;\n\n    move-result-object v0\n\n    if-eqz v0, :cond_27\n\n    .line 3334\n    invoke-static {}, Lconditions/TestComplexIf3;->-get28()Lconditions/TestComplexIf3$OnExitListener;\n\n    move-result-object v0\n\n    invoke-interface {v0}, Lconditions/TestComplexIf3$OnExitListener;->onExit()V\n\n    goto :goto_27\n\n    .line 3340\n    :sswitch_6f\n    invoke-virtual {p1}, Landroid/os/Message;->getData()Landroid/os/Bundle;\n\n    move-result-object v0\n\n    .line 3341\n    const-string/jumbo v3, \"value\"\n\n    invoke-virtual {v0, v3}, Landroid/os/Bundle;->getInt(Ljava/lang/String;)I\n\n    move-result v3\n\n    .line 3347\n    if-nez v3, :cond_8e\n\n    .line 3349\n    const-string/jumbo v2, \"flag\"\n\n    invoke-virtual {v0, v2, v6}, Landroid/os/Bundle;->putInt(Ljava/lang/String;I)V\n\n    .line 3351\n    const-string/jumbo v2, \"key\"\n\n    invoke-virtual {v0, v2, v1}, Landroid/os/Bundle;->putBoolean(Ljava/lang/String;Z)V\n\n    .line 3352\n    iget-object v1, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v1, v0}, Lconditions/TestComplexIf3;->-wrap28(Lconditions/TestComplexIf3;Landroid/os/Bundle;)V\n\n    goto :goto_27\n\n    .line 3357\n    :cond_8e\n    iget-object v1, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v1}, Lconditions/TestComplexIf3;->-get22(Lconditions/TestComplexIf3;)I\n\n    move-result v1\n\n    const/4 v3, 0x7\n\n    if-eq v1, v3, :cond_27\n\n    .line 3358\n    const-string/jumbo v1, \"flag\"\n\n    invoke-virtual {v0, v1, v6}, Landroid/os/Bundle;->putInt(Ljava/lang/String;I)V\n\n    .line 3360\n    const-string/jumbo v1, \"key\"\n\n    invoke-virtual {v0, v1, v2}, Landroid/os/Bundle;->putBoolean(Ljava/lang/String;Z)V\n\n    .line 3361\n    iget-object v1, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v1, v0}, Lconditions/TestComplexIf3;->-wrap28(Lconditions/TestComplexIf3;Landroid/os/Bundle;)V\n\n    goto/16 :goto_27\n\n    .line 3368\n    :sswitch_aa\n    invoke-virtual {p1}, Landroid/os/Message;->getData()Landroid/os/Bundle;\n\n    move-result-object v0\n\n    .line 3369\n    const-string/jumbo v1, \"f\"\n\n    invoke-virtual {v0, v1}, Landroid/os/Bundle;->getFloat(Ljava/lang/String;)F\n\n    move-result v0\n\n    .line 3370\n    iget-object v1, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v1, v0}, Lconditions/TestComplexIf3;->-wrap12(Lconditions/TestComplexIf3;F)Z\n\n    move-result v1\n\n    .line 3372\n    if-nez v1, :cond_c7\n\n    .line 3373\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0}, Lconditions/TestComplexIf3;->-get7(Lconditions/TestComplexIf3;)I\n\n    move-result v0\n\n    invoke-static {v0}, Ltest/Utils;->computeFrequency(I)F\n\n    move-result v0\n\n    .line 3375\n    :cond_c7\n    new-instance v2, Landroid/os/Bundle;\n\n    const/4 v3, 0x3\n\n    invoke-direct {v2, v3}, Landroid/os/Bundle;-><init>(I)V\n\n    .line 3376\n    const-string/jumbo v3, \"flag\"\n\n    invoke-virtual {v2, v3, v7}, Landroid/os/Bundle;->putInt(Ljava/lang/String;I)V\n\n    .line 3378\n    const-string/jumbo v3, \"key\"\n\n    invoke-virtual {v2, v3, v1}, Landroid/os/Bundle;->putBoolean(Ljava/lang/String;Z)V\n\n    .line 3379\n    const-string/jumbo v1, \"key\"\n\n    invoke-virtual {v2, v1, v0}, Landroid/os/Bundle;->putFloat(Ljava/lang/String;F)V\n\n    .line 3380\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0, v2}, Lconditions/TestComplexIf3;->-wrap28(Lconditions/TestComplexIf3;Landroid/os/Bundle;)V\n\n    goto/16 :goto_27\n\n    .line 3385\n    :sswitch_e6\n    invoke-virtual {p1}, Landroid/os/Message;->getData()Landroid/os/Bundle;\n\n    move-result-object v0\n\n    .line 3386\n    iget-object v3, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v3, v1}, Lconditions/TestComplexIf3;->-set5(Lconditions/TestComplexIf3;Z)Z\n\n    .line 3387\n    iget-object v1, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    const-string/jumbo v3, \"f\"\n\n    invoke-virtual {v0, v3}, Landroid/os/Bundle;->getFloat(Ljava/lang/String;)F\n\n    move-result v3\n\n    .line 3388\n    const-string/jumbo v4, \"o\"\n\n    invoke-virtual {v0, v4}, Landroid/os/Bundle;->getBoolean(Ljava/lang/String;)Z\n\n    move-result v0\n\n    .line 3387\n    invoke-static {v1, v3, v0}, Lconditions/TestComplexIf3;->-wrap13(Lconditions/TestComplexIf3;FZ)F\n\n    move-result v0\n\n    .line 3390\n    invoke-static {v0}, Ltest/Utils;->computeStation(F)I\n\n    move-result v1\n\n    .line 3391\n    invoke-static {v1}, Ltest/Utils;->isValidStation(I)Z\n\n    move-result v1\n\n    if-eqz v1, :cond_2cd\n\n    .line 3392\n    iget-object v1, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v1, v0}, Lconditions/TestComplexIf3;->-wrap12(Lconditions/TestComplexIf3;F)Z\n\n    move-result v1\n\n    .line 3395\n    :goto_113\n    if-nez v1, :cond_11f\n\n    .line 3396\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0}, Lconditions/TestComplexIf3;->-get7(Lconditions/TestComplexIf3;)I\n\n    move-result v0\n\n    invoke-static {v0}, Ltest/Utils;->computeFrequency(I)F\n\n    move-result v0\n\n    .line 3398\n    :cond_11f\n    new-instance v3, Landroid/os/Bundle;\n\n    const/4 v4, 0x2\n\n    invoke-direct {v3, v4}, Landroid/os/Bundle;-><init>(I)V\n\n    .line 3399\n    const-string/jumbo v4, \"flag\"\n\n    invoke-virtual {v3, v4, v7}, Landroid/os/Bundle;->putInt(Ljava/lang/String;I)V\n\n    .line 3401\n    const-string/jumbo v4, \"key_is_tune\"\n\n    invoke-virtual {v3, v4, v1}, Landroid/os/Bundle;->putBoolean(Ljava/lang/String;Z)V\n\n    .line 3402\n    const-string/jumbo v1, \"key_tune_to_station\"\n\n    invoke-virtual {v3, v1, v0}, Landroid/os/Bundle;->putFloat(Ljava/lang/String;F)V\n\n    .line 3403\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0, v3}, Lconditions/TestComplexIf3;->-wrap28(Lconditions/TestComplexIf3;Landroid/os/Bundle;)V\n\n    .line 3404\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0, v2}, Lconditions/TestComplexIf3;->-set5(Lconditions/TestComplexIf3;Z)Z\n\n    goto/16 :goto_27\n\n    .line 3414\n    :sswitch_143\n    iget-object v3, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v3, v1}, Lconditions/TestComplexIf3;->-set4(Lconditions/TestComplexIf3;Z)Z\n\n    .line 3418\n    iget-object v3, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    iget-object v4, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v4}, Lconditions/TestComplexIf3;->-get6(Lconditions/TestComplexIf3;)Landroid/content/Context;\n\n    move-result-object v4\n\n    invoke-static {v4}, Ltest/Station;->getCurrentStation(Landroid/content/Context;)I\n\n    move-result v4\n\n    invoke-static {v3, v4}, Lconditions/TestComplexIf3;->-set0(Lconditions/TestComplexIf3;I)I\n\n    .line 3419\n    iget-object v3, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v3}, Lconditions/TestComplexIf3;->-get19(Lconditions/TestComplexIf3;)I\n\n    move-result v3\n\n    sget v4, Lconditions/TestComplexIf3;->POWER_UP:I\n\n    if-eq v3, v4, :cond_185\n\n    .line 3420\n    iget-object v3, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    iget-object v4, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v4}, Lconditions/TestComplexIf3;->-get7(Lconditions/TestComplexIf3;)I\n\n    move-result v4\n\n    invoke-static {v4}, Ltest/Utils;->computeFrequency(I)F\n\n    move-result v4\n\n    invoke-static {v3, v4}, Lconditions/TestComplexIf3;->-wrap10(Lconditions/TestComplexIf3;F)Z\n\n    move-result v3\n\n    if-eqz v3, :cond_21f\n\n    .line 3421\n    iget-object v3, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    iget-object v4, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v4}, Lconditions/TestComplexIf3;->-get7(Lconditions/TestComplexIf3;)I\n\n    move-result v4\n\n    invoke-static {v4}, Ltest/Utils;->computeFrequency(I)F\n\n    move-result v4\n\n    invoke-static {v3, v4}, Lconditions/TestComplexIf3;->-wrap8(Lconditions/TestComplexIf3;F)Z\n\n    move-result v3\n\n    .line 3419\n    if-eqz v3, :cond_2c9\n\n    .line 3422\n    :cond_185\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0}, Lconditions/TestComplexIf3;->-get27(Lconditions/TestComplexIf3;)Landroid/os/PowerManager$WakeLock;\n\n    move-result-object v0\n\n    if-eqz v0, :cond_222\n\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0}, Lconditions/TestComplexIf3;->-get27(Lconditions/TestComplexIf3;)Landroid/os/PowerManager$WakeLock;\n\n    move-result-object v0\n\n    invoke-virtual {v0}, Landroid/os/PowerManager$WakeLock;->isHeld()Z\n\n    move-result v0\n\n    xor-int/lit8 v0, v0, 0x1\n\n    if-eqz v0, :cond_2c6\n\n    .line 3424\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0}, Lconditions/TestComplexIf3;->-get27(Lconditions/TestComplexIf3;)Landroid/os/PowerManager$WakeLock;\n\n    move-result-object v0\n\n    invoke-virtual {v0}, Landroid/os/PowerManager$WakeLock;->acquire()V\n\n    move v0, v1\n\n    .line 3426\n    :goto_1a5\n    const-string/jumbo v3, \"Service\"\n\n    const-string/jumbo v4, \"handle: start\"\n\n    invoke-static {v3, v4}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 3427\n    iget-object v3, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v3}, Lconditions/TestComplexIf3;->-wrap14(Lconditions/TestComplexIf3;)[I\n\n    move-result-object v3\n\n    .line 3429\n    :goto_1b4\n    const-string/jumbo v4, \"Service\"\n\n    const-string/jumbo v5, \"handle: end\"\n\n    invoke-static {v4, v5}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 3431\n    if-eqz v3, :cond_224\n\n    aget v4, v3, v2\n\n    const/16 v5, -0x64\n\n    if-ne v4, v5, :cond_224\n\n    .line 3434\n    const/4 v3, -0x1\n\n    .line 3433\n    filled-new-array {v3, v2}, [I\n\n    move-result-object v3\n\n    move-object v4, v3\n\n    move v3, v2\n\n    .line 3448\n    :goto_1cc\n    iget-object v5, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v5}, Lconditions/TestComplexIf3;->-get9(Lconditions/TestComplexIf3;)Z\n\n    move-result v5\n\n    if-eqz v5, :cond_1d9\n\n    .line 3449\n    iget-object v5, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-virtual {v5, v2}, Lconditions/TestComplexIf3;->setMute(Z)I\n\n    .line 3452\n    :cond_1d9\n    if-eqz v0, :cond_1f8\n\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0}, Lconditions/TestComplexIf3;->-get27(Lconditions/TestComplexIf3;)Landroid/os/PowerManager$WakeLock;\n\n    move-result-object v0\n\n    if-eqz v0, :cond_1f8\n\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0}, Lconditions/TestComplexIf3;->-get27(Lconditions/TestComplexIf3;)Landroid/os/PowerManager$WakeLock;\n\n    move-result-object v0\n\n    invoke-virtual {v0}, Landroid/os/PowerManager$WakeLock;->isHeld()Z\n\n    move-result v0\n\n    if-eqz v0, :cond_1f8\n\n    .line 3453\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0}, Lconditions/TestComplexIf3;->-get27(Lconditions/TestComplexIf3;)Landroid/os/PowerManager$WakeLock;\n\n    move-result-object v0\n\n    invoke-virtual {v0}, Landroid/os/PowerManager$WakeLock;->release()V\n\n    .line 3456\n    :cond_1f8\n    new-instance v0, Landroid/os/Bundle;\n\n    invoke-direct {v0, v6}, Landroid/os/Bundle;-><init>(I)V\n\n    .line 3457\n    const-string/jumbo v5, \"callback_flag\"\n\n    .line 3458\n    const/16 v6, 0xd\n\n    .line 3457\n    invoke-virtual {v0, v5, v6}, Landroid/os/Bundle;->putInt(Ljava/lang/String;I)V\n\n    .line 3460\n    const-string/jumbo v5, \"key_station_num\"\n\n    aget v1, v4, v1\n\n    invoke-virtual {v0, v5, v1}, Landroid/os/Bundle;->putInt(Ljava/lang/String;I)V\n\n    .line 3461\n    const-string/jumbo v1, \"key_is_scan\"\n\n    invoke-virtual {v0, v1, v3}, Landroid/os/Bundle;->putBoolean(Ljava/lang/String;Z)V\n\n    .line 3463\n    iget-object v1, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v1, v2}, Lconditions/TestComplexIf3;->-set4(Lconditions/TestComplexIf3;Z)Z\n\n    .line 3465\n    iget-object v1, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v1, v0}, Lconditions/TestComplexIf3;->-wrap29(Lconditions/TestComplexIf3;Landroid/os/Bundle;)V\n\n    goto/16 :goto_27\n\n    :cond_21f\n    move-object v3, v0\n\n    move v0, v2\n\n    .line 3421\n    goto :goto_1b4\n\n    :cond_222\n    move v0, v2\n\n    .line 3422\n    goto :goto_1a5\n\n    .line 3437\n    :cond_224\n    iget-object v4, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v4, v3}, Lconditions/TestComplexIf3;->-wrap15(Lconditions/TestComplexIf3;[I)[I\n\n    move-result-object v3\n\n    .line 3438\n    aget v4, v3, v2\n\n    .line 3439\n    iget-object v4, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    iget-object v5, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v5}, Lconditions/TestComplexIf3;->-get7(Lconditions/TestComplexIf3;)I\n\n    move-result v5\n\n    invoke-static {v5}, Ltest/Utils;->computeFrequency(I)F\n\n    move-result v5\n\n    invoke-static {v4, v5}, Lconditions/TestComplexIf3;->-wrap12(Lconditions/TestComplexIf3;F)Z\n\n    move-result v4\n\n    if-eqz v4, :cond_2c2\n\n    .line 3440\n    iget-object v4, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    iget-object v5, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v5}, Lconditions/TestComplexIf3;->-get7(Lconditions/TestComplexIf3;)I\n\n    move-result v5\n\n    invoke-virtual {v4, v5}, Lconditions/TestComplexIf3;->initService(I)V\n\n    move-object v4, v3\n\n    move v3, v1\n\n    goto :goto_1cc\n\n    .line 3470\n    :sswitch_24c\n    invoke-virtual {p1}, Landroid/os/Message;->getData()Landroid/os/Bundle;\n\n    move-result-object v0\n\n    .line 3471\n    const-string/jumbo v1, \"key_audiofocus_changed\"\n\n    invoke-virtual {v0, v1}, Landroid/os/Bundle;->getInt(Ljava/lang/String;)I\n\n    move-result v0\n\n    .line 3472\n    iget-object v1, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v1, v0}, Lconditions/TestComplexIf3;->-wrap43(Lconditions/TestComplexIf3;I)V\n\n    goto/16 :goto_27\n\n    .line 3476\n    :sswitch_25e\n    invoke-virtual {p1}, Landroid/os/Message;->getData()Landroid/os/Bundle;\n\n    move-result-object v0\n\n    .line 3477\n    iget-object v1, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    const-string/jumbo v2, \"option\"\n\n    invoke-virtual {v0, v2}, Landroid/os/Bundle;->getBoolean(Ljava/lang/String;)Z\n\n    move-result v0\n\n    invoke-static {v1, v0}, Lconditions/TestComplexIf3;->-wrap19(Lconditions/TestComplexIf3;Z)I\n\n    goto/16 :goto_27\n\n    .line 3481\n    :sswitch_270\n    invoke-virtual {p1}, Landroid/os/Message;->getData()Landroid/os/Bundle;\n\n    move-result-object v0\n\n    .line 3482\n    iget-object v1, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    const-string/jumbo v2, \"option\"\n\n    invoke-virtual {v0, v2}, Landroid/os/Bundle;->getBoolean(Ljava/lang/String;)Z\n\n    move-result v0\n\n    invoke-virtual {v1, v0}, Lconditions/TestComplexIf3;->setMute(Z)I\n\n    goto/16 :goto_27\n\n    .line 3486\n    :sswitch_282\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0}, Lconditions/TestComplexIf3;->-wrap16(Lconditions/TestComplexIf3;)I\n\n    goto/16 :goto_27\n\n    .line 3491\n    :sswitch_289\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0}, Lconditions/TestComplexIf3;->-wrap38(Lconditions/TestComplexIf3;)V\n\n    goto/16 :goto_27\n\n    .line 3495\n    :sswitch_290\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0}, Lconditions/TestComplexIf3;->-wrap11(Lconditions/TestComplexIf3;)Z\n\n    goto/16 :goto_27\n\n    .line 3499\n    :sswitch_297\n    invoke-virtual {p1}, Landroid/os/Message;->getData()Landroid/os/Bundle;\n\n    move-result-object v0\n\n    .line 3500\n    iget-object v1, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    const-string/jumbo v2, \"option\"\n\n    invoke-virtual {v0, v2}, Landroid/os/Bundle;->getBoolean(Ljava/lang/String;)Z\n\n    move-result v0\n\n    invoke-static {v1, v0}, Lconditions/TestComplexIf3;->-wrap36(Lconditions/TestComplexIf3;Z)V\n\n    goto/16 :goto_27\n\n    .line 3504\n    :sswitch_2a9\n    invoke-virtual {p1}, Landroid/os/Message;->getData()Landroid/os/Bundle;\n\n    move-result-object v0\n\n    .line 3505\n    iget-object v1, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    const-string/jumbo v2, \"name\"\n\n    invoke-virtual {v0, v2}, Landroid/os/Bundle;->getString(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v0\n\n    invoke-static {v1, v0}, Lconditions/TestComplexIf3;->-wrap32(Lconditions/TestComplexIf3;Ljava/lang/String;)V\n\n    goto/16 :goto_27\n\n    .line 3510\n    :sswitch_2bb\n    iget-object v0, p0, Lconditions/TestComplexIf3$Handler;->this$0:Lconditions/TestComplexIf3;\n\n    invoke-static {v0}, Lconditions/TestComplexIf3;->-wrap37(Lconditions/TestComplexIf3;)V\n\n    goto/16 :goto_27\n\n    :cond_2c2\n    move-object v4, v3\n\n    move v3, v1\n\n    goto/16 :goto_1cc\n\n    :cond_2c6\n    move v0, v2\n\n    goto/16 :goto_1a5\n\n    :cond_2c9\n    move-object v3, v0\n\n    move v0, v2\n\n    goto/16 :goto_1b4\n\n    :cond_2cd\n    move v1, v2\n\n    goto/16 :goto_113\n\n    .line 3308\n    :sswitch_data_2d0\n    .sparse-switch\n        0x4 -> :sswitch_6f\n        0x5 -> :sswitch_25e\n        0x7 -> :sswitch_270\n        0x9 -> :sswitch_28\n        0xa -> :sswitch_32\n        0xb -> :sswitch_38\n        0xd -> :sswitch_143\n        0xf -> :sswitch_aa\n        0x10 -> :sswitch_e6\n        0x12 -> :sswitch_282\n        0x15 -> :sswitch_297\n        0x16 -> :sswitch_289\n        0x17 -> :sswitch_290\n        0x1a -> :sswitch_2a9\n        0x1e -> :sswitch_24c\n        0x66 -> :sswitch_2bb\n    .end sparse-switch\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestComplexIf4.smali",
    "content": ".class public final Lconditions/TestComplexIf4;\n.super Ljava/lang/Object;\n\n# virtual methods\n.method public final test()V\n    .registers 14\n\n    const/4 v1, 0x0\n    const/16 v6, 0x0\n\n    :loop_0\n    if-ge v1, v1, :cond_0\n    goto :loop_0\n\n    :cond_0\n\n    if-lt v1, v6, :cond_1\n    goto/16 :goto_0\n    :cond_1\n\n    cmp-long v2, v6, v6\n    if-nez v2, :cond_2\n\n    goto/16 :near_end\n\n    :cond_2\n\n    if-le v2, v1, :cond_3\n    if-lez v1, :cond_4\n    goto :near_end\n\n    :cond_4\n    const/4 v5, 0x0\n\n    if-ge v5, v1, :cond_5\n    :cond_5\n    cmp-long v5, v6, v6\n\n    if-ltz v5, :cond_3\n    goto :near_end\n\n    :cond_3\n    cmp-long v5, v6, v6\n\n    :goto_0\n    if-eqz v1, :near_end\n\n    const/4 v1, 0x0\n\n    goto :cond_6\n\n    :near_end\n    const/4 v1, 0x0\n\n    :cond_6\n    if-ne v1, v1, :cond_magic\n    :cond_magic\n    const/4 v1, 0x0\n.end method"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestConditions18.smali",
    "content": ".class public final Lconditions/TestConditions18;\n.super Ljava/lang/Object;\n\n.field private map:Ljava/util/Map;\n\n.method public test(Ljava/lang/Object;)Z\n    .locals 1\n\n    if-eq p0, p1, :cond_1\n\n    instance-of v0, p1, Lconditions/TestConditions18;\n\n    if-eqz v0, :cond_0\n\n    check-cast p1, Lconditions/TestConditions18;\n\n    iget-object v0, p0, Lconditions/TestConditions18;->map:Ljava/util/Map;\n\n    iget-object p1, p1, Lconditions/TestConditions18;->map:Ljava/util/Map;\n\n    invoke-static {v0, p1}, Lconditions/TestConditions18;->st(Ljava/lang/Object;Ljava/lang/Object;)Z\n\n    move-result p1\n\n    if-eqz p1, :cond_0\n\n    goto :goto_0\n\n    :cond_0\n    const/4 p1, 0x0\n\n    return p1\n\n    :cond_1\n    :goto_0\n    const/4 p1, 0x1\n\n    return p1\n.end method\n\n.method private static st(Ljava/lang/Object;Ljava/lang/Object;)Z\n    .locals 1\n    const/4 v0, 0x0\n    return v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestConditions21.smali",
    "content": ".class public final Lconditions/TestConditions21;\n.super Ljava/lang/Object;\n\n.method public check(Ljava/lang/Object;)Z\n    .locals 2\n\n    if-eq p0, p1, :ret_true\n\n    instance-of v0, p1, Ljava/util/List;\n    if-eqz v0, :ret_false\n\n    check-cast p1, Ljava/util/List;\n\n    invoke-interface {p1}, Ljava/util/List;->isEmpty()Z\n    move-result v0\n\n    if-nez v0, :ret_false\n\n    invoke-interface {p1, p0}, Ljava/util/List;->contains(Ljava/lang/Object;)Z\n    move-result v0\n\n    if-eqz v0, :ret_false\n\n    goto :ret_true\n\n    :ret_false\n    const/4 p1, 0x0\n    return p1\n\n    :ret_true\n    const/4 p1, 0x1\n    return p1\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestIfAndSwitch.smali",
    "content": "###### Class conditions.TestIfAndSwitch (conditions.TestIfAndSwitch)\n.class Lconditions/TestIfAndSwitch;\n.super Ljava/lang/Object;\n.source \"TestIfAndSwitch.java\"\n\n\n# static fields\n.field private static final ACTION_MOVE:I = 0x2\n\n.field private static final C:I\n\n.field private static i:I\n\n.field private static final rd:Ljava/util/Random;\n\n\n# direct methods\n.method static constructor <clinit>()V\n    .registers 1\n\n    .line 8\n    new-instance v0, Ljava/util/Random;\n\n    invoke-direct {v0}, Ljava/util/Random;-><init>()V\n\n    sput-object v0, Lconditions/TestIfAndSwitch;->rd:Ljava/util/Random;\n\n    return-void\n.end method\n\n.method constructor <init>()V\n    .registers 1\n\n    .line 3\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n.method public static ifAndSwitch()Z\n    .registers 4\n\n    .line 12\n    nop\n\n    .line 13\n    sget-object v0, Lconditions/TestIfAndSwitch;->rd:Ljava/util/Random;\n\n    invoke-virtual {v0}, Ljava/util/Random;->nextInt()I\n\n    move-result v0\n\n    const/4 v1, 0x2\n\n    const/4 v2, 0x1\n\n    const/4 v3, 0x0\n\n    if-ne v0, v1, :cond_14\n\n    .line 14\n    sget v0, Lconditions/TestIfAndSwitch;->i:I\n\n    packed-switch v0, :pswitch_data_1a\n\n    goto :goto_14\n\n    .line 16\n    :pswitch_12\n    const/4 v0, 0x1\n\n    goto :goto_15\n\n    .line 20\n    :cond_14\n    :goto_14\n    const/4 v0, 0x0\n\n    :goto_15\n    if-eqz v0, :cond_18\n\n    .line 21\n    return v2\n\n    .line 23\n    :cond_18\n    return v3\n\n    nop\n\n    :pswitch_data_1a\n    .packed-switch 0x0\n        :pswitch_12\n    .end packed-switch\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestIfCodeStyle.smali",
    "content": ".class public Lconditions/TestIfCodeStyle;\n.super Ljava/lang/Object;\n.implements Landroid/os/Parcelable;\n\n\n.field public isActive:Z\n.field public isFactory:Z\n.field public moduleName:Ljava/lang/String;\n.field public modulePath:Ljava/lang/String;\n.field public preinstalledModulePath:Ljava/lang/String;\n.field public versionCode:J\n.field public versionName:Ljava/lang/String;\n\n\n.method public final readFromParcel(Landroid/os/Parcel;)V\n    .registers 9\n    .param p1, \"_aidl_parcel\"    # Landroid/os/Parcel;\n\n    .line 44\n    invoke-virtual {p1}, Landroid/os/Parcel;->dataPosition()I\n    move-result v0\n\n    .line 45\n    .local v0, \"_aidl_start_pos\":I\n    invoke-virtual {p1}, Landroid/os/Parcel;->readInt()I\n    move-result v1\n\n    .line 47\n    .local v1, \"_aidl_parcelable_size\":I\n    const-string v2, \"Overflow in the size of parcelable\"\n    const v3, 0x7fffffff\n    if-gez v1, :cond_1e\n\n    .line 63\n    sub-int/2addr v3, v1\n\n    if-gt v0, v3, :cond_18\n\n    .line 66\n    add-int v2, v0, v1\n    invoke-virtual {p1, v2}, Landroid/os/Parcel;->setDataPosition(I)V\n\n    .line 47\n    return-void\n\n    .line 64\n    :cond_18\n    new-instance v3, Ljava/lang/RuntimeException;\n    invoke-direct {v3, v2}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/String;)V\n    throw v3\n\n    .line 48\n    :cond_1e\n    :try_start_1e\n    invoke-virtual {p1}, Landroid/os/Parcel;->dataPosition()I\n\n    move-result v4\n    :try_end_22\n    .catchall {:try_start_1e .. :try_end_22} :catchall_fd\n\n    sub-int/2addr v4, v0\n\n    if-lt v4, v1, :cond_34\n\n    .line 63\n    sub-int/2addr v3, v1\n    if-gt v0, v3, :cond_2e\n\n    .line 66\n    add-int v2, v0, v1\n    invoke-virtual {p1, v2}, Landroid/os/Parcel;->setDataPosition(I)V\n\n    .line 48\n    return-void\n\n    .line 64\n    :cond_2e\n    new-instance v3, Ljava/lang/RuntimeException;\n    invoke-direct {v3, v2}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/String;)V\n    throw v3\n\n    .line 49\n    :cond_34\n    :try_start_34\n    invoke-virtual {p1}, Landroid/os/Parcel;->readString()Ljava/lang/String;\n    move-result-object v4\n\n    iput-object v4, p0, Lconditions/TestIfCodeStyle;->moduleName:Ljava/lang/String;\n\n    .line 50\n    invoke-virtual {p1}, Landroid/os/Parcel;->dataPosition()I\n    move-result v4\n    :try_end_3e\n    .catchall {:try_start_34 .. :try_end_3e} :catchall_fd\n\n    sub-int/2addr v4, v0\n\n    if-lt v4, v1, :cond_50\n\n    .line 63\n    sub-int/2addr v3, v1\n\n    if-gt v0, v3, :cond_4a\n\n    .line 66\n    add-int v2, v0, v1\n    invoke-virtual {p1, v2}, Landroid/os/Parcel;->setDataPosition(I)V\n\n    .line 50\n    return-void\n\n    .line 64\n    :cond_4a\n    new-instance v3, Ljava/lang/RuntimeException;\n    invoke-direct {v3, v2}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/String;)V\n    throw v3\n\n    .line 51\n    :cond_50\n    :try_start_50\n    invoke-virtual {p1}, Landroid/os/Parcel;->readString()Ljava/lang/String;\n    move-result-object v4\n    iput-object v4, p0, Lconditions/TestIfCodeStyle;->modulePath:Ljava/lang/String;\n\n    .line 52\n    invoke-virtual {p1}, Landroid/os/Parcel;->dataPosition()I\n    move-result v4\n\n    :try_end_5a\n    .catchall {:try_start_50 .. :try_end_5a} :catchall_fd\n\n    sub-int/2addr v4, v0\n    if-lt v4, v1, :cond_6c\n\n    .line 63\n    sub-int/2addr v3, v1\n    if-gt v0, v3, :cond_66\n\n    .line 66\n    add-int v2, v0, v1\n    invoke-virtual {p1, v2}, Landroid/os/Parcel;->setDataPosition(I)V\n\n    .line 52\n    return-void\n\n    .line 64\n    :cond_66\n    new-instance v3, Ljava/lang/RuntimeException;\n    invoke-direct {v3, v2}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/String;)V\n    throw v3\n\n    .line 53\n    :cond_6c\n    :try_start_6c\n    invoke-virtual {p1}, Landroid/os/Parcel;->readString()Ljava/lang/String;\n    move-result-object v4\n    iput-object v4, p0, Lconditions/TestIfCodeStyle;->preinstalledModulePath:Ljava/lang/String;\n\n    .line 54\n    invoke-virtual {p1}, Landroid/os/Parcel;->dataPosition()I\n    move-result v4\n\n    :try_end_76\n    .catchall {:try_start_6c .. :try_end_76} :catchall_fd\n\n    sub-int/2addr v4, v0\n    if-lt v4, v1, :cond_88\n\n    .line 63\n    sub-int/2addr v3, v1\n    if-gt v0, v3, :cond_82\n\n    .line 66\n    add-int v2, v0, v1\n    invoke-virtual {p1, v2}, Landroid/os/Parcel;->setDataPosition(I)V\n\n    .line 54\n    return-void\n\n    .line 64\n    :cond_82\n    new-instance v3, Ljava/lang/RuntimeException;\n    invoke-direct {v3, v2}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/String;)V\n    throw v3\n\n    .line 55\n    :cond_88\n    :try_start_88\n    invoke-virtual {p1}, Landroid/os/Parcel;->readLong()J\n    move-result-wide v4\n    iput-wide v4, p0, Lconditions/TestIfCodeStyle;->versionCode:J\n\n    .line 56\n    invoke-virtual {p1}, Landroid/os/Parcel;->dataPosition()I\n    move-result v4\n\n    :try_end_92\n    .catchall {:try_start_88 .. :try_end_92} :catchall_fd\n\n    sub-int/2addr v4, v0\n    if-lt v4, v1, :cond_a4\n\n    .line 63\n    sub-int/2addr v3, v1\n    if-gt v0, v3, :cond_9e\n\n    .line 66\n    add-int v2, v0, v1\n    invoke-virtual {p1, v2}, Landroid/os/Parcel;->setDataPosition(I)V\n\n    .line 56\n    return-void\n\n    .line 64\n    :cond_9e\n    new-instance v3, Ljava/lang/RuntimeException;\n    invoke-direct {v3, v2}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/String;)V\n    throw v3\n\n    .line 57\n    :cond_a4\n    :try_start_a4\n    invoke-virtual {p1}, Landroid/os/Parcel;->readString()Ljava/lang/String;\n    move-result-object v4\n    iput-object v4, p0, Lconditions/TestIfCodeStyle;->versionName:Ljava/lang/String;\n\n    .line 58\n    invoke-virtual {p1}, Landroid/os/Parcel;->dataPosition()I\n    move-result v4\n    :try_end_ae\n    .catchall {:try_start_a4 .. :try_end_ae} :catchall_fd\n\n    sub-int/2addr v4, v0\n    if-lt v4, v1, :cond_c0\n\n    .line 63\n    sub-int/2addr v3, v1\n    if-gt v0, v3, :cond_ba\n\n    .line 66\n    add-int v2, v0, v1\n    invoke-virtual {p1, v2}, Landroid/os/Parcel;->setDataPosition(I)V\n\n    .line 58\n    return-void\n\n    .line 64\n    :cond_ba\n    new-instance v3, Ljava/lang/RuntimeException;\n    invoke-direct {v3, v2}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/String;)V\n    throw v3\n\n    .line 59\n    :cond_c0\n    :try_start_c0\n    invoke-virtual {p1}, Landroid/os/Parcel;->readInt()I\n    move-result v4\n    const/4 v5, 0x1\n    const/4 v6, 0x0\n    if-eqz v4, :cond_ca\n    move v4, v5\n    goto :goto_cb\n\n    :cond_ca\n    move v4, v6\n\n    :goto_cb\n    iput-boolean v4, p0, Lconditions/TestIfCodeStyle;->isFactory:Z\n\n    .line 60\n    invoke-virtual {p1}, Landroid/os/Parcel;->dataPosition()I\n\n    move-result v4\n    :try_end_d1\n    .catchall {:try_start_c0 .. :try_end_d1} :catchall_fd\n\n    sub-int/2addr v4, v0\n    if-lt v4, v1, :cond_e3\n\n    .line 63\n    sub-int/2addr v3, v1\n\n    if-gt v0, v3, :cond_dd\n\n    .line 66\n    add-int v2, v0, v1\n    invoke-virtual {p1, v2}, Landroid/os/Parcel;->setDataPosition(I)V\n\n    .line 60\n    return-void\n\n    .line 64\n    :cond_dd\n    new-instance v3, Ljava/lang/RuntimeException;\n    invoke-direct {v3, v2}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/String;)V\n    throw v3\n\n    .line 61\n    :cond_e3\n    :try_start_e3\n    invoke-virtual {p1}, Landroid/os/Parcel;->readInt()I\n    move-result v4\n\n    if-eqz v4, :cond_ea\n    goto :goto_eb\n\n    :cond_ea\n    move v5, v6\n\n    :goto_eb\n    iput-boolean v5, p0, Lconditions/TestIfCodeStyle;->isActive:Z\n    :try_end_ed\n    .catchall {:try_start_e3 .. :try_end_ed} :catchall_fd\n\n    .line 63\n    sub-int/2addr v3, v1\n\n    if-gt v0, v3, :cond_f7\n\n    .line 66\n    add-int v2, v0, v1\n    invoke-virtual {p1, v2}, Landroid/os/Parcel;->setDataPosition(I)V\n\n    .line 67\n    nop\n\n    .line 68\n    return-void\n\n    .line 64\n    :cond_f7\n    new-instance v3, Ljava/lang/RuntimeException;\n    invoke-direct {v3, v2}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/String;)V\n    throw v3\n\n    .line 63\n    :catchall_fd\n    move-exception v4\n    sub-int/2addr v3, v1\n    if-le v0, v3, :cond_107\n\n    .line 64\n    new-instance v3, Ljava/lang/RuntimeException;\n    invoke-direct {v3, v2}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/String;)V\n    throw v3\n\n    .line 66\n    :cond_107\n    add-int v2, v0, v1\n    invoke-virtual {p1, v2}, Landroid/os/Parcel;->setDataPosition(I)V\n\n    .line 67\n    throw v4\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestIfCodeStyle2.smali",
    "content": ".class public Lconditions/TestIfCodeStyle2;\n.super Ljava/lang/Object;\n\n.method public execute(Ltest/TestSender;Ljava/lang/String;[Ljava/lang/String;)Z\n    .registers 21\n    .param p1, \"sender\"    # Ltest/TestSender;\n    .param p2, \"currentAlias\"    # Ljava/lang/String;\n    .param p3, \"args\"    # [Ljava/lang/String;\n    .prologue\n    .line 33\n    invoke-virtual/range {p0 .. p1}, Lconditions/TestIfCodeStyle2;->testPermission(Ltest/TestSender;)Z\n    move-result v4\n    if-nez v4, :cond_8\n    const/4 v4, 0x1\n    .line 165\n    :goto_7\n    return v4\n    .line 35\n    :cond_8\n    move-object/from16 v0, p3\n    array-length v4, v0\n    const/4 v5, 0x2\n    if-ge v4, v5, :cond_32\n    .line 36\n    new-instance v4, Ljava/lang/StringBuilder;\n    invoke-direct {v4}, Ljava/lang/StringBuilder;-><init>()V\n    sget-object v5, Ltest/ChatColor;->RED:Ltest/ChatColor;\n    invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/Object;)Ljava/lang/StringBuilder;\n    move-result-object v4\n    const-string v5, \"Usage: \"\n    invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v4\n    move-object/from16 v0, p0\n    iget-object v5, v0, Lconditions/TestIfCodeStyle2;->usageMessage:Ljava/lang/String;\n    invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v4\n    invoke-virtual {v4}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-interface {v0, v4}, Ltest/TestSender;->sendMessage(Ljava/lang/String;)V\n    .line 37\n    const/4 v4, 0x0\n    goto :goto_7\n    .line 40\n    :cond_32\n    const/4 v4, 0x0\n    aget-object v4, p3, v4\n    const-string v5, \"give\"\n    invoke-virtual {v4, v5}, Ljava/lang/String;->equalsIgnoreCase(Ljava/lang/String;)Z\n    move-result v4\n    if-nez v4, :cond_61\n    .line 41\n    new-instance v4, Ljava/lang/StringBuilder;\n    invoke-direct {v4}, Ljava/lang/StringBuilder;-><init>()V\n    sget-object v5, Ltest/ChatColor;->RED:Ltest/ChatColor;\n    invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/Object;)Ljava/lang/StringBuilder;\n    move-result-object v4\n    const-string v5, \"Usage: \"\n    invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v4\n    move-object/from16 v0, p0\n    iget-object v5, v0, Lconditions/TestIfCodeStyle2;->usageMessage:Ljava/lang/String;\n    invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v4\n    invoke-virtual {v4}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-interface {v0, v4}, Ltest/TestSender;->sendMessage(Ljava/lang/String;)V\n    .line 42\n    const/4 v4, 0x0\n    goto :goto_7\n    .line 45\n    :cond_61\n    const/4 v4, 0x1\n    aget-object v16, p3, v4\n    .line 46\n    .local v16, \"statisticString\":Ljava/lang/String;\n    const/4 v2, 0x0\n    .line 48\n    .local v2, \"player\":Ltest/entity/Player;\n    move-object/from16 v0, p3\n    array-length v4, v0\n    const/4 v5, 0x2\n    if-le v4, v5, :cond_7d\n    .line 49\n    const/4 v4, 0x1\n    aget-object v4, p3, v4\n    invoke-static {v4}, Ltest/Bukkit;->getPlayer(Ljava/lang/String;)Ltest/entity/Player;\n    move-result-object v2\n    .line 54\n    :cond_72\n    :goto_72\n    if-nez v2, :cond_88\n    .line 55\n    const-string v4, \"You must specify which player you wish to perform this action on.\"\n    move-object/from16 v0, p1\n    invoke-interface {v0, v4}, Ltest/TestSender;->sendMessage(Ljava/lang/String;)V\n    .line 56\n    const/4 v4, 0x1\n    goto :goto_7\n    .line 50\n    :cond_7d\n    move-object/from16 v0, p1\n    instance-of v4, v0, Ltest/entity/Player;\n    if-eqz v4, :cond_72\n    move-object/from16 v2, p1\n    .line 51\n    check-cast v2, Ltest/entity/Player;\n    goto :goto_72\n    .line 59\n    :cond_88\n    const-string v4, \"*\"\n    move-object/from16 v0, v16\n    invoke-virtual {v0, v4}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n    move-result v4\n    if-eqz v4, :cond_d7\n    .line 60\n    invoke-static {}, Ltest/Achievement;->values()[Ltest/Achievement;\n    move-result-object v5\n    array-length v7, v5\n    const/4 v4, 0x0\n    :goto_98\n    if-ge v4, v7, :cond_bf\n    aget-object v13, v5, v4\n    .line 61\n    .local v13, \"achievement\":Ltest/Achievement;\n    invoke-interface {v2, v13}, Ltest/entity/Player;->hasAchievement(Ltest/Achievement;)Z\n    move-result v8\n    if-eqz v8, :cond_a5\n    .line 60\n    :cond_a2\n    :goto_a2\n    add-int/lit8 v4, v4, 0x1\n    goto :goto_98\n    .line 64\n    :cond_a5\n    new-instance v1, Ltest/event/player/PlayerAchievementAwardedEvent;\n    invoke-direct {v1, v2, v13}, Ltest/event/player/PlayerAchievementAwardedEvent;-><init>(Ltest/entity/Player;Ltest/Achievement;)V\n    .line 65\n    .local v1, \"event\":Ltest/event/player/PlayerAchievementAwardedEvent;\n    invoke-static {}, Ltest/Bukkit;->getServer()Ltest/Server;\n    move-result-object v8\n    invoke-interface {v8}, Ltest/Server;->getPluginManager()Ltest/plugin/PluginManager;\n    move-result-object v8\n    invoke-interface {v8, v1}, Ltest/plugin/PluginManager;->callEvent(Ltest/event/Event;)V\n    .line 66\n    invoke-virtual {v1}, Ltest/event/player/PlayerAchievementAwardedEvent;->isCancelled()Z\n    move-result v8\n    if-nez v8, :cond_a2\n    .line 67\n    invoke-interface {v2, v13}, Ltest/entity/Player;->awardAchievement(Ltest/Achievement;)V\n    goto :goto_a2\n    .line 70\n    .end local v1    # \"event\":Ltest/event/player/PlayerAchievementAwardedEvent;\n    .end local v13    # \"achievement\":Ltest/Achievement;\n    :cond_bf\n    const-string v4, \"Successfully given all achievements to %s\"\n    const/4 v5, 0x1\n    new-array v5, v5, [Ljava/lang/Object;\n    const/4 v7, 0x0\n    invoke-interface {v2}, Ltest/entity/Player;->getName()Ljava/lang/String;\n    move-result-object v8\n    aput-object v8, v5, v7\n    invoke-static {v4, v5}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-static {v0, v4}, Ltest/command/Command;->broadcastCommandMessage(Ltest/TestSender;Ljava/lang/String;)V\n    .line 71\n    const/4 v4, 0x1\n    goto/16 :goto_7\n    .line 74\n    :cond_d7\n    invoke-static {}, Ltest/Bukkit;->getUnsafe()Ltest/UnsafeValues;\n    move-result-object v4\n    move-object/from16 v0, v16\n    invoke-interface {v4, v0}, Ltest/UnsafeValues;->getAchievementFromInternalName(Ljava/lang/String;)Ltest/Achievement;\n    move-result-object v13\n    .line 75\n    .restart local v13    # \"achievement\":Ltest/Achievement;\n    invoke-static {}, Ltest/Bukkit;->getUnsafe()Ltest/UnsafeValues;\n    move-result-object v4\n    move-object/from16 v0, v16\n    invoke-interface {v4, v0}, Ltest/UnsafeValues;->getStatisticFromInternalName(Ljava/lang/String;)Ltest/Statistic;\n    move-result-object v3\n    .line 77\n    .local v3, \"statistic\":Ltest/Statistic;\n    if-eqz v13, :cond_15d\n    .line 78\n    invoke-interface {v2, v13}, Ltest/entity/Player;->hasAchievement(Ltest/Achievement;)Z\n    move-result v4\n    if-eqz v4, :cond_10e\n    .line 79\n    const-string v4, \"%s already has achievement %s\"\n    const/4 v5, 0x2\n    new-array v5, v5, [Ljava/lang/Object;\n    const/4 v7, 0x0\n    invoke-interface {v2}, Ltest/entity/Player;->getName()Ljava/lang/String;\n    move-result-object v8\n    aput-object v8, v5, v7\n    const/4 v7, 0x1\n    aput-object v16, v5, v7\n    invoke-static {v4, v5}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-interface {v0, v4}, Ltest/TestSender;->sendMessage(Ljava/lang/String;)V\n    .line 80\n    const/4 v4, 0x1\n    goto/16 :goto_7\n    .line 83\n    :cond_10e\n    new-instance v1, Ltest/event/player/PlayerAchievementAwardedEvent;\n    invoke-direct {v1, v2, v13}, Ltest/event/player/PlayerAchievementAwardedEvent;-><init>(Ltest/entity/Player;Ltest/Achievement;)V\n    .line 84\n    .restart local v1    # \"event\":Ltest/event/player/PlayerAchievementAwardedEvent;\n    invoke-static {}, Ltest/Bukkit;->getServer()Ltest/Server;\n    move-result-object v4\n    invoke-interface {v4}, Ltest/Server;->getPluginManager()Ltest/plugin/PluginManager;\n    move-result-object v4\n    invoke-interface {v4, v1}, Ltest/plugin/PluginManager;->callEvent(Ltest/event/Event;)V\n    .line 85\n    invoke-virtual {v1}, Ltest/event/player/PlayerAchievementAwardedEvent;->isCancelled()Z\n    move-result v4\n    if-eqz v4, :cond_13f\n    .line 86\n    const-string v4, \"Unable to award %s the achievement %s\"\n    const/4 v5, 0x2\n    new-array v5, v5, [Ljava/lang/Object;\n    const/4 v7, 0x0\n    invoke-interface {v2}, Ltest/entity/Player;->getName()Ljava/lang/String;\n    move-result-object v8\n    aput-object v8, v5, v7\n    const/4 v7, 0x1\n    aput-object v16, v5, v7\n    invoke-static {v4, v5}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-interface {v0, v4}, Ltest/TestSender;->sendMessage(Ljava/lang/String;)V\n    .line 87\n    const/4 v4, 0x1\n    goto/16 :goto_7\n    .line 89\n    :cond_13f\n    invoke-interface {v2, v13}, Ltest/entity/Player;->awardAchievement(Ltest/Achievement;)V\n    .line 91\n    const-string v4, \"Successfully given %s the stat %s\"\n    const/4 v5, 0x2\n    new-array v5, v5, [Ljava/lang/Object;\n    const/4 v7, 0x0\n    invoke-interface {v2}, Ltest/entity/Player;->getName()Ljava/lang/String;\n    move-result-object v8\n    aput-object v8, v5, v7\n    const/4 v7, 0x1\n    aput-object v16, v5, v7\n    invoke-static {v4, v5}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-static {v0, v4}, Ltest/command/Command;->broadcastCommandMessage(Ltest/TestSender;Ljava/lang/String;)V\n    .line 92\n    const/4 v4, 0x1\n    goto/16 :goto_7\n    .line 95\n    .end local v1    # \"event\":Ltest/event/player/PlayerAchievementAwardedEvent;\n    :cond_15d\n    if-nez v3, :cond_173\n    .line 96\n    const-string v4, \"Unknown achievement or statistic \\'%s\\'\"\n    const/4 v5, 0x1\n    new-array v5, v5, [Ljava/lang/Object;\n    const/4 v7, 0x0\n    aput-object v16, v5, v7\n    invoke-static {v4, v5}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-interface {v0, v4}, Ltest/TestSender;->sendMessage(Ljava/lang/String;)V\n    .line 97\n    const/4 v4, 0x1\n    goto/16 :goto_7\n    .line 100\n    :cond_173\n    invoke-virtual {v3}, Ltest/Statistic;->getType()Ltest/Statistic$Type;\n    move-result-object v4\n    sget-object v5, Ltest/Statistic$Type;->UNTYPED:Ltest/Statistic$Type;\n    if-ne v4, v5, :cond_1d4\n    .line 101\n    new-instance v1, Ltest/event/player/PlayerStatisticIncrementEvent;\n    invoke-interface {v2, v3}, Ltest/entity/Player;->getStatistic(Ltest/Statistic;)I\n    move-result v4\n    invoke-interface {v2, v3}, Ltest/entity/Player;->getStatistic(Ltest/Statistic;)I\n    move-result v5\n    add-int/lit8 v5, v5, 0x1\n    invoke-direct {v1, v2, v3, v4, v5}, Ltest/event/player/PlayerStatisticIncrementEvent;-><init>(Ltest/entity/Player;Ltest/Statistic;II)V\n    .line 102\n    .local v1, \"event\":Ltest/event/player/PlayerStatisticIncrementEvent;\n    invoke-static {}, Ltest/Bukkit;->getServer()Ltest/Server;\n    move-result-object v4\n    invoke-interface {v4}, Ltest/Server;->getPluginManager()Ltest/plugin/PluginManager;\n    move-result-object v4\n    invoke-interface {v4, v1}, Ltest/plugin/PluginManager;->callEvent(Ltest/event/Event;)V\n    .line 103\n    invoke-virtual {v1}, Ltest/event/player/PlayerStatisticIncrementEvent;->isCancelled()Z\n    move-result v4\n    if-eqz v4, :cond_1b6\n    .line 104\n    const-string v4, \"Unable to increment %s for %s\"\n    const/4 v5, 0x2\n    new-array v5, v5, [Ljava/lang/Object;\n    const/4 v7, 0x0\n    aput-object v16, v5, v7\n    const/4 v7, 0x1\n    invoke-interface {v2}, Ltest/entity/Player;->getName()Ljava/lang/String;\n    move-result-object v8\n    aput-object v8, v5, v7\n    invoke-static {v4, v5}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-interface {v0, v4}, Ltest/TestSender;->sendMessage(Ljava/lang/String;)V\n    .line 105\n    const/4 v4, 0x1\n    goto/16 :goto_7\n    .line 107\n    :cond_1b6\n    invoke-interface {v2, v3}, Ltest/entity/Player;->incrementStatistic(Ltest/Statistic;)V\n    .line 108\n    const-string v4, \"Successfully given %s the stat %s\"\n    const/4 v5, 0x2\n    new-array v5, v5, [Ljava/lang/Object;\n    const/4 v7, 0x0\n    invoke-interface {v2}, Ltest/entity/Player;->getName()Ljava/lang/String;\n    move-result-object v8\n    aput-object v8, v5, v7\n    const/4 v7, 0x1\n    aput-object v16, v5, v7\n    invoke-static {v4, v5}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-static {v0, v4}, Ltest/command/Command;->broadcastCommandMessage(Ltest/TestSender;Ljava/lang/String;)V\n    .line 109\n    const/4 v4, 0x1\n    goto/16 :goto_7\n    .line 112\n    .end local v1    # \"event\":Ltest/event/player/PlayerStatisticIncrementEvent;\n    :cond_1d4\n    invoke-virtual {v3}, Ltest/Statistic;->getType()Ltest/Statistic$Type;\n    move-result-object v4\n    sget-object v5, Ltest/Statistic$Type;->ENTITY:Ltest/Statistic$Type;\n    if-ne v4, v5, :cond_274\n    .line 113\n    const-string v4, \".\"\n    move-object/from16 v0, v16\n    invoke-virtual {v0, v4}, Ljava/lang/String;->lastIndexOf(Ljava/lang/String;)I\n    move-result v4\n    add-int/lit8 v4, v4, 0x1\n    move-object/from16 v0, v16\n    invoke-virtual {v0, v4}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n    move-result-object v4\n    invoke-static {v4}, Ltest/entity/EntityType;->fromName(Ljava/lang/String;)Ltest/entity/EntityType;\n    move-result-object v6\n    .line 115\n    .local v6, \"entityType\":Ltest/entity/EntityType;\n    if-nez v6, :cond_206\n    .line 116\n    const-string v4, \"Unknown achievement or statistic \\'%s\\'\"\n    const/4 v5, 0x1\n    new-array v5, v5, [Ljava/lang/Object;\n    const/4 v7, 0x0\n    aput-object v16, v5, v7\n    invoke-static {v4, v5}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-interface {v0, v4}, Ltest/TestSender;->sendMessage(Ljava/lang/String;)V\n    .line 117\n    const/4 v4, 0x1\n    goto/16 :goto_7\n    .line 120\n    :cond_206\n    new-instance v1, Ltest/event/player/PlayerStatisticIncrementEvent;\n    invoke-interface {v2, v3}, Ltest/entity/Player;->getStatistic(Ltest/Statistic;)I\n    move-result v4\n    invoke-interface {v2, v3}, Ltest/entity/Player;->getStatistic(Ltest/Statistic;)I\n    move-result v5\n    add-int/lit8 v5, v5, 0x1\n    invoke-direct/range {v1 .. v6}, Ltest/event/player/PlayerStatisticIncrementEvent;-><init>(Ltest/entity/Player;Ltest/Statistic;IILtest/entity/EntityType;)V\n    .line 121\n    .restart local v1    # \"event\":Ltest/event/player/PlayerStatisticIncrementEvent;\n    invoke-static {}, Ltest/Bukkit;->getServer()Ltest/Server;\n    move-result-object v4\n    invoke-interface {v4}, Ltest/Server;->getPluginManager()Ltest/plugin/PluginManager;\n    move-result-object v4\n    invoke-interface {v4, v1}, Ltest/plugin/PluginManager;->callEvent(Ltest/event/Event;)V\n    .line 122\n    invoke-virtual {v1}, Ltest/event/player/PlayerStatisticIncrementEvent;->isCancelled()Z\n    move-result v4\n    if-eqz v4, :cond_241\n    .line 123\n    const-string v4, \"Unable to increment %s for %s\"\n    const/4 v5, 0x2\n    new-array v5, v5, [Ljava/lang/Object;\n    const/4 v7, 0x0\n    aput-object v16, v5, v7\n    const/4 v7, 0x1\n    invoke-interface {v2}, Ltest/entity/Player;->getName()Ljava/lang/String;\n    move-result-object v8\n    aput-object v8, v5, v7\n    invoke-static {v4, v5}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-interface {v0, v4}, Ltest/TestSender;->sendMessage(Ljava/lang/String;)V\n    .line 124\n    const/4 v4, 0x1\n    goto/16 :goto_7\n    .line 128\n    :cond_241\n    :try_start_241\n    invoke-interface {v2, v3, v6}, Ltest/entity/Player;->incrementStatistic(Ltest/Statistic;Ltest/entity/EntityType;)V\n    :try_end_244\n    .catch Ljava/lang/IllegalArgumentException; {:try_start_241 .. :try_end_244} :catch_25f\n    .line 164\n    .end local v6    # \"entityType\":Ltest/entity/EntityType;\n    :goto_244\n    const-string v4, \"Successfully given %s the stat %s\"\n    const/4 v5, 0x2\n    new-array v5, v5, [Ljava/lang/Object;\n    const/4 v7, 0x0\n    invoke-interface {v2}, Ltest/entity/Player;->getName()Ljava/lang/String;\n    move-result-object v8\n    aput-object v8, v5, v7\n    const/4 v7, 0x1\n    aput-object v16, v5, v7\n    invoke-static {v4, v5}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-static {v0, v4}, Ltest/command/Command;->broadcastCommandMessage(Ltest/TestSender;Ljava/lang/String;)V\n    .line 165\n    const/4 v4, 0x1\n    goto/16 :goto_7\n    .line 129\n    .restart local v6    # \"entityType\":Ltest/entity/EntityType;\n    :catch_25f\n    move-exception v14\n    .line 130\n    .local v14, \"e\":Ljava/lang/IllegalArgumentException;\n    const-string v4, \"Unknown achievement or statistic \\'%s\\'\"\n    const/4 v5, 0x1\n    new-array v5, v5, [Ljava/lang/Object;\n    const/4 v7, 0x0\n    aput-object v16, v5, v7\n    invoke-static {v4, v5}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-interface {v0, v4}, Ltest/TestSender;->sendMessage(Ljava/lang/String;)V\n    .line 131\n    const/4 v4, 0x1\n    goto/16 :goto_7\n    .line 136\n    .end local v1    # \"event\":Ltest/event/player/PlayerStatisticIncrementEvent;\n    .end local v6    # \"entityType\":Ltest/entity/EntityType;\n    .end local v14    # \"e\":Ljava/lang/IllegalArgumentException;\n    :cond_274\n    :try_start_274\n    const-string v4, \".\"\n    move-object/from16 v0, v16\n    invoke-virtual {v0, v4}, Ljava/lang/String;->lastIndexOf(Ljava/lang/String;)I\n    move-result v4\n    add-int/lit8 v4, v4, 0x1\n    move-object/from16 v0, v16\n    invoke-virtual {v0, v4}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n    move-result-object v9\n    const/4 v10, 0x0\n    const v11, 0x7fffffff\n    const/4 v12, 0x1\n    move-object/from16 v7, p0\n    move-object/from16 v8, p1\n    invoke-virtual/range {v7 .. v12}, Lconditions/TestIfCodeStyle2;->getInteger(Ltest/TestSender;Ljava/lang/String;IIZ)I\n    :try_end_290\n    .catch Ljava/lang/NumberFormatException; {:try_start_274 .. :try_end_290} :catch_2ab\n    move-result v15\n    .line 142\n    .local v15, \"id\":I\n    invoke-static {v15}, Ltest/Material;->getMaterial(I)Ltest/Material;\n    move-result-object v12\n    .line 144\n    .local v12, \"material\":Ltest/Material;\n    if-nez v12, :cond_2b8\n    .line 145\n    const-string v4, \"Unknown achievement or statistic \\'%s\\'\"\n    const/4 v5, 0x1\n    new-array v5, v5, [Ljava/lang/Object;\n    const/4 v7, 0x0\n    aput-object v16, v5, v7\n    invoke-static {v4, v5}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-interface {v0, v4}, Ltest/TestSender;->sendMessage(Ljava/lang/String;)V\n    .line 146\n    const/4 v4, 0x1\n    goto/16 :goto_7\n    .line 137\n    .end local v12    # \"material\":Ltest/Material;\n    .end local v15    # \"id\":I\n    :catch_2ab\n    move-exception v14\n    .line 138\n    .local v14, \"e\":Ljava/lang/NumberFormatException;\n    invoke-virtual {v14}, Ljava/lang/NumberFormatException;->getMessage()Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-interface {v0, v4}, Ltest/TestSender;->sendMessage(Ljava/lang/String;)V\n    .line 139\n    const/4 v4, 0x1\n    goto/16 :goto_7\n    .line 149\n    .end local v14    # \"e\":Ljava/lang/NumberFormatException;\n    .restart local v12    # \"material\":Ltest/Material;\n    .restart local v15    # \"id\":I\n    :cond_2b8\n    new-instance v1, Ltest/event/player/PlayerStatisticIncrementEvent;\n    invoke-interface {v2, v3}, Ltest/entity/Player;->getStatistic(Ltest/Statistic;)I\n    move-result v10\n    invoke-interface {v2, v3}, Ltest/entity/Player;->getStatistic(Ltest/Statistic;)I\n    move-result v4\n    add-int/lit8 v11, v4, 0x1\n    move-object v7, v1\n    move-object v8, v2\n    move-object v9, v3\n    invoke-direct/range {v7 .. v12}, Ltest/event/player/PlayerStatisticIncrementEvent;-><init>(Ltest/entity/Player;Ltest/Statistic;IILtest/Material;)V\n    .line 150\n    .restart local v1    # \"event\":Ltest/event/player/PlayerStatisticIncrementEvent;\n    invoke-static {}, Ltest/Bukkit;->getServer()Ltest/Server;\n    move-result-object v4\n    invoke-interface {v4}, Ltest/Server;->getPluginManager()Ltest/plugin/PluginManager;\n    move-result-object v4\n    invoke-interface {v4, v1}, Ltest/plugin/PluginManager;->callEvent(Ltest/event/Event;)V\n    .line 151\n    invoke-virtual {v1}, Ltest/event/player/PlayerStatisticIncrementEvent;->isCancelled()Z\n    move-result v4\n    if-eqz v4, :cond_2f6\n    .line 152\n    const-string v4, \"Unable to increment %s for %s\"\n    const/4 v5, 0x2\n    new-array v5, v5, [Ljava/lang/Object;\n    const/4 v7, 0x0\n    aput-object v16, v5, v7\n    const/4 v7, 0x1\n    invoke-interface {v2}, Ltest/entity/Player;->getName()Ljava/lang/String;\n    move-result-object v8\n    aput-object v8, v5, v7\n    invoke-static {v4, v5}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-interface {v0, v4}, Ltest/TestSender;->sendMessage(Ljava/lang/String;)V\n    .line 153\n    const/4 v4, 0x1\n    goto/16 :goto_7\n    .line 157\n    :cond_2f6\n    :try_start_2f6\n    invoke-interface {v2, v3, v12}, Ltest/entity/Player;->incrementStatistic(Ltest/Statistic;Ltest/Material;)V\n    :try_end_2f9\n    .catch Ljava/lang/IllegalArgumentException; {:try_start_2f6 .. :try_end_2f9} :catch_2fb\n    goto/16 :goto_244\n    .line 158\n    :catch_2fb\n    move-exception v14\n    .line 159\n    .local v14, \"e\":Ljava/lang/IllegalArgumentException;\n    const-string v4, \"Unknown achievement or statistic \\'%s\\'\"\n    const/4 v5, 0x1\n    new-array v5, v5, [Ljava/lang/Object;\n    const/4 v7, 0x0\n    aput-object v16, v5, v7\n    invoke-static {v4, v5}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\n    move-result-object v4\n    move-object/from16 v0, p1\n    invoke-interface {v0, v4}, Ltest/TestSender;->sendMessage(Ljava/lang/String;)V\n    .line 160\n    const/4 v4, 0x1\n    goto/16 :goto_7\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestIfElseAndConditionIntermediateInstruction.smali",
    "content": "###### Class conditions.TestIfElseAndConditionIntermediateInstruction (conditions.TestIfElseAndConditionIntermediateInstruction)\n.class public Lconditions/TestIfElseAndConditionIntermediateInstruction;\n.super Ljava/lang/Object;\n.source \"TestIfElseAndConditionIntermediateInstruction.java\"\n\n\n# static fields\n.field private static final CONST:F = 342.0f\n\n\n# instance fields\n.field private bool:Z\n\n.field private num:F\n\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    .line 3\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n.method private nothing1()V\n    .registers 1\n\n    .line 19\n    return-void\n.end method\n\n.method private nothing2()V\n    .registers 1\n\n    .line 23\n    return-void\n.end method\n\n\n# virtual methods\n.method public function()V\n    .registers 3\n\n    .line 9\n    iget-boolean v0, p0, Lconditions/TestIfElseAndConditionIntermediateInstruction;->bool:Z\n\n    if-eqz v0, :cond_12\n\n    iget v0, p0, Lconditions/TestIfElseAndConditionIntermediateInstruction;->num:F\n\n    const/high16 v1, 0x3f800000    # 1.0f\n\n    cmpg-float v1, v0, v1\n\n    if-gez v1, :cond_12\n\n    .line 10\n    const/high16 v1, 0x43ab0000    # 342.0f\n\n    add-float/2addr v0, v1\n\n    iput v0, p0, Lconditions/TestIfElseAndConditionIntermediateInstruction;->num:F\n\n    goto :goto_15\n\n    .line 12\n    :cond_12\n    invoke-direct {p0}, Lconditions/TestIfElseAndConditionIntermediateInstruction;->nothing2()V\n\n    .line 14\n    :goto_15\n    invoke-direct {p0}, Lconditions/TestIfElseAndConditionIntermediateInstruction;->nothing1()V\n\n    .line 15\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestInnerAssign3.smali",
    "content": ".class public Lconditions/TestInnerAssign3;\n.super LTestSuper;\n.source \"Test.java\"\n\n.method public test()V\n    .locals 5\n\n    const/4 v0, 0\n    const/4 v1, 0\n    const/4 v4, 0\n\n    if-eqz v4, :cond_0\n\n    const/4 v2, 0\n\n    invoke-virtual {v2}, LTestClass1;->testMethod()LTestClass2;\n\n    move-result-object v0\n\n    if-eqz v0, :cond_0\n\n    if-eq v1, v0, :cond_0\n\n    iget-object v3, v2, LTestClass1;->testField:LTestClass3;\n\n    if-eqz v3, :cond_0\n\n    :cond_0\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestOutBlock.smali",
    "content": ".class public Lconditions/TestOutBlock;\n.super Lcom/eltechs/axs/activities/FrameworkActivity;\n\n\n.method protected onCreate(Landroid/os/Bundle;)V\n    .registers 9\n\n    .line 98\n    invoke-super {p0, p1}, Lcom/eltechs/axs/activities/FrameworkActivity;->onCreate(Landroid/os/Bundle;)V\n\n    .line 101\n    invoke-virtual {p0}, Lconditions/TestOutBlock;->getApplicationState()Lcom/eltechs/axs/applicationState/ApplicationStateBase;\n\n    move-result-object p1\n\n    .line 102\n    invoke-interface {p1}, Lcom/eltechs/axs/applicationState/ApplicationStateBase;->getEnvironment()Lcom/eltechs/axs/environmentService/AXSEnvironment;\n\n    move-result-object v0\n\n    const-class v1, Lcom/eltechs/axs/environmentService/components/XServerComponent;\n\n    invoke-virtual {v0, v1}, Lcom/eltechs/axs/environmentService/AXSEnvironment;->getComponent(Ljava/lang/Class;)Lcom/eltechs/axs/environmentService/EnvironmentComponent;\n\n    move-result-object v0\n\n    check-cast v0, Lcom/eltechs/axs/environmentService/components/XServerComponent;\n\n    .line 103\n    invoke-virtual {p0}, Lconditions/TestOutBlock;->getIntent()Landroid/content/Intent;\n\n    move-result-object v1\n\n    const-string v2, \"facadeclass\"\n\n    invoke-virtual {v1, v2}, Landroid/content/Intent;->getSerializableExtra(Ljava/lang/String;)Ljava/io/Serializable;\n\n    move-result-object v1\n\n    check-cast v1, Ljava/lang/Class;\n\n    if-eqz v1, :cond_46\n\n    const/4 v2, 0x2\n\n    const/4 v3, 0x0\n\n    .line 108\n    :try_start_23\n    new-array v4, v2, [Ljava/lang/Class;\n\n    const-class v5, Lcom/eltechs/axs/xserver/XServer;\n\n    aput-object v5, v4, v3\n\n    const-class v5, Lcom/eltechs/axs/applicationState/ApplicationStateBase;\n\n    const/4 v6, 0x1\n\n    aput-object v5, v4, v6\n\n    invoke-virtual {v1, v4}, Ljava/lang/Class;->getDeclaredConstructor([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;\n\n    move-result-object v1\n\n    .line 109\n    new-array v2, v2, [Ljava/lang/Object;\n\n    invoke-virtual {v0}, Lcom/eltechs/axs/environmentService/components/XServerComponent;->getXServer()Lcom/eltechs/axs/xserver/XServer;\n\n    move-result-object v4\n\n    aput-object v4, v2, v3\n\n    aput-object p1, v2, v6\n\n    invoke-virtual {v1, v2}, Ljava/lang/reflect/Constructor;->newInstance([Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v1\n\n    check-cast v1, Lcom/eltechs/axs/xserver/ViewFacade;\n    :try_end_42\n    .catch Ljava/lang/Exception; {:try_start_23 .. :try_end_42} :catch_43\n\n    goto :goto_47\n\n    .line 112\n    :catch_43\n    invoke-static {v3}, Lcom/eltechs/axs/helpers/Assert;->state(Z)V\n\n    :cond_46\n    const/4 v1, 0x0\n\n    .line 118\n    :goto_47\n    invoke-virtual {p0}, Lconditions/TestOutBlock;->getWindow()Landroid/view/Window;\n\n    move-result-object v2\n\n    const/16 v3, 0x80\n\n    invoke-virtual {v2, v3}, Landroid/view/Window;->addFlags(I)V\n\n    .line 120\n    invoke-virtual {p0}, Lconditions/TestOutBlock;->getWindow()Landroid/view/Window;\n\n    move-result-object v2\n\n    const/high16 v3, 0x400000\n\n    invoke-virtual {v2, v3}, Landroid/view/Window;->addFlags(I)V\n\n    .line 125\n    sget v2, Lcom/eltechs/axs/R$layout;->main:I\n\n    invoke-virtual {p0, v2}, Lconditions/TestOutBlock;->setContentView(I)V\n\n    .line 127\n    invoke-direct {p0}, Lconditions/TestOutBlock;->checkForSuddenDeath()Z\n\n    move-result v2\n\n    if-eqz v2, :cond_65\n\n    return-void\n\n    .line 135\n    :cond_65\n    new-instance v2, Lcom/eltechs/axs/widgets/viewOfXServer/ViewOfXServer;\n\n    invoke-virtual {v0}, Lcom/eltechs/axs/environmentService/components/XServerComponent;->getXServer()Lcom/eltechs/axs/xserver/XServer;\n\n    move-result-object v0\n\n    invoke-interface {p1}, Lcom/eltechs/axs/applicationState/ApplicationStateBase;->getXServerViewConfiguration()Lcom/eltechs/axs/configuration/XServerViewConfiguration;\n\n    move-result-object p1\n\n    invoke-direct {v2, p0, v0, v1, p1}, Lcom/eltechs/axs/widgets/viewOfXServer/ViewOfXServer;-><init>(Landroid/content/Context;Lcom/eltechs/axs/xserver/XServer;Lcom/eltechs/axs/xserver/ViewFacade;Lcom/eltechs/axs/configuration/XServerViewConfiguration;)V\n\n    iput-object v2, p0, Lconditions/TestOutBlock;->viewOfXServer:Lcom/eltechs/axs/widgets/viewOfXServer/ViewOfXServer;\n\n    .line 137\n    iget-object p1, p0, Lconditions/TestOutBlock;->periodicIabCheckTimer:Landroid/os/CountDownTimer;\n\n    invoke-virtual {p1}, Landroid/os/CountDownTimer;->start()Landroid/os/CountDownTimer;\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestTernary4.smali",
    "content": ".class public final Lconditions/TestTernary4;\n.super Ljava/lang/Object;\n\n.field private defaultValuesByPath:Ljava/util/Map;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ljava/util/Map<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/lang/Object;\",\n            \">;\"\n        }\n    .end annotation\n.end field\n\n.field private valuesByPath:Ljava/util/Map;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ljava/util/Map<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/lang/Object;\",\n            \">;\"\n        }\n    .end annotation\n.end field\n\n.method private test(Ljava/util/HashMap;)Ljava/util/Set;\n    .registers 10\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/util/HashMap<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/lang/Object;\",\n            \">;)\",\n            \"Ljava/util/Set;\"\n        }\n    .end annotation\n\n    .line 278\n    new-instance v0, Ljava/util/HashSet;\n\n    invoke-direct {v0}, Ljava/util/HashSet;-><init>()V\n\n    .line 280\n    iget-object v1, p0, Lconditions/TestTernary4;->defaultValuesByPath:Ljava/util/Map;\n\n    monitor-enter v1\n\n    .line 281\n    :try_start_14\n    iget-object v3, p0, Lconditions/TestTernary4;->defaultValuesByPath:Ljava/util/Map;\n\n    invoke-interface {v3}, Ljava/util/Map;->keySet()Ljava/util/Set;\n\n    move-result-object v3\n\n    invoke-interface {v3}, Ljava/util/Set;->iterator()Ljava/util/Iterator;\n\n    move-result-object v3\n\n    :cond_1e\n    :goto_1e\n    invoke-interface {v3}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v4\n\n    if-eqz v4, :cond_4c\n\n    invoke-interface {v3}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v4\n\n    check-cast v4, Ljava/lang/String;\n\n    .line 286\n    invoke-virtual {p1, v4}, Ljava/util/HashMap;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v5\n\n    const/4 v6, 0x1\n\n    if-eqz v5, :cond_3b\n\n    .line 287\n    invoke-direct {p0, v4}, Lconditions/TestTernary4;->getValueObject(Ljava/lang/String;)Ljava/lang/Object;\n\n    move-result-object v7\n\n    invoke-virtual {v7, v5}, Ljava/lang/Object;->equals(Ljava/lang/Object;)Z\n\n    move-result v5\n\n    xor-int/2addr v5, v6\n\n    goto :goto_46\n\n    .line 289\n    :cond_3b\n    iget-object v5, p0, Lconditions/TestTernary4;->valuesByPath:Ljava/util/Map;\n\n    invoke-interface {v5, v4}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v5\n\n    if-eqz v5, :cond_45\n\n    const/4 v5, 0x1\n\n    goto :goto_46\n\n    :cond_45\n    const/4 v5, 0x0\n\n    :goto_46\n    if-eqz v5, :cond_1e\n\n    .line 293\n    invoke-interface {v0, v4}, Ljava/util/Set;->add(Ljava/lang/Object;)Z\n\n    goto :goto_1e\n\n    .line 296\n    :cond_4c\n    monitor-exit v1\n\n    return-object v0\n\n    :catchall_4e\n    move-exception p1\n\n    monitor-exit v1\n    :try_end_50\n    .catchall {:try_start_14 .. :try_end_50} :catchall_4e\n\n    throw p1\n.end method\n\n.method private getValueObject(Ljava/lang/String;)Ljava/lang/Object;\n    .locals 1\n    const/4 v0, 0x0\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestTernaryInIf2.smali",
    "content": ".class public LTestTernaryInIf2;\n.super Ljava/lang/Object;\n\n\n# instance fields\n.field private a:Ljava/lang/String;\n\n.field private b:Ljava/lang/String;\n\n.field private c:Ljava/lang/String;\n\n# direct methods\n.method public constructor <init>()V\n    .locals 0\n\n    .line 10\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n\n# virtual methods\n.method public equals(Ljava/lang/Object;)Z\n    .locals 4\n\n    const/4 v0, 0x1\n\n    if-ne p0, p1, :cond_0\n\n    return v0\n\n    :cond_0\n    const/4 v1, 0x0\n\n    if-eqz p1, :cond_a\n\n    .line 110\n    invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class;\n\n    move-result-object v2\n\n    invoke-virtual {p1}, Ljava/lang/Object;->getClass()Ljava/lang/Class;\n\n    move-result-object v3\n\n    if-eq v2, v3, :cond_1\n\n    goto :goto_4\n\n    .line 112\n    :cond_1\n    check-cast p1, LTestTernaryInIf2;\n\n    .line 114\n    iget-object v2, p0, LTestTernaryInIf2;->a:Ljava/lang/String;\n\n    if-eqz v2, :cond_2\n\n    iget-object v2, p0, LTestTernaryInIf2;->a:Ljava/lang/String;\n\n    iget-object v3, p1, LTestTernaryInIf2;->a:Ljava/lang/String;\n\n    invoke-virtual {v2, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v2\n\n    if-nez v2, :cond_3\n\n    goto :goto_0\n\n    :cond_2\n    iget-object v2, p1, LTestTernaryInIf2;->a:Ljava/lang/String;\n\n    if-eqz v2, :cond_3\n\n    :goto_0\n    return v1\n\n    .line 116\n    :cond_3\n    iget-object v2, p0, LTestTernaryInIf2;->b:Ljava/lang/String;\n\n    if-eqz v2, :cond_4\n\n    iget-object v2, p0, LTestTernaryInIf2;->b:Ljava/lang/String;\n\n    iget-object v3, p1, LTestTernaryInIf2;->b:Ljava/lang/String;\n\n    invoke-virtual {v2, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v2\n\n    if-nez v2, :cond_5\n\n    goto :goto_1\n\n    :cond_4\n    iget-object v2, p1, LTestTernaryInIf2;->b:Ljava/lang/String;\n\n    if-eqz v2, :cond_5\n\n    :goto_1\n    return v1\n\n    .line 118\n    :cond_5\n    iget-object v2, p0, LTestTernaryInIf2;->c:Ljava/lang/String;\n\n    if-eqz v2, :cond_6\n\n    iget-object v2, p0, LTestTernaryInIf2;->c:Ljava/lang/String;\n\n    iget-object v3, p1, LTestTernaryInIf2;->c:Ljava/lang/String;\n\n    invoke-virtual {v2, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v2\n\n    if-nez v2, :cond_7\n\n    return v1\n\n\n    :cond_6\n    iget-object v2, p1, LTestTernaryInIf2;->c:Ljava/lang/String;\n\n    if-eqz v2, :cond_7\n\n    .line 120\n    :cond_7\n\n    :cond_8\n    const/4 v0, 0x0\n\n    :goto_3\n    return v0\n\n    :cond_a\n    :goto_4\n    return v1\n.end method\n\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestTernaryInIf3.smali",
    "content": ".class public Lconditions/TestTernaryInIf3;\n.super Ljava/lang/Object;\n\n.method public static final A01(LX/73h;FFZ)Landroid/util/Pair;\n    .registers 7\n\n    .line 0\n    const/4 v1, 0x0\n\n    .line 1\n    cmpl-float v0, p1, v1\n\n    .line 2\n    .line 3\n    if-eqz v0, :cond_33\n\n    .line 4\n    .line 5\n    cmpl-float v0, p2, v1\n\n    .line 6\n    .line 7\n    if-eqz v0, :cond_33\n\n    .line 8\n    .line 9\n    if-eqz p3, :cond_24\n\n    .line 10\n    .line 11\n    cmpg-float v0, p2, v1\n\n    .line 12\n    .line 13\n    if-ltz v0, :cond_26\n\n    .line 14\n    .line 15\n    :cond_f\n    iget-object v1, p0, LX/73h;->A00:LX/5Yj;\n\n    .line 16\n    .line 17\n    const-string v0, \"translationXCurveDownwards\"\n\n    .line 18\n    .line 19\n    invoke-virtual {v1, v0}, LX/5Yj;->A03(Ljava/lang/String;)LX/5Wd;\n\n    .line 20\n    .line 21\n    .line 22\n    move-result-object v2\n\n    .line 23\n    iget-object v1, p0, LX/73h;->A00:LX/5Yj;\n\n    .line 24\n    .line 25\n    const-string v0, \"translationYCurveDownwards\"\n\n    .line 26\n    .line 27\n    :goto_1b\n    invoke-virtual {v1, v0}, LX/5Yj;->A03(Ljava/lang/String;)LX/5Wd;\n\n    .line 28\n    .line 29\n    .line 30\n    move-result-object v0\n\n    .line 31\n    invoke-static {v2, v0}, LX/0xz;->A0F(Ljava/lang/Object;Ljava/lang/Object;)Landroid/util/Pair;\n\n    .line 32\n    .line 33\n    .line 34\n    move-result-object v0\n\n    .line 35\n    return-object v0\n\n    .line 36\n    :cond_24\n    if-lez v0, :cond_f\n\n    .line 37\n    .line 38\n    :cond_26\n    iget-object v1, p0, LX/73h;->A00:LX/5Yj;\n\n    .line 39\n    .line 40\n    const-string v0, \"translationXCurveUpwards\"\n\n    .line 41\n    .line 42\n    invoke-virtual {v1, v0}, LX/5Yj;->A03(Ljava/lang/String;)LX/5Wd;\n\n    .line 43\n    .line 44\n    .line 45\n    move-result-object v2\n\n    .line 46\n    iget-object v1, p0, LX/73h;->A00:LX/5Yj;\n\n    .line 47\n    .line 48\n    const-string v0, \"translationYCurveUpwards\"\n\n    .line 49\n    .line 50\n    goto :goto_1b\n\n    .line 51\n    :cond_33\n    iget-object v1, p0, LX/73h;->A00:LX/5Yj;\n\n    .line 52\n    .line 53\n    const-string v0, \"translationXLinear\"\n\n    .line 54\n    .line 55\n    invoke-virtual {v1, v0}, LX/5Yj;->A03(Ljava/lang/String;)LX/5Wd;\n\n    .line 56\n    .line 57\n    .line 58\n    move-result-object v2\n\n    .line 59\n    iget-object v1, p0, LX/73h;->A00:LX/5Yj;\n\n    .line 60\n    .line 61\n    const-string v0, \"translationYLinear\"\n\n    .line 62\n    .line 63\n    goto :goto_1b\n    .line 64\n    .line 65\n    .line 66\n    .line 67\n    .line 68\n    .line 69\n    .line 70\n    .line 71\n    .line 72\n    .line 73\n    .line 74\n    .line 75\n    .line 76\n    .line 77\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/conditions/TestTernaryOneBranchInConstructor2.smali",
    "content": ".class public final Lconditions/TestTernaryOneBranchInConstructor2;\n.super Ljava/lang/Object;\n\n\n.method public constructor <init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V\n    .locals 1\n\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n.method public synthetic constructor <init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZII)V\n    .locals 1\n\n    and-int/lit8 p6, p5, 0x2\n\n    const-string v0, \"\"\n\n    if-eqz p6, :cond_0\n\n    move-object p2, v0\n\n    :cond_0\n    and-int/lit8 p6, p5, 0x4\n\n    if-eqz p6, :cond_1\n\n    move-object p3, v0\n\n    :cond_1\n    and-int/lit8 p5, p5, 0x8\n\n    if-eqz p5, :cond_2\n\n    const/4 p4, 0x0\n\n    :cond_2\n    invoke-direct {p0, p1, p2, p3, p4}, Lconditions/TestTernaryOneBranchInConstructor2;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/debuginfo/TestVariablesNames.smali",
    "content": ".class public LTestVariablesNames;\n.super Ljava/lang/Object;\n.source \"TestVariablesNames.java\"\n\n.method public test(Ljava/lang/String;I)V\n    .registers 10\n\n    .prologue\n    .line 17\n    invoke-direct {p0, p1}, LTestVariablesNames;->f1(Ljava/lang/String;)V\n\n    .line 18\n    add-int/lit8 p1, p2, 0x3\n\n    .line 19\n    new-instance v5, Ljava/lang/StringBuilder;\n\n    invoke-direct {v5}, Ljava/lang/StringBuilder;-><init>()V\n\n    const-string v6, \"i\"\n\n    invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v5\n\n    invoke-virtual {v5, p1}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;\n\n    move-result-object v5\n\n    invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v3\n\n    .line 20\n    invoke-direct {p0, p1, v3}, LTestVariablesNames;->f2(ILjava/lang/String;)V\n\n    .line 21\n    mul-int/lit8 v5, p1, 0x5\n\n    int-to-double p1, v5\n\n    .line 22\n    new-instance v5, Ljava/lang/StringBuilder;\n\n    invoke-direct {v5}, Ljava/lang/StringBuilder;-><init>()V\n\n    const-string v6, \"d\"\n\n    invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v5\n\n    invoke-virtual {v5, p1, v1}, Ljava/lang/StringBuilder;->append(D)Ljava/lang/StringBuilder;\n\n    move-result-object v5\n\n    invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v4\n\n    .line 23\n    invoke-direct {p0, p1, v1, v4}, LTestVariablesNames;->f3(DLjava/lang/String;)V\n\n    .line 24\n    return-void\n.end method\n\n\n.method public constructor <init>()V\n    .registers 1\n    .prologue\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n    return-void\n.end method\n\n.method private f1(Ljava/lang/String;)V\n    .registers 2\n    .prologue\n    return-void\n.end method\n\n.method private f2(ILjava/lang/String;)V\n    .registers 3\n    .prologue\n    return-void\n.end method\n\n.method private f3(DLjava/lang/String;)V\n    .registers 4\n    .prologue\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/enums/TestEnumKotlinEntries.smali",
    "content": ".class public final enum Lenums/TestEnumKotlinEntries;\n.super Ljava/lang/Enum;\n.source \"TestEnumKotlinEntries.kt\"\n\n\n# annotations\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"Ljava/lang/Enum<\",\n        \"Lenums/TestEnumKotlinEntries;\",\n        \">;\"\n    }\n.end annotation\n\n.annotation runtime Lkotlin/Metadata;\n    d1 = {\n        \"\\u0000\\u000c\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0010\\u0010\\n\\u0002\\u0008\\u0005\\u0008\\u0086\\u0081\\u0002\\u0018\\u00002\\u0008\\u0012\\u0004\\u0012\\u00020\\u00000\\u0001B\\t\\u0008\\u0002\\u00a2\\u0006\\u0004\\u0008\\u0002\\u0010\\u0003j\\u0002\\u0008\\u0004j\\u0002\\u0008\\u0005j\\u0002\\u0008\\u0006\"\n    }\n    d2 = {\n        \"Lenums/TestEnumKotlinEntries;\",\n        \"\",\n        \"<init>\",\n        \"(Ljava/lang/String;I)V\",\n        \"ALPHA\",\n        \"BETA\",\n        \"GAMMA\"\n    }\n    k = 0x1\n    mv = {\n        0x2,\n        0x3,\n        0x0\n    }\n    xi = 0x30\n.end annotation\n\n\n# static fields\n.field private static final synthetic $ENTRIES:Lkotlin/enums/EnumEntries;\n\n.field private static final synthetic $VALUES:[Lenums/TestEnumKotlinEntries;\n\n.field public static final enum ALPHA:Lenums/TestEnumKotlinEntries;\n\n.field public static final enum BETA:Lenums/TestEnumKotlinEntries;\n\n.field public static final enum GAMMA:Lenums/TestEnumKotlinEntries;\n\n\n# direct methods\n.method private static final synthetic $values()[Lenums/TestEnumKotlinEntries;\n    .registers 3\n\n    const/4 v0, 0x3\n\n    new-array v0, v0, [Lenums/TestEnumKotlinEntries;\n\n    const/4 v1, 0x0\n\n    sget-object v2, Lenums/TestEnumKotlinEntries;->ALPHA:Lenums/TestEnumKotlinEntries;\n\n    aput-object v2, v0, v1\n\n    const/4 v1, 0x1\n\n    sget-object v2, Lenums/TestEnumKotlinEntries;->BETA:Lenums/TestEnumKotlinEntries;\n\n    aput-object v2, v0, v1\n\n    const/4 v1, 0x2\n\n    sget-object v2, Lenums/TestEnumKotlinEntries;->GAMMA:Lenums/TestEnumKotlinEntries;\n\n    aput-object v2, v0, v1\n\n    return-object v0\n.end method\n\n.method static constructor <clinit>()V\n    .registers 3\n\n    .line 4\n    new-instance v0, Lenums/TestEnumKotlinEntries;\n\n    const-string v1, \"ALPHA\"\n\n    const/4 v2, 0x0\n\n    invoke-direct {v0, v1, v2}, Lenums/TestEnumKotlinEntries;-><init>(Ljava/lang/String;I)V\n\n    sput-object v0, Lenums/TestEnumKotlinEntries;->ALPHA:Lenums/TestEnumKotlinEntries;\n\n    .line 5\n    new-instance v0, Lenums/TestEnumKotlinEntries;\n\n    const-string v1, \"BETA\"\n\n    const/4 v2, 0x1\n\n    invoke-direct {v0, v1, v2}, Lenums/TestEnumKotlinEntries;-><init>(Ljava/lang/String;I)V\n\n    sput-object v0, Lenums/TestEnumKotlinEntries;->BETA:Lenums/TestEnumKotlinEntries;\n\n    .line 6\n    new-instance v0, Lenums/TestEnumKotlinEntries;\n\n    const-string v1, \"GAMMA\"\n\n    const/4 v2, 0x2\n\n    invoke-direct {v0, v1, v2}, Lenums/TestEnumKotlinEntries;-><init>(Ljava/lang/String;I)V\n\n    sput-object v0, Lenums/TestEnumKotlinEntries;->GAMMA:Lenums/TestEnumKotlinEntries;\n\n    invoke-static {}, Lenums/TestEnumKotlinEntries;->$values()[Lenums/TestEnumKotlinEntries;\n\n    move-result-object v0\n\n    sput-object v0, Lenums/TestEnumKotlinEntries;->$VALUES:[Lenums/TestEnumKotlinEntries;\n\n    check-cast v0, [Ljava/lang/Enum;\n\n    invoke-static {v0}, Lkotlin/enums/EnumEntriesKt;->enumEntries([Ljava/lang/Enum;)Lkotlin/enums/EnumEntries;\n\n    move-result-object v0\n\n    sput-object v0, Lenums/TestEnumKotlinEntries;->$ENTRIES:Lkotlin/enums/EnumEntries;\n\n    return-void\n.end method\n\n.method private constructor <init>(Ljava/lang/String;I)V\n    .registers 3\n    .param p1, \"$enum$name\"    # Ljava/lang/String;\n    .param p2, \"$enum$ordinal\"    # I\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"()V\"\n        }\n    .end annotation\n\n    .line 3\n    invoke-direct {p0, p1, p2}, Ljava/lang/Enum;-><init>(Ljava/lang/String;I)V\n\n    return-void\n.end method\n\n.method public static getEntries()Lkotlin/enums/EnumEntries;\n    .registers 1\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"()\",\n            \"Lkotlin/enums/EnumEntries<\",\n            \"Lenums/TestEnumKotlinEntries;\",\n            \">;\"\n        }\n    .end annotation\n\n    sget-object v0, Lenums/TestEnumKotlinEntries;->$ENTRIES:Lkotlin/enums/EnumEntries;\n\n    return-object v0\n.end method\n\n.method public static valueOf(Ljava/lang/String;)Lenums/TestEnumKotlinEntries;\n    .registers 2\n\n    const-class v0, Lenums/TestEnumKotlinEntries;\n\n    invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;\n\n    move-result-object v0\n\n    check-cast v0, Lenums/TestEnumKotlinEntries;\n\n    return-object v0\n.end method\n\n.method public static values()[Lenums/TestEnumKotlinEntries;\n    .registers 1\n\n    sget-object v0, Lenums/TestEnumKotlinEntries;->$VALUES:[Lenums/TestEnumKotlinEntries;\n\n    invoke-virtual {v0}, Ljava/lang/Object;->clone()Ljava/lang/Object;\n\n    move-result-object v0\n\n    check-cast v0, [Lenums/TestEnumKotlinEntries;\n\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/enums/TestEnumObfuscated.smali",
    "content": "###### Class jadx.tests.integration.enums.TestEnums3$TestCls.Numbers (jadx.tests.integration.enums.TestEnums3$TestCls$Numbers)\n.class public final enum Lenums/TestEnumObfuscated;\n.super Ljava/lang/Enum;\n\n# static fields\n.field private static final synthetic $VLS:[Lenums/TestEnumObfuscated;\n.field public static final enum ONE:Lenums/TestEnumObfuscated;\n.field public static final enum TWO:Lenums/TestEnumObfuscated;\n\n# instance fields\n.field private final num:I\n\n# direct methods\n.method static constructor <clinit>()V\n    .registers 7\n\n    .prologue\n    const/4 v6, 0x3\n    const/4 v5, 0x0\n    const/4 v4, 0x2\n    const/4 v3, 0x1\n\n    new-instance v0, Lenums/TestEnumObfuscated;\n    const-string v1, \"ONE\"\n    invoke-direct {v0, v1, v5, v3}, Lenums/TestEnumObfuscated;-><init>(Ljava/lang/String;II)V\n    sput-object v0, Lenums/TestEnumObfuscated;->ONE:Lenums/TestEnumObfuscated;\n    new-instance v0, Lenums/TestEnumObfuscated;\n\n    const-string v1, \"TWO\"\n    invoke-direct {v0, v1, v3, v4}, Lenums/TestEnumObfuscated;-><init>(Ljava/lang/String;II)V\n    sput-object v0, Lenums/TestEnumObfuscated;->TWO:Lenums/TestEnumObfuscated;\n    const/4 v0, 0x2\n\n    new-array v0, v0, [Lenums/TestEnumObfuscated;\n    sget-object v1, Lenums/TestEnumObfuscated;->ONE:Lenums/TestEnumObfuscated;\n    aput-object v1, v0, v5\n    sget-object v1, Lenums/TestEnumObfuscated;->TWO:Lenums/TestEnumObfuscated;\n    aput-object v1, v0, v3\n    sput-object v0, Lenums/TestEnumObfuscated;->$VLS:[Lenums/TestEnumObfuscated;\n\n    return-void\n.end method\n\n.method private constructor <init>(Ljava/lang/String;II)V\n    .registers 4\n\n    .prologue\n    invoke-direct {p0, p1, p2}, Ljava/lang/Enum;-><init>(Ljava/lang/String;I)V\n    iput p3, p0, Lenums/TestEnumObfuscated;->num:I\n    return-void\n.end method\n\n.method public static vo(Ljava/lang/String;)Lenums/TestEnumObfuscated;\n    .registers 2\n\n    .prologue\n    const-class v0, Lenums/TestEnumObfuscated;\n    invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;\n    move-result-object v0\n    check-cast v0, Lenums/TestEnumObfuscated;\n    return-object v0\n.end method\n\n.method public static vs()[Lenums/TestEnumObfuscated;\n    .registers 1\n\n    .prologue\n    sget-object v0, Lenums/TestEnumObfuscated;->$VLS:[Lenums/TestEnumObfuscated;\n    invoke-virtual {v0}, [Lenums/TestEnumObfuscated;->clone()Ljava/lang/Object;\n    move-result-object v0\n    check-cast v0, [Lenums/TestEnumObfuscated;\n    return-object v0\n.end method\n\n.method public static values()[Lenums/TestEnumObfuscated;\n    .registers 1\n    sget-object v0, Lenums/TestEnumObfuscated;->$VLS:[Lenums/TestEnumObfuscated;\n    return v0\n.end method\n\n.method public static valuesCount()I\n    .registers 2\n    invoke-static {v0, p0}, Lenums/TestEnumObfuscated;->vs()[Lenums/TestEnumObfuscated;\n    move-result-object v0\n    array-length v1, v0\n    return v1\n.end method\n\n.method public static valuesFieldUse()I\n    .registers 2\n    sget-object v0, Lenums/TestEnumObfuscated;->$VLS:[Lenums/TestEnumObfuscated;\n    array-length v1, v0\n    return v1\n.end method\n\n.method public synthetic getNum()I\n    .registers 2\n\n    .prologue\n    iget v0, p0, Lenums/TestEnumObfuscated;->num:I\n    return v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/enums/TestEnumUsesOtherEnum.smali",
    "content": ".class public final enum Lenums/TestEnumUsesOtherEnum;\n.super Ljava/lang/Enum;\n.source \"\"\n\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"Ljava/lang/Enum<\",\n        \"Lenums/TestEnumUsesOtherEnum;\",\n        \">;\"\n    }\n.end annotation\n\n\n.field public static final enum A:Lenums/TestEnumUsesOtherEnum;\n.field private static final synthetic B:[Lenums/TestEnumUsesOtherEnum;\n.field public static final enum i:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum j:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum k:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum l:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum m:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum n:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum o:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum p:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum q:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum r:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum s:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum t:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum u:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum v:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum w:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum x:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum y:Lenums/TestEnumUsesOtherEnum;\n.field public static final enum z:Lenums/TestEnumUsesOtherEnum;\n\n\n# instance fields\n.field public c:Ljava/lang/String;\n.field public d:Ljava/lang/String;\n.field public e:Ljava/lang/Integer;\n.field public f:Z\n.field public g:Z\n.field public h:[Ljava/lang/String;\n\n\n.method static constructor <clinit>()V\n    .registers 53\n\n    new-instance v9, Lenums/TestEnumUsesOtherEnum;\n\n    const/4 v10, 0x1\n\n    invoke-static {v10}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;\n\n    move-result-object v11\n\n    const/16 v12, 0xc\n\n    new-array v8, v12, [Ljava/lang/String;\n\n    const-string v0, \"VARCHAR\"\n\n    const/4 v13, 0x0\n\n    invoke-static {v13}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;\n\n    move-result-object v23\n\n    aput-object v0, v8, v13\n\n    const-string v0, \"VARCHAR2\"\n\n    aput-object v0, v8, v10\n\n    const-string v0, \"TEXT\"\n\n    const/4 v15, 0x2\n\n    aput-object v0, v8, v15\n\n    const-string v0, \"STRING\"\n\n    const/4 v14, 0x3\n\n    aput-object v0, v8, v14\n\n    const-string v0, \"VARBINARY\"\n\n    const/16 v24, 0x4\n\n    aput-object v0, v8, v24\n\n    const-string v0, \"TINYTEXT\"\n\n    const/4 v7, 0x5\n\n    aput-object v0, v8, v7\n\n    const-string v0, \"MEDIUMTEXT\"\n\n    const/16 v25, 0x6\n\n    aput-object v0, v8, v25\n\n    const-string v0, \"LONGTEXT\"\n\n    const/4 v6, 0x7\n\n    aput-object v0, v8, v6\n\n    const-string v0, \"NVARCHAR\"\n\n    const/16 v5, 0x8\n\n    aput-object v0, v8, v5\n\n    const-string v0, \"NTEXT\"\n\n    const/16 v26, 0x9\n\n    aput-object v0, v8, v26\n\n    const-string v0, \"MEMO\"\n\n    const/16 v27, 0xa\n\n    aput-object v0, v8, v27\n\n    const-string v0, \"HYPERLINK\"\n\n    const/16 v28, 0xb\n\n    aput-object v0, v8, v28\n\n    const-string v1, \"VARCHAR\"\n\n    const/4 v2, 0x0\n\n    const-string v3, \"VARCHAR\"\n\n    const-string v4, \"100\"\n\n    const/16 v16, 0x0\n\n    const/16 v17, 0x1\n\n    move-object v0, v9\n\n    const/16 v12, 0x8\n\n    move-object v5, v11\n\n    const/4 v12, 0x7\n\n    move/from16 v6, v16\n\n    move/from16 v7, v17\n\n    invoke-direct/range {v0 .. v8}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ZZ[Ljava/lang/String;)V\n\n    sput-object v9, Lenums/TestEnumUsesOtherEnum;->i:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v30, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v8, v15, [Ljava/lang/String;\n\n    const-string v0, \"CHAR\"\n\n    aput-object v0, v8, v13\n\n    const-string v0, \"NCHAR\"\n\n    aput-object v0, v8, v10\n\n    const-string v1, \"CHAR\"\n\n    const/4 v2, 0x1\n\n    const-string v3, \"CHAR\"\n\n    const-string v4, \"1\"\n\n    const/4 v6, 0x0\n\n    const/4 v7, 0x1\n\n    move-object/from16 v0, v30\n\n    invoke-direct/range {v0 .. v8}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ZZ[Ljava/lang/String;)V\n\n    sput-object v30, Lenums/TestEnumUsesOtherEnum;->j:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v31, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v8, v12, [Ljava/lang/String;\n\n    const-string v0, \"INT\"\n\n    aput-object v0, v8, v13\n\n    const-string v0, \"INTEGER\"\n\n    aput-object v0, v8, v10\n\n    const-string v0, \"NUMBER\"\n\n    aput-object v0, v8, v15\n\n    const-string v0, \"SERIAL\"\n\n    aput-object v0, v8, v14\n\n    const-string v0, \"YEAR\"\n\n    aput-object v0, v8, v24\n\n    const-string v0, \"BIT\"\n\n    const/4 v7, 0x5\n\n    aput-object v0, v8, v7\n\n    const-string v0, \"AUTONUMBER\"\n\n    aput-object v0, v8, v25\n\n    const-string v1, \"INT\"\n\n    const/4 v2, 0x2\n\n    const-string v3, \"INT\"\n\n    const-string v4, \"10\"\n\n    const/4 v6, 0x1\n\n    move-object/from16 v0, v31\n\n    const/4 v11, 0x5\n\n    move/from16 v7, v16\n\n    invoke-direct/range {v0 .. v8}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ZZ[Ljava/lang/String;)V\n\n    sput-object v31, Lenums/TestEnumUsesOtherEnum;->k:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v0, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v1, v10, [Ljava/lang/String;\n\n    const-string v2, \"TINYINT\"\n\n    aput-object v2, v1, v13\n\n    const-string v17, \"TINYINT\"\n\n    const/16 v18, 0x3\n\n    const-string v19, \"TINYINT\"\n\n    move-object/from16 v16, v0\n\n    move-object/from16 v20, v31\n\n    move-object/from16 v21, v1\n\n    invoke-direct/range {v16 .. v21}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Lenums/TestEnumUsesOtherEnum;[Ljava/lang/String;)V\n\n    sput-object v0, Lenums/TestEnumUsesOtherEnum;->l:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v1, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v2, v15, [Ljava/lang/String;\n\n    const-string v3, \"SMALLINT\"\n\n    aput-object v3, v2, v13\n\n    const-string v3, \"SMALLSERIAL\"\n\n    aput-object v3, v2, v10\n\n    const-string v17, \"SMALLINT\"\n\n    const/16 v18, 0x4\n\n    const-string v19, \"SMALLINT\"\n\n    move-object/from16 v16, v1\n\n    move-object/from16 v21, v2\n\n    invoke-direct/range {v16 .. v21}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Lenums/TestEnumUsesOtherEnum;[Ljava/lang/String;)V\n\n    sput-object v1, Lenums/TestEnumUsesOtherEnum;->m:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v2, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v3, v10, [Ljava/lang/String;\n\n    const-string v4, \"MEDIUMINT\"\n\n    aput-object v4, v3, v13\n\n    const-string v17, \"MEDIUMINT\"\n\n    const/16 v18, 0x5\n\n    const-string v19, \"MEDIUMINT\"\n\n    move-object/from16 v16, v2\n\n    move-object/from16 v21, v3\n\n    invoke-direct/range {v16 .. v21}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Lenums/TestEnumUsesOtherEnum;[Ljava/lang/String;)V\n\n    sput-object v2, Lenums/TestEnumUsesOtherEnum;->n:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v3, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v4, v15, [Ljava/lang/String;\n\n    const-string v5, \"BIGINT\"\n\n    aput-object v5, v4, v13\n\n    const-string v5, \"BIGSERIAL\"\n\n    aput-object v5, v4, v10\n\n    const-string v17, \"BIGINT\"\n\n    const/16 v18, 0x6\n\n    const-string v19, \"BIGINT\"\n\n    move-object/from16 v16, v3\n\n    move-object/from16 v21, v4\n\n    invoke-direct/range {v16 .. v21}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Lenums/TestEnumUsesOtherEnum;[Ljava/lang/String;)V\n\n    sput-object v3, Lenums/TestEnumUsesOtherEnum;->o:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v4, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v5, v14, [Ljava/lang/String;\n\n    const-string v6, \"BOOLEAN\"\n\n    aput-object v6, v5, v13\n\n    const-string v6, \"BOOL\"\n\n    aput-object v6, v5, v10\n\n    const-string v6, \"YES/NO\"\n\n    aput-object v6, v5, v15\n\n    const-string v6, \"BOOLEAN\"\n\n    const/16 v16, 0x7\n\n    const-string v17, \"BOOLEAN\"\n\n    const/16 v18, 0x0\n\n    const/16 v20, 0x0\n\n    const/16 v21, 0x0\n\n    const/4 v7, 0x3\n\n    move-object v14, v4\n\n    const/4 v8, 0x2\n\n    move-object v15, v6\n\n    move-object/from16 v19, v23\n\n    move-object/from16 v22, v5\n\n    invoke-direct/range {v14 .. v22}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ZZ[Ljava/lang/String;)V\n\n    sput-object v4, Lenums/TestEnumUsesOtherEnum;->p:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v5, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v6, v12, [Ljava/lang/String;\n\n    const-string v14, \"BLOB\"\n\n    aput-object v14, v6, v13\n\n    const-string v14, \"BYTEA\"\n\n    aput-object v14, v6, v10\n\n    const-string v14, \"BINARY\"\n\n    aput-object v14, v6, v8\n\n    const-string v14, \"TINYBLOB\"\n\n    aput-object v14, v6, v7\n\n    const-string v14, \"MEDIUMBLOB\"\n\n    aput-object v14, v6, v24\n\n    const-string v14, \"LONGBLOB\"\n\n    aput-object v14, v6, v11\n\n    const-string v14, \"IMAGE\"\n\n    aput-object v14, v6, v25\n\n    const-string v15, \"BLOB\"\n\n    const/16 v16, 0x8\n\n    const-string v17, \"BLOB\"\n\n    move-object v14, v5\n\n    move-object/from16 v22, v6\n\n    invoke-direct/range {v14 .. v22}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ZZ[Ljava/lang/String;)V\n\n    sput-object v5, Lenums/TestEnumUsesOtherEnum;->q:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v6, Lenums/TestEnumUsesOtherEnum;\n\n    invoke-static {v8}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;\n\n    move-result-object v37\n\n    const/16 v14, 0x8\n\n    new-array v15, v14, [Ljava/lang/String;\n\n    const-string v14, \"FLOAT\"\n\n    aput-object v14, v15, v13\n\n    const-string v14, \"REAL\"\n\n    aput-object v14, v15, v10\n\n    const-string v14, \"DEC\"\n\n    aput-object v14, v15, v8\n\n    const-string v14, \"DECIMAL\"\n\n    aput-object v14, v15, v7\n\n    const-string v14, \"MONEY\"\n\n    aput-object v14, v15, v24\n\n    const-string v14, \"SMALLMONEY\"\n\n    aput-object v14, v15, v11\n\n    const-string v14, \"SINGLE\"\n\n    aput-object v14, v15, v25\n\n    const-string v14, \"CURRENCY\"\n\n    aput-object v14, v15, v12\n\n    const-string v33, \"FLOAT\"\n\n    const/16 v34, 0x9\n\n    const-string v35, \"FLOAT\"\n\n    const-string v36, \"10, 5\"\n\n    const/16 v38, 0x1\n\n    const/16 v39, 0x0\n\n    move-object/from16 v32, v6\n\n    move-object/from16 v40, v15\n\n    invoke-direct/range {v32 .. v40}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ZZ[Ljava/lang/String;)V\n\n    sput-object v6, Lenums/TestEnumUsesOtherEnum;->r:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v29, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v14, v8, [Ljava/lang/String;\n\n    const-string v15, \"DOUBLE\"\n\n    aput-object v15, v14, v13\n\n    const-string v15, \"DOUBLE PRECISION\"\n\n    aput-object v15, v14, v10\n\n    const-string v33, \"DOUBLE\"\n\n    const/16 v34, 0xa\n\n    const-string v35, \"DOUBLE\"\n\n    move-object/from16 v32, v29\n\n    move-object/from16 v36, v6\n\n    move-object/from16 v37, v14\n\n    invoke-direct/range {v32 .. v37}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Lenums/TestEnumUsesOtherEnum;[Ljava/lang/String;)V\n\n    sput-object v29, Lenums/TestEnumUsesOtherEnum;->s:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v32, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v14, v10, [Ljava/lang/String;\n\n    const-string v15, \"SET\"\n\n    aput-object v15, v14, v13\n\n    const-string v37, \"SET\"\n\n    const/16 v38, 0xb\n\n    const-string v39, \"SET\"\n\n    const-string v40, \"a, b, c\"\n\n    const/16 v41, 0x0\n\n    const/16 v42, 0x0\n\n    const/16 v43, 0x0\n\n    move-object/from16 v36, v32\n\n    move-object/from16 v44, v14\n\n    invoke-direct/range {v36 .. v44}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ZZ[Ljava/lang/String;)V\n\n    sput-object v32, Lenums/TestEnumUsesOtherEnum;->t:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v33, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v14, v10, [Ljava/lang/String;\n\n    const-string v15, \"ENUM\"\n\n    aput-object v15, v14, v13\n\n    const-string v45, \"ENUM\"\n\n    const/16 v46, 0xc\n\n    const-string v47, \"ENUM\"\n\n    const-string v48, \"a, b, c\"\n\n    const/16 v49, 0x0\n\n    const/16 v50, 0x0\n\n    const/16 v51, 0x0\n\n    move-object/from16 v44, v33\n\n    move-object/from16 v52, v14\n\n    invoke-direct/range {v44 .. v52}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ZZ[Ljava/lang/String;)V\n\n    sput-object v33, Lenums/TestEnumUsesOtherEnum;->u:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v34, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v15, v10, [Ljava/lang/String;\n\n    const-string v14, \"DATE\"\n\n    aput-object v14, v15, v13\n\n    const-string v16, \"DATE\"\n\n    const/16 v17, 0xd\n\n    const-string v18, \"DATE\"\n\n    const-string v19, \"\"\n\n    move-object/from16 v14, v34\n\n    move-object/from16 v22, v15\n\n    move-object/from16 v15, v16\n\n    move/from16 v16, v17\n\n    move-object/from16 v17, v18\n\n    move-object/from16 v18, v19\n\n    move-object/from16 v19, v23\n\n    invoke-direct/range {v14 .. v22}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ZZ[Ljava/lang/String;)V\n\n    sput-object v34, Lenums/TestEnumUsesOtherEnum;->v:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v35, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v14, v10, [Ljava/lang/String;\n\n    const-string v15, \"TIME\"\n\n    aput-object v15, v14, v13\n\n    const-string v16, \"TIME\"\n\n    const/16 v17, 0xe\n\n    const-string v18, \"TIME\"\n\n    move-object/from16 v15, v35\n\n    move-object/from16 v19, v34\n\n    move-object/from16 v20, v14\n\n    invoke-direct/range {v15 .. v20}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Lenums/TestEnumUsesOtherEnum;[Ljava/lang/String;)V\n\n    sput-object v35, Lenums/TestEnumUsesOtherEnum;->w:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v36, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v14, v11, [Ljava/lang/String;\n\n    const-string v15, \"DATETIME\"\n\n    aput-object v15, v14, v13\n\n    const-string v15, \"DATETIME2\"\n\n    aput-object v15, v14, v10\n\n    const-string v15, \"SMALLDATETIME\"\n\n    aput-object v15, v14, v8\n\n    const-string v15, \"DATETIMEOFFSET\"\n\n    aput-object v15, v14, v7\n\n    const-string v15, \"DATE/TIME\"\n\n    aput-object v15, v14, v24\n\n    const-string v16, \"DATETIME\"\n\n    const/16 v17, 0xf\n\n    const-string v18, \"DATETIME\"\n\n    move-object/from16 v15, v36\n\n    move-object/from16 v20, v14\n\n    invoke-direct/range {v15 .. v20}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Lenums/TestEnumUsesOtherEnum;[Ljava/lang/String;)V\n\n    sput-object v36, Lenums/TestEnumUsesOtherEnum;->x:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v37, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v14, v10, [Ljava/lang/String;\n\n    const-string v15, \"TIMESTAMP\"\n\n    aput-object v15, v14, v13\n\n    const-string v16, \"TIMESTAMP\"\n\n    const/16 v17, 0x10\n\n    const-string v18, \"TIMESTAMP\"\n\n    move-object/from16 v15, v37\n\n    move-object/from16 v20, v14\n\n    invoke-direct/range {v15 .. v20}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Lenums/TestEnumUsesOtherEnum;[Ljava/lang/String;)V\n\n    sput-object v37, Lenums/TestEnumUsesOtherEnum;->y:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v38, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v15, v8, [Ljava/lang/String;\n\n    const-string v14, \"JSON\"\n\n    aput-object v14, v15, v13\n\n    const-string v14, \"JSONB\"\n\n    aput-object v14, v15, v10\n\n    const-string v16, \"JSON\"\n\n    const/16 v17, 0x11\n\n    const-string v18, \"JSON\"\n\n    const/16 v19, 0x0\n\n    const/16 v20, 0x0\n\n    const/16 v21, 0x1\n\n    move-object/from16 v14, v38\n\n    move-object/from16 v22, v15\n\n    move-object/from16 v15, v16\n\n    move/from16 v16, v17\n\n    move-object/from16 v17, v18\n\n    move-object/from16 v18, v19\n\n    move-object/from16 v19, v23\n\n    invoke-direct/range {v14 .. v22}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ZZ[Ljava/lang/String;)V\n\n    sput-object v38, Lenums/TestEnumUsesOtherEnum;->z:Lenums/TestEnumUsesOtherEnum;\n\n    new-instance v39, Lenums/TestEnumUsesOtherEnum;\n\n    new-array v15, v10, [Ljava/lang/String;\n\n    const-string v14, \"UUID\"\n\n    aput-object v14, v15, v13\n\n    const-string v16, \"UUID\"\n\n    const/16 v17, 0x12\n\n    const-string v18, \"UUID\"\n\n    const/16 v19, 0x0\n\n    move-object/from16 v14, v39\n\n    move-object/from16 v22, v15\n\n    move-object/from16 v15, v16\n\n    move/from16 v16, v17\n\n    move-object/from16 v17, v18\n\n    move-object/from16 v18, v19\n\n    move-object/from16 v19, v23\n\n    invoke-direct/range {v14 .. v22}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ZZ[Ljava/lang/String;)V\n\n    sput-object v39, Lenums/TestEnumUsesOtherEnum;->A:Lenums/TestEnumUsesOtherEnum;\n\n    const/16 v14, 0x13\n\n    new-array v14, v14, [Lenums/TestEnumUsesOtherEnum;\n\n    aput-object v9, v14, v13\n\n    aput-object v30, v14, v10\n\n    aput-object v31, v14, v8\n\n    aput-object v0, v14, v7\n\n    aput-object v1, v14, v24\n\n    aput-object v2, v14, v11\n\n    aput-object v3, v14, v25\n\n    aput-object v4, v14, v12\n\n    const/16 v0, 0x8\n\n    aput-object v5, v14, v0\n\n    aput-object v6, v14, v26\n\n    aput-object v29, v14, v27\n\n    aput-object v32, v14, v28\n\n    const/16 v0, 0xc\n\n    aput-object v33, v14, v0\n\n    const/16 v0, 0xd\n\n    aput-object v34, v14, v0\n\n    const/16 v0, 0xe\n\n    aput-object v35, v14, v0\n\n    const/16 v0, 0xf\n\n    aput-object v36, v14, v0\n\n    const/16 v0, 0x10\n\n    aput-object v37, v14, v0\n\n    const/16 v0, 0x11\n\n    aput-object v38, v14, v0\n\n    const/16 v0, 0x12\n\n    aput-object v39, v14, v0\n\n    sput-object v14, Lenums/TestEnumUsesOtherEnum;->B:[Lenums/TestEnumUsesOtherEnum;\n\n    return-void\n.end method\n\n.method private constructor <init>(Ljava/lang/String;ILjava/lang/String;Lenums/TestEnumUsesOtherEnum;[Ljava/lang/String;)V\n    .registers 15\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/lang/String;\",\n            \"Lenums/TestEnumUsesOtherEnum;\",\n            \"[\",\n            \"Ljava/lang/String;\",\n            \")V\"\n        }\n    .end annotation\n\n    iget-object v4, p4, Lenums/TestEnumUsesOtherEnum;->d:Ljava/lang/String;\n\n    iget-object v5, p4, Lenums/TestEnumUsesOtherEnum;->e:Ljava/lang/Integer;\n\n    iget-boolean v6, p4, Lenums/TestEnumUsesOtherEnum;->f:Z\n\n    iget-boolean v7, p4, Lenums/TestEnumUsesOtherEnum;->g:Z\n\n    move-object v0, p0\n\n    move-object v1, p1\n\n    move v2, p2\n\n    move-object v3, p3\n\n    move-object v8, p5\n\n    invoke-direct/range {v0 .. v8}, Lenums/TestEnumUsesOtherEnum;-><init>(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ZZ[Ljava/lang/String;)V\n\n    return-void\n.end method\n\n.method private constructor <init>(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ZZ[Ljava/lang/String;)V\n    .registers 9\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/lang/String;\",\n            \"Ljava/lang/String;\",\n            \"Ljava/lang/Integer;\",\n            \"ZZ[\",\n            \"Ljava/lang/String;\",\n            \")V\"\n        }\n    .end annotation\n\n    invoke-direct {p0, p1, p2}, Ljava/lang/Enum;-><init>(Ljava/lang/String;I)V\n\n    iput-object p3, p0, Lenums/TestEnumUsesOtherEnum;->c:Ljava/lang/String;\n\n    iput-object p4, p0, Lenums/TestEnumUsesOtherEnum;->d:Ljava/lang/String;\n\n    iput-object p5, p0, Lenums/TestEnumUsesOtherEnum;->e:Ljava/lang/Integer;\n\n    iput-boolean p6, p0, Lenums/TestEnumUsesOtherEnum;->f:Z\n\n    iput-boolean p7, p0, Lenums/TestEnumUsesOtherEnum;->g:Z\n\n    iput-object p8, p0, Lenums/TestEnumUsesOtherEnum;->h:[Ljava/lang/String;\n\n    return-void\n.end method\n\n.method public static valueOf(Ljava/lang/String;)Lenums/TestEnumUsesOtherEnum;\n    .registers 2\n\n    const-class v0, Lenums/TestEnumUsesOtherEnum;\n\n    invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;\n\n    move-result-object p0\n\n    check-cast p0, Lenums/TestEnumUsesOtherEnum;\n\n    return-object p0\n.end method\n\n.method public static values()[Lenums/TestEnumUsesOtherEnum;\n    .registers 1\n\n    sget-object v0, Lenums/TestEnumUsesOtherEnum;->B:[Lenums/TestEnumUsesOtherEnum;\n\n    invoke-virtual {v0}, [Lenums/TestEnumUsesOtherEnum;->clone()Ljava/lang/Object;\n\n    move-result-object v0\n\n    check-cast v0, [Lenums/TestEnumUsesOtherEnum;\n\n    return-object v0\n.end method\n\n\n# virtual methods\n.method public c()Z\n    .registers 2\n\n    iget-object v0, p0, Lenums/TestEnumUsesOtherEnum;->e:Ljava/lang/Integer;\n\n    if-eqz v0, :cond_d\n\n    invoke-virtual {v0}, Ljava/lang/Integer;->intValue()I\n\n    move-result v0\n\n    if-eqz v0, :cond_b\n\n    goto :goto_d\n\n    :cond_b\n    const/4 v0, 0x0\n\n    goto :goto_e\n\n    :cond_d\n    :goto_d\n    const/4 v0, 0x1\n\n    :goto_e\n    return v0\n.end method\n\n.method public toString()Ljava/lang/String;\n    .registers 2\n    iget-object v0, p0, Lenums/TestEnumUsesOtherEnum;->c:Ljava/lang/String;\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/enums/TestEnumWithFields.smali",
    "content": ".class public final enum Lenums/TestEnumWithFields;\n.super Ljava/lang/Enum;\n\n\n# annotations\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"Ljava/lang/Enum<\",\n        \"Lenums/TestEnumWithFields;\",\n        \">;\"\n    }\n.end annotation\n\n\n# static fields\n.field public static final synthetic $VALUES:[Lenums/TestEnumWithFields;\n\n.field public static final DEFAULT:Lenums/TestEnumWithFields;\n\n.field public static final enum DISABLED:Lenums/TestEnumWithFields;\n\n.field public static final enum FIVE_SECONDS:Lenums/TestEnumWithFields;\n\n.field public static final MAX:Lenums/TestEnumWithFields;\n\n.field public static final enum TWO_AND_A_HALF_SECONDS:Lenums/TestEnumWithFields;\n\n.field public static final sValues:[Lenums/TestEnumWithFields;\n\n\n# instance fields\n.field public final mRawValue:I\n\n\n# direct methods\n.method public static constructor <clinit>()V\n    .locals 6\n\n    .line 1\n    new-instance v0, Lenums/TestEnumWithFields;\n\n    const/4 v1, 0x0\n\n    const-string v2, \"DISABLED\"\n\n    invoke-direct {v0, v2, v1, v1}, Lenums/TestEnumWithFields;-><init>(Ljava/lang/String;II)V\n\n    sput-object v0, Lenums/TestEnumWithFields;->DISABLED:Lenums/TestEnumWithFields;\n\n    .line 2\n    new-instance v0, Lenums/TestEnumWithFields;\n\n    const/4 v2, 0x1\n\n    const-string v3, \"TWO_AND_A_HALF_SECONDS\"\n\n    invoke-direct {v0, v3, v2, v2}, Lenums/TestEnumWithFields;-><init>(Ljava/lang/String;II)V\n\n    sput-object v0, Lenums/TestEnumWithFields;->TWO_AND_A_HALF_SECONDS:Lenums/TestEnumWithFields;\n\n    .line 3\n    new-instance v0, Lenums/TestEnumWithFields;\n\n    const/4 v3, 0x2\n\n    const-string v4, \"FIVE_SECONDS\"\n\n    invoke-direct {v0, v4, v3, v3}, Lenums/TestEnumWithFields;-><init>(Ljava/lang/String;II)V\n\n    sput-object v0, Lenums/TestEnumWithFields;->FIVE_SECONDS:Lenums/TestEnumWithFields;\n\n    const/4 v4, 0x3\n\n    new-array v4, v4, [Lenums/TestEnumWithFields;\n\n    .line 4\n    sget-object v5, Lenums/TestEnumWithFields;->DISABLED:Lenums/TestEnumWithFields;\n\n    aput-object v5, v4, v1\n\n    sget-object v1, Lenums/TestEnumWithFields;->TWO_AND_A_HALF_SECONDS:Lenums/TestEnumWithFields;\n\n    aput-object v1, v4, v2\n\n    aput-object v0, v4, v3\n\n    sput-object v4, Lenums/TestEnumWithFields;->$VALUES:[Lenums/TestEnumWithFields;\n\n    .line 5\n    sput-object v5, Lenums/TestEnumWithFields;->DEFAULT:Lenums/TestEnumWithFields;\n\n    .line 6\n    sput-object v0, Lenums/TestEnumWithFields;->MAX:Lenums/TestEnumWithFields;\n\n    .line 7\n    invoke-static {}, Lenums/TestEnumWithFields;->values()[Lenums/TestEnumWithFields;\n\n    move-result-object v0\n\n    sput-object v0, Lenums/TestEnumWithFields;->sValues:[Lenums/TestEnumWithFields;\n\n    return-void\n.end method\n\n.method public constructor <init>(Ljava/lang/String;II)V\n    .locals 0\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(I)V\"\n        }\n    .end annotation\n\n    .line 1\n    invoke-direct {p0, p1, p2}, Ljava/lang/Enum;-><init>(Ljava/lang/String;I)V\n\n    iput p3, p0, Lenums/TestEnumWithFields;->mRawValue:I\n\n    return-void\n.end method\n\n.method public static valueOf(Ljava/lang/String;)Lenums/TestEnumWithFields;\n    .locals 1\n\n    .line 1\n    const-class v0, Lenums/TestEnumWithFields;\n\n    invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;\n\n    move-result-object p0\n\n    check-cast p0, Lenums/TestEnumWithFields;\n\n    return-object p0\n.end method\n\n.method public static values()[Lenums/TestEnumWithFields;\n    .locals 1\n\n    .line 1\n    sget-object v0, Lenums/TestEnumWithFields;->$VALUES:[Lenums/TestEnumWithFields;\n\n    invoke-virtual {v0}, [Lenums/TestEnumWithFields;->clone()Ljava/lang/Object;\n\n    move-result-object v0\n\n    check-cast v0, [Lenums/TestEnumWithFields;\n\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/enums/TestEnums10.smali",
    "content": ".class public final enum Lenums/TestEnums10;\n.super Ljava/lang/Enum;\n.source \"\"\n\n.field public static final synthetic A02:[Lenums/TestEnums10;\n\n.field public static final enum A03:Lenums/TestEnums10;\n.field public static final enum A04:Lenums/TestEnums10;\n.field public static final enum A05:Lenums/TestEnums10;\n.field public static final enum A06:Lenums/TestEnums10;\n.field public static final enum A07:Lenums/TestEnums10;\n.field public static final enum A08:Lenums/TestEnums10;\n.field public static final enum A09:Lenums/TestEnums10;\n.field public static final enum A0A:Lenums/TestEnums10;\n.field public static final enum A0B:Lenums/TestEnums10;\n.field public static final enum A0C:Lenums/TestEnums10;\n\n.field public final A00:I\n\n.method public static constructor <clinit>()V\n    .locals 33\n    const/16 v28, 0x0\n    const/16 v27, 0x1\n    const-string v3, \"CONNECT\"\n    new-instance v26, Lenums/TestEnums10;\n    move-object/from16 v2, v26\n    move/from16 v1, v28\n    move/from16 v0, v27\n    invoke-direct {v2, v3, v1, v0}, Lenums/TestEnums10;-><init>(Ljava/lang/String;II)V\n    sput-object v26, Lenums/TestEnums10;->A04:Lenums/TestEnums10;\n    const/4 v4, 0x2\n    const-string v2, \"CONNACK\"\n    new-instance v25, Lenums/TestEnums10;\n    move-object/from16 v1, v25\n    invoke-direct {v1, v2, v0, v4}, Lenums/TestEnums10;-><init>(Ljava/lang/String;II)V\n    sput-object v25, Lenums/TestEnums10;->A03:Lenums/TestEnums10;\n    const/4 v6, 0x3\n    const-string v1, \"PUBLISH\"\n    new-instance v24, Lenums/TestEnums10;\n    move-object/from16 v0, v24\n    invoke-direct {v0, v1, v4, v6}, Lenums/TestEnums10;-><init>(Ljava/lang/String;II)V\n    sput-object v24, Lenums/TestEnums10;->A08:Lenums/TestEnums10;\n    const/4 v7, 0x4\n    const-string v1, \"PUBACK\"\n    new-instance v23, Lenums/TestEnums10;\n    move-object/from16 v0, v23\n    invoke-direct {v0, v1, v6, v7}, Lenums/TestEnums10;-><init>(Ljava/lang/String;II)V\n    sput-object v23, Lenums/TestEnums10;->A07:Lenums/TestEnums10;\n    const/4 v8, 0x5\n    const-string v1, \"PUBREC\"\n    new-instance v22, Lenums/TestEnums10;\n    move-object/from16 v0, v22\n    invoke-direct {v0, v1, v7, v8}, Lenums/TestEnums10;-><init>(Ljava/lang/String;II)V\n    const/4 v9, 0x6\n    const-string v1, \"PUBREL\"\n    new-instance v21, Lenums/TestEnums10;\n    move-object/from16 v0, v21\n    invoke-direct {v0, v1, v8, v9}, Lenums/TestEnums10;-><init>(Ljava/lang/String;II)V\n    const/4 v10, 0x7\n    const-string v1, \"PUBCOMP\"\n    new-instance v20, Lenums/TestEnums10;\n    move-object/from16 v0, v20\n    invoke-direct {v0, v1, v9, v10}, Lenums/TestEnums10;-><init>(Ljava/lang/String;II)V\n    const/16 v11, 0x8\n    const-string v1, \"SUBSCRIBE\"\n    new-instance v19, Lenums/TestEnums10;\n    move-object/from16 v0, v19\n    invoke-direct {v0, v1, v10, v11}, Lenums/TestEnums10;-><init>(Ljava/lang/String;II)V\n    sput-object v19, Lenums/TestEnums10;->A0A:Lenums/TestEnums10;\n    const/16 v12, 0x9\n    const-string v1, \"SUBACK\"\n    new-instance v18, Lenums/TestEnums10;\n    move-object/from16 v0, v18\n    invoke-direct {v0, v1, v11, v12}, Lenums/TestEnums10;-><init>(Ljava/lang/String;II)V\n    sput-object v18, Lenums/TestEnums10;->A09:Lenums/TestEnums10;\n    const/16 v13, 0xa\n    const-string v1, \"UNSUBSCRIBE\"\n    new-instance v17, Lenums/TestEnums10;\n    move-object/from16 v0, v17\n    invoke-direct {v0, v1, v12, v13}, Lenums/TestEnums10;-><init>(Ljava/lang/String;II)V\n    sput-object v17, Lenums/TestEnums10;->A0C:Lenums/TestEnums10;\n    const/16 v14, 0xb\n    const-string v0, \"UNSUBACK\"\n    new-instance v5, Lenums/TestEnums10;\n    invoke-direct {v5, v0, v13, v14}, Lenums/TestEnums10;-><init>(Ljava/lang/String;II)V\n    sput-object v5, Lenums/TestEnums10;->A0B:Lenums/TestEnums10;\n    const/16 v15, 0xc\n    const-string v0, \"PINGREQ\"\n    new-instance v3, Lenums/TestEnums10;\n    invoke-direct {v3, v0, v14, v15}, Lenums/TestEnums10;-><init>(Ljava/lang/String;II)V\n    sput-object v3, Lenums/TestEnums10;->A05:Lenums/TestEnums10;\n    const/16 v2, 0xd\n    const-string v0, \"PINGRESP\"\n    new-instance v1, Lenums/TestEnums10;\n    invoke-direct {v1, v0, v15, v2}, Lenums/TestEnums10;-><init>(Ljava/lang/String;II)V\n    sput-object v1, Lenums/TestEnums10;->A06:Lenums/TestEnums10;\n    const/16 v15, 0xe\n    const-string v0, \"DISCONNECT\"\n    new-instance v16, Lenums/TestEnums10;\n    move-object/from16 v29, v16\n    move-object/from16 v30, v0\n    move/from16 v31, v2\n    move/from16 v32, v15\n    invoke-direct/range {v29 .. v32}, Lenums/TestEnums10;-><init>(Ljava/lang/String;II)V\n    new-array v15, v15, [Lenums/TestEnums10;\n    aput-object v26, v15, v28\n    aput-object v25, v15, v27\n    aput-object v24, v15, v4\n    aput-object v23, v15, v6\n    aput-object v22, v15, v7\n    aput-object v21, v15, v8\n    aput-object v20, v15, v9\n    aput-object v19, v15, v10\n    aput-object v18, v15, v11\n    aput-object v17, v15, v12\n    aput-object v5, v15, v13\n    aput-object v3, v15, v14\n    const/16 v0, 0xc\n    aput-object v1, v15, v0\n    aput-object v16, v15, v2\n    sput-object v15, Lenums/TestEnums10;->A02:[Lenums/TestEnums10;\n\n    return-void\n.end method\n\n.method public constructor <init>(Ljava/lang/String;II)V\n    .locals 0\n    invoke-direct {p0, p1, p2}, Ljava/lang/Enum;-><init>(Ljava/lang/String;I)V\n    iput p3, p0, Lenums/TestEnums10;->A00:I\n    return-void\n.end method\n\n.method public static valueOf(Ljava/lang/String;)Lenums/TestEnums10;\n    .locals 1\n    const-class v0, Lenums/TestEnums10;\n    invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;\n    move-result-object v0\n    check-cast v0, Lenums/TestEnums10;\n    return-object v0\n.end method\n\n.method public static values()[Lenums/TestEnums10;\n    .locals 1\n    sget-object v0, Lenums/TestEnums10;->A02:[Lenums/TestEnums10;\n    invoke-virtual {v0}, [Ljava/lang/Object;->clone()Ljava/lang/Object;\n    move-result-object v0\n    check-cast v0, [Lenums/TestEnums10;\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/enums/TestEnums5.smali",
    "content": ".class final enum Lkotlin/collections/State;\n.super Ljava/lang/Enum;\n\n\n# annotations\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"Ljava/lang/Enum<\",\n        \"Lkotlin/collections/State;\",\n        \">;\"\n    }\n.end annotation\n\n# static fields\n.field private static final synthetic $VALUES:[Lkotlin/collections/State;\n\n.field public static final enum Done:Lkotlin/collections/State;\n\n.field public static final enum Failed:Lkotlin/collections/State;\n\n.field public static final enum NotReady:Lkotlin/collections/State;\n\n.field public static final enum Ready:Lkotlin/collections/State;\n\n\n# direct methods\n.method static constructor <clinit>()V\n    .registers 4\n\n    const/4 v0, 0x4\n\n    new-array v0, v0, [Lkotlin/collections/State;\n\n    new-instance v1, Lkotlin/collections/State;\n\n    const-string v2, \"Ready\"\n\n    const/4 v3, 0x0\n\n    invoke-direct {v1, v2, v3}, Lkotlin/collections/State;-><init>(Ljava/lang/String;I)V\n\n    sput-object v1, Lkotlin/collections/State;->Ready:Lkotlin/collections/State;\n\n    aput-object v1, v0, v3\n\n    new-instance v1, Lkotlin/collections/State;\n\n    const-string v2, \"NotReady\"\n\n    const/4 v3, 0x1\n\n    invoke-direct {v1, v2, v3}, Lkotlin/collections/State;-><init>(Ljava/lang/String;I)V\n\n    sput-object v1, Lkotlin/collections/State;->NotReady:Lkotlin/collections/State;\n\n    aput-object v1, v0, v3\n\n    new-instance v1, Lkotlin/collections/State;\n\n    const-string v2, \"Done\"\n\n    const/4 v3, 0x2\n\n    invoke-direct {v1, v2, v3}, Lkotlin/collections/State;-><init>(Ljava/lang/String;I)V\n\n    sput-object v1, Lkotlin/collections/State;->Done:Lkotlin/collections/State;\n\n    aput-object v1, v0, v3\n\n    new-instance v1, Lkotlin/collections/State;\n\n    const-string v2, \"Failed\"\n\n    const/4 v3, 0x3\n\n    invoke-direct {v1, v2, v3}, Lkotlin/collections/State;-><init>(Ljava/lang/String;I)V\n\n    sput-object v1, Lkotlin/collections/State;->Failed:Lkotlin/collections/State;\n\n    aput-object v1, v0, v3\n\n    sput-object v0, Lkotlin/collections/State;->$VALUES:[Lkotlin/collections/State;\n\n    return-void\n.end method\n\n.method protected constructor <init>(Ljava/lang/String;I)V\n    .registers 3\n    .param p1, \"$enum_name_or_ordinal$0\"    # Ljava/lang/String;\n    .param p2, \"$enum_name_or_ordinal$1\"    # I\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"()V\"\n        }\n    .end annotation\n\n    .line 4\n    invoke-direct {p0, p1, p2}, Ljava/lang/Enum;-><init>(Ljava/lang/String;I)V\n\n    return-void\n.end method\n\n.method public static valueOf(Ljava/lang/String;)Lkotlin/collections/State;\n    .registers 2\n\n    const-class v0, Lkotlin/collections/State;\n\n    invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;\n\n    move-result-object p0\n\n    check-cast p0, Lkotlin/collections/State;\n\n    return-object p0\n.end method\n\n.method public static values()[Lkotlin/collections/State;\n    .registers 1\n\n    sget-object v0, Lkotlin/collections/State;->$VALUES:[Lkotlin/collections/State;\n\n    invoke-virtual {v0}, [Lkotlin/collections/State;->clone()Ljava/lang/Object;\n\n    move-result-object v0\n\n    check-cast v0, [Lkotlin/collections/State;\n\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/enums/TestEnums8.smali",
    "content": ".class public final enum Lenums/TestEnums8;\n.super Ljava/lang/Enum;\n.source \"SourceFile\"\n\n\n# annotations\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"Ljava/lang/Enum<\",\n        \"Lenums/TestEnums8;\",\n        \">;\"\n    }\n.end annotation\n\n\n# static fields\n.field private static final synthetic $VALUES:[Lenums/TestEnums8;\n\n.field private static final FOR_BITS:[Lenums/TestEnums8;\n\n.field public static final enum H:Lenums/TestEnums8;\n\n.field public static final enum L:Lenums/TestEnums8;\n\n.field public static final enum M:Lenums/TestEnums8;\n\n.field public static final enum Q:Lenums/TestEnums8;\n\n\n# instance fields\n.field private final bits:I\n\n\n# direct methods\n.method static constructor <clinit>()V\n    .locals 10\n\n    .line 28\n    new-instance v0, Lenums/TestEnums8;\n\n    const/4 v1, 0x1\n\n    const/4 v2, 0x0\n\n    const-string v3, \"L\"\n\n    invoke-direct {v0, v3, v2, v1}, Lenums/TestEnums8;-><init>(Ljava/lang/String;II)V\n\n    sput-object v0, Lenums/TestEnums8;->L:Lenums/TestEnums8;\n\n    .line 30\n    new-instance v0, Lenums/TestEnums8;\n\n    const-string v3, \"M\"\n\n    invoke-direct {v0, v3, v1, v2}, Lenums/TestEnums8;-><init>(Ljava/lang/String;II)V\n\n    sput-object v0, Lenums/TestEnums8;->M:Lenums/TestEnums8;\n\n    .line 32\n    new-instance v0, Lenums/TestEnums8;\n\n    const/4 v3, 0x3\n\n    const/4 v4, 0x2\n\n    const-string v5, \"Q\"\n\n    invoke-direct {v0, v5, v4, v3}, Lenums/TestEnums8;-><init>(Ljava/lang/String;II)V\n\n    sput-object v0, Lenums/TestEnums8;->Q:Lenums/TestEnums8;\n\n    .line 34\n    new-instance v0, Lenums/TestEnums8;\n\n    const-string v5, \"H\"\n\n    invoke-direct {v0, v5, v3, v4}, Lenums/TestEnums8;-><init>(Ljava/lang/String;II)V\n\n    sput-object v0, Lenums/TestEnums8;->H:Lenums/TestEnums8;\n\n    const/4 v0, 0x4\n\n    new-array v5, v0, [Lenums/TestEnums8;\n\n    .line 25\n    sget-object v6, Lenums/TestEnums8;->L:Lenums/TestEnums8;\n\n    aput-object v6, v5, v2\n\n    sget-object v7, Lenums/TestEnums8;->M:Lenums/TestEnums8;\n\n    aput-object v7, v5, v1\n\n    sget-object v8, Lenums/TestEnums8;->Q:Lenums/TestEnums8;\n\n    aput-object v8, v5, v4\n\n    sget-object v9, Lenums/TestEnums8;->H:Lenums/TestEnums8;\n\n    aput-object v9, v5, v3\n\n    sput-object v5, Lenums/TestEnums8;->$VALUES:[Lenums/TestEnums8;\n\n    new-array v0, v0, [Lenums/TestEnums8;\n\n    aput-object v7, v0, v2\n\n    aput-object v6, v0, v1\n\n    aput-object v9, v0, v4\n\n    aput-object v8, v0, v3\n\n    .line 36\n    sput-object v0, Lenums/TestEnums8;->FOR_BITS:[Lenums/TestEnums8;\n\n    return-void\n.end method\n\n.method private constructor <init>(Ljava/lang/String;II)V\n    .locals 0\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(I)V\"\n        }\n    .end annotation\n\n    .line 40\n    invoke-direct {p0, p1, p2}, Ljava/lang/Enum;-><init>(Ljava/lang/String;I)V\n\n    .line 41\n    iput p3, p0, Lenums/TestEnums8;->bits:I\n\n    return-void\n.end method\n\n.method public static forBits(I)Lenums/TestEnums8;\n    .locals 2\n\n    if-ltz p0, :cond_0\n\n    .line 53\n    sget-object v0, Lenums/TestEnums8;->FOR_BITS:[Lenums/TestEnums8;\n\n    array-length v1, v0\n\n    if-ge p0, v1, :cond_0\n\n    .line 56\n    aget-object p0, v0, p0\n\n    return-object p0\n\n    .line 54\n    :cond_0\n    new-instance p0, Ljava/lang/IllegalArgumentException;\n\n    invoke-direct {p0}, Ljava/lang/IllegalArgumentException;-><init>()V\n\n    throw p0\n.end method\n\n.method public static valueOf(Ljava/lang/String;)Lenums/TestEnums8;\n    .locals 1\n\n    .line 25\n    const-class v0, Lenums/TestEnums8;\n\n    invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;\n\n    move-result-object p0\n\n    check-cast p0, Lenums/TestEnums8;\n\n    return-object p0\n.end method\n\n.method public static values()[Lenums/TestEnums8;\n    .locals 1\n\n    .line 25\n    sget-object v0, Lenums/TestEnums8;->$VALUES:[Lenums/TestEnums8;\n\n    invoke-virtual {v0}, [Lenums/TestEnums8;->clone()Ljava/lang/Object;\n\n    move-result-object v0\n\n    check-cast v0, [Lenums/TestEnums8;\n\n    return-object v0\n.end method\n\n\n# virtual methods\n.method public getBits()I\n    .locals 1\n\n    .line 45\n    iget v0, p0, Lenums/TestEnums8;->bits:I\n\n    return v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/enums/TestEnumsWithStaticFields.smali",
    "content": ".class public final enum Lenums/TestEnumsWithStaticFields;\n.super Ljava/lang/Enum;\n.source \"SourceFile\"\n\n# interfaces\n.implements Lx/a/c;\n\n\n# annotations\n.annotation system Ldalvik/annotation/MemberClasses;\n    value = {\n        Lx/a/d$a;\n    }\n.end annotation\n\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"Ljava/lang/Enum\",\n        \"<\",\n        \"Lenums/TestEnumsWithStaticFields;\",\n        \">;\",\n        \"Lx/a/c;\"\n    }\n.end annotation\n\n\n# static fields\n.field public static final enum sA:Lenums/TestEnumsWithStaticFields;\n\n.field private static sB:Lx/a/c;\n\n.field private static final synthetic sC:[Lenums/TestEnumsWithStaticFields;\n\n\n# direct methods\n.method static constructor <clinit>()V\n    .registers 4\n\n    .prologue\n    const v3, 0x23900\n\n    const/4 v2, 0x0\n\n    invoke-static {v3}, Lx/q;->i(I)V\n\n    .line 10\n    new-instance v0, Lenums/TestEnumsWithStaticFields;\n\n    const-string/jumbo v1, \"INSTANCE\"\n\n    invoke-direct {v0, v1}, Lenums/TestEnumsWithStaticFields;-><init>(Ljava/lang/String;)V\n\n    sput-object v0, Lenums/TestEnumsWithStaticFields;->sA:Lenums/TestEnumsWithStaticFields;\n\n    .line 9\n    const/4 v0, 0x1\n\n    new-array v0, v0, [Lenums/TestEnumsWithStaticFields;\n\n    sget-object v1, Lenums/TestEnumsWithStaticFields;->sA:Lenums/TestEnumsWithStaticFields;\n\n    aput-object v1, v0, v2\n\n    sput-object v0, Lenums/TestEnumsWithStaticFields;->sC:[Lenums/TestEnumsWithStaticFields;\n\n    .line 36\n    new-instance v0, Lx/a/d$a;\n\n    invoke-direct {v0, v2}, Lx/a/d$a;-><init>(B)V\n\n    sput-object v0, Lenums/TestEnumsWithStaticFields;->sB:Lx/a/c;\n\n    invoke-static {v3}, Lx/q;->o(I)V\n\n    return-void\n.end method\n\n.method private constructor <init>(Ljava/lang/String;)V\n    .registers 3\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"()V\"\n        }\n    .end annotation\n\n    .prologue\n    .line 9\n    const/4 v0, 0x0\n\n    invoke-direct {p0, p1, v0}, Ljava/lang/Enum;-><init>(Ljava/lang/String;I)V\n\n    return-void\n.end method\n\n.method public static a(Lx/a/c;)V\n    .registers 1\n\n    .prologue\n    .line 79\n    if-eqz p0, :cond_4\n\n    .line 80\n    sput-object p0, Lenums/TestEnumsWithStaticFields;->sB:Lx/a/c;\n\n    .line 82\n    :cond_4\n    return-void\n.end method\n\n.method public static valueOf(Ljava/lang/String;)Lenums/TestEnumsWithStaticFields;\n    .registers 3\n\n    .prologue\n    const v1, 0x238f8\n\n    invoke-static {v1}, Lx/q;->i(I)V\n\n    .line 9\n    const-class v0, Lenums/TestEnumsWithStaticFields;\n\n    invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;\n\n    move-result-object v0\n\n    check-cast v0, Lenums/TestEnumsWithStaticFields;\n\n    invoke-static {v1}, Lx/q;->o(I)V\n\n    return-object v0\n.end method\n\n.method public static values()[Lenums/TestEnumsWithStaticFields;\n    .registers 2\n\n    .prologue\n    const v1, 0x238f7\n\n    invoke-static {v1}, Lx/q;->i(I)V\n\n    .line 9\n    sget-object v0, Lenums/TestEnumsWithStaticFields;->sC:[Lenums/TestEnumsWithStaticFields;\n\n    invoke-virtual {v0}, [Lenums/TestEnumsWithStaticFields;->clone()Ljava/lang/Object;\n\n    move-result-object v0\n\n    check-cast v0, [Lenums/TestEnumsWithStaticFields;\n\n    invoke-static {v1}, Lx/q;->o(I)V\n\n    return-object v0\n.end method\n\n\n# virtual methods\n.method public final FR(I)V\n    .registers 4\n\n    .prologue\n    const v1, 0x238fb\n\n    invoke-static {v1}, Lx/q;->i(I)V\n\n    .line 96\n    sget-object v0, Lenums/TestEnumsWithStaticFields;->sB:Lx/a/c;\n\n    invoke-interface {v0, p1}, Lx/a/c;->FR(I)V\n\n    .line 97\n    invoke-static {v1}, Lx/q;->o(I)V\n\n    return-void\n.end method\n\n"
  },
  {
    "path": "jadx-core/src/test/smali/enums/TestSwitchOverEnum/Count.smali",
    "content": ".class public final enum Lenums/TestSwitchOverEnum$Count;\n.super Ljava/lang/Enum;\n\n.field private static final synthetic $VALUES:[Lenums/TestSwitchOverEnum$Count;\n.field public static final enum ONE:Lenums/TestSwitchOverEnum$Count;\n.field public static final enum THREE:Lenums/TestSwitchOverEnum$Count;\n.field public static final enum TWO:Lenums/TestSwitchOverEnum$Count;\n\n.method private static synthetic $values()[Lenums/TestSwitchOverEnum$Count;\n    .registers 3\n    const/4 v0, 0x3\n    new-array v0, v0, [Lenums/TestSwitchOverEnum$Count;\n    const/4 v1, 0x0\n    sget-object v2, Lenums/TestSwitchOverEnum$Count;->ONE:Lenums/TestSwitchOverEnum$Count;\n    aput-object v2, v0, v1\n    const/4 v1, 0x1\n    sget-object v2, Lenums/TestSwitchOverEnum$Count;->TWO:Lenums/TestSwitchOverEnum$Count;\n    aput-object v2, v0, v1\n    const/4 v1, 0x2\n    sget-object v2, Lenums/TestSwitchOverEnum$Count;->THREE:Lenums/TestSwitchOverEnum$Count;\n    aput-object v2, v0, v1\n    return-object v0\n.end method\n\n.method static constructor <clinit>()V\n    .registers 3\n    new-instance v0, Lenums/TestSwitchOverEnum$Count;\n    const-string v1, \"ONE\"\n    const/4 v2, 0x0\n    invoke-direct {v0, v1, v2}, Lenums/TestSwitchOverEnum$Count;-><init>(Ljava/lang/String;I)V\n    sput-object v0, Lenums/TestSwitchOverEnum$Count;->ONE:Lenums/TestSwitchOverEnum$Count;\n    new-instance v0, Lenums/TestSwitchOverEnum$Count;\n    const-string v1, \"TWO\"\n    const/4 v2, 0x1\n    invoke-direct {v0, v1, v2}, Lenums/TestSwitchOverEnum$Count;-><init>(Ljava/lang/String;I)V\n    sput-object v0, Lenums/TestSwitchOverEnum$Count;->TWO:Lenums/TestSwitchOverEnum$Count;\n    new-instance v0, Lenums/TestSwitchOverEnum$Count;\n    const-string v1, \"THREE\"\n    const/4 v2, 0x2\n    invoke-direct {v0, v1, v2}, Lenums/TestSwitchOverEnum$Count;-><init>(Ljava/lang/String;I)V\n    sput-object v0, Lenums/TestSwitchOverEnum$Count;->THREE:Lenums/TestSwitchOverEnum$Count;\n    invoke-static {}, Lenums/TestSwitchOverEnum$Count;->$values()[Lenums/TestSwitchOverEnum$Count;\n    move-result-object v0\n    sput-object v0, Lenums/TestSwitchOverEnum$Count;->$VALUES:[Lenums/TestSwitchOverEnum$Count;\n    return-void\n.end method\n\n.method private constructor <init>(Ljava/lang/String;I)V\n    .registers 3\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"()V\"\n        }\n    .end annotation\n\n    invoke-direct {p0, p1, p2}, Ljava/lang/Enum;-><init>(Ljava/lang/String;I)V\n    return-void\n.end method\n\n.method public static valueOf(Ljava/lang/String;)Lenums/TestSwitchOverEnum$Count;\n    .registers 2\n    const-class v0, Lenums/TestSwitchOverEnum$Count;\n    invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;\n    move-result-object v0\n    check-cast v0, Lenums/TestSwitchOverEnum$Count;\n    return-object v0\n.end method\n\n.method public static values()[Lenums/TestSwitchOverEnum$Count;\n    .registers 1\n    sget-object v0, Lenums/TestSwitchOverEnum$Count;->$VALUES:[Lenums/TestSwitchOverEnum$Count;\n    invoke-virtual {v0}, [Lenums/TestSwitchOverEnum$Count;->clone()Ljava/lang/Object;\n    move-result-object v0\n    check-cast v0, [Lenums/TestSwitchOverEnum$Count;\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/enums/TestSwitchOverEnum/TestSwitchOverEnum.smali",
    "content": ".class public Lenums/TestSwitchOverEnum;\n.super Ljava/lang/Object;\n\n.method public test(Lenums/TestSwitchOverEnum$Count;)I\n    .registers 3\n    .param p1, \"v\"\n\n    invoke-virtual {p1}, Lenums/TestSwitchOverEnum$Count;->ordinal()I\n    move-result v0\n\n    packed-switch v0, :pswitch_data\n    const/4 v0, 0x0\n\n    :goto_8\n    return v0\n\n    :pswitch_9\n    const/4 v0, 0x1\n    goto :goto_8\n\n    :pswitch_b\n    const/4 v0, 0x2\n    goto :goto_8\n\n    :pswitch_data\n    .packed-switch 0x0\n        :pswitch_9\n        :pswitch_b\n    .end packed-switch\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/fallback/TestFallbackManyNops.smali",
    "content": ".class public Lfallback/TestFallbackManyNops;\n.super Ljava/lang/Object;\n\n.method public static test()V\n\t.registers 1\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\treturn-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/generics/TestClassSignature.smali",
    "content": ".class public abstract Lgenerics/TestClassSignature;\n.super Ljava/lang/Object;\n.source \"SourceFile\"\n\n# interfaces\n.implements Ljava/util/Iterator;\n\n\n# annotations\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"<T:\",\n        \"Ljava/lang/Object;\",\n        \">\",\n        \"Lgenerics/TestClassSignature<\",\n        \"TT;>;\"\n    }\n.end annotation\n\n\n# instance fields\n.field public f:Ljava/lang/Object;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"TT;\"\n        }\n    .end annotation\n.end field\n\n\n# direct methods\n.method public constructor <init>(Ljava/lang/Object;)V\n    .registers 2\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(TT;)V\"\n        }\n    .end annotation\n\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n    iput-object p1, p0, Lgenerics/TestClassSignature;->f:Ljava/lang/Object;\n    return-void\n.end method\n\n\n# virtual methods\n.method public abstract a(Ljava/lang/Object;)Ljava/lang/Object;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(TT;)TT;\"\n        }\n    .end annotation\n.end method\n\n.method public final hasNext()Z\n    .registers 2\n\n    .line 1\n    iget-object v0, p0, Lgenerics/TestClassSignature;->f:Ljava/lang/Object;\n    if-eqz v0, :cond_6\n    const/4 v0, 0x1\n    goto :goto_7\n\n    :cond_6\n    const/4 v0, 0x0\n\n    :goto_7\n    return v0\n.end method\n\n.method public final next()Ljava/lang/Object;\n    .registers 3\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"()TT;\"\n        }\n    .end annotation\n\n    invoke-virtual {p0}, Lgenerics/TestClassSignature;->hasNext()Z\n    move-result v0\n    if-eqz v0, :cond_1b\n\n    :try_start_6\n    iget-object v0, p0, Lgenerics/TestClassSignature;->f:Ljava/lang/Object;\n    :try_end_8\n    .catchall {:try_start_6 .. :try_end_8} :catchall_11\n\n    iget-object v1, p0, Lgenerics/TestClassSignature;->f:Ljava/lang/Object;\n    invoke-virtual {p0, v1}, Lgenerics/TestClassSignature;->a(Ljava/lang/Object;)Ljava/lang/Object;\n    move-result-object v1\n    iput-object v1, p0, Lgenerics/TestClassSignature;->f:Ljava/lang/Object;\n    return-object v0\n\n    :catchall_11\n    move-exception v0\n\n    iget-object v1, p0, Lgenerics/TestClassSignature;->f:Ljava/lang/Object;\n    invoke-virtual {p0, v1}, Lgenerics/TestClassSignature;->a(Ljava/lang/Object;)Ljava/lang/Object;\n    move-result-object v1\n    iput-object v1, p0, Lgenerics/TestClassSignature;->f:Ljava/lang/Object;\n    throw v0\n\n    :cond_1b\n    new-instance v0, Ljava/util/NoSuchElementException;\n    invoke-direct {v0}, Ljava/util/NoSuchElementException;-><init>()V\n    throw v0\n.end method\n\n.method public final remove()V\n    .registers 2\n\n    new-instance v0, Ljava/lang/UnsupportedOperationException;\n    invoke-direct {v0}, Ljava/lang/UnsupportedOperationException;-><init>()V\n    throw v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/generics/TestMethodOverride.smali",
    "content": ".class public final Lgenerics/TestMethodOverride;\n.super Ljava/lang/Object;\n\n# interfaces\n.implements Landroid/os/Parcelable$Creator;\n\n\n# annotations\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"Ljava/lang/Object;\",\n        \"Landroid/os/Parcelable$Creator<\",\n        \"Ljava/lang/String;\",\n        \">;\"\n    }\n.end annotation\n\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    .line 1\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n\n# virtual methods\n.method public final synthetic createFromParcel(Landroid/os/Parcel;)Ljava/lang/Object;\n    .registers 2\n\n    const/4 v0, 0x0\n\n    return-object v0\n.end method\n\n.method public final synthetic newArray(I)[Ljava/lang/Object;\n    .registers 2\n\n    .line 4\n    new-array p1, p1, [Ljava/lang/String;\n\n    return-object p1\n.end method\n\n"
  },
  {
    "path": "jadx-core/src/test/smali/generics/TestMissingGenericsTypes2.smali",
    "content": ".class public Lgenerics/TestMissingGenericsTypes2;\n.super Ljava/lang/Object;\n.source \"TestMissingGenericsTypes2.java\"\n\n# interfaces\n.implements Ljava/lang/Iterable;\n\n\n# annotations\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"<T:\",\n        \"Ljava/lang/Object;\",\n        \">\",\n        \"Ljava/lang/Object;\",\n        \"Ljava/lang/Iterable<\",\n        \"TT;>;\"\n    }\n.end annotation\n\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    .local p0, \"this\":Lgenerics/TestMissingGenericsTypes2;, \"Lgenerics/TestMissingGenericsTypes2<TT;>;\"\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n.method private doSomething(Ljava/lang/String;)V\n    .registers 2\n    .param p1, \"s\"    # Ljava/lang/String;\n\n    .local p0, \"this\":Lgenerics/TestMissingGenericsTypes2;, \"Lgenerics/TestMissingGenericsTypes2<TT;>;\"\n    return-void\n.end method\n\n\n# virtual methods\n.method public iterator()Ljava/util/Iterator;\n    .registers 2\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"()\",\n            \"Ljava/util/Iterator<\",\n            \"TT;>;\"\n        }\n    .end annotation\n\n    .local p0, \"this\":Lgenerics/TestMissingGenericsTypes2;, \"Lgenerics/TestMissingGenericsTypes2<TT;>;\"\n    const/4 v0, 0x0\n\n    return-object v0\n.end method\n\n.method public test(Lgenerics/TestMissingGenericsTypes2;)V\n    .registers 4\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Lgenerics/TestMissingGenericsTypes2<\",\n            \"Ljava/lang/String;\",\n            \">;)V\"\n        }\n    .end annotation\n\n    .local p0, \"this\":Lgenerics/TestMissingGenericsTypes2;, \"Lgenerics/TestMissingGenericsTypes2<TT;>;\"\n    .local p1, \"l\":Lgenerics/TestMissingGenericsTypes2;, \"Lgenerics/TestMissingGenericsTypes2<Ljava/lang/String;>;\"\n    invoke-virtual {p1}, Lgenerics/TestMissingGenericsTypes2;->iterator()Ljava/util/Iterator;\n\n    move-result-object v0\n\n    # original:\n    # .local v0, \"i\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    # manipulated: removed generic type\n    .local v0, \"i\":Ljava/util/Iterator;\n\n    :goto_4\n    invoke-interface {v0}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v1\n\n    if-eqz v1, :cond_14\n\n    invoke-interface {v0}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v1\n\n    check-cast v1, Ljava/lang/String;\n\n    .local v1, \"s\":Ljava/lang/String;\n    invoke-direct {p0, v1}, Lgenerics/TestMissingGenericsTypes2;->doSomething(Ljava/lang/String;)V\n\n    .end local v1    # \"s\":Ljava/lang/String;\n    goto :goto_4\n\n    :cond_14\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/generics/TestSyntheticOverride/KotlinFunction1.smali",
    "content": ".class public interface abstract Lkotlin/jvm/functions/Function1;\n.super Ljava/lang/Object;\n.source \"SourceFile\"\n\n.implements Lkotlin/Function;\n\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"<P1:\",\n        \"Ljava/lang/Object;\",\n        \"R:\",\n        \"Ljava/lang/Object;\",\n        \">\",\n        \"Ljava/lang/Object;\",\n        \"Lkotlin/Function<\",\n        \"TR;>;\"\n    }\n.end annotation\n\n.method public abstract invoke(Ljava/lang/Object;)Ljava/lang/Object;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(TP1;)TR;\"\n        }\n    .end annotation\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/generics/TestSyntheticOverride/TestSyntheticOverride.smali",
    "content": ".class final Lgenerics/TestSyntheticOverride;\n.super Ljava/lang/Object;\n\n.implements Lkotlin/jvm/functions/Function1;\n\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"Lkotlin/jvm/internal/Lambda;\",\n        \"Lkotlin/jvm/functions/Function1<\",\n        \"Ljava/lang/String;\",\n        \"Lkotlin/Unit;\",\n        \">;\"\n    }\n.end annotation\n\n.field final synthetic this$0:Lgenerics/TestSyntheticOverrideParent;\n\n\n.method public bridge synthetic invoke(Ljava/lang/Object;)Ljava/lang/Object;\n    .registers 2\n    check-cast p1, Ljava/lang/String;\n    invoke-virtual {p0, p1}, Lgenerics/TestSyntheticOverride;->invoke(Ljava/lang/String;)V\n    sget-object p1, Lkotlin/Unit;->INSTANCE:Lkotlin/Unit;\n    return-object p1\n.end method\n\n.method public final invoke(Ljava/lang/String;)V\n    .registers 5\n    iget-object v0, p0, Lgenerics/TestSyntheticOverride;->this$0:Lgenerics/TestSyntheticOverrideParent;\n    invoke-static {v0}, Lgenerics/TestSyntheticOverride;->access$getDialog$p(Lgenerics/TestSyntheticOverride;)Landroidx/appcompat/app/AlertDialog;\n    move-result-object v0\n    if-nez v0, :cond_9\n    goto :goto_c\n\n    :cond_9\n    invoke-virtual {v0}, Landroidx/appcompat/app/AppCompatDialog;->dismiss()V\n\n    :goto_c\n    iget-object v0, p0, Lgenerics/TestSyntheticOverride;->this$0:Lgenerics/TestSyntheticOverrideParent;\n    invoke-virtual {v0}, Landroid/app/Activity;->getIntent()Landroid/content/Intent;\n    move-result-object v1\n    const-string v2, \"intent\"\n    invoke-static {v1, v2}, Lkotlin/jvm/internal/Intrinsics;->checkNotNullExpressionValue(Ljava/lang/Object;Ljava/lang/String;)V\n    invoke-static {v0, v1, p1}, Lgenerics/TestSyntheticOverride;->access$getChooserIntent(Lgenerics/TestSyntheticOverride;Landroid/content/Intent;Ljava/lang/String;)Landroid/content/Intent;\n    move-result-object p1\n    invoke-virtual {v0, p1}, Landroid/app/Activity;->startActivity(Landroid/content/Intent;)V\n    iget-object p1, p0, Lgenerics/TestSyntheticOverride;->this$0:Lgenerics/TestSyntheticOverrideParent;\n    invoke-virtual {p1}, Landroid/app/Activity;->finish()V\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inline/TestGetterInlineNegative.smali",
    "content": ".class public Linline/TestGetterInlineNegative;\n.super Ljava/lang/Object;\n\n.field public static final field:Ljava/lang/String; = \"some string\"\n\n.method public static synthetic getter()Ljava/lang/String;\n    .locals 1\n\n    sget-object v0, Linline/TestGetterInlineNegative;->field:Ljava/lang/String;\n\n    return-object v0\n.end method\n\n.method public test()V\n    .locals 1\n\n    invoke-static {}, Linline/TestGetterInlineNegative;->getter()Ljava/lang/String;\n\n    return-void\n.end method\n\n.method public test2()Ljava/lang/String;\n    .locals 2\n\n    invoke-static {}, Linline/TestGetterInlineNegative;->getter()Ljava/lang/String;\n    move-result-object v1\n\n    return-object v1\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inline/TestInline7.smali",
    "content": ".class public Linline/TestInline7;\n.super Ljava/lang/Object;\n\n.method public onViewCreated(Landroid/view/View;Landroid/os/Bundle;)V\n    .locals 4\n    .param p2    # Landroid/os/Bundle;\n        .annotation build Landroid/support/annotation/Nullable;\n        .end annotation\n    .end param\n\n    .line 42\n    invoke-super {p0, p1, p2}, Lapp/navigation/fragment/NodeFragment;->onViewCreated(Landroid/view/View;Landroid/os/Bundle;)V\n\n    .line 43\n    sget p2, Lapp/wallet/R$id;->done_button_early_release_failure:I\n\n    invoke-virtual {p1, p2}, Landroid/view/View;->findViewById(I)Landroid/view/View;\n\n    move-result-object p2\n\n    new-instance v0, Lapp/common/utils/SafeClickListener;\n\n    invoke-direct {v0, p0}, Lapp/common/utils/SafeClickListener;-><init>(Lapp/common/utils/ISafeClickVerifierListener;)V\n\n    invoke-virtual {p2, v0}, Landroid/view/View;->setOnClickListener(Landroid/view/View$OnClickListener;)V\n\n    .line 44\n    invoke-virtual {p0}, Linline/TestInline7;->getArguments()Landroid/os/Bundle;\n\n    move-result-object p2\n\n    if-eqz p2, :cond_0\n\n    .line 46\n    sget v0, Lapp/wallet/R$id;->summary_content_early_release_failure:I\n\n    invoke-virtual {p1, v0}, Landroid/view/View;->findViewById(I)Landroid/view/View;\n\n    move-result-object p1\n\n    check-cast p1, Landroid/widget/TextView;\n\n    sget v0, Lapp/wallet/R$string;->withdraw_id_capture_failure_content:I\n\n    const/4 v1, 0x2\n\n    new-array v1, v1, [Ljava/lang/Object;\n\n    const/4 v2, 0x0\n\n    const-string v3, \"withdrawAmount\"\n\n    invoke-virtual {p2, v3}, Landroid/os/Bundle;->getString(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v3\n\n    aput-object v3, v1, v2\n\n    const/4 v2, 0x1\n\n    const-string v3, \"withdrawHoldTime\"\n\n    invoke-virtual {p2, v3}, Landroid/os/Bundle;->getString(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object p2\n\n    aput-object p2, v1, v2\n\n    invoke-virtual {p0, v0, v1}, Linline/TestInline7;->getString(I[Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object p2\n\n    invoke-virtual {p1, p2}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V\n\n    :cond_0\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inline/TestInstanceLambda/Lambda.smali",
    "content": ".class public Linline/Lambda$1;\n.super Ljava/lang/Object;\n\n.implements Ljava/util/function/Function;\n\n\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"Ljava/lang/Object;\",\n        \"Ljava/util/function/Function\",\n        \"<TT;TT;>;\"\n    }\n.end annotation\n\n.field public static final INSTANCE:Linline/Lambda$1;\n\n.method static constructor <clinit>()V\n    .registers 1\n    new-instance v0, Linline/Lambda$1;\n    invoke-direct {v0}, Linline/Lambda$1;-><init>()V\n    sput-object v0, Linline/Lambda$1;->INSTANCE:Linline/Lambda$1;\n    return-void\n.end method\n\n.method private constructor <init>()V\n    .registers 1\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n    return-void\n.end method\n\n.method public final apply(Ljava/lang/Object;)Ljava/lang/Object;\n    .registers 2\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(TT;)TT;\"\n        }\n    .end annotation\n\n    return-object p1\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inline/TestInstanceLambda/TestCls.smali",
    "content": ".class public Linline/TestCls;\n.super Ljava/lang/Object;\n\n.method public test(Ljava/util/List;)Ljava/util/Map;\n    .registers 3\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"<T:\",\n            \"Ljava/lang/Object;\",\n            \">(\",\n            \"Ljava/util/List\",\n            \"<+TT;>;)\",\n            \"Ljava/util/Map\",\n            \"<TT;TT;>;\"\n        }\n    .end annotation\n\n    sget-object v0, Linline/Lambda$1;->INSTANCE:Linline/Lambda$1;\n    invoke-static {p1, v0}, Linline/TestCls;->toMap(Ljava/util/List;Ljava/util/function/Function;)Ljava/util/Map;\n    move-result-object v0\n    return-object v0\n.end method\n\n.method private static toMap(Ljava/util/List;Ljava/util/function/Function;)Ljava/util/Map;\n    .registers 4\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"<T:\",\n            \"Ljava/lang/Object;\",\n            \">(\",\n            \"Ljava/util/List\",\n            \"<+TT;>;\",\n            \"Ljava/util/function/Function\",\n            \"<TT;TT;>;)\",\n            \"Ljava/util/Map\",\n            \"<TT;TT;>;\"\n        }\n    .end annotation\n\n    const/4 v0, 0x0\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inline/TestMethodInline/A.smali",
    "content": ".class public Linline/A;\n.super Ljava/lang/Object;\n\n.method public static useMth()V\n    .locals 1\n\n    invoke-static {}, Linline/other/B;->bridgeMth()V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inline/TestMethodInline/B.smali",
    "content": ".class public Linline/other/B;\n.super Ljava/lang/Object;\n\n.method public static bridge synthetic bridgeMth()V\n    .locals 1\n\n    invoke-static {}, Linline/other/C;->test()V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inline/TestMethodInline/C.smali",
    "content": ".class Linline/other/C; # package-private\n.super Ljava/lang/Object;\n\n.method public static test()V\n    .locals 1\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inline/TestOverlapSyntheticMethods.smali",
    "content": ".class public Linline/TestOverlapSyntheticMethods;\n.super Ljava/lang/Object;\n\n.method public synthetic a(I)I\n    .registers 2\n    return p1\n.end method\n\n.method public synthetic a(I)Ljava/lang/String;\n    .registers 4\n    new-instance v0, Ljava/lang/StringBuilder;\n    invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V\n    const-string v1, \"i:\"\n    invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v0\n    invoke-virtual {v0, p1}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;\n    move-result-object v0\n    invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n    move-result-object v0\n    return-object v0\n.end method\n\n.method public test(I)Ljava/lang/String;\n    .registers 4\n    new-instance v0, Ljava/lang/StringBuilder;\n    invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V\n    invoke-virtual {p0, p1}, Linline/TestOverlapSyntheticMethods;->a(I)I\n    move-result v1\n    invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;\n    move-result-object v0\n    const-string v1, \"|\"\n    invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v0\n    invoke-virtual {p0, p1}, Linline/TestOverlapSyntheticMethods;->a(I)Ljava/lang/String;\n    move-result-object v1\n    invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v0\n    invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n    move-result-object v0\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inline/TestOverrideBridgeMerge.smali",
    "content": ".class public Linline/TestOverrideBridgeMerge;\n.super Ljava/lang/Object;\n\n.implements Ljava/util/function/Function;\n\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"Ljava/lang/Object;\",\n        \"Ljava/util/function/Function\",\n        \"<\",\n        \"Ljava/lang/String;\",\n        \"Ljava/lang/Integer;\",\n        \">;\"\n    }\n.end annotation\n\n.method public constructor <init>()V\n    .registers 1\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n    return-void\n.end method\n\n.method public bridge synthetic apply(Ljava/lang/Object;)Ljava/lang/Object;\n    .registers 3\n    check-cast p1, Ljava/lang/String;\n    invoke-virtual {p0, p1}, Linline/TestOverrideBridgeMerge;->test(Ljava/lang/String;)Ljava/lang/Integer;\n    move-result-object v0\n    return-object v0\n.end method\n\n.method public test(Ljava/lang/String;)Ljava/lang/Integer;\n    .registers 3\n    .param p1, \"str\"    # Ljava/lang/String;\n    invoke-virtual {p1}, Ljava/lang/String;->length()I\n    move-result v0\n    invoke-static {v0}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;\n    move-result-object v0\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inline/TestSyntheticClassInline/A.smali",
    "content": ".class Linline/A;\n.super Ljava/lang/Object;\n.source \"TestJavaClass.java\"\n\n.method constructor <init>()V\n    .registers 1\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n    return-void\n.end method\n\n.method static synthetic lambda$test$0(JJ)Ljava/lang/Long;\n    .registers 8\n    .param p0, \"x1\"    # J\n    .param p2, \"x2\"    # J\n\n    invoke-static {}, Ljava/lang/System;->currentTimeMillis()J\n    move-result-wide v0\n    .local v0, \"y1\":J\n    add-long v2, p0, v0\n    add-long/2addr v2, p2\n    invoke-static {v2, v3}, Ljava/lang/Long;->valueOf(J)Ljava/lang/Long;\n    move-result-object v2\n    return-object v2\n.end method\n\n.method static test(JJ)Ljava/util/function/Supplier;\n    .registers 5\n    .param p0, \"x1\"    # J\n    .param p2, \"x2\"    # J\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(JJ)\",\n            \"Ljava/util/function/Supplier<\",\n            \"Ljava/lang/Long;\",\n            \">;\"\n        }\n    .end annotation\n\n    new-instance v0, Linline/A$$ExternalSyntheticLambda0;\n    invoke-direct {v0, p0, p1, p2, p3}, Linline/A$$ExternalSyntheticLambda0;-><init>(JJ)V\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inline/TestSyntheticClassInline/B.smali",
    "content": ".class public final synthetic Linline/A$$ExternalSyntheticLambda0;\n.super Ljava/lang/Object;\n.source \"D8$$SyntheticClass\"\n\n.implements Ljava/util/function/Supplier;\n\n.field public final synthetic f$0:J\n.field public final synthetic f$1:J\n\n.method public synthetic constructor <init>(JJ)V\n    .registers 5\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n    iput-wide p1, p0, Linline/A$$ExternalSyntheticLambda0;->f$0:J\n    iput-wide p3, p0, Linline/A$$ExternalSyntheticLambda0;->f$1:J\n    return-void\n.end method\n\n.method public final get()Ljava/lang/Object;\n    .registers 5\n    iget-wide v0, p0, Linline/A$$ExternalSyntheticLambda0;->f$0:J\n    iget-wide v2, p0, Linline/A$$ExternalSyntheticLambda0;->f$1:J\n    invoke-static {v0, v1, v2, v3}, Linline/A;->lambda$test$0(JJ)Ljava/lang/Long;\n    move-result-object v0\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inline/TestSyntheticInline3/KotlinFunction1.smali",
    "content": ".class public interface abstract Lkotlin/jvm/functions/Function1;\n.super Ljava/lang/Object;\n.source \"SourceFile\"\n\n.implements Lkotlin/Function;\n\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"<P1:\",\n        \"Ljava/lang/Object;\",\n        \"R:\",\n        \"Ljava/lang/Object;\",\n        \">\",\n        \"Ljava/lang/Object;\",\n        \"Lkotlin/Function<\",\n        \"TR;>;\"\n    }\n.end annotation\n\n.method public abstract invoke(Ljava/lang/Object;)Ljava/lang/Object;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(TP1;)TR;\"\n        }\n    .end annotation\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inline/TestSyntheticInline3/TestSyntheticInline3$onCreate$1.smali",
    "content": ".class final Linline/TestSyntheticInline3$onCreate$1;\n.super Lkotlin/jvm/internal/Lambda;\n\n.implements Lkotlin/jvm/functions/Function1;\n\n.annotation system Ldalvik/annotation/InnerClass;\n    accessFlags = 0x18\n    name = null\n.end annotation\n\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"Lkotlin/jvm/internal/Lambda;\",\n        \"Lkotlin/jvm/functions/Function1<\",\n        \"Ljava/lang/String;\",\n        \"Lkotlin/Unit;\",\n        \">;\"\n    }\n.end annotation\n\n\n.field final synthetic this$0:Linline/TestSyntheticInline3;\n\n\n.method constructor <init>(Linline/TestSyntheticInline3;)V\n    .registers 2\n    iput-object p1, p0, Linline/TestSyntheticInline3$onCreate$1;->this$0:Linline/TestSyntheticInline3;\n    const/4 p1, 0x1\n    invoke-direct {p0, p1}, Lkotlin/jvm/internal/Lambda;-><init>(I)V\n    return-void\n.end method\n\n\n.method public bridge synthetic invoke(Ljava/lang/Object;)Ljava/lang/Object;\n    .registers 2\n    check-cast p1, Ljava/lang/String;\n    invoke-virtual {p0, p1}, Linline/TestSyntheticInline3$onCreate$1;->invoke(Ljava/lang/String;)V\n    sget-object p1, Lkotlin/Unit;->INSTANCE:Lkotlin/Unit;\n    return-object p1\n.end method\n\n.method public final invoke(Ljava/lang/String;)V\n    .registers 5\n    iget-object v0, p0, Linline/TestSyntheticInline3$onCreate$1;->this$0:Linline/TestSyntheticInline3;\n    invoke-static {v0}, Linline/TestSyntheticInline3;->access$getDialog$p(Linline/TestSyntheticInline3;)Landroidx/appcompat/app/AlertDialog;\n    move-result-object v0\n    if-nez v0, :cond_9\n    goto :goto_c\n    :cond_9\n    invoke-virtual {v0}, Landroidx/appcompat/app/AppCompatDialog;->dismiss()V\n    :goto_c\n    iget-object v0, p0, Linline/TestSyntheticInline3$onCreate$1;->this$0:Linline/TestSyntheticInline3;\n    invoke-virtual {v0}, Landroid/app/Activity;->getIntent()Landroid/content/Intent;\n    move-result-object v1\n    const-string v2, \"intent\"\n    invoke-static {v1, v2}, Lkotlin/jvm/internal/Intrinsics;->checkNotNullExpressionValue(Ljava/lang/Object;Ljava/lang/String;)V\n    invoke-static {v0, v1, p1}, Linline/TestSyntheticInline3;->access$getChooserIntent(Linline/TestSyntheticInline3;Landroid/content/Intent;Ljava/lang/String;)Landroid/content/Intent;\n    move-result-object p1\n    invoke-virtual {v0, p1}, Landroid/app/Activity;->startActivity(Landroid/content/Intent;)V\n    iget-object p1, p0, Linline/TestSyntheticInline3$onCreate$1;->this$0:Linline/TestSyntheticInline3;\n    invoke-virtual {p1}, Landroid/app/Activity;->finish()V\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inline/TestSyntheticInline3/TestSyntheticInline3.smali",
    "content": ".class public final Linline/TestSyntheticInline3;\n.super Landroid/app/Activity;\n\n.field private dialog:Landroidx/appcompat/app/AlertDialog;\n\n.method public static final synthetic access$getChooserIntent(Linline/TestSyntheticInline3;Landroid/content/Intent;Ljava/lang/String;)Landroid/content/Intent;\n    .registers 3\n    invoke-direct {p0, p1, p2}, Linline/TestSyntheticInline3;->getChooserIntent(Landroid/content/Intent;Ljava/lang/String;)Landroid/content/Intent;\n    move-result-object p0\n    return-object p0\n.end method\n\n.method public static final synthetic access$getDialog$p(Linline/TestSyntheticInline3;)Landroidx/appcompat/app/AlertDialog;\n    .registers 1\n    iget-object p0, p0, Linline/TestSyntheticInline3;->dialog:Landroidx/appcompat/app/AlertDialog;\n    return-object p0\n.end method\n\n.method protected onCreate(Landroid/os/Bundle;)V\n    .registers 3\n    invoke-super {p0, p1}, Landroidx/fragment/app/FragmentActivity;->onCreate(Landroid/os/Bundle;)V\n    new-instance v0, Linline/TestSyntheticInline3$onCreate$1;\n    invoke-direct {v0, p0}, Linline/TestSyntheticInline3$onCreate$1;-><init>(Linline/TestSyntheticInline3;)V\n    return-void\n.end method\n\n\n.method private final getChooserIntent(Landroid/content/Intent;Ljava/lang/String;)Landroid/content/Intent;\n    .registers 5\n    new-instance v0, Landroid/content/Intent;\n    invoke-direct {v0, p1}, Landroid/content/Intent;-><init>(Landroid/content/Intent;)V\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inner/TestAnonymousClass14/OuterCls$1.smali",
    "content": ".class Linner/OuterCls$1;\n.super Ljava/lang/Thread;\n.source \"SourceFile\"\n\n.annotation system Ldalvik/annotation/InnerClass;\n    accessFlags = 0x0\n    name = null\n.end annotation\n\n.field final synthetic this$0:Linner/OuterCls;\n\n\n# direct methods\n.method constructor <init>(Linner/OuterCls;Ljava/lang/Runnable;)V\n    .locals 0\n\n    iput-object p1, p0, Linner/OuterCls$1;->this$0:Linner/OuterCls;\n\n    return-void\n.end method\n\n\n# virtual methods\n.method public someMethod()V\n    .locals 3\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inner/TestAnonymousClass14/OuterCls$TestCls.smali",
    "content": ".class Linner/OuterCls$TestCls;\n.super Ljava/lang/Object;\n.source \"SourceFile\"\n\n\n# annotations\n.annotation system Ldalvik/annotation/EnclosingClass;\n    value = Linner/OuterCls;\n.end annotation\n\n.annotation system Ldalvik/annotation/InnerClass;\n    accessFlags = 0x0\n    name = \"TestCls\"\n.end annotation\n\n.field final synthetic this$0:Linner/OuterCls;\n\n\n# direct methods\n.method private constructor <init>(Linner/OuterCls;)V\n    .locals 0\n\n    iput-object p1, p0, Linner/OuterCls$TestCls;->this$0:Linner/OuterCls;\n\n    new-instance p1, Ljava/util/ArrayList;\n\n    invoke-direct {p1}, Ljava/util/ArrayList;-><init>()V\n\n    return-void\n.end method\n\n.method synthetic constructor <init>(Linner/OuterCls;Linner/OuterCls$1;)V\n    .locals 0\n\n    invoke-direct {p0, p1}, Linner/OuterCls$TestCls;-><init>(Linner/OuterCls;)V\n\n    return-void\n.end method\n\n"
  },
  {
    "path": "jadx-core/src/test/smali/inner/TestAnonymousClass14/OuterCls.smali",
    "content": ".class public Linner/OuterCls;\n.super Ljava/lang/Object;\n.source \"SourceFile\"\n\n# interfaces\n.implements Ljava/lang/Runnable;\n\n\n# annotations\n.annotation system Ldalvik/annotation/MemberClasses;\n    value = {\n        Linner/OuterCls$TestCls;\n    }\n.end annotation\n\n\n# direct methods\n.method static constructor <clinit>()V\n    .locals 0\n\n    return-void\n.end method\n\n.method public constructor <init>()V\n    .locals 1\n\n    return-void\n.end method\n\n.method public makeTestCls()V\n    .locals 2\n\n    new-instance v1, Linner/OuterCls$TestCls;\n\n    const/4 v0, 0x0\n\n    invoke-direct {v1, p0, v0}, Linner/OuterCls$TestCls;-><init>(Linner/OuterCls;Linner/OuterCls$1;)V\n\n    return-void\n.end method\n\n.method public makeAnonymousCls()V\n    .locals 2\n\n    new-instance v1, Linner/OuterCls$1;\n\n    invoke-direct {v1, p0, p0}, Linner/OuterCls$1;-><init>(Linner/OuterCls;Ljava/lang/Runnable;)V\n\n    invoke-direct {p0, v1}, Linner/OuterCls;->use(Ljava/lang/Thread;)V\n\n    return-void\n.end method\n\n.method public run()V\n    .locals 2\n\n    return-void\n.end method\n\n.method public use(Ljava/lang/Thread;)V\n    .locals 2\n\n    return-void\n.end method\n\n"
  },
  {
    "path": "jadx-core/src/test/smali/inner/TestAnonymousClass19/ATestCls.smali",
    "content": ".class public Linner/ATestCls;\n.super Ljava/lang/Object;\n\n.method public constructor <init>()V\n    .registers 1\n\n    .prologue\n    .line 11\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n\n# virtual methods\n.method public test(ZZ)V\n    .registers 5\n    .param p1, \"a\"    # Z\n    .param p2, \"b\"    # Z\n\n    .prologue\n    .line 14\n    if-eqz p1, :cond_e\n\n    if-eqz p2, :cond_e\n\n    const/4 v0, 0x1\n\n    .line 15\n    .local v0, \"c\":Z\n    :goto_5\n    new-instance v1, Linner/Lambda$TestCls$1;\n\n    invoke-direct {v1, p0, p1, p2, v0}, Linner/Lambda$TestCls$1;-><init>(Linner/ATestCls;ZZZ)V\n\n    invoke-virtual {p0, v1}, Linner/ATestCls;->use(Ljava/lang/Runnable;)V\n\n    .line 21\n    return-void\n\n    .line 14\n    .end local v0    # \"c\":Z\n    :cond_e\n    const/4 v0, 0x0\n\n    goto :goto_5\n.end method\n\n.method public use(Ljava/lang/Runnable;)V\n    .registers 2\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inner/TestAnonymousClass19/Lambda$TestCls$1.smali",
    "content": ".class public final synthetic Linner/Lambda$TestCls$1;\n.super Ljava/lang/Object;\n\n.implements Ljava/lang/Runnable;\n\n.field final synthetic this$0:Linner/ATestCls;\n.field final synthetic val$a:Z\n.field final synthetic val$b:Z\n.field final synthetic val$c:Z\n\n.method constructor <init>(Linner/ATestCls;ZZZ)V\n    .registers 5\n    .param p1, \"this$0\"\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"()V\"\n        }\n    .end annotation\n\n    .prologue\n    .line 15\n    iput-object p1, p0, Linner/Lambda$TestCls$1;->this$0:Linner/ATestCls;\n    iput-boolean p2, p0, Linner/Lambda$TestCls$1;->val$a:Z\n    iput-boolean p3, p0, Linner/Lambda$TestCls$1;->val$b:Z\n    iput-boolean p4, p0, Linner/Lambda$TestCls$1;->val$c:Z\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n    return-void\n.end method\n\n.method public run()V\n    .registers 4\n\n    .prologue\n    .line 18\n    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;\n    new-instance v1, Ljava/lang/StringBuilder;\n    invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V\n    iget-boolean v2, p0, Linner/Lambda$TestCls$1;->val$a:Z\n    invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Z)Ljava/lang/StringBuilder;\n    move-result-object v1\n    const-string v2, \" && \"\n    invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v1\n    iget-boolean v2, p0, Linner/Lambda$TestCls$1;->val$b:Z\n    invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Z)Ljava/lang/StringBuilder;\n    move-result-object v1\n    const-string v2, \" = \"\n    invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v1\n    iget-boolean v2, p0, Linner/Lambda$TestCls$1;->val$c:Z\n    invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Z)Ljava/lang/StringBuilder;\n    move-result-object v1\n    invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n    move-result-object v1\n    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n    .line 19\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inner/TestIncorrectAnonymousClass/TestCls$1.smali",
    "content": ".class public final Linner/TestCls$1;\n.super Ljava/lang/Object;\n\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n\n# virtual methods\n.method public invoke()V\n    .registers 2\n\n    new-instance v0, Linner/TestCls$1;\n\n    invoke-direct {v0}, Linner/TestCls$1;-><init>()V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inner/TestIncorrectAnonymousClass/TestCls.smali",
    "content": ".class public Linner/TestCls;\n.super Ljava/lang/Object;\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n.method public test()V\n    .registers 2\n\n    new-instance v0, Linner/TestCls$1;\n\n    invoke-direct {v0}, Linner/TestCls$1;-><init>()V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inner/TestInnerClassFakeSyntheticConstructor.smali",
    "content": ".class public Ljadx/tests/inner/TestCls;\n.super Ljava/lang/Object;\n\n# direct methods\n.method public synthetic constructor <init>(Ljava/lang/String;)V\n    .registers 3\n    .param p1, \"a\"    # Ljava/lang/String;\n\n    .prologue\n    const/4 v0, 0x1\n\n    invoke-direct {p0, p1, v0}, Ljadx/tests/inner/TestCls;-><init>(Ljava/lang/String;Z)V\n\n    return-void\n.end method\n\n.method public constructor <init>(Ljava/lang/String;Z)V\n    .registers 3\n    .param p1, \"a\"    # Ljava/lang/String;\n    .param p2, \"b\"    # Z\n\n    .prologue\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n.method public static build(Ljava/lang/String;)Ljadx/tests/inner/TestCls;\n    .registers 2\n    .param p0, \"str\"    # Ljava/lang/String;\n\n    .prologue\n    new-instance v0, Ljadx/tests/inner/TestCls;\n\n    invoke-direct {v0, p0}, Ljadx/tests/inner/TestCls;-><init>(Ljava/lang/String;)V\n\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inner/TestInnerClassSyntheticRename.smali",
    "content": ".class Linner/TestInnerClassSyntheticRename;\n.super Landroid/os/AsyncTask;\n\n\n# annotations\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"Landroid/os/AsyncTask<\",\n        \"Landroid/net/Uri;\",\n        \"Landroid/net/Uri;\",\n        \"Ljava/util/List<\",\n        \"Landroid/net/Uri;\",\n        \">;>;\"\n    }\n.end annotation\n\n\n# direct methods\n.method private constructor <init>(Lcom/github/skylot/testasync/MainActivity;)V\n    .locals 0\n\n    invoke-direct {p0}, Landroid/os/AsyncTask;-><init>()V\n\n    return-void\n.end method\n\n\n# virtual methods\n.method protected varargs a([Landroid/net/Uri;)Ljava/util/List;\n    .locals 1\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"([\",\n            \"Landroid/net/Uri;\",\n            \")\",\n            \"Ljava/util/List<\",\n            \"Landroid/net/Uri;\",\n            \">;\"\n        }\n    .end annotation\n\n    const-string p1, \"TestCls\"\n\n    const-string v0, \"doInBackground\"\n\n    invoke-static {p1, v0}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I\n\n    const/4 p1, 0x0\n\n    return-object p1\n.end method\n\n.method protected a(Ljava/util/List;)V\n    .locals 1\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/util/List<\",\n            \"Landroid/net/Uri;\",\n            \">;)V\"\n        }\n    .end annotation\n\n    const-string p1, \"TestCls\"\n\n    const-string v0, \"onPostExecute\"\n\n    invoke-static {p1, v0}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I\n\n    return-void\n.end method\n\n.method protected synthetic doInBackground([Ljava/lang/Object;)Ljava/lang/Object;\n    .locals 0\n\n    check-cast p1, [Landroid/net/Uri;\n\n    invoke-virtual {p0, p1}, Linner/TestInnerClassSyntheticRename;->a([Landroid/net/Uri;)Ljava/util/List;\n\n    move-result-object p1\n\n    return-object p1\n.end method\n\n.method protected synthetic onPostExecute(Ljava/lang/Object;)V\n    .locals 0\n\n    check-cast p1, Ljava/util/List;\n\n    invoke-virtual {p0, p1}, Linner/TestInnerClassSyntheticRename;->a(Ljava/util/List;)V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inner/TestNestedAnonymousClass/A.smali",
    "content": ".class public Linner/A;\n.super Ljava/lang/Object;\n\n.method public constructor <init>()V\n    .registers 1\n    .prologue\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n    return-void\n.end method\n\n.method public test()V\n    .registers 2\n\n    .prologue\n    new-instance v0, Linner/B;\n    invoke-direct {v0, p0}, Linner/B;-><init>(Linner/A;)V\n    invoke-virtual {p0, v0}, Linner/A;->use(Ljava/util/concurrent/Callable;)V\n    return-void\n.end method\n\n.method public use(Ljava/util/concurrent/Callable;)V\n    .registers 2\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/util/concurrent/Callable\",\n            \"<\",\n            \"Ljava/lang/Runnable;\",\n            \">;)V\"\n        }\n    .end annotation\n    .prologue\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inner/TestNestedAnonymousClass/B.smali",
    "content": ".class synthetic Linner/B;\n.super Ljava/lang/Object;\n.implements Ljava/util/concurrent/Callable;\n\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"Ljava/lang/Object;\",\n        \"Ljava/util/concurrent/Callable\",\n        \"<\",\n        \"Ljava/lang/Runnable;\",\n        \">;\"\n    }\n.end annotation\n\n.field final synthetic this$0:Linner/A;\n\n.method constructor <init>(Linner/A;)V\n    .registers 2\n    .prologue\n    iput-object p1, p0, Linner/B;->this$0:Linner/A;\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n    return-void\n.end method\n\n\n.method public bridge synthetic call()Ljava/lang/Object;\n    .registers 2\n    .annotation system Ldalvik/annotation/Throws;\n        value = {\n            Ljava/lang/Exception;\n        }\n    .end annotation\n\n    .prologue\n    invoke-virtual {p0}, Linner/B;->call()Ljava/lang/Runnable;\n    move-result-object v0\n    return-object v0\n.end method\n\n.method public call()Ljava/lang/Runnable;\n    .registers 2\n    .prologue\n    new-instance v0, Linner/C;\n    invoke-direct {v0, p0}, Linner/C;-><init>(Linner/B;)V\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inner/TestNestedAnonymousClass/C.smali",
    "content": ".class synthetic Linner/C;\n.super Ljava/lang/Object;\n\n.implements Ljava/lang/Runnable;\n\n.field final synthetic this$1:Linner/B;\n\n.method constructor <init>(Linner/B;)V\n    .registers 2\n    .prologue\n    iput-object p1, p0, Linner/C;->this$1:Linner/B;\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n    return-void\n.end method\n\n.method public run()V\n    .registers 3\n    .prologue\n    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;\n    const-string v1, \"run\"\n    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inner/TestSyntheticMthRename/TestCls$A.smali",
    "content": ".class public final Linner/TestCls$A;\n.super Ljava/lang/Object;\n.source \"TestCls.java\"\n\n# interfaces\n.implements Linner/TestCls$I;\n\n\n# annotations\n.annotation system Ldalvik/annotation/EnclosingClass;\n    value = Linner/TestCls;\n.end annotation\n\n.annotation system Ldalvik/annotation/InnerClass;\n    accessFlags = 0x19\n    name = \"A\"\n.end annotation\n\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"Ljava/lang/Object;\",\n        \"Linner/TestCls$I\",\n        \"<\",\n        \"Ljava/lang/String;\",\n        \"Ljava/lang/Runnable;\",\n        \">;\"\n    }\n.end annotation\n\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    .prologue\n    .line 9\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n.method private varargs renamedCall([Ljava/lang/Runnable;)Ljava/lang/String;\n    .registers 3\n    .param p1, \"p\"    # [Ljava/lang/Runnable;\n\n    .prologue\n    .line 12\n    const-string v0, \"str\"\n\n    return-object v0\n.end method\n\n# virtual methods\n.method public synthetic call([Ljava/lang/Object;)Ljava/lang/Object;\n    .registers 3\n\n    .prologue\n    .line 9\n    check-cast p1, [Ljava/lang/Runnable;\n\n    invoke-virtual {p0, p1}, Linner/TestCls$A;->renamedCall([Ljava/lang/Runnable;)Ljava/lang/String;\n\n    move-result-object v0\n\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inner/TestSyntheticMthRename/TestCls$I.smali",
    "content": ".class public interface abstract Linner/TestCls$I;\n.super Ljava/lang/Object;\n.source \"TestCls.java\"\n\n\n# annotations\n.annotation system Ldalvik/annotation/EnclosingClass;\n    value = Linner/TestCls;\n.end annotation\n\n.annotation system Ldalvik/annotation/InnerClass;\n    accessFlags = 0x609\n    name = \"I\"\n.end annotation\n\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"<R:\",\n        \"Ljava/lang/Object;\",\n        \"P:\",\n        \"Ljava/lang/Object;\",\n        \">\",\n        \"Ljava/lang/Object;\"\n    }\n.end annotation\n\n\n# virtual methods\n.method public varargs abstract call([Ljava/lang/Object;)Ljava/lang/Object;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"([TP;)TR;\"\n        }\n    .end annotation\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/inner/TestSyntheticMthRename/TestCls.smali",
    "content": ".class public Linner/TestCls;\n.super Ljava/lang/Object;\n.source \"TestCls.java\"\n\n\n# annotations\n.annotation system Ldalvik/annotation/MemberClasses;\n    value = {\n        Linner/TestCls$A;,\n        Linner/TestCls$I;\n    }\n.end annotation\n\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    .prologue\n    .line 3\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/invoke/TestCastInOverloadedInvoke2.smali",
    "content": ".class public Linvoke/TestCastInOverloadedInvoke2;\n.super Ljava/lang/Object;\n\n.method public test()V\n    .locals 3\n\n    new-instance v0, Landroid/content/Intent;\n\n    invoke-direct {v0}, Landroid/content/Intent;-><init>()V\n\n    const-string v1, \"param\"\n\n    const/4 v2, 0x0\n\n    invoke-virtual {v0, v1, v2}, Landroid/content/Intent;->putExtra(Ljava/lang/String;Landroid/os/Parcelable;)Landroid/content/Intent;\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/invoke/TestConstructorWithMoves.smali",
    "content": ".class public Linvoke/TestConstructorWithMoves;\n.super Ljava/lang/Object;\n\n.method static public test()Z\n    .registers 11\n\n    new-instance v5, Ljava/lang/Boolean;\n    move-object v8, v5\n    move-object v5, v8\n    move-object v6, v8\n    const-string v7, \"test\"\n    invoke-direct {v6, v7}, Ljava/lang/Boolean;-><init>(Ljava/lang/String;)V\n    check-cast v5, Ljava/lang/Boolean;\n    invoke-virtual {v5}, Ljava/lang/Boolean;->booleanValue()Z\n    move-result v5\n    move v3, v5\n    return v3\n.end method\n\n"
  },
  {
    "path": "jadx-core/src/test/smali/invoke/TestPolymorphicInvoke.smali",
    "content": ".class public Linvoke/TestPolymorphicInvoke;\n.super Ljava/lang/Object;\n\n.method public func(II)Ljava/lang/String;\n    .registers 4\n    .param p1, \"a\"    # I\n    .param p2, \"c\"    # I\n\n    .line 23\n    add-int v0, p1, p2\n    invoke-static {v0}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;\n    move-result-object v0\n    return-object v0\n.end method\n\n.method public test()V\n    .registers 7\n\n    .line 32\n    :try_start_0\n    invoke-static {}, Ljava/lang/invoke/MethodHandles;->lookup()Ljava/lang/invoke/MethodHandles$Lookup;\n    move-result-object v0\n\n    .line 33\n    .local v0, \"lookup\":Ljava/lang/invoke/MethodHandles$Lookup;\n    const-class v1, Ljava/lang/String;\n    sget-object v2, Ljava/lang/Integer;->TYPE:Ljava/lang/Class;\n    const/4 v3, 0x1\n    new-array v3, v3, [Ljava/lang/Class;\n    const/4 v4, 0x0\n    sget-object v5, Ljava/lang/Integer;->TYPE:Ljava/lang/Class;\n    aput-object v5, v3, v4\n    invoke-static {v1, v2, v3}, Ljava/lang/invoke/MethodType;->methodType(Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;\n    move-result-object v1\n\n    .line 34\n    .local v1, \"methodType\":Ljava/lang/invoke/MethodType;\n    const-class v2, Linvoke/TestPolymorphicInvoke;\n    const-string v3, \"func\"\n    invoke-virtual {v0, v2, v3, v1}, Ljava/lang/invoke/MethodHandles$Lookup;->findVirtual(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;\n    move-result-object v2\n\n    .line 35\n    .local v2, \"methodHandle\":Ljava/lang/invoke/MethodHandle;\n    const/16 v3, 0xa\n    const/16 v4, 0x14\n\n    invoke-polymorphic {v2, p0, v3, v4}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Linvoke/TestPolymorphicInvoke;II)Ljava/lang/String;\n\tmove-result-object v3\n\n    .line 36\n    .local v3, \"ret\":Ljava/lang/String;\n    sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;\n    invoke-virtual {v4, v3}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n    :try_end_2a\n    .catchall {:try_start_0 .. :try_end_2a} :catchall_2b\n\n    .line 39\n    .end local v0    # \"lookup\":Ljava/lang/invoke/MethodHandles$Lookup;\n    .end local v1    # \"methodType\":Ljava/lang/invoke/MethodType;\n    .end local v2    # \"methodHandle\":Ljava/lang/invoke/MethodHandle;\n    .end local v3    # \"ret\":Ljava/lang/String;\n    goto :goto_2f\n\n    .line 37\n    :catchall_2b\n    move-exception v0\n\n    .line 38\n    .local v0, \"e\":Ljava/lang/Throwable;\n    invoke-virtual {v0}, Ljava/lang/Throwable;->printStackTrace()V\n\n    .line 40\n    .end local v0    # \"e\":Ljava/lang/Throwable;\n    :goto_2f\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/invoke/TestRawCustomInvoke.smali",
    "content": ".class public Linvoke/TestRawCustomInvoke;\n.super Ljava/lang/Object;\n\n.method public static func(ID)Ljava/lang/String;\n    .registers 5\n    int-to-double v0, p0\n    add-double/2addr v0, p1\n    invoke-static {v0, v1}, Ljava/lang/String;->valueOf(D)Ljava/lang/String;\n    move-result-object p0\n    return-object p0\n.end method\n\n.method private static staticBootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;\n    .registers 5\n    :try_start_0\n    new-instance v0, Ljava/lang/invoke/ConstantCallSite;\n    invoke-virtual {p0}, Ljava/lang/invoke/MethodHandles$Lookup;->lookupClass()Ljava/lang/Class;\n    move-result-object v1\n    invoke-virtual {p0, v1, p1, p2}, Ljava/lang/invoke/MethodHandles$Lookup;->findStatic(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;\n    move-result-object p0\n    invoke-direct {v0, p0}, Ljava/lang/invoke/ConstantCallSite;-><init>(Ljava/lang/invoke/MethodHandle;)V\n    :try_end_d\n    .catch Ljava/lang/NoSuchMethodException; {:try_start_0 .. :try_end_d} :catch_e\n    .catch Ljava/lang/IllegalAccessException; {:try_start_0 .. :try_end_d} :catch_e\n    return-object v0\n    :catch_e\n    move-exception p0\n    new-instance p1, Ljava/lang/RuntimeException;\n    invoke-direct {p1, p0}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/Throwable;)V\n    throw p1\n.end method\n\n.method public test()Ljava/lang/String;\n    .registers 3\n    :try_start_0\n\n    const/4 v0, 0x1\n    const-wide/high16 v1, 0x4000000000000000L    # 2.0\n    invoke-custom {v0, v1}, call_site_0(\"func\", (ID)Ljava/lang/String;)@Linvoke/TestRawCustomInvoke;->staticBootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;\n\n    move-result-object v0\n\n    :try_end_25\n    .catchall {:try_start_0 .. :try_end_25} :catchall_26\n    return-object v0\n    :catchall_26\n    move-exception v0\n    invoke-static {v0}, Lorg/junit/jupiter/api/Assertions;->fail(Ljava/lang/Throwable;)Ljava/lang/Object;\n    const/4 v0, 0x0\n    return-object v0\n.end method\n\n.method public check()V\n    .registers 3\n    invoke-virtual {p0}, Linvoke/TestRawCustomInvoke;->test()Ljava/lang/String;\n    move-result-object v0\n    invoke-static {v0}, Ljadx/tests/api/utils/assertj/JadxAssertions;->assertThat(Ljava/lang/String;)Ljadx/tests/api/utils/assertj/JadxCodeAssertions;\n    move-result-object v0\n    const-string v1, \"3.0\"\n    invoke-virtual {v0, v1}, Ljadx/tests/api/utils/assertj/JadxCodeAssertions;->isEqualTo(Ljava/lang/String;)Lorg/assertj/core/api/AbstractStringAssert;\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/loops/TestBreakInLoop6.smali",
    "content": ".class public Lloops/TestBreakInLoop6;\n.super Ljava/lang/Object;\n\n\n.method public test()J\n    .registers 19\n    move-object/from16 v0, p0\n    const-wide v1, 0x1L\n    iget-object v3, v0, Ltest/Test;->j:[Ltest/Test;\n    array-length v4, v3\n    const/4 v6, 0x0\n    :goto_c\n    if-ge v6, v4, :cond_64\n\n    aget-object v7, v3, v6\n    invoke-interface {v7}, Ltest/Test;->h()J\n\n    move-result-wide v8\n    const-wide v11, 0x2L\n\n    cmp-long v13, v8, v11\n\n    if-eqz v13, :cond_4e\n    cmp-long v13, v1, v11\n\n    if-nez v13, :cond_4e\n    move-wide v1, v8\n    iget-object v11, v0, Ltest/Test;->i:[Ltest/Test;\n\n    array-length v12, v11\n\n    const/4 v13, 0x0\n\n    :goto_28\n    if-ge v13, v12, :cond_4e\n\n    aget-object v14, v11, v13\n    if-ne v14, v7, :cond_2f\n    goto :cond_4e\n    :cond_2f\n    invoke-interface {v14, v1, v2}, Ltest/Test;->f(J)J\n    move-result-wide v15\n    cmp-long v17, v15, v1\n    if-nez v17, :cond_3a\n    add-int/lit8 v13, v13, 0x1\n    goto :goto_28\n\n    :cond_3a\n    new-instance v3, Ljava/lang/IllegalStateException;\n    invoke-direct {v3}, Ljava/lang/IllegalStateException;-><init>()V\n    throw v3\n\n    :cond_4e\n    add-int/lit8 v6, v6, 0x1\n    goto :goto_c\n\n    :cond_64\n    return-wide v1\n.end method"
  },
  {
    "path": "jadx-core/src/test/smali/loops/TestEndlessLoop2.smali",
    "content": ".class Lloops/TestEndlessLoop2;\n.super Ljava/lang/Object;\n\n.field instanceCount:J\n\n.method test([Ljava/lang/String;)V\n    .registers 10\n\n    const/16 p1, 0xb\n    invoke-virtual {p0, p1}, Lloops/TestEndlessLoop2;->vMeth(I)V\n    const/16 v0, 0xf1\n    const-wide/high16 v1, 0x4032000000000000L    # 18.0\n\n    :goto_a\n    const-wide/high16 v3, 0x4076000000000000L    # 352.0\n    const/4 v5, 0x1\n    cmpg-double v6, v1, v3\n    if-gez v6, :cond_1c\n    const/4 v0, 0x1\n\n    :goto_12\n    add-int/2addr v0, v5\n    const/16 v3, 0x4b\n    if-ge v0, v3, :cond_18\n    goto :goto_12\n\n    :cond_18\n    const-wide/high16 v3, 0x3ff0000000000000L    # 1.0\n    add-double/2addr v1, v3\n    goto :goto_a\n\n    :cond_1c\n    iget-wide v3, p0, Lloops/TestEndlessLoop2;->instanceCount:J\n    long-to-int v4, v3\n    const/16 v3, 0xb\n\n    :goto_21\n    const/16 v6, 0xf3\n\n    if-ge v5, v6, :cond_41\n    rem-int/lit8 v6, v5, 0x9\n    add-int/lit8 v6, v6, 0x12\n    if-eq v6, p1, :cond_3e\n    const/16 v7, 0x15\n    if-eq v6, v7, :cond_36\n    const/16 v7, 0x16\n    if-eq v6, v7, :cond_34\n    goto :goto_3b\n\n\n    :cond_34\n    add-int/2addr v4, v0\n    goto :goto_3b\n\n    :cond_36\n    const v6, 0xeed9\n    div-int/2addr v3, v6\n    nop\n\n    :goto_3b\n    add-int/lit8 v5, v5, 0x1\n    goto :goto_21\n\n    :cond_3e\n    nop\n\n    :goto_3f\n    nop\n\t# endless loop with empty body\n    goto :goto_3f\n\n    :cond_41\n    sget-object p1, Ljava/lang/System;->out:Ljava/io/PrintStream;\n    invoke-static {v1, v2}, Ljava/lang/Double;->doubleToLongBits(D)J\n    move-result-wide v0\n    new-instance v2, Ljava/lang/StringBuilder;\n    invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V\n    const-string v5, \"i21 d2 i22 = \"\n    invoke-virtual {v2, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;\n    const-string v3, \",\"\n    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    invoke-virtual {v2, v0, v1}, Ljava/lang/StringBuilder;->append(J)Ljava/lang/StringBuilder;\n    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    invoke-virtual {v2, v4}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;\n    invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n    move-result-object v0\n    invoke-virtual {p1, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/loops/TestIfInLoop4.smali",
    "content": ".class public LTestIfInLoop4;\n.super Ljava/lang/Object;\n\n.method public test()Z\n    .registers 5\n\n    move-object/from16 v0, p0\n\n    const/4 v2, 0x0\n    const/4 v3, 0x1\n\n    :goto_0\n    iget v1, v0, LTestIfInLoop4;->x:I\n\n    if-ge v2, v1, :goto_1\n    if-gtz v2, :cond\n    if-gez v2, :cond\n\n    if-ltz v2, :goto_1\n    if-ge v2, v1, :goto_1\n\n    goto :goto_1\n\n    :cond\n    goto :goto_0\n\n    :goto_1\n    return v3\n.end method\n\n"
  },
  {
    "path": "jadx-core/src/test/smali/loops/TestLoopCondition5.smali",
    "content": ".class public LTestLoopCondition5;\n.super Ljava/lang/Object;\n.source \"TestLoopCondition5.java\"\n\n.method private static lastIndexOf([IIII)I\n    .locals 1\n\n    add-int/lit8 p3, p3, -0x1\n\n    :goto_0\n    const/4 v0, -0x1\n\n    if-lt p3, p2, :cond_1\n\n    .line 219\n    aget v0, p0, p3\n\n    if-ne v0, p1, :cond_0\n\n    return p3\n\n    :cond_0\n    add-int/lit8 p3, p3, -0x1\n\n    goto :goto_0\n\n    :cond_1\n    move p3, v0\n\n    return p3\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/loops/TestLoopRestore.smali",
    "content": ".class public Lloops/TestLoopRestore;\n.super Ljava/lang/Object;\n.source \"SourceFile.java\"\n\n.method private test([B)Ljava/lang/String;\n    .registers 10\n\n    const/16 v0, 0x10\n    new-array v0, v0, [C\n    fill-array-data v0, :array_3c\n\n    :try_start_7\n    const-string v1, \"MD5\"\n    invoke-static {v1}, Ljava/security/MessageDigest;->getInstance(Ljava/lang/String;)Ljava/security/MessageDigest;\n    move-result-object v1\n\n    invoke-virtual {v1, p1}, Ljava/security/MessageDigest;->update([B)V\n    invoke-virtual {v1}, Ljava/security/MessageDigest;->digest()[B\n    move-result-object p1\n\n    array-length v1, p1\n    mul-int/lit8 v2, v1, 0x2\n    new-array v2, v2, [C\n    :try_end_19\n    .catch Ljava/lang/Exception; {:try_start_7 .. :try_end_19} :catch_3a\n\n    const/4 v3, 0x0\n    const/4 v4, 0x0\n\n    :goto_1b\n    if-ge v3, v1, :cond_34\n\n    aget-byte v5, p1, v3\n    add-int/lit8 v6, v4, 0x1\n    ushr-int/lit8 v7, v5, 0x4\n    and-int/lit8 v7, v7, 0xf\n    aget-char v7, v0, v7\n    aput-char v7, v2, v4\n    add-int/lit8 v4, v6, 0x1\n    and-int/lit8 v5, v5, 0xf\n    aget-char v5, v0, v5\n    aput-char v5, v2, v6\n    add-int/lit8 v3, v3, 0x1\n    goto :goto_1b\n\n    :cond_34\n    new-instance p1, Ljava/lang/String;\n    invoke-direct {p1, v2}, Ljava/lang/String;-><init>([C)V\n    return-object p1\n\n    :catch_3a\n    const/4 p1, 0x0\n    return-object p1\n\n    :array_3c\n    .array-data 2\n        0x30s\n        0x31s\n        0x32s\n        0x33s\n        0x34s\n        0x35s\n        0x36s\n        0x37s\n        0x38s\n        0x39s\n        0x61s\n        0x62s\n        0x63s\n        0x64s\n        0x65s\n        0x66s\n    .end array-data\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/loops/TestLoopRestore3.smali",
    "content": ".class public Lloops/TestLoopRestore3;\n.super Ljava/lang/Object;\n\n.method public final b(Ljava/lang/String;Lb/U53$b;)V\n    .registers 8\n\n    iget-object v0, p0, Lb/X53;->e:Ljava/util/concurrent/atomic/AtomicReference;\n    :goto_2\n    invoke-virtual {v0}, Ljava/util/concurrent/atomic/AtomicReference;->get()Ljava/lang/Object;\n    move-result-object v1\n    move-object v2, v1\n    check-cast v2, Ljava/util/List;\n    move-object v3, v2\n    check-cast v3, Ljava/lang/Iterable;\n    instance-of v4, v3, Ljava/util/Collection;\n    if-eqz v4, :cond_1a\n\n    move-object v4, v3\n    check-cast v4, Ljava/util/Collection;\n    invoke-interface {v4}, Ljava/util/Collection;->isEmpty()Z\n    move-result v4\n    if-eqz v4, :cond_1a\n    goto :goto_33\n\n    :cond_1a\n    invoke-interface {v3}, Ljava/lang/Iterable;->iterator()Ljava/util/Iterator;\n    move-result-object v3\n\n    :cond_1e\n    invoke-interface {v3}, Ljava/util/Iterator;->hasNext()Z\n    move-result v4\n    if-eqz v4, :cond_33\n\n    invoke-interface {v3}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n    move-result-object v4\n    check-cast v4, Lb/X53$c;\n    iget-object v4, v4, Lb/X53$c;->b:Ljava/lang/String;\n    invoke-static {v4, p1}, Lkotlin/jvm/internal/Intrinsics;->a(Ljava/lang/Object;Ljava/lang/Object;)Z\n    move-result v4\n    if-eqz v4, :cond_1e\n    goto :goto_40\n\n    :cond_33\n    :goto_33\n    check-cast v2, Ljava/util/Collection;\n    new-instance v3, Lb/X53$c;\n    sget-object v4, Lb/Pd2;->a:Lb/Pd2;\n    invoke-direct {v3, p2, p1, v4}, Lb/X53$c;-><init>(Lb/U53$b;Ljava/lang/String;Ljava/util/List;)V\n    invoke-static {v2, v3}, Lb/R31;->a0(Ljava/util/Collection;Ljava/lang/Object;)Ljava/util/ArrayList;\n    move-result-object v2\n\n    :cond_40\n    :goto_40\n    invoke-virtual {v0, v1, v2}, Ljava/util/concurrent/atomic/AtomicReference;->compareAndSet(Ljava/lang/Object;Ljava/lang/Object;)Z\n    move-result v3\n    if-eqz v3, :cond_47\n    return-void\n\n    :cond_47\n    invoke-virtual {v0}, Ljava/util/concurrent/atomic/AtomicReference;->get()Ljava/lang/Object;\n    move-result-object v3\n    if-eq v3, v1, :cond_40\n    goto :goto_2\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/loops/TestMultiEntryLoop.smali",
    "content": ".class public Lloops/TestMultiEntryLoop;\n.super Ljava/lang/Object;\n\n.field private static arr:[B\n\n.method private static test(III)Ljava/lang/String;\n    .registers 9\n\n    mul-int/lit8 p1, p1, 0x2\n\n    rsub-int/lit8 p1, p1, 0x6f\n\n    mul-int/lit8 p0, p0, 0x2\n\n    add-int/lit8 p0, p0, 0x1c\n\n    mul-int/lit8 p2, p2, 0x2\n\n    add-int/lit8 p2, p2, 0x4\n\n    new-instance v0, Ljava/lang/String;\n\n    const/4 v5, -0x1\n\n    sget-object v4, Lloops/TestMultiEntryLoop;->arr:[B\n\n    new-array v1, p0, [B\n\n    add-int/lit8 p0, p0, -0x1\n\n    if-nez v4, :cond_1e\n\n    move v2, p1\n\n    move v3, p2\n\n    :goto_19\n    add-int/2addr v2, v3\n\n    add-int/lit8 p1, v2, -0x8\n\n    add-int/lit8 p2, p2, 0x1\n\n    :cond_1e\n    add-int/lit8 v5, v5, 0x1\n\n    int-to-byte v2, p1\n\n    aput-byte v2, v1, v5\n\n    if-ne v5, p0, :cond_2a\n\n    invoke-direct {v0, v1}, Ljava/lang/String;-><init>([B)V\n\n    return-object v0\n\n    :cond_2a\n    move v2, p1\n\n    aget-byte v3, v4, p2\n\n    goto :goto_19\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/loops/TestMultiEntryLoop2.smali",
    "content": ".class public Lloops/TestMultiEntryLoop2;\n.super Ljava/lang/Object;\n\n.field public list:Ljava/util/List;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ljava/util/List<\",\n            \"Ljava/lang/String;\",\n            \">;\"\n        }\n    .end annotation\n.end field\n\n.method private test(Ljava/lang/String;II)V\n    .registers 14\n\n    if-ne p2, p3, :cond_3\n    return-void\n\n    :cond_3\n    invoke-virtual {p1, p2}, Ljava/lang/String;->charAt(I)C\n\n    move-result v0\n    const/16 v1, 0x2f\n    const-string v2, \"\"\n    const/4 v3, 0x1\n\n    if-eq v0, v1, :cond_1e\n    const/16 v1, 0x5c\n    if-ne v0, v1, :cond_13\n\n    goto :goto_1e\n\n    :cond_13\n    iget-object v0, p0, Lloops/TestMultiEntryLoop2;->list:Ljava/util/List;\n    invoke-interface {v0}, Ljava/util/List;->size()I\n    move-result v1\n    sub-int/2addr v1, v3\n    invoke-interface {v0, v1, v2}, Ljava/util/List;->set(ILjava/lang/Object;)Ljava/lang/Object;\n    goto :goto_29\n\n    :cond_1e\n    :goto_1e\n    iget-object v0, p0, Lloops/TestMultiEntryLoop2;->list:Ljava/util/List;\n    invoke-interface {v0}, Ljava/util/List;->clear()V\n    .line 4\n    iget-object v0, p0, Lloops/TestMultiEntryLoop2;->list:Ljava/util/List;\n    invoke-interface {v0, v2}, Ljava/util/List;->add(Ljava/lang/Object;)Z\n    goto :goto_41\n\n    :cond_29\n    :goto_29\n    move v6, p2\n    if-ge v6, p3, :cond_44\n    const-string p2, \"/\\\\\"\n    .line 5\n    invoke-static {p1, v6, p3, p2}, Lloops/TestMultiEntryLoop2;->delimiterOffset(Ljava/lang/String;IILjava/lang/String;)I\n    move-result p2\n    if-ge p2, p3, :cond_36\n    move v0, v3\n    goto :goto_37\n\n    :cond_36\n    const/4 v0, 0x0\n    :goto_37\n    const/4 v9, 0x1\n    move-object v4, p0\n    move-object v5, p1\n    move v7, p2\n    move v8, v0\n    .line 6\n    invoke-direct/range {v4 .. v9}, Lloops/TestMultiEntryLoop2;->push(Ljava/lang/String;IIZZ)V\n    if-eqz v0, :cond_29\n\n    :goto_41\n    add-int/lit8 p2, p2, 0x1\n    goto :goto_29\n\n    :cond_44\n    return-void\n.end method\n\n.method private push(Ljava/lang/String;IIZZ)V\n    .locals 0\n    return-void\n.end method\n\n.method private delimiterOffset(Ljava/lang/String;IILjava/lang/String;)I\n    .locals 1\n    const/4 v0, 0x0\n    return v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/names/TestCaseSensitiveChecks/1.smali",
    "content": ".class public LA;\n.super Ljava/lang/Object;\n"
  },
  {
    "path": "jadx-core/src/test/smali/names/TestCaseSensitiveChecks/2.smali",
    "content": ".class public La;\n.super Ljava/lang/Object;\n"
  },
  {
    "path": "jadx-core/src/test/smali/names/TestClassNameWithInvalidChar/a.smali",
    "content": ".class public Ldo-;\n.super Ljava/lang/Object;\n"
  },
  {
    "path": "jadx-core/src/test/smali/names/TestClassNameWithInvalidChar/b.smali",
    "content": ".class public Li-f;\n.super Ljava/lang/Object;\n"
  },
  {
    "path": "jadx-core/src/test/smali/names/TestDefPkgRename/a.smali",
    "content": ".class public LA;\n.super Ljava/lang/Object;\n"
  },
  {
    "path": "jadx-core/src/test/smali/names/TestDefPkgRename/b.smali",
    "content": ".class public Lpkg/B;\n.super Ljava/lang/Object;\n\n.field public a:LA;\n"
  },
  {
    "path": "jadx-core/src/test/smali/names/TestDuplicatedNames.smali",
    "content": ".class public LTestDuplicatedNames;\n.super Ljava/lang/Object;\n.source \"TestDuplicatedNames.java\"\n\n\n# instance fields\n.field public fieldName:Ljava/lang/String;\n.field public fieldName:Ljava/lang/Object;\n\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    .prologue\n    .line 3\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n\n# virtual methods\n.method public run()Ljava/lang/String;\n    .registers 2\n\n    .prologue\n    iget-object v0, p0, LTestDuplicatedNames;->fieldName:Ljava/lang/String;\n\n    return-object v0\n.end method\n\n.method public run()Ljava/lang/Object;\n    .registers 2\n\n    .prologue\n    iget-object v0, p0, LTestDuplicatedNames;->fieldName:Ljava/lang/Object;\n\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/names/TestFieldCollideWithPackage/1.smali",
    "content": ".class public Lfirst/A;\n.super Ljava/lang/Object;\n\n.field public first:Lfirst/A;\n.field public second:Lsecond/A;\n\n.method public test()Ljava/lang/String;\n    .registers 2\n\n    invoke-static {}, Lsecond/A;->call()Ljava/lang/String;\n\n    move-result-object v0\n\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/names/TestFieldCollideWithPackage/2.smali",
    "content": ".class public Lsecond/A;\n.super Ljava/lang/Object;\n\n.method static public call()Ljava/lang/String;\n    .registers 1\n\n    const v0, 0\n\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/names/TestLocalVarCollideWithPackage/1.smali",
    "content": ".class public Lfirst/A;\n.super Ljava/lang/Object;\n\n.method public test()Ljava/lang/String;\n    .registers 2\n\n    new-instance v1, Lpkg/Second;\n\n    invoke-direct {v1}, Lpkg/Second;-><init>()V\n\n    .local v1, \"second\":Lpkg/Second;\n\n    invoke-static {}, Lsecond/A;->call()Ljava/lang/String;\n\n    iget-object v0, v1, Lpkg/Second;->str:Ljava/lang/String;\n\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/names/TestLocalVarCollideWithPackage/2.smali",
    "content": ".class public Lsecond/A;\n.super Ljava/lang/Object;\n\n.method static public call()Ljava/lang/String;\n    .registers 1\n\n    const v0, 0\n\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/names/TestLocalVarCollideWithPackage/3.smali",
    "content": ".class public Lpkg/Second;\n.super Ljava/lang/Object;\n\n.field public str:Ljava/lang/String;\n"
  },
  {
    "path": "jadx-core/src/test/smali/names/TestReservedClassNames.smali",
    "content": ".class public Ldo;\n.super Ljava/lang/Object;\n\n# direct methods\n.method public constructor <init>()V\n    .locals 0\n\n    .line 3\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/names/TestReservedNames.smali",
    "content": ".class public Lnames/TestReservedNames;\n.super Ljava/lang/Object;\n.source \"TestReservedNames.java\"\n\n\n# instance fields\n.field public do:Ljava/lang/String; # reserved name\n.field public 0f:Ljava/lang/String; # invalid identifier\n\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    .prologue\n    .line 3\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n\n# virtual methods\n.method public try()Ljava/lang/String;\n    .registers 2\n\n    .prologue\n    .line 8\n    iget-object v0, p0, Lnames/TestReservedNames;->do:Ljava/lang/String;\n\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/names/TestReservedPackageNames/a.smali",
    "content": ".class public Ldo/if/A;\n.super Ljava/lang/Object;\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestAllNops.smali",
    "content": ".class public Lothers/TestAllNops;\n.super Ljava/lang/Object;\n\n.method public constructor <init>()V\n    .registers 1\n\n    .line 55\n    nop\n\n    nop\n\n    nop\n\n    nop\n.end method\n\n.method private test()Z\n    .registers 11\n\n    .line 1480\n    nop\n\n    nop\n\n    .line 1481\n    nop\n\n    nop\n\n    nop\n\n    nop\n\n    nop\n\n    nop\n\n    .line 1485\n    nop\n\n    nop\n\n    .line 1486\n    nop\n\n    nop\n\n    .line 1487\n    nop\n\n.end method\n\n.method private testWithTryCatch()Z\n    .registers 11\n\n    .line 1480\n    :try_start_0\n    nop\n    nop\n\n    .line 1481\n    nop\n\n    nop\n\n    nop\n\n    nop\n\n    nop\n\n    nop\n\n    .line 1485\n    nop\n\n    nop\n\n    :try_end_35\n    .catch Ljava/security/NoSuchAlgorithmException; {:try_start_0 .. :try_end_35} :catch_36\n\n    nop\n\n    .line 1547\n    :catch_36\n\n    nop\n\n    .line 1487\n    nop\n\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestBadClassAccessModifiers/A.smali",
    "content": ".class public Lothers/A;\n.super Ljava/lang/Object;\n.source \"A.java\"\n\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    .line 3\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n\n# virtual methods\n.method public call()V\n    .registers 1\n\n    .line 5\n    invoke-static {}, Lothers/B$BB$BBB;->test()V\n\n    .line 6\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestBadClassAccessModifiers/B$BB$BBB.smali",
    "content": ".class public Lothers/B$BB$BBB;\n.super Ljava/lang/Object;\n.source \"B.java\"\n\n\n# annotations\n.annotation system Ldalvik/annotation/EnclosingClass;\n    value = Lothers/B$BB;\n.end annotation\n\n.annotation system Ldalvik/annotation/InnerClass;\n    accessFlags = 0x9\n    name = \"BBB\"\n.end annotation\n\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    .line 12\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n.method public static test()V\n    .registers 0\n\n    .line 14\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestBadClassAccessModifiers/B$BB.smali",
    "content": ".class public Lothers/B$BB;\n.super Ljava/lang/Object;\n.source \"B.java\"\n\n\n# annotations\n.annotation system Ldalvik/annotation/EnclosingClass;\n    value = Lothers/B;\n.end annotation\n\n.annotation system Ldalvik/annotation/InnerClass;\n    accessFlags = 0xa\n    name = \"BB\"\n.end annotation\n\n.annotation system Ldalvik/annotation/MemberClasses;\n    value = {\n        Lothers/B$BB$BBB;\n    }\n.end annotation\n\n\n# direct methods\n.method private constructor <init>()V\n    .registers 1\n\n    .line 11\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestBadClassAccessModifiers/B.smali",
    "content": ".class public Lothers/B;\n.super Ljava/lang/Object;\n.source \"B.java\"\n\n\n# annotations\n.annotation system Ldalvik/annotation/MemberClasses;\n    value = {\n        Lothers/B$BB;,\n        Lothers/B$BBpr;\n    }\n.end annotation\n\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    .line 3\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestBadMethodAccessModifiers/TestCls$A.smali",
    "content": ".class public abstract Lothers/TestCls$A;\n.super Ljava/lang/Object;\n.source \"TestCls.java\"\n\n\n# annotations\n.annotation system Ldalvik/annotation/EnclosingClass;\n    value = Lothers/TestCls;\n.end annotation\n\n.annotation system Ldalvik/annotation/InnerClass;\n    accessFlags = 0x401\n    name = \"A\"\n.end annotation\n\n\n# instance fields\n.field final synthetic this$0:Lothers/TestCls;\n\n\n# direct methods\n.method public constructor <init>(Lothers/TestCls;)V\n    .registers 2\n    .param p1, \"this$0\"    # Lothers/TestCls;\n\n    .prologue\n    .line 5\n    iput-object p1, p0, Lothers/TestCls$A;->this$0:Lothers/TestCls;\n\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n\n# virtual methods\n.method public abstract test()V\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestBadMethodAccessModifiers/TestCls$B.smali",
    "content": ".class public Lothers/TestCls$B;\n.super Lothers/TestCls$A;\n.source \"TestCls.java\"\n\n\n# annotations\n.annotation system Ldalvik/annotation/EnclosingClass;\n    value = Lothers/TestCls;\n.end annotation\n\n.annotation system Ldalvik/annotation/InnerClass;\n    accessFlags = 0x1\n    name = \"B\"\n.end annotation\n\n\n# instance fields\n.field final synthetic this$0:Lothers/TestCls;\n\n\n# direct methods\n.method public constructor <init>(Lothers/TestCls;)V\n    .registers 2\n    .param p1, \"this$0\"    # Lothers/TestCls;\n\n    .prologue\n    .line 9\n    iput-object p1, p0, Lothers/TestCls$B;->this$0:Lothers/TestCls;\n\n    invoke-direct {p0, p1}, Lothers/TestCls$A;-><init>(Lothers/TestCls;)V\n\n    return-void\n.end method\n\n\n# virtual methods\n.method protected test()V\n    .registers 1\n\n    .prologue\n    .line 11\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestBadMethodAccessModifiers/TestCls.smali",
    "content": ".class public Lothers/TestCls;\n.super Ljava/lang/Object;\n.source \"TestCls.java\"\n\n\n# annotations\n.annotation system Ldalvik/annotation/MemberClasses;\n    value = {\n        Lothers/TestCls$B;,\n        Lothers/TestCls$A;\n    }\n.end annotation\n\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    .prologue\n    .line 3\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestConstructor.smali",
    "content": ".class public Lothers/TestConstructor;\n.super Ljava/lang/Object;\n\n.method private test(DDLSomeObject;)LSomeObject;\n    .locals 22\n    .param p1, \"arg1\"    # D\n    .param p3, \"arg2\"    # D\n    .param p5, \"arg3\"    # LSomeObject;\n\n    .prologue\n    .line 54\n\n    new-instance v17, LSomeObject;\n\n    move-object/from16 v0, v17\n\n    move-object/from16 v1, p5\n\n    invoke-direct {v0, v1}, LSomeObject;-><init>(LSomeObject;)V\n\n    .line 59\n    .local v17, \"localSomeObject\":LSomeObject;\n\n    return-object v17\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestConstructor2/A.smali",
    "content": ".class public final Lothers/TestConstructor2$A;\n.super Ljava/lang/Object;\n\n.field public a:I\n.field public b:Ljava/util/ArrayDeque;\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestConstructor2/TestConstructor2.smali",
    "content": ".class public Lothers/TestConstructor2;\n.super Ljava/lang/Object;\n\n.field public a:Ljava/util/HashMap;\n\n.method public final a(III)V\n    .registers 6\n\n    .line 1\n    .line 2\n    new-instance v0, Lothers/TestConstructor2$A;\n\n    .line 3\n    .line 4\n    .line 5\n    invoke-direct {v0}, Ljava/lang/Object;-><init>()V\n\n    .line 6\n    .line 7\n    if-gt p2, p3, :cond_1c\n\n    .line 8\n    .line 9\n    new-instance p2, Ljava/util/ArrayDeque;\n\n    .line 10\n    .line 11\n    iget v1, v0, Lothers/TestConstructor2$A;->a:I\n\n    .line 12\n    .line 13\n    .line 14\n    invoke-direct {p2, v1}, Ljava/util/ArrayDeque;-><init>(I)V\n\n    .line 15\n    .line 16\n    iput-object p2, v0, Lothers/TestConstructor2$A;->b:Ljava/util/ArrayDeque;\n\n    .line 17\n    .line 18\n    iput p3, v0, Lothers/TestConstructor2$A;->a:I\n\n    .line 19\n    .line 20\n    iget-object p2, p0, Lothers/TestConstructor2;->a:Ljava/util/HashMap;\n\n    .line 21\n    .line 22\n    .line 23\n    invoke-static {p1}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;\n\n    .line 24\n    move-result-object p1\n\n    .line 25\n    .line 26\n    .line 27\n    invoke-virtual {p2, p1, v0}, Ljava/util/HashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n\n    .line 28\n    return-void\n\n    .line 29\n    .line 30\n    :cond_1c\n    new-instance p1, Ljava/lang/IllegalArgumentException;\n\n    .line 31\n    .line 32\n    const-string/jumbo p2, \"error\"\n\n    .line 33\n    .line 34\n    .line 35\n    invoke-direct {p1, p2}, Ljava/lang/IllegalArgumentException;-><init>(Ljava/lang/String;)V\n\n    .line 36\n    throw p1\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestConstructorBranched.smali",
    "content": ".class public Lothers/TestConstructorBranched;\n.super Ljava/lang/Object;\n\n.method public test(Ljava/util/Collection;)Ljava/util/Set;\n    .registers 4\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/util/Collection\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \">;)\",\n            \"Ljava/util/Set\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \">;\"\n        }\n    .end annotation\n\n    new-instance v0, Ljava/util/HashSet;\n\n    if-nez p1, :cond_d\n    invoke-direct {v0}, Ljava/util/HashSet;-><init>()V\n    goto :goto_7\n\n    :cond_d\n    invoke-direct {v0, p1}, Ljava/util/HashSet;-><init>(Ljava/util/Collection;)V\n\n    :goto_7\n    const-string v1, \"end\"\n    invoke-interface {v0, v1}, Ljava/util/Set;->add(Ljava/lang/Object;)Z\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestConstructorBranched2.smali",
    "content": ".class public Lothers/TestConstructorBranched2;\n.super Ljava/lang/Object;\n\n.method private test(Ljava/util/List;)Ljava/lang/String;\n    .locals 12\n    iget-boolean v1, p0, Landroidx/gridlayout/widget/GridLayout$Axis;->horizontal:Z\n    const/4 v2, 0x1\n    if-eqz v1, :cond_0\n    const-string/jumbo v1, \"x\"\n    goto :goto_0\n\n    :cond_0\n    const-string/jumbo v1, \"y\"\n\n    :goto_0\n    new-instance v3, Ljava/lang/StringBuilder;\n    invoke-direct {v3}, Ljava/lang/StringBuilder;-><init>()V\n    const/4 v4, 0x1\n    invoke-interface {p1}, Ljava/util/List;->iterator()Ljava/util/Iterator;\n    move-result-object v5\n    const/16 v6, 0x98\n\n    :goto_1\n    invoke-interface {v5}, Ljava/util/Iterator;->hasNext()Z\n    move-result v6\n    if-eqz v6, :cond_3\n    invoke-interface {v5}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n    move-result-object v6\n    check-cast v6, Landroidx/gridlayout/widget/GridLayout$Arc;\n    if-eqz v4, :cond_1\n    const/4 v4, 0x0\n    goto :goto_2\n\n    :cond_1\n    const-string v7, \", \"\n    invoke-virtual {v3, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v3\n\n    :goto_2\n    iget-object v7, v6, Landroidx/gridlayout/widget/GridLayout$Arc;->span:Landroidx/gridlayout/widget/GridLayout$Interval;\n    iget v7, v7, Landroidx/gridlayout/widget/GridLayout$Interval;->min:I\n    iget-object v8, v6, Landroidx/gridlayout/widget/GridLayout$Arc;->span:Landroidx/gridlayout/widget/GridLayout$Interval;\n    iget v8, v8, Landroidx/gridlayout/widget/GridLayout$Interval;->max:I\n    iget-object v9, v6, Landroidx/gridlayout/widget/GridLayout$Arc;->value:Landroidx/gridlayout/widget/GridLayout$MutableInt;\n    iget v9, v9, Landroidx/gridlayout/widget/GridLayout$MutableInt;->value:I\n    const-string v10, \"-\"\n    new-instance v11, Ljava/lang/StringBuilder;\n    if-ge v7, v8, :cond_2\n    invoke-direct {v11}, Ljava/lang/StringBuilder;-><init>()V\n    invoke-virtual {v11, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v11\n    invoke-virtual {v11, v8}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;\n    move-result-object v11\n    invoke-virtual {v11, v10}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v10\n    invoke-virtual {v10, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v10\n    invoke-virtual {v10, v7}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;\n    move-result-object v10\n    const-string v11, \">=\"\n    invoke-virtual {v10, v11}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v10\n    invoke-virtual {v10, v9}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;\n    move-result-object v10\n    invoke-virtual {v10}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n    move-result-object v10\n    goto :goto_3\n\n    :cond_2\n    invoke-direct {v11}, Ljava/lang/StringBuilder;-><init>()V\n    invoke-virtual {v11, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v11\n    invoke-virtual {v11, v7}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;\n    move-result-object v11\n    invoke-virtual {v11, v10}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v10\n    invoke-virtual {v10, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v10\n    invoke-virtual {v10, v8}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;\n    move-result-object v10\n    const-string v11, \"<=\"\n    invoke-virtual {v10, v11}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v10\n    neg-int v11, v9\n    invoke-virtual {v10, v11}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;\n    move-result-object v10\n    invoke-virtual {v10}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n    move-result-object v10\n\n    :goto_3\n    invoke-virtual {v3, v10}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    goto/16 :goto_1\n\n    :cond_3\n    invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n    move-result-object v5\n    return-object v5\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestConstructorBranched3.smali",
    "content": ".class public Lothers/TestConstructorBranched3;\n.super Ljava/lang/Object;\n\n.method public static final test(Ljava/lang/Class;)Lml/f;\n    .registers 5\n    const/4 v0, 0x0\n    :goto_1\n    invoke-virtual {p0}, Ljava/lang/Class;->isArray()Z\n    move-result v1\n    if-eqz v1, :cond_13\n    add-int/lit8 v0, v0, 0x1\n    invoke-virtual {p0}, Ljava/lang/Class;->getComponentType()Ljava/lang/Class;\n    move-result-object p0\n    const-string v1, \"currentClass.componentType\"\n    invoke-static {p0, v1}, Lve/e;->l(Ljava/lang/Object;Ljava/lang/String;)V\n    goto :goto_1\n\n    :cond_13\n    invoke-virtual {p0}, Ljava/lang/Class;->isPrimitive()Z\n    move-result v1\n    if-eqz v1, :cond_68\n    sget-object v1, Ljava/lang/Void;->TYPE:Ljava/lang/Class;\n    invoke-static {p0, v1}, Lve/e;->g(Ljava/lang/Object;Ljava/lang/Object;)Z\n    move-result v1\n\n    if-eqz v1, :cond_31\n    new-instance p0, Lml/f;\n    sget-object v1, Lgk/k$a;->e:Lhl/c;\n    invoke-virtual {v1}, Lhl/c;->i()Lhl/b;\n    move-result-object v1\n    invoke-static {v1}, Lhl/a;->l(Lhl/b;)Lhl/a;\n    move-result-object v1\n    invoke-direct {p0, v1, v0}, Lml/f;-><init>(Lhl/a;I)V\n    return-object p0\n\n    :cond_31\n    invoke-virtual {p0}, Ljava/lang/Class;->getName()Ljava/lang/String;\n    move-result-object p0\n    invoke-static {p0}, Lpl/b;->c(Ljava/lang/String;)Lpl/b;\n    move-result-object p0\n    invoke-virtual {p0}, Lpl/b;->r()Lgk/i;\n    move-result-object p0\n    const-string v1, \"get(currentClass.name).primitiveType\"\n    invoke-static {p0, v1}, Lve/e;->l(Ljava/lang/Object;Ljava/lang/String;)V\n    new-instance v1, Lml/f;\n    if-lez v0, :cond_58\n    .line 1\n    iget-object p0, p0, Lgk/i;->d:Ljj/d;\n    invoke-interface {p0}, Ljj/d;->getValue()Ljava/lang/Object;\n    move-result-object p0\n    check-cast p0, Lhl/b;\n    .line 2\n    invoke-static {p0}, Lhl/a;->l(Lhl/b;)Lhl/a;\n    move-result-object p0\n    add-int/lit8 v0, v0, -0x1\n    invoke-direct {v1, p0, v0}, Lml/f;-><init>(Lhl/a;I)V\n    return-object v1\n\n    .line 3\n    :cond_58\n    iget-object p0, p0, Lgk/i;->c:Ljj/d;\n    invoke-interface {p0}, Ljj/d;->getValue()Ljava/lang/Object;\n    move-result-object p0\n    check-cast p0, Lhl/b;\n    .line 4\n    invoke-static {p0}, Lhl/a;->l(Lhl/b;)Lhl/a;\n    move-result-object p0\n    invoke-direct {v1, p0, v0}, Lml/f;-><init>(Lhl/a;I)V\n    return-object v1\n\n    :cond_68\n    invoke-static {p0}, Lpk/b;->b(Ljava/lang/Class;)Lhl/a;\n    move-result-object p0\n    sget-object v1, Lik/c;->a:Lik/c;\n    invoke-virtual {p0}, Lhl/a;->b()Lhl/b;\n    move-result-object v2\n    const-string v3, \"javaClassId.asSingleFqName()\"\n    invoke-static {v2, v3}, Lve/e;->l(Ljava/lang/Object;Ljava/lang/String;)V\n    invoke-virtual {v1, v2}, Lik/c;->f(Lhl/b;)Lhl/a;\n    move-result-object v1\n    if-nez v1, :cond_7e\n    goto :goto_7f\n\n    :cond_7e\n    move-object p0, v1\n\n    :goto_7f\n    new-instance v1, Lml/f;\n    invoke-direct {v1, p0, v0}, Lml/f;-><init>(Lhl/a;I)V\n    return-object v1\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestDeadBlockReferencesStart.smali",
    "content": ".class Lothers/TestDeadBlockReferencesStart;\n.super Ljava/lang/Object;\n\n.method public test()V\n    .registers 6\n\n     :start\n     return-void\n\n     goto :start\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestExplicitOverride.smali",
    "content": "###### Class others.TestExplicitOverride (others.TestExplicitOverride)\n.class public Lothers/TestExplicitOverride;\n.super Ljava/lang/Exception;\n.source \"TestExplicitOverride.java\"\n\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    .prologue\n    .line 3\n    invoke-direct {p0}, Ljava/lang/Exception;-><init>()V\n\n    return-void\n.end method\n\n\n# virtual methods\n.method public getMessage()Ljava/lang/String;\n    .registers 2\n\n    .annotation runtime Ljava/lang/Override;\n    .end annotation\n\n    .prologue\n    .line 7\n    invoke-super {p0}, Ljava/lang/Exception;->getMessage()Ljava/lang/String;\n\n    move-result-object v0\n\n    return-object v0\n.end method\n\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestFieldInitOrder2.smali",
    "content": ".class public Lothers/TestFieldInitOrder2;\n.super Ljava/lang/Object;\n\n.field private static final VALUE:Ljava/lang/String;\n.field static final ZPREFIX:Ljava/lang/String; = \"SOME_\"\n\n\n# direct methods\n.method static constructor <clinit>()V\n    .registers 2\n\n    new-instance v0, Ljava/lang/StringBuilder;\n    invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V\n\n    sget-object v1, Lothers/TestFieldInitOrder2;->ZPREFIX:Ljava/lang/String;\n\n    invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v0\n\n    const-string v1, \"VALUE\"\n\n    invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v0\n\n    invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n    move-result-object v0\n\n    sput-object v0, Lothers/TestFieldInitOrder2;->VALUE:Ljava/lang/String;\n    return-void\n.end method\n\n.method public constructor <init>()V\n    .registers 1\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n    return-void\n.end method\n\n\n.method public check()V\n    .registers 3\n    sget-object v0, Lothers/TestFieldInitOrder2;->VALUE:Ljava/lang/String;\n    invoke-static {v0}, Ljadx/tests/api/utils/assertj/JadxAssertions;->assertThat(Ljava/lang/String;)Ljadx/tests/api/utils/assertj/JadxCodeAssertions;\n    move-result-object v0\n    const-string v1, \"SOME_VALUE\"\n    invoke-virtual {v0, v1}, Ljadx/tests/api/utils/assertj/JadxCodeAssertions;->isEqualTo(Ljava/lang/String;)Lorg/assertj/core/api/AbstractStringAssert;\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestFieldUsageMove.smali",
    "content": ".class public Lothers/TestFieldUsageMove;\n.super Ljava/lang/Object;\n\n.method public static test(Ljava/lang/Object;)V\n    .registers 4\n\n    .line 4\n    instance-of v0, p0, Ljava/lang/Boolean;\n    if-eqz v0, :cond_1a\n    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;\n    new-instance v1, Ljava/lang/StringBuilder;\n    invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V\n    const-string v2, \"Boolean: \"\n    invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    invoke-virtual {v1, p0}, Ljava/lang/StringBuilder;->append(Ljava/lang/Object;)Ljava/lang/StringBuilder;\n    invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n    move-result-object v1\n    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n\n    .line 5\n    :cond_1a\n    instance-of v0, p0, Ljava/lang/Float;\n    if-eqz v0, :cond_34\n    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;\n    new-instance v1, Ljava/lang/StringBuilder;\n    invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V\n    const-string v2, \"Float: \"\n    invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    invoke-virtual {v1, p0}, Ljava/lang/StringBuilder;->append(Ljava/lang/Object;)Ljava/lang/StringBuilder;\n    invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n    move-result-object p0\n\n    invoke-virtual {v0, p0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n\n    .line 6\n    :cond_34\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestFixClassAccessModifiers/Cls.smali",
    "content": ".class public Lothers/Cls;\n.super Ljava/lang/Object;\n\n.annotation system Ldalvik/annotation/MemberClasses;\n    value = {\n        Lothers/Cls$InnerCls;\n    }\n.end annotation\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestFixClassAccessModifiers/InnerCls.smali",
    "content": ".class private Lothers/Cls$InnerCls;\n.super Ljava/lang/Object;\n\n.annotation system Ldalvik/annotation/EnclosingClass;\n    value = Lothers/Cls;\n.end annotation\n\n.annotation system Ldalvik/annotation/InnerClass;\n    accessFlags = 0xA\n    name = \"InnerCls\"\n.end annotation\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestFixClassAccessModifiers/TestCls.smali",
    "content": ".class public Lothers/TestCls;\n.super Ljava/lang/Object;\n\n.field public field:Lothers/Cls$InnerCls;\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestIncorrectFieldSignature.smali",
    "content": ".class public Lothers/TestIncorrectFieldSignature;\n.super Ljava/lang/Object;\n\n.field public static A:Z\n\t.annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Lint;\"\n        }\n    .end annotation\n.end field\n\n.field public static B:Ljava/lang/Boolean;\n\t.annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Lpkg/int;\"\n        }\n    .end annotation\n.end field\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestIncorrectMethodSignature.smali",
    "content": ".class public Lothers/TestIncorrectMethodSignature;\n.super Ljava/lang/RuntimeException;\n.source \"TestIncorrectMethodSignature.java\"\n\n.method public constructor <init>(Ljava/lang/String;)V\n    .registers 2\n\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(J)V\"\n        }\n    .end annotation\n\n    invoke-direct {p0, p1}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/String;)V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestInlineVarArg.smali",
    "content": ".class public Lothers/TestInlineVarArg;\n.super Ljava/lang/Object;\n\n.method public static varargs f([Ljava/lang/String;)V\n    .registers 1\n    return-void\n.end method\n\n.method public test()V\n    .registers 5\n\n    const/4 v2, 0x3\n\n    new-array v1, v2, [Ljava/lang/String;\n\n    move-object v0, v1\n\n    const/4 v2, 0x0\n\n    const-string v3, \"a\"\n\n    aput-object v3, v0, v2\n\n    const/4 v2, 0x1\n\n    const-string v3, \"b\"\n\n    aput-object v3, v0, v2\n\n    const/4 v2, 0x2\n\n    const-string v3, \"c\"\n\n    aput-object v3, v0, v2\n\n    move-object v1, v0\n\n    invoke-static {v1}, Lothers/TestInlineVarArg;->f([Ljava/lang/String;)V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestInsnsBeforeSuper/A.smali",
    "content": ".class public Lothers/A;\n.super Ljava/lang/Object;\n\n\n# direct methods\n.method public constructor <init>(Ljava/lang/String;)V\n    .registers 3\n\n    .prologue\n\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestInsnsBeforeSuper/B.smali",
    "content": ".class public Lothers/B;\n.super Lothers/A;\n\n\n# direct methods\n.method public constructor <init>(Ljava/lang/String;)V\n    .registers 3\n\n    .prologue\n    invoke-static {p1}, Lothers/B;->checkNull(Ljava/lang/Object;)V\n\n    invoke-direct {p0, p1}, Lothers/A;-><init>(Ljava/lang/String;)V\n\n    return-void\n.end method\n\n\n.method public static checkNull(Ljava/lang/Object;)V\n    .registers 3\n\n    .prologue\n    if-nez p0, :cond_8\n\n    new-instance v0, Ljava/lang/NullPointerException;\n    invoke-direct {v0}, Ljava/lang/NullPointerException;-><init>()V\n    throw v0\n\n    :cond_8\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestInsnsBeforeSuper2.smali",
    "content": ".class public Lothers/TestInsnsBeforeSuper2;\n.super Ljava/lang/Exception;\n.source \"MyException.java\"\n\n# instance fields\n.field private mErrorType:I\n\n\n# direct methods\n.method public constructor <init>(Ljava/lang/String;I)V\n    .locals 8\n\n    .prologue\n    move-object v0, p0\n\n    .local v0, \"this\":Lothers/TestInsnsBeforeSuper2;\n    move-object v1, p1\n\n    .local v1, \"message\":Ljava/lang/String;\n    move v2, p2\n\n    .line 39\n    .local v2, \"errorType\":I\n    move-object v3, v0\n\n    .local v3, \"this\":Lothers/TestInsnsBeforeSuper2;\n    move-object v4, v1\n\n    .local v4, \"message\":Ljava/lang/String;\n    move v5, v2\n\n    .line 51\n    .end local v0    # \"this\":Lothers/TestInsnsBeforeSuper2;\n    .end local v1    # \"message\":Ljava/lang/String;\n    .end local v2    # \"errorType\":I\n    .local v5, \"errorType\":I\n    move-object v6, v1\n\n    invoke-direct {v0, v6}, Ljava/lang/Exception;-><init>(Ljava/lang/String;)V\n\n    .line 39\n    const/4 v7, 0x0\n\n    iput v7, v0, Lothers/TestInsnsBeforeSuper2;->mErrorType:I\n\n    .line 52\n    iput v2, v0, Lothers/TestInsnsBeforeSuper2;->mErrorType:I\n\n    .line 53\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestInsnsBeforeThis.smali",
    "content": ".class public Lothers/TestInsnsBeforeThis;\n.super Ljava/lang/Object;\n\n.method public constructor <init>(Ljava/lang/String;)V\n    .registers 3\n\n    .prologue\n    invoke-static {p1}, Lothers/TestInsnsBeforeThis;->checkNull(Ljava/lang/Object;)V\n\n    invoke-direct {p1}, Ljava/lang/String;->length()I\n    move-result v0\n\n    invoke-direct {p0, v0}, Lothers/TestInsnsBeforeThis;-><init>(I)V\n\n    return-void\n.end method\n\n.method public constructor <init>(I)V\n    .registers 3\n\n    .prologue\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n.method public static checkNull(Ljava/lang/Object;)V\n    .registers 3\n\n    .prologue\n    if-nez p0, :cond_8\n\n    new-instance v0, Ljava/lang/NullPointerException;\n    invoke-direct {v0}, Ljava/lang/NullPointerException;-><init>()V\n    throw v0\n\n    :cond_8\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestInvalidExceptions.smali",
    "content": ".class public Lothers/TestInvalidExceptions;\n.super Ljava/lang/Object;\n\n.method private invalidException()V\n    .registers 3\n    .annotation system Ldalvik/annotation/Throws;\n        value = {\n            Ljava/lang/String;\n        }\n    .end annotation\n\n    new-instance v0, Ljava/io/FileNotFoundException;\n    const-string v1, \"\"\n    invoke-direct {v0, v1}, Ljava/io/FileNotFoundException;-><init>(Ljava/lang/String;)V\n    throw v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestInvalidExceptions2.smali",
    "content": ".class public Lothers/TestInvalidExceptions2;\n.super Ljava/lang/Object;\n\n.method private throwPossibleExceptionType()V\n    .registers 3\n    .annotation system Ldalvik/annotation/Throws;\n        value = {\n            Ljadx/UnknownTypeHierarchyException;\n        }\n    .end annotation\n\n    new-instance v0, Ljadx/UnknownTypeHierarchyException;\n    const-string v1, \"\"\n    invoke-direct {v0, v1}, Ljadx/UnknownTypeHierarchyException;-><init>(Ljava/lang/String;)V\n    throw v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestMissingExceptions.smali",
    "content": ".class public Lothers/TestMissingExceptions;\n.super Ljava/lang/Object;\n\n.method private exceptionSource()V\n    .registers 3\n\n    new-instance v0, Ljava/io/FileNotFoundException;\n    const-string v1, \"\"\n    invoke-direct {v0, v1}, Ljava/io/FileNotFoundException;-><init>(Ljava/lang/String;)V\n    throw v0\n.end method\n\n.method public doSomething1(I)V\n    .registers 3\n    .param p1, \"i\"    # I\n\n    const/4 v0, 0x1\n\n    if-ne p1, v0, :cond_7\n\n    invoke-virtual {p0, p1}, Lothers/TestMissingExceptions;->doSomething2(I)V\n    goto :goto_a\n\n    :cond_7\n    invoke-virtual {p0, p1}, Lothers/TestMissingExceptions;->doSomething1(I)V\n\n    :goto_a\n    return-void\n.end method\n\n.method public doSomething2(I)V\n    .registers 3\n    .param p1, \"i\"    # I\n\n    const/4 v0, 0x1\n\n    if-ne p1, v0, :cond_7\n\n    invoke-direct {p0}, Lothers/TestMissingExceptions;->exceptionSource()V\n\n    goto :goto_a\n\n    :cond_7\n    invoke-virtual {p0, p1}, Lothers/TestMissingExceptions;->doSomething1(I)V\n\n    :goto_a\n    return-void\n.end method\n\n.method public mergeThrownExcetions()V\n    .registers 1\n    .annotation system Ldalvik/annotation/Throws;\n        value = {\n            Ljava/io/IOException;\n        }\n    .end annotation\n\n    invoke-direct {p0}, Lothers/TestMissingExceptions;->exceptionSource()V\n    return-void\n.end method\n\n.method public missingThrowsAnnotation()V\n    .registers 1\n\n    invoke-direct {p0}, Lothers/TestMissingExceptions;->exceptionSource()V\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestMoveInline.smali",
    "content": ".class public Lothers/TestMoveInline;\n.super Ljava/lang/Object;\n\n.field private h:[B\n.field private a:Lothers/TestMoveInline;\n\n.method public k([BII)V\n    .registers 5\n    return-void\n.end method\n\n.method public test(I)V\n    .registers 7\n\n    const/4 v0, 0x0\n    move v1, v0\n\n    :goto_2\n    and-int/lit8 v2, p1, -0x80\n    if-nez v2, :cond_13\n\n    .line 1\n    iget-object v2, p0, Lothers/TestMoveInline;->h:[B\n    add-int/lit8 v3, v1, 0x1\n    int-to-byte p1, p1\n    aput-byte p1, v2, v1\n\n    .line 2\n    iget-object p1, p0, Lothers/TestMoveInline;->a:Lothers/TestMoveInline;\n    invoke-virtual {p1, v2, v0, v3}, Lothers/TestMoveInline;->k([BII)V\n    return-void\n\n    .line 3\n    :cond_13\n    iget-object v2, p0, Lothers/TestMoveInline;->h:[B\n    add-int/lit8 v3, v1, 0x1\n    and-int/lit8 v4, p1, 0x7f\n    or-int/lit16 v4, v4, 0x80\n    int-to-byte v4, v4\n    aput-byte v4, v2, v1\n    ushr-int/lit8 p1, p1, 0x7\n    move v1, v3\n    goto :goto_2\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestMultipleNOPs/test.smali",
    "content": ".class final LseC/dujmehn/Cutyq/e;\n.super Ljava/lang/Object;\n\n# interfaces\n.implements Ljava/lang/Runnable;\n\n\n.method public static test()Ljava/lang/String;\n    .registers 9\n\n    nop\n\n    nop\n\n    nop\n\n    nop\n\n    .prologue\n    nop\n\n    const-string v5, \"VA==\"\n\n    nop\n\n    nop\n\n    .local v5, \"x_gfxj\":Ljava/lang/String;\n    nop\n\n    const-string v1, \"54b6610e1af242f78e38a5866eaa7c41\"\n\n    nop\n\n    nop\n\n    .local v1, \"p\":Ljava/lang/String;\n    nop\n\n    invoke-virtual {v5}, Ljava/lang/String;->getBytes()[B\n\n    nop\n\n    nop\n\n    move-result-object v7\n\n    nop\n\n    nop\n\n    const/4 v8, 0x0\n\n    nop\n\n    nop\n\n    invoke-static {v7, v8}, Landroid/util/Base64;->decode([BI)[B\n\n    nop\n\n    nop\n\n    move-result-object v3\n\n    nop\n\n    nop\n\n    .local v3, \"wjxzqy\":[B\n    nop\n\n    new-instance v4, Ljava/lang/String;\n\n    nop\n\n    nop\n\n    invoke-direct {v4, v3}, Ljava/lang/String;-><init>([B)V\n\n    nop\n\n    nop\n\n    .local v4, \"x\":Ljava/lang/String;\n    nop\n\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    nop\n\n    nop\n\n    invoke-direct {v6}, Ljava/lang/StringBuilder;-><init>()V\n\n    nop\n\n    nop\n\n    .local v6, \"xg\":Ljava/lang/StringBuilder;\n    nop\n\n    const/4 v0, 0x0\n\n    nop\n\n    nop\n\n    .local v0, \"n\":I\n    nop\n\n    :goto_3b\n    nop\n\n    invoke-virtual {v4}, Ljava/lang/String;->length()I\n\n    nop\n\n    nop\n\n    move-result v7\n\n    nop\n\n    nop\n\n    if-ge v0, v7, :cond_76\n\n    nop\n\n    nop\n\n    invoke-virtual {v4, v0}, Ljava/lang/String;->charAt(I)C\n\n    nop\n\n    nop\n\n    move-result v7\n\n    nop\n\n    nop\n\n    invoke-virtual {v1}, Ljava/lang/String;->length()I\n\n    nop\n\n    nop\n\n    move-result v8\n\n    nop\n\n    nop\n\n    rem-int v8, v0, v8\n\n    nop\n\n    nop\n\n    invoke-virtual {v1, v8}, Ljava/lang/String;->charAt(I)C\n\n    nop\n\n    nop\n\n    move-result v8\n\n    nop\n\n    nop\n\n    xor-int/2addr v7, v8\n\n    nop\n\n    nop\n\n    int-to-char v7, v7\n\n    nop\n\n    nop\n\n    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(C)Ljava/lang/StringBuilder;\n\n    nop\n\n    nop\n\n    add-int/lit8 v0, v0, 0x1\n\n    nop\n\n    nop\n\n    goto :goto_3b\n\n    nop\n\n    nop\n\n    :cond_76\n    nop\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    nop\n\n    nop\n\n    move-result-object v2\n\n    nop\n\n    nop\n\n    .local v2, \"w\":Ljava/lang/String;\n    nop\n\n    return-object v2\n\n    nop\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestN21.smali",
    "content": ".class public Lothers/TestN21;\n.super Ljava/lang/Object;\n\n.method private static test([BI)I\n    .locals 5\n\n    const/4 v1, 0x0\n\n    const/16 v0, 0xe\n\n    aget-byte v0, p0, v0\n\n    shl-int/lit8 v0, v0, 0x10\n\n    move v2, v1\n\n    :goto_0\n    if-nez v2, :cond_1\n\n    const/4 v2, 0x3\n\n    and-int/lit16 v3, p1, 0xff\n\n    :try_start_0\n    aget-byte v3, p0, v3\n\n    and-int/lit16 v3, v3, 0xff\n\n    shr-int/lit8 v4, p1, 0x8\n\n    and-int/lit16 v4, v4, 0xff\n\n    aget-byte v4, p0, v4\n\n    and-int/lit16 v4, v4, 0xff\n\n    shl-int/lit8 v4, v4, 0x8\n\n    or-int/2addr v3, v4\n\n    shr-int/lit8 v4, p1, 0x10\n\n    and-int/lit16 v4, v4, 0xff\n\n    aget-byte v4, p0, v4\n\n    and-int/lit16 v4, v4, 0xff\n\n    shl-int/lit8 v4, v4, 0x10\n\n    or-int/2addr v3, v4\n\n    shr-int/lit8 v4, p1, 0x18\n\n    and-int/lit16 v4, v4, 0xff\n\n    aget-byte v0, p0, v4\n    :try_end_0\n    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_1\n\n    shl-int/lit8 v0, v0, 0x18\n\n    or-int/2addr v0, v3\n\n    :cond_0\n    :goto_1\n    return v0\n\n    :catch_0\n    move-exception v2\n\n    :cond_1\n    if-nez v1, :cond_0\n\n    const/4 v1, 0x2\n\n    and-int/lit8 v2, p1, 0x7f\n\n    :try_start_1\n    aget-byte v0, p0, v2\n    :try_end_1\n    .catch Ljava/lang/Exception; {:try_start_1 .. :try_end_1} :catch_0\n\n    shr-int/lit8 v0, v0, 0x8\n\n    goto :goto_1\n\n    :catch_1\n    move-exception v3\n\n    goto :goto_0\n.end method\n\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestOverridePackagePrivateMethod/A.smali",
    "content": ".class public Ltest/A;\n.super Ljava/lang/Object;\n\n.method a()V # package-private\n    .locals 1\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestOverridePackagePrivateMethod/B.smali",
    "content": ".class public Ltest/B;\n.super Ltest/A;\n\n.method public a()V\n    .locals 1\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestOverridePackagePrivateMethod/C.smali",
    "content": ".class public Lother/C;\n.super Ltest/A;\n\n.method public a()V\n    .locals 1\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestOverrideWithSameName/A.smali",
    "content": ".class interface abstract Ltest/A;\n.super Ljava/lang/Object;\n\n.method public abstract a()Ltest/B;\n.end method\n\n.method public abstract a()Ltest/C;\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestOverrideWithSameName/B.smali",
    "content": ".class abstract Ltest/B;\n.super Ljava/lang/Object;\n\n.implements Ltest/A;\n\n.method public a()Ltest/C;\n    .registers 2\n    const/4 v0, 0x0\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestOverrideWithSameName/C.smali",
    "content": ".class public Ltest/C;\n.super Ltest/B;\n\n.method public a()Ltest/B;\n    .registers 2\n    const/4 v0, 0x0\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestSuperLoop/A.smali",
    "content": ".class public LA;\n.super LB;\n\n.field public a:I\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestSuperLoop/B.smali",
    "content": ".class public LB;\n.super LA;\n\n.field public b:I\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestSyntheticConstructor/BuggyConstructor.smali",
    "content": ".class public LBuggyConstructor;\n.super Ljava/lang/Object;\n.source \"BuggyConstructor.java\"\n\n#.implements LInterfaceClass;\n\n.method public synthetic constructor <init>()V\n    .locals 0\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestSyntheticConstructor/Test.smali",
    "content": ".class public Lothers/Test;\n.super Ljava/lang/Object;\n.source \"Test.java\"\n\n.field public static final A00:Ljava/lang/Object;\n\n.method public static constructor <clinit>()V\n    .locals 1\n    new-instance v0, LBuggyConstructor;\n    invoke-direct {v0}, LBuggyConstructor;-><init>()V\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/others/TestUsageApacheHttpClient.smali",
    "content": "###### Class Lothers.TestUsageApacheHttpClient (TestUsageApacheHttpClient)\n.class public Lothers/TestUsageApacheHttpClient;\n.super Ljava/lang/Object;\n.source \"TestUsageApacheHttpClient.java\"\n\n# instance fields\n.field private httpClient:Lorg/apache/http/client/HttpClient;\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    .line 3\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/rename/TestUsingSourceFileName/b.smali",
    "content": ".class Lb;\n.super Ljava/lang/Object;\n.source \"a.java\"\n\n.method constructor <init>()V\n    .registers 1\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/special/TestPackageInfoSupport/pkg1.smali",
    "content": ".class interface abstract synthetic Lspecial/pkg1/package-info;\n.super Ljava/lang/Object;\n.source \"package-info.java\"\n\n.annotation runtime Ljava/lang/Deprecated;\n.end annotation\n"
  },
  {
    "path": "jadx-core/src/test/smali/special/TestPackageInfoSupport/pkg2.smali",
    "content": ".class interface abstract synthetic Lspecial/pkg2/package-info;\n.super Ljava/lang/Object;\n\n.annotation runtime Lorg/jetbrains/annotations/ApiStatus$Internal;\n.end annotation\n"
  },
  {
    "path": "jadx-core/src/test/smali/special/TestPackageInfoSupport/pkg3.smali",
    "content": ".class interface Lspecial/pkg3/package-info;\n.super Ljava/lang/Object;\n"
  },
  {
    "path": "jadx-core/src/test/smali/switches/TestSwitchOverStrings3.smali",
    "content": ".class public LTestSwitchOverStrings3;\n.super Ljava/lang/Object;\n\n.method public test3(Ljava/lang/String;)I\n    .registers 5\n\n    .line 87\n    invoke-virtual {p1}, Ljava/lang/String;->hashCode()I\n\n    move-result v0\n\n    const/4 v1, 0x0\n\n    const/4 v2, 0x1\n\n    packed-switch v0, :pswitch_data_38\n\n    :cond_9\n    goto :goto_32\n\n    :pswitch_a\n    const-string v0, \"branch4\"\n\n    invoke-virtual {p1, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result p1\n\n    if-eqz p1, :cond_9\n\n    const/4 p1, 0x3\n\n    goto :goto_33\n\n    :pswitch_14\n    const-string v0, \"branch3\"\n\n    invoke-virtual {p1, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result p1\n\n    if-eqz p1, :cond_9\n\n    const/4 p1, 0x2\n\n    goto :goto_33\n\n    :pswitch_1e\n    const-string v0, \"branch2\"\n\n    invoke-virtual {p1, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result p1\n\n    if-eqz p1, :cond_9\n\n    const/4 p1, 0x1\n\n    goto :goto_33\n\n    :pswitch_28\n    const-string v0, \"branch1\"\n\n    invoke-virtual {p1, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result p1\n\n    if-eqz p1, :cond_9\n\n    const/4 p1, 0x0\n\n    goto :goto_33\n\n    :goto_32\n    const/4 p1, -0x1\n\n    :goto_33\n    packed-switch p1, :pswitch_data_44\n\n    .line 94\n    return v1\n\n    .line 90\n    :pswitch_37\n    return v2\n\n    :pswitch_data_38\n    .packed-switch 0x8358ecf\n        :pswitch_28\n        :pswitch_1e\n        :pswitch_14\n        :pswitch_a\n    .end packed-switch\n\n    :pswitch_data_44\n    .packed-switch 0x0\n        :pswitch_37\n        :pswitch_37\n    .end packed-switch\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/switches/TestSwitchOverStrings4.smali",
    "content": ".class public LTestSwitchOverStrings4;\n.super Ljava/lang/Object;\n\n.method public static test4(Ljava/lang/String;)I\n    .registers 10\n\n    const/16 v3, 0x0\n\n    const/16 v4, -0x1\n\n    if-nez p0, :cond_26\n\n    return v4\n\n    :cond_26\n    .line 202\n\n    invoke-virtual {p0}, Ljava/lang/String;->hashCode()I\n\n    move-result v2\n\n    sparse-switch v2, :sswitch_data_222\n\n    const/4 v0, -0x1\n\n    const/16 v2, 0x13\n\n    goto/16 :goto_20a\n\n    :sswitch_3b\n    const/16 v2, 0x13\n\n    const-string/jumbo v1, \"video/x-matroska\"\n\n    invoke-virtual {p0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_48\n\n    goto/16 :goto_207\n\n    :cond_48\n    const/16 v0, 0x1\n\n    goto/16 :goto_20a\n\n    :sswitch_1fd\n    const/16 v2, 0x13\n\n    const-string v1, \"audio/eac3-joc\"\n\n    invoke-virtual {p0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-nez v0, :cond_209\n\n    :goto_207\n    const/4 v0, -0x1\n\n    goto :goto_20a\n\n    :cond_209\n    const/4 v0, 0x0\n\n    :goto_20a\n    packed-switch v0, :pswitch_data_29c\n\n    return v4\n\n    :pswitch_216\n    return v2\n\n    :pswitch_221\n    return v3\n\n    :sswitch_data_222\n    .sparse-switch\n        0xb269699 -> :sswitch_1fd\n        0x79909c15 -> :sswitch_3b\n    .end sparse-switch\n\n    :pswitch_data_29c\n    .packed-switch 0x0\n        :pswitch_221\n        :pswitch_216\n    .end packed-switch\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/synchronize/TestNestedSynchronize.smali",
    "content": ".class public final Lsynchronize/TestNestedSynchronize;\n.super Ljava/lang/Object;\n.source \"TestNestedSynchronize.java\"\n\n.method public final test()V\n    .locals 2\n    const/4 v0, 0\n    const/4 v1, 0\n    monitor-enter v0\n    monitor-enter v1\n    monitor-exit v1\n    monitor-exit v0\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/synchronize/TestSynchronized4.smali",
    "content": ".class public Lsynchronize/TestSynchronized4;\n.super Ljava/lang/Object;\n\n.field private final obj:Ljava/lang/Object;\n\n.method public constructor <init>()V\n    .registers 2\n\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    new-instance v0, Ljava/lang/Object;\n\n    invoke-direct {v0}, Ljava/lang/Object;-><init>()V\n\n    iput-object v0, p0, Lsynchronize/TestSynchronized4;->obj:Ljava/lang/Object;\n\n    return-void\n.end method\n\n.method public test(I)Z\n    .registers 4\n\n    iget-object v1, p0, Lsynchronize/TestSynchronized4;->obj:Ljava/lang/Object;\n\n    monitor-enter v1\n\n    :try_start_3\n    invoke-direct {p0, p1}, Lsynchronize/TestSynchronized4;->isZero(I)Z\n    move-result v0\n\n    if-eqz v0, :cond_11\n\n    iget-object v0, p0, Lsynchronize/TestSynchronized4;->obj:Ljava/lang/Object;\n\n    invoke-direct {p0, v0, p1}, Lsynchronize/TestSynchronized4;->call(Ljava/lang/Object;I)Z\n    move-result v0\n\n    monitor-exit v1\n\n    :goto_10\n    return v0\n\n    :cond_11\n    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;\n\n    invoke-virtual {v0}, Ljava/io/PrintStream;->println()V\n\n    invoke-direct {p0}, Lsynchronize/TestSynchronized4;->getField()Ljava/lang/Object;\n\n    move-result-object v0\n\n    if-nez v0, :cond_22\n\n    const/4 v0, 0x1\n\n    :goto_1d\n    monitor-exit v1\n    return v0\n\n    :catchall_1f\n    move-exception v0\n\n    monitor-exit v1\n    :try_end_21\n    .catchall {:try_start_3 .. :try_end_21} :catchall_1f\n\n    throw v0\n\n    :cond_22\n    const/4 v0, 0x0\n\n    goto :goto_1d\n.end method\n\n.method private call(Ljava/lang/Object;I)Z\n    .registers 4\n\n    const/4 v0, 0x0\n\n    return v0\n.end method\n\n.method private getField()Ljava/lang/Object;\n    .registers 2\n\n    const/4 v0, 0x0\n\n    return-object v0\n.end method\n\n.method private isZero(I)Z\n    .registers 3\n\n    if-nez p1, :cond_4\n\n    const/4 v0, 0x1\n\n    :goto_3\n    return v0\n\n    :cond_4\n    const/4 v0, 0x0\n\n    goto :goto_3\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/synchronize/TestSynchronized5.smali",
    "content": ".class public Lsynchronize/TestSynchronized5;\n.super Ljava/lang/Object;\n\n.method public final get()V\n    .registers 6\n\n    monitor-enter p0\n\n    :try_start_1\n\n    const/4 v0, 0\n\n    if-eqz v0, :cond_1\n\n    monitor-exit p0\n\n    return-void\n\n    :cond_1\n\n    monitor-exit p0\n    :try_end_1\n    .catchall {:try_start_1 .. :try_end_1} :catchall_1\n\n    monitor-enter p0\n\n    :try_start_2\n\n    const/4 v1, 1\n\n    if-eqz v1, :cond_2\n\n    invoke-static {}, Ljava/lang/System;->gc()V\n\n    :cond_2\n\n    monitor-exit p0\n    :try_end_2\n    .catchall {:try_start_2 .. :try_end_2} :catchall_2\n\n    return-void\n\n    :catchall_2\n    move-exception v0\n\n    :try_start_3\n    monitor-exit p0\n    :try_end_3\n    .catchall {:try_start_3 .. :try_end_3} :catchall_2\n\n    throw v0\n\n    :catchall_1\n    move-exception v0\n\n    :try_start_4\n    monitor-exit p0\n    :try_end_4\n    .catchall {:try_start_4 .. :try_end_4} :catchall_1\n\n    throw v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/synchronize/TestSynchronized6.smali",
    "content": ".class public Lsynchronize/TestSynchronized6;\n.super Ljava/lang/Object;\n\n.field private final lock:Ljava/lang/Object;\n\n.method public constructor <init>()V\n    .registers 2\n\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n    new-instance v0, Ljava/lang/Object;\n    invoke-direct {v0}, Ljava/lang/Object;-><init>()V\n    iput-object v0, p0, Lsynchronize/TestSynchronized6;->lock:Ljava/lang/Object;\n    return-void\n.end method\n\n.method private test(Ljava/lang/Object;)Z\n    .locals 2\n\n    .line 169\n    iget-object v0, p0, Lsynchronize/TestSynchronized6;->lock:Ljava/lang/Object;\n    monitor-enter v0\n\n    .line 170\n    :try_start_0\n    invoke-direct {p0, p1}, Lsynchronize/TestSynchronized6;->isA(Ljava/lang/Object;)Z\n    move-result v1\n\n    if-nez v1, :cond_1\n\n    invoke-direct {p0, p1}, Lsynchronize/TestSynchronized6;->isB(Ljava/lang/Object;)Z\n    move-result p1\n\n    if-eqz p1, :cond_0\n    goto :goto_0\n\n    :cond_0\n    const/4 p1, 0x0\n    goto :goto_1\n\n    :cond_1\n    :goto_0\n    const/4 p1, 0x1\n\n    :goto_1\n    monitor-exit v0\n\n    return p1\n\n    :catchall_0\n    move-exception p1\n\n    .line 171\n    monitor-exit v0\n    :try_end_0\n    .catchall {:try_start_0 .. :try_end_0} :catchall_0\n\n    throw p1\n.end method\n\n.method private isA(Ljava/lang/Object;)Z\n    .registers 3\n    const/4 v0, 0x0\n    return v0\n.end method\n\n.method private isB(Ljava/lang/Object;)Z\n    .registers 3\n    const/4 v0, 0x0\n    return v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/trycatch/TestEmptyCatch.smali",
    "content": ".class synthetic Ltrycatch/TestEmptyCatch;\n.super Ljava/lang/Object;\n\n\n# static fields\n.field static final synthetic $SwitchMap$Access:[I\n\n\n# direct methods\n.method static constructor <clinit>()V\n    .locals 3\n\n    .line 49\n    invoke-static {}, Lother/EnumAccess;->values()[Lother/EnumAccess;\n\n    move-result-object v0\n\n    array-length v0, v0\n\n    new-array v0, v0, [I\n\n    sput-object v0, Ltrycatch/TestEmptyCatch;->$SwitchMap$Access:[I\n\n    :try_start_0\n    sget-object v1, Lother/EnumAccess;->QUERY:Lother/EnumAccess;\n\n    invoke-virtual {v1}, Ljava/lang/Enum;->ordinal()I\n\n    move-result v1\n\n    const/4 v2, 0x1\n\n    aput v2, v0, v1\n    :try_end_0\n    .catch Ljava/lang/NoSuchFieldError; {:try_start_0 .. :try_end_0} :catch_0\n\n    :catch_0\n    :try_start_1\n    sget-object v0, Ltrycatch/TestEmptyCatch;->$SwitchMap$Access:[I\n\n    sget-object v1, Lother/EnumAccess;->MODIFY:Lother/EnumAccess;\n\n    invoke-virtual {v1}, Ljava/lang/Enum;->ordinal()I\n\n    move-result v1\n\n    const/4 v2, 0x2\n\n    aput v2, v0, v1\n    :try_end_1\n    .catch Ljava/lang/NoSuchFieldError; {:try_start_1 .. :try_end_1} :catch_1\n\n    :catch_1\n    :try_start_2\n    sget-object v0, Ltrycatch/TestEmptyCatch;->$SwitchMap$Access:[I\n\n    sget-object v1, Lother/EnumAccess;->MODIFY_CONST:Lother/EnumAccess;\n\n    invoke-virtual {v1}, Ljava/lang/Enum;->ordinal()I\n\n    move-result v1\n\n    const/4 v2, 0x3\n\n    aput v2, v0, v1\n    :try_end_2\n    .catch Ljava/lang/NoSuchFieldError; {:try_start_2 .. :try_end_2} :catch_2\n\n    :catch_2\n    :try_start_3\n    sget-object v0, Ltrycatch/TestEmptyCatch;->$SwitchMap$Access:[I\n\n    sget-object v1, Lother/EnumAccess;->MODIFY_GETTER_SETTER:Lother/EnumAccess;\n\n    invoke-virtual {v1}, Ljava/lang/Enum;->ordinal()I\n\n    move-result v1\n\n    const/4 v2, 0x4\n\n    aput v2, v0, v1\n    :try_end_3\n    .catch Ljava/lang/NoSuchFieldError; {:try_start_3 .. :try_end_3} :catch_3\n\n    :catch_3\n    :try_start_4\n    sget-object v0, Ltrycatch/TestEmptyCatch;->$SwitchMap$Access:[I\n\n    sget-object v1, Lother/EnumAccess;->CONVERT_ACCESSOR_TO_DATA:Lother/EnumAccess;\n\n    invoke-virtual {v1}, Ljava/lang/Enum;->ordinal()I\n\n    move-result v1\n\n    const/4 v2, 0x5\n\n    aput v2, v0, v1\n    :try_end_4\n    .catch Ljava/lang/NoSuchFieldError; {:try_start_4 .. :try_end_4} :catch_4\n\n    :catch_4\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/trycatch/TestFinally3.smali",
    "content": ".class public Ltrycatch/TestFinally3;\n.super Ljava/lang/Object;\n\n.field public bytes:[B\n\n.method public test()[B\n    .registers 4\n    .annotation system Ldalvik/annotation/Throws;\n        value = {\n            Ljava/lang/Exception;\n        }\n    .end annotation\n\n    const/4 v0, 0x0\n\n    :try_start_1\n    iget-object v1, p0, Ltrycatch/TestFinally3;->bytes:[B\n\n    if-nez v1, :cond_1a\n\n    invoke-direct {p0}, Ltrycatch/TestFinally3;->validate()Z\n    :try_end_8\n    .catchall {:try_start_1 .. :try_end_8} :catchall_24\n\n    move-result v1\n\n    if-nez v1, :cond_10\n\n    invoke-static {v0}, Ltrycatch/TestFinally3;->close(Ljava/io/InputStream;)V\n    return-object v0\n\n    :cond_10\n    :try_start_10\n    invoke-direct {p0}, Ltrycatch/TestFinally3;->getInputStream()Ljava/io/InputStream;\n    move-result-object v0\n\n    invoke-direct {p0, v0}, Ltrycatch/TestFinally3;->read(Ljava/io/InputStream;)[B\n    move-result-object v1\n\n    iput-object v1, p0, Ltrycatch/TestFinally3;->bytes:[B\n\n    :cond_1a\n    iget-object v1, p0, Ltrycatch/TestFinally3;->bytes:[B\n\n    invoke-direct {p0, v1}, Ltrycatch/TestFinally3;->convert([B)[B\n    :try_end_1f\n    .catchall {:try_start_10 .. :try_end_1f} :catchall_24\n\n    move-result-object v1\n\n    invoke-static {v0}, Ltrycatch/TestFinally3;->close(Ljava/io/InputStream;)V\n    return-object v1\n\n    :catchall_24\n    move-exception v1\n    invoke-static {v0}, Ltrycatch/TestFinally3;->close(Ljava/io/InputStream;)V\n    throw v1\n.end method\n\n.method private convert([B)[B\n    .registers 3\n    .annotation system Ldalvik/annotation/Throws;\n        value = {\n            Ljava/lang/Exception;\n        }\n    .end annotation\n\n    const/4 v0, 0x0\n    new-array v0, v0, [B\n    return-object v0\n.end method\n\n.method private static close(Ljava/io/InputStream;)V\n    .registers 1\n    return-void\n.end method\n\n.method private getInputStream()Ljava/io/InputStream;\n    .registers 3\n    .annotation system Ldalvik/annotation/Throws;\n        value = {\n            Ljava/lang/Exception;\n        }\n    .end annotation\n\n    new-instance v0, Ljava/io/ByteArrayInputStream;\n    const/4 v1, 0x0\n    new-array v1, v1, [B\n    invoke-direct {v0, v1}, Ljava/io/ByteArrayInputStream;-><init>([B)V\n    return-object v0\n.end method\n\n.method private read(Ljava/io/InputStream;)[B\n    .registers 3\n    .annotation system Ldalvik/annotation/Throws;\n        value = {\n            Ljava/lang/Exception;\n        }\n    .end annotation\n\n    const/4 v0, 0x0\n    new-array v0, v0, [B\n    return-object v0\n.end method\n\n.method private validate()Z\n    .registers 2\n    .annotation system Ldalvik/annotation/Throws;\n        value = {\n            Ljava/lang/Exception;\n        }\n    .end annotation\n\n    const/4 v0, 0x0\n    return v0\n.end method\n\n\n"
  },
  {
    "path": "jadx-core/src/test/smali/trycatch/TestLoopInTryCatch.smali",
    "content": ".class public Ltrycatch/TestLoopInTryCatch;\n.super Ljava/lang/Object;\n.source \"SourceFile\"\n\n.method public static test()V\n    .registers 6\n\n    :try_start\n\n    :loop\n\n    invoke-static {}, Ltrycatch/TestLoopInTryCatch;->getI()I\n    move-result v1\n\n    const/4 v2, 0x1\n\n    if-eq v1, v2, :cond\n\n    const/4 v3, 0x2\n\n    if-eq v1, v3, :cond\n\n    goto :loop\n\n    :cond\n    if-eq v1, v2, :end\n\tinvoke-static {}, Ltrycatch/TestLoopInTryCatch;->getI()I\n    return-void\n\n    :try_end\n    .catch Ljava/lang/RuntimeException; {:try_start .. :try_end} :end\n\n    :end\n    return-void\n.end method\n\n.method public static getI()I\n    .locals 2\n\n    const/4 v1, 0x1\n    return v1\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/trycatch/TestMultiExceptionCatchSameJump.smali",
    "content": ".class public Ltrycatch/TestMultiExceptionCatchSameJump;\n.super Ljava/lang/Object;\n.source \"TestMultiExceptionCatchSameJump.java\"\n\n.method public test()V\n    .locals 2\n\n    .line 17\n    :try_start_0\n    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;\n\n    const-string v1, \"Test\"\n\n    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n    :try_end_0\n    .catch Ljava/security/ProviderException; {:try_start_0 .. :try_end_0} :catch_0\n    .catch Ljava/time/DateTimeException; {:try_start_0 .. :try_end_0} :catch_0\n\n    .line 20\n    nop\n\n    .line 22\n    return-void\n\n    .line 18\n    :catch_0\n    move-exception v0\n\n    .line 19\n    .local v0, \"e\":Ljava/lang/RuntimeException;\n    new-instance v1, Ljava/lang/RuntimeException;\n\n    invoke-direct {v1, v0}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/Throwable;)V\n\n    throw v1\n.end method\n\n"
  },
  {
    "path": "jadx-core/src/test/smali/trycatch/TestNestedTryCatch4.smali",
    "content": ".class public Ltrycatch/TestNestedTryCatch4;\n.super Landroid/app/NativeActivity;\n\n.method private test(Landroid/content/Intent;)V\n    .registers 11\n    .annotation system Ldalvik/annotation/MethodParameters;\n        accessFlags = {\n            0x0\n        }\n        names = {\n            \"intent\"\n        }\n    .end annotation\n\n    const-string v0, \"IOException while closing input stream\\n\"\n\n    if-nez p1, :cond_5\n\n    return-void\n\n    :cond_5\n    const-string v1, \"intent_cmd\"\n\n    .line 1740\n    invoke-virtual {p1, v1}, Landroid/content/Intent;->getStringExtra(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v1\n\n    const-string v2, \"MCPE\"\n\n    if-eqz v1, :cond_80\n\n    .line 1741\n    invoke-virtual {v1}, Ljava/lang/String;->length()I\n\n    move-result v3\n\n    if-lez v3, :cond_80\n\n    .line 1743\n    :try_start_15\n    new-instance p1, Lorg/json/JSONObject;\n\n    invoke-direct {p1, v1}, Lorg/json/JSONObject;-><init>(Ljava/lang/String;)V\n\n    const-string v0, \"Command\"\n\n    .line 1744\n    invoke-virtual {p1, v0}, Lorg/json/JSONObject;->getString(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v0\n\n    const-string v1, \"keyboardResult\"\n\n    .line 1745\n    invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v1\n\n    if-eqz v1, :cond_33\n\n    const-string v0, \"Text\"\n\n    .line 1746\n    invoke-virtual {p1, v0}, Lorg/json/JSONObject;->getString(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object p1\n\n    invoke-virtual {p0, p1}, Ltrycatch/TestNestedTryCatch4;->nativeSetTextboxText(Ljava/lang/String;)V\n\n    goto/16 :goto_208\n\n    :cond_33\n    const-string v1, \"fileDialogResult\"\n\n    .line 1748\n    invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-eqz v0, :cond_208\n\n    iget-wide v0, p0, Ltrycatch/TestNestedTryCatch4;->mFileDialogCallback:J\n\n    const-wide/16 v3, 0x0\n\n    cmp-long v5, v0, v3\n\n    if-eqz v5, :cond_208\n\n    const-string v0, \"Result\"\n\n    .line 1749\n    invoke-virtual {p1, v0}, Lorg/json/JSONObject;->getString(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v0\n\n    const-string v1, \"Ok\"\n\n    invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-eqz v0, :cond_5d\n\n    .line 1750\n    iget-wide v0, p0, Ltrycatch/TestNestedTryCatch4;->mFileDialogCallback:J\n\n    const-string v5, \"Path\"\n\n    invoke-virtual {p1, v5}, Lorg/json/JSONObject;->getString(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object p1\n\n    invoke-virtual {p0, v0, v1, p1}, Ltrycatch/TestNestedTryCatch4;->nativeOnPickImageSuccess(JLjava/lang/String;)V\n\n    goto :goto_62\n\n    .line 1753\n    :cond_5d\n    iget-wide v0, p0, Ltrycatch/TestNestedTryCatch4;->mFileDialogCallback:J\n\n    invoke-virtual {p0, v0, v1}, Ltrycatch/TestNestedTryCatch4;->nativeOnPickImageCanceled(J)V\n\n    .line 1755\n    :goto_62\n    iput-wide v3, p0, Ltrycatch/TestNestedTryCatch4;->mFileDialogCallback:J\n    :try_end_64\n    .catch Lorg/json/JSONException; {:try_start_15 .. :try_end_64} :catch_66\n\n    goto/16 :goto_208\n\n    :catch_66\n    move-exception p1\n\n    .line 1759\n    new-instance v0, Ljava/lang/StringBuilder;\n\n    invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V\n\n    const-string v1, \"JSONObject exception:\"\n\n    invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {p1}, Lorg/json/JSONException;->toString()Ljava/lang/String;\n\n    move-result-object p1\n\n    invoke-virtual {v0, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object p1\n\n    invoke-static {v2, p1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    return-void\n\n    .line 1765\n    :cond_80\n    invoke-virtual {p1}, Landroid/content/Intent;->getAction()Ljava/lang/String;\n\n    move-result-object v1\n\n    .line 1766\n    invoke-virtual {p1}, Landroid/content/Intent;->getType()Ljava/lang/String;\n\n    const-string/jumbo v3, \"xbox_live_game_invite\"\n\n    .line 1768\n    invoke-virtual {v3, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v3\n\n    if-eqz v3, :cond_b0\n\n    const-string/jumbo v0, \"xbl\"\n\n    .line 1771\n    invoke-virtual {p1, v0}, Landroid/content/Intent;->getStringExtra(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object p1\n\n    .line 1772\n    new-instance v0, Ljava/lang/StringBuilder;\n\n    invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V\n\n    const-string v3, \"[XboxLive] Received Invite \"\n\n    invoke-virtual {v0, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v0, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v0\n\n    invoke-static {v2, v0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 1776\n    invoke-virtual {p0, v1, p1}, Ltrycatch/TestNestedTryCatch4;->nativeProcessIntentUriQuery(Ljava/lang/String;Ljava/lang/String;)V\n\n    goto/16 :goto_208\n\n    :cond_b0\n    const-string v3, \"android.intent.action.VIEW\"\n\n    .line 1779\n    invoke-virtual {v3, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v3\n\n    if-nez v3, :cond_c0\n\n    const-string v3, \"org.chromium.arc.intent.action.VIEW\"\n\n    invoke-virtual {v3, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v1\n\n    if-eqz v1, :cond_208\n\n    .line 1780\n    :cond_c0\n    invoke-virtual {p1}, Landroid/content/Intent;->getScheme()Ljava/lang/String;\n\n    move-result-object v1\n\n    .line 1781\n    invoke-virtual {p1}, Landroid/content/Intent;->getData()Landroid/net/Uri;\n\n    move-result-object p1\n\n    if-nez p1, :cond_cb\n\n    return-void\n\n    :cond_cb\n    const-string v3, \"minecraft\"\n\n    .line 1787\n    invoke-virtual {v3, v1}, Ljava/lang/String;->equalsIgnoreCase(Ljava/lang/String;)Z\n\n    move-result v3\n\n    if-nez v3, :cond_1f9\n\n    const-string v3, \"minecraftedu\"\n\n    invoke-virtual {v3, v1}, Ljava/lang/String;->equalsIgnoreCase(Ljava/lang/String;)Z\n\n    move-result v3\n\n    if-eqz v3, :cond_dd\n\n    goto/16 :goto_1f9\n\n    :cond_dd\n    const-string v3, \"file\"\n\n    .line 1795\n    invoke-virtual {v3, v1}, Ljava/lang/String;->equalsIgnoreCase(Ljava/lang/String;)Z\n\n    move-result v3\n\n    const-string v4, \"&\"\n\n    if-eqz v3, :cond_108\n\n    .line 1798\n    new-instance v0, Ljava/lang/StringBuilder;\n\n    invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V\n\n    invoke-virtual {p1}, Landroid/net/Uri;->getPath()Ljava/lang/String;\n\n    move-result-object v1\n\n    invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v0, v4}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {p1}, Landroid/net/Uri;->getPath()Ljava/lang/String;\n\n    move-result-object p1\n\n    invoke-virtual {v0, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object p1\n\n    const-string v0, \"fileIntent\"\n\n    invoke-virtual {p0, v0, p1}, Ltrycatch/TestNestedTryCatch4;->nativeProcessIntentUriQuery(Ljava/lang/String;Ljava/lang/String;)V\n\n    goto/16 :goto_208\n\n    :cond_108\n    const-string v3, \"content\"\n\n    .line 1800\n    invoke-virtual {v3, v1}, Ljava/lang/String;->equalsIgnoreCase(Ljava/lang/String;)Z\n\n    move-result v1\n\n    if-eqz v1, :cond_208\n\n    .line 1803\n    new-instance v1, Ljava/io/File;\n\n    invoke-virtual {p1}, Landroid/net/Uri;->getPath()Ljava/lang/String;\n\n    move-result-object v3\n\n    invoke-direct {v1, v3}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v1}, Ljava/io/File;->getName()Ljava/lang/String;\n\n    move-result-object v1\n\n    .line 1804\n    new-instance v3, Ljava/io/File;\n\n    new-instance v5, Ljava/lang/StringBuilder;\n\n    invoke-direct {v5}, Ljava/lang/StringBuilder;-><init>()V\n\n    invoke-virtual {p0}, Ltrycatch/TestNestedTryCatch4;->getApplicationContext()Landroid/content/Context;\n\n    move-result-object v6\n\n    invoke-virtual {v6}, Landroid/content/Context;->getCacheDir()Ljava/io/File;\n\n    move-result-object v6\n\n    invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/Object;)Ljava/lang/StringBuilder;\n\n    const-string v6, \"/\"\n\n    invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v5, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v1\n\n    invoke-direct {v3, v1}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    .line 1806\n    invoke-virtual {p0}, Ltrycatch/TestNestedTryCatch4;->getContentResolver()Landroid/content/ContentResolver;\n\n    move-result-object v1\n\n    .line 1810\n    :try_start_142\n    invoke-virtual {v1, p1}, Landroid/content/ContentResolver;->openInputStream(Landroid/net/Uri;)Ljava/io/InputStream;\n\n    move-result-object v1\n    :try_end_146\n    .catch Ljava/io/IOException; {:try_start_142 .. :try_end_146} :catch_1df\n\n    .line 1818\n    :try_start_146\n    new-instance v5, Ljava/io/FileOutputStream;\n\n    invoke-direct {v5, v3}, Ljava/io/FileOutputStream;-><init>(Ljava/io/File;)V\n\n    const/high16 v6, 0x100000\n\n    new-array v6, v6, [B\n\n    .line 1824\n    :goto_14f\n    invoke-virtual {v1, v6}, Ljava/io/InputStream;->read([B)I\n\n    move-result v7\n\n    const/4 v8, -0x1\n\n    if-eq v7, v8, :cond_15b\n\n    const/4 v8, 0x0\n\n    .line 1825\n    invoke-virtual {v5, v6, v8, v7}, Ljava/io/OutputStream;->write([BII)V\n\n    goto :goto_14f\n\n    .line 1828\n    :cond_15b\n    invoke-virtual {v5}, Ljava/io/OutputStream;->close()V\n\n    const-string v5, \"contentIntent\"\n\n    .line 1831\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    invoke-direct {v6}, Ljava/lang/StringBuilder;-><init>()V\n\n    invoke-virtual {p1}, Landroid/net/Uri;->getPath()Ljava/lang/String;\n\n    move-result-object p1\n\n    invoke-virtual {v6, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v6, v4}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v3}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;\n\n    move-result-object p1\n\n    invoke-virtual {v6, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object p1\n\n    invoke-virtual {p0, v5, p1}, Ltrycatch/TestNestedTryCatch4;->nativeProcessIntentUriQuery(Ljava/lang/String;Ljava/lang/String;)V\n    :try_end_17d\n    .catch Ljava/io/IOException; {:try_start_146 .. :try_end_17d} :catch_18b\n    .catchall {:try_start_146 .. :try_end_17d} :catchall_189\n\n    .line 1842\n    :try_start_17d\n    invoke-virtual {v1}, Ljava/io/InputStream;->close()V\n    :try_end_180\n    .catch Ljava/io/IOException; {:try_start_17d .. :try_end_180} :catch_182\n\n    goto/16 :goto_208\n\n    :catch_182\n    move-exception p1\n\n    .line 1845\n    new-instance v1, Ljava/lang/StringBuilder;\n\n    invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V\n\n    goto :goto_1b1\n\n    :catchall_189\n    move-exception p1\n\n    goto :goto_1c3\n\n    :catch_18b\n    move-exception p1\n\n    .line 1833\n    :try_start_18c\n    new-instance v4, Ljava/lang/StringBuilder;\n\n    invoke-direct {v4}, Ljava/lang/StringBuilder;-><init>()V\n\n    const-string v5, \"IOException while copying file from content intent\\n\"\n\n    invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {p1}, Ljava/io/IOException;->toString()Ljava/lang/String;\n\n    move-result-object p1\n\n    invoke-virtual {v4, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v4}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object p1\n\n    invoke-static {v2, p1}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I\n    :try_end_1a4\n    .catchall {:try_start_18c .. :try_end_1a4} :catchall_189\n\n    .line 1837\n    :try_start_1a4\n    invoke-virtual {v3}, Ljava/io/File;->delete()Z\n    :try_end_1a7\n    .catch Ljava/lang/Exception; {:try_start_1a4 .. :try_end_1a7} :catch_1a7\n    .catchall {:try_start_1a4 .. :try_end_1a7} :catchall_189\n\n    .line 1842\n    :catch_1a7\n    :try_start_1a7\n    invoke-virtual {v1}, Ljava/io/InputStream;->close()V\n    :try_end_1aa\n    .catch Ljava/io/IOException; {:try_start_1a7 .. :try_end_1aa} :catch_1ab\n\n    goto :goto_208\n\n    :catch_1ab\n    move-exception p1\n\n    .line 1845\n    new-instance v1, Ljava/lang/StringBuilder;\n\n    invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V\n\n    :goto_1b1\n    invoke-virtual {v1, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {p1}, Ljava/io/IOException;->toString()Ljava/lang/String;\n\n    move-result-object p1\n\n    invoke-virtual {v1, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object p1\n\n    invoke-static {v2, p1}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto :goto_208\n\n    .line 1842\n    :goto_1c3\n    :try_start_1c3\n    invoke-virtual {v1}, Ljava/io/InputStream;->close()V\n    :try_end_1c6\n    .catch Ljava/io/IOException; {:try_start_1c3 .. :try_end_1c6} :catch_1c7\n\n    goto :goto_1de\n\n    :catch_1c7\n    move-exception v1\n\n    .line 1845\n    new-instance v3, Ljava/lang/StringBuilder;\n\n    invoke-direct {v3}, Ljava/lang/StringBuilder;-><init>()V\n\n    invoke-virtual {v3, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v1}, Ljava/io/IOException;->toString()Ljava/lang/String;\n\n    move-result-object v0\n\n    invoke-virtual {v3, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v0\n\n    invoke-static {v2, v0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 1847\n    :goto_1de\n    throw p1\n\n    :catch_1df\n    move-exception p1\n\n    .line 1813\n    new-instance v0, Ljava/lang/StringBuilder;\n\n    invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V\n\n    const-string v1, \"IOException while opening file from content intent\\n\"\n\n    invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {p1}, Ljava/io/IOException;->toString()Ljava/lang/String;\n\n    move-result-object p1\n\n    invoke-virtual {v0, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object p1\n\n    invoke-static {v2, p1}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I\n\n    return-void\n\n    .line 1788\n    :cond_1f9\n    :goto_1f9\n    invoke-virtual {p1}, Landroid/net/Uri;->getHost()Ljava/lang/String;\n\n    move-result-object v0\n\n    .line 1789\n    invoke-virtual {p1}, Landroid/net/Uri;->getQuery()Ljava/lang/String;\n\n    move-result-object p1\n\n    if-nez v0, :cond_205\n\n    if-eqz p1, :cond_208\n\n    .line 1792\n    :cond_205\n    invoke-virtual {p0, v0, p1}, Ltrycatch/TestNestedTryCatch4;->nativeProcessIntentUriQuery(Ljava/lang/String;Ljava/lang/String;)V\n\n    :cond_208\n    :goto_208\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/trycatch/TestNestedTryCatch5.smali",
    "content": ".class public Ltrycatch/TestNestedTryCatch5;\n.super Ljava/lang/Object;\n\n.method public test(Landroid/database/sqlite/SQLiteDatabase;)V\n    .registers 13\n\n    const/4 v0, 0x0\n    invoke-static {p1, v0}, LX/7Yz;->A0I(Ljava/lang/Object;I)V\n    iget-boolean v0, p0, LX/00y;->A00:Z\n    if-nez v0, :cond_9e\n\n    :try_start_8\n    iget-object v8, p0, LX/00y;->A03:LX/0Ux;\n    iget-object v0, p0, LX/00y;->A04:LX/0Km;\n    invoke-static {p1, v0}, LX/00y;->A01(Landroid/database/sqlite/SQLiteDatabase;LX/0Km;)LX/0g9;\n    move-result-object v10\n\n    check-cast v8, LX/0AB;\n    invoke-virtual {v8, v10}, LX/0AB;->A05(LX/0wU;)V\n    iget-object v0, v8, LX/0AB;->A01:LX/0Z4;\n    iget-object v9, v0, LX/0Z4;->A00:Landroidx/work/impl/WorkDatabase_Impl;\n    iput-object v10, v9, LX/0Rt;->A0B:LX/0wU;\n    const-string v0, \"PRAGMA foreign_keys = ON\"\n    invoke-interface {v10, v0}, LX/0wU;->Auv(Ljava/lang/String;)V\n    iget-object v1, v9, LX/0Rt;->A06:LX/0Uj;\n    iget-object v2, v1, LX/0Uj;->A05:Ljava/lang/Object;\n\n    monitor-enter v2\n    :try_end_25\n    .catchall {:try_start_8 .. :try_end_25} :catchall_95\n\n    :try_start_25\n    iget-boolean v0, v1, LX/0Uj;->A0D:Z\n\n    if-eqz v0, :cond_31\n    const-string v1, \"ROOM\"\n    const-string v0, \"Invalidation tracker is initialized twice :/.\"\n    invoke-static {v1, v0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I\n    goto :goto_4e\n\n    :cond_31\n    const-string v0, \"PRAGMA temp_store = MEMORY;\"\n    invoke-interface {v10, v0}, LX/0wU;->Auv(Ljava/lang/String;)V\n    const-string v0, \"PRAGMA recursive_triggers=\\'ON\\';\"\n    invoke-interface {v10, v0}, LX/0wU;->Auv(Ljava/lang/String;)V\n    const-string v0, \"CREATE TEMP TABLE room_table_modification_log (table_id INTEGER PRIMARY KEY, invalidated INTEGER NOT NULL DEFAULT 0)\"\n    invoke-interface {v10, v0}, LX/0wU;->Auv(Ljava/lang/String;)V\n    invoke-virtual {v1, v10}, LX/0Uj;->A00(LX/0wU;)V\n    const-string v0, \"UPDATE room_table_modification_log SET invalidated = 0 WHERE invalidated = 1\"\n    invoke-interface {v10, v0}, LX/0wU;->ArR(Ljava/lang/String;)LX/0wJ;\n    move-result-object v0\n\n    iput-object v0, v1, LX/0Uj;->A0C:LX/0wJ;\n    const/4 v0, 0x1\n    iput-boolean v0, v1, LX/0Uj;->A0D:Z\n    :try_end_4e\n    .catchall {:try_start_25 .. :try_end_4e} :catchall_92\n\n    :goto_4e\n    :try_start_4e\n    monitor-exit v2\n\n    iget-object v0, v9, LX/0Rt;->A01:Ljava/util/List;\n    if-eqz v0, :cond_8e\n    invoke-interface {v0}, Ljava/util/List;->size()I\n    move-result v7\n\n    const/4 v6, 0x0\n\n    :goto_58\n    if-ge v6, v7, :cond_8e\n\n    iget-object v0, v9, LX/0Rt;->A01:Ljava/util/List;\n    invoke-interface {v0, v6}, Ljava/util/List;->get(I)Ljava/lang/Object;\n    iget-object v5, v10, LX/0g9;->A00:Landroid/database/sqlite/SQLiteDatabase;\n    invoke-virtual {v5}, Landroid/database/sqlite/SQLiteDatabase;->beginTransaction()V\n    :try_end_64\n    .catchall {:try_start_4e .. :try_end_64} :catchall_95\n\n    :try_start_64\n    invoke-static {}, LX/001;->A0r()Ljava/lang/StringBuilder;\n    move-result-object v4\n\n    const-string v0, \"DELETE FROM workspec WHERE state IN (2, 3, 5) AND (last_enqueue_time + minimum_retention_duration) < \"\n    invoke-virtual {v4, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    invoke-static {}, Ljava/lang/System;->currentTimeMillis()J\n    move-result-wide v2\n\n    sget-wide v0, LX/0Jm;->A00:J\n    sub-long/2addr v2, v0\n    invoke-virtual {v4, v2, v3}, Ljava/lang/StringBuilder;->append(J)Ljava/lang/StringBuilder;\n    const-string v0, \" AND (SELECT COUNT(*)=0 FROM dependency WHERE     prerequisite_id=id AND     work_spec_id NOT IN         (SELECT id FROM workspec WHERE state IN (2, 3, 5)))\"\n    invoke-static {v0, v4}, LX/000;->A0X(Ljava/lang/String;Ljava/lang/StringBuilder;)Ljava/lang/String;\n    move-result-object v0\n\n    invoke-interface {v10, v0}, LX/0wU;->Auv(Ljava/lang/String;)V\n    invoke-virtual {v5}, Landroid/database/sqlite/SQLiteDatabase;->setTransactionSuccessful()V\n    :try_end_83\n    .catchall {:try_start_64 .. :try_end_83} :catchall_89\n\n    :try_start_83\n    invoke-virtual {v5}, Landroid/database/sqlite/SQLiteDatabase;->endTransaction()V\n    add-int/lit8 v6, v6, 0x1\n    goto :goto_58\n\n    :catchall_89\n    move-exception v0\n    invoke-virtual {v5}, Landroid/database/sqlite/SQLiteDatabase;->endTransaction()V\n    goto :goto_94\n\n    :cond_8e\n    const/4 v0, 0x0\n    iput-object v0, v8, LX/0AB;->A00:LX/0N8;\n    goto :goto_9e\n\n    :catchall_92\n    move-exception v0\n    monitor-exit v2\n\n    :goto_94\n    throw v0\n    :try_end_95\n    .catchall {:try_start_83 .. :try_end_95} :catchall_95\n\n    :catchall_95\n    move-exception v2\n    sget-object v1, LX/0Fu;->A04:LX/0Fu;\n    new-instance v0, LX/0oe;\n    invoke-direct {v0, v1, v2}, LX/0oe;-><init>(LX/0Fu;Ljava/lang/Throwable;)V\n    throw v0\n\n    :cond_9e\n    :goto_9e\n    const/4 v0, 0x1\n    iput-boolean v0, p0, LX/00y;->A01:Z\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/trycatch/TestTryCatch10.smali",
    "content": ".class public Ltrycatch/TestTryCatch10;\n.super Ljava/lang/Object;\n\n.field public static VERSION:I\n\n.method public static test(I)Z\n    .registers 5\n\n    sget v0, Ltrycatch/TestTryCatch10;->VERSION:I\n    const/16 v1, 0x1d\n    const/4 v2, 0x0\n    if-lt v0, v1, :cond_1b\n    const-string v0, \"custom\"\n    invoke-static {v0}, Ltrycatch/TestTryCatch10;->check(Ljava/lang/String;)Z\n    move-result v0\n    if-nez v0, :cond_10\n    goto :goto_1b\n\n    :cond_10\n    :try_start_10\n    invoke-static {p0}, Ltrycatch/TestTryCatch10;->getVar(I)I\n    move-result p0\n    :try_end_18\n    .catch Ljava/lang/Exception; {:try_start_10 .. :try_end_18} :catch_1b\n\n    if-eqz p0, :cond_1b\n    const/4 v2, 0x1\n\n    :catch_1b\n    :cond_1b\n    :goto_1b\n    return v2\n.end method\n\n.method public static getVar(I)I\n    .locals 0\n    return p0\n.end method\n\n.method public static check(Ljava/lang/String;)Z\n    .locals 1\n    const/4 v0, 0x0\n    return v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/trycatch/TestTryCatchFinally10.smali",
    "content": ".class public Ltrycatch/TestTryCatchFinally10;\n.super Ljava/lang/Object;\n\n\n# static fields\n.field private static final l:Llog/DebugLogger;\n\n.method public static test(Landroid/content/Context;I)Ljava/lang/String;\n    .locals 2\n\n    .line 46\n    invoke-static {p0}, LCommonContracts;->requireNonNull(Ljava/lang/Object;)V\n\n    const/4 v0, 0x0\n\n    .line 50\n    :try_start_0\n    invoke-virtual {p0}, Landroid/content/Context;->getResources()Landroid/content/res/Resources;\n\n    move-result-object p0\n\n    invoke-virtual {p0, p1}, Landroid/content/res/Resources;->openRawResource(I)Ljava/io/InputStream;\n\n    move-result-object v0\n\n    .line 51\n    new-instance p0, Ljava/util/Scanner;\n\n    invoke-direct {p0, v0}, Ljava/util/Scanner;-><init>(Ljava/io/InputStream;)V\n\n    const-string p1, \"\\\\A\"\n\n    invoke-virtual {p0, p1}, Ljava/util/Scanner;->useDelimiter(Ljava/lang/String;)Ljava/util/Scanner;\n\n    move-result-object p0\n\n    .line 52\n    invoke-virtual {p0}, Ljava/util/Scanner;->hasNext()Z\n\n    move-result p1\n\n    if-eqz p1, :cond_0\n\n    invoke-virtual {p0}, Ljava/util/Scanner;->next()Ljava/lang/String;\n\n    move-result-object p0\n\n    goto :goto_0\n\n    :cond_0\n    const-string p0, \"\"\n    :try_end_0\n    .catchall {:try_start_0 .. :try_end_0} :catchall_0\n\n    :goto_0\n    if-eqz v0, :cond_1\n\n    .line 56\n    :try_start_1\n    invoke-virtual {v0}, Ljava/io/InputStream;->close()V\n    :try_end_1\n    .catch Ljava/io/IOException; {:try_start_1 .. :try_end_1} :catch_0\n\n    goto :goto_1\n\n    :catch_0\n    move-exception p1\n\n    .line 58\n    sget-object v0, Ltrycatch/TestTryCatchFinally10;->l:Llog/DebugLogger;\n\n    sget-object v1, Llog/DebugLogger$LogLevel;->ERROR:Llog/DebugLogger$LogLevel;\n\n    invoke-virtual {v0, v1, p1}, Llog/DebugLogger;->logException(Llog/DebugLogger$LogLevel;Ljava/lang/Exception;)V\n\n    :cond_1\n    :goto_1\n    return-object p0\n\n    :catchall_0\n    move-exception p0\n\n    if-eqz v0, :cond_2\n\n    .line 56\n    :try_start_2\n    invoke-virtual {v0}, Ljava/io/InputStream;->close()V\n    :try_end_2\n    .catch Ljava/io/IOException; {:try_start_2 .. :try_end_2} :catch_1\n\n    goto :goto_2\n\n    :catch_1\n    move-exception p1\n\n    .line 58\n    sget-object v0, Ltrycatch/TestTryCatchFinally10;->l:Llog/DebugLogger;\n\n    sget-object v1, Llog/DebugLogger$LogLevel;->ERROR:Llog/DebugLogger$LogLevel;\n\n    invoke-virtual {v0, v1, p1}, Llog/DebugLogger;->logException(Llog/DebugLogger$LogLevel;Ljava/lang/Exception;)V\n\n    .line 61\n    :cond_2\n    :goto_2\n    throw p0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/trycatch/TestTryCatchFinally15.smali",
    "content": ".class public Ltrycatch/TestTryCatchFinally15;\n.super Ljava/lang/Object;\n\n.implements Landroid/os/IInterface;\n\n.field private final zza:Landroid/os/IBinder;\n.field private final zzb:Ljava/lang/String;\n\n.method protected final test(ILandroid/os/Parcel;)Landroid/os/Parcel;\n    .registers 6\n    .annotation system Ldalvik/annotation/Throws;\n        value = {\n            Landroid/os/RemoteException;\n        }\n    .end annotation\n\n    .line 1\n    invoke-static {}, Landroid/os/Parcel;->obtain()Landroid/os/Parcel;\n    move-result-object v0\n\n    :try_start_4\n    iget-object v1, p0, Ltrycatch/TestTryCatchFinally15;->zza:Landroid/os/IBinder;\n    const/4 v2, 0x0\n\n    .line 2\n    invoke-interface {v1, p1, p2, v0, v2}, Landroid/os/IBinder;->transact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z\n    .line 3\n    invoke-virtual {v0}, Landroid/os/Parcel;->readException()V\n    :try_end_d\n    .catch Ljava/lang/RuntimeException; {:try_start_4 .. :try_end_d} :catch_13\n    .catchall {:try_start_4 .. :try_end_d} :catchall_11\n\n    .line 6\n    invoke-virtual {p2}, Landroid/os/Parcel;->recycle()V\n    return-object v0\n\n    :catchall_11\n    move-exception p1\n    goto :goto_18\n\n    .line 5\n    :catch_13\n    move-exception p1\n\n    .line 4\n    :try_start_14\n    invoke-virtual {v0}, Landroid/os/Parcel;->recycle()V\n\n    .line 5\n    throw p1\n    :try_end_18\n    .catchall {:try_start_14 .. :try_end_18} :catchall_11\n\n    .line 6\n    :goto_18\n    invoke-virtual {p2}, Landroid/os/Parcel;->recycle()V\n\n    .line 7\n    throw p1\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/trycatch/TestTryCatchLastInsn.smali",
    "content": ".class public Ltrycatch/TestTryCatchLastInsn;\n.super Ljava/lang/Object;\n.source \"TestTryCatchLastInsn.java\"\n\n.method public test()Ljava/lang/Exception;\n    .registers 6\n\n    .prologue\n    const-string v1, \"result\"\n\n    :try_start\n    invoke-direct {p0}, Ltrycatch/TestTryCatchLastInsn;->call()Ljava/lang/Exception;\n    move-result-object v1\n    :try_end\n    .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch\n\n    :goto_return\n    return-object v1\n\n    :catch\n    move-exception v4\n    sget-object v3, Ljava/lang/System;->out:Ljava/io/PrintStream;\n    invoke-virtual {v3, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n    move-object v1, v4\n    goto :goto_return\n.end method\n\n\n.method private call()Ljava/lang/Exception;\n    .registers 2\n    new-instance v0, Ljava/lang/Exception;\n    invoke-direct {v0}, Ljava/lang/Exception;-><init>()V\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/trycatch/TestTryCatchMultiException2.smali",
    "content": ".class public Ltrycatch/TestTryCatchMultiException2;\n.super Ljava/lang/Object;\n\n\n.method public static test()Z\n    .registers 5\n\n    :try_start_b\n    const-string v0, \"c\"\n    invoke-static {v0}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class;\n    move-result-object v1\n\n    const/4 v0, 0x0\n    const-string v2, \"b\"\n    new-array v3, v0, [Ljava/lang/Class;\n    invoke-virtual {v1, v2, v3}, Ljava/lang/Class;->getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;\n    move-result-object v2\n\n    new-array v3, v0, [Ljava/lang/Object;\n    invoke-virtual {v2, v1, v3}, Ljava/lang/reflect/Method;->invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;\n    move-result-object v1\n\n    check-cast v1, Ljava/lang/Boolean;\n    invoke-virtual {v1}, Ljava/lang/Boolean;->booleanValue()Z\n\n    move-result v1\n    :try_end_2f\n    .catch Ljava/lang/ClassNotFoundException; {:try_start_b .. :try_end_2f} :catch_30\n    .catch Ljava/lang/NoSuchMethodException; {:try_start_b .. :try_end_2f} :catch_30\n    .catch Ljava/lang/Exception; {:try_start_b .. :try_end_2f} :catch_30\n    .catchall {:try_start_b .. :try_end_2f} :catchall_30\n\n    return v1\n\n    :catch_30\n    :catchall_30\n    return v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/trycatch/TestTryCatchNoMoveExc.smali",
    "content": ".class public Ltrycatch/TestTryCatchNoMoveExc;\n.super Ljava/lang/Object;\n\n.method private static test(Ljava/lang/AutoCloseable;)V\n    .locals 0\n\n    if-eqz p0, :cond_0\n\n    .line 187\n    :try_start_0\n    invoke-interface {p0}, Ljava/lang/AutoCloseable;->close()V\n    :try_end_0\n    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0\n\n    :catch_0\n    :cond_0\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/trycatch/TestTryCatchNoMoveExc2.smali",
    "content": ".class public Ltrycatch/TestTryCatchNoMoveExc2;\n.super Ljava/lang/Object;\n\n.method private static test(Ljava/lang/AutoCloseable;)V\n    .locals 0\n\n    :try_start_0\n    invoke-interface {p0}, Ljava/lang/AutoCloseable;->close()V\n    :try_end_0\n    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0\n\n    :catch_0\n    invoke-static {}, Ljava/lang/System;->nanoTime()J\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/trycatch/TestTryCatchStartOnMove.smali",
    "content": ".class public Ltrycatch/TestTryCatchStartOnMove;\n.super Ljava/lang/Object;\n\n\n# direct methods\n.method private static test(Ljava/lang/String;)V\n    .registers 5\n\n    :try_start\n    move v3, p0\n    invoke-static {v3}, Ltrycatch/TestTryCatchStartOnMove;->call(Ljava/lang/String;)V\n    :try_end\n    .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch\n\n    :goto_ret\n    return-void\n\n    :catch\n    move-exception v0\n    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;\n    new-instance v1, Ljava/lang/StringBuilder;\n    invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V\n    const-string v2, \"Failed call for \"\n    invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v1\n    invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v1\n    invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n    move-result-object v1\n    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n    goto :goto_ret\n.end method\n\n\n.method public constructor <init>()V\n    .registers 1\n    invoke-direct {p0}, Ljadx/tests/api/SmaliTest;-><init>()V\n    return-void\n.end method\n\n\n.method private static call(Ljava/lang/String;)V\n    .registers 1\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/trycatch/TestTryWithEmptyCatchTriple.smali",
    "content": ".class public Ltrycatch/TestTryWithEmptyCatchTriple;\n.super Ljava/lang/Object;\n\n.field static field:[I\n\n.method static test()V\n    .registers 3\n    const/4 v0, 0x1\n\n    :try_start_1\n    sget-object v1, Ltrycatch/TestTryWithEmptyCatchTriple;->field:[I\n\n    const/4 v2, 0x0\n\n    aput v0, v1, v2\n\n    :try_end_1\n    .catch Ljava/lang/Error; {:try_start_1 .. :try_end_1} :catch_1\n\n    :catch_1\n\n    sget-object v1, Ltrycatch/TestTryWithEmptyCatchTriple;->field:[I\n\n    array-length v1, v1\n\n    new-array v1, v1, [I\n\n    sput-object v1, Ltrycatch/TestTryWithEmptyCatchTriple;->field:[I\n\n    :try_start_2\n    sget-object v1, Ltrycatch/TestTryWithEmptyCatchTriple;->field:[I\n\n    const/4 v2, 0x0\n\n    aput v0, v1, v2\n    :try_end_2\n    .catch Ljava/lang/Error; {:try_start_2 .. :try_end_2} :catch_2\n\n    :catch_2\n    :try_start_3\n    sget-object v0, Ltrycatch/TestTryWithEmptyCatchTriple;->field:[I\n\n    const/4 v1, 0x0\n\n    const/4 v2, 0x2\n\n    aput v2, v0, v1\n    :try_end_3\n    .catch Ljava/lang/Error; {:try_start_3 .. :try_end_3} :catch_3\n\n    :catch_3\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/trycatch/TestUnreachableCatch.smali",
    "content": ".class public Ltrycatch/TestUnreachableCatch;\n.super Ljava/lang/Object;\n\n.method private static prepareFontData(Landroid/content/Context;[Landroid/provider/FontsContract$FontInfo;Landroid/os/CancellationSignal;)Ljava/util/Map;\n    .locals 18\n    .param p0, \"context\"    # Landroid/content/Context;\n    .param p1, \"fonts\"    # [Landroid/provider/FontsContract$FontInfo;\n    .param p2, \"cancellationSignal\"    # Landroid/os/CancellationSignal;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Landroid/content/Context;\",\n            \"[\",\n            \"Landroid/provider/FontsContract$FontInfo;\",\n            \"Landroid/os/CancellationSignal;\",\n            \")\",\n            \"Ljava/util/Map<\",\n            \"Landroid/net/Uri;\",\n            \"Ljava/nio/ByteBuffer;\",\n            \">;\"\n        }\n    .end annotation\n\n    .line 728\n    move-object/from16 v1, p1\n\n    new-instance v0, Ljava/util/HashMap;\n\n    invoke-direct {v0}, Ljava/util/HashMap;-><init>()V\n\n    move-object v2, v0\n\n    .line 729\n    .local v2, \"out\":Ljava/util/HashMap;, \"Ljava/util/HashMap<Landroid/net/Uri;Ljava/nio/ByteBuffer;>;\"\n    invoke-virtual/range {p0 .. p0}, Landroid/content/Context;->getContentResolver()Landroid/content/ContentResolver;\n\n    move-result-object v3\n\n    .line 731\n    .local v3, \"resolver\":Landroid/content/ContentResolver;\n    array-length v4, v1\n\n    const/4 v0, 0x0\n\n    move v5, v0\n\n    :goto_0\n    if-ge v5, v4, :cond_5\n\n    aget-object v6, v1, v5\n\n    .line 732\n    .local v6, \"font\":Landroid/provider/FontsContract$FontInfo;\n    invoke-virtual {v6}, Landroid/provider/FontsContract$FontInfo;->getResultCode()I\n\n    move-result v0\n\n    if-eqz v0, :cond_0\n\n    .line 733\n    move-object/from16 v9, p2\n\n    goto/16 :goto_5\n\n    .line 736\n    :cond_0\n    invoke-virtual {v6}, Landroid/provider/FontsContract$FontInfo;->getUri()Landroid/net/Uri;\n\n    move-result-object v7\n\n    .line 737\n    .local v7, \"uri\":Landroid/net/Uri;\n    invoke-virtual {v2, v7}, Ljava/util/HashMap;->containsKey(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-eqz v0, :cond_1\n\n    .line 738\n    move-object/from16 v9, p2\n\n    goto :goto_5\n\n    .line 741\n    :cond_1\n    const/4 v8, 0x0\n\n    .line 742\n    .local v8, \"buffer\":Ljava/nio/ByteBuffer;\n    :try_start_0\n    const-string/jumbo v0, \"r\"\n    :try_end_0\n    .catch Ljava/io/IOException; {:try_start_0 .. :try_end_0} :catch_2\n\n    .line 743\n    move-object/from16 v9, p2\n\n    :try_start_1\n    invoke-virtual {v3, v7, v0, v9}, Landroid/content/ContentResolver;->openFileDescriptor(Landroid/net/Uri;Ljava/lang/String;Landroid/os/CancellationSignal;)Landroid/os/ParcelFileDescriptor;\n\n    move-result-object v0\n    :try_end_1\n    .catch Ljava/io/IOException; {:try_start_1 .. :try_end_1} :catch_1\n\n    move-object v10, v0\n\n    .line 744\n    .local v10, \"pfd\":Landroid/os/ParcelFileDescriptor;\n    if-eqz v10, :cond_3\n\n    .line 745\n    :try_start_2\n    new-instance v0, Ljava/io/FileInputStream;\n\n    .line 746\n    invoke-virtual {v10}, Landroid/os/ParcelFileDescriptor;->getFileDescriptor()Ljava/io/FileDescriptor;\n\n    move-result-object v11\n\n    invoke-direct {v0, v11}, Ljava/io/FileInputStream;-><init>(Ljava/io/FileDescriptor;)V\n    :try_end_2\n    .catch Ljava/io/IOException; {:try_start_2 .. :try_end_2} :catch_0\n    .catchall {:try_start_2 .. :try_end_2} :catchall_2\n\n    move-object v11, v0\n\n    .line 747\n    .local v11, \"fis\":Ljava/io/FileInputStream;\n    :try_start_3\n    invoke-virtual {v11}, Ljava/io/FileInputStream;->getChannel()Ljava/nio/channels/FileChannel;\n\n    move-result-object v12\n\n    .line 748\n    .local v12, \"fileChannel\":Ljava/nio/channels/FileChannel;\n    invoke-virtual {v12}, Ljava/nio/channels/FileChannel;->size()J\n\n    move-result-wide v16\n\n    .line 749\n    .local v16, \"size\":J\n    sget-object v13, Ljava/nio/channels/FileChannel$MapMode;->READ_ONLY:Ljava/nio/channels/FileChannel$MapMode;\n\n    const-wide/16 v14, 0x0\n\n    invoke-virtual/range {v12 .. v17}, Ljava/nio/channels/FileChannel;->map(Ljava/nio/channels/FileChannel$MapMode;JJ)Ljava/nio/MappedByteBuffer;\n\n    move-result-object v0\n    :try_end_3\n    .catchall {:try_start_3 .. :try_end_3} :catchall_0\n\n    move-object v8, v0\n\n    .line 750\n    .end local v12    # \"fileChannel\":Ljava/nio/channels/FileChannel;\n    .end local v16    # \"size\":J\n    :try_start_4\n    invoke-virtual {v11}, Ljava/io/FileInputStream;->close()V\n    :try_end_4\n    .catch Ljava/io/IOException; {:try_start_4 .. :try_end_4} :catch_0\n    .catchall {:try_start_4 .. :try_end_4} :catchall_2\n\n    .line 752\n    .end local v11    # \"fis\":Ljava/io/FileInputStream;\n    goto :goto_3\n\n    .line 745\n    .restart local v11    # \"fis\":Ljava/io/FileInputStream;\n    :catchall_0\n    move-exception v0\n\n    move-object v12, v0\n\n    :try_start_5\n    invoke-virtual {v11}, Ljava/io/FileInputStream;->close()V\n    :try_end_5\n    .catchall {:try_start_5 .. :try_end_5} :catchall_1\n\n    goto :goto_1\n\n    :catchall_1\n    move-exception v0\n\n    move-object v13, v0\n\n    :try_start_6\n    invoke-virtual {v12, v13}, Ljava/lang/Throwable;->addSuppressed(Ljava/lang/Throwable;)V\n\n    .end local v2    # \"out\":Ljava/util/HashMap;, \"Ljava/util/HashMap<Landroid/net/Uri;Ljava/nio/ByteBuffer;>;\"\n    .end local v3    # \"resolver\":Landroid/content/ContentResolver;\n    .end local v6    # \"font\":Landroid/provider/FontsContract$FontInfo;\n    .end local v7    # \"uri\":Landroid/net/Uri;\n    .end local v8    # \"buffer\":Ljava/nio/ByteBuffer;\n    .end local v10    # \"pfd\":Landroid/os/ParcelFileDescriptor;\n    .end local p0    # \"context\":Landroid/content/Context;\n    .end local p1    # \"fonts\":[Landroid/provider/FontsContract$FontInfo;\n    .end local p2    # \"cancellationSignal\":Landroid/os/CancellationSignal;\n    :goto_1\n    throw v12\n    :try_end_6\n    .catch Ljava/io/IOException; {:try_start_6 .. :try_end_6} :catch_0\n    .catchall {:try_start_6 .. :try_end_6} :catchall_2\n\n    .line 742\n    .end local v11    # \"fis\":Ljava/io/FileInputStream;\n    .restart local v2    # \"out\":Ljava/util/HashMap;, \"Ljava/util/HashMap<Landroid/net/Uri;Ljava/nio/ByteBuffer;>;\"\n    .restart local v3    # \"resolver\":Landroid/content/ContentResolver;\n    .restart local v6    # \"font\":Landroid/provider/FontsContract$FontInfo;\n    .restart local v7    # \"uri\":Landroid/net/Uri;\n    .restart local v8    # \"buffer\":Ljava/nio/ByteBuffer;\n    .restart local v10    # \"pfd\":Landroid/os/ParcelFileDescriptor;\n    .restart local p0    # \"context\":Landroid/content/Context;\n    .restart local p1    # \"fonts\":[Landroid/provider/FontsContract$FontInfo;\n    .restart local p2    # \"cancellationSignal\":Landroid/os/CancellationSignal;\n    :catchall_2\n    move-exception v0\n\n    move-object v11, v0\n\n    if-eqz v10, :cond_2\n\n    :try_start_7\n    invoke-virtual {v10}, Landroid/os/ParcelFileDescriptor;->close()V\n    :try_end_7\n    .catchall {:try_start_7 .. :try_end_7} :catchall_3\n\n    goto :goto_2\n\n    :catchall_3\n    move-exception v0\n\n    move-object v12, v0\n\n    :try_start_8\n    invoke-virtual {v11, v12}, Ljava/lang/Throwable;->addSuppressed(Ljava/lang/Throwable;)V\n\n    .end local v2    # \"out\":Ljava/util/HashMap;, \"Ljava/util/HashMap<Landroid/net/Uri;Ljava/nio/ByteBuffer;>;\"\n    .end local v3    # \"resolver\":Landroid/content/ContentResolver;\n    .end local v6    # \"font\":Landroid/provider/FontsContract$FontInfo;\n    .end local v7    # \"uri\":Landroid/net/Uri;\n    .end local v8    # \"buffer\":Ljava/nio/ByteBuffer;\n    .end local p0    # \"context\":Landroid/content/Context;\n    .end local p1    # \"fonts\":[Landroid/provider/FontsContract$FontInfo;\n    .end local p2    # \"cancellationSignal\":Landroid/os/CancellationSignal;\n    :cond_2\n    :goto_2\n    throw v11\n\n    .line 750\n    .restart local v2    # \"out\":Ljava/util/HashMap;, \"Ljava/util/HashMap<Landroid/net/Uri;Ljava/nio/ByteBuffer;>;\"\n    .restart local v3    # \"resolver\":Landroid/content/ContentResolver;\n    .restart local v6    # \"font\":Landroid/provider/FontsContract$FontInfo;\n    .restart local v7    # \"uri\":Landroid/net/Uri;\n    .restart local v8    # \"buffer\":Ljava/nio/ByteBuffer;\n    .restart local p0    # \"context\":Landroid/content/Context;\n    .restart local p1    # \"fonts\":[Landroid/provider/FontsContract$FontInfo;\n    .restart local p2    # \"cancellationSignal\":Landroid/os/CancellationSignal;\n    :catch_0\n    move-exception v0\n\n    .line 754\n    :cond_3\n    :goto_3\n    if-eqz v10, :cond_4\n\n    invoke-virtual {v10}, Landroid/os/ParcelFileDescriptor;->close()V\n    :try_end_8\n    .catch Ljava/io/IOException; {:try_start_8 .. :try_end_8} :catch_1\n\n    .line 756\n    .end local v10    # \"pfd\":Landroid/os/ParcelFileDescriptor;\n    :cond_4\n    goto :goto_4\n\n    .line 754\n    :catch_1\n    move-exception v0\n\n    goto :goto_4\n\n    :catch_2\n    move-exception v0\n\n    move-object/from16 v9, p2\n\n    .line 760\n    :goto_4\n    invoke-virtual {v2, v7, v8}, Ljava/util/HashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n\n    .line 731\n    .end local v6    # \"font\":Landroid/provider/FontsContract$FontInfo;\n    .end local v7    # \"uri\":Landroid/net/Uri;\n    .end local v8    # \"buffer\":Ljava/nio/ByteBuffer;\n    :goto_5\n    add-int/lit8 v5, v5, 0x1\n\n    goto :goto_0\n\n    .line 762\n    :cond_5\n    move-object/from16 v9, p2\n\n    invoke-static {v2}, Ljava/util/Collections;->unmodifiableMap(Ljava/util/Map;)Ljava/util/Map;\n\n    move-result-object v0\n\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestConstInline.smali",
    "content": ".class public Ltypes/TestConstInline;\n.super Ljava/lang/Object;\n\n.method private static test(Z)Ljava/lang/String;\n    .registers 4\n    .param p0, \"b\"    # Z\n\n    if-eqz p0, :cond_d\n    invoke-static {}, Ltypes/TestConstInline;->list()Ljava/util/List;\n    move-result-object v0\n    const-string v1, \"1\"\n    goto :goto_return\n\n    :cond_d\n    const/4 v2, 0x0\n    # chained move instead zero const loading\n    move v0, v2\n    move v1, v0\n    goto :goto_return\n\n    :goto_return\n    invoke-static {v0, v1}, Ltypes/TestConstInline;->use(Ljava/util/List;Ljava/lang/String;)Ljava/lang/String;\n    move-result-object v2\n    return-object v2\n.end method\n\n.method private static use(Ljava/util/List;Ljava/lang/String;)Ljava/lang/String;\n    .registers 3\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/util/List\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \">;\",\n            \"Ljava/lang/String;\",\n            \")\",\n            \"Ljava/lang/String;\"\n        }\n    .end annotation\n\n    new-instance v0, Ljava/lang/StringBuilder;\n    invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V\n\n    invoke-virtual {v0, p0}, Ljava/lang/StringBuilder;->append(Ljava/lang/Object;)Ljava/lang/StringBuilder;\n    move-result-object v0\n\n    invoke-virtual {v0, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    move-result-object v0\n\n    invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n    move-result-object v0\n\n    return-object v0\n.end method\n\n.method private static list()Ljava/util/List;\n    .registers 1\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"()\",\n            \"Ljava/util/List\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \">;\"\n        }\n    .end annotation\n\n    invoke-static {}, Ljava/util/Collections;->emptyList()Ljava/util/List;\n    move-result-object v0\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestGenerics2.smali",
    "content": ".class public final Ltypes/TestGenerics2;\n.super Ljava/lang/Object;\n.source \"SourceFile\"\n\n# instance fields\n.field private field:Ljava/util/Map;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ljava/util/Map<\",\n            \"Ljava/lang/Integer;\",\n            \"Ljava/lang/String;\",\n            \">;\"\n        }\n    .end annotation\n.end field\n\n.method public test()V\n    .registers 5\n\n    iget-object v4, p0, Ltypes/TestGenerics2;->field:Ljava/util/Map;\n\n    invoke-interface {v4}, Ljava/util/Map;->size()I\n    move-result v0\n\n    invoke-static {v0}, Ltypes/TestGenerics2;->useInt(I)V\n\n    invoke-interface {v4}, Ljava/util/Map;->entrySet()Ljava/util/Set;\n    move-result-object v4\n\n    invoke-interface {v4}, Ljava/util/Set;->iterator()Ljava/util/Iterator;\n    move-result-object v4\n\n    :goto_16\n    invoke-interface {v4}, Ljava/util/Iterator;->hasNext()Z\n    move-result v0\n\n    if-eqz v0, :ret\n\n    invoke-interface {v4}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n    move-result-object v0\n\n    invoke-interface {v0}, Ljava/util/Map$Entry;->getKey()Ljava/lang/Object;\n    move-result-object v1\n\n    check-cast v1, Ljava/lang/Integer;\n\n    invoke-virtual {v1}, Ljava/lang/Integer;->intValue()I\n    move-result v1\n\n    invoke-static {v1}, Ltypes/TestGenerics2;->useInt(I)V\n\n    invoke-interface {v0}, Ljava/util/Map$Entry;->getValue()Ljava/lang/Object;\n    move-result-object v0\n\n    check-cast v0, Ljava/lang/String;\n\n    invoke-interface {v0, p1}, Ljava/lang/String;->trim()Ljava/lang/String;\n\n    goto :goto_16\n\n    :ret\n    return-void\n.end method\n\n.method public static useInt(I)V\n    .registers 3\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestGenericsInFullInnerCls/FieldCls.smali",
    "content": ".class public Ltypes/FieldCls;\n.super Ljava/lang/Object;\n\n.field private a:Landroidx/compose/animation/core/bb;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ltypes/test/ba<\",\n            \"Ltypes/n;\",\n            \">.types/test/bb<\",\n            \"Ltypes/n;\",\n            \"Ltypes/n;\",\n            \">;\"\n        }\n    .end annotation\n.end field\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestGenericsInFullInnerCls/ba.smali",
    "content": ".class public final Ltypes/test/ba;\n.super Ljava/lang/Object;\n\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"<S:\",\n        \"Ljava/lang/Object;\",\n        \">\",\n        \"Ljava/lang/Object;\"\n    }\n.end annotation\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestGenericsInFullInnerCls/bb.smali",
    "content": ".class public final Ltypes/test/bb;\n.super Ljava/lang/Object;\n\n\n# annotations\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"<T:\",\n        \"Ljava/lang/Object;\",\n        \"V:\",\n        \"Ltypes/n;\",\n        \">\",\n        \"Ljava/lang/Object;\"\n    }\n.end annotation\n\n.field private b:Ltypes/test/ba;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ltypes/test/ba<\",\n            \"TS;>;\"\n        }\n    .end annotation\n.end field\n\n.field private c:Landroidx/compose/animation/core/bc;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ltypes/test/ba<\",\n            \"TS;>.types/test/bb<TT;TV;>.types/test/bc<TT;TV;>;\"\n        }\n    .end annotation\n.end field\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestGenericsInFullInnerCls/bc.smali",
    "content": ".class public final Ltypes/test/bc;\n.super Ljava/lang/Object;\n\n\n# annotations\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"<T:\",\n        \"Ljava/lang/Object;\",\n        \"V:\",\n        \"Ltypes/n;\",\n        \">\",\n        \"Ljava/lang/Object;\",\n        \"Ltypes/test/ca<\",\n        \"TT;>;\"\n    }\n.end annotation\n\n\n.field private a:Ltypes/test/ba;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ltypes/test/ba<\",\n            \"TS;>;\"\n        }\n    .end annotation\n.end field\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestGenericsInFullInnerCls/n.smali",
    "content": ".class public Ltypes/n;\n.super Ljava/lang/Object;\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestPrimitiveConversion.smali",
    "content": ".class public Ltypes/TestPrimitiveConversion;\n.super Ljava/lang/Object;\n\n.method public test(JZ)V\n    .registers 5\n\n    invoke-static {p1, p2, p3}, Ltypes/TestPrimitiveConversion;->putByte(JB)V\n    return-void\n.end method\n\n.method private static putByte(JB)V\n    .registers 3\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestPrimitiveConversion2.smali",
    "content": ".class public Ltypes/TestPrimitiveConversion2;\n.super Ljava/lang/Object;\n\n\n.method protected test(Landroid/widget/TextView;Lapp/ItemCurrency;Lapp/ItemCurrency;Lapp/ItemCurrency;Lapp/ItemCurrency;ZLapp/SearchListItem;)Z\n    .locals 4\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Landroid/widget/TextView;\",\n            \"Lapp/ItemCurrency;\",\n            \"Lapp/ItemCurrency;\",\n            \"Lapp/ItemCurrency;\",\n            \"Lapp/ItemCurrency;\",\n            \"ZLapp/SearchListItem;)Z\"\n        }\n    .end annotation\n\n    .line 573\n    invoke-direct {p0, p2, p3}, Lapp/DefaultItemAdapter;->getConvertedPrice(Lapp/ItemCurrency;Lapp/ItemCurrency;)Lapp/ItemCurrency;\n\n    move-result-object p3\n\n    const/4 v0, 0x0\n\n    if-eqz p3, :cond_3\n\n    .line 577\n    iget-object v1, p3, Lapp/ItemCurrency;->code:Ljava/lang/String;\n\n    iget-object p2, p2, Lapp/ItemCurrency;->code:Ljava/lang/String;\n\n    invoke-virtual {v1, p2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result p2\n\n    const/4 v1, 0x1\n\n    xor-int/2addr p2, v1\n\n    or-int/lit8 v2, p2, 0x2\n\n    .line 581\n    iget-object v3, p3, Lapp/ItemCurrency;->value:Ljava/lang/String;\n\n    iget-object p3, p3, Lapp/ItemCurrency;->code:Ljava/lang/String;\n\n    invoke-virtual {p0, v3, p3, v2}, Lapp/ItemAdapter;->formatCurrency(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;\n\n    move-result-object p3\n\n    if-eqz p2, :cond_0\n\n    if-eqz p3, :cond_0\n\n    .line 586\n    new-instance v1, Ljava/lang/StringBuilder;\n\n    invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V\n\n    invoke-virtual {v1, p3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    const-string p3, \" \"\n\n    invoke-virtual {v1, p3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object p3\n\n    const/4 v1, 0x2\n\n    :cond_0\n    if-eqz p6, :cond_1\n\n    if-eqz p7, :cond_1\n\n    .line 594\n    invoke-direct {p0, p4, p5}, Lapp/DefaultItemAdapter;->getConvertedPrice(Lapp/ItemCurrency;Lapp/ItemCurrency;)Lapp/ItemCurrency;\n\n    move-result-object p5\n\n    if-eqz p5, :cond_1\n\n    .line 598\n    iget-object p6, p5, Lapp/ItemCurrency;->code:Ljava/lang/String;\n\n    iget-object p4, p4, Lapp/ItemCurrency;->code:Ljava/lang/String;\n\n    invoke-virtual {p6, p4}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result p4\n\n    .line 599\n    iget-object p5, p5, Lapp/ItemCurrency;->code:Ljava/lang/String;\n\n    invoke-direct {p0, p5, p3, p7, p4}, Lapp/DefaultItemAdapter;->getPrice(Ljava/lang/String;Ljava/lang/String;Lapp/SearchListItem;Z)Landroid/text/Spannable;\n\n    move-result-object v0\n\n    :cond_1\n    if-nez v0, :cond_2\n\n    .line 605\n    sget-object p4, Landroid/graphics/Typeface;->DEFAULT:Landroid/graphics/Typeface;\n\n    invoke-virtual {p1, p4, v1}, Landroid/widget/TextView;->setTypeface(Landroid/graphics/Typeface;I)V\n\n    .line 606\n    invoke-virtual {p1, p3}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V\n\n    goto :goto_0\n\n    .line 609\n    :cond_2\n    invoke-virtual {p1, v0}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V\n\n    goto :goto_0\n\n    .line 612\n    :cond_3\n    invoke-virtual {p1, v0}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V\n\n    const/4 p2, 0x0\n\n    :goto_0\n    return p2\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestTypeResolver10.smali",
    "content": ".class public Ltypes/TestTypeResolver10;\n.super Ljava/lang/Object;\n.source \"SourceFile\"\n\n\n.method private test(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;\n    .locals 2\n\n    invoke-virtual {p1}, Ljava/lang/String;->isEmpty()Z\n\n    move-result v0\n\n    const/4 v1, 0x0\n\n    if-eqz v0, :cond_0\n\n    return-object v1\n\n    :cond_0\n    invoke-virtual {p2}, Ljava/lang/String;->isEmpty()Z\n\n    move-result v0\n\n    if-eqz v0, :cond_1\n\n    return-object v1\n\n    :cond_1\n    :try_start_0\n    invoke-static {p2}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I\n\n    move-result p2\n\n    if-eqz p2, :cond_4\n\n    const/4 v0, 0x1\n\n    if-eq p2, v0, :cond_3\n\n    const/4 v0, 0x3\n\n    if-eq p2, v0, :cond_2\n\n    goto :goto_1\n\n    .line 4\n    :cond_2\n    invoke-static {p1}, Ljava/lang/Boolean;->parseBoolean(Ljava/lang/String;)Z\n\n    move-result p1\n\n    invoke-static {p1}, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;\n\n    move-result-object p1\n\n    :cond_3\n    :goto_0\n    move-object v1, p1\n\n    goto :goto_1\n\n    .line 5\n    :cond_4\n    invoke-static {p1}, Ljava/lang/Integer;->valueOf(Ljava/lang/String;)Ljava/lang/Integer;\n\n    move-result-object p1\n    :try_end_0\n    .catch Ljava/lang/NumberFormatException; {:try_start_0 .. :try_end_0} :catch_0\n\n    goto :goto_0\n\n    :catch_0\n    move-exception p1\n\n    .line 6\n    invoke-virtual {p1}, Ljava/lang/NumberFormatException;->printStackTrace()V\n\n    :goto_1\n    return-object v1\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestTypeResolver14.smali",
    "content": ".class public Ltypes/TestTypeResolver14;\n.super Ljava/lang/Object;\n.source \"SourceFile\"\n\n.method public test()Ljava/util/Date;\n    .registers 5\n    .annotation system Ldalvik/annotation/Throws;\n        value = {\n            Ljava/lang/Exception;\n        }\n    .end annotation\n\n    .line 472\n    const/4 v2, 0x0\n    const/4 v3, 0x0\n\n    invoke-static {v3, v2}, Landroidx/room/util/DBUtil;->query(ZLandroid/os/CancellationSignal;)Landroid/database/Cursor;\n    move-result-object v0\n\n    .line 475\n    :try_start_e\n    invoke-interface {v0}, Landroid/database/Cursor;->moveToFirst()Z\n    move-result v1\n\n    if-eqz v1, :cond_2d\n\n    .line 477\n    invoke-interface {v0, v3}, Landroid/database/Cursor;->isNull(I)Z\n    move-result v1\n\n    if-eqz v1, :cond_1b\n\n    goto :goto_23\n\n    .line 480\n    :cond_1b\n    invoke-interface {v0, v3}, Landroid/database/Cursor;->getLong(I)J\n    move-result-wide v1\n\n    invoke-static {v1, v2}, Ljava/lang/Long;->valueOf(J)Ljava/lang/Long;\n    move-result-object v2\n\n    .line 482\n    :goto_23\n    iget-object v1, p0, Ltypes/TestTypeResolver14$8;->this$0:Ltypes/TestTypeResolver14;\n\n    invoke-virtual {v1, v2}, Ltypeconverters/DateTypeConverter;->toDate(Ljava/lang/Long;)Ljava/util/Date;\n    move-result-object v2\n\n    :try_end_2d\n    .catchall {:try_start_e .. :try_end_2d} :catchall_31\n\n    .line 488\n    :cond_2d\n    invoke-interface {v0}, Landroid/database/Cursor;->close()V\n    return-object v2\n\n    :catchall_31\n    move-exception v1\n    invoke-interface {v0}, Landroid/database/Cursor;->close()V\n    .line 489\n    throw v1\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestTypeResolver15.smali",
    "content": ".class public Ltypes/TestTypeResolver15;\n.super Ljava/lang/Object;\n\n.method private test(Z)V\n    .locals 2\n\n    if-eqz p1, :cond_0\n\n\tconst/4 v1, 0x0\n\tgoto :goto_0\n\n\t:cond_0\n\tconst/16 v1, 0x8\n\n\t:goto_0\n\tinvoke-virtual {p0, v1}, Ltypes/TestTypeResolver15;->useInt(I)V\n\n\txor-int/lit8 p1, p1, 0x1\n\tinvoke-virtual {p0, p1}, Ltypes/TestTypeResolver15;->useInt(I)V\n\n\treturn-void\n.end method\n\n.method private useInt(I)V\n    .registers 2\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestTypeResolver16.smali",
    "content": ".class public Ltypes/TestTypeResolver16;\n.super Ljava/lang/Object;\n\n.method public final test(Ljava/util/List;Ljava/util/Set;Ljava/util/function/Function;)Ljava/util/List;\n    .locals 1\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"<T:\", \"Ljava/lang/Object;\", \"K:\", \"Ljava/lang/Object;\", \">(\",\n            \"Ljava/util/List<\", \"+TT;>;\",\n            \"Ljava/util/Set<\", \"+TT;>;\",\n            \"Ljava/util/function/Function<\", \"-TT;+TK;>;)\",\n            \"Ljava/util/List<\", \"TT;>;\"\n        }\n    .end annotation\n\n    if-eqz p2, :cond_1\n    if-eqz p1, :cond_0\n\n    .line 85\n    move-object v0, p1\n    check-cast v0, Ljava/util/Collection;\n    check-cast p2, Ljava/lang/Iterable;\n    invoke-static {v0, p2, p3}, Ltypes/TestTypeResolver16;->union(Ljava/util/Collection;Ljava/lang/Iterable;Ljava/util/function/Function;)Ljava/util/List;\n    move-result-object p2\n    goto :goto_0\n\n    :cond_0\n    const/4 p2, 0x0\n\n    :goto_0\n    if-eqz p2, :cond_1\n    move-object p1, p2\n\n    :cond_1\n    if-eqz p1, :cond_2\n    goto :goto_1\n\n    :cond_2\n    invoke-static {}, Ltypes/TestTypeResolver16;->emptyList()Ljava/util/List;\n    move-result-object p1\n\n    :goto_1\n    return-object p1\n.end method\n\n\n.method public static final union(Ljava/util/Collection;Ljava/lang/Iterable;Ljava/util/function/Function;)Ljava/util/List;\n    .locals 4\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"<T:\",\n            \"Ljava/lang/Object;\",\n            \"K:\",\n            \"Ljava/lang/Object;\",\n            \">(\",\n            \"Ljava/util/Collection<\",\n            \"+TT;>;\",\n            \"Ljava/lang/Iterable<\",\n            \"+TT;>;\",\n            \"Ljava/util/function/Function<\",\n            \"-TT;+TK;>;)\",\n            \"Ljava/util/List<\",\n            \"TT;>;\"\n        }\n    .end annotation\n\n\tconst/4 v0, 0x0\n    return-object v0\n.end method\n\n.method public static final emptyList()Ljava/util/List;\n    .locals 1\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"<T:\",\n            \"Ljava/lang/Object;\",\n            \">()\",\n            \"Ljava/util/List<\",\n            \"TT;>;\"\n        }\n    .end annotation\n\n\tconst/4 v0, 0x0\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestTypeResolver17.smali",
    "content": ".class public Ltypes/TestTypeResolver17;\n.super Ljava/lang/Object;\n\n\n.method private static closeQuietly(Ljava/lang/AutoCloseable;)V\n    .locals 0\n    return-void\n.end method\n\n.method private static test(Landroid/content/Context;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;\n    .locals 7\n\n    .line 159\n    invoke-virtual {p0}, Landroid/content/Context;->getContentResolver()Landroid/content/ContentResolver;\n\n    move-result-object v0\n\n    const/4 p0, 0x1\n\n    const/4 v6, 0x0\n\n    :try_start_0\n    new-array v2, p0, [Ljava/lang/String;\n\n    const/4 p0, 0x0\n\n    aput-object p2, v2, p0\n\n    const/4 v3, 0x0\n\n    const/4 v4, 0x0\n\n    const/4 v5, 0x0\n\n    move-object v1, p1\n\n    .line 163\n    invoke-virtual/range {v0 .. v5}, Landroid/content/ContentResolver;->query(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;\n\n    move-result-object v6\n\n    .line 164\n    invoke-interface {v6}, Landroid/database/Cursor;->moveToFirst()Z\n\n    move-result p1\n\n    if-eqz p1, :cond_0\n\n    invoke-interface {v6, p0}, Landroid/database/Cursor;->isNull(I)Z\n\n    move-result p1\n\n    if-nez p1, :cond_0\n\n    .line 165\n    invoke-interface {v6, p0}, Landroid/database/Cursor;->getString(I)Ljava/lang/String;\n\n    move-result-object p0\n    :try_end_0\n    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0\n    .catchall {:try_start_0 .. :try_end_0} :catchall_0\n\n    .line 173\n    invoke-static {v6}, Ltypes/TestTypeResolver17;->closeQuietly(Ljava/lang/AutoCloseable;)V\n\n    return-object p0\n\n    :cond_0\n    invoke-static {v6}, Ltypes/TestTypeResolver17;->closeQuietly(Ljava/lang/AutoCloseable;)V\n\n    return-object p3\n\n    :catchall_0\n    move-exception p0\n\n    goto :goto_0\n\n    :catch_0\n    move-exception p0\n\n    :try_start_1\n    const-string p1, \"DocumentFile\"\n\n    .line 170\n    new-instance p2, Ljava/lang/StringBuilder;\n\n    invoke-direct {p2}, Ljava/lang/StringBuilder;-><init>()V\n\n    const-string v0, \"Failed query: \"\n\n    invoke-virtual {p2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {p2, p0}, Ljava/lang/StringBuilder;->append(Ljava/lang/Object;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {p2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object p0\n\n    invoke-static {p1, p0}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n    :try_end_1\n    .catchall {:try_start_1 .. :try_end_1} :catchall_0\n\n    .line 173\n    invoke-static {v6}, Ltypes/TestTypeResolver17;->closeQuietly(Ljava/lang/AutoCloseable;)V\n\n    return-object p3\n\n    :goto_0\n    invoke-static {v6}, Ltypes/TestTypeResolver17;->closeQuietly(Ljava/lang/AutoCloseable;)V\n\n    throw p0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestTypeResolver20/Sequence.smali",
    "content": ".class public interface abstract Lkotlin/sequences/Sequence;\n.super Ljava/lang/Object;\n.source \"SourceFile\"\n\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"<T:\",\n        \"Ljava/lang/Object;\",\n        \">\",\n        \"Ljava/lang/Object;\"\n    }\n.end annotation\n\n.method public abstract iterator()Ljava/util/Iterator;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"()\",\n            \"Ljava/util/Iterator<\",\n            \"TT;>;\"\n        }\n    .end annotation\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestTypeResolver20/TestTypeResolver20.smali",
    "content": ".class public Ltypes/TestTypeResolver20;\n.super Ljava/lang/Object;\n.source \"SourceFile\"\n\n\n.method public static final max(Lkotlin/sequences/Sequence;)Ljava/lang/Comparable;\n    .registers 4\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"<T::\",\n            \"Ljava/lang/Comparable<\",\n            \"-TT;>;>(\",\n            \"Lkotlin/sequences/Sequence<\",\n            \"+TT;>;)TT;\"\n        }\n    .end annotation\n\n    .line 1147\n    invoke-interface {p0}, Lkotlin/sequences/Sequence;->iterator()Ljava/util/Iterator;\n    move-result-object p0\n\n    .line 1148\n    invoke-interface {p0}, Ljava/util/Iterator;->hasNext()Z\n    move-result v0\n\n    if-nez v0, :cond_11\n\n    const/4 p0, 0x0\n    return-object p0\n\n    .line 1149\n    :cond_11\n    invoke-interface {p0}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n    move-result-object v0\n    check-cast v0, Ljava/lang/Comparable;\n\n    .line 1150\n    :cond_17\n    :goto_17\n    invoke-interface {p0}, Ljava/util/Iterator;->hasNext()Z\n    move-result v1\n\n    if-eqz v1, :cond_2b\n\n    .line 1151\n    invoke-interface {p0}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n    move-result-object v1\n    check-cast v1, Ljava/lang/Comparable;\n\n    .line 1152\n    invoke-interface {v0, v1}, Ljava/lang/Comparable;->compareTo(Ljava/lang/Object;)I\n    move-result v2\n\n    if-gez v2, :cond_17\n\n    move-object v0, v1\n    goto :goto_17\n\n    :cond_2b\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestTypeResolver21.smali",
    "content": ".class public Ltypes/TestTypeResolver21;\n.super Ljava/lang/Object;\n.source \"TestTypeResolver21.java\"\n\n\n.method public test(Ljava/lang/Object;)Ljava/lang/Number;\n    .registers 4\n    .param p1, \"objectArray\"    # Ljava/lang/Object;\n\n    .prologue\n    .line 16\n    check-cast p1, [Ljava/lang/Object;\n    .end local p1    # \"objectArray\":Ljava/lang/Object;\n    move-object v0, p1\n    check-cast v0, [Ljava/lang/Object;\n\n    .line 17\n    .local v0, \"arr\":[Ljava/lang/Object;\n    const/4 v1, 0x0\n    aget-object v1, v0, v1\n    check-cast v1, Ljava/lang/Number;\n    return-object v1\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestTypeResolver24/T1.smali",
    "content": ".class LT1;\n.super Ljava/lang/Object;\n\n.method public foo1()V\n    .registers 1\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestTypeResolver24/T2.smali",
    "content": ".class LT2;\n.super Ljava/lang/Object;\n\n.method public foo2()V\n    .registers 1\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestTypeResolver24/Test1.smali",
    "content": ".class public LTest1;\n.super Ljava/lang/Object;\n\n.method public test()V\n    .registers 3\n    const/4 v0, 0x0\n    move-object v1, v0\n    check-cast v1, LT1;\n    invoke-virtual {v0}, LT1;->foo1()V\n    move-object v1, v0\n    check-cast v1, LT2;\n    invoke-virtual {v0}, LT2;->foo2()V\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestTypeResolver25.smali",
    "content": ".class public abstract Ltypes/TestTypeResolver25;\n.super Ljava/lang/Object;\n\n.field public final a:Ljava/util/Map;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ljava/util/Map<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/lang/Object;\",\n            \">;\"\n        }\n    .end annotation\n.end field\n\n.field public volatile b:Z\n\n.method public k(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;\n    .registers 6\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"<T:\",\n            \"Ljava/lang/Object;\",\n            \">(\",\n            \"Ljava/lang/String;\",\n            \"TT;)TT;\"\n        }\n    .end annotation\n\n    .line 1\n    iget-object v0, p0, Ltypes/TestTypeResolver25;->a:Ljava/util/Map;\n\n    monitor-enter v0\n\n    .line 2\n    :try_start_3\n    iget-object v1, p0, Ltypes/TestTypeResolver25;->a:Ljava/util/Map;\n\n    invoke-interface {v1, p1}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v1\n\n    if-nez v1, :cond_10\n\n    .line 3\n    iget-object v2, p0, Ltypes/TestTypeResolver25;->a:Ljava/util/Map;\n\n    invoke-interface {v2, p1, p2}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n\n    .line 4\n    :cond_10\n    monitor-exit v0\n    :try_end_11\n    .catchall {:try_start_3 .. :try_end_11} :catchall_2c\n\n    if-nez v1, :cond_14\n\n    goto :goto_15\n\n    :cond_14\n    move-object p2, v1\n\n    .line 5\n    :goto_15\n    iget-boolean p1, p0, Ltypes/TestTypeResolver25;->b:Z\n\n    if-eqz p1, :cond_2b\n\n    .line 6\n    instance-of p1, p2, Ljava/io/Closeable;\n\n    if-eqz p1, :cond_2b\n\n    .line 7\n    :try_start_1d\n    move-object p1, p2\n\n    check-cast p1, Ljava/io/Closeable;\n\n    invoke-interface {p1}, Ljava/io/Closeable;->close()V\n    :try_end_23\n    .catch Ljava/io/IOException; {:try_start_1d .. :try_end_23} :catch_24\n\n    goto :goto_2b\n\n    :catch_24\n    move-exception p1\n\n    .line 8\n    new-instance p2, Ljava/lang/RuntimeException;\n\n    invoke-direct {p2, p1}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/Throwable;)V\n\n    throw p2\n\n    :cond_2b\n    :goto_2b\n    return-object p2\n\n    :catchall_2c\n    move-exception p1\n\n    .line 9\n    :try_start_2d\n    monitor-exit v0\n    :try_end_2e\n    .catchall {:try_start_2d .. :try_end_2e} :catchall_2c\n\n    throw p1\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestTypeResolver5.smali",
    "content": ".class public Ltypes/TestTypeResolver5;\n.super Landroid/content/Context;\n.source \"SourceFile\"\n\n\n.field public static final EXTERNAL_SOURCE:Ljava/lang/String; = \"externalsource\"\n.field public static final IS_APPBOY_CAMPAIGN:Ljava/lang/String; = \"appBoyCampaign\"\n.field public static final IS_NEWS_FEED:Ljava/lang/String; = \"isNewsFeed\"\n\n\n# direct methods\n.method public constructor <init>()V\n    .locals 0\n\n    .prologue\n    .line 35\n    invoke-direct {p0}, Landroid/content/Context;-><init>()V\n\n    return-void\n.end method\n\n.method private openNextScreen(Landroid/os/Bundle;)V\n    .locals 3\n\n    .prologue\n    const/4 v1, 0x0\n\n    .line 56\n    if-eqz p1, :cond_2\n\n    const-string v0, \"externalsource\"\n\n    invoke-virtual {p1, v0}, Landroid/os/Bundle;->containsKey(Ljava/lang/String;)Z\n\n    move-result v0\n\n    if-eqz v0, :cond_2\n\n    const-string v0, \"externalsource\"\n\n    .line 57\n    invoke-virtual {p1, v0}, Landroid/os/Bundle;->getString(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v0\n\n    move-object v2, v0\n\n    .line 58\n    :goto_0\n    if-eqz p1, :cond_3\n\n    const-string v0, \"isNewsFeed\"\n\n    invoke-virtual {p1, v0}, Landroid/os/Bundle;->containsKey(Ljava/lang/String;)Z\n\n    move-result v0\n\n    if-eqz v0, :cond_3\n\n    const-string v0, \"isNewsFeed\"\n\n    .line 59\n    invoke-virtual {p1, v0}, Landroid/os/Bundle;->getBoolean(Ljava/lang/String;)Z\n\n    move-result v0\n\n    .line 61\n    :goto_1\n    invoke-static {v2}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z\n\n    move-result v2\n\n    if-nez v2, :cond_0\n\n    const/4 v1, 0x1\n\n    .line 64\n    :cond_0\n    if-eqz p1, :cond_1\n\n    .line 65\n    new-instance v2, Landroid/webkit/WebView;\n\n    invoke-direct {v2, p0}, Landroid/webkit/WebView;-><init>(Landroid/content/Context;)V\n\n    .line 66\n    invoke-direct {p0, v2, p1}, Ltypes/TestTypeResolver5;->runJavaScriptForCampaign(Landroid/webkit/WebView;Landroid/os/Bundle;)V\n\n    .line 70\n    :cond_1\n    if-eqz v0, :cond_4\n\n    .line 72\n    invoke-direct {p0, p1}, Ltypes/TestTypeResolver5;->startHomeActivity(Landroid/os/Bundle;)V\n\n    .line 73\n    invoke-virtual {p0}, Ltypes/TestTypeResolver5;->finish()V\n\n    .line 80\n    :goto_2\n    return-void\n\n    .line 57\n    :cond_2\n    const-string v0, \"\"\n\n    move-object v2, v0\n\n    goto :goto_0\n\n    :cond_3\n    move v0, v1\n\n    .line 59\n    goto :goto_1\n\n    .line 74\n    :cond_4\n    invoke-virtual {p0}, Ltypes/TestTypeResolver5;->isTaskRoot()Z\n\n    move-result v0\n\n    if-nez v0, :cond_5\n\n    if-eqz v1, :cond_6\n\n    .line 76\n    :cond_5\n    invoke-direct {p0, p1}, Ltypes/TestTypeResolver5;->openSplash(Landroid/os/Bundle;)V\n\n    goto :goto_2\n\n    .line 78\n    :cond_6\n    invoke-virtual {p0}, Ltypes/TestTypeResolver5;->finish()V\n\n    goto :goto_2\n.end method\n\n.method private openSplash(Landroid/os/Bundle;)V\n    .locals 1\n    return-void\n.end method\n\n.method private runJavaScriptForCampaign(Landroid/webkit/WebView;Landroid/os/Bundle;)V\n    .locals 1\n    return-void\n.end method\n\n.method private startHomeActivity(Landroid/os/Bundle;)V\n    .locals 1\n    return-void\n.end method\n\n.method public onBackPressed()V\n    .locals 1\n    return-void\n.end method\n\n.method public onCreate(Landroid/os/Bundle;)V\n    .locals 1\n    return-void\n.end method\n\n.method protected onPause()V\n    .locals 1\n    return-void\n.end method\n\n.method protected onStart()V\n    .locals 1\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestTypeResolver8/A.smali",
    "content": ".class public Ltypes/TestCls$A;\n.super Ljava/lang/Object;\n\n\n# annotations\n.annotation system Ldalvik/annotation/EnclosingClass;\n    value = Ljadx/tests/integration/types/TestTypeResolver8$TestCls;\n.end annotation\n\n.annotation system Ldalvik/annotation/InnerClass;\n    accessFlags = 0x9\n    name = \"A\"\n.end annotation\n\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestTypeResolver8/B.smali",
    "content": ".class public Ltypes/TestCls$B;\n.super Ljava/lang/Object;\n\n\n# annotations\n.annotation system Ldalvik/annotation/EnclosingClass;\n    value = Ltypes/TestCls;\n.end annotation\n\n.annotation system Ldalvik/annotation/InnerClass;\n    accessFlags = 0x9\n    name = \"B\"\n.end annotation\n\n\n# direct methods\n.method public constructor <init>(Ltypes/A;)V\n    .registers 2\n\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/types/TestTypeResolver8/TestCls.smali",
    "content": ".class public Ltypes/TestCls;\n.super Ljava/lang/Object;\n\n\n# annotations\n.annotation system Ldalvik/annotation/EnclosingClass;\n    value = Ljadx/tests/integration/types/TestTypeResolver8;\n.end annotation\n\n.annotation system Ldalvik/annotation/InnerClass;\n    accessFlags = 0x9\n    name = \"TestCls\"\n.end annotation\n\n.annotation system Ldalvik/annotation/MemberClasses;\n    value = {\n        Ltypes/TestCls$B;,\n        Ltypes/TestCls$A;\n    }\n.end annotation\n\n\n# instance fields\n.field private f:Ltypes/TestCls$A;\n\n\n# direct methods\n.method public constructor <init>()V\n    .registers 1\n\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n.method private use(Ltypes/TestCls$B;)V\n    .registers 2\n\n    return-void\n.end method\n\n\n# virtual methods\n.method public test()V\n    .registers 3\n\n    iget-object v1, p0, Ltypes/TestCls;->f:Ltypes/TestCls$A;\n\n    if-eqz v1, :cond_a\n\n    new-instance v0, Ltypes/TestCls$B;\n\n    invoke-direct {v0, v1}, Ltypes/TestCls$B;-><init>(Ltypes/TestCls$A;)V\n\n    move v1, v0\n\n    :cond_a\n    invoke-direct {p0, v1}, Ltypes/TestCls;->use(Ltypes/TestCls$B;)V\n\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/variables/TestThisBranchDup.smali",
    "content": ".class public final Lvariables/TestThisBranchDup;\n.super Ljava/lang/Object;\n\n.method public constructor <init>(ZZZLh3/t;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V\n    .registers 10\n\n    and-int/lit8 p8, p7, 0x1\n    if-eqz p8, :cond_5\n    const/4 p1, 0x0\n\n    :cond_5\n    and-int/lit8 p8, p7, 0x2\n    const/4 v0, 0x1\n    if-eqz p8, :cond_b\n    move p2, v0\n\n    :cond_b\n    and-int/lit8 p8, p7, 0x4\n    if-eqz p8, :cond_10\n    move p3, v0\n\n    :cond_10\n    and-int/lit8 p8, p7, 0x8\n    if-eqz p8, :cond_16\n    .line 11\n    sget-object p4, Lh3/t;->Inherit:Lh3/t;\n\n    :cond_16\n    and-int/lit8 p8, p7, 0x10\n    if-eqz p8, :cond_1b\n    move p5, v0\n\n    :cond_1b\n    and-int/lit8 p7, p7, 0x20\n    if-eqz p7, :cond_27\n    move p8, v0\n    move-object p6, p4\n    move p7, p5\n    move p4, p2\n    move p5, p3\n    move-object p2, p0\n    move p3, p1\n    goto :goto_2e\n\n    :cond_27\n    move p8, p6\n    move p7, p5\n    move p5, p3\n    move-object p6, p4\n    move p3, p1\n    move p4, p2\n    move-object p2, p0\n\n    .line 12\n    :goto_2e\n    invoke-direct/range {p2 .. p8}, Lvariables/TestThisBranchDup;-><init>(ZZZLh3/t;ZZ)V\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/variables/TestVariables6.smali",
    "content": ".class public LTestVariables6;\n.super Lcom/paypal/android/p2pmobile/wallet/banksandcards/fragments/BasePaymentFragment;\n.source \"SourceFile\"\n\n# interfaces\n.implements Landroid/support/v13/app/FragmentCompat$OnRequestPermissionsResultCallback;\n.implements Landroid/widget/TextView$OnEditorActionListener;\n.implements Lcom/paypal/android/p2pmobile/common/utils/ISafeClickVerifierListener;\n.implements Lcom/paypal/android/p2pmobile/common/widgets/CSCTextWatcher$ICSCTextWatcherListener;\n\n\n# annotations\n.annotation system Ldalvik/annotation/MemberClasses;\n    value = {\n        Lcom/paypal/android/p2pmobile/wallet/banksandcards/fragments/EnterCardFragment$IEnterCardFragmentListener;\n    }\n.end annotation\n\n\n.field private static final DATE_SEPARATOR:C = '/'\n\n.field mDateFormatOrder:Lcom/paypal/android/p2pmobile/common/utils/ValidatedDateFormatOrder;\n\n\n# direct methods\n.method static constructor <clinit>()V\n    .locals 0\n\n    return-void\n.end method\n\n.method public constructor <init>()V\n    .locals 1\n\n    return-void\n.end method\n\n.method private bindStartDateToMutableCredebitCard(Lcom/paypal/android/foundation/wallet/model/MutableCredebitCard;)Z\n    .locals 10\n    .param p1    # Lcom/paypal/android/foundation/wallet/model/MutableCredebitCard;\n        .annotation build Landroid/support/annotation/NonNull;\n        .end annotation\n    .end param\n\n    .line 1024\n    iget-object v0, p0, LTestVariables6;->mFinancialInstrumentMetadataDefinition:Lcom/paypal/android/foundation/wallet/model/FinancialInstrumentMetadataDefinition;\n\n    invoke-virtual {v0}, Lcom/paypal/android/foundation/wallet/model/FinancialInstrumentMetadataDefinition;->getStartMonth()Lcom/paypal/android/foundation/wallet/model/FinancialInstrumentMetadataAttribute;\n\n    move-result-object v0\n\n    .line 1025\n    iget-object v1, p0, LTestVariables6;->mFinancialInstrumentMetadataDefinition:Lcom/paypal/android/foundation/wallet/model/FinancialInstrumentMetadataDefinition;\n\n    invoke-virtual {v1}, Lcom/paypal/android/foundation/wallet/model/FinancialInstrumentMetadataDefinition;->getStartYear()Lcom/paypal/android/foundation/wallet/model/FinancialInstrumentMetadataAttribute;\n\n    move-result-object v1\n\n    const/4 v2, 0x2\n\n    .line 1026\n    new-array v2, v2, [Lcom/paypal/android/foundation/wallet/model/FinancialInstrumentMetadataAttribute;\n\n    const/4 v3, 0x0\n\n    aput-object v0, v2, v3\n\n    const/4 v0, 0x1\n\n    aput-object v1, v2, v0\n\n    invoke-static {v2}, Lcom/paypal/android/p2pmobile/wallet/banksandcards/utils/EnterCardFragmentUtils;->attributesAreRequired([Lcom/paypal/android/foundation/wallet/model/FinancialInstrumentMetadataAttribute;)Z\n\n    move-result v2\n\n    if-nez v2, :cond_0\n\n    return v0\n\n    .line 1030\n    :cond_0\n    invoke-virtual {p0}, LTestVariables6;->getView()Landroid/view/View;\n\n    move-result-object v2\n\n    if-nez v2, :cond_1\n\n    return v3\n\n    .line 1035\n    :cond_1\n    invoke-virtual {v1}, Lcom/paypal/android/foundation/wallet/model/FinancialInstrumentMetadataAttribute;->getMaximumLength()I\n\n    move-result v6\n\n    .line 1036\n    sget v1, Lcom/paypal/android/p2pmobile/wallet/R$id;->enter_card_start_date:I\n\n    invoke-virtual {v2, v1}, Landroid/view/View;->findViewById(I)Landroid/view/View;\n\n    move-result-object v1\n\n    check-cast v1, Landroid/widget/TextView;\n\n    .line 1038\n    new-instance v2, Lcom/paypal/android/p2pmobile/common/utils/DateStringParser;\n\n    invoke-virtual {v1}, Landroid/widget/TextView;->getText()Ljava/lang/CharSequence;\n\n    move-result-object v1\n\n    invoke-interface {v1}, Ljava/lang/CharSequence;->toString()Ljava/lang/String;\n\n    move-result-object v5\n\n    iget-object v7, p0, LTestVariables6;->mDateFormatOrder:Lcom/paypal/android/p2pmobile/common/utils/ValidatedDateFormatOrder;\n\n    const/16 v8, 0x2f\n\n    const/4 v9, 0x0\n\n    move-object v4, v2\n\n    invoke-direct/range {v4 .. v9}, Lcom/paypal/android/p2pmobile/common/utils/DateStringParser;-><init>(Ljava/lang/String;ILcom/paypal/android/p2pmobile/common/utils/ValidatedDateFormatOrder;CZ)V\n\n    .line 1039\n    invoke-virtual {v2}, Lcom/paypal/android/p2pmobile/common/utils/DateStringParser;->isError()Z\n\n    move-result v1\n\n    if-nez v1, :cond_2\n\n    .line 1040\n    invoke-virtual {v2}, Lcom/paypal/android/p2pmobile/common/utils/DateStringParser;->getDate()Ljava/util/Date;\n\n    move-result-object v1\n\n    invoke-virtual {p1, v1}, Lcom/paypal/android/foundation/wallet/model/MutableCredebitCard;->setIssueDate(Ljava/util/Date;)V\n\n    return v0\n\n    :cond_2\n    return v3\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/variables/TestVariablesGeneric.smali",
    "content": ".class public Lvariables/TestVariablesGeneric;\n.super Ljava/lang/Object;\n.source \"SourceFile\"\n\n.method public static a(Lrx/i;Lrx/c;)Lrx/j;\n    .locals 3\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"<T:\",\n            \"Ljava/lang/Object;\",\n            \">(\",\n            \"Lrx/i<\",\n            \"-TT;>;\",\n            \"Lrx/c<\",\n            \"TT;>;)\",\n            \"Lrx/j;\"\n        }\n    .end annotation\n\n    if-nez p0, :cond_0\n\n    .line 10325\n    new-instance p0, Ljava/lang/IllegalArgumentException;\n\n    const-string p1, \"subscriber can not be null\"\n\n    invoke-direct {p0, p1}, Ljava/lang/IllegalArgumentException;-><init>(Ljava/lang/String;)V\n\n    throw p0\n\n    .line 10327\n    :cond_0\n    iget-object v0, p1, Lrx/c;->a:Lrx/c$a;\n\n    if-nez v0, :cond_1\n\n    .line 10328\n    new-instance p0, Ljava/lang/IllegalStateException;\n\n    const-string p1, \"onSubscribe function can not be null.\"\n\n    invoke-direct {p0, p1}, Ljava/lang/IllegalStateException;-><init>(Ljava/lang/String;)V\n\n    throw p0\n\n    .line 10336\n    :cond_1\n    invoke-virtual {p0}, Lrx/i;->onStart()V\n\n    .line 10343\n    instance-of v0, p0, Lrx/c/c;\n\n    if-nez v0, :cond_2\n\n    .line 10345\n    new-instance v0, Lrx/c/c;\n\n    invoke-direct {v0, p0}, Lrx/c/c;-><init>(Lrx/i;)V\n\n    move-object p0, v0\n\n    .line 10352\n    :cond_2\n    :try_start_0\n    iget-object v0, p1, Lrx/c;->a:Lrx/c$a;\n\n    invoke-static {p1, v0}, Lrx/d/c;->a(Lrx/c;Lrx/c$a;)Lrx/c$a;\n\n    move-result-object p1\n\n    invoke-interface {p1, p0}, Lrx/c$a;->call(Ljava/lang/Object;)V\n\n    .line 10353\n    invoke-static {p0}, Lrx/d/c;->a(Lrx/j;)Lrx/j;\n\n    move-result-object p1\n    :try_end_0\n    .catch Ljava/lang/Throwable; {:try_start_0 .. :try_end_0} :catch_0\n\n    return-object p1\n\n    :catch_0\n    move-exception p1\n\n    .line 10356\n    invoke-static {p1}, Lrx/exceptions/a;->b(Ljava/lang/Throwable;)V\n\n    .line 10358\n    invoke-virtual {p0}, Lrx/i;->isUnsubscribed()Z\n\n    move-result v0\n\n    if-eqz v0, :cond_3\n\n    .line 10359\n    invoke-static {p1}, Lrx/d/c;->b(Ljava/lang/Throwable;)Ljava/lang/Throwable;\n\n    move-result-object p0\n\n    invoke-static {p0}, Lrx/d/c;->a(Ljava/lang/Throwable;)V\n\n    goto :goto_0\n\n    .line 10363\n    :cond_3\n    :try_start_1\n    invoke-static {p1}, Lrx/d/c;->b(Ljava/lang/Throwable;)Ljava/lang/Throwable;\n\n    move-result-object v0\n\n    invoke-virtual {p0, v0}, Lrx/i;->onError(Ljava/lang/Throwable;)V\n    :try_end_1\n    .catch Ljava/lang/Throwable; {:try_start_1 .. :try_end_1} :catch_1\n\n    .line 10375\n    :goto_0\n    invoke-static {}, Lrx/f/e;->b()Lrx/j;\n\n    move-result-object p0\n\n    return-object p0\n\n    :catch_1\n    move-exception p0\n\n    .line 10365\n    invoke-static {p0}, Lrx/exceptions/a;->b(Ljava/lang/Throwable;)V\n\n    .line 10368\n    new-instance v0, Lrx/exceptions/OnErrorFailedException;\n\n    new-instance v1, Ljava/lang/StringBuilder;\n\n    const-string v2, \"Error occurred attempting to subscribe [\"\n\n    invoke-direct {v1, v2}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {p1}, Ljava/lang/Throwable;->getMessage()Ljava/lang/String;\n\n    move-result-object p1\n\n    invoke-virtual {v1, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    const-string p1, \"] and then again while trying to pass to onError.\"\n\n    invoke-virtual {v1, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object p1\n\n    invoke-direct {v0, p1, p0}, Lrx/exceptions/OnErrorFailedException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V\n\n    .line 10370\n    invoke-static {v0}, Lrx/d/c;->b(Ljava/lang/Throwable;)Ljava/lang/Throwable;\n\n    .line 10372\n    throw v0\n.end method\n"
  },
  {
    "path": "jadx-core/src/test/smali/variables/TestVariablesInLoop.smali",
    "content": ".class public abstract Lvariables/TestVariablesInLoop;\n.super Ljava/lang/Object;\n.source \"SourceFile\"\n\n.implements Ljava/util/List;\n\n.annotation system Ldalvik/annotation/Signature;\n    value = {\n        \"Ljava/lang/Object;\",\n        \"Ljava/util/List<\",\n        \"Ljava/lang/Long;\",\n        \">;\"\n    }\n.end annotation\n\n.method static test(Ljava/util/List;)I\n    .locals 5\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/util/List<\",\n            \"Ljava/lang/Long;\",\n            \">;)I\"\n        }\n    .end annotation\n\n    invoke-interface {p0}, Ljava/util/List;->size()I\n\n    move-result v0\n\n    const/4 v1, 0x0\n\n    if-nez v0, :cond_0\n\n    return v1\n\n    :cond_0\n    instance-of v2, p0, Lvariables/TestVariablesInLoop;\n\n    if-eqz v2, :cond_1\n\n    check-cast p0, Lvariables/TestVariablesInLoop;\n\n    const/4 v2, 0x0\n\n    :goto_0\n    if-ge v1, v0, :cond_2\n\n    invoke-virtual {p0, v1}, Lvariables/TestVariablesInLoop;->getLong(I)J\n\n    move-result-wide v3\n\n    invoke-static {v3, v4}, Lvariables/TestVariablesInLoop;->mth(J)I\n\n    move-result v3\n\n    add-int/2addr v2, v3\n\n    add-int/lit8 v1, v1, 0x1\n\n    goto :goto_0\n\n    :cond_1\n    const/4 v2, 0x0\n\n    :goto_1\n    if-ge v1, v0, :cond_2\n\n    invoke-interface {p0, v1}, Ljava/util/List;->get(I)Ljava/lang/Object;\n\n    move-result-object v3\n\n    check-cast v3, Ljava/lang/Long;\n\n    invoke-virtual {v3}, Ljava/lang/Long;->longValue()J\n\n    move-result-wide v3\n\n    invoke-static {v3, v4}, Lvariables/TestVariablesInLoop;->mth(J)I\n\n    move-result v3\n\n    add-int/2addr v2, v3\n\n    add-int/lit8 v1, v1, 0x1\n\n    goto :goto_1\n\n    :cond_2\n    return v2\n.end method\n\n.method public final getLong(I)J\n    .locals 2\n    const/16 v0, 0x0\n    return-wide v0\n.end method\n\n.method static mth(J)I\n    .locals 2\n    const/4 v0, 0x0\n    return v0\n.end method\n"
  },
  {
    "path": "jadx-gui/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-kotlin\")\n\tid(\"application\")\n\tid(\"jadx-library\")\n\tid(\"com.gradleup.shadow\") version \"8.3.8\"\n\tid(\"edu.sc.seis.launch4j\") version \"4.0.0\"\n\tid(\"org.beryx.runtime\") version \"2.0.1\"\n}\n\ndependencies {\n\timplementation(project(\":jadx-core\"))\n\timplementation(project(\":jadx-cli\"))\n\timplementation(project(\":jadx-plugins-tools\"))\n\timplementation(project(\":jadx-commons:jadx-app-commons\"))\n\n\t// import mappings\n\timplementation(project(\":jadx-plugins:jadx-rename-mappings\"))\n\n\timplementation(\"org.jcommander:jcommander:2.0\")\n\timplementation(\"ch.qos.logback:logback-classic:1.5.21\")\n\timplementation(\"io.github.oshai:kotlin-logging-jvm:7.0.13\")\n\n\timplementation(\"com.fifesoft:rsyntaxtextarea:3.6.1\")\n\timplementation(\"com.fifesoft:autocomplete:3.3.2\")\n\timplementation(\"org.drjekyll:fontchooser:3.1.0\")\n\timplementation(\"hu.kazocsaba:image-viewer:1.2.3\")\n\timplementation(\"com.twelvemonkeys.imageio:imageio-webp:3.12.0\") // WebP support for image viewer\n\n\timplementation(\"com.formdev:flatlaf:3.7\")\n\timplementation(\"com.formdev:flatlaf-intellij-themes:3.7\")\n\timplementation(\"com.formdev:flatlaf-extras:3.7\")\n\timplementation(\"com.formdev:flatlaf-fonts-inter:4.1\")\n\timplementation(\"com.formdev:flatlaf-fonts-jetbrains-mono:2.304\")\n\n\timplementation(\"com.google.code.gson:gson:2.13.2\")\n\timplementation(\"org.apache.commons:commons-lang3:3.20.0\")\n\timplementation(\"org.apache.commons:commons-text:1.15.0\")\n\timplementation(\"commons-io:commons-io:2.21.0\")\n\n\timplementation(\"io.reactivex.rxjava3:rxjava:3.1.12\")\n\timplementation(\"com.github.akarnokd:rxjava3-swing:3.1.1\")\n\timplementation(\"com.android.tools.build:apksig:8.13.1\")\n\timplementation(\"io.github.skylot:jdwp:2.0.0\")\n\n\t// Library for hex viewing data\n\tval bined = \"0.2.2\"\n\timplementation(\"org.exbin.bined:bined-swing:$bined\")\n\timplementation(\"org.exbin.bined:bined-highlight-swing:$bined\")\n\timplementation(\"org.exbin.bined:bined-swing-section:$bined\")\n\timplementation(\"org.exbin.auxiliary:binary_data:$bined\")\n\timplementation(\"org.exbin.auxiliary:binary_data-array:$bined\")\n\n\t// Library for rendering GraphViz DOT files\n\timplementation(\"guru.nidi:graphviz-java:0.18.1\")\n\timplementation(\"com.eclipsesource.j2v8:j2v8_linux_x86_64:4.6.0\")\n\timplementation(\"com.eclipsesource.j2v8:j2v8_win32_x86_64:4.6.0\")\n\n\ttestImplementation(project.project(\":jadx-core\").sourceSets.getByName(\"test\").output)\n}\n\nval jadxVersion: String by rootProject.extra\n\ntasks.test {\n\texclude(\"**/tmp/*\")\n}\n\napplication {\n\tapplicationName = (\"jadx-gui\")\n\tmainClass.set(\"jadx.gui.JadxGUI\")\n\tapplicationDefaultJvmArgs =\n\t\tlistOf(\n\t\t\t\"-Xms128M\",\n\t\t\t\"-XX:MaxRAMPercentage=70.0\",\n\t\t\t\"-Dawt.useSystemAAFontSettings=lcd\",\n\t\t\t\"-Dswing.aatext=true\",\n\t\t\t\"-Djava.util.Arrays.useLegacyMergeSort=true\",\n\t\t\t// disable zip checks (#1962)\n\t\t\t\"-Djdk.util.zip.disableZip64ExtraFieldValidation=true\",\n\t\t\t// needed for ktlint formatter\n\t\t\t\"-XX:+IgnoreUnrecognizedVMOptions\",\n\t\t\t\"--add-opens=java.base/java.lang=ALL-UNNAMED\",\n\t\t\t// Foreign API access for 'directories' library (Windows only)\n\t\t\t\"--enable-native-access=ALL-UNNAMED\",\n\t\t\t// flags to fix UI ghosting (#2225)\n\t\t\t\"-Dsun.java2d.noddraw=true\",\n\t\t\t\"-Dsun.java2d.d3d=false\",\n\t\t\t\"-Dsun.java2d.ddforcevram=true\",\n\t\t\t\"-Dsun.java2d.ddblit=false\",\n\t\t\t\"-Dswing.useflipBufferStrategy=true\",\n\t\t)\n\tapplicationDistribution.from(\"$rootDir\") {\n\t\tinclude(\"README.md\")\n\t\tinclude(\"NOTICE\")\n\t\tinclude(\"LICENSE\")\n\t}\n}\n\ntasks.jar {\n\tmanifest {\n\t\tattributes(mapOf(\"Main-Class\" to application.mainClass.get()))\n\t}\n}\n\ntasks.shadowJar {\n\tisZip64 = true\n\tmergeServiceFiles()\n\tmanifest {\n\t\tfrom(tasks.jar.get().manifest)\n\t}\n}\n\n// workaround to exclude shadowJar 'all' artifact from publishing to maven\nproject.components.withType(AdhocComponentWithVariants::class.java).forEach { c ->\n\tc.withVariantsFromConfiguration(project.configurations.shadowRuntimeElements.get()) {\n\t\tskip()\n\t}\n}\n\ntasks.startShadowScripts {\n\tdoLast {\n\t\tval newWindowsScriptContent =\n\t\t\twindowsScript.readText()\n\t\t\t\t.replace(\"java.exe\", \"javaw.exe\")\n\t\t\t\t.replace(\"\\\"%JAVA_EXE%\\\" %DEFAULT_JVM_OPTS%\", \"start \\\"jadx-gui\\\" /B \\\"%JAVA_EXE%\\\" %DEFAULT_JVM_OPTS%\")\n\t\t// Add launch script path as a property\n\t\tval newUnixScriptContent =\n\t\t\tunixScript.readText()\n\t\t\t\t.replace(\n\t\t\t\t\tRegex(\"DEFAULT_JVM_OPTS=.+\", RegexOption.MULTILINE),\n\t\t\t\t\t{ result -> result.value + \"\\\" \\\\\\\"-Djadx.launchScript.path=\\$(realpath $0)\\\\\\\"\\\"\" },\n\t\t\t\t)\n\t\twindowsScript.writeText(newWindowsScriptContent)\n\t\tunixScript.writeText(newUnixScriptContent)\n\t}\n}\n\nlaunch4j {\n\tmainClassName.set(application.mainClass.get())\n\tcopyConfigurable.set(listOf<Any>())\n\tdontWrapJar.set(true)\n\ticon.set(\"$projectDir/src/main/resources/logos/jadx-logo.ico\")\n\toutfile.set(\"jadx-gui-$jadxVersion.exe\")\n\tversion.set(jadxVersion)\n\tcopyright.set(\"Skylot\")\n\twindowTitle.set(\"jadx\")\n\tcompanyName.set(\"jadx\")\n\tjreMinVersion.set(\"11\")\n\tjvmOptions.set(escapeJVMOptions())\n\trequires64Bit.set(true)\n\tdownloadUrl.set(\"https://www.oracle.com/java/technologies/downloads/#jdk21-windows\")\n\tsupportUrl.set(\"https://github.com/skylot/jadx\")\n\n\tbundledJrePath.set(if (project.hasProperty(\"bundleJRE\")) \"%EXEDIR%/jre\" else \"%JAVA_HOME%\")\n\tclasspath.set(tasks.getByName(\"shadowJar\").outputs.files.map { \"%EXEDIR%/lib/${it.name}\" }.sorted().toList())\n\tprintln(\"Launch4J classpath: ${classpath.get()}\")\n\n\tchdir.set(\"\") // don't change current dir\n\tlibraryDir.set(\"\") // don't add any libs\n}\n\nfun escapeJVMOptions(): List<String> {\n\treturn application.applicationDefaultJvmArgs\n\t\t.toList()\n\t\t.map { if (it.startsWith(\"-D\")) \"\\\"$it\\\"\" else it }\n}\n\nruntime {\n\taddOptions(\"--strip-debug\", \"--no-header-files\", \"--no-man-pages\")\n\taddModules(\n\t\t\"java.desktop\",\n\t\t\"java.naming\",\n\t\t\"java.xml\",\n\t\t// needed for \"https\" protocol to download plugins and updates\n\t\t\"jdk.crypto.cryptoki\",\n\t\t\"jdk.accessibility\",\n\t)\n\tjpackage {\n\t\timageOptions = listOf(\"--icon\", \"$projectDir/src/main/resources/logos/jadx-logo.ico\")\n\t\tskipInstaller = true\n\t\ttargetPlatformName = \"win\"\n\t}\n\tlauncher {\n\t\tnoConsole = true\n\t}\n}\n\nval copyDistWin by tasks.registering(Copy::class) {\n\tdescription = \"Copy files for Windows bundle\"\n\n\tval libTask = tasks.getByName(\"shadowJar\")\n\tdependsOn(libTask)\n\tfrom(libTask.outputs) {\n\t\tinclude(\"*.jar\")\n\t\tinto(\"lib\")\n\t}\n\tval exeTask = tasks.getByName(\"createExe\")\n\tdependsOn(exeTask)\n\tfrom(exeTask.outputs) {\n\t\tinclude(\"*.exe\")\n\t}\n\tinto(layout.buildDirectory.dir(\"jadx-gui-win\"))\n\tduplicatesStrategy = DuplicatesStrategy.EXCLUDE\n}\n\nval copyDistWinWithJre by tasks.registering(Copy::class) {\n\tdescription = \"Copy files for Windows with JRE bundle\"\n\n\tval jreTask = tasks.runtime.get()\n\tdependsOn(jreTask)\n\tfrom(jreTask.jreDir) {\n\t\tinclude(\"**/*\")\n\t\tinto(\"jre\")\n\t}\n\tval libTask = tasks.getByName(\"shadowJar\")\n\tdependsOn(libTask)\n\tfrom(libTask.outputs) {\n\t\tinclude(\"*.jar\")\n\t\tinto(\"lib\")\n\t}\n\tval exeTask = tasks.getByName(\"createExe\")\n\tdependsOn(exeTask)\n\tfrom(exeTask.outputs) {\n\t\tinclude(\"*.exe\")\n\t}\n\tinto(layout.buildDirectory.dir(\"jadx-gui-with-jre-win\"))\n\tduplicatesStrategy = DuplicatesStrategy.EXCLUDE\n}\n\n/**\n * Register and expose distribution artifacts to use in top level packaging tasks\n */\nval distWinConfiguration by configurations.creating {\n\tisCanBeResolved = false\n}\nval distWinWithJreConfiguration by configurations.creating {\n\tisCanBeResolved = false\n}\nartifacts {\n\tadd(distWinConfiguration.name, copyDistWin)\n\tadd(distWinWithJreConfiguration.name, copyDistWinWithJre)\n}\n\nval syncNLSLines by tasks.registering(JavaExec::class) {\n\tgroup = \"jadx-dev\"\n\tdescription = \"Utility task to sync new/missing translation using EN as a reference\"\n\n\tclasspath = sourceSets.main.get().runtimeClasspath\n\tmainClass.set(\"jadx.gui.utils.tools.SyncNLSLines\")\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/JadxGUI.java",
    "content": "package jadx.gui;\n\nimport java.awt.Desktop;\n\nimport javax.swing.SwingUtilities;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.cli.JadxCLIArgs;\nimport jadx.cli.config.JadxConfigAdapter;\nimport jadx.commons.app.JadxSystemInfo;\nimport jadx.core.Jadx;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.gui.logs.LogCollector;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.settings.JadxSettingsData;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.LafManager;\nimport jadx.gui.utils.NLS;\n\npublic class JadxGUI {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxGUI.class);\n\n\tpublic static void main(String[] args) {\n\t\ttry {\n\t\t\tJadxConfigAdapter<JadxSettingsData> configAdapter = JadxSettings.buildConfigAdapter();\n\t\t\tJadxSettingsData settingsData = JadxCLIArgs.processArgs(args, new JadxSettingsData(), configAdapter);\n\t\t\tif (settingsData == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tJadxSettings settings = new JadxSettings(configAdapter);\n\t\t\tsettings.loadSettingsData(settingsData);\n\n\t\t\tLogCollector.register();\n\t\t\tprintSystemInfo();\n\t\t\tNLS.setLocale(settings.getLangLocale());\n\t\t\tSwingUtilities.invokeLater(() -> {\n\t\t\t\tLafManager.init(settings);\n\t\t\t\tsettings.getFontSettings().updateDefaultFont();\n\t\t\t\tMainWindow mw = new MainWindow(settings);\n\t\t\t\tregisterOpenFileHandler(mw);\n\t\t\t\tmw.init();\n\t\t\t});\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Error: {}\", e.getMessage(), e);\n\t\t\tSystem.exit(1);\n\t\t}\n\t}\n\n\tprivate static void registerOpenFileHandler(MainWindow mw) {\n\t\ttry {\n\t\t\tif (Desktop.isDesktopSupported()) {\n\t\t\t\tDesktop desktop = Desktop.getDesktop();\n\t\t\t\tif (desktop.isSupported(Desktop.Action.APP_OPEN_FILE)) {\n\t\t\t\t\tdesktop.setOpenFileHandler(e -> mw.open(FileUtils.toPaths(e.getFiles())));\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Throwable e) {\n\t\t\tLOG.error(\"Failed to register open file handler\", e);\n\t\t}\n\t}\n\n\tprivate static void printSystemInfo() {\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tLOG.debug(\"Starting jadx-gui. Version: '{}'. JVM: {} {}. OS: {}, version: {}, arch: {}\",\n\t\t\t\t\tJadx.getVersion(),\n\t\t\t\t\tJadxSystemInfo.JAVA_VM, JadxSystemInfo.JAVA_VER,\n\t\t\t\t\tJadxSystemInfo.OS_NAME, JadxSystemInfo.OS_VERSION, JadxSystemInfo.OS_ARCH);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/JadxWrapper.java",
    "content": "package jadx.gui;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.JadxArgs;\nimport jadx.api.JadxDecompiler;\nimport jadx.api.JavaClass;\nimport jadx.api.JavaNode;\nimport jadx.api.JavaPackage;\nimport jadx.api.ResourceFile;\nimport jadx.api.impl.InMemoryCodeCache;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.plugins.pass.JadxPassInfo;\nimport jadx.api.plugins.pass.impl.SimpleJadxPassInfo;\nimport jadx.api.plugins.pass.types.JadxPreparePass;\nimport jadx.api.usage.impl.EmptyUsageInfoCache;\nimport jadx.api.usage.impl.InMemoryUsageInfoCache;\nimport jadx.cli.JadxAppCommon;\nimport jadx.cli.plugins.JadxFilesGetter;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.ProcessState;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.plugins.AppContext;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.cache.code.CodeCacheMode;\nimport jadx.gui.cache.code.CodeStringCache;\nimport jadx.gui.cache.code.disk.BufferCodeCache;\nimport jadx.gui.cache.code.disk.DiskCodeCache;\nimport jadx.gui.cache.usage.UsageInfoCache;\nimport jadx.gui.plugins.context.CommonGuiPluginsContext;\nimport jadx.gui.settings.JadxProject;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.CacheObject;\nimport jadx.plugins.tools.JadxExternalPluginsLoader;\n\nimport static jadx.core.dex.nodes.ProcessState.GENERATED_AND_UNLOADED;\nimport static jadx.core.dex.nodes.ProcessState.NOT_LOADED;\nimport static jadx.core.dex.nodes.ProcessState.PROCESS_COMPLETE;\n\n@SuppressWarnings(\"ConstantConditions\")\npublic class JadxWrapper {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxWrapper.class);\n\n\tprivate static final Object DECOMPILER_UPDATE_SYNC = new Object();\n\n\tprivate final MainWindow mainWindow;\n\tprivate volatile @Nullable JadxDecompiler decompiler;\n\tprivate CommonGuiPluginsContext guiPluginsContext;\n\n\tpublic JadxWrapper(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t}\n\n\tpublic void open() {\n\t\tclose();\n\t\ttry {\n\t\t\tsynchronized (DECOMPILER_UPDATE_SYNC) {\n\t\t\t\tJadxProject project = getProject();\n\t\t\t\tJadxArgs jadxArgs = getSettings().toJadxArgs();\n\t\t\t\tjadxArgs.setPluginLoader(new JadxExternalPluginsLoader());\n\t\t\t\tjadxArgs.setFilesGetter(JadxFilesGetter.INSTANCE);\n\t\t\t\tproject.fillJadxArgs(jadxArgs);\n\t\t\t\tJadxAppCommon.applyEnvVars(jadxArgs);\n\n\t\t\t\tdecompiler = new JadxDecompiler(jadxArgs);\n\t\t\t\tguiPluginsContext = initGuiPluginsContext(decompiler, mainWindow);\n\t\t\t\tinitUsageCache(jadxArgs);\n\t\t\t\tregisterCodeCache(decompiler);\n\t\t\t\tdecompiler.setEventsImpl(mainWindow.events());\n\t\t\t\tdecompiler.load();\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Jadx decompiler wrapper init error\", e);\n\t\t\tclose();\n\t\t}\n\t}\n\n\t// TODO: check and move into core package\n\tpublic void unloadClasses() {\n\t\tgetCurrentDecompiler().ifPresent(decompiler -> {\n\t\t\tfor (ClassNode cls : decompiler.getRoot().getClasses()) {\n\t\t\t\tProcessState clsState = cls.getState();\n\t\t\t\tcls.unload();\n\t\t\t\tcls.setState(clsState == PROCESS_COMPLETE ? GENERATED_AND_UNLOADED : NOT_LOADED);\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic void close() {\n\t\ttry {\n\t\t\tsynchronized (DECOMPILER_UPDATE_SYNC) {\n\t\t\t\tif (decompiler != null) {\n\t\t\t\t\tdecompiler.close();\n\t\t\t\t\tdecompiler = null;\n\t\t\t\t}\n\t\t\t\tif (guiPluginsContext != null) {\n\t\t\t\t\tresetGuiPluginsContext();\n\t\t\t\t\tguiPluginsContext = null;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Jadx decompiler close error\", e);\n\t\t} finally {\n\t\t\tmainWindow.getCacheObject().reset();\n\t\t}\n\t}\n\n\t/**\n\t * Disk cache require loaded classes to operate, but cache should be set before 'after load' event\n\t * to allow plugins decompile classes with cache enabled.\n\t * To resolve this, register last 'prepare' pass for cache initialization.\n\t */\n\tprivate void registerCodeCache(JadxDecompiler jadxDecompiler) {\n\t\tCodeCacheMode codeCacheMode = getSettings().getCodeCacheMode();\n\t\tif (codeCacheMode == CodeCacheMode.MEMORY) {\n\t\t\tjadxDecompiler.getArgs().setCodeCache(new InMemoryCodeCache());\n\t\t\treturn;\n\t\t}\n\t\tjadxDecompiler.addCustomPass(new JadxPreparePass() {\n\t\t\t@Override\n\t\t\tpublic JadxPassInfo getInfo() {\n\t\t\t\treturn new SimpleJadxPassInfo(\"CacheInit\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void init(RootNode root) {\n\t\t\t\tswitch (getSettings().getCodeCacheMode()) {\n\t\t\t\t\tcase DISK_WITH_CACHE:\n\t\t\t\t\t\troot.getArgs().setCodeCache(new CodeStringCache(buildBufferedDiskCache(root)));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase DISK:\n\t\t\t\t\t\troot.getArgs().setCodeCache(buildBufferedDiskCache(root));\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate BufferCodeCache buildBufferedDiskCache(RootNode root) {\n\t\tDiskCodeCache diskCache = new DiskCodeCache(root, getProject().getCacheDir());\n\t\treturn new BufferCodeCache(diskCache);\n\t}\n\n\tprivate void initUsageCache(JadxArgs jadxArgs) {\n\t\tswitch (getSettings().getUsageCacheMode()) {\n\t\t\tcase NONE:\n\t\t\t\tjadxArgs.setUsageInfoCache(new EmptyUsageInfoCache());\n\t\t\t\tbreak;\n\t\t\tcase MEMORY:\n\t\t\t\tjadxArgs.setUsageInfoCache(new InMemoryUsageInfoCache());\n\t\t\t\tbreak;\n\t\t\tcase DISK:\n\t\t\t\tjadxArgs.setUsageInfoCache(new UsageInfoCache(getProject().getCacheDir(), jadxArgs.getInputFiles()));\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tpublic static CommonGuiPluginsContext initGuiPluginsContext(JadxDecompiler decompiler, MainWindow mainWindow) {\n\t\tCommonGuiPluginsContext guiPluginsContext = new CommonGuiPluginsContext(mainWindow);\n\t\tdecompiler.getPluginManager().registerAddPluginListener(pluginContext -> {\n\t\t\tAppContext appContext = new AppContext();\n\t\t\tappContext.setGuiContext(guiPluginsContext.buildForPlugin(pluginContext));\n\t\t\tappContext.setFilesGetter(decompiler.getArgs().getFilesGetter());\n\t\t\tpluginContext.setAppContext(appContext);\n\t\t});\n\t\treturn guiPluginsContext;\n\t}\n\n\tpublic CommonGuiPluginsContext getGuiPluginsContext() {\n\t\treturn guiPluginsContext;\n\t}\n\n\tpublic void resetGuiPluginsContext() {\n\t\tguiPluginsContext.reset();\n\t}\n\n\tpublic void reloadPasses() {\n\t\tresetGuiPluginsContext();\n\t\tdecompiler.reloadPasses();\n\t}\n\n\t/**\n\t * Get the complete list of classes\n\t */\n\tpublic List<JavaClass> getClasses() {\n\t\treturn getDecompiler().getClasses();\n\t}\n\n\t/**\n\t * Get all classes that are not excluded by the excluded packages settings\n\t */\n\tpublic List<JavaClass> getIncludedClasses() {\n\t\tList<JavaClass> classList = getDecompiler().getClasses();\n\t\tList<String> excludedPackages = getExcludedPackages();\n\t\tif (excludedPackages.isEmpty()) {\n\t\t\treturn classList;\n\t\t}\n\t\treturn classList.stream()\n\t\t\t\t.filter(cls -> isClassIncluded(excludedPackages, cls))\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\t/**\n\t * Get all classes that are not excluded by the excluded packages settings including inner classes\n\t */\n\tpublic List<JavaClass> getIncludedClassesWithInners() {\n\t\tList<JavaClass> classes = getDecompiler().getClassesWithInners();\n\t\tList<String> excludedPackages = getExcludedPackages();\n\t\tif (excludedPackages.isEmpty()) {\n\t\t\treturn classes;\n\t\t}\n\t\treturn classes.stream()\n\t\t\t\t.filter(cls -> isClassIncluded(excludedPackages, cls))\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tprivate static boolean isClassIncluded(List<String> excludedPackages, JavaClass cls) {\n\t\tfor (String exclude : excludedPackages) {\n\t\t\tString clsFullName = cls.getFullName();\n\t\t\tif (clsFullName.equals(exclude)\n\t\t\t\t\t|| clsFullName.startsWith(exclude + '.')) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic List<List<JavaClass>> buildDecompileBatches(List<JavaClass> classes) {\n\t\treturn getDecompiler().getDecompileScheduler().buildBatches(classes);\n\t}\n\n\t// TODO: move to CLI and filter classes in JadxDecompiler\n\tpublic List<String> getExcludedPackages() {\n\t\tString excludedPackages = getSettings().getExcludedPackages().trim();\n\t\tif (excludedPackages.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn Arrays.asList(excludedPackages.split(\" +\"));\n\t}\n\n\tpublic void setExcludedPackages(List<String> packagesToExclude) {\n\t\tgetSettings().setExcludedPackages(String.join(\" \", packagesToExclude).trim());\n\t\tgetSettings().sync();\n\t}\n\n\tpublic void addExcludedPackage(String packageToExclude) {\n\t\tString newExclusion = getSettings().getExcludedPackages() + ' ' + packageToExclude;\n\t\tgetSettings().setExcludedPackages(newExclusion.trim());\n\t\tgetSettings().sync();\n\t}\n\n\tpublic void removeExcludedPackage(String packageToRemoveFromExclusion) {\n\t\tList<String> list = new ArrayList<>(getExcludedPackages());\n\t\tlist.remove(packageToRemoveFromExclusion);\n\t\tgetSettings().setExcludedPackages(String.join(\" \", list));\n\t\tgetSettings().sync();\n\t}\n\n\tpublic Optional<JadxDecompiler> getCurrentDecompiler() {\n\t\tsynchronized (DECOMPILER_UPDATE_SYNC) {\n\t\t\treturn Optional.ofNullable(decompiler);\n\t\t}\n\t}\n\n\t/**\n\t * TODO: make method private\n\t * Do not store JadxDecompiler in fields to not leak old instances\n\t */\n\tpublic @NotNull JadxDecompiler getDecompiler() {\n\t\tif (decompiler == null || decompiler.getRoot() == null) {\n\t\t\tthrow new JadxRuntimeException(\"Decompiler not yet loaded\");\n\t\t}\n\t\treturn decompiler;\n\t}\n\n\t// TODO: forbid usage of this method\n\tpublic RootNode getRootNode() {\n\t\treturn getDecompiler().getRoot();\n\t}\n\n\tpublic void reloadCodeData() {\n\t\tgetDecompiler().reloadCodeData();\n\t}\n\n\tpublic JavaNode getJavaNodeByRef(ICodeNodeRef nodeRef) {\n\t\treturn getDecompiler().getJavaNodeByRef(nodeRef);\n\t}\n\n\tpublic @Nullable JavaNode getEnclosingNode(ICodeInfo codeInfo, int pos) {\n\t\treturn getDecompiler().getEnclosingNode(codeInfo, pos);\n\t}\n\n\tpublic List<JavaPackage> getPackages() {\n\t\treturn getDecompiler().getPackages();\n\t}\n\n\tpublic List<ResourceFile> getResources() {\n\t\treturn getDecompiler().getResources();\n\t}\n\n\tpublic JadxArgs getArgs() {\n\t\treturn getDecompiler().getArgs();\n\t}\n\n\tpublic JadxProject getProject() {\n\t\treturn mainWindow.getProject();\n\t}\n\n\tpublic JadxSettings getSettings() {\n\t\treturn mainWindow.getSettings();\n\t}\n\n\tpublic CacheObject getCache() {\n\t\treturn mainWindow.getCacheObject();\n\t}\n\n\t/**\n\t * @param fullName Full name of an outer class. Inner classes are not supported.\n\t */\n\tpublic @Nullable JavaClass searchJavaClassByFullAlias(String fullName) {\n\t\treturn getDecompiler().getClasses().stream()\n\t\t\t\t.filter(cls -> cls.getFullName().equals(fullName))\n\t\t\t\t.findFirst()\n\t\t\t\t.orElse(null);\n\t}\n\n\tpublic @Nullable JavaClass searchJavaClassByOrigClassName(String fullName) {\n\t\treturn getDecompiler().searchJavaClassByOrigFullName(fullName);\n\t}\n\n\t/**\n\t * @param rawName Full raw name of an outer class. Inner classes are not supported.\n\t */\n\tpublic @Nullable JavaClass searchJavaClassByRawName(String rawName) {\n\t\treturn getDecompiler().getClasses().stream()\n\t\t\t\t.filter(cls -> cls.getRawName().equals(rawName))\n\t\t\t\t.findFirst()\n\t\t\t\t.orElse(null);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/CodeCacheMode.java",
    "content": "package jadx.gui.cache.code;\n\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport jadx.gui.utils.NLS;\n\npublic enum CodeCacheMode {\n\tMEMORY(\"preferences.codeCacheMode.memory\", \"preferences.codeCacheMode.memory.desc\"),\n\tDISK_WITH_CACHE(\"preferences.codeCacheMode.diskWithCache\", \"preferences.codeCacheMode.diskWithCache.desc\"),\n\tDISK(\"preferences.codeCacheMode.disk\", \"preferences.codeCacheMode.disk.desc\");\n\n\tprivate final String labelKey;\n\tprivate final String descKey;\n\n\tCodeCacheMode(String labelKey, String descKey) {\n\t\tthis.labelKey = labelKey;\n\t\tthis.descKey = descKey;\n\t}\n\n\tpublic String getLocalizedName() {\n\t\treturn NLS.str(labelKey);\n\t}\n\n\tpublic String getDesc() {\n\t\treturn NLS.str(descKey);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getLocalizedName();\n\t}\n\n\tpublic static String buildToolTip() {\n\t\treturn Stream.of(values())\n\t\t\t\t.map(v -> v.getLocalizedName() + \" - \" + v.getDesc())\n\t\t\t\t.collect(Collectors.joining(\"\\n\"));\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/CodeStringCache.java",
    "content": "package jadx.gui.cache.code;\n\nimport java.io.IOException;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.TimeUnit;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.reactivestreams.Subscriber;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.reactivex.rxjava3.disposables.Disposable;\nimport io.reactivex.rxjava3.processors.PublishProcessor;\n\nimport jadx.api.ICodeCache;\nimport jadx.api.ICodeInfo;\nimport jadx.api.impl.DelegateCodeCache;\nimport jadx.gui.utils.UiUtils;\n\n/**\n * Keep code strings for faster search\n */\npublic class CodeStringCache extends DelegateCodeCache {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CodeStringCache.class);\n\n\tprivate final Map<String, String> codeCache = new ConcurrentHashMap<>();\n\tprivate final Subscriber<Boolean> subscriber;\n\tprivate final Disposable disposable;\n\n\tpublic CodeStringCache(ICodeCache backCache) {\n\t\tsuper(backCache);\n\t\t// reset cache if free memory is low\n\t\t// check only on changes (with debounce) to reduce background checks if app not used\n\t\tPublishProcessor<Boolean> processor = PublishProcessor.create();\n\t\tsubscriber = processor;\n\t\tdisposable = processor.debounce(3, TimeUnit.SECONDS)\n\t\t\t\t.map(v -> UiUtils.isFreeMemoryAvailable())\n\t\t\t\t.filter(v -> !v)\n\t\t\t\t.subscribe(v -> {\n\t\t\t\t\tLOG.warn(\"Free memory is low! Reset code strings cache. Cache size {}\", codeCache.size());\n\t\t\t\t\tcodeCache.clear();\n\t\t\t\t\tSystem.gc();\n\t\t\t\t});\n\t}\n\n\t@Override\n\t@Nullable\n\tpublic String getCode(String clsFullName) {\n\t\tsubscriber.onNext(Boolean.TRUE);\n\t\tString code = codeCache.get(clsFullName);\n\t\tif (code != null) {\n\t\t\treturn code;\n\t\t}\n\t\tString backCode = backCache.getCode(clsFullName);\n\t\tif (backCode != null) {\n\t\t\tcodeCache.put(clsFullName, backCode);\n\t\t}\n\t\treturn backCode;\n\t}\n\n\t@Override\n\tpublic @NotNull ICodeInfo get(String clsFullName) {\n\t\tsubscriber.onNext(Boolean.TRUE);\n\t\treturn super.get(clsFullName);\n\t}\n\n\t@Override\n\tpublic void add(String clsFullName, ICodeInfo codeInfo) {\n\t\tsubscriber.onNext(Boolean.TRUE);\n\t\tcodeCache.put(clsFullName, codeInfo.getCodeStr());\n\t\tbackCache.add(clsFullName, codeInfo);\n\t}\n\n\t@Override\n\tpublic void remove(String clsFullName) {\n\t\tcodeCache.remove(clsFullName);\n\t\tbackCache.remove(clsFullName);\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\ttry {\n\t\t\tbackCache.close();\n\t\t} finally {\n\t\t\tcodeCache.clear();\n\t\t\tsubscriber.onComplete();\n\t\t\tdisposable.dispose();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/FixedCodeCache.java",
    "content": "package jadx.gui.cache.code;\n\nimport jadx.api.ICodeCache;\nimport jadx.api.ICodeInfo;\nimport jadx.api.impl.DelegateCodeCache;\n\n/**\n * Code cache with fixed size of wrapped code cache ('remove' and 'add' methods will do nothing).\n */\npublic class FixedCodeCache extends DelegateCodeCache {\n\n\tpublic FixedCodeCache(ICodeCache codeCache) {\n\t\tsuper(codeCache);\n\t}\n\n\t@Override\n\tpublic void remove(String clsFullName) {\n\t\t// no op\n\t}\n\n\t@Override\n\tpublic void add(String clsFullName, ICodeInfo codeInfo) {\n\t\t// no op\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/disk/BufferCodeCache.java",
    "content": "package jadx.gui.cache.code.disk;\n\nimport java.io.IOException;\nimport java.util.Deque;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentLinkedDeque;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ICodeCache;\nimport jadx.api.ICodeInfo;\n\npublic class BufferCodeCache implements ICodeCache {\n\n\tprivate static final int BUFFER_SIZE = 20;\n\n\tprivate final ICodeCache backCache;\n\tprivate final Map<String, ICodeInfo> cache = new ConcurrentHashMap<>();\n\tprivate final Deque<String> buffer = new ConcurrentLinkedDeque<>();\n\n\tpublic BufferCodeCache(ICodeCache backCache) {\n\t\tthis.backCache = backCache;\n\t}\n\n\tprivate void addInternal(String clsFullName, ICodeInfo codeInfo) {\n\t\tcache.put(clsFullName, codeInfo);\n\t\tbuffer.addLast(clsFullName);\n\t\tif (buffer.size() > BUFFER_SIZE) {\n\t\t\tString removedKey = buffer.removeFirst();\n\t\t\tcache.remove(removedKey);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean contains(String clsFullName) {\n\t\tif (cache.containsKey(clsFullName)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn backCache.contains(clsFullName);\n\t}\n\n\t@Override\n\tpublic void add(String clsFullName, ICodeInfo codeInfo) {\n\t\taddInternal(clsFullName, codeInfo);\n\t\tbackCache.add(clsFullName, codeInfo);\n\t}\n\n\t@Override\n\tpublic @NotNull ICodeInfo get(String clsFullName) {\n\t\tICodeInfo codeInfo = cache.get(clsFullName);\n\t\tif (codeInfo != null) {\n\t\t\treturn codeInfo;\n\t\t}\n\t\tICodeInfo backCodeInfo = backCache.get(clsFullName);\n\t\tif (backCodeInfo != ICodeInfo.EMPTY) {\n\t\t\taddInternal(clsFullName, backCodeInfo);\n\t\t}\n\t\treturn backCodeInfo;\n\t}\n\n\t@Override\n\tpublic @Nullable String getCode(String clsFullName) {\n\t\tICodeInfo codeInfo = cache.get(clsFullName);\n\t\tif (codeInfo != null) {\n\t\t\treturn codeInfo.getCodeStr();\n\t\t}\n\t\treturn backCache.getCode(clsFullName);\n\t}\n\n\t@Override\n\tpublic void remove(String clsFullName) {\n\t\tcache.remove(clsFullName);\n\t\tbackCache.remove(clsFullName);\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\tcache.clear();\n\t\tbuffer.clear();\n\t\tbackCache.close();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/disk/CodeMetadataAdapter.java",
    "content": "package jadx.gui.cache.code.disk;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.DataInput;\nimport java.io.DataInputStream;\nimport java.io.DataOutput;\nimport java.io.DataOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.impl.AnnotatedCodeInfo;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeMetadata;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.gui.cache.code.disk.adapters.CodeAnnotationAdapter;\nimport jadx.gui.cache.code.disk.adapters.DataAdapterHelper;\n\nimport static java.nio.file.StandardOpenOption.CREATE;\nimport static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;\nimport static java.nio.file.StandardOpenOption.WRITE;\n\npublic class CodeMetadataAdapter {\n\tprivate static final byte[] JADX_METADATA_HEADER = \"jadxmd\".getBytes(StandardCharsets.US_ASCII);\n\n\tprivate final CodeAnnotationAdapter codeAnnotationAdapter;\n\n\tpublic CodeMetadataAdapter(RootNode root) {\n\t\tcodeAnnotationAdapter = new CodeAnnotationAdapter(root);\n\t}\n\n\tpublic void write(Path metadataFile, ICodeMetadata metadata) {\n\t\tFileUtils.makeDirsForFile(metadataFile);\n\t\ttry (OutputStream fileOutput = Files.newOutputStream(metadataFile, WRITE, CREATE, TRUNCATE_EXISTING);\n\t\t\t\tDataOutputStream out = new DataOutputStream(new BufferedOutputStream(fileOutput))) {\n\t\t\tout.write(JADX_METADATA_HEADER);\n\t\t\twriteLines(out, metadata.getLineMapping());\n\t\t\twriteAnnotations(out, metadata.getAsMap());\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to write metadata file\", e);\n\t\t}\n\t}\n\n\tpublic ICodeInfo readAndBuild(Path metadataFile, String code) {\n\t\tif (!Files.exists(metadataFile)) {\n\t\t\treturn new SimpleCodeInfo(code);\n\t\t}\n\t\ttry (InputStream fileInput = Files.newInputStream(metadataFile);\n\t\t\t\tDataInputStream in = new DataInputStream(new BufferedInputStream(fileInput))) {\n\t\t\tin.skipBytes(JADX_METADATA_HEADER.length);\n\t\t\tMap<Integer, Integer> lines = readLines(in);\n\t\t\tMap<Integer, ICodeAnnotation> annotations = readAnnotations(in);\n\t\t\treturn new AnnotatedCodeInfo(code, lines, annotations);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to parse code annotations\", e);\n\t\t}\n\t}\n\n\tprivate void writeLines(DataOutput out, Map<Integer, Integer> lines) throws IOException {\n\t\tout.writeInt(lines.size());\n\t\tfor (Map.Entry<Integer, Integer> entry : lines.entrySet()) {\n\t\t\tDataAdapterHelper.writeUVInt(out, entry.getKey());\n\t\t\tDataAdapterHelper.writeUVInt(out, entry.getValue());\n\t\t}\n\t}\n\n\tprivate Map<Integer, Integer> readLines(DataInput in) throws IOException {\n\t\tint size = in.readInt();\n\t\tif (size == 0) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tMap<Integer, Integer> lines = new HashMap<>(size);\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tint key = DataAdapterHelper.readUVInt(in);\n\t\t\tint value = DataAdapterHelper.readUVInt(in);\n\t\t\tlines.put(key, value);\n\t\t}\n\t\treturn lines;\n\t}\n\n\tprivate void writeAnnotations(DataOutputStream out, Map<Integer, ICodeAnnotation> annotations) throws IOException {\n\t\tout.writeInt(annotations.size());\n\t\tfor (Map.Entry<Integer, ICodeAnnotation> entry : annotations.entrySet()) {\n\t\t\tDataAdapterHelper.writeUVInt(out, entry.getKey());\n\t\t\tcodeAnnotationAdapter.write(out, entry.getValue());\n\t\t}\n\t}\n\n\tprivate Map<Integer, ICodeAnnotation> readAnnotations(DataInputStream in) throws IOException {\n\t\tint size = in.readInt();\n\t\tif (size == 0) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tMap<Integer, ICodeAnnotation> map = new HashMap<>(size);\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tint pos = DataAdapterHelper.readUVInt(in);\n\t\t\tICodeAnnotation ann = codeAnnotationAdapter.read(in);\n\t\t\tif (ann != null) {\n\t\t\t\tmap.put(pos, ann);\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/disk/DiskCodeCache.java",
    "content": "package jadx.gui.cache.code.disk;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Stream;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeCache;\nimport jadx.api.ICodeInfo;\nimport jadx.api.JadxArgs;\nimport jadx.api.JadxDecompiler;\nimport jadx.core.Jadx;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\n\npublic class DiskCodeCache implements ICodeCache {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DiskCodeCache.class);\n\n\tprivate static final int DATA_FORMAT_VERSION = 15;\n\n\tprivate final Path baseDir;\n\tprivate final Path srcDir;\n\tprivate final Path metaDir;\n\tprivate final Path codeVersionFile;\n\tprivate final String codeVersion;\n\tprivate final CodeMetadataAdapter codeMetadataAdapter;\n\tprivate final ExecutorService writePool;\n\tprivate final Map<String, CacheData> clsDataMap;\n\n\tpublic DiskCodeCache(RootNode root, Path projectCacheDir) {\n\t\tbaseDir = projectCacheDir.resolve(\"code\");\n\t\tsrcDir = baseDir.resolve(\"sources\");\n\t\tmetaDir = baseDir.resolve(\"metadata\");\n\t\tcodeVersionFile = baseDir.resolve(\"code-version\");\n\t\tJadxArgs args = root.getArgs();\n\t\tcodeVersion = buildCodeVersion(args, root.getDecompiler());\n\t\twritePool = Executors.newFixedThreadPool(args.getThreadsCount());\n\t\tcodeMetadataAdapter = new CodeMetadataAdapter(root);\n\t\tclsDataMap = buildClassDataMap(root.getClasses());\n\t\tif (checkCodeVersion()) {\n\t\t\tloadCachedSet();\n\t\t} else {\n\t\t\treset();\n\t\t}\n\t}\n\n\tprivate boolean checkCodeVersion() {\n\t\ttry {\n\t\t\tif (!Files.exists(codeVersionFile)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tString currentCodeVer = FileUtils.readFile(codeVersionFile);\n\t\t\treturn currentCodeVer.equals(codeVersion);\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to load code version file\", e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate void reset() {\n\t\ttry {\n\t\t\tlong start = System.currentTimeMillis();\n\t\t\tLOG.info(\"Resetting disk code cache, base dir: {}\", baseDir.toAbsolutePath());\n\t\t\tFileUtils.deleteDirIfExists(baseDir);\n\t\t\tif (Files.exists(baseDir.getParent().resolve(codeVersionFile.getFileName()))) {\n\t\t\t\t// remove old version cache files\n\t\t\t\tFileUtils.deleteDirIfExists(baseDir.getParent());\n\t\t\t}\n\t\t\tFileUtils.makeDirs(srcDir);\n\t\t\tFileUtils.makeDirs(metaDir);\n\t\t\tFileUtils.writeFile(codeVersionFile, codeVersion);\n\t\t\tif (LOG.isDebugEnabled()) {\n\t\t\t\tLOG.info(\"Reset done in: {}ms\", System.currentTimeMillis() - start);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to reset code cache\", e);\n\t\t} finally {\n\t\t\tclsDataMap.values().forEach(d -> d.setCached(false));\n\t\t}\n\t}\n\n\t/**\n\t * Async writes backed by in-memory store\n\t */\n\t@Override\n\tpublic void add(String clsFullName, ICodeInfo codeInfo) {\n\t\tCacheData clsData = getClsData(clsFullName);\n\t\tclsData.setTmpCodeInfo(codeInfo);\n\t\tclsData.setCached(true);\n\t\twritePool.execute(() -> {\n\t\t\ttry {\n\t\t\t\tint clsId = clsData.getClsId();\n\t\t\t\tICodeInfo code = clsData.getTmpCodeInfo();\n\t\t\t\tif (code != null) {\n\t\t\t\t\tFileUtils.writeFile(getJavaFile(clsId), code.getCodeStr());\n\t\t\t\t\tcodeMetadataAdapter.write(getMetadataFile(clsId), code.getCodeMetadata());\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to write code cache for \" + clsFullName, e);\n\t\t\t\tremove(clsFullName);\n\t\t\t} finally {\n\t\t\t\tclsData.setTmpCodeInfo(null);\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic @Nullable String getCode(String clsFullName) {\n\t\ttry {\n\t\t\tif (!contains(clsFullName)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tCacheData clsData = getClsData(clsFullName);\n\t\t\tICodeInfo tmpCodeInfo = clsData.getTmpCodeInfo();\n\t\t\tif (tmpCodeInfo != null) {\n\t\t\t\treturn tmpCodeInfo.getCodeStr();\n\t\t\t}\n\t\t\tPath javaFile = getJavaFile(clsData.getClsId());\n\t\t\tif (!Files.exists(javaFile)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn FileUtils.readFile(javaFile);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to read class code for {}\", clsFullName, e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic @NotNull ICodeInfo get(String clsFullName) {\n\t\ttry {\n\t\t\tif (!contains(clsFullName)) {\n\t\t\t\treturn ICodeInfo.EMPTY;\n\t\t\t}\n\t\t\tCacheData clsData = getClsData(clsFullName);\n\t\t\tICodeInfo tmpCodeInfo = clsData.getTmpCodeInfo();\n\t\t\tif (tmpCodeInfo != null) {\n\t\t\t\treturn tmpCodeInfo;\n\t\t\t}\n\t\t\tint clsId = clsData.getClsId();\n\t\t\tPath javaFile = getJavaFile(clsId);\n\t\t\tif (!Files.exists(javaFile)) {\n\t\t\t\treturn ICodeInfo.EMPTY;\n\t\t\t}\n\t\t\tString code = FileUtils.readFile(javaFile);\n\t\t\treturn codeMetadataAdapter.readAndBuild(getMetadataFile(clsId), code);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to read code cache for {}\", clsFullName, e);\n\t\t\treturn ICodeInfo.EMPTY;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean contains(String clsFullName) {\n\t\treturn getClsData(clsFullName).isCached();\n\t}\n\n\t@Override\n\tpublic void remove(String clsFullName) {\n\t\ttry {\n\t\t\tCacheData clsData = getClsData(clsFullName);\n\t\t\tif (clsData.isCached()) {\n\t\t\t\tclsData.setCached(false);\n\t\t\t\tif (clsData.getTmpCodeInfo() == null) {\n\t\t\t\t\tLOG.debug(\"Removing class info from disk: {}\", clsFullName);\n\t\t\t\t\tint clsId = clsData.getClsId();\n\t\t\t\t\tFiles.deleteIfExists(getJavaFile(clsId));\n\t\t\t\t\tFiles.deleteIfExists(getMetadataFile(clsId));\n\t\t\t\t} else {\n\t\t\t\t\t// class info not yet written to disk\n\t\t\t\t\tclsData.setTmpCodeInfo(null);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to remove code cache for \" + clsFullName, e);\n\t\t}\n\t}\n\n\tprivate String buildCodeVersion(JadxArgs args, @Nullable JadxDecompiler decompiler) {\n\t\tList<File> inputFiles = new ArrayList<>(args.getInputFiles());\n\t\tif (args.getGeneratedRenamesMappingFileMode().shouldRead()\n\t\t\t\t&& args.getGeneratedRenamesMappingFile() != null\n\t\t\t\t&& args.getGeneratedRenamesMappingFile().exists()) {\n\t\t\tinputFiles.add(args.getGeneratedRenamesMappingFile());\n\t\t}\n\t\treturn DATA_FORMAT_VERSION\n\t\t\t\t+ \":\" + Jadx.getVersion()\n\t\t\t\t+ \":\" + args.makeCodeArgsHash(decompiler)\n\t\t\t\t+ \":\" + FileUtils.buildInputsHash(Utils.collectionMap(inputFiles, File::toPath));\n\t}\n\n\tprivate CacheData getClsData(String clsFullName) {\n\t\tCacheData clsData = clsDataMap.get(clsFullName);\n\t\tif (clsData == null) {\n\t\t\tthrow new JadxRuntimeException(\"Unknown class name: \" + clsFullName);\n\t\t}\n\t\treturn clsData;\n\t}\n\n\tprivate void loadCachedSet() {\n\t\tlong start = System.currentTimeMillis();\n\t\tBitSet cachedSet = new BitSet(clsDataMap.size());\n\t\ttry (Stream<Path> stream = Files.walk(metaDir)) {\n\t\t\tstream.forEach(file -> {\n\t\t\t\tString fileName = file.getFileName().toString();\n\t\t\t\tif (fileName.endsWith(\".jadxmd\")) {\n\t\t\t\t\tString idStr = StringUtils.removeSuffix(fileName, \".jadxmd\");\n\t\t\t\t\tint clsId = Integer.parseInt(idStr, 16);\n\t\t\t\t\tcachedSet.set(clsId);\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to enumerate cached classes\", e);\n\t\t}\n\t\tint count = 0;\n\t\tfor (CacheData data : clsDataMap.values()) {\n\t\t\tint clsId = data.getClsId();\n\t\t\tif (cachedSet.get(clsId)) {\n\t\t\t\tdata.setCached(true);\n\t\t\t\tcount++;\n\t\t\t}\n\t\t}\n\t\tLOG.info(\"Found {} classes in disk cache, time: {}ms, dir: {}\",\n\t\t\t\tcount, System.currentTimeMillis() - start, metaDir.getParent());\n\t}\n\n\tprivate Path getJavaFile(int clsId) {\n\t\treturn srcDir.resolve(getPathForClsId(clsId, \".java\"));\n\t}\n\n\tprivate Path getMetadataFile(int clsId) {\n\t\treturn metaDir.resolve(getPathForClsId(clsId, \".jadxmd\"));\n\t}\n\n\tprivate Path getPathForClsId(int clsId, String ext) {\n\t\t// all classes divided between 256 top level folders\n\t\tString firstByte = FileUtils.byteToHex(clsId);\n\t\treturn Paths.get(firstByte, FileUtils.intToHex(clsId) + ext);\n\t}\n\n\tprivate Map<String, CacheData> buildClassDataMap(List<ClassNode> classes) {\n\t\tint clsCount = classes.size();\n\t\tMap<String, CacheData> map = new HashMap<>(clsCount);\n\t\tfor (int i = 0; i < clsCount; i++) {\n\t\t\tClassNode cls = classes.get(i);\n\t\t\tmap.put(cls.getRawName(), new CacheData(i));\n\t\t}\n\t\treturn map;\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\tsynchronized (this) {\n\t\t\ttry {\n\t\t\t\twritePool.shutdown();\n\t\t\t\tboolean completed = writePool.awaitTermination(1, TimeUnit.MINUTES);\n\t\t\t\tif (!completed) {\n\t\t\t\t\tLOG.warn(\"Disk code cache closing terminated by timeout\");\n\t\t\t\t}\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\tLOG.error(\"Failed to close disk code cache\", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static final class CacheData {\n\t\tprivate final int clsId;\n\t\tprivate boolean cached;\n\t\tprivate @Nullable ICodeInfo tmpCodeInfo;\n\n\t\tpublic CacheData(int clsId) {\n\t\t\tthis.clsId = clsId;\n\t\t}\n\n\t\tpublic int getClsId() {\n\t\t\treturn clsId;\n\t\t}\n\n\t\tpublic boolean isCached() {\n\t\t\treturn cached;\n\t\t}\n\n\t\tpublic void setCached(boolean cached) {\n\t\t\tthis.cached = cached;\n\t\t}\n\n\t\tpublic @Nullable ICodeInfo getTmpCodeInfo() {\n\t\t\treturn tmpCodeInfo;\n\t\t}\n\n\t\tpublic void setTmpCodeInfo(@Nullable ICodeInfo tmpCodeInfo) {\n\t\t\tthis.tmpCodeInfo = tmpCodeInfo;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/disk/adapters/ArgTypeAdapter.java",
    "content": "package jadx.gui.cache.code.disk.adapters;\n\nimport java.io.DataInput;\nimport java.io.DataOutput;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class ArgTypeAdapter implements DataAdapter<ArgType> {\n\n\tpublic static final ArgTypeAdapter INSTANCE = new ArgTypeAdapter();\n\n\tprivate enum Types {\n\t\tNULL,\n\t\tUNKNOWN,\n\t\tPRIMITIVE,\n\t\tARRAY,\n\t\tOBJECT,\n\t\tWILDCARD,\n\t\tGENERIC,\n\t\tTYPE_VARIABLE,\n\t\tOUTER_GENERIC\n\t}\n\n\t@Override\n\tpublic void write(DataOutput out, ArgType value) throws IOException {\n\t\tif (value == null) {\n\t\t\twriteType(out, Types.NULL);\n\t\t\treturn;\n\t\t}\n\t\tif (!value.isTypeKnown()) {\n\t\t\twriteType(out, Types.UNKNOWN);\n\t\t\treturn;\n\t\t}\n\t\tif (value.isPrimitive()) {\n\t\t\twriteType(out, Types.PRIMITIVE);\n\t\t\tout.writeByte(value.getPrimitiveType().getShortName().charAt(0));\n\t\t\treturn;\n\t\t}\n\t\tif (value.getOuterType() != null) {\n\t\t\twriteType(out, Types.OUTER_GENERIC);\n\t\t\twrite(out, value.getOuterType());\n\t\t\twrite(out, value.getInnerType());\n\t\t\treturn;\n\t\t}\n\t\tif (value.getWildcardType() != null) {\n\t\t\twriteType(out, Types.WILDCARD);\n\t\t\tArgType.WildcardBound bound = value.getWildcardBound();\n\t\t\tout.writeByte(bound.getNum());\n\t\t\tif (bound != ArgType.WildcardBound.UNBOUND) {\n\t\t\t\twrite(out, value.getWildcardType());\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (value.isGeneric()) {\n\t\t\twriteType(out, Types.GENERIC);\n\t\t\tout.writeUTF(value.getObject());\n\t\t\twriteTypesList(out, value.getGenericTypes());\n\t\t\treturn;\n\t\t}\n\t\tif (value.isGenericType()) {\n\t\t\twriteType(out, Types.TYPE_VARIABLE);\n\t\t\tout.writeUTF(value.getObject());\n\t\t\twriteTypesList(out, value.getExtendTypes());\n\t\t\treturn;\n\t\t}\n\t\tif (value.isObject()) {\n\t\t\twriteType(out, Types.OBJECT);\n\t\t\tout.writeUTF(value.getObject());\n\t\t\treturn;\n\t\t}\n\t\tif (value.isArray()) {\n\t\t\twriteType(out, Types.ARRAY);\n\t\t\tout.writeByte(value.getArrayDimension());\n\t\t\twrite(out, value.getArrayRootElement());\n\t\t\treturn;\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Cannot save type: \" + value + \", cls: \" + value.getClass());\n\t}\n\n\tprivate void writeType(DataOutput out, Types type) throws IOException {\n\t\tout.writeByte(type.ordinal());\n\t}\n\n\t@Override\n\tpublic ArgType read(DataInput in) throws IOException {\n\t\tbyte typeOrdinal = in.readByte();\n\t\tTypes type = Types.values()[typeOrdinal];\n\t\tswitch (type) {\n\t\t\tcase NULL:\n\t\t\t\treturn null;\n\n\t\t\tcase UNKNOWN:\n\t\t\t\treturn ArgType.UNKNOWN;\n\n\t\t\tcase PRIMITIVE:\n\t\t\t\tchar shortName = (char) in.readByte();\n\t\t\t\treturn ArgType.parse(shortName);\n\n\t\t\tcase OUTER_GENERIC:\n\t\t\t\tArgType outerType = read(in);\n\t\t\t\tArgType innerType = read(in);\n\t\t\t\treturn ArgType.outerGeneric(outerType, innerType);\n\n\t\t\tcase WILDCARD:\n\t\t\t\tArgType.WildcardBound bound = ArgType.WildcardBound.getByNum(in.readByte());\n\t\t\t\tif (bound == ArgType.WildcardBound.UNBOUND) {\n\t\t\t\t\treturn ArgType.WILDCARD;\n\t\t\t\t}\n\t\t\t\tArgType objType = read(in);\n\t\t\t\treturn ArgType.wildcard(objType, bound);\n\n\t\t\tcase GENERIC:\n\t\t\t\tString clsType = in.readUTF();\n\t\t\t\treturn ArgType.generic(clsType, readTypesList(in));\n\n\t\t\tcase TYPE_VARIABLE:\n\t\t\t\tString typeVar = in.readUTF();\n\t\t\t\tList<ArgType> extendTypes = readTypesList(in);\n\t\t\t\treturn ArgType.genericType(typeVar, extendTypes);\n\n\t\t\tcase OBJECT:\n\t\t\t\treturn ArgType.object(in.readUTF());\n\n\t\t\tcase ARRAY:\n\t\t\t\tint dim = in.readByte();\n\t\t\t\tArgType rootType = read(in);\n\t\t\t\treturn ArgType.array(rootType, dim);\n\n\t\t\tdefault:\n\t\t\t\tthrow new RuntimeException(\"Unexpected arg type: \" + type);\n\t\t}\n\t}\n\n\tprivate void writeTypesList(DataOutput out, List<ArgType> types) throws IOException {\n\t\tout.writeByte(types.size());\n\t\tfor (ArgType type : types) {\n\t\t\twrite(out, type);\n\t\t}\n\t}\n\n\tprivate List<ArgType> readTypesList(DataInput in) throws IOException {\n\t\tbyte size = in.readByte();\n\t\tif (size == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<ArgType> list = new ArrayList<>(size);\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tlist.add(read(in));\n\t\t}\n\t\treturn list;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/disk/adapters/ClassNodeAdapter.java",
    "content": "package jadx.gui.cache.code.disk.adapters;\n\nimport java.io.DataInput;\nimport java.io.DataOutput;\nimport java.io.IOException;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.RootNode;\n\npublic class ClassNodeAdapter implements DataAdapter<ClassNode> {\n\tprivate final RootNode root;\n\n\tpublic ClassNodeAdapter(RootNode root) {\n\t\tthis.root = root;\n\t}\n\n\t@Override\n\tpublic void write(DataOutput out, ClassNode value) throws IOException {\n\t\tout.writeUTF(value.getClassInfo().getRawName());\n\t}\n\n\t@Override\n\tpublic ClassNode read(DataInput in) throws IOException {\n\t\treturn root.resolveRawClass(in.readUTF());\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/disk/adapters/CodeAnnotationAdapter.java",
    "content": "package jadx.gui.cache.code.disk.adapters;\n\nimport java.io.DataInput;\nimport java.io.DataOutput;\nimport java.io.IOException;\nimport java.util.EnumMap;\nimport java.util.Map;\n\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeAnnotation.AnnType;\nimport jadx.core.dex.nodes.RootNode;\n\npublic class CodeAnnotationAdapter implements DataAdapter<ICodeAnnotation> {\n\tprivate final Map<AnnType, TypeInfo> adaptersByCls;\n\tprivate final TypeInfo[] adaptersByTag;\n\n\tpublic CodeAnnotationAdapter(RootNode root) {\n\t\tMap<AnnType, DataAdapter<?>> map = registerAdapters(root);\n\t\tint size = map.size();\n\t\tadaptersByCls = new EnumMap<>(AnnType.class);\n\t\tadaptersByTag = new TypeInfo[size + 1];\n\t\tint tag = 1;\n\t\tfor (Map.Entry<AnnType, DataAdapter<?>> entry : map.entrySet()) {\n\t\t\tTypeInfo typeInfo = new TypeInfo(tag, entry.getValue());\n\t\t\tadaptersByCls.put(entry.getKey(), typeInfo);\n\t\t\tadaptersByTag[tag] = typeInfo;\n\t\t\ttag++;\n\t\t}\n\t}\n\n\tprivate Map<AnnType, DataAdapter<?>> registerAdapters(RootNode root) {\n\t\tMap<AnnType, DataAdapter<?>> map = new EnumMap<>(AnnType.class);\n\t\tMethodNodeAdapter mthAdapter = new MethodNodeAdapter(root);\n\t\tmap.put(AnnType.CLASS, new ClassNodeAdapter(root));\n\t\tmap.put(AnnType.FIELD, new FieldNodeAdapter(root));\n\t\tmap.put(AnnType.METHOD, mthAdapter);\n\t\tmap.put(AnnType.DECLARATION, new NodeDeclareRefAdapter(this));\n\t\tmap.put(AnnType.VAR, new VarNodeAdapter(mthAdapter));\n\t\tmap.put(AnnType.VAR_REF, VarRefAdapter.INSTANCE);\n\t\tmap.put(AnnType.OFFSET, InsnCodeOffsetAdapter.INSTANCE);\n\t\tmap.put(AnnType.END, new NodeEndAdapter());\n\t\treturn map;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic void write(DataOutput out, ICodeAnnotation value) throws IOException {\n\t\tif (value == null) {\n\t\t\tout.writeByte(0);\n\t\t\treturn;\n\t\t}\n\t\tTypeInfo typeInfo = adaptersByCls.get(value.getAnnType());\n\t\tif (typeInfo == null) {\n\t\t\tthrow new RuntimeException(\"Unexpected code annotation type: \" + value.getClass().getSimpleName());\n\t\t}\n\t\tout.writeByte(typeInfo.getTag());\n\t\ttypeInfo.getAdapter().write(out, value);\n\t}\n\n\t@Override\n\tpublic ICodeAnnotation read(DataInput in) throws IOException {\n\t\tint tag = in.readByte();\n\t\tif (tag == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tTypeInfo typeInfo = adaptersByTag[tag];\n\t\tif (typeInfo == null) {\n\t\t\tthrow new RuntimeException(\"Unknown type tag: \" + tag);\n\t\t}\n\t\treturn (ICodeAnnotation) typeInfo.getAdapter().read(in);\n\t}\n\n\t@SuppressWarnings(\"rawtypes\")\n\tprivate static class TypeInfo {\n\t\tprivate final int tag;\n\t\tprivate final DataAdapter adapter;\n\n\t\tprivate TypeInfo(int tag, DataAdapter adapter) {\n\t\t\tthis.tag = tag;\n\t\t\tthis.adapter = adapter;\n\t\t}\n\n\t\tpublic int getTag() {\n\t\t\treturn tag;\n\t\t}\n\n\t\tpublic DataAdapter getAdapter() {\n\t\t\treturn adapter;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/disk/adapters/DataAdapter.java",
    "content": "package jadx.gui.cache.code.disk.adapters;\n\nimport java.io.DataInput;\nimport java.io.DataOutput;\nimport java.io.IOException;\n\npublic interface DataAdapter<T> {\n\n\tvoid write(DataOutput out, T value) throws IOException;\n\n\tT read(DataInput in) throws IOException;\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/disk/adapters/DataAdapterHelper.java",
    "content": "package jadx.gui.cache.code.disk.adapters;\n\nimport java.io.DataInput;\nimport java.io.DataOutput;\nimport java.io.IOException;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic class DataAdapterHelper {\n\n\tpublic static void writeNullableUTF(DataOutput out, @Nullable String str) throws IOException {\n\t\tif (str == null) {\n\t\t\tout.writeByte(0);\n\t\t} else {\n\t\t\tout.writeByte(1);\n\t\t\tout.writeUTF(str);\n\t\t}\n\t}\n\n\tpublic static @Nullable String readNullableUTF(DataInput in) throws IOException {\n\t\tif (in.readByte() == 0) {\n\t\t\treturn null;\n\t\t}\n\t\treturn in.readUTF();\n\t}\n\n\t/**\n\t * Write unsigned variable length integer (ULEB128 encoding)\n\t */\n\tpublic static void writeUVInt(DataOutput out, int val) throws IOException {\n\t\tif (val < 0) {\n\t\t\tthrow new IllegalArgumentException(\"Expect value >= 0, got: \" + val);\n\t\t}\n\t\tint current = val;\n\t\tint next = val;\n\t\twhile (true) {\n\t\t\tnext >>>= 7;\n\t\t\tif (next == 0) {\n\t\t\t\t// last byte\n\t\t\t\tout.writeByte(current & 0x7f);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tout.writeByte((current & 0x7f) | 0x80);\n\t\t\tcurrent = next;\n\t\t}\n\t}\n\n\t/**\n\t * Read unsigned variable length integer (ULEB128 encoding)\n\t */\n\tpublic static int readUVInt(DataInput in) throws IOException {\n\t\tint result = 0;\n\t\tint shift = 0;\n\t\twhile (true) {\n\t\t\tbyte v = in.readByte();\n\t\t\tresult |= (v & (byte) 0x7f) << shift;\n\t\t\tshift += 7;\n\t\t\tif ((v & 0x80) != 0x80) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/disk/adapters/FieldNodeAdapter.java",
    "content": "package jadx.gui.cache.code.disk.adapters;\n\nimport java.io.DataInput;\nimport java.io.DataOutput;\nimport java.io.IOException;\n\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.RootNode;\n\npublic class FieldNodeAdapter implements DataAdapter<FieldNode> {\n\tprivate final RootNode root;\n\n\tpublic FieldNodeAdapter(RootNode root) {\n\t\tthis.root = root;\n\t}\n\n\t@Override\n\tpublic void write(DataOutput out, FieldNode value) throws IOException {\n\t\tFieldInfo fieldInfo = value.getFieldInfo();\n\t\tout.writeUTF(fieldInfo.getDeclClass().getRawName());\n\t\tout.writeUTF(fieldInfo.getShortId());\n\t}\n\n\t@Override\n\tpublic FieldNode read(DataInput in) throws IOException {\n\t\tString cls = in.readUTF();\n\t\tString sign = in.readUTF();\n\t\tClassNode clsNode = root.resolveRawClass(cls);\n\t\tif (clsNode == null) {\n\t\t\tthrow new RuntimeException(\"Class not found: \" + cls);\n\t\t}\n\t\tFieldNode fieldNode = clsNode.searchFieldByShortId(sign);\n\t\tif (fieldNode == null) {\n\t\t\tthrow new RuntimeException(\"Field not found: \" + cls + \".\" + sign);\n\t\t}\n\t\treturn fieldNode;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/disk/adapters/InsnCodeOffsetAdapter.java",
    "content": "package jadx.gui.cache.code.disk.adapters;\n\nimport java.io.DataInput;\nimport java.io.DataOutput;\nimport java.io.IOException;\n\nimport jadx.api.metadata.annotations.InsnCodeOffset;\n\npublic class InsnCodeOffsetAdapter implements DataAdapter<InsnCodeOffset> {\n\n\tpublic static final InsnCodeOffsetAdapter INSTANCE = new InsnCodeOffsetAdapter();\n\n\t@Override\n\tpublic void write(DataOutput out, InsnCodeOffset value) throws IOException {\n\t\tDataAdapterHelper.writeUVInt(out, value.getOffset());\n\t}\n\n\t@Override\n\tpublic InsnCodeOffset read(DataInput in) throws IOException {\n\t\treturn new InsnCodeOffset(DataAdapterHelper.readUVInt(in));\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/disk/adapters/MethodNodeAdapter.java",
    "content": "package jadx.gui.cache.code.disk.adapters;\n\nimport java.io.DataInput;\nimport java.io.DataOutput;\nimport java.io.IOException;\n\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\n\npublic class MethodNodeAdapter implements DataAdapter<MethodNode> {\n\tprivate final RootNode root;\n\n\tpublic MethodNodeAdapter(RootNode root) {\n\t\tthis.root = root;\n\t}\n\n\t@Override\n\tpublic void write(DataOutput out, MethodNode value) throws IOException {\n\t\tMethodInfo methodInfo = value.getMethodInfo();\n\t\tout.writeUTF(methodInfo.getDeclClass().getRawName());\n\t\tout.writeUTF(methodInfo.getShortId());\n\t}\n\n\t@Override\n\tpublic MethodNode read(DataInput in) throws IOException {\n\t\tString cls = in.readUTF();\n\t\tString sign = in.readUTF();\n\t\treturn root.resolveDirectMethod(cls, sign);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/disk/adapters/NodeDeclareRefAdapter.java",
    "content": "package jadx.gui.cache.code.disk.adapters;\n\nimport java.io.DataInput;\nimport java.io.DataOutput;\nimport java.io.IOException;\n\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.metadata.annotations.NodeDeclareRef;\n\npublic class NodeDeclareRefAdapter implements DataAdapter<NodeDeclareRef> {\n\tprivate final CodeAnnotationAdapter refAdapter;\n\n\tpublic NodeDeclareRefAdapter(CodeAnnotationAdapter refAdapter) {\n\t\tthis.refAdapter = refAdapter;\n\t}\n\n\t@Override\n\tpublic void write(DataOutput out, NodeDeclareRef value) throws IOException {\n\t\tICodeNodeRef node = value.getNode();\n\t\tif (node == null) {\n\t\t\tthrow new RuntimeException(\"Null node in NodeDeclareRef\");\n\t\t}\n\t\trefAdapter.write(out, node);\n\t\tDataAdapterHelper.writeUVInt(out, value.getDefPos());\n\t}\n\n\t@Override\n\tpublic NodeDeclareRef read(DataInput in) throws IOException {\n\t\tICodeNodeRef ref = (ICodeNodeRef) refAdapter.read(in);\n\t\tint defPos = DataAdapterHelper.readUVInt(in);\n\t\tNodeDeclareRef nodeDeclareRef = new NodeDeclareRef(ref);\n\t\tnodeDeclareRef.setDefPos(defPos);\n\t\t// restore def position if loading metadata without actual decompilation\n\t\tref.setDefPosition(defPos);\n\t\treturn nodeDeclareRef;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/disk/adapters/NodeEndAdapter.java",
    "content": "package jadx.gui.cache.code.disk.adapters;\n\nimport java.io.DataInput;\nimport java.io.DataOutput;\nimport java.io.IOException;\n\nimport jadx.api.metadata.annotations.NodeEnd;\n\npublic class NodeEndAdapter implements DataAdapter<NodeEnd> {\n\n\t@Override\n\tpublic void write(DataOutput out, NodeEnd value) throws IOException {\n\t}\n\n\t@Override\n\tpublic NodeEnd read(DataInput in) throws IOException {\n\t\treturn NodeEnd.VALUE;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/disk/adapters/VarNodeAdapter.java",
    "content": "package jadx.gui.cache.code.disk.adapters;\n\nimport java.io.DataInput;\nimport java.io.DataOutput;\nimport java.io.IOException;\n\nimport jadx.api.metadata.annotations.VarNode;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.MethodNode;\n\nimport static jadx.gui.cache.code.disk.adapters.DataAdapterHelper.readNullableUTF;\nimport static jadx.gui.cache.code.disk.adapters.DataAdapterHelper.readUVInt;\nimport static jadx.gui.cache.code.disk.adapters.DataAdapterHelper.writeNullableUTF;\nimport static jadx.gui.cache.code.disk.adapters.DataAdapterHelper.writeUVInt;\n\npublic class VarNodeAdapter implements DataAdapter<VarNode> {\n\tprivate final MethodNodeAdapter mthAdapter;\n\n\tpublic VarNodeAdapter(MethodNodeAdapter mthAdapter) {\n\t\tthis.mthAdapter = mthAdapter;\n\t}\n\n\t@Override\n\tpublic void write(DataOutput out, VarNode value) throws IOException {\n\t\tmthAdapter.write(out, value.getMth());\n\t\twriteUVInt(out, value.getReg());\n\t\twriteUVInt(out, value.getSsa());\n\t\tArgTypeAdapter.INSTANCE.write(out, value.getType());\n\t\twriteNullableUTF(out, value.getName());\n\t}\n\n\t@Override\n\tpublic VarNode read(DataInput in) throws IOException {\n\t\tMethodNode mth = mthAdapter.read(in);\n\t\tint reg = readUVInt(in);\n\t\tint ssa = readUVInt(in);\n\t\tArgType type = ArgTypeAdapter.INSTANCE.read(in);\n\t\tString name = readNullableUTF(in);\n\t\treturn new VarNode(mth, reg, ssa, type, name);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/code/disk/adapters/VarRefAdapter.java",
    "content": "package jadx.gui.cache.code.disk.adapters;\n\nimport java.io.DataInput;\nimport java.io.DataOutput;\nimport java.io.IOException;\n\nimport jadx.api.metadata.annotations.VarRef;\n\npublic class VarRefAdapter implements DataAdapter<VarRef> {\n\n\tpublic static final VarRefAdapter INSTANCE = new VarRefAdapter();\n\n\t@Override\n\tpublic void write(DataOutput out, VarRef value) throws IOException {\n\t\tint refPos = value.getRefPos();\n\t\tDataAdapterHelper.writeUVInt(out, refPos);\n\t}\n\n\t@Override\n\tpublic VarRef read(DataInput in) throws IOException {\n\t\tint refPos = DataAdapterHelper.readUVInt(in);\n\t\treturn VarRef.fromPos(refPos);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/manager/CacheEntry.java",
    "content": "package jadx.gui.cache.manager;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class CacheEntry implements Comparable<CacheEntry> {\n\n\tprivate String project;\n\tprivate String cache;\n\tprivate long timestamp;\n\n\tpublic String getProject() {\n\t\treturn project;\n\t}\n\n\tpublic void setProject(String project) {\n\t\tthis.project = project;\n\t}\n\n\tpublic String getCache() {\n\t\treturn cache;\n\t}\n\n\tpublic void setCache(String cache) {\n\t\tthis.cache = cache;\n\t}\n\n\tpublic long getTimestamp() {\n\t\treturn timestamp;\n\t}\n\n\tpublic void setTimestamp(long timestamp) {\n\t\tthis.timestamp = timestamp;\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull CacheEntry other) {\n\t\t// recent entries first\n\t\treturn -Long.compare(timestamp, other.timestamp);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"CacheEntry{project=\" + project + \", cache=\" + cache + \"}\";\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/manager/CacheManager.java",
    "content": "package jadx.gui.cache.manager;\n\nimport java.io.BufferedReader;\nimport java.lang.reflect.Type;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.nio.file.StandardOpenOption;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.Gson;\nimport com.google.gson.reflect.TypeToken;\n\nimport jadx.api.plugins.utils.CommonFileUtils;\nimport jadx.core.utils.GsonUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.gui.settings.JadxProject;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.settings.data.ProjectData;\nimport jadx.gui.utils.files.JadxFiles;\n\npublic class CacheManager {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CacheManager.class);\n\n\tprivate static final Gson GSON = GsonUtils.buildGson();\n\tprivate static final Type CACHES_TYPE = new TypeToken<List<CacheEntry>>() {\n\t}.getType();\n\n\tprivate final Map<String, CacheEntry> cacheMap;\n\tprivate final JadxSettings settings;\n\n\tpublic CacheManager(JadxSettings settings) {\n\t\tthis.settings = settings;\n\t\tthis.cacheMap = loadCaches();\n\t}\n\n\t/**\n\t * If project cache is set -&gt; check if cache entry exists for this project.\n\t * If not -&gt; calculate new and add entry.\n\t */\n\tpublic Path getCacheDir(JadxProject project, @Nullable String cacheDirStr) {\n\t\tif (cacheDirStr == null) {\n\t\t\tPath newProjectCacheDir = buildCacheDir(project);\n\t\t\taddEntry(projectToKey(project), newProjectCacheDir);\n\t\t\treturn newProjectCacheDir;\n\t\t}\n\t\tPath cacheDir = resolveCacheDirStr(cacheDirStr, project.getProjectPath());\n\t\treturn verifyEntry(project, cacheDir);\n\t}\n\n\tpublic void projectPathUpdate(JadxProject project, Path newPath) {\n\t\tif (Objects.equals(project.getProjectPath(), newPath)) {\n\t\t\treturn;\n\t\t}\n\t\tString key = projectToKey(project);\n\t\tCacheEntry prevEntry = cacheMap.remove(key);\n\t\tif (prevEntry == null) {\n\t\t\treturn;\n\t\t}\n\t\tCacheEntry newEntry = new CacheEntry();\n\t\tnewEntry.setProject(pathToString(newPath));\n\t\tnewEntry.setCache(prevEntry.getCache());\n\t\taddEntry(newEntry);\n\t}\n\n\tpublic List<CacheEntry> getCachesList() {\n\t\tList<CacheEntry> list = new ArrayList<>(cacheMap.values());\n\t\tCollections.sort(list);\n\t\treturn list;\n\t}\n\n\tpublic synchronized void removeCacheEntry(CacheEntry entry) {\n\t\ttry {\n\t\t\tcacheMap.remove(entry.getProject());\n\t\t\tsaveCaches(cacheMap);\n\t\t\tFileUtils.deleteDirIfExists(Paths.get(entry.getCache()));\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to remove cache entry: \" + entry.getCache(), e);\n\t\t}\n\t}\n\n\tprivate Path resolveCacheDirStr(String cacheDirStr, Path projectPath) {\n\t\tPath path = Paths.get(cacheDirStr);\n\t\tif (path.isAbsolute() || projectPath == null) {\n\t\t\treturn path;\n\t\t}\n\t\treturn projectPath.resolveSibling(path);\n\t}\n\n\tpublic String buildCacheDirStr(Path dir) {\n\t\tif (Objects.equals(settings.getCacheDir(), \".\")) {\n\t\t\treturn dir.getFileName().toString();\n\t\t}\n\t\treturn pathToString(dir);\n\t}\n\n\tprivate Path buildCacheDir(JadxProject project) {\n\t\tString cacheDirValue = settings.getCacheDir();\n\t\tif (Objects.equals(cacheDirValue, \".\")) {\n\t\t\treturn buildLocalCacheDir(project);\n\t\t}\n\t\tPath cacheBaseDir = cacheDirValue == null ? JadxFiles.PROJECTS_CACHE_DIR : Paths.get(cacheDirValue);\n\t\treturn cacheBaseDir.resolve(buildProjectUniqName(project));\n\t}\n\n\tprivate static Path buildLocalCacheDir(JadxProject project) {\n\t\tPath projectPath = project.getProjectPath();\n\t\tif (projectPath != null) {\n\t\t\treturn projectPath.resolveSibling(projectPath.getFileName() + \".cache\");\n\t\t}\n\t\tList<Path> files = project.getFilePaths();\n\t\tif (files.isEmpty()) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to build local cache dir\");\n\t\t}\n\t\tPath path = files.stream()\n\t\t\t\t.filter(p -> !p.getFileName().toString().endsWith(\".jadx.kts\"))\n\t\t\t\t.findFirst()\n\t\t\t\t.orElseGet(() -> files.get(0));\n\t\tString name = CommonFileUtils.removeFileExtension(path.getFileName().toString());\n\t\treturn path.resolveSibling(name + \".jadx.cache\");\n\t}\n\n\tprivate Path verifyEntry(JadxProject project, Path cacheDir) {\n\t\tboolean cacheExists = Files.exists(cacheDir);\n\t\tString key = projectToKey(project);\n\t\tCacheEntry entry = cacheMap.get(key);\n\t\tif (entry == null) {\n\t\t\tPath newCacheDir = cacheExists ? cacheDir : buildCacheDir(project);\n\t\t\taddEntry(key, newCacheDir);\n\t\t\treturn newCacheDir;\n\t\t}\n\t\tif (entry.getCache().equals(pathToString(cacheDir)) && cacheExists) {\n\t\t\t// same and exists\n\t\t\treturn cacheDir;\n\t\t}\n\t\t// remove previous cache dir\n\t\tFileUtils.deleteDirIfExists(Paths.get(entry.getCache()));\n\n\t\tPath newCacheDir = cacheExists ? cacheDir : buildCacheDir(project);\n\t\tentry.setCache(pathToString(newCacheDir));\n\t\tentry.setTimestamp(System.currentTimeMillis());\n\t\tsaveCaches(cacheMap);\n\t\treturn newCacheDir;\n\t}\n\n\tprivate void addEntry(String projectKey, Path cacheDir) {\n\t\tCacheEntry entry = new CacheEntry();\n\t\tentry.setProject(projectKey);\n\t\tentry.setCache(pathToString(cacheDir));\n\t\taddEntry(entry);\n\t}\n\n\tprivate void addEntry(CacheEntry entry) {\n\t\tentry.setTimestamp(System.currentTimeMillis());\n\t\tcacheMap.put(entry.getProject(), entry);\n\t\tsaveCaches(cacheMap);\n\t}\n\n\tprivate String projectToKey(JadxProject project) {\n\t\tPath projectPath = project.getProjectPath();\n\t\tif (projectPath != null) {\n\t\t\treturn pathToString(projectPath);\n\t\t}\n\t\treturn \"tmp:\" + buildProjectUniqName(project);\n\t}\n\n\tprivate static String buildProjectUniqName(JadxProject project) {\n\t\treturn project.getName() + \"-\" + FileUtils.buildInputsHash(project.getFilePaths());\n\t}\n\n\tpublic static String pathToString(Path path) {\n\t\ttry {\n\t\t\treturn path.toAbsolutePath().normalize().toString();\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to expand path: \" + path, e);\n\t\t}\n\t}\n\n\tprivate synchronized Map<String, CacheEntry> loadCaches() {\n\t\tList<CacheEntry> list = null;\n\t\tif (Files.exists(JadxFiles.CACHES_LIST)) {\n\t\t\ttry (BufferedReader reader = Files.newBufferedReader(JadxFiles.CACHES_LIST)) {\n\t\t\t\tlist = GSON.fromJson(reader, CACHES_TYPE);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.warn(\"Failed to load caches list\", e);\n\t\t\t}\n\t\t} else {\n\t\t\treturn initFromRecentProjects();\n\t\t}\n\t\tif (Utils.isEmpty(list)) {\n\t\t\treturn new HashMap<>();\n\t\t}\n\t\tMap<String, CacheEntry> map = new HashMap<>(list.size());\n\t\tfor (CacheEntry entry : list) {\n\t\t\tmap.put(entry.getProject(), entry);\n\t\t}\n\t\treturn map;\n\t}\n\n\tprivate synchronized void saveCaches(Map<String, CacheEntry> map) {\n\t\tList<CacheEntry> list = new ArrayList<>(map.values());\n\t\tCollections.sort(list);\n\t\tString json = GSON.toJson(list, CACHES_TYPE);\n\t\ttry {\n\t\t\tFiles.writeString(JadxFiles.CACHES_LIST, json, StandardCharsets.UTF_8,\n\t\t\t\t\tStandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to write caches file\", e);\n\t\t}\n\t}\n\n\t/**\n\t * Load caches info from recent projects list.\n\t * Help for initial migration.\n\t */\n\tprivate Map<String, CacheEntry> initFromRecentProjects() {\n\t\ttry {\n\t\t\tMap<String, CacheEntry> map = new HashMap<>();\n\t\t\tlong t = System.currentTimeMillis();\n\t\t\tfor (Path project : settings.getRecentProjects()) {\n\t\t\t\ttry {\n\t\t\t\t\tProjectData data = JadxProject.loadProjectData(project);\n\t\t\t\t\tString cacheDir = data.getCacheDir();\n\t\t\t\t\tif (cacheDir == null) {\n\t\t\t\t\t\t// no cache dir, ignore\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tPath cachePath = resolveCacheDirStr(cacheDir, project);\n\t\t\t\t\tif (!Files.isDirectory(cachePath)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tString key = pathToString(project);\n\t\t\t\t\tCacheEntry entry = new CacheEntry();\n\t\t\t\t\tentry.setProject(key);\n\t\t\t\t\tentry.setCache(pathToString(cachePath));\n\t\t\t\t\tentry.setTimestamp(t++); // keep projects order\n\t\t\t\t\tmap.put(key, entry);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.warn(\"Failed to load project file: {}\", project, e);\n\t\t\t\t}\n\t\t\t}\n\t\t\tsaveCaches(map);\n\t\t\treturn map;\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to fill cache list from recent projects\", e);\n\t\t\treturn new HashMap<>();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/usage/CachedMethodRef.java",
    "content": "package jadx.gui.cache.usage;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.IMethodRef;\n\npublic class CachedMethodRef implements IMethodRef {\n\n\tprivate String parentClassType;\n\tprivate String name;\n\tprivate String returnType;\n\tprivate List<String> argTypes;\n\n\tpublic CachedMethodRef(String parentClassType, String name, String returnType, List<String> argTypes) {\n\t\tthis.parentClassType = parentClassType;\n\t\tthis.name = name;\n\t\tthis.returnType = returnType;\n\t\tthis.argTypes = argTypes;\n\t}\n\n\t@Override\n\tpublic String getParentClassType() {\n\t\treturn parentClassType;\n\t}\n\n\tpublic void setParentClassType(String parentClassType) {\n\t\tthis.parentClassType = parentClassType;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\t@Override\n\tpublic String getReturnType() {\n\t\treturn returnType;\n\t}\n\n\tpublic void setReturnType(String returnType) {\n\t\tthis.returnType = returnType;\n\t}\n\n\t@Override\n\tpublic List<String> getArgTypes() {\n\t\treturn argTypes;\n\t}\n\n\tpublic void setArgTypes(List<String> argTypes) {\n\t\tthis.argTypes = argTypes;\n\t}\n\n\t@Override\n\tpublic int getUniqId() {\n\t\tthrow new UnsupportedOperationException(\"Unimplemented method 'getUniqId'\");\n\t}\n\n\t@Override\n\tpublic void load() {\n\t\tthrow new UnsupportedOperationException(\"Unimplemented method 'load'\");\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/usage/ClsUsageData.java",
    "content": "package jadx.gui.cache.usage;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nfinal class ClsUsageData {\n\tprivate final String rawName;\n\n\tprivate List<String> clsDeps;\n\tprivate List<String> clsUsage;\n\tprivate List<MthRef> clsUseInMth;\n\n\tprivate final Map<String, FldUsageData> fldUsage = new HashMap<>();\n\tprivate final Map<String, MthUsageData> mthUsage = new HashMap<>();\n\n\tClsUsageData(String rawName) {\n\t\tthis.rawName = rawName;\n\t}\n\n\tpublic String getRawName() {\n\t\treturn rawName;\n\t}\n\n\tpublic List<String> getClsDeps() {\n\t\treturn clsDeps;\n\t}\n\n\tpublic void setClsDeps(List<String> clsDeps) {\n\t\tthis.clsDeps = clsDeps;\n\t}\n\n\tpublic List<String> getClsUsage() {\n\t\treturn clsUsage;\n\t}\n\n\tpublic void setClsUsage(List<String> clsUsage) {\n\t\tthis.clsUsage = clsUsage;\n\t}\n\n\tpublic List<MthRef> getClsUseInMth() {\n\t\treturn clsUseInMth;\n\t}\n\n\tpublic void setClsUseInMth(List<MthRef> clsUseInMth) {\n\t\tthis.clsUseInMth = clsUseInMth;\n\t}\n\n\tpublic Map<String, FldUsageData> getFldUsage() {\n\t\treturn fldUsage;\n\t}\n\n\tpublic Map<String, MthUsageData> getMthUsage() {\n\t\treturn mthUsage;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/usage/CollectUsageData.java",
    "content": "package jadx.gui.cache.usage;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.usage.IUsageInfoVisitor;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.Utils;\n\nfinal class CollectUsageData implements IUsageInfoVisitor {\n\tprivate final RawUsageData data;\n\n\tpublic CollectUsageData(RawUsageData usageData) {\n\t\tdata = usageData;\n\t}\n\n\t@Override\n\tpublic void visitClassDeps(ClassNode cls, List<ClassNode> deps) {\n\t\tdata.getClassData(cls).setClsDeps(clsNodesRef(deps));\n\t}\n\n\t@Override\n\tpublic void visitClassUsage(ClassNode cls, List<ClassNode> usage) {\n\t\tdata.getClassData(cls).setClsUsage(clsNodesRef(usage));\n\t}\n\n\t@Override\n\tpublic void visitClassUseInMethods(ClassNode cls, List<MethodNode> methods) {\n\t\tdata.getClassData(cls).setClsUseInMth(mthNodesRef(methods));\n\t}\n\n\t@Override\n\tpublic void visitFieldsUsage(FieldNode fld, List<MethodNode> methods) {\n\t\tdata.getFieldData(fld).setUsage(mthNodesRef(methods));\n\t}\n\n\t@Override\n\tpublic void visitMethodsUsage(MethodNode mth, List<MethodNode> methods) {\n\t\tdata.getMethodData(mth).setUsage(mthNodesRef(methods));\n\t}\n\n\t@Override\n\tpublic void visitMethodsUses(MethodNode mth, List<MethodNode> methods) {\n\t\tdata.getMethodData(mth).setUses(mthNodesRef(methods));\n\t}\n\n\t@Override\n\tpublic void visitUnresolvedMethodsUsage(MethodNode mth, List<IMethodRef> methods) {\n\t\tdata.getMethodData(mth).setUnresolvedUsage(methods);\n\t}\n\n\t@Override\n\tpublic void visitIsSelfCall(MethodNode mth, boolean isSelfCall) {\n\t\tdata.getMethodData(mth).setCallsSelf(isSelfCall);\n\t}\n\n\t@Override\n\tpublic void visitComplete() {\n\t\tdata.collectClassesWithoutData();\n\t}\n\n\tprivate List<String> clsNodesRef(List<ClassNode> usage) {\n\t\treturn Utils.collectionMap(usage, ClassNode::getRawName);\n\t}\n\n\tprivate List<MthRef> mthNodesRef(List<MethodNode> methods) {\n\t\treturn Utils.collectionMap(methods, m -> data.getMethodData(m).getMthRef());\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/usage/FldRef.java",
    "content": "package jadx.gui.cache.usage;\n\nfinal class FldRef {\n\tprivate final String cls;\n\tprivate final String shortId;\n\n\tFldRef(String cls, String shortId) {\n\t\tthis.cls = cls;\n\t\tthis.shortId = shortId;\n\t}\n\n\tpublic String getCls() {\n\t\treturn cls;\n\t}\n\n\tpublic String getShortId() {\n\t\treturn shortId;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/usage/FldUsageData.java",
    "content": "package jadx.gui.cache.usage;\n\nimport java.util.List;\n\nfinal class FldUsageData {\n\tprivate final FldRef fldRef;\n\tprivate List<MthRef> usage;\n\n\tpublic FldUsageData(FldRef fldRef) {\n\t\tthis.fldRef = fldRef;\n\t}\n\n\tpublic FldRef getFldRef() {\n\t\treturn fldRef;\n\t}\n\n\tpublic List<MthRef> getUsage() {\n\t\treturn usage;\n\t}\n\n\tpublic void setUsage(List<MthRef> usage) {\n\t\tthis.usage = usage;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/usage/MthRef.java",
    "content": "package jadx.gui.cache.usage;\n\nfinal class MthRef {\n\tprivate final String cls;\n\tprivate final String shortId;\n\n\tMthRef(String cls, String shortId) {\n\t\tthis.cls = cls;\n\t\tthis.shortId = shortId;\n\t}\n\n\tpublic String getCls() {\n\t\treturn cls;\n\t}\n\n\tpublic String getShortId() {\n\t\treturn shortId;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof MthRef)) {\n\t\t\treturn false;\n\t\t}\n\t\tMthRef other = (MthRef) o;\n\t\treturn cls.equals(other.cls)\n\t\t\t\t&& shortId.equals(other.shortId);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn 31 * cls.hashCode() + shortId.hashCode();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/usage/MthUsageData.java",
    "content": "package jadx.gui.cache.usage;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.IMethodRef;\n\nfinal class MthUsageData {\n\tprivate final MthRef mthRef;\n\tprivate List<MthRef> usage;\n\tprivate List<MthRef> uses;\n\tprivate List<IMethodRef> unresolvedUsage;\n\tprivate boolean callsSelf;\n\n\tpublic MthUsageData(MthRef mthRef) {\n\t\tthis.mthRef = mthRef;\n\t}\n\n\tpublic MthRef getMthRef() {\n\t\treturn mthRef;\n\t}\n\n\tpublic List<MthRef> getUsage() {\n\t\treturn usage;\n\t}\n\n\tpublic void setUsage(List<MthRef> usage) {\n\t\tthis.usage = usage;\n\t}\n\n\tpublic List<MthRef> getUses() {\n\t\treturn uses;\n\t}\n\n\tpublic void setUses(List<MthRef> uses) {\n\t\tthis.uses = uses;\n\t}\n\n\tpublic List<IMethodRef> getUnresolvedUsage() {\n\t\treturn unresolvedUsage;\n\t}\n\n\tpublic void setUnresolvedUsage(List<IMethodRef> unresolvedUsage) {\n\t\tthis.unresolvedUsage = unresolvedUsage;\n\t}\n\n\tpublic boolean callsSelf() {\n\t\treturn callsSelf;\n\t}\n\n\tpublic void setCallsSelf(boolean callsSelf) {\n\t\tthis.callsSelf = callsSelf;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/usage/RawUsageData.java",
    "content": "package jadx.gui.cache.usage;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\n\nclass RawUsageData {\n\n\tprivate final Map<String, ClsUsageData> clsMap = new HashMap<>();\n\tprivate List<String> classesWithoutData = Collections.emptyList();\n\n\tpublic Map<String, ClsUsageData> getClsMap() {\n\t\treturn clsMap;\n\t}\n\n\tpublic List<String> getClassesWithoutData() {\n\t\treturn classesWithoutData;\n\t}\n\n\tpublic ClsUsageData getClassData(ClassNode cls) {\n\t\treturn getClassData(cls.getRawName());\n\t}\n\n\tpublic ClsUsageData getClassData(String clsRawName) {\n\t\treturn clsMap.computeIfAbsent(clsRawName, ClsUsageData::new);\n\t}\n\n\tpublic MthUsageData getMethodData(MethodNode mth) {\n\t\tClassNode parentClass = mth.getParentClass();\n\t\tString shortId = mth.getMethodInfo().getShortId();\n\t\treturn getClassData(parentClass).getMthUsage().computeIfAbsent(shortId,\n\t\t\t\tm -> new MthUsageData(new MthRef(parentClass.getRawName(), shortId)));\n\t}\n\n\tpublic FldUsageData getFieldData(FieldNode fld) {\n\t\tClassNode parentClass = fld.getParentClass();\n\t\tString shortId = fld.getFieldInfo().getShortId();\n\t\treturn getClassData(parentClass).getFldUsage().computeIfAbsent(shortId,\n\t\t\t\tm -> new FldUsageData(new FldRef(parentClass.getRawName(), shortId)));\n\t}\n\n\tpublic void collectClassesWithoutData() {\n\t\tSet<String> allClasses = new HashSet<>(clsMap.size() * 2);\n\t\tfor (ClsUsageData usageData : clsMap.values()) {\n\t\t\tList<String> deps = usageData.getClsDeps();\n\t\t\tif (deps != null) {\n\t\t\t\tallClasses.addAll(deps);\n\t\t\t}\n\t\t\tList<String> usage = usageData.getClsUsage();\n\t\t\tif (usage != null) {\n\t\t\t\tallClasses.addAll(usage);\n\t\t\t}\n\t\t}\n\t\tallClasses.removeAll(clsMap.keySet());\n\t\tclassesWithoutData = new ArrayList<>(allClasses);\n\t\tCollections.sort(classesWithoutData);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/usage/UsageCacheMode.java",
    "content": "package jadx.gui.cache.usage;\n\npublic enum UsageCacheMode {\n\tNONE,\n\tMEMORY,\n\tDISK\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/usage/UsageData.java",
    "content": "package jadx.gui.cache.usage;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.usage.IUsageInfoData;\nimport jadx.api.usage.IUsageInfoVisitor;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nclass UsageData implements IUsageInfoData {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(UsageData.class);\n\n\tprivate final RootNode root;\n\tprivate final RawUsageData rawUsageData;\n\n\tpublic UsageData(RootNode root, RawUsageData rawUsageData) {\n\t\tthis.root = root;\n\t\tthis.rawUsageData = rawUsageData;\n\t}\n\n\t@Override\n\tpublic void apply() {\n\t\tMap<String, ClsUsageData> clsMap = rawUsageData.getClsMap();\n\t\tfor (ClassNode cls : root.getClasses()) {\n\t\t\tString clsRawName = cls.getRawName();\n\t\t\tClsUsageData clsUsageData = clsMap.get(clsRawName);\n\t\t\tif (clsUsageData != null) {\n\t\t\t\tapplyForClass(clsUsageData, cls);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void applyForClass(ClassNode cls) {\n\t\tString clsRawName = cls.getRawName();\n\t\tClsUsageData clsUsageData = rawUsageData.getClsMap().get(clsRawName);\n\t\tif (clsUsageData == null) {\n\t\t\tLOG.debug(\"No usage data for class: {}\", clsRawName);\n\t\t\treturn;\n\t\t}\n\t\tapplyForClass(clsUsageData, cls);\n\t}\n\n\tprivate void applyForClass(ClsUsageData clsUsageData, ClassNode cls) {\n\t\tcls.setDependencies(resolveClsList(clsUsageData.getClsDeps()));\n\t\tcls.setUseIn(resolveClsList(clsUsageData.getClsUsage()));\n\t\tcls.setUseInMth(resolveMthList(clsUsageData.getClsUseInMth()));\n\n\t\tMap<String, MthUsageData> mthUsage = clsUsageData.getMthUsage();\n\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\tMthUsageData mthUsageData = mthUsage.get(mth.getMethodInfo().getShortId());\n\t\t\tif (mthUsageData != null) {\n\t\t\t\tmth.setUseIn(resolveMthList(mthUsageData.getUsage()));\n\t\t\t\tmth.setUsed(resolveMthList(mthUsageData.getUses()));\n\t\t\t\tmth.setUnresolvedUsed(mthUsageData.getUnresolvedUsage());\n\t\t\t\tmth.setCallsSelf(mthUsageData.callsSelf());\n\t\t\t}\n\t\t}\n\t\tMap<String, FldUsageData> fldUsage = clsUsageData.getFldUsage();\n\t\tfor (FieldNode fld : cls.getFields()) {\n\t\t\tFldUsageData fldUsageData = fldUsage.get(fld.getFieldInfo().getShortId());\n\t\t\tif (fldUsageData != null) {\n\t\t\t\tfld.setUseIn(resolveMthList(fldUsageData.getUsage()));\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void visitUsageData(IUsageInfoVisitor visitor) {\n\t\tthrow new JadxRuntimeException(\"Not implemented\");\n\t}\n\n\tprivate List<ClassNode> resolveClsList(List<String> clsList) {\n\t\treturn Utils.collectionMap(clsList, root::resolveRawClass);\n\t}\n\n\tprivate List<MethodNode> resolveMthList(List<MthRef> mthRefList) {\n\t\treturn Utils.collectionMap(mthRefList, m -> root.resolveDirectMethod(m.getCls(), m.getShortId()));\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/usage/UsageFileAdapter.java",
    "content": "package jadx.gui.cache.usage;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.DataInputStream;\nimport java.io.DataOutputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.usage.IUsageInfoData;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.gui.cache.code.disk.adapters.DataAdapterHelper;\n\nimport static java.nio.file.StandardOpenOption.CREATE;\nimport static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;\nimport static java.nio.file.StandardOpenOption.WRITE;\n\npublic class UsageFileAdapter extends DataAdapterHelper {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(UsageFileAdapter.class);\n\n\tprivate static final int USAGE_DATA_VERSION = 2;\n\tprivate static final byte[] JADX_USAGE_HEADER = \"jadx.usage\".getBytes(StandardCharsets.US_ASCII);\n\n\tpublic static synchronized @Nullable RawUsageData load(Path usageFile, List<File> inputs) {\n\t\tif (!Files.isRegularFile(usageFile)) {\n\t\t\treturn null;\n\t\t}\n\t\tlong start = System.currentTimeMillis();\n\t\ttry (DataInputStream in = new DataInputStream(new BufferedInputStream(Files.newInputStream(usageFile)))) {\n\t\t\tin.skipBytes(JADX_USAGE_HEADER.length);\n\t\t\tint dataVersion = in.readInt();\n\t\t\tif (dataVersion != USAGE_DATA_VERSION) {\n\t\t\t\tLOG.debug(\"Found old usage data format\");\n\t\t\t\tFileUtils.deleteFileIfExists(usageFile);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tString inputsHash = buildInputsHash(inputs);\n\t\t\tString fileInputsHash = in.readUTF();\n\t\t\tif (!inputsHash.equals(fileInputsHash)) {\n\t\t\t\tLOG.debug(\"Found usage data with different inputs hash\");\n\t\t\t\tFileUtils.deleteFileIfExists(usageFile);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tRawUsageData data = readData(in);\n\t\t\tif (LOG.isDebugEnabled()) {\n\t\t\t\tLOG.debug(\"Loaded usage data from disk cache, classes count: {}, time: {}ms, file: {}\",\n\t\t\t\t\t\tdata.getClsMap().size(), System.currentTimeMillis() - start, usageFile);\n\t\t\t}\n\t\t\treturn data;\n\t\t} catch (Exception e) {\n\t\t\ttry {\n\t\t\t\tFileUtils.deleteFileIfExists(usageFile);\n\t\t\t} catch (IOException ex) {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t\tLOG.error(\"Failed to load usage data file\", e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static synchronized void save(IUsageInfoData data, Path usageFile, List<File> inputs) {\n\t\tlong start = System.currentTimeMillis();\n\t\tFileUtils.makeDirsForFile(usageFile);\n\t\tString inputsHash = buildInputsHash(inputs);\n\t\tRawUsageData usageData = new RawUsageData();\n\t\tdata.visitUsageData(new CollectUsageData(usageData));\n\t\ttry (OutputStream fileOutput = Files.newOutputStream(usageFile, WRITE, CREATE, TRUNCATE_EXISTING);\n\t\t\t\tDataOutputStream out = new DataOutputStream(new BufferedOutputStream(fileOutput))) {\n\t\t\tout.write(JADX_USAGE_HEADER);\n\t\t\tout.writeInt(USAGE_DATA_VERSION);\n\t\t\tout.writeUTF(inputsHash);\n\t\t\twriteData(out, usageData);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to save usage data file\", e);\n\t\t\ttry {\n\t\t\t\tFileUtils.deleteFileIfExists(usageFile);\n\t\t\t} catch (IOException ex) {\n\t\t\t\tLOG.error(\"Failed to delete usage data file: {}\", usageFile, ex);\n\t\t\t}\n\t\t}\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tLOG.debug(\"Usage data saved, time: {}ms, file: {}\", System.currentTimeMillis() - start, usageFile);\n\t\t}\n\t}\n\n\tprivate static RawUsageData readData(DataInputStream in) throws IOException {\n\t\tRawUsageData data = new RawUsageData();\n\t\tint clsCount = readUVInt(in);\n\t\tint clsWithoutDataCount = readUVInt(in);\n\n\t\t// Class information\n\t\tString[] clsNames = new String[clsCount + clsWithoutDataCount];\n\t\tClsUsageData[] classes = new ClsUsageData[clsCount];\n\t\tint c = 0;\n\t\tfor (int i = 0; i < clsCount; i++) {\n\t\t\tString clsRawName = in.readUTF();\n\t\t\tclasses[i] = data.getClassData(clsRawName);\n\t\t\tclsNames[c++] = clsRawName;\n\t\t}\n\t\tfor (int i = 0; i < clsWithoutDataCount; i++) {\n\t\t\tclsNames[c++] = in.readUTF();\n\t\t}\n\n\t\t// Method information\n\t\tint mthCount = readUVInt(in);\n\t\tMthRef[] methods = new MthRef[mthCount];\n\t\tfor (int i = 0; i < mthCount; i++) {\n\t\t\tint clsId = readUVInt(in);\n\t\t\tString mthShortId = in.readUTF();\n\t\t\tClsUsageData cls = classes[clsId];\n\t\t\tMthRef mthRef = new MthRef(cls.getRawName(), mthShortId);\n\t\t\tcls.getMthUsage().put(mthShortId, new MthUsageData(mthRef));\n\t\t\tmethods[i] = mthRef;\n\t\t}\n\n\t\t// Unresolved method information\n\t\tint uMthCount = readUVInt(in);\n\t\tIMethodRef[] unresolvedMethods = new IMethodRef[uMthCount];\n\t\tfor (int i = 0; i < uMthCount; i++) {\n\t\t\tString name = in.readUTF();\n\t\t\tString parentClassType = in.readUTF();\n\t\t\tString returnType = in.readUTF();\n\t\t\tint argCount = in.readInt();\n\t\t\tString[] args = new String[argCount];\n\t\t\tfor (int j = 0; j < argCount; j++) {\n\t\t\t\targs[j] = in.readUTF();\n\t\t\t}\n\t\t\tIMethodRef iMethodRef = new CachedMethodRef(parentClassType, name, returnType, Arrays.asList(args));\n\t\t\tunresolvedMethods[i] = iMethodRef;\n\t\t}\n\n\t\t// Usage data\n\t\tfor (int i = 0; i < clsCount; i++) {\n\t\t\tClsUsageData cls = data.getClassData(clsNames[i]);\n\t\t\tcls.setClsDeps(readClsList(in, clsNames));\n\t\t\tcls.setClsUsage(readClsList(in, clsNames));\n\t\t\tcls.setClsUseInMth(readMthList(in, methods));\n\n\t\t\tint mCount = readUVInt(in);\n\t\t\tfor (int m = 0; m < mCount; m++) {\n\t\t\t\tMthRef mthRef = methods[readUVInt(in)];\n\t\t\t\tMthUsageData mthUsageData = cls.getMthUsage().get(mthRef.getShortId());\n\t\t\t\tmthUsageData.setUsage(readMthList(in, methods));\n\t\t\t\tmthUsageData.setUses(readMthList(in, methods));\n\t\t\t\tmthUsageData.setUnresolvedUsage(readUnresolvedMthList(in, unresolvedMethods));\n\t\t\t\tmthUsageData.setCallsSelf(in.readBoolean());\n\t\t\t}\n\t\t\tint fCount = readUVInt(in);\n\t\t\tfor (int f = 0; f < fCount; f++) {\n\t\t\t\tString fldShortId = in.readUTF();\n\t\t\t\tcls.getFldUsage().computeIfAbsent(fldShortId,\n\t\t\t\t\t\tfldId -> new FldUsageData(new FldRef(cls.getRawName(), fldId)))\n\t\t\t\t\t\t.setUsage(readMthList(in, methods));\n\t\t\t}\n\t\t}\n\t\treturn data;\n\t}\n\n\tprivate static void writeData(DataOutputStream out, RawUsageData usageData) throws IOException {\n\t\tMap<String, Integer> clsMap = new HashMap<>();\n\t\tMap<MthRef, Integer> mthMap = new HashMap<>();\n\t\tMap<IMethodRef, Integer> uMthMap = new HashMap<>();\n\t\tMap<String, ClsUsageData> clsDataMap = usageData.getClsMap();\n\t\tList<String> classes = new ArrayList<>(clsDataMap.keySet());\n\t\tCollections.sort(classes);\n\t\tList<String> classesWithoutData = usageData.getClassesWithoutData();\n\n\t\t// Class information\n\t\twriteUVInt(out, classes.size());\n\t\twriteUVInt(out, classesWithoutData.size());\n\t\tint i = 0;\n\t\tfor (String cls : classes) {\n\t\t\tout.writeUTF(cls);\n\t\t\tclsMap.put(cls, i++);\n\t\t}\n\t\tfor (String cls : classesWithoutData) {\n\t\t\tout.writeUTF(cls);\n\t\t\tclsMap.put(cls, i++);\n\t\t}\n\n\t\t// Method information\n\t\tList<MthRef> methods = clsDataMap.values().stream()\n\t\t\t\t.flatMap(c -> c.getMthUsage().values().stream())\n\t\t\t\t.map(MthUsageData::getMthRef)\n\t\t\t\t.collect(Collectors.toList());\n\t\twriteUVInt(out, methods.size());\n\t\tint j = 0;\n\t\tfor (MthRef mth : methods) {\n\t\t\twriteUVInt(out, clsMap.get(mth.getCls()));\n\t\t\tout.writeUTF(mth.getShortId());\n\t\t\tmthMap.put(mth, j++);\n\t\t}\n\n\t\t// Unresolved method information\n\t\tSet<IMethodRef> unresolvedMethods = clsDataMap.values().stream()\n\t\t\t\t.flatMap(classUsageData -> classUsageData.getMthUsage().values().stream())\n\t\t\t\t.flatMap(methodUsageData -> {\n\t\t\t\t\tList<IMethodRef> unresolvedUsageList = methodUsageData.getUnresolvedUsage();\n\t\t\t\t\treturn (unresolvedUsageList == null) ? null : unresolvedUsageList.stream();\n\t\t\t\t})\n\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t.collect(Collectors.toSet());\n\t\twriteUVInt(out, unresolvedMethods.size());\n\t\tint k = 0;\n\t\tfor (IMethodRef uMth : unresolvedMethods) {\n\t\t\tString name = uMth.getName();\n\t\t\tout.writeUTF((name == null) ? \"\" : name);\n\t\t\tString parentClassType = uMth.getParentClassType();\n\t\t\tout.writeUTF((parentClassType == null) ? \"\" : parentClassType);\n\t\t\tString returnType = uMth.getReturnType();\n\t\t\tout.writeUTF((returnType == null) ? \"\" : returnType);\n\t\t\tList<String> argTypes = uMth.getArgTypes();\n\t\t\tif (argTypes == null) {\n\t\t\t\tout.writeInt(0);\n\t\t\t} else {\n\t\t\t\tout.writeInt(argTypes.size());\n\t\t\t\tfor (String arg : argTypes) {\n\t\t\t\t\tout.writeUTF(arg);\n\t\t\t\t}\n\t\t\t}\n\t\t\tuMthMap.put(uMth, k++);\n\t\t}\n\n\t\t// Usage data\n\t\tfor (String cls : classes) {\n\t\t\tClsUsageData clsData = clsDataMap.get(cls);\n\t\t\twriteClsList(out, clsMap, clsData.getClsDeps());\n\t\t\twriteClsList(out, clsMap, clsData.getClsUsage());\n\t\t\twriteMthList(out, mthMap, clsData.getClsUseInMth());\n\n\t\t\twriteUVInt(out, clsData.getMthUsage().size());\n\t\t\tfor (MthUsageData mthData : clsData.getMthUsage().values()) {\n\t\t\t\twriteUVInt(out, mthMap.get(mthData.getMthRef()));\n\t\t\t\twriteMthList(out, mthMap, mthData.getUsage());\n\t\t\t\twriteMthList(out, mthMap, mthData.getUses());\n\t\t\t\twriteUnresolvedMthList(out, uMthMap, mthData.getUnresolvedUsage());\n\t\t\t\tout.writeBoolean(mthData.callsSelf());\n\t\t\t}\n\n\t\t\twriteUVInt(out, clsData.getFldUsage().size());\n\t\t\tfor (FldUsageData fldData : clsData.getFldUsage().values()) {\n\t\t\t\tout.writeUTF(fldData.getFldRef().getShortId());\n\t\t\t\twriteMthList(out, mthMap, fldData.getUsage());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static List<String> readClsList(DataInputStream in, String[] classes) throws IOException {\n\t\tint count = readUVInt(in);\n\t\tif (count == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<String> list = new ArrayList<>(count);\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tlist.add(classes[readUVInt(in)]);\n\t\t}\n\t\treturn list;\n\t}\n\n\tprivate static void writeClsList(DataOutputStream out, Map<String, Integer> clsMap, List<String> clsList) throws IOException {\n\t\tif (Utils.isEmpty(clsList)) {\n\t\t\twriteUVInt(out, 0);\n\t\t\treturn;\n\t\t}\n\t\twriteUVInt(out, clsList.size());\n\t\tfor (String cls : clsList) {\n\t\t\tInteger clsId = clsMap.get(cls);\n\t\t\tif (clsId == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Unknown class in usage: \" + cls);\n\t\t\t}\n\t\t\twriteUVInt(out, clsId);\n\t\t}\n\t}\n\n\tprivate static List<MthRef> readMthList(DataInputStream in, MthRef[] methods) throws IOException {\n\t\tint count = readUVInt(in);\n\t\tif (count == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<MthRef> list = new ArrayList<>(count);\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tlist.add(methods[readUVInt(in)]);\n\t\t}\n\t\treturn list;\n\t}\n\n\tprivate static void writeMthList(DataOutputStream out, Map<MthRef, Integer> mthMap, List<MthRef> mthList) throws IOException {\n\t\tif (Utils.isEmpty(mthList)) {\n\t\t\twriteUVInt(out, 0);\n\t\t\treturn;\n\t\t}\n\t\twriteUVInt(out, mthList.size());\n\t\tfor (MthRef mth : mthList) {\n\t\t\twriteUVInt(out, mthMap.get(mth));\n\t\t}\n\t}\n\n\tprivate static List<IMethodRef> readUnresolvedMthList(DataInputStream in, IMethodRef[] methods) throws IOException {\n\t\tint count = readUVInt(in);\n\t\tif (count == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<IMethodRef> list = new ArrayList<>(count);\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tlist.add(methods[readUVInt(in)]);\n\t\t}\n\t\treturn list;\n\t}\n\n\tprivate static void writeUnresolvedMthList(DataOutputStream out, Map<IMethodRef, Integer> uMthMap, List<IMethodRef> mthList)\n\t\t\tthrows IOException {\n\t\tif (Utils.isEmpty(mthList)) {\n\t\t\twriteUVInt(out, 0);\n\t\t\treturn;\n\t\t}\n\t\twriteUVInt(out, mthList.size());\n\t\tfor (IMethodRef mth : mthList) {\n\t\t\twriteUVInt(out, uMthMap.get(mth));\n\t\t}\n\t}\n\n\tprivate static String buildInputsHash(List<File> inputs) {\n\t\tList<Path> paths = inputs.stream()\n\t\t\t\t.filter(f -> !f.getName().endsWith(\".jadx.kts\"))\n\t\t\t\t.map(File::toPath)\n\t\t\t\t.collect(Collectors.toList());\n\t\treturn FileUtils.buildInputsHash(paths);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/cache/usage/UsageInfoCache.java",
    "content": "package jadx.gui.cache.usage;\n\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.usage.IUsageInfoCache;\nimport jadx.api.usage.IUsageInfoData;\nimport jadx.api.usage.impl.InMemoryUsageInfoCache;\nimport jadx.core.dex.nodes.RootNode;\n\npublic class UsageInfoCache implements IUsageInfoCache {\n\n\tprivate static final Object LOAD_DATA_SYNC = new Object();\n\n\tprivate final Path usageFile;\n\tprivate final List<File> inputs;\n\tprivate final InMemoryUsageInfoCache memCache = new InMemoryUsageInfoCache();\n\tprivate @Nullable RawUsageData rawUsageData;\n\n\tpublic UsageInfoCache(Path cacheDir, List<File> inputFiles) {\n\t\tusageFile = cacheDir.resolve(\"usage\");\n\t\tinputs = inputFiles;\n\t}\n\n\t@Override\n\tpublic @Nullable IUsageInfoData get(RootNode root) {\n\t\tIUsageInfoData memData = memCache.get(root);\n\t\tif (memData != null) {\n\t\t\treturn memData;\n\t\t}\n\t\tsynchronized (LOAD_DATA_SYNC) {\n\t\t\tif (rawUsageData == null) {\n\t\t\t\trawUsageData = UsageFileAdapter.load(usageFile, inputs);\n\t\t\t}\n\t\t\tif (rawUsageData != null) {\n\t\t\t\tUsageData data = new UsageData(root, rawUsageData);\n\t\t\t\tmemCache.set(root, data);\n\t\t\t\treturn data;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void set(RootNode root, IUsageInfoData data) {\n\t\tmemCache.set(root, data);\n\t\tUsageFileAdapter.save(data, usageFile, inputs);\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\trawUsageData = null;\n\t\tmemCache.close();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/ArtAdapter.java",
    "content": "package jadx.gui.device.debugger;\n\npublic class ArtAdapter {\n\n\tpublic interface IArtAdapter {\n\t\tint getRuntimeRegNum(int smaliNum, int regCount, int paramStart);\n\n\t\tboolean readNullObject();\n\n\t\tString typeForNull();\n\t}\n\n\tpublic static IArtAdapter getAdapter(int androidReleaseVer) {\n\t\tif (androidReleaseVer <= 8) {\n\t\t\treturn new AndroidOreoAndBelow();\n\t\t} else {\n\t\t\treturn new AndroidPieAndAbove();\n\t\t}\n\t}\n\n\tpublic static class AndroidOreoAndBelow implements IArtAdapter {\n\t\t@Override\n\t\tpublic int getRuntimeRegNum(int smaliNum, int regCount, int paramStart) {\n\t\t\tint localRegCount = regCount - paramStart;\n\t\t\treturn (smaliNum + localRegCount) % regCount;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean readNullObject() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic String typeForNull() {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tpublic static class AndroidPieAndAbove implements IArtAdapter {\n\t\t@Override\n\t\tpublic int getRuntimeRegNum(int smaliNum, int regCount, int paramStart) {\n\t\t\treturn smaliNum;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean readNullObject() {\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic String typeForNull() {\n\t\t\treturn \"zero value\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/BreakpointManager.java",
    "content": "package jadx.gui.device.debugger;\n\nimport java.io.Reader;\nimport java.lang.reflect.Type;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.AbstractMap.SimpleEntry;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.Gson;\nimport com.google.gson.reflect.TypeToken;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.gui.device.debugger.smali.Smali;\nimport jadx.gui.treemodel.JClass;\n\nimport static jadx.core.utils.GsonUtils.buildGson;\n\npublic class BreakpointManager {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(BreakpointManager.class);\n\n\tprivate static final Gson GSON = buildGson();\n\tprivate static final Type TYPE_TOKEN = new TypeToken<Map<String, List<FileBreakpoint>>>() {\n\t}.getType();\n\n\tprivate static @NotNull Map<String, List<FileBreakpoint>> bpm = Collections.emptyMap();\n\tprivate static @Nullable Path savePath;\n\tprivate static DebugController debugController;\n\tprivate static Map<String, Entry<ClassNode, Listener>> listeners = Collections.emptyMap(); // class full name as key\n\n\tpublic static void saveAndExit() {\n\t\tsync();\n\t\tbpm = Collections.emptyMap();\n\t\tsavePath = null;\n\t\tlisteners = Collections.emptyMap();\n\t}\n\n\tpublic static void init(@Nullable Path baseDir) {\n\t\tPath saveDir = baseDir != null ? baseDir : Paths.get(\".\");\n\t\tsavePath = saveDir.resolve(\"breakpoints.json\"); // TODO: move into project file or same dir as project file\n\t\tif (Files.exists(savePath)) {\n\t\t\ttry (Reader reader = Files.newBufferedReader(savePath, StandardCharsets.UTF_8)) {\n\t\t\t\tbpm = GSON.fromJson(reader, TYPE_TOKEN);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to read breakpoints config: {}\", savePath, e);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @param listener When breakpoint is failed to set during debugging, this listener will be called.\n\t */\n\tpublic static void addListener(JClass topCls, Listener listener) {\n\t\tif (listeners.isEmpty()) {\n\t\t\tlisteners = new HashMap<>();\n\t\t}\n\t\tlisteners.put(DbgUtils.getRawFullName(topCls),\n\t\t\t\tnew SimpleEntry<>(topCls.getCls().getClassNode(), listener));\n\t}\n\n\tpublic static void removeListener(JClass topCls) {\n\t\tlisteners.remove(DbgUtils.getRawFullName(topCls));\n\t}\n\n\tpublic static List<Integer> getPositions(JClass topCls) {\n\t\tList<FileBreakpoint> bps = bpm.get(DbgUtils.getRawFullName(topCls));\n\t\tif (bps != null && bps.size() > 0) {\n\t\t\tSmali smali = DbgUtils.getSmali(topCls.getCls().getClassNode());\n\t\t\tif (smali != null) {\n\t\t\t\tList<Integer> posList = new ArrayList<>(bps.size());\n\t\t\t\tfor (FileBreakpoint bp : bps) {\n\t\t\t\t\tint pos = smali.getInsnPosByCodeOffset(bp.getFullMthRawID(), bp.codeOffset);\n\t\t\t\t\tif (pos > -1) {\n\t\t\t\t\t\tposList.add(pos);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn posList;\n\t\t\t}\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\tpublic static boolean set(JClass topCls, int line) {\n\t\tEntry<String, Integer> lineInfo = DbgUtils.getCodeOffsetInfoByLine(topCls, line);\n\t\tif (lineInfo != null) {\n\t\t\tif (bpm.isEmpty()) {\n\t\t\t\tbpm = new HashMap<>();\n\t\t\t}\n\t\t\tString name = DbgUtils.getRawFullName(topCls);\n\t\t\tList<FileBreakpoint> list = bpm.computeIfAbsent(name, k -> new ArrayList<>());\n\t\t\tFileBreakpoint bkp = list.stream()\n\t\t\t\t\t.filter(bp -> bp.codeOffset == lineInfo.getValue() && bp.getFullMthRawID().equals(lineInfo.getKey()))\n\t\t\t\t\t.findFirst()\n\t\t\t\t\t.orElse(null);\n\t\t\tboolean ok = true;\n\t\t\tif (bkp == null) {\n\t\t\t\tString[] sigs = DbgUtils.sepClassAndMthSig(lineInfo.getKey());\n\t\t\t\tif (sigs != null && sigs.length == 2) {\n\t\t\t\t\tFileBreakpoint bp = new FileBreakpoint(sigs[0], sigs[1], lineInfo.getValue());\n\t\t\t\t\tlist.add(bp);\n\t\t\t\t\tif (debugController != null) {\n\t\t\t\t\t\tok = debugController.setBreakpoint(bp);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ok;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static boolean remove(JClass topCls, int line) {\n\t\tEntry<String, Integer> lineInfo = DbgUtils.getCodeOffsetInfoByLine(topCls, line);\n\t\tif (lineInfo != null) {\n\t\t\tList<FileBreakpoint> bps = bpm.get(DbgUtils.getRawFullName(topCls));\n\t\t\tfor (Iterator<FileBreakpoint> it = bps.iterator(); it.hasNext();) {\n\t\t\t\tFileBreakpoint bp = it.next();\n\t\t\t\tif (bp.codeOffset == lineInfo.getValue() && bp.getFullMthRawID().equals(lineInfo.getKey())) {\n\t\t\t\t\tit.remove();\n\t\t\t\t\tif (debugController != null) {\n\t\t\t\t\t\treturn debugController.removeBreakpoint(bp);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static void sync() {\n\t\tif (savePath == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (bpm.isEmpty() && !Files.exists(savePath)) {\n\t\t\t// user didn't do anything with breakpoint so don't output breakpoint file.\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tFiles.write(savePath, GSON.toJson(bpm).getBytes(StandardCharsets.UTF_8));\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to write breakpoints config: {}\", savePath, e);\n\t\t}\n\t}\n\n\tpublic interface Listener {\n\t\tvoid breakpointDisabled(int codeOffset);\n\t}\n\n\tprotected static class FileBreakpoint {\n\t\tpublic String cls;\n\t\tpublic String mth;\n\t\tpublic long codeOffset;\n\n\t\tpublic FileBreakpoint() {\n\t\t\t// needed for JSON deserialization\n\t\t}\n\n\t\tprivate FileBreakpoint(String cls, String mth, long codeOffset) {\n\t\t\tthis.cls = cls;\n\t\t\tthis.mth = mth;\n\t\t\tthis.codeOffset = codeOffset;\n\t\t}\n\n\t\tprotected String getFullMthRawID() {\n\t\t\treturn cls + \".\" + mth;\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(codeOffset, cls, mth);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tif (obj instanceof FileBreakpoint) {\n\t\t\t\tif (obj == this) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tFileBreakpoint fbp = (FileBreakpoint) obj;\n\t\t\t\treturn fbp.codeOffset == codeOffset && fbp.cls.equals(cls) && fbp.mth.equals(mth);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprotected static List<FileBreakpoint> getAllBreakpoints() {\n\t\tList<FileBreakpoint> bpList = new ArrayList<>();\n\t\tfor (Entry<String, List<FileBreakpoint>> entry : bpm.entrySet()) {\n\t\t\tbpList.addAll(entry.getValue());\n\t\t}\n\t\treturn bpList;\n\t}\n\n\tprotected static void failBreakpoint(FileBreakpoint bp) {\n\t\tEntry<ClassNode, Listener> entry = listeners.get(bp.cls);\n\t\tif (entry != null) {\n\t\t\tint pos = DbgUtils.getSmali(entry.getKey())\n\t\t\t\t\t.getInsnPosByCodeOffset(bp.getFullMthRawID(), bp.codeOffset);\n\t\t\tpos = Math.max(0, pos);\n\t\t\tentry.getValue().breakpointDisabled(pos);\n\t\t}\n\t}\n\n\tprotected static void setDebugController(DebugController controller) {\n\t\tdebugController = controller;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/DbgUtils.java",
    "content": "package jadx.gui.device.debugger;\n\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxDecompiler;\nimport jadx.api.JavaClass;\nimport jadx.core.deobf.NameMapper;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.utils.android.AndroidManifestParser;\nimport jadx.core.utils.android.AppAttribute;\nimport jadx.core.utils.android.ApplicationParams;\nimport jadx.gui.device.debugger.smali.Smali;\nimport jadx.gui.device.debugger.smali.SmaliMethodNode;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic class DbgUtils {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DbgUtils.class);\n\n\tprivate static Map<ClassInfo, Smali> smaliCache = Collections.emptyMap();\n\n\tprotected static Smali getSmali(ClassNode topCls) {\n\t\tif (smaliCache == Collections.EMPTY_MAP) {\n\t\t\tsmaliCache = new HashMap<>();\n\t\t}\n\t\treturn smaliCache.computeIfAbsent(topCls.getTopParentClass().getClassInfo(),\n\t\t\t\tc -> Smali.disassemble(topCls));\n\t}\n\n\tpublic static String getSmaliCode(ClassNode topCls) {\n\t\tSmali smali = getSmali(topCls);\n\t\tif (smali != null) {\n\t\t\treturn smali.getCode();\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static Entry<String, Integer> getCodeOffsetInfoByLine(JClass cls, int line) {\n\t\tSmali smali = getSmali(cls.getCls().getClassNode().getTopParentClass());\n\t\tif (smali != null) {\n\t\t\treturn smali.getMthFullIDAndCodeOffsetByLine(line);\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Nullable\n\tpublic static SmaliMethodNode getSmaliMethodNode(JClass cls, String mthRawFullID) {\n\t\tSmali smali = getSmali(cls.getCls().getClassNode().getTopParentClass());\n\t\tif (smali != null) {\n\t\t\treturn smali.getMethodNode(mthRawFullID);\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static void printSmaliLineMapping(SmaliMethodNode smn) {\n\t\tfor (Map.Entry<Integer, Integer> lineToCodeOffset : smn.getLineMapping().entrySet()) {\n\t\t\tLOG.debug(\"line={} -> codeOffset={}\", lineToCodeOffset.getKey(), lineToCodeOffset.getValue());\n\t\t}\n\t}\n\n\tpublic static String[] sepClassAndMthSig(String fullSig) {\n\t\tint pos = fullSig.indexOf('(');\n\t\tif (pos != -1) {\n\t\t\tpos = fullSig.lastIndexOf('.', pos);\n\t\t\tif (pos != -1) {\n\t\t\t\tString[] sigs = new String[2];\n\t\t\t\tsigs[0] = fullSig.substring(0, pos);\n\t\t\t\tsigs[1] = fullSig.substring(pos + 1);\n\t\t\t\treturn sigs;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t// doesn't replace $\n\tpublic static String classSigToRawFullName(String clsSig) {\n\t\tif (clsSig != null && clsSig.startsWith(\"L\") && clsSig.endsWith(\";\")) {\n\t\t\tclsSig = clsSig.substring(1, clsSig.length() - 1)\n\t\t\t\t\t.replace(\"/\", \".\");\n\t\t}\n\t\treturn clsSig;\n\t}\n\n\t// replaces $\n\tpublic static String classSigToFullName(String clsSig) {\n\t\tif (clsSig != null && clsSig.startsWith(\"L\") && clsSig.endsWith(\";\")) {\n\t\t\tclsSig = clsSig.substring(1, clsSig.length() - 1)\n\t\t\t\t\t.replace(\"/\", \".\")\n\t\t\t\t\t.replace(\"$\", \".\");\n\t\t}\n\t\treturn clsSig;\n\t}\n\n\tpublic static String getRawFullName(JClass topCls) {\n\t\treturn topCls.getCls().getClassNode().getClassInfo().makeRawFullName();\n\t}\n\n\tpublic static boolean isStringObjectSig(String objectSig) {\n\t\treturn objectSig.equals(\"Ljava/lang/String;\");\n\t}\n\n\tpublic static JClass getTopClassBySig(String clsSig, MainWindow mainWindow) {\n\t\tclsSig = DbgUtils.classSigToFullName(clsSig);\n\t\tJavaClass cls = mainWindow.getWrapper().getDecompiler().searchJavaClassOrItsParentByOrigFullName(clsSig);\n\t\tif (cls != null) {\n\t\t\tJClass jc = mainWindow.getCacheObject().getNodeCache().makeFrom(cls);\n\t\t\treturn jc.getRootClass();\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static JClass getJClass(JavaClass cls, MainWindow mainWindow) {\n\t\treturn mainWindow.getCacheObject().getNodeCache().makeFrom(cls);\n\t}\n\n\tpublic static ClassNode getClassNodeBySig(String clsSig, MainWindow mainWindow) {\n\t\tclsSig = DbgUtils.classSigToFullName(clsSig);\n\t\treturn mainWindow.getWrapper().getDecompiler().searchClassNodeByOrigFullName(clsSig);\n\t}\n\n\tpublic static boolean isPrintableChar(int c) {\n\t\treturn 32 <= c && c <= 126;\n\t}\n\n\tpublic static final class AppData {\n\t\tprivate final String appPackage;\n\t\tprivate final JavaClass mainActivityCls;\n\n\t\tpublic AppData(String appPackage, JavaClass mainActivityCls) {\n\t\t\tthis.appPackage = appPackage;\n\t\t\tthis.mainActivityCls = mainActivityCls;\n\t\t}\n\n\t\tpublic String getAppPackage() {\n\t\t\treturn appPackage;\n\t\t}\n\n\t\tpublic JavaClass getMainActivityCls() {\n\t\t\treturn mainActivityCls;\n\t\t}\n\n\t\tpublic String getProcessName() {\n\t\t\treturn appPackage + '/' + mainActivityCls.getClassNode().getClassInfo().getFullName();\n\t\t}\n\t}\n\n\tpublic static @Nullable AppData parseAppData(MainWindow mw) {\n\t\tJadxDecompiler decompiler = mw.getWrapper().getDecompiler();\n\t\tString appPkg = decompiler.getRoot().getAppPackage();\n\t\tif (appPkg == null) {\n\t\t\tUiUtils.errorMessage(mw, NLS.str(\"error_dialog.not_found\", \"App package\"));\n\t\t\treturn null;\n\t\t}\n\t\tAndroidManifestParser parser = new AndroidManifestParser(\n\t\t\t\tAndroidManifestParser.getAndroidManifest(decompiler.getResources()),\n\t\t\t\tEnumSet.of(AppAttribute.MAIN_ACTIVITY),\n\t\t\t\tdecompiler.getArgs().getSecurity());\n\t\tif (!parser.isManifestFound()) {\n\t\t\tUiUtils.errorMessage(mw, NLS.str(\"error_dialog.not_found\", \"AndroidManifest.xml\"));\n\t\t\treturn null;\n\t\t}\n\t\tApplicationParams results = parser.parse();\n\t\tString mainActivityName = results.getMainActivity();\n\t\tif (mainActivityName == null) {\n\t\t\tUiUtils.errorMessage(mw, NLS.str(\"adb_dialog.msg_read_mani_fail\"));\n\t\t\treturn null;\n\t\t}\n\t\tif (!NameMapper.isValidFullIdentifier(mainActivityName)) {\n\t\t\tUiUtils.errorMessage(mw, \"Invalid main activity name\");\n\t\t\treturn null;\n\t\t}\n\t\tJavaClass mainActivityClass = results.getMainActivityJavaClass(decompiler);\n\t\tif (mainActivityClass == null) {\n\t\t\tUiUtils.errorMessage(mw, NLS.str(\"error_dialog.not_found\", \"Main activity class\"));\n\t\t\treturn null;\n\t\t}\n\t\treturn new AppData(appPkg, mainActivityClass);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/DebugController.java",
    "content": "package jadx.gui.device.debugger;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport javax.swing.JOptionPane;\nimport javax.swing.tree.DefaultMutableTreeNode;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.device.debugger.BreakpointManager.FileBreakpoint;\nimport jadx.gui.device.debugger.SmaliDebugger.Frame;\nimport jadx.gui.device.debugger.SmaliDebugger.RuntimeBreakpoint;\nimport jadx.gui.device.debugger.SmaliDebugger.RuntimeDebugInfo;\nimport jadx.gui.device.debugger.SmaliDebugger.RuntimeField;\nimport jadx.gui.device.debugger.SmaliDebugger.RuntimeRegister;\nimport jadx.gui.device.debugger.SmaliDebugger.RuntimeValue;\nimport jadx.gui.device.debugger.SmaliDebugger.RuntimeVarInfo;\nimport jadx.gui.device.debugger.smali.Smali;\nimport jadx.gui.device.debugger.smali.SmaliRegister;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.ui.panel.IDebugController;\nimport jadx.gui.ui.panel.JDebuggerPanel;\nimport jadx.gui.ui.panel.JDebuggerPanel.IListElement;\nimport jadx.gui.ui.panel.JDebuggerPanel.ValueTreeNode;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic final class DebugController implements SmaliDebugger.SuspendListener, IDebugController {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DebugController.class);\n\tprivate static final String ONCREATE_SIGNATURE = \"onCreate(Landroid/os/Bundle;)V\";\n\tprivate static final Map<String, RuntimeType> TYPE_MAP = new HashMap<>();\n\tprivate static final RuntimeType[] POSSIBLE_TYPES = { RuntimeType.OBJECT, RuntimeType.INT, RuntimeType.LONG };\n\tprivate static final int DEFAULT_CACHE_SIZE = 512;\n\n\tprivate JDebuggerPanel debuggerPanel;\n\tprivate SmaliDebugger debugger;\n\tprivate ArtAdapter.IArtAdapter art;\n\tprivate final CurrentInfo cur = new CurrentInfo();\n\n\tprivate BreakpointStore bpStore;\n\tprivate boolean updateAllFldAndReg = false; // update all fields and registers\n\tprivate ValueTreeNode toBeUpdatedTreeNode; // a field or register number.\n\tprivate volatile boolean isSuspended = true;\n\tprivate boolean hasResumed;\n\tprivate ResumeCmd run;\n\tprivate ResumeCmd stepOver;\n\tprivate ResumeCmd stepInto;\n\tprivate ResumeCmd stepOut;\n\tprivate StateListener stateListener;\n\n\tprivate final Map<String, RegisterObserver> regAdaMap = new ConcurrentHashMap<>();\n\n\tprivate final ExecutorService updateQueue = Executors.newSingleThreadExecutor();\n\tprivate final ExecutorService lazyQueue = Executors.newSingleThreadExecutor();\n\n\t@Override\n\tpublic boolean startDebugger(JDebuggerPanel debuggerPanel, String adbHost, int adbPort, int androidVer) {\n\t\tif (TYPE_MAP.isEmpty()) {\n\t\t\tinitTypeMap();\n\t\t}\n\t\tthis.debuggerPanel = debuggerPanel;\n\t\tUiUtils.uiRunAndWait(debuggerPanel::resetUI);\n\t\ttry {\n\t\t\tdebugger = SmaliDebugger.attach(adbHost, adbPort, this);\n\t\t} catch (SmaliDebuggerException e) {\n\t\t\tJOptionPane.showMessageDialog(debuggerPanel.getMainWindow(), e.getMessage(),\n\t\t\t\t\tNLS.str(\"error_dialog.title\"), JOptionPane.ERROR_MESSAGE);\n\t\t\tlogErr(e);\n\t\t\treturn false;\n\t\t}\n\t\tart = ArtAdapter.getAdapter(androidVer);\n\t\tresetAllInfo();\n\t\thasResumed = false;\n\t\trun = debugger::resume;\n\t\tstepOver = debugger::stepOver;\n\t\tstepInto = debugger::stepInto;\n\t\tstepOut = debugger::stepOut;\n\t\tstopAtOnCreate();\n\t\tif (bpStore == null) {\n\t\t\tbpStore = new BreakpointStore();\n\t\t} else {\n\t\t\tbpStore.reset();\n\t\t}\n\t\tBreakpointManager.setDebugController(this);\n\t\tinitBreakpoints(BreakpointManager.getAllBreakpoints());\n\t\treturn true;\n\t}\n\n\tprivate void openMainActivityTab(JClass mainActivity) {\n\t\tString fullID = DbgUtils.getRawFullName(mainActivity) + \".\" + ONCREATE_SIGNATURE;\n\t\tSmali smali = DbgUtils.getSmali(mainActivity.getCls().getClassNode());\n\t\tint pos = smali.getMethodDefPos(fullID);\n\t\tint finalPos = Math.max(1, pos);\n\t\tdebuggerPanel.scrollToSmaliLine(mainActivity, finalPos, true);\n\t}\n\n\tprivate void stopAtOnCreate() {\n\t\tDbgUtils.AppData appData = DbgUtils.parseAppData(debuggerPanel.getMainWindow());\n\t\tif (appData == null) {\n\t\t\tdebuggerPanel.log(\"Failed to set breakpoint at onCreate, you have to do it yourself.\");\n\t\t\treturn;\n\t\t}\n\t\tJClass mainActivity = DbgUtils.getJClass(appData.getMainActivityCls(), debuggerPanel.getMainWindow());\n\t\tlazyQueue.execute(() -> openMainActivityTab(mainActivity));\n\t\tString clsSig = DbgUtils.getRawFullName(mainActivity);\n\t\ttry {\n\t\t\tlong id = debugger.getClassID(clsSig, true);\n\t\t\tif (id != -1) {\n\t\t\t\treturn; // this app is running, we can't stop at onCreate anymore.\n\t\t\t}\n\t\t\tdebuggerPanel.log(String.format(\"Breakpoint will set at %s.%s\", clsSig, ONCREATE_SIGNATURE));\n\t\t\tdebugger.regMethodEntryEventSync(clsSig, ONCREATE_SIGNATURE::equals);\n\t\t} catch (SmaliDebuggerException e) {\n\t\t\tlogErr(e, String.format(\"Failed set breakpoint at %s.%s\", clsSig, ONCREATE_SIGNATURE));\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isSuspended() {\n\t\treturn isSuspended;\n\t}\n\n\t@Override\n\tpublic boolean isDebugging() {\n\t\treturn debugger != null;\n\t}\n\n\t@Override\n\tpublic boolean run() {\n\t\treturn execResumeCmd(run);\n\t}\n\n\t@Override\n\tpublic boolean stepInto() {\n\t\treturn execResumeCmd(stepInto);\n\t}\n\n\t@Override\n\tpublic boolean stepOver() {\n\t\treturn execResumeCmd(stepOver);\n\t}\n\n\t@Override\n\tpublic boolean stepOut() {\n\t\treturn execResumeCmd(stepOut);\n\t}\n\n\t@Override\n\tpublic boolean pause() {\n\t\tif (isDebugging()) {\n\t\t\ttry {\n\t\t\t\tdebugger.suspend();\n\t\t\t} catch (SmaliDebuggerException e) {\n\t\t\t\tlogErr(e);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tsetDebuggerState(true, false);\n\t\t\tresetAllInfo();\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean stop() {\n\t\tif (isDebugging()) {\n\t\t\ttry {\n\t\t\t\tdebugger.exit();\n\t\t\t} catch (SmaliDebuggerException e) {\n\t\t\t\tlogErr(e);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean exit() {\n\t\tif (isDebugging()) {\n\t\t\tsetDebuggerState(true, true);\n\t\t\tstop();\n\t\t\tdebugger = null;\n\t\t}\n\t\tBreakpointManager.setDebugController(null);\n\t\tdebuggerPanel.getMainWindow().destroyDebuggerPanel();\n\t\tdebuggerPanel = null;\n\t\treturn true;\n\t}\n\n\t/**\n\t * @param type must be one of int, long, float, double, string or object.\n\t */\n\t@Override\n\tpublic boolean modifyRegValue(ValueTreeNode valNode, ArgType type, Object value) {\n\t\tcheckType(type, value);\n\t\tif (isDebugging() && isSuspended()) {\n\t\t\treturn modifyValueInternal(valNode, castType(type), value);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String getProcessName() {\n\t\tDbgUtils.AppData appData = DbgUtils.parseAppData(debuggerPanel.getMainWindow());\n\t\tif (appData == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn appData.getProcessName();\n\t}\n\n\tprivate RuntimeType castType(ArgType type) {\n\t\tif (type == ArgType.INT) {\n\t\t\treturn RuntimeType.INT;\n\t\t}\n\t\tif (type == ArgType.STRING) {\n\t\t\treturn RuntimeType.STRING;\n\t\t}\n\t\tif (type == ArgType.LONG) {\n\t\t\treturn RuntimeType.LONG;\n\t\t}\n\t\tif (type == ArgType.FLOAT) {\n\t\t\treturn RuntimeType.FLOAT;\n\t\t}\n\t\tif (type == ArgType.DOUBLE) {\n\t\t\treturn RuntimeType.DOUBLE;\n\t\t}\n\t\tif (type == ArgType.OBJECT) {\n\t\t\treturn RuntimeType.OBJECT;\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Unexpected type: \" + type);\n\t}\n\n\tprotected static RuntimeType castType(String type) {\n\t\tRuntimeType rt = null;\n\t\tif (!StringUtils.isEmpty(type)) {\n\t\t\trt = TYPE_MAP.get(type);\n\t\t}\n\t\tif (rt == null) {\n\t\t\trt = POSSIBLE_TYPES[0];\n\t\t}\n\t\treturn rt;\n\t}\n\n\tprivate void checkType(ArgType type, Object value) {\n\t\tif (!(type == ArgType.INT && value instanceof Integer)\n\t\t\t\t&& !(type == ArgType.STRING && value instanceof String)\n\t\t\t\t&& !(type == ArgType.LONG && value instanceof Long)\n\t\t\t\t&& !(type == ArgType.FLOAT && value instanceof Float)\n\t\t\t\t&& !(type == ArgType.DOUBLE && value instanceof Double)\n\t\t\t\t&& !(type == ArgType.OBJECT && value instanceof Long)) {\n\t\t\tthrow new JadxRuntimeException(\"Type must be one of int, long, float, double, String or Object.\");\n\t\t}\n\t}\n\n\tprivate boolean modifyValueInternal(ValueTreeNode valNode, RuntimeType type, Object value) {\n\t\tif (valNode instanceof RegTreeNode) {\n\t\t\ttry {\n\t\t\t\tRegTreeNode regNode = (RegTreeNode) valNode;\n\t\t\t\tdebugger.setValueSync(\n\t\t\t\t\t\tregNode.getRuntimeRegNum(),\n\t\t\t\t\t\ttype,\n\t\t\t\t\t\tvalue,\n\t\t\t\t\t\tcur.frame.getThreadID(),\n\t\t\t\t\t\tcur.frame.getFrame().getID());\n\t\t\t\tlazyQueue.execute(() -> {\n\t\t\t\t\tsetRegsNotUpdated();\n\t\t\t\t\tupdateRegister((RegTreeNode) valNode, type, true);\n\t\t\t\t});\n\t\t\t} catch (SmaliDebuggerException e) {\n\t\t\t\tlogErr(e);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (valNode instanceof FieldTreeNode) {\n\t\t\t// TODO: check type.\n\t\t\tFieldTreeNode fldNode = (FieldTreeNode) valNode;\n\t\t\ttry {\n\t\t\t\tdebugger.setValueSync(\n\t\t\t\t\t\tfldNode.getObjectID(),\n\t\t\t\t\t\t((RuntimeField) fldNode.getRuntimeValue()).getFieldID(),\n\t\t\t\t\t\tfldNode.getRuntimeField().getType(),\n\t\t\t\t\t\tvalue);\n\t\t\t\tlazyQueue.execute(() -> {\n\t\t\t\t\tupdateField((FieldTreeNode) valNode);\n\t\t\t\t});\n\t\t\t} catch (SmaliDebuggerException e) {\n\t\t\t\tlogErr(e);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate interface ResumeCmd {\n\t\tvoid exec() throws SmaliDebuggerException;\n\t}\n\n\tprivate boolean execResumeCmd(ResumeCmd cmd) {\n\t\tif (!hasResumed) {\n\t\t\tif (cmd != run) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\thasResumed = true;\n\t\t}\n\t\tif (isDebugging() && isSuspended()) {\n\t\t\tupdateAllFldAndReg = cmd == run;\n\t\t\tsetDebuggerState(false, false);\n\t\t\ttry {\n\t\t\t\tcmd.exec();\n\t\t\t\treturn true;\n\t\t\t} catch (SmaliDebuggerException e) {\n\t\t\t\tlogErr(e);\n\t\t\t\tsetDebuggerState(true, false);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * @param suspended suspended by step, breakpoint, etc..\n\t * @param stopped   remote app had been terminated, it's used to\n\t *                  change icons only, to check if it's running use isDebugging() instead.\n\t */\n\tprivate void setDebuggerState(boolean suspended, boolean stopped) {\n\t\tisSuspended = suspended;\n\t\tif (stopped) {\n\t\t\thasResumed = false;\n\t\t}\n\t\tif (stateListener != null) {\n\t\t\tstateListener.onStateChanged(suspended, stopped);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStateListener(StateListener listener) {\n\t\tstateListener = listener;\n\t}\n\n\t@Override\n\tpublic void onSuspendEvent(SuspendInfo info) {\n\t\tif (!isDebugging()) {\n\t\t\treturn;\n\t\t}\n\t\tif (info.isTerminated()) {\n\t\t\tdebuggerPanel.log(\"Debugger exited.\");\n\t\t\tsetDebuggerState(true, true);\n\t\t\tdebugger = null;\n\t\t\treturn;\n\t\t}\n\t\tsetDebuggerState(true, false);\n\t\tlong threadID = info.getThreadID();\n\t\tint refreshLevel = 2; // update all threads, stack frames, registers and fields.\n\t\tif (cur.frame != null) {\n\t\t\tif (threadID == cur.frame.getThreadID()\n\t\t\t\t\t&& info.getClassID() == cur.frame.getClsID()\n\t\t\t\t\t&& info.getMethodID() == cur.frame.getMthID()) {\n\n\t\t\t\trefreshLevel = 1; // relevant registers or fields.\n\t\t\t} else {\n\t\t\t\tcur.frame.getClsID();\n\t\t\t}\n\t\t\tsetRegsNotUpdated();\n\t\t}\n\t\tif (refreshLevel == 2) {\n\t\t\tupdateAllInfo(threadID, info.getOffset());\n\t\t} else {\n\t\t\tif (cur.smali != null && cur.frame != null) {\n\t\t\t\trefreshRegInfo(info.getOffset());\n\t\t\t\trefreshCurFrame(threadID, info.getOffset());\n\t\t\t\tif (updateAllFldAndReg) {\n\t\t\t\t\tdebuggerPanel.resetRegTreeNodes();\n\t\t\t\t\tupdateAllRegisters(cur.frame);\n\t\t\t\t} else if (toBeUpdatedTreeNode != null) {\n\t\t\t\t\tlazyQueue.execute(() -> updateRegOrField(toBeUpdatedTreeNode));\n\t\t\t\t}\n\t\t\t\tmarkCodeOffset(info.getOffset());\n\t\t\t} else {\n\t\t\t\tdebuggerPanel.resetRegTreeNodes();\n\t\t\t}\n\t\t\tif (cur.frame != null) {\n\t\t\t\t// update current code offset in stack frame.\n\t\t\t\tcur.frame.updateCodeOffset(info.getOffset());\n\t\t\t\tdebuggerPanel.refreshStackFrameList(Collections.emptyList());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void refreshRegInfo(long codeOffset) {\n\t\tList<RegisterObserver.Info> list = cur.regAdapter.getInfoAt(codeOffset);\n\t\tfor (RegisterObserver.Info info : list) {\n\t\t\tRegTreeNode reg = cur.frame.getRegNodes().get(info.getSmaliRegNum());\n\t\t\tif (info.isLoad()) {\n\t\t\t\tapplyDbgInfo(reg, info.getName(), info.getType());\n\t\t\t} else {\n\t\t\t\treg.setAlias(\"\");\n\t\t\t\treg.setAbsoluteType(false);\n\t\t\t}\n\t\t}\n\t\tif (list.size() > 0) {\n\t\t\tdebuggerPanel.refreshRegisterTree();\n\t\t}\n\t}\n\n\tprivate void updateRegOrField(ValueTreeNode valTreeNode) {\n\t\tif (valTreeNode instanceof RegTreeNode) {\n\t\t\tupdateRegister((RegTreeNode) valTreeNode, null, true);\n\t\t\treturn;\n\t\t}\n\t\tif (valTreeNode instanceof FieldTreeNode) {\n\t\t\tupdateField((FieldTreeNode) valTreeNode);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tpublic void updateField(FieldTreeNode node) {\n\t\ttry {\n\t\t\tsetFieldsNotUpdated();\n\t\t\tdebugger.getValueSync(node.getObjectID(), node.getRuntimeField());\n\t\t\tdecodeRuntimeValue(node);\n\t\t\tdebuggerPanel.updateThisTree(node);\n\t\t} catch (SmaliDebuggerException e) {\n\t\t\tlogErr(e);\n\t\t}\n\t}\n\n\tpublic boolean updateRegister(RegTreeNode regNode, RuntimeType type, boolean retry) {\n\t\tif (type == null) {\n\t\t\tif (regNode.isAbsoluteType()) {\n\t\t\t\ttype = castType(regNode.getType());\n\t\t\t} else {\n\t\t\t\ttype = POSSIBLE_TYPES[0];\n\t\t\t}\n\t\t}\n\t\tboolean ok = false;\n\t\tRuntimeRegister register = null;\n\t\ttry {\n\t\t\tregister = debugger.getRegisterSync(\n\t\t\t\t\tcur.frame.getThreadID(),\n\t\t\t\t\tcur.frame.getFrame().getID(),\n\t\t\t\t\tregNode.getRuntimeRegNum(),\n\t\t\t\t\ttype);\n\t\t} catch (SmaliDebuggerException e) {\n\t\t\tif (retry) {\n\t\t\t\tif (debugger.errIsTypeMismatched(e.getErrCode())) {\n\t\t\t\t\tRuntimeType[] types = getPossibleTypes(type);\n\t\t\t\t\tfor (RuntimeType nextType : types) {\n\t\t\t\t\t\tok = updateRegister(regNode, nextType, false);\n\t\t\t\t\t\tif (ok) {\n\t\t\t\t\t\t\tregNode.updateType(nextType.getDesc());\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tlogErr(e.getMessage() + \" for \" + regNode.getName());\n\t\t\t\t\tregNode.updateType(null);\n\t\t\t\t\tregNode.updateValue(null);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (register != null) {\n\t\t\tregNode.updateReg(register);\n\t\t\tdecodeRuntimeValue(regNode);\n\t\t}\n\t\tdebuggerPanel.updateRegTree(regNode);\n\t\treturn ok;\n\t}\n\n\tprivate RuntimeType[] getPossibleTypes(RuntimeType cur) {\n\t\tRuntimeType[] types = new RuntimeType[2];\n\t\tfor (int i = 0, j = 0; i < POSSIBLE_TYPES.length; i++) {\n\t\t\tif (cur != POSSIBLE_TYPES[i]) {\n\t\t\t\ttypes[j++] = POSSIBLE_TYPES[i];\n\t\t\t}\n\t\t}\n\t\treturn types;\n\t}\n\n\t// when single stepping we can detect which reg need to be updated.\n\tprivate void markNextToBeUpdated(long codeOffset) {\n\t\tif (codeOffset != -1) {\n\t\t\tObject rst = cur.smali.getResultRegOrField(cur.mthFullID, codeOffset);\n\t\t\ttoBeUpdatedTreeNode = null;\n\t\t\tif (cur.frame != null) {\n\t\t\t\tif (rst instanceof Integer) {\n\t\t\t\t\tint regNum = (int) rst;\n\t\t\t\t\tif (cur.frame.getRegNodes().size() > regNum) {\n\t\t\t\t\t\ttoBeUpdatedTreeNode = cur.frame.getRegNodes().get(regNum);\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (rst instanceof FieldInfo) {\n\t\t\t\t\tFieldInfo info = (FieldInfo) rst;\n\t\t\t\t\ttoBeUpdatedTreeNode = cur.frame.getFieldNodes()\n\t\t\t\t\t\t\t.stream()\n\t\t\t\t\t\t\t.filter(f -> f.getName().equals(info.getName()))\n\t\t\t\t\t\t\t.findFirst()\n\t\t\t\t\t\t\t.orElse(null);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void updateAllThreads() {\n\t\tList<Long> threads;\n\t\ttry {\n\t\t\tthreads = debugger.getAllThreadsSync();\n\t\t} catch (SmaliDebuggerException e) {\n\t\t\tlogErr(e);\n\t\t\treturn;\n\t\t}\n\t\tList<ThreadBoxElement> threadEleList = new ArrayList<>(threads.size());\n\t\tfor (Long thread : threads) {\n\t\t\tThreadBoxElement ele = new ThreadBoxElement(thread);\n\t\t\tthreadEleList.add(ele);\n\t\t}\n\t\tdebuggerPanel.refreshThreadBox(threadEleList);\n\t\tlazyQueue.execute(() -> {\n\t\t\tfor (ThreadBoxElement ele : threadEleList) { // get thread names\n\t\t\t\ttry {\n\t\t\t\t\tele.setName(debugger.getThreadNameSync(ele.getThreadID()));\n\t\t\t\t} catch (SmaliDebuggerException e) {\n\t\t\t\t\tlogErr(e);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdebuggerPanel.refreshThreadBox(Collections.emptyList());\n\t\t});\n\t}\n\n\tprivate FrameNode updateAllStackFrames(long threadID) {\n\t\tList<SmaliDebugger.Frame> frames = Collections.emptyList();\n\t\ttry {\n\t\t\tframes = debugger.getFramesSync(threadID);\n\t\t} catch (SmaliDebuggerException e) {\n\t\t\tlogErr(e);\n\t\t}\n\t\tif (frames.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tList<FrameNode> frameEleList = new ArrayList<>(frames.size());\n\t\tfor (SmaliDebugger.Frame frame : frames) {\n\t\t\tFrameNode ele = new FrameNode(threadID, frame);\n\t\t\tframeEleList.add(ele);\n\t\t}\n\t\tFrameNode curEle = frameEleList.get(0);\n\t\tfetchStackFrameNames(curEle);\n\n\t\tdebuggerPanel.refreshStackFrameList(frameEleList);\n\t\tlazyQueue.execute(() -> { // get class & method names for frames\n\t\t\tfor (int i = 1; i < frameEleList.size(); i++) {\n\t\t\t\tfetchStackFrameNames(frameEleList.get(i));\n\t\t\t}\n\t\t\tdebuggerPanel.refreshStackFrameList(Collections.emptyList());\n\t\t});\n\t\treturn frameEleList.get(0);\n\t}\n\n\tprivate void fetchStackFrameNames(FrameNode ele) {\n\t\ttry {\n\t\t\tlong clsID = ele.getFrame().getClassID();\n\t\t\tString clsSig = debugger.getClassSignatureSync(clsID);\n\t\t\tString mthSig = debugger.getMethodSignatureSync(clsID, ele.getFrame().getMethodID());\n\t\t\tele.setSignatures(clsSig, mthSig);\n\t\t} catch (SmaliDebuggerException e) {\n\t\t\tlogErr(e);\n\t\t}\n\t}\n\n\tprivate Smali decodeSmali(FrameNode frame) {\n\t\tif (cur.frame.getClsSig() != null) {\n\t\t\tJClass jClass = DbgUtils.getTopClassBySig(frame.getClsSig(), debuggerPanel.getMainWindow());\n\t\t\tif (jClass != null) {\n\t\t\t\tClassNode cNode = jClass.getCls().getClassNode();\n\t\t\t\tcur.clsNode = jClass;\n\t\t\t\tcur.mthFullID = DbgUtils.classSigToRawFullName(frame.getClsSig()) + \".\" + frame.getMthSig();\n\t\t\t\treturn DbgUtils.getSmali(cNode);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate void refreshCurFrame(long threadID, long codeOffset) {\n\t\ttry {\n\t\t\tFrame frame = debugger.getCurrentFrame(threadID);\n\t\t\tcur.frame.setFrame(frame);\n\t\t\tcur.frame.updateCodeOffset(codeOffset);\n\t\t} catch (SmaliDebuggerException e) {\n\t\t\tlogErr(e);\n\t\t}\n\t}\n\n\tprivate void updateAllFields(FrameNode frame) {\n\t\tList<FieldNode> fldNodes = Collections.emptyList();\n\t\tString clsSig = frame.getClsSig();\n\t\tif (clsSig != null) {\n\t\t\tClassNode clsNode = DbgUtils.getClassNodeBySig(clsSig, debuggerPanel.getMainWindow());\n\t\t\tif (clsNode != null) {\n\t\t\t\tfldNodes = clsNode.getFields();\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tlong thisID = debugger.getThisID(frame.getThreadID(), frame.getFrame().getID());\n\t\t\tList<RuntimeField> flds = debugger.getAllFieldsSync(frame.getClsID());\n\t\t\tList<FieldTreeNode> nodes = new ArrayList<>(flds.size());\n\t\t\tfor (RuntimeField fld : flds) {\n\t\t\t\tFieldTreeNode fldNode = new FieldTreeNode(fld, thisID);\n\t\t\t\tfldNodes.stream()\n\t\t\t\t\t\t.filter(f -> f.getName().equals(fldNode.getName()))\n\t\t\t\t\t\t.findFirst()\n\t\t\t\t\t\t.ifPresent(smaliFld -> fldNode.setAlias(smaliFld.getAlias()));\n\t\t\t\tnodes.add(fldNode);\n\t\t\t}\n\t\t\tdebuggerPanel.updateThisFieldNodes(nodes);\n\t\t\tframe.setFieldNodes(nodes);\n\t\t\tif (thisID > 0 && nodes.size() > 0) {\n\t\t\t\tlazyQueue.execute(() -> updateAllFieldValues(thisID, frame));\n\t\t\t}\n\t\t} catch (SmaliDebuggerException e) {\n\t\t\tlogErr(e);\n\t\t}\n\t}\n\n\tprivate void updateAllFieldValues(long thisID, FrameNode frame) {\n\t\tList<FieldTreeNode> nodes = frame.getFieldNodes();\n\t\tif (nodes.size() > 0) {\n\t\t\tList<FieldTreeNode> flds = new ArrayList<>(nodes.size());\n\t\t\tList<RuntimeField> rts = new ArrayList<>(nodes.size());\n\t\t\tnodes.forEach(n -> {\n\t\t\t\tRuntimeField f = n.getRuntimeField();\n\t\t\t\tif (f.isBelongToThis()) {\n\t\t\t\t\tflds.add(n);\n\t\t\t\t\trts.add(f);\n\t\t\t\t}\n\t\t\t});\n\t\t\ttry {\n\t\t\t\tdebugger.getAllFieldValuesSync(thisID, rts);\n\t\t\t\tflds.forEach(n -> decodeRuntimeValue(n));\n\t\t\t\tdebuggerPanel.refreshThisFieldTree();\n\t\t\t} catch (SmaliDebuggerException e) {\n\t\t\t\tlogErr(e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void updateAllRegisters(FrameNode frame) {\n\t\tUiUtils.uiRun(() -> {\n\t\t\tif (!buildRegTreeNodes(frame).isEmpty()) {\n\t\t\t\tfetchAllRegisters(frame);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void fetchAllRegisters(FrameNode frame) {\n\t\tList<SmaliRegister> regs = cur.regAdapter.getInitializedList(frame.getCodeOffset());\n\t\tfor (SmaliRegister reg : regs) {\n\t\t\tRuntimeVarInfo info = cur.regAdapter.getInfo(reg.getRuntimeRegNum(), frame.getCodeOffset());\n\t\t\tRegTreeNode regNode = frame.getRegNodes().get(reg.getRegNum());\n\t\t\tif (info != null) {\n\t\t\t\tapplyDbgInfo(regNode, info);\n\t\t\t}\n\t\t\tupdateRegister(regNode, null, true);\n\t\t}\n\t}\n\n\tprivate void applyDbgInfo(RegTreeNode rn, RuntimeVarInfo info) {\n\t\tapplyDbgInfo(rn, info.getName(), info.getType());\n\t}\n\n\tprivate void applyDbgInfo(RegTreeNode rn, String alias, String type) {\n\t\trn.setAlias(alias);\n\t\trn.updateType(type);\n\t\trn.setAbsoluteType(true);\n\t}\n\n\tprivate void setRegsNotUpdated() {\n\t\tif (cur.frame != null) {\n\t\t\tfor (RegTreeNode regNode : cur.frame.getRegNodes()) {\n\t\t\t\tregNode.setUpdated(false);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void setFieldsNotUpdated() {\n\t\tif (cur.frame != null) {\n\t\t\tfor (FieldTreeNode node : cur.frame.getFieldNodes()) {\n\t\t\t\tnode.setUpdated(false);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate List<RegTreeNode> buildRegTreeNodes(FrameNode frame) {\n\t\tList<SmaliRegister> regs = cur.smali.getRegisterList(cur.mthFullID);\n\t\tList<RegTreeNode> regNodes = new ArrayList<>(regs.size());\n\t\tList<RegTreeNode> inRtOrder = new ArrayList<>(regs.size());\n\n\t\tregs.forEach(r -> {\n\t\t\tRegTreeNode rn = new RegTreeNode(r);\n\t\t\tregNodes.add(rn);\n\t\t\tinRtOrder.add(rn);\n\t\t});\n\t\tinRtOrder.sort(Comparator.comparingInt(RegTreeNode::getRuntimeRegNum));\n\t\tframe.setRegNodes(regNodes);\n\t\tdebuggerPanel.updateRegTreeNodes(inRtOrder);\n\t\tdebuggerPanel.refreshRegisterTree();\n\t\treturn regNodes;\n\t}\n\n\tprivate boolean decodeRuntimeValue(RuntimeValueTreeNode valNode) {\n\t\tRuntimeValue rValue = valNode.getRuntimeValue();\n\t\tRuntimeType type = rValue.getType();\n\t\tif (!valNode.isAbsoluteType()) {\n\t\t\tvalNode.updateType(null);\n\t\t}\n\t\ttry {\n\t\t\tswitch (type) {\n\t\t\t\tcase OBJECT:\n\t\t\t\t\treturn decodeObject(valNode);\n\t\t\t\tcase STRING:\n\t\t\t\t\tString str = \"\\\"\" + debugger.readStringSync(rValue) + \"\\\"\";\n\t\t\t\t\tvalNode.updateType(\"java.lang.String\")\n\t\t\t\t\t\t\t.updateTypeID(debugger.readID(rValue))\n\t\t\t\t\t\t\t.updateValue(str);\n\t\t\t\t\tbreak;\n\t\t\t\tcase INT:\n\t\t\t\t\tvalNode.updateValue(Integer.toString(debugger.readInt(rValue)));\n\t\t\t\t\tbreak;\n\t\t\t\tcase LONG:\n\t\t\t\t\tvalNode.updateValue(Long.toString(debugger.readAll(rValue)));\n\t\t\t\t\tbreak;\n\t\t\t\tcase ARRAY:\n\t\t\t\t\tdecodeArrayVal(valNode);\n\t\t\t\t\tbreak;\n\t\t\t\tcase BOOLEAN: {\n\t\t\t\t\tint b = debugger.readByte(rValue);\n\t\t\t\t\tvalNode.updateValue(b == 1 ? \"true\" : \"false\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase SHORT:\n\t\t\t\t\tvalNode.updateValue(Short.toString(debugger.readShort(rValue)));\n\t\t\t\t\tbreak;\n\t\t\t\tcase CHAR:\n\t\t\t\tcase BYTE: {\n\t\t\t\t\tint b = (int) debugger.readAll(rValue);\n\t\t\t\t\tif (DbgUtils.isPrintableChar(b)) {\n\t\t\t\t\t\tvalNode.updateValue(type == RuntimeType.CHAR ? String.valueOf((char) b) : String.valueOf((byte) b));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvalNode.updateValue(String.valueOf(b));\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase DOUBLE:\n\t\t\t\t\tdouble d = debugger.readDouble(rValue);\n\t\t\t\t\tvalNode.updateValue(Double.toString(d));\n\t\t\t\t\tbreak;\n\t\t\t\tcase FLOAT:\n\t\t\t\t\tfloat f = debugger.readFloat(rValue);\n\t\t\t\t\tvalNode.updateValue(Float.toString(f));\n\t\t\t\t\tbreak;\n\t\t\t\tcase VOID:\n\t\t\t\t\tvalNode.updateType(\"void\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase THREAD:\n\t\t\t\t\tvalNode.updateType(\"thread\").updateTypeID(debugger.readID(rValue));\n\t\t\t\t\tbreak;\n\t\t\t\tcase THREAD_GROUP:\n\t\t\t\t\tvalNode.updateType(\"thread_group\").updateTypeID(debugger.readID(rValue));\n\t\t\t\t\tbreak;\n\t\t\t\tcase CLASS_LOADER:\n\t\t\t\t\tvalNode.updateType(\"class_loader\").updateTypeID(debugger.readID(rValue));\n\t\t\t\t\tbreak;\n\t\t\t\tcase CLASS_OBJECT:\n\t\t\t\t\tvalNode.updateType(\"class_object\").updateTypeID(debugger.readID(rValue));\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t} catch (SmaliDebuggerException e) {\n\t\t\tlogErr(e);\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate boolean decodeObject(RuntimeValueTreeNode valNode) {\n\t\tRuntimeValue rValue = valNode.getRuntimeValue();\n\t\tboolean ok = true;\n\t\tif (debugger.readID(rValue) == 0) {\n\t\t\tif (valNode.isAbsoluteType()) {\n\t\t\t\tvalNode.updateValue(\"null\");\n\t\t\t\treturn ok;\n\t\t\t} else if (!art.readNullObject()) {\n\t\t\t\tvalNode.updateType(art.typeForNull());\n\t\t\t\tvalNode.updateValue(\"0\");\n\t\t\t\treturn ok;\n\t\t\t}\n\t\t}\n\t\tString sig;\n\t\ttry {\n\t\t\tsig = debugger.readObjectSignatureSync(rValue);\n\t\t\tvalNode.updateType(String.format(\"%s@%d\", DbgUtils.classSigToRawFullName(sig),\n\t\t\t\t\tdebugger.readID(rValue)));\n\t\t} catch (SmaliDebuggerException e) {\n\t\t\tok = debugger.errIsInvalidObject(e.getErrCode()) && valNode instanceof RegTreeNode;\n\t\t\tif (ok) {\n\t\t\t\ttry {\n\t\t\t\t\tRegTreeNode reg = (RegTreeNode) valNode;\n\t\t\t\t\tRuntimeRegister rr = debugger.getRegisterSync(\n\t\t\t\t\t\t\tcur.frame.getThreadID(),\n\t\t\t\t\t\t\tcur.frame.getFrame().getID(),\n\t\t\t\t\t\t\treg.getRuntimeRegNum(), RuntimeType.INT);\n\t\t\t\t\treg.updateReg(rr);\n\t\t\t\t\trValue = rr;\n\t\t\t\t\tvalNode.updateType(RuntimeType.INT.getDesc());\n\t\t\t\t\tvalNode.updateValue(Long.toString((int) debugger.readAll(rValue)));\n\t\t\t\t} catch (SmaliDebuggerException except) {\n\t\t\t\t\tlogErr(except, String.format(\"Update %s failed, %s\", valNode.getName(), except.getMessage()));\n\t\t\t\t\tvalNode.updateValue(except.getMessage());\n\t\t\t\t\tok = false;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlogErr(e);\n\t\t\t}\n\t\t}\n\t\treturn ok;\n\t}\n\n\tprivate void decodeArrayVal(RuntimeValueTreeNode valNode) throws SmaliDebuggerException {\n\t\tString type = debugger.readObjectSignatureSync(valNode.getRuntimeValue());\n\t\tArgType argType = ArgType.parse(type);\n\t\tString javaType = argType.toString();\n\t\tEntry<Integer, List<Long>> ret = debugger.readArray(valNode.getRuntimeValue(), 0, 0);\n\t\tjavaType = javaType.substring(0, javaType.length() - 1) + ret.getKey() + \"]\";\n\t\tvalNode.updateType(javaType + \"@\" + debugger.readID(valNode.getRuntimeValue()));\n\n\t\tif (argType.getArrayElement().isPrimitive()) {\n\t\t\tfor (Long aLong : ret.getValue()) {\n\t\t\t\tvalNode.add(new DefaultMutableTreeNode(Long.toString(aLong)));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tString typeSig = type.substring(1);\n\t\tif (DbgUtils.isStringObjectSig(typeSig)) {\n\t\t\tfor (Long aLong : ret.getValue()) {\n\t\t\t\tvalNode.add(new DefaultMutableTreeNode(debugger.readStringSync(aLong)));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\ttypeSig = DbgUtils.classSigToRawFullName(typeSig);\n\t\tfor (Long aLong : ret.getValue()) {\n\t\t\tvalNode.add(new DefaultMutableTreeNode(String.format(\"%s@%d\", typeSig, aLong)));\n\t\t}\n\t}\n\n\tprivate void updateAllInfo(long threadID, long codeOffset) {\n\t\tupdateQueue.execute(() -> {\n\t\t\tresetAllInfo();\n\t\t\tcur.frame = updateAllStackFrames(threadID);\n\t\t\tif (cur.frame != null) {\n\t\t\t\tlazyQueue.execute(() -> updateAllFields(cur.frame));\n\t\t\t\tif (cur.frame.getClsSig() == null || cur.frame.getMthSig() == null) {\n\t\t\t\t\tfetchStackFrameNames(cur.frame);\n\t\t\t\t}\n\t\t\t\tcur.smali = decodeSmali(cur.frame);\n\t\t\t\tif (cur.smali != null) {\n\t\t\t\t\tcur.regAdapter = regAdaMap.computeIfAbsent(cur.mthFullID,\n\t\t\t\t\t\t\tk -> RegisterObserver.merge(\n\t\t\t\t\t\t\t\t\tgetRuntimeDebugInfo(cur.frame),\n\t\t\t\t\t\t\t\t\tgetSmaliRegisterList(),\n\t\t\t\t\t\t\t\t\tart,\n\t\t\t\t\t\t\t\t\tcur.mthFullID));\n\n\t\t\t\t\tif (cur.smali.getRegCount(cur.mthFullID) > 0) {\n\t\t\t\t\t\tupdateAllRegisters(cur.frame);\n\t\t\t\t\t}\n\t\t\t\t\tmarkCodeOffset(codeOffset);\n\t\t\t\t}\n\t\t\t}\n\t\t\tupdateAllThreads();\n\t\t});\n\t}\n\n\tprivate List<SmaliRegister> getSmaliRegisterList() {\n\t\tint regCount = cur.smali.getRegCount(cur.mthFullID);\n\t\tint paramStart = cur.smali.getParamRegStart(cur.mthFullID);\n\t\tList<SmaliRegister> srs = cur.smali.getRegisterList(cur.mthFullID);\n\t\tfor (SmaliRegister sr : srs) {\n\t\t\tsr.setRuntimeRegNum(art.getRuntimeRegNum(sr.getRegNum(), regCount, paramStart));\n\t\t}\n\t\treturn srs;\n\t}\n\n\tprivate void resetAllInfo() {\n\t\tisSuspended = true;\n\t\ttoBeUpdatedTreeNode = null;\n\t\tcur.reset();\n\t\tUiUtils.uiRun(debuggerPanel::resetAllDebuggingInfo);\n\t}\n\n\tprivate List<RuntimeVarInfo> getRuntimeDebugInfo(FrameNode frame) {\n\t\ttry {\n\t\t\tRuntimeDebugInfo dbgInfo = debugger.getRuntimeDebugInfo(frame.getClsID(), frame.getMthID());\n\t\t\tif (dbgInfo != null) {\n\t\t\t\treturn dbgInfo.getInfoList();\n\t\t\t}\n\t\t} catch (SmaliDebuggerException ignore) {\n\t\t\t// logErr(e);\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\tprivate void markCodeOffset(long codeOffset) {\n\t\tscrollToPos(codeOffset);\n\t\tmarkNextToBeUpdated(codeOffset);\n\t}\n\n\tprivate void logErr(Exception e, String extra) {\n\t\tdebuggerPanel.log(e.getMessage());\n\t\tdebuggerPanel.log(extra);\n\t\tLOG.error(extra, e);\n\t}\n\n\tprivate void logErr(Exception e) {\n\t\tdebuggerPanel.log(e.getMessage());\n\t\tLOG.error(\"Debug error\", e);\n\t}\n\n\tprivate void logErr(String e) {\n\t\tdebuggerPanel.log(e);\n\t\tLOG.error(\"Debug error: {}\", e);\n\t}\n\n\tprivate void scrollToPos(long codeOffset) {\n\t\tint pos = -1;\n\t\tif (codeOffset > -1) {\n\t\t\tpos = cur.smali.getInsnPosByCodeOffset(cur.mthFullID, codeOffset);\n\t\t}\n\t\tif (pos == -1) {\n\t\t\tpos = cur.smali.getMethodDefPos(cur.mthFullID);\n\t\t\tif (pos == -1) {\n\t\t\t\tdebuggerPanel.log(\"Can't scroll to \" + cur.mthFullID);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tdebuggerPanel.scrollToSmaliLine(cur.clsNode, pos, true);\n\t}\n\n\tprivate void initBreakpoints(List<FileBreakpoint> fbps) {\n\t\tif (fbps.size() == 0) {\n\t\t\treturn;\n\t\t}\n\t\tboolean fetch = true;\n\t\tfor (FileBreakpoint fbp : fbps) {\n\t\t\ttry {\n\t\t\t\tlong id = debugger.getClassID(fbp.cls, fetch);\n\t\t\t\t// only fetch classes from JVM once,\n\t\t\t\t// if this time this class hasn't been loaded then it won't load next time, cuz JVM is freezed.\n\t\t\t\tfetch = false;\n\t\t\t\tif (id > -1) {\n\t\t\t\t\tsetBreakpoint(id, fbp);\n\t\t\t\t} else {\n\t\t\t\t\tsetDelayBreakpoint(fbp);\n\t\t\t\t}\n\t\t\t} catch (SmaliDebuggerException e) {\n\t\t\t\tlogErr(e);\n\t\t\t\tfailBreakpoint(fbp, e.getMessage());\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected boolean setBreakpoint(FileBreakpoint bp) {\n\t\tif (!isDebugging()) {\n\t\t\treturn true;\n\t\t}\n\t\ttry {\n\t\t\tlong cid = debugger.getClassID(bp.cls, true);\n\t\t\tif (cid > -1) {\n\t\t\t\tsetBreakpoint(cid, bp);\n\t\t\t} else {\n\t\t\t\tsetDelayBreakpoint(bp);\n\t\t\t}\n\t\t} catch (SmaliDebuggerException e) {\n\t\t\tlogErr(e);\n\t\t\tBreakpointManager.failBreakpoint(bp);\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate void setDelayBreakpoint(FileBreakpoint bp) {\n\t\tboolean hasSet = bpStore.hasSetDelaied(bp.cls);\n\t\tbpStore.add(bp, null);\n\t\tif (!hasSet) {\n\t\t\tupdateQueue.execute(() -> {\n\t\t\t\ttry {\n\t\t\t\t\tdebugger.regClassPrepareEventForBreakpoint(bp.cls, id -> {\n\t\t\t\t\t\tList<FileBreakpoint> list = bpStore.get(bp.cls);\n\t\t\t\t\t\tfor (FileBreakpoint fbp : list) {\n\t\t\t\t\t\t\tsetBreakpoint(id, fbp);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t} catch (SmaliDebuggerException e) {\n\t\t\t\t\tlogErr(e);\n\t\t\t\t\tfailBreakpoint(bp, \"\");\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\tprotected void setBreakpoint(long cid, FileBreakpoint fbp) {\n\t\ttry {\n\t\t\tlong mid = debugger.getMethodID(cid, fbp.mth);\n\t\t\tif (mid > -1) {\n\t\t\t\tRuntimeBreakpoint rbp = debugger.makeBreakpoint(cid, mid, fbp.codeOffset);\n\t\t\t\tdebugger.setBreakpoint(rbp);\n\t\t\t\tbpStore.add(fbp, rbp);\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch (SmaliDebuggerException e) {\n\t\t\tlogErr(e);\n\t\t}\n\t\tfailBreakpoint(fbp, \"Failed to get method for breakpoint, \" + fbp.mth + \":\" + fbp.codeOffset);\n\t}\n\n\tprivate void failBreakpoint(FileBreakpoint fbp, String msg) {\n\t\tif (!msg.isEmpty()) {\n\t\t\tdebuggerPanel.log(msg);\n\t\t}\n\t\tbpStore.removeBreakpoint(fbp);\n\t\tBreakpointManager.failBreakpoint(fbp);\n\t}\n\n\tprotected boolean removeBreakpoint(FileBreakpoint fbp) {\n\t\tif (!isDebugging()) {\n\t\t\treturn true;\n\t\t}\n\t\tRuntimeBreakpoint rbp = bpStore.removeBreakpoint(fbp);\n\t\tif (rbp != null) {\n\t\t\ttry {\n\t\t\t\tdebugger.removeBreakpoint(rbp);\n\t\t\t} catch (SmaliDebuggerException e) {\n\t\t\t\tlogErr(e);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static RuntimeBreakpoint delayBP = null;\n\n\tprivate class BreakpointStore {\n\t\tMap<FileBreakpoint, RuntimeBreakpoint> bpm = Collections.emptyMap();\n\n\t\tBreakpointStore() {\n\t\t\tif (delayBP == null) {\n\t\t\t\tdelayBP = debugger.makeBreakpoint(-1, -1, -1);\n\t\t\t}\n\t\t}\n\n\t\tvoid reset() {\n\t\t\tbpm.clear();\n\t\t}\n\n\t\tboolean hasSetDelaied(String cls) {\n\t\t\tfor (Entry<FileBreakpoint, RuntimeBreakpoint> entry : bpm.entrySet()) {\n\t\t\t\tif (entry.getValue() == delayBP && entry.getKey().cls.equals(cls)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tList<FileBreakpoint> get(String cls) {\n\t\t\tList<FileBreakpoint> fbps = new ArrayList<>();\n\t\t\tbpm.forEach((k, v) -> {\n\t\t\t\tif (v == delayBP && k.cls.equals(cls)) {\n\t\t\t\t\tfbps.add(k);\n\t\t\t\t\tbpm.remove(k);\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn fbps;\n\t\t}\n\n\t\tvoid add(FileBreakpoint fbp, RuntimeBreakpoint rbp) {\n\t\t\tif (bpm == Collections.EMPTY_MAP) {\n\t\t\t\tbpm = new ConcurrentHashMap<>();\n\t\t\t}\n\t\t\tbpm.put(fbp, rbp == null ? delayBP : rbp);\n\t\t}\n\n\t\tRuntimeBreakpoint removeBreakpoint(FileBreakpoint fbp) {\n\t\t\treturn bpm.remove(fbp);\n\t\t}\n\t}\n\n\tpublic class FrameNode implements IListElement {\n\t\tprivate SmaliDebugger.Frame frame;\n\t\tprivate final long threadID;\n\t\tprivate String clsSig;\n\t\tprivate String mthSig;\n\t\tprivate StringBuilder cache;\n\t\tprivate long codeOffset = -1;\n\t\tprivate List<RegTreeNode> regNodes;\n\t\tprivate List<FieldTreeNode> thisNodes;\n\t\tprivate long thisID;\n\n\t\tpublic FrameNode(long threadID, SmaliDebugger.Frame frame) {\n\t\t\tcache = new StringBuilder(DEFAULT_CACHE_SIZE);\n\t\t\tthis.frame = frame;\n\t\t\tthis.threadID = threadID;\n\t\t\tregNodes = Collections.emptyList();\n\t\t\tthisNodes = Collections.emptyList();\n\t\t}\n\n\t\tpublic SmaliDebugger.Frame getFrame() {\n\t\t\treturn frame;\n\t\t}\n\n\t\tpublic void setFrame(SmaliDebugger.Frame frame) {\n\t\t\tthis.frame = frame;\n\t\t}\n\n\t\tpublic long getClsID() {\n\t\t\treturn frame.getClassID();\n\t\t}\n\n\t\tpublic long getMthID() {\n\t\t\treturn frame.getMethodID();\n\t\t}\n\n\t\tpublic long getThreadID() {\n\t\t\treturn threadID;\n\t\t}\n\n\t\tpublic long getThisID() {\n\t\t\treturn thisID;\n\t\t}\n\n\t\tpublic void setThisID(long thisID) {\n\t\t\tthis.thisID = thisID;\n\t\t}\n\n\t\tpublic void setSignatures(String clsSig, String mthSig) {\n\t\t\tthis.clsSig = clsSig;\n\t\t\tthis.mthSig = mthSig;\n\t\t\tresetCache();\n\t\t}\n\n\t\tpublic String getClsSig() {\n\t\t\treturn clsSig;\n\t\t}\n\n\t\tpublic String getMthSig() {\n\t\t\treturn mthSig;\n\t\t}\n\n\t\tpublic void updateCodeOffset(long codeOffset) {\n\t\t\tthis.codeOffset = codeOffset;\n\t\t\tif (this.codeOffset > -1) {\n\t\t\t\tresetCache();\n\t\t\t}\n\t\t}\n\n\t\tpublic long getCodeOffset() {\n\t\t\treturn codeOffset == -1 ? frame.getCodeIndex() : codeOffset;\n\t\t}\n\n\t\tpublic void setRegNodes(List<RegTreeNode> regNodes) {\n\t\t\tthis.regNodes = regNodes;\n\t\t}\n\n\t\tpublic List<RegTreeNode> getRegNodes() {\n\t\t\treturn regNodes;\n\t\t}\n\n\t\tpublic List<FieldTreeNode> getFieldNodes() {\n\t\t\treturn thisNodes;\n\t\t}\n\n\t\tpublic void setFieldNodes(List<FieldTreeNode> thisNodes) {\n\t\t\tthis.thisNodes = thisNodes;\n\t\t}\n\n\t\t@Override\n\t\tpublic void onSelected() {\n\t\t\tif (clsSig != null) {\n\t\t\t\tJClass cls = DbgUtils.getTopClassBySig(clsSig, debuggerPanel.getMainWindow());\n\t\t\t\tif (cls != null) {\n\t\t\t\t\tSmali smali = DbgUtils.getSmali(cls.getCls().getClassNode());\n\t\t\t\t\tif (smali != null) {\n\t\t\t\t\t\tint pos = smali.getInsnPosByCodeOffset(\n\t\t\t\t\t\t\t\tDbgUtils.classSigToRawFullName(clsSig) + \".\" + mthSig,\n\t\t\t\t\t\t\t\tgetCodeOffset());\n\t\t\t\t\t\tdebuggerPanel.scrollToSmaliLine(cls, Math.max(0, pos), true);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdebuggerPanel.log(\"Can't open smali panel for \" + clsSig + \"->\" + mthSig);\n\t\t\t}\n\t\t}\n\n\t\tprivate void resetCache() {\n\t\t\t// Do not reuse thee existing cache instance as this can result in\n\t\t\t// multi-threading access issues in case toString() method is active\n\t\t\tthis.cache = new StringBuilder(DEFAULT_CACHE_SIZE);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tStringBuilder sbCache = cache;\n\t\t\tif (sbCache.length() == 0) {\n\t\t\t\tlong off = getCodeOffset();\n\t\t\t\tif (off < 0) {\n\t\t\t\t\tsbCache.append(String.format(\"index: %-4d \", off));\n\t\t\t\t} else {\n\t\t\t\t\tsbCache.append(String.format(\"index: %04x \", off));\n\t\t\t\t}\n\t\t\t\tif (clsSig == null) {\n\t\t\t\t\tsbCache.append(\"clsID: \").append(frame.getClassID());\n\t\t\t\t} else {\n\t\t\t\t\tsbCache.append(clsSig).append(\"->\");\n\t\t\t\t}\n\t\t\t\tif (mthSig == null) {\n\t\t\t\t\tsbCache.append(\" mthID: \").append(frame.getMethodID());\n\t\t\t\t} else {\n\t\t\t\t\tsbCache.append(mthSig);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn sbCache.toString();\n\t\t}\n\t}\n\n\tprivate static class ThreadBoxElement implements IListElement {\n\t\tprivate long threadID;\n\t\tprivate String name;\n\n\t\tpublic ThreadBoxElement(long threadID) {\n\t\t\tthis.threadID = threadID;\n\t\t}\n\n\t\tpublic void setName(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\tpublic long getThreadID() {\n\t\t\treturn threadID;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tif (name == null) {\n\t\t\t\treturn \"thread id: \" + threadID;\n\t\t\t}\n\t\t\treturn \"thread id: \" + threadID + \" name:\" + name;\n\t\t}\n\n\t\t@Override\n\t\tpublic void onSelected() {\n\n\t\t}\n\t}\n\n\tprivate static class RegTreeNode extends RuntimeValueTreeNode {\n\t\tprivate static final long serialVersionUID = -1111111202103122234L;\n\n\t\tprivate final SmaliRegister smaliReg;\n\t\tprivate RuntimeRegister runtimeReg;\n\t\tprivate String value;\n\t\tprivate String type;\n\t\tprivate String alias;\n\t\tprivate boolean absType;\n\n\t\tpublic RegTreeNode(SmaliRegister smaliReg) {\n\t\t\tthis.smaliReg = smaliReg;\n\t\t}\n\n\t\tpublic void updateReg(RuntimeRegister reg) {\n\t\t\truntimeReg = reg;\n\t\t}\n\n\t\tpublic void setAlias(String alias) {\n\t\t\tthis.alias = alias;\n\t\t}\n\n\t\t@Override\n\t\tpublic RegTreeNode updateValue(String value) {\n\t\t\tsetUpdated(true);\n\t\t\tthis.value = value;\n\t\t\tremoveAllChildren();\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic RegTreeNode updateType(String type) {\n\t\t\tif (this.type == null || !this.type.equals(type)) {\n\t\t\t\tthis.type = type;\n\t\t\t\treset();\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate void reset() {\n\t\t\tvalue = null;\n\t\t\tremoveAllChildren();\n\t\t\tsetUpdated(true);\n\t\t\tthis.absType = false;\n\t\t\tupdateTypeID(0);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\tif (!StringUtils.isEmpty(alias)) {\n\t\t\t\treturn String.format(\"%s (%s)\", smaliReg.getName(), alias);\n\t\t\t}\n\t\t\treturn String.format(\"%-3s\", smaliReg.getName());\n\t\t}\n\n\t\t@Override\n\t\t@Nullable\n\t\tpublic String getValue() {\n\t\t\treturn value;\n\t\t}\n\n\t\tpublic RuntimeRegister getRuntimeReg() {\n\t\t\treturn runtimeReg;\n\t\t}\n\n\t\tpublic int getRuntimeRegNum() {\n\t\t\treturn smaliReg.getRuntimeRegNum();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getType() {\n\t\t\tif (type != null) {\n\t\t\t\treturn type;\n\t\t\t}\n\t\t\tif (runtimeReg != null) {\n\t\t\t\treturn runtimeReg.getType().getDesc();\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic RuntimeValue getRuntimeValue() {\n\t\t\treturn getRuntimeReg();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isAbsoluteType() {\n\t\t\treturn absType;\n\t\t}\n\n\t\tpublic void setAbsoluteType(boolean abs) {\n\t\t\tabsType = abs;\n\t\t}\n\t}\n\n\tstatic class FieldTreeNode extends RuntimeValueTreeNode {\n\t\tprivate static final long serialVersionUID = -1111111202103122235L;\n\n\t\tprivate final RuntimeField field;\n\t\tprivate String value;\n\t\tprivate String alias;\n\t\tprivate long objectID;\n\n\t\tprivate FieldTreeNode(RuntimeField field, long id) {\n\t\t\tthis.field = field;\n\t\t\tobjectID = id;\n\t\t}\n\n\t\tpublic long getObjectID() {\n\t\t\treturn objectID;\n\t\t}\n\n\t\tpublic void setObjectID(long id) {\n\t\t\tthis.objectID = id;\n\t\t}\n\n\t\tpublic RuntimeField getRuntimeField() {\n\t\t\treturn this.field;\n\t\t}\n\n\t\tpublic void setAlias(String alias) {\n\t\t\tthis.alias = alias;\n\t\t}\n\n\t\t@Override\n\t\tpublic FieldTreeNode updateValue(String val) {\n\t\t\tsetUpdated(true);\n\t\t\tvalue = val;\n\t\t\tremoveAllChildren();\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic FieldTreeNode updateType(String val) {\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\tif (StringUtils.isEmpty(alias) || alias.equals(field.getName())) {\n\t\t\t\treturn field.getName();\n\t\t\t}\n\t\t\treturn field.getName() + \" (\" + alias + \")\";\n\t\t}\n\n\t\t@Override\n\t\tpublic String getValue() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getType() {\n\t\t\treturn ArgType.parse(field.getFieldType()).toString();\n\t\t}\n\n\t\t@Override\n\t\tpublic RuntimeValue getRuntimeValue() {\n\t\t\treturn field;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isAbsoluteType() {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tprivate abstract static class RuntimeValueTreeNode extends ValueTreeNode {\n\t\tprivate static final long serialVersionUID = -1111111202103260222L;\n\t\tprivate long typeID;\n\n\t\t@Override\n\t\tpublic ValueTreeNode updateTypeID(long id) {\n\t\t\tthis.typeID = id;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic long getTypeID() {\n\t\t\treturn this.typeID;\n\t\t}\n\n\t\tpublic abstract RuntimeValue getRuntimeValue();\n\n\t\tpublic abstract boolean isAbsoluteType();\n\t}\n\n\tprivate class CurrentInfo {\n\t\tJClass clsNode;\n\t\tString mthFullID;\n\t\tSmali smali;\n\t\tFrameNode frame;\n\t\tRegisterObserver regAdapter;\n\n\t\tpublic void reset() {\n\t\t\tframe = null;\n\t\t\tsmali = null;\n\t\t\tclsNode = null;\n\t\t\tregAdapter = null;\n\t\t\tmthFullID = \"\";\n\t\t}\n\t}\n\n\tprivate static void initTypeMap() {\n\t\tTYPE_MAP.put(\"I\", RuntimeType.INT);\n\t\tTYPE_MAP.put(\"Z\", RuntimeType.INT);\n\t\tTYPE_MAP.put(\"B\", RuntimeType.INT);\n\t\tTYPE_MAP.put(\"C\", RuntimeType.INT);\n\t\tTYPE_MAP.put(\"F\", RuntimeType.INT);\n\t\tTYPE_MAP.put(\"S\", RuntimeType.INT);\n\t\tTYPE_MAP.put(\"V\", RuntimeType.INT);\n\t\tTYPE_MAP.put(\"int\", RuntimeType.INT);\n\t\tTYPE_MAP.put(\"boolean\", RuntimeType.INT);\n\t\tTYPE_MAP.put(\"byte\", RuntimeType.INT);\n\t\tTYPE_MAP.put(\"short\", RuntimeType.INT);\n\t\tTYPE_MAP.put(\"char\", RuntimeType.INT);\n\t\tTYPE_MAP.put(\"float\", RuntimeType.INT);\n\t\tTYPE_MAP.put(\"void\", RuntimeType.INT);\n\t\tTYPE_MAP.put(\"L\", RuntimeType.LONG);\n\t\tTYPE_MAP.put(\"D\", RuntimeType.LONG);\n\t\tTYPE_MAP.put(\"long\", RuntimeType.LONG);\n\t\tTYPE_MAP.put(\"double\", RuntimeType.LONG);\n\t\tTYPE_MAP.put(\"java.lang.String\", RuntimeType.STRING);\n\t\tTYPE_MAP.put(\"Ljava/lang/String;\", RuntimeType.STRING);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/DebugSettings.java",
    "content": "package jadx.gui.device.debugger;\n\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.StringUtils;\nimport jadx.gui.device.protocol.ADB;\nimport jadx.gui.device.protocol.ADBDevice;\nimport jadx.gui.utils.NLS;\n\npublic class DebugSettings {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DebugSettings.class);\n\n\tprivate static final int FORWARD_TCP_PORT = 33233;\n\n\tpublic static final DebugSettings INSTANCE = new DebugSettings();\n\n\tprivate int ver;\n\tprivate String pid;\n\tprivate String name;\n\tprivate ADBDevice device;\n\tprivate int forwardTcpPort = FORWARD_TCP_PORT;\n\tprivate String expectPkg = \"\";\n\tprivate boolean autoAttachPkg = false;\n\n\tprivate DebugSettings() {\n\t}\n\n\tpublic void set(ADBDevice device, int ver, String pid, String name) {\n\t\tthis.ver = ver;\n\t\tthis.pid = pid;\n\t\tthis.name = name;\n\t\tthis.device = device;\n\t\tthis.autoAttachPkg = false;\n\t\tthis.expectPkg = \"\";\n\t}\n\n\tpublic DebugSettings setPid(String pid) {\n\t\tthis.pid = pid;\n\t\treturn this;\n\t}\n\n\tpublic DebugSettings setName(String name) {\n\t\tthis.name = name;\n\t\treturn this;\n\t}\n\n\tpublic String forwardJDWP() {\n\t\tint localPort = forwardTcpPort;\n\t\tString resultDesc = \"\";\n\t\ttry {\n\t\t\tdo {\n\t\t\t\tADBDevice.ForwardResult rst = device.forwardJDWP(localPort + \"\", pid);\n\t\t\t\tif (rst.state == 0) {\n\t\t\t\t\tforwardTcpPort = localPort;\n\t\t\t\t\treturn \"\";\n\t\t\t\t}\n\t\t\t\tif (rst.state == 1) {\n\t\t\t\t\tif (rst.desc.contains(\"Only one usage of each socket address\")) { // port is taken by other process\n\t\t\t\t\t\tif (localPort < 65536) {\n\t\t\t\t\t\t\tlocalPort++; // retry\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tresultDesc = rst.desc;\n\t\t\t\tbreak;\n\t\t\t} while (true);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"JDWP forward error\", e);\n\t\t}\n\t\tif (StringUtils.isEmpty(resultDesc)) {\n\t\t\tresultDesc = NLS.str(\"adb_dialog.forward_fail\");\n\t\t}\n\t\treturn resultDesc;\n\t}\n\n\t// we have to remove all ports that forwarding the jdwp:pid, otherwise our JDWP handshake may fail.\n\tpublic void clearForward() {\n\t\tString jdwpPid = \" jdwp:\" + pid;\n\t\tString tcpPort = \" tcp:\" + forwardTcpPort;\n\t\ttry {\n\t\t\tList<String> list = ADB.listForward(device.getDeviceInfo().getAdbHost(),\n\t\t\t\t\tdevice.getDeviceInfo().getAdbPort());\n\t\t\tfor (String s : list) {\n\t\t\t\tif (s.startsWith(device.getSerial()) && s.endsWith(jdwpPid) && !s.contains(tcpPort)) {\n\t\t\t\t\tString[] fields = s.split(\"\\\\s+\");\n\t\t\t\t\tfor (String field : fields) {\n\t\t\t\t\t\tif (field.startsWith(\"tcp:\")) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tdevice.removeForward(field.substring(\"tcp:\".length()));\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tLOG.error(\"JDWP remove forward error\", e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"JDWP clear forward error\", e);\n\t\t}\n\t}\n\n\tpublic boolean isBeingDebugged() {\n\t\tString jdwpPid = \" jdwp:\" + pid;\n\t\tString tcpPort = \" tcp:\" + forwardTcpPort;\n\t\ttry {\n\t\t\tList<String> list = ADB.listForward(device.getDeviceInfo().getAdbHost(),\n\t\t\t\t\tdevice.getDeviceInfo().getAdbPort());\n\t\t\tfor (String s : list) {\n\t\t\t\tif (s.startsWith(device.getSerial()) && s.endsWith(jdwpPid)) {\n\t\t\t\t\treturn !s.contains(tcpPort);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"ADB list forward error\", e);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic int getVer() {\n\t\treturn ver;\n\t}\n\n\tpublic String getPid() {\n\t\treturn pid;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic ADBDevice getDevice() {\n\t\treturn device;\n\t}\n\n\tpublic int getForwardTcpPort() {\n\t\treturn forwardTcpPort;\n\t}\n\n\tpublic String getExpectPkg() {\n\t\treturn expectPkg;\n\t}\n\n\tpublic void setExpectPkg(String expectPkg) {\n\t\tthis.expectPkg = expectPkg;\n\t}\n\n\tpublic boolean isAutoAttachPkg() {\n\t\treturn autoAttachPkg;\n\t}\n\n\tpublic void setAutoAttachPkg(boolean autoAttachPkg) {\n\t\tthis.autoAttachPkg = autoAttachPkg;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/EventListenerAdapter.java",
    "content": "package jadx.gui.device.debugger;\n\nimport io.github.skylot.jdwp.JDWP.Event.Composite.BreakpointEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.ClassPrepareEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.ClassUnloadEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.ExceptionEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.FieldAccessEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.FieldModificationEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.MethodEntryEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.MethodExitEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.MethodExitWithReturnValueEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.MonitorContendedEnterEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.MonitorContendedEnteredEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.MonitorWaitEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.MonitorWaitedEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.SingleStepEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.ThreadDeathEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.ThreadStartEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.VMDeathEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.VMStartEvent;\n\nabstract class EventListenerAdapter {\n\tvoid onVMStart(VMStartEvent event) {\n\t}\n\n\tvoid onVMDeath(VMDeathEvent event) {\n\t}\n\n\tvoid onSingleStep(SingleStepEvent event) {\n\t}\n\n\tvoid onBreakpoint(BreakpointEvent event) {\n\t}\n\n\tvoid onMethodEntry(MethodEntryEvent event) {\n\t}\n\n\tvoid onMethodExit(MethodExitEvent event) {\n\t}\n\n\tvoid onMethodExitWithReturnValue(MethodExitWithReturnValueEvent event) {\n\t}\n\n\tvoid onMonitorContendedEnter(MonitorContendedEnterEvent event) {\n\t}\n\n\tvoid onMonitorContendedEntered(MonitorContendedEnteredEvent event) {\n\t}\n\n\tvoid onMonitorWait(MonitorWaitEvent event) {\n\t}\n\n\tvoid onMonitorWaited(MonitorWaitedEvent event) {\n\t}\n\n\tvoid onException(ExceptionEvent event) {\n\t}\n\n\tvoid onThreadStart(ThreadStartEvent event) {\n\t}\n\n\tvoid onThreadDeath(ThreadDeathEvent event) {\n\t}\n\n\tvoid onClassPrepare(ClassPrepareEvent event) {\n\t}\n\n\tvoid onClassUnload(ClassUnloadEvent event) {\n\t}\n\n\tvoid onFieldAccess(FieldAccessEvent event) {\n\t}\n\n\tvoid onFieldModification(FieldModificationEvent event) {\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/LogcatController.java",
    "content": "package jadx.gui.device.debugger;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.time.format.DateTimeFormatter;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.Timer;\nimport java.util.TimerTask;\nimport java.util.TreeSet;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.device.protocol.ADBDevice;\nimport jadx.gui.ui.panel.LogcatPanel;\n\npublic class LogcatController {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(LogcatController.class);\n\n\tprivate final ADBDevice adbDevice;\n\tprivate final LogcatPanel logcatPanel;\n\tprivate Timer timer;\n\tprivate final String timezone;\n\tprivate LogcatInfo recent = null;\n\tprivate List<LogcatInfo> events = new ArrayList<>();\n\tprivate LogcatFilter filter = new LogcatFilter();\n\tprivate String status = \"null\";\n\n\tpublic LogcatController(LogcatPanel logcatPanel, ADBDevice adbDevice) throws IOException {\n\t\tthis.adbDevice = adbDevice;\n\t\tthis.logcatPanel = logcatPanel;\n\t\tthis.timezone = adbDevice.getTimezone();\n\t\tthis.startLogcat();\n\t}\n\n\tpublic void startLogcat() {\n\t\ttimer = new Timer();\n\t\ttimer.schedule(new TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tgetLog();\n\t\t\t}\n\t\t}, 0, 1000);\n\t\tthis.status = \"running\";\n\t}\n\n\tpublic void stopLogcat() {\n\t\ttimer.cancel();\n\t\tthis.status = \"stopped\";\n\t}\n\n\tpublic String getStatus() {\n\t\treturn this.status;\n\t}\n\n\tpublic void clearLogcat() {\n\t\ttry {\n\t\t\tadbDevice.clearLogcat();\n\t\t\tclearEvents();\n\t\t} catch (IOException e) {\n\t\t\tLOG.error(\"Failed to clear Logcat\", e);\n\t\t}\n\t}\n\n\tprivate void getLog() {\n\t\tif (!logcatPanel.isReady()) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tbyte[] buf;\n\t\t\tif (recent == null) {\n\t\t\t\tbuf = adbDevice.getBinaryLogcat();\n\t\t\t} else {\n\t\t\t\tbuf = adbDevice.getBinaryLogcat(recent.getAfterTimestamp());\n\t\t\t}\n\t\t\tif (buf == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tByteBuffer in = ByteBuffer.wrap(buf);\n\t\t\tin.order(ByteOrder.LITTLE_ENDIAN);\n\t\t\twhile (in.remaining() > 20) {\n\n\t\t\t\tLogcatInfo eInfo = null;\n\t\t\t\tbyte[] msgBuf;\n\t\t\t\tshort eLen = in.getShort();\n\t\t\t\tshort eHdrLen = in.getShort();\n\t\t\t\tif (eLen + eHdrLen > in.remaining()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tswitch (eHdrLen) {\n\t\t\t\t\tcase 20: // header length 20 == version 1\n\t\t\t\t\t\teInfo = new LogcatInfo(eLen, eHdrLen, in.getInt(), in.getInt(), in.getInt(), in.getInt(), in.get());\n\t\t\t\t\t\tmsgBuf = new byte[eLen];\n\t\t\t\t\t\tin.get(msgBuf, 0, eLen - 1);\n\t\t\t\t\t\teInfo.setMsg(msgBuf);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 24: // header length 24 == version 2 / 3\n\t\t\t\t\t\teInfo = new LogcatInfo(eLen, eHdrLen, in.getInt(), in.getInt(), in.getInt(), in.getInt(), in.getInt(), in.get());\n\t\t\t\t\t\tmsgBuf = new byte[eLen];\n\t\t\t\t\t\tin.get(msgBuf, 0, eLen - 1);\n\t\t\t\t\t\teInfo.setMsg(msgBuf);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 28: // header length 28 == version 4\n\t\t\t\t\t\teInfo = new LogcatInfo(eLen, eHdrLen, in.getInt(), in.getInt(), in.getInt(), in.getInt(), in.getInt(), in.getInt(),\n\t\t\t\t\t\t\t\tin.get());\n\t\t\t\t\t\tmsgBuf = new byte[eLen];\n\t\t\t\t\t\tin.get(msgBuf, 0, eLen - 1);\n\t\t\t\t\t\teInfo.setMsg(msgBuf);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (eInfo == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (recent == null) {\n\t\t\t\t\trecent = eInfo;\n\t\t\t\t} else if (recent.getInstant().isBefore(eInfo.getInstant())) {\n\t\t\t\t\trecent = eInfo;\n\t\t\t\t}\n\n\t\t\t\tif (filter.doFilter(eInfo)) {\n\t\t\t\t\tlogcatPanel.log(eInfo);\n\t\t\t\t}\n\t\t\t\tevents.add(eInfo);\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to get logcat message\", e);\n\t\t}\n\t}\n\n\tpublic boolean reload() {\n\t\tstopLogcat();\n\t\tboolean ok = logcatPanel.clearLogcatArea();\n\t\tif (ok) {\n\t\t\tevents.forEach((eInfo) -> {\n\t\t\t\tif (filter.doFilter(eInfo)) {\n\t\t\t\t\tlogcatPanel.log(eInfo);\n\t\t\t\t}\n\t\t\t});\n\t\t\tstartLogcat();\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic void clearEvents() {\n\t\tthis.recent = null;\n\t\tthis.events = new ArrayList<>();\n\t}\n\n\tpublic void exit() {\n\t\tstopLogcat();\n\t\tfilter = new LogcatFilter();\n\t\trecent = null;\n\t}\n\n\tpublic LogcatFilter getFilter() {\n\t\treturn this.filter;\n\t}\n\n\tpublic class LogcatFilter {\n\n\t\tprivate final Set<Integer> pid;\n\t\tprivate final Set<Byte> msgType;\n\n\t\tpublic LogcatFilter() {\n\t\t\tthis(new TreeSet<>(), new TreeSet<>(List.of((byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5, (byte) 6, (byte) 7, (byte) 8)));\n\t\t}\n\n\t\tpublic LogcatFilter(Set<Integer> pid, Set<Byte> msgType) {\n\t\t\tthis.pid = pid;\n\t\t\tthis.msgType = msgType;\n\t\t}\n\n\t\tpublic void addPid(int pid) {\n\t\t\tthis.pid.add(pid);\n\t\t}\n\n\t\tpublic void removePid(int pid) {\n\t\t\tthis.pid.remove(pid);\n\t\t}\n\n\t\tpublic void togglePid(int pid, boolean state) {\n\t\t\tif (state) {\n\t\t\t\taddPid(pid);\n\t\t\t} else {\n\t\t\t\tremovePid(pid);\n\t\t\t}\n\t\t}\n\n\t\tpublic void addMsgType(byte msgType) {\n\t\t\tthis.msgType.add(msgType);\n\t\t}\n\n\t\tpublic void removeMsgType(byte msgType) {\n\t\t\tthis.msgType.remove(msgType);\n\t\t}\n\n\t\tpublic void toggleMsgType(byte msgType, boolean state) {\n\t\t\tif (state) {\n\t\t\t\taddMsgType(msgType);\n\t\t\t} else {\n\t\t\t\tremoveMsgType(msgType);\n\t\t\t}\n\t\t}\n\n\t\tpublic boolean doFilter(LogcatInfo inInfo) {\n\t\t\treturn (pid.contains(inInfo.getPid())) && msgType.contains(inInfo.getMsgType());\n\t\t}\n\n\t\tpublic List<LogcatInfo> getFilteredList(List<LogcatInfo> inInfoList) {\n\t\t\tList<LogcatInfo> outInfoList = new ArrayList<>();\n\t\t\tinInfoList.forEach((inInfo) -> {\n\t\t\t\tif (doFilter(inInfo)) {\n\t\t\t\t\toutInfoList.add(inInfo);\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn outInfoList;\n\t\t}\n\t}\n\n\tpublic class LogcatInfo {\n\t\tprivate String msg;\n\t\tprivate final byte msgType;\n\t\tprivate final int nsec;\n\t\tprivate final int pid;\n\t\tprivate final int sec;\n\t\tprivate final int tid;\n\t\tprivate final short hdrSize;\n\t\tprivate final short len;\n\t\tprivate final short version;\n\t\tprivate int lid;\n\t\tprivate int uid;\n\n\t\tpublic LogcatInfo(short len, short hdrSize, int pid, int tid, int sec, int nsec, byte msgType) {\n\t\t\tthis.hdrSize = hdrSize;\n\t\t\tthis.len = len;\n\t\t\tthis.msgType = msgType;\n\t\t\tthis.nsec = nsec;\n\t\t\tthis.pid = pid;\n\t\t\tthis.sec = sec;\n\t\t\tthis.tid = tid;\n\t\t\tthis.version = 1;\n\t\t}\n\n\t\t// Version 2 and 3 both have the same arguments\n\t\tpublic LogcatInfo(short len, short hdrSize, int pid, int tid, int sec, int nsec, int lid, byte msgType) {\n\t\t\tthis.hdrSize = hdrSize;\n\t\t\tthis.len = len;\n\t\t\tthis.lid = lid;\n\t\t\tthis.msgType = msgType;\n\t\t\tthis.nsec = nsec;\n\t\t\tthis.pid = pid;\n\t\t\tthis.sec = sec;\n\t\t\tthis.tid = tid;\n\t\t\tthis.version = 3;\n\t\t}\n\n\t\tpublic LogcatInfo(short len, short hdrSize, int pid, int tid, int sec, int nsec, int lid, int uid, byte msgType) {\n\t\t\tthis.hdrSize = hdrSize;\n\t\t\tthis.len = len;\n\t\t\tthis.lid = lid;\n\t\t\tthis.msgType = msgType;\n\t\t\tthis.nsec = nsec;\n\t\t\tthis.pid = pid;\n\t\t\tthis.sec = sec;\n\t\t\tthis.tid = tid;\n\t\t\tthis.uid = uid;\n\t\t\tthis.version = 4;\n\t\t}\n\n\t\tpublic void setMsg(byte[] msg) {\n\t\t\tthis.msg = new String(msg);\n\t\t}\n\n\t\tpublic short getVersion() {\n\t\t\treturn this.version;\n\t\t}\n\n\t\tpublic short getLen() {\n\t\t\treturn this.len;\n\t\t}\n\n\t\tpublic short getHeaderLen() {\n\t\t\treturn this.hdrSize;\n\t\t}\n\n\t\tpublic int getPid() {\n\t\t\treturn this.pid;\n\t\t}\n\n\t\tpublic int getTid() {\n\t\t\treturn this.tid;\n\t\t}\n\n\t\tpublic int getSec() {\n\t\t\treturn this.sec;\n\t\t}\n\n\t\tpublic int getNSec() {\n\t\t\treturn this.nsec;\n\t\t}\n\n\t\tpublic int getLid() {\n\t\t\treturn this.lid;\n\t\t}\n\n\t\tpublic int getUid() {\n\t\t\treturn this.uid;\n\t\t}\n\n\t\tpublic Instant getInstant() {\n\t\t\treturn Instant.ofEpochSecond(getSec(), getNSec());\n\t\t}\n\n\t\tpublic String getTimestamp() {\n\t\t\tDateTimeFormatter dtFormat = DateTimeFormatter.ofPattern(\"MM-dd HH:mm:ss.SSS\").withZone(ZoneId.of(timezone));\n\t\t\treturn dtFormat.format(getInstant());\n\t\t}\n\n\t\tpublic String getAfterTimestamp() {\n\t\t\tDateTimeFormatter dtFormat = DateTimeFormatter.ofPattern(\"MM-dd HH:mm:ss.SSS\").withZone(ZoneId.of(timezone));\n\t\t\treturn dtFormat.format(getInstant().plusMillis(1));\n\t\t}\n\n\t\tpublic byte getMsgType() {\n\t\t\treturn this.msgType;\n\t\t}\n\n\t\tpublic String getMsgTypeString() {\n\t\t\tswitch (getMsgType()) {\n\t\t\t\tcase 0:\n\t\t\t\t\treturn \"Unknown\";\n\t\t\t\tcase 1:\n\t\t\t\t\treturn \"Default\";\n\t\t\t\tcase 2:\n\t\t\t\t\treturn \"Verbose\";\n\t\t\t\tcase 3:\n\t\t\t\t\treturn \"Debug\";\n\t\t\t\tcase 4:\n\t\t\t\t\treturn \"Info\";\n\t\t\t\tcase 5:\n\t\t\t\t\treturn \"Warn\";\n\t\t\t\tcase 6:\n\t\t\t\t\treturn \"Error\";\n\t\t\t\tcase 7:\n\t\t\t\t\treturn \"Fatal\";\n\t\t\t\tcase 8:\n\t\t\t\t\treturn \"Silent\";\n\t\t\t\tdefault:\n\t\t\t\t\treturn \"Unknown\";\n\t\t\t}\n\t\t}\n\n\t\tpublic String getMsg() {\n\t\t\treturn this.msg;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/RegisterObserver.java",
    "content": "package jadx.gui.device.debugger;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.gui.device.debugger.SmaliDebugger.RuntimeVarInfo;\nimport jadx.gui.device.debugger.smali.SmaliRegister;\n\npublic class RegisterObserver {\n\n\tprivate Map<Long, List<Info>> infoMap;\n\tprivate final List<SmaliRegisterMapping> regList;\n\tprivate final ArtAdapter.IArtAdapter art;\n\tprivate final String mthFullID;\n\tprivate boolean hasDbgInfo = false;\n\n\tprivate RegisterObserver(ArtAdapter.IArtAdapter art, String mthFullID) {\n\t\tthis.regList = new ArrayList<>();\n\t\tthis.infoMap = Collections.emptyMap();\n\t\tthis.art = art;\n\t\tthis.mthFullID = mthFullID;\n\t}\n\n\t@NotNull\n\tpublic static RegisterObserver merge(List<RuntimeVarInfo> rtRegs, List<SmaliRegister> smaliRegs, ArtAdapter.IArtAdapter art,\n\t\t\tString mthFullID) {\n\t\tRegisterObserver adapter = new RegisterObserver(art, mthFullID);\n\t\tadapter.hasDbgInfo = !rtRegs.isEmpty();\n\t\tif (adapter.hasDbgInfo) {\n\t\t\tadapter.infoMap = new HashMap<>();\n\t\t}\n\t\tfor (SmaliRegister sr : smaliRegs) {\n\t\t\tadapter.regList.add(new SmaliRegisterMapping(sr));\n\t\t}\n\t\tadapter.regList.sort(Comparator.comparingInt(r -> r.getSmaliRegister().getRuntimeRegNum()));\n\t\tfor (RuntimeVarInfo rt : rtRegs) {\n\t\t\tfinal SmaliRegisterMapping smaliRegMapping = adapter.getRegListEntry(rt.getRegNum());\n\t\t\tfinal SmaliRegister smaliReg = smaliRegMapping.getSmaliRegister();\n\t\t\tsmaliRegMapping.addRuntimeVarInfo(rt);\n\n\t\t\tString type = rt.getSignature();\n\t\t\tif (type.isEmpty()) {\n\t\t\t\ttype = rt.getType();\n\t\t\t}\n\t\t\tArgType at = ArgType.parse(type);\n\t\t\tif (at != null) {\n\t\t\t\ttype = at.toString();\n\t\t\t}\n\t\t\tInfo load = new Info(smaliReg.getRegNum(), true, rt.getName(), type);\n\t\t\tInfo unload = new Info(smaliReg.getRegNum(), false, null, null);\n\t\t\tadapter.infoMap.computeIfAbsent((long) rt.getStartOffset(), k -> new ArrayList<>())\n\t\t\t\t\t.add(load);\n\t\t\tadapter.infoMap.computeIfAbsent((long) rt.getEndOffset(), k -> new ArrayList<>())\n\t\t\t\t\t.add(unload);\n\t\t}\n\t\treturn adapter;\n\t}\n\n\t@NotNull\n\tpublic List<SmaliRegister> getInitializedList(long codeOffset) {\n\t\tList<SmaliRegister> ret = Collections.emptyList();\n\t\tfor (SmaliRegisterMapping smaliRegisterMapping : regList) {\n\t\t\tif (smaliRegisterMapping.getSmaliRegister().isInitialized(codeOffset)) {\n\t\t\t\tif (ret.isEmpty()) {\n\t\t\t\t\tret = new ArrayList<>();\n\t\t\t\t}\n\t\t\t\tret.add(smaliRegisterMapping.getSmaliRegister());\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n\n\t@Nullable\n\tpublic RuntimeVarInfo getInfo(int runtimeNum, long codeOffset) {\n\t\tSmaliRegisterMapping list = getRegListEntry(runtimeNum);\n\t\tfor (RuntimeVarInfo info : list.getRuntimeVarInfoList()) {\n\t\t\tif (info.getStartOffset() > codeOffset) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (info.isInitialized(codeOffset)) {\n\t\t\t\treturn info;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate SmaliRegisterMapping getRegListEntry(int regNum) {\n\t\ttry {\n\t\t\treturn regList.get(regNum);\n\t\t} catch (IndexOutOfBoundsException e) {\n\t\t\tthrow new RuntimeException(\n\t\t\t\t\tString.format(\"Register %d does not exist (size: %d).\\n %s\\n Method: %s\",\n\t\t\t\t\t\t\tregNum, regList.size(), buildDeviceInfo(), mthFullID),\n\t\t\t\t\te);\n\t\t}\n\t}\n\n\tprivate String buildDeviceInfo() {\n\t\tDebugSettings debugSettings = DebugSettings.INSTANCE;\n\t\treturn \"Device: \" + debugSettings.getDevice().getDeviceInfo()\n\t\t\t\t+ \", Android: \" + debugSettings.getVer()\n\t\t\t\t+ \", ArtAdapter: \" + art.getClass().getSimpleName();\n\t}\n\n\t@NotNull\n\tpublic List<Info> getInfoAt(long codeOffset) {\n\t\tif (hasDbgInfo) {\n\t\t\tList<Info> list = infoMap.get(codeOffset);\n\t\t\tif (list != null) {\n\t\t\t\treturn list;\n\t\t\t}\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\tpublic static class SmaliRegisterMapping {\n\t\tprivate final SmaliRegister smaliRegister;\n\n\t\tprivate List<RuntimeVarInfo> rtList = Collections.emptyList();\n\n\t\tpublic SmaliRegisterMapping(SmaliRegister smaliRegister) {\n\t\t\tthis.smaliRegister = smaliRegister;\n\t\t}\n\n\t\tpublic SmaliRegister getSmaliRegister() {\n\t\t\treturn smaliRegister;\n\t\t}\n\n\t\t@NotNull\n\t\tpublic List<RuntimeVarInfo> getRuntimeVarInfoList() {\n\t\t\treturn rtList;\n\t\t}\n\n\t\tpublic void addRuntimeVarInfo(RuntimeVarInfo rt) {\n\t\t\tif (rtList.isEmpty()) {\n\t\t\t\trtList = new ArrayList<>();\n\t\t\t}\n\t\t\trtList.add(rt);\n\t\t}\n\t}\n\n\tpublic static class Info {\n\t\tprivate final int smaliRegNum;\n\t\tprivate final boolean load;\n\t\tprivate final String name;\n\t\tprivate final String type;\n\n\t\tprivate Info(int smaliRegNum, boolean load, String name, String type) {\n\t\t\tthis.smaliRegNum = smaliRegNum;\n\t\t\tthis.load = load;\n\t\t\tthis.name = name;\n\t\t\tthis.type = type;\n\t\t}\n\n\t\tpublic int getSmaliRegNum() {\n\t\t\treturn smaliRegNum;\n\t\t}\n\n\t\tpublic boolean isLoad() {\n\t\t\treturn load;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\n\t\tpublic String getType() {\n\t\t\treturn type;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/RuntimeType.java",
    "content": "package jadx.gui.device.debugger;\n\nimport io.github.skylot.jdwp.JDWP;\n\npublic enum RuntimeType {\n\tARRAY(91, \"[]\"),\n\tBYTE(66, \"byte\"),\n\tCHAR(67, \"char\"),\n\tOBJECT(76, \"object\"),\n\tFLOAT(70, \"float\"),\n\tDOUBLE(68, \"double\"),\n\tINT(73, \"int\"),\n\tLONG(74, \"long\"),\n\tSHORT(83, \"short\"),\n\tVOID(86, \"void\"),\n\tBOOLEAN(90, \"boolean\"),\n\tSTRING(115, \"string\"),\n\tTHREAD(116, \"thread\"),\n\tTHREAD_GROUP(103, \"thread_group\"),\n\tCLASS_LOADER(108, \"class_loader\"),\n\tCLASS_OBJECT(99, \"class_object\");\n\n\tprivate final int jdwpTag;\n\tprivate final String desc;\n\n\tRuntimeType(int tag, String desc) {\n\t\tthis.jdwpTag = tag;\n\t\tthis.desc = desc;\n\t}\n\n\tpublic int getTag() {\n\t\treturn jdwpTag;\n\t}\n\n\tpublic String getDesc() {\n\t\treturn this.desc;\n\t}\n\n\t/**\n\t * Converts a <code>JDWP.Tag</code> to a {@link RuntimeType}\n\t *\n\t * @param tag\n\t * @return\n\t * @throws SmaliDebuggerException\n\t */\n\tpublic static RuntimeType fromJdwpTag(int tag) throws SmaliDebuggerException {\n\t\tswitch (tag) {\n\t\t\tcase JDWP.Tag.ARRAY:\n\t\t\t\treturn RuntimeType.ARRAY;\n\t\t\tcase JDWP.Tag.BYTE:\n\t\t\t\treturn RuntimeType.BYTE;\n\t\t\tcase JDWP.Tag.CHAR:\n\t\t\t\treturn RuntimeType.CHAR;\n\t\t\tcase JDWP.Tag.OBJECT:\n\t\t\t\treturn RuntimeType.OBJECT;\n\t\t\tcase JDWP.Tag.FLOAT:\n\t\t\t\treturn RuntimeType.FLOAT;\n\t\t\tcase JDWP.Tag.DOUBLE:\n\t\t\t\treturn RuntimeType.DOUBLE;\n\t\t\tcase JDWP.Tag.INT:\n\t\t\t\treturn RuntimeType.INT;\n\t\t\tcase JDWP.Tag.LONG:\n\t\t\t\treturn RuntimeType.LONG;\n\t\t\tcase JDWP.Tag.SHORT:\n\t\t\t\treturn RuntimeType.SHORT;\n\t\t\tcase JDWP.Tag.VOID:\n\t\t\t\treturn RuntimeType.VOID;\n\t\t\tcase JDWP.Tag.BOOLEAN:\n\t\t\t\treturn RuntimeType.BOOLEAN;\n\t\t\tcase JDWP.Tag.STRING:\n\t\t\t\treturn RuntimeType.STRING;\n\t\t\tcase JDWP.Tag.THREAD:\n\t\t\t\treturn RuntimeType.THREAD;\n\t\t\tcase JDWP.Tag.THREAD_GROUP:\n\t\t\t\treturn RuntimeType.THREAD_GROUP;\n\t\t\tcase JDWP.Tag.CLASS_LOADER:\n\t\t\t\treturn RuntimeType.CLASS_LOADER;\n\t\t\tcase JDWP.Tag.CLASS_OBJECT:\n\t\t\t\treturn RuntimeType.CLASS_OBJECT;\n\t\t\tdefault:\n\t\t\t\tthrow new SmaliDebuggerException(\"Unexpected value: \" + tag);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/SmaliDebugger.java",
    "content": "package jadx.gui.device.debugger;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.Socket;\nimport java.util.AbstractMap.SimpleEntry;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.SynchronousQueue;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.github.skylot.jdwp.JDWP;\nimport io.github.skylot.jdwp.JDWP.ArrayReference.Length.LengthReplyData;\nimport io.github.skylot.jdwp.JDWP.ByteBuffer;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.BreakpointEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.ClassPrepareEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.ClassUnloadEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.EventData;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.ExceptionEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.FieldAccessEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.FieldModificationEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.MethodEntryEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.MethodExitEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.MethodExitWithReturnValueEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.MonitorContendedEnterEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.MonitorContendedEnteredEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.MonitorWaitEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.MonitorWaitedEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.SingleStepEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.ThreadDeathEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.ThreadStartEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.VMDeathEvent;\nimport io.github.skylot.jdwp.JDWP.Event.Composite.VMStartEvent;\nimport io.github.skylot.jdwp.JDWP.EventRequest.Set.ClassMatchRequest;\nimport io.github.skylot.jdwp.JDWP.EventRequest.Set.CountRequest;\nimport io.github.skylot.jdwp.JDWP.EventRequest.Set.LocationOnlyRequest;\nimport io.github.skylot.jdwp.JDWP.EventRequest.Set.StepRequest;\nimport io.github.skylot.jdwp.JDWP.Method.VariableTableWithGeneric.VarTableWithGenericData;\nimport io.github.skylot.jdwp.JDWP.Method.VariableTableWithGeneric.VarWithGenericSlot;\nimport io.github.skylot.jdwp.JDWP.ObjectReference;\nimport io.github.skylot.jdwp.JDWP.ObjectReference.ReferenceType.ReferenceTypeReplyData;\nimport io.github.skylot.jdwp.JDWP.ObjectReference.SetValues.FieldValueSetter;\nimport io.github.skylot.jdwp.JDWP.Packet;\nimport io.github.skylot.jdwp.JDWP.ReferenceType.FieldsWithGeneric.FieldsWithGenericData;\nimport io.github.skylot.jdwp.JDWP.ReferenceType.FieldsWithGeneric.FieldsWithGenericReplyData;\nimport io.github.skylot.jdwp.JDWP.ReferenceType.MethodsWithGeneric.MethodsWithGenericData;\nimport io.github.skylot.jdwp.JDWP.ReferenceType.MethodsWithGeneric.MethodsWithGenericReplyData;\nimport io.github.skylot.jdwp.JDWP.ReferenceType.Signature.SignatureReplyData;\nimport io.github.skylot.jdwp.JDWP.StackFrame.GetValues.GetValuesReplyData;\nimport io.github.skylot.jdwp.JDWP.StackFrame.GetValues.GetValuesSlots;\nimport io.github.skylot.jdwp.JDWP.StackFrame.SetValues.SlotValueSetter;\nimport io.github.skylot.jdwp.JDWP.StackFrame.ThisObject.ThisObjectReplyData;\nimport io.github.skylot.jdwp.JDWP.StringReference.Value.ValueReplyData;\nimport io.github.skylot.jdwp.JDWP.ThreadReference.Frames.FramesReplyData;\nimport io.github.skylot.jdwp.JDWP.ThreadReference.Frames.FramesReplyDataFrames;\nimport io.github.skylot.jdwp.JDWP.ThreadReference.Name.NameReplyData;\nimport io.github.skylot.jdwp.JDWP.VirtualMachine.AllClassesWithGeneric.AllClassesWithGenericData;\nimport io.github.skylot.jdwp.JDWP.VirtualMachine.AllClassesWithGeneric.AllClassesWithGenericReplyData;\nimport io.github.skylot.jdwp.JDWP.VirtualMachine.AllThreads.AllThreadsReplyData;\nimport io.github.skylot.jdwp.JDWP.VirtualMachine.AllThreads.AllThreadsReplyDataThreads;\nimport io.github.skylot.jdwp.JDWP.VirtualMachine.CreateString.CreateStringReplyData;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.gui.device.debugger.smali.RegisterInfo;\nimport jadx.gui.utils.IOUtils;\nimport jadx.gui.utils.ObjectPool;\n\n// TODO: Finish error notification, inner errors should be logged let user notice.\n\npublic class SmaliDebugger {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SmaliDebugger.class);\n\tprivate final JDWP jdwp;\n\tprivate final int localTcpPort;\n\tprivate final InputStream inputStream;\n\tprivate final OutputStream outputStream;\n\n\t// All event callbacks will be called in this queue, e.g. class prepare/unload\n\tprivate static final Executor EVENT_LISTENER_QUEUE = Executors.newSingleThreadExecutor();\n\n\t// Handle callbacks of single step, breakpoint and watchpoint\n\tprivate static final Executor SUSPEND_LISTENER_QUEUE = Executors.newSingleThreadExecutor();\n\n\tprivate final Map<Integer, ICommandResult> callbackMap = new ConcurrentHashMap<>();\n\tprivate final Map<Integer, EventListenerAdapter> eventListenerMap = new ConcurrentHashMap<>();\n\n\tprivate final Map<String, AllClassesWithGenericData> classMap = new ConcurrentHashMap<>();\n\tprivate final Map<Long, AllClassesWithGenericData> classIDMap = new ConcurrentHashMap<>();\n\tprivate final Map<Long, List<MethodsWithGenericData>> clsMethodMap = new ConcurrentHashMap<>();\n\tprivate final Map<Long, List<FieldsWithGenericData>> clsFieldMap = new ConcurrentHashMap<>();\n\tprivate Map<Long, Map<Long, RuntimeDebugInfo>> varMap = Collections.emptyMap(); // cls id: <mth id: var table>\n\n\tprivate final CountRequest oneOffEventReq;\n\tprivate final AtomicInteger idGenerator = new AtomicInteger(1);\n\n\tprivate final SuspendInfo suspendInfo = new SuspendInfo();\n\tprivate final SuspendListener suspendListener;\n\n\tprivate ObjectPool<List<GetValuesSlots>> slotsPool;\n\tprivate ObjectPool<List<JDWP.EventRequestEncoder>> stepReqPool;\n\tprivate ObjectPool<SynchronousQueue<Packet>> syncQueuePool;\n\tprivate ObjectPool<List<Long>> fieldIdPool;\n\tprivate final Map<Integer, Thread> syncQueueMap = new ConcurrentHashMap<>();\n\tprivate final AtomicInteger syncQueueID = new AtomicInteger(0);\n\n\tprivate static final ICommandResult SKIP_RESULT = res -> {\n\t};\n\n\tprivate SmaliDebugger(SuspendListener suspendListener, int localTcpPort, JDWP jdwp, InputStream inputStream,\n\t\t\tOutputStream outputStream) {\n\t\tthis.jdwp = jdwp;\n\t\tthis.localTcpPort = localTcpPort;\n\t\tthis.suspendListener = suspendListener;\n\t\tthis.inputStream = inputStream;\n\t\tthis.outputStream = outputStream;\n\n\t\toneOffEventReq = jdwp.eventRequest().cmdSet().newCountRequest();\n\t\toneOffEventReq.count = 1;\n\t}\n\n\t/**\n\t * After a successful attach the remote app will be suspended, so we have times to\n\t * set breakpoints or do any other things, after that call resume() to activate the app.\n\t */\n\tpublic static SmaliDebugger attach(String host, int port, SuspendListener suspendListener) throws SmaliDebuggerException {\n\t\ttry {\n\t\t\tbyte[] bytes = JDWP.IDSizes.encode().getBytes();\n\t\t\tJDWP.setPacketID(bytes, 1);\n\t\t\tLOG.debug(\"Connecting to ADB {}:{}\", host, port);\n\t\t\tSocket socket = new Socket(host, port);\n\t\t\tInputStream inputStream = socket.getInputStream();\n\t\t\tOutputStream outputStream = socket.getOutputStream();\n\n\t\t\tsocket.setSoTimeout(5000);\n\t\t\tJDWP jdwp = initJDWP(outputStream, inputStream);\n\t\t\tsocket.setSoTimeout(0); // set back to 0 so the decodingLoop won't break for timeout.\n\n\t\t\tSmaliDebugger debugger = new SmaliDebugger(suspendListener, port, jdwp, inputStream, outputStream);\n\n\t\t\tdebugger.decodingLoop();\n\t\t\tdebugger.listenClassUnloadEvent();\n\t\t\tdebugger.initPools();\n\t\t\treturn debugger;\n\t\t} catch (IOException e) {\n\t\t\tthrow new SmaliDebuggerException(\"Attach failed\", e);\n\t\t}\n\t}\n\n\tprivate void onSuspended(long thread, long clazz, long mth, long offset) {\n\t\tsuspendInfo.update()\n\t\t\t\t.updateThread(thread)\n\t\t\t\t.updateClass(clazz)\n\t\t\t\t.updateMethod(mth)\n\t\t\t\t.updateOffset(offset);\n\t\tif (suspendInfo.isAnythingChanged()) {\n\t\t\tSUSPEND_LISTENER_QUEUE.execute(() -> suspendListener.onSuspendEvent(suspendInfo));\n\t\t}\n\t}\n\n\tpublic void stepInto() throws SmaliDebuggerException {\n\t\tsendStepRequest(suspendInfo.getThreadID(), JDWP.StepDepth.INTO);\n\t}\n\n\tpublic void stepOver() throws SmaliDebuggerException {\n\t\tsendStepRequest(suspendInfo.getThreadID(), JDWP.StepDepth.OVER);\n\t}\n\n\tpublic void stepOut() throws SmaliDebuggerException {\n\t\tsendStepRequest(suspendInfo.getThreadID(), JDWP.StepDepth.OUT);\n\t}\n\n\tpublic void exit() throws SmaliDebuggerException {\n\t\tPacket res = sendCommandSync(jdwp.virtualMachine().cmdExit().encode(-1));\n\t\ttryThrowError(res);\n\t}\n\n\tpublic void detach() throws SmaliDebuggerException {\n\t\tPacket res = sendCommandSync(jdwp.virtualMachine().cmdDispose().encode());\n\t\ttryThrowError(res);\n\t}\n\n\tprivate void initPools() {\n\t\tslotsPool = new ObjectPool<>(() -> {\n\t\t\tList<GetValuesSlots> slots = new ArrayList<>(1);\n\t\t\tGetValuesSlots slot = jdwp.stackFrame().cmdGetValues().newValuesSlots();\n\t\t\tslot.slot = 0;\n\t\t\tslot.sigbyte = JDWP.Tag.OBJECT;\n\t\t\tslots.add(slot);\n\t\t\treturn slots;\n\t\t});\n\t\tstepReqPool = new ObjectPool<>(() -> {\n\t\t\tList<JDWP.EventRequestEncoder> eventEncoders = new ArrayList<>(2);\n\t\t\teventEncoders.add(jdwp.eventRequest().cmdSet().newStepRequest());\n\t\t\teventEncoders.add(oneOffEventReq);\n\t\t\treturn eventEncoders;\n\t\t});\n\t\tsyncQueuePool = new ObjectPool<>(SynchronousQueue::new);\n\t\tfieldIdPool = new ObjectPool<>(() -> {\n\t\t\tList<Long> ids = new ArrayList<>(1);\n\t\t\tids.add((long) -1);\n\t\t\treturn ids;\n\t\t});\n\t}\n\n\t/**\n\t * @param regNum If it's an argument, just pass its index, non-static method should be index + 1.\n\t *               e.g. void a(int b, int c), you want the value of b, then should pass 1 (0 + 1),\n\t *               this is a virtual method, so 0 is for the this object and 1 is the real index of b.\n\t *               <p>\n\t *               If it's a variable then should be the reg number + number of arguments and + 1\n\t *               if it's in a non-static method.\n\t *               e.g. to get the value of v3 in a virtual method with 2 arguments, should pass\n\t *               6 (3 + 2 + 1 = 6).\n\t */\n\tpublic RuntimeRegister getRegisterSync(long threadID, long frameID, int regNum, RuntimeType type) throws SmaliDebuggerException {\n\t\tList<GetValuesSlots> slots = slotsPool.get();\n\t\tGetValuesSlots slot = slots.get(0);\n\t\tslot.slot = regNum;\n\t\tslot.sigbyte = (byte) type.getTag();\n\t\tPacket res = sendCommandSync(jdwp.stackFrame().cmdGetValues().encode(threadID, frameID, slots));\n\t\ttryThrowError(res);\n\t\tslotsPool.put(slots);\n\t\tGetValuesReplyData val = jdwp.stackFrame().cmdGetValues().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\treturn buildRegister(regNum, val.values.get(0).slotValue.tag, val.values.get(0).slotValue.idOrValue);\n\t}\n\n\tpublic long getThisID(long threadID, long frameID) throws SmaliDebuggerException {\n\t\tPacket res = sendCommandSync(jdwp.stackFrame().cmdThisObject().encode(threadID, frameID));\n\t\ttryThrowError(res);\n\t\tThisObjectReplyData data = jdwp.stackFrame().cmdThisObject().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\treturn data.objectThis.objectID;\n\t}\n\n\tpublic List<RuntimeField> getAllFieldsSync(long clsID) throws SmaliDebuggerException {\n\t\treturn getAllFields(clsID);\n\t}\n\n\tpublic void getFieldValueSync(long clsID, RuntimeField fld) throws SmaliDebuggerException {\n\t\tList<RuntimeField> list = new ArrayList<>(1);\n\t\tlist.add(fld);\n\t\tgetAllFieldValuesSync(clsID, list);\n\t}\n\n\tpublic void getAllFieldValuesSync(long thisID, List<RuntimeField> flds) throws SmaliDebuggerException {\n\t\tList<Long> ids = new ArrayList<>(flds.size());\n\t\tflds.forEach(f -> ids.add(f.getFieldID()));\n\t\tPacket res = sendCommandSync(jdwp.objectReference().cmdGetValues().encode(thisID, ids));\n\t\ttryThrowError(res);\n\t\tObjectReference.GetValues.GetValuesReplyData data =\n\t\t\t\tjdwp.objectReference().cmdGetValues().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\tList<ObjectReference.GetValues.GetValuesReplyDataValues> values = data.values;\n\t\tfor (int i = 0; i < values.size(); i++) {\n\t\t\tObjectReference.GetValues.GetValuesReplyDataValues value = values.get(i);\n\t\t\tflds.get(i).setValue(value.value.idOrValue)\n\t\t\t\t\t.setType(RuntimeType.fromJdwpTag(value.value.tag));\n\t\t}\n\t}\n\n\tpublic Frame getCurrentFrame(long threadID) throws SmaliDebuggerException {\n\t\treturn getCurrentFrameInternal(threadID);\n\t}\n\n\tpublic List<Frame> getFramesSync(long threadID) throws SmaliDebuggerException {\n\t\treturn getAllFrames(threadID);\n\t}\n\n\tpublic List<Long> getAllThreadsSync() throws SmaliDebuggerException {\n\t\treturn getAllThreads();\n\t}\n\n\t@Nullable\n\tpublic String getThreadNameSync(long threadID) throws SmaliDebuggerException {\n\t\treturn sendThreadNameReq(threadID);\n\t}\n\n\t@Nullable\n\tpublic String getClassSignatureSync(long classID) throws SmaliDebuggerException {\n\t\treturn getClassSignatureInternal(classID);\n\t}\n\n\t@Nullable\n\tpublic String getMethodSignatureSync(long classID, long methodID) throws SmaliDebuggerException {\n\t\treturn getMethodSignatureInternal(classID, methodID);\n\t}\n\n\tpublic boolean errIsTypeMismatched(int errCode) {\n\t\treturn errCode == JDWP.Error.TYPE_MISMATCH;\n\t}\n\n\tpublic boolean errIsInvalidSlot(int errCode) {\n\t\treturn errCode == JDWP.Error.INVALID_SLOT;\n\t}\n\n\tpublic boolean errIsInvalidObject(int errCode) {\n\t\treturn errCode == JDWP.Error.INVALID_OBJECT;\n\t}\n\n\tprivate static class ClassListenerInfo {\n\t\tint prepareReqID;\n\t\tint unloadReqID;\n\t\tClassListener listener;\n\n\t\tvoid reset(ClassListener l) {\n\t\t\tthis.listener = l;\n\t\t\tthis.prepareReqID = -1;\n\t\t\tthis.unloadReqID = -1;\n\t\t}\n\t}\n\n\tprivate ClassListenerInfo clsListener;\n\n\t/**\n\t * Listens for class preparation and unload events.\n\t */\n\tpublic void setClassListener(ClassListener listener) throws SmaliDebuggerException {\n\t\tif (clsListener != null) {\n\t\t\tif (listener != clsListener.listener) {\n\t\t\t\tunregisterEventSync(JDWP.EventKind.CLASS_PREPARE, clsListener.prepareReqID);\n\t\t\t\tunregisterEventSync(JDWP.EventKind.CLASS_UNLOAD, clsListener.unloadReqID);\n\t\t\t}\n\t\t} else {\n\t\t\tclsListener = new ClassListenerInfo();\n\t\t}\n\t\tclsListener.reset(listener);\n\t\tregClassPrepareEvent(clsListener);\n\t\tregClassUnloadEvent(clsListener);\n\t}\n\n\tprivate void regClassUnloadEvent(ClassListenerInfo info) throws SmaliDebuggerException {\n\t\tPacket res = sendCommandSync(\n\t\t\t\tjdwp.eventRequest().cmdSet().newClassExcludeRequest((byte) JDWP.EventKind.CLASS_UNLOAD,\n\t\t\t\t\t\t(byte) JDWP.SuspendPolicy.NONE, \"java.*\"));\n\t\ttryThrowError(res);\n\t\tinfo.unloadReqID = jdwp.eventRequest().cmdSet().decodeRequestID(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\teventListenerMap.put(info.unloadReqID, new EventListenerAdapter() {\n\t\t\t@Override\n\t\t\tvoid onClassUnload(ClassUnloadEvent event) {\n\t\t\t\tinfo.listener.onUnloaded(DbgUtils.classSigToRawFullName(event.signature));\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void regClassPrepareEvent(ClassListenerInfo info) throws SmaliDebuggerException {\n\t\tPacket res = sendCommandSync(\n\t\t\t\tjdwp.eventRequest().cmdSet().newClassExcludeRequest((byte) JDWP.EventKind.CLASS_PREPARE,\n\t\t\t\t\t\t(byte) JDWP.SuspendPolicy.NONE, \"java.*\"));\n\t\ttryThrowError(res);\n\t\tinfo.prepareReqID = jdwp.eventRequest().cmdSet().decodeRequestID(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\teventListenerMap.put(info.prepareReqID, new EventListenerAdapter() {\n\t\t\t@Override\n\t\t\tvoid onClassPrepare(ClassPrepareEvent event) {\n\t\t\t\tinfo.listener.onPrepared(DbgUtils.classSigToRawFullName(event.signature), event.typeID);\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic void regClassPrepareEventForBreakpoint(String clsSig, ClassPrepareListener l) throws SmaliDebuggerException {\n\t\tPacket res = sendCommandSync(buildClassMatchReqForBreakpoint(clsSig, JDWP.EventKind.CLASS_PREPARE));\n\t\ttryThrowError(res);\n\t\tint reqID = jdwp.eventRequest().cmdSet().decodeRequestID(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\teventListenerMap.put(reqID, new EventListenerAdapter() {\n\t\t\t@Override\n\t\t\tvoid onClassPrepare(ClassPrepareEvent event) {\n\t\t\t\tEVENT_LISTENER_QUEUE.execute(() -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tl.onPrepared(event.typeID);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\teventListenerMap.remove(reqID);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tresume();\n\t\t\t\t\t\t} catch (SmaliDebuggerException e) {\n\t\t\t\t\t\t\tLOG.error(\"Resume failed\", e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic interface MethodEntryListener {\n\t\t/**\n\t\t * return true to remove\n\t\t */\n\t\tboolean entry(String mthSig);\n\t}\n\n\tpublic void regMethodEntryEventSync(String clsSig, MethodEntryListener l) throws SmaliDebuggerException {\n\t\tPacket res = sendCommandSync(\n\t\t\t\tjdwp.eventRequest().cmdSet().newClassMatchRequest((byte) JDWP.EventKind.METHOD_ENTRY,\n\t\t\t\t\t\t(byte) JDWP.SuspendPolicy.ALL, clsSig));\n\t\ttryThrowError(res);\n\t\tint reqID = jdwp.eventRequest().cmdSet().decodeRequestID(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\teventListenerMap.put(reqID, new EventListenerAdapter() {\n\t\t\t@Override\n\t\t\tvoid onMethodEntry(MethodEntryEvent event) {\n\t\t\t\tEVENT_LISTENER_QUEUE.execute(() -> {\n\t\t\t\t\tboolean removeListener = false;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tString sig = getMethodSignatureInternal(event.location.classID, event.location.methodID);\n\t\t\t\t\t\tremoveListener = l.entry(sig);\n\t\t\t\t\t\tif (removeListener) {\n\t\t\t\t\t\t\tsendCommand(jdwp.eventRequest().cmdClear().encode((byte) JDWP.EventKind.METHOD_ENTRY, reqID), SKIP_RESULT);\n\t\t\t\t\t\t\tonSuspended(event.thread, event.location.classID, event.location.methodID, -1);\n\t\t\t\t\t\t\teventListenerMap.remove(reqID);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (SmaliDebuggerException e) {\n\t\t\t\t\t\tLOG.error(\"Method entry failed\", e);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tif (!removeListener) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tresume();\n\t\t\t\t\t\t\t} catch (SmaliDebuggerException e) {\n\t\t\t\t\t\t\t\tLOG.error(\"Resume failed\", e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void unregisterEventSync(int eventKind, int reqID) throws SmaliDebuggerException {\n\t\teventListenerMap.remove(reqID);\n\t\tPacket rst = sendCommandSync(jdwp.eventRequest().cmdClear().encode((byte) eventKind, reqID));\n\t\ttryThrowError(rst);\n\t}\n\n\tpublic String readObjectSignatureSync(RuntimeValue val) throws SmaliDebuggerException {\n\t\tlong objID = readID(val);\n\t\t// get type reference by object id.\n\t\tPacket res = sendCommandSync(jdwp.objectReference().cmdReferenceType().encode(objID));\n\t\ttryThrowError(res);\n\t\tReferenceTypeReplyData data = jdwp.objectReference().cmdReferenceType().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\n\t\t// get signature by type reference id.\n\t\tres = sendCommandSync(jdwp.referenceType().cmdSignature().encode(data.typeID));\n\t\ttryThrowError(res);\n\t\tSignatureReplyData sigData = jdwp.referenceType().cmdSignature().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\treturn sigData.signature;\n\t}\n\n\tpublic String readStringSync(RuntimeValue val) throws SmaliDebuggerException {\n\t\treturn readStringSync(readID(val));\n\t}\n\n\tpublic String readStringSync(long id) throws SmaliDebuggerException {\n\t\tPacket res = sendCommandSync(jdwp.stringReference().cmdValue().encode(id));\n\t\ttryThrowError(res);\n\t\tValueReplyData strData = jdwp.stringReference().cmdValue().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\treturn strData.stringValue;\n\t}\n\n\tpublic boolean setValueSync(int runtimeRegNum, RuntimeType type, Object val, long threadID, long frameID)\n\t\t\tthrows SmaliDebuggerException {\n\t\tif (type == RuntimeType.STRING) {\n\t\t\tlong newID = createString((String) val);\n\t\t\tif (newID == -1) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tval = newID;\n\t\t\ttype = RuntimeType.OBJECT;\n\t\t}\n\t\tList<SlotValueSetter> setters = buildRegValueSetter(type.getTag(), runtimeRegNum);\n\t\tJDWP.encodeAny(setters.get(0).slotValue.idOrValue, val);\n\t\tPacket res = sendCommandSync(jdwp.stackFrame().cmdSetValues().encode(threadID, frameID, setters));\n\t\ttryThrowError(res);\n\t\treturn jdwp.stackFrame().cmdSetValues().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t}\n\n\tpublic boolean setValueSync(long objID, long fldID, RuntimeType type, Object val) throws SmaliDebuggerException {\n\t\tif (type == RuntimeType.STRING) {\n\t\t\tlong newID = createString((String) val);\n\t\t\tif (newID == -1) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tval = newID;\n\t\t}\n\t\tList<FieldValueSetter> setters = buildFieldValueSetter();\n\t\tFieldValueSetter setter = setters.get(0);\n\t\tsetter.fieldID = fldID;\n\t\tJDWP.encodeAny(setter.value.idOrValue, val);\n\t\tPacket res = sendCommandSync(jdwp.objectReference().cmdSetValues().encode(objID, setters));\n\t\ttryThrowError(res);\n\t\treturn jdwp.objectReference().cmdSetValues().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t}\n\n\tpublic void getValueSync(long objID, RuntimeField fld) throws SmaliDebuggerException {\n\t\tList<Long> ids = fieldIdPool.get();\n\t\tids.set(0, fld.getFieldID());\n\t\tPacket res = sendCommandSync(jdwp.objectReference().cmdGetValues().encode(objID, ids));\n\t\ttryThrowError(res);\n\t\tObjectReference.GetValues.GetValuesReplyData data =\n\t\t\t\tjdwp.objectReference().cmdGetValues().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\tfld.setValue(data.values.get(0).value.idOrValue)\n\t\t\t\t.setType(RuntimeType.fromJdwpTag(data.values.get(0).value.tag));\n\t}\n\n\tprivate long createString(String localStr) throws SmaliDebuggerException {\n\t\tPacket res = sendCommandSync(jdwp.virtualMachine().cmdCreateString().encode(localStr));\n\t\ttryThrowError(res);\n\t\tCreateStringReplyData id = jdwp.virtualMachine().cmdCreateString().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\treturn id.stringObject;\n\t}\n\n\tpublic long readID(RuntimeValue val) {\n\t\treturn JDWP.decodeBySize(val.getRawVal().getBytes(), 0, val.getRawVal().size());\n\t}\n\n\tpublic String readArraySignature(RuntimeValue val) throws SmaliDebuggerException {\n\t\treturn readObjectSignatureSync(val);\n\t}\n\n\tpublic int readArrayLength(RuntimeValue val) throws SmaliDebuggerException {\n\t\tPacket res = sendCommandSync(jdwp.arrayReference().cmdLength().encode(readID(val)));\n\t\ttryThrowError(res);\n\t\tLengthReplyData data = jdwp.arrayReference().cmdLength().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\treturn data.arrayLength;\n\t}\n\n\t/**\n\t * @param startIndex less than 0 means 0\n\t * @param len        less than or equals 0 means the maximum value 99 or the rest of the elements.\n\t * @return An entry, The key is the total length of this array when len is &lt;= 0, otherwise 0,\n\t *         the value, if this array is an object array then it's object ids.\n\t */\n\tpublic Entry<Integer, List<Long>> readArray(RuntimeValue reg, int startIndex, int len) throws SmaliDebuggerException {\n\t\tlong id = readID(reg);\n\t\tEntry<Integer, List<Long>> ret;\n\t\tif (len <= 0) {\n\t\t\tPacket res = sendCommandSync(jdwp.arrayReference().cmdLength().encode(id));\n\t\t\ttryThrowError(res);\n\t\t\tLengthReplyData data = jdwp.arrayReference().cmdLength().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\t\tlen = Math.min(99, data.arrayLength);\n\t\t\tret = new SimpleEntry<>(data.arrayLength, null);\n\t\t} else {\n\t\t\tret = new SimpleEntry<>(0, null);\n\t\t}\n\t\tstartIndex = Math.max(0, startIndex);\n\t\tPacket res = sendCommandSync(jdwp.arrayReference().cmdGetValues().encode(id, startIndex, len));\n\t\ttryThrowError(res);\n\t\tJDWP.ArrayReference.GetValues.GetValuesReplyData valData =\n\t\t\t\tjdwp.arrayReference().cmdGetValues().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\tret.setValue(valData.values.idOrValues);\n\t\treturn ret;\n\t}\n\n\tpublic byte readByte(RuntimeValue val) {\n\t\treturn JDWP.decodeByte(val.getRawVal().getBytes(), 0);\n\t}\n\n\tpublic char readChar(RuntimeValue val) {\n\t\treturn JDWP.decodeChar(val.getRawVal().getBytes(), 0);\n\t}\n\n\tpublic short readShort(RuntimeValue val) {\n\t\treturn JDWP.decodeShort(val.getRawVal().getBytes(), 0);\n\t}\n\n\tpublic int readInt(RuntimeValue val) {\n\t\treturn JDWP.decodeInt(val.getRawVal().getBytes(), 0);\n\t}\n\n\tpublic float readFloat(RuntimeValue val) {\n\t\treturn JDWP.decodeFloat(val.getRawVal().getBytes(), 0);\n\t}\n\n\t/**\n\t * @param val has 8 bytes mostly\n\t */\n\tpublic long readAll(RuntimeValue val) {\n\t\treturn JDWP.decodeBySize(val.getRawVal().getBytes(), 0, Math.min(val.getRawVal().size(), 8));\n\t}\n\n\tpublic double readDouble(RuntimeValue val) {\n\t\treturn JDWP.decodeDouble(val.getRawVal().getBytes(), 0);\n\t}\n\n\t@Nullable\n\tpublic RuntimeDebugInfo getRuntimeDebugInfo(long clsID, long mthID) throws SmaliDebuggerException {\n\t\tMap<Long, RuntimeDebugInfo> secMap = varMap.get(clsID);\n\t\tRuntimeDebugInfo info = null;\n\t\tif (secMap != null) {\n\t\t\tinfo = secMap.get(mthID);\n\t\t}\n\t\tif (info == null) {\n\t\t\tinfo = initDebugInfo(clsID, mthID);\n\t\t}\n\t\treturn info;\n\t}\n\n\tprivate RuntimeDebugInfo initDebugInfo(long clsID, long mthID) throws SmaliDebuggerException {\n\t\tPacket res = sendCommandSync(jdwp.method().cmdVariableTableWithGeneric.encode(clsID, mthID));\n\t\ttryThrowError(res);\n\t\tVarTableWithGenericData data = jdwp.method().cmdVariableTableWithGeneric.decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\tif (varMap == Collections.EMPTY_MAP) {\n\t\t\tvarMap = new ConcurrentHashMap<>();\n\t\t}\n\t\tRuntimeDebugInfo info = new RuntimeDebugInfo(data);\n\t\tvarMap.computeIfAbsent(clsID, k -> new HashMap<>()).put(mthID, info);\n\t\treturn info;\n\t}\n\n\tprivate static JDWP initJDWP(OutputStream outputStream, InputStream inputStream) throws SmaliDebuggerException {\n\t\ttry {\n\t\t\thandShake(outputStream, inputStream);\n\t\t\toutputStream.write(JDWP.Suspend.encode().setPacketID(1).getBytes()); // suspend all threads\n\t\t\tPacket res = readPacket(inputStream);\n\t\t\ttryThrowError(res);\n\t\t\tif (res.isReplyPacket() && res.getID() == 1) {\n\t\t\t\t// get id sizes for decoding & encoding of jdwp packets.\n\t\t\t\toutputStream.write(JDWP.IDSizes.encode().setPacketID(1).getBytes());\n\t\t\t\tres = readPacket(inputStream);\n\t\t\t\ttryThrowError(res);\n\t\t\t\tif (res.isReplyPacket() && res.getID() == 1) {\n\t\t\t\t\tJDWP.IDSizes.IDSizesReplyData sizes = JDWP.IDSizes.decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\t\t\t\treturn new JDWP(sizes);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tthrow new SmaliDebuggerException(e);\n\t\t}\n\t\tthrow new SmaliDebuggerException(\"Failed to init JDWP.\");\n\t}\n\n\tprivate static void handShake(OutputStream outputStream, InputStream inputStream) throws SmaliDebuggerException {\n\t\tbyte[] buf;\n\t\ttry {\n\t\t\toutputStream.write(JDWP.encodeHandShakePacket());\n\t\t\tbuf = IOUtils.readNBytes(inputStream, 14);\n\t\t} catch (Exception e) {\n\t\t\tthrow new SmaliDebuggerException(\"jdwp handshake failed\", e);\n\t\t}\n\t\tif (buf == null || !JDWP.decodeHandShakePacket(buf)) {\n\t\t\tthrow new SmaliDebuggerException(\"jdwp handshake bad reply\");\n\t\t}\n\t}\n\n\tprivate MethodsWithGenericData getMethodBySig(long classID, String sig) {\n\t\tList<MethodsWithGenericData> methods = clsMethodMap.get(classID);\n\t\tif (methods != null) {\n\t\t\tfor (MethodsWithGenericData method : methods) {\n\t\t\t\tif (sig.startsWith(method.name + \"(\") && sig.endsWith(method.signature)) {\n\t\t\t\t\treturn method;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate int genID() {\n\t\treturn idGenerator.getAndAdd(1);\n\t}\n\n\t/**\n\t * Read & decode packets from Socket connection\n\t */\n\tprivate void decodingLoop() {\n\t\tExecutors.newSingleThreadExecutor().execute(() -> {\n\t\t\tboolean errFromCallback;\n\t\t\twhile (true) {\n\t\t\t\terrFromCallback = false;\n\t\t\t\ttry {\n\t\t\t\t\tPacket res = readPacket(inputStream);\n\t\t\t\t\tif (res == null) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tsuspendInfo.nextRound();\n\t\t\t\t\tICommandResult callback = callbackMap.remove(res.getID());\n\t\t\t\t\tif (callback != null) {\n\t\t\t\t\t\tif (callback != SKIP_RESULT) {\n\t\t\t\t\t\t\terrFromCallback = true;\n\t\t\t\t\t\t\tcallback.onCommandReply(res);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (res.getCommandSetID() == 64 && res.getCommandID() == 100) { // command from JVM\n\t\t\t\t\t\terrFromCallback = true;\n\t\t\t\t\t\tdecodeCompositeEvents(res);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tprintUnexpectedID(res.getID());\n\t\t\t\t\t}\n\t\t\t\t} catch (SmaliDebuggerException e) {\n\t\t\t\t\tLOG.error(\"Error in debugger decoding loop\", e);\n\t\t\t\t\tif (!errFromCallback) { // fatal error\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tsuspendInfo.setTerminated();\n\t\t\tclearWaitingSyncQueue();\n\t\t\tsuspendListener.onSuspendEvent(suspendInfo);\n\t\t});\n\t}\n\n\tprivate void sendCommand(ByteBuffer buf, ICommandResult callback) throws SmaliDebuggerException {\n\t\tint id = genID();\n\t\tcallbackMap.put(id, callback);\n\t\ttry {\n\t\t\toutputStream.write(buf.setPacketID(id).getBytes());\n\t\t} catch (IOException e) {\n\t\t\tthrow new SmaliDebuggerException(e);\n\t\t}\n\t}\n\n\t/**\n\t * Do not use this method inside a ICommandResult callback, it will cause deadlock.\n\t * It should be used in a thread.\n\t */\n\tprivate Packet sendCommandSync(ByteBuffer buf) throws SmaliDebuggerException {\n\t\tSynchronousQueue<Packet> store = syncQueuePool.get();\n\t\tsendCommand(buf, res -> {\n\t\t\ttry {\n\t\t\t\tstore.put(res);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Command send failed\", e);\n\t\t\t}\n\t\t});\n\t\tInteger id = syncQueueID.getAndAdd(1);\n\t\ttry {\n\t\t\tsyncQueueMap.put(id, Thread.currentThread());\n\t\t\treturn store.take();\n\t\t} catch (InterruptedException e) {\n\t\t\tthrow new SmaliDebuggerException(e);\n\t\t} finally {\n\t\t\tsyncQueueMap.remove(id);\n\t\t\tsyncQueuePool.put(store);\n\t\t}\n\t}\n\n\t// called by decodingLoop() when fatal error occurred,\n\t// if don't do so the store.take() may block forever.\n\tprivate void clearWaitingSyncQueue() {\n\t\tsyncQueueMap.keySet().forEach(k -> {\n\t\t\tThread t = syncQueueMap.remove(k);\n\t\t\tif (t != null) {\n\t\t\t\tt.interrupt();\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void printUnexpectedID(int id) throws SmaliDebuggerException {\n\t\tthrow new SmaliDebuggerException(\"Missing handler for this id: \" + id);\n\t}\n\n\tprivate void decodeCompositeEvents(Packet res) throws SmaliDebuggerException {\n\t\tEventData data = jdwp.event().cmdComposite().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\tfor (JDWP.EventRequestDecoder event : data.events) {\n\t\t\tEventListenerAdapter listener = eventListenerMap.get(event.getRequestID());\n\t\t\tif (listener == null) {\n\t\t\t\tLOG.error(\"Missing handler for id: {}\", event.getRequestID());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (event instanceof VMStartEvent) {\n\t\t\t\tlistener.onVMStart((VMStartEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof VMDeathEvent) {\n\t\t\t\tlistener.onVMDeath((VMDeathEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof SingleStepEvent) {\n\t\t\t\tlistener.onSingleStep((SingleStepEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof BreakpointEvent) {\n\t\t\t\tlistener.onBreakpoint((BreakpointEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof MethodEntryEvent) {\n\t\t\t\tlistener.onMethodEntry((MethodEntryEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof MethodExitEvent) {\n\t\t\t\tlistener.onMethodExit((MethodExitEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof MethodExitWithReturnValueEvent) {\n\t\t\t\tlistener.onMethodExitWithReturnValue((MethodExitWithReturnValueEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof MonitorContendedEnterEvent) {\n\t\t\t\tlistener.onMonitorContendedEnter((MonitorContendedEnterEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof MonitorContendedEnteredEvent) {\n\t\t\t\tlistener.onMonitorContendedEntered((MonitorContendedEnteredEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof MonitorWaitEvent) {\n\t\t\t\tlistener.onMonitorWait((MonitorWaitEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof MonitorWaitedEvent) {\n\t\t\t\tlistener.onMonitorWaited((MonitorWaitedEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof ExceptionEvent) {\n\t\t\t\tlistener.onException((ExceptionEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof ThreadStartEvent) {\n\t\t\t\tlistener.onThreadStart((ThreadStartEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof ThreadDeathEvent) {\n\t\t\t\tlistener.onThreadDeath((ThreadDeathEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof ClassPrepareEvent) {\n\t\t\t\tlistener.onClassPrepare((ClassPrepareEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof ClassUnloadEvent) {\n\t\t\t\tlistener.onClassUnload((ClassUnloadEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof FieldAccessEvent) {\n\t\t\t\tlistener.onFieldAccess((FieldAccessEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof FieldModificationEvent) {\n\t\t\t\tlistener.onFieldModification((FieldModificationEvent) event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow new SmaliDebuggerException(\"Unexpected event: \" + event);\n\t\t}\n\t}\n\n\tprivate final EventListenerAdapter stepListener = new EventListenerAdapter() {\n\t\t@Override\n\t\tvoid onSingleStep(SingleStepEvent event) {\n\t\t\tonSuspended(event.thread,\n\t\t\t\t\tevent.location.classID,\n\t\t\t\t\tevent.location.methodID,\n\t\t\t\t\tevent.location.index);\n\t\t}\n\t};\n\n\tprivate void sendStepRequest(long threadID, int depth) throws SmaliDebuggerException {\n\t\tList<JDWP.EventRequestEncoder> stepReq = buildStepRequest(threadID, JDWP.StepSize.MIN, depth);\n\t\tByteBuffer stepEncodedBuf = jdwp.eventRequest().cmdSet().encode(\n\t\t\t\t(byte) JDWP.EventKind.SINGLE_STEP,\n\t\t\t\t(byte) JDWP.SuspendPolicy.ALL,\n\t\t\t\tstepReq);\n\t\tstepReqPool.put(stepReq);\n\t\tsendCommand(stepEncodedBuf, res -> {\n\t\t\ttryThrowError(res);\n\t\t\tint reqID = jdwp.eventRequest().cmdSet().decodeRequestID(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\t\teventListenerMap.put(reqID, stepListener);\n\t\t});\n\t\tresume();\n\t}\n\n\tpublic void resume() throws SmaliDebuggerException {\n\t\tsendCommand(JDWP.Resume.encode(), SKIP_RESULT);\n\t}\n\n\tpublic void suspend() throws SmaliDebuggerException {\n\t\tsendCommand(JDWP.Suspend.encode(), SKIP_RESULT);\n\t}\n\n\tpublic void setBreakpoint(RuntimeBreakpoint bp) throws SmaliDebuggerException {\n\t\tsendCommand(buildBreakpointRequest(bp), res -> {\n\t\t\ttryThrowError(res);\n\t\t\tbp.reqID = jdwp.eventRequest().cmdSet().decodeRequestID(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\t\teventListenerMap.put(bp.reqID, new EventListenerAdapter() {\n\t\t\t\t@Override\n\t\t\t\tvoid onBreakpoint(BreakpointEvent event) {\n\t\t\t\t\tonSuspended(event.thread,\n\t\t\t\t\t\t\tevent.location.classID,\n\t\t\t\t\t\t\tevent.location.methodID,\n\t\t\t\t\t\t\tevent.location.index);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\tpublic long getClassID(String clsSig, boolean fetch) throws SmaliDebuggerException {\n\t\tdo {\n\t\t\tAllClassesWithGenericData data = classMap.get(clsSig);\n\t\t\tif (data == null) {\n\t\t\t\tif (fetch) {\n\t\t\t\t\tgetAllClasses();\n\t\t\t\t\tfetch = false;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\treturn data.typeID;\n\t\t\t}\n\t\t} while (true);\n\t\treturn -1;\n\t}\n\n\tpublic long getMethodID(long cid, String mthSig) throws SmaliDebuggerException {\n\t\tinitClassCache(cid);\n\t\tMethodsWithGenericData data = getMethodBySig(cid, mthSig);\n\t\tif (data != null) {\n\t\t\treturn data.methodID;\n\t\t}\n\t\treturn -1;\n\t}\n\n\tpublic void initClassCache(long clsID) throws SmaliDebuggerException {\n\t\tinitFields(clsID);\n\t\tinitMethods(clsID);\n\t}\n\n\tpublic void removeBreakpoint(RuntimeBreakpoint bp) throws SmaliDebuggerException {\n\t\tsendCommand(jdwp.eventRequest().cmdClear().encode((byte) JDWP.EventKind.BREAKPOINT, bp.reqID), SKIP_RESULT);\n\t}\n\n\tprivate ByteBuffer buildBreakpointRequest(RuntimeBreakpoint bp) {\n\t\tLocationOnlyRequest req = jdwp.eventRequest().cmdSet().newLocationOnlyRequest();\n\t\treq.loc.classID = bp.clsID;\n\t\treq.loc.methodID = bp.mthID;\n\t\treq.loc.index = bp.offset;\n\t\treq.loc.tag = JDWP.TypeTag.CLASS;\n\t\tList<JDWP.EventRequestEncoder> list = new ArrayList<>(1);\n\t\tlist.add(req);\n\t\treturn jdwp.eventRequest().cmdSet().encode((byte) JDWP.EventKind.BREAKPOINT,\n\t\t\t\t(byte) JDWP.SuspendPolicy.ALL, list);\n\t}\n\n\t/**\n\t * Builds a one-off class prepare event for setting up breakpoints.\n\t */\n\tprivate ByteBuffer buildClassMatchReqForBreakpoint(String cls, int eventKind) {\n\t\tList<JDWP.EventRequestEncoder> encoders = new ArrayList<>(2);\n\t\tClassMatchRequest match = jdwp.eventRequest().cmdSet().newClassMatchRequest();\n\t\tencoders.add(match);\n\t\tencoders.add(oneOffEventReq);\n\t\tmatch.classPattern = cls;\n\t\treturn jdwp.eventRequest().cmdSet().encode((byte) eventKind,\n\t\t\t\t(byte) JDWP.SuspendPolicy.ALL, encoders);\n\t}\n\n\tprivate List<JDWP.EventRequestEncoder> buildStepRequest(long threadID, int stepSize, int stepDepth) {\n\t\tList<JDWP.EventRequestEncoder> eventEncoders = stepReqPool.get();\n\t\tStepRequest req = (StepRequest) eventEncoders.get(0);\n\t\treq.size = stepSize;\n\t\treq.depth = stepDepth;\n\t\treq.thread = threadID;\n\t\treturn eventEncoders;\n\t}\n\n\tprivate List<FieldValueSetter> buildFieldValueSetter() {\n\t\tFieldValueSetter setter = jdwp.objectReference().cmdSetValues().new FieldValueSetter();\n\t\tsetter.value = jdwp.new UntaggedValuePacket();\n\t\tsetter.value.idOrValue = new ByteBuffer();\n\t\tList<FieldValueSetter> setters = new ArrayList<>(1);\n\t\tsetters.add(setter);\n\t\treturn setters;\n\t}\n\n\tprivate List<SlotValueSetter> buildRegValueSetter(int tag, int regNum) {\n\t\tList<SlotValueSetter> setters = new ArrayList<>(1);\n\t\tSlotValueSetter setter = jdwp.stackFrame().cmdSetValues().new SlotValueSetter();\n\t\tsetters.add(setter);\n\t\tsetter.slot = regNum;\n\t\tsetter.slotValue = jdwp.new ValuePacket();\n\t\tsetter.slotValue.tag = tag;\n\t\tsetter.slotValue.idOrValue = new ByteBuffer();\n\t\treturn setters;\n\t}\n\n\tprivate String getClassSignatureInternal(long id) throws SmaliDebuggerException {\n\t\tAllClassesWithGenericData data = classIDMap.get(id);\n\t\tif (data == null) {\n\t\t\tgetAllClasses();\n\t\t}\n\t\tdata = classIDMap.get(id);\n\t\tif (data != null) {\n\t\t\treturn data.signature;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String getMethodSignatureInternal(long clsID, long mthID) throws SmaliDebuggerException {\n\t\tList<MethodsWithGenericData> mthData = clsMethodMap.get(clsID);\n\t\tif (mthData == null) {\n\t\t\tPacket res = sendCommandSync(jdwp.referenceType().cmdMethodsWithGeneric().encode(clsID));\n\t\t\ttryThrowError(res);\n\t\t\tMethodsWithGenericReplyData data =\n\t\t\t\t\tjdwp.referenceType().cmdMethodsWithGeneric().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\t\tmthData = data.declared;\n\t\t\tclsMethodMap.put(clsID, mthData);\n\t\t}\n\t\tif (mthData != null) {\n\t\t\tfor (MethodsWithGenericData data : mthData) {\n\t\t\t\tif (data.methodID == mthID) {\n\t\t\t\t\treturn data.name + data.signature;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String sendThreadNameReq(long id) throws SmaliDebuggerException {\n\t\tPacket res = sendCommandSync(jdwp.threadReference().cmdName().encode(id));\n\t\ttryThrowError(res);\n\t\tNameReplyData nameData = jdwp.threadReference().cmdName().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\treturn nameData.threadName;\n\t}\n\n\tprivate List<RuntimeField> getAllFields(long clsID) throws SmaliDebuggerException {\n\t\tinitFields(clsID);\n\t\tList<FieldsWithGenericData> flds = clsFieldMap.get(clsID);\n\t\tif (flds != null && flds.size() > 0) {\n\t\t\tList<RuntimeField> rfs = new ArrayList<>(flds.size());\n\t\t\tfor (FieldsWithGenericData fld : flds) {\n\t\t\t\tString type = fld.signature;\n\t\t\t\tif (fld.genericSignature != null && !fld.genericSignature.trim().isEmpty()) {\n\t\t\t\t\ttype += \"<\" + fld.genericSignature + \">\";\n\t\t\t\t}\n\t\t\t\trfs.add(new RuntimeField(fld.name, type, fld.fieldID, fld.modBits));\n\t\t\t}\n\t\t\treturn rfs;\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\tpublic Frame getCurrentFrameInternal(long threadID) throws SmaliDebuggerException {\n\t\tPacket res = sendCommandSync(jdwp.threadReference().cmdFrames().encode(threadID, 0, 1));\n\t\ttryThrowError(res);\n\t\tFramesReplyData frameData = jdwp.threadReference().cmdFrames().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\tFramesReplyDataFrames frame = frameData.frames.get(0);\n\t\treturn new Frame(frame.frameID, frame.location.classID, frame.location.methodID,\n\t\t\t\tframe.location.index);\n\t}\n\n\tprivate List<Frame> getAllFrames(long threadID) throws SmaliDebuggerException {\n\t\tPacket res = sendCommandSync(jdwp.threadReference().cmdFrames().encode(threadID, 0, -1));\n\t\ttryThrowError(res);\n\t\tFramesReplyData frameData = jdwp.threadReference().cmdFrames().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\tList<Frame> frames = new ArrayList<>();\n\t\tfor (FramesReplyDataFrames frame : frameData.frames) {\n\t\t\tframes.add(new Frame(frame.frameID, frame.location.classID,\n\t\t\t\t\tframe.location.methodID, frame.location.index));\n\t\t}\n\t\treturn frames;\n\t}\n\n\tprivate List<Long> getAllThreads() throws SmaliDebuggerException {\n\t\tPacket res = sendCommandSync(jdwp.virtualMachine().cmdAllThreads().encode());\n\t\ttryThrowError(res);\n\t\tAllThreadsReplyData data;\n\t\tdata = jdwp.virtualMachine().cmdAllThreads().decode(res.getBuf(),\n\t\t\t\tJDWP.PACKET_HEADER_SIZE);\n\t\tList<Long> threads = new ArrayList<>(data.threads.size());\n\t\tfor (AllThreadsReplyDataThreads thread : data.threads) {\n\t\t\tthreads.add(thread.thread);\n\t\t}\n\t\treturn threads;\n\t}\n\n\tprivate void getAllClasses() throws SmaliDebuggerException {\n\t\tPacket res = sendCommandSync(jdwp.virtualMachine().cmdAllClassesWithGeneric().encode());\n\t\ttryThrowError(res);\n\t\tAllClassesWithGenericReplyData classData =\n\t\t\t\tjdwp.virtualMachine().cmdAllClassesWithGeneric().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\tfor (AllClassesWithGenericData aClass : classData.classes) {\n\t\t\tclassMap.put(DbgUtils.classSigToRawFullName(aClass.signature), aClass);\n\t\t\tclassIDMap.put(aClass.typeID, aClass);\n\t\t}\n\t}\n\n\tprivate void initFields(long clsID) throws SmaliDebuggerException {\n\t\tif (clsFieldMap.get(clsID) == null) {\n\t\t\tPacket res = sendCommandSync(jdwp.referenceType().cmdFieldsWithGeneric().encode(clsID));\n\t\t\ttryThrowError(res);\n\t\t\tFieldsWithGenericReplyData data =\n\t\t\t\t\tjdwp.referenceType().cmdFieldsWithGeneric().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\t\tclsFieldMap.put(clsID, data.declared);\n\t\t}\n\t}\n\n\tprivate void initMethods(long clsID) throws SmaliDebuggerException {\n\t\tif (clsMethodMap.get(clsID) == null) {\n\t\t\tPacket res = sendCommandSync(jdwp.referenceType().cmdMethodsWithGeneric().encode(clsID));\n\t\t\ttryThrowError(res);\n\t\t\tMethodsWithGenericReplyData data =\n\t\t\t\t\tjdwp.referenceType().cmdMethodsWithGeneric().decode(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\t\tclsMethodMap.put(clsID, data.declared);\n\t\t}\n\t}\n\n\t/**\n\t * Removes class cache when it's unloaded from JVM.\n\t */\n\tprivate void listenClassUnloadEvent() throws SmaliDebuggerException {\n\t\tsendCommand(\n\t\t\t\tjdwp.eventRequest().cmdSet().encode((byte) JDWP.EventKind.CLASS_UNLOAD,\n\t\t\t\t\t\t(byte) JDWP.SuspendPolicy.NONE, Collections.emptyList()),\n\t\t\t\tres -> {\n\t\t\t\t\tint reqID = jdwp.eventRequest().cmdSet().decodeRequestID(res.getBuf(), JDWP.PACKET_HEADER_SIZE);\n\t\t\t\t\teventListenerMap.put(reqID, new EventListenerAdapter() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tvoid onClassUnload(ClassUnloadEvent event) {\n\t\t\t\t\t\t\tEVENT_LISTENER_QUEUE.execute(() -> {\n\t\t\t\t\t\t\t\tSystem.out.printf(\"ClassUnloaded: %s%n\", event.signature);\n\t\t\t\t\t\t\t\tAllClassesWithGenericData clsData = classMap.remove(event.signature);\n\t\t\t\t\t\t\t\tif (clsData != null) {\n\t\t\t\t\t\t\t\t\tclassIDMap.remove(clsData.typeID);\n\t\t\t\t\t\t\t\t\tclsFieldMap.remove(clsData.typeID);\n\t\t\t\t\t\t\t\t\tclsMethodMap.remove(clsData.typeID);\n\t\t\t\t\t\t\t\t\tvarMap.remove(clsData.typeID);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t});\n\t}\n\n\t/**\n\t * Reads a JDWP packet.\n\t */\n\t@Nullable\n\tprivate static Packet readPacket(InputStream inputStream) throws SmaliDebuggerException {\n\t\ttry {\n\t\t\tbyte[] header = IOUtils.readNBytes(inputStream, JDWP.PACKET_HEADER_SIZE);\n\t\t\tif (header == null) {\n\t\t\t\t// stream ended\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tint bodyLength = JDWP.getPacketLength(header, 0) - JDWP.PACKET_HEADER_SIZE;\n\t\t\tif (bodyLength <= 0) {\n\t\t\t\treturn Packet.make(header);\n\t\t\t}\n\t\t\tbyte[] body = IOUtils.readNBytes(inputStream, bodyLength);\n\t\t\tif (body == null) {\n\t\t\t\tthrow new SmaliDebuggerException(\"Stream truncated\");\n\t\t\t}\n\t\t\treturn Packet.make(concatBytes(header, body));\n\t\t} catch (IOException e) {\n\t\t\tthrow new SmaliDebuggerException(\"Read packer error\", e);\n\t\t}\n\t}\n\n\tprivate static byte[] concatBytes(byte[] buf1, byte[] buf2) {\n\t\tbyte[] tempBuf = new byte[buf1.length + buf2.length];\n\t\tSystem.arraycopy(buf1, 0, tempBuf, 0, buf1.length);\n\t\tSystem.arraycopy(buf2, 0, tempBuf, buf1.length, buf2.length);\n\t\treturn tempBuf;\n\t}\n\n\tprivate static void tryThrowError(@Nullable Packet res) throws SmaliDebuggerException {\n\t\tif (res == null) {\n\t\t\tthrow new SmaliDebuggerException(\"Stream ended\");\n\t\t}\n\t\tif (res.isError()) {\n\t\t\tthrow new SmaliDebuggerException(\"(JDWP Error Code:\" + res.getErrorCode() + \") \"\n\t\t\t\t\t+ res.getErrorText(), res.getErrorCode());\n\t\t}\n\t}\n\n\tprivate interface ICommandResult {\n\t\tvoid onCommandReply(Packet res) throws SmaliDebuggerException;\n\t}\n\n\tpublic static class RuntimeField extends RuntimeValue {\n\t\tprivate final String name;\n\t\tprivate final String fldType;\n\t\tprivate final long fieldID;\n\t\tprivate final int modBits;\n\n\t\tprivate RuntimeField(String name, String type, long fieldID, int modBits) {\n\t\t\tsuper(null, null);\n\t\t\tthis.name = name;\n\t\t\tthis.fldType = type;\n\t\t\tthis.fieldID = fieldID;\n\t\t\tthis.modBits = modBits;\n\t\t}\n\n\t\tpublic String getFieldType() {\n\t\t\treturn fldType;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\n\t\tpublic long getFieldID() {\n\t\t\treturn fieldID;\n\t\t}\n\n\t\tprivate RuntimeField setValue(ByteBuffer rawVal) {\n\t\t\tsuper.rawVal = rawVal;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic boolean isBelongToThis() {\n\t\t\treturn !AccessFlags.hasFlag(modBits, AccessFlags.STATIC)\n\t\t\t\t\t&& !AccessFlags.hasFlag(modBits, AccessFlags.SYNTHETIC);\n\t\t}\n\t}\n\n\tpublic static class RuntimeBreakpoint {\n\t\tprivate long clsID;\n\t\tprivate long mthID;\n\t\tprivate long offset;\n\t\tprivate int reqID;\n\n\t\tpublic long getCodeOffset() {\n\t\t\treturn offset;\n\t\t}\n\t}\n\n\tpublic RuntimeBreakpoint makeBreakpoint(long cid, long mid, long offset) {\n\t\tRuntimeBreakpoint bp = new RuntimeBreakpoint();\n\t\tbp.clsID = cid;\n\t\tbp.mthID = mid;\n\t\tbp.offset = offset;\n\t\treturn bp;\n\t}\n\n\tprivate RuntimeRegister buildRegister(int num, int tag, ByteBuffer buf) throws SmaliDebuggerException {\n\t\treturn new RuntimeRegister(num, RuntimeType.fromJdwpTag(tag), buf);\n\t}\n\n\tpublic static class RuntimeValue {\n\t\tprotected ByteBuffer rawVal;\n\t\tprotected RuntimeType type;\n\n\t\tRuntimeValue(RuntimeType type, ByteBuffer rawVal) {\n\t\t\tthis.rawVal = rawVal;\n\t\t\tthis.type = type;\n\t\t}\n\n\t\tpublic RuntimeType getType() {\n\t\t\treturn type;\n\t\t}\n\n\t\tpublic void setType(RuntimeType type) {\n\t\t\tthis.type = type;\n\t\t}\n\n\t\tprivate ByteBuffer getRawVal() {\n\t\t\treturn rawVal;\n\t\t}\n\t}\n\n\tpublic static class RuntimeRegister extends RuntimeValue {\n\t\tprivate final int num;\n\n\t\tprivate RuntimeRegister(int num, RuntimeType type, ByteBuffer rawVal) {\n\t\t\tsuper(type, rawVal);\n\t\t\tthis.num = num;\n\t\t}\n\n\t\tpublic int getRegNum() {\n\t\t\treturn num;\n\t\t}\n\t}\n\n\tpublic static class RuntimeVarInfo extends RegisterInfo {\n\t\tprivate final VarWithGenericSlot slot;\n\n\t\tprivate RuntimeVarInfo(VarWithGenericSlot slot) {\n\t\t\tthis.slot = slot;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn slot.name;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getRegNum() {\n\t\t\treturn slot.slot;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getType() {\n\t\t\tString gen = getSignature();\n\t\t\tif (gen == null || gen.isEmpty()) {\n\t\t\t\treturn this.slot.signature;\n\t\t\t}\n\t\t\treturn gen;\n\t\t}\n\n\t\t@NotNull\n\t\t@Override\n\t\tpublic String getSignature() {\n\t\t\treturn this.slot.genericSignature.trim();\n\t\t}\n\n\t\t@Override\n\t\tpublic int getStartOffset() {\n\t\t\treturn (int) slot.codeIndex;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getEndOffset() {\n\t\t\treturn (int) (slot.codeIndex + slot.length);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isMarkedAsParameter() {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic static class RuntimeDebugInfo {\n\t\tprivate final List<RuntimeVarInfo> infoList;\n\n\t\tprivate RuntimeDebugInfo(VarTableWithGenericData data) {\n\t\t\tinfoList = new ArrayList<>(data.slots.size());\n\t\t\tfor (VarWithGenericSlot slot : data.slots) {\n\t\t\t\tinfoList.add(new RuntimeVarInfo(slot));\n\t\t\t}\n\t\t}\n\n\t\tpublic List<RuntimeVarInfo> getInfoList() {\n\t\t\treturn infoList;\n\t\t}\n\t}\n\n\tpublic static class Frame {\n\t\tprivate final long id;\n\t\tprivate final long clsID;\n\t\tprivate final long mthID;\n\t\tprivate final long index;\n\n\t\tprivate Frame(long id, long clsID, long mthID, long index) {\n\t\t\tthis.id = id;\n\t\t\tthis.clsID = clsID;\n\t\t\tthis.mthID = mthID;\n\t\t\tthis.index = index;\n\t\t}\n\n\t\tpublic long getID() {\n\t\t\treturn id;\n\t\t}\n\n\t\tpublic long getClassID() {\n\t\t\treturn clsID;\n\t\t}\n\n\t\tpublic long getMethodID() {\n\t\t\treturn mthID;\n\t\t}\n\n\t\tpublic long getCodeIndex() {\n\t\t\treturn index;\n\t\t}\n\t}\n\n\tpublic interface ClassPrepareListener {\n\t\tvoid onPrepared(long id);\n\t}\n\n\tpublic interface ClassListener {\n\t\tvoid onPrepared(String cls, long id);\n\n\t\tvoid onUnloaded(String cls);\n\t}\n\n\t/**\n\t * Listener for breakpoint, watch, step, etc.\n\t */\n\tpublic interface SuspendListener {\n\t\t/**\n\t\t * For step, breakpoint, watchpoint, and any other events that suspend the JVM.\n\t\t * This method will be called in stateListenQueue.\n\t\t */\n\t\tvoid onSuspendEvent(SuspendInfo current);\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/SmaliDebuggerException.java",
    "content": "package jadx.gui.device.debugger;\n\npublic class SmaliDebuggerException extends Exception {\n\tprivate final int errCode;\n\tprivate static final long serialVersionUID = -1111111202102191403L;\n\n\tpublic SmaliDebuggerException(Exception e) {\n\t\tsuper(e);\n\t\terrCode = -1;\n\t}\n\n\tpublic SmaliDebuggerException(String msg) {\n\t\tsuper(msg);\n\t\tthis.errCode = -1;\n\t}\n\n\tpublic SmaliDebuggerException(String msg, Exception e) {\n\t\tsuper(msg, e);\n\t\terrCode = -1;\n\t}\n\n\tpublic SmaliDebuggerException(String msg, int errCode) {\n\t\tsuper(msg);\n\t\tthis.errCode = errCode;\n\t}\n\n\tpublic int getErrCode() {\n\t\treturn errCode;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/SuspendInfo.java",
    "content": "package jadx.gui.device.debugger;\n\npublic class SuspendInfo {\n\tprivate boolean terminated;\n\tprivate boolean newRound;\n\tprivate final InfoSetter updater = new InfoSetter();\n\n\tpublic long getThreadID() {\n\t\treturn updater.thread;\n\t}\n\n\tpublic long getClassID() {\n\t\treturn updater.clazz;\n\t}\n\n\tpublic long getMethodID() {\n\t\treturn updater.method;\n\t}\n\n\tpublic long getOffset() {\n\t\treturn updater.offset;\n\t}\n\n\tInfoSetter update() {\n\t\tupdater.changed = false;\n\t\tupdater.nextRound(newRound);\n\t\tthis.newRound = false;\n\t\treturn updater;\n\t}\n\n\t// called by decodingLoop, to tell the updater even though the values are the same,\n\t// they are decoded from another packet, they should be treated as new.\n\tvoid nextRound() {\n\t\tnewRound = true;\n\t}\n\n\t// according to JDWP document it's legal to fire two or more events on a same location,\n\t// e.g. one for single step and the other for breakpoint, so when this happened we only\n\t// want one of them.\n\tboolean isAnythingChanged() {\n\t\treturn updater.changed;\n\t}\n\n\tpublic boolean isTerminated() {\n\t\treturn terminated;\n\t}\n\n\tvoid setTerminated() {\n\t\tterminated = true;\n\t}\n\n\tstatic class InfoSetter {\n\t\tprivate long thread;\n\t\tprivate long clazz;\n\t\tprivate long method;\n\t\tprivate long offset; // code offset;\n\t\tprivate boolean changed;\n\n\t\tvoid nextRound(boolean newRound) {\n\t\t\tif (!changed) {\n\t\t\t\tchanged = newRound;\n\t\t\t}\n\t\t}\n\n\t\tInfoSetter updateThread(long thread) {\n\t\t\tif (!changed) {\n\t\t\t\tchanged = this.thread != thread;\n\t\t\t}\n\t\t\tthis.thread = thread;\n\t\t\treturn this;\n\t\t}\n\n\t\tInfoSetter updateClass(long clazz) {\n\t\t\tif (!changed) {\n\t\t\t\tchanged = this.clazz != clazz;\n\t\t\t}\n\t\t\tthis.clazz = clazz;\n\t\t\treturn this;\n\t\t}\n\n\t\tInfoSetter updateMethod(long method) {\n\t\t\tif (!changed) {\n\t\t\t\tchanged = this.method != method;\n\t\t\t}\n\t\t\tthis.method = method;\n\t\t\treturn this;\n\t\t}\n\n\t\tInfoSetter updateOffset(long offset) {\n\t\t\tif (!changed) {\n\t\t\t\tchanged = this.offset != offset;\n\t\t\t}\n\t\t\tthis.offset = offset;\n\t\t\treturn this;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/smali/RegisterInfo.java",
    "content": "package jadx.gui.device.debugger.smali;\n\nimport jadx.api.plugins.input.data.ILocalVar;\n\npublic abstract class RegisterInfo implements ILocalVar {\n\n\tpublic boolean isInitialized(long codeOffset) {\n\t\treturn codeOffset >= getStartOffset() && codeOffset < getEndOffset();\n\t}\n\n\tpublic boolean isUnInitialized(long codeOffset) {\n\t\treturn codeOffset < getStartOffset() || codeOffset >= getEndOffset();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/smali/Smali.java",
    "content": "package jadx.gui.device.debugger.smali;\n\nimport java.util.AbstractMap.SimpleEntry;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.JadxArgs;\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.api.plugins.input.data.AccessFlagsScope;\nimport jadx.api.plugins.input.data.ICatch;\nimport jadx.api.plugins.input.data.IClassData;\nimport jadx.api.plugins.input.data.ICodeReader;\nimport jadx.api.plugins.input.data.IDebugInfo;\nimport jadx.api.plugins.input.data.IFieldData;\nimport jadx.api.plugins.input.data.ILocalVar;\nimport jadx.api.plugins.input.data.IMethodData;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.input.data.ITry;\nimport jadx.api.plugins.input.data.annotations.AnnotationVisibility;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationsAttr;\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.api.plugins.input.insns.InsnIndexType;\nimport jadx.api.plugins.input.insns.Opcode;\nimport jadx.api.plugins.input.insns.custom.ISwitchPayload;\nimport jadx.core.dex.attributes.AttributeStorage;\nimport jadx.core.dex.instructions.IndexInsnNode;\nimport jadx.core.dex.instructions.InsnDecoder;\nimport jadx.core.dex.instructions.InsnType;\nimport jadx.core.dex.instructions.InvokeNode;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.InsnNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\nimport static jadx.api.plugins.input.data.AccessFlagsScope.FIELD;\nimport static jadx.api.plugins.input.data.AccessFlagsScope.METHOD;\nimport static jadx.api.plugins.input.insns.Opcode.CONST;\nimport static jadx.api.plugins.input.insns.Opcode.CONST_METHOD_HANDLE;\nimport static jadx.api.plugins.input.insns.Opcode.CONST_METHOD_TYPE;\nimport static jadx.api.plugins.input.insns.Opcode.CONST_WIDE;\nimport static jadx.api.plugins.input.insns.Opcode.FILLED_NEW_ARRAY;\nimport static jadx.api.plugins.input.insns.Opcode.FILLED_NEW_ARRAY_RANGE;\nimport static jadx.api.plugins.input.insns.Opcode.FILL_ARRAY_DATA_PAYLOAD;\nimport static jadx.api.plugins.input.insns.Opcode.INVOKE_CUSTOM;\nimport static jadx.api.plugins.input.insns.Opcode.INVOKE_CUSTOM_RANGE;\nimport static jadx.api.plugins.input.insns.Opcode.INVOKE_POLYMORPHIC;\nimport static jadx.api.plugins.input.insns.Opcode.INVOKE_POLYMORPHIC_RANGE;\nimport static jadx.api.plugins.input.insns.Opcode.PACKED_SWITCH;\nimport static jadx.api.plugins.input.insns.Opcode.PACKED_SWITCH_PAYLOAD;\nimport static jadx.api.plugins.input.insns.Opcode.SPARSE_SWITCH;\nimport static jadx.api.plugins.input.insns.Opcode.SPARSE_SWITCH_PAYLOAD;\n\npublic class Smali {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(Smali.class);\n\n\tprivate static SmaliInsnDecoder insnDecoder = null;\n\n\tprivate ICodeInfo codeInfo;\n\tprivate final Map<String, SmaliMethodNode> insnMap = new HashMap<>(); // fullRawId of method as key\n\n\tprivate final boolean printFileOffset = true;\n\tprivate final boolean printBytecode = true;\n\n\tprivate boolean isJavaBytecode;\n\n\tprivate Smali() {\n\t}\n\n\tpublic static Smali disassemble(ClassNode cls) {\n\t\tcls = cls.getTopParentClass();\n\t\tSmaliWriter code = new SmaliWriter(cls);\n\t\tSmali smali = new Smali();\n\t\tsmali.isJavaBytecode = cls.getInputFileName().endsWith(\".class\"); // TODO: add flag to api\n\t\tsmali.writeClass(code, cls);\n\t\tsmali.codeInfo = code.finish();\n\t\treturn smali;\n\t}\n\n\tpublic String getCode() {\n\t\treturn codeInfo.getCodeStr();\n\t}\n\n\tpublic int getMethodDefPos(String mthFullRawID) {\n\t\tSmaliMethodNode info = insnMap.get(mthFullRawID);\n\t\tif (info != null) {\n\t\t\treturn info.getDefPos();\n\t\t}\n\t\treturn -1;\n\t}\n\n\t@Nullable\n\tpublic SmaliMethodNode getMethodNode(String mthFullRawID) {\n\t\treturn insnMap.get(mthFullRawID);\n\t}\n\n\tpublic int getRegCount(String mthFullRawID) {\n\t\tSmaliMethodNode info = insnMap.get(mthFullRawID);\n\t\tif (info != null) {\n\t\t\treturn info.getRegCount();\n\t\t}\n\t\treturn -1;\n\t}\n\n\tpublic int getParamRegStart(String mthFullRawID) {\n\t\tSmaliMethodNode info = insnMap.get(mthFullRawID);\n\t\tif (info != null) {\n\t\t\treturn info.getParamRegStart();\n\t\t}\n\t\treturn -1;\n\t}\n\n\tpublic int getInsnPosByCodeOffset(String mthFullRawID, long codeOffset) {\n\t\tSmaliMethodNode info = insnMap.get(mthFullRawID);\n\t\tif (info != null) {\n\t\t\treturn info.getInsnPos(codeOffset);\n\t\t}\n\t\treturn -1;\n\t}\n\n\t@Nullable\n\tpublic Entry<String, Integer> getMthFullIDAndCodeOffsetByLine(int line) {\n\t\tfor (Entry<String, SmaliMethodNode> entry : insnMap.entrySet()) {\n\t\t\tInteger codeOffset = entry.getValue().getLineMapping().get(line);\n\t\t\tif (codeOffset != null) {\n\t\t\t\treturn new SimpleEntry<>(entry.getKey(), codeOffset);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic List<SmaliRegister> getRegisterList(String mthFullRawID) {\n\t\tSmaliMethodNode node = insnMap.get(mthFullRawID);\n\t\tif (node != null) {\n\t\t\treturn node.getRegList();\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\t/**\n\t * @return null for no result, FieldInfo for field, Integer for register.\n\t */\n\t@Nullable\n\tpublic Object getResultRegOrField(String mthFullRawID, long codeOffset) {\n\t\tSmaliMethodNode info = insnMap.get(mthFullRawID);\n\t\tif (info != null) {\n\t\t\tInsnNode insn = info.getInsnNode(codeOffset);\n\t\t\tif (insn != null) {\n\t\t\t\tif (insn.getType() == InsnType.IPUT) {\n\t\t\t\t\treturn ((IndexInsnNode) insn).getIndex();\n\t\t\t\t}\n\t\t\t\tif (insn.getType() == InsnType.INVOKE) {\n\t\t\t\t\tif (insn instanceof InvokeNode) {\n\t\t\t\t\t\tif (insn.getArgsCount() > 0) {\n\t\t\t\t\t\t\treturn ((RegisterArg) insn.getArg(0)).getRegNum();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tRegisterArg regArg = insn.getResult();\n\t\t\t\tif (regArg != null) {\n\t\t\t\t\treturn regArg.getRegNum();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate void writeClass(SmaliWriter smali, ClassNode cls) {\n\t\tIClassData clsData = cls.getClsData();\n\t\tif (clsData == null) {\n\t\t\tsmali.startLine(String.format(\"###### Class %s is created by jadx\", cls.getFullName()));\n\t\t\treturn;\n\t\t}\n\t\tAttributeStorage clsAttributes = AttributeStorage.fromList(clsData.getAttributes());\n\t\tsmali.startLine(\"Class: \" + clsData.getType())\n\t\t\t\t.startLine(\"AccessFlags: \" + AccessFlags.format(clsData.getAccessFlags(), AccessFlagsScope.CLASS))\n\t\t\t\t.startLine(\"SuperType: \" + clsData.getSuperType())\n\t\t\t\t.startLine(\"Interfaces: \" + clsData.getInterfacesTypes())\n\t\t\t\t.startLine(\"SourceFile: \" + clsAttributes.get(JadxAttrType.SOURCE_FILE));\n\n\t\tAnnotationsAttr annotationsAttr = clsAttributes.get(JadxAttrType.ANNOTATION_LIST);\n\t\tif (annotationsAttr != null) {\n\t\t\tCollection<IAnnotation> annos = annotationsAttr.getList();\n\t\t\tif (!annos.isEmpty()) {\n\t\t\t\tsmali.startLine(String.format(\"# %d annotations\", annos.size()));\n\t\t\t\twriteAnnotations(smali, new ArrayList<>(annos));\n\t\t\t\tsmali.startLine();\n\t\t\t}\n\t\t}\n\n\t\tList<RawField> fields = new ArrayList<>();\n\t\tint[] colWidths = new int[] { 0, 0 }; // first is access flag, second is name\n\t\tint[] mthIndex = new int[] { 0 };\n\t\tLineInfo line = new LineInfo();\n\t\tclsData.visitFieldsAndMethods(\n\t\t\t\tf -> {\n\t\t\t\t\tRawField fld = RawField.make(f);\n\t\t\t\t\tfields.add(fld);\n\t\t\t\t\tif (fld.accessFlag.length() > colWidths[0]) {\n\t\t\t\t\t\tcolWidths[0] = fld.accessFlag.length();\n\t\t\t\t\t}\n\t\t\t\t\tif (fld.name.length() > colWidths[1]) {\n\t\t\t\t\t\tcolWidths[1] = fld.name.length();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tm -> {\n\t\t\t\t\tif (!fields.isEmpty()) {\n\t\t\t\t\t\twriteFields(smali, clsData, fields, colWidths);\n\t\t\t\t\t\tfields.clear();\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\twriteMethod(smali, cls.getMethods().get(mthIndex[0]++), m, line);\n\t\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\t\tIMethodRef methodRef = m.getMethodRef();\n\t\t\t\t\t\tString mthFullName = methodRef.getParentClassType() + \"->\" + methodRef.getName();\n\t\t\t\t\t\tsmali.setIndent(0);\n\t\t\t\t\t\tsmali.startLine(\"Failed to write method: \" + mthFullName + \"\\n\" + Utils.getStackTrace(e));\n\t\t\t\t\t\tLOG.error(\"Failed to write smali code for method: {}\", mthFullName, e);\n\t\t\t\t\t}\n\t\t\t\t\tline.reset();\n\t\t\t\t});\n\n\t\tif (!fields.isEmpty()) { // in case there's no methods.\n\t\t\twriteFields(smali, clsData, fields, colWidths);\n\t\t}\n\t\tfor (ClassNode innerClass : cls.getInnerClasses()) {\n\t\t\twriteClass(smali, innerClass);\n\t\t}\n\t}\n\n\tprivate void writeFields(SmaliWriter smali, IClassData classData, List<RawField> fields, int[] colWidths) {\n\t\tint staticIdx = 0;\n\t\tsmali.startLine().startLine(\"# fields\");\n\t\tString whites = new String(new byte[Math.max(colWidths[0], colWidths[1])]).replace(\"\\0\", \" \");\n\t\tfor (RawField fld : fields) {\n\t\t\tsmali.startLine();\n\t\t\tint pad = colWidths[0] - fld.accessFlag.length();\n\t\t\tif (pad > 0) {\n\t\t\t\tfld.accessFlag += whites.substring(0, pad);\n\t\t\t}\n\t\t\tsmali.add(\".field \").add(fld.accessFlag);\n\t\t\tpad = colWidths[1] - fld.name.length();\n\t\t\tif (pad > 0) {\n\t\t\t\tfld.name += whites.substring(0, pad);\n\t\t\t}\n\t\t\tsmali.add(fld.name).add(\" \");\n\t\t\tsmali.add(\": \").add(fld.type);\n\t\t\tif (fld.isStatic) {\n\t\t\t\tEncodedValue constVal = fld.attributes.get(JadxAttrType.CONSTANT_VALUE);\n\t\t\t\tif (constVal != null) {\n\t\t\t\t\tsmali.add(\" # init val = \");\n\t\t\t\t\twriteEncodedValue(smali, constVal, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\tAnnotationsAttr annotationsAttr = fld.attributes.get(JadxAttrType.ANNOTATION_LIST);\n\t\t\tif (annotationsAttr != null) {\n\t\t\t\tsmali.incIndent();\n\t\t\t\twriteAnnotations(smali, annotationsAttr.getList());\n\t\t\t\tsmali.decIndent();\n\t\t\t}\n\t\t}\n\t\tsmali.startLine();\n\t}\n\n\tprivate void writeMethod(SmaliWriter smali, MethodNode methodNode, IMethodData mth, LineInfo line) {\n\t\tif (insnDecoder == null) {\n\t\t\tinsnDecoder = new SmaliInsnDecoder(methodNode);\n\t\t}\n\t\tsmali.startLine().startLine(\".method \");\n\t\twriteMethodDef(smali, mth, line);\n\t\tICodeReader codeReader = mth.getCodeReader();\n\t\tif (codeReader != null) {\n\t\t\tint regsCount = codeReader.getRegistersCount();\n\t\t\tline.smaliMthNode.setParamRegStart(getParamStartRegNum(mth));\n\t\t\tline.smaliMthNode.setRegCount(regsCount);\n\t\t\tMap<Long, InsnNode> nodes = new HashMap<>(codeReader.getUnitsCount() / 2);\n\t\t\tline.smaliMthNode.setInsnNodes(nodes, codeReader.getUnitsCount());\n\t\t\tline.smaliMthNode.initRegInfoList(regsCount, codeReader.getUnitsCount());\n\n\t\t\tsmali.incIndent();\n\t\t\tsmali.startLine(\".registers \").add(Integer.toString(regsCount));\n\n\t\t\twriteTries(codeReader, line);\n\t\t\tIDebugInfo debugInfo = codeReader.getDebugInfo();\n\t\t\tList<ILocalVar> localVars = debugInfo != null ? debugInfo.getLocalVars() : Collections.emptyList();\n\t\t\tformatMthParamInfo(mth, smali, line, regsCount, localVars);\n\t\t\tif (debugInfo != null) {\n\t\t\t\tformatDbgInfo(debugInfo, localVars, line);\n\t\t\t}\n\t\t\tsmali.newLine();\n\t\t\tsmali.startLine();\n\t\t\t// first pass to fill payload offsets for switch instructions\n\t\t\tcodeReader.visitInstructions(insn -> {\n\t\t\t\tOpcode opcode = insn.getOpcode();\n\t\t\t\tif (opcode == PACKED_SWITCH || opcode == SPARSE_SWITCH) {\n\t\t\t\t\tinsn.decode();\n\t\t\t\t\tline.addPayloadOffset(insn.getOffset(), insn.getTarget());\n\t\t\t\t}\n\t\t\t});\n\t\t\tcodeReader.visitInstructions(insn -> {\n\t\t\t\tInsnNode node = decodeInsn(insn, line);\n\t\t\t\tnodes.put((long) insn.getOffset(), node);\n\t\t\t});\n\t\t\tline.write(smali);\n\t\t\tinsnMap.put(methodNode.getMethodInfo().getRawFullId(), line.smaliMthNode);\n\n\t\t\tsmali.decIndent();\n\t\t}\n\t\tsmali.startLine(\".end method\");\n\t}\n\n\tprivate void writeTries(ICodeReader codeReader, LineInfo line) {\n\t\tList<ITry> tries = codeReader.getTries();\n\t\tfor (ITry aTry : tries) {\n\t\t\tint end = aTry.getEndOffset();\n\t\t\tString tryEndTip = String.format(FMT_TRY_END_TAG, end);\n\t\t\tString tryStartTip = String.format(FMT_TRY_TAG, aTry.getStartOffset());\n\t\t\tString tryStartTipExtra = \" # :\" + tryStartTip.substring(0, tryStartTip.length() - 1);\n\n\t\t\tline.addTip(aTry.getStartOffset(), tryStartTip, \" # :\" + tryEndTip.substring(0, tryEndTip.length() - 1));\n\t\t\tline.addTip(end, tryEndTip, tryStartTipExtra);\n\n\t\t\tICatch iCatch = aTry.getCatch();\n\t\t\tint[] addresses = iCatch.getHandlers();\n\t\t\tint addr;\n\t\t\tfor (int i = 0; i < addresses.length; i++) {\n\t\t\t\taddr = addresses[i];\n\t\t\t\tString catchTip = String.format(FMT_CATCH_TAG, addr);\n\t\t\t\tline.addTip(addr, catchTip, \" # \" + iCatch.getTypes()[i]);\n\t\t\t\tline.addTip(addr, catchTip, tryStartTipExtra);\n\t\t\t\tline.addTip(aTry.getStartOffset(), tryStartTip, \" # :\" + catchTip.substring(0, catchTip.length() - 1));\n\t\t\t}\n\t\t\taddr = iCatch.getCatchAllHandler();\n\t\t\tif (addr > -1) {\n\t\t\t\tString catchAllTip = String.format(FMT_CATCH_ALL_TAG, addr);\n\t\t\t\tline.addTip(addr, catchAllTip, tryStartTipExtra);\n\t\t\t\tline.addTip(aTry.getStartOffset(), tryStartTip, \" # :\" + catchAllTip.substring(0, catchAllTip.length() - 1));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate InsnNode decodeInsn(InsnData insn, LineInfo lineInfo) {\n\t\tinsn.decode();\n\t\tInsnNode node = insnDecoder.decode(insn);\n\t\tformatInsn(insn, node, lineInfo);\n\t\treturn node;\n\t}\n\n\tprivate void formatInsn(InsnData insn, InsnNode node, LineInfo line) {\n\t\tStringBuilder lw = line.getLineWriter();\n\t\tlw.delete(0, lw.length());\n\t\tfmtCols(insn, line);\n\t\tif (fmtPayloadInsn(insn, line)) {\n\t\t\treturn;\n\t\t}\n\t\tlw.append(formatInsnName(insn)).append(\" \");\n\t\tfmtRegs(insn, node.getType(), line);\n\t\tif (!tryFormatTargetIns(insn, node.getType(), line)) {\n\t\t\tif (hasLiteral(insn)) {\n\t\t\t\tlw.append(\", \").append(literal(insn));\n\t\t\t} else if (node.getType() == InsnType.INVOKE) {\n\t\t\t\tlw.append(\", \").append(method(insn));\n\t\t\t} else if (insn.getIndexType() == InsnIndexType.FIELD_REF) {\n\t\t\t\tlw.append(\", \").append(field(insn));\n\t\t\t} else if (insn.getIndexType() == InsnIndexType.STRING_REF) {\n\t\t\t\tlw.append(\", \").append(str(insn));\n\t\t\t} else if (insn.getIndexType() == InsnIndexType.TYPE_REF) {\n\t\t\t\tlw.append(\", \").append(type(insn));\n\t\t\t} else if (insn.getOpcode() == CONST_METHOD_HANDLE) {\n\t\t\t\tlw.append(\", \").append(methodHandle(insn));\n\t\t\t} else if (insn.getOpcode() == CONST_METHOD_TYPE) {\n\t\t\t\tlw.append(\", \").append(proto(insn, insn.getIndex()));\n\t\t\t}\n\t\t}\n\t\tline.addInsnLine(insn.getOffset(), lw.toString());\n\t}\n\n\tprivate String formatInsnName(InsnData insn) {\n\t\tif (isJavaBytecode) {\n\t\t\t// add api opcode, because registers not used\n\t\t\treturn String.format(\"%-\" + INSN_COL_WIDTH + \"s | %-15s\",\n\t\t\t\t\tinsn.getOpcodeMnemonic(), insn.getOpcode().name().toLowerCase(Locale.ROOT).replace('_', '-'));\n\t\t}\n\t\treturn String.format(FMT_INSN_COL, insn.getOpcodeMnemonic());\n\t}\n\n\tprivate boolean tryFormatTargetIns(InsnData insn, InsnType insnType, LineInfo line) {\n\t\tswitch (insnType) {\n\t\t\tcase IF: {\n\t\t\t\tint target = insn.getTarget();\n\t\t\t\tline.addTip(target, String.format(FMT_COND_TAG, target), \"\");\n\t\t\t\tline.getLineWriter().append(\", \").append(String.format(FMT_COND, target));\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase GOTO: {\n\t\t\t\tint target = insn.getTarget();\n\t\t\t\tline.addTip(target, String.format(FMT_GOTO_TAG, target), \"\");\n\t\t\t\tline.getLineWriter().append(String.format(FMT_GOTO, target));\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase FILL_ARRAY: {\n\t\t\t\tint target = insn.getTarget();\n\t\t\t\tline.addTip(target, String.format(FMT_DATA_TAG, target), \"\");\n\t\t\t\tline.getLineWriter().append(\", \").append(String.format(FMT_DATA, target));\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase SWITCH: {\n\t\t\t\tint target = insn.getTarget();\n\t\t\t\tif (insn.getOpcode() == Opcode.PACKED_SWITCH) {\n\t\t\t\t\tline.addTip(target, String.format(FMT_P_SWITCH_TAG, target), \"\");\n\t\t\t\t\tline.getLineWriter().append(\", \").append(String.format(FMT_P_SWITCH, target));\n\t\t\t\t} else {\n\t\t\t\t\tline.addTip(target, String.format(FMT_S_SWITCH_TAG, target), \"\");\n\t\t\t\t\tline.getLineWriter().append(\", \").append(String.format(FMT_S_SWITCH, target));\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static boolean hasStaticFlag(int flag) {\n\t\treturn (flag & AccessFlags.STATIC) != 0;\n\t}\n\n\tprivate void writeMethodDef(SmaliWriter smali, IMethodData mth, LineInfo lineInfo) {\n\t\tsmali.add(AccessFlags.format(mth.getAccessFlags(), METHOD));\n\n\t\tIMethodRef methodRef = mth.getMethodRef();\n\t\tmethodRef.load();\n\t\tlineInfo.smaliMthNode.setDefPos(smali.getLength());\n\t\tsmali.add(methodRef.getName())\n\t\t\t\t.add('(');\n\t\tmethodRef.getArgTypes().forEach(smali::add);\n\t\tsmali.add(')');\n\t\tsmali.add(methodRef.getReturnType());\n\n\t\tAttributeStorage mthAttributes = AttributeStorage.fromList(mth.getAttributes());\n\t\tAnnotationsAttr annotationsAttr = mthAttributes.get(JadxAttrType.ANNOTATION_LIST);\n\t\tif (annotationsAttr != null && !annotationsAttr.isEmpty()) {\n\t\t\tsmali.incIndent();\n\t\t\twriteAnnotations(smali, annotationsAttr.getList());\n\t\t\tsmali.decIndent();\n\t\t\tsmali.startLine();\n\t\t}\n\t}\n\n\tprivate void formatMthParamInfo(IMethodData mth, SmaliWriter smali, LineInfo line,\n\t\t\tint regsCount, List<ILocalVar> localVars) {\n\t\tList<String> types = mth.getMethodRef().getArgTypes();\n\t\tif (types.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tint paramStart = 0;\n\t\tint regNum = line.smaliMthNode.getParamRegStart();\n\t\tif (!hasStaticFlag(mth.getAccessFlags())) {\n\t\t\t// add 'this' register\n\t\t\tline.addRegName(regNum, \"p0\");\n\t\t\tline.smaliMthNode.setParamReg(regNum, \"p0\");\n\t\t\tregNum++;\n\t\t\tparamStart++;\n\t\t}\n\t\tif (localVars.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tILocalVar[] params = new ILocalVar[regsCount];\n\t\tfor (ILocalVar var : localVars) {\n\t\t\tif (var.isMarkedAsParameter()) {\n\t\t\t\tparams[var.getRegNum()] = var;\n\t\t\t}\n\t\t}\n\t\tsmali.newLine();\n\t\tfor (String paramType : types) {\n\t\t\tILocalVar param = params[regNum];\n\t\t\tif (param != null) {\n\t\t\t\tString name = Utils.getOrElse(param.getName(), \"\");\n\t\t\t\tString type = Utils.getOrElse(param.getSignature(), paramType);\n\t\t\t\tString varName = \"p\" + paramStart;\n\t\t\t\tsmali.startLine(String.format(\".param %s, \\\"%s\\\" # %s\", varName, name, type));\n\t\t\t\tline.addRegName(regNum, varName);\n\t\t\t\tline.smaliMthNode.setParamReg(regNum, varName);\n\t\t\t}\n\t\t\tint regSize = isWideType(paramType) ? 2 : 1;\n\t\t\tregNum += regSize;\n\t\t\tparamStart += regSize;\n\t\t}\n\t}\n\n\tprivate static int getParamStartRegNum(IMethodData mth) {\n\t\tICodeReader codeReader = mth.getCodeReader();\n\t\tif (codeReader != null) {\n\t\t\tint startNum = codeReader.getRegistersCount();\n\t\t\tif (startNum > 0) {\n\t\t\t\tfor (String argType : mth.getMethodRef().getArgTypes()) {\n\t\t\t\t\tif (isWideType(argType)) {\n\t\t\t\t\t\tstartNum -= 2;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstartNum -= 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!hasStaticFlag(mth.getAccessFlags())) {\n\t\t\t\t\tstartNum--;\n\t\t\t\t}\n\t\t\t\treturn startNum;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\tprivate static boolean isWideType(String type) {\n\t\treturn type.equals(\"D\") || type.equals(\"J\");\n\t}\n\n\tprivate void writeAnnotations(SmaliWriter smali, List<IAnnotation> annoList) {\n\t\tif (annoList.size() > 0) {\n\t\t\tfor (int i = 0; i < annoList.size(); i++) {\n\t\t\t\tsmali.startLine();\n\t\t\t\twriteAnnotation(smali, annoList.get(i));\n\t\t\t\tif (i != annoList.size() - 1) {\n\t\t\t\t\tsmali.startLine();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void writeAnnotation(SmaliWriter smali, IAnnotation anno) {\n\t\tsmali.add(\".annotation\")\n\t\t\t\t.add(\" \");\n\t\tAnnotationVisibility vby = anno.getVisibility();\n\t\tif (vby != null) {\n\t\t\tsmali.add(vby.toString().toLowerCase()).add(\" \");\n\t\t}\n\t\tsmali.add(anno.getAnnotationClass());\n\t\tanno.getValues().forEach((k, v) -> {\n\t\t\tsmali.incIndent();\n\t\t\tsmali.startLine(k).add(\" = \");\n\t\t\twriteEncodedValue(smali, v, true);\n\t\t\tsmali.decIndent();\n\t\t});\n\t\tsmali.startLine(\".end annotation\");\n\t}\n\n\tprivate void formatDbgInfo(IDebugInfo dbgInfo, List<ILocalVar> localVars, LineInfo line) {\n\t\tdbgInfo.getSourceLineMapping().forEach((codeOffset, srcLine) -> {\n\t\t\tif (codeOffset > -1) {\n\t\t\t\tline.addDebugLineTip(codeOffset, String.format(\".line %d\", srcLine), \"\");\n\t\t\t}\n\t\t});\n\t\tfor (ILocalVar localVar : localVars) {\n\t\t\tif (localVar.isMarkedAsParameter()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString type = localVar.getType();\n\t\t\tString sign = localVar.getSignature();\n\t\t\tString longTypeStr;\n\t\t\tif (sign == null || sign.trim().isEmpty()) {\n\t\t\t\tlongTypeStr = String.format(\", \\\"%s\\\":%s\", localVar.getName(), type);\n\t\t\t} else {\n\t\t\t\tlongTypeStr = String.format(\", \\\"%s\\\":%s, \\\"%s\\\"\", localVar.getName(), type, localVar.getSignature());\n\t\t\t}\n\t\t\tline.addTip(\n\t\t\t\t\tlocalVar.getStartOffset(),\n\t\t\t\t\t\".local \" + formatVarName(line.smaliMthNode, localVar),\n\t\t\t\t\tlongTypeStr);\n\t\t\tline.addTip(\n\t\t\t\t\tlocalVar.getEndOffset(),\n\t\t\t\t\t\".end local \" + formatVarName(line.smaliMthNode, localVar),\n\t\t\t\t\tString.format(\" # \\\"%s\\\":%s\", localVar.getName(), type));\n\t\t}\n\t}\n\n\tprivate String formatVarName(SmaliMethodNode smaliMthNode, ILocalVar localVar) {\n\t\tint paramRegStart = smaliMthNode.getParamRegStart();\n\t\tint regNum = localVar.getRegNum();\n\t\tif (regNum < paramRegStart) {\n\t\t\treturn \"v\" + regNum;\n\t\t}\n\t\treturn \"p\" + (regNum - paramRegStart);\n\t}\n\n\tprivate void writeEncodedValue(SmaliWriter smali, EncodedValue value, boolean wrapArray) {\n\t\tStringUtils stringUtils = smali.getClassNode().root().getStringUtils();\n\t\tswitch (value.getType()) {\n\t\t\tcase ENCODED_ARRAY:\n\t\t\t\tsmali.add(\"{\");\n\t\t\t\tif (wrapArray) {\n\t\t\t\t\tsmali.incIndent();\n\t\t\t\t\tsmali.startLine();\n\t\t\t\t}\n\t\t\t\tList<EncodedValue> values = (List<EncodedValue>) value.getValue();\n\t\t\t\tfor (int i = 0; i < values.size(); i++) {\n\t\t\t\t\twriteEncodedValue(smali, values.get(i), wrapArray);\n\t\t\t\t\tif (i != values.size() - 1) {\n\t\t\t\t\t\tsmali.add(\",\");\n\t\t\t\t\t\tif (wrapArray) {\n\t\t\t\t\t\t\tsmali.startLine();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsmali.add(\" \");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (wrapArray) {\n\t\t\t\t\tsmali.decIndent();\n\t\t\t\t\tsmali.startLine(\"}\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_NULL:\n\t\t\t\tsmali.add(\"null\");\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_ANNOTATION:\n\t\t\t\twriteAnnotation(smali, (IAnnotation) value.getValue());\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_BYTE:\n\t\t\t\tsmali.add(stringUtils.formatByte((Byte) value.getValue(), false));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_SHORT:\n\t\t\t\tsmali.add(stringUtils.formatShort((Short) value.getValue(), false));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_CHAR:\n\t\t\t\tsmali.add(stringUtils.unescapeChar((Character) value.getValue()));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_INT:\n\t\t\t\tsmali.add(stringUtils.formatInteger((Integer) value.getValue(), false));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_LONG:\n\t\t\t\tsmali.add(stringUtils.formatLong((Long) value.getValue(), false));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_FLOAT:\n\t\t\t\tsmali.add(StringUtils.formatFloat((Float) value.getValue()));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_DOUBLE:\n\t\t\t\tsmali.add(StringUtils.formatDouble((Double) value.getValue()));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_STRING:\n\t\t\t\tsmali.add(stringUtils.unescapeString((String) value.getValue()));\n\t\t\t\tbreak;\n\t\t\tcase ENCODED_TYPE:\n\t\t\t\tsmali.add(ArgType.parse((String) value.getValue()) + \".class\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tsmali.add(String.valueOf(value.getValue()));\n\t\t}\n\t}\n\n\tprivate static final int CODE_OFFSET_COLUMN_WIDTH = 4;\n\tprivate static final int BYTECODE_COLUMN_WIDTH = 20 + 3; // 3 for ellipses.\n\tprivate static final String FMT_BYTECODE_COL = \"%-\" + (BYTECODE_COLUMN_WIDTH - 3) + \"s\";\n\n\tprivate static final int INSN_COL_WIDTH = \"const-method-handle\".length();\n\tprivate static final String FMT_INSN_COL = \"%-\" + INSN_COL_WIDTH + \"s\";\n\tprivate static final String FMT_FILE_OFFSET = \"%08x:\";\n\tprivate static final String FMT_CODE_OFFSET = \"%04x:\";\n\tprivate static final String FMT_TARGET_OFFSET = \"%04x\";\n\tprivate static final String FMT_GOTO = \":goto_\" + FMT_TARGET_OFFSET;\n\tprivate static final String FMT_COND = \":cond_\" + FMT_TARGET_OFFSET;\n\tprivate static final String FMT_DATA = \":array_\" + FMT_TARGET_OFFSET;\n\tprivate static final String FMT_P_SWITCH = \":p_switch_\" + FMT_TARGET_OFFSET;\n\tprivate static final String FMT_S_SWITCH = \":s_switch_\" + FMT_TARGET_OFFSET;\n\tprivate static final String FMT_P_SWITCH_CASE = \":p_case_\" + FMT_TARGET_OFFSET;\n\tprivate static final String FMT_S_SWITCH_CASE = \":s_case_\" + FMT_TARGET_OFFSET;\n\n\tprivate static final String FMT_TRY_TAG = \"try_\" + FMT_TARGET_OFFSET + \":\";\n\tprivate static final String FMT_TRY_END_TAG = \"try_end_\" + FMT_TARGET_OFFSET + \":\";\n\tprivate static final String FMT_CATCH_TAG = \"catch_\" + FMT_TARGET_OFFSET + \":\";\n\tprivate static final String FMT_CATCH_ALL_TAG = \"catch_all_\" + FMT_TARGET_OFFSET + \":\";\n\tprivate static final String FMT_GOTO_TAG = \"goto_\" + FMT_TARGET_OFFSET + \":\";\n\tprivate static final String FMT_COND_TAG = \"cond_\" + FMT_TARGET_OFFSET + \":\";\n\tprivate static final String FMT_DATA_TAG = \"array_\" + FMT_TARGET_OFFSET + \":\";\n\tprivate static final String FMT_P_SWITCH_TAG = \"p_switch_\" + FMT_TARGET_OFFSET + \":\";\n\tprivate static final String FMT_S_SWITCH_TAG = \"s_switch_\" + FMT_TARGET_OFFSET + \":\";\n\tprivate static final String FMT_P_SWITCH_CASE_TAG = \"p_case_\" + FMT_TARGET_OFFSET + \":\";\n\tprivate static final String FMT_S_SWITCH_CASE_TAG = \"s_case_\" + FMT_TARGET_OFFSET + \":\";\n\n\tprivate void fmtRegs(InsnData insn, InsnType insnType, LineInfo line) {\n\t\tboolean appendBrace = insnType == InsnType.INVOKE || isRegList(insn);\n\t\tStringBuilder lw = line.getLineWriter();\n\t\tif (insnType == InsnType.INVOKE) {\n\t\t\tint resultReg = insn.getResultReg();\n\t\t\tif (resultReg != -1) {\n\t\t\t\tlw.append(line.getRegName(resultReg)).append(\" <= \");\n\t\t\t}\n\t\t}\n\t\tif (appendBrace) {\n\t\t\tlw.append(\"{\");\n\t\t}\n\t\tif (isRangeRegIns(insn)) {\n\t\t\tlw.append(line.getRegName(insn.getReg(0)))\n\t\t\t\t\t.append(\" .. \")\n\t\t\t\t\t.append(line.getRegName(insn.getReg(insn.getRegsCount() - 1)));\n\t\t} else if (insn.getRegsCount() > 0) {\n\t\t\tfor (int i = 0; i < insn.getRegsCount(); i++) {\n\t\t\t\tif (i > 0) {\n\t\t\t\t\tlw.append(\", \");\n\t\t\t\t}\n\t\t\t\tlw.append(line.getRegName(insn.getReg(i)));\n\t\t\t}\n\t\t}\n\t\tif (appendBrace) {\n\t\t\tlw.append(\"}\");\n\t\t}\n\t}\n\n\tprivate int getInsnColStart() {\n\t\tint start = 0;\n\t\tif (printFileOffset) {\n\t\t\tstart += 8 + 1 + 1; // plus 1s for space and the ':'\n\t\t}\n\t\tif (printBytecode) {\n\t\t\tstart += BYTECODE_COLUMN_WIDTH + 1; // plus 1 for space\n\t\t}\n\t\treturn start;\n\t}\n\n\tprivate void fmtCols(InsnData insn, LineInfo line) {\n\t\tif (printFileOffset) {\n\t\t\tline.getLineWriter().append(String.format(FMT_FILE_OFFSET + \" \", insn.getFileOffset()));\n\t\t}\n\t\tif (printBytecode) {\n\t\t\tformatByteCode(line.getLineWriter(), insn.getByteCode());\n\t\t\tline.getLineWriter().append(\" \");\n\t\t\tline.getLineWriter().append(String.format(FMT_CODE_OFFSET + \" \", insn.getOffset()));\n\t\t}\n\t}\n\n\tprivate void formatByteCode(StringBuilder smali, byte[] bytes) {\n\t\tint maxLen = Math.min(bytes.length, 4 * 2); // limit to 4 units\n\t\tStringBuilder inHex = new StringBuilder();\n\t\tfor (int i = 0; i < maxLen; i++) {\n\t\t\tinHex.append(String.format(\"%02x\", bytes[i]));\n\t\t\tif (i % 2 == 1) {\n\t\t\t\tinHex.append(' ');\n\t\t\t}\n\t\t}\n\t\tsmali.append(String.format(FMT_BYTECODE_COL, inHex));\n\t\tif (maxLen < bytes.length) {\n\t\t\tsmali.append(\"...\");\n\t\t} else {\n\t\t\tsmali.append(\"   \");\n\t\t}\n\t}\n\n\tprivate boolean fmtPayloadInsn(InsnData insn, LineInfo line) {\n\t\tOpcode opcode = insn.getOpcode();\n\t\tif (opcode == PACKED_SWITCH_PAYLOAD) {\n\t\t\tline.getLineWriter().append(\"packed-switch-payload\");\n\t\t\tline.addInsnLine(insn.getOffset(), line.getLineWriter().toString());\n\n\t\t\tISwitchPayload payload = (ISwitchPayload) insn.getPayload();\n\t\t\tif (payload != null) {\n\t\t\t\tfmtSwitchPayload(insn, FMT_P_SWITCH_CASE, FMT_P_SWITCH_CASE_TAG, line, payload);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tif (opcode == SPARSE_SWITCH_PAYLOAD) {\n\t\t\tline.getLineWriter().append(\"sparse-switch-payload\");\n\t\t\tline.addInsnLine(insn.getOffset(), line.getLineWriter().toString());\n\n\t\t\tISwitchPayload payload = (ISwitchPayload) insn.getPayload();\n\t\t\tif (payload != null) {\n\t\t\t\tfmtSwitchPayload(insn, FMT_S_SWITCH_CASE, FMT_S_SWITCH_CASE_TAG, line, payload);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tif (opcode == FILL_ARRAY_DATA_PAYLOAD) {\n\t\t\tline.getLineWriter().append(\"fill-array-data-payload\");\n\t\t\tline.addInsnLine(insn.getOffset(), line.getLineWriter().toString());\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate void fmtSwitchPayload(InsnData insn, String fmtTarget, String fmtTag, LineInfo line, ISwitchPayload payload) {\n\t\tint lineStart = getInsnColStart();\n\t\tlineStart += CODE_OFFSET_COLUMN_WIDTH + 1 + 1; // plus 1s for space and the ':'\n\t\tString basicIndent = new String(new byte[lineStart]).replace(\"\\0\", \" \");\n\t\tString indent = JadxArgs.DEFAULT_INDENT_STR + basicIndent;\n\t\tint[] keys = payload.getKeys();\n\t\tint[] targets = payload.getTargets();\n\t\tInteger switchOffset = line.payloadOffsetMap.get(insn.getOffset());\n\t\tif (switchOffset == null) {\n\t\t\tthrow new JadxRuntimeException(\"Unknown switch insn for payload at \" + insn.getOffset());\n\t\t}\n\t\tfor (int i = 0; i < keys.length; i++) {\n\t\t\tint target = switchOffset + targets[i];\n\t\t\tline.addInsnLine(insn.getOffset(),\n\t\t\t\t\tString.format(\"%scase %d: -> \" + fmtTarget, indent, keys[i], target));\n\t\t\tline.addTip(target,\n\t\t\t\t\tString.format(fmtTag, target), String.format(\" # case %d\", keys[i]));\n\t\t}\n\t\tline.addInsnLine(insn.getOffset(), basicIndent + \".end payload\");\n\t}\n\n\tprivate static String literal(InsnData insn) {\n\t\tlong it = insn.getLiteral();\n\t\tString tip = \"\";\n\t\tif (it > Integer.MAX_VALUE) {\n\t\t\tif (isWideIns(insn)) {\n\t\t\t\ttip = \" # double: \" + Double.longBitsToDouble(it);\n\t\t\t} else if (getOpenCodeByte(insn) == 0x15) { // CONST_HIGH16 = 0x15;\n\t\t\t\ttip = \" # float: \" + Float.intBitsToFloat((int) it);\n\t\t\t}\n\t\t} else if (it <= 0) {\n\t\t\treturn \"\" + it + tip;\n\t\t}\n\t\treturn \"0x\" + Long.toHexString(it) + tip;\n\t}\n\n\tprivate static String str(InsnData insn) {\n\t\treturn String.format(\"\\\"%s\\\" # string@%04x\",\n\t\t\t\tinsn.getIndexAsString()\n\t\t\t\t\t\t.replace(\"\\n\", \"\\\\n\")\n\t\t\t\t\t\t.replace(\"\\t\", \"\\\\t\"),\n\t\t\t\tinsn.getIndex());\n\t}\n\n\tprivate static String type(InsnData insn) {\n\t\treturn String.format(\"%s # type@%04x\", insn.getIndexAsType(), insn.getIndex());\n\t}\n\n\tprivate static String field(InsnData insn) {\n\t\treturn String.format(\"%s # field@%04x\", insn.getIndexAsField().toString(), insn.getIndex());\n\t}\n\n\tprivate static String method(InsnData insn) {\n\t\tOpcode op = insn.getOpcode();\n\t\tif (op == INVOKE_CUSTOM || op == INVOKE_CUSTOM_RANGE) {\n\t\t\tinsn.getIndexAsCallSite().load();\n\t\t\treturn String.format(\"%s # call_site@%04x\", insn.getIndexAsCallSite().toString(), insn.getIndex());\n\t\t}\n\t\tIMethodRef mthRef = insn.getIndexAsMethod();\n\t\tmthRef.load();\n\t\tif (op == INVOKE_POLYMORPHIC || op == INVOKE_POLYMORPHIC_RANGE) {\n\t\t\treturn String.format(\"%s, %s # method@%04x, proto@%04x\",\n\t\t\t\t\tmthRef.toString(), insn.getIndexAsProto(insn.getTarget()).toString(),\n\t\t\t\t\tinsn.getIndex(), insn.getTarget());\n\t\t}\n\t\treturn String.format(\"%s # method@%04x\", mthRef.toString(), insn.getIndex());\n\t}\n\n\tprivate static String proto(InsnData insn, int protoIndex) {\n\t\treturn String.format(\"%s # proto@%04x\", insn.getIndexAsProto(protoIndex).toString(), protoIndex);\n\t}\n\n\tprivate static String methodHandle(InsnData insn) {\n\t\treturn String.format(\"%s # method_handle@%04x\",\n\t\t\t\tinsn.getIndexAsMethodHandle().toString(), insn.getIndex());\n\t}\n\n\tprotected static boolean isRangeRegIns(InsnData insn) {\n\t\tswitch (insn.getOpcode()) {\n\t\t\tcase INVOKE_VIRTUAL_RANGE:\n\t\t\tcase INVOKE_SUPER_RANGE:\n\t\t\tcase INVOKE_DIRECT_RANGE:\n\t\t\tcase INVOKE_STATIC_RANGE:\n\t\t\tcase INVOKE_INTERFACE_RANGE:\n\t\t\tcase FILLED_NEW_ARRAY_RANGE:\n\t\t\tcase INVOKE_CUSTOM_RANGE:\n\t\t\tcase INVOKE_POLYMORPHIC_RANGE:\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static int getOpenCodeByte(InsnData insn) {\n\t\treturn insn.getRawOpcodeUnit() & 0xff;\n\t}\n\n\tprivate static boolean isWideIns(InsnData insn) {\n\t\treturn insn.getOpcode() == CONST_WIDE;\n\t}\n\n\tprivate static boolean hasLiteral(InsnData insn) {\n\t\tint opcode = getOpenCodeByte(insn);\n\t\treturn insn.getOpcode() == CONST\n\t\t\t\t|| insn.getOpcode() == CONST_WIDE\n\t\t\t\t|| (opcode >= 0xd0 && opcode <= 0xe2); // add-int/lit16 to ushr-int/lit8\n\t}\n\n\tprivate static boolean isRegList(InsnData insn) {\n\t\treturn insn.getOpcode() == FILLED_NEW_ARRAY || insn.getOpcode() == FILLED_NEW_ARRAY_RANGE;\n\t}\n\n\tprivate class LineInfo {\n\t\tprivate SmaliMethodNode smaliMthNode = new SmaliMethodNode();\n\t\tprivate final StringBuilder lineWriter = new StringBuilder(50);\n\n\t\tprivate String lastDebugTip = \"\";\n\t\tprivate final Map<Integer, List<String>> insnOffsetMap = new LinkedHashMap<>();\n\t\tprivate final Map<Integer, String> regNameMap = new HashMap<>();\n\t\tprivate Map<Integer, Map<String, Object>> tipMap = Collections.emptyMap();\n\t\tprivate Map<Integer, Integer> payloadOffsetMap = Collections.emptyMap();\n\n\t\tpublic LineInfo() {\n\t\t}\n\n\t\tpublic StringBuilder getLineWriter() {\n\t\t\treturn lineWriter;\n\t\t}\n\n\t\tpublic void reset() {\n\t\t\tlastDebugTip = \"\";\n\t\t\tpayloadOffsetMap = Collections.emptyMap();\n\t\t\ttipMap = Collections.emptyMap();\n\t\t\tinsnOffsetMap.clear();\n\t\t\tregNameMap.clear();\n\t\t\tsmaliMthNode = new SmaliMethodNode();\n\t\t}\n\n\t\tpublic void addRegName(int regNum, String name) {\n\t\t\tregNameMap.put(regNum, name);\n\t\t}\n\n\t\tpublic String getRegName(int regNum) {\n\t\t\tString name = regNameMap.get(regNum);\n\t\t\tif (name == null) {\n\t\t\t\tname = \"v\" + regNum;\n\t\t\t}\n\t\t\treturn name;\n\t\t}\n\n\t\tpublic void addInsnLine(int codeOffset, String insnLine) {\n\t\t\tList<String> insnList = insnOffsetMap.computeIfAbsent(codeOffset, k -> new ArrayList<>(1));\n\t\t\tinsnList.add(insnLine);\n\t\t}\n\n\t\tpublic void addTip(int offset, String tip, String extra) {\n\t\t\tif (tipMap.isEmpty()) {\n\t\t\t\ttipMap = new LinkedHashMap<>();\n\t\t\t}\n\t\t\tMap<String, Object> innerMap = tipMap.computeIfAbsent(offset, k -> new LinkedHashMap<>());\n\t\t\tObject obj = innerMap.get(tip);\n\t\t\tif (obj != null) {\n\t\t\t\tif (obj instanceof String) {\n\t\t\t\t\tif (obj.equals(\"\")) {\n\t\t\t\t\t\tinnerMap.put(tip, 2);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tList<String> extras = new ArrayList<>(2);\n\t\t\t\t\t\textras.add((String) obj);\n\t\t\t\t\t\textras.add(extra);\n\t\t\t\t\t\tinnerMap.put(tip, extras);\n\t\t\t\t\t}\n\t\t\t\t} else if (obj instanceof Integer) {\n\t\t\t\t\tinnerMap.put(tip, ((int) obj) + 1);\n\t\t\t\t} else if (obj instanceof List) {\n\t\t\t\t\tif (!extra.isEmpty()) {\n\t\t\t\t\t\tList<String> extras = (List<String>) obj;\n\t\t\t\t\t\textras.add(extra);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tinnerMap.put(tip, extra);\n\t\t\t}\n\t\t}\n\n\t\tpublic void addDebugLineTip(int offset, String tip, String extra) {\n\t\t\tif (tip.equals(lastDebugTip)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlastDebugTip = tip;\n\t\t\tif (tipMap.isEmpty()) {\n\t\t\t\ttipMap = new LinkedHashMap<>();\n\t\t\t}\n\t\t\tMap<String, Object> innerMap = tipMap.computeIfAbsent(offset, k -> new LinkedHashMap<>());\n\t\t\tinnerMap.put(tip, extra);\n\t\t}\n\n\t\tpublic void addPayloadOffset(int curOffset, int payloadOffset) {\n\t\t\tif (payloadOffsetMap.isEmpty()) {\n\t\t\t\tpayloadOffsetMap = new HashMap<>();\n\t\t\t}\n\t\t\tpayloadOffsetMap.put(payloadOffset, curOffset);\n\t\t}\n\n\t\tpublic void write(SmaliWriter smali) {\n\t\t\tint lineOffset = getInsnColStart();\n\t\t\tfor (Entry<Integer, List<String>> entry : insnOffsetMap.entrySet()) {\n\t\t\t\twriteTip(smali, entry.getKey(), lineOffset);\n\t\t\t\tsmaliMthNode.setInsnInfo(entry.getKey(), lineOffset + smali.getLength());\n\t\t\t\tsmaliMthNode.attachLine(smali.getLine(), entry.getKey());\n\t\t\t\tsmali.attachSourceLine(entry.getKey());\n\t\t\t\tfor (String s : entry.getValue()) {\n\t\t\t\t\tsmali.add(s).startLine();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void writeTip(SmaliWriter smali, int codeOffset, int lineOffset) {\n\t\t\tMap<String, Object> tip = tipMap.get(codeOffset);\n\t\t\tif (tip != null) {\n\t\t\t\tfor (Entry<String, Object> entry : tip.entrySet()) {\n\t\t\t\t\tint start = Math.max(0, lineOffset - entry.getKey().length());\n\t\t\t\t\tif (start > 0) {\n\t\t\t\t\t\tsmali.add(new String(new byte[start]).replace(\"\\0\", \" \"));\n\t\t\t\t\t}\n\t\t\t\t\tif (entry.getValue() instanceof Integer) {\n\t\t\t\t\t\tsmali.add(String.format(\"%s # %d refs\", entry.getKey(), entry.getValue()))\n\t\t\t\t\t\t\t\t.startLine();\n\t\t\t\t\t} else if (entry.getValue() instanceof String) {\n\t\t\t\t\t\tsmali.add(String.format(\"%s%s\", entry.getKey(), entry.getValue()))\n\t\t\t\t\t\t\t\t.startLine();\n\t\t\t\t\t} else if (entry.getValue() instanceof List) {\n\t\t\t\t\t\tList<String> extras = (List<String>) entry.getValue();\n\t\t\t\t\t\tsmali.add(String.format(\"%s%s\", entry.getKey(), extras.get(0)))\n\t\t\t\t\t\t\t\t.startLine();\n\t\t\t\t\t\tString pad = new String(new byte[lineOffset]).replace(\"\\0\", \" \");\n\t\t\t\t\t\tfor (int i = 1; i < extras.size(); i++) {\n\t\t\t\t\t\t\tsmali.add(String.format(\"%s%s\", pad, extras.get(i)))\n\t\t\t\t\t\t\t\t\t.startLine();\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsmali.add(String.format(\"%s%s\", entry.getKey(), entry.getValue()))\n\t\t\t\t\t\t\t\t.startLine();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static class SmaliInsnDecoder extends InsnDecoder {\n\t\t@Override\n\t\tprotected @NotNull InsnNode decode(InsnData insn) {\n\t\t\ttry {\n\t\t\t\treturn super.decode(insn);\n\t\t\t} catch (Exception e) {\n\t\t\t\tswitch (insn.getOpcode()) {\n\t\t\t\t\tcase INVOKE_CUSTOM:\n\t\t\t\t\tcase INVOKE_CUSTOM_RANGE:\n\t\t\t\t\tcase INVOKE_POLYMORPHIC:\n\t\t\t\t\tcase INVOKE_POLYMORPHIC_RANGE:\n\t\t\t\t\tcase CONST_METHOD_HANDLE:\n\t\t\t\t\tcase CONST_METHOD_TYPE:\n\t\t\t\t\t\treturn new InsnNode(InsnType.INVOKE, insn.getRegsCount());\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic SmaliInsnDecoder(MethodNode mthNode) {\n\t\t\tsuper(mthNode);\n\t\t}\n\n\t\t@Override\n\t\tpublic InsnNode[] process(ICodeReader codeReader) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static class RawField {\n\t\tboolean isStatic;\n\t\tString accessFlag;\n\t\tString name;\n\t\tString type;\n\t\tAttributeStorage attributes;\n\n\t\tprivate static RawField make(IFieldData f) {\n\t\t\tRawField field = new RawField();\n\t\t\tfield.isStatic = hasStaticFlag(f.getAccessFlags());\n\t\t\tfield.accessFlag = AccessFlags.format(f.getAccessFlags(), FIELD);\n\t\t\tfield.name = f.getName();\n\t\t\tfield.type = f.getType();\n\t\t\tfield.attributes = AttributeStorage.fromList(f.getAttributes());\n\t\t\treturn field;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/smali/SmaliMethodNode.java",
    "content": "package jadx.gui.device.debugger.smali;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport jadx.core.dex.instructions.args.InsnArg;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.nodes.InsnNode;\n\npublic class SmaliMethodNode {\n\tprivate Map<Long, InsnNode> nodes; // codeOffset: InsnNode\n\tprivate List<SmaliRegister> regList;\n\tprivate int[] insnPos;\n\tprivate int defPos;\n\tprivate Map<Integer, Integer> lineMapping = Collections.emptyMap(); // line: codeOffset\n\tprivate int paramRegStart;\n\tprivate int regCount;\n\n\tpublic int getParamRegStart() {\n\t\treturn this.paramRegStart;\n\t}\n\n\tpublic int getRegCount() {\n\t\treturn this.regCount;\n\t}\n\n\t/**\n\t * Returns the line mapping of the:\n\t * 'output disassembled smali file line index' to 'dex instruction position code offset'\n\t * The value is the same as {@link InsnNode#getOffset()}\n\t *\n\t * @return the line mapping\n\t */\n\tpublic Map<Integer, Integer> getLineMapping() {\n\t\treturn lineMapping;\n\t}\n\n\tpublic void initRegInfoList(int regCount, int insnCount) {\n\t\tregList = new ArrayList<>(regCount);\n\t\tfor (int i = 0; i < regCount; i++) {\n\t\t\tregList.add(new SmaliRegister(i, insnCount));\n\t\t}\n\t}\n\n\tpublic int getInsnPos(long codeOffset) {\n\t\tif (insnPos != null && codeOffset < insnPos.length) {\n\t\t\treturn insnPos[(int) codeOffset];\n\t\t}\n\t\treturn -1;\n\t}\n\n\tpublic int getDefPos() {\n\t\treturn defPos;\n\t}\n\n\tpublic InsnNode getInsnNode(long codeOffset) {\n\t\treturn nodes.get(codeOffset);\n\t}\n\n\tpublic List<SmaliRegister> getRegList() {\n\t\treturn regList;\n\t}\n\n\tprotected SmaliMethodNode() {\n\t}\n\n\tprotected void setRegCount(int regCount) {\n\t\tthis.regCount = regCount;\n\t}\n\n\tprotected void attachLine(int line, int codeOffset) {\n\t\tif (lineMapping.isEmpty()) {\n\t\t\tlineMapping = new HashMap<>();\n\t\t}\n\t\tlineMapping.put(line, codeOffset);\n\t}\n\n\tprotected void setInsnInfo(int codeOffset, int pos) {\n\t\tif (insnPos != null && codeOffset < insnPos.length) {\n\t\t\tinsnPos[codeOffset] = pos;\n\t\t}\n\t\tInsnNode insn = getInsnNode(codeOffset);\n\t\tRegisterArg r = insn.getResult();\n\t\tif (r != null) {\n\t\t\tregList.get(r.getRegNum()).setStartOffset(codeOffset);\n\t\t}\n\t\tfor (InsnArg arg : insn.getArguments()) {\n\t\t\tif (arg instanceof RegisterArg) {\n\t\t\t\tregList.get(((RegisterArg) arg).getRegNum()).setStartOffset(codeOffset);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected void setDefPos(int pos) {\n\t\tdefPos = pos;\n\t}\n\n\tprotected void setParamReg(int regNum, String name) {\n\t\tSmaliRegister r = regList.get(regNum);\n\t\tr.setParam(name);\n\t}\n\n\tprotected void setParamRegStart(int paramRegStart) {\n\t\tthis.paramRegStart = paramRegStart;\n\t}\n\n\tprotected void setInsnNodes(Map<Long, InsnNode> nodes, int insnCount) {\n\t\tthis.nodes = nodes;\n\t\tinsnPos = new int[insnCount];\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/smali/SmaliRegister.java",
    "content": "package jadx.gui.device.debugger.smali;\n\npublic class SmaliRegister extends RegisterInfo {\n\tprivate final int num;\n\tprivate String paramName;\n\tprivate final int endOffset;\n\tprivate int startOffset;\n\tprivate boolean isParam;\n\tprivate int runtimeNum;\n\n\tpublic SmaliRegister(int num, int insnCount) {\n\t\tthis.num = num;\n\t\tthis.endOffset = insnCount;\n\t\tthis.startOffset = insnCount;\n\t}\n\n\tpublic int getRuntimeRegNum() {\n\t\treturn runtimeNum;\n\t}\n\n\tpublic void setRuntimeRegNum(int runtimeNum) {\n\t\tthis.runtimeNum = runtimeNum;\n\t}\n\n\t@Override\n\tpublic boolean isInitialized(long codeOffset) {\n\t\treturn codeOffset > getStartOffset() && codeOffset < getEndOffset();\n\t}\n\n\tprotected void setParam(String name) {\n\t\tparamName = name;\n\t\tisParam = true;\n\t}\n\n\tprotected void setStartOffset(int off) {\n\t\tif (off < startOffset) {\n\t\t\tstartOffset = off;\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn paramName != null ? paramName : \"v\" + num;\n\t}\n\n\t@Override\n\tpublic int getRegNum() {\n\t\treturn num;\n\t}\n\n\t@Override\n\tpublic String getType() {\n\t\treturn \"\";\n\t}\n\n\t@Override\n\tpublic String getSignature() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic int getStartOffset() {\n\t\treturn startOffset;\n\t}\n\n\t@Override\n\tpublic int getEndOffset() {\n\t\treturn endOffset;\n\t}\n\n\t@Override\n\tpublic boolean isMarkedAsParameter() {\n\t\treturn isParam;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/debugger/smali/SmaliWriter.java",
    "content": "package jadx.gui.device.debugger.smali;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.api.impl.SimpleCodeWriter;\nimport jadx.core.dex.nodes.ClassNode;\n\npublic class SmaliWriter extends SimpleCodeWriter {\n\n\tprivate int line = 0;\n\tprivate final ClassNode cls;\n\n\tpublic SmaliWriter(ClassNode cls) {\n\t\tsuper(cls.root().getArgs());\n\t\tthis.cls = cls;\n\t}\n\n\tpublic ClassNode getClassNode() {\n\t\treturn cls;\n\t}\n\n\t@Override\n\tprotected void addLine() {\n\t\tsuper.addLine();\n\t\tline++;\n\t}\n\n\t@Override\n\tpublic int getLine() {\n\t\treturn line;\n\t}\n\n\t@Override\n\tpublic ICodeInfo finish() {\n\t\treturn new SimpleCodeInfo(buf.toString());\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/protocol/ADB.java",
    "content": "package jadx.gui.device.protocol;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.Socket;\nimport java.net.SocketException;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.StringJoiner;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.log.LogUtils;\nimport jadx.gui.utils.IOUtils;\n\npublic class ADB {\n\n\tpublic static final Charset ADB_CHARSET = StandardCharsets.UTF_8;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ADB.class);\n\n\tprivate static final int DEFAULT_PORT = 5037;\n\tprivate static final String DEFAULT_ADDR = \"localhost\";\n\n\tprivate static final String CMD_FEATURES = \"000dhost:features\";\n\tprivate static final String CMD_TRACK_DEVICES = \"0014host:track-devices-l\";\n\tprivate static final byte[] OKAY = \"OKAY\".getBytes(ADB_CHARSET);\n\tprivate static final byte[] FAIL = \"FAIL\".getBytes(ADB_CHARSET);\n\n\tstatic boolean isOkay(InputStream stream, String command) throws IOException {\n\t\tbyte[] buf = IOUtils.readNBytes(stream, 4);\n\t\tif (Arrays.equals(buf, OKAY)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (Arrays.equals(buf, FAIL)) {\n\t\t\t// Observed that after FAIL the length in hex follows and afterwards an error message,\n\t\t\t// but it is unclear if this is true for all cases where isOkay is used.\n\t\t\t// int msgLen = Integer.parseInt(new String(IOUtils.readNBytes(stream, 4)), 16);\n\t\t\t// byte[] errorMsg = IOUtils.readNBytes(stream, msgLen);\n\t\t\t// LOG.error(\"isOkay failed: received error message: {}\", new String(errorMsg));\n\t\t\tLOG.error(\"isOkay failed for command: {}\", command);\n\t\t\treturn false;\n\t\t}\n\t\tif (buf == null) {\n\t\t\tthrow new IOException(\"isOkay failed - steam ended\");\n\t\t}\n\t\tthrow new IOException(\"isOkay failed - unexpected response \" + new String(buf, ADB_CHARSET));\n\t}\n\n\tpublic static byte[] exec(String cmd, OutputStream outputStream, InputStream inputStream) throws IOException {\n\t\treturn execCommandSync(outputStream, inputStream, cmd);\n\t}\n\n\tpublic static byte[] exec(String cmd) throws IOException {\n\t\ttry (Socket socket = connect()) {\n\t\t\treturn exec(cmd, socket.getOutputStream(), socket.getInputStream());\n\t\t}\n\t}\n\n\tpublic static Socket connect() throws IOException {\n\t\treturn connect(DEFAULT_ADDR, DEFAULT_PORT);\n\t}\n\n\tpublic static Socket connect(String host, int port) throws IOException {\n\t\treturn new Socket(host, port);\n\t}\n\n\tstatic boolean execCommandAsync(OutputStream outputStream, InputStream inputStream, String cmd) throws IOException {\n\t\toutputStream.write(cmd.getBytes(ADB_CHARSET));\n\t\treturn isOkay(inputStream, \"execCommandAsync\");\n\t}\n\n\tprivate static byte[] execCommandSync(OutputStream outputStream, InputStream inputStream, String cmd) throws IOException {\n\t\toutputStream.write(cmd.getBytes(ADB_CHARSET));\n\t\tif (isOkay(inputStream, \"execCommandSync\")) {\n\t\t\treturn readServiceProtocol(inputStream);\n\t\t}\n\t\treturn null;\n\t}\n\n\tstatic byte[] readServiceProtocol(InputStream stream) {\n\t\ttry {\n\t\t\tbyte[] buf = IOUtils.readNBytes(stream, 4);\n\t\t\tif (buf == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tint len = hexToInt(buf);\n\t\t\tbyte[] result;\n\t\t\tif (len == 0) {\n\t\t\t\tresult = new byte[0];\n\t\t\t} else {\n\t\t\t\tresult = IOUtils.readNBytes(stream, len);\n\t\t\t}\n\t\t\tif (LOG.isTraceEnabled()) {\n\t\t\t\tLOG.trace(\"readServiceProtocol result: {}\", LogUtils.escape(result));\n\t\t\t}\n\t\t\treturn result;\n\t\t} catch (SocketException e) {\n\t\t\tLOG.warn(\"Aborting readServiceProtocol: {}\", e.toString());\n\t\t} catch (IOException e) {\n\t\t\tLOG.error(\"Failed to read readServiceProtocol\", e);\n\t\t}\n\t\treturn null;\n\t}\n\n\tstatic boolean setSerial(String serial, OutputStream outputStream, InputStream inputStream) throws IOException {\n\t\tcheckSerial(serial);\n\t\tLOG.trace(\"setSerial({})\", serial);\n\t\tString setSerialCmd = String.format(\"host:tport:serial:%s\", serial);\n\t\tsetSerialCmd = String.format(\"%04x%s\", setSerialCmd.length(), setSerialCmd);\n\t\toutputStream.write(setSerialCmd.getBytes(ADB_CHARSET));\n\t\tboolean ok = isOkay(inputStream, setSerialCmd);\n\t\tif (ok) {\n\t\t\t// skip the shell-state-id returned by ADB server, it's not important for the following actions.\n\t\t\tinputStream.readNBytes(8);\n\t\t} else {\n\t\t\tLOG.error(\"setSerial command {} failed\", LogUtils.escape(setSerialCmd));\n\t\t}\n\t\treturn ok;\n\t}\n\n\tprivate static byte[] execShellCommandRaw(String cmd, OutputStream outputStream, InputStream inputStream) throws IOException {\n\t\tcmd = String.format(\"shell,v2,TERM=xterm-256color,raw:%s\", cmd);\n\t\tcmd = String.format(\"%04x%s\", cmd.length(), cmd);\n\t\toutputStream.write(cmd.getBytes(ADB_CHARSET));\n\t\tif (isOkay(inputStream, cmd)) {\n\t\t\treturn ShellProtocol.readStdout(inputStream);\n\t\t}\n\t\treturn null;\n\t}\n\n\tstatic byte[] execShellCommandRaw(String serial, String cmd, OutputStream outputStream, InputStream inputStream) throws IOException {\n\t\tif (setSerial(serial, outputStream, inputStream)) {\n\t\t\treturn execShellCommandRaw(cmd, outputStream, inputStream);\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static List<String> getFeatures() throws IOException {\n\t\tbyte[] rst = exec(CMD_FEATURES);\n\t\tif (rst != null) {\n\t\t\treturn Arrays.asList(new String(rst, ADB_CHARSET).trim().split(\",\"));\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\tpublic static boolean startServer(String adbPath, int port) throws IOException {\n\t\tString tcpPort = String.format(\"tcp:%d\", port);\n\t\tList<String> command = Arrays.asList(adbPath, \"-L\", tcpPort, \"start-server\");\n\t\tjava.lang.Process proc = new ProcessBuilder(command)\n\t\t\t\t.redirectErrorStream(true)\n\t\t\t\t.start();\n\t\ttry {\n\t\t\t// Wait for the adb server to start. On Windows even on a fast system 6 seconds are not unusual.\n\t\t\tproc.waitFor(10, TimeUnit.SECONDS);\n\t\t\tproc.exitValue();\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"ADB start server failed with command: {}\", String.join(\" \", command), e);\n\t\t\tproc.destroyForcibly();\n\t\t\treturn false;\n\t\t}\n\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\ttry (InputStream in = proc.getInputStream()) {\n\t\t\tint read;\n\t\t\tbyte[] buf = new byte[1024];\n\t\t\twhile ((read = in.read(buf)) >= 0) {\n\t\t\t\tout.write(buf, 0, read);\n\t\t\t}\n\t\t}\n\t\treturn out.toString().contains(tcpPort);\n\t}\n\n\tpublic static boolean isServerRunning(String host, int port) {\n\t\ttry (Socket sock = new Socket(host, port)) {\n\t\t\treturn true;\n\t\t} catch (Exception e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * @return a socket connected to adb server, otherwise null\n\t */\n\tpublic static Socket listenForDeviceState(DeviceStateListener listener, String host, int port) throws IOException {\n\t\tSocket socket = connect(host, port);\n\t\tInputStream inputStream = socket.getInputStream();\n\t\tOutputStream outputStream = socket.getOutputStream();\n\t\tif (!execCommandAsync(outputStream, inputStream, CMD_TRACK_DEVICES)) {\n\t\t\tsocket.close();\n\t\t\treturn null;\n\t\t}\n\t\tExecutorService listenThread = Executors.newFixedThreadPool(1);\n\t\tlistenThread.execute(() -> {\n\t\t\twhile (true) {\n\t\t\t\tbyte[] res = readServiceProtocol(inputStream);\n\t\t\t\tif (res == null) {\n\t\t\t\t\tbreak; // socket disconnected\n\t\t\t\t}\n\t\t\t\tif (listener != null) {\n\t\t\t\t\tString payload = new String(res, ADB_CHARSET);\n\t\t\t\t\tString[] deviceLines = payload.split(\"\\n\");\n\t\t\t\t\tList<ADBDeviceInfo> deviceInfoList = new ArrayList<>(deviceLines.length);\n\t\t\t\t\tfor (String deviceLine : deviceLines) {\n\t\t\t\t\t\tif (!deviceLine.trim().isEmpty()) {\n\t\t\t\t\t\t\tdeviceInfoList.add(new ADBDeviceInfo(deviceLine, host, port));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tlistener.onDeviceStatusChange(deviceInfoList);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (listener != null) {\n\t\t\t\tlistener.adbDisconnected();\n\t\t\t}\n\t\t});\n\t\treturn socket;\n\t}\n\n\tpublic static List<String> listForward(String host, int port) throws IOException {\n\t\ttry (Socket socket = connect(host, port)) {\n\t\t\tString cmd = \"0011host:list-forward\";\n\t\t\tInputStream inputStream = socket.getInputStream();\n\t\t\tOutputStream outputStream = socket.getOutputStream();\n\t\t\toutputStream.write(cmd.getBytes(ADB_CHARSET));\n\t\t\tif (isOkay(inputStream, \"listForward\")) {\n\t\t\t\tbyte[] bytes = readServiceProtocol(inputStream);\n\t\t\t\tif (bytes != null) {\n\t\t\t\t\tString[] forwards = new String(bytes, ADB_CHARSET).split(\"\\n\");\n\t\t\t\t\treturn Stream.of(forwards).map(String::trim).collect(Collectors.toList());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\tpublic static boolean removeForward(String host, int port, String serial, String localPort) throws IOException {\n\t\ttry (Socket socket = connect(host, port)) {\n\t\t\tString cmd = String.format(\"host:killforward:tcp:%s\", localPort);\n\t\t\tcmd = String.format(\"%04x%s\", cmd.length(), cmd);\n\t\t\tInputStream inputStream = socket.getInputStream();\n\t\t\tOutputStream outputStream = socket.getOutputStream();\n\t\t\tif (setSerial(serial, outputStream, inputStream)) {\n\t\t\t\toutputStream.write(cmd.getBytes(ADB_CHARSET));\n\t\t\t\treturn isOkay(inputStream, \"removeForward1\") && isOkay(inputStream, \"removeForward2\");\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t// Little endian\n\tprivate static int readInt(byte[] bytes, int start) {\n\t\tint result = (bytes[start] & 0xff);\n\t\tresult += ((bytes[start + 1] & 0xff) << 8);\n\t\tresult += ((bytes[start + 2] & 0xff) << 16);\n\t\tresult += (bytes[start + 3] & 0xff) << 24;\n\t\treturn result;\n\t}\n\n\tprivate static byte[] appendBytes(byte[] dest, byte[] src, int realSrcSize) {\n\t\tbyte[] rst = new byte[dest.length + realSrcSize];\n\t\tSystem.arraycopy(dest, 0, rst, 0, dest.length);\n\t\tSystem.arraycopy(src, 0, rst, dest.length, realSrcSize);\n\t\treturn rst;\n\t}\n\n\tprivate static final Pattern SERIAL_PATTERN = Pattern.compile(\"^[\\\\w-]{10,20}$\");\n\n\tprivate static void checkSerial(String serial) {\n\t\tif (!SERIAL_PATTERN.matcher(serial).matches()) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid serial: \" + serial);\n\t\t}\n\t}\n\n\t/**\n\t * Convert 4 hex characters to int\n\t *\n\t * @param hex\n\t * @return\n\t */\n\tprivate static int hexToInt(byte[] hex) {\n\t\tint n = 0;\n\t\tbyte b;\n\t\tfor (int i = 0; i < 4; i++) {\n\t\t\tb = hex[i];\n\t\t\tswitch (b) {\n\t\t\t\tcase '0':\n\t\t\t\tcase '1':\n\t\t\t\tcase '2':\n\t\t\t\tcase '3':\n\t\t\t\tcase '4':\n\t\t\t\tcase '5':\n\t\t\t\tcase '6':\n\t\t\t\tcase '7':\n\t\t\t\tcase '8':\n\t\t\t\tcase '9':\n\t\t\t\t\tb -= '0';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'a':\n\t\t\t\tcase 'b':\n\t\t\t\tcase 'c':\n\t\t\t\tcase 'd':\n\t\t\t\tcase 'e':\n\t\t\t\tcase 'f':\n\t\t\t\t\tb = (byte) (b - 'a' + 10);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'A':\n\t\t\t\tcase 'B':\n\t\t\t\tcase 'C':\n\t\t\t\tcase 'D':\n\t\t\t\tcase 'E':\n\t\t\t\tcase 'F':\n\t\t\t\t\tb = (byte) (b - 'A' + 10);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tn = (n << 4) | (b & 0xff);\n\t\t}\n\t\treturn n;\n\t}\n\n\tpublic interface JDWPProcessListener {\n\t\tvoid jdwpProcessOccurred(ADBDevice device, Set<String> id);\n\n\t\tvoid jdwpListenerClosed(ADBDevice device);\n\t}\n\n\tpublic interface DeviceStateListener {\n\t\tvoid onDeviceStatusChange(List<ADBDeviceInfo> deviceInfoList);\n\n\t\tvoid adbDisconnected();\n\t}\n\n\tpublic static class Process {\n\t\tpublic String user;\n\t\tpublic String pid;\n\t\tpublic String ppid;\n\t\tpublic String name;\n\n\t\tpublic static Process make(String processLine) {\n\t\t\tString[] fields = processLine.split(\"\\\\s+\");\n\t\t\tif (fields.length >= 4) {\n\t\t\t\t// 0 for user, 1 for pid, 2 for ppid, the last one for name\n\t\t\t\tProcess proc = new Process();\n\t\t\t\tproc.user = fields[0];\n\t\t\t\tproc.pid = fields[1];\n\t\t\t\tproc.ppid = fields[2];\n\t\t\t\tproc.name = fields[fields.length - 1];\n\t\t\t\treturn proc;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn new StringJoiner(\", \", Process.class.getSimpleName() + \"[\", \"]\")\n\t\t\t\t\t.add(\"user='\" + user + \"'\")\n\t\t\t\t\t.add(\"pid='\" + pid + \"'\")\n\t\t\t\t\t.add(\"ppid='\" + ppid + \"'\")\n\t\t\t\t\t.add(\"name='\" + name + \"'\")\n\t\t\t\t\t.toString();\n\t\t}\n\t}\n\n\tprivate static class ShellProtocol {\n\t\tprivate static final int ID_STD_IN = 0;\n\t\tprivate static final int ID_STD_OUT = 1;\n\t\tprivate static final int ID_STD_ERR = 2;\n\t\tprivate static final int ID_EXIT = 3;\n\n\t\t// Close subprocess stdin if possible.\n\t\tprivate static final int ID_CLOSE_STDIN = 4;\n\n\t\t// Window size change (an ASCII version of struct winsize).\n\t\tprivate static final int ID_WINDOW_SIZE_CHANGE = 5;\n\n\t\t// Indicates an invalid or unknown packet.\n\t\tprivate static final int ID_INVALID = 255;\n\n\t\tpublic static byte[] readStdout(InputStream inputStream) throws IOException {\n\t\t\tbyte[] header = new byte[5];\n\t\t\tByteArrayOutputStream payload = new ByteArrayOutputStream();\n\t\t\tbyte[] tempBuf = new byte[1024];\n\t\t\tfor (boolean exit = false; !exit;) {\n\t\t\t\tIOUtils.read(inputStream, header);\n\t\t\t\texit = header[0] == ID_EXIT;\n\t\t\t\tint payloadSize = readInt(header, 1);\n\t\t\t\tif (tempBuf.length < payloadSize) {\n\t\t\t\t\ttempBuf = new byte[payloadSize];\n\t\t\t\t}\n\t\t\t\tint readSize = IOUtils.read(inputStream, tempBuf, 0, payloadSize);\n\t\t\t\tif (readSize != payloadSize) {\n\t\t\t\t\tLOG.error(\"Failed to read ShellProtocol data\");\n\t\t\t\t\treturn null; // we don't want corrupted data.\n\t\t\t\t}\n\t\t\t\tpayload.write(tempBuf, 0, readSize);\n\t\t\t}\n\t\t\treturn payload.toByteArray();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/protocol/ADBDevice.java",
    "content": "package jadx.gui.device.protocol;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.Socket;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.log.LogUtils;\nimport jadx.gui.device.protocol.ADB.JDWPProcessListener;\nimport jadx.gui.device.protocol.ADB.Process;\n\nimport static jadx.gui.device.protocol.ADB.ADB_CHARSET;\n\npublic class ADBDevice {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ADBDevice.class);\n\n\tprivate static final String CMD_TRACK_JDWP = \"000atrack-jdwp\";\n\tprivate static final Pattern TIMESTAMP_FORMAT = Pattern.compile(\"^[0-9]{2}\\\\-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\\\\.[0-9]{3}$\");\n\tADBDeviceInfo info;\n\tString androidReleaseVer;\n\tvolatile Socket jdwpListenerSock;\n\n\tpublic ADBDevice(ADBDeviceInfo info) {\n\t\tthis.info = info;\n\t}\n\n\tpublic ADBDeviceInfo getDeviceInfo() {\n\t\treturn info;\n\t}\n\n\tpublic boolean updateDeviceInfo(ADBDeviceInfo info) {\n\t\tif (info.getSerial() == null || info.getSerial().isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tboolean matched = this.info.getSerial().equals(info.getSerial());\n\t\tif (matched) {\n\t\t\tthis.info = info;\n\t\t}\n\t\treturn matched;\n\t}\n\n\tpublic String getSerial() {\n\t\treturn info.getSerial();\n\t}\n\n\tpublic boolean removeForward(String localPort) throws IOException {\n\t\treturn ADB.removeForward(info.getAdbHost(), info.getAdbPort(), info.getSerial(), localPort);\n\t}\n\n\tpublic ForwardResult forwardJDWP(String localPort, String jdwpPid) throws IOException {\n\t\ttry (Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort())) {\n\t\t\tString cmd = String.format(\"host:forward:tcp:%s;jdwp:%s\", localPort, jdwpPid);\n\t\t\tcmd = String.format(\"%04x%s\", cmd.length(), cmd);\n\t\t\tInputStream inputStream = socket.getInputStream();\n\t\t\tOutputStream outputStream = socket.getOutputStream();\n\t\t\tForwardResult rst;\n\t\t\tif (ADB.setSerial(info.getSerial(), outputStream, inputStream)) {\n\t\t\t\toutputStream.write(cmd.getBytes(ADB_CHARSET));\n\t\t\t\tif (!ADB.isOkay(inputStream, \"forwardJDWP1\")) {\n\t\t\t\t\trst = new ForwardResult(1, ADB.readServiceProtocol(inputStream));\n\t\t\t\t} else if (!ADB.isOkay(inputStream, \"forwardJDWP2\")) {\n\t\t\t\t\trst = new ForwardResult(2, ADB.readServiceProtocol(inputStream));\n\t\t\t\t} else {\n\t\t\t\t\trst = new ForwardResult(0, null);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trst = new ForwardResult(1, \"Unknown error.\".getBytes(ADB_CHARSET));\n\t\t\t}\n\t\t\treturn rst;\n\t\t}\n\t}\n\n\tpublic static class ForwardResult {\n\t\t/**\n\t\t * 0 for success, 1 for failed at binding to local tcp, 2 for failed at remote.\n\t\t */\n\t\tpublic int state;\n\t\tpublic String desc;\n\n\t\tpublic ForwardResult(int state, byte[] desc) {\n\t\t\tif (desc != null) {\n\t\t\t\tthis.desc = new String(desc, ADB_CHARSET);\n\t\t\t} else {\n\t\t\t\tthis.desc = \"\";\n\t\t\t}\n\t\t\tthis.state = state;\n\t\t}\n\t}\n\n\t/**\n\t * @return pid otherwise -1\n\t */\n\tpublic int launchApp(String fullAppName) throws IOException, InterruptedException {\n\t\tbyte[] res;\n\t\ttry (Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort())) {\n\t\t\tString cmd = \"am start -D -n \" + fullAppName;\n\t\t\tres = ADB.execShellCommandRaw(info.getSerial(), cmd, socket.getOutputStream(), socket.getInputStream());\n\t\t\tif (res == null) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t\tString rst = new String(res, ADB_CHARSET).trim();\n\t\tif (rst.startsWith(\"Starting: Intent {\") && rst.endsWith(fullAppName + \" }\")) {\n\t\t\tThread.sleep(40);\n\t\t\tString pkg = fullAppName.split(\"/\")[0];\n\t\t\tfor (Process process : getProcessByPkg(pkg)) {\n\t\t\t\treturn Integer.parseInt(process.pid);\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\t/**\n\t * @return binary output of logcat\n\t */\n\tpublic byte[] getBinaryLogcat() throws IOException {\n\t\ttry (Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort())) {\n\t\t\tString cmd = \"logcat -dB\";\n\t\t\treturn ADB.execShellCommandRaw(info.getSerial(), cmd, socket.getOutputStream(), socket.getInputStream());\n\t\t}\n\t}\n\n\t/**\n\t * @return binary output of logcat after provided timestamp\n\t *         Timestamp is in the format 09-08 02:18:03.131\n\t */\n\tpublic byte[] getBinaryLogcat(String timestamp) throws IOException {\n\t\ttry (Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort())) {\n\t\t\tMatcher matcher = TIMESTAMP_FORMAT.matcher(timestamp);\n\t\t\tif (!matcher.find()) {\n\t\t\t\tLOG.error(\"Invalid Logcat Timestamp {}\", timestamp);\n\t\t\t}\n\t\t\tString cmd = \"logcat -dB -t \\\"\" + timestamp + \"\\\"\";\n\t\t\treturn ADB.execShellCommandRaw(info.getSerial(), cmd, socket.getOutputStream(), socket.getInputStream());\n\t\t}\n\t}\n\n\t/**\n\t * Binary output of logcat -c\n\t */\n\tpublic void clearLogcat() throws IOException {\n\t\ttry (Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort())) {\n\t\t\tString cmd = \"logcat -c\";\n\t\t\tADB.execShellCommandRaw(info.getSerial(), cmd, socket.getOutputStream(), socket.getInputStream());\n\t\t}\n\t}\n\n\t/**\n\t * @return Timezone for the attached android device\n\t */\n\tpublic String getTimezone() throws IOException {\n\t\ttry (Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort())) {\n\t\t\tString cmd = \"getprop persist.sys.timezone\";\n\t\t\tbyte[] tz = ADB.execShellCommandRaw(info.getSerial(), cmd, socket.getOutputStream(), socket.getInputStream());\n\t\t\tif (tz == null) {\n\t\t\t\tthrow new IOException(\"Failed to get timezone\");\n\t\t\t}\n\t\t\treturn new String(tz, ADB_CHARSET).trim();\n\t\t}\n\t}\n\n\tpublic String getAndroidReleaseVersion() {\n\t\tif (!StringUtils.isEmpty(androidReleaseVer)) {\n\t\t\treturn androidReleaseVer;\n\t\t}\n\t\ttry {\n\t\t\tList<String> list = getProp(\"ro.build.version.release\");\n\t\t\tif (!list.isEmpty()) {\n\t\t\t\treturn list.get(0);\n\t\t\t}\n\t\t\tLOG.error(\"Failed to get android release version - no result\");\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to get android release version\", e);\n\t\t\tandroidReleaseVer = \"\";\n\t\t}\n\t\treturn androidReleaseVer;\n\t}\n\n\tpublic List<String> getProp(String entry) throws IOException {\n\t\tLOG.debug(\"ADB getProp({})\", entry);\n\t\ttry (Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort())) {\n\t\t\tList<String> props = Collections.emptyList();\n\t\t\tString cmd = \"getprop\";\n\t\t\tif (!StringUtils.isEmpty(entry)) {\n\t\t\t\tcmd += \" \" + entry;\n\t\t\t}\n\t\t\tbyte[] payload = ADB.execShellCommandRaw(info.getSerial(), cmd,\n\t\t\t\t\tsocket.getOutputStream(), socket.getInputStream());\n\t\t\tif (payload != null) {\n\t\t\t\tprops = new ArrayList<>();\n\t\t\t\tString[] lines = new String(payload, ADB_CHARSET).split(\"\\n\");\n\t\t\t\tfor (String line : lines) {\n\t\t\t\t\tline = line.trim();\n\t\t\t\t\tif (!line.isEmpty()) {\n\t\t\t\t\t\tprops.add(line);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tLOG.trace(\"ADB getProp({}) = {}\", entry, props);\n\t\t\treturn props;\n\t\t}\n\t}\n\n\tpublic List<Process> getProcessByPkg(String pkg) throws IOException {\n\t\treturn getProcessList(\"ps | grep \" + pkg);\n\t}\n\n\tpublic List<Process> getProcessList() throws IOException {\n\t\treturn getProcessList(\"ps\");\n\t}\n\n\tprivate List<Process> getProcessList(String cmd) throws IOException {\n\t\ttry (Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort())) {\n\t\t\tList<Process> procs = new ArrayList<>();\n\t\t\tbyte[] payload = ADB.execShellCommandRaw(info.getSerial(), cmd,\n\t\t\t\t\tsocket.getOutputStream(), socket.getInputStream());\n\t\t\tif (payload != null) {\n\t\t\t\tString ps = new String(payload, ADB_CHARSET);\n\t\t\t\t// LOG.trace(\"ADB getProcessList({}) = {}\", cmd, ps);\n\t\t\t\tString[] psLines = ps.split(\"\\n\");\n\t\t\t\tfor (String line : psLines) {\n\t\t\t\t\tline = line.trim();\n\t\t\t\t\tif (line.isEmpty()) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tProcess proc = Process.make(line);\n\t\t\t\t\tif (proc != null) {\n\t\t\t\t\t\tprocs.add(proc);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tLOG.error(\"Unexpected process info data received: \\\"{}\\\"\", LogUtils.escape(line));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn procs;\n\t\t}\n\t}\n\n\tpublic boolean listenForJDWP(JDWPProcessListener listener) throws IOException {\n\t\tif (this.jdwpListenerSock != null) {\n\t\t\treturn false;\n\t\t}\n\t\tjdwpListenerSock = ADB.connect(this.info.getAdbHost(), this.info.getAdbPort());\n\t\tInputStream inputStream = jdwpListenerSock.getInputStream();\n\t\tOutputStream outputStream = jdwpListenerSock.getOutputStream();\n\t\tif (ADB.setSerial(info.getSerial(), outputStream, inputStream)\n\t\t\t\t&& ADB.execCommandAsync(outputStream, inputStream, CMD_TRACK_JDWP)) {\n\t\t\tnew Thread(() -> {\n\t\t\t\tfor (;;) {\n\t\t\t\t\tbyte[] res = ADB.readServiceProtocol(inputStream);\n\t\t\t\t\tif (res != null) {\n\t\t\t\t\t\tif (listener != null) {\n\t\t\t\t\t\t\tString payload = new String(res, ADB_CHARSET);\n\t\t\t\t\t\t\tString[] ids = payload.split(\"\\n\");\n\t\t\t\t\t\t\tSet<String> idList = new HashSet<>(ids.length);\n\t\t\t\t\t\t\tfor (String id : ids) {\n\t\t\t\t\t\t\t\tif (!id.trim().isEmpty()) {\n\t\t\t\t\t\t\t\t\tidList.add(id);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (idList.isEmpty()) {\n\t\t\t\t\t\t\t\tLOG.info(\"No debuggable app process found on device {}\", info.getSerial());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlistener.jdwpProcessOccurred(this, idList);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else { // socket disconnected\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (listener != null) {\n\t\t\t\t\tthis.jdwpListenerSock = null;\n\t\t\t\t\tlistener.jdwpListenerClosed(this);\n\t\t\t\t}\n\t\t\t}).start();\n\t\t\treturn true;\n\t\t} else {\n\t\t\tjdwpListenerSock.close();\n\t\t\tjdwpListenerSock = null;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic void stopListenForJDWP() {\n\t\tif (jdwpListenerSock != null) {\n\t\t\ttry {\n\t\t\t\tjdwpListenerSock.close();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"JDWP socket close failed\", e);\n\t\t\t}\n\t\t}\n\t\tthis.jdwpListenerSock = null;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn info.getSerial().hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (obj instanceof ADBDevice) {\n\t\t\tString otherSerial = ((ADBDevice) obj).getDeviceInfo().getSerial();\n\t\t\treturn otherSerial.equals(info.getSerial());\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn info.getAllInfo();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/device/protocol/ADBDeviceInfo.java",
    "content": "package jadx.gui.device.protocol;\n\nimport java.util.Map;\nimport java.util.TreeMap;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.log.LogUtils;\n\npublic class ADBDeviceInfo {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ADBDeviceInfo.class);\n\tprivate final String adbHost;\n\tprivate final int adbPort;\n\tprivate final String serial;\n\tprivate final String state;\n\tprivate final String model;\n\tprivate final String allInfo;\n\n\t/**\n\t * Store the device info property values like \"device\" \"model\" \"product\" or \"transport_id\"\n\t */\n\tprivate final Map<String, String> propertiesMap = new TreeMap<>();\n\n\tADBDeviceInfo(String info, String host, int port) {\n\t\tString[] infoFields = info.trim().split(\"\\\\s+\");\n\t\tallInfo = String.join(\" \", infoFields);\n\t\tif (infoFields.length > 2) {\n\t\t\tserial = infoFields[0];\n\t\t\tstate = infoFields[1];\n\n\t\t\tfor (int i = 2; i < infoFields.length; i++) {\n\t\t\t\tString field = infoFields[i];\n\t\t\t\tint idx = field.indexOf(':');\n\t\t\t\tif (idx > 0) {\n\t\t\t\t\tString key = field.substring(0, idx);\n\t\t\t\t\tString value = field.substring(idx + 1);\n\t\t\t\t\tif (!value.isEmpty()) {\n\t\t\t\t\t\tpropertiesMap.put(key, value);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tmodel = propertiesMap.getOrDefault(\"model\", serial);\n\t\t} else {\n\t\t\tLOG.error(\"Unable to extract device information from {}\", LogUtils.escape(info));\n\t\t\tserial = \"\";\n\t\t\tstate = \"unknown\";\n\t\t\tmodel = \"unknown\";\n\t\t}\n\t\tadbHost = host;\n\t\tadbPort = port;\n\t}\n\n\tpublic boolean isOnline() {\n\t\treturn state.equals(\"device\");\n\t}\n\n\tpublic String getAdbHost() {\n\t\treturn adbHost;\n\t}\n\n\tpublic int getAdbPort() {\n\t\treturn adbPort;\n\t}\n\n\tpublic String getSerial() {\n\t\treturn serial;\n\t}\n\n\tpublic String getState() {\n\t\treturn state;\n\t}\n\n\tpublic String getModel() {\n\t\treturn model;\n\t}\n\n\tpublic String getAllInfo() {\n\t\treturn allInfo;\n\t}\n\n\tpublic String getProperty(String key) {\n\t\treturn this.propertiesMap.get(key);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn allInfo;\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/events/JadxGuiEvents.java",
    "content": "package jadx.gui.events;\n\nimport jadx.api.plugins.events.JadxEventType;\nimport jadx.gui.events.types.TreeUpdate;\n\nimport static jadx.api.plugins.events.JadxEventType.create;\n\npublic class JadxGuiEvents {\n\n\tpublic static final JadxEventType<TreeUpdate> TREE_UPDATE = create(\"TREE_UPDATE\");\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/events/services/RenameService.java",
    "content": "package jadx.gui.events.services;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxDecompiler;\nimport jadx.api.JavaNode;\nimport jadx.api.data.ICodeRename;\nimport jadx.api.data.impl.JadxCodeData;\nimport jadx.api.plugins.events.JadxEvents;\nimport jadx.api.plugins.events.types.NodeRenamedByUser;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.jobs.TaskStatus;\nimport jadx.gui.settings.JadxProject;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.treemodel.JRenameNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.codearea.ClassCodeContentPanel;\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.CacheObject;\nimport jadx.gui.utils.JNodeCache;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\n/**\n * Rename service listen for user rename events.\n * For each event:\n * - add/update rename entry in project code data\n * - update code and/or invalidate cache for related classes\n * - apply all needed UI updates (tabs, classes tree)\n */\npublic class RenameService {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(RenameService.class);\n\n\tpublic static void init(MainWindow mainWindow) {\n\t\tRenameService renameService = new RenameService(mainWindow);\n\t\tmainWindow.events().global().addListener(JadxEvents.NODE_RENAMED_BY_USER, renameService::process);\n\t}\n\n\tprivate final MainWindow mainWindow;\n\n\tprivate RenameService(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t}\n\n\tprivate void process(NodeRenamedByUser event) {\n\t\ttry {\n\t\t\tLOG.debug(\"Applying rename event: {}\", event);\n\t\t\tlong timeStarted = System.nanoTime();\n\t\t\tJRenameNode node = getRenameNode(event);\n\t\t\tupdateCodeRenames(set -> processRename(node, event, set));\n\t\t\trefreshState(node, timeStarted);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Rename failed\", e);\n\t\t\tUiUtils.errorMessage(mainWindow, \"Rename failed:\\n\" + Utils.getStackTrace(e));\n\t\t}\n\t}\n\n\tprivate @NotNull JRenameNode getRenameNode(NodeRenamedByUser event) {\n\t\tObject renameNode = event.getRenameNode();\n\t\tif (renameNode instanceof JRenameNode) {\n\t\t\treturn (JRenameNode) renameNode;\n\t\t}\n\t\tJadxDecompiler decompiler = mainWindow.getWrapper().getDecompiler();\n\t\tJavaNode javaNode = decompiler.getJavaNodeByRef(event.getNode());\n\t\tif (javaNode != null) {\n\t\t\tJNode node = mainWindow.getCacheObject().getNodeCache().makeFrom(javaNode);\n\t\t\tif (node instanceof JRenameNode) {\n\t\t\t\treturn (JRenameNode) node;\n\t\t\t}\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Failed to resolve node: \" + event.getNode());\n\t}\n\n\tprivate void processRename(JRenameNode node, NodeRenamedByUser event, Set<ICodeRename> renames) {\n\t\tICodeRename rename = node.buildCodeRename(event.getNewName(), renames);\n\t\trenames.remove(rename);\n\t\tif (event.isResetName() || event.getNewName().isEmpty()) {\n\t\t\tnode.removeAlias();\n\t\t} else {\n\t\t\trenames.add(rename);\n\t\t}\n\t}\n\n\tprivate void updateCodeRenames(Consumer<Set<ICodeRename>> updater) {\n\t\tJadxProject project = mainWindow.getProject();\n\t\tJadxCodeData codeData = project.getCodeData();\n\t\tif (codeData == null) {\n\t\t\tcodeData = new JadxCodeData();\n\t\t}\n\t\tSet<ICodeRename> set = new HashSet<>(codeData.getRenames());\n\t\tupdater.accept(set);\n\t\tList<ICodeRename> list = new ArrayList<>(set);\n\t\tCollections.sort(list);\n\t\tcodeData.setRenames(list);\n\t\tproject.setCodeData(codeData);\n\t}\n\n\tprivate void refreshState(JRenameNode node, long timeStarted) {\n\t\tList<JavaNode> toUpdate = new ArrayList<>();\n\t\tnode.addUpdateNodes(toUpdate);\n\n\t\tJNodeCache nodeCache = mainWindow.getCacheObject().getNodeCache();\n\t\tSet<JClass> updatedTopClasses = toUpdate\n\t\t\t\t.stream()\n\t\t\t\t.map(JavaNode::getTopParentClass)\n\t\t\t\t.map(nodeCache::makeFrom)\n\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t.collect(Collectors.toSet());\n\n\t\tLOG.debug(\"Classes to update: {}\", updatedTopClasses);\n\t\tif (updatedTopClasses.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tmainWindow.getBackgroundExecutor().execute(\"Refreshing\",\n\t\t\t\t() -> {\n\t\t\t\t\tmainWindow.getWrapper().reloadCodeData();\n\t\t\t\t\t// Reload all the classes in the background process, rather than using the UI thread for\n\t\t\t\t\t// decompilation. We don't just use codeArea.backgroundRefreshClass because it would spawn a\n\t\t\t\t\t// separate background process, whereas we would like it to happen in this one.\n\t\t\t\t\tfor (ContentPanel tab : mainWindow.getTabbedPane().getTabs()) {\n\t\t\t\t\t\tJClass rootClass = tab.getNode().getRootClass();\n\t\t\t\t\t\tif (updatedTopClasses.contains(rootClass)) {\n\t\t\t\t\t\t\trootClass.reload(mainWindow.getCacheObject());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tUiUtils.uiRunAndWait(() -> refreshTabs(mainWindow.getTabbedPane(), updatedTopClasses));\n\t\t\t\t\trefreshClasses(updatedTopClasses);\n\t\t\t\t\tLOG.debug(\"Finished rename, took \" + (System.nanoTime() - timeStarted) + \" ns\");\n\t\t\t\t},\n\t\t\t\t(status) -> {\n\t\t\t\t\tif (status == TaskStatus.CANCEL_BY_MEMORY) {\n\t\t\t\t\t\tmainWindow.showHeapUsageBar();\n\t\t\t\t\t\tUiUtils.errorMessage(mainWindow, NLS.str(\"message.memoryLow\"));\n\t\t\t\t\t}\n\t\t\t\t\tnode.reload(mainWindow);\n\t\t\t\t});\n\t}\n\n\tprivate void refreshClasses(Set<JClass> updatedTopClasses) {\n\t\tCacheObject cache = mainWindow.getCacheObject();\n\t\tif (updatedTopClasses.size() < 10) {\n\t\t\t// small batch => reload\n\t\t\tLOG.debug(\"Classes to reload: {}\", updatedTopClasses.size());\n\t\t\tfor (JClass cls : updatedTopClasses) {\n\t\t\t\ttry {\n\t\t\t\t\tcls.reload(cache);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.error(\"Failed to reload class: {}\", cls.getFullName(), e);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// big batch => unload\n\t\t\tLOG.debug(\"Classes to unload: {}\", updatedTopClasses.size());\n\t\t\tfor (JClass cls : updatedTopClasses) {\n\t\t\t\ttry {\n\t\t\t\t\tcls.unload(cache);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.error(\"Failed to unload class: {}\", cls.getFullName(), e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void refreshTabs(TabbedPane tabbedPane, Set<JClass> updatedClasses) {\n\t\tfor (ContentPanel tab : tabbedPane.getTabs()) {\n\t\t\tJClass rootClass = tab.getNode().getRootClass();\n\t\t\tif (updatedClasses.remove(rootClass)) {\n\t\t\t\tClassCodeContentPanel contentPanel = (ClassCodeContentPanel) tab;\n\t\t\t\tCodeArea codeArea = (CodeArea) contentPanel.getJavaCodePanel().getCodeArea();\n\t\t\t\tcodeArea.refreshClass(true);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/events/types/JadxGuiEventsImpl.java",
    "content": "package jadx.gui.events.types;\n\nimport java.util.function.Consumer;\n\nimport jadx.api.plugins.events.IJadxEvent;\nimport jadx.api.plugins.events.IJadxEvents;\nimport jadx.api.plugins.events.JadxEventType;\nimport jadx.core.plugins.events.JadxEventsImpl;\n\n/**\n * Special events implementation to operate on both: global UI and project events.\n * Project events hold listeners only while a project opened and reset them on close.\n */\npublic class JadxGuiEventsImpl implements IJadxEvents {\n\n\tprivate final IJadxEvents global = new JadxEventsImpl();\n\tprivate final IJadxEvents project = new JadxEventsImpl();\n\n\tpublic IJadxEvents global() {\n\t\treturn global;\n\t}\n\n\t@Override\n\tpublic void send(IJadxEvent event) {\n\t\tglobal.send(event);\n\t\tproject.send(event);\n\t}\n\n\t@Override\n\tpublic <E extends IJadxEvent> void addListener(JadxEventType<E> eventType, Consumer<E> listener) {\n\t\tproject.addListener(eventType, listener);\n\t}\n\n\t@Override\n\tpublic <E extends IJadxEvent> void removeListener(JadxEventType<E> eventType, Consumer<E> listener) {\n\t\tproject.removeListener(eventType, listener);\n\t}\n\n\t@Override\n\tpublic void reset() {\n\t\tproject.reset();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/events/types/TreeUpdate.java",
    "content": "package jadx.gui.events.types;\n\nimport jadx.api.plugins.events.IJadxEvent;\nimport jadx.api.plugins.events.JadxEventType;\nimport jadx.gui.events.JadxGuiEvents;\nimport jadx.gui.treemodel.JRoot;\n\npublic class TreeUpdate implements IJadxEvent {\n\n\tprivate final JRoot jRoot;\n\n\tpublic TreeUpdate(JRoot jRoot) {\n\t\tthis.jRoot = jRoot;\n\t}\n\n\tpublic JRoot getJRoot() {\n\t\treturn jRoot;\n\t}\n\n\t@Override\n\tpublic JadxEventType<TreeUpdate> getType() {\n\t\treturn JadxGuiEvents.TREE_UPDATE;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/BackgroundExecutor.java",
    "content": "package jadx.gui.jobs;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.utils.tasks.ITaskExecutor;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.panel.ProgressPanel;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\n/**\n * Run tasks in the background with progress bar indication.\n * Use instance created in {@link MainWindow}.\n */\npublic class BackgroundExecutor {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(BackgroundExecutor.class);\n\n\tprivate final JadxSettings settings;\n\tprivate final ProgressUpdater progressUpdater;\n\n\tprivate ThreadPoolExecutor taskQueueExecutor;\n\tprivate final Map<Long, InternalTask> taskRunning = new ConcurrentHashMap<>();\n\tprivate final AtomicLong idSupplier = new AtomicLong(0);\n\n\tpublic BackgroundExecutor(JadxSettings settings, ProgressPanel progressPane) {\n\t\tthis.settings = Objects.requireNonNull(settings);\n\t\tthis.progressUpdater = new ProgressUpdater(progressPane, this::taskCanceled);\n\t\treset();\n\t}\n\n\tpublic synchronized void execute(IBackgroundTask task) {\n\t\tInternalTask internalTask = buildTask(task);\n\t\ttaskQueueExecutor.execute(() -> runTask(internalTask));\n\t}\n\n\tpublic synchronized Future<TaskStatus> executeWithFuture(IBackgroundTask task) {\n\t\tInternalTask internalTask = buildTask(task);\n\t\treturn taskQueueExecutor.submit(() -> {\n\t\t\trunTask(internalTask);\n\t\t\treturn internalTask.getStatus();\n\t\t});\n\t}\n\n\tpublic synchronized void cancelAll() {\n\t\ttry {\n\t\t\ttaskRunning.values().forEach(this::cancelTask);\n\t\t\ttaskQueueExecutor.shutdownNow();\n\t\t\tboolean complete = taskQueueExecutor.awaitTermination(3, TimeUnit.SECONDS);\n\t\t\tif (complete) {\n\t\t\t\tLOG.debug(\"Background task executor canceled successfully\");\n\t\t\t} else {\n\t\t\t\tString taskNames = taskRunning.values().stream()\n\t\t\t\t\t\t.map(t -> t.getBgTask().getTitle())\n\t\t\t\t\t\t.collect(Collectors.joining(\", \"));\n\t\t\t\tLOG.debug(\"Background task executor cancel failed. Running tasks: {}\", taskNames);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Error terminating task executor\", e);\n\t\t} finally {\n\t\t\treset();\n\t\t}\n\t}\n\n\tpublic synchronized void waitForComplete() {\n\t\ttry {\n\t\t\t// add empty task and wait its completion\n\t\t\ttaskQueueExecutor.submit(UiUtils.EMPTY_RUNNABLE).get();\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to wait tasks completion\", e);\n\t\t}\n\t}\n\n\tpublic void execute(String title, List<Runnable> backgroundJobs, Consumer<TaskStatus> onFinishUiRunnable) {\n\t\texecute(new SimpleTask(title, backgroundJobs, onFinishUiRunnable));\n\t}\n\n\tpublic void execute(String title, Runnable backgroundRunnable, Consumer<TaskStatus> onFinishUiRunnable) {\n\t\texecute(new SimpleTask(title, Collections.singletonList(backgroundRunnable), onFinishUiRunnable));\n\t}\n\n\tpublic void execute(String title, Runnable backgroundRunnable) {\n\t\texecute(new SimpleTask(title, Collections.singletonList(backgroundRunnable)));\n\t}\n\n\tpublic void startLoading(Runnable backgroundRunnable, Runnable onFinishUiRunnable) {\n\t\texecute(new SimpleTask(NLS.str(\"progress.load\"), backgroundRunnable, onFinishUiRunnable));\n\t}\n\n\tpublic void startLoading(Runnable backgroundRunnable) {\n\t\texecute(new SimpleTask(NLS.str(\"progress.load\"), backgroundRunnable));\n\t}\n\n\tprivate synchronized void reset() {\n\t\ttaskQueueExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1, Utils.simpleThreadFactory(\"bg\"));\n\t\ttaskRunning.clear();\n\t\tidSupplier.set(0);\n\t}\n\n\tprivate InternalTask buildTask(IBackgroundTask task) {\n\t\tlong id = idSupplier.incrementAndGet();\n\t\tInternalTask internalTask = new InternalTask(id, task);\n\t\ttaskRunning.put(id, internalTask);\n\t\treturn internalTask;\n\t}\n\n\tprivate void runTask(InternalTask internalTask) {\n\t\ttry {\n\t\t\tIBackgroundTask task = internalTask.getBgTask();\n\t\t\tITaskExecutor taskExecutor = task.scheduleTasks();\n\t\t\ttaskExecutor.setThreadsCount(settings.getThreadsCount());\n\t\t\tint tasksCount = taskExecutor.getTasksCount();\n\t\t\tinternalTask.setTaskExecutor(taskExecutor);\n\t\t\tinternalTask.setJobsCount(tasksCount);\n\t\t\tif (UiUtils.JADX_GUI_DEBUG) {\n\t\t\t\tLOG.debug(\"Starting background task '{}', jobs count: {}, time limit: {} ms, memory check: {}\",\n\t\t\t\t\t\ttask.getTitle(), tasksCount, task.timeLimit(), task.checkMemoryUsage());\n\t\t\t}\n\t\t\tlong startTime = System.currentTimeMillis();\n\t\t\tSupplier<TaskStatus> cancelCheck = buildCancelCheck(internalTask, startTime);\n\t\t\tinternalTask.taskStart(startTime, cancelCheck);\n\t\t\tprogressUpdater.addTask(internalTask);\n\t\t\ttaskExecutor.execute();\n\t\t\ttaskExecutor.awaitTermination();\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Task failed\", e);\n\t\t\tinternalTask.setStatus(TaskStatus.ERROR);\n\t\t} finally {\n\t\t\ttaskComplete(internalTask);\n\t\t}\n\t}\n\n\tprivate void taskComplete(InternalTask internalTask) {\n\t\ttry {\n\t\t\tIBackgroundTask task = internalTask.getBgTask();\n\t\t\tinternalTask.setJobsComplete(internalTask.getTaskExecutor().getProgress());\n\t\t\tinternalTask.setStatus(TaskStatus.COMPLETE);\n\t\t\tinternalTask.updateExecTime();\n\t\t\ttask.onDone(internalTask);\n\t\t\t// treat UI task operations as part of the task to not mix with others\n\t\t\tUiUtils.uiRunAndWait(() -> {\n\t\t\t\ttry {\n\t\t\t\t\ttask.onFinish(internalTask);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.error(\"Task onFinish failed\", e);\n\t\t\t\t\tinternalTask.setStatus(TaskStatus.ERROR);\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Task complete failed\", e);\n\t\t\tinternalTask.setStatus(TaskStatus.ERROR);\n\t\t} finally {\n\t\t\tinternalTask.taskComplete();\n\t\t\tprogressUpdater.taskComplete(internalTask);\n\t\t\tremoveTask(internalTask);\n\t\t}\n\t}\n\n\tprivate void removeTask(InternalTask internalTask) {\n\t\ttaskRunning.remove(internalTask.getId());\n\t}\n\n\tprivate void cancelTask(InternalTask internalTask) {\n\t\ttry {\n\t\t\tIBackgroundTask task = internalTask.getBgTask();\n\t\t\tif (!internalTask.isRunning()) {\n\t\t\t\t// task complete or not yet started\n\t\t\t\ttask.cancel();\n\t\t\t\tremoveTask(internalTask);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tITaskExecutor taskExecutor = internalTask.getTaskExecutor();\n\t\t\t// force termination\n\t\t\ttask.cancel();\n\t\t\ttaskExecutor.terminate();\n\n\t\t\tExecutorService executor = taskExecutor.getInternalExecutor();\n\t\t\tif (executor == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tint cancelTimeout = task.getCancelTimeoutMS();\n\t\t\tif (cancelTimeout != 0) {\n\t\t\t\tif (executor.awaitTermination(cancelTimeout, TimeUnit.MILLISECONDS)) {\n\t\t\t\t\tLOG.debug(\"Task cancel complete\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tLOG.debug(\"Forcing tasks cancel\");\n\t\t\texecutor.shutdownNow();\n\t\t\tboolean complete = executor.awaitTermination(task.getShutdownTimeoutMS(), TimeUnit.MILLISECONDS);\n\t\t\tLOG.debug(\"Forced task cancel status: {}\", complete\n\t\t\t\t\t? \"success\"\n\t\t\t\t\t: \"fail, still active: \" + (taskExecutor.getTasksCount() - taskExecutor.getProgress()));\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to cancel task: {}\", internalTask, e);\n\t\t}\n\t}\n\n\t/**\n\t * Task cancel notification from progress updater\n\t */\n\tprivate void taskCanceled(InternalTask task) {\n\t\tcancelTask(task);\n\t}\n\n\tprivate Supplier<TaskStatus> buildCancelCheck(InternalTask internalTask, long startTime) {\n\t\tIBackgroundTask task = internalTask.getBgTask();\n\t\tint timeLimit = task.timeLimit();\n\t\tlong waitUntilTime = timeLimit == 0 ? 0 : startTime + timeLimit;\n\t\tboolean checkMemoryUsage = task.checkMemoryUsage();\n\t\treturn () -> {\n\t\t\tif (task.isCanceled() || Thread.currentThread().isInterrupted()) {\n\t\t\t\treturn TaskStatus.CANCEL_BY_USER;\n\t\t\t}\n\t\t\tif (waitUntilTime != 0 && waitUntilTime < System.currentTimeMillis()) {\n\t\t\t\tLOG.warn(\"Task '{}' execution timeout, force cancel\", task.getTitle());\n\t\t\t\treturn TaskStatus.CANCEL_BY_TIMEOUT;\n\t\t\t}\n\t\t\tif (checkMemoryUsage && !UiUtils.isFreeMemoryAvailable()) {\n\t\t\t\tLOG.warn(\"High memory usage: {}\", UiUtils.memoryInfo());\n\t\t\t\tif (internalTask.getTaskExecutor().getThreadsCount() == 1) {\n\t\t\t\t\tLOG.warn(\"Task '{}' memory limit reached, force cancel\", task.getTitle());\n\t\t\t\t\treturn TaskStatus.CANCEL_BY_MEMORY;\n\t\t\t\t}\n\t\t\t\tLOG.warn(\"Low free memory, reduce processing threads count to 1\");\n\t\t\t\t// reduce threads count and continue\n\t\t\t\tinternalTask.getTaskExecutor().setThreadsCount(1);\n\t\t\t\tSystem.gc();\n\t\t\t\tUiUtils.sleep(1000); // wait GC\n\t\t\t\tif (!UiUtils.isFreeMemoryAvailable()) {\n\t\t\t\t\tLOG.error(\"Task '{}' memory limit reached (after GC), force cancel\", task.getTitle());\n\t\t\t\t\treturn TaskStatus.CANCEL_BY_MEMORY;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/Cancelable.java",
    "content": "package jadx.gui.jobs;\n\npublic interface Cancelable {\n\tboolean isCanceled();\n\n\tvoid cancel();\n\n\tdefault int getCancelTimeoutMS() {\n\t\treturn 2000;\n\t}\n\n\tdefault int getShutdownTimeoutMS() {\n\t\treturn 5000;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/CancelableBackgroundTask.java",
    "content": "package jadx.gui.jobs;\n\nimport java.util.concurrent.atomic.AtomicBoolean;\n\npublic abstract class CancelableBackgroundTask implements IBackgroundTask {\n\n\tprivate final AtomicBoolean cancel = new AtomicBoolean(false);\n\n\t@Override\n\tpublic boolean isCanceled() {\n\t\treturn cancel.get();\n\t}\n\n\t@Override\n\tpublic void cancel() {\n\t\tcancel.set(true);\n\t}\n\n\tpublic void resetCancel() {\n\t\tcancel.set(false);\n\t}\n\n\t@Override\n\tpublic boolean canBeCanceled() {\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/DecompileTask.java",
    "content": "package jadx.gui.jobs;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport javax.swing.JOptionPane;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeCache;\nimport jadx.api.JavaClass;\nimport jadx.api.utils.tasks.ITaskExecutor;\nimport jadx.commons.app.JadxCommonEnv;\nimport jadx.core.utils.tasks.TaskExecutor;\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\n\npublic class DecompileTask extends CancelableBackgroundTask {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DecompileTask.class);\n\n\tprivate static final int CLS_LIMIT = JadxCommonEnv.getInt(\"JADX_CLS_PROCESS_LIMIT\", 50);\n\n\tpublic static int calcDecompileTimeLimit(int classCount) {\n\t\treturn classCount * CLS_LIMIT + 5000;\n\t}\n\n\tprivate final MainWindow mainWindow;\n\tprivate final JadxWrapper wrapper;\n\tprivate final AtomicInteger complete = new AtomicInteger(0);\n\tprivate int expectedCompleteCount;\n\n\tprivate ProcessResult result;\n\n\tpublic DecompileTask(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.wrapper = mainWindow.getWrapper();\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn NLS.str(\"progress.decompile\");\n\t}\n\n\t@Override\n\tpublic ITaskExecutor scheduleTasks() {\n\t\tTaskExecutor executor = new TaskExecutor();\n\t\texecutor.addParallelTasks(scheduleJobs());\n\t\treturn executor;\n\t}\n\n\tpublic List<Runnable> scheduleJobs() {\n\t\tif (mainWindow.getCacheObject().isFullDecompilationFinished()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\n\t\tList<JavaClass> classes = wrapper.getIncludedClasses();\n\t\texpectedCompleteCount = classes.size();\n\t\tcomplete.set(0);\n\n\t\tList<List<JavaClass>> batches;\n\t\ttry {\n\t\t\tbatches = wrapper.buildDecompileBatches(classes);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Decompile batches build error\", e);\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn getJobs(batches);\n\t}\n\n\tprivate List<Runnable> getJobs(List<List<JavaClass>> batches) {\n\t\tICodeCache codeCache = wrapper.getArgs().getCodeCache();\n\t\tList<Runnable> jobs = new ArrayList<>(batches.size());\n\t\tfor (List<JavaClass> batch : batches) {\n\t\t\tjobs.add(() -> {\n\t\t\t\tfor (JavaClass cls : batch) {\n\t\t\t\t\tif (isCanceled()) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (!codeCache.contains(cls.getRawName())) {\n\t\t\t\t\t\t\tcls.decompile();\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\t\tLOG.error(\"Failed to decompile class: {}\", cls, e);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tcomplete.incrementAndGet();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\treturn jobs;\n\t}\n\n\t@Override\n\tpublic void onDone(ITaskInfo taskInfo) {\n\t\tlong taskTime = taskInfo.getTime();\n\t\tlong avgPerCls = taskTime / Math.max(expectedCompleteCount, 1);\n\t\tint timeLimit = timeLimit();\n\t\tint skippedCls = expectedCompleteCount - complete.get();\n\t\tif (LOG.isInfoEnabled()) {\n\t\t\tLOG.info(\"Decompile and index task complete in \" + taskTime + \" ms (avg \" + avgPerCls + \" ms per class)\"\n\t\t\t\t\t+ \", classes: \" + expectedCompleteCount\n\t\t\t\t\t+ \", skipped: \" + skippedCls\n\t\t\t\t\t+ \", time limit:{ total: \" + timeLimit + \"ms, per cls: \" + CLS_LIMIT + \"ms }\"\n\t\t\t\t\t+ \", status: \" + taskInfo.getStatus());\n\t\t}\n\t\tresult = new ProcessResult(skippedCls, taskInfo.getStatus(), timeLimit);\n\n\t\twrapper.unloadClasses();\n\t\tprocessDecompilationResults();\n\t\tSystem.gc();\n\n\t\tmainWindow.getCacheObject().setFullDecompilationFinished(skippedCls == 0);\n\t}\n\n\tprivate void processDecompilationResults() {\n\t\tint skippedCls = result.getSkipped();\n\t\tif (skippedCls == 0) {\n\t\t\treturn;\n\t\t}\n\t\tTaskStatus status = result.getStatus();\n\t\tLOG.warn(\"Decompile and indexing of some classes skipped: {}, status: {}\", skippedCls, status);\n\t\tswitch (status) {\n\t\t\tcase CANCEL_BY_USER: {\n\t\t\t\tString reason = NLS.str(\"message.userCancelTask\");\n\t\t\t\tString message = NLS.str(\"message.indexIncomplete\", reason, skippedCls);\n\t\t\t\tJOptionPane.showMessageDialog(mainWindow, message);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase CANCEL_BY_TIMEOUT: {\n\t\t\t\tString reason = NLS.str(\"message.taskTimeout\", result.getTimeLimit());\n\t\t\t\tString message = NLS.str(\"message.indexIncomplete\", reason, skippedCls);\n\t\t\t\tJOptionPane.showMessageDialog(mainWindow, message);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase CANCEL_BY_MEMORY: {\n\t\t\t\tmainWindow.showHeapUsageBar();\n\t\t\t\tJOptionPane.showMessageDialog(mainWindow, NLS.str(\"message.indexingClassesSkipped\", skippedCls));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean canBeCanceled() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int timeLimit() {\n\t\treturn calcDecompileTimeLimit(expectedCompleteCount);\n\t}\n\n\t@Override\n\tpublic boolean checkMemoryUsage() {\n\t\treturn true;\n\t}\n\n\tpublic ProcessResult getResult() {\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/ExportTask.java",
    "content": "package jadx.gui.jobs;\n\nimport java.io.File;\n\nimport javax.swing.JOptionPane;\n\nimport jadx.api.ICodeCache;\nimport jadx.api.utils.tasks.ITaskExecutor;\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.cache.code.CodeCacheMode;\nimport jadx.gui.cache.code.FixedCodeCache;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\n\npublic class ExportTask extends CancelableBackgroundTask {\n\n\tprivate final MainWindow mainWindow;\n\tprivate final JadxWrapper wrapper;\n\tprivate final File saveDir;\n\n\tprivate int timeLimit;\n\tprivate ICodeCache uiCodeCache;\n\n\tpublic ExportTask(MainWindow mainWindow, JadxWrapper wrapper, File saveDir) {\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.wrapper = wrapper;\n\t\tthis.saveDir = saveDir;\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn NLS.str(\"msg.saving_sources\");\n\t}\n\n\t@Override\n\tpublic ITaskExecutor scheduleTasks() {\n\t\twrapCodeCache();\n\t\twrapper.getArgs().setRootDir(saveDir);\n\t\tITaskExecutor saveTasks = wrapper.getDecompiler().getSaveTaskExecutor();\n\t\tthis.timeLimit = DecompileTask.calcDecompileTimeLimit(saveTasks.getTasksCount());\n\t\treturn saveTasks;\n\t}\n\n\tprivate void wrapCodeCache() {\n\t\tuiCodeCache = wrapper.getArgs().getCodeCache();\n\t\tif (mainWindow.getSettings().getCodeCacheMode() != CodeCacheMode.DISK) {\n\t\t\t// do not save newly decompiled code in cache to not increase memory usage\n\t\t\t// TODO: maybe make memory limited cache?\n\t\t\twrapper.getArgs().setCodeCache(new FixedCodeCache(uiCodeCache));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onFinish(ITaskInfo taskInfo) {\n\t\t// restore initial code cache\n\t\twrapper.getArgs().setCodeCache(uiCodeCache);\n\t\tif (taskInfo.getJobsSkipped() == 0) {\n\t\t\treturn;\n\t\t}\n\t\tString reason = getIncompleteReason(taskInfo.getStatus());\n\t\tif (reason != null) {\n\t\t\tJOptionPane.showMessageDialog(mainWindow,\n\t\t\t\t\tNLS.str(\"message.saveIncomplete\", reason, taskInfo.getJobsSkipped()),\n\t\t\t\t\tNLS.str(\"message.errorTitle\"), JOptionPane.ERROR_MESSAGE);\n\t\t}\n\t}\n\n\tprivate String getIncompleteReason(TaskStatus status) {\n\t\tswitch (status) {\n\t\t\tcase CANCEL_BY_USER:\n\t\t\t\treturn NLS.str(\"message.userCancelTask\");\n\n\t\t\tcase CANCEL_BY_TIMEOUT:\n\t\t\t\treturn NLS.str(\"message.taskTimeout\", timeLimit());\n\n\t\t\tcase CANCEL_BY_MEMORY:\n\t\t\t\tmainWindow.showHeapUsageBar();\n\t\t\t\treturn NLS.str(\"message.memoryLow\");\n\n\t\t\tcase ERROR:\n\t\t\t\treturn NLS.str(\"message.taskError\");\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic int timeLimit() {\n\t\treturn timeLimit;\n\t}\n\n\t@Override\n\tpublic boolean canBeCanceled() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean checkMemoryUsage() {\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/IBackgroundTask.java",
    "content": "package jadx.gui.jobs;\n\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.utils.tasks.ITaskExecutor;\n\npublic interface IBackgroundTask extends Cancelable {\n\n\tString getTitle();\n\n\tITaskExecutor scheduleTasks();\n\n\t/**\n\t * Called on executor thread after the all jobs finished.\n\t */\n\tdefault void onDone(ITaskInfo taskInfo) {\n\t}\n\n\t/**\n\t * Executed on the Event Dispatch Thread after the all jobs finished.\n\t */\n\tdefault void onFinish(ITaskInfo taskInfo) {\n\t}\n\n\tdefault boolean canBeCanceled() {\n\t\treturn false;\n\t}\n\n\t/**\n\t * Global (for all jobs) time limit in milliseconds (0 - to disable).\n\t */\n\tdefault int timeLimit() {\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Executor will check memory usage on every tick and cancel job if no free memory available.\n\t */\n\tdefault boolean checkMemoryUsage() {\n\t\treturn false;\n\t}\n\n\t/**\n\t * Get task progress (Optional)\n\t */\n\tdefault @Nullable ITaskProgress getTaskProgress() {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Return progress notifications listener (use executor tick rate and thread) (Optional)\n\t */\n\tdefault @Nullable Consumer<ITaskProgress> getProgressListener() {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Silent task: don't show progress\n\t */\n\tdefault boolean isSilent() {\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/ITaskInfo.java",
    "content": "package jadx.gui.jobs;\n\npublic interface ITaskInfo {\n\tTaskStatus getStatus();\n\n\tlong getJobsCount();\n\n\tlong getJobsComplete();\n\n\tlong getJobsSkipped();\n\n\tlong getTime();\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/ITaskProgress.java",
    "content": "package jadx.gui.jobs;\n\npublic interface ITaskProgress {\n\n\tint progress();\n\n\tint total();\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/InternalTask.java",
    "content": "package jadx.gui.jobs;\n\nimport java.util.concurrent.Delayed;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.function.Supplier;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.api.utils.tasks.ITaskExecutor;\n\npublic class InternalTask implements Delayed, ITaskInfo {\n\tprivate final long id;\n\tprivate final IBackgroundTask bgTask;\n\tprivate final AtomicBoolean running = new AtomicBoolean(false);\n\tprivate final AtomicLong nextUpdate = new AtomicLong(0);\n\tprivate final AtomicBoolean firstUpdate = new AtomicBoolean(true);\n\n\tprivate long startTime;\n\tprivate long execTime;\n\tprivate Supplier<TaskStatus> cancelCheck;\n\tprivate TaskStatus status = TaskStatus.WAIT;\n\tprivate ITaskExecutor taskExecutor;\n\tprivate long jobsCount;\n\tprivate long jobsComplete;\n\n\tpublic InternalTask(long id, IBackgroundTask task) {\n\t\tthis.id = id;\n\t\tthis.bgTask = task;\n\t}\n\n\tpublic void taskStart(long startTime, Supplier<TaskStatus> cancelCheck) {\n\t\tthis.startTime = startTime;\n\t\tthis.cancelCheck = cancelCheck;\n\t\tthis.status = TaskStatus.STARTED;\n\t\tthis.running.set(true);\n\t}\n\n\tpublic void taskComplete() {\n\t\tthis.running.set(false);\n\t\tif (status == TaskStatus.STARTED) {\n\t\t\t// might be already set to error or cancel\n\t\t\tthis.status = TaskStatus.COMPLETE;\n\t\t}\n\t\tupdateExecTime();\n\t}\n\n\tpublic long getId() {\n\t\treturn id;\n\t}\n\n\tpublic IBackgroundTask getBgTask() {\n\t\treturn bgTask;\n\t}\n\n\tpublic void setNextUpdate(long nextUpdate) {\n\t\tthis.nextUpdate.set(nextUpdate);\n\t}\n\n\tpublic boolean isRunning() {\n\t\treturn running.get();\n\t}\n\n\tpublic boolean checkForFirstUpdate() {\n\t\treturn firstUpdate.compareAndExchange(true, false);\n\t}\n\n\tpublic Supplier<TaskStatus> getCancelCheck() {\n\t\treturn cancelCheck;\n\t}\n\n\tpublic long getStartTime() {\n\t\treturn startTime;\n\t}\n\n\t@Override\n\tpublic TaskStatus getStatus() {\n\t\treturn status;\n\t}\n\n\tpublic void setStatus(TaskStatus taskStatus) {\n\t\tthis.status = taskStatus;\n\t}\n\n\tpublic ITaskExecutor getTaskExecutor() {\n\t\treturn taskExecutor;\n\t}\n\n\tpublic void setTaskExecutor(ITaskExecutor taskExecutor) {\n\t\tthis.taskExecutor = taskExecutor;\n\t}\n\n\t@Override\n\tpublic long getJobsComplete() {\n\t\treturn jobsComplete;\n\t}\n\n\tpublic void setJobsComplete(long jobsComplete) {\n\t\tthis.jobsComplete = jobsComplete;\n\t}\n\n\t@Override\n\tpublic long getJobsCount() {\n\t\treturn jobsCount;\n\t}\n\n\tpublic void setJobsCount(long jobsCount) {\n\t\tthis.jobsCount = jobsCount;\n\t}\n\n\t@Override\n\tpublic long getJobsSkipped() {\n\t\treturn jobsCount - jobsComplete;\n\t}\n\n\t@Override\n\tpublic long getTime() {\n\t\treturn execTime;\n\t}\n\n\tpublic void updateExecTime() {\n\t\tthis.execTime = System.currentTimeMillis() - startTime;\n\t}\n\n\t@Override\n\tpublic long getDelay(@NotNull TimeUnit unit) {\n\t\treturn unit.convert(nextUpdate.get() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull Delayed o) {\n\t\treturn Long.compare(nextUpdate.get(), ((InternalTask) o).nextUpdate.get());\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"InternalTask{\" + bgTask.getTitle() + \", status=\" + status\n\t\t\t\t+ \", progress=\" + jobsComplete + \" of \" + jobsCount + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/LoadTask.java",
    "content": "package jadx.gui.jobs;\n\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\n\nimport jadx.api.utils.tasks.ITaskExecutor;\nimport jadx.core.utils.tasks.TaskExecutor;\nimport jadx.gui.utils.NLS;\n\n/**\n * Load task: prepare data in background task and use that data in UI task\n */\npublic class LoadTask<T> extends CancelableBackgroundTask {\n\tprivate final String title;\n\tprivate final AtomicReference<T> taskData;\n\tprivate final Runnable bgTask;\n\tprivate final Runnable uiTask;\n\n\tpublic LoadTask(Supplier<T> loadBgTask, Consumer<T> uiTask) {\n\t\tthis(NLS.str(\"progress.load\"), loadBgTask, uiTask);\n\t}\n\n\tpublic LoadTask(String title, Supplier<T> loadBgTask, Consumer<T> uiTask) {\n\t\tthis.title = title;\n\t\tthis.taskData = new AtomicReference<>();\n\t\tthis.bgTask = () -> taskData.set(loadBgTask.get());\n\t\tthis.uiTask = () -> uiTask.accept(taskData.get());\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn title;\n\t}\n\n\t@Override\n\tpublic ITaskExecutor scheduleTasks() {\n\t\tTaskExecutor executor = new TaskExecutor();\n\t\texecutor.addSequentialTask(bgTask);\n\t\treturn executor;\n\t}\n\n\t@Override\n\tpublic void onFinish(ITaskInfo taskInfo) {\n\t\tuiTask.run();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/ProcessResult.java",
    "content": "package jadx.gui.jobs;\n\npublic class ProcessResult {\n\tprivate final int skipped;\n\tprivate final TaskStatus status;\n\tprivate final int timeLimit;\n\n\tpublic ProcessResult(int skipped, TaskStatus status, int timeLimit) {\n\t\tthis.skipped = skipped;\n\t\tthis.status = status;\n\t\tthis.timeLimit = timeLimit;\n\t}\n\n\tpublic int getSkipped() {\n\t\treturn skipped;\n\t}\n\n\tpublic TaskStatus getStatus() {\n\t\treturn status;\n\t}\n\n\tpublic int getTimeLimit() {\n\t\treturn timeLimit;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/ProgressUpdater.java",
    "content": "package jadx.gui.jobs;\n\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.DelayQueue;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.function.Consumer;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.Utils;\nimport jadx.gui.ui.panel.ProgressPanel;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\n@SuppressWarnings({ \"FieldCanBeLocal\", \"InfiniteLoopStatement\" })\npublic class ProgressUpdater {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ProgressUpdater.class);\n\n\tprivate static final int UPDATE_INTERVAL_MS = 1000;\n\n\tprivate final ProgressPanel progressPane;\n\tprivate final Consumer<InternalTask> cancelCallback;\n\tprivate final ExecutorService bgExecutor = Executors.newSingleThreadExecutor(Utils.simpleThreadFactory(\"jadx-progress\"));\n\tprivate final BlockingQueue<InternalTask> tasks = new DelayQueue<>();\n\n\tpublic ProgressUpdater(ProgressPanel progressPane, Consumer<InternalTask> cancelCallback) {\n\t\tthis.progressPane = progressPane;\n\t\tthis.cancelCallback = cancelCallback;\n\t\tthis.bgExecutor.execute(this::updateLoop);\n\t}\n\n\tpublic void addTask(InternalTask task) {\n\t\tif (task.getBgTask().isSilent()) {\n\t\t\treturn;\n\t\t}\n\t\tscheduleNextUpdate(task);\n\t}\n\n\tpublic void taskComplete(InternalTask task) {\n\t\ttask.setNextUpdate(0);\n\t\tupdateProgress(task);\n\t}\n\n\tprivate void scheduleNextUpdate(InternalTask task) {\n\t\ttask.setNextUpdate(System.currentTimeMillis() + UPDATE_INTERVAL_MS);\n\t\ttasks.add(task);\n\t}\n\n\tprivate void updateLoop() {\n\t\twhile (true) {\n\t\t\ttry {\n\t\t\t\tInternalTask task = tasks.take();\n\t\t\t\tif (task.isRunning()) {\n\t\t\t\t\tupdateProgress(task);\n\t\t\t\t\tcancelCheck(task);\n\t\t\t\t\tscheduleNextUpdate(task);\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.warn(\"Error in ProgressUpdater loop\", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void updateProgress(InternalTask internalTask) {\n\t\tUiUtils.uiRun(() -> {\n\t\t\tIBackgroundTask bgTask = internalTask.getBgTask();\n\t\t\tif (internalTask.isRunning()) {\n\t\t\t\tif (internalTask.checkForFirstUpdate()) {\n\t\t\t\t\tprogressPane.setLabel(bgTask.getTitle() + \"… \");\n\t\t\t\t\tprogressPane.setCancelButtonVisible(bgTask.canBeCanceled());\n\t\t\t\t\tprogressPane.setVisible(true);\n\t\t\t\t}\n\t\t\t\tITaskProgress taskProgress = bgTask.getTaskProgress();\n\t\t\t\tif (taskProgress == null) {\n\t\t\t\t\tint progress = internalTask.getTaskExecutor().getProgress();\n\t\t\t\t\ttaskProgress = new TaskProgress(progress, internalTask.getJobsCount());\n\t\t\t\t}\n\t\t\t\tprogressPane.setProgress(taskProgress);\n\t\t\t\tConsumer<ITaskProgress> onProgressListener = bgTask.getProgressListener();\n\t\t\t\tif (onProgressListener != null) {\n\t\t\t\t\tonProgressListener.accept(taskProgress);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tprogressPane.reset();\n\t\t\t\tprogressPane.setVisible(false);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void cancelCheck(InternalTask task) {\n\t\tTaskStatus taskStatus = task.getCancelCheck().get();\n\t\tif (taskStatus == null) {\n\t\t\treturn;\n\t\t}\n\t\ttask.setStatus(taskStatus);\n\t\tUiUtils.uiRun(() -> {\n\t\t\tIBackgroundTask bgTask = task.getBgTask();\n\t\t\tprogressPane.setLabel(bgTask.getTitle() + \" (\" + NLS.str(\"progress.canceling\") + \")… \");\n\t\t\tprogressPane.setCancelButtonVisible(false);\n\t\t\tprogressPane.setIndeterminate(true);\n\t\t});\n\t\tcancelCallback.accept(task);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/SilentTask.java",
    "content": "package jadx.gui.jobs;\n\nimport jadx.api.utils.tasks.ITaskExecutor;\nimport jadx.core.utils.tasks.TaskExecutor;\n\n/**\n * Simple and short task, will not show progress\n */\npublic class SilentTask extends CancelableBackgroundTask {\n\tprivate final Runnable task;\n\n\tpublic SilentTask(Runnable backgroundTask) {\n\t\tthis.task = backgroundTask;\n\t}\n\n\t@Override\n\tpublic boolean isSilent() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn \"<silent>\";\n\t}\n\n\t@Override\n\tpublic ITaskExecutor scheduleTasks() {\n\t\tTaskExecutor executor = new TaskExecutor();\n\t\texecutor.addSequentialTask(task);\n\t\treturn executor;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/SimpleTask.java",
    "content": "package jadx.gui.jobs;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.utils.tasks.ITaskExecutor;\nimport jadx.core.utils.tasks.TaskExecutor;\n\n/**\n * Simple not cancelable task with memory check\n */\npublic class SimpleTask implements IBackgroundTask {\n\tprivate final String title;\n\tprivate final List<Runnable> jobs;\n\tprivate final @Nullable Consumer<TaskStatus> onFinish;\n\n\tpublic SimpleTask(String title, Runnable run) {\n\t\tthis(title, Collections.singletonList(run), null);\n\t}\n\n\tpublic SimpleTask(String title, Runnable run, Runnable onFinish) {\n\t\tthis(title, Collections.singletonList(run), s -> onFinish.run());\n\t}\n\n\tpublic SimpleTask(String title, List<Runnable> jobs) {\n\t\tthis(title, jobs, null);\n\t}\n\n\tpublic SimpleTask(String title, List<Runnable> jobs, @Nullable Consumer<TaskStatus> onFinish) {\n\t\tthis.title = title;\n\t\tthis.jobs = jobs;\n\t\tthis.onFinish = onFinish;\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn title;\n\t}\n\n\tpublic List<Runnable> getJobs() {\n\t\treturn jobs;\n\t}\n\n\tpublic @Nullable Consumer<TaskStatus> getOnFinish() {\n\t\treturn onFinish;\n\t}\n\n\t@Override\n\tpublic ITaskExecutor scheduleTasks() {\n\t\tTaskExecutor executor = new TaskExecutor();\n\t\texecutor.addParallelTasks(jobs);\n\t\treturn executor;\n\t}\n\n\t@Override\n\tpublic void onFinish(ITaskInfo taskInfo) {\n\t\tif (onFinish != null) {\n\t\t\tonFinish.accept(taskInfo.getStatus());\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean checkMemoryUsage() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean canBeCanceled() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isCanceled() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void cancel() {\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/TaskProgress.java",
    "content": "package jadx.gui.jobs;\n\nimport jadx.gui.utils.UiUtils;\n\npublic class TaskProgress implements ITaskProgress {\n\tprivate int progress;\n\tprivate int total;\n\n\tpublic TaskProgress() {\n\t\tthis(0, 100);\n\t}\n\n\tpublic TaskProgress(long progress, long total) {\n\t\tthis(UiUtils.calcProgress(progress, total), 100);\n\t}\n\n\tpublic TaskProgress(int progress, int total) {\n\t\tthis.progress = progress;\n\t\tthis.total = total;\n\t}\n\n\t@Override\n\tpublic int progress() {\n\t\treturn progress;\n\t}\n\n\t@Override\n\tpublic int total() {\n\t\treturn total;\n\t}\n\n\tpublic void updateProgress(int progress) {\n\t\tthis.progress = progress;\n\t}\n\n\tpublic void updateTotal(int total) {\n\t\tthis.total = total;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"TaskProgress{\" + progress + \" of \" + total + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/TaskStatus.java",
    "content": "package jadx.gui.jobs;\n\npublic enum TaskStatus {\n\tWAIT,\n\tSTARTED,\n\tCOMPLETE,\n\tCANCEL_BY_USER,\n\tCANCEL_BY_TIMEOUT,\n\tCANCEL_BY_MEMORY,\n\tERROR\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/jobs/TaskWithExtraOnFinish.java",
    "content": "package jadx.gui.jobs;\n\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.utils.tasks.ITaskExecutor;\n\n/**\n * Add additional `onFinish` action to the existing task\n */\npublic class TaskWithExtraOnFinish implements IBackgroundTask {\n\tprivate final IBackgroundTask task;\n\tprivate final Consumer<TaskStatus> extraOnFinish;\n\n\tpublic TaskWithExtraOnFinish(IBackgroundTask task, Runnable extraOnFinish) {\n\t\tthis(task, s -> extraOnFinish.run());\n\t}\n\n\tpublic TaskWithExtraOnFinish(IBackgroundTask task, Consumer<TaskStatus> extraOnFinish) {\n\t\tthis.task = Objects.requireNonNull(task);\n\t\tthis.extraOnFinish = Objects.requireNonNull(extraOnFinish);\n\t}\n\n\t@Override\n\tpublic void onFinish(ITaskInfo taskInfo) {\n\t\ttask.onFinish(taskInfo);\n\t\textraOnFinish.accept(taskInfo.getStatus());\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn task.getTitle();\n\t}\n\n\t@Override\n\tpublic ITaskExecutor scheduleTasks() {\n\t\treturn task.scheduleTasks();\n\t}\n\n\t@Override\n\tpublic void onDone(ITaskInfo taskInfo) {\n\t\ttask.onDone(taskInfo);\n\t}\n\n\t@Override\n\tpublic @Nullable Consumer<ITaskProgress> getProgressListener() {\n\t\treturn task.getProgressListener();\n\t}\n\n\t@Override\n\tpublic @Nullable ITaskProgress getTaskProgress() {\n\t\treturn task.getTaskProgress();\n\t}\n\n\t@Override\n\tpublic boolean canBeCanceled() {\n\t\treturn task.canBeCanceled();\n\t}\n\n\t@Override\n\tpublic boolean isCanceled() {\n\t\treturn task.isCanceled();\n\t}\n\n\t@Override\n\tpublic void cancel() {\n\t\ttask.cancel();\n\t}\n\n\t@Override\n\tpublic int timeLimit() {\n\t\treturn task.timeLimit();\n\t}\n\n\t@Override\n\tpublic boolean checkMemoryUsage() {\n\t\treturn task.checkMemoryUsage();\n\t}\n\n\t@Override\n\tpublic int getCancelTimeoutMS() {\n\t\treturn task.getCancelTimeoutMS();\n\t}\n\n\t@Override\n\tpublic int getShutdownTimeoutMS() {\n\t\treturn task.getShutdownTimeoutMS();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/logs/ILogListener.java",
    "content": "package jadx.gui.logs;\n\npublic interface ILogListener {\n\tvoid onAppend(LogEvent logEvent);\n\n\tvoid onReload();\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/logs/IssuesListener.java",
    "content": "package jadx.gui.logs;\n\nimport javax.swing.SwingUtilities;\n\nimport ch.qos.logback.classic.Level;\n\nimport jadx.gui.ui.panel.IssuesPanel;\nimport jadx.gui.utils.rx.DebounceUpdate;\n\npublic class IssuesListener implements ILogListener {\n\tprivate final IssuesPanel issuesPanel;\n\tprivate final DebounceUpdate updater;\n\n\tprivate int errors = 0;\n\tprivate int warnings = 0;\n\n\tpublic IssuesListener(IssuesPanel issuesPanel) {\n\t\tthis.issuesPanel = issuesPanel;\n\t\tthis.updater = new DebounceUpdate(500, this::onUpdate);\n\t}\n\n\tprivate void onUpdate() {\n\t\tSwingUtilities.invokeLater(() -> issuesPanel.onUpdate(errors, warnings));\n\t}\n\n\t@Override\n\tpublic void onAppend(LogEvent logEvent) {\n\t\tswitch (logEvent.getLevel().toInt()) {\n\t\t\tcase Level.ERROR_INT:\n\t\t\t\terrors++;\n\t\t\t\tupdater.requestUpdate();\n\t\t\t\tbreak;\n\n\t\t\tcase Level.WARN_INT:\n\t\t\t\twarnings++;\n\t\t\t\tupdater.requestUpdate();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onReload() {\n\t\terrors = 0;\n\t\twarnings = 0;\n\t\tupdater.requestUpdate();\n\t}\n\n\tpublic int getErrors() {\n\t\treturn errors;\n\t}\n\n\tpublic int getWarnings() {\n\t\treturn warnings;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/logs/LimitedQueue.java",
    "content": "package jadx.gui.logs;\n\nimport java.util.AbstractQueue;\nimport java.util.ArrayDeque;\nimport java.util.Deque;\nimport java.util.Iterator;\n\npublic class LimitedQueue<T> extends AbstractQueue<T> {\n\n\tprivate final Deque<T> deque = new ArrayDeque<>();\n\tprivate final int limit;\n\n\tpublic LimitedQueue(int limit) {\n\t\tthis.limit = limit;\n\t}\n\n\t@Override\n\tpublic Iterator<T> iterator() {\n\t\treturn deque.iterator();\n\t}\n\n\t@Override\n\tpublic int size() {\n\t\treturn deque.size();\n\t}\n\n\t@Override\n\tpublic boolean offer(T t) {\n\t\tdeque.addLast(t);\n\t\tif (deque.size() > limit) {\n\t\t\tdeque.removeFirst();\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic T poll() {\n\t\treturn deque.poll();\n\t}\n\n\t@Override\n\tpublic T peek() {\n\t\treturn deque.peek();\n\t}\n\n\t@Override\n\tpublic void clear() {\n\t\tdeque.clear();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/logs/LogAppender.java",
    "content": "package jadx.gui.logs;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\n\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.utils.UiUtils;\n\nclass LogAppender implements ILogListener {\n\tprivate final LogOptions options;\n\tprivate final RSyntaxTextArea textArea;\n\n\tpublic LogAppender(LogOptions options, RSyntaxTextArea textArea) {\n\t\tthis.options = options;\n\t\tthis.textArea = textArea;\n\t}\n\n\t@Override\n\tpublic void onAppend(LogEvent logEvent) {\n\t\tif (accept(logEvent)) {\n\t\t\tUiUtils.uiRun(() -> textArea.append(logEvent.getMsg()));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onReload() {\n\t\tUiUtils.uiRunAndWait(() -> textArea.append(StringUtils.repeat('=', 100) + '\\n'));\n\t}\n\n\tprivate boolean accept(LogEvent logEvent) {\n\t\tboolean byLevel = logEvent.getLevel().isGreaterOrEqual(options.getLogLevel());\n\t\tif (!byLevel) {\n\t\t\treturn false;\n\t\t}\n\t\tswitch (options.getMode()) {\n\t\t\tcase ALL:\n\t\t\t\treturn true;\n\n\t\t\tcase ALL_SCRIPTS:\n\t\t\t\treturn logEvent.getLoggerName().startsWith(\"JadxScript:\");\n\n\t\t\tcase CURRENT_SCRIPT:\n\t\t\t\treturn logEvent.getLoggerName().equals(options.getFilter());\n\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unexpected log mode: \" + options.getMode());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/logs/LogCollector.java",
    "content": "package jadx.gui.logs;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Queue;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.LoggerFactory;\n\nimport ch.qos.logback.classic.Logger;\nimport ch.qos.logback.classic.LoggerContext;\nimport ch.qos.logback.classic.PatternLayout;\nimport ch.qos.logback.classic.spi.ILoggingEvent;\nimport ch.qos.logback.core.AppenderBase;\nimport ch.qos.logback.core.Layout;\n\npublic class LogCollector extends AppenderBase<ILoggingEvent> {\n\tpublic static final int BUFFER_SIZE = 5000;\n\n\tprivate static final LogCollector INSTANCE = new LogCollector();\n\n\tpublic static LogCollector getInstance() {\n\t\treturn INSTANCE;\n\t}\n\n\tpublic static void register() {\n\t\tLogger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);\n\t\tLoggerContext loggerContext = rootLogger.getLoggerContext();\n\n\t\tPatternLayout layout = new PatternLayout();\n\t\tlayout.setContext(loggerContext);\n\t\tlayout.setPattern(\"%-5level: %msg%n\");\n\t\tlayout.start();\n\n\t\tINSTANCE.setContext(loggerContext);\n\t\tINSTANCE.setLayout(layout);\n\t\tINSTANCE.start();\n\n\t\trootLogger.addAppender(INSTANCE);\n\t}\n\n\tprivate final List<ILogListener> listeners = new ArrayList<>();\n\tprivate final Queue<LogEvent> buffer = new LimitedQueue<>(BUFFER_SIZE);\n\n\tprivate Layout<ILoggingEvent> layout;\n\n\tpublic LogCollector() {\n\t\tsetName(\"LogCollector\");\n\t}\n\n\t@Override\n\tprotected synchronized void append(ILoggingEvent event) {\n\t\tString msg = layout.doLayout(event);\n\t\tLogEvent logEvent = new LogEvent(event.getLevel(), event.getLoggerName(), msg);\n\t\tbuffer.offer(logEvent);\n\t\tlisteners.forEach(l -> l.onAppend(logEvent));\n\t}\n\n\tprivate void setLayout(Layout<ILoggingEvent> layout) {\n\t\tthis.layout = layout;\n\t}\n\n\tpublic synchronized void registerListener(ILogListener listener) {\n\t\tlisteners.add(listener);\n\t\tbuffer.forEach(listener::onAppend);\n\t}\n\n\tpublic synchronized boolean removeListener(@Nullable ILogListener listener) {\n\t\tif (listener == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn this.listeners.removeIf(l -> l == listener);\n\t}\n\n\tpublic synchronized boolean removeListenerByClass(Class<?> listenerCls) {\n\t\treturn this.listeners.removeIf(l -> l.getClass().equals(listenerCls));\n\t}\n\n\tpublic synchronized void reset() {\n\t\tbuffer.clear();\n\t\tlisteners.forEach(ILogListener::onReload);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/logs/LogEvent.java",
    "content": "package jadx.gui.logs;\n\nimport ch.qos.logback.classic.Level;\n\npublic final class LogEvent {\n\tprivate final Level level;\n\tprivate final String loggerName;\n\tprivate final String msg;\n\n\tLogEvent(Level level, String loggerName, String msg) {\n\t\tthis.level = level;\n\t\tthis.loggerName = loggerName;\n\t\tthis.msg = msg;\n\t}\n\n\tpublic Level getLevel() {\n\t\treturn level;\n\t}\n\n\tpublic String getLoggerName() {\n\t\treturn loggerName;\n\t}\n\n\tpublic String getMsg() {\n\t\treturn msg;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn level + \": \" + loggerName + \" - \" + msg;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/logs/LogMode.java",
    "content": "package jadx.gui.logs;\n\nimport org.apache.commons.lang3.StringUtils;\n\nimport jadx.gui.utils.NLS;\n\npublic enum LogMode {\n\tALL,\n\tALL_SCRIPTS,\n\tCURRENT_SCRIPT;\n\n\tprivate static final String[] NLS_STRINGS = StringUtils.split(NLS.str(\"log_viewer.modes\"), '|');\n\n\tpublic String getLocalizedName() {\n\t\treturn NLS_STRINGS[this.ordinal()];\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getLocalizedName();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/logs/LogOptions.java",
    "content": "package jadx.gui.logs;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport ch.qos.logback.classic.Level;\n\nimport jadx.core.utils.Utils;\n\npublic class LogOptions {\n\n\t/**\n\t * Store latest requested log options\n\t */\n\tprivate static LogOptions current = new LogOptions(LogMode.ALL, Level.INFO, null);\n\n\tpublic static LogOptions allWithLevel(@Nullable Level logLevel) {\n\t\tLevel level = Utils.getOrElse(logLevel, current.getLogLevel());\n\t\treturn store(new LogOptions(LogMode.ALL, level, null));\n\t}\n\n\tpublic static LogOptions forLevel(@Nullable Level logLevel) {\n\t\tLevel level = Utils.getOrElse(logLevel, current.getLogLevel());\n\t\treturn store(new LogOptions(current.getMode(), level, current.getFilter()));\n\t}\n\n\tpublic static LogOptions forMode(LogMode mode) {\n\t\treturn store(new LogOptions(mode, current.getLogLevel(), current.getFilter()));\n\t}\n\n\tpublic static LogOptions forScript(String scriptName) {\n\t\tString filter = \"JadxScript:\" + scriptName;\n\t\treturn store(new LogOptions(LogMode.CURRENT_SCRIPT, current.getLogLevel(), filter));\n\t}\n\n\tpublic static LogOptions current() {\n\t\treturn current;\n\t}\n\n\tprivate static LogOptions store(LogOptions logOptions) {\n\t\tcurrent = logOptions;\n\t\treturn logOptions;\n\t}\n\n\tprivate final LogMode mode;\n\tprivate final Level logLevel;\n\tprivate final @Nullable String filter;\n\n\tprivate LogOptions(LogMode mode, Level logLevel, @Nullable String filter) {\n\t\tthis.mode = mode;\n\t\tthis.logLevel = logLevel;\n\t\tthis.filter = filter;\n\t}\n\n\tpublic LogMode getMode() {\n\t\treturn mode;\n\t}\n\n\tpublic Level getLogLevel() {\n\t\treturn logLevel;\n\t}\n\n\tpublic @Nullable String getFilter() {\n\t\treturn filter;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"LogOptions{mode=\" + mode + \", logLevel=\" + logLevel + \", filter='\" + filter + '\\'' + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/logs/LogPanel.java",
    "content": "package jadx.gui.logs;\n\nimport java.awt.BorderLayout;\nimport java.awt.Dimension;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\nimport javax.swing.JComboBox;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.event.ChangeListener;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.jetbrains.annotations.Nullable;\n\nimport ch.qos.logback.classic.Level;\n\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.codearea.AbstractCodeArea;\nimport jadx.gui.ui.tab.TabBlueprint;\nimport jadx.gui.utils.NLS;\n\npublic class LogPanel extends JPanel {\n\tprivate static final long serialVersionUID = -8077649118322056081L;\n\n\tprivate static final Level[] LEVEL_ITEMS = { Level.DEBUG, Level.INFO, Level.WARN, Level.ERROR, Level.OFF };\n\n\tprivate final MainWindow mainWindow;\n\tprivate final Runnable dockAction;\n\tprivate final Runnable hideAction;\n\n\tprivate RSyntaxTextArea textPane;\n\tprivate JComboBox<LogMode> modeCb;\n\tprivate JComboBox<Level> levelCb;\n\n\tprivate ChangeListener activeTabListener;\n\n\tpublic LogPanel(MainWindow mainWindow, LogOptions logOptions, Runnable dockAction, Runnable hideAction) {\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.dockAction = dockAction;\n\t\tthis.hideAction = hideAction;\n\t\tinitUI(logOptions);\n\t\tapplyLogOptions(logOptions);\n\t}\n\n\tpublic void applyLogOptions(LogOptions logOptions) {\n\t\tif (logOptions.getMode() == LogMode.CURRENT_SCRIPT) {\n\t\t\tString scriptName = getCurrentScriptName();\n\t\t\tif (scriptName != null) {\n\t\t\t\tlogOptions = LogOptions.forScript(scriptName);\n\t\t\t}\n\t\t\tregisterActiveTabListener();\n\t\t} else {\n\t\t\tremoveActiveTabListener();\n\t\t}\n\t\tif (modeCb.getSelectedItem() != logOptions.getMode()) {\n\t\t\tmodeCb.setSelectedItem(logOptions.getMode());\n\t\t}\n\t\tif (levelCb.getSelectedItem() != logOptions.getLogLevel()) {\n\t\t\tlevelCb.setSelectedItem(logOptions.getLogLevel());\n\t\t}\n\t\tregisterLogListener(logOptions);\n\t}\n\n\tpublic void loadSettings() {\n\t\tAbstractCodeArea.loadCommonSettings(mainWindow, textPane);\n\t}\n\n\tprivate void initUI(LogOptions logOptions) {\n\t\tJadxSettings settings = mainWindow.getSettings();\n\t\ttextPane = AbstractCodeArea.getDefaultArea(mainWindow);\n\t\ttextPane.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));\n\n\t\tmodeCb = new JComboBox<>(LogMode.values());\n\t\tmodeCb.setSelectedItem(logOptions.getMode());\n\t\tmodeCb.addActionListener(e -> applyLogOptions(LogOptions.forMode((LogMode) modeCb.getSelectedItem())));\n\t\tJLabel modeLabel = new JLabel(NLS.str(\"log_viewer.mode\"));\n\t\tmodeLabel.setLabelFor(modeCb);\n\n\t\tlevelCb = new JComboBox<>(LEVEL_ITEMS);\n\t\tlevelCb.setSelectedItem(logOptions.getLogLevel());\n\t\tlevelCb.addActionListener(e -> applyLogOptions(LogOptions.forLevel((Level) levelCb.getSelectedItem())));\n\t\tJLabel levelLabel = new JLabel(NLS.str(\"log_viewer.log_level\"));\n\t\tlevelLabel.setLabelFor(levelCb);\n\n\t\tJButton clearBtn = new JButton(NLS.str(\"log_viewer.clear\"));\n\t\tclearBtn.addActionListener(ev -> {\n\t\t\tLogCollector.getInstance().reset();\n\t\t\ttextPane.setText(\"\");\n\t\t});\n\n\t\tJButton dockBtn = new JButton(NLS.str(settings.isDockLogViewer() ? \"log_viewer.undock\" : \"log_viewer.dock\"));\n\t\tdockBtn.addActionListener(ev -> dockAction.run());\n\n\t\tJButton hideBtn = new JButton(NLS.str(\"log_viewer.hide\"));\n\t\thideBtn.addActionListener(ev -> hideAction.run());\n\n\t\tJPanel start = new JPanel();\n\t\tstart.setLayout(new BoxLayout(start, BoxLayout.LINE_AXIS));\n\t\tstart.add(modeLabel);\n\t\tstart.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tstart.add(modeCb);\n\t\tstart.add(Box.createRigidArea(new Dimension(15, 0)));\n\t\tstart.add(levelLabel);\n\t\tstart.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tstart.add(levelCb);\n\t\tstart.add(Box.createRigidArea(new Dimension(5, 0)));\n\n\t\tJPanel end = new JPanel();\n\t\tend.setLayout(new BoxLayout(end, BoxLayout.LINE_AXIS));\n\t\tend.add(clearBtn);\n\t\tend.add(Box.createRigidArea(new Dimension(15, 0)));\n\t\tend.add(dockBtn);\n\t\tend.add(Box.createRigidArea(new Dimension(15, 0)));\n\t\tend.add(hideBtn);\n\n\t\tJPanel controlPane = new JPanel();\n\t\tcontrolPane.setLayout(new BorderLayout());\n\t\tcontrolPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\n\t\tcontrolPane.add(start, BorderLayout.LINE_START);\n\t\tcontrolPane.add(end, BorderLayout.LINE_END);\n\n\t\tJScrollPane scrollPane = new JScrollPane(textPane);\n\n\t\tsetLayout(new BorderLayout(5, 5));\n\t\tadd(controlPane, BorderLayout.PAGE_START);\n\t\tadd(scrollPane, BorderLayout.CENTER);\n\t}\n\n\tprivate void registerLogListener(LogOptions logOptions) {\n\t\tLogCollector logCollector = LogCollector.getInstance();\n\t\tlogCollector.removeListenerByClass(LogAppender.class);\n\t\ttextPane.setText(\"\");\n\t\tlogCollector.registerListener(new LogAppender(logOptions, textPane));\n\t}\n\n\tprivate @Nullable String getCurrentScriptName() {\n\t\tTabBlueprint selectedTab = mainWindow.getTabsController().getSelectedTab();\n\t\tif (selectedTab != null) {\n\t\t\tJNode node = selectedTab.getNode();\n\t\t\tif (node.getClass().getSimpleName().equals(\"JInputScript\")) { // TODO: register custom log filters\n\t\t\t\treturn node.getName();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate synchronized void registerActiveTabListener() {\n\t\tremoveActiveTabListener();\n\t\tactiveTabListener = e -> {\n\t\t\tString scriptName = getCurrentScriptName();\n\t\t\tif (scriptName != null) {\n\t\t\t\tapplyLogOptions(LogOptions.forScript(scriptName));\n\t\t\t}\n\t\t};\n\t\tmainWindow.getTabbedPane().addChangeListener(activeTabListener);\n\t}\n\n\tprivate synchronized void removeActiveTabListener() {\n\t\tif (activeTabListener != null) {\n\t\t\tmainWindow.getTabbedPane().removeChangeListener(activeTabListener);\n\t\t\tactiveTabListener = null;\n\t\t}\n\t}\n\n\tpublic void dispose() {\n\t\tLogCollector.getInstance().removeListenerByClass(LogAppender.class);\n\t\tremoveActiveTabListener();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/plugins/context/CodePopupAction.java",
    "content": "package jadx.gui.plugins.context;\n\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport javax.swing.KeyStroke;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.action.JNodeAction;\nimport jadx.gui.ui.codearea.CodeArea;\n\npublic class CodePopupAction {\n\tprivate final String name;\n\tprivate final Function<ICodeNodeRef, Boolean> enabledCheck;\n\tprivate final String keyBinding;\n\tprivate final Consumer<ICodeNodeRef> action;\n\n\tpublic CodePopupAction(String name, Function<ICodeNodeRef, Boolean> enabled, String keyBinding, Consumer<ICodeNodeRef> action) {\n\t\tthis.name = name;\n\t\tthis.enabledCheck = enabled;\n\t\tthis.keyBinding = keyBinding;\n\t\tthis.action = action;\n\t}\n\n\tpublic JNodeAction buildAction(CodeArea codeArea) {\n\t\treturn new NodeAction(this, codeArea);\n\t}\n\n\tprivate static class NodeAction extends JNodeAction {\n\t\tprivate final CodePopupAction data;\n\n\t\tpublic NodeAction(CodePopupAction data, CodeArea codeArea) {\n\t\t\tsuper(data.name, codeArea);\n\t\t\tsetName(data.name);\n\t\t\tsetShortcutComponent(codeArea);\n\t\t\tif (data.keyBinding != null) {\n\t\t\t\tKeyStroke key = KeyStroke.getKeyStroke(data.keyBinding);\n\t\t\t\tif (key == null) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Failed to parse key stroke: \" + data.keyBinding);\n\t\t\t\t}\n\t\t\t\tsetKeyBinding(key);\n\t\t\t}\n\t\t\tthis.data = data;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isActionEnabled(@Nullable JNode node) {\n\t\t\tif (node == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tICodeNodeRef codeNode = node.getCodeNodeRef();\n\t\t\tif (codeNode == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn data.enabledCheck.apply(codeNode);\n\t\t}\n\n\t\t@Override\n\t\tpublic void runAction(JNode node) {\n\t\t\tRunnable r = () -> data.action.accept(node.getCodeNodeRef());\n\t\t\tgetCodeArea().getMainWindow().getBackgroundExecutor().execute(data.name, r);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/plugins/context/CommonGuiPluginsContext.java",
    "content": "package jadx.gui.plugins.context;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.plugins.PluginContext;\nimport jadx.gui.settings.data.ITabStatePersist;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.codearea.JNodePopupBuilder;\nimport jadx.gui.utils.ui.ActionHandler;\n\npublic class CommonGuiPluginsContext {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CommonGuiPluginsContext.class);\n\n\tprivate final MainWindow mainWindow;\n\tprivate final Map<PluginContext, GuiPluginContext> pluginsMap = new HashMap<>();\n\n\tprivate final List<CodePopupAction> codePopupActionList = new ArrayList<>();\n\tprivate final List<TreePopupMenuEntry> treePopupMenuEntries = new ArrayList<>();\n\tprivate final List<ITreeInputCategory> treeInputCategories = new ArrayList<>();\n\tprivate final List<ITabStatePersist> tabStatePersistAdapters = new ArrayList<>();\n\n\tpublic CommonGuiPluginsContext(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t}\n\n\tpublic GuiPluginContext buildForPlugin(PluginContext pluginContext) {\n\t\tGuiPluginContext guiPluginContext = new GuiPluginContext(this, pluginContext);\n\t\tpluginsMap.put(pluginContext, guiPluginContext);\n\t\treturn guiPluginContext;\n\t}\n\n\tpublic @Nullable GuiPluginContext getPluginGuiContext(PluginContext pluginContext) {\n\t\treturn pluginsMap.get(pluginContext);\n\t}\n\n\tpublic @Nullable GuiPluginContext getGuiPluginContextById(String pluginId) {\n\t\tfor (GuiPluginContext guiPluginContext : pluginsMap.values()) {\n\t\t\tif (guiPluginContext.getPluginContext().getPluginId().equals(pluginId)) {\n\t\t\t\treturn guiPluginContext;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic void reset() {\n\t\tcodePopupActionList.clear();\n\t\ttreePopupMenuEntries.clear();\n\t\ttreeInputCategories.clear();\n\t\tmainWindow.resetPluginsMenu();\n\t}\n\n\tpublic MainWindow getMainWindow() {\n\t\treturn mainWindow;\n\t}\n\n\tpublic List<CodePopupAction> getCodePopupActionList() {\n\t\treturn codePopupActionList;\n\t}\n\n\tpublic List<TreePopupMenuEntry> getTreePopupMenuEntries() {\n\t\treturn treePopupMenuEntries;\n\t}\n\n\tpublic List<ITreeInputCategory> getTreeInputCategories() {\n\t\treturn treeInputCategories;\n\t}\n\n\tpublic List<ITabStatePersist> getTabStatePersistAdapters() {\n\t\treturn tabStatePersistAdapters;\n\t}\n\n\tpublic void addMenuAction(String name, Runnable action) {\n\t\tActionHandler item = new ActionHandler(ev -> {\n\t\t\ttry {\n\t\t\t\tmainWindow.getBackgroundExecutor().execute(name, action);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Error running action for menu item: {}\", name, e);\n\t\t\t}\n\t\t});\n\t\titem.setNameAndDesc(name);\n\t\tmainWindow.addToPluginsMenu(item);\n\t}\n\n\tpublic void appendPopupMenus(CodeArea codeArea, JNodePopupBuilder popup) {\n\t\tif (codePopupActionList.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tpopup.addSeparator();\n\t\tfor (CodePopupAction codePopupAction : codePopupActionList) {\n\t\t\tpopup.add(codePopupAction.buildAction(codeArea));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/plugins/context/GuiPluginContext.java",
    "content": "package jadx.gui.plugins.context;\n\nimport java.awt.Container;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport javax.swing.ImageIcon;\nimport javax.swing.JFrame;\nimport javax.swing.JPanel;\nimport javax.swing.KeyStroke;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxDecompiler;\nimport jadx.api.JavaClass;\nimport jadx.api.JavaNode;\nimport jadx.api.gui.tree.ITreeNode;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.plugins.events.IJadxEvents;\nimport jadx.api.plugins.events.types.NodeRenamedByUser;\nimport jadx.api.plugins.gui.ISettingsGroup;\nimport jadx.api.plugins.gui.JadxGuiContext;\nimport jadx.api.plugins.gui.JadxGuiSettings;\nimport jadx.core.plugins.PluginContext;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.settings.data.ITabStatePersist;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.codearea.AbstractCodeArea;\nimport jadx.gui.ui.codearea.AbstractCodeContentPanel;\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.dialog.UsageDialog;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.utils.IconsCache;\nimport jadx.gui.utils.UiUtils;\n\npublic class GuiPluginContext implements JadxGuiContext {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(GuiPluginContext.class);\n\n\tprivate final CommonGuiPluginsContext commonContext;\n\tprivate final PluginContext pluginContext;\n\n\tprivate @Nullable ISettingsGroup customSettingsGroup;\n\n\tpublic GuiPluginContext(CommonGuiPluginsContext commonContext, PluginContext pluginContext) {\n\t\tthis.commonContext = commonContext;\n\t\tthis.pluginContext = pluginContext;\n\t}\n\n\tpublic CommonGuiPluginsContext getCommonContext() {\n\t\treturn commonContext;\n\t}\n\n\tpublic PluginContext getPluginContext() {\n\t\treturn pluginContext;\n\t}\n\n\t@Override\n\tpublic JFrame getMainFrame() {\n\t\treturn commonContext.getMainWindow();\n\t}\n\n\t@Override\n\tpublic void uiRun(Runnable runnable) {\n\t\tUiUtils.uiRun(runnable);\n\t}\n\n\t@Override\n\tpublic void addMenuAction(String name, Runnable action) {\n\t\tcommonContext.addMenuAction(name, action);\n\t}\n\n\t@Override\n\tpublic void addPopupMenuAction(String name, @Nullable Function<ICodeNodeRef, Boolean> enabled,\n\t\t\t@Nullable String keyBinding, Consumer<ICodeNodeRef> action) {\n\t\tcommonContext.getCodePopupActionList().add(new CodePopupAction(name, enabled, keyBinding, action));\n\t}\n\n\t@Override\n\tpublic void addTreePopupMenuEntry(String name, Predicate<ITreeNode> addPredicate, Consumer<ITreeNode> action) {\n\t\tcommonContext.getTreePopupMenuEntries().add(new TreePopupMenuEntry(name, addPredicate, action));\n\t}\n\n\tpublic void registerTreeInputCategory(ITreeInputCategory inputCategory) {\n\t\tcommonContext.getTreeInputCategories().add(inputCategory);\n\t}\n\n\tpublic void registerTabStatePersistAdapter(ITabStatePersist tabStatePersist) {\n\t\tcommonContext.getTabStatePersistAdapters().add(tabStatePersist);\n\t}\n\n\t@Override\n\tpublic boolean registerGlobalKeyBinding(String id, String keyBinding, Runnable action) {\n\t\tKeyStroke keyStroke = KeyStroke.getKeyStroke(keyBinding);\n\t\tif (keyStroke == null) {\n\t\t\tthrow new IllegalArgumentException(\"Failed to parse key binding: \" + keyBinding);\n\t\t}\n\t\tJPanel mainPanel = (JPanel) commonContext.getMainWindow().getContentPane();\n\t\tObject prevBinding = mainPanel.getInputMap().get(keyStroke);\n\t\tif (prevBinding != null) {\n\t\t\treturn false;\n\t\t}\n\t\tUiUtils.addKeyBinding(mainPanel, keyStroke, id, action);\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void copyToClipboard(String str) {\n\t\tUiUtils.copyToClipboard(str);\n\t}\n\n\t@Override\n\tpublic JadxGuiSettings settings() {\n\t\treturn new GuiSettingsContext(this);\n\t}\n\n\tvoid setCustomSettings(ISettingsGroup customSettingsGroup) {\n\t\tthis.customSettingsGroup = customSettingsGroup;\n\t}\n\n\tpublic @Nullable ISettingsGroup getCustomSettingsGroup() {\n\t\treturn customSettingsGroup;\n\t}\n\n\t@Nullable\n\tprivate CodeArea getCodeArea() {\n\t\tContainer contentPane = commonContext.getMainWindow().getTabbedPane().getSelectedContentPanel();\n\t\tif (contentPane instanceof AbstractCodeContentPanel) {\n\t\t\tAbstractCodeArea codeArea = ((AbstractCodeContentPanel) contentPane).getCodeArea();\n\t\t\tif (codeArea instanceof CodeArea) {\n\t\t\t\treturn (CodeArea) codeArea;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic ImageIcon getSVGIcon(String name) {\n\t\ttry {\n\t\t\treturn IconsCache.getSVGIcon(name);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to load icon: {}\", name, e);\n\t\t\treturn IconsCache.getSVGIcon(\"ui/error\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic ICodeNodeRef getNodeUnderCaret() {\n\t\tCodeArea codeArea = getCodeArea();\n\t\tif (codeArea != null) {\n\t\t\tJNode nodeUnderCaret = codeArea.getNodeUnderCaret();\n\t\t\tif (nodeUnderCaret != null) {\n\t\t\t\treturn nodeUnderCaret.getCodeNodeRef();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic ICodeNodeRef getNodeUnderMouse() {\n\t\tCodeArea codeArea = getCodeArea();\n\t\tif (codeArea != null) {\n\t\t\tJNode nodeUnderMouse = codeArea.getNodeUnderMouse();\n\t\t\tif (nodeUnderMouse != null) {\n\t\t\t\treturn nodeUnderMouse.getCodeNodeRef();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic ICodeNodeRef getEnclosingNodeUnderCaret() {\n\t\tCodeArea codeArea = getCodeArea();\n\t\tif (codeArea != null) {\n\t\t\tJNode nodeUnderMouse = codeArea.getEnclosingNodeUnderCaret();\n\t\t\tif (nodeUnderMouse != null) {\n\t\t\t\treturn nodeUnderMouse.getCodeNodeRef();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic ICodeNodeRef getEnclosingNodeUnderMouse() {\n\t\tCodeArea codeArea = getCodeArea();\n\t\tif (codeArea != null) {\n\t\t\tJNode nodeUnderMouse = codeArea.getEnclosingNodeUnderMouse();\n\t\t\tif (nodeUnderMouse != null) {\n\t\t\t\treturn nodeUnderMouse.getCodeNodeRef();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean open(ICodeNodeRef ref) {\n\t\tcommonContext.getMainWindow().getTabsController().codeJump(getJNodeFromRef(ref));\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void openUsageDialog(ICodeNodeRef ref) {\n\t\tUsageDialog.open(commonContext.getMainWindow(), getJNodeFromRef(ref));\n\t}\n\n\tprivate JNode getJNodeFromRef(ICodeNodeRef ref) {\n\t\treturn commonContext.getMainWindow().getCacheObject().getNodeCache().makeFrom(ref);\n\t}\n\n\t@Override\n\tpublic void reloadActiveTab() {\n\t\tUiUtils.uiRun(() -> {\n\t\t\tCodeArea codeArea = getCodeArea();\n\t\t\tif (codeArea != null) {\n\t\t\t\tcodeArea.refreshClass();\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic void reloadAllTabs() {\n\t\tUiUtils.uiRun(() -> {\n\t\t\tfor (ContentPanel contentPane : commonContext.getMainWindow().getTabbedPane().getTabs()) {\n\t\t\t\tif (contentPane instanceof AbstractCodeContentPanel) {\n\t\t\t\t\tAbstractCodeArea codeArea = ((AbstractCodeContentPanel) contentPane).getCodeArea();\n\t\t\t\t\tif (codeArea instanceof CodeArea) {\n\t\t\t\t\t\t((CodeArea) codeArea).refreshClass();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic void applyNodeRename(ICodeNodeRef nodeRef) {\n\t\tJadxDecompiler decompiler = commonContext.getMainWindow().getWrapper().getDecompiler();\n\t\tJavaNode javaNode = decompiler.getJavaNodeByRef(nodeRef);\n\t\tif (javaNode == null) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to resolve node ref: \" + nodeRef);\n\t\t}\n\t\tString newName;\n\t\tif (javaNode instanceof JavaClass) {\n\t\t\t// package can have alias\n\t\t\tnewName = javaNode.getFullName();\n\t\t} else {\n\t\t\tnewName = javaNode.getName();\n\t\t}\n\t\tIJadxEvents events = commonContext.getMainWindow().events();\n\t\tevents.send(new NodeRenamedByUser(nodeRef, \"\", newName));\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/plugins/context/GuiSettingsContext.java",
    "content": "package jadx.gui.plugins.context;\n\nimport java.util.List;\n\nimport jadx.api.plugins.gui.ISettingsGroup;\nimport jadx.api.plugins.gui.JadxGuiSettings;\nimport jadx.api.plugins.options.OptionDescription;\nimport jadx.gui.settings.ui.SubSettingsGroup;\nimport jadx.gui.settings.ui.plugins.PluginSettings;\nimport jadx.gui.ui.MainWindow;\n\npublic class GuiSettingsContext implements JadxGuiSettings {\n\tprivate final GuiPluginContext guiPluginContext;\n\n\tpublic GuiSettingsContext(GuiPluginContext guiPluginContext) {\n\t\tthis.guiPluginContext = guiPluginContext;\n\t}\n\n\t@Override\n\tpublic void setCustomSettingsGroup(ISettingsGroup group) {\n\t\tguiPluginContext.setCustomSettings(group);\n\t}\n\n\t@Override\n\tpublic ISettingsGroup buildSettingsGroupForOptions(String title, List<OptionDescription> options) {\n\t\tMainWindow mainWindow = guiPluginContext.getCommonContext().getMainWindow();\n\t\tPluginSettings pluginsSettings = new PluginSettings(mainWindow, mainWindow.getSettings());\n\t\tSubSettingsGroup settingsGroup = new SubSettingsGroup(title);\n\t\tpluginsSettings.addOptions(settingsGroup, options);\n\t\treturn settingsGroup;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/plugins/context/ITreeInputCategory.java",
    "content": "package jadx.gui.plugins.context;\n\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport org.jetbrains.annotations.ApiStatus;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.gui.treemodel.JNode;\n\n/**\n * Custom category for 'Inputs' tree section\n */\n@ApiStatus.Experimental\npublic interface ITreeInputCategory {\n\n\t/**\n\t * Check if file should be moved into this category\n\t */\n\tboolean filesFilter(Path file);\n\n\t/**\n\t * Build node for filtered files.\n\t * Can be called with empty list (empty category might be useful)\n\t *\n\t * @return category node or null if not needed\n\t */\n\t@Nullable\n\tJNode buildInputNode(List<Path> files);\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/plugins/context/TreePopupMenuEntry.java",
    "content": "package jadx.gui.plugins.context;\n\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\n\nimport javax.swing.JMenuItem;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.gui.tree.ITreeNode;\n\npublic class TreePopupMenuEntry {\n\tprivate final String name;\n\tprivate final Predicate<ITreeNode> addPredicate;\n\tprivate final Consumer<ITreeNode> action;\n\n\tpublic TreePopupMenuEntry(String name, Predicate<ITreeNode> addPredicate, Consumer<ITreeNode> action) {\n\t\tthis.name = name;\n\t\tthis.addPredicate = addPredicate;\n\t\tthis.action = action;\n\t}\n\n\tpublic @Nullable JMenuItem buildEntry(ITreeNode node) {\n\t\tif (!addPredicate.test(node)) {\n\t\t\treturn null;\n\t\t}\n\t\tJMenuItem menuItem = new JMenuItem(name);\n\t\tmenuItem.addActionListener(ev -> action.accept(node));\n\t\treturn menuItem;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/plugins/mappings/JInputMapping.java",
    "content": "package jadx.gui.plugins.mappings;\n\nimport java.nio.file.Path;\n\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\nimport javax.swing.JPopupMenu;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jetbrains.annotations.NotNull;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JEditableNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.codearea.CodeContentPanel;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.ui.SimpleMenuItem;\n\npublic class JInputMapping extends JEditableNode {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JInputMapping.class);\n\n\tprivate static final ImageIcon MAPPING_ICON = UiUtils.openSvgIcon(\"nodes/abbreviatePackageNames\");\n\n\tprivate final Path mappingPath;\n\tprivate final String name;\n\n\tpublic JInputMapping(Path mappingPath) {\n\t\tthis.mappingPath = mappingPath;\n\t\tthis.name = mappingPath.getFileName().toString();\n\t}\n\n\t@Override\n\tpublic boolean hasContent() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic ContentPanel getContentPanel(TabbedPane tabbedPane) {\n\t\treturn new CodeContentPanel(tabbedPane, this);\n\t}\n\n\t@Override\n\tpublic @NotNull ICodeInfo getCodeInfo() {\n\t\ttry {\n\t\t\treturn new SimpleCodeInfo(FileUtils.readFile(mappingPath));\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to read mapping file: \" + mappingPath.toAbsolutePath(), e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void save(String newContent) {\n\t\ttry {\n\t\t\tFileUtils.writeFile(mappingPath, newContent);\n\t\t\tLOG.debug(\"Mapping saved: {}\", mappingPath.toAbsolutePath());\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to write mapping file: \" + mappingPath.toAbsolutePath(), e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic JPopupMenu onTreePopupMenu(MainWindow mainWindow) {\n\t\tJPopupMenu menu = new JPopupMenu();\n\t\tmenu.add(new SimpleMenuItem(NLS.str(\"popup.remove\"),\n\t\t\t\t() -> mainWindow.getRenameMappings().closeMappingsAndRemoveFromProject()));\n\t\treturn menu;\n\t}\n\n\t@Override\n\tpublic String getSyntaxName() {\n\t\treturn SyntaxConstants.SYNTAX_STYLE_NONE;\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn MAPPING_ICON;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic String getTooltip() {\n\t\treturn mappingPath.normalize().toAbsolutePath().toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/plugins/mappings/RenameMappingsGui.java",
    "content": "package jadx.gui.plugins.mappings;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.function.Consumer;\nimport java.util.stream.Stream;\n\nimport javax.swing.Action;\nimport javax.swing.JFileChooser;\nimport javax.swing.JMenu;\nimport javax.swing.JOptionPane;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport net.fabricmc.mappingio.MappingReader;\nimport net.fabricmc.mappingio.format.MappingFormat;\n\nimport jadx.api.args.UserRenamesMappingsMode;\nimport jadx.api.plugins.utils.CommonFileUtils;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.Utils;\nimport jadx.gui.jobs.TaskStatus;\nimport jadx.gui.settings.JadxProject;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.treemodel.JRoot;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.filedialog.FileDialogWrapper;\nimport jadx.gui.ui.filedialog.FileOpenMode;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.ui.ActionHandler;\nimport jadx.plugins.mappings.RenameMappingsOptions;\nimport jadx.plugins.mappings.save.MappingExporter;\n\npublic class RenameMappingsGui {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(RenameMappingsGui.class);\n\n\tprivate final MainWindow mainWindow;\n\n\tprivate boolean renamesChanged = false;\n\tprivate JInputMapping mappingNode;\n\n\tprivate transient JMenu openMappingsMenu;\n\tprivate transient Action saveMappingsAction;\n\tprivate transient JMenu saveMappingsAsMenu;\n\tprivate transient Action closeMappingsAction;\n\n\tpublic RenameMappingsGui(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t\tmainWindow.addLoadListener(this::onLoad);\n\t\tmainWindow.addTreeUpdateListener(this::treeUpdate);\n\t}\n\n\tpublic void addMenuActions(JMenu menu) {\n\t\topenMappingsMenu = new JMenu(NLS.str(\"file.open_mappings\"));\n\t\topenMappingsMenu.add(new ActionHandler(ev -> openMappings(MappingFormat.PROGUARD_FILE, true))\n\t\t\t\t.withNameAndDesc(\"Proguard (inverted)\"));\n\t\topenMappingsMenu.add(new ActionHandler(ev -> openMappings(MappingFormat.PROGUARD_FILE, false)).withNameAndDesc(\"Proguard\"));\n\n\t\tsaveMappingsAction = new ActionHandler(this::saveMappings).withNameAndDesc(NLS.str(\"file.save_mappings\"));\n\n\t\tsaveMappingsAsMenu = new JMenu(NLS.str(\"file.save_mappings_as\"));\n\n\t\tfor (MappingFormat mappingFormat : MappingFormat.values()) {\n\t\t\tif (mappingFormat != MappingFormat.PROGUARD_FILE) {\n\t\t\t\topenMappingsMenu.add(new ActionHandler(ev -> openMappings(mappingFormat, false))\n\t\t\t\t\t\t.withNameAndDesc(mappingFormat.name));\n\t\t\t}\n\t\t\tsaveMappingsAsMenu.add(new ActionHandler(ev -> saveMappingsAs(mappingFormat))\n\t\t\t\t\t.withNameAndDesc(mappingFormat.name));\n\t\t}\n\n\t\tcloseMappingsAction = new ActionHandler(ev -> closeMappingsAndRemoveFromProject())\n\t\t\t\t.withNameAndDesc(NLS.str(\"file.close_mappings\"));\n\n\t\tmenu.addSeparator();\n\t\tmenu.add(openMappingsMenu);\n\t\tmenu.add(saveMappingsAction);\n\t\tmenu.add(saveMappingsAsMenu);\n\t\tmenu.add(closeMappingsAction);\n\t}\n\n\tprivate boolean onLoad(boolean loaded) {\n\t\trenamesChanged = false;\n\t\tmappingNode = null;\n\t\tif (loaded) {\n\t\t\tRootNode rootNode = mainWindow.getWrapper().getRootNode();\n\t\t\trootNode.registerCodeDataUpdateListener(codeData -> onRename());\n\t\t} else {\n\t\t\t// project or window close\n\t\t\tJadxProject project = mainWindow.getProject();\n\t\t\tJadxSettings settings = mainWindow.getSettings();\n\t\t\tif (project.getMappingsPath() != null\n\t\t\t\t\t&& settings.getUserRenamesMappingsMode() == UserRenamesMappingsMode.READ_AND_AUTOSAVE_BEFORE_CLOSING) {\n\t\t\t\tsaveMappings();\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate void onRename() {\n\t\tJadxProject project = mainWindow.getProject();\n\t\tJadxSettings settings = mainWindow.getSettings();\n\t\tif (project.getMappingsPath() != null\n\t\t\t\t&& settings.getUserRenamesMappingsMode() == UserRenamesMappingsMode.READ_AND_AUTOSAVE_EVERY_CHANGE) {\n\t\t\tsaveMappings();\n\t\t} else {\n\t\t\trenamesChanged = true;\n\t\t\tUiUtils.uiRun(mainWindow::update);\n\t\t}\n\t}\n\n\tpublic void onUpdate(boolean loaded) {\n\t\tJadxProject project = mainWindow.getProject();\n\t\topenMappingsMenu.setEnabled(loaded);\n\t\tsaveMappingsAction.setEnabled(loaded && renamesChanged && project.getMappingsPath() != null);\n\t\tsaveMappingsAsMenu.setEnabled(loaded);\n\t\tcloseMappingsAction.setEnabled(project.getMappingsPath() != null);\n\t}\n\n\tprivate void treeUpdate(JRoot treeRoot) {\n\t\tif (mappingNode != null) {\n\t\t\t// already added\n\t\t\treturn;\n\t\t}\n\t\tPath mappingsPath = mainWindow.getProject().getMappingsPath();\n\t\tif (mappingsPath == null) {\n\t\t\treturn;\n\t\t}\n\t\tJNode node = treeRoot.followStaticPath(\"JInputs\");\n\t\tJNode currentNode = node.removeNode(n -> n.getClass().equals(JInputMapping.class));\n\t\tif (currentNode != null) {\n\t\t\t// close opened tab\n\t\t\tTabbedPane tabbedPane = mainWindow.getTabbedPane();\n\t\t\tContentPanel openedTab = tabbedPane.getTabByNode(currentNode);\n\t\t\tif (openedTab != null) {\n\t\t\t\ttabbedPane.closeCodePanel(openedTab);\n\t\t\t}\n\t\t}\n\t\tmappingNode = new JInputMapping(mappingsPath);\n\t\tnode.add(mappingNode);\n\t}\n\n\tprivate void openMappings(MappingFormat mappingFormat, boolean inverted) {\n\t\tFileDialogWrapper fileDialog = new FileDialogWrapper(mainWindow, FileOpenMode.CUSTOM_OPEN);\n\t\tfileDialog.setTitle(NLS.str(\"file.open_mappings\"));\n\t\tif (mappingFormat.hasSingleFile()) {\n\t\t\tfileDialog.setFileExtList(Collections.singletonList(mappingFormat.fileExt));\n\t\t\tfileDialog.setSelectionMode(JFileChooser.FILES_ONLY);\n\t\t} else {\n\t\t\tfileDialog.setSelectionMode(JFileChooser.DIRECTORIES_ONLY);\n\t\t}\n\t\tList<Path> selectedPaths = fileDialog.show();\n\t\tif (selectedPaths.size() != 1) {\n\t\t\treturn;\n\t\t}\n\t\tPath filePath = selectedPaths.get(0);\n\t\tLOG.info(\"Loading mappings from: {}\", filePath.toAbsolutePath());\n\t\tJadxProject project = mainWindow.getProject();\n\t\tproject.setMappingsPath(filePath);\n\t\tproject.updatePluginOptions(options -> {\n\t\t\toptions.put(RenameMappingsOptions.FORMAT_OPT, mappingFormat.name());\n\t\t\toptions.put(RenameMappingsOptions.INVERT_OPT, inverted ? \"yes\" : \"no\");\n\t\t});\n\t\tmainWindow.reopen();\n\t}\n\n\tpublic void closeMappingsAndRemoveFromProject() {\n\t\tmainWindow.getProject().setMappingsPath(null);\n\t\tmainWindow.reopen();\n\t}\n\n\tprivate void saveMappings() {\n\t\trenamesChanged = false;\n\t\tsaveInBackground(getCurrentMappingFormat(),\n\t\t\t\tmainWindow.getProject().getMappingsPath(),\n\t\t\t\ts -> mainWindow.update());\n\t}\n\n\tprivate void saveMappingsAs(MappingFormat mappingFormat) {\n\t\tFileDialogWrapper fileDialog = new FileDialogWrapper(mainWindow, FileOpenMode.CUSTOM_SAVE);\n\t\tfileDialog.setTitle(NLS.str(\"file.save_mappings_as\"));\n\t\tif (mappingFormat.hasSingleFile()) {\n\t\t\tPath currentDir = Utils.getOrElse(fileDialog.getCurrentDir(), CommonFileUtils.CWD_PATH);\n\t\t\tfileDialog.setSelectedFile(currentDir.resolve(\"mappings.\" + mappingFormat.fileExt));\n\t\t\tfileDialog.setFileExtList(Collections.singletonList(mappingFormat.fileExt));\n\t\t\tfileDialog.setSelectionMode(JFileChooser.FILES_ONLY);\n\t\t} else {\n\t\t\tfileDialog.setSelectionMode(JFileChooser.DIRECTORIES_ONLY);\n\t\t}\n\t\tList<Path> selectedPaths = fileDialog.show();\n\t\tif (selectedPaths.size() != 1) {\n\t\t\treturn;\n\t\t}\n\t\tPath selectedPath = selectedPaths.get(0);\n\t\tPath savePath;\n\t\t// Append file extension if missing\n\t\tif (mappingFormat.hasSingleFile()\n\t\t\t\t&& !selectedPath.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(mappingFormat.fileExt)) {\n\t\t\tsavePath = selectedPath.resolveSibling(selectedPath.getFileName() + \".\" + mappingFormat.fileExt);\n\t\t} else {\n\t\t\tsavePath = selectedPath;\n\t\t}\n\t\t// If the target file already exists (and it's not an empty directory), show an overwrite\n\t\t// confirmation\n\t\tif (Files.exists(savePath)) {\n\t\t\tboolean emptyDir = false;\n\t\t\ttry (Stream<Path> entries = Files.list(savePath)) {\n\t\t\t\temptyDir = entries.findFirst().isEmpty();\n\t\t\t} catch (IOException ignored) {\n\t\t\t}\n\t\t\tif (!emptyDir) {\n\t\t\t\tint res = JOptionPane.showConfirmDialog(\n\t\t\t\t\t\tmainWindow,\n\t\t\t\t\t\tNLS.str(\"confirm.save_as_message\", savePath.getFileName()),\n\t\t\t\t\t\tNLS.str(\"confirm.save_as_title\"),\n\t\t\t\t\t\tJOptionPane.YES_NO_OPTION);\n\t\t\t\tif (res == JOptionPane.NO_OPTION) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tLOG.info(\"Saving mappings to: {}\", savePath.toAbsolutePath());\n\t\tJadxProject project = mainWindow.getProject();\n\t\tproject.setMappingsPath(savePath);\n\t\tproject.updatePluginOptions(options -> {\n\t\t\toptions.put(RenameMappingsOptions.FORMAT_OPT, mappingFormat.name());\n\t\t\toptions.put(RenameMappingsOptions.INVERT_OPT, \"no\");\n\t\t});\n\t\tsaveInBackground(mappingFormat, savePath, s -> {\n\t\t\tmappingNode = null;\n\t\t\tmainWindow.reloadTree();\n\t\t});\n\t}\n\n\tprivate void saveInBackground(MappingFormat mappingFormat, Path savePath, Consumer<TaskStatus> onFinishUiRunnable) {\n\t\tmainWindow.getBackgroundExecutor().execute(NLS.str(\"progress.save_mappings\"),\n\t\t\t\t() -> new MappingExporter(mainWindow.getWrapper().getRootNode())\n\t\t\t\t\t\t.exportMappings(savePath, mainWindow.getProject().getCodeData(), mappingFormat),\n\t\t\t\tonFinishUiRunnable);\n\t}\n\n\tprivate MappingFormat getCurrentMappingFormat() {\n\t\tJadxProject project = mainWindow.getProject();\n\t\tString fmtStr = project.getPluginOption(RenameMappingsOptions.FORMAT_OPT);\n\t\tif (fmtStr != null) {\n\t\t\treturn MappingFormat.valueOf(fmtStr);\n\t\t}\n\t\tPath mappingsPath = project.getMappingsPath();\n\t\ttry {\n\t\t\treturn MappingReader.detectFormat(mappingsPath);\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(\"Failed to detect mapping format for: \" + mappingsPath);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/plugins/quark/QuarkDialog.java",
    "content": "package jadx.gui.plugins.quark;\n\nimport java.awt.BorderLayout;\nimport java.awt.Container;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Path;\nimport java.nio.file.PathMatcher;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport javax.swing.JButton;\nimport javax.swing.JComboBox;\nimport javax.swing.JDialog;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.WindowConstants;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.ui.NodeLabel;\n\npublic class QuarkDialog extends JDialog {\n\tprivate static final long serialVersionUID = 4855753773520368215L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(QuarkDialog.class);\n\n\tprivate final transient JadxSettings settings;\n\tprivate final transient MainWindow mainWindow;\n\tprivate final List<Path> files;\n\n\tprivate JComboBox<Path> fileSelectCombo;\n\n\tpublic QuarkDialog(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.settings = mainWindow.getSettings();\n\t\tthis.files = filterOpenFiles(mainWindow);\n\t\tif (files.isEmpty()) {\n\t\t\tUiUtils.errorMessage(mainWindow, \"Quark is unable to analyze loaded files\");\n\t\t\tLOG.error(\"Quark: The files cannot be analyzed: {}\", mainWindow.getProject().getFilePaths());\n\t\t\treturn;\n\t\t}\n\t\tinitUI();\n\t}\n\n\tprivate List<Path> filterOpenFiles(MainWindow mainWindow) {\n\t\tPathMatcher matcher = FileSystems.getDefault().getPathMatcher(\"glob:**.{apk,dex}\");\n\t\treturn mainWindow.getProject().getFilePaths()\n\t\t\t\t.stream()\n\t\t\t\t.filter(matcher::matches)\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tprivate void initUI() {\n\t\tJLabel description = new JLabel(\"Analyzing apk using Quark-Engine\");\n\t\tJLabel selectApkText = new JLabel(\"Select Apk/Dex\");\n\t\tdescription.setAlignmentX(0.5f);\n\n\t\tfileSelectCombo = new JComboBox<>(files.toArray(new Path[0]));\n\t\tfileSelectCombo.setRenderer((list, value, index, isSelected, cellHasFocus) -> new NodeLabel(value.getFileName().toString()));\n\n\t\tJPanel textPane = new JPanel();\n\t\ttextPane.add(description);\n\n\t\tJPanel selectApkPanel = new JPanel();\n\t\tselectApkPanel.add(selectApkText);\n\t\tselectApkPanel.add(fileSelectCombo);\n\n\t\tJPanel buttonPane = new JPanel();\n\t\tJButton start = new JButton(\"Start\");\n\t\tJButton close = new JButton(\"Close\");\n\t\tclose.addActionListener(event -> close());\n\t\tstart.addActionListener(event -> startQuarkTasks());\n\t\tbuttonPane.add(start);\n\t\tbuttonPane.add(close);\n\t\tgetRootPane().setDefaultButton(close);\n\n\t\tJPanel centerPane = new JPanel();\n\t\tcenterPane.add(selectApkPanel);\n\t\tContainer contentPane = getContentPane();\n\n\t\tcontentPane.add(textPane, BorderLayout.PAGE_START);\n\t\tcontentPane.add(centerPane);\n\t\tcontentPane.add(buttonPane, BorderLayout.PAGE_END);\n\n\t\tsetTitle(\"Quark Engine\");\n\t\tpack();\n\t\tif (!mainWindow.getSettings().loadWindowPos(this)) {\n\t\t\tsetSize(300, 140);\n\t\t}\n\t\tsetLocationRelativeTo(null);\n\t\tsetDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\n\t\tsetModalityType(ModalityType.APPLICATION_MODAL);\n\t\tUiUtils.addEscapeShortCutToDispose(this);\n\t}\n\n\tprivate void startQuarkTasks() {\n\t\tPath apkFile = (Path) fileSelectCombo.getSelectedItem();\n\t\tnew QuarkManager(mainWindow, apkFile).start();\n\t\tclose();\n\t}\n\n\tprivate void close() {\n\t\tdispose();\n\t}\n\n\t@Override\n\tpublic void dispose() {\n\t\tsettings.saveWindowPos(this);\n\t\tsuper.dispose();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/plugins/quark/QuarkManager.java",
    "content": "package jadx.gui.plugins.quark;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.InputStreamReader;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.swing.JOptionPane;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport ch.qos.logback.classic.Level;\n\nimport jadx.commons.app.JadxSystemInfo;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.jobs.BackgroundExecutor;\nimport jadx.gui.logs.LogOptions;\nimport jadx.gui.treemodel.JRoot;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.UiUtils;\n\npublic class QuarkManager {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(QuarkManager.class);\n\n\tprivate static final Path QUARK_DIR_PATH = Paths.get(System.getProperty(\"user.home\"), \".quark-engine\");\n\tprivate static final Path VENV_PATH = QUARK_DIR_PATH.resolve(\"quark_venv\");\n\tprivate static final int LARGE_APK_SIZE = 30;\n\n\tprivate final MainWindow mainWindow;\n\tprivate final Path apkPath;\n\n\tprivate boolean useVEnv;\n\tprivate boolean installComplete;\n\tprivate Path reportFile;\n\n\tpublic QuarkManager(MainWindow mainWindow, Path apkPath) {\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.apkPath = apkPath;\n\t}\n\n\tpublic void start() {\n\t\tif (!checkFileSize(LARGE_APK_SIZE)) {\n\t\t\tint result = JOptionPane.showConfirmDialog(mainWindow,\n\t\t\t\t\t\"The selected file size is too large (over 30M) that may take a long time to analyze, do you want to continue\",\n\t\t\t\t\t\"Quark: Warning\", JOptionPane.YES_NO_OPTION);\n\t\t\tif (result == JOptionPane.NO_OPTION) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tBackgroundExecutor executor = mainWindow.getBackgroundExecutor();\n\t\texecutor.execute(\"Quark install\", this::checkInstall,\n\t\t\t\tinstallStatus -> executor.execute(\"Quark analysis\", this::startAnalysis, analysisStatus -> loadReport()));\n\t}\n\n\tprivate void checkInstall() {\n\t\ttry {\n\t\t\tif (checkCommand(\"quark\")) {\n\t\t\t\tuseVEnv = false;\n\t\t\t\tinstallComplete = true;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tuseVEnv = true;\n\t\t\tif (checkVEnvCommand(\"quark\")) {\n\t\t\t\tinstallComplete = true;\n\t\t\t\tinstallQuark(); // upgrade quark\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tint result = JOptionPane.showConfirmDialog(mainWindow,\n\t\t\t\t\t\"Quark is not installed, do you want to install it from PyPI?\", \"Warning\",\n\t\t\t\t\tJOptionPane.YES_NO_OPTION);\n\t\t\tif (result == JOptionPane.NO_OPTION) {\n\t\t\t\tinstallComplete = false;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcreateVirtualEnv();\n\t\t\tinstallQuark();\n\t\t\tinstallComplete = true;\n\t\t} catch (Exception e) {\n\t\t\tUiUtils.errorMessage(mainWindow, e.getMessage());\n\t\t\tLOG.error(\"Failed to install quark\", e);\n\t\t\tinstallComplete = false;\n\t\t}\n\t}\n\n\tprivate void startAnalysis() {\n\t\tif (!installComplete) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tupdateQuarkRules();\n\t\t\treportFile = Files.createTempFile(\"QuarkReport-\", \".json\").toAbsolutePath();\n\t\t\tList<String> cmd = new ArrayList<>();\n\t\t\tcmd.add(getCommand(\"quark\"));\n\t\t\tcmd.add(\"-a\");\n\t\t\tcmd.add(apkPath.toString());\n\t\t\tcmd.add(\"-o\");\n\t\t\tcmd.add(reportFile.toString());\n\t\t\trunCommand(cmd);\n\t\t} catch (Exception e) {\n\t\t\tUiUtils.errorMessage(mainWindow, \"Failed to execute Quark\");\n\t\t\tLOG.error(\"Failed to execute Quark\", e);\n\t\t}\n\t}\n\n\tprivate void loadReport() {\n\t\ttry {\n\t\t\tQuarkReportNode quarkNode = new QuarkReportNode(reportFile);\n\t\t\tJRoot root = mainWindow.getTreeRoot();\n\t\t\troot.replaceCustomNode(quarkNode);\n\t\t\troot.update();\n\t\t\tmainWindow.reloadTree();\n\t\t\tmainWindow.getTabsController().selectTab(quarkNode);\n\t\t} catch (Exception e) {\n\t\t\tUiUtils.errorMessage(mainWindow, \"Failed to load Quark report.\");\n\t\t\tLOG.error(\"Failed to load Quark report.\", e);\n\t\t}\n\t}\n\n\tprivate void createVirtualEnv() {\n\t\tif (Files.exists(getVenvPath(\"activate\"))) {\n\t\t\treturn;\n\t\t}\n\t\tFile directory = QUARK_DIR_PATH.toFile();\n\t\tif (!directory.isDirectory()) {\n\t\t\tif (!directory.mkdirs()) {\n\t\t\t\tthrow new JadxRuntimeException(\"Failed create directory: \" + directory);\n\t\t\t}\n\t\t}\n\t\tList<String> cmd = new ArrayList<>();\n\t\tif (JadxSystemInfo.IS_WINDOWS) {\n\t\t\tcmd.add(\"python\");\n\t\t\tcmd.add(\"-m\");\n\t\t\tcmd.add(\"venv\");\n\t\t} else {\n\t\t\tcmd.add(\"virtualenv\");\n\t\t}\n\t\tcmd.add(VENV_PATH.toString());\n\t\ttry {\n\t\t\trunCommand(cmd);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to create virtual environment\", e);\n\t\t}\n\t}\n\n\tprivate void installQuark() {\n\t\tList<String> cmd = new ArrayList<>();\n\t\tcmd.add(getCommand(\"pip3\"));\n\t\tcmd.add(\"install\");\n\t\tcmd.add(\"setuptools\");\n\t\tcmd.add(\"quark-engine\");\n\t\tcmd.add(\"--upgrade\");\n\t\ttry {\n\t\t\trunCommand(cmd);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to install quark-engine\", e);\n\t\t}\n\t}\n\n\tprivate void updateQuarkRules() {\n\t\tList<String> cmd = new ArrayList<>();\n\t\tcmd.add(getCommand(\"freshquark\"));\n\t\ttry {\n\t\t\trunCommand(cmd);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to update quark rules\", e);\n\t\t}\n\t}\n\n\tpublic boolean checkFileSize(int sizeThreshold) {\n\t\ttry {\n\t\t\tint fileSize = (int) Files.size(apkPath) / 1024 / 1024;\n\t\t\tif (fileSize > sizeThreshold) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to calculate file: {}\", e.getMessage(), e);\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate String getCommand(String cmd) {\n\t\tif (useVEnv) {\n\t\t\treturn getVenvPath(cmd).toAbsolutePath().toString();\n\t\t}\n\t\treturn cmd;\n\t}\n\n\tprivate boolean checkVEnvCommand(String cmd) {\n\t\tPath venvPath = getVenvPath(cmd);\n\t\treturn checkCommand(venvPath.toAbsolutePath().toString());\n\t}\n\n\tprivate Path getVenvPath(String cmd) {\n\t\tif (JadxSystemInfo.IS_WINDOWS) {\n\t\t\treturn VENV_PATH.resolve(\"Scripts\").resolve(cmd + \".exe\");\n\t\t}\n\t\treturn VENV_PATH.resolve(\"bin\").resolve(cmd);\n\t}\n\n\tprivate void runCommand(List<String> cmd) throws Exception {\n\t\tmainWindow.showLogViewer(LogOptions.forLevel(Level.INFO));\n\t\tLOG.info(\"Running command: {}\", String.join(\" \", cmd));\n\t\tProcessBuilder builder = new ProcessBuilder(cmd);\n\t\tbuilder.redirectErrorStream(true);\n\t\tProcess process = builder.start();\n\t\ttry (BufferedReader buf = new BufferedReader(new InputStreamReader(process.getInputStream()))) {\n\t\t\tbuf.lines().forEach(msg -> LOG.info(\"# {}\", msg));\n\t\t} finally {\n\t\t\tprocess.waitFor();\n\t\t}\n\t\tif (process.exitValue() != 0) {\n\t\t\tthrow new RuntimeException(\"Execution failed (exit code \" + process.exitValue() + \") - command \"\n\t\t\t\t\t+ String.join(\" \", cmd) + \"\\nPlease see command log output what was going wrong.\");\n\t\t}\n\t}\n\n\tprivate boolean checkCommand(String... cmd) {\n\t\ttry {\n\t\t\tProcess process = Runtime.getRuntime().exec(cmd);\n\t\t\tprocess.waitFor();\n\t\t} catch (Exception e) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/plugins/quark/QuarkReportData.java",
    "content": "package jadx.gui.plugins.quark;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.annotations.SerializedName;\n\nimport jadx.core.utils.Utils;\n\n@SuppressWarnings(\"MemberName\")\npublic class QuarkReportData {\n\n\tpublic static class Crime {\n\t\tpublic String crime;\n\t\tpublic String confidence;\n\t\tpublic List<String> permissions;\n\n\t\tList<Method> native_api;\n\t\tList<JsonElement> combination;\n\t\tList<Map<String, InvokePlace>> register;\n\n\t\tpublic int parseConfidence() {\n\t\t\treturn Integer.parseInt(confidence.replace(\"%\", \"\"));\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tfinal StringBuffer sb = new StringBuffer(\"Crime{\");\n\t\t\tsb.append(\"crime='\").append(crime).append('\\'');\n\t\t\tsb.append(\", confidence='\").append(confidence).append('\\'');\n\t\t\tsb.append(\", permissions=\").append(permissions);\n\t\t\tsb.append(\", native_api=\").append(native_api);\n\t\t\tsb.append(\", combination=\").append(combination);\n\t\t\tsb.append(\", register=\").append(register);\n\t\t\tsb.append('}');\n\t\t\treturn sb.toString();\n\t\t}\n\t}\n\n\tpublic static class Method {\n\t\t@SerializedName(\"class\")\n\t\tString cls;\n\t\tString method;\n\t\tString descriptor;\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tsb.append(Utils.cleanObjectName(cls)).append(\".\").append(method);\n\t\t\tif (descriptor != null) {\n\t\t\t\tsb.append(descriptor);\n\t\t\t}\n\t\t\treturn sb.toString();\n\t\t}\n\t}\n\n\tpublic static class InvokePlace {\n\t\tList<String> first;\n\t\tList<String> second;\n\t}\n\n\tString apk_filename;\n\tString threat_level;\n\tint total_score;\n\tList<Crime> crimes;\n\n\tpublic void validate() {\n\t\tif (crimes == null) {\n\t\t\tthrow new RuntimeException(\"Invalid data: \\\"crimes\\\" list missing\");\n\t\t}\n\t\tfor (Crime crime : crimes) {\n\t\t\tif (crime.confidence == null) {\n\t\t\t\tthrow new RuntimeException(\"Confidence value missing: \" + crime);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tcrime.parseConfidence();\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new RuntimeException(\"Invalid crime entry: \" + crime);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/plugins/quark/QuarkReportNode.java",
    "content": "package jadx.gui.plugins.quark;\n\nimport java.io.BufferedReader;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\n\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.apache.commons.text.StringEscapeUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.core.utils.GsonUtils;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.panel.HtmlPanel;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.UiUtils;\n\npublic class QuarkReportNode extends JNode {\n\n\tprivate static final long serialVersionUID = -766800957202637021L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(QuarkReportNode.class);\n\n\tprivate static final ImageIcon ICON = UiUtils.openSvgIcon(\"ui/quark\");\n\n\tprivate final Path reportFile;\n\n\tprivate ICodeInfo errorContent;\n\n\tpublic QuarkReportNode(Path reportFile) {\n\t\tthis.reportFile = reportFile;\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn ICON;\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn \"Quark analysis report\";\n\t}\n\n\t@Override\n\tpublic boolean hasContent() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic ContentPanel getContentPanel(TabbedPane tabbedPane) {\n\t\ttry {\n\t\t\tQuarkReportData data;\n\t\t\ttry (BufferedReader reader = Files.newBufferedReader(reportFile)) {\n\t\t\t\tdata = GsonUtils.buildGson().fromJson(reader, QuarkReportData.class);\n\t\t\t}\n\t\t\tdata.validate();\n\t\t\treturn new QuarkReportPanel(tabbedPane, this, data);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Quark report parse error\", e);\n\t\t\tStringEscapeUtils.Builder builder = StringEscapeUtils.builder(StringEscapeUtils.ESCAPE_HTML4);\n\t\t\tbuilder.append(\"<h2>\");\n\t\t\tbuilder.escape(\"Quark analysis failed!\");\n\t\t\tbuilder.append(\"</h2>\");\n\t\t\tbuilder.append(\"<h3>\");\n\t\t\tbuilder.append(\"Error: \").escape(e.getMessage());\n\t\t\tbuilder.append(\"</h3>\");\n\t\t\tbuilder.append(\"<pre>\");\n\t\t\tbuilder.escape(ExceptionUtils.getStackTrace(e));\n\t\t\tbuilder.append(\"</pre>\");\n\t\t\terrorContent = new SimpleCodeInfo(builder.toString());\n\t\t\treturn new HtmlPanel(tabbedPane, this);\n\t\t}\n\t}\n\n\t@Override\n\tpublic ICodeInfo getCodeInfo() {\n\t\treturn errorContent;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/plugins/quark/QuarkReportPanel.java",
    "content": "package jadx.gui.plugins.quark;\n\nimport java.awt.BorderLayout;\nimport java.awt.Component;\nimport java.awt.Font;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.util.Comparator;\nimport java.util.Enumeration;\nimport java.util.IdentityHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.JEditorPane;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTree;\nimport javax.swing.ScrollPaneConstants;\nimport javax.swing.SwingUtilities;\nimport javax.swing.event.TreeExpansionEvent;\nimport javax.swing.event.TreeExpansionListener;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.MutableTreeNode;\nimport javax.swing.tree.TreeCellRenderer;\nimport javax.swing.tree.TreeNode;\nimport javax.swing.tree.TreePath;\n\nimport org.apache.commons.text.StringEscapeUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.beust.jcommander.Strings;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonElement;\n\nimport jadx.api.JavaClass;\nimport jadx.api.JavaMethod;\nimport jadx.core.utils.Utils;\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.treemodel.JMethod;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.JNodeCache;\nimport jadx.gui.utils.ui.NodeLabel;\n\npublic class QuarkReportPanel extends ContentPanel {\n\tprivate static final long serialVersionUID = -242266836695889206L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(QuarkReportPanel.class);\n\n\tprivate final QuarkReportData data;\n\tprivate final JNodeCache nodeCache;\n\n\tprivate JEditorPane header;\n\tprivate JTree tree;\n\tprivate DefaultMutableTreeNode treeRoot;\n\tprivate Font font;\n\tprivate Font boldFont;\n\tprivate CachingTreeCellRenderer cellRenderer;\n\n\tprotected QuarkReportPanel(TabbedPane panel, QuarkReportNode node, QuarkReportData data) {\n\t\tsuper(panel, node);\n\t\tthis.data = data;\n\t\tthis.nodeCache = panel.getMainWindow().getCacheObject().getNodeCache();\n\t\tprepareData();\n\t\tinitUI();\n\t\tloadSettings();\n\t}\n\n\tprivate void prepareData() {\n\t\tdata.crimes.sort(Comparator.comparingInt(c -> -c.parseConfidence()));\n\t}\n\n\tprivate void initUI() {\n\t\tsetLayout(new BorderLayout());\n\n\t\theader = new JEditorPane();\n\t\theader.setContentType(\"text/html\");\n\t\theader.setEditable(false);\n\t\theader.setText(buildHeader());\n\n\t\tcellRenderer = new CachingTreeCellRenderer();\n\t\ttreeRoot = new TextTreeNode(\"Potential Malicious Activities:\").bold();\n\t\ttree = buildTree();\n\t\tfor (QuarkReportData.Crime crime : data.crimes) {\n\t\t\ttreeRoot.add(new CrimeTreeNode(crime));\n\t\t}\n\t\ttree.expandRow(0);\n\t\ttree.expandRow(1);\n\n\t\tJScrollPane tableScroll = new JScrollPane(tree);\n\t\ttableScroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);\n\n\t\tJPanel mainPanel = new JPanel();\n\t\tmainPanel.setLayout(new BorderLayout());\n\t\tmainPanel.add(header, BorderLayout.PAGE_START);\n\t\tmainPanel.add(tableScroll, BorderLayout.CENTER);\n\n\t\tadd(mainPanel);\n\t}\n\n\tprivate JTree buildTree() {\n\t\tJTree tree = new JTree(treeRoot);\n\t\ttree.setLayout(new BorderLayout());\n\t\ttree.setBorder(BorderFactory.createEmptyBorder());\n\t\ttree.setShowsRootHandles(false);\n\t\ttree.setScrollsOnExpand(false);\n\t\ttree.setSelectionModel(null);\n\t\ttree.setCellRenderer(cellRenderer);\n\t\ttree.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(MouseEvent event) {\n\t\t\t\tif (SwingUtilities.isLeftMouseButton(event)) {\n\t\t\t\t\tObject node = getNodeUnderMouse(tree, event);\n\t\t\t\t\tif (node instanceof MethodTreeNode) {\n\t\t\t\t\t\tJMethod method = ((MethodTreeNode) node).getJMethod();\n\t\t\t\t\t\ttabbedPane.getTabsController().codeJump(method);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\ttree.addTreeExpansionListener(new TreeExpansionListener() {\n\t\t\t@Override\n\t\t\tpublic void treeExpanded(TreeExpansionEvent event) {\n\t\t\t\tTreePath path = event.getPath();\n\t\t\t\tObject leaf = path.getLastPathComponent();\n\t\t\t\tif (leaf instanceof CrimeTreeNode) {\n\t\t\t\t\tCrimeTreeNode node = (CrimeTreeNode) leaf;\n\t\t\t\t\tEnumeration<TreeNode> children = node.children();\n\t\t\t\t\twhile (children.hasMoreElements()) {\n\t\t\t\t\t\tTreeNode child = children.nextElement();\n\t\t\t\t\t\ttree.expandPath(path.pathByAddingChild(child));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void treeCollapsed(TreeExpansionEvent event) {\n\t\t\t}\n\t\t});\n\t\treturn tree;\n\t}\n\n\tprivate String buildHeader() {\n\t\tStringEscapeUtils.Builder builder = StringEscapeUtils.builder(StringEscapeUtils.ESCAPE_HTML4);\n\t\tbuilder.append(\"<h1>Quark Analysis Report</h1>\");\n\t\tbuilder.append(\"<h3>\");\n\t\tbuilder.append(\"File: \").append(data.apk_filename);\n\t\tbuilder.append(\"<br>\");\n\t\tbuilder.append(\"Treat level: \").append(data.threat_level);\n\t\tbuilder.append(\"<br>\");\n\t\tbuilder.append(\"Total score: \").append(Integer.toString(data.total_score));\n\t\tbuilder.append(\"</h3>\");\n\t\treturn builder.toString();\n\t}\n\n\t@Override\n\tpublic void loadSettings() {\n\t\tFont settingsFont = getMainWindow().getSettings().getCodeFont();\n\t\tthis.font = settingsFont.deriveFont(settingsFont.getSize2D() + 1.f);\n\t\tthis.boldFont = font.deriveFont(Font.BOLD);\n\t\theader.setFont(font);\n\t\ttree.setFont(font);\n\t\tcellRenderer.clearCache();\n\t}\n\n\tprivate static Object getNodeUnderMouse(JTree tree, MouseEvent mouseEvent) {\n\t\tTreePath path = tree.getPathForLocation(mouseEvent.getX(), mouseEvent.getY());\n\t\treturn path != null ? path.getLastPathComponent() : null;\n\t}\n\n\tprivate static class CachingTreeCellRenderer implements TreeCellRenderer {\n\t\tprivate final Map<BaseTreeNode, Component> cache = new IdentityHashMap<>();\n\n\t\t@Override\n\t\tpublic Component getTreeCellRendererComponent(JTree tr, Object value, boolean selected,\n\t\t\t\tboolean expanded, boolean leaf, int row, boolean focus) {\n\t\t\treturn cache.computeIfAbsent((BaseTreeNode) value, BaseTreeNode::render);\n\t\t}\n\n\t\tpublic void clearCache() {\n\t\t\tcache.clear();\n\t\t}\n\t}\n\n\tprivate abstract static class BaseTreeNode extends DefaultMutableTreeNode {\n\t\tprivate static final long serialVersionUID = 7197501219150495889L;\n\n\t\tpublic BaseTreeNode(Object userObject) {\n\t\t\tsuper(userObject);\n\t\t}\n\n\t\tpublic abstract Component render();\n\t}\n\n\tprivate class TextTreeNode extends BaseTreeNode {\n\t\tprivate static final long serialVersionUID = 6763410122501083453L;\n\n\t\tprivate boolean bold;\n\n\t\tpublic TextTreeNode(String text) {\n\t\t\tsuper(text);\n\t\t}\n\n\t\tpublic TextTreeNode bold() {\n\t\t\tbold = true;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic Component render() {\n\t\t\tJLabel label = new NodeLabel(((String) getUserObject()));\n\t\t\tlabel.setFont(bold ? boldFont : font);\n\t\t\tlabel.setIcon(null);\n\t\t\tlabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\n\t\t\treturn label;\n\t\t}\n\t}\n\n\tprivate class CrimeTreeNode extends TextTreeNode {\n\t\tprivate static final long serialVersionUID = -1464310215237483911L;\n\n\t\tprivate final QuarkReportData.Crime crime;\n\n\t\tpublic CrimeTreeNode(QuarkReportData.Crime crime) {\n\t\t\tsuper(crime.crime);\n\t\t\tthis.crime = crime;\n\t\t\tbold();\n\t\t\taddDetails();\n\t\t}\n\n\t\tprivate void addDetails() {\n\t\t\tadd(new TextTreeNode(\"Confidence: \" + crime.confidence));\n\t\t\tif (Utils.notEmpty(crime.permissions)) {\n\t\t\t\tadd(new TextTreeNode(\"Permissions: \" + Strings.join(\", \", crime.permissions)));\n\t\t\t}\n\t\t\tif (Utils.notEmpty(crime.native_api)) {\n\t\t\t\tTextTreeNode node = new TextTreeNode(\"Native API\");\n\t\t\t\tfor (QuarkReportData.Method method : crime.native_api) {\n\t\t\t\t\tnode.add(new TextTreeNode(method.toString()));\n\t\t\t\t}\n\t\t\t\tadd(node);\n\t\t\t}\n\t\t\tList<JsonElement> combination = crime.combination;\n\t\t\tif (Utils.notEmpty(combination) && combination.get(0) instanceof JsonArray) {\n\t\t\t\tTextTreeNode node = new TextTreeNode(\"Combination\");\n\t\t\t\tint size = combination.size();\n\t\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\t\tTextTreeNode set = new TextTreeNode(\"Set \" + i);\n\t\t\t\t\tJsonArray array = (JsonArray) combination.get(i);\n\t\t\t\t\tfor (JsonElement ele : array) {\n\t\t\t\t\t\tString mth = ele.getAsString();\n\t\t\t\t\t\tset.add(resolveMethod(mth));\n\t\t\t\t\t}\n\t\t\t\t\tnode.add(set);\n\t\t\t\t}\n\t\t\t\tadd(node);\n\t\t\t}\n\t\t\tif (Utils.notEmpty(crime.register)) {\n\t\t\t\tTextTreeNode node = new TextTreeNode(\"Invocations\");\n\t\t\t\tfor (Map<String, QuarkReportData.InvokePlace> invokeMap : crime.register) {\n\t\t\t\t\tinvokeMap.forEach((key, value) -> node.add(resolveMethod(key)));\n\t\t\t\t}\n\t\t\t\tadd(node);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn crime.crime;\n\t\t}\n\t}\n\n\tpublic MutableTreeNode resolveMethod(String descr) {\n\t\ttry {\n\t\t\tString[] parts = removeQuotes(descr).split(\" \", 3);\n\t\t\tString cls = Utils.cleanObjectName(parts[0].replace('$', '.'));\n\t\t\tString mth = parts[1] + parts[2].replace(\" \", \"\");\n\t\t\tMainWindow mainWindow = getMainWindow();\n\t\t\tJadxWrapper wrapper = mainWindow.getWrapper();\n\t\t\tJavaClass javaClass = wrapper.searchJavaClassByRawName(cls);\n\t\t\tif (javaClass == null) {\n\t\t\t\treturn new TextTreeNode(cls + \".\" + mth);\n\t\t\t}\n\t\t\tJavaMethod javaMethod = javaClass.searchMethodByShortId(mth);\n\t\t\tif (javaMethod == null) {\n\t\t\t\treturn new TextTreeNode(javaClass.getFullName() + \".\" + mth);\n\t\t\t}\n\t\t\treturn new MethodTreeNode(javaMethod);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to parse method descriptor string: {}\", descr, e);\n\t\t\treturn new TextTreeNode(descr);\n\t\t}\n\t}\n\n\tprivate static String removeQuotes(String descr) {\n\t\tif (descr.charAt(0) == '\\'') {\n\t\t\treturn descr.substring(1, descr.length() - 1);\n\t\t}\n\t\treturn descr;\n\t}\n\n\tprivate class MethodTreeNode extends BaseTreeNode {\n\t\tprivate static final long serialVersionUID = 4350343915220068508L;\n\n\t\tprivate final JavaMethod mth;\n\t\tprivate final JMethod jnode;\n\n\t\tpublic MethodTreeNode(JavaMethod mth) {\n\t\t\tsuper(mth);\n\t\t\tthis.mth = mth;\n\t\t\tthis.jnode = (JMethod) nodeCache.makeFrom(mth);\n\t\t}\n\n\t\tpublic JMethod getJMethod() {\n\t\t\treturn jnode;\n\t\t}\n\n\t\t@Override\n\t\tpublic Component render() {\n\t\t\tJLabel label = new NodeLabel(mth.toString());\n\t\t\tlabel.setFont(font);\n\t\t\tlabel.setIcon(jnode.getIcon());\n\t\t\tlabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\n\t\t\treturn label;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/report/ExceptionData.java",
    "content": "package jadx.gui.report;\n\nfinal class ExceptionData {\n\tprivate final Throwable exception;\n\tprivate final String githubProject;\n\n\tExceptionData(Throwable exception, String githubProject) {\n\t\tthis.exception = exception;\n\t\tthis.githubProject = githubProject;\n\t}\n\n\tpublic Throwable getException() {\n\t\treturn exception;\n\t}\n\n\tpublic String getGithubProject() {\n\t\treturn githubProject;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/report/ExceptionDialog.java",
    "content": "package jadx.gui.report;\n\nimport java.awt.BorderLayout;\nimport java.awt.Color;\nimport java.awt.Dimension;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.Insets;\nimport java.awt.Toolkit;\nimport java.awt.event.KeyEvent;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.lang.management.ManagementFactory;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.swing.JButton;\nimport javax.swing.JComponent;\nimport javax.swing.JDialog;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTextArea;\nimport javax.swing.KeyStroke;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxDecompiler;\nimport jadx.cli.config.JadxConfigAdapter;\nimport jadx.commons.app.JadxSystemInfo;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.settings.JadxSettingsData;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.LafManager;\nimport jadx.gui.utils.Link;\nimport jadx.gui.utils.TextStandardActions;\n\npublic class ExceptionDialog extends JDialog {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ExceptionDialog.class);\n\n\tprivate static final String FMT_DETAIL_LENGTH = \"-13\";\n\n\tExceptionDialog(MainWindow mainWindow, ExceptionData data) {\n\t\tsuper(mainWindow, \"Jadx Error\");\n\t\tthis.getContentPane().setLayout(new BorderLayout());\n\t\tJPanel titlePanel = new JPanel(new GridBagLayout());\n\t\tGridBagConstraints c = new GridBagConstraints();\n\t\tc.fill = GridBagConstraints.CENTER;\n\t\tc.gridx = 0;\n\t\tc.weightx = 1.0;\n\t\tc.insets = new Insets(2, 5, 5, 5);\n\t\tJLabel titleLabel = new JLabel(\"<html><h1>An error occurred</h1><p>Jadx encountered an unexpected error.</p></html>\");\n\n\t\tMap<String, String> details = new LinkedHashMap<>();\n\t\tdetails.put(\"Jadx version\", JadxDecompiler.getVersion());\n\t\tdetails.put(\"Java version\", JadxSystemInfo.JAVA_VER);\n\t\tdetails.put(\"Java VM\", String.format(\"%s %s\",\n\t\t\t\tSystem.getProperty(\"java.vm.vendor\", \"?\"), System.getProperty(\"java.vm.name\", \"?\")));\n\t\tdetails.put(\"Platform\", String.format(\"%s (%s %s)\",\n\t\t\t\tJadxSystemInfo.OS_NAME, JadxSystemInfo.OS_VERSION, JadxSystemInfo.OS_ARCH));\n\t\tRuntime runtime = Runtime.getRuntime();\n\t\tdetails.put(\"Max heap size\", String.format(\"%d MB\", runtime.maxMemory() / (1024 * 1024)));\n\n\t\ttry {\n\t\t\t// TODO: Use ProcessHandle.current().info().commandLine() once min Java is 9+\n\t\t\tList<String> args = ManagementFactory.getRuntimeMXBean().getInputArguments();\n\t\t\tdetails.put(\"Program args\", String.join(\" \", args));\n\t\t} catch (Throwable t) {\n\t\t\tLOG.error(\"failed to get program arguments\", t);\n\t\t}\n\n\t\tThrowable ex = data.getException();\n\t\tStringWriter stackTraceWriter = new StringWriter(1024);\n\t\tex.printStackTrace(new PrintWriter(stackTraceWriter));\n\t\tfinal String stackTrace = stackTraceWriter.toString();\n\n\t\tString issueTitle;\n\t\ttry {\n\t\t\tissueTitle = URLEncoder.encode(ex.toString(), StandardCharsets.UTF_8);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"URL encoding of title failed\", e);\n\t\t\tissueTitle = ex.getClass().getSimpleName();\n\t\t}\n\n\t\tString message = \"Please describe what you did before the error occurred.\\n\\n\";\n\t\tmessage += \"**IMPORTANT!** If the error occurs with a specific APK file please attach or provide link to apk file!\\n\\n\";\n\n\t\tStringBuilder detailsIssueBuilder = new StringBuilder();\n\t\tdetails.forEach((key, value) -> detailsIssueBuilder.append(String.format(\"* %s: %s\\n\", key, value)));\n\n\t\tString body = String.format(\"%s%s\\n```\\n%s\\n```\", message, detailsIssueBuilder, stackTrace);\n\n\t\tString issueBody;\n\t\ttry {\n\t\t\tissueBody = URLEncoder.encode(body, StandardCharsets.UTF_8);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"URL encoding of body failed\", e);\n\t\t\tissueBody = \"Please copy the displayed text in the Jadx error dialog and paste it here\";\n\t\t}\n\n\t\tc.gridy = 0;\n\t\ttitlePanel.add(titleLabel, c);\n\n\t\tString project = data.getGithubProject();\n\t\tif (!project.isEmpty()) {\n\t\t\tString url = String.format(\"https://github.com/%s/issues/new?labels=bug&title=%s&body=%s\",\n\t\t\t\t\tproject, issueTitle, issueBody);\n\t\t\tLink issueLink = new Link(\"<html><u><b>Create a new issue at GitHub</b></u></html>\", url);\n\t\t\tc.gridy = 1;\n\t\t\ttitlePanel.add(issueLink, c);\n\t\t}\n\t\tJTextArea messageArea = new JTextArea();\n\t\tTextStandardActions.attach(messageArea);\n\t\tmessageArea.setEditable(false);\n\t\tmessageArea.setFont(mainWindow.getSettings().getCodeFont().deriveFont(12f));\n\t\tmessageArea.setForeground(Color.BLACK);\n\t\tmessageArea.setBackground(Color.WHITE);\n\n\t\tStringBuilder detailsTextBuilder = new StringBuilder();\n\t\tdetails.forEach((key, value) -> detailsTextBuilder.append(String.format(\"%\" + FMT_DETAIL_LENGTH + \"s: %s\\n\", key, value)));\n\n\t\tmessageArea.setText(detailsTextBuilder + \"\\n\" + stackTrace);\n\n\t\tJPanel buttonPanel = new JPanel();\n\t\tJButton exitButton = new JButton(\"Terminate Jadx\");\n\t\texitButton.addActionListener(event -> System.exit(1));\n\t\tbuttonPanel.add(exitButton);\n\t\tJButton closeButton = new JButton(\"Go back to Jadx\");\n\t\tcloseButton.addActionListener(event -> setVisible(false));\n\t\tbuttonPanel.add(closeButton);\n\t\tJScrollPane messageAreaScroller = new JScrollPane(messageArea);\n\t\tmessageAreaScroller.setMinimumSize(new Dimension(600, 400));\n\t\tmessageAreaScroller.setPreferredSize(new Dimension(600, 400));\n\n\t\tthis.add(titlePanel, BorderLayout.NORTH);\n\t\tthis.add(messageAreaScroller, BorderLayout.CENTER);\n\t\tthis.add(buttonPanel, BorderLayout.SOUTH);\n\t\tthis.pack();\n\n\t\tjavax.swing.SwingUtilities.invokeLater(() -> messageAreaScroller.getVerticalScrollBar().setValue(0));\n\n\t\tfinal Toolkit toolkit = Toolkit.getDefaultToolkit();\n\t\tfinal Dimension screenSize = toolkit.getScreenSize();\n\t\tfinal int x = (screenSize.width - getWidth()) / 2;\n\t\tfinal int y = (screenSize.height - getHeight()) / 2;\n\t\tsetLocation(x, y);\n\n\t\tgetRootPane().registerKeyboardAction(event -> setVisible(false),\n\t\t\t\tKeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),\n\t\t\t\tJComponent.WHEN_IN_FOCUSED_WINDOW);\n\n\t\tthis.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);\n\t\tthis.setVisible(true);\n\t}\n\n\tpublic static void throwTestException() {\n\t\ttry {\n\t\t\tthrow new RuntimeException(\"Inner exception message\");\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Outer exception message\", e);\n\t\t}\n\t}\n\n\tpublic static void showTestExceptionDialog() {\n\t\ttry {\n\t\t\tthrowTestException();\n\t\t} catch (Exception e) {\n\t\t\tnew ExceptionDialog(null, new ExceptionData(e, JadxExceptionHandler.MAIN_PROJECT_STRING));\n\t\t}\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tJadxConfigAdapter<JadxSettingsData> configAdapter = JadxSettings.buildConfigAdapter();\n\t\tconfigAdapter.useConfigRef(\"\");\n\t\tJadxSettingsData settingsData = configAdapter.load();\n\t\tif (settingsData != null) {\n\t\t\tJadxSettings settings = new JadxSettings(configAdapter);\n\t\t\tsettings.loadSettingsData(settingsData);\n\t\t\tLafManager.init(settings);\n\t\t}\n\t\tshowTestExceptionDialog();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/report/JadxExceptionHandler.java",
    "content": "package jadx.gui.report;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.ui.MainWindow;\nimport jadx.plugins.tools.JadxPluginsTools;\nimport jadx.plugins.tools.data.JadxPluginMetadata;\n\nimport static jadx.plugins.tools.JadxExternalPluginsLoader.JADX_PLUGIN_CLASSLOADER_PREFIX;\n\npublic class JadxExceptionHandler implements Thread.UncaughtExceptionHandler {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxExceptionHandler.class);\n\n\tpublic static final String MAIN_PROJECT_STRING = \"skylot/jadx\";\n\n\tpublic static void register(MainWindow mainWindow) {\n\t\tThread.setDefaultUncaughtExceptionHandler(new JadxExceptionHandler(mainWindow));\n\t}\n\n\tprivate final MainWindow mainWindow;\n\n\tprivate JadxExceptionHandler(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t}\n\n\t@Override\n\tpublic void uncaughtException(Thread thread, Throwable ex) {\n\t\tLOG.error(\"Exception was thrown\", ex);\n\t\tnew ExceptionDialog(mainWindow, buildExceptionData(ex));\n\t}\n\n\tprivate ExceptionData buildExceptionData(Throwable ex) {\n\t\tfor (StackTraceElement stackTraceElement : ex.getStackTrace()) {\n\t\t\tString classLoaderName = stackTraceElement.getClassLoaderName();\n\t\t\tif (classLoaderName != null && classLoaderName.startsWith(JADX_PLUGIN_CLASSLOADER_PREFIX)) {\n\t\t\t\t// plugin exception\n\t\t\t\tString jarName = classLoaderName.substring(JADX_PLUGIN_CLASSLOADER_PREFIX.length());\n\t\t\t\tString pluginProject = resolvePluginByJarName(jarName);\n\t\t\t\tLOG.debug(\"Report exception in plugin: {}\", pluginProject);\n\t\t\t\treturn new ExceptionData(ex, pluginProject);\n\t\t\t}\n\t\t}\n\t\treturn new ExceptionData(ex, MAIN_PROJECT_STRING);\n\t}\n\n\tprivate String resolvePluginByJarName(String jarName) {\n\t\tfor (JadxPluginMetadata jadxPluginMetadata : JadxPluginsTools.getInstance().getInstalled()) {\n\t\t\tif (jadxPluginMetadata.getJar().equals(jarName)) {\n\t\t\t\tString githubProject = getGithubProject(jadxPluginMetadata.getLocationId());\n\t\t\t\treturn githubProject != null ? githubProject : \"\";\n\t\t\t}\n\t\t}\n\t\treturn \"\";\n\t}\n\n\tprivate static @Nullable String getGithubProject(String locationId) {\n\t\tif (locationId.startsWith(\"github:\")) {\n\t\t\treturn locationId.substring(\"github:\".length()).replace(':', '/');\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/search/ISearchMethod.java",
    "content": "package jadx.gui.search;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.apache.commons.lang3.StringUtils;\n\npublic interface ISearchMethod {\n\tint find(String input, String subStr, int start);\n\n\tstatic ISearchMethod build(SearchSettings searchSettings) {\n\t\tif (searchSettings.isUseRegex()) {\n\t\t\tPattern pattern = searchSettings.getPattern();\n\t\t\treturn (input, subStr, start) -> {\n\t\t\t\tMatcher matcher = pattern.matcher(input);\n\t\t\t\tif (matcher.find(start)) {\n\t\t\t\t\treturn matcher.start();\n\t\t\t\t}\n\t\t\t\treturn -1;\n\t\t\t};\n\t\t}\n\t\tif (searchSettings.isIgnoreCase()) {\n\t\t\treturn StringUtils::indexOfIgnoreCase;\n\t\t}\n\t\treturn String::indexOf;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/search/ISearchProvider.java",
    "content": "package jadx.gui.search;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.gui.jobs.Cancelable;\nimport jadx.gui.jobs.ITaskProgress;\nimport jadx.gui.treemodel.JNode;\n\npublic interface ISearchProvider extends ITaskProgress {\n\n\t/**\n\t * Return next result or null if search complete\n\t */\n\t@Nullable\n\tJNode next(Cancelable cancelable);\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/search/SearchJob.java",
    "content": "package jadx.gui.search;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.treemodel.JNode;\n\npublic class SearchJob implements Runnable {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SearchJob.class);\n\n\tprivate final SearchTask searchTask;\n\tprivate final ISearchProvider provider;\n\n\tpublic SearchJob(SearchTask task, ISearchProvider provider) {\n\t\tthis.searchTask = task;\n\t\tthis.provider = provider;\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\twhile (true) {\n\t\t\ttry {\n\t\t\t\tJNode result = provider.next(searchTask);\n\t\t\t\tif (result == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (searchTask.addResult(result)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.warn(\"Search error, provider: {}\", provider.getClass().getSimpleName(), e);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic ISearchProvider getProvider() {\n\t\treturn provider;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/search/SearchSettings.java",
    "content": "package jadx.gui.search;\n\nimport java.util.regex.Pattern;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.JadxDecompiler;\nimport jadx.api.JavaClass;\nimport jadx.api.JavaPackage;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.utils.exceptions.InvalidDataException;\nimport jadx.gui.search.providers.ResourceFilter;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JResource;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\n\npublic class SearchSettings {\n\tprivate final String searchString;\n\tprivate boolean useRegex;\n\tprivate boolean ignoreCase;\n\tprivate String searchPkgStr;\n\tprivate String resFilterStr;\n\tprivate int resSizeLimit; // in MB\n\n\tprivate JClass activeCls;\n\tprivate JResource activeResource;\n\tprivate Pattern regexPattern;\n\tprivate ISearchMethod searchMethod;\n\tprivate JavaPackage searchPackage;\n\tprivate ResourceFilter resourceFilter;\n\n\tpublic SearchSettings(String searchString) {\n\t\tthis.searchString = searchString;\n\t}\n\n\tpublic @Nullable String prepare(MainWindow mainWindow) {\n\t\tif (useRegex) {\n\t\t\ttry {\n\t\t\t\tint flags = ignoreCase ? Pattern.CASE_INSENSITIVE : 0;\n\t\t\t\tthis.regexPattern = Pattern.compile(searchString, flags);\n\t\t\t} catch (Exception e) {\n\t\t\t\treturn \"Invalid Regex: \" + e.getMessage();\n\t\t\t}\n\t\t}\n\t\tif (!searchPkgStr.isBlank()) {\n\t\t\tJadxDecompiler decompiler = mainWindow.getWrapper().getDecompiler();\n\t\t\tPackageNode pkg = decompiler.getRoot().resolvePackage(searchPkgStr);\n\t\t\tif (pkg == null) {\n\t\t\t\treturn NLS.str(\"search_dialog.package_not_found\");\n\t\t\t}\n\t\t\tsearchPackage = pkg.getJavaNode();\n\t\t}\n\t\tsearchMethod = ISearchMethod.build(this);\n\t\ttry {\n\t\t\tresourceFilter = ResourceFilter.parse(resFilterStr);\n\t\t} catch (InvalidDataException e) {\n\t\t\treturn \"Invalid resource file filter: \" + e.getMessage();\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic boolean isMatch(String searchArea) {\n\t\treturn searchMethod.find(searchArea, this.searchString, 0) != -1;\n\t}\n\n\tpublic boolean isUseRegex() {\n\t\treturn this.useRegex;\n\t}\n\n\tpublic void setUseRegex(boolean useRegex) {\n\t\tthis.useRegex = useRegex;\n\t}\n\n\tpublic boolean isIgnoreCase() {\n\t\treturn this.ignoreCase;\n\t}\n\n\tpublic void setIgnoreCase(boolean ignoreCase) {\n\t\tthis.ignoreCase = ignoreCase;\n\t}\n\n\tpublic JavaPackage getSearchPackage() {\n\t\treturn this.searchPackage;\n\t}\n\n\tpublic boolean isInSearchPkg(JavaClass cls) {\n\t\treturn cls.getJavaPackage().isDescendantOf(searchPackage);\n\t}\n\n\tpublic void setSearchPkgStr(String searchPkgStr) {\n\t\tthis.searchPkgStr = searchPkgStr;\n\t}\n\n\tpublic String getSearchString() {\n\t\treturn this.searchString;\n\t}\n\n\tpublic Pattern getPattern() {\n\t\treturn this.regexPattern;\n\t}\n\n\tpublic JClass getActiveCls() {\n\t\treturn activeCls;\n\t}\n\n\tpublic void setActiveCls(JClass activeCls) {\n\t\tthis.activeCls = activeCls;\n\t}\n\n\tpublic JResource getActiveResource() {\n\t\treturn activeResource;\n\t}\n\n\tpublic void setActiveResource(JResource activeResource) {\n\t\tthis.activeResource = activeResource;\n\t}\n\n\tpublic ISearchMethod getSearchMethod() {\n\t\treturn searchMethod;\n\t}\n\n\tpublic void setResFilterStr(String resFilterStr) {\n\t\tthis.resFilterStr = resFilterStr;\n\t}\n\n\tpublic ResourceFilter getResourceFilter() {\n\t\treturn resourceFilter;\n\t}\n\n\tpublic int getResSizeLimit() {\n\t\treturn resSizeLimit;\n\t}\n\n\tpublic void setResSizeLimit(int resSizeLimit) {\n\t\tthis.resSizeLimit = resSizeLimit;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/search/SearchTask.java",
    "content": "package jadx.gui.search;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.BiConsumer;\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.utils.tasks.ITaskExecutor;\nimport jadx.core.utils.tasks.TaskExecutor;\nimport jadx.gui.jobs.BackgroundExecutor;\nimport jadx.gui.jobs.CancelableBackgroundTask;\nimport jadx.gui.jobs.ITaskInfo;\nimport jadx.gui.jobs.ITaskProgress;\nimport jadx.gui.jobs.TaskProgress;\nimport jadx.gui.jobs.TaskStatus;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\n\npublic class SearchTask extends CancelableBackgroundTask {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SearchTask.class);\n\n\tprivate final BackgroundExecutor backgroundExecutor;\n\tprivate final Consumer<JNode> resultsListener;\n\tprivate final BiConsumer<ITaskInfo, Boolean> onFinish;\n\tprivate final List<SearchJob> jobs = new ArrayList<>();\n\tprivate final TaskProgress taskProgress = new TaskProgress();\n\n\tprivate final AtomicInteger resultsCount = new AtomicInteger(0);\n\tprivate int resultsLimit;\n\tprivate Future<TaskStatus> future;\n\n\tprivate Consumer<ITaskProgress> progressListener;\n\n\tpublic SearchTask(MainWindow mainWindow, Consumer<JNode> results, BiConsumer<ITaskInfo, Boolean> onFinish) {\n\t\tthis.backgroundExecutor = mainWindow.getBackgroundExecutor();\n\t\tthis.resultsListener = results;\n\t\tthis.onFinish = onFinish;\n\t}\n\n\tpublic void addProviderJob(ISearchProvider provider) {\n\t\tjobs.add(new SearchJob(this, provider));\n\t}\n\n\tpublic void setResultsLimit(int limit) {\n\t\tthis.resultsLimit = limit;\n\t}\n\n\tpublic synchronized void fetchResults() {\n\t\tif (future != null) {\n\t\t\tthrow new IllegalStateException(\"Previous task not yet finished\");\n\t\t}\n\t\tresetCancel();\n\t\tresultsCount.set(0);\n\t\ttaskProgress.updateTotal(jobs.stream().mapToInt(s -> s.getProvider().total()).sum());\n\t\tfuture = backgroundExecutor.executeWithFuture(this);\n\t}\n\n\tpublic synchronized boolean addResult(JNode resultNode) {\n\t\tif (isCanceled()) {\n\t\t\t// ignore new results after cancel\n\t\t\treturn true;\n\t\t}\n\t\tthis.resultsListener.accept(resultNode);\n\t\tif (resultsLimit != 0 && resultsCount.incrementAndGet() >= resultsLimit) {\n\t\t\tcancel();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic synchronized void waitTask() {\n\t\tif (future == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tfuture.get(200, TimeUnit.MILLISECONDS);\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Search task wait error\", e);\n\t\t\tfuture.cancel(true);\n\t\t} finally {\n\t\t\tfuture = null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn NLS.str(\"search_dialog.tip_searching\");\n\t}\n\n\t@Override\n\tpublic ITaskExecutor scheduleTasks() {\n\t\tTaskExecutor executor = new TaskExecutor();\n\t\texecutor.addParallelTasks(jobs);\n\t\treturn executor;\n\t}\n\n\t@Override\n\tpublic void onFinish(ITaskInfo task) {\n\t\tboolean complete = !isCanceled()\n\t\t\t\t&& task.getStatus() == TaskStatus.COMPLETE\n\t\t\t\t&& task.getJobsComplete() == task.getJobsCount();\n\t\tthis.onFinish.accept(task, complete);\n\t}\n\n\t@Override\n\tpublic boolean checkMemoryUsage() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic @NotNull ITaskProgress getTaskProgress() {\n\t\ttaskProgress.updateProgress(jobs.stream().mapToInt(s -> s.getProvider().progress()).sum());\n\t\treturn taskProgress;\n\t}\n\n\tpublic void setProgressListener(Consumer<ITaskProgress> progressListener) {\n\t\tthis.progressListener = progressListener;\n\t}\n\n\t@Override\n\tpublic @Nullable Consumer<ITaskProgress> getProgressListener() {\n\t\treturn this.progressListener;\n\t}\n\n\t@Override\n\tpublic int getCancelTimeoutMS() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int getShutdownTimeoutMS() {\n\t\treturn 10;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/search/providers/BaseSearchProvider.java",
    "content": "package jadx.gui.search.providers;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport jadx.api.JadxDecompiler;\nimport jadx.api.JavaClass;\nimport jadx.api.JavaNode;\nimport jadx.core.dex.nodes.ICodeNode;\nimport jadx.gui.search.ISearchMethod;\nimport jadx.gui.search.ISearchProvider;\nimport jadx.gui.search.SearchSettings;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.JNodeCache;\n\npublic abstract class BaseSearchProvider implements ISearchProvider {\n\n\tprivate final JNodeCache nodeCache;\n\tprivate final JadxDecompiler decompiler;\n\tprotected final ISearchMethod searchMth;\n\tprotected final String searchStr;\n\tprotected final List<JavaClass> classes;\n\tprotected final SearchSettings searchSettings;\n\n\tpublic BaseSearchProvider(MainWindow mw, SearchSettings searchSettings, List<JavaClass> classes) {\n\t\tthis.nodeCache = mw.getCacheObject().getNodeCache();\n\t\tthis.decompiler = mw.getWrapper().getDecompiler();\n\t\tthis.searchMth = searchSettings.getSearchMethod();\n\t\tthis.searchStr = searchSettings.getSearchString();\n\t\tif (searchSettings.getSearchPackage() != null) {\n\t\t\tthis.classes = classes\n\t\t\t\t\t.stream()\n\t\t\t\t\t.filter(c -> c.getJavaPackage().isDescendantOf(searchSettings.getSearchPackage()))\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t} else {\n\t\t\tthis.classes = classes;\n\t\t}\n\t\tthis.searchSettings = searchSettings;\n\t}\n\n\tprotected boolean isMatch(String str) {\n\t\treturn searchMth.find(str, searchStr, 0) != -1;\n\t}\n\n\tprotected JNode convert(JavaNode node) {\n\t\treturn nodeCache.makeFrom(node);\n\t}\n\n\tprotected JClass convert(JavaClass cls) {\n\t\treturn nodeCache.makeFrom(cls);\n\t}\n\n\tprotected JNode convert(ICodeNode codeNode) {\n\t\tJavaNode node = Objects.requireNonNull(decompiler.getJavaNodeByRef(codeNode));\n\t\treturn Objects.requireNonNull(nodeCache.makeFrom(node));\n\t}\n\n\t@Override\n\tpublic int total() {\n\t\treturn classes.size();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/search/providers/ClassSearchProvider.java",
    "content": "package jadx.gui.search.providers;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.JavaClass;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.gui.jobs.Cancelable;\nimport jadx.gui.search.SearchSettings;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\n\npublic final class ClassSearchProvider extends BaseSearchProvider {\n\n\tprivate int clsNum = 0;\n\n\tpublic ClassSearchProvider(MainWindow mw, SearchSettings searchSettings, List<JavaClass> classes) {\n\t\tsuper(mw, searchSettings, classes);\n\t}\n\n\t@Override\n\tpublic @Nullable JNode next(Cancelable cancelable) {\n\t\twhile (true) {\n\t\t\tif (cancelable.isCanceled() || clsNum >= classes.size()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tJavaClass curCls = classes.get(clsNum++);\n\t\t\tif (checkCls(curCls)) {\n\t\t\t\treturn convert(curCls);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate boolean checkCls(JavaClass cls) {\n\t\tClassInfo clsInfo = cls.getClassNode().getClassInfo();\n\t\treturn isMatch(clsInfo.getShortName())\n\t\t\t\t|| isMatch(clsInfo.getFullName())\n\t\t\t\t|| isMatch(clsInfo.getAliasFullName())\n\t\t\t\t|| isMatch(clsInfo.getRawName());\n\t}\n\n\t@Override\n\tpublic int progress() {\n\t\treturn clsNum;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/search/providers/CodeSearchProvider.java",
    "content": "package jadx.gui.search.providers;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeCache;\nimport jadx.api.JavaClass;\nimport jadx.api.JavaNode;\nimport jadx.api.metadata.ICodeMetadata;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.utils.CodeUtils;\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.jobs.Cancelable;\nimport jadx.gui.search.SearchSettings;\nimport jadx.gui.treemodel.CodeNode;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\n\nimport static jadx.core.utils.Utils.getOrElse;\n\npublic final class CodeSearchProvider extends BaseSearchProvider {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CodeSearchProvider.class);\n\n\tprivate final ICodeCache codeCache;\n\tprivate final JadxWrapper wrapper;\n\tprivate final @Nullable Set<JavaClass> includedClasses;\n\n\tprivate @Nullable String code;\n\tprivate int clsNum = 0;\n\tprivate int pos = 0;\n\n\tpublic CodeSearchProvider(MainWindow mw, SearchSettings searchSettings,\n\t\t\tList<JavaClass> classes, @Nullable Set<JavaClass> includedClasses) {\n\t\tsuper(mw, searchSettings, classes);\n\t\tthis.codeCache = mw.getWrapper().getArgs().getCodeCache();\n\t\tthis.wrapper = mw.getWrapper();\n\t\tthis.includedClasses = includedClasses;\n\t}\n\n\t@Override\n\tpublic @Nullable JNode next(Cancelable cancelable) {\n\t\tSet<JavaClass> inclCls = includedClasses;\n\t\twhile (true) {\n\t\t\tif (cancelable.isCanceled() || clsNum >= classes.size()) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tJavaClass cls = classes.get(clsNum);\n\t\t\tif (inclCls == null || inclCls.contains(cls)) {\n\t\t\t\tString clsCode = code;\n\t\t\t\tif (clsCode == null && !cls.isInner() && !cls.isNoCode()) {\n\t\t\t\t\tclsCode = getClassCode(cls, codeCache);\n\t\t\t\t}\n\t\t\t\tif (clsCode != null) {\n\t\t\t\t\tJNode newResult = searchNext(cls, clsCode);\n\t\t\t\t\tif (newResult != null) {\n\t\t\t\t\t\tcode = clsCode;\n\t\t\t\t\t\treturn newResult;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// force decompilation for not included classes\n\t\t\t\tcls.decompile();\n\t\t\t}\n\t\t\tclsNum++;\n\t\t\tpos = 0;\n\t\t\tcode = null;\n\t\t}\n\t}\n\n\tprivate @Nullable JNode searchNext(JavaClass javaClass, String clsCode) {\n\t\tint newPos = searchMth.find(clsCode, searchStr, pos);\n\t\tif (newPos == -1) {\n\t\t\treturn null;\n\t\t}\n\t\tint lineStart = 1 + CodeUtils.getNewLinePosBefore(clsCode, newPos);\n\t\tint lineEnd = CodeUtils.getNewLinePosAfter(clsCode, newPos);\n\t\tint end = lineEnd == -1 ? clsCode.length() : lineEnd;\n\t\tString line = clsCode.substring(lineStart, end);\n\t\tthis.pos = end;\n\t\tJClass rootCls = convert(javaClass);\n\t\tJNode enclosingNode = getOrElse(getEnclosingNode(javaClass, end), rootCls);\n\t\treturn new CodeNode(rootCls, enclosingNode, line.trim(), newPos);\n\t}\n\n\tprivate @Nullable JNode getEnclosingNode(JavaClass javaCls, int pos) {\n\t\ttry {\n\t\t\tICodeMetadata metadata = javaCls.getCodeInfo().getCodeMetadata();\n\t\t\tICodeNodeRef nodeRef = metadata.getNodeAt(pos);\n\t\t\tJavaNode encNode = wrapper.getJavaNodeByRef(nodeRef);\n\t\t\tif (encNode != null) {\n\t\t\t\treturn convert(encNode);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.debug(\"Failed to resolve enclosing node\", e);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String getClassCode(JavaClass javaClass, ICodeCache codeCache) {\n\t\ttry {\n\t\t\t// quick check for if code already in cache\n\t\t\tString code = codeCache.getCode(javaClass.getRawName());\n\t\t\tif (code != null) {\n\t\t\t\treturn code;\n\t\t\t}\n\t\t\t// start decompilation\n\t\t\treturn javaClass.getCode();\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to get class code: {}\", javaClass, e);\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic int progress() {\n\t\treturn clsNum;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/search/providers/CommentSearchProvider.java",
    "content": "package jadx.gui.search.providers;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.swing.Icon;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.JavaClass;\nimport jadx.api.JavaField;\nimport jadx.api.JavaMethod;\nimport jadx.api.JavaNode;\nimport jadx.api.data.ICodeComment;\nimport jadx.api.data.IJavaCodeRef;\nimport jadx.api.data.IJavaNodeRef;\nimport jadx.api.metadata.annotations.InsnCodeOffset;\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.jobs.Cancelable;\nimport jadx.gui.search.ISearchProvider;\nimport jadx.gui.search.SearchSettings;\nimport jadx.gui.settings.JadxProject;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JMethod;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.CacheObject;\nimport jadx.gui.utils.JNodeCache;\nimport jadx.gui.utils.JumpPosition;\n\npublic class CommentSearchProvider implements ISearchProvider {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CommentSearchProvider.class);\n\tprivate final JadxWrapper wrapper;\n\tprivate final CacheObject cacheObject;\n\tprivate final JadxProject project;\n\tprivate final SearchSettings searchSettings;\n\tprivate final Set<JavaClass> searchClsSet;\n\n\tprivate int progress = 0;\n\n\tpublic CommentSearchProvider(MainWindow mw, SearchSettings searchSettings, List<JavaClass> searchClasses) {\n\t\tthis.wrapper = mw.getWrapper();\n\t\tthis.cacheObject = mw.getCacheObject();\n\t\tthis.project = mw.getProject();\n\t\tthis.searchSettings = searchSettings;\n\t\tthis.searchClsSet = new HashSet<>(searchClasses);\n\t}\n\n\t@Override\n\tpublic @Nullable JNode next(Cancelable cancelable) {\n\t\twhile (!cancelable.isCanceled()) {\n\t\t\tList<ICodeComment> comments = project.getCodeData().getComments();\n\t\t\tif (progress >= comments.size()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tICodeComment comment = comments.get(progress++);\n\t\t\tJNode result = isMatch(searchSettings, comment);\n\t\t\tif (result != null) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Nullable\n\tprivate JNode isMatch(SearchSettings searchSettings, ICodeComment comment) {\n\t\tboolean all = searchSettings.getSearchString().isEmpty();\n\t\tif (all || searchSettings.isMatch(comment.getComment())) {\n\t\t\tJNode refNode = getRefNode(comment);\n\t\t\tif (refNode == null) {\n\t\t\t\tLOG.warn(\"Failed to get ref node for comment: {}\", comment);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (searchClsSet.contains(refNode.getRootClass().getCls())) {\n\t\t\t\treturn getCommentNode(comment, refNode);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate @NotNull RefCommentNode getCommentNode(ICodeComment comment, JNode refNode) {\n\t\tIJavaNodeRef nodeRef = comment.getNodeRef();\n\t\tif (nodeRef.getType() == IJavaNodeRef.RefType.METHOD && comment.getCodeRef() != null) {\n\t\t\treturn new CodeCommentNode((JMethod) refNode, comment);\n\t\t}\n\t\treturn new RefCommentNode(refNode, comment.getComment());\n\t}\n\n\t@Nullable\n\tprivate JNode getRefNode(ICodeComment comment) {\n\t\tIJavaNodeRef nodeRef = comment.getNodeRef();\n\t\tJavaClass javaClass = wrapper.searchJavaClassByOrigClassName(nodeRef.getDeclaringClass());\n\t\tif (javaClass == null) {\n\t\t\treturn null;\n\t\t}\n\t\tJNodeCache nodeCache = cacheObject.getNodeCache();\n\t\tswitch (nodeRef.getType()) {\n\t\t\tcase CLASS:\n\t\t\t\treturn nodeCache.makeFrom(javaClass);\n\n\t\t\tcase FIELD:\n\t\t\t\tfor (JavaField field : javaClass.getFields()) {\n\t\t\t\t\tif (field.getFieldNode().getFieldInfo().getShortId().equals(nodeRef.getShortId())) {\n\t\t\t\t\t\treturn nodeCache.makeFrom(field);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase METHOD:\n\t\t\t\tfor (JavaMethod mth : javaClass.getMethods()) {\n\t\t\t\t\tif (mth.getMethodNode().getMethodInfo().getShortId().equals(nodeRef.getShortId())) {\n\t\t\t\t\t\treturn nodeCache.makeFrom(mth);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static final class CodeCommentNode extends RefCommentNode {\n\t\tprivate static final long serialVersionUID = 6208192811789176886L;\n\n\t\tprivate final int offset;\n\t\tprivate JumpPosition pos;\n\n\t\tpublic CodeCommentNode(JMethod node, ICodeComment comment) {\n\t\t\tsuper(node, comment.getComment());\n\t\t\tIJavaCodeRef codeRef = Objects.requireNonNull(comment.getCodeRef(), \"Null comment code ref\");\n\t\t\tthis.offset = codeRef.getIndex();\n\t\t}\n\n\t\t@Override\n\t\tpublic int getPos() {\n\t\t\treturn getCachedPos().getPos();\n\t\t}\n\n\t\tprivate synchronized JumpPosition getCachedPos() {\n\t\t\tif (pos == null) {\n\t\t\t\tpos = getJumpPos();\n\t\t\t}\n\t\t\treturn pos;\n\t\t}\n\n\t\t/**\n\t\t * Lazy decompilation to get comment location if requested\n\t\t */\n\t\tprivate JumpPosition getJumpPos() {\n\t\t\tJavaMethod javaMethod = ((JMethod) node).getJavaMethod();\n\t\t\tICodeInfo codeInfo = javaMethod.getTopParentClass().getCodeInfo();\n\t\t\tint methodDefPos = javaMethod.getDefPos();\n\t\t\tJumpPosition jump = codeInfo.getCodeMetadata().searchDown(methodDefPos,\n\t\t\t\t\t(pos, ann) -> ann instanceof InsnCodeOffset && ((InsnCodeOffset) ann).getOffset() == offset\n\t\t\t\t\t\t\t? new JumpPosition(node, pos)\n\t\t\t\t\t\t\t: null);\n\t\t\tif (jump != null) {\n\t\t\t\treturn jump;\n\t\t\t}\n\t\t\treturn new JumpPosition(node);\n\t\t}\n\t}\n\n\tprivate static class RefCommentNode extends JNode {\n\t\tprivate static final long serialVersionUID = 3887992236082515752L;\n\n\t\tprotected final JNode node;\n\t\tprotected final String comment;\n\n\t\tpublic RefCommentNode(JNode node, String comment) {\n\t\t\tthis.node = node;\n\t\t\tthis.comment = comment;\n\t\t}\n\n\t\t@Override\n\t\tpublic JClass getRootClass() {\n\t\t\treturn node.getRootClass();\n\t\t}\n\n\t\t@Override\n\t\tpublic JavaNode getJavaNode() {\n\t\t\treturn node.getJavaNode();\n\t\t}\n\n\t\t@Override\n\t\tpublic JClass getJParent() {\n\t\t\treturn node.getJParent();\n\t\t}\n\n\t\t@Override\n\t\tpublic Icon getIcon() {\n\t\t\treturn node.getIcon();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getSyntaxName() {\n\t\t\treturn SyntaxConstants.SYNTAX_STYLE_NONE; // comment is always plain text\n\t\t}\n\n\t\t@Override\n\t\tpublic String makeString() {\n\t\t\treturn node.makeString();\n\t\t}\n\n\t\t@Override\n\t\tpublic String makeLongString() {\n\t\t\treturn node.makeLongString();\n\t\t}\n\n\t\t@Override\n\t\tpublic String makeStringHtml() {\n\t\t\treturn node.makeStringHtml();\n\t\t}\n\n\t\t@Override\n\t\tpublic String makeLongStringHtml() {\n\t\t\treturn node.makeLongStringHtml();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean disableHtml() {\n\t\t\treturn node.disableHtml();\n\t\t}\n\n\t\t@Override\n\t\tpublic int getPos() {\n\t\t\treturn node.getPos();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getTooltip() {\n\t\t\treturn node.getTooltip();\n\t\t}\n\n\t\t@Override\n\t\tpublic String makeDescString() {\n\t\t\treturn comment;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean hasDescString() {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int progress() {\n\t\treturn progress;\n\t}\n\n\t@Override\n\tpublic int total() {\n\t\treturn project.getCodeData().getComments().size();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/search/providers/FieldSearchProvider.java",
    "content": "package jadx.gui.search.providers;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.JavaClass;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.gui.jobs.Cancelable;\nimport jadx.gui.search.SearchSettings;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\n\npublic final class FieldSearchProvider extends BaseSearchProvider {\n\n\tprivate int clsNum = 0;\n\tprivate int fldNum = 0;\n\n\tpublic FieldSearchProvider(MainWindow mw, SearchSettings searchSettings, List<JavaClass> classes) {\n\t\tsuper(mw, searchSettings, classes);\n\t}\n\n\t@Override\n\tpublic @Nullable JNode next(Cancelable cancelable) {\n\t\twhile (true) {\n\t\t\tif (cancelable.isCanceled()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tJavaClass cls = classes.get(clsNum);\n\t\t\tList<FieldNode> fields = cls.getClassNode().getFields();\n\t\t\tif (fldNum < fields.size()) {\n\t\t\t\tFieldNode fld = fields.get(fldNum++);\n\t\t\t\tif (checkField(fld.getFieldInfo())) {\n\t\t\t\t\treturn convert(fld);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tclsNum++;\n\t\t\t\tfldNum = 0;\n\t\t\t\tif (clsNum >= classes.size()) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate boolean checkField(FieldInfo fieldInfo) {\n\t\treturn isMatch(fieldInfo.getName())\n\t\t\t\t|| isMatch(fieldInfo.getAlias())\n\t\t\t\t|| isMatch(fieldInfo.getFullId());\n\t}\n\n\t@Override\n\tpublic int progress() {\n\t\treturn clsNum;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/search/providers/MergedSearchProvider.java",
    "content": "package jadx.gui.search.providers;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.gui.jobs.Cancelable;\nimport jadx.gui.search.ISearchProvider;\nimport jadx.gui.treemodel.JNode;\n\n/**\n * Search provider for sequential execution of nested search providers\n */\npublic class MergedSearchProvider implements ISearchProvider {\n\n\tprivate final List<ISearchProvider> list = new ArrayList<>();\n\tprivate int current;\n\tprivate int total;\n\n\tpublic void add(ISearchProvider provider) {\n\t\tlist.add(provider);\n\t}\n\n\tpublic boolean isEmpty() {\n\t\treturn list.isEmpty();\n\t}\n\n\tpublic void prepare() {\n\t\tcurrent = list.isEmpty() ? -1 : 0;\n\t\ttotal = list.stream().mapToInt(ISearchProvider::total).sum();\n\t}\n\n\t@Override\n\tpublic @Nullable JNode next(Cancelable cancelable) {\n\t\tif (current == -1) {\n\t\t\treturn null;\n\t\t}\n\t\twhile (true) {\n\t\t\tJNode next = list.get(current).next(cancelable);\n\t\t\tif (next != null) {\n\t\t\t\treturn next;\n\t\t\t}\n\t\t\tcurrent++;\n\t\t\tif (current >= list.size() || cancelable.isCanceled()) {\n\t\t\t\t// search complete\n\t\t\t\tcurrent = -1;\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic int progress() {\n\t\treturn list.stream().mapToInt(ISearchProvider::progress).sum();\n\t}\n\n\t@Override\n\tpublic int total() {\n\t\treturn total;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/search/providers/MethodSearchProvider.java",
    "content": "package jadx.gui.search.providers;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.JavaClass;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.gui.jobs.Cancelable;\nimport jadx.gui.search.SearchSettings;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\n\npublic final class MethodSearchProvider extends BaseSearchProvider {\n\n\tprivate int clsNum = 0;\n\tprivate int mthNum = 0;\n\n\tpublic MethodSearchProvider(MainWindow mw, SearchSettings searchSettings, List<JavaClass> classes) {\n\t\tsuper(mw, searchSettings, classes);\n\t}\n\n\t@Override\n\tpublic @Nullable JNode next(Cancelable cancelable) {\n\t\tif (classes.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\twhile (true) {\n\t\t\tif (cancelable.isCanceled()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tJavaClass cls = classes.get(clsNum);\n\t\t\tList<MethodNode> methods = cls.getClassNode().getMethods();\n\t\t\tif (mthNum < methods.size()) {\n\t\t\t\tMethodNode mth = methods.get(mthNum++);\n\t\t\t\tif (checkMth(mth.getMethodInfo())) {\n\t\t\t\t\treturn convert(mth);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tclsNum++;\n\t\t\t\tmthNum = 0;\n\t\t\t\tif (clsNum >= classes.size()) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate boolean checkMth(MethodInfo mthInfo) {\n\t\treturn isMatch(mthInfo.getShortId())\n\t\t\t\t|| isMatch(mthInfo.getAlias())\n\t\t\t\t|| isMatch(mthInfo.getFullId())\n\t\t\t\t|| isMatch(mthInfo.getAliasFullName());\n\t}\n\n\t@Override\n\tpublic int progress() {\n\t\treturn clsNum;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/search/providers/ResourceFilter.java",
    "content": "package jadx.gui.search.providers;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport jadx.api.resources.ResourceContentType;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.InvalidDataException;\n\nimport static jadx.api.resources.ResourceContentType.CONTENT_BINARY;\nimport static jadx.api.resources.ResourceContentType.CONTENT_TEXT;\n\npublic class ResourceFilter {\n\n\tprivate static final ResourceFilter ANY = new ResourceFilter(Set.of(), Set.of());\n\n\tprivate static final String VAR_TEXT = \"$TEXT\";\n\tprivate static final String VAR_BIN = \"$BIN\";\n\n\tpublic static final String DEFAULT_STR = VAR_TEXT;\n\n\tpublic static ResourceFilter parse(String filterStr) {\n\t\tString str = filterStr.trim();\n\t\tif (str.isEmpty() || str.equals(\"*\")) {\n\t\t\treturn ANY;\n\t\t}\n\t\tSet<ResourceContentType> contentTypes = EnumSet.noneOf(ResourceContentType.class);\n\t\tSet<String> extSet = new LinkedHashSet<>();\n\t\tString[] parts = filterStr.split(\"[|, ]\");\n\t\tfor (String part : parts) {\n\t\t\tif (part.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (part.startsWith(\"$\")) {\n\t\t\t\tswitch (part) {\n\t\t\t\t\tcase VAR_TEXT:\n\t\t\t\t\t\tcontentTypes.add(CONTENT_TEXT);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase VAR_BIN:\n\t\t\t\t\t\tcontentTypes.add(CONTENT_BINARY);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow new InvalidDataException(\"Unknown var name: \" + part);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\textSet.add(part);\n\t\t\t}\n\t\t}\n\t\treturn new ResourceFilter(contentTypes, extSet);\n\t}\n\n\tpublic static String format(ResourceFilter filter) {\n\t\tif (filter.isAnyFile()) {\n\t\t\treturn \"*\";\n\t\t}\n\t\tList<String> list = new ArrayList<>();\n\t\tSet<ResourceContentType> types = filter.getContentTypes();\n\t\tif (types.contains(CONTENT_TEXT)) {\n\t\t\tlist.add(VAR_TEXT);\n\t\t}\n\t\tif (types.contains(CONTENT_BINARY)) {\n\t\t\tlist.add(VAR_BIN);\n\t\t}\n\t\tlist.addAll(filter.getExtSet());\n\t\treturn Utils.listToString(list, \"|\");\n\t}\n\n\tpublic static String withContentType(String filterStr, Set<ResourceContentType> contentTypes) {\n\t\tResourceFilter filter = parse(filterStr);\n\t\treturn format(new ResourceFilter(contentTypes, filter.getExtSet()));\n\t}\n\n\tprivate final boolean anyFile;\n\tprivate final Set<ResourceContentType> contentTypes;\n\tprivate final Set<String> extSet;\n\n\tprivate ResourceFilter(Set<ResourceContentType> contentTypes, Set<String> extSet) {\n\t\tthis.anyFile = contentTypes.isEmpty() && extSet.isEmpty();\n\t\tthis.contentTypes = contentTypes.isEmpty() ? Set.of() : contentTypes;\n\t\tthis.extSet = extSet.isEmpty() ? Set.of() : extSet;\n\t}\n\n\tpublic boolean isAnyFile() {\n\t\treturn anyFile;\n\t}\n\n\tpublic Set<ResourceContentType> getContentTypes() {\n\t\treturn contentTypes;\n\t}\n\n\tpublic Set<String> getExtSet() {\n\t\treturn extSet;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn format(this);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/search/providers/ResourceSearchProvider.java",
    "content": "package jadx.gui.search.providers;\n\nimport java.util.ArrayDeque;\nimport java.util.Collections;\nimport java.util.Deque;\nimport java.util.Enumeration;\n\nimport javax.swing.tree.TreeNode;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourceType;\nimport jadx.api.plugins.utils.CommonFileUtils;\nimport jadx.api.resources.ResourceContentType;\nimport jadx.api.utils.CodeUtils;\nimport jadx.gui.jobs.Cancelable;\nimport jadx.gui.search.ISearchProvider;\nimport jadx.gui.search.SearchSettings;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.treemodel.JResSearchNode;\nimport jadx.gui.treemodel.JResource;\nimport jadx.gui.treemodel.JRoot;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.dialog.SearchDialog;\nimport jadx.gui.utils.NLS;\n\npublic class ResourceSearchProvider implements ISearchProvider {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ResourceSearchProvider.class);\n\n\tprivate final SearchSettings searchSettings;\n\tprivate final SearchDialog searchDialog;\n\tprivate final ResourceFilter resourceFilter;\n\tprivate final int sizeLimit;\n\n\t/**\n\t * Resources queue for process. Using UI nodes to reuse loading cache\n\t */\n\tprivate final Deque<JResource> resQueue;\n\tprivate int pos;\n\n\tprivate int loadErrors = 0;\n\tprivate int skipBySize = 0;\n\n\tpublic ResourceSearchProvider(MainWindow mw, SearchSettings searchSettings, SearchDialog searchDialog) {\n\t\tthis.searchSettings = searchSettings;\n\t\tthis.resourceFilter = searchSettings.getResourceFilter();\n\t\tthis.sizeLimit = searchSettings.getResSizeLimit() * 1024 * 1024;\n\t\tthis.searchDialog = searchDialog;\n\t\tJResource activeResource = searchSettings.getActiveResource();\n\t\tif (activeResource != null) {\n\t\t\tthis.resQueue = new ArrayDeque<>(Collections.singleton(activeResource));\n\t\t} else {\n\t\t\tthis.resQueue = initResQueue(mw);\n\t\t}\n\t}\n\n\t@Override\n\tpublic @Nullable JNode next(Cancelable cancelable) {\n\t\twhile (true) {\n\t\t\tif (cancelable.isCanceled()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tJResource resNode = getNextResFile(cancelable);\n\t\t\tif (resNode == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tJNode newResult = search(resNode);\n\t\t\tif (newResult != null) {\n\t\t\t\treturn newResult;\n\t\t\t}\n\t\t\tpos = 0;\n\t\t\tresQueue.removeLast();\n\t\t\taddChildren(resNode);\n\t\t\tif (resQueue.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate JNode search(JResource resNode) {\n\t\tString content;\n\t\ttry {\n\t\t\tcontent = resNode.getCodeInfo().getCodeStr();\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to load resource node content\", e);\n\t\t\treturn null;\n\t\t}\n\t\tString searchString = searchSettings.getSearchString();\n\t\tint newPos = searchSettings.getSearchMethod().find(content, searchString, pos);\n\t\tif (newPos == -1) {\n\t\t\treturn null;\n\t\t}\n\t\tif (resNode.getContentType() == ResourceContentType.CONTENT_TEXT) {\n\t\t\tint lineStart = 1 + CodeUtils.getNewLinePosBefore(content, newPos);\n\t\t\tint lineEnd = CodeUtils.getNewLinePosAfter(content, newPos);\n\t\t\tint end = lineEnd == -1 ? content.length() : lineEnd;\n\t\t\tString line = content.substring(lineStart, end);\n\t\t\tthis.pos = end;\n\t\t\treturn new JResSearchNode(resNode, line.trim(), newPos);\n\t\t} else {\n\t\t\tint start = Math.max(0, newPos - 30);\n\t\t\tint end = Math.min(newPos + 50, content.length());\n\t\t\tString line = content.substring(start, end);\n\t\t\tthis.pos = newPos + searchString.length() + 1;\n\t\t\treturn new JResSearchNode(resNode, line, newPos);\n\t\t}\n\t}\n\n\tprivate @Nullable JResource getNextResFile(Cancelable cancelable) {\n\t\twhile (true) {\n\t\t\tJResource node = resQueue.peekLast();\n\t\t\tif (node == null || cancelable.isCanceled()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (node.getType() == JResource.JResType.FILE) {\n\t\t\t\tif (shouldProcess(node) && loadResNode(node)) {\n\t\t\t\t\treturn node;\n\t\t\t\t}\n\t\t\t\tresQueue.removeLast();\n\t\t\t} else {\n\t\t\t\t// dir\n\t\t\t\tresQueue.removeLast();\n\t\t\t\tloadResNode(node);\n\t\t\t\taddChildren(node);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void updateProgressInfo() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tif (loadErrors != 0) {\n\t\t\tsb.append(\"  \").append(NLS.str(\"search_dialog.resources_load_errors\", loadErrors));\n\t\t}\n\t\tif (skipBySize != 0) {\n\t\t\tsb.append(\"  \").append(NLS.str(\"search_dialog.resources_skip_by_size\", skipBySize));\n\t\t}\n\t\tif (sb.length() != 0) {\n\t\t\tsb.append(\"  \").append(NLS.str(\"search_dialog.resources_check_logs\"));\n\t\t}\n\t\tsearchDialog.updateProgressLabel(sb.toString());\n\t}\n\n\tprivate boolean loadResNode(JResource node) {\n\t\ttry {\n\t\t\tnode.loadNode();\n\t\t\treturn true;\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Error load resource node: {}\", node, e);\n\t\t\tloadErrors++;\n\t\t\tupdateProgressInfo();\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate void addChildren(JResource resNode) {\n\t\tresQueue.addAll(resNode.getSubNodes());\n\t}\n\n\tprivate static Deque<JResource> initResQueue(MainWindow mw) {\n\t\tJRoot jRoot = mw.getTreeRoot();\n\t\tDeque<JResource> deque = new ArrayDeque<>(jRoot.getChildCount());\n\t\tEnumeration<TreeNode> children = jRoot.children();\n\t\twhile (children.hasMoreElements()) {\n\t\t\tTreeNode node = children.nextElement();\n\t\t\tif (node instanceof JResource) {\n\t\t\t\tJResource resNode = (JResource) node;\n\t\t\t\tdeque.add(resNode);\n\t\t\t}\n\t\t}\n\t\treturn deque;\n\t}\n\n\tprivate boolean shouldProcess(JResource resNode) {\n\t\tif (resNode.getResFile().getType() == ResourceType.ARSC) {\n\t\t\t// don't check the size of generated resource table, it will also skip all subfiles\n\t\t\treturn resourceFilter.isAnyFile()\n\t\t\t\t\t|| resourceFilter.getContentTypes().contains(ResourceContentType.CONTENT_TEXT)\n\t\t\t\t\t|| resourceFilter.getExtSet().contains(\"xml\");\n\t\t}\n\t\tif (!isAllowedFileType(resNode)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn isAllowedFileSize(resNode);\n\t}\n\n\tprivate boolean isAllowedFileType(JResource resNode) {\n\t\tResourceFile resFile = resNode.getResFile();\n\t\tif (resourceFilter.isAnyFile()) {\n\t\t\treturn true;\n\t\t}\n\t\tResourceContentType resContentType = resNode.getContentType();\n\t\tif (resourceFilter.getContentTypes().contains(resContentType)) {\n\t\t\treturn true;\n\t\t}\n\t\tString fileExt = CommonFileUtils.getFileExtension(resFile.getOriginalName());\n\t\tif (fileExt != null && resourceFilter.getExtSet().contains(fileExt)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (resContentType == ResourceContentType.CONTENT_UNKNOWN\n\t\t\t\t&& resourceFilter.getContentTypes().contains(ResourceContentType.CONTENT_BINARY)) {\n\t\t\t// treat unknown file type as binary\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate boolean isAllowedFileSize(JResource resNode) {\n\t\tif (sizeLimit <= 0) {\n\t\t\treturn true;\n\t\t}\n\t\ttry {\n\t\t\tint charsCount = resNode.getCodeInfo().getCodeStr().length();\n\t\t\tlong size = charsCount * 8L;\n\t\t\tif (size > sizeLimit) {\n\t\t\t\tLOG.info(\"Resource search skipped because of size limit. Resource '{}' size {} bytes, limit: {}\",\n\t\t\t\t\t\tresNode.getName(), size, sizeLimit);\n\t\t\t\tskipBySize++;\n\t\t\t\tupdateProgressInfo();\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Resource load error: {}\", resNode, e);\n\t\t\tloadErrors++;\n\t\t\tupdateProgressInfo();\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int progress() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int total() {\n\t\treturn 0;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/JadxConfigExcludeExport.java",
    "content": "package jadx.gui.settings;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Annotation to mark fields excluded from config export/copy actions.\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.FIELD)\npublic @interface JadxConfigExcludeExport {\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/JadxGUIArgs.java",
    "content": "package jadx.gui.settings;\n\nimport com.beust.jcommander.Parameter;\n\nimport jadx.cli.JadxCLIArgs;\nimport jadx.cli.config.JadxConfigExclude;\n\npublic class JadxGUIArgs extends JadxCLIArgs {\n\n\t@JadxConfigExclude\n\t@Parameter(\n\t\t\tnames = { \"-sc\", \"--select-class\" },\n\t\t\tdescription = \"GUI: Open the selected class and show the decompiled code\"\n\t)\n\tprivate String cmdSelectClass = null;\n\n\tpublic String getCmdSelectClass() {\n\t\treturn cmdSelectClass;\n\t}\n\n\tpublic void setCmdSelectClass(String cmdSelectClass) {\n\t\tthis.cmdSelectClass = cmdSelectClass;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java",
    "content": "package jadx.gui.settings;\n\nimport java.io.Reader;\nimport java.io.Writer;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.StringJoiner;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.Gson;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.data.ICodeComment;\nimport jadx.api.data.ICodeRename;\nimport jadx.api.data.IJavaCodeRef;\nimport jadx.api.data.IJavaNodeRef;\nimport jadx.api.data.impl.JadxCodeComment;\nimport jadx.api.data.impl.JadxCodeData;\nimport jadx.api.data.impl.JadxCodeRef;\nimport jadx.api.data.impl.JadxCodeRename;\nimport jadx.api.data.impl.JadxNodeRef;\nimport jadx.api.plugins.utils.CommonFileUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.gui.cache.manager.CacheManager;\nimport jadx.gui.settings.data.ProjectData;\nimport jadx.gui.settings.data.SaveOptionEnum;\nimport jadx.gui.settings.data.TabViewState;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.codearea.EditorViewState;\nimport jadx.gui.utils.RelativePathTypeAdapter;\n\nimport static jadx.core.utils.GsonUtils.defaultGsonBuilder;\nimport static jadx.core.utils.GsonUtils.interfaceReplace;\n\npublic class JadxProject {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxProject.class);\n\n\tpublic static final String PROJECT_EXTENSION = \"jadx\";\n\n\tprivate static final int SEARCH_HISTORY_LIMIT = 30;\n\n\tprivate final transient MainWindow mainWindow;\n\tprivate final transient TabStateViewAdapter tabStateViewAdapter = new TabStateViewAdapter();\n\n\tprivate transient String name = \"New Project\";\n\tprivate transient @Nullable Path projectPath;\n\n\tprivate transient boolean initial = true;\n\tprivate transient boolean saved;\n\n\tprivate final ProjectData data;\n\n\tpublic JadxProject(MainWindow mainWindow) {\n\t\tthis(mainWindow, new ProjectData());\n\t}\n\n\tprivate JadxProject(MainWindow mainWindow, ProjectData projectData) {\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.data = Objects.requireNonNull(projectData);\n\t}\n\n\tpublic void fillJadxArgs(JadxArgs jadxArgs) {\n\t\tjadxArgs.setInputFiles(FileUtils.toFiles(getFilePaths()));\n\t\tif (jadxArgs.getUserRenamesMappingsPath() == null) {\n\t\t\tjadxArgs.setUserRenamesMappingsPath(getMappingsPath());\n\t\t}\n\t\tjadxArgs.setCodeData(getCodeData());\n\t\tjadxArgs.getPluginOptions().putAll(data.getPluginOptions());\n\t}\n\n\tpublic @Nullable Path getWorkingDir() {\n\t\tif (projectPath != null) {\n\t\t\treturn projectPath.toAbsolutePath().getParent();\n\t\t}\n\t\tList<Path> files = data.getFiles();\n\t\tif (!files.isEmpty()) {\n\t\t\tPath path = files.get(0);\n\t\t\treturn path.toAbsolutePath().getParent();\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * @return null if project not saved\n\t */\n\tpublic @Nullable Path getProjectPath() {\n\t\treturn projectPath;\n\t}\n\n\tprivate void setProjectPath(@NotNull Path projectPath) {\n\t\tthis.projectPath = projectPath;\n\t\tthis.name = CommonFileUtils.removeFileExtension(projectPath.getFileName().toString());\n\t\tchanged();\n\t}\n\n\tpublic List<Path> getFilePaths() {\n\t\treturn data.getFiles();\n\t}\n\n\tpublic void setFilePaths(List<Path> files) {\n\t\tif (files.equals(getFilePaths())) {\n\t\t\treturn;\n\t\t}\n\t\tif (files.isEmpty()) {\n\t\t\tdata.setFiles(files);\n\t\t\tname = \"\";\n\t\t} else {\n\t\t\tCollections.sort(files);\n\t\t\tdata.setFiles(files);\n\t\t\tStringJoiner joiner = new StringJoiner(\"_\");\n\t\t\tfor (Path p : files) {\n\t\t\t\tPath fileNamePart = p.getFileName();\n\t\t\t\tif (fileNamePart == null) {\n\t\t\t\t\tjoiner.add(p.toString());\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tString fileName = fileNamePart.toString();\n\t\t\t\tif (!fileName.endsWith(\".jadx.kts\")) {\n\t\t\t\t\tjoiner.add(CommonFileUtils.removeFileExtension(fileName));\n\t\t\t\t}\n\t\t\t}\n\t\t\tString joinedName = joiner.toString();\n\t\t\tname = StringUtils.abbreviate(joinedName, 100);\n\t\t}\n\t\tchanged();\n\t}\n\n\tpublic void setTreeExpansions(List<String> list) {\n\t\tif (list.equals(data.getTreeExpansionsV2())) {\n\t\t\treturn;\n\t\t}\n\t\tdata.setTreeExpansionsV2(list);\n\t\tchanged();\n\t}\n\n\tpublic List<String> getTreeExpansions() {\n\t\treturn data.getTreeExpansionsV2();\n\t}\n\n\tpublic JadxCodeData getCodeData() {\n\t\treturn data.getCodeData();\n\t}\n\n\tpublic void setCodeData(JadxCodeData codeData) {\n\t\tdata.setCodeData(codeData);\n\t\tchanged();\n\t}\n\n\tpublic void saveOpenTabs(List<EditorViewState> tabs) {\n\t\tList<TabViewState> tabStateList = tabs.stream()\n\t\t\t\t.map(tabStateViewAdapter::build)\n\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t.collect(Collectors.toList());\n\t\tif (data.setOpenTabs(tabStateList)) {\n\t\t\tchanged();\n\t\t}\n\t}\n\n\tpublic List<EditorViewState> getOpenTabs(MainWindow mw) {\n\t\ttabStateViewAdapter.setCustomAdapters(mw.getWrapper().getGuiPluginsContext().getTabStatePersistAdapters());\n\t\treturn data.getOpenTabs().stream()\n\t\t\t\t.map(s -> tabStateViewAdapter.load(mw, s))\n\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tpublic Path getMappingsPath() {\n\t\treturn data.getMappingsPath();\n\t}\n\n\tpublic void setMappingsPath(Path mappingsPath) {\n\t\tdata.setMappingsPath(mappingsPath);\n\t\tchanged();\n\t}\n\n\t/**\n\t * Do not expose options map directly to be able to intercept changes\n\t */\n\tpublic void updatePluginOptions(Consumer<Map<String, String>> update) {\n\t\tupdate.accept(data.getPluginOptions());\n\t\tchanged();\n\t}\n\n\tpublic @Nullable String getPluginOption(String key) {\n\t\treturn data.getPluginOptions().get(key);\n\t}\n\n\tprivate Path cacheDir;\n\n\tpublic Path getCacheDir() {\n\t\tif (cacheDir == null) {\n\t\t\tcacheDir = resolveCachePath(data.getCacheDir());\n\t\t}\n\t\treturn cacheDir;\n\t}\n\n\tpublic void resetCacheDir() {\n\t\tcacheDir = resolveCachePath(null);\n\t}\n\n\tprivate Path resolveCachePath(@Nullable String cacheDirStr) {\n\t\tCacheManager cacheManager = mainWindow.getCacheManager();\n\t\tPath newCacheDir = cacheManager.getCacheDir(this, cacheDirStr);\n\t\tString newCacheStr = cacheManager.buildCacheDirStr(newCacheDir);\n\t\tif (!newCacheStr.equals(cacheDirStr)) {\n\t\t\tdata.setCacheDir(newCacheStr);\n\t\t\tchanged();\n\t\t}\n\t\treturn newCacheDir;\n\t}\n\n\tpublic boolean isEnableLiveReload() {\n\t\treturn data.isEnableLiveReload();\n\t}\n\n\tpublic void setEnableLiveReload(boolean newValue) {\n\t\tif (newValue != data.isEnableLiveReload()) {\n\t\t\tdata.setEnableLiveReload(newValue);\n\t\t\tchanged();\n\t\t}\n\t}\n\n\tpublic List<String> getSearchHistory() {\n\t\treturn data.getSearchHistory();\n\t}\n\n\tpublic void addToSearchHistory(String str) {\n\t\tif (str == null || str.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tList<String> list = data.getSearchHistory();\n\t\tif (!list.isEmpty() && list.get(0).equals(str)) {\n\t\t\treturn;\n\t\t}\n\t\tlist.remove(str);\n\t\tlist.add(0, str);\n\t\tif (list.size() > SEARCH_HISTORY_LIMIT) {\n\t\t\tlist.remove(list.size() - 1);\n\t\t}\n\t\tdata.setSearchHistory(list);\n\t\tchanged();\n\t}\n\n\tpublic void setSearchResourcesFilter(String searchResourcesFilter) {\n\t\tdata.setSearchResourcesFilter(searchResourcesFilter);\n\t}\n\n\tpublic String getSearchResourcesFilter() {\n\t\treturn data.getSearchResourcesFilter();\n\t}\n\n\tpublic void setSearchResourcesSizeLimit(int searchResourcesSizeLimit) {\n\t\tdata.setSearchResourcesSizeLimit(searchResourcesSizeLimit);\n\t}\n\n\tpublic int getSearchResourcesSizeLimit() {\n\t\treturn data.getSearchResourcesSizeLimit();\n\t}\n\n\tprivate void changed() {\n\t\tJadxSettings settings = mainWindow.getSettings();\n\t\tif (settings != null && settings.getSaveOption() == SaveOptionEnum.ALWAYS) {\n\t\t\tsave();\n\t\t} else {\n\t\t\tsaved = false;\n\t\t}\n\t\tinitial = false;\n\t\tmainWindow.updateProject(this);\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic boolean isSaveFileSelected() {\n\t\treturn projectPath != null;\n\t}\n\n\tpublic boolean isSaved() {\n\t\treturn saved;\n\t}\n\n\tpublic boolean isInitial() {\n\t\treturn initial;\n\t}\n\n\tpublic void saveAs(Path path) {\n\t\tmainWindow.getCacheManager().projectPathUpdate(this, path);\n\t\tsetProjectPath(path);\n\t\tsave();\n\t}\n\n\tpublic void save() {\n\t\tPath savePath = getProjectPath();\n\t\tif (savePath != null) {\n\t\t\tPath basePath = savePath.toAbsolutePath().getParent();\n\t\t\ttry (Writer writer = Files.newBufferedWriter(savePath, StandardCharsets.UTF_8)) {\n\t\t\t\tbuildGson(basePath).toJson(data, writer);\n\t\t\t\tsaved = true;\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new RuntimeException(\"Error saving project\", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static JadxProject load(MainWindow mainWindow, Path path) {\n\t\tProjectData projectData = loadProjectData(path);\n\t\tJadxProject project = new JadxProject(mainWindow, projectData);\n\t\tproject.saved = true;\n\t\tproject.setProjectPath(path);\n\t\treturn project;\n\t}\n\n\tpublic static ProjectData loadProjectData(Path path) {\n\t\tPath basePath = path.toAbsolutePath().getParent();\n\t\ttry (Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {\n\t\t\treturn buildGson(basePath).fromJson(reader, ProjectData.class);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to load project file: \" + path, e);\n\t\t}\n\t}\n\n\tprivate static Gson buildGson(Path basePath) {\n\t\treturn defaultGsonBuilder()\n\t\t\t\t.registerTypeHierarchyAdapter(Path.class, new RelativePathTypeAdapter(basePath))\n\t\t\t\t.registerTypeAdapter(ICodeComment.class, interfaceReplace(JadxCodeComment.class))\n\t\t\t\t.registerTypeAdapter(ICodeRename.class, interfaceReplace(JadxCodeRename.class))\n\t\t\t\t.registerTypeAdapter(IJavaNodeRef.class, interfaceReplace(JadxNodeRef.class))\n\t\t\t\t.registerTypeAdapter(IJavaCodeRef.class, interfaceReplace(JadxCodeRef.class))\n\t\t\t\t.create();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java",
    "content": "package jadx.gui.settings;\n\nimport java.awt.Font;\nimport java.awt.GraphicsDevice;\nimport java.awt.GraphicsEnvironment;\nimport java.awt.Rectangle;\nimport java.awt.Window;\nimport java.nio.file.Path;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.swing.JFrame;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.ExclusionStrategy;\nimport com.google.gson.FieldAttributes;\nimport com.google.gson.Gson;\n\nimport jadx.api.CommentsLevel;\nimport jadx.api.DecompilationMode;\nimport jadx.api.JadxArgs;\nimport jadx.api.args.GeneratedRenamesMappingFileMode;\nimport jadx.api.args.IntegerFormat;\nimport jadx.api.args.ResourceNameSource;\nimport jadx.api.args.UseSourceNameAsClassNameAlias;\nimport jadx.api.args.UserRenamesMappingsMode;\nimport jadx.cli.config.JadxConfigAdapter;\nimport jadx.cli.config.JadxConfigExclude;\nimport jadx.core.utils.GsonUtils;\nimport jadx.gui.cache.code.CodeCacheMode;\nimport jadx.gui.cache.usage.UsageCacheMode;\nimport jadx.gui.settings.data.SaveOptionEnum;\nimport jadx.gui.settings.data.ShortcutsWrapper;\nimport jadx.gui.settings.font.FontSettings;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.tab.dnd.TabDndGhostType;\nimport jadx.gui.utils.LangLocale;\nimport jadx.gui.utils.PathTypeAdapter;\nimport jadx.gui.utils.RectangleTypeAdapter;\n\nimport static jadx.gui.settings.JadxSettingsData.CURRENT_SETTINGS_VERSION;\n\npublic class JadxSettings {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxSettings.class);\n\n\tprivate static final int RECENT_PROJECTS_COUNT = 30;\n\n\tprivate final JadxConfigAdapter<JadxSettingsData> configAdapter;\n\tprivate final Object dataWriteSync = new Object();\n\tprivate final ShortcutsWrapper shortcutsWrapper = new ShortcutsWrapper();\n\tprivate final FontSettings fontSettings = new FontSettings();\n\n\tprivate JadxSettingsData settingsData;\n\n\tpublic JadxSettings(JadxConfigAdapter<JadxSettingsData> configAdapter) {\n\t\tthis.configAdapter = configAdapter;\n\t}\n\n\tpublic static JadxConfigAdapter<JadxSettingsData> buildConfigAdapter() {\n\t\treturn new JadxConfigAdapter<>(JadxSettingsData.class, \"gui\", gsonBuilder -> {\n\t\t\tgsonBuilder.registerTypeHierarchyAdapter(Path.class, PathTypeAdapter.singleton());\n\t\t\tgsonBuilder.registerTypeHierarchyAdapter(Rectangle.class, RectangleTypeAdapter.singleton());\n\t\t});\n\t}\n\n\tpublic String getSettingsJsonString() {\n\t\treturn configAdapter.objectToJsonString(settingsData);\n\t}\n\n\tpublic void loadSettingsFromJsonString(String jsonStr) {\n\t\tloadSettingsData(configAdapter.jsonStringToObject(jsonStr));\n\t}\n\n\tpublic void loadSettingsData(JadxSettingsData settingsData) {\n\t\tthis.settingsData = settingsData;\n\t\tupgradeSettings(settingsData.getSettingsVersion());\n\t\tfixOnLoad();\n\t\t// update custom fields\n\t\tshortcutsWrapper.updateShortcuts(settingsData.getShortcuts());\n\t\tfontSettings.bindData(settingsData);\n\t}\n\n\tprivate void upgradeSettings(int fromVersion) {\n\t\tif (settingsData.getSettingsVersion() == CURRENT_SETTINGS_VERSION) {\n\t\t\treturn;\n\t\t}\n\t\tLOG.debug(\"upgrade settings from version: {} to {}\", fromVersion, CURRENT_SETTINGS_VERSION);\n\t\tif (fromVersion <= 22) {\n\t\t\tfromVersion++;\n\t\t}\n\t\tif (fromVersion != CURRENT_SETTINGS_VERSION) {\n\t\t\tLOG.warn(\"Incorrect settings upgrade. Expected version: {}, got: {}\", CURRENT_SETTINGS_VERSION, fromVersion);\n\t\t}\n\t\tsettingsData.setSettingsVersion(CURRENT_SETTINGS_VERSION);\n\t\tsync();\n\t}\n\n\tprivate void fixOnLoad() {\n\t\tif (settingsData.getThreadsCount() <= 0) {\n\t\t\tsettingsData.setThreadsCount(JadxArgs.DEFAULT_THREADS_COUNT);\n\t\t}\n\t\tif (settingsData.getDeobfuscationMinLength() < 0) {\n\t\t\tsettingsData.setDeobfuscationMinLength(0);\n\t\t}\n\t\tif (settingsData.getDeobfuscationMaxLength() < 0) {\n\t\t\tsettingsData.setDeobfuscationMaxLength(0);\n\t\t}\n\t}\n\n\tpublic void sync() {\n\t\tsynchronized (dataWriteSync) {\n\t\t\tconfigAdapter.save(settingsData);\n\t\t}\n\t}\n\n\tpublic String exportSettingsString() {\n\t\tGson gson = GsonUtils.defaultGsonBuilder()\n\t\t\t\t.setExclusionStrategies(new ExclusionStrategy() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic boolean shouldSkipField(FieldAttributes f) {\n\t\t\t\t\t\treturn f.getAnnotation(JadxConfigExclude.class) != null\n\t\t\t\t\t\t\t\t|| f.getAnnotation(JadxConfigExcludeExport.class) != null;\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic boolean shouldSkipClass(Class<?> clazz) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.create();\n\t\treturn gson.toJson(settingsData);\n\t}\n\n\tpublic JadxArgs toJadxArgs() {\n\t\treturn settingsData.toJadxArgs();\n\t}\n\n\tpublic List<String> getFiles() {\n\t\treturn settingsData.getFiles();\n\t}\n\n\tpublic String getCmdSelectClass() {\n\t\treturn settingsData.getCmdSelectClass();\n\t}\n\n\tpublic Path getLastOpenFilePath() {\n\t\treturn settingsData.getLastOpenFilePath();\n\t}\n\n\tpublic void setLastOpenFilePath(Path lastOpenFilePath) {\n\t\tsettingsData.setLastOpenFilePath(lastOpenFilePath);\n\t}\n\n\tpublic Path getLastSaveProjectPath() {\n\t\treturn settingsData.getLastSaveProjectPath();\n\t}\n\n\tpublic void setLastSaveProjectPath(Path lastSaveProjectPath) {\n\t\tsettingsData.setLastSaveProjectPath(lastSaveProjectPath);\n\t}\n\n\tpublic Path getLastSaveFilePath() {\n\t\treturn settingsData.getLastSaveFilePath();\n\t}\n\n\tpublic void setLastSaveFilePath(Path lastSaveFilePath) {\n\t\tsettingsData.setLastSaveFilePath(lastSaveFilePath);\n\t}\n\n\tpublic boolean isFlattenPackage() {\n\t\treturn settingsData.isFlattenPackage();\n\t}\n\n\tpublic void setFlattenPackage(boolean flattenPackage) {\n\t\tsettingsData.setFlattenPackage(flattenPackage);\n\t}\n\n\tpublic boolean isCheckForUpdates() {\n\t\treturn settingsData.isCheckForUpdates();\n\t}\n\n\tpublic void setCheckForUpdates(boolean checkForUpdates) {\n\t\tsettingsData.setCheckForUpdates(checkForUpdates);\n\t\tsync();\n\t}\n\n\tpublic boolean isDisableTooltipOnHover() {\n\t\treturn settingsData.isDisableTooltipOnHover();\n\t}\n\n\tpublic void setDisableTooltipOnHover(boolean disableTooltipOnHover) {\n\t\tsettingsData.setDisableTooltipOnHover(disableTooltipOnHover);\n\t}\n\n\tpublic List<Path> getRecentProjects() {\n\t\treturn Collections.unmodifiableList(settingsData.getRecentProjects());\n\t}\n\n\tpublic void addRecentProject(@Nullable Path projectPath) {\n\t\tif (projectPath == null) {\n\t\t\treturn;\n\t\t}\n\t\tList<Path> recentProjects = settingsData.getRecentProjects();\n\t\tPath normPath = projectPath.toAbsolutePath().normalize();\n\t\trecentProjects.remove(normPath);\n\t\trecentProjects.add(0, normPath);\n\t\tint count = recentProjects.size();\n\t\tif (count > RECENT_PROJECTS_COUNT) {\n\t\t\trecentProjects.subList(RECENT_PROJECTS_COUNT, count).clear();\n\t\t}\n\t}\n\n\tpublic void removeRecentProject(Path projectPath) {\n\t\tList<Path> recentProjects = settingsData.getRecentProjects();\n\t\trecentProjects.remove(projectPath);\n\t}\n\n\tprivate static String makeWindowId(Window window) {\n\t\treturn window.getClass().getSimpleName();\n\t}\n\n\t@SuppressWarnings(\"ConstantValue\")\n\tpublic void saveWindowPos(Window window) {\n\t\tif (window == null) {\n\t\t\treturn;\n\t\t}\n\t\tsynchronized (dataWriteSync) {\n\t\t\tRectangle bounds = window.getBounds();\n\t\t\tif (bounds != null) {\n\t\t\t\tWindowLocation pos = new WindowLocation(makeWindowId(window), bounds);\n\t\t\t\tsettingsData.getWindowPos().put(pos.getWindowId(), pos);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic boolean loadWindowPos(Window window) {\n\t\tString windowId = makeWindowId(window);\n\t\tWindowLocation pos = settingsData.getWindowPos().get(windowId);\n\t\tif (pos == null) {\n\t\t\treturn false;\n\t\t}\n\t\tRectangle bounds = pos.getBounds();\n\t\tif (bounds == null || !isAccessibleInAnyScreen(windowId, bounds)) {\n\t\t\treturn false;\n\t\t}\n\t\twindow.setBounds(bounds);\n\t\tif (window instanceof MainWindow) {\n\t\t\t((JFrame) window).setExtendedState(getMainWindowExtendedState());\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean isAccessibleInAnyScreen(String windowId, Rectangle windowBounds) {\n\t\tfor (GraphicsDevice gd : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()) {\n\t\t\tRectangle screenBounds = gd.getDefaultConfiguration().getBounds();\n\t\t\tif (screenBounds.intersects(windowBounds)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tLOG.debug(\"Window saved position was ignored: {}, bounds: {}\", windowId, windowBounds);\n\t\treturn false;\n\t}\n\n\tpublic int getMainWindowExtendedState() {\n\t\treturn settingsData.getMainWindowExtendedState();\n\t}\n\n\tpublic void setMainWindowExtendedState(int mainWindowExtendedState) {\n\t\tsettingsData.setMainWindowExtendedState(mainWindowExtendedState);\n\t}\n\n\tpublic boolean isShowHeapUsageBar() {\n\t\treturn settingsData.isShowHeapUsageBar();\n\t}\n\n\tpublic void setShowHeapUsageBar(boolean showHeapUsageBar) {\n\t\tsettingsData.setShowHeapUsageBar(showHeapUsageBar);\n\t}\n\n\tpublic boolean isAlwaysSelectOpened() {\n\t\treturn settingsData.isAlwaysSelectOpened();\n\t}\n\n\tpublic void setAlwaysSelectOpened(boolean alwaysSelectOpened) {\n\t\tsettingsData.setAlwaysSelectOpened(alwaysSelectOpened);\n\t}\n\n\tpublic boolean isEnablePreviewTab() {\n\t\treturn settingsData.isEnablePreviewTab();\n\t}\n\n\tpublic void setEnablePreviewTab(boolean enablePreviewTab) {\n\t\tsettingsData.setEnablePreviewTab(enablePreviewTab);\n\t}\n\n\tpublic boolean isUseAlternativeFileDialog() {\n\t\treturn settingsData.isUseAlternativeFileDialog();\n\t}\n\n\tpublic void setUseAlternativeFileDialog(boolean useAlternativeFileDialog) {\n\t\tsettingsData.setUseAlternativeFileDialog(useAlternativeFileDialog);\n\t}\n\n\tpublic String getExcludedPackages() {\n\t\treturn settingsData.getExcludedPackages();\n\t}\n\n\tpublic void setExcludedPackages(String excludedPackages) {\n\t\tsettingsData.setExcludedPackages(excludedPackages);\n\t}\n\n\tpublic LangLocale getLangLocale() {\n\t\treturn settingsData.getLangLocale();\n\t}\n\n\tpublic void setLangLocale(LangLocale langLocale) {\n\t\tsettingsData.setLangLocale(langLocale);\n\t}\n\n\tpublic boolean isAutoStartJobs() {\n\t\treturn settingsData.isAutoStartJobs();\n\t}\n\n\tpublic void setAutoStartJobs(boolean autoStartJobs) {\n\t\tsettingsData.setAutoStartJobs(autoStartJobs);\n\t}\n\n\tpublic ShortcutsWrapper getShortcuts() {\n\t\treturn shortcutsWrapper;\n\t}\n\n\tpublic int getTreeWidth() {\n\t\treturn settingsData.getTreeWidth();\n\t}\n\n\tpublic void setTreeWidth(int treeWidth) {\n\t\tsettingsData.setTreeWidth(treeWidth);\n\t}\n\n\tpublic float getUiZoom() {\n\t\treturn settingsData.getUiZoom();\n\t}\n\n\tpublic void setUiZoom(float uiZoom) {\n\t\tsettingsData.setUiZoom(uiZoom);\n\t\tfontSettings.applyUiZoom(uiZoom, isApplyUiZoomToFonts());\n\t}\n\n\tpublic boolean isApplyUiZoomToFonts() {\n\t\treturn settingsData.isApplyUiZoomToFonts();\n\t}\n\n\tpublic void setApplyUiZoomToFonts(boolean applyUiZoomToFonts) {\n\t\tsettingsData.setApplyUiZoomToFonts(applyUiZoomToFonts);\n\t\tfontSettings.applyUiZoom(getUiZoom(), applyUiZoomToFonts);\n\t}\n\n\tpublic FontSettings getFontSettings() {\n\t\treturn fontSettings;\n\t}\n\n\tpublic Font getUiFont() {\n\t\treturn fontSettings.getUiFontAdapter().getEffectiveFont();\n\t}\n\n\tpublic void setUiFont(Font font) {\n\t\tfontSettings.getUiFontAdapter().setFont(font);\n\t}\n\n\tpublic Font getCodeFont() {\n\t\treturn fontSettings.getCodeFontAdapter().getEffectiveFont();\n\t}\n\n\tpublic void setCodeFont(Font font) {\n\t\tfontSettings.getCodeFontAdapter().setFont(font);\n\t}\n\n\tpublic Font getSmaliFont() {\n\t\treturn fontSettings.getSmaliFontAdapter().getEffectiveFont();\n\t}\n\n\tpublic void setSmaliFont(Font font) {\n\t\tfontSettings.getSmaliFontAdapter().setFont(font);\n\t}\n\n\tpublic String getEditorTheme() {\n\t\treturn settingsData.getEditorTheme();\n\t}\n\n\tpublic void setEditorTheme(String editorTheme) {\n\t\tsettingsData.setEditorTheme(editorTheme);\n\t}\n\n\tpublic String getLafTheme() {\n\t\treturn settingsData.getLafTheme();\n\t}\n\n\tpublic void setLafTheme(String lafTheme) {\n\t\tsettingsData.setLafTheme(lafTheme);\n\t}\n\n\tpublic boolean isCodeAreaLineWrap() {\n\t\treturn settingsData.isCodeAreaLineWrap();\n\t}\n\n\tpublic void setCodeAreaLineWrap(boolean lineWrap) {\n\t\tsettingsData.setCodeAreaLineWrap(lineWrap);\n\t}\n\n\tpublic int getSearchResultsPerPage() {\n\t\treturn settingsData.getSearchResultsPerPage();\n\t}\n\n\tpublic void setSearchResultsPerPage(int searchResultsPerPage) {\n\t\tsettingsData.setSearchResultsPerPage(searchResultsPerPage);\n\t}\n\n\tpublic boolean isUseAutoSearch() {\n\t\treturn settingsData.isUseAutoSearch();\n\t}\n\n\tpublic void saveUseAutoSearch(boolean useAutoSearch) {\n\t\tsettingsData.setUseAutoSearch(useAutoSearch);\n\t\tsync();\n\t}\n\n\tpublic void saveKeepCommonDialogOpen(boolean keepCommonDialogOpen) {\n\t\tsettingsData.setKeepCommonDialogOpen(keepCommonDialogOpen);\n\t\tsync();\n\t}\n\n\tpublic boolean isKeepCommonDialogOpen() {\n\t\treturn settingsData.isKeepCommonDialogOpen();\n\t}\n\n\tpublic int getMainWindowVerticalSplitterLoc() {\n\t\treturn settingsData.getMainWindowVerticalSplitterLoc();\n\t}\n\n\tpublic void setMainWindowVerticalSplitterLoc(int location) {\n\t\tsettingsData.setMainWindowVerticalSplitterLoc(location);\n\t}\n\n\tpublic int getDebuggerStackFrameSplitterLoc() {\n\t\treturn settingsData.getDebuggerStackFrameSplitterLoc();\n\t}\n\n\tpublic void setDebuggerStackFrameSplitterLoc(int location) {\n\t\tsettingsData.setDebuggerStackFrameSplitterLoc(location);\n\t}\n\n\tpublic int getDebuggerVarTreeSplitterLoc() {\n\t\treturn settingsData.getDebuggerVarTreeSplitterLoc();\n\t}\n\n\tpublic void setDebuggerVarTreeSplitterLoc(int location) {\n\t\tsettingsData.setDebuggerVarTreeSplitterLoc(location);\n\t}\n\n\tpublic String getAdbDialogHost() {\n\t\treturn settingsData.getAdbDialogHost();\n\t}\n\n\tpublic void setAdbDialogHost(String adbDialogHost) {\n\t\tsettingsData.setAdbDialogHost(adbDialogHost);\n\t}\n\n\tpublic String getAdbDialogPath() {\n\t\treturn settingsData.getAdbDialogPath();\n\t}\n\n\tpublic void setAdbDialogPath(String adbDialogPath) {\n\t\tsettingsData.setAdbDialogPath(adbDialogPath);\n\t}\n\n\tpublic String getAdbDialogPort() {\n\t\treturn settingsData.getAdbDialogPort();\n\t}\n\n\tpublic void setAdbDialogPort(String adbDialogPort) {\n\t\tsettingsData.setAdbDialogPort(adbDialogPort);\n\t}\n\n\tpublic CommentsLevel getCommentsLevel() {\n\t\treturn settingsData.getCommentsLevel();\n\t}\n\n\tpublic void setCommentsLevel(CommentsLevel level) {\n\t\tsettingsData.setCommentsLevel(level);\n\t}\n\n\tpublic int getTypeUpdatesLimitCount() {\n\t\treturn settingsData.getTypeUpdatesLimitCount();\n\t}\n\n\tpublic void setTypeUpdatesLimitCount(int typeUpdatesLimitCount) {\n\t\tsettingsData.setTypeUpdatesLimitCount(typeUpdatesLimitCount);\n\t}\n\n\tpublic LineNumbersMode getLineNumbersMode() {\n\t\treturn settingsData.getLineNumbersMode();\n\t}\n\n\tpublic void setLineNumbersMode(LineNumbersMode lineNumbersMode) {\n\t\tsettingsData.setLineNumbersMode(lineNumbersMode);\n\t}\n\n\tpublic CodeCacheMode getCodeCacheMode() {\n\t\treturn settingsData.getCodeCacheMode();\n\t}\n\n\tpublic void setCodeCacheMode(CodeCacheMode codeCacheMode) {\n\t\tsettingsData.setCodeCacheMode(codeCacheMode);\n\t}\n\n\tpublic UsageCacheMode getUsageCacheMode() {\n\t\treturn settingsData.getUsageCacheMode();\n\t}\n\n\tpublic void setUsageCacheMode(UsageCacheMode usageCacheMode) {\n\t\tsettingsData.setUsageCacheMode(usageCacheMode);\n\t}\n\n\tpublic @Nullable String getCacheDir() {\n\t\treturn settingsData.getCacheDir();\n\t}\n\n\tpublic void setCacheDir(@Nullable String cacheDir) {\n\t\tsettingsData.setCacheDir(cacheDir);\n\t}\n\n\tpublic boolean isJumpOnDoubleClick() {\n\t\treturn settingsData.isJumpOnDoubleClick();\n\t}\n\n\tpublic void setJumpOnDoubleClick(boolean jumpOnDoubleClick) {\n\t\tsettingsData.setJumpOnDoubleClick(jumpOnDoubleClick);\n\t}\n\n\tpublic boolean isDockLogViewer() {\n\t\treturn settingsData.isDockLogViewer();\n\t}\n\n\tpublic void saveDockLogViewer(boolean dockLogViewer) {\n\t\tsettingsData.setDockLogViewer(dockLogViewer);\n\t\tsync();\n\t}\n\n\tpublic boolean isDockQuickTabs() {\n\t\treturn settingsData.isDockQuickTabs();\n\t}\n\n\tpublic void saveDockQuickTabs(boolean dockQuickTabs) {\n\t\tsettingsData.setDockQuickTabs(dockQuickTabs);\n\t\tsync();\n\t}\n\n\tpublic XposedCodegenLanguage getXposedCodegenLanguage() {\n\t\treturn settingsData.getXposedCodegenLanguage();\n\t}\n\n\tpublic void setXposedCodegenLanguage(XposedCodegenLanguage language) {\n\t\tsettingsData.setXposedCodegenLanguage(language);\n\t}\n\n\tpublic JadxUpdateChannel getJadxUpdateChannel() {\n\t\treturn settingsData.getJadxUpdateChannel();\n\t}\n\n\tpublic void setJadxUpdateChannel(JadxUpdateChannel channel) {\n\t\tsettingsData.setJadxUpdateChannel(channel);\n\t}\n\n\tpublic TabDndGhostType getTabDndGhostType() {\n\t\treturn settingsData.getTabDndGhostType();\n\t}\n\n\tpublic void setTabDndGhostType(TabDndGhostType tabDndGhostType) {\n\t\tsettingsData.setTabDndGhostType(tabDndGhostType);\n\t}\n\n\tpublic boolean isRestoreSwitchOverString() {\n\t\treturn settingsData.isRestoreSwitchOverString();\n\t}\n\n\tpublic void setRestoreSwitchOverString(boolean restoreSwitchOverString) {\n\t\tsettingsData.setRestoreSwitchOverString(restoreSwitchOverString);\n\t}\n\n\tpublic boolean isRenamePrintable() {\n\t\treturn settingsData.isRenamePrintable();\n\t}\n\n\tpublic UserRenamesMappingsMode getUserRenamesMappingsMode() {\n\t\treturn settingsData.getUserRenamesMappingsMode();\n\t}\n\n\tpublic void setUserRenamesMappingsMode(UserRenamesMappingsMode userRenamesMappingsMode) {\n\t\tsettingsData.setUserRenamesMappingsMode(userRenamesMappingsMode);\n\t}\n\n\tpublic boolean isInlineAnonymousClasses() {\n\t\treturn settingsData.isInlineAnonymousClasses();\n\t}\n\n\tpublic void setInlineAnonymousClasses(boolean inlineAnonymousClasses) {\n\t\tsettingsData.setInlineAnonymousClasses(inlineAnonymousClasses);\n\t}\n\n\tpublic boolean isRespectBytecodeAccessModifiers() {\n\t\treturn settingsData.isRespectBytecodeAccessModifiers();\n\t}\n\n\tpublic void setRespectBytecodeAccessModifiers(boolean respectBytecodeAccessModifiers) {\n\t\tsettingsData.setRespectBytecodeAccessModifiers(respectBytecodeAccessModifiers);\n\t}\n\n\tpublic boolean isRenameCaseSensitive() {\n\t\treturn settingsData.isRenameCaseSensitive();\n\t}\n\n\tpublic DecompilationMode getDecompilationMode() {\n\t\treturn settingsData.getDecompilationMode();\n\t}\n\n\tpublic void setDecompilationMode(DecompilationMode decompilationMode) {\n\t\tsettingsData.setDecompilationMode(decompilationMode);\n\t}\n\n\tpublic boolean isInlineMethods() {\n\t\treturn settingsData.isInlineMethods();\n\t}\n\n\tpublic void setInlineMethods(boolean inlineMethods) {\n\t\tsettingsData.setInlineMethods(inlineMethods);\n\t}\n\n\tpublic boolean isFsCaseSensitive() {\n\t\treturn settingsData.isFsCaseSensitive();\n\t}\n\n\tpublic void setFsCaseSensitive(boolean fsCaseSensitive) {\n\t\tsettingsData.setFsCaseSensitive(fsCaseSensitive);\n\t}\n\n\tpublic boolean isExtractFinally() {\n\t\treturn settingsData.isExtractFinally();\n\t}\n\n\tpublic void setExtractFinally(boolean extractFinally) {\n\t\tsettingsData.setExtractFinally(extractFinally);\n\t}\n\n\tpublic int getSourceNameRepeatLimit() {\n\t\treturn settingsData.getSourceNameRepeatLimit();\n\t}\n\n\tpublic void setSourceNameRepeatLimit(int sourceNameRepeatLimit) {\n\t\tsettingsData.setSourceNameRepeatLimit(sourceNameRepeatLimit);\n\t}\n\n\tpublic boolean isRenameValid() {\n\t\treturn settingsData.isRenameValid();\n\t}\n\n\tpublic boolean isSkipXmlPrettyPrint() {\n\t\treturn settingsData.isSkipXmlPrettyPrint();\n\t}\n\n\tpublic void setSkipXmlPrettyPrint(boolean skipXmlPrettyPrint) {\n\t\tsettingsData.setSkipXmlPrettyPrint(skipXmlPrettyPrint);\n\t}\n\n\tpublic UseSourceNameAsClassNameAlias getUseSourceNameAsClassNameAlias() {\n\t\treturn settingsData.getUseSourceNameAsClassNameAlias();\n\t}\n\n\tpublic void setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias useSourceNameAsClassNameAlias) {\n\t\tsettingsData.setUseSourceNameAsClassNameAlias(useSourceNameAsClassNameAlias);\n\t}\n\n\tpublic boolean isShowInconsistentCode() {\n\t\treturn settingsData.isShowInconsistentCode();\n\t}\n\n\tpublic void setShowInconsistentCode(boolean showInconsistentCode) {\n\t\tsettingsData.setShowInconsistentCode(showInconsistentCode);\n\t}\n\n\tpublic boolean isCfgOutput() {\n\t\treturn settingsData.isCfgOutput();\n\t}\n\n\tpublic void setCfgOutput(boolean cfgOutput) {\n\t\tsettingsData.setCfgOutput(cfgOutput);\n\t}\n\n\tpublic boolean isEscapeUnicode() {\n\t\treturn settingsData.isEscapeUnicode();\n\t}\n\n\tpublic void setEscapeUnicode(boolean escapeUnicode) {\n\t\tsettingsData.setEscapeUnicode(escapeUnicode);\n\t}\n\n\tpublic JadxArgs.UseKotlinMethodsForVarNames getUseKotlinMethodsForVarNames() {\n\t\treturn settingsData.getUseKotlinMethodsForVarNames();\n\t}\n\n\tpublic void setUseKotlinMethodsForVarNames(JadxArgs.UseKotlinMethodsForVarNames useKotlinMethodsForVarNames) {\n\t\tsettingsData.setUseKotlinMethodsForVarNames(useKotlinMethodsForVarNames);\n\t}\n\n\tpublic String getDeobfuscationWhitelistStr() {\n\t\treturn settingsData.getDeobfuscationWhitelistStr();\n\t}\n\n\tpublic void setDeobfuscationWhitelistStr(String deobfuscationWhitelistStr) {\n\t\tsettingsData.setDeobfuscationWhitelistStr(deobfuscationWhitelistStr);\n\t}\n\n\tpublic String getGeneratedRenamesMappingFile() {\n\t\treturn settingsData.getGeneratedRenamesMappingFile();\n\t}\n\n\tpublic boolean isRawCfgOutput() {\n\t\treturn settingsData.isRawCfgOutput();\n\t}\n\n\tpublic void setRawCfgOutput(boolean rawCfgOutput) {\n\t\tsettingsData.setRawCfgOutput(rawCfgOutput);\n\t}\n\n\tpublic boolean isMoveInnerClasses() {\n\t\treturn settingsData.isMoveInnerClasses();\n\t}\n\n\tpublic void setMoveInnerClasses(boolean moveInnerClasses) {\n\t\tsettingsData.setMoveInnerClasses(moveInnerClasses);\n\t}\n\n\tpublic boolean isUseDx() {\n\t\treturn settingsData.isUseDx();\n\t}\n\n\tpublic void setUseDx(boolean useDx) {\n\t\tsettingsData.setUseDx(useDx);\n\t}\n\n\tpublic boolean isAddDebugLines() {\n\t\treturn settingsData.isAddDebugLines();\n\t}\n\n\tpublic boolean isUseHeadersForDetectResourceExtensions() {\n\t\treturn settingsData.isUseHeadersForDetectResourceExtensions();\n\t}\n\n\tpublic void setUseHeadersForDetectResourceExtensions(boolean useHeadersForDetectResourceExtensions) {\n\t\tsettingsData.setUseHeadersForDetectResourceExtensions(useHeadersForDetectResourceExtensions);\n\t}\n\n\tpublic Map<String, String> getPluginOptions() {\n\t\treturn settingsData.getPluginOptions();\n\t}\n\n\tpublic boolean isDeobfuscationOn() {\n\t\treturn settingsData.isDeobfuscationOn();\n\t}\n\n\tpublic void setDeobfuscationOn(boolean deobfuscationOn) {\n\t\tsettingsData.setDeobfuscationOn(deobfuscationOn);\n\t}\n\n\tpublic boolean isReplaceConsts() {\n\t\treturn settingsData.isReplaceConsts();\n\t}\n\n\tpublic void setReplaceConsts(boolean replaceConsts) {\n\t\tsettingsData.setReplaceConsts(replaceConsts);\n\t}\n\n\tpublic boolean isAllowInlineKotlinLambda() {\n\t\treturn settingsData.isAllowInlineKotlinLambda();\n\t}\n\n\tpublic void setAllowInlineKotlinLambda(boolean allowInlineKotlinLambda) {\n\t\tsettingsData.setAllowInlineKotlinLambda(allowInlineKotlinLambda);\n\t}\n\n\tpublic void setDeobfuscationUseSourceNameAsAlias(Boolean deobfuscationUseSourceNameAsAlias) {\n\t\tsettingsData.setDeobfuscationUseSourceNameAsAlias(deobfuscationUseSourceNameAsAlias);\n\t}\n\n\tpublic void setRenameFlags(Set<JadxArgs.RenameEnum> renameFlags) {\n\t\tsettingsData.setRenameFlags(renameFlags);\n\t}\n\n\tpublic void updateRenameFlag(JadxArgs.RenameEnum flag, boolean enabled) {\n\t\tif (enabled) {\n\t\t\tsettingsData.getRenameFlags().add(flag);\n\t\t} else {\n\t\t\tsettingsData.getRenameFlags().remove(flag);\n\t\t}\n\t}\n\n\tpublic void setUserRenamesMappingsPath(Path userRenamesMappingsPath) {\n\t\tsettingsData.setUserRenamesMappingsPath(userRenamesMappingsPath);\n\t}\n\n\tpublic boolean isSkipSources() {\n\t\treturn settingsData.isSkipSources();\n\t}\n\n\tpublic boolean isDebugInfo() {\n\t\treturn settingsData.isDebugInfo();\n\t}\n\n\tpublic void setDebugInfo(boolean debugInfo) {\n\t\tsettingsData.setDebugInfo(debugInfo);\n\t}\n\n\tpublic boolean isSkipResources() {\n\t\treturn settingsData.isSkipResources();\n\t}\n\n\tpublic void setSkipResources(boolean skipResources) {\n\t\tsettingsData.setSkipResources(skipResources);\n\t}\n\n\tpublic ResourceNameSource getResourceNameSource() {\n\t\treturn settingsData.getResourceNameSource();\n\t}\n\n\tpublic void setResourceNameSource(ResourceNameSource resourceNameSource) {\n\t\tsettingsData.setResourceNameSource(resourceNameSource);\n\t}\n\n\tpublic IntegerFormat getIntegerFormat() {\n\t\treturn settingsData.getIntegerFormat();\n\t}\n\n\tpublic void setIntegerFormat(IntegerFormat format) {\n\t\tsettingsData.setIntegerFormat(format);\n\t}\n\n\tpublic boolean isFallbackMode() {\n\t\treturn settingsData.isFallbackMode();\n\t}\n\n\tpublic boolean isUseImports() {\n\t\treturn settingsData.isUseImports();\n\t}\n\n\tpublic void setUseImports(boolean useImports) {\n\t\tsettingsData.setUseImports(useImports);\n\t}\n\n\tpublic int getDeobfuscationMinLength() {\n\t\treturn settingsData.getDeobfuscationMinLength();\n\t}\n\n\tpublic void setDeobfuscationMinLength(int deobfuscationMinLength) {\n\t\tsettingsData.setDeobfuscationMinLength(deobfuscationMinLength);\n\t}\n\n\tpublic GeneratedRenamesMappingFileMode getGeneratedRenamesMappingFileMode() {\n\t\treturn settingsData.getGeneratedRenamesMappingFileMode();\n\t}\n\n\tpublic void setGeneratedRenamesMappingFileMode(GeneratedRenamesMappingFileMode generatedRenamesMappingFileMode) {\n\t\tsettingsData.setGeneratedRenamesMappingFileMode(generatedRenamesMappingFileMode);\n\t}\n\n\tpublic int getDeobfuscationMaxLength() {\n\t\treturn settingsData.getDeobfuscationMaxLength();\n\t}\n\n\tpublic void setDeobfuscationMaxLength(int deobfuscationMaxLength) {\n\t\tsettingsData.setDeobfuscationMaxLength(deobfuscationMaxLength);\n\t}\n\n\tpublic int getThreadsCount() {\n\t\treturn settingsData.getThreadsCount();\n\t}\n\n\tpublic void setThreadsCount(int threadsCount) {\n\t\tsettingsData.setThreadsCount(threadsCount);\n\t}\n\n\tpublic SaveOptionEnum getSaveOption() {\n\t\treturn settingsData.getSaveOption();\n\t}\n\n\tpublic void setSaveOption(SaveOptionEnum saveOption) {\n\t\tsettingsData.setSaveOption(saveOption);\n\t}\n\n\tpublic boolean isSmaliAreaShowBytecode() {\n\t\treturn settingsData.isSmaliAreaShowBytecode();\n\t}\n\n\tpublic void setSmaliAreaShowBytecode(boolean smaliAreaShowBytecode) {\n\t\tsettingsData.setSmaliAreaShowBytecode(smaliAreaShowBytecode);\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsData.java",
    "content": "package jadx.gui.settings;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.swing.JFrame;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport com.google.gson.annotations.SerializedName;\n\nimport jadx.cli.LogHelper;\nimport jadx.gui.cache.code.CodeCacheMode;\nimport jadx.gui.cache.usage.UsageCacheMode;\nimport jadx.gui.settings.data.SaveOptionEnum;\nimport jadx.gui.ui.action.ActionModel;\nimport jadx.gui.ui.tab.dnd.TabDndGhostType;\nimport jadx.gui.utils.LafManager;\nimport jadx.gui.utils.LangLocale;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.shortcut.Shortcut;\n\n/**\n * Data class to hold all jadx-gui settings.\n * Also inherit all options from jadx-cli.\n * Serialized/deserialized as JSON in {@link jadx.cli.config.JadxConfigAdapter}.\n * Annotation {@link JadxConfigExcludeExport} used to exclude environment (files, window states)\n * fields from copy/export.\n */\npublic class JadxSettingsData extends JadxGUIArgs {\n\tpublic static final int CURRENT_SETTINGS_VERSION = 23;\n\n\tprivate static final Path USER_HOME = Paths.get(System.getProperty(\"user.home\"));\n\n\t@JadxConfigExcludeExport\n\tprivate Path lastSaveProjectPath = USER_HOME;\n\t@JadxConfigExcludeExport\n\tprivate Path lastOpenFilePath = USER_HOME;\n\t@JadxConfigExcludeExport\n\tprivate Path lastSaveFilePath = USER_HOME;\n\t@JadxConfigExcludeExport\n\tprivate List<Path> recentProjects = new ArrayList<>();\n\n\t@JadxConfigExcludeExport\n\tprivate Map<String, WindowLocation> windowPos = new HashMap<>();\n\t@JadxConfigExcludeExport\n\tprivate int mainWindowExtendedState = JFrame.NORMAL;\n\n\tprivate boolean flattenPackage = false;\n\tprivate boolean checkForUpdates = true;\n\tprivate JadxUpdateChannel jadxUpdateChannel = JadxUpdateChannel.STABLE;\n\n\tprivate float uiZoom = 1.0f;\n\tprivate boolean applyUiZoomToFonts = true;\n\n\tprivate String uiFontStr = \"\";\n\t@SerializedName(value = \"codeFontStr\", alternate = \"fontStr\")\n\tprivate String codeFontStr = \"\";\n\tprivate String smaliFontStr = \"\";\n\n\tprivate String editorTheme = \"\";\n\n\tprivate String lafTheme = LafManager.INITIAL_THEME_NAME;\n\tprivate LangLocale langLocale = NLS.defaultLocale();\n\tprivate boolean autoStartJobs = false;\n\tprivate String excludedPackages = \"\";\n\tprivate SaveOptionEnum saveOption = SaveOptionEnum.ASK;\n\n\tprivate Map<ActionModel, Shortcut> shortcuts = new HashMap<>();\n\n\tprivate boolean showHeapUsageBar = false;\n\tprivate boolean alwaysSelectOpened = false;\n\tprivate boolean enablePreviewTab = false;\n\tprivate boolean useAlternativeFileDialog = false;\n\tprivate boolean codeAreaLineWrap = false;\n\tprivate int searchResultsPerPage = 50;\n\tprivate boolean useAutoSearch = true;\n\tprivate boolean keepCommonDialogOpen = false;\n\tprivate boolean smaliAreaShowBytecode = false;\n\tprivate LineNumbersMode lineNumbersMode = LineNumbersMode.AUTO;\n\n\tprivate int mainWindowVerticalSplitterLoc = 300;\n\tprivate int debuggerStackFrameSplitterLoc = 300;\n\tprivate int debuggerVarTreeSplitterLoc = 700;\n\n\tprivate String adbDialogPath = \"\";\n\tprivate String adbDialogHost = \"localhost\";\n\tprivate String adbDialogPort = \"5037\";\n\n\tprivate CodeCacheMode codeCacheMode = CodeCacheMode.DISK;\n\tprivate UsageCacheMode usageCacheMode = UsageCacheMode.DISK;\n\n\t/**\n\t * Cache dir option values:\n\t * null - default (system)\n\t * \".\" - at project dir\n\t * other - custom\n\t */\n\tprivate @Nullable String cacheDir = null;\n\n\tprivate boolean jumpOnDoubleClick = true;\n\tprivate boolean disableTooltipOnHover = false;\n\n\tprivate XposedCodegenLanguage xposedCodegenLanguage = XposedCodegenLanguage.JAVA;\n\n\tprivate int treeWidth = 130;\n\tprivate boolean dockLogViewer = true;\n\tprivate boolean dockQuickTabs = false;\n\tprivate TabDndGhostType tabDndGhostType = TabDndGhostType.OUTLINE;\n\n\tprivate int settingsVersion = CURRENT_SETTINGS_VERSION;\n\n\tpublic JadxSettingsData() {\n\t\tthis.logLevel = LogHelper.LogLevelEnum.INFO;\n\t}\n\n\tpublic String getAdbDialogHost() {\n\t\treturn adbDialogHost;\n\t}\n\n\tpublic void setAdbDialogHost(String adbDialogHost) {\n\t\tthis.adbDialogHost = adbDialogHost;\n\t}\n\n\tpublic String getAdbDialogPath() {\n\t\treturn adbDialogPath;\n\t}\n\n\tpublic void setAdbDialogPath(String adbDialogPath) {\n\t\tthis.adbDialogPath = adbDialogPath;\n\t}\n\n\tpublic String getAdbDialogPort() {\n\t\treturn adbDialogPort;\n\t}\n\n\tpublic void setAdbDialogPort(String adbDialogPort) {\n\t\tthis.adbDialogPort = adbDialogPort;\n\t}\n\n\tpublic boolean isAlwaysSelectOpened() {\n\t\treturn alwaysSelectOpened;\n\t}\n\n\tpublic void setAlwaysSelectOpened(boolean alwaysSelectOpened) {\n\t\tthis.alwaysSelectOpened = alwaysSelectOpened;\n\t}\n\n\tpublic boolean isAutoStartJobs() {\n\t\treturn autoStartJobs;\n\t}\n\n\tpublic void setAutoStartJobs(boolean autoStartJobs) {\n\t\tthis.autoStartJobs = autoStartJobs;\n\t}\n\n\tpublic @Nullable String getCacheDir() {\n\t\treturn cacheDir;\n\t}\n\n\tpublic void setCacheDir(@Nullable String cacheDir) {\n\t\tthis.cacheDir = cacheDir;\n\t}\n\n\tpublic boolean isCheckForUpdates() {\n\t\treturn checkForUpdates;\n\t}\n\n\tpublic void setCheckForUpdates(boolean checkForUpdates) {\n\t\tthis.checkForUpdates = checkForUpdates;\n\t}\n\n\tpublic boolean isCodeAreaLineWrap() {\n\t\treturn codeAreaLineWrap;\n\t}\n\n\tpublic void setCodeAreaLineWrap(boolean codeAreaLineWrap) {\n\t\tthis.codeAreaLineWrap = codeAreaLineWrap;\n\t}\n\n\tpublic CodeCacheMode getCodeCacheMode() {\n\t\treturn codeCacheMode;\n\t}\n\n\tpublic void setCodeCacheMode(CodeCacheMode codeCacheMode) {\n\t\tthis.codeCacheMode = codeCacheMode;\n\t}\n\n\tpublic int getDebuggerStackFrameSplitterLoc() {\n\t\treturn debuggerStackFrameSplitterLoc;\n\t}\n\n\tpublic void setDebuggerStackFrameSplitterLoc(int debuggerStackFrameSplitterLoc) {\n\t\tthis.debuggerStackFrameSplitterLoc = debuggerStackFrameSplitterLoc;\n\t}\n\n\tpublic int getDebuggerVarTreeSplitterLoc() {\n\t\treturn debuggerVarTreeSplitterLoc;\n\t}\n\n\tpublic void setDebuggerVarTreeSplitterLoc(int debuggerVarTreeSplitterLoc) {\n\t\tthis.debuggerVarTreeSplitterLoc = debuggerVarTreeSplitterLoc;\n\t}\n\n\tpublic boolean isDisableTooltipOnHover() {\n\t\treturn disableTooltipOnHover;\n\t}\n\n\tpublic void setDisableTooltipOnHover(boolean disableTooltipOnHover) {\n\t\tthis.disableTooltipOnHover = disableTooltipOnHover;\n\t}\n\n\tpublic boolean isDockLogViewer() {\n\t\treturn dockLogViewer;\n\t}\n\n\tpublic void setDockLogViewer(boolean dockLogViewer) {\n\t\tthis.dockLogViewer = dockLogViewer;\n\t}\n\n\tpublic boolean isDockQuickTabs() {\n\t\treturn dockQuickTabs;\n\t}\n\n\tpublic void setDockQuickTabs(boolean dockQuickTabs) {\n\t\tthis.dockQuickTabs = dockQuickTabs;\n\t}\n\n\tpublic String getEditorTheme() {\n\t\treturn editorTheme;\n\t}\n\n\tpublic void setEditorTheme(String editorTheme) {\n\t\tthis.editorTheme = editorTheme;\n\t}\n\n\tpublic boolean isEnablePreviewTab() {\n\t\treturn enablePreviewTab;\n\t}\n\n\tpublic void setEnablePreviewTab(boolean enablePreviewTab) {\n\t\tthis.enablePreviewTab = enablePreviewTab;\n\t}\n\n\tpublic String getExcludedPackages() {\n\t\treturn excludedPackages;\n\t}\n\n\tpublic void setExcludedPackages(String excludedPackages) {\n\t\tthis.excludedPackages = excludedPackages;\n\t}\n\n\tpublic boolean isFlattenPackage() {\n\t\treturn flattenPackage;\n\t}\n\n\tpublic void setFlattenPackage(boolean flattenPackage) {\n\t\tthis.flattenPackage = flattenPackage;\n\t}\n\n\tpublic JadxUpdateChannel getJadxUpdateChannel() {\n\t\treturn jadxUpdateChannel;\n\t}\n\n\tpublic void setJadxUpdateChannel(JadxUpdateChannel jadxUpdateChannel) {\n\t\tthis.jadxUpdateChannel = jadxUpdateChannel;\n\t}\n\n\tpublic boolean isJumpOnDoubleClick() {\n\t\treturn jumpOnDoubleClick;\n\t}\n\n\tpublic void setJumpOnDoubleClick(boolean jumpOnDoubleClick) {\n\t\tthis.jumpOnDoubleClick = jumpOnDoubleClick;\n\t}\n\n\tpublic boolean isKeepCommonDialogOpen() {\n\t\treturn keepCommonDialogOpen;\n\t}\n\n\tpublic void setKeepCommonDialogOpen(boolean keepCommonDialogOpen) {\n\t\tthis.keepCommonDialogOpen = keepCommonDialogOpen;\n\t}\n\n\tpublic String getLafTheme() {\n\t\treturn lafTheme;\n\t}\n\n\tpublic void setLafTheme(String lafTheme) {\n\t\tthis.lafTheme = lafTheme;\n\t}\n\n\tpublic LangLocale getLangLocale() {\n\t\treturn langLocale;\n\t}\n\n\tpublic void setLangLocale(LangLocale langLocale) {\n\t\tthis.langLocale = langLocale;\n\t}\n\n\tpublic Path getLastOpenFilePath() {\n\t\treturn lastOpenFilePath;\n\t}\n\n\tpublic void setLastOpenFilePath(Path lastOpenFilePath) {\n\t\tthis.lastOpenFilePath = lastOpenFilePath;\n\t}\n\n\tpublic Path getLastSaveFilePath() {\n\t\treturn lastSaveFilePath;\n\t}\n\n\tpublic void setLastSaveFilePath(Path lastSaveFilePath) {\n\t\tthis.lastSaveFilePath = lastSaveFilePath;\n\t}\n\n\tpublic Path getLastSaveProjectPath() {\n\t\treturn lastSaveProjectPath;\n\t}\n\n\tpublic void setLastSaveProjectPath(Path lastSaveProjectPath) {\n\t\tthis.lastSaveProjectPath = lastSaveProjectPath;\n\t}\n\n\tpublic LineNumbersMode getLineNumbersMode() {\n\t\treturn lineNumbersMode;\n\t}\n\n\tpublic void setLineNumbersMode(LineNumbersMode lineNumbersMode) {\n\t\tthis.lineNumbersMode = lineNumbersMode;\n\t}\n\n\tpublic int getMainWindowExtendedState() {\n\t\treturn mainWindowExtendedState;\n\t}\n\n\tpublic void setMainWindowExtendedState(int mainWindowExtendedState) {\n\t\tthis.mainWindowExtendedState = mainWindowExtendedState;\n\t}\n\n\tpublic int getMainWindowVerticalSplitterLoc() {\n\t\treturn mainWindowVerticalSplitterLoc;\n\t}\n\n\tpublic void setMainWindowVerticalSplitterLoc(int mainWindowVerticalSplitterLoc) {\n\t\tthis.mainWindowVerticalSplitterLoc = mainWindowVerticalSplitterLoc;\n\t}\n\n\tpublic List<Path> getRecentProjects() {\n\t\treturn recentProjects;\n\t}\n\n\tpublic void setRecentProjects(List<Path> recentProjects) {\n\t\tthis.recentProjects = recentProjects;\n\t}\n\n\tpublic SaveOptionEnum getSaveOption() {\n\t\treturn saveOption;\n\t}\n\n\tpublic void setSaveOption(SaveOptionEnum saveOption) {\n\t\tthis.saveOption = saveOption;\n\t}\n\n\tpublic int getSearchResultsPerPage() {\n\t\treturn searchResultsPerPage;\n\t}\n\n\tpublic void setSearchResultsPerPage(int searchResultsPerPage) {\n\t\tthis.searchResultsPerPage = searchResultsPerPage;\n\t}\n\n\tpublic int getSettingsVersion() {\n\t\treturn settingsVersion;\n\t}\n\n\tpublic void setSettingsVersion(int settingsVersion) {\n\t\tthis.settingsVersion = settingsVersion;\n\t}\n\n\tpublic Map<ActionModel, Shortcut> getShortcuts() {\n\t\treturn shortcuts;\n\t}\n\n\tpublic void setShortcuts(Map<ActionModel, Shortcut> shortcuts) {\n\t\tthis.shortcuts = shortcuts;\n\t}\n\n\tpublic boolean isShowHeapUsageBar() {\n\t\treturn showHeapUsageBar;\n\t}\n\n\tpublic void setShowHeapUsageBar(boolean showHeapUsageBar) {\n\t\tthis.showHeapUsageBar = showHeapUsageBar;\n\t}\n\n\tpublic boolean isSmaliAreaShowBytecode() {\n\t\treturn smaliAreaShowBytecode;\n\t}\n\n\tpublic void setSmaliAreaShowBytecode(boolean smaliAreaShowBytecode) {\n\t\tthis.smaliAreaShowBytecode = smaliAreaShowBytecode;\n\t}\n\n\tpublic String getUiFontStr() {\n\t\treturn uiFontStr;\n\t}\n\n\tpublic void setUiFontStr(String uiFontStr) {\n\t\tthis.uiFontStr = uiFontStr;\n\t}\n\n\tpublic String getCodeFontStr() {\n\t\treturn codeFontStr;\n\t}\n\n\tpublic void setCodeFontStr(String codeFontStr) {\n\t\tthis.codeFontStr = codeFontStr;\n\t}\n\n\tpublic String getSmaliFontStr() {\n\t\treturn smaliFontStr;\n\t}\n\n\tpublic void setSmaliFontStr(String smaliFontStr) {\n\t\tthis.smaliFontStr = smaliFontStr;\n\t}\n\n\tpublic TabDndGhostType getTabDndGhostType() {\n\t\treturn tabDndGhostType;\n\t}\n\n\tpublic void setTabDndGhostType(TabDndGhostType tabDndGhostType) {\n\t\tthis.tabDndGhostType = tabDndGhostType;\n\t}\n\n\tpublic int getTreeWidth() {\n\t\treturn treeWidth;\n\t}\n\n\tpublic void setTreeWidth(int treeWidth) {\n\t\tthis.treeWidth = treeWidth;\n\t}\n\n\tpublic float getUiZoom() {\n\t\treturn uiZoom;\n\t}\n\n\tpublic void setUiZoom(float uiZoom) {\n\t\tthis.uiZoom = uiZoom;\n\t}\n\n\tpublic boolean isApplyUiZoomToFonts() {\n\t\treturn applyUiZoomToFonts;\n\t}\n\n\tpublic void setApplyUiZoomToFonts(boolean applyUiZoomToFonts) {\n\t\tthis.applyUiZoomToFonts = applyUiZoomToFonts;\n\t}\n\n\tpublic UsageCacheMode getUsageCacheMode() {\n\t\treturn usageCacheMode;\n\t}\n\n\tpublic void setUsageCacheMode(UsageCacheMode usageCacheMode) {\n\t\tthis.usageCacheMode = usageCacheMode;\n\t}\n\n\tpublic boolean isUseAlternativeFileDialog() {\n\t\treturn useAlternativeFileDialog;\n\t}\n\n\tpublic void setUseAlternativeFileDialog(boolean useAlternativeFileDialog) {\n\t\tthis.useAlternativeFileDialog = useAlternativeFileDialog;\n\t}\n\n\tpublic boolean isUseAutoSearch() {\n\t\treturn useAutoSearch;\n\t}\n\n\tpublic void setUseAutoSearch(boolean useAutoSearch) {\n\t\tthis.useAutoSearch = useAutoSearch;\n\t}\n\n\tpublic Map<String, WindowLocation> getWindowPos() {\n\t\treturn windowPos;\n\t}\n\n\tpublic void setWindowPos(Map<String, WindowLocation> windowPos) {\n\t\tthis.windowPos = windowPos;\n\t}\n\n\tpublic XposedCodegenLanguage getXposedCodegenLanguage() {\n\t\treturn xposedCodegenLanguage;\n\t}\n\n\tpublic void setXposedCodegenLanguage(XposedCodegenLanguage xposedCodegenLanguage) {\n\t\tthis.xposedCodegenLanguage = xposedCodegenLanguage;\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/JadxUpdateChannel.java",
    "content": "package jadx.gui.settings;\n\npublic enum JadxUpdateChannel {\n\tSTABLE,\n\tUNSTABLE,\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/LineNumbersMode.java",
    "content": "package jadx.gui.settings;\n\npublic enum LineNumbersMode {\n\tDISABLE,\n\tNORMAL,\n\tDEBUG,\n\tAUTO\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/TabStateViewAdapter.java",
    "content": "package jadx.gui.settings;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JavaClass;\nimport jadx.gui.plugins.mappings.JInputMapping;\nimport jadx.gui.settings.data.ITabStatePersist;\nimport jadx.gui.settings.data.TabViewState;\nimport jadx.gui.settings.data.ViewPoint;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.treemodel.JResource;\nimport jadx.gui.treemodel.JSubResource;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.codearea.EditorViewState;\nimport jadx.gui.utils.UiUtils;\n\npublic class TabStateViewAdapter {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(TabStateViewAdapter.class);\n\n\tprivate final Map<String, ITabStatePersist> customAdaptersMap = new HashMap<>();\n\n\tpublic @Nullable TabViewState build(EditorViewState viewState) {\n\t\tTabViewState tvs = new TabViewState();\n\t\ttvs.setSubPath(viewState.getSubPath());\n\t\tif (!saveJNode(tvs, viewState.getNode())) {\n\t\t\tif (UiUtils.JADX_GUI_DEBUG) {\n\t\t\t\tLOG.warn(\"Can't save view state: {}\", viewState);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\ttvs.setCaret(viewState.getCaretPos());\n\t\ttvs.setView(new ViewPoint(viewState.getViewPoint()));\n\t\ttvs.setActive(viewState.isActive());\n\t\ttvs.setPinned(viewState.isPinned());\n\t\ttvs.setBookmarked(viewState.isBookmarked());\n\t\ttvs.setHidden(viewState.isHidden());\n\t\ttvs.setPreviewTab(viewState.isPreviewTab());\n\t\treturn tvs;\n\t}\n\n\tpublic @Nullable EditorViewState load(MainWindow mw, TabViewState tvs) {\n\t\ttry {\n\t\t\tJNode node = loadJNode(mw, tvs);\n\t\t\tif (node == null) {\n\t\t\t\tif (UiUtils.JADX_GUI_DEBUG) {\n\t\t\t\t\tLOG.warn(\"Can't restore view for {}\", tvs);\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tEditorViewState viewState = new EditorViewState(node, tvs.getSubPath(), tvs.getCaret(), tvs.getView().toPoint());\n\t\t\tviewState.setActive(tvs.isActive());\n\t\t\tviewState.setPinned(tvs.isPinned());\n\t\t\tviewState.setBookmarked(tvs.isBookmarked());\n\t\t\tviewState.setHidden(tvs.isHidden());\n\t\t\tviewState.setPreviewTab(tvs.isPreviewTab());\n\t\t\treturn viewState;\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to load tab state: {}\", tvs, e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic void setCustomAdapters(List<ITabStatePersist> customAdapters) {\n\t\tcustomAdaptersMap.clear();\n\t\tfor (ITabStatePersist customAdapter : customAdapters) {\n\t\t\tcustomAdaptersMap.put(customAdapter.getNodeClass().getName(), customAdapter);\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate JNode loadJNode(MainWindow mw, TabViewState tvs) {\n\t\tswitch (tvs.getType()) {\n\t\t\tcase \"class\":\n\t\t\t\tJavaClass javaClass = mw.getWrapper().searchJavaClassByRawName(tvs.getTabPath());\n\t\t\t\tif (javaClass != null) {\n\t\t\t\t\treturn mw.getCacheObject().getNodeCache().makeFrom(javaClass);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"resource\":\n\t\t\t\treturn mw.getTreeRoot().searchResourceByName(tvs.getTabPath());\n\n\t\t\tcase \"sub-resource\":\n\t\t\t\tString[] parts = tvs.getTabPath().split(JSubResource.SUB_RES_PREFIX);\n\t\t\t\tJResource baseRes = mw.getTreeRoot().searchResourceByName(parts[0]);\n\t\t\t\tif (baseRes != null) {\n\t\t\t\t\tString subName = parts[1];\n\t\t\t\t\treturn baseRes.searchDepthNode(n -> n.getName().equals(subName)); // will load node before search\n\t\t\t\t}\n\t\t\t\treturn null;\n\n\t\t\tcase \"mapping\":\n\t\t\t\treturn mw.getTreeRoot().followStaticPath(\"JInputs\").searchNode(node -> node instanceof JInputMapping);\n\t\t}\n\t\tITabStatePersist statePersist = customAdaptersMap.get(tvs.getType());\n\t\tif (statePersist != null) {\n\t\t\ttry {\n\t\t\t\treturn statePersist.load(tvs.getTabPath());\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to restore tab for custom node adapter: {}\", tvs.getType(), e);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate boolean saveJNode(TabViewState tvs, JNode node) {\n\t\tif (node instanceof JClass) {\n\t\t\ttvs.setType(\"class\");\n\t\t\ttvs.setTabPath(((JClass) node).getCls().getRawName());\n\t\t\treturn true;\n\t\t}\n\t\tif (node instanceof JSubResource) {\n\t\t\tJSubResource subRes = (JSubResource) node;\n\t\t\ttvs.setType(\"sub-resource\");\n\t\t\ttvs.setTabPath(subRes.getBaseRes().getName() + JSubResource.SUB_RES_PREFIX + subRes.getName());\n\t\t\treturn true;\n\t\t}\n\t\tif (node instanceof JResource) {\n\t\t\ttvs.setType(\"resource\");\n\t\t\ttvs.setTabPath(node.getName());\n\t\t\treturn true;\n\t\t}\n\t\tif (node instanceof JInputMapping) {\n\t\t\ttvs.setType(\"mapping\");\n\t\t\treturn true;\n\t\t}\n\n\t\tString typeName = node.getClass().getName();\n\t\tITabStatePersist statePersist = customAdaptersMap.get(typeName);\n\t\tif (statePersist != null) {\n\t\t\ttry {\n\t\t\t\ttvs.setTabPath(statePersist.save(node));\n\t\t\t\ttvs.setType(statePersist.getNodeClass().getName());\n\t\t\t\treturn true;\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to save state for custom node: {}\", typeName, e);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/WindowLocation.java",
    "content": "package jadx.gui.settings;\n\nimport java.awt.Rectangle;\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.Nullable;\n\n@SuppressWarnings(\"unused\")\npublic class WindowLocation {\n\tprivate String windowId;\n\tprivate @Nullable Rectangle bounds;\n\n\t// Don't remove. Used in JSON serialization\n\tpublic WindowLocation() {\n\t}\n\n\tpublic WindowLocation(String windowId, @Nullable Rectangle bounds) {\n\t\tthis.windowId = windowId;\n\t\tthis.bounds = bounds;\n\t}\n\n\tpublic String getWindowId() {\n\t\treturn windowId;\n\t}\n\n\tpublic void setWindowId(String windowId) {\n\t\tthis.windowId = windowId;\n\t}\n\n\tpublic @Nullable Rectangle getBounds() {\n\t\treturn bounds;\n\t}\n\n\tpublic void setBounds(@Nullable Rectangle bounds) {\n\t\tthis.bounds = bounds;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hashCode(windowId);\n\t}\n\n\t@Override\n\tpublic final boolean equals(Object o) {\n\t\tif (o instanceof WindowLocation) {\n\t\t\tWindowLocation that = (WindowLocation) o;\n\t\t\treturn Objects.equals(windowId, that.windowId)\n\t\t\t\t\t&& Objects.equals(bounds, that.bounds);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"WindowLocation{id=\" + windowId + \", bounds=\" + bounds + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/XposedCodegenLanguage.java",
    "content": "package jadx.gui.settings;\n\npublic enum XposedCodegenLanguage {\n\tJAVA,\n\tKOTLIN,\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/data/ITabStatePersist.java",
    "content": "package jadx.gui.settings.data;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.gui.treemodel.JNode;\n\n/**\n * Adapter interface to allow save/load state of opened tabs\n */\npublic interface ITabStatePersist {\n\n\tClass<? extends JNode> getNodeClass();\n\n\tString save(JNode node);\n\n\t@Nullable\n\tJNode load(String stateStr);\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/data/ProjectData.java",
    "content": "package jadx.gui.settings.data;\n\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.data.impl.JadxCodeData;\nimport jadx.gui.search.providers.ResourceFilter;\n\npublic class ProjectData {\n\tprivate int projectVersion = 2;\n\tprivate List<Path> files = new ArrayList<>();\n\tprivate List<String> treeExpansionsV2 = new ArrayList<>();\n\tprivate JadxCodeData codeData = new JadxCodeData();\n\tprivate List<TabViewState> openTabs = Collections.emptyList();\n\tprivate @Nullable Path mappingsPath;\n\tprivate @Nullable String cacheDir; // don't use relative path adapter\n\tprivate boolean enableLiveReload = false;\n\tprivate List<String> searchHistory = new ArrayList<>();\n\tprivate String searchResourcesFilter = ResourceFilter.DEFAULT_STR;\n\tprivate int searchResourcesSizeLimit = 0; // in MB\n\n\tprotected Map<String, String> pluginOptions = new HashMap<>();\n\n\tpublic List<Path> getFiles() {\n\t\treturn files;\n\t}\n\n\tpublic void setFiles(List<Path> files) {\n\t\tthis.files = Objects.requireNonNull(files);\n\t}\n\n\tpublic List<String> getTreeExpansionsV2() {\n\t\treturn treeExpansionsV2;\n\t}\n\n\tpublic void setTreeExpansionsV2(List<String> treeExpansionsV2) {\n\t\tthis.treeExpansionsV2 = treeExpansionsV2;\n\t}\n\n\tpublic JadxCodeData getCodeData() {\n\t\treturn codeData;\n\t}\n\n\tpublic void setCodeData(JadxCodeData codeData) {\n\t\tthis.codeData = codeData;\n\t}\n\n\tpublic int getProjectVersion() {\n\t\treturn projectVersion;\n\t}\n\n\tpublic void setProjectVersion(int projectVersion) {\n\t\tthis.projectVersion = projectVersion;\n\t}\n\n\tpublic List<TabViewState> getOpenTabs() {\n\t\treturn openTabs;\n\t}\n\n\tpublic boolean setOpenTabs(List<TabViewState> openTabs) {\n\t\tif (this.openTabs.equals(openTabs)) {\n\t\t\treturn false;\n\t\t}\n\t\tthis.openTabs = openTabs;\n\t\treturn true;\n\t}\n\n\t@Nullable\n\tpublic Path getMappingsPath() {\n\t\treturn mappingsPath;\n\t}\n\n\tpublic void setMappingsPath(Path mappingsPath) {\n\t\tthis.mappingsPath = mappingsPath;\n\t}\n\n\tpublic @Nullable String getCacheDir() {\n\t\treturn cacheDir;\n\t}\n\n\tpublic void setCacheDir(@Nullable String cacheDir) {\n\t\tthis.cacheDir = cacheDir;\n\t}\n\n\tpublic boolean isEnableLiveReload() {\n\t\treturn enableLiveReload;\n\t}\n\n\tpublic void setEnableLiveReload(boolean enableLiveReload) {\n\t\tthis.enableLiveReload = enableLiveReload;\n\t}\n\n\tpublic List<String> getSearchHistory() {\n\t\treturn searchHistory;\n\t}\n\n\tpublic void setSearchHistory(List<String> searchHistory) {\n\t\tthis.searchHistory = searchHistory;\n\t}\n\n\tpublic String getSearchResourcesFilter() {\n\t\treturn searchResourcesFilter;\n\t}\n\n\tpublic void setSearchResourcesFilter(String searchResourcesFilter) {\n\t\tthis.searchResourcesFilter = searchResourcesFilter;\n\t}\n\n\tpublic int getSearchResourcesSizeLimit() {\n\t\treturn searchResourcesSizeLimit;\n\t}\n\n\tpublic void setSearchResourcesSizeLimit(int searchResourcesSizeLimit) {\n\t\tthis.searchResourcesSizeLimit = searchResourcesSizeLimit;\n\t}\n\n\tpublic Map<String, String> getPluginOptions() {\n\t\treturn pluginOptions;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/data/SaveOptionEnum.java",
    "content": "package jadx.gui.settings.data;\n\npublic enum SaveOptionEnum {\n\tASK,\n\tNEVER,\n\tALWAYS\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/data/ShortcutsWrapper.java",
    "content": "package jadx.gui.settings.data;\n\nimport java.util.Map;\n\nimport jadx.gui.ui.action.ActionModel;\nimport jadx.gui.utils.shortcut.Shortcut;\n\npublic class ShortcutsWrapper {\n\tprivate Map<ActionModel, Shortcut> shortcuts;\n\n\tpublic void updateShortcuts(Map<ActionModel, Shortcut> shortcuts) {\n\t\tthis.shortcuts = shortcuts;\n\t}\n\n\tpublic Shortcut get(ActionModel actionModel) {\n\t\treturn shortcuts.getOrDefault(actionModel, actionModel.getDefaultShortcut());\n\t}\n\n\tpublic void put(ActionModel actionModel, Shortcut shortcut) {\n\t\tshortcuts.put(actionModel, shortcut);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/data/TabViewState.java",
    "content": "package jadx.gui.settings.data;\n\npublic class TabViewState {\n\tprivate String type;\n\tprivate String tabPath;\n\tprivate String subPath;\n\tprivate int caret;\n\tprivate ViewPoint view;\n\tboolean active;\n\tboolean pinned;\n\tboolean bookmarked;\n\tboolean hidden;\n\tboolean previewTab;\n\n\tpublic String getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setType(String type) {\n\t\tthis.type = type;\n\t}\n\n\tpublic String getTabPath() {\n\t\treturn tabPath;\n\t}\n\n\tpublic void setTabPath(String tabPath) {\n\t\tthis.tabPath = tabPath;\n\t}\n\n\tpublic String getSubPath() {\n\t\treturn subPath;\n\t}\n\n\tpublic void setSubPath(String subPath) {\n\t\tthis.subPath = subPath;\n\t}\n\n\tpublic int getCaret() {\n\t\treturn caret;\n\t}\n\n\tpublic void setCaret(int caret) {\n\t\tthis.caret = caret;\n\t}\n\n\tpublic ViewPoint getView() {\n\t\treturn view;\n\t}\n\n\tpublic void setView(ViewPoint view) {\n\t\tthis.view = view;\n\t}\n\n\tpublic boolean isActive() {\n\t\treturn active;\n\t}\n\n\tpublic void setActive(boolean active) {\n\t\tthis.active = active;\n\t}\n\n\tpublic boolean isPinned() {\n\t\treturn pinned;\n\t}\n\n\tpublic void setPinned(boolean pinned) {\n\t\tthis.pinned = pinned;\n\t}\n\n\tpublic boolean isBookmarked() {\n\t\treturn bookmarked;\n\t}\n\n\tpublic void setBookmarked(boolean bookmarked) {\n\t\tthis.bookmarked = bookmarked;\n\t}\n\n\tpublic boolean isHidden() {\n\t\treturn hidden;\n\t}\n\n\tpublic void setHidden(boolean hidden) {\n\t\tthis.hidden = hidden;\n\t}\n\n\tpublic boolean isPreviewTab() {\n\t\treturn previewTab;\n\t}\n\n\tpublic void setPreviewTab(boolean previewTab) {\n\t\tthis.previewTab = previewTab;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"TabViewState{type=\" + type + \", tabPath=\" + tabPath + \", subPath=\" + subPath + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/data/ViewPoint.java",
    "content": "package jadx.gui.settings.data;\n\nimport java.awt.Point;\n\npublic class ViewPoint {\n\n\tprivate int x;\n\tprivate int y;\n\n\tpublic ViewPoint() {\n\t\tthis(0, 0);\n\t}\n\n\tpublic ViewPoint(Point p) {\n\t\tthis(p.x, p.y);\n\t}\n\n\tpublic ViewPoint(int x, int y) {\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t}\n\n\tpublic Point toPoint() {\n\t\treturn new Point(x, y);\n\t}\n\n\tpublic int getX() {\n\t\treturn x;\n\t}\n\n\tpublic void setX(int x) {\n\t\tthis.x = x;\n\t}\n\n\tpublic int getY() {\n\t\treturn y;\n\t}\n\n\tpublic void setY(int y) {\n\t\tthis.y = y;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ViewPoint{\" + x + \", \" + y + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/font/FontAdapter.java",
    "content": "package jadx.gui.settings.font;\n\nimport java.awt.Font;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.Utils;\nimport jadx.gui.utils.FontUtils;\nimport jadx.gui.utils.UiUtils;\n\n/**\n * Common handler for font updates and sync with settings data.\n */\npublic class FontAdapter {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(FontAdapter.class);\n\n\tprivate Font defaultFont;\n\tprivate Font font;\n\tprivate Font effectiveFont;\n\tprivate Consumer<String> fontSetter;\n\tprivate float uiZoom;\n\n\tpublic FontAdapter(Font defaultFont) {\n\t\tObjects.requireNonNull(defaultFont);\n\t\tthis.defaultFont = defaultFont;\n\t\tthis.font = defaultFont;\n\t}\n\n\t/**\n\t * Load current font from data, and save font setter to future sync\n\t */\n\tpublic void bindData(String fontStr, Consumer<String> fontStrSetter) {\n\t\tfont = loadFromStr(fontStr);\n\t\tfontSetter = fontStrSetter;\n\t}\n\n\tpublic void setDefaultFont(Font newDefaultFont) {\n\t\tObjects.requireNonNull(newDefaultFont);\n\t\tFont prevDefaultFont = defaultFont;\n\t\tdefaultFont = newDefaultFont;\n\t\tif (font == prevDefaultFont) {\n\t\t\t// font was set to default => update it also\n\t\t\tsetFont(newDefaultFont);\n\t\t}\n\t}\n\n\tpublic Font getFont() {\n\t\treturn font;\n\t}\n\n\tpublic Font getEffectiveFont() {\n\t\treturn effectiveFont;\n\t}\n\n\tpublic void setFont(@Nullable Font newFont) {\n\t\tfont = Utils.getOrElse(newFont, defaultFont);\n\t\tfontSetter.accept(getFontStr());\n\t\tapplyFontZoom();\n\t}\n\n\tpublic void setUiZoom(float uiZoom) {\n\t\tthis.uiZoom = uiZoom;\n\t\tapplyFontZoom();\n\t}\n\n\tprivate Font loadFromStr(String fontStr) {\n\t\tif (fontStr != null && !fontStr.isEmpty()) {\n\t\t\ttry {\n\t\t\t\treturn FontUtils.loadByStr(fontStr);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.warn(\"Failed to load font: {}, reset to default\", fontStr, e);\n\t\t\t}\n\t\t}\n\t\treturn defaultFont;\n\t}\n\n\tprivate String getFontStr() {\n\t\tif (font == defaultFont) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn FontUtils.convertToStr(font);\n\t}\n\n\tprivate void applyFontZoom() {\n\t\tif (UiUtils.nearlyEqual(uiZoom, 1.0f)) {\n\t\t\teffectiveFont = font;\n\t\t} else {\n\t\t\teffectiveFont = font.deriveFont(font.getSize2D() * uiZoom);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/font/FontSettings.java",
    "content": "package jadx.gui.settings.font;\n\nimport java.awt.Font;\n\nimport javax.swing.UIManager;\n\nimport com.formdev.flatlaf.FlatLaf;\nimport com.formdev.flatlaf.fonts.inter.FlatInterFont;\nimport com.formdev.flatlaf.fonts.jetbrains_mono.FlatJetBrainsMonoFont;\nimport com.formdev.flatlaf.util.FontUtils;\n\nimport jadx.gui.settings.JadxSettingsData;\nimport jadx.gui.utils.UiUtils;\n\n/**\n * Handle all font related settings\n */\npublic class FontSettings {\n\n\tstatic {\n\t\tFlatInterFont.install();\n\t\tFlatJetBrainsMonoFont.install();\n\t\tFlatLaf.setPreferredMonospacedFontFamily(FlatJetBrainsMonoFont.FAMILY);\n\t}\n\n\tprivate final FontAdapter uiFontAdapter;\n\tprivate final FontAdapter codeFontAdapter;\n\tprivate final FontAdapter smaliFontAdapter;\n\n\tprivate float uiZoom;\n\tprivate boolean applyUiZoomToFonts;\n\n\tpublic FontSettings() {\n\t\tint defFontSize = 13;\n\t\tFont defUiFont = FontUtils.getCompositeFont(FlatInterFont.FAMILY, Font.PLAIN, defFontSize);\n\t\tFont defCodeFont = FontUtils.getCompositeFont(FlatJetBrainsMonoFont.FAMILY, Font.PLAIN, defFontSize);\n\t\tuiFontAdapter = new FontAdapter(defUiFont);\n\t\tcodeFontAdapter = new FontAdapter(defCodeFont);\n\t\tsmaliFontAdapter = new FontAdapter(defCodeFont);\n\t}\n\n\tpublic void bindData(JadxSettingsData data) {\n\t\tuiFontAdapter.bindData(data.getUiFontStr(), data::setUiFontStr);\n\t\tcodeFontAdapter.bindData(data.getCodeFontStr(), data::setCodeFontStr);\n\t\tsmaliFontAdapter.bindData(data.getSmaliFontStr(), data::setSmaliFontStr);\n\t\tapplyUiZoom(data.getUiZoom(), data.isApplyUiZoomToFonts());\n\t}\n\n\t/**\n\t * Fetch and apply default font settings after FlatLaf init.\n\t */\n\tpublic void updateDefaultFont() {\n\t\tFont defaultFont = UIManager.getFont(\"defaultFont\");\n\t\tif (defaultFont != null) {\n\t\t\tuiFontAdapter.setDefaultFont(defaultFont);\n\t\t}\n\t}\n\n\tpublic synchronized void applyUiZoom(float newUiZoom, boolean newApplyUiZoomToFonts) {\n\t\tif (UiUtils.nearlyEqual(uiZoom, newUiZoom) && applyUiZoomToFonts == newApplyUiZoomToFonts) {\n\t\t\treturn;\n\t\t}\n\t\tuiZoom = newUiZoom;\n\t\tapplyUiZoomToFonts = newApplyUiZoomToFonts;\n\n\t\tfloat effectiveFontZoom = newApplyUiZoomToFonts ? newUiZoom : 1.0f;\n\t\tuiFontAdapter.setUiZoom(effectiveFontZoom);\n\t\tcodeFontAdapter.setUiZoom(effectiveFontZoom);\n\t\tsmaliFontAdapter.setUiZoom(effectiveFontZoom);\n\t}\n\n\tpublic FontAdapter getUiFontAdapter() {\n\t\treturn uiFontAdapter;\n\t}\n\n\tpublic FontAdapter getCodeFontAdapter() {\n\t\treturn codeFontAdapter;\n\t}\n\n\tpublic FontAdapter getSmaliFontAdapter() {\n\t\treturn smaliFontAdapter;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/JadxSettingsWindow.java",
    "content": "package jadx.gui.settings.ui;\n\nimport java.awt.BorderLayout;\nimport java.awt.Component;\nimport java.awt.Container;\nimport java.awt.Dimension;\nimport java.awt.FlowLayout;\nimport java.awt.Font;\nimport java.awt.Toolkit;\nimport java.awt.datatransfer.Clipboard;\nimport java.awt.datatransfer.StringSelection;\nimport java.awt.event.ItemEvent;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.InputMap;\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JComboBox;\nimport javax.swing.JComponent;\nimport javax.swing.JDialog;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JSpinner;\nimport javax.swing.JSplitPane;\nimport javax.swing.KeyStroke;\nimport javax.swing.ScrollPaneConstants;\nimport javax.swing.SpinnerNumberModel;\nimport javax.swing.SwingUtilities;\nimport javax.swing.WindowConstants;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.CommentsLevel;\nimport jadx.api.DecompilationMode;\nimport jadx.api.JadxArgs;\nimport jadx.api.JadxArgs.UseKotlinMethodsForVarNames;\nimport jadx.api.JadxDecompiler;\nimport jadx.api.args.GeneratedRenamesMappingFileMode;\nimport jadx.api.args.IntegerFormat;\nimport jadx.api.args.ResourceNameSource;\nimport jadx.api.args.UseSourceNameAsClassNameAlias;\nimport jadx.api.plugins.events.JadxEvents;\nimport jadx.api.plugins.events.types.ReloadSettingsWindow;\nimport jadx.api.plugins.gui.ISettingsGroup;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.settings.JadxSettingsData;\nimport jadx.gui.settings.JadxUpdateChannel;\nimport jadx.gui.settings.LineNumbersMode;\nimport jadx.gui.settings.XposedCodegenLanguage;\nimport jadx.gui.settings.data.SaveOptionEnum;\nimport jadx.gui.settings.font.FontAdapter;\nimport jadx.gui.settings.font.FontSettings;\nimport jadx.gui.settings.ui.cache.CacheSettingsGroup;\nimport jadx.gui.settings.ui.font.JadxFontDialog;\nimport jadx.gui.settings.ui.plugins.PluginSettings;\nimport jadx.gui.settings.ui.shortcut.ShortcutsSettingsGroup;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.codearea.theme.EditorThemeManager;\nimport jadx.gui.ui.codearea.theme.ThemeIdAndName;\nimport jadx.gui.ui.tab.dnd.TabDndGhostType;\nimport jadx.gui.utils.FontUtils;\nimport jadx.gui.utils.LafManager;\nimport jadx.gui.utils.LangLocale;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.ui.ActionHandler;\n\npublic class JadxSettingsWindow extends JDialog {\n\tprivate static final long serialVersionUID = -1804570470377354148L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxSettingsWindow.class);\n\n\tprivate final transient MainWindow mainWindow;\n\tprivate final transient JadxSettings settings;\n\tprivate final transient String startSettings;\n\tprivate final transient String startSettingsHash;\n\tprivate final transient LangLocale prevLang;\n\tprivate final transient Consumer<ReloadSettingsWindow> reloadListener;\n\n\tprivate transient boolean needReload = false;\n\tprivate transient SettingsTree tree;\n\tprivate List<ISettingsGroup> groups;\n\tprivate JPanel wrapGroupPanel;\n\n\tpublic JadxSettingsWindow(MainWindow mainWindow, JadxSettings settings) {\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.settings = settings;\n\t\tthis.startSettings = settings.getSettingsJsonString();\n\t\tthis.startSettingsHash = calcSettingsHash();\n\t\tthis.prevLang = settings.getLangLocale();\n\n\t\tinitUI();\n\n\t\tsetTitle(NLS.str(\"preferences.title\"));\n\t\tsetDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\n\t\tsetModalityType(ModalityType.APPLICATION_MODAL);\n\t\tpack();\n\t\tUiUtils.setWindowIcons(this);\n\t\tsetLocationRelativeTo(null);\n\t\tif (!mainWindow.getSettings().loadWindowPos(this)) {\n\t\t\tsetSize(700, 800);\n\t\t}\n\t\treloadListener = ev -> UiUtils.uiRun(this::reloadUI);\n\t\tmainWindow.events().global().addListener(JadxEvents.RELOAD_SETTINGS_WINDOW, reloadListener);\n\t}\n\n\tprivate void reloadUI() {\n\t\tint[] selection = tree.getSelectionRows();\n\t\tcloseGroups(false);\n\t\tgetContentPane().removeAll();\n\t\tinitUI();\n\t\t// wait for other events to process\n\t\tUiUtils.uiRun(() -> {\n\t\t\ttree.setSelectionRows(selection);\n\t\t\tSwingUtilities.updateComponentTreeUI(this);\n\t\t});\n\t}\n\n\tprivate void initUI() {\n\t\twrapGroupPanel = new JPanel(new BorderLayout(10, 10));\n\n\t\tgroups = new ArrayList<>();\n\t\tgroups.add(makeDecompilationGroup());\n\t\tgroups.add(makeDeobfuscationGroup());\n\t\tgroups.add(makeRenameGroup());\n\t\tgroups.add(new CacheSettingsGroup(this));\n\t\tgroups.add(makeAppearanceGroup());\n\t\tgroups.add(new ShortcutsSettingsGroup(this, settings));\n\t\tgroups.add(makeProjectGroup());\n\t\tgroups.add(new PluginSettings(mainWindow, settings).build());\n\t\tgroups.add(makeOtherGroup());\n\n\t\ttree = new SettingsTree(this);\n\t\ttree.init(groups);\n\t\ttree.setFocusable(true);\n\t\tJScrollPane leftPane = new JScrollPane(tree);\n\t\tleftPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 3, 3));\n\n\t\tJScrollPane rightPane = new JScrollPane(wrapGroupPanel);\n\t\trightPane.getVerticalScrollBar().setUnitIncrement(16);\n\t\trightPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);\n\t\trightPane.setBorder(BorderFactory.createEmptyBorder(10, 3, 3, 10));\n\n\t\tJSplitPane splitPane = new JSplitPane();\n\t\tsplitPane.setResizeWeight(0.2);\n\t\tsplitPane.setLeftComponent(leftPane);\n\t\tsplitPane.setRightComponent(rightPane);\n\n\t\tContainer contentPane = getContentPane();\n\t\tcontentPane.add(splitPane, BorderLayout.CENTER);\n\t\tcontentPane.add(buildButtonsPane(), BorderLayout.PAGE_END);\n\n\t\tKeyStroke strokeEsc = KeyStroke.getKeyStroke(\"ESCAPE\");\n\t\tInputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);\n\t\tinputMap.put(strokeEsc, \"ESCAPE\");\n\t\tgetRootPane().getActionMap().put(\"ESCAPE\", new ActionHandler(this::cancel));\n\t}\n\n\tprivate JPanel buildButtonsPane() {\n\t\tJButton saveBtn = new JButton(NLS.str(\"preferences.save\"));\n\t\tsaveBtn.addActionListener(event -> save());\n\n\t\tJButton cancelButton = new JButton(NLS.str(\"preferences.cancel\"));\n\t\tcancelButton.addActionListener(event -> cancel());\n\n\t\tJButton resetBtn = new JButton(NLS.str(\"preferences.reset\"));\n\t\tresetBtn.addActionListener(event -> reset());\n\n\t\tJButton copyBtn = new JButton(NLS.str(\"preferences.copy\"));\n\t\tcopyBtn.addActionListener(event -> copySettings());\n\n\t\tJPanel buttonPane = new JPanel();\n\t\tbuttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));\n\t\tbuttonPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));\n\t\tbuttonPane.add(resetBtn);\n\t\tbuttonPane.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tbuttonPane.add(copyBtn);\n\t\tbuttonPane.add(Box.createHorizontalGlue());\n\t\tbuttonPane.add(saveBtn);\n\t\tbuttonPane.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tbuttonPane.add(cancelButton);\n\n\t\tgetRootPane().setDefaultButton(saveBtn);\n\t\treturn buttonPane;\n\t}\n\n\t/**\n\t * Activate the settings page by location.\n\t *\n\t * @param location - can be title of a settings group or settings group class implementation (end\n\t *                 with .class)\n\t */\n\tpublic void activatePage(String location) {\n\t\tif (location.endsWith(\".class\")) {\n\t\t\tString clsName = StringUtils.removeSuffix(location, \".class\");\n\t\t\tfor (ISettingsGroup group : groups) {\n\t\t\t\tString groupCls = group.getClass().getSimpleName();\n\t\t\t\tif (groupCls.equals(clsName)) {\n\t\t\t\t\tselectGroup(group);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new JadxRuntimeException(\"No setting group class: \" + location);\n\t\t} else {\n\t\t\tfor (ISettingsGroup group : groups) {\n\t\t\t\tif (group.getTitle().equals(location)) {\n\t\t\t\t\tselectGroup(group);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new JadxRuntimeException(\"No setting group with title: \" + location);\n\t\t}\n\t}\n\n\tpublic void selectGroup(ISettingsGroup group) {\n\t\ttree.selectGroup(group);\n\t}\n\n\tpublic void activateGroup(@Nullable ISettingsGroup group) {\n\t\twrapGroupPanel.removeAll();\n\t\tif (group != null) {\n\t\t\twrapGroupPanel.add(group.buildComponent());\n\t\t}\n\t\twrapGroupPanel.updateUI();\n\t}\n\n\tprivate static void enableComponents(Container container, boolean enable) {\n\t\tfor (Component component : container.getComponents()) {\n\t\t\tif (component instanceof Container) {\n\t\t\t\tenableComponents((Container) component, enable);\n\t\t\t}\n\t\t\tcomponent.setEnabled(enable);\n\t\t}\n\t}\n\n\tprivate SettingsGroup makeDeobfuscationGroup() {\n\t\tJCheckBox deobfOn = new JCheckBox();\n\t\tdeobfOn.setSelected(settings.isDeobfuscationOn());\n\t\tdeobfOn.addItemListener(e -> {\n\t\t\tsettings.setDeobfuscationOn(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tSpinnerNumberModel minLenModel = new SpinnerNumberModel(settings.getDeobfuscationMinLength(), 0, Integer.MAX_VALUE, 1);\n\t\tJSpinner minLenSpinner = new JSpinner(minLenModel);\n\t\tminLenSpinner.addChangeListener(e -> {\n\t\t\tsettings.setDeobfuscationMinLength((Integer) minLenSpinner.getValue());\n\t\t\tneedReload();\n\t\t});\n\n\t\tSpinnerNumberModel maxLenModel = new SpinnerNumberModel(settings.getDeobfuscationMaxLength(), 0, Integer.MAX_VALUE, 1);\n\t\tJSpinner maxLenSpinner = new JSpinner(maxLenModel);\n\t\tmaxLenSpinner.addChangeListener(e -> {\n\t\t\tsettings.setDeobfuscationMaxLength((Integer) maxLenSpinner.getValue());\n\t\t\tneedReload();\n\t\t});\n\n\t\tJComboBox<ResourceNameSource> resNamesSource = new JComboBox<>(ResourceNameSource.values());\n\t\tresNamesSource.setSelectedItem(settings.getResourceNameSource());\n\t\tresNamesSource.addActionListener(e -> {\n\t\t\tsettings.setResourceNameSource((ResourceNameSource) resNamesSource.getSelectedItem());\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox useHeaders = new JCheckBox();\n\t\tuseHeaders.setSelected(settings.isUseHeadersForDetectResourceExtensions());\n\t\tuseHeaders.addItemListener(e -> {\n\t\t\tsettings.setUseHeadersForDetectResourceExtensions(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJComboBox<GeneratedRenamesMappingFileMode> generatedRenamesMappingFileModeCB =\n\t\t\t\tnew JComboBox<>(GeneratedRenamesMappingFileMode.values());\n\t\tgeneratedRenamesMappingFileModeCB.setSelectedItem(settings.getGeneratedRenamesMappingFileMode());\n\t\tgeneratedRenamesMappingFileModeCB.addActionListener(e -> {\n\t\t\tGeneratedRenamesMappingFileMode newValue =\n\t\t\t\t\t(GeneratedRenamesMappingFileMode) generatedRenamesMappingFileModeCB.getSelectedItem();\n\t\t\tif (newValue != settings.getGeneratedRenamesMappingFileMode()) {\n\t\t\t\tsettings.setGeneratedRenamesMappingFileMode(newValue);\n\t\t\t\tneedReload();\n\t\t\t}\n\t\t});\n\n\t\tJButton editWhitelistedEntities = new JButton(NLS.str(\"preferences.excludedPackages.button\"));\n\t\teditWhitelistedEntities.addActionListener(event -> {\n\t\t\tString prevWhitelistedEntities = settings.getDeobfuscationWhitelistStr();\n\t\t\tString result = JOptionPane.showInputDialog(this,\n\t\t\t\t\tNLS.str(\"preferences.deobfuscation_whitelist.editDialog\"),\n\t\t\t\t\tprevWhitelistedEntities);\n\t\t\tif (result != null) {\n\t\t\t\tsettings.setDeobfuscationWhitelistStr(result);\n\t\t\t\tif (!prevWhitelistedEntities.equals(result)) {\n\t\t\t\t\tneedReload();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tSettingsGroup deobfGroup = new SettingsGroup(NLS.str(\"preferences.deobfuscation\"));\n\t\tdeobfGroup.addRow(NLS.str(\"preferences.deobfuscation_on\"), deobfOn);\n\t\tdeobfGroup.addRow(NLS.str(\"preferences.deobfuscation_min_len\"), minLenSpinner);\n\t\tdeobfGroup.addRow(NLS.str(\"preferences.deobfuscation_max_len\"), maxLenSpinner);\n\t\tdeobfGroup.addRow(NLS.str(\"preferences.deobfuscation_res_name_source\"), resNamesSource);\n\t\tdeobfGroup.addRow(NLS.str(\"preferences.deobfuscation_res_use_headers\"), useHeaders);\n\t\tdeobfGroup.addRow(NLS.str(\"preferences.generated_renames_mapping_file_mode\"), generatedRenamesMappingFileModeCB);\n\t\tdeobfGroup.addRow(NLS.str(\"preferences.deobfuscation_whitelist\"),\n\t\t\t\tNLS.str(\"preferences.deobfuscation_whitelist.tooltip\"), editWhitelistedEntities);\n\n\t\tdeobfGroup.end();\n\n\t\tCollection<JComponent> connectedComponents = Arrays.asList(minLenSpinner, maxLenSpinner);\n\t\tdeobfOn.addItemListener(e -> enableComponentList(connectedComponents, e.getStateChange() == ItemEvent.SELECTED));\n\t\tenableComponentList(connectedComponents, settings.isDeobfuscationOn());\n\t\treturn deobfGroup;\n\t}\n\n\tprivate SettingsGroup makeRenameGroup() {\n\t\tJCheckBox renameCaseSensitive = new JCheckBox();\n\t\trenameCaseSensitive.setSelected(settings.isRenameCaseSensitive());\n\t\trenameCaseSensitive.addItemListener(e -> {\n\t\t\tsettings.updateRenameFlag(JadxArgs.RenameEnum.CASE, e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox renameValid = new JCheckBox();\n\t\trenameValid.setSelected(settings.isRenameValid());\n\t\trenameValid.addItemListener(e -> {\n\t\t\tsettings.updateRenameFlag(JadxArgs.RenameEnum.VALID, e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox renamePrintable = new JCheckBox();\n\t\trenamePrintable.setSelected(settings.isRenamePrintable());\n\t\trenamePrintable.addItemListener(e -> {\n\t\t\tsettings.updateRenameFlag(JadxArgs.RenameEnum.PRINTABLE, e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJComboBox<UseSourceNameAsClassNameAlias> useSourceNameAsClassNameAlias = new JComboBox<>(UseSourceNameAsClassNameAlias.values());\n\t\tuseSourceNameAsClassNameAlias.setSelectedItem(settings.getUseSourceNameAsClassNameAlias());\n\t\tuseSourceNameAsClassNameAlias.addActionListener(e -> {\n\t\t\tsettings.setUseSourceNameAsClassNameAlias((UseSourceNameAsClassNameAlias) useSourceNameAsClassNameAlias.getSelectedItem());\n\t\t\tneedReload();\n\t\t});\n\n\t\tJSpinner repeatLimit = new JSpinner(new SpinnerNumberModel(settings.getSourceNameRepeatLimit(), 1, Integer.MAX_VALUE, 1));\n\t\trepeatLimit.addChangeListener(e -> {\n\t\t\tsettings.setSourceNameRepeatLimit((Integer) repeatLimit.getValue());\n\t\t\tneedReload();\n\t\t});\n\n\t\tSettingsGroup group = new SettingsGroup(NLS.str(\"preferences.rename\"));\n\t\tgroup.addRow(NLS.str(\"preferences.rename_case\"), renameCaseSensitive);\n\t\tgroup.addRow(NLS.str(\"preferences.rename_valid\"), renameValid);\n\t\tgroup.addRow(NLS.str(\"preferences.rename_printable\"), renamePrintable);\n\t\tgroup.addRow(NLS.str(\"preferences.rename_use_source_name_as_class_name_alias\"), useSourceNameAsClassNameAlias);\n\t\tgroup.addRow(NLS.str(\"preferences.rename_source_name_repeat_limit\"), repeatLimit);\n\t\treturn group;\n\t}\n\n\tprivate void enableComponentList(Collection<JComponent> connectedComponents, boolean enabled) {\n\t\tconnectedComponents.forEach(comp -> comp.setEnabled(enabled));\n\t}\n\n\tprivate SettingsGroup makeProjectGroup() {\n\t\tJComboBox<SaveOptionEnum> dropdown = new JComboBox<>(SaveOptionEnum.values());\n\t\tdropdown.setSelectedItem(settings.getSaveOption());\n\t\tdropdown.addActionListener(e -> {\n\t\t\tsettings.setSaveOption((SaveOptionEnum) dropdown.getSelectedItem());\n\t\t\tneedReload();\n\t\t});\n\n\t\tSettingsGroup group = new SettingsGroup(NLS.str(\"preferences.project\"));\n\t\tgroup.addRow(NLS.str(\"preferences.saveOption\"), dropdown);\n\n\t\treturn group;\n\t}\n\n\tprivate SettingsGroup makeAppearanceGroup() {\n\t\tJComboBox<LangLocale> languageCbx = new JComboBox<>(NLS.getLangLocales());\n\t\tfor (LangLocale locale : NLS.getLangLocales()) {\n\t\t\tif (locale.equals(settings.getLangLocale())) {\n\t\t\t\tlanguageCbx.setSelectedItem(locale);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tlanguageCbx.addActionListener(e -> settings.setLangLocale((LangLocale) languageCbx.getSelectedItem()));\n\n\t\tEditorThemeManager editorThemeManager = mainWindow.getEditorThemeManager();\n\t\tJComboBox<ThemeIdAndName> themesCbx = new JComboBox<>(editorThemeManager.getThemeIdNameArray());\n\t\tthemesCbx.setSelectedItem(editorThemeManager.getCurrentThemeIdName());\n\t\tthemesCbx.addActionListener(evt -> {\n\t\t\tThemeIdAndName selected = (ThemeIdAndName) themesCbx.getSelectedItem();\n\t\t\tif (selected != null) {\n\t\t\t\tsettings.setEditorTheme(selected.getId());\n\t\t\t\tmainWindow.loadSettings();\n\t\t\t}\n\t\t});\n\n\t\tJComboBox<String> lafCbx = new JComboBox<>(LafManager.getThemes());\n\t\tlafCbx.setSelectedItem(settings.getLafTheme());\n\t\tlafCbx.addActionListener(e -> {\n\t\t\tsettings.setLafTheme((String) lafCbx.getSelectedItem());\n\t\t\tmainWindow.loadSettings();\n\t\t});\n\n\t\tJSpinner uiZoomSpinner = new JSpinner(new SpinnerNumberModel(settings.getUiZoom(), 0.1, 10.0, 0.25));\n\t\tuiZoomSpinner.addChangeListener(e -> {\n\t\t\tfloat zoomValue = ((Double) uiZoomSpinner.getValue()).floatValue();\n\t\t\tsettings.setUiZoom(zoomValue);\n\t\t\tmainWindow.loadSettings();\n\t\t});\n\n\t\tJCheckBox applyUiZoomToFontsChB = new JCheckBox();\n\t\tapplyUiZoomToFontsChB.setSelected(settings.isApplyUiZoomToFonts());\n\t\tapplyUiZoomToFontsChB.addItemListener(e -> {\n\t\t\tsettings.setApplyUiZoomToFonts(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tmainWindow.loadSettings();\n\t\t});\n\n\t\tSettingsGroup group = new SettingsGroup(NLS.str(\"preferences.appearance\"));\n\t\tgroup.addRow(NLS.str(\"preferences.language\"), languageCbx);\n\t\tgroup.addRow(NLS.str(\"preferences.ui_zoom\"), uiZoomSpinner);\n\t\tgroup.addRow(NLS.str(\"preferences.apply_ui_zoom_to_fonts\"), applyUiZoomToFontsChB);\n\n\t\tFontSettings fontSettings = settings.getFontSettings();\n\t\taddFontEditor(group, NLS.str(\"preferences.ui_font\"), fontSettings.getUiFontAdapter(), false);\n\t\taddFontEditor(group, NLS.str(\"preferences.code_font\"), fontSettings.getCodeFontAdapter(), false);\n\t\taddFontEditor(group, NLS.str(\"preferences.smali_font\"), fontSettings.getSmaliFontAdapter(), true);\n\n\t\tgroup.addRow(NLS.str(\"preferences.laf_theme\"), lafCbx);\n\t\tgroup.addRow(NLS.str(\"preferences.theme\"), themesCbx);\n\n\t\tJComboBox<TabDndGhostType> tabDndGhostTypeCbx = new JComboBox<>(TabDndGhostType.values());\n\t\ttabDndGhostTypeCbx.setSelectedItem(settings.getTabDndGhostType());\n\t\ttabDndGhostTypeCbx.addActionListener(e -> {\n\t\t\tsettings.setTabDndGhostType((TabDndGhostType) tabDndGhostTypeCbx.getSelectedItem());\n\t\t\tmainWindow.loadSettings();\n\t\t});\n\t\tgroup.addRow(NLS.str(\"preferences.tab_dnd_appearance\"), tabDndGhostTypeCbx);\n\n\t\treturn group;\n\t}\n\n\tprivate void addFontEditor(SettingsGroup group, String title, FontAdapter fontAdapter, boolean monospace) {\n\t\tJLabel fontLabel = new JLabel(getFontLabelStr(fontAdapter.getFont()));\n\t\tJButton fontBtn = new JButton(NLS.str(\"preferences.select_font\"));\n\t\tfontBtn.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(MouseEvent e) {\n\t\t\t\tFont font = new JadxFontDialog(JadxSettingsWindow.this, settings, title)\n\t\t\t\t\t\t.select(fontAdapter.getFont(), monospace);\n\t\t\t\tif (font != null) {\n\t\t\t\t\tfontLabel.setText(getFontLabelStr(font));\n\t\t\t\t\tfontAdapter.setFont(font);\n\t\t\t\t\tmainWindow.loadSettings();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tJPanel fontPanel = new JPanel();\n\t\tfontPanel.setLayout(new FlowLayout(FlowLayout.LEFT));\n\t\tfontPanel.add(fontLabel);\n\t\tfontPanel.add(fontBtn);\n\t\tgroup.addRow(title, fontPanel);\n\t}\n\n\tprivate static String getFontLabelStr(Font font) {\n\t\treturn font.getFamily()\n\t\t\t\t+ ' ' + FontUtils.convertFontStyleToString(font.getStyle())\n\t\t\t\t+ ' ' + font.getSize();\n\t}\n\n\tprivate SettingsGroup makeDecompilationGroup() {\n\t\tJCheckBox useDx = new JCheckBox();\n\t\tuseDx.setSelected(settings.isUseDx());\n\t\tuseDx.addItemListener(e -> {\n\t\t\tsettings.setUseDx(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJComboBox<DecompilationMode> decompilationModeComboBox = new JComboBox<>(DecompilationMode.values());\n\t\tdecompilationModeComboBox.setSelectedItem(settings.getDecompilationMode());\n\t\tdecompilationModeComboBox.addActionListener(e -> {\n\t\t\tsettings.setDecompilationMode((DecompilationMode) decompilationModeComboBox.getSelectedItem());\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox showInconsistentCode = new JCheckBox();\n\t\tshowInconsistentCode.setSelected(settings.isShowInconsistentCode());\n\t\tshowInconsistentCode.addItemListener(e -> {\n\t\t\tsettings.setShowInconsistentCode(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox resourceDecode = new JCheckBox();\n\t\tresourceDecode.setSelected(settings.isSkipResources());\n\t\tresourceDecode.addItemListener(e -> {\n\t\t\tsettings.setSkipResources(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\t// fix for #1331\n\t\tint threadsCountValue = settings.getThreadsCount();\n\t\tint threadsCountMax = Math.max(2, Math.max(threadsCountValue, Runtime.getRuntime().availableProcessors() * 2));\n\t\tSpinnerNumberModel spinnerModel = new SpinnerNumberModel(threadsCountValue, 1, threadsCountMax, 1);\n\t\tJSpinner threadsCount = new JSpinner(spinnerModel);\n\t\tthreadsCount.addChangeListener(e -> {\n\t\t\tsettings.setThreadsCount((Integer) threadsCount.getValue());\n\t\t\tneedReload();\n\t\t});\n\n\t\tJButton editExcludedPackages = new JButton(NLS.str(\"preferences.excludedPackages.button\"));\n\t\teditExcludedPackages.addActionListener(event -> {\n\n\t\t\tString oldExcludedPackages = settings.getExcludedPackages();\n\t\t\tString result = JOptionPane.showInputDialog(this, NLS.str(\"preferences.excludedPackages.editDialog\"),\n\t\t\t\t\tsettings.getExcludedPackages());\n\t\t\tif (result != null) {\n\t\t\t\tsettings.setExcludedPackages(result);\n\t\t\t\tif (!oldExcludedPackages.equals(result)) {\n\t\t\t\t\tneedReload();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tJCheckBox autoStartJobs = new JCheckBox();\n\t\tautoStartJobs.setSelected(settings.isAutoStartJobs());\n\t\tautoStartJobs.addItemListener(e -> settings.setAutoStartJobs(e.getStateChange() == ItemEvent.SELECTED));\n\n\t\tJCheckBox escapeUnicode = new JCheckBox();\n\t\tescapeUnicode.setSelected(settings.isEscapeUnicode());\n\t\tescapeUnicode.addItemListener(e -> {\n\t\t\tsettings.setEscapeUnicode(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox replaceConsts = new JCheckBox();\n\t\treplaceConsts.setSelected(settings.isReplaceConsts());\n\t\treplaceConsts.addItemListener(e -> {\n\t\t\tsettings.setReplaceConsts(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox respectBytecodeAccessModifiers = new JCheckBox();\n\t\trespectBytecodeAccessModifiers.setSelected(settings.isRespectBytecodeAccessModifiers());\n\t\trespectBytecodeAccessModifiers.addItemListener(e -> {\n\t\t\tsettings.setRespectBytecodeAccessModifiers(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox useImports = new JCheckBox();\n\t\tuseImports.setSelected(settings.isUseImports());\n\t\tuseImports.addItemListener(e -> {\n\t\t\tsettings.setUseImports(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox useDebugInfo = new JCheckBox();\n\t\tuseDebugInfo.setSelected(settings.isDebugInfo());\n\t\tuseDebugInfo.addItemListener(e -> {\n\t\t\tsettings.setDebugInfo(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox inlineAnonymous = new JCheckBox();\n\t\tinlineAnonymous.setSelected(settings.isInlineAnonymousClasses());\n\t\tinlineAnonymous.addItemListener(e -> {\n\t\t\tsettings.setInlineAnonymousClasses(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox inlineMethods = new JCheckBox();\n\t\tinlineMethods.setSelected(settings.isInlineMethods());\n\t\tinlineMethods.addItemListener(e -> {\n\t\t\tsettings.setInlineMethods(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox inlineKotlinLambdas = new JCheckBox();\n\t\tinlineKotlinLambdas.setSelected(settings.isAllowInlineKotlinLambda());\n\t\tinlineKotlinLambdas.addItemListener(e -> {\n\t\t\tsettings.setAllowInlineKotlinLambda(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox moveInnerClasses = new JCheckBox();\n\t\tmoveInnerClasses.setSelected(settings.isMoveInnerClasses());\n\t\tmoveInnerClasses.addItemListener(e -> {\n\t\t\tsettings.setMoveInnerClasses(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox extractFinally = new JCheckBox();\n\t\textractFinally.setSelected(settings.isExtractFinally());\n\t\textractFinally.addItemListener(e -> {\n\t\t\tsettings.setExtractFinally(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox restoreSwitchOverString = new JCheckBox();\n\t\trestoreSwitchOverString.setSelected(settings.isRestoreSwitchOverString());\n\t\trestoreSwitchOverString.addItemListener(e -> {\n\t\t\tsettings.setRestoreSwitchOverString(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox fsCaseSensitive = new JCheckBox();\n\t\tfsCaseSensitive.setSelected(settings.isFsCaseSensitive());\n\t\tfsCaseSensitive.addItemListener(e -> {\n\t\t\tsettings.setFsCaseSensitive(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJComboBox<UseKotlinMethodsForVarNames> kotlinRenameVars = new JComboBox<>(UseKotlinMethodsForVarNames.values());\n\t\tkotlinRenameVars.setSelectedItem(settings.getUseKotlinMethodsForVarNames());\n\t\tkotlinRenameVars.addActionListener(e -> {\n\t\t\tsettings.setUseKotlinMethodsForVarNames((UseKotlinMethodsForVarNames) kotlinRenameVars.getSelectedItem());\n\t\t\tneedReload();\n\t\t});\n\n\t\tJComboBox<CommentsLevel> commentsLevel = new JComboBox<>(CommentsLevel.values());\n\t\tcommentsLevel.setSelectedItem(settings.getCommentsLevel());\n\t\tcommentsLevel.addActionListener(e -> {\n\t\t\tsettings.setCommentsLevel((CommentsLevel) commentsLevel.getSelectedItem());\n\t\t\tneedReload();\n\t\t});\n\n\t\tJComboBox<IntegerFormat> integerFormat = new JComboBox<>(IntegerFormat.values());\n\t\tintegerFormat.setSelectedItem(settings.getIntegerFormat());\n\t\tintegerFormat.addActionListener(e -> {\n\t\t\tsettings.setIntegerFormat((IntegerFormat) integerFormat.getSelectedItem());\n\t\t\tneedReload();\n\t\t});\n\n\t\tJSpinner typeUpdatesLimitCount = new JSpinner(\n\t\t\t\tnew SpinnerNumberModel(settings.getTypeUpdatesLimitCount(), 1, Short.MAX_VALUE, 1));\n\t\ttypeUpdatesLimitCount.addChangeListener(e -> {\n\t\t\tint newValue = (Integer) typeUpdatesLimitCount.getValue();\n\t\t\tif (newValue < 1) {\n\t\t\t\tUiUtils.uiRun(() -> typeUpdatesLimitCount.setValue(1));\n\t\t\t} else {\n\t\t\t\tsettings.setTypeUpdatesLimitCount(newValue);\n\t\t\t\tneedReload();\n\t\t\t}\n\t\t});\n\n\t\tSettingsGroup other = new SettingsGroup(NLS.str(\"preferences.decompile\"));\n\t\tother.addRow(NLS.str(\"preferences.threads\"), threadsCount);\n\t\tother.addRow(NLS.str(\"preferences.excludedPackages\"),\n\t\t\t\tNLS.str(\"preferences.excludedPackages.tooltip\"), editExcludedPackages);\n\t\tother.addRow(NLS.str(\"preferences.start_jobs\"), autoStartJobs);\n\t\tother.addRow(NLS.str(\"preferences.decompilationMode\"), decompilationModeComboBox);\n\t\tother.addRow(NLS.str(\"preferences.showInconsistentCode\"), showInconsistentCode);\n\t\tother.addRow(NLS.str(\"preferences.escapeUnicode\"), escapeUnicode);\n\t\tother.addRow(NLS.str(\"preferences.replaceConsts\"), replaceConsts);\n\t\tother.addRow(NLS.str(\"preferences.respectBytecodeAccessModifiers\"), respectBytecodeAccessModifiers);\n\t\tother.addRow(NLS.str(\"preferences.useImports\"), useImports);\n\t\tother.addRow(NLS.str(\"preferences.useDebugInfo\"), useDebugInfo);\n\t\tother.addRow(NLS.str(\"preferences.inlineAnonymous\"), inlineAnonymous);\n\t\tother.addRow(NLS.str(\"preferences.inlineMethods\"), inlineMethods);\n\t\tother.addRow(NLS.str(\"preferences.inlineKotlinLambdas\"), inlineKotlinLambdas);\n\t\tother.addRow(NLS.str(\"preferences.moveInnerClasses\"), moveInnerClasses);\n\t\tother.addRow(NLS.str(\"preferences.extractFinally\"), extractFinally);\n\t\tother.addRow(NLS.str(\"preferences.restoreSwitchOverString\"), restoreSwitchOverString);\n\t\tother.addRow(NLS.str(\"preferences.fsCaseSensitive\"), fsCaseSensitive);\n\t\tother.addRow(NLS.str(\"preferences.useDx\"), useDx);\n\t\tother.addRow(NLS.str(\"preferences.skipResourcesDecode\"), resourceDecode);\n\t\tother.addRow(NLS.str(\"preferences.useKotlinMethodsForVarNames\"), kotlinRenameVars);\n\t\tother.addRow(NLS.str(\"preferences.commentsLevel\"), commentsLevel);\n\t\tother.addRow(NLS.str(\"preferences.integerFormat\"), integerFormat);\n\t\tother.addRow(NLS.str(\"preferences.typeUpdatesCountLimit\"), typeUpdatesLimitCount);\n\t\treturn other;\n\t}\n\n\tprivate SettingsGroup makeOtherGroup() {\n\t\tJComboBox<LineNumbersMode> lineNumbersMode = new JComboBox<>(LineNumbersMode.values());\n\t\tlineNumbersMode.setSelectedItem(settings.getLineNumbersMode());\n\t\tlineNumbersMode.addActionListener(e -> {\n\t\t\tsettings.setLineNumbersMode((LineNumbersMode) lineNumbersMode.getSelectedItem());\n\t\t\tmainWindow.loadSettings();\n\t\t});\n\n\t\tJCheckBox jumpOnDoubleClick = new JCheckBox();\n\t\tjumpOnDoubleClick.setSelected(settings.isJumpOnDoubleClick());\n\t\tjumpOnDoubleClick.addItemListener(e -> settings.setJumpOnDoubleClick(e.getStateChange() == ItemEvent.SELECTED));\n\n\t\tJSpinner resultsPerPage = new JSpinner(\n\t\t\t\tnew SpinnerNumberModel(settings.getSearchResultsPerPage(), 0, Integer.MAX_VALUE, 1));\n\t\tresultsPerPage.addChangeListener(ev -> settings.setSearchResultsPerPage((Integer) resultsPerPage.getValue()));\n\n\t\tJCheckBox useAltFileDialog = new JCheckBox();\n\t\tuseAltFileDialog.setSelected(settings.isUseAlternativeFileDialog());\n\t\tuseAltFileDialog.addItemListener(e -> settings.setUseAlternativeFileDialog(e.getStateChange() == ItemEvent.SELECTED));\n\n\t\tJCheckBox update = new JCheckBox();\n\t\tupdate.setSelected(settings.isCheckForUpdates());\n\t\tupdate.addItemListener(e -> settings.setCheckForUpdates(e.getStateChange() == ItemEvent.SELECTED));\n\n\t\tJCheckBox disableTooltipOnHover = new JCheckBox();\n\t\tdisableTooltipOnHover.setSelected(settings.isDisableTooltipOnHover());\n\t\tdisableTooltipOnHover.addItemListener(e -> settings.setDisableTooltipOnHover(e.getStateChange() == ItemEvent.SELECTED));\n\n\t\tJCheckBox cfg = new JCheckBox();\n\t\tcfg.setSelected(settings.isCfgOutput());\n\t\tcfg.addItemListener(e -> {\n\t\t\tsettings.setCfgOutput(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJCheckBox rawCfg = new JCheckBox();\n\t\trawCfg.setSelected(settings.isRawCfgOutput());\n\t\trawCfg.addItemListener(e -> {\n\t\t\tsettings.setRawCfgOutput(e.getStateChange() == ItemEvent.SELECTED);\n\t\t\tneedReload();\n\t\t});\n\n\t\tJComboBox<XposedCodegenLanguage> xposedCodegenLanguage = new JComboBox<>(XposedCodegenLanguage.values());\n\t\txposedCodegenLanguage.setSelectedItem(settings.getXposedCodegenLanguage());\n\t\txposedCodegenLanguage.addActionListener(e -> {\n\t\t\tsettings.setXposedCodegenLanguage((XposedCodegenLanguage) xposedCodegenLanguage.getSelectedItem());\n\t\t\tmainWindow.loadSettings();\n\t\t});\n\n\t\tJComboBox<JadxUpdateChannel> updateChannel = new JComboBox<>(JadxUpdateChannel.values());\n\t\tupdateChannel.setSelectedItem(settings.getJadxUpdateChannel());\n\t\tupdateChannel.addActionListener(e -> {\n\t\t\tsettings.setJadxUpdateChannel((JadxUpdateChannel) updateChannel.getSelectedItem());\n\t\t\tmainWindow.loadSettings();\n\t\t});\n\n\t\tSettingsGroup group = new SettingsGroup(NLS.str(\"preferences.other\"));\n\t\tgroup.addRow(NLS.str(\"preferences.lineNumbersMode\"), lineNumbersMode);\n\t\tgroup.addRow(NLS.str(\"preferences.jumpOnDoubleClick\"), jumpOnDoubleClick);\n\t\tgroup.addRow(NLS.str(\"preferences.disable_tooltip_on_hover\"), disableTooltipOnHover);\n\t\tgroup.addRow(NLS.str(\"preferences.search_results_per_page\"), resultsPerPage);\n\t\tgroup.addRow(NLS.str(\"preferences.useAlternativeFileDialog\"), useAltFileDialog);\n\t\tgroup.addRow(NLS.str(\"preferences.cfg\"), cfg);\n\t\tgroup.addRow(NLS.str(\"preferences.raw_cfg\"), rawCfg);\n\t\tgroup.addRow(NLS.str(\"preferences.xposed_codegen_language\"), xposedCodegenLanguage);\n\t\tgroup.addRow(NLS.str(\"preferences.check_for_updates\"), update);\n\t\tgroup.addRow(NLS.str(\"preferences.update_channel\"), updateChannel);\n\t\treturn group;\n\t}\n\n\tprivate void closeGroups(boolean save) {\n\t\tfor (ISettingsGroup group : groups) {\n\t\t\tgroup.close(save);\n\t\t}\n\t}\n\n\tprivate void save() {\n\t\tcloseGroups(true);\n\t\tsettings.sync();\n\t\tenableComponents(this, false);\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\tif (shouldReload()) {\n\t\t\t\tmainWindow.getShortcutsController().loadSettings();\n\t\t\t\tmainWindow.reopen();\n\t\t\t}\n\t\t\tif (!settings.getLangLocale().equals(prevLang)) {\n\t\t\t\tJOptionPane.showMessageDialog(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\tNLS.str(\"msg.language_changed\", settings.getLangLocale()),\n\t\t\t\t\t\tNLS.str(\"msg.language_changed_title\", settings.getLangLocale()),\n\t\t\t\t\t\tJOptionPane.INFORMATION_MESSAGE);\n\t\t\t}\n\t\t\tdispose();\n\t\t});\n\t}\n\n\tprivate void cancel() {\n\t\tcloseGroups(false);\n\t\tsettings.loadSettingsFromJsonString(startSettings);\n\t\tmainWindow.loadSettings();\n\t\tdispose();\n\t}\n\n\tprivate void reset() {\n\t\tint res = JOptionPane.showConfirmDialog(\n\t\t\t\tJadxSettingsWindow.this,\n\t\t\t\tNLS.str(\"preferences.reset_message\"),\n\t\t\t\tNLS.str(\"preferences.reset_title\"),\n\t\t\t\tJOptionPane.YES_NO_OPTION);\n\t\tif (res == JOptionPane.YES_OPTION) {\n\t\t\tsettings.loadSettingsData(new JadxSettingsData());\n\t\t\tmainWindow.loadSettings();\n\t\t\tneedReload();\n\t\t\tgetContentPane().removeAll();\n\t\t\tinitUI();\n\t\t\tpack();\n\t\t\trepaint();\n\t\t}\n\t}\n\n\tprivate void copySettings() {\n\t\tString settingsText = settings.exportSettingsString();\n\t\tClipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();\n\t\tStringSelection selection = new StringSelection(settingsText);\n\t\tclipboard.setContents(selection, selection);\n\t\tJOptionPane.showMessageDialog(\n\t\t\t\tJadxSettingsWindow.this,\n\t\t\t\tNLS.str(\"preferences.copy_message\"));\n\t}\n\n\tpublic void needReload() {\n\t\tneedReload = true;\n\t}\n\n\tprivate boolean shouldReload() {\n\t\treturn needReload || !startSettingsHash.equals(calcSettingsHash());\n\t}\n\n\t@SuppressWarnings(\"resource\")\n\tprivate String calcSettingsHash() {\n\t\tJadxDecompiler decompiler = mainWindow.getWrapper().getCurrentDecompiler().orElse(null);\n\t\treturn settings.toJadxArgs().makeCodeArgsHash(decompiler);\n\t}\n\n\tpublic MainWindow getMainWindow() {\n\t\treturn mainWindow;\n\t}\n\n\t@Override\n\tpublic void dispose() {\n\t\tmainWindow.events().global().removeListener(JadxEvents.RELOAD_SETTINGS_WINDOW, reloadListener);\n\t\tsettings.saveWindowPos(this);\n\t\tsuper.dispose();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/SettingsGroup.java",
    "content": "package jadx.gui.settings.ui;\n\nimport java.awt.BorderLayout;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.Insets;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.JComponent;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.SwingConstants;\n\nimport jadx.api.plugins.gui.ISettingsGroup;\n\npublic class SettingsGroup implements ISettingsGroup {\n\tprivate final String title;\n\tprivate final JPanel panel;\n\tprivate final JPanel gridPanel;\n\tprivate final GridBagConstraints c;\n\tprivate int row;\n\n\tpublic SettingsGroup(String title) {\n\t\tthis.title = title;\n\t\tgridPanel = new JPanel(new GridBagLayout());\n\t\tc = new GridBagConstraints();\n\t\tc.insets = new Insets(5, 5, 5, 5);\n\t\tc.weighty = 1.0;\n\n\t\tpanel = new JPanel();\n\t\tpanel.setLayout(new BorderLayout(5, 5));\n\t\tpanel.setBorder(BorderFactory.createTitledBorder(title));\n\t\tpanel.add(gridPanel, BorderLayout.PAGE_START);\n\t}\n\n\tpublic JLabel addRow(String label, JComponent comp) {\n\t\treturn addRow(label, null, comp);\n\t}\n\n\tpublic JLabel addRow(String label, String tooltip, JComponent comp) {\n\t\tJLabel rowLbl = new JLabel(label);\n\t\trowLbl.setLabelFor(comp);\n\t\trowLbl.setHorizontalAlignment(SwingConstants.LEFT);\n\t\tif (tooltip != null) {\n\t\t\trowLbl.setToolTipText(tooltip);\n\t\t\tcomp.setToolTipText(tooltip);\n\t\t} else {\n\t\t\tcomp.setToolTipText(label);\n\t\t}\n\t\tcomp.getAccessibleContext().setAccessibleName(label);\n\n\t\tc.gridy = row++;\n\t\tc.gridx = 0;\n\t\tc.gridwidth = 1;\n\t\tc.anchor = GridBagConstraints.LINE_START;\n\t\tc.weightx = 0.1;\n\t\tc.fill = GridBagConstraints.LINE_START;\n\t\tgridPanel.add(rowLbl, c);\n\t\tc.gridx = 1;\n\t\tc.gridwidth = GridBagConstraints.REMAINDER;\n\t\tc.anchor = GridBagConstraints.LINE_START;\n\t\tc.weightx = 0.7;\n\t\tc.fill = GridBagConstraints.LINE_START;\n\n\t\tgridPanel.add(comp, c);\n\t\tcomp.addPropertyChangeListener(\"enabled\", evt -> rowLbl.setEnabled((boolean) evt.getNewValue()));\n\t\treturn rowLbl;\n\t}\n\n\tpublic void end() {\n\t\tgridPanel.add(Box.createVerticalGlue());\n\t}\n\n\t@Override\n\tpublic JComponent buildComponent() {\n\t\treturn panel;\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn title;\n\t}\n\n\tpublic JPanel getGridPanel() {\n\t\treturn gridPanel;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn title;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/SettingsTree.java",
    "content": "package jadx.gui.settings.ui;\n\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.Objects;\n\nimport javax.swing.JTree;\nimport javax.swing.event.TreeExpansionEvent;\nimport javax.swing.event.TreeWillExpandListener;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.DefaultTreeModel;\nimport javax.swing.tree.ExpandVetoException;\nimport javax.swing.tree.TreeNode;\nimport javax.swing.tree.TreePath;\nimport javax.swing.tree.TreeSelectionModel;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.gui.ISettingsGroup;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.utils.NLS;\n\npublic class SettingsTree extends JTree {\n\tprivate final JadxSettingsWindow settingsWindow;\n\n\tpublic SettingsTree(JadxSettingsWindow settingsWindow) {\n\t\tthis.settingsWindow = settingsWindow;\n\t}\n\n\tpublic void init(List<ISettingsGroup> groups) {\n\t\tDefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode(NLS.str(\"preferences.title\"));\n\t\taddGroups(treeRoot, groups);\n\t\tsetModel(new DefaultTreeModel(treeRoot));\n\t\tgetSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);\n\t\tsetFocusable(false);\n\t\taddTreeSelectionListener(ev -> switchGroup());\n\t\t// expand all nodes and disallow collapsing\n\t\tsetNodeExpandedState(this, treeRoot, true);\n\t\taddTreeWillExpandListener(new DisableRootCollapseListener(treeRoot));\n\t\taddSelectionRow(1);\n\t}\n\n\tprivate static void addGroups(DefaultMutableTreeNode base, List<ISettingsGroup> groups) {\n\t\tfor (ISettingsGroup group : groups) {\n\t\t\tSettingsTreeNode node = new SettingsTreeNode(group);\n\t\t\tbase.add(node);\n\t\t\taddGroups(node, group.getSubGroups());\n\t\t}\n\t}\n\n\tpublic void selectGroup(ISettingsGroup group) {\n\t\tSettingsTreeNode node = searchTreeNode(group);\n\t\tif (node == null) {\n\t\t\tthrow new JadxRuntimeException(\"Settings group not found: \" + group);\n\t\t}\n\t\tsetSelectionPath(new TreePath(node.getPath()));\n\t}\n\n\tprivate @Nullable SettingsTreeNode searchTreeNode(ISettingsGroup group) {\n\t\tDefaultMutableTreeNode root = (DefaultMutableTreeNode) getModel().getRoot();\n\t\tEnumeration<TreeNode> enumeration = root.children();\n\t\twhile (enumeration.hasMoreElements()) {\n\t\t\tSettingsTreeNode node = (SettingsTreeNode) enumeration.nextElement();\n\t\t\tif (node.getGroup() == group) {\n\t\t\t\treturn node;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate void switchGroup() {\n\t\tObject selected = getLastSelectedPathComponent();\n\t\tif (selected instanceof SettingsTreeNode) {\n\t\t\tISettingsGroup group = ((SettingsTreeNode) selected).getGroup();\n\t\t\tsettingsWindow.activateGroup(group);\n\t\t} else {\n\t\t\tsettingsWindow.activateGroup(null);\n\t\t}\n\t}\n\n\tprivate static void setNodeExpandedState(JTree tree, TreeNode node, boolean expanded) {\n\t\tList<? extends TreeNode> list = Collections.list(node.children());\n\t\tfor (TreeNode treeNode : list) {\n\t\t\tsetNodeExpandedState(tree, treeNode, expanded);\n\t\t}\n\t\tDefaultMutableTreeNode mutableTreeNode = (DefaultMutableTreeNode) node;\n\t\tif (!expanded && mutableTreeNode.isRoot()) {\n\t\t\treturn;\n\t\t}\n\t\tTreePath path = new TreePath(mutableTreeNode.getPath());\n\t\tif (expanded) {\n\t\t\ttree.expandPath(path);\n\t\t} else {\n\t\t\ttree.collapsePath(path);\n\t\t}\n\t}\n\n\tprivate static class DisableRootCollapseListener implements TreeWillExpandListener {\n\t\tprivate final DefaultMutableTreeNode treeRoot;\n\n\t\tpublic DisableRootCollapseListener(DefaultMutableTreeNode treeRoot) {\n\t\t\tthis.treeRoot = treeRoot;\n\t\t}\n\n\t\t@Override\n\t\tpublic void treeWillExpand(TreeExpansionEvent event) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {\n\t\t\tObject current = event.getPath().getLastPathComponent();\n\t\t\tif (Objects.equals(current, treeRoot)) {\n\t\t\t\tthrow new ExpandVetoException(event, \"Root collapsing not allowed\");\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/SettingsTreeNode.java",
    "content": "package jadx.gui.settings.ui;\n\nimport javax.swing.tree.DefaultMutableTreeNode;\n\nimport jadx.api.plugins.gui.ISettingsGroup;\n\npublic class SettingsTreeNode extends DefaultMutableTreeNode {\n\tprivate final ISettingsGroup group;\n\n\tpublic SettingsTreeNode(ISettingsGroup group) {\n\t\tthis.group = group;\n\t}\n\n\tpublic ISettingsGroup getGroup() {\n\t\treturn group;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn group.getTitle();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/SubSettingsGroup.java",
    "content": "package jadx.gui.settings.ui;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.api.plugins.gui.ISettingsGroup;\n\npublic class SubSettingsGroup extends SettingsGroup {\n\n\tprivate final List<ISettingsGroup> groups = new ArrayList<>();\n\n\tpublic SubSettingsGroup(String title) {\n\t\tsuper(title);\n\t}\n\n\t@Override\n\tpublic List<ISettingsGroup> getSubGroups() {\n\t\treturn groups;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/cache/CacheSettingsGroup.java",
    "content": "package jadx.gui.settings.ui.cache;\n\nimport java.awt.BorderLayout;\nimport java.awt.GridLayout;\nimport java.nio.file.Path;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.ButtonGroup;\nimport javax.swing.JButton;\nimport javax.swing.JComboBox;\nimport javax.swing.JComponent;\nimport javax.swing.JFileChooser;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JRadioButton;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTextField;\nimport javax.swing.UIManager;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.gui.ISettingsGroup;\nimport jadx.gui.cache.code.CodeCacheMode;\nimport jadx.gui.cache.usage.UsageCacheMode;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.settings.ui.JadxSettingsWindow;\nimport jadx.gui.settings.ui.SettingsGroup;\nimport jadx.gui.ui.filedialog.FileDialogWrapper;\nimport jadx.gui.ui.filedialog.FileOpenMode;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.files.JadxFiles;\nimport jadx.gui.utils.ui.DocumentUpdateListener;\n\npublic class CacheSettingsGroup implements ISettingsGroup {\n\n\tprivate final String title = NLS.str(\"preferences.cache\");\n\tprivate final JadxSettingsWindow settingsWindow;\n\n\tprivate JTextField customDirField;\n\tprivate JButton selectDirBtn;\n\n\tpublic CacheSettingsGroup(JadxSettingsWindow settingsWindow) {\n\t\tthis.settingsWindow = settingsWindow;\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn title;\n\t}\n\n\t@Override\n\tpublic JComponent buildComponent() {\n\t\tJPanel options = new JPanel();\n\t\toptions.setLayout(new BoxLayout(options, BoxLayout.PAGE_AXIS));\n\t\toptions.add(buildBaseOptions());\n\t\toptions.add(buildLocationSelector());\n\n\t\tJPanel mainPanel = new JPanel();\n\t\tmainPanel.setLayout(new BorderLayout());\n\t\tmainPanel.add(options, BorderLayout.PAGE_START);\n\t\tmainPanel.add(buildCachesView(), BorderLayout.CENTER);\n\t\treturn mainPanel;\n\t}\n\n\tprivate JPanel buildCachesView() {\n\t\tCachesTable cachesTable = new CachesTable(settingsWindow.getMainWindow());\n\t\tJScrollPane scrollPane = new JScrollPane(cachesTable);\n\t\tcachesTable.setFillsViewportHeight(true);\n\t\tcachesTable.updateData();\n\n\t\tJButton calcUsage = new JButton(NLS.str(\"preferences.cache.btn.usage\"));\n\t\tcalcUsage.addActionListener(ev -> cachesTable.updateSizes());\n\n\t\tJButton deleteSelected = new JButton(NLS.str(\"preferences.cache.btn.delete_selected\"));\n\t\tdeleteSelected.addActionListener(ev -> cachesTable.deleteSelected());\n\n\t\tJButton deleteAll = new JButton(NLS.str(\"preferences.cache.btn.delete_all\"));\n\t\tdeleteAll.addActionListener(ev -> cachesTable.deleteAll());\n\n\t\tJPanel buttons = new JPanel();\n\t\tbuttons.setLayout(new BoxLayout(buttons, BoxLayout.LINE_AXIS));\n\t\tbuttons.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));\n\t\tbuttons.add(calcUsage);\n\t\tbuttons.add(Box.createHorizontalGlue());\n\t\tbuttons.add(deleteSelected);\n\t\tbuttons.add(deleteAll);\n\n\t\tJPanel panel = new JPanel();\n\t\tpanel.setLayout(new BorderLayout());\n\t\tpanel.setBorder(BorderFactory.createTitledBorder(NLS.str(\"preferences.cache.table.title\")));\n\t\tpanel.add(scrollPane, BorderLayout.CENTER);\n\t\tpanel.add(buttons, BorderLayout.PAGE_END);\n\t\treturn panel;\n\t}\n\n\tprivate JComponent buildLocationSelector() {\n\t\tJPanel panel = new JPanel();\n\t\tpanel.setLayout(new GridLayout(0, 1));\n\t\tpanel.setBorder(BorderFactory.createCompoundBorder(\n\t\t\t\tBorderFactory.createTitledBorder(NLS.str(\"preferences.cache.location\")),\n\t\t\t\tBorderFactory.createEmptyBorder(10, 10, 10, 10)));\n\n\t\tcustomDirField = new JTextField();\n\t\tcustomDirField.setColumns(10);\n\t\tcustomDirField.getDocument().addDocumentListener(new DocumentUpdateListener(ev -> {\n\t\t\tsettingsWindow.getMainWindow().getSettings().setCacheDir(customDirField.getText());\n\t\t}));\n\n\t\tselectDirBtn = new JButton();\n\t\tselectDirBtn.setIcon(UIManager.getIcon(\"Tree.closedIcon\"));\n\t\tselectDirBtn.addActionListener(e -> {\n\t\t\tFileDialogWrapper fd = new FileDialogWrapper(settingsWindow.getMainWindow(), FileOpenMode.CUSTOM_OPEN);\n\t\t\tfd.setFileExtList(Collections.emptyList());\n\t\t\tfd.setSelectionMode(JFileChooser.DIRECTORIES_ONLY);\n\t\t\tList<Path> paths = fd.show();\n\t\t\tif (!paths.isEmpty()) {\n\t\t\t\tString dir = paths.get(0).toAbsolutePath().toString();\n\t\t\t\tcustomDirField.setText(dir);\n\t\t\t\tsettingsWindow.getMainWindow().getSettings().setCacheDir(dir);\n\t\t\t}\n\t\t});\n\n\t\tJRadioButton defOpt = new JRadioButton(NLS.str(\"preferences.cache.location_default\"));\n\t\tdefOpt.setToolTipText(JadxFiles.CACHE_DIR.toString());\n\t\tdefOpt.addActionListener(e -> changeCacheLocation(null));\n\t\tJRadioButton localOpt = new JRadioButton(NLS.str(\"preferences.cache.location_local\"));\n\t\tlocalOpt.addActionListener(e -> changeCacheLocation(\".\"));\n\t\tJRadioButton customOpt = new JRadioButton(NLS.str(\"preferences.cache.location_custom\"));\n\t\tcustomOpt.addActionListener(e -> changeCacheLocation(\"\"));\n\n\t\tButtonGroup group = new ButtonGroup();\n\t\tgroup.add(defOpt);\n\t\tgroup.add(localOpt);\n\t\tgroup.add(customOpt);\n\n\t\tpanel.add(defOpt);\n\t\tpanel.add(localOpt);\n\n\t\tJPanel custom = new JPanel();\n\t\tcustom.setLayout(new BoxLayout(custom, BoxLayout.LINE_AXIS));\n\t\tcustom.add(customOpt);\n\t\tcustom.add(Box.createHorizontalStrut(15));\n\t\tcustom.add(customDirField);\n\t\tcustom.add(selectDirBtn);\n\t\tpanel.add(custom);\n\n\t\tString cacheDir = settingsWindow.getMainWindow().getSettings().getCacheDir();\n\t\tif (cacheDir == null) {\n\t\t\tdefOpt.setSelected(true);\n\t\t\tchangeCacheLocation(null);\n\t\t} else if (cacheDir.equals(\".\")) {\n\t\t\tlocalOpt.setSelected(true);\n\t\t\tchangeCacheLocation(cacheDir);\n\t\t} else {\n\t\t\tcustomOpt.setSelected(true);\n\t\t\tcustomDirField.setText(cacheDir);\n\t\t\tchangeCacheLocation(\"\");\n\t\t}\n\t\tJLabel notice = new JLabel(NLS.str(\"preferences.cache.change_notice\"));\n\t\tnotice.setEnabled(false);\n\t\tpanel.add(notice);\n\t\treturn panel;\n\t}\n\n\tprivate void changeCacheLocation(@Nullable String locValue) {\n\t\tboolean custom = Objects.equals(locValue, \"\");\n\t\tcustomDirField.setEnabled(custom);\n\t\tselectDirBtn.setEnabled(custom);\n\t\tif (!custom) {\n\t\t\tsettingsWindow.getMainWindow().getSettings().setCacheDir(locValue);\n\t\t}\n\t}\n\n\tprivate JComponent buildBaseOptions() {\n\t\tJadxSettings settings = settingsWindow.getMainWindow().getSettings();\n\n\t\tJComboBox<CodeCacheMode> codeCacheModeComboBox = new JComboBox<>(CodeCacheMode.values());\n\t\tcodeCacheModeComboBox.setSelectedItem(settings.getCodeCacheMode());\n\t\tcodeCacheModeComboBox.addActionListener(e -> {\n\t\t\tsettings.setCodeCacheMode((CodeCacheMode) codeCacheModeComboBox.getSelectedItem());\n\t\t\tsettingsWindow.needReload();\n\t\t});\n\n\t\tJComboBox<UsageCacheMode> usageCacheModeComboBox = new JComboBox<>(UsageCacheMode.values());\n\t\tusageCacheModeComboBox.setSelectedItem(settings.getUsageCacheMode());\n\t\tusageCacheModeComboBox.addActionListener(e -> {\n\t\t\tsettings.setUsageCacheMode((UsageCacheMode) usageCacheModeComboBox.getSelectedItem());\n\t\t\tsettingsWindow.needReload();\n\t\t});\n\n\t\tSettingsGroup group = new SettingsGroup(title);\n\t\tgroup.addRow(NLS.str(\"preferences.codeCacheMode\"), CodeCacheMode.buildToolTip(), codeCacheModeComboBox);\n\t\tgroup.addRow(NLS.str(\"preferences.usageCacheMode\"), usageCacheModeComboBox);\n\t\treturn group.buildComponent();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/cache/CachesTable.java",
    "content": "package jadx.gui.settings.ui.cache;\n\nimport java.awt.Dimension;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport javax.swing.JTable;\nimport javax.swing.ListSelectionModel;\n\nimport org.apache.commons.io.FileUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.events.types.ReloadProject;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.Utils;\nimport jadx.gui.cache.manager.CacheManager;\nimport jadx.gui.settings.JadxProject;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.ui.MousePressedHandler;\n\npublic class CachesTable extends JTable {\n\tprivate static final long serialVersionUID = 5984107298264276049L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CachesTable.class);\n\n\tprivate final MainWindow mainWindow;\n\tprivate final CachesTableModel dataModel;\n\n\tpublic CachesTable(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.dataModel = new CachesTableModel();\n\t\tsetModel(dataModel);\n\t\tsetDefaultRenderer(Object.class, new CachesTableRenderer());\n\n\t\tsetSelectionMode(ListSelectionModel.SINGLE_SELECTION);\n\t\tsetAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);\n\t\tsetShowHorizontalLines(true);\n\t\tsetDragEnabled(false);\n\t\tsetColumnSelectionAllowed(false);\n\t\tsetAutoscrolls(true);\n\t\tsetFocusable(false);\n\n\t\taddMouseListener(new MousePressedHandler(ev -> {\n\t\t\tint row = rowAtPoint(ev.getPoint());\n\t\t\tif (row != -1) {\n\t\t\t\tdataModel.changeSelection(row);\n\t\t\t\tUiUtils.uiRun(this::updateUI);\n\t\t\t}\n\t\t}));\n\t}\n\n\tpublic void updateData() {\n\t\tList<TableRow> rows = mainWindow.getCacheManager().getCachesList().stream()\n\t\t\t\t.map(TableRow::new)\n\t\t\t\t.collect(Collectors.toList());\n\t\tupdateRows(rows);\n\t}\n\n\tpublic void reloadData() {\n\t\tMap<String, String> prevUsageMap = dataModel.getRows().stream()\n\t\t\t\t.collect(Collectors.toMap(TableRow::getProject, TableRow::getUsage));\n\n\t\tList<TableRow> rows = mainWindow.getCacheManager().getCachesList().stream()\n\t\t\t\t.map(TableRow::new)\n\t\t\t\t.peek(r -> r.setUsage(Utils.getOrElse(prevUsageMap.get(r.getProject()), \"-\")))\n\t\t\t\t.collect(Collectors.toList());\n\t\tupdateRows(rows);\n\t}\n\n\tprivate void updateRows(List<TableRow> rows) {\n\t\tdataModel.setRows(rows);\n\n\t\t// fix allocated space for default 20 rows\n\t\tint width = getPreferredSize().width;\n\t\tint height = rows.size() * getRowHeight();\n\t\tsetPreferredScrollableViewportSize(new Dimension(width, height));\n\n\t\tUiUtils.uiRun(this::updateUI);\n\t}\n\n\tpublic void updateSizes() {\n\t\tList<Runnable> list = dataModel.getRows().stream()\n\t\t\t\t.map(row -> (Runnable) () -> calcSize(row))\n\t\t\t\t.collect(Collectors.toList());\n\t\tmainWindow.getBackgroundExecutor().execute(\n\t\t\t\tNLS.str(\"preferences.cache.task.usage\"),\n\t\t\t\tlist,\n\t\t\t\tstatus -> updateUI());\n\t}\n\n\tprivate void calcSize(TableRow row) {\n\t\tString cacheDir = row.getCacheEntry().getCache();\n\t\ttry {\n\t\t\tPath dir = Paths.get(cacheDir);\n\t\t\tif (Files.isDirectory(dir)) {\n\t\t\t\tlong size = calcSizeOfDirectory(dir);\n\t\t\t\trow.setUsage(FileUtils.byteCountToDisplaySize(size));\n\t\t\t} else {\n\t\t\t\trow.setUsage(\"not found\");\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to calculate size of directory: {}\", cacheDir, e);\n\t\t\trow.setUsage(\"error\");\n\t\t}\n\t}\n\n\tprivate static long calcSizeOfDirectory(Path dir) {\n\t\ttry (Stream<Path> stream = Files.walk(dir)) {\n\t\t\tlong blockSize = Files.getFileStore(dir).getBlockSize();\n\t\t\treturn stream.mapToLong(p -> {\n\t\t\t\tif (Files.isRegularFile(p)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlong fileSize = Files.size(p);\n\t\t\t\t\t\t// ceil round to blockSize\n\t\t\t\t\t\treturn (fileSize / blockSize + 1L) * blockSize;\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLOG.error(\"Failed to get file size: {}\", p, e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t}).sum();\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to calculate directory size: {}\", dir, e);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tpublic void deleteSelected() {\n\t\tdelete(ListUtils.filter(dataModel.getRows(), TableRow::isSelected));\n\t}\n\n\tpublic void deleteAll() {\n\t\tdelete(dataModel.getRows());\n\t}\n\n\tprivate void delete(List<TableRow> rows) {\n\t\t// force reload if cache for current project is deleted\n\t\tboolean reload = searchCurrentProject(rows);\n\n\t\tList<Runnable> list = rows.stream()\n\t\t\t\t.map(TableRow::getCacheEntry)\n\t\t\t\t.map(entry -> (Runnable) () -> mainWindow.getCacheManager().removeCacheEntry(entry))\n\t\t\t\t.collect(Collectors.toList());\n\t\tmainWindow.getBackgroundExecutor().execute(\n\t\t\t\tNLS.str(\"preferences.cache.task.delete\"),\n\t\t\t\tlist,\n\t\t\t\tstatus -> {\n\t\t\t\t\treloadData();\n\t\t\t\t\tif (reload) {\n\t\t\t\t\t\tmainWindow.events().send(ReloadProject.EVENT);\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\tprivate boolean searchCurrentProject(List<TableRow> rows) {\n\t\tJadxProject project = mainWindow.getProject();\n\t\tif (!project.getFilePaths().isEmpty()) {\n\t\t\tString cacheStr = CacheManager.pathToString(project.getCacheDir());\n\t\t\tfor (TableRow row : rows) {\n\t\t\t\tif (row.getCacheEntry().getCache().equals(cacheStr)) {\n\t\t\t\t\tproject.resetCacheDir();\n\t\t\t\t\tLOG.debug(\"Found current project in cache delete list -> request full reload\");\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/cache/CachesTableModel.java",
    "content": "package jadx.gui.settings.ui.cache;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.swing.table.AbstractTableModel;\n\nimport jadx.gui.utils.NLS;\n\npublic class CachesTableModel extends AbstractTableModel {\n\tprivate static final long serialVersionUID = -7725573085995496397L;\n\n\tprivate static final String[] COLUMN_NAMES = {\n\t\t\tNLS.str(\"preferences.cache.table.project\"),\n\t\t\tNLS.str(\"preferences.cache.table.size\")\n\t};\n\n\tprivate transient List<TableRow> rows = Collections.emptyList();\n\n\tpublic void setRows(List<TableRow> list) {\n\t\tthis.rows = list;\n\t}\n\n\tpublic List<TableRow> getRows() {\n\t\treturn rows;\n\t}\n\n\t@Override\n\tpublic int getRowCount() {\n\t\treturn rows.size();\n\t}\n\n\t@Override\n\tpublic int getColumnCount() {\n\t\treturn 2;\n\t}\n\n\t@Override\n\tpublic String getColumnName(int index) {\n\t\treturn COLUMN_NAMES[index];\n\t}\n\n\t@Override\n\tpublic Class<?> getColumnClass(int columnIndex) {\n\t\treturn TableRow.class;\n\t}\n\n\t@Override\n\tpublic TableRow getValueAt(int rowIndex, int columnIndex) {\n\t\treturn rows.get(rowIndex);\n\t}\n\n\tpublic void changeSelection(int idx) {\n\t\tTableRow row = rows.get(idx);\n\t\trow.setSelected(!row.isSelected());\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/cache/CachesTableRenderer.java",
    "content": "package jadx.gui.settings.ui.cache;\n\nimport java.awt.Component;\n\nimport javax.swing.JLabel;\nimport javax.swing.JTable;\nimport javax.swing.table.TableCellRenderer;\n\npublic class CachesTableRenderer implements TableCellRenderer {\n\n\tprivate final JLabel label;\n\n\tpublic CachesTableRenderer() {\n\t\tlabel = new JLabel();\n\t\tlabel.setOpaque(true);\n\t}\n\n\t@Override\n\tpublic Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {\n\t\tTableRow obj = (TableRow) value;\n\t\tswitch (column) {\n\t\t\tcase 0:\n\t\t\t\tlabel.setText(obj.getProject());\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tlabel.setText(obj.getUsage());\n\t\t\t\tbreak;\n\t\t}\n\t\tlabel.setToolTipText(obj.getCacheEntry().getCache());\n\n\t\tif (obj.isSelected()) {\n\t\t\tlabel.setBackground(table.getSelectionBackground());\n\t\t\tlabel.setForeground(table.getSelectionForeground());\n\t\t} else {\n\t\t\tlabel.setBackground(table.getBackground());\n\t\t\tlabel.setForeground(table.getForeground());\n\t\t}\n\t\treturn label;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/cache/TableRow.java",
    "content": "package jadx.gui.settings.ui.cache;\n\nimport java.nio.file.Paths;\n\nimport jadx.api.plugins.utils.CommonFileUtils;\nimport jadx.gui.cache.manager.CacheEntry;\n\nfinal class TableRow {\n\tprivate final CacheEntry cacheEntry;\n\tprivate final String project;\n\tprivate String usage;\n\tprivate boolean selected = false;\n\n\tpublic TableRow(CacheEntry cacheEntry) {\n\t\tthis.cacheEntry = cacheEntry;\n\t\tthis.project = cutProjectName(cacheEntry.getProject());\n\t\tthis.usage = \"-\";\n\t}\n\n\tprivate String cutProjectName(String project) {\n\t\tif (project.startsWith(\"tmp:\")) {\n\t\t\tint hashStart = project.lastIndexOf('-');\n\t\t\tint endIdx = hashStart != -1 ? hashStart : project.length();\n\t\t\treturn project.substring(4, endIdx) + \" (Temp)\";\n\t\t}\n\t\treturn CommonFileUtils.removeFileExtension(Paths.get(project).getFileName().toString());\n\t}\n\n\tpublic CacheEntry getCacheEntry() {\n\t\treturn cacheEntry;\n\t}\n\n\tpublic String getProject() {\n\t\treturn project;\n\t}\n\n\tpublic String getUsage() {\n\t\treturn usage;\n\t}\n\n\tpublic void setUsage(String usage) {\n\t\tthis.usage = usage;\n\t}\n\n\tpublic boolean isSelected() {\n\t\treturn selected;\n\t}\n\n\tpublic void setSelected(boolean selected) {\n\t\tthis.selected = selected;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/font/FontChooserHack.java",
    "content": "package jadx.gui.settings.ui.font;\n\nimport java.lang.reflect.Field;\n\nimport javax.swing.JCheckBox;\nimport javax.swing.JPanel;\n\nimport org.drjekyll.fontchooser.FontChooser;\nimport org.drjekyll.fontchooser.panes.FamilyPane;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class FontChooserHack {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(FontChooserHack.class);\n\n\tpublic static void setOnlyMonospace(FontChooser fontChooser) {\n\t\ttry {\n\t\t\tFamilyPane familyPane = (FamilyPane) getPrivateField(fontChooser, \"familyPane\");\n\t\t\tJCheckBox monospacedCheckBox = (JCheckBox) getPrivateField(familyPane, \"monospacedCheckBox\");\n\t\t\tmonospacedCheckBox.setSelected(true);\n\t\t\tmonospacedCheckBox.setEnabled(false);\n\t\t} catch (Throwable e) {\n\t\t\tLOG.debug(\"Failed to set only monospace check box\", e);\n\t\t}\n\t}\n\n\tpublic static void hidePreview(FontChooser fontChooser) {\n\t\ttry {\n\t\t\tJPanel previewPanel = (JPanel) getPrivateField(fontChooser, \"previewPanel\");\n\t\t\tpreviewPanel.setVisible(false);\n\t\t} catch (Throwable e) {\n\t\t\tLOG.debug(\"Failed to hide preview panel\", e);\n\t\t}\n\t}\n\n\tprivate static Object getPrivateField(Object obj, String fieldName) throws NoSuchFieldException, IllegalAccessException {\n\t\tField f = obj.getClass().getDeclaredField(fieldName);\n\t\tf.setAccessible(true);\n\t\treturn f.get(obj);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/font/JadxFontDialog.java",
    "content": "package jadx.gui.settings.ui.font;\n\nimport java.awt.BorderLayout;\nimport java.awt.Dialog;\nimport java.awt.FlowLayout;\nimport java.awt.Font;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.JButton;\nimport javax.swing.JDialog;\nimport javax.swing.JPanel;\nimport javax.swing.WindowConstants;\n\nimport org.drjekyll.fontchooser.FontChooser;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.utils.FontUtils;\nimport jadx.gui.utils.NLS;\n\npublic class JadxFontDialog extends JDialog {\n\tprivate static final long serialVersionUID = 7609857698785777587L;\n\n\tprivate final FontChooser fontChooser = new FontChooser();\n\tprivate final JadxSettings settings;\n\tprivate boolean selected = false;\n\n\tpublic JadxFontDialog(Dialog parent, JadxSettings settings, String title) {\n\t\tsuper(parent, title, true);\n\t\tthis.settings = settings;\n\t\tinitComponents();\n\t\tsetDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\n\t\tif (!settings.loadWindowPos(this)) {\n\t\t\tpack();\n\t\t}\n\t}\n\n\tpublic @Nullable Font select(Font currentFont, boolean onlyMonospace) {\n\t\tfontChooser.setSelectedFont(currentFont);\n\t\tif (onlyMonospace) {\n\t\t\tFontChooserHack.setOnlyMonospace(fontChooser);\n\t\t}\n\t\tsetVisible(true);\n\t\tFont selectedFont = fontChooser.getSelectedFont();\n\t\tif (selected && !selectedFont.equals(currentFont)) {\n\t\t\treturn FontUtils.getCompositeFont(selectedFont.getFamily(), selectedFont.getStyle(), selectedFont.getSize());\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate void initComponents() {\n\t\tJPanel chooserPanel = new JPanel();\n\t\tchooserPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 10));\n\t\tchooserPanel.setLayout(new BorderLayout(0, 10));\n\t\tchooserPanel.add(fontChooser);\n\n\t\tJPanel controlPanel = new JPanel();\n\t\tcontrolPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\n\t\tcontrolPanel.setLayout(new FlowLayout(FlowLayout.TRAILING));\n\n\t\tJButton okBtn = new JButton();\n\t\tokBtn.setText(NLS.str(\"common_dialog.ok\"));\n\t\tokBtn.setMnemonic('o');\n\t\tokBtn.addActionListener(event -> {\n\t\t\tselected = true;\n\t\t\tdispose();\n\t\t});\n\n\t\tJButton cancelBtn = new JButton();\n\t\tcancelBtn.setText(NLS.str(\"common_dialog.cancel\"));\n\t\tcancelBtn.setMnemonic('c');\n\t\tcancelBtn.addActionListener(event -> dispose());\n\n\t\tcontrolPanel.add(okBtn);\n\t\tcontrolPanel.add(cancelBtn);\n\n\t\tadd(chooserPanel);\n\t\tadd(controlPanel, BorderLayout.PAGE_END);\n\t\tgetRootPane().setDefaultButton(okBtn);\n\t}\n\n\t@Override\n\tpublic void dispose() {\n\t\tsettings.saveWindowPos(this);\n\t\tsuper.dispose();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/plugins/AvailablePluginNode.java",
    "content": "package jadx.gui.settings.ui.plugins;\n\nimport jadx.plugins.tools.data.JadxPluginListEntry;\n\npublic class AvailablePluginNode extends BasePluginListNode {\n\n\tprivate final JadxPluginListEntry metadata;\n\n\tpublic AvailablePluginNode(JadxPluginListEntry metadata) {\n\t\tthis.metadata = metadata;\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn metadata.getName();\n\t}\n\n\t@Override\n\tpublic boolean hasDetails() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String getPluginId() {\n\t\treturn metadata.getPluginId();\n\t}\n\n\t@Override\n\tpublic String getDescription() {\n\t\treturn metadata.getDescription();\n\t}\n\n\t@Override\n\tpublic String getHomepage() {\n\t\treturn metadata.getHomepage();\n\t}\n\n\t@Override\n\tpublic String getLocationId() {\n\t\treturn metadata.getLocationId();\n\t}\n\n\t@Override\n\tpublic PluginAction getAction() {\n\t\treturn PluginAction.INSTALL;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/plugins/BasePluginListNode.java",
    "content": "package jadx.gui.settings.ui.plugins;\n\nimport org.jetbrains.annotations.Nullable;\n\nabstract class BasePluginListNode {\n\n\tpublic abstract String getTitle();\n\n\tpublic abstract boolean hasDetails();\n\n\tpublic String getPluginId() {\n\t\treturn null;\n\t}\n\n\tpublic String getDescription() {\n\t\treturn null;\n\t}\n\n\tpublic String getHomepage() {\n\t\treturn null;\n\t}\n\n\tpublic @Nullable String getLocationId() {\n\t\treturn null;\n\t}\n\n\tpublic @Nullable String getVersion() {\n\t\treturn null;\n\t}\n\n\tpublic boolean isDisabled() {\n\t\treturn false;\n\t}\n\n\tpublic PluginAction getAction() {\n\t\treturn PluginAction.NONE;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/plugins/InstallPluginDialog.java",
    "content": "package jadx.gui.settings.ui.plugins;\n\nimport java.awt.BorderLayout;\nimport java.awt.Dimension;\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\nimport javax.swing.JDialog;\nimport javax.swing.JFileChooser;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JTextField;\nimport javax.swing.WindowConstants;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.formdev.flatlaf.FlatClientProperties;\n\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.filedialog.FileDialogWrapper;\nimport jadx.gui.ui.filedialog.FileOpenMode;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.TextStandardActions;\nimport jadx.gui.utils.UiUtils;\n\npublic class InstallPluginDialog extends JDialog {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(InstallPluginDialog.class);\n\tprivate static final long serialVersionUID = 5304314264730563853L;\n\n\tprivate final MainWindow mainWindow;\n\tprivate final PluginSettings pluginsSettings;\n\tprivate JTextField locationFld;\n\n\tpublic InstallPluginDialog(MainWindow mainWindow, PluginSettings pluginsSettings) {\n\t\tsuper(mainWindow, NLS.str(\"preferences.plugins.install\"));\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.pluginsSettings = pluginsSettings;\n\t\tinit();\n\t}\n\n\tprivate void init() {\n\t\tlocationFld = new JTextField();\n\t\tlocationFld.setAlignmentX(LEFT_ALIGNMENT);\n\t\tlocationFld.setColumns(50);\n\t\tTextStandardActions.attach(locationFld);\n\t\tlocationFld.putClientProperty(FlatClientProperties.TEXT_FIELD_SHOW_CLEAR_BUTTON, true);\n\n\t\tJLabel locationLbl = new JLabel(NLS.str(\"preferences.plugins.location_id_label\"));\n\t\tlocationLbl.setLabelFor(locationFld);\n\n\t\tJPanel locationPanel = new JPanel();\n\t\tlocationPanel.setLayout(new BoxLayout(locationPanel, BoxLayout.LINE_AXIS));\n\t\tlocationPanel.add(locationLbl);\n\t\tlocationPanel.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tlocationPanel.add(locationFld);\n\n\t\tJButton fileBtn = new JButton(NLS.str(\"preferences.plugins.plugin_jar\"));\n\t\tfileBtn.addActionListener(ev -> openPluginFile());\n\t\tJLabel fileLbl = new JLabel(NLS.str(\"preferences.plugins.plugin_jar_label\"));\n\t\tfileLbl.setLabelFor(fileBtn);\n\n\t\tJPanel filePanel = new JPanel();\n\t\tfilePanel.setLayout(new BoxLayout(filePanel, BoxLayout.LINE_AXIS));\n\t\tfilePanel.add(fileLbl);\n\t\tfilePanel.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tfilePanel.add(fileBtn);\n\n\t\tJPanel mainPanel = new JPanel();\n\t\tmainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));\n\t\tmainPanel.add(locationPanel);\n\t\tmainPanel.add(Box.createRigidArea(new Dimension(0, 5)));\n\t\tmainPanel.add(filePanel);\n\n\t\tJButton installBtn = new JButton(NLS.str(\"preferences.plugins.install_btn\"));\n\t\tinstallBtn.addActionListener(ev -> install());\n\t\tJButton cancelBtn = new JButton(NLS.str(\"preferences.cancel\"));\n\t\tcancelBtn.addActionListener(ev -> dispose());\n\n\t\tJPanel buttonPane = new JPanel();\n\t\tbuttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));\n\t\t// TODO: add operation progress\n\t\tbuttonPane.add(Box.createHorizontalGlue());\n\t\tbuttonPane.add(installBtn);\n\t\tbuttonPane.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tbuttonPane.add(cancelBtn);\n\t\tgetRootPane().setDefaultButton(installBtn);\n\n\t\tJPanel contentPanel = new JPanel();\n\t\tcontentPanel.setLayout(new BorderLayout(5, 5));\n\t\tcontentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));\n\t\tcontentPanel.add(mainPanel, BorderLayout.PAGE_START);\n\t\tcontentPanel.add(buttonPane, BorderLayout.PAGE_END);\n\t\tgetContentPane().add(contentPanel);\n\n\t\tpack();\n\t\tsetLocationRelativeTo(null);\n\t\tsetDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\n\t\tsetModalityType(ModalityType.APPLICATION_MODAL);\n\t\tUiUtils.addEscapeShortCutToDispose(this);\n\t}\n\n\tprivate void openPluginFile() {\n\t\tFileDialogWrapper fd = new FileDialogWrapper(mainWindow, FileOpenMode.CUSTOM_OPEN);\n\t\tfd.setTitle(NLS.str(\"preferences.plugins.plugin_jar\"));\n\t\tfd.setFileExtList(List.of(\"jar\", \"zip\"));\n\t\tfd.setSelectionMode(JFileChooser.FILES_ONLY);\n\t\tList<Path> files = fd.show();\n\t\tif (files.size() == 1) {\n\t\t\tlocationFld.setText(\"file:\" + files.get(0).toAbsolutePath());\n\t\t}\n\t}\n\n\tprivate void install() {\n\t\tpluginsSettings.install(locationFld.getText());\n\t\tdispose();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/plugins/InstalledPluginNode.java",
    "content": "package jadx.gui.settings.ui.plugins;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.plugins.tools.data.JadxPluginMetadata;\n\npublic class InstalledPluginNode extends BasePluginListNode {\n\tprivate final JadxPluginMetadata metadata;\n\n\tpublic InstalledPluginNode(JadxPluginMetadata metadata) {\n\t\tthis.metadata = metadata;\n\t}\n\n\t@Override\n\tpublic @Nullable String getTitle() {\n\t\treturn metadata.getName();\n\t}\n\n\t@Override\n\tpublic boolean hasDetails() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String getPluginId() {\n\t\treturn metadata.getPluginId();\n\t}\n\n\t@Override\n\tpublic String getDescription() {\n\t\treturn metadata.getDescription();\n\t}\n\n\t@Override\n\tpublic String getHomepage() {\n\t\treturn metadata.getHomepage();\n\t}\n\n\t@Override\n\tpublic PluginAction getAction() {\n\t\treturn PluginAction.UNINSTALL;\n\t}\n\n\t@Override\n\tpublic @Nullable String getVersion() {\n\t\treturn metadata.getVersion();\n\t}\n\n\t@Override\n\tpublic boolean isDisabled() {\n\t\treturn metadata.isDisabled();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn metadata.getName();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/plugins/LoadedPluginNode.java",
    "content": "package jadx.gui.settings.ui.plugins;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.plugins.PluginContext;\n\npublic class LoadedPluginNode extends BasePluginListNode {\n\tprivate final PluginContext plugin;\n\n\tpublic LoadedPluginNode(PluginContext plugin) {\n\t\tthis.plugin = plugin;\n\t}\n\n\t@Override\n\tpublic @Nullable String getTitle() {\n\t\treturn plugin.getPluginInfo().getName();\n\t}\n\n\t@Override\n\tpublic boolean hasDetails() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String getPluginId() {\n\t\treturn plugin.getPluginId();\n\t}\n\n\t@Override\n\tpublic String getDescription() {\n\t\treturn plugin.getPluginInfo().getDescription();\n\t}\n\n\t@Override\n\tpublic String getHomepage() {\n\t\treturn plugin.getPluginInfo().getHomepage();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn plugin.getPluginInfo().getName();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/plugins/PluginAction.java",
    "content": "package jadx.gui.settings.ui.plugins;\n\npublic enum PluginAction {\n\tNONE,\n\tINSTALL,\n\tUNINSTALL\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/plugins/PluginSettings.java",
    "content": "package jadx.gui.settings.ui.plugins;\n\nimport java.awt.event.ItemEvent;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Consumer;\nimport java.util.function.IntSupplier;\n\nimport javax.swing.JCheckBox;\nimport javax.swing.JComboBox;\nimport javax.swing.JComponent;\nimport javax.swing.JLabel;\nimport javax.swing.JSpinner;\nimport javax.swing.JTextField;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport ch.qos.logback.classic.Level;\n\nimport jadx.api.plugins.events.types.ReloadProject;\nimport jadx.api.plugins.gui.ISettingsGroup;\nimport jadx.api.plugins.gui.JadxGuiContext;\nimport jadx.api.plugins.options.JadxPluginOptions;\nimport jadx.api.plugins.options.OptionDescription;\nimport jadx.api.plugins.options.OptionFlag;\nimport jadx.api.plugins.options.OptionType;\nimport jadx.core.plugins.PluginContext;\nimport jadx.core.utils.Utils;\nimport jadx.gui.logs.LogOptions;\nimport jadx.gui.plugins.context.GuiPluginContext;\nimport jadx.gui.settings.JadxProject;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.settings.ui.SettingsGroup;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.plugins.CloseablePlugins;\nimport jadx.gui.utils.plugins.CollectPlugins;\nimport jadx.gui.utils.plugins.SettingsGroupPluginWrap;\nimport jadx.gui.utils.ui.DocumentUpdateListener;\nimport jadx.plugins.tools.JadxPluginsTools;\nimport jadx.plugins.tools.data.JadxPluginMetadata;\nimport jadx.plugins.tools.data.JadxPluginUpdate;\n\npublic class PluginSettings {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(PluginSettings.class);\n\n\tprivate final MainWindow mainWindow;\n\tprivate final JadxSettings settings;\n\n\tpublic PluginSettings(MainWindow mainWindow, JadxSettings settings) {\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.settings = settings;\n\t}\n\n\tpublic ISettingsGroup build() {\n\t\tCloseablePlugins collectedPlugins = new CollectPlugins(mainWindow).build();\n\t\tISettingsGroup pluginsGroup = new PluginSettingsGroup(this, mainWindow, collectedPlugins);\n\t\tfor (PluginContext context : collectedPlugins.getList()) {\n\t\t\tISettingsGroup pluginGroup = addPluginGroup(context);\n\t\t\tif (pluginGroup != null) {\n\t\t\t\tpluginsGroup.getSubGroups().add(new SettingsGroupPluginWrap(context.getPluginId(), pluginGroup));\n\t\t\t}\n\t\t}\n\t\treturn pluginsGroup;\n\t}\n\n\tpublic void addPlugin() {\n\t\tnew InstallPluginDialog(mainWindow, this).setVisible(true);\n\t}\n\n\tprivate void requestReload() {\n\t\tmainWindow.events().send(ReloadProject.EVENT);\n\t}\n\n\tpublic void install(String locationId) {\n\t\tmainWindow.getBackgroundExecutor().execute(NLS.str(\"preferences.plugins.task.installing\"),\n\t\t\t\t() -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tJadxPluginMetadata metadata = JadxPluginsTools.getInstance().install(locationId);\n\t\t\t\t\t\tLOG.info(\"Plugin installed: {}\", metadata);\n\t\t\t\t\t\trequestReload();\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLOG.error(\"Plugin install failed\", e);\n\t\t\t\t\t\tmainWindow.showLogViewer(LogOptions.forLevel(Level.ERROR));\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\tpublic void uninstall(String pluginId) {\n\t\tmainWindow.getBackgroundExecutor().execute(NLS.str(\"preferences.plugins.task.uninstalling\"), () -> {\n\t\t\tboolean success = JadxPluginsTools.getInstance().uninstall(pluginId);\n\t\t\tif (success) {\n\t\t\t\tLOG.info(\"Uninstall complete\");\n\t\t\t\trequestReload();\n\t\t\t} else {\n\t\t\t\tLOG.warn(\"Uninstall failed\");\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic void changeDisableStatus(String pluginId, boolean disabled) {\n\t\tmainWindow.getBackgroundExecutor().execute(\n\t\t\t\tNLS.str(\"preferences.plugins.task.status\"),\n\t\t\t\t() -> JadxPluginsTools.getInstance().changeDisabledStatus(pluginId, disabled),\n\t\t\t\ts -> requestReload());\n\t}\n\n\tvoid updateAll() {\n\t\tmainWindow.getBackgroundExecutor().execute(NLS.str(\"preferences.plugins.task.updating\"), () -> {\n\t\t\tList<JadxPluginUpdate> updates = JadxPluginsTools.getInstance().updateAll();\n\t\t\tif (!updates.isEmpty()) {\n\t\t\t\tLOG.info(\"Updates: {}\\n  \", Utils.listToString(updates, \"\\n  \"));\n\t\t\t\trequestReload();\n\t\t\t} else {\n\t\t\t\tLOG.info(\"No updates found\");\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate ISettingsGroup addPluginGroup(PluginContext context) {\n\t\tJadxGuiContext guiContext = context.getGuiContext();\n\t\tif (guiContext instanceof GuiPluginContext) {\n\t\t\tGuiPluginContext pluginGuiContext = (GuiPluginContext) guiContext;\n\t\t\tISettingsGroup customSettingsGroup = pluginGuiContext.getCustomSettingsGroup();\n\t\t\tif (customSettingsGroup != null) {\n\t\t\t\treturn customSettingsGroup;\n\t\t\t}\n\t\t}\n\t\tJadxPluginOptions options = context.getOptions();\n\t\tif (options == null) {\n\t\t\treturn null;\n\t\t}\n\t\tList<OptionDescription> optionsDescriptions = options.getOptionsDescriptions();\n\t\tif (optionsDescriptions.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tSettingsGroup settingsGroup = new SettingsGroup(context.getPluginInfo().getName());\n\t\taddOptions(settingsGroup, optionsDescriptions);\n\t\treturn settingsGroup;\n\t}\n\n\tpublic void addOptions(SettingsGroup pluginGroup, List<OptionDescription> optionsDescriptions) {\n\t\tfor (OptionDescription opt : optionsDescriptions) {\n\t\t\tif (opt.getFlags().contains(OptionFlag.HIDE_IN_GUI)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString optName = opt.name();\n\t\t\tString title = opt.description();\n\t\t\tConsumer<String> updateFunc;\n\t\t\tString curValue;\n\t\t\tif (opt.getFlags().contains(OptionFlag.PER_PROJECT)) {\n\t\t\t\tJadxProject project = mainWindow.getProject();\n\t\t\t\tupdateFunc = value -> project.updatePluginOptions(m -> m.put(optName, value));\n\t\t\t\tcurValue = project.getPluginOption(optName);\n\t\t\t} else {\n\t\t\t\tMap<String, String> optionsMap = settings.getPluginOptions();\n\t\t\t\tupdateFunc = value -> optionsMap.put(optName, value);\n\t\t\t\tcurValue = optionsMap.get(optName);\n\t\t\t}\n\t\t\tString value = curValue != null ? curValue : opt.defaultValue();\n\n\t\t\tJComponent editor = null;\n\t\t\tif (opt.values().isEmpty() || opt.getType() == OptionType.BOOLEAN) {\n\t\t\t\ttry {\n\t\t\t\t\teditor = getPluginOptionEditor(opt, value, updateFunc);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.error(\"Failed to add editor for plugin option: {}\", optName, e);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tJComboBox<String> combo = new JComboBox<>(opt.values().toArray(new String[0]));\n\t\t\t\tcombo.setSelectedItem(value);\n\t\t\t\tcombo.addActionListener(e -> updateFunc.accept((String) combo.getSelectedItem()));\n\t\t\t\teditor = combo;\n\t\t\t}\n\t\t\tif (editor != null) {\n\t\t\t\tJLabel label = pluginGroup.addRow(title, editor);\n\t\t\t\tboolean enabled = !opt.getFlags().contains(OptionFlag.DISABLE_IN_GUI);\n\t\t\t\tif (!enabled) {\n\t\t\t\t\tlabel.setEnabled(false);\n\t\t\t\t\teditor.setEnabled(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate JComponent getPluginOptionEditor(OptionDescription opt, String value, Consumer<String> updateFunc) {\n\t\tswitch (opt.getType()) {\n\t\t\tcase STRING:\n\t\t\t\tJTextField textField = new JTextField();\n\t\t\t\ttextField.setText(value == null ? \"\" : value);\n\t\t\t\ttextField.getDocument().addDocumentListener(\n\t\t\t\t\t\tnew DocumentUpdateListener(event -> updateFunc.accept(textField.getText())));\n\t\t\t\treturn textField;\n\n\t\t\tcase NUMBER:\n\t\t\t\tJSpinner numberField = new JSpinner();\n\t\t\t\tnumberField.setValue(safeStringToInt(value, () -> safeStringToInt(opt.defaultValue(), () -> {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Failed to parse integer default value: \" + opt.defaultValue());\n\t\t\t\t})));\n\t\t\t\tnumberField.addChangeListener(e -> updateFunc.accept(numberField.getValue().toString()));\n\t\t\t\treturn numberField;\n\n\t\t\tcase BOOLEAN:\n\t\t\t\tJCheckBox boolField = new JCheckBox();\n\t\t\t\tboolField.setSelected(Objects.equals(value, \"yes\") || Objects.equals(value, \"true\"));\n\t\t\t\tboolField.addItemListener(e -> {\n\t\t\t\t\tboolean editorValue = e.getStateChange() == ItemEvent.SELECTED;\n\t\t\t\t\tupdateFunc.accept(editorValue ? \"yes\" : \"no\");\n\t\t\t\t});\n\t\t\t\treturn boolField;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static int safeStringToInt(String value, IntSupplier defValueSupplier) {\n\t\tif (value == null) {\n\t\t\treturn defValueSupplier.getAsInt();\n\t\t}\n\t\ttry {\n\t\t\treturn Integer.parseInt(value);\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed parse string to int: {}\", value, e);\n\t\t\treturn defValueSupplier.getAsInt();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/plugins/PluginSettingsGroup.java",
    "content": "package jadx.gui.settings.ui.plugins;\n\nimport java.awt.BorderLayout;\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.Font;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.DefaultListModel;\nimport javax.swing.JButton;\nimport javax.swing.JComponent;\nimport javax.swing.JLabel;\nimport javax.swing.JList;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JSplitPane;\nimport javax.swing.JTextPane;\nimport javax.swing.ListCellRenderer;\nimport javax.swing.ListSelectionModel;\nimport javax.swing.SwingConstants;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.gui.ISettingsGroup;\nimport jadx.core.plugins.PluginContext;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.Utils;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.Link;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.plugins.CloseablePlugins;\nimport jadx.plugins.tools.JadxPluginsList;\nimport jadx.plugins.tools.JadxPluginsTools;\nimport jadx.plugins.tools.data.JadxPluginMetadata;\n\nclass PluginSettingsGroup implements ISettingsGroup {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(PluginSettingsGroup.class);\n\n\tprivate final PluginSettings pluginsSettings;\n\tprivate final MainWindow mainWindow;\n\tprivate final String title;\n\tprivate final List<ISettingsGroup> subGroups = new ArrayList<>();\n\tprivate final CloseablePlugins collectedPlugins;\n\n\tprivate JPanel detailsPanel;\n\n\tpublic PluginSettingsGroup(PluginSettings pluginSettings, MainWindow mainWindow, CloseablePlugins collectedPlugins) {\n\t\tthis.pluginsSettings = pluginSettings;\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.title = NLS.str(\"preferences.plugins\");\n\t\tthis.collectedPlugins = collectedPlugins;\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn title;\n\t}\n\n\t@Override\n\tpublic List<ISettingsGroup> getSubGroups() {\n\t\treturn subGroups;\n\t}\n\n\t@Override\n\tpublic JComponent buildComponent() {\n\t\t// lazy load main page\n\t\treturn buildMainSettingsPage();\n\t}\n\n\t@Override\n\tpublic void close(boolean save) {\n\t\tsubGroups.forEach(subGroup -> subGroup.close(save));\n\t\tcollectedPlugins.close();\n\t}\n\n\tprivate JPanel buildMainSettingsPage() {\n\t\tJButton installPluginBtn = new JButton(NLS.str(\"preferences.plugins.install\"));\n\t\tinstallPluginBtn.addActionListener(ev -> pluginsSettings.addPlugin());\n\n\t\tJButton updateAllBtn = new JButton(NLS.str(\"preferences.plugins.update_all\"));\n\t\tupdateAllBtn.addActionListener(ev -> pluginsSettings.updateAll());\n\n\t\tJPanel actionsPanel = new JPanel();\n\t\tactionsPanel.setLayout(new BoxLayout(actionsPanel, BoxLayout.LINE_AXIS));\n\t\tactionsPanel.add(installPluginBtn);\n\t\tactionsPanel.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tactionsPanel.add(updateAllBtn);\n\n\t\tDefaultListModel<BasePluginListNode> listModel = new DefaultListModel<>();\n\t\tJList<BasePluginListNode> pluginList = new JList<>(listModel);\n\t\tpluginList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\n\t\tpluginList.setCellRenderer(new PluginsListCellRenderer());\n\t\tpluginList.addListSelectionListener(ev -> onSelection(pluginList.getSelectedValue()));\n\t\tpluginList.setFocusable(true);\n\n\t\tJScrollPane scrollPane = new JScrollPane(pluginList);\n\t\tscrollPane.setMinimumSize(new Dimension(80, 120));\n\n\t\tdetailsPanel = new JPanel(new BorderLayout(5, 5));\n\t\tdetailsPanel.setBorder(BorderFactory.createCompoundBorder(\n\t\t\t\tBorderFactory.createTitledBorder(NLS.str(\"preferences.plugins.details\")),\n\t\t\t\tBorderFactory.createEmptyBorder(10, 10, 10, 10)));\n\t\tdetailsPanel.setLayout(new BoxLayout(detailsPanel, BoxLayout.PAGE_AXIS));\n\n\t\tJSplitPane splitPanel = new JSplitPane();\n\t\tsplitPanel.setBorder(BorderFactory.createEmptyBorder(10, 2, 2, 2));\n\t\tsplitPanel.setLeftComponent(scrollPane);\n\t\tsplitPanel.setRightComponent(detailsPanel);\n\n\t\tJPanel mainPanel = new JPanel();\n\t\tmainPanel.setLayout(new BorderLayout(5, 5));\n\t\tmainPanel.setBorder(BorderFactory.createTitledBorder(title));\n\t\tmainPanel.add(actionsPanel, BorderLayout.PAGE_START);\n\t\tmainPanel.add(splitPanel, BorderLayout.CENTER);\n\n\t\tapplyData(listModel);\n\t\treturn mainPanel;\n\t}\n\n\tprivate void applyData(DefaultListModel<BasePluginListNode> listModel) {\n\t\tList<JadxPluginMetadata> installed = JadxPluginsTools.getInstance().getInstalled();\n\t\tList<BasePluginListNode> nodes = new ArrayList<>(installed.size() + collectedPlugins.getList().size());\n\t\tSet<String> installedSet = new HashSet<>(installed.size());\n\t\tfor (JadxPluginMetadata pluginMetadata : installed) {\n\t\t\tinstalledSet.add(pluginMetadata.getPluginId());\n\t\t\tnodes.add(new InstalledPluginNode(pluginMetadata));\n\t\t}\n\t\tfor (PluginContext plugin : collectedPlugins.getList()) {\n\t\t\tif (!installedSet.contains(plugin.getPluginId())) {\n\t\t\t\tnodes.add(new LoadedPluginNode(plugin));\n\t\t\t}\n\t\t}\n\t\tnodes.sort(Comparator.comparing(BasePluginListNode::getTitle));\n\n\t\tfillListModel(listModel, nodes, Collections.emptyList());\n\t\tloadAvailablePlugins(listModel, nodes, installedSet);\n\t}\n\n\tprivate static void fillListModel(DefaultListModel<BasePluginListNode> listModel,\n\t\t\tList<BasePluginListNode> nodes, List<AvailablePluginNode> available) {\n\t\tlistModel.clear();\n\t\tlistModel.addElement(new TitleNode(\"Installed\"));\n\t\tnodes.stream().filter(n -> n.getAction() == PluginAction.UNINSTALL).forEach(listModel::addElement);\n\t\tlistModel.addElement(new TitleNode(\"Available\"));\n\t\tlistModel.addAll(available);\n\t\tlistModel.addElement(new TitleNode(\"Bundled\"));\n\t\tnodes.stream().filter(n -> n.getAction() == PluginAction.NONE).forEach(listModel::addElement);\n\t}\n\n\tprivate void loadAvailablePlugins(DefaultListModel<BasePluginListNode> listModel,\n\t\t\tList<BasePluginListNode> nodes, Set<String> installedSet) {\n\t\tmainWindow.getBackgroundExecutor().execute(\n\t\t\t\tNLS.str(\"preferences.plugins.task.downloading_list\"),\n\t\t\t\t() -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tJadxPluginsList.getInstance().get(availablePlugins -> {\n\t\t\t\t\t\t\tList<AvailablePluginNode> availableNodes = availablePlugins.stream()\n\t\t\t\t\t\t\t\t\t.filter(availablePlugin -> !installedSet.contains(availablePlugin.getPluginId()))\n\t\t\t\t\t\t\t\t\t.map(AvailablePluginNode::new)\n\t\t\t\t\t\t\t\t\t.collect(Collectors.toList());\n\t\t\t\t\t\t\tUiUtils.uiRunAndWait(() -> fillListModel(listModel, nodes, availableNodes));\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLOG.warn(\"Failed to load available plugins list\", e);\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\tprivate void onSelection(BasePluginListNode node) {\n\t\tdetailsPanel.removeAll();\n\t\tif (node.hasDetails()) {\n\t\t\tJLabel nameLbl = new JLabel(node.getTitle());\n\t\t\tFont baseFont = nameLbl.getFont();\n\t\t\tnameLbl.setFont(baseFont.deriveFont(Font.BOLD, baseFont.getSize2D() + 2));\n\n\t\t\tJLabel homeLink = null;\n\t\t\tString homepage = node.getHomepage();\n\t\t\tif (StringUtils.notBlank(homepage)) {\n\t\t\t\thomeLink = new Link(\"Homepage: \" + homepage, homepage);\n\t\t\t\thomeLink.setHorizontalAlignment(SwingConstants.LEFT);\n\t\t\t}\n\n\t\t\tJTextPane descArea = new JTextPane();\n\t\t\tdescArea.setText(node.getDescription());\n\t\t\tdescArea.setFont(baseFont.deriveFont(baseFont.getSize2D() + 1));\n\t\t\tdescArea.setEditable(false);\n\t\t\tdescArea.setBorder(BorderFactory.createEmptyBorder());\n\t\t\tdescArea.setOpaque(true);\n\n\t\t\tJPanel top = new JPanel();\n\t\t\ttop.setLayout(new BoxLayout(top, BoxLayout.LINE_AXIS));\n\t\t\ttop.setBorder(BorderFactory.createEmptyBorder(10, 2, 10, 2));\n\t\t\ttop.add(nameLbl);\n\t\t\ttop.add(Box.createHorizontalGlue());\n\t\t\tJButton actionBtn = makeActionButton(node);\n\t\t\tif (actionBtn != null) {\n\t\t\t\ttop.add(actionBtn);\n\t\t\t}\n\t\t\tif (node.getAction() == PluginAction.UNINSTALL) {\n\t\t\t\t// TODO: allow disable bundled plugins\n\t\t\t\tboolean disabled = node.isDisabled();\n\t\t\t\tString statusChangeLabel = disabled\n\t\t\t\t\t\t? NLS.str(\"preferences.plugins.enable_btn\")\n\t\t\t\t\t\t: NLS.str(\"preferences.plugins.disable_btn\");\n\t\t\t\tJButton statusBtn = new JButton(statusChangeLabel);\n\t\t\t\tstatusBtn.addActionListener(ev -> pluginsSettings.changeDisableStatus(node.getPluginId(), !disabled));\n\t\t\t\ttop.add(Box.createHorizontalStrut(10));\n\t\t\t\ttop.add(statusBtn);\n\t\t\t}\n\n\t\t\tJPanel center = new JPanel();\n\t\t\tcenter.setLayout(new BoxLayout(center, BoxLayout.PAGE_AXIS));\n\t\t\tcenter.setBorder(BorderFactory.createEmptyBorder(10, 2, 10, 2));\n\t\t\tcenter.add(descArea);\n\t\t\tif (homeLink != null) {\n\t\t\t\tJPanel link = new JPanel();\n\t\t\t\tlink.setLayout(new BoxLayout(link, BoxLayout.LINE_AXIS));\n\t\t\t\tlink.add(homeLink);\n\t\t\t\tlink.add(Box.createHorizontalGlue());\n\t\t\t\tcenter.add(link);\n\t\t\t}\n\t\t\tcenter.add(Box.createVerticalGlue());\n\n\t\t\tdetailsPanel.add(top, BorderLayout.PAGE_START);\n\t\t\tdetailsPanel.add(center, BorderLayout.CENTER);\n\t\t}\n\t\tdetailsPanel.updateUI();\n\t}\n\n\tprivate @Nullable JButton makeActionButton(BasePluginListNode node) {\n\t\tswitch (node.getAction()) {\n\t\t\tcase NONE:\n\t\t\t\treturn null;\n\t\t\tcase INSTALL: {\n\t\t\t\tJButton installBtn = new JButton(NLS.str(\"preferences.plugins.install_btn\"));\n\t\t\t\tinstallBtn.addActionListener(ev -> pluginsSettings.install(node.getLocationId()));\n\t\t\t\treturn installBtn;\n\t\t\t}\n\t\t\tcase UNINSTALL: {\n\t\t\t\tJButton uninstallBtn = new JButton(NLS.str(\"preferences.plugins.uninstall_btn\"));\n\t\t\t\tuninstallBtn.addActionListener(ev -> pluginsSettings.uninstall(node.getPluginId()));\n\t\t\t\treturn uninstallBtn;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static class PluginsListCellRenderer implements ListCellRenderer<BasePluginListNode> {\n\t\tprivate final JPanel panel;\n\t\tprivate final JLabel nameLbl;\n\t\tprivate final JLabel versionLbl;\n\t\tprivate final JLabel titleLbl;\n\n\t\tpublic PluginsListCellRenderer() {\n\t\t\tpanel = new JPanel();\n\t\t\tpanel.setOpaque(true);\n\t\t\tpanel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));\n\t\t\tpanel.setBorder(BorderFactory.createEmptyBorder(2, 10, 2, 10));\n\n\t\t\tnameLbl = new JLabel(\"\");\n\t\t\tnameLbl.setFont(nameLbl.getFont().deriveFont(Font.BOLD));\n\t\t\tnameLbl.setOpaque(true);\n\t\t\tversionLbl = new JLabel(\"\");\n\t\t\tversionLbl.setOpaque(true);\n\t\t\tversionLbl.setPreferredSize(new Dimension(40, 10));\n\n\t\t\tpanel.add(nameLbl);\n\t\t\tpanel.add(Box.createHorizontalStrut(20));\n\t\t\tpanel.add(Box.createHorizontalGlue());\n\t\t\tpanel.add(versionLbl);\n\t\t\tpanel.add(Box.createHorizontalStrut(10));\n\n\t\t\ttitleLbl = new JLabel();\n\t\t\ttitleLbl.setHorizontalAlignment(SwingConstants.CENTER);\n\t\t\ttitleLbl.setPreferredSize(new Dimension(40, 10));\n\t\t}\n\n\t\t@Override\n\t\tpublic Component getListCellRendererComponent(JList<? extends BasePluginListNode> list,\n\t\t\t\tBasePluginListNode plugin, int index, boolean isSelected, boolean cellHasFocus) {\n\t\t\tif (!plugin.hasDetails()) {\n\t\t\t\ttitleLbl.setText(plugin.getTitle());\n\t\t\t\treturn titleLbl;\n\t\t\t}\n\t\t\tnameLbl.setText(plugin.getTitle());\n\t\t\tnameLbl.setToolTipText(plugin.getLocationId());\n\t\t\tversionLbl.setText(Utils.getOrElse(plugin.getVersion(), \"\"));\n\t\t\tpanel.getAccessibleContext().setAccessibleName(plugin.getTitle());\n\n\t\t\tboolean enabled = !plugin.isDisabled();\n\t\t\tnameLbl.setEnabled(enabled);\n\t\t\tversionLbl.setEnabled(enabled);\n\n\t\t\tif (isSelected) {\n\t\t\t\tpanel.setBackground(list.getSelectionBackground());\n\t\t\t\tnameLbl.setBackground(list.getSelectionBackground());\n\t\t\t\tnameLbl.setForeground(list.getSelectionForeground());\n\t\t\t\tversionLbl.setBackground(list.getSelectionBackground());\n\t\t\t} else {\n\t\t\t\tpanel.setBackground(list.getBackground());\n\t\t\t\tnameLbl.setBackground(list.getBackground());\n\t\t\t\tnameLbl.setForeground(list.getForeground());\n\t\t\t\tversionLbl.setBackground(list.getBackground());\n\t\t\t}\n\t\t\treturn panel;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/plugins/TitleNode.java",
    "content": "package jadx.gui.settings.ui.plugins;\n\npublic class TitleNode extends BasePluginListNode {\n\tprivate final String title;\n\n\tpublic TitleNode(String title) {\n\t\tthis.title = title;\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn title;\n\t}\n\n\t@Override\n\tpublic boolean hasDetails() {\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/shortcut/ShortcutEdit.java",
    "content": "package jadx.gui.settings.ui.shortcut;\n\nimport java.awt.AWTEvent;\nimport java.awt.KeyboardFocusManager;\nimport java.awt.Toolkit;\nimport java.awt.event.FocusEvent;\nimport java.awt.event.FocusListener;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.MouseEvent;\n\nimport javax.swing.BoxLayout;\nimport javax.swing.Icon;\nimport javax.swing.JButton;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JTextField;\nimport javax.swing.UIManager;\n\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.settings.ui.JadxSettingsWindow;\nimport jadx.gui.ui.action.ActionModel;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.shortcut.Shortcut;\n\npublic class ShortcutEdit extends JPanel {\n\tprivate static final Icon CLEAR_ICON = UiUtils.openSvgIcon(\"ui/close\");\n\n\tprivate final ActionModel actionModel;\n\tprivate final JadxSettingsWindow settingsWindow;\n\tprivate final JadxSettings settings;\n\tprivate final TextField textField;\n\n\tpublic Shortcut shortcut;\n\n\tpublic ShortcutEdit(ActionModel actionModel, JadxSettingsWindow settingsWindow, JadxSettings settings) {\n\t\tthis.actionModel = actionModel;\n\t\tthis.settings = settings;\n\t\tthis.settingsWindow = settingsWindow;\n\n\t\ttextField = new TextField();\n\t\tJButton clearButton = new JButton(CLEAR_ICON);\n\n\t\tsetLayout(new BoxLayout(this, BoxLayout.X_AXIS));\n\t\tadd(textField);\n\t\tadd(clearButton);\n\n\t\tclearButton.addActionListener(e -> {\n\t\t\tsetShortcut(Shortcut.none());\n\t\t\tsaveShortcut();\n\t\t});\n\t}\n\n\tpublic void setShortcut(Shortcut shortcut) {\n\t\tthis.shortcut = shortcut;\n\t\ttextField.reload();\n\t}\n\n\tprivate void saveShortcut() {\n\t\tsettings.getShortcuts().put(actionModel, shortcut);\n\t\tsettingsWindow.needReload();\n\t}\n\n\tprivate boolean verifyShortcut(Shortcut shortcut) {\n\t\tActionModel otherAction = null;\n\t\tfor (ActionModel a : ActionModel.values()) {\n\t\t\tif (actionModel != a && shortcut.equals(settings.getShortcuts().get(a))) {\n\t\t\t\totherAction = a;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (otherAction != null) {\n\t\t\tint dialogResult = JOptionPane.showConfirmDialog(\n\t\t\t\t\tthis,\n\t\t\t\t\tNLS.str(\"msg.duplicate_shortcut\",\n\t\t\t\t\t\t\tshortcut,\n\t\t\t\t\t\t\totherAction.getName(),\n\t\t\t\t\t\t\totherAction.getCategory().getName()),\n\t\t\t\t\tNLS.str(\"msg.warning_title\"),\n\t\t\t\t\tJOptionPane.YES_NO_OPTION);\n\t\t\tif (dialogResult != 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate class TextField extends JTextField {\n\t\tprivate Shortcut tempShortcut;\n\n\t\tpublic TextField() {\n\t\t\tKeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(ev -> {\n\t\t\t\tif (!isListening()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (ev.getID() == KeyEvent.KEY_PRESSED) {\n\t\t\t\t\tShortcut pressedShortcut = Shortcut.keyboard(ev.getKeyCode(), ev.getModifiersEx());\n\t\t\t\t\tif (pressedShortcut.isValidKeyboard()) {\n\t\t\t\t\t\ttempShortcut = pressedShortcut;\n\t\t\t\t\t\trefresh(tempShortcut);\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttempShortcut = null;\n\t\t\t\t\t}\n\t\t\t\t} else if (ev.getID() == KeyEvent.KEY_RELEASED) {\n\t\t\t\t\tremoveFocus();\n\t\t\t\t}\n\t\t\t\tev.consume();\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\taddFocusListener(new FocusListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void focusGained(FocusEvent ev) {\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void focusLost(FocusEvent ev) {\n\t\t\t\t\tif (tempShortcut != null) {\n\t\t\t\t\t\tif (verifyShortcut(tempShortcut)) {\n\t\t\t\t\t\t\tshortcut = tempShortcut;\n\t\t\t\t\t\t\tsaveShortcut();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treload();\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttempShortcut = null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tToolkit.getDefaultToolkit().addAWTEventListener(event -> {\n\t\t\t\tif (!isListening()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (event instanceof MouseEvent) {\n\t\t\t\t\tMouseEvent mouseEvent = (MouseEvent) event;\n\t\t\t\t\tif (mouseEvent.getID() == MouseEvent.MOUSE_PRESSED) {\n\t\t\t\t\t\tint mouseButton = mouseEvent.getButton();\n\n\t\t\t\t\t\tif (mouseButton <= MouseEvent.BUTTON1) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (mouseButton <= MouseEvent.BUTTON3) {\n\t\t\t\t\t\t\tint dialogResult = JOptionPane.showConfirmDialog(\n\t\t\t\t\t\t\t\t\tthis,\n\t\t\t\t\t\t\t\t\tNLS.str(\"msg.common_mouse_shortcut\"),\n\t\t\t\t\t\t\t\t\tNLS.str(\"msg.warning_title\"),\n\t\t\t\t\t\t\t\t\tJOptionPane.YES_NO_OPTION);\n\t\t\t\t\t\t\tif (dialogResult != 0) {\n\t\t\t\t\t\t\t\t((MouseEvent) event).consume();\n\t\t\t\t\t\t\t\ttempShortcut = null;\n\t\t\t\t\t\t\t\tremoveFocus();\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t((MouseEvent) event).consume();\n\t\t\t\t\t\ttempShortcut = Shortcut.mouse(mouseButton);\n\t\t\t\t\t\trefresh(tempShortcut);\n\t\t\t\t\t\tremoveFocus();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, AWTEvent.MOUSE_EVENT_MASK);\n\t\t}\n\n\t\tpublic void reload() {\n\t\t\trefresh(shortcut);\n\t\t}\n\n\t\tprivate void refresh(Shortcut displayedShortcut) {\n\t\t\tif (displayedShortcut == null || displayedShortcut.isNone()) {\n\t\t\t\tsetText(\"None\");\n\t\t\t\tsetForeground(UIManager.getColor(\"TextArea.inactiveForeground\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsetText(displayedShortcut.toString());\n\t\t\tsetForeground(UIManager.getColor(\"TextArea.foreground\"));\n\t\t}\n\n\t\tprivate void removeFocus() {\n\t\t\t// triggers focusLost\n\t\t\tgetRootPane().requestFocus();\n\t\t}\n\n\t\tprivate boolean isListening() {\n\t\t\treturn isFocusOwner();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/settings/ui/shortcut/ShortcutsSettingsGroup.java",
    "content": "package jadx.gui.settings.ui.shortcut;\n\nimport java.awt.BorderLayout;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport javax.swing.JComponent;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\n\nimport jadx.api.plugins.gui.ISettingsGroup;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.settings.ui.JadxSettingsWindow;\nimport jadx.gui.settings.ui.SettingsGroup;\nimport jadx.gui.ui.action.ActionCategory;\nimport jadx.gui.ui.action.ActionModel;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.shortcut.Shortcut;\n\npublic class ShortcutsSettingsGroup implements ISettingsGroup {\n\tprivate final JadxSettingsWindow settingsWindow;\n\tprivate final JadxSettings settings;\n\n\tpublic ShortcutsSettingsGroup(JadxSettingsWindow settingsWindow, JadxSettings settings) {\n\t\tthis.settingsWindow = settingsWindow;\n\t\tthis.settings = settings;\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn NLS.str(\"preferences.shortcuts\");\n\t}\n\n\t@Override\n\tpublic JComponent buildComponent() {\n\t\tJPanel panel = new JPanel();\n\t\tpanel.setLayout(new BorderLayout());\n\t\tpanel.add(new JLabel(NLS.str(\"preferences.select_shortcuts\")), BorderLayout.NORTH);\n\t\treturn panel;\n\t}\n\n\t@Override\n\tpublic List<ISettingsGroup> getSubGroups() {\n\t\treturn Arrays.stream(ActionCategory.values())\n\t\t\t\t.map(this::makeShortcutsGroup)\n\t\t\t\t.collect(Collectors.toUnmodifiableList());\n\t}\n\n\tprivate SettingsGroup makeShortcutsGroup(ActionCategory category) {\n\t\tSettingsGroup group = new SettingsGroup(category.getName());\n\t\tfor (ActionModel actionModel : ActionModel.select(category)) {\n\t\t\tShortcut shortcut = settings.getShortcuts().get(actionModel);\n\t\t\tShortcutEdit edit = new ShortcutEdit(actionModel, settingsWindow, settings);\n\t\t\tedit.setShortcut(shortcut);\n\t\t\tgroup.addRow(actionModel.getName(), edit);\n\t\t}\n\t\treturn group;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/tree/TreeExpansionService.java",
    "content": "package jadx.gui.tree;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport javax.swing.JTree;\nimport javax.swing.tree.DefaultTreeModel;\nimport javax.swing.tree.TreeNode;\nimport javax.swing.tree.TreePath;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.jobs.LoadTask;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.treemodel.JPackage;\nimport jadx.gui.treemodel.JRoot;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.JNodeCache;\nimport jadx.gui.utils.UiUtils;\n\npublic class TreeExpansionService {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(TreeExpansionService.class);\n\tprivate static final boolean DEBUG = false;\n\n\tprivate static final Comparator<TreePath> PATH_LENGTH_REVERSE = Comparator.comparingInt(p -> -p.getPathCount());\n\n\tprivate final MainWindow mainWindow;\n\tprivate final JTree tree;\n\tprivate final JNodeCache nodeCache;\n\n\tpublic TreeExpansionService(MainWindow mainWindow, JTree tree) {\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.tree = tree;\n\t\tthis.nodeCache = mainWindow.getCacheObject().getNodeCache();\n\t}\n\n\tpublic List<String> save() {\n\t\tif (tree.getRowCount() == 0 || mainWindow.getWrapper().getCurrentDecompiler().isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<TreePath> expandedPaths = collectExpandedPaths(tree);\n\t\tList<String> list = new ArrayList<>();\n\t\tfor (TreePath expandedPath : expandedPaths) {\n\t\t\tlist.add(savePath(expandedPath));\n\t\t}\n\t\tif (DEBUG) {\n\t\t\tLOG.debug(\"Saving tree expansions:\\n {}\", Utils.listToString(list, \"\\n \"));\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic void load(List<String> treeExpansions) {\n\t\tmainWindow.getBackgroundExecutor().execute(new LoadTask<>(\n\t\t\t\t() -> {\n\t\t\t\t\tList<TreePath> expandedPaths = new ArrayList<>();\n\t\t\t\t\tloadPaths(treeExpansions, expandedPaths);\n\t\t\t\t\t// send expand event to load sub-nodes and wait for completion\n\t\t\t\t\tUiUtils.uiRunAndWait(() -> expandedPaths.forEach(path -> {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\ttree.fireTreeWillExpand(path);\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tthrow new JadxRuntimeException(\"Tree expand error\", e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}));\n\t\t\t\t\treturn expandedPaths;\n\t\t\t\t},\n\t\t\t\texpandedPaths -> {\n\t\t\t\t\t// expand paths after a loading task is finished\n\t\t\t\t\texpandedPaths.forEach(tree::expandPath);\n\t\t\t\t}));\n\t}\n\n\tprivate void loadPaths(List<String> treeExpansions, List<TreePath> expandedPaths) {\n\t\tif (DEBUG) {\n\t\t\tLOG.debug(\"Restoring tree expansions:\\n {}\", Utils.listToString(treeExpansions, \"\\n \"));\n\t\t}\n\t\tfor (String treeExpansion : treeExpansions) {\n\t\t\ttry {\n\t\t\t\tTreePath treePath = loadPath(treeExpansion);\n\t\t\t\tif (treePath != null) {\n\t\t\t\t\texpandedPaths.add(treePath);\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.warn(\"Failed to load tree expansion entry: {}\", treeExpansion, e);\n\t\t\t}\n\t\t}\n\t\tif (DEBUG) {\n\t\t\tLOG.debug(\"Restored expanded tree paths:\\n {}\", Utils.listToString(expandedPaths, \"\\n \"));\n\t\t}\n\t}\n\n\tprivate String savePath(TreePath path) {\n\t\tJNode node = (JNode) path.getLastPathComponent();\n\t\tif (node instanceof JPackage) {\n\t\t\treturn \"p:\" + ((JPackage) node).getPkg().getRawFullName();\n\t\t}\n\t\tif (node instanceof JClass) {\n\t\t\treturn \"c:\" + ((JClass) node).getCls().getRawName();\n\t\t}\n\t\treturn Arrays.stream(path.getPath())\n\t\t\t\t.map(p -> ((JNode) p).getID())\n\t\t\t\t.skip(1) // skip root\n\t\t\t\t.collect(Collectors.joining(\"//\", \"t:\", \"\"));\n\t}\n\n\tprivate @Nullable TreePath loadPath(String pathStr) {\n\t\tString pathData = pathStr.substring(2);\n\t\tswitch (pathStr.charAt(0)) {\n\t\t\tcase 'c':\n\t\t\t\treturn getTreePathForRef(getRoot().resolveRawClass(pathData));\n\t\t\tcase 'p':\n\t\t\t\treturn getTreePathForRef(getRoot().resolvePackage(pathData));\n\t\t\tcase 't':\n\t\t\t\treturn resolveTreePath(pathData.split(\"//\"));\n\n\t\t\tdefault:\n\t\t\t\tthrow new JadxRuntimeException(\"Unknown tree expansion path type: \" + pathStr);\n\t\t}\n\t}\n\n\tprivate @Nullable TreePath resolveTreePath(String[] pathArr) {\n\t\tJNode current = (JNode) tree.getModel().getRoot();\n\t\tfor (String nodeStr : pathArr) {\n\t\t\tJNode node = current.searchNode(n -> n.getID().equals(nodeStr));\n\t\t\tif (node == null) {\n\t\t\t\tif (DEBUG) {\n\t\t\t\t\tList<String> children = current.childrenList().stream()\n\t\t\t\t\t\t\t.map(n -> ((JNode) n).getID())\n\t\t\t\t\t\t\t.collect(Collectors.toList());\n\t\t\t\t\tLOG.warn(\"Failed to restore path: {}, node '{}' not found in '{}' children: {}\",\n\t\t\t\t\t\t\tArrays.toString(pathArr), nodeStr, current, children);\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tcurrent = node;\n\t\t}\n\t\treturn new TreePath(current.getPath());\n\t}\n\n\tprivate @Nullable TreePath getTreePathForRef(@Nullable ICodeNodeRef ref) {\n\t\tif (ref == null) {\n\t\t\treturn null;\n\t\t}\n\t\tJNode node = nodeCache.makeFrom(ref);\n\t\tif (node.getParent() == null) {\n\t\t\tif (DEBUG) {\n\t\t\t\tLOG.warn(\"Resolving node not from tree: {}\", node);\n\t\t\t}\n\t\t\tJNode treeNode = ((JRoot) tree.getModel().getRoot()).searchNode(node);\n\t\t\tif (treeNode == null) {\n\t\t\t\tif (DEBUG) {\n\t\t\t\t\tLOG.error(\"Node not found in tree: {}\", node);\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tnode = treeNode;\n\t\t}\n\t\tTreeNode[] pathNodes = ((DefaultTreeModel) tree.getModel()).getPathToRoot(node);\n\t\tif (pathNodes == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new TreePath(pathNodes);\n\t}\n\n\tprivate static List<TreePath> collectExpandedPaths(JTree tree) {\n\t\tTreePath root = tree.getPathForRow(0);\n\t\tEnumeration<TreePath> expandedDescendants = tree.getExpandedDescendants(root);\n\t\tif (expandedDescendants == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<TreePath> expandedPaths = new ArrayList<>();\n\t\twhile (expandedDescendants.hasMoreElements()) {\n\t\t\tTreePath path = expandedDescendants.nextElement();\n\t\t\tif (path.getPathCount() > 1) {\n\t\t\t\texpandedPaths.add(path);\n\t\t\t}\n\t\t}\n\t\t// filter out sub-paths\n\t\texpandedPaths.sort(PATH_LENGTH_REVERSE); // put the longest paths before sub-paths\n\t\tList<TreePath> result = new ArrayList<>();\n\t\tfor (TreePath path : expandedPaths) {\n\t\t\tif (!isSubPath(result, path)) {\n\t\t\t\tresult.add(path);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate static boolean isSubPath(List<TreePath> paths, TreePath path) {\n\t\tfor (TreePath addedPath : paths) {\n\t\t\tif (path.isDescendant(addedPath)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate RootNode getRoot() {\n\t\treturn mainWindow.getWrapper().getDecompiler().getRoot();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/ApkSignatureNode.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.io.File;\nimport java.security.cert.Certificate;\nimport java.util.List;\nimport java.util.concurrent.ExecutionException;\nimport java.util.stream.Collectors;\n\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\nimport javax.swing.SwingUtilities;\nimport javax.swing.SwingWorker;\n\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.apache.commons.text.StringEscapeUtils;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.android.apksig.ApkVerifier;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourceType;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.panel.HtmlPanel;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.CertificateManager;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.zip.IZipEntry;\n\npublic class ApkSignatureNode extends JNode {\n\tprivate static final long serialVersionUID = -9121321926113143407L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ApkSignatureNode.class);\n\n\tprivate static final ImageIcon CERTIFICATE_ICON = UiUtils.openSvgIcon(\"nodes/styleKeyPack\");\n\n\tprivate final transient File openFile;\n\tprivate ICodeInfo content;\n\tprivate volatile boolean loadingStarted = false;\n\tprivate static TabbedPane tabbedPane;\n\n\t@Nullable\n\tpublic static ApkSignatureNode getApkSignature(JadxWrapper wrapper) {\n\t\t// Only show the ApkSignature node if an AndroidManifest.xml is present.\n\t\t// Without a manifest the Google ApkVerifier refuses to work.\n\t\tFile apkFile = null;\n\t\tfor (ResourceFile resFile : wrapper.getResources()) {\n\t\t\tif (resFile.getType() == ResourceType.MANIFEST) {\n\t\t\t\tIZipEntry zipEntry = resFile.getZipEntry();\n\t\t\t\tif (zipEntry != null) {\n\t\t\t\t\tapkFile = zipEntry.getZipFile();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (apkFile == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new ApkSignatureNode(apkFile);\n\t}\n\n\tpublic ApkSignatureNode(File openFile) {\n\t\tthis.openFile = openFile;\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn CERTIFICATE_ICON;\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn \"APK signature\";\n\t}\n\n\t@Override\n\tpublic boolean hasContent() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic ContentPanel getContentPanel(TabbedPane tabbedPane) {\n\t\tApkSignatureNode.tabbedPane = tabbedPane;\n\t\treturn new HtmlPanel(tabbedPane, this);\n\t}\n\n\t@Override\n\tpublic ICodeInfo getCodeInfo() {\n\t\tif (content != null) {\n\t\t\treturn content;\n\t\t}\n\n\t\t// If loading hasn't started yet, start it.\n\t\tif (!loadingStarted) {\n\t\t\tloadingStarted = true;\n\t\t\tSwingUtilities.invokeLater(() -> {\n\t\t\t\tnew ApkSignatureWorker(this).execute();\n\t\t\t});\n\n\t\t\treturn new SimpleCodeInfo(StringEscapeUtils.escapeHtml4(NLS.str(\"apkSignature.loading\")));\n\t\t}\n\n\t\treturn new SimpleCodeInfo(StringEscapeUtils.escapeHtml4(NLS.str(\"apkSignature.loading\")));\n\t}\n\n\tprivate void writeCertificate(StringEscapeUtils.Builder builder, Certificate cert) {\n\t\tCertificateManager certMgr = new CertificateManager(cert);\n\t\tbuilder.append(\"<blockquote><pre>\");\n\t\tbuilder.escape(certMgr.generateHeader());\n\t\tbuilder.append(\"</pre><pre>\");\n\t\tbuilder.escape(certMgr.generatePublicKey());\n\t\tbuilder.append(\"</pre><pre>\");\n\t\tbuilder.escape(certMgr.generateSignature());\n\t\tbuilder.append(\"</pre><pre>\");\n\t\tbuilder.append(certMgr.generateFingerprint());\n\t\tbuilder.append(\"</pre></blockquote>\");\n\t}\n\n\tprivate static void writeIssues(StringEscapeUtils.Builder builder, String issueType, List<ApkVerifier.IssueWithParams> issueList) {\n\t\tif (!issueList.isEmpty()) {\n\t\t\tbuilder.append(\"<h3>\");\n\t\t\tbuilder.escape(issueType);\n\t\t\tbuilder.append(\"</h3>\");\n\t\t\tbuilder.append(\"<blockquote>\");\n\t\t\t// Unprotected Zip entry issues are very common, handle them separately\n\t\t\tList<ApkVerifier.IssueWithParams> unprotIssues = issueList.stream()\n\t\t\t\t\t.filter(i -> i.getIssue() == ApkVerifier.Issue.JAR_SIG_UNPROTECTED_ZIP_ENTRY)\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t\tif (!unprotIssues.isEmpty()) {\n\t\t\t\tbuilder.append(\"<h4>\");\n\t\t\t\tbuilder.escape(NLS.str(\"apkSignature.unprotectedEntry\"));\n\t\t\t\tbuilder.append(\"</h4><blockquote>\");\n\t\t\t\tfor (ApkVerifier.IssueWithParams issue : unprotIssues) {\n\t\t\t\t\tbuilder.escape((String) issue.getParams()[0]);\n\t\t\t\t\tbuilder.append(\"<br>\");\n\t\t\t\t}\n\t\t\t\tbuilder.append(\"</blockquote>\");\n\t\t\t}\n\t\t\tList<ApkVerifier.IssueWithParams> remainingIssues = issueList.stream()\n\t\t\t\t\t.filter(i -> i.getIssue() != ApkVerifier.Issue.JAR_SIG_UNPROTECTED_ZIP_ENTRY)\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t\tif (!remainingIssues.isEmpty()) {\n\t\t\t\tbuilder.append(\"<pre>\\n\");\n\t\t\t\tfor (ApkVerifier.IssueWithParams issue : remainingIssues) {\n\t\t\t\t\tbuilder.escape(issue.toString());\n\t\t\t\t\tbuilder.append(\"\\n\");\n\t\t\t\t}\n\t\t\t\tbuilder.append(\"</pre>\\n\");\n\t\t\t}\n\t\t\tbuilder.append(\"</blockquote>\");\n\t\t}\n\t}\n\n\tprivate static class ApkSignatureWorker extends SwingWorker<ICodeInfo, Void> {\n\t\tprivate final ApkSignatureNode node;\n\n\t\tpublic ApkSignatureWorker(ApkSignatureNode node) {\n\t\t\tthis.node = node;\n\t\t}\n\n\t\t@Override\n\t\tprotected ICodeInfo doInBackground() {\n\t\t\tLOG.debug(\"Starting APK signature verification for {}\", node.openFile);\n\t\t\tApkVerifier verifier = new ApkVerifier.Builder(node.openFile).build();\n\t\t\ttry {\n\t\t\t\tApkVerifier.Result result = verifier.verify();\n\n\t\t\t\t// Build HTML content\n\t\t\t\tStringEscapeUtils.Builder builder = StringEscapeUtils.builder(StringEscapeUtils.ESCAPE_HTML4);\n\t\t\t\tbuilder.append(\"<h1>APK signature verification result:</h1>\");\n\n\t\t\t\tbuilder.append(\"<p><b>\");\n\t\t\t\tif (result.isVerified()) {\n\t\t\t\t\tbuilder.escape(NLS.str(\"apkSignature.verificationSuccess\"));\n\t\t\t\t} else {\n\t\t\t\t\tbuilder.escape(NLS.str(\"apkSignature.verificationFailed\"));\n\t\t\t\t}\n\t\t\t\tbuilder.append(\"</b></p>\");\n\n\t\t\t\tfinal String err = NLS.str(\"apkSignature.errors\");\n\t\t\t\tfinal String warn = NLS.str(\"apkSignature.warnings\");\n\t\t\t\tfinal String sigSuccKey = \"apkSignature.signatureSuccess\";\n\t\t\t\tfinal String sigFailKey = \"apkSignature.signatureFailed\";\n\n\t\t\t\tApkSignatureNode parentNode = node;\n\n\t\t\t\twriteIssues(builder, err, result.getErrors());\n\n\t\t\t\tif (!result.getV1SchemeSigners().isEmpty()) {\n\t\t\t\t\tbuilder.append(\"<h2>\");\n\t\t\t\t\tbuilder.escape(NLS.str(result.isVerifiedUsingV1Scheme() ? sigSuccKey : sigFailKey, 1));\n\t\t\t\t\tbuilder.append(\"</h2>\\n\");\n\n\t\t\t\t\tbuilder.append(\"<blockquote>\");\n\t\t\t\t\tfor (ApkVerifier.Result.V1SchemeSignerInfo signer : result.getV1SchemeSigners()) {\n\t\t\t\t\t\tbuilder.append(\"<h3>\");\n\t\t\t\t\t\tbuilder.escape(NLS.str(\"apkSignature.signer\"));\n\t\t\t\t\t\tbuilder.append(\" \");\n\t\t\t\t\t\tbuilder.escape(signer.getName());\n\t\t\t\t\t\tbuilder.append(\" (\");\n\t\t\t\t\t\tbuilder.escape(signer.getSignatureFileName());\n\t\t\t\t\t\tbuilder.append(\")\");\n\t\t\t\t\t\tbuilder.append(\"</h3>\");\n\t\t\t\t\t\tparentNode.writeCertificate(builder, signer.getCertificate());\n\t\t\t\t\t\twriteIssues(builder, err, signer.getErrors());\n\t\t\t\t\t\twriteIssues(builder, warn, signer.getWarnings());\n\t\t\t\t\t}\n\t\t\t\t\tbuilder.append(\"</blockquote>\");\n\t\t\t\t}\n\t\t\t\tif (!result.getV2SchemeSigners().isEmpty()) {\n\t\t\t\t\tbuilder.append(\"<h2>\");\n\t\t\t\t\tbuilder.escape(NLS.str(result.isVerifiedUsingV2Scheme() ? sigSuccKey : sigFailKey, 2));\n\t\t\t\t\tbuilder.append(\"</h2>\\n\");\n\n\t\t\t\t\tbuilder.append(\"<blockquote>\");\n\t\t\t\t\tfor (ApkVerifier.Result.V2SchemeSignerInfo signer : result.getV2SchemeSigners()) {\n\t\t\t\t\t\tbuilder.append(\"<h3>\");\n\t\t\t\t\t\tbuilder.escape(NLS.str(\"apkSignature.signer\"));\n\t\t\t\t\t\tbuilder.append(\" \");\n\t\t\t\t\t\tbuilder.append(Integer.toString(signer.getIndex() + 1));\n\t\t\t\t\t\tbuilder.append(\"</h3>\");\n\t\t\t\t\t\tparentNode.writeCertificate(builder, signer.getCertificate());\n\t\t\t\t\t\twriteIssues(builder, err, signer.getErrors());\n\t\t\t\t\t\twriteIssues(builder, warn, signer.getWarnings());\n\t\t\t\t\t}\n\t\t\t\t\tbuilder.append(\"</blockquote>\");\n\t\t\t\t}\n\t\t\t\tif (!result.getV3SchemeSigners().isEmpty()) {\n\t\t\t\t\tbuilder.append(\"<h2>\");\n\t\t\t\t\tbuilder.escape(NLS.str(result.isVerifiedUsingV3Scheme() ? sigSuccKey : sigFailKey, 3));\n\t\t\t\t\tbuilder.append(\"</h2>\\n\");\n\n\t\t\t\t\tbuilder.append(\"<blockquote>\");\n\t\t\t\t\tfor (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV3SchemeSigners()) {\n\t\t\t\t\t\tbuilder.append(\"<h3>\");\n\t\t\t\t\t\tbuilder.escape(NLS.str(\"apkSignature.signer\"));\n\t\t\t\t\t\tbuilder.append(\" \");\n\t\t\t\t\t\tbuilder.append(Integer.toString(signer.getIndex() + 1));\n\t\t\t\t\t\tbuilder.append(\"</h3>\");\n\t\t\t\t\t\tparentNode.writeCertificate(builder, signer.getCertificate());\n\t\t\t\t\t\twriteIssues(builder, err, signer.getErrors());\n\t\t\t\t\t\twriteIssues(builder, warn, signer.getWarnings());\n\t\t\t\t\t}\n\t\t\t\t\tbuilder.append(\"</blockquote>\");\n\t\t\t\t}\n\t\t\t\tif (!result.getV31SchemeSigners().isEmpty()) {\n\t\t\t\t\tbuilder.append(\"<h2>\");\n\t\t\t\t\tbuilder.escape(NLS.str(result.isVerifiedUsingV31Scheme() ? sigSuccKey : sigFailKey, 31));\n\t\t\t\t\tbuilder.append(\"</h2>\\n\");\n\n\t\t\t\t\tbuilder.append(\"<blockquote>\");\n\t\t\t\t\tfor (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV31SchemeSigners()) {\n\t\t\t\t\t\tbuilder.append(\"<h3>\");\n\t\t\t\t\t\tbuilder.escape(NLS.str(\"apkSignature.signer\"));\n\t\t\t\t\t\tbuilder.append(\" \");\n\t\t\t\t\t\tbuilder.append(Integer.toString(signer.getIndex() + 1));\n\t\t\t\t\t\tbuilder.append(\"</h3>\");\n\t\t\t\t\t\tparentNode.writeCertificate(builder, signer.getCertificate());\n\t\t\t\t\t\twriteIssues(builder, err, signer.getErrors());\n\t\t\t\t\t\twriteIssues(builder, warn, signer.getWarnings());\n\t\t\t\t\t}\n\t\t\t\t\tbuilder.append(\"</blockquote>\");\n\t\t\t\t}\n\t\t\t\twriteIssues(builder, warn, result.getWarnings());\n\n\t\t\t\treturn new SimpleCodeInfo(builder.toString());\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to verify APK signature for {}\", node.openFile, e);\n\t\t\t\tStringEscapeUtils.Builder builder = StringEscapeUtils.builder(StringEscapeUtils.ESCAPE_HTML4);\n\t\t\t\tbuilder.append(\"<h1>\");\n\t\t\t\tbuilder.escape(NLS.str(\"apkSignature.exception\"));\n\t\t\t\tbuilder.append(\"</h1><pre>\");\n\t\t\t\tbuilder.escape(ExceptionUtils.getStackTrace(e));\n\t\t\t\tbuilder.append(\"</pre>\");\n\t\t\t\treturn new SimpleCodeInfo(builder.toString());\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tprotected void done() {\n\t\t\ttry {\n\t\t\t\tnode.content = get();\n\t\t\t\tif (tabbedPane != null) {\n\t\t\t\t\tContentPanel panel = tabbedPane.getTabByNode(node);\n\t\t\t\t\tif (panel instanceof HtmlPanel) {\n\t\t\t\t\t\t((HtmlPanel) panel).loadContent(node);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tLOG.warn(\"Could not find TabbedPane to refresh ApkSignatureNode panel.\");\n\t\t\t\t}\n\n\t\t\t} catch (InterruptedException | ExecutionException e) {\n\t\t\t\tLOG.error(\"Error during APK signature verification SwingWorker\", e);\n\t\t\t\tStringEscapeUtils.Builder builder = StringEscapeUtils.builder(StringEscapeUtils.ESCAPE_HTML4);\n\t\t\t\tbuilder.append(\"<h1>\");\n\t\t\t\tbuilder.escape(NLS.str(\"apkSignature.exception\"));\n\t\t\t\tbuilder.append(\"</h1><pre>\");\n\t\t\t\tThrowable cause = (e instanceof ExecutionException) ? e.getCause() : e;\n\t\t\t\tbuilder.escape(ExceptionUtils.getStackTrace(cause));\n\t\t\t\tbuilder.append(\"</pre>\");\n\t\t\t\tnode.content = new SimpleCodeInfo(builder.toString());\n\n\t\t\t\tif (tabbedPane != null) {\n\t\t\t\t\tContentPanel panel = tabbedPane.getTabByNode(node);\n\t\t\t\t\tif (panel instanceof HtmlPanel) {\n\t\t\t\t\t\t((HtmlPanel) panel).loadContent(node);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tnode.loadingStarted = false;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/CodeNode.java",
    "content": "package jadx.gui.treemodel;\n\nimport javax.swing.Icon;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.api.JavaNode;\n\npublic class CodeNode extends JNode {\n\tprivate static final long serialVersionUID = 1658650786734966545L;\n\n\tprivate final transient JClass rootCls;\n\tprivate final transient JNode jNode;\n\tprivate final transient String line;\n\tprivate final transient int pos;\n\n\tpublic CodeNode(JClass rootCls, JNode jNode, String lineStr, int pos) {\n\t\tthis.rootCls = rootCls;\n\t\tthis.jNode = jNode;\n\t\tthis.line = lineStr;\n\t\tthis.pos = pos;\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn jNode.getIcon();\n\t}\n\n\t@Override\n\tpublic JavaNode getJavaNode() {\n\t\treturn jNode.getJavaNode();\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn getRootClass();\n\t}\n\n\t@Override\n\tpublic JClass getRootClass() {\n\t\treturn rootCls;\n\t}\n\n\t@Override\n\tpublic String makeDescString() {\n\t\treturn line;\n\t}\n\n\t@Override\n\tpublic boolean hasDescString() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn jNode.makeString();\n\t}\n\n\t@Override\n\tpublic String makeStringHtml() {\n\t\treturn jNode.makeStringHtml();\n\t}\n\n\t@Override\n\tpublic String makeLongString() {\n\t\treturn jNode.makeLongString();\n\t}\n\n\t@Override\n\tpublic String makeLongStringHtml() {\n\t\treturn jNode.makeLongStringHtml();\n\t}\n\n\t@Override\n\tpublic boolean disableHtml() {\n\t\treturn jNode.disableHtml();\n\t}\n\n\t@Override\n\tpublic String getSyntaxName() {\n\t\treturn jNode.getSyntaxName();\n\t}\n\n\t@Override\n\tpublic int getPos() {\n\t\treturn pos;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof CodeNode)) {\n\t\t\treturn false;\n\t\t}\n\t\tCodeNode codeNode = (CodeNode) o;\n\t\treturn jNode.equals(codeNode.jNode);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn jNode.hashCode();\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull JNode other) {\n\t\tif (other instanceof CodeNode) {\n\t\t\treturn jNode.compareTo(((CodeNode) other).jNode);\n\t\t}\n\t\treturn super.compareTo(other);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JClass.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\nimport javax.swing.JPopupMenu;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.JavaClass;\nimport jadx.api.JavaField;\nimport jadx.api.JavaMethod;\nimport jadx.api.JavaNode;\nimport jadx.api.data.ICodeRename;\nimport jadx.api.data.impl.JadxCodeRename;\nimport jadx.api.data.impl.JadxNodeRef;\nimport jadx.core.deobf.NameMapper;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.nodes.ICodeNode;\nimport jadx.gui.jobs.SimpleTask;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.codearea.ClassCodeContentPanel;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.popupmenu.JClassPopupMenu;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.CacheObject;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.JNodeCache;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic class JClass extends JLoadableNode implements JRenameNode {\n\tprivate static final long serialVersionUID = -1239986875244097177L;\n\n\tprivate static final ImageIcon ICON_CLASS_ABSTRACT = UiUtils.openSvgIcon(\"nodes/abstractClass\");\n\tprivate static final ImageIcon ICON_CLASS_PUBLIC = UiUtils.openSvgIcon(\"nodes/publicClass\");\n\tprivate static final ImageIcon ICON_CLASS_PRIVATE = UiUtils.openSvgIcon(\"nodes/privateClass\");\n\tprivate static final ImageIcon ICON_CLASS_PROTECTED = UiUtils.openSvgIcon(\"nodes/protectedClass\");\n\tprivate static final ImageIcon ICON_INTERFACE = UiUtils.openSvgIcon(\"nodes/interface\");\n\tprivate static final ImageIcon ICON_ENUM = UiUtils.openSvgIcon(\"nodes/enum\");\n\tprivate static final ImageIcon ICON_ANNOTATION = UiUtils.openSvgIcon(\"nodes/annotationtype\");\n\n\tprivate final transient JavaClass cls;\n\tprivate final transient JClass jParent;\n\tprivate final transient JNodeCache nodeCache;\n\n\tprivate transient boolean loaded;\n\n\t/**\n\t * Should be called only from JNodeCache!\n\t */\n\tpublic JClass(JavaClass cls, JClass parent, JNodeCache nodeCache) {\n\t\tthis.cls = cls;\n\t\tthis.jParent = parent;\n\t\tthis.loaded = parent != null;\n\t\tthis.nodeCache = nodeCache;\n\t}\n\n\tpublic JavaClass getCls() {\n\t\treturn cls;\n\t}\n\n\t@Override\n\tpublic boolean canRename() {\n\t\treturn !cls.getClassNode().contains(AFlag.DONT_RENAME);\n\t}\n\n\t@Override\n\tpublic void loadNode() {\n\t\tgetRootClass().load();\n\t}\n\n\t@Override\n\tpublic synchronized @Nullable SimpleTask getLoadTask() {\n\t\tif (loaded) {\n\t\t\treturn null;\n\t\t}\n\t\tJClass rootClass = getRootClass();\n\t\treturn new SimpleTask(NLS.str(\"progress.decompile\"),\n\t\t\t\t() -> rootClass.getCls().getClassNode().decompile(), // run decompilation in background\n\t\t\t\trootClass::load // load class internals and update UI\n\t\t);\n\t}\n\n\tprivate synchronized void load() {\n\t\tif (loaded) {\n\t\t\treturn;\n\t\t}\n\t\tcls.decompile();\n\t\tloaded = true;\n\t\tupdate();\n\t}\n\n\tpublic synchronized ICodeInfo reload(CacheObject cache) {\n\t\tcache.getNodeCache().removeWholeClass(cls);\n\t\tICodeInfo codeInfo = cls.reload();\n\t\tloaded = true;\n\t\tupdate();\n\t\treturn codeInfo;\n\t}\n\n\tpublic synchronized void unload(CacheObject cache) {\n\t\tcache.getNodeCache().removeWholeClass(cls);\n\t\tcls.unload();\n\t\tloaded = false;\n\t}\n\n\tpublic synchronized void update() {\n\t\tremoveAllChildren();\n\t\tif (!loaded) {\n\t\t\tadd(new TextNode(NLS.str(\"tree.loading\")));\n\t\t} else {\n\t\t\tfor (JavaClass javaClass : cls.getInnerClasses()) {\n\t\t\t\tJClass innerCls = nodeCache.makeFrom(javaClass);\n\t\t\t\tadd(innerCls);\n\t\t\t\tinnerCls.update();\n\t\t\t}\n\t\t\tfor (JavaField f : cls.getFields()) {\n\t\t\t\tadd(nodeCache.makeFrom(f));\n\t\t\t}\n\t\t\tfor (JavaMethod m : cls.getMethods()) {\n\t\t\t\tadd(nodeCache.makeFrom(m));\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic ICodeInfo getCodeInfo() {\n\t\treturn cls.getCodeInfo();\n\t}\n\n\t@Override\n\tpublic boolean hasContent() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic ContentPanel getContentPanel(TabbedPane tabbedPane) {\n\t\treturn new ClassCodeContentPanel(tabbedPane, this);\n\t}\n\n\tpublic String getSmali() {\n\t\treturn cls.getSmali();\n\t}\n\n\t@Override\n\tpublic String getSyntaxName() {\n\t\treturn SyntaxConstants.SYNTAX_STYLE_JAVA;\n\t}\n\n\t@Override\n\tpublic JPopupMenu onTreePopupMenu(MainWindow mainWindow) {\n\t\treturn new JClassPopupMenu(mainWindow, this);\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\tAccessInfo accessInfo = cls.getAccessInfo();\n\t\tif (accessInfo.isEnum()) {\n\t\t\treturn ICON_ENUM;\n\t\t}\n\t\tif (accessInfo.isAnnotation()) {\n\t\t\treturn ICON_ANNOTATION;\n\t\t}\n\t\tif (accessInfo.isInterface()) {\n\t\t\treturn ICON_INTERFACE;\n\t\t}\n\t\tif (accessInfo.isAbstract()) {\n\t\t\treturn ICON_CLASS_ABSTRACT;\n\t\t}\n\t\tif (accessInfo.isProtected()) {\n\t\t\treturn ICON_CLASS_PROTECTED;\n\t\t}\n\t\tif (accessInfo.isPrivate()) {\n\t\t\treturn ICON_CLASS_PRIVATE;\n\t\t}\n\t\tif (accessInfo.isPublic()) {\n\t\t\treturn ICON_CLASS_PUBLIC;\n\t\t}\n\t\treturn Icons.CLASS;\n\t}\n\n\t@Override\n\tpublic JavaNode getJavaNode() {\n\t\treturn cls;\n\t}\n\n\t@Override\n\tpublic ICodeNode getCodeNodeRef() {\n\t\treturn cls.getClassNode();\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn jParent;\n\t}\n\n\t@Override\n\tpublic JClass getRootClass() {\n\t\tif (jParent == null) {\n\t\t\treturn this;\n\t\t}\n\t\treturn jParent.getRootClass();\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn cls.getName();\n\t}\n\n\tpublic String getFullName() {\n\t\treturn cls.getFullName();\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn makeLongStringHtml();\n\t}\n\n\t@Override\n\tpublic boolean isValidName(String newName) {\n\t\tif (NameMapper.isValidIdentifier(newName)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (cls.isInner()) {\n\t\t\t// disallow to change package for inner classes\n\t\t\treturn false;\n\t\t}\n\t\tif (NameMapper.isValidFullIdentifier(newName)) {\n\t\t\treturn true;\n\t\t}\n\t\t// moving to default pkg\n\t\treturn newName.startsWith(\".\") && NameMapper.isValidIdentifier(newName.substring(1));\n\t}\n\n\t@Override\n\tpublic ICodeRename buildCodeRename(String newName, Set<ICodeRename> renames) {\n\t\treturn new JadxCodeRename(JadxNodeRef.forCls(cls), newName);\n\t}\n\n\t@Override\n\tpublic void removeAlias() {\n\t\t// reset only short name, package name should be reset explicitly using PackageNode\n\t\tcls.getClassNode().rename(\"\");\n\t}\n\n\t@Override\n\tpublic void addUpdateNodes(List<JavaNode> toUpdate) {\n\t\ttoUpdate.add(cls);\n\t\ttoUpdate.addAll(cls.getUseIn());\n\t}\n\n\t@Override\n\tpublic void reload(MainWindow mainWindow) {\n\t\t// TODO: rebuild packages only if class package has been changed\n\t\tmainWindow.reloadTreePreservingState();\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn cls.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\treturn this == obj || obj instanceof JClass && cls.equals(((JClass) obj).cls);\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn cls.getName();\n\t}\n\n\t@Override\n\tpublic String makeLongString() {\n\t\treturn cls.getFullName();\n\t}\n\n\tpublic int compareToCls(@NotNull JClass otherCls) {\n\t\treturn this.getCls().getRawName().compareTo(otherCls.getCls().getRawName());\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull JNode other) {\n\t\tif (other instanceof JClass) {\n\t\t\treturn compareToCls((JClass) other);\n\t\t}\n\t\tif (other instanceof JMethod) {\n\t\t\tint cmp = compareToCls(other.getJParent());\n\t\t\tif (cmp != 0) {\n\t\t\t\treturn cmp;\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\t\treturn super.compareTo(other);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JEditableNode.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\n\npublic abstract class JEditableNode extends JNode {\n\n\tprivate volatile boolean changed = false;\n\tprivate final List<Consumer<Boolean>> changeListeners = new ArrayList<>();\n\n\tpublic abstract void save(String newContent);\n\n\t@Override\n\tpublic boolean isEditable() {\n\t\treturn true;\n\t}\n\n\tpublic boolean isChanged() {\n\t\treturn changed;\n\t}\n\n\tpublic void setChanged(boolean changed) {\n\t\tif (this.changed != changed) {\n\t\t\tthis.changed = changed;\n\t\t\tfor (Consumer<Boolean> changeListener : changeListeners) {\n\t\t\t\tchangeListener.accept(changed);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void addChangeListener(Consumer<Boolean> listener) {\n\t\tchangeListeners.add(listener);\n\t\tlistener.accept(changed);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JField.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Set;\n\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\nimport javax.swing.JPopupMenu;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.api.JavaField;\nimport jadx.api.JavaNode;\nimport jadx.api.data.ICodeRename;\nimport jadx.api.data.impl.JadxCodeRename;\nimport jadx.api.data.impl.JadxNodeRef;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.core.deobf.NameMapper;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.dialog.RenameDialog;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.UiUtils;\n\npublic class JField extends JNode implements JRenameNode {\n\tprivate static final long serialVersionUID = 1712572192106793359L;\n\n\tprivate static final ImageIcon ICON_FLD_PRI = UiUtils.openSvgIcon(\"nodes/privateField\");\n\tprivate static final ImageIcon ICON_FLD_PRO = UiUtils.openSvgIcon(\"nodes/protectedField\");\n\tprivate static final ImageIcon ICON_FLD_PUB = UiUtils.openSvgIcon(\"nodes/publicField\");\n\tprivate final transient JavaField field;\n\tprivate final transient JClass jParent;\n\n\t/**\n\t * Should be called only from JNodeCache!\n\t */\n\tpublic JField(JavaField javaField, JClass jClass) {\n\t\tthis.field = javaField;\n\t\tthis.jParent = jClass;\n\t}\n\n\tpublic JavaField getJavaField() {\n\t\treturn (JavaField) getJavaNode();\n\t}\n\n\t@Override\n\tpublic JavaNode getJavaNode() {\n\t\treturn field;\n\t}\n\n\t@Override\n\tpublic ICodeNodeRef getCodeNodeRef() {\n\t\treturn field.getFieldNode();\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn jParent;\n\t}\n\n\t@Override\n\tpublic JClass getRootClass() {\n\t\treturn jParent.getRootClass();\n\t}\n\n\t@Override\n\tpublic boolean canRename() {\n\t\treturn !field.getFieldNode().contains(AFlag.DONT_RENAME);\n\t}\n\n\t@Override\n\tpublic JPopupMenu onTreePopupMenu(MainWindow mainWindow) {\n\t\treturn RenameDialog.buildRenamePopup(mainWindow, this);\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn makeLongStringHtml();\n\t}\n\n\t@Override\n\tpublic ICodeRename buildCodeRename(String newName, Set<ICodeRename> renames) {\n\t\treturn new JadxCodeRename(JadxNodeRef.forFld(field), newName);\n\t}\n\n\t@Override\n\tpublic boolean isValidName(String newName) {\n\t\treturn NameMapper.isValidIdentifier(newName);\n\t}\n\n\t@Override\n\tpublic void removeAlias() {\n\t\tfield.removeAlias();\n\t}\n\n\t@Override\n\tpublic void addUpdateNodes(List<JavaNode> toUpdate) {\n\t\ttoUpdate.add(field);\n\t\ttoUpdate.addAll(field.getUseIn());\n\t}\n\n\t@Override\n\tpublic void reload(MainWindow mainWindow) {\n\t\tmainWindow.reloadTreePreservingState();\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\tAccessInfo af = field.getAccessFlags();\n\t\treturn UiUtils.makeIcon(af, ICON_FLD_PUB, ICON_FLD_PRI, ICON_FLD_PRO, Icons.FIELD);\n\t}\n\n\t@Override\n\tpublic String getSyntaxName() {\n\t\treturn SyntaxConstants.SYNTAX_STYLE_JAVA;\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn UiUtils.typeFormat(field.getName(), field.getType());\n\t}\n\n\t@Override\n\tpublic String makeStringHtml() {\n\t\treturn UiUtils.typeFormatHtml(field.getName(), field.getType());\n\t}\n\n\t@Override\n\tpublic String makeLongString() {\n\t\treturn UiUtils.typeFormat(field.getFullName(), field.getType());\n\t}\n\n\t@Override\n\tpublic String makeLongStringHtml() {\n\t\treturn UiUtils.typeFormatHtml(field.getFullName(), field.getType());\n\t}\n\n\t@Override\n\tpublic String getTooltip() {\n\t\tString fullType = UiUtils.escapeHtml(field.getType().toString());\n\t\treturn UiUtils.wrapHtml(fullType + ' ' + UiUtils.escapeHtml(field.getName()));\n\t}\n\n\t@Override\n\tpublic String makeDescString() {\n\t\treturn UiUtils.typeStr(field.getType()) + \" \" + field.getName();\n\t}\n\n\t@Override\n\tpublic boolean disableHtml() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean hasDescString() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn field.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\treturn this == o || o instanceof JField && field.equals(((JField) o).field);\n\t}\n\n\tprivate static final Comparator<JField> COMPARATOR = Comparator\n\t\t\t.comparing(JField::getJParent)\n\t\t\t.thenComparing(JNode::getName)\n\t\t\t.thenComparingInt(JField::getPos);\n\n\tpublic int compareToFld(@NotNull JField other) {\n\t\treturn COMPARATOR.compare(this, other);\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull JNode other) {\n\t\tif (other instanceof JField) {\n\t\t\treturn compareToFld(((JField) other));\n\t\t}\n\t\treturn super.compareTo(other);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JInputFile.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.nio.file.Path;\nimport java.util.Objects;\n\nimport javax.swing.Icon;\nimport javax.swing.JPopupMenu;\n\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.ui.SimpleMenuItem;\n\npublic class JInputFile extends JNode {\n\n\tprivate final Path filePath;\n\n\tpublic JInputFile(Path filePath) {\n\t\tthis.filePath = Objects.requireNonNull(filePath);\n\t}\n\n\t@Override\n\tpublic JPopupMenu onTreePopupMenu(MainWindow mainWindow) {\n\t\treturn buildInputFilePopupMenu(mainWindow, filePath);\n\t}\n\n\tpublic static JPopupMenu buildInputFilePopupMenu(MainWindow mainWindow, Path filePath) {\n\t\tJPopupMenu menu = new JPopupMenu();\n\t\tmenu.add(new SimpleMenuItem(NLS.str(\"popup.add_files\"), mainWindow::addFiles));\n\t\tmenu.add(new SimpleMenuItem(NLS.str(\"popup.remove\"), () -> mainWindow.removeInput(filePath)));\n\t\tmenu.add(new SimpleMenuItem(NLS.str(\"popup.rename\"), () -> mainWindow.renameInput(filePath)));\n\t\treturn menu;\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn Icons.FILE;\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn filePath.getFileName().toString();\n\t}\n\n\t@Override\n\tpublic String getTooltip() {\n\t\treturn filePath.normalize().toAbsolutePath().toString();\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn filePath.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\treturn ((JInputFile) o).filePath.equals(filePath);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"JInputFile{\" + filePath + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JInputFiles.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\nimport javax.swing.JPopupMenu;\n\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.ui.SimpleMenuItem;\n\npublic class JInputFiles extends JNode {\n\tprivate static final ImageIcon INPUT_FILES_ICON = UiUtils.openSvgIcon(\"nodes/moduleDirectory\");\n\n\tpublic JInputFiles(List<Path> files) {\n\t\tfor (Path file : files) {\n\t\t\tString fileName = file.getFileName().toString();\n\t\t\tif (fileName.endsWith(\".smali\")) {\n\t\t\t\tadd(new JInputSmaliFile(file));\n\t\t\t} else {\n\t\t\t\tadd(new JInputFile(file));\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic JPopupMenu onTreePopupMenu(MainWindow mainWindow) {\n\t\tJPopupMenu menu = new JPopupMenu();\n\t\tmenu.add(new SimpleMenuItem(NLS.str(\"popup.add_files\"), mainWindow::addFiles));\n\t\treturn menu;\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn INPUT_FILES_ICON;\n\t}\n\n\t@Override\n\tpublic String getID() {\n\t\treturn \"JInputFiles\";\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn NLS.str(\"tree.input_files\");\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JInputSmaliFile.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.nio.file.Path;\nimport java.util.Objects;\n\nimport javax.swing.Icon;\nimport javax.swing.JPopupMenu;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.codearea.AbstractCodeArea;\nimport jadx.gui.ui.codearea.CodeContentPanel;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.Icons;\n\npublic class JInputSmaliFile extends JEditableNode {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JInputSmaliFile.class);\n\n\tprivate final Path filePath;\n\n\tpublic JInputSmaliFile(Path filePath) {\n\t\tthis.filePath = Objects.requireNonNull(filePath);\n\t}\n\n\t@Override\n\tpublic JPopupMenu onTreePopupMenu(MainWindow mainWindow) {\n\t\treturn JInputFile.buildInputFilePopupMenu(mainWindow, filePath);\n\t}\n\n\t@Override\n\tpublic boolean hasContent() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic @Nullable ContentPanel getContentPanel(TabbedPane tabbedPane) {\n\t\treturn new CodeContentPanel(tabbedPane, this);\n\t}\n\n\t@Override\n\tpublic String getSyntaxName() {\n\t\treturn AbstractCodeArea.SYNTAX_STYLE_SMALI;\n\t}\n\n\t@Override\n\tpublic ICodeInfo getCodeInfo() {\n\t\ttry {\n\t\t\treturn new SimpleCodeInfo(FileUtils.readFile(filePath));\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to read file: \" + filePath.toAbsolutePath(), e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void save(String newContent) {\n\t\ttry {\n\t\t\tFileUtils.writeFile(filePath, newContent);\n\t\t\tLOG.debug(\"File saved: {}\", filePath.toAbsolutePath());\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to write file: \" + filePath.toAbsolutePath(), e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn Icons.FILE;\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn filePath.getFileName().toString();\n\t}\n\n\t@Override\n\tpublic String getTooltip() {\n\t\treturn filePath.normalize().toAbsolutePath().toString();\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn filePath.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\treturn ((JInputSmaliFile) o).filePath.equals(filePath);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JInputs.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\n\nimport jadx.core.utils.files.FileUtils;\nimport jadx.gui.settings.JadxProject;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.plugins.TreeInputsHelper;\n\npublic class JInputs extends JNode {\n\tprivate static final ImageIcon INPUTS_ICON = UiUtils.openSvgIcon(\"nodes/projectStructure\");\n\n\tpublic JInputs(MainWindow mainWindow) {\n\t\tJadxProject project = mainWindow.getProject();\n\t\tList<Path> inputs = project.getFilePaths();\n\t\tList<Path> files = FileUtils.expandDirs(inputs);\n\t\tTreeInputsHelper inputsHelper = new TreeInputsHelper(mainWindow);\n\t\tinputsHelper.processInputs(files);\n\t\tadd(new JInputFiles(inputsHelper.getSimpleFiles()));\n\t\tinputsHelper.getCustomNodes().forEach(this::add);\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn INPUTS_ICON;\n\t}\n\n\t@Override\n\tpublic String getID() {\n\t\treturn \"JInputs\";\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn NLS.str(\"tree.inputs_title\");\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JLoadableNode.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.util.function.Predicate;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.gui.jobs.IBackgroundTask;\n\npublic abstract class JLoadableNode extends JNode {\n\tprivate static final long serialVersionUID = 5543590584166374958L;\n\n\tpublic abstract void loadNode();\n\n\tpublic abstract @Nullable IBackgroundTask getLoadTask();\n\n\t@Override\n\tpublic @Nullable JNode searchNode(Predicate<JNode> filter) {\n\t\tloadNode();\n\t\treturn super.searchNode(filter);\n\t}\n\n\t@Override\n\tpublic @Nullable JNode searchDepthNode(Predicate<JNode> filter) {\n\t\tloadNode();\n\t\treturn super.searchDepthNode(filter);\n\t}\n\n\t@Override\n\tpublic @Nullable JNode removeNode(Predicate<JNode> filter) {\n\t\tloadNode();\n\t\treturn super.removeNode(filter);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JMethod.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Set;\n\nimport javax.swing.Icon;\nimport javax.swing.JPopupMenu;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.api.JavaMethod;\nimport jadx.api.JavaNode;\nimport jadx.api.data.ICodeRename;\nimport jadx.api.data.impl.JadxCodeRename;\nimport jadx.api.data.impl.JadxNodeRef;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.core.deobf.NameMapper;\nimport jadx.core.dex.attributes.AFlag;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.cellrenders.MethodRenderHelper;\nimport jadx.gui.ui.dialog.RenameDialog;\nimport jadx.gui.utils.UiUtils;\n\npublic class JMethod extends JNode implements JRenameNode {\n\tprivate static final long serialVersionUID = 3834526867464663751L;\n\n\tprivate final transient JavaMethod mth;\n\tprivate final transient JClass jParent;\n\n\t/**\n\t * Should be called only from JNodeCache!\n\t */\n\tpublic JMethod(JavaMethod javaMethod, JClass jClass) {\n\t\tthis.mth = javaMethod;\n\t\tthis.jParent = jClass;\n\t}\n\n\t@Override\n\tpublic JavaNode getJavaNode() {\n\t\treturn mth;\n\t}\n\n\tpublic JavaMethod getJavaMethod() {\n\t\treturn mth;\n\t}\n\n\t@Override\n\tpublic ICodeNodeRef getCodeNodeRef() {\n\t\treturn mth.getMethodNode();\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn jParent;\n\t}\n\n\tpublic ArgType getReturnType() {\n\t\treturn mth.getReturnType();\n\t}\n\n\t@Override\n\tpublic JClass getRootClass() {\n\t\treturn jParent.getRootClass();\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn MethodRenderHelper.getIcon(mth);\n\t}\n\n\t@Override\n\tpublic String getSyntaxName() {\n\t\treturn SyntaxConstants.SYNTAX_STYLE_JAVA;\n\t}\n\n\t@Override\n\tpublic JPopupMenu onTreePopupMenu(MainWindow mainWindow) {\n\t\treturn RenameDialog.buildRenamePopup(mainWindow, this);\n\t}\n\n\tString makeBaseString() {\n\t\treturn MethodRenderHelper.makeBaseString(mth);\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn mth.getName();\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn makeLongStringHtml();\n\t}\n\n\t@Override\n\tpublic boolean canRename() {\n\t\tif (mth.isClassInit()) {\n\t\t\treturn false;\n\t\t}\n\t\treturn !mth.getMethodNode().contains(AFlag.DONT_RENAME);\n\t}\n\n\t@Override\n\tpublic JRenameNode replace() {\n\t\tif (mth.isConstructor()) {\n\t\t\t// rename class instead constructor\n\t\t\treturn jParent;\n\t\t}\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic ICodeRename buildCodeRename(String newName, Set<ICodeRename> renames) {\n\t\tList<JavaMethod> relatedMethods = mth.getOverrideRelatedMethods();\n\t\tif (!relatedMethods.isEmpty()) {\n\t\t\tfor (JavaMethod relatedMethod : relatedMethods) {\n\t\t\t\trenames.remove(new JadxCodeRename(JadxNodeRef.forMth(relatedMethod), \"\"));\n\t\t\t}\n\t\t}\n\t\treturn new JadxCodeRename(JadxNodeRef.forMth(mth), newName);\n\t}\n\n\t@Override\n\tpublic boolean isValidName(String newName) {\n\t\treturn NameMapper.isValidIdentifier(newName);\n\t}\n\n\t@Override\n\tpublic void removeAlias() {\n\t\tmth.removeAlias();\n\t}\n\n\t@Override\n\tpublic void addUpdateNodes(List<JavaNode> toUpdate) {\n\t\ttoUpdate.add(mth);\n\t\ttoUpdate.addAll(mth.getUseIn());\n\t\tList<JavaMethod> overrideRelatedMethods = mth.getOverrideRelatedMethods();\n\t\ttoUpdate.addAll(overrideRelatedMethods);\n\t\tfor (JavaMethod ovrdMth : overrideRelatedMethods) {\n\t\t\ttoUpdate.addAll(ovrdMth.getUseIn());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void reload(MainWindow mainWindow) {\n\t\tmainWindow.reloadTreePreservingState();\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn UiUtils.typeFormat(makeBaseString(), getReturnType());\n\t}\n\n\t@Override\n\tpublic String makeStringHtml() {\n\t\treturn UiUtils.typeFormatHtml(makeBaseString(), getReturnType());\n\t}\n\n\t@Override\n\tpublic String makeLongString() {\n\t\tString name = mth.getDeclaringClass().getFullName() + '.' + makeBaseString();\n\t\treturn UiUtils.typeFormat(name, getReturnType());\n\t}\n\n\t@Override\n\tpublic String makeLongStringHtml() {\n\t\tString name = mth.getDeclaringClass().getFullName() + '.' + makeBaseString();\n\t\treturn UiUtils.typeFormatHtml(name, getReturnType());\n\t}\n\n\t@Override\n\tpublic boolean disableHtml() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String makeDescString() {\n\t\treturn UiUtils.typeStr(getReturnType()) + \" \" + makeBaseString();\n\t}\n\n\t@Override\n\tpublic boolean hasDescString() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int getPos() {\n\t\treturn mth.getDefPos();\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn mth.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\treturn this == o || o instanceof JMethod && mth.equals(((JMethod) o).mth);\n\t}\n\n\tprivate static final Comparator<JMethod> COMPARATOR = Comparator\n\t\t\t.comparing(JMethod::getJParent)\n\t\t\t.thenComparing(jMethod -> jMethod.mth.getMethodNode().getMethodInfo().getShortId())\n\t\t\t.thenComparingInt(JMethod::getPos);\n\n\tpublic int compareToMth(@NotNull JMethod other) {\n\t\treturn COMPARATOR.compare(this, other);\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull JNode other) {\n\t\tif (other instanceof JMethod) {\n\t\t\treturn compareToMth(((JMethod) other));\n\t\t}\n\t\tif (other instanceof JClass) {\n\t\t\tJClass cls = (JClass) other;\n\t\t\tint cmp = jParent.compareToCls(cls);\n\t\t\tif (cmp != 0) {\n\t\t\t\treturn cmp;\n\t\t\t}\n\t\t\treturn 1;\n\t\t}\n\t\treturn super.compareTo(other);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JNode.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.util.Comparator;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport javax.swing.JPopupMenu;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.TreeNode;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.JavaNode;\nimport jadx.api.gui.tree.ITreeNode;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.resources.ResourceContentType;\nimport jadx.core.utils.ListUtils;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.tab.TabbedPane;\n\npublic abstract class JNode extends DefaultMutableTreeNode implements ITreeNode, Comparable<JNode> {\n\n\tprivate static final long serialVersionUID = -5154479091781041008L;\n\n\tpublic abstract JClass getJParent();\n\n\t/**\n\t * Return top level JClass or self if already at top.\n\t */\n\tpublic JClass getRootClass() {\n\t\treturn null;\n\t}\n\n\tpublic JavaNode getJavaNode() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic ICodeNodeRef getCodeNodeRef() {\n\t\treturn null;\n\t}\n\n\tpublic boolean hasContent() {\n\t\treturn false;\n\t}\n\n\tpublic @Nullable ContentPanel getContentPanel(TabbedPane tabbedPane) {\n\t\treturn null;\n\t}\n\n\tpublic String getSyntaxName() {\n\t\treturn SyntaxConstants.SYNTAX_STYLE_NONE;\n\t}\n\n\tpublic ICodeInfo getCodeInfo() {\n\t\treturn ICodeInfo.EMPTY;\n\t}\n\n\tpublic ResourceContentType getContentType() {\n\t\treturn ResourceContentType.CONTENT_TEXT;\n\t}\n\n\tpublic boolean isEditable() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\tJavaNode javaNode = getJavaNode();\n\t\tif (javaNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn javaNode.getName();\n\t}\n\n\tpublic boolean supportsQuickTabs() {\n\t\treturn true;\n\t}\n\n\tpublic @Nullable JPopupMenu onTreePopupMenu(MainWindow mainWindow) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getID() {\n\t\treturn makeString();\n\t}\n\n\tpublic abstract String makeString();\n\n\tpublic String makeStringHtml() {\n\t\treturn makeString();\n\t}\n\n\tpublic String makeDescString() {\n\t\treturn null;\n\t}\n\n\tpublic boolean hasDescString() {\n\t\treturn false;\n\t}\n\n\tpublic String makeLongString() {\n\t\treturn makeString();\n\t}\n\n\tpublic String makeLongStringHtml() {\n\t\treturn makeLongString();\n\t}\n\n\tpublic boolean disableHtml() {\n\t\treturn true;\n\t}\n\n\tpublic int getPos() {\n\t\tJavaNode javaNode = getJavaNode();\n\t\tif (javaNode == null) {\n\t\t\treturn -1;\n\t\t}\n\t\treturn javaNode.getDefPos();\n\t}\n\n\tpublic String getTooltip() {\n\t\treturn makeLongStringHtml();\n\t}\n\n\tpublic @Nullable JNode searchNode(Predicate<JNode> filter) {\n\t\tEnumeration<?> en = this.children();\n\t\twhile (en.hasMoreElements()) {\n\t\t\tJNode node = (JNode) en.nextElement();\n\t\t\tif (filter.test(node)) {\n\t\t\t\treturn node;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic @Nullable JNode searchDepthNode(Predicate<JNode> filter) {\n\t\tEnumeration<?> en = this.breadthFirstEnumeration();\n\t\twhile (en.hasMoreElements()) {\n\t\t\tJNode node = (JNode) en.nextElement();\n\t\t\tif (filter.test(node)) {\n\t\t\t\treturn node;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Remove and return first found node\n\t */\n\tpublic @Nullable JNode removeNode(Predicate<JNode> filter) {\n\t\tEnumeration<?> en = this.children();\n\t\twhile (en.hasMoreElements()) {\n\t\t\tJNode node = (JNode) en.nextElement();\n\t\t\tif (filter.test(node)) {\n\t\t\t\tthis.remove(node);\n\t\t\t\treturn node;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic List<TreeNode> childrenList() {\n\t\treturn ListUtils.enumerationToList(this.children());\n\t}\n\n\tprivate static final Comparator<JNode> COMPARATOR = Comparator\n\t\t\t.comparing(JNode::makeLongString)\n\t\t\t.thenComparingInt(JNode::getPos);\n\n\t@Override\n\tpublic int compareTo(@NotNull JNode other) {\n\t\treturn COMPARATOR.compare(this, other);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn makeString();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JPackage.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.swing.Icon;\nimport javax.swing.JPopupMenu;\n\nimport jadx.api.JavaNode;\nimport jadx.api.JavaPackage;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.popupmenu.JPackagePopupMenu;\nimport jadx.gui.utils.Icons;\n\nimport static jadx.gui.utils.UiUtils.escapeHtml;\nimport static jadx.gui.utils.UiUtils.fadeHtml;\nimport static jadx.gui.utils.UiUtils.wrapHtml;\n\npublic class JPackage extends JNode {\n\tprivate static final long serialVersionUID = -4120718634156839804L;\n\n\tpublic static final String PACKAGE_DEFAULT_HTML_STR = wrapHtml(fadeHtml(escapeHtml(\"<empty>\")));\n\n\tprivate final JavaPackage pkg;\n\tprivate final boolean enabled;\n\tprivate final List<JClass> classes;\n\tprivate final List<JPackage> subPackages;\n\n\t/**\n\t * Package created by full package alias, don't have a raw package reference.\n\t * `pkg` field point to the closest raw package leaf.\n\t */\n\tprivate final boolean synthetic;\n\n\tprivate String name;\n\n\t/**\n\t * Should be called only from JNodeCache!\n\t */\n\tpublic JPackage(JavaPackage pkg, boolean enabled, List<JClass> classes, List<JPackage> subPackages, boolean synthetic) {\n\t\tthis.pkg = pkg;\n\t\tthis.enabled = enabled;\n\t\tthis.classes = classes;\n\t\tthis.subPackages = subPackages;\n\t\tthis.synthetic = synthetic;\n\t}\n\n\tpublic static JPackage makeTmpRoot() {\n\t\treturn new JPackage(null, true, Collections.emptyList(), new ArrayList<>(), true);\n\t}\n\n\tpublic void update() {\n\t\tremoveAllChildren();\n\t\tif (isEnabled()) {\n\t\t\tfor (JPackage pkg : subPackages) {\n\t\t\t\tpkg.update();\n\t\t\t\tadd(pkg);\n\t\t\t}\n\t\t\tfor (JClass cls : classes) {\n\t\t\t\tcls.update();\n\t\t\t\tadd(cls);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic JPopupMenu onTreePopupMenu(MainWindow mainWindow) {\n\t\treturn new JPackagePopupMenu(mainWindow, this);\n\t}\n\n\tpublic JavaPackage getPkg() {\n\t\treturn pkg;\n\t}\n\n\tpublic JavaNode getJavaNode() {\n\t\treturn pkg;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic List<JPackage> getSubPackages() {\n\t\treturn subPackages;\n\t}\n\n\tpublic List<JClass> getClasses() {\n\t\treturn classes;\n\t}\n\n\tpublic boolean isEnabled() {\n\t\treturn enabled;\n\t}\n\n\tpublic boolean isSynthetic() {\n\t\treturn synthetic;\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn Icons.PACKAGE;\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\treturn pkg.equals(((JPackage) o).pkg);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn pkg.hashCode();\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic String makeStringHtml() {\n\t\tif (name.isEmpty()) {\n\t\t\treturn PACKAGE_DEFAULT_HTML_STR;\n\t\t}\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic boolean disableHtml() {\n\t\tif (name.isEmpty()) {\n\t\t\t// show PACKAGE_DEFAULT_HTML_STR for empty package\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String makeLongString() {\n\t\treturn pkg.getFullName();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn name;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JRenameNode.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport javax.swing.Icon;\n\nimport jadx.api.JavaNode;\nimport jadx.api.data.ICodeRename;\nimport jadx.gui.ui.MainWindow;\n\npublic interface JRenameNode {\n\n\tJavaNode getJavaNode();\n\n\tString getTitle();\n\n\tString getName();\n\n\tIcon getIcon();\n\n\tboolean canRename();\n\n\tdefault JRenameNode replace() {\n\t\treturn this;\n\t}\n\n\tICodeRename buildCodeRename(String newName, Set<ICodeRename> renames);\n\n\tboolean isValidName(String newName);\n\n\tvoid removeAlias();\n\n\tvoid addUpdateNodes(List<JavaNode> toUpdate);\n\n\tvoid reload(MainWindow mainWindow);\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JResSearchNode.java",
    "content": "package jadx.gui.treemodel;\n\nimport javax.swing.Icon;\n\nimport jadx.core.utils.StringUtils;\n\npublic class JResSearchNode extends JNode {\n\tprivate static final long serialVersionUID = -2222084945157778639L;\n\tprivate final transient JResource resNode;\n\tprivate final transient String text;\n\tprivate final transient int pos;\n\n\tpublic JResSearchNode(JResource resNode, String text, int pos) {\n\t\tthis.pos = pos;\n\t\tthis.text = text;\n\t\tthis.resNode = resNode;\n\t}\n\n\tpublic JResource getResNode() {\n\t\treturn resNode;\n\t}\n\n\tpublic int getPos() {\n\t\treturn pos;\n\t}\n\n\t@Override\n\tpublic String makeDescString() {\n\t\treturn text;\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn resNode.getJParent();\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn resNode.getName();\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn resNode.makeString();\n\t}\n\n\t@Override\n\tpublic String makeLongString() {\n\t\treturn resNode.makeLongString();\n\t}\n\n\t@Override\n\tpublic String makeLongStringHtml() {\n\t\treturn resNode.makeLongStringHtml();\n\t}\n\n\t@Override\n\tpublic String getTooltip() {\n\t\treturn resNode.getTooltip();\n\t}\n\n\t@Override\n\tpublic boolean disableHtml() {\n\t\treturn resNode.disableHtml();\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn resNode.getIcon();\n\t}\n\n\t@Override\n\tpublic boolean hasDescString() {\n\t\treturn !StringUtils.isEmpty(text);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JResource.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\nimport javax.swing.JPopupMenu;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourceType;\nimport jadx.api.ResourcesLoader;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.api.resources.ResourceContentType;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.xmlgen.ResContainer;\nimport jadx.gui.jobs.SimpleTask;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.codearea.AbstractCodeArea;\nimport jadx.gui.ui.codearea.BinaryContentPanel;\nimport jadx.gui.ui.codearea.CodeContentPanel;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.panel.FontPanel;\nimport jadx.gui.ui.panel.ImagePanel;\nimport jadx.gui.ui.popupmenu.JResourcePopupMenu;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.res.ResTableHelper;\n\npublic class JResource extends JLoadableNode {\n\tprivate static final long serialVersionUID = -201018424302612434L;\n\n\tprivate static final ImageIcon ROOT_ICON = UiUtils.openSvgIcon(\"nodes/resourcesRoot\");\n\tprivate static final ImageIcon ARSC_ICON = UiUtils.openSvgIcon(\"nodes/resourceBundle\");\n\tprivate static final ImageIcon XML_ICON = UiUtils.openSvgIcon(\"nodes/xml\");\n\tprivate static final ImageIcon IMAGE_ICON = UiUtils.openSvgIcon(\"nodes/ImagesFileType\");\n\tprivate static final ImageIcon SO_ICON = UiUtils.openSvgIcon(\"nodes/binaryFile\");\n\tprivate static final ImageIcon MANIFEST_ICON = UiUtils.openSvgIcon(\"nodes/manifest\");\n\tprivate static final ImageIcon JAVA_ICON = UiUtils.openSvgIcon(\"nodes/java\");\n\tprivate static final ImageIcon APK_ICON = UiUtils.openSvgIcon(\"nodes/archiveApk\");\n\tprivate static final ImageIcon AUDIO_ICON = UiUtils.openSvgIcon(\"nodes/audioFile\");\n\tprivate static final ImageIcon VIDEO_ICON = UiUtils.openSvgIcon(\"nodes/videoFile\");\n\tprivate static final ImageIcon FONT_ICON = UiUtils.openSvgIcon(\"nodes/fontFile\");\n\tprivate static final ImageIcon HTML_ICON = UiUtils.openSvgIcon(\"nodes/html\");\n\tprivate static final ImageIcon JSON_ICON = UiUtils.openSvgIcon(\"nodes/json\");\n\tprivate static final ImageIcon TEXT_ICON = UiUtils.openSvgIcon(\"nodes/text\");\n\tprivate static final ImageIcon ARCHIVE_ICON = UiUtils.openSvgIcon(\"nodes/archive\");\n\tprivate static final ImageIcon UNKNOWN_ICON = UiUtils.openSvgIcon(\"nodes/unknown\");\n\n\tpublic static final Comparator<JResource> RESOURCES_COMPARATOR =\n\t\t\tComparator.<JResource>comparingInt(r -> r.type.ordinal())\n\t\t\t\t\t.thenComparing(JResource::getName, String.CASE_INSENSITIVE_ORDER);\n\n\tpublic enum JResType {\n\t\tROOT,\n\t\tDIR,\n\t\tFILE\n\t}\n\n\tprivate final transient String name;\n\tprivate final transient String shortName;\n\tprivate final transient JResType type;\n\tprivate final transient ResourceFile resFile;\n\n\tprivate transient volatile boolean loaded;\n\tprivate transient List<JResource> subNodes = Collections.emptyList();\n\tprivate transient ICodeInfo content = ICodeInfo.EMPTY;\n\n\tpublic JResource(@Nullable ResourceFile resFile, String name, JResType type) {\n\t\tthis(resFile, name, name, type);\n\t}\n\n\tpublic JResource(@Nullable ResourceFile resFile, String name, String shortName, JResType type) {\n\t\tif (resFile == null && type == JResType.FILE) {\n\t\t\tthrow new IllegalArgumentException(\"Null resource file\");\n\t\t}\n\t\tthis.resFile = resFile;\n\t\tthis.name = name;\n\t\tthis.shortName = shortName;\n\t\tthis.type = type;\n\t\tthis.loaded = false;\n\t}\n\n\tpublic synchronized void update() {\n\t\tremoveAllChildren();\n\t\tif (Utils.isEmpty(subNodes)) {\n\t\t\tif (type == JResType.DIR || type == JResType.ROOT\n\t\t\t\t\t|| resFile.getType() == ResourceType.ARSC) {\n\t\t\t\t// fake leaf to force show expand button\n\t\t\t\t// real sub nodes will load on expand in loadNode() method\n\t\t\t\tadd(new TextNode(NLS.str(\"tree.loading\")));\n\t\t\t}\n\t\t} else {\n\t\t\tfor (JResource res : subNodes) {\n\t\t\t\tres.update();\n\t\t\t\tadd(res);\n\t\t\t}\n\t\t\tif (type != JResType.FILE) {\n\t\t\t\t// no content, nothing to load\n\t\t\t\tloaded = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic synchronized void loadNode() {\n\t\tgetCodeInfo();\n\t\tupdate();\n\t}\n\n\t@Override\n\tpublic synchronized SimpleTask getLoadTask() {\n\t\tif (loaded) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new SimpleTask(NLS.str(\"progress.load\"), this::getCodeInfo, this::update);\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic String getShortName() {\n\t\treturn shortName;\n\t}\n\n\tpublic JResType getType() {\n\t\treturn type;\n\t}\n\n\tpublic List<JResource> getSubNodes() {\n\t\treturn subNodes;\n\t}\n\n\tpublic void addSubNode(JResource node) {\n\t\tsubNodes = ListUtils.safeAdd(subNodes, node);\n\t}\n\n\tpublic void sortSubNodes() {\n\t\tsortResNodes(subNodes);\n\t}\n\n\tprivate static void sortResNodes(List<JResource> nodes) {\n\t\tif (Utils.notEmpty(nodes)) {\n\t\t\tnodes.forEach(JResource::sortSubNodes);\n\t\t\tnodes.sort(RESOURCES_COMPARATOR);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean hasContent() {\n\t\treturn resFile != null;\n\t}\n\n\t@Override\n\tpublic @Nullable ContentPanel getContentPanel(TabbedPane tabbedPane) {\n\t\tif (resFile == null) {\n\t\t\treturn null;\n\t\t}\n\t\t// TODO: allow to register custom viewers\n\t\tswitch (resFile.getType()) {\n\t\t\tcase IMG:\n\t\t\t\treturn new ImagePanel(tabbedPane, this);\n\t\t\tcase FONT:\n\t\t\t\treturn new FontPanel(tabbedPane, this);\n\t\t}\n\t\tif (getContentType() == ResourceContentType.CONTENT_BINARY) {\n\t\t\treturn new BinaryContentPanel(tabbedPane, this, false);\n\t\t}\n\t\tif (getSyntaxByExtension(resFile.getDeobfName()) != null) {\n\t\t\treturn new CodeContentPanel(tabbedPane, this);\n\t\t}\n\t\t// unknown file type, show both text and binary\n\t\treturn new BinaryContentPanel(tabbedPane, this, true);\n\t}\n\n\t@Override\n\tpublic JPopupMenu onTreePopupMenu(MainWindow mainWindow) {\n\t\treturn new JResourcePopupMenu(mainWindow, this);\n\t}\n\n\t@Override\n\tpublic synchronized ICodeInfo getCodeInfo() {\n\t\tif (loaded) {\n\t\t\treturn content;\n\t\t}\n\t\tICodeInfo codeInfo = loadContent();\n\t\tcontent = codeInfo;\n\t\tloaded = true;\n\t\treturn codeInfo;\n\t}\n\n\t@Override\n\tpublic ResourceContentType getContentType() {\n\t\tif (type == JResType.FILE) {\n\t\t\treturn resFile.getType().getContentType();\n\t\t}\n\t\treturn ResourceContentType.CONTENT_NONE;\n\t}\n\n\tprivate ICodeInfo loadContent() {\n\t\tif (resFile == null || type != JResType.FILE) {\n\t\t\treturn ICodeInfo.EMPTY;\n\t\t}\n\t\tResContainer rc = resFile.loadContent();\n\t\tif (rc == null) {\n\t\t\treturn ICodeInfo.EMPTY;\n\t\t}\n\t\tif (rc.getDataType() == ResContainer.DataType.RES_TABLE) {\n\t\t\tICodeInfo codeInfo = loadCurrentSingleRes(rc);\n\t\t\tList<JResource> nodes = ResTableHelper.buildTree(this, rc);\n\t\t\tsortResNodes(nodes);\n\t\t\tsubNodes = nodes;\n\t\t\tUiUtils.uiRun(this::update);\n\t\t\treturn codeInfo;\n\t\t}\n\t\t// single node\n\t\treturn loadCurrentSingleRes(rc);\n\t}\n\n\tprivate ICodeInfo loadCurrentSingleRes(ResContainer rc) {\n\t\tswitch (rc.getDataType()) {\n\t\t\tcase TEXT:\n\t\t\tcase RES_TABLE:\n\t\t\t\treturn rc.getText();\n\n\t\t\tcase RES_LINK:\n\t\t\t\ttry {\n\t\t\t\t\tResourceFile resourceFile = rc.getResLink();\n\t\t\t\t\treturn ResourcesLoader.decodeStream(resourceFile, (size, is) -> {\n\t\t\t\t\t\t// TODO: check size before loading\n\t\t\t\t\t\tif (size > 10 * 1024 * 1024L) {\n\t\t\t\t\t\t\treturn new SimpleCodeInfo(\"File too large for view\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tCharset charset;\n\t\t\t\t\t\tif (resourceFile.getType().getContentType() == ResourceContentType.CONTENT_TEXT) {\n\t\t\t\t\t\t\tcharset = StandardCharsets.UTF_8;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// force one byte charset for binary data to have the same offsets as in a byte array\n\t\t\t\t\t\t\tcharset = StandardCharsets.US_ASCII;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn ResourcesLoader.loadToCodeWriter(is, charset);\n\t\t\t\t\t});\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\treturn new SimpleCodeInfo(\"Failed to load resource file:\\n\" + Utils.getStackTrace(e));\n\t\t\t\t}\n\n\t\t\tcase DECODED_DATA:\n\t\t\tdefault:\n\t\t\t\treturn new SimpleCodeInfo(\"Unexpected resource type: \" + rc);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getSyntaxName() {\n\t\tif (resFile == null) {\n\t\t\treturn null;\n\t\t}\n\t\tswitch (resFile.getType()) {\n\t\t\tcase CODE:\n\t\t\t\treturn super.getSyntaxName();\n\n\t\t\tcase MANIFEST:\n\t\t\tcase XML:\n\t\t\tcase ARSC:\n\t\t\t\treturn SyntaxConstants.SYNTAX_STYLE_XML;\n\n\t\t\tdefault:\n\t\t\t\tString syntax = getSyntaxByExtension(resFile.getDeobfName());\n\t\t\t\tif (syntax != null) {\n\t\t\t\t\treturn syntax;\n\t\t\t\t}\n\t\t\t\treturn super.getSyntaxName();\n\t\t}\n\t}\n\n\tprivate static final Map<String, String> EXTENSION_TO_FILE_SYNTAX = jadx.core.utils.Utils.newConstStringMap(\n\t\t\t\"java\", SyntaxConstants.SYNTAX_STYLE_JAVA,\n\t\t\t\"smali\", AbstractCodeArea.SYNTAX_STYLE_SMALI,\n\t\t\t\"js\", SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT,\n\t\t\t\"ts\", SyntaxConstants.SYNTAX_STYLE_TYPESCRIPT,\n\t\t\t\"json\", SyntaxConstants.SYNTAX_STYLE_JSON,\n\t\t\t\"css\", SyntaxConstants.SYNTAX_STYLE_CSS,\n\t\t\t\"less\", SyntaxConstants.SYNTAX_STYLE_LESS,\n\t\t\t\"html\", SyntaxConstants.SYNTAX_STYLE_HTML,\n\t\t\t\"xml\", SyntaxConstants.SYNTAX_STYLE_XML,\n\t\t\t\"yaml\", SyntaxConstants.SYNTAX_STYLE_YAML,\n\t\t\t\"properties\", SyntaxConstants.SYNTAX_STYLE_PROPERTIES_FILE,\n\t\t\t\"ini\", SyntaxConstants.SYNTAX_STYLE_INI,\n\t\t\t\"sql\", SyntaxConstants.SYNTAX_STYLE_SQL);\n\n\tprivate String getSyntaxByExtension(String name) {\n\t\tint dot = name.lastIndexOf('.');\n\t\tif (dot == -1) {\n\t\t\treturn null;\n\t\t}\n\t\tString ext = name.substring(dot + 1);\n\t\treturn EXTENSION_TO_FILE_SYNTAX.get(ext);\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\tswitch (type) {\n\t\t\tcase ROOT:\n\t\t\t\treturn ROOT_ICON;\n\t\t\tcase DIR:\n\t\t\t\treturn Icons.FOLDER;\n\n\t\t\tcase FILE:\n\t\t\t\tResourceType resType = resFile.getType();\n\t\t\t\tswitch (resType) {\n\t\t\t\t\tcase MANIFEST:\n\t\t\t\t\t\treturn MANIFEST_ICON;\n\t\t\t\t\tcase ARSC:\n\t\t\t\t\t\treturn ARSC_ICON;\n\t\t\t\t\tcase XML:\n\t\t\t\t\t\treturn XML_ICON;\n\t\t\t\t\tcase IMG:\n\t\t\t\t\t\treturn IMAGE_ICON;\n\t\t\t\t\tcase LIB:\n\t\t\t\t\t\treturn SO_ICON;\n\t\t\t\t\tcase CODE:\n\t\t\t\t\t\treturn JAVA_ICON;\n\t\t\t\t\tcase APK:\n\t\t\t\t\t\treturn APK_ICON;\n\t\t\t\t\tcase VIDEOS:\n\t\t\t\t\t\treturn VIDEO_ICON;\n\t\t\t\t\tcase SOUNDS:\n\t\t\t\t\t\treturn AUDIO_ICON;\n\t\t\t\t\tcase FONT:\n\t\t\t\t\t\treturn FONT_ICON;\n\t\t\t\t\tcase HTML:\n\t\t\t\t\t\treturn HTML_ICON;\n\t\t\t\t\tcase JSON:\n\t\t\t\t\t\treturn JSON_ICON;\n\t\t\t\t\tcase TEXT:\n\t\t\t\t\t\treturn TEXT_ICON;\n\t\t\t\t\tcase ARCHIVE:\n\t\t\t\t\t\treturn ARCHIVE_ICON;\n\t\t\t\t\tcase UNKNOWN:\n\t\t\t\t\t\treturn UNKNOWN_ICON;\n\t\t\t\t}\n\t\t\t\treturn UNKNOWN_ICON;\n\t\t}\n\t\treturn Icons.FILE;\n\t}\n\n\tpublic static boolean isSupportedForView(ResourceType type) {\n\t\tswitch (type) {\n\t\t\tcase SOUNDS:\n\t\t\tcase VIDEOS:\n\t\t\tcase ARCHIVE:\n\t\t\tcase APK:\n\t\t\t\treturn false;\n\n\t\t\tcase MANIFEST:\n\t\t\tcase XML:\n\t\t\tcase ARSC:\n\t\t\tcase IMG:\n\t\t\tcase LIB:\n\t\t\tcase FONT:\n\t\t\tcase TEXT:\n\t\t\tcase JSON:\n\t\t\tcase HTML:\n\t\t\tcase UNKNOWN:\n\t\t\t\treturn true;\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static boolean isOpenInExternalTool(ResourceType type) {\n\t\tswitch (type) {\n\t\t\tcase SOUNDS:\n\t\t\tcase VIDEOS:\n\t\t\t\treturn true;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic ResourceFile getResFile() {\n\t\treturn resFile;\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getID() {\n\t\tif (type == JResType.ROOT) {\n\t\t\treturn \"JResources\";\n\t\t}\n\t\treturn makeString();\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn shortName;\n\t}\n\n\t@Override\n\tpublic String makeLongString() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tJResource other = (JResource) o;\n\t\treturn name.equals(other.name) && type.equals(other.type);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn name.hashCode() + 31 * type.ordinal();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.regex.Pattern;\n\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\nimport javax.swing.tree.TreeNode;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ResourceFile;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.settings.JadxProject;\nimport jadx.gui.treemodel.JResource.JResType;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic class JRoot extends JNode {\n\tprivate static final long serialVersionUID = 8888495789773527342L;\n\n\tprivate static final ImageIcon ROOT_ICON = UiUtils.openSvgIcon(\"nodes/rootPackageFolder\");\n\n\tprivate final transient JadxWrapper wrapper;\n\tprivate final transient MainWindow mainWindow;\n\n\tprivate transient boolean flatPackages = false;\n\n\tprivate final transient List<JNode> customNodes = new ArrayList<>();\n\n\tpublic JRoot(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.wrapper = mainWindow.getWrapper();\n\t}\n\n\tpublic final void update() {\n\t\tremoveAllChildren();\n\t\tadd(new JInputs(mainWindow));\n\t\tadd(new JSources(this, wrapper));\n\n\t\tList<ResourceFile> resources = wrapper.getResources();\n\t\tif (!resources.isEmpty()) {\n\t\t\tadd(getHierarchyResources(resources));\n\t\t}\n\t\tfor (JNode customNode : customNodes) {\n\t\t\tadd(customNode);\n\t\t}\n\t}\n\n\tprivate JResource getHierarchyResources(List<ResourceFile> resources) {\n\t\tJResource root = new JResource(null, NLS.str(\"tree.resources_title\"), JResType.ROOT);\n\t\tString splitPathStr = Pattern.quote(File.separator);\n\t\tfor (ResourceFile rf : resources) {\n\t\t\tString rfName;\n\t\t\tif (rf.getZipEntry() != null) {\n\t\t\t\trfName = rf.getDeobfName();\n\t\t\t} else {\n\t\t\t\trfName = new File(rf.getDeobfName()).getName();\n\t\t\t}\n\t\t\tString[] parts = new File(rfName).getPath().split(splitPathStr);\n\t\t\tJResource curRf = root;\n\t\t\tint count = parts.length;\n\t\t\tfor (int i = 0; i < count - 1; i++) {\n\t\t\t\tString name = parts[i];\n\t\t\t\tJResource subRF = getSubNodeByName(curRf, name);\n\t\t\t\tif (subRF == null) {\n\t\t\t\t\tsubRF = new JResource(null, name, JResType.DIR);\n\t\t\t\t\tcurRf.addSubNode(subRF);\n\t\t\t\t}\n\t\t\t\tcurRf = subRF;\n\t\t\t}\n\t\t\tJResource leaf = new JResource(rf, rf.getDeobfName(), parts[count - 1], JResType.FILE);\n\t\t\tcurRf.addSubNode(leaf);\n\t\t}\n\t\troot.sortSubNodes();\n\t\troot.update();\n\t\treturn root;\n\t}\n\n\tpublic static JResource getSubNodeByName(JResource rf, String name) {\n\t\tfor (JResource sub : rf.getSubNodes()) {\n\t\t\tif (sub.getName().equals(name)) {\n\t\t\t\treturn sub;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic @Nullable JResource searchResourceByName(String name) {\n\t\tEnumeration<?> en = this.breadthFirstEnumeration();\n\t\twhile (en.hasMoreElements()) {\n\t\t\tObject obj = en.nextElement();\n\t\t\tif (obj instanceof JResource) {\n\t\t\t\tJResource res = (JResource) obj;\n\t\t\t\tif (res.getName().equals(name)) {\n\t\t\t\t\treturn res;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic @Nullable JNode searchNode(JNode node) {\n\t\tEnumeration<?> en = this.breadthFirstEnumeration();\n\t\twhile (en.hasMoreElements()) {\n\t\t\tObject obj = en.nextElement();\n\t\t\tif (node.equals(obj)) {\n\t\t\t\treturn (JNode) obj;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic JNode followStaticPath(String... path) {\n\t\tList<String> list = Arrays.asList(path);\n\t\tJNode node = getNodeByClsPath(this, 0, list);\n\t\tif (node == null) {\n\t\t\tthrow new JadxRuntimeException(\"Incorrect static path in tree: \" + list);\n\t\t}\n\t\treturn node;\n\t}\n\n\tprivate static @Nullable JNode getNodeByClsPath(JNode start, int pos, List<String> path) {\n\t\tif (pos >= path.size()) {\n\t\t\treturn start;\n\t\t}\n\t\tString clsName = path.get(pos);\n\t\tEnumeration<TreeNode> en = start.children();\n\t\twhile (en.hasMoreElements()) {\n\t\t\tJNode node = (JNode) en.nextElement();\n\t\t\tif (node.getClass().getSimpleName().equals(clsName)) {\n\t\t\t\treturn getNodeByClsPath(node, pos + 1, path);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic boolean isFlatPackages() {\n\t\treturn flatPackages;\n\t}\n\n\tpublic void setFlatPackages(boolean flatPackages) {\n\t\tif (this.flatPackages != flatPackages) {\n\t\t\tthis.flatPackages = flatPackages;\n\t\t\tupdate();\n\t\t}\n\t}\n\n\tpublic void replaceCustomNode(@Nullable JNode node) {\n\t\tif (node == null) {\n\t\t\treturn;\n\t\t}\n\t\tClass<?> nodeCls = node.getClass();\n\t\tcustomNodes.removeIf(n -> n.getClass().equals(nodeCls));\n\t\tcustomNodes.add(node);\n\t}\n\n\tpublic List<JNode> getCustomNodes() {\n\t\treturn customNodes;\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn ROOT_ICON;\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getID() {\n\t\treturn \"JRoot\";\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\tJadxProject project = wrapper.getProject();\n\t\tif (project.getProjectPath() != null) {\n\t\t\treturn project.getName();\n\t\t}\n\t\tList<Path> paths = project.getFilePaths();\n\t\tint count = paths.size();\n\t\tif (count == 0) {\n\t\t\treturn \"File not open\";\n\t\t}\n\t\tif (count == 1) {\n\t\t\tPath fileNamePath = paths.get(0).getFileName();\n\t\t\tif (fileNamePath != null) {\n\t\t\t\treturn fileNamePath.toString();\n\t\t\t}\n\t\t\treturn paths.get(0).toString();\n\t\t}\n\t\treturn count + \" files\";\n\t}\n\n\t@Override\n\tpublic String getTooltip() {\n\t\tList<Path> paths = wrapper.getProject().getFilePaths();\n\t\tint count = paths.size();\n\t\tif (count < 2) {\n\t\t\treturn null;\n\t\t}\n\t\t// Show list of loaded files (full path)\n\t\tStringBuilder sb = new StringBuilder(\"<html>\");\n\t\tfor (Path p : paths) {\n\t\t\tsb.append(UiUtils.escapeHtml(p.toString()));\n\t\t\tsb.append(\"<br>\");\n\t\t}\n\t\tsb.append(\"</html>\");\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JSources.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.util.List;\n\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\n\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.pkgs.PackageHelper;\n\npublic class JSources extends JNode {\n\tprivate static final long serialVersionUID = 8962924556824862801L;\n\n\tprivate static final ImageIcon ROOT_ICON = UiUtils.openSvgIcon(\"nodes/packageClasses\");\n\n\tprivate final transient JadxWrapper wrapper;\n\tprivate final transient boolean flatPackages;\n\n\tpublic JSources(JRoot jRoot, JadxWrapper wrapper) {\n\t\tthis.flatPackages = jRoot.isFlatPackages();\n\t\tthis.wrapper = wrapper;\n\t\tupdate();\n\t}\n\n\tpublic final void update() {\n\t\tremoveAllChildren();\n\t\tPackageHelper packageHelper = wrapper.getCache().getPackageHelper();\n\t\tList<JPackage> roots = packageHelper.getRoots(flatPackages);\n\t\tfor (JPackage rootPkg : roots) {\n\t\t\trootPkg.update();\n\t\t\tadd(rootPkg);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn ROOT_ICON;\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getID() {\n\t\treturn \"JSources\";\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn NLS.str(\"tree.sources_title\");\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JSubResource.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ResourceFile;\n\n/**\n * Resource inside resource file.\n * Add base file prefix to distinguish from other files.\n */\npublic class JSubResource extends JResource {\n\tpublic static final String SUB_RES_PREFIX = \":/\";\n\n\tpublic JResource baseRes;\n\n\tpublic JSubResource(JResource baseRes, @Nullable ResourceFile resFile, String name, String shortName, JResType type) {\n\t\tsuper(resFile, name, shortName, type);\n\t\tthis.baseRes = Objects.requireNonNull(baseRes);\n\t}\n\n\tpublic JResource getBaseRes() {\n\t\treturn baseRes;\n\t}\n\n\t@Override\n\tpublic String makeLongString() {\n\t\treturn baseRes.makeLongString() + SUB_RES_PREFIX + super.makeLongString();\n\t}\n\n\t@Override\n\tpublic final boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof JSubResource)) {\n\t\t\treturn false;\n\t\t}\n\t\tJSubResource other = (JSubResource) o;\n\t\treturn baseRes.equals(other.baseRes)\n\t\t\t\t&& getName().equals(other.getName())\n\t\t\t\t&& getType().equals(other.getType());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn baseRes.hashCode() + 31 * super.hashCode();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/JVariable.java",
    "content": "package jadx.gui.treemodel;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport javax.swing.Icon;\n\nimport jadx.api.JavaNode;\nimport jadx.api.JavaVariable;\nimport jadx.api.data.ICodeRename;\nimport jadx.api.data.impl.JadxCodeRef;\nimport jadx.api.data.impl.JadxCodeRename;\nimport jadx.api.data.impl.JadxNodeRef;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.core.deobf.NameMapper;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.UiUtils;\n\npublic class JVariable extends JNode implements JRenameNode {\n\tprivate static final long serialVersionUID = -3002100457834453783L;\n\n\tprivate final JMethod jMth;\n\tprivate final JavaVariable var;\n\n\tpublic JVariable(JMethod jMth, JavaVariable var) {\n\t\tthis.jMth = jMth;\n\t\tthis.var = var;\n\t}\n\n\tpublic JavaVariable getJavaVarNode() {\n\t\treturn var;\n\t}\n\n\t@Override\n\tpublic JavaNode getJavaNode() {\n\t\treturn var;\n\t}\n\n\t@Override\n\tpublic JClass getRootClass() {\n\t\treturn jMth.getRootClass();\n\t}\n\n\t@Override\n\tpublic ICodeNodeRef getCodeNodeRef() {\n\t\treturn var.getVarNode();\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn jMth.getJParent();\n\t}\n\n\t@Override\n\tpublic int getPos() {\n\t\treturn var.getDefPos();\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn var.getName();\n\t}\n\n\t@Override\n\tpublic String makeLongString() {\n\t\treturn var.getFullName();\n\t}\n\n\t@Override\n\tpublic String makeLongStringHtml() {\n\t\treturn UiUtils.typeFormatHtml(var.getName(), var.getType());\n\t}\n\n\t@Override\n\tpublic boolean disableHtml() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String getTooltip() {\n\t\tString name = var.getName() + \" (r\" + var.getReg() + \"v\" + var.getSsa() + \")\";\n\t\tString fullType = UiUtils.escapeHtml(var.getType().toString());\n\t\treturn UiUtils.wrapHtml(fullType + ' ' + UiUtils.escapeHtml(name));\n\t}\n\n\t@Override\n\tpublic boolean canRename() {\n\t\treturn var.getName() != null;\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn makeLongStringHtml();\n\t}\n\n\t@Override\n\tpublic boolean isValidName(String newName) {\n\t\treturn NameMapper.isValidIdentifier(newName);\n\t}\n\n\t@Override\n\tpublic ICodeRename buildCodeRename(String newName, Set<ICodeRename> renames) {\n\t\treturn new JadxCodeRename(JadxNodeRef.forMth(var.getMth()), JadxCodeRef.forVar(var), newName);\n\t}\n\n\t@Override\n\tpublic void removeAlias() {\n\t\tvar.removeAlias();\n\t}\n\n\t@Override\n\tpublic void addUpdateNodes(List<JavaNode> toUpdate) {\n\t\ttoUpdate.add(var.getMth());\n\t}\n\n\t@Override\n\tpublic void reload(MainWindow mainWindow) {\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/treemodel/TextNode.java",
    "content": "package jadx.gui.treemodel;\n\nimport javax.swing.Icon;\n\npublic class TextNode extends JNode {\n\n\tprivate static final long serialVersionUID = 2342749142368352232L;\n\n\tprivate final String label;\n\n\tpublic TextNode(String str) {\n\t\tthis.label = str;\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn label;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/HeapUsageBar.java",
    "content": "package jadx.gui.ui;\n\nimport java.awt.Color;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\n\nimport javax.swing.FocusManager;\nimport javax.swing.JProgressBar;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport hu.akarnokd.rxjava3.swing.SwingSchedulers;\nimport io.reactivex.rxjava3.core.Flowable;\nimport io.reactivex.rxjava3.disposables.Disposable;\nimport io.reactivex.rxjava3.schedulers.Schedulers;\n\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic class HeapUsageBar extends JProgressBar {\n\tprivate static final long serialVersionUID = -8739563124249884967L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(HeapUsageBar.class);\n\n\tprivate static final double GB = 1024 * 1024 * 1024d;\n\n\tprivate static final Color GREEN = new Color(0, 180, 0);\n\tprivate static final Color RED = new Color(200, 0, 0);\n\n\tprivate final transient Runtime runtime = Runtime.getRuntime();\n\tprivate final transient FocusManager focusManager = FocusManager.getCurrentManager();\n\n\tprivate final double maxGB;\n\tprivate final long limit;\n\tprivate long peakUsed;\n\tprivate final String labelTemplate;\n\n\tprivate transient Disposable timer;\n\tprivate transient Color currentColor;\n\n\tpublic HeapUsageBar() {\n\t\tsetBorderPainted(false);\n\t\tsetStringPainted(true);\n\n\t\tlong maxMemory = runtime.maxMemory();\n\t\tpeakUsed = 0;\n\t\tmaxGB = maxMemory / GB;\n\t\tlimit = maxMemory - UiUtils.MIN_FREE_MEMORY;\n\t\tlabelTemplate = NLS.str(\"heapUsage.text\");\n\n\t\tsetMaximum((int) (maxMemory / 1024));\n\t\tsetColor(GREEN);\n\n\t\taddMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(MouseEvent e) {\n\t\t\t\tRuntime.getRuntime().gc();\n\t\t\t\tHeapUsageBar.this.update();\n\t\t\t\tif (LOG.isDebugEnabled()) {\n\t\t\t\t\tLOG.debug(\"Memory used: {}\", UiUtils.memoryInfo());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic void setVisible(boolean enabled) {\n\t\tsuper.setVisible(enabled);\n\t\tif (enabled) {\n\t\t\tstartTimer();\n\t\t} else {\n\t\t\treset();\n\t\t}\n\t}\n\n\tprivate static class UpdateData {\n\t\tint value;\n\t\tString label;\n\t\tColor color;\n\t}\n\n\tprivate static final UpdateData SKIP_UPDATE = new UpdateData();\n\n\tprivate void startTimer() {\n\t\tif (timer != null) {\n\t\t\treturn;\n\t\t}\n\t\tupdate();\n\t\ttimer = Flowable.interval(2, TimeUnit.SECONDS, Schedulers.newThread())\n\t\t\t\t.map(i -> prepareUpdate())\n\t\t\t\t.filter(update -> update != SKIP_UPDATE)\n\t\t\t\t.distinctUntilChanged((a, b) -> Objects.equals(a.label, b.label)) // pass only if label changed\n\t\t\t\t.subscribeOn(SwingSchedulers.edt())\n\t\t\t\t.subscribe(this::applyUpdate);\n\t}\n\n\tpublic UpdateData prepareUpdate() {\n\t\tif (focusManager.getActiveWindow() == null) {\n\t\t\t// skip update if app window not active\n\t\t\treturn SKIP_UPDATE;\n\t\t}\n\t\tUpdateData updateData = new UpdateData();\n\t\tlong used = runtime.totalMemory() - runtime.freeMemory();\n\t\tif (used > peakUsed) {\n\t\t\tpeakUsed = used;\n\t\t}\n\t\tupdateData.value = (int) (used / 1024);\n\t\tupdateData.label = String.format(labelTemplate, used / GB, maxGB, peakUsed / GB);\n\t\tupdateData.color = used > limit ? RED : GREEN;\n\t\treturn updateData;\n\t}\n\n\tpublic void applyUpdate(UpdateData update) {\n\t\tsetValue(update.value);\n\t\tsetString(update.label);\n\t\tsetColor(update.color);\n\t}\n\n\tprivate void setColor(Color color) {\n\t\tif (currentColor != color) {\n\t\t\tsetForeground(color);\n\t\t\tcurrentColor = color;\n\t\t}\n\t}\n\n\tprivate void update() {\n\t\tUpdateData update = prepareUpdate();\n\t\tif (update != SKIP_UPDATE) {\n\t\t\tapplyUpdate(update);\n\t\t}\n\t}\n\n\tpublic void reset() {\n\t\tif (timer != null) {\n\t\t\ttimer.dispose();\n\t\t\ttimer = null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/JadxEventQueue.java",
    "content": "package jadx.gui.ui;\n\nimport java.awt.AWTEvent;\nimport java.awt.EventQueue;\nimport java.awt.Toolkit;\nimport java.awt.event.InputEvent;\nimport java.awt.event.MouseEvent;\nimport java.awt.event.MouseWheelEvent;\n\nimport jadx.commons.app.JadxSystemInfo;\n\npublic class JadxEventQueue extends EventQueue {\n\n\tprivate static final boolean IS_X_TOOLKIT = JadxSystemInfo.IS_LINUX\n\t\t\t&& \"sun.awt.X11.XToolkit\".equals(Toolkit.getDefaultToolkit().getClass().getName());\n\n\tpublic static void register() {\n\t\tif (IS_X_TOOLKIT) {\n\t\t\tToolkit.getDefaultToolkit().getSystemEventQueue().push(new JadxEventQueue());\n\t\t}\n\t}\n\n\tprivate JadxEventQueue() {\n\t}\n\n\t@Override\n\tprotected void dispatchEvent(AWTEvent event) {\n\t\tAWTEvent mappedEvent = mapEvent(event);\n\t\tsuper.dispatchEvent(mappedEvent);\n\t}\n\n\tprivate static AWTEvent mapEvent(AWTEvent event) {\n\t\tif (IS_X_TOOLKIT && event instanceof MouseEvent && ((MouseEvent) event).getButton() > 3) {\n\t\t\treturn mapXWindowMouseEvent((MouseEvent) event);\n\t\t}\n\t\treturn event;\n\t}\n\n\t@SuppressWarnings({ \"deprecation\", \"MagicConstant\" })\n\tprivate static AWTEvent mapXWindowMouseEvent(MouseEvent src) {\n\t\tif (src.getButton() < 6) {\n\t\t\t// buttons 4-5 come from touchpad, they must be converted to horizontal scrolling events\n\t\t\tint modifiers = src.getModifiers() | InputEvent.SHIFT_DOWN_MASK;\n\t\t\treturn new MouseWheelEvent(src.getComponent(), MouseEvent.MOUSE_WHEEL, src.getWhen(), modifiers,\n\t\t\t\t\tsrc.getX(), src.getY(), 0, false, MouseWheelEvent.WHEEL_UNIT_SCROLL,\n\t\t\t\t\tsrc.getClickCount(), src.getButton() == 4 ? -1 : 1);\n\t\t} else {\n\t\t\t// Here we \"shift\" events with buttons `6` and `7` to similar events with buttons 4 and 5\n\t\t\t// See `java.awt.InputEvent#BUTTON_DOWN_MASK`, 1<<14 is the 4th physical button, 1<<15 is the 5th.\n\t\t\tint modifiers = src.getModifiers() | (1 << (8 + src.getButton()));\n\t\t\treturn new MouseEvent(src.getComponent(), src.getID(), src.getWhen(), modifiers,\n\t\t\t\t\tsrc.getX(), src.getY(), 1, src.isPopupTrigger(), src.getButton() - 2);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/MainDropTarget.java",
    "content": "package jadx.gui.ui;\n\nimport java.awt.datatransfer.DataFlavor;\nimport java.awt.datatransfer.Transferable;\nimport java.awt.dnd.DnDConstants;\nimport java.awt.dnd.DropTargetDragEvent;\nimport java.awt.dnd.DropTargetDropEvent;\nimport java.awt.dnd.DropTargetEvent;\nimport java.awt.dnd.DropTargetListener;\nimport java.io.File;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.files.FileUtils;\n\n/**\n * Enables drop support from external applications for the {@link MainWindow} (load dropped APK\n * file)\n */\npublic class MainDropTarget implements DropTargetListener {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(MainDropTarget.class);\n\n\tprivate final MainWindow mainWindow;\n\n\tpublic MainDropTarget(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t}\n\n\tprotected void processDrag(DropTargetDragEvent dtde) {\n\t\tif (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {\n\t\t\tdtde.acceptDrag(DnDConstants.ACTION_COPY);\n\t\t} else {\n\t\t\tdtde.rejectDrag();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void dragEnter(DropTargetDragEvent dtde) {\n\t\tprocessDrag(dtde);\n\t}\n\n\t@Override\n\tpublic void dragOver(DropTargetDragEvent dtde) {\n\t\tprocessDrag(dtde);\n\t}\n\n\t@Override\n\tpublic void dropActionChanged(DropTargetDragEvent dtde) {\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void drop(DropTargetDropEvent dtde) {\n\t\tif (!dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {\n\t\t\tdtde.rejectDrop();\n\t\t\treturn;\n\t\t}\n\t\tdtde.acceptDrop(dtde.getDropAction());\n\t\ttry {\n\t\t\tTransferable transferable = dtde.getTransferable();\n\t\t\tList<File> transferData = (List<File>) transferable.getTransferData(DataFlavor.javaFileListFlavor);\n\t\t\tif (!transferData.isEmpty()) {\n\t\t\t\tdtde.dropComplete(true);\n\t\t\t\tmainWindow.open(FileUtils.toPaths(transferData));\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"File drop operation failed\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void dragExit(DropTargetEvent dte) {\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java",
    "content": "package jadx.gui.ui;\n\nimport java.awt.BorderLayout;\nimport java.awt.Component;\nimport java.awt.Desktop;\nimport java.awt.Dimension;\nimport java.awt.DisplayMode;\nimport java.awt.Font;\nimport java.awt.GraphicsDevice;\nimport java.awt.GraphicsEnvironment;\nimport java.awt.dnd.DnDConstants;\nimport java.awt.dnd.DropTarget;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.FocusAdapter;\nimport java.awt.event.FocusEvent;\nimport java.awt.event.KeyAdapter;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.awt.event.WindowAdapter;\nimport java.awt.event.WindowEvent;\nimport java.awt.geom.AffineTransform;\nimport java.io.File;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Timer;\nimport java.util.TimerTask;\nimport java.util.function.Consumer;\n\nimport javax.swing.AbstractAction;\nimport javax.swing.Action;\nimport javax.swing.Box;\nimport javax.swing.JCheckBox;\nimport javax.swing.JCheckBoxMenuItem;\nimport javax.swing.JFrame;\nimport javax.swing.JLabel;\nimport javax.swing.JMenu;\nimport javax.swing.JMenuItem;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JPopupMenu;\nimport javax.swing.JScrollPane;\nimport javax.swing.JSplitPane;\nimport javax.swing.JToggleButton;\nimport javax.swing.JToolBar;\nimport javax.swing.JTree;\nimport javax.swing.SwingUtilities;\nimport javax.swing.ToolTipManager;\nimport javax.swing.UIManager;\nimport javax.swing.WindowConstants;\nimport javax.swing.event.TreeExpansionEvent;\nimport javax.swing.event.TreeWillExpandListener;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.DefaultTreeCellRenderer;\nimport javax.swing.tree.DefaultTreeModel;\nimport javax.swing.tree.TreeNode;\nimport javax.swing.tree.TreePath;\nimport javax.swing.tree.TreeSelectionModel;\n\nimport org.exbin.bined.swing.section.SectCodeArea;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.formdev.flatlaf.FlatLaf;\nimport com.formdev.flatlaf.extras.FlatInspector;\nimport com.formdev.flatlaf.extras.FlatUIDefaultsInspector;\nimport com.formdev.flatlaf.util.UIScale;\n\nimport ch.qos.logback.classic.Level;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.JavaClass;\nimport jadx.api.JavaNode;\nimport jadx.api.ResourceFile;\nimport jadx.api.plugins.events.JadxEvents;\nimport jadx.api.plugins.events.types.ReloadProject;\nimport jadx.api.plugins.events.types.ReloadSettingsWindow;\nimport jadx.api.plugins.utils.CommonFileUtils;\nimport jadx.commons.app.JadxSystemInfo;\nimport jadx.core.Jadx;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.android.AndroidManifestParser;\nimport jadx.core.utils.android.AppAttribute;\nimport jadx.core.utils.android.ApplicationParams;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.cache.manager.CacheManager;\nimport jadx.gui.device.debugger.BreakpointManager;\nimport jadx.gui.events.services.RenameService;\nimport jadx.gui.events.types.JadxGuiEventsImpl;\nimport jadx.gui.jobs.BackgroundExecutor;\nimport jadx.gui.jobs.DecompileTask;\nimport jadx.gui.jobs.ExportTask;\nimport jadx.gui.jobs.IBackgroundTask;\nimport jadx.gui.jobs.TaskStatus;\nimport jadx.gui.jobs.TaskWithExtraOnFinish;\nimport jadx.gui.logs.LogCollector;\nimport jadx.gui.logs.LogOptions;\nimport jadx.gui.logs.LogPanel;\nimport jadx.gui.plugins.context.CommonGuiPluginsContext;\nimport jadx.gui.plugins.context.TreePopupMenuEntry;\nimport jadx.gui.plugins.mappings.RenameMappingsGui;\nimport jadx.gui.plugins.quark.QuarkDialog;\nimport jadx.gui.report.ExceptionDialog;\nimport jadx.gui.report.JadxExceptionHandler;\nimport jadx.gui.settings.JadxProject;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.settings.data.SaveOptionEnum;\nimport jadx.gui.settings.ui.JadxSettingsWindow;\nimport jadx.gui.tree.TreeExpansionService;\nimport jadx.gui.treemodel.ApkSignatureNode;\nimport jadx.gui.treemodel.JLoadableNode;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.treemodel.JPackage;\nimport jadx.gui.treemodel.JResource;\nimport jadx.gui.treemodel.JRoot;\nimport jadx.gui.ui.action.ActionModel;\nimport jadx.gui.ui.action.JadxGuiAction;\nimport jadx.gui.ui.codearea.AbstractCodeArea;\nimport jadx.gui.ui.codearea.AbstractCodeContentPanel;\nimport jadx.gui.ui.codearea.EditorViewState;\nimport jadx.gui.ui.codearea.theme.EditorThemeManager;\nimport jadx.gui.ui.dialog.ADBDialog;\nimport jadx.gui.ui.dialog.AboutDialog;\nimport jadx.gui.ui.dialog.CharsetDialog;\nimport jadx.gui.ui.dialog.GotoAddressDialog;\nimport jadx.gui.ui.dialog.LogViewerDialog;\nimport jadx.gui.ui.dialog.SearchDialog;\nimport jadx.gui.ui.export.ExportProjectDialog;\nimport jadx.gui.ui.filedialog.FileDialogWrapper;\nimport jadx.gui.ui.filedialog.FileOpenMode;\nimport jadx.gui.ui.hexviewer.HexInspectorPanel;\nimport jadx.gui.ui.hexviewer.HexPreviewPanel;\nimport jadx.gui.ui.menu.HiddenMenuItem;\nimport jadx.gui.ui.menu.JadxMenu;\nimport jadx.gui.ui.menu.JadxMenuBar;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.panel.IssuesPanel;\nimport jadx.gui.ui.panel.JDebuggerPanel;\nimport jadx.gui.ui.panel.ProgressPanel;\nimport jadx.gui.ui.popupmenu.RecentProjectsMenuListener;\nimport jadx.gui.ui.startpage.StartPageNode;\nimport jadx.gui.ui.tab.EditorSyncManager;\nimport jadx.gui.ui.tab.NavigationController;\nimport jadx.gui.ui.tab.QuickTabsTree;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.ui.tab.TabsController;\nimport jadx.gui.ui.tab.dnd.TabDndController;\nimport jadx.gui.ui.treenodes.SummaryNode;\nimport jadx.gui.ui.treenodes.UndisplayedStringsNode;\nimport jadx.gui.update.JadxUpdate;\nimport jadx.gui.utils.CacheObject;\nimport jadx.gui.utils.DesktopEntryUtils;\nimport jadx.gui.utils.FontUtils;\nimport jadx.gui.utils.ILoadListener;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.LafManager;\nimport jadx.gui.utils.Link;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.dbg.UIWatchDog;\nimport jadx.gui.utils.fileswatcher.LiveReloadWorker;\nimport jadx.gui.utils.shortcut.ShortcutsController;\nimport jadx.gui.utils.ui.ActionHandler;\nimport jadx.gui.utils.ui.FileOpenerHelper;\nimport jadx.gui.utils.ui.NodeLabel;\n\npublic class MainWindow extends JFrame {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(MainWindow.class);\n\n\tprivate static final String DEFAULT_TITLE = \"jadx-gui\";\n\n\tprivate static final double BORDER_RATIO = 0.15;\n\tprivate static final double WINDOW_RATIO = 1 - BORDER_RATIO * 2;\n\tpublic static final double SPLIT_PANE_RESIZE_WEIGHT = 0.15;\n\n\tprivate final transient JadxWrapper wrapper;\n\tprivate final transient JadxSettings settings;\n\tprivate final transient CacheObject cacheObject;\n\tprivate final transient CacheManager cacheManager;\n\tprivate final transient BackgroundExecutor backgroundExecutor;\n\tprivate final transient JadxGuiEventsImpl events = new JadxGuiEventsImpl();\n\tprivate final transient TreeExpansionService treeExpansionService;\n\n\tprivate final TabsController tabsController;\n\tprivate final NavigationController navController;\n\tprivate final EditorSyncManager editorSyncManager;\n\tprivate final EditorThemeManager editorThemeManager;\n\n\tprivate transient @NotNull JadxProject project;\n\n\tprivate transient JadxGuiAction newProjectAction;\n\tprivate transient JadxGuiAction saveProjectAction;\n\n\tprivate transient JPanel mainPanel;\n\tprivate transient JSplitPane treeSplitPane;\n\tprivate transient JSplitPane rightSplitPane;\n\tprivate transient JSplitPane bottomSplitPane;\n\tprivate transient JSplitPane quickTabsAndCodeSplitPane;\n\n\tprivate JTree tree;\n\tprivate DefaultTreeModel treeModel;\n\tprivate JRoot treeRoot;\n\tprivate TabbedPane tabbedPane;\n\tprivate HeapUsageBar heapUsageBar;\n\tprivate transient boolean treeReloading;\n\n\tprivate boolean isFlattenPackage;\n\tprivate JToggleButton flatPkgButton;\n\tprivate JCheckBoxMenuItem flatPkgMenuItem;\n\n\tprivate JToggleButton deobfToggleBtn;\n\tprivate JCheckBoxMenuItem deobfMenuItem;\n\n\tprivate JCheckBoxMenuItem liveReloadMenuItem;\n\tprivate final LiveReloadWorker liveReloadWorker;\n\n\tprivate transient Link updateLink;\n\tprivate transient ProgressPanel progressPane;\n\n\tprivate transient IssuesPanel issuesPanel;\n\tprivate transient @Nullable LogPanel logPanel;\n\tprivate transient @Nullable JDebuggerPanel debuggerPanel;\n\tprivate transient @Nullable QuickTabsTree quickTabsTree;\n\n\tprivate final List<ILoadListener> loadListeners = new ArrayList<>();\n\tprivate final List<Consumer<JRoot>> treeUpdateListener = new ArrayList<>();\n\tprivate boolean loaded;\n\tprivate boolean settingsOpen = false;\n\tprivate boolean showUndisplayedCharsDialog;\n\n\tprivate final ShortcutsController shortcutsController;\n\tprivate JadxMenuBar menuBar;\n\tprivate JMenu pluginsMenu;\n\tpublic JMenu hexViewerMenu;\n\n\tprivate final transient RenameMappingsGui renameMappings;\n\n\tpublic MainWindow(JadxSettings settings) {\n\t\tthis.settings = settings;\n\t\tthis.project = new JadxProject(this);\n\t\tthis.wrapper = new JadxWrapper(this);\n\t\tthis.cacheObject = new CacheObject(wrapper);\n\t\tthis.liveReloadWorker = new LiveReloadWorker(this);\n\t\tthis.renameMappings = new RenameMappingsGui(this);\n\t\tthis.cacheManager = new CacheManager(settings);\n\t\tthis.shortcutsController = new ShortcutsController(settings);\n\t\tthis.tabsController = new TabsController(this);\n\t\tthis.navController = new NavigationController(this);\n\t\tthis.editorThemeManager = new EditorThemeManager(settings);\n\n\t\tJadxEventQueue.register();\n\t\tJadxExceptionHandler.register(this);\n\t\tresetCache();\n\t\tinitUI();\n\t\tthis.editorSyncManager = new EditorSyncManager(this, tabbedPane);\n\t\tthis.backgroundExecutor = new BackgroundExecutor(settings, progressPane);\n\t\tthis.treeExpansionService = new TreeExpansionService(this, tree);\n\t\tinitMenuAndToolbar();\n\t\tUiUtils.setWindowIcons(this);\n\t\tthis.shortcutsController.registerMouseEventListener(this);\n\t\tloadSettings();\n\t\tinitEvents();\n\n\t\tupdate();\n\t\tcheckForUpdate();\n\t}\n\n\tpublic void init() {\n\t\tpack();\n\t\tsetLocationAndPosition();\n\t\ttreeSplitPane.setDividerLocation(settings.getTreeWidth());\n\t\theapUsageBar.setVisible(settings.isShowHeapUsageBar());\n\t\tsetVisible(true);\n\t\tprocessCommandLineArgs();\n\t}\n\n\tprivate void processCommandLineArgs() {\n\t\tif (settings.getFiles().isEmpty()) {\n\t\t\ttabsController.selectTab(new StartPageNode());\n\t\t} else {\n\t\t\topen(FileUtils.fileNamesToPaths(settings.getFiles()), this::handleSelectClassOption);\n\t\t}\n\t}\n\n\tprivate void handleSelectClassOption() {\n\t\tif (settings.getCmdSelectClass() != null) {\n\t\t\tJavaNode javaNode = wrapper.searchJavaClassByFullAlias(settings.getCmdSelectClass());\n\t\t\tif (javaNode == null) {\n\t\t\t\tjavaNode = wrapper.searchJavaClassByOrigClassName(settings.getCmdSelectClass());\n\t\t\t}\n\t\t\tif (javaNode == null) {\n\t\t\t\tJOptionPane.showMessageDialog(this,\n\t\t\t\t\t\tNLS.str(\"msg.cmd_select_class_error\", settings.getCmdSelectClass()),\n\t\t\t\t\t\tNLS.str(\"error_dialog.title\"), JOptionPane.ERROR_MESSAGE);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttabsController.codeJump(cacheObject.getNodeCache().makeFrom(javaNode));\n\t\t}\n\t}\n\n\tprivate void checkForUpdate() {\n\t\tif (!settings.isCheckForUpdates()) {\n\t\t\treturn;\n\t\t}\n\t\tnew JadxUpdate().check(settings.getJadxUpdateChannel(), release -> SwingUtilities.invokeLater(() -> {\n\t\t\tswitch (settings.getJadxUpdateChannel()) {\n\t\t\t\tcase STABLE:\n\t\t\t\t\tupdateLink.setUrl(JadxUpdate.JADX_RELEASES_URL);\n\t\t\t\t\tbreak;\n\t\t\t\tcase UNSTABLE:\n\t\t\t\t\tupdateLink.setUrl(JadxUpdate.JADX_ARTIFACTS_URL);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tupdateLink.setText(NLS.str(\"menu.update_label\", release.getName()));\n\t\t\tupdateLink.setVisible(true);\n\t\t}));\n\t}\n\n\tpublic void openFileDialog() {\n\t\tshowOpenDialog(FileOpenMode.OPEN);\n\t}\n\n\tpublic void openProjectDialog() {\n\t\tshowOpenDialog(FileOpenMode.OPEN_PROJECT);\n\t}\n\n\tprivate void showOpenDialog(FileOpenMode mode) {\n\t\tsaveAll();\n\t\tif (!ensureProjectIsSaved()) {\n\t\t\treturn;\n\t\t}\n\t\tFileDialogWrapper fileDialog = new FileDialogWrapper(this, mode);\n\t\tList<Path> openPaths = fileDialog.show();\n\t\tif (!openPaths.isEmpty()) {\n\t\t\tsettings.setLastOpenFilePath(fileDialog.getCurrentDir());\n\t\t\topen(openPaths);\n\t\t}\n\t}\n\n\tpublic void addFiles() {\n\t\tFileDialogWrapper fileDialog = new FileDialogWrapper(this, FileOpenMode.ADD);\n\t\tList<Path> addPaths = fileDialog.show();\n\t\tif (!addPaths.isEmpty()) {\n\t\t\taddFiles(addPaths);\n\t\t}\n\t}\n\n\tpublic void addFiles(List<Path> addPaths) {\n\t\tproject.setFilePaths(ListUtils.distinctMergeSortedLists(addPaths, project.getFilePaths()));\n\t\treopen();\n\t}\n\n\tprivate void newProject() {\n\t\tsaveAll();\n\t\tif (!ensureProjectIsSaved()) {\n\t\t\treturn;\n\t\t}\n\t\tUiUtils.bgRun(() -> {\n\t\t\tcloseAll();\n\t\t\tupdateProject(new JadxProject(this));\n\t\t});\n\t}\n\n\tprivate void saveProject() {\n\t\tsaveOpenTabs();\n\t\tif (!project.isSaveFileSelected()) {\n\t\t\tsaveProjectAs();\n\t\t} else {\n\t\t\tproject.save();\n\t\t\tupdate();\n\t\t}\n\t}\n\n\tprivate void saveProjectAs() {\n\t\tFileDialogWrapper fileDialog = new FileDialogWrapper(this, FileOpenMode.SAVE_PROJECT);\n\t\tif (project.getFilePaths().size() == 1) {\n\t\t\t// If there is only one file loaded we suggest saving the jadx project file next to the loaded file\n\t\t\tPath projectPath = getProjectPathForFile(this.project.getFilePaths().get(0));\n\t\t\tfileDialog.setSelectedFile(projectPath);\n\t\t}\n\t\tList<Path> saveFiles = fileDialog.show();\n\t\tif (saveFiles.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tsettings.setLastSaveProjectPath(fileDialog.getCurrentDir());\n\t\tPath savePath = saveFiles.get(0);\n\t\tif (!savePath.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(JadxProject.PROJECT_EXTENSION)) {\n\t\t\tsavePath = savePath.resolveSibling(savePath.getFileName() + \".\" + JadxProject.PROJECT_EXTENSION);\n\t\t}\n\t\tif (Files.exists(savePath)) {\n\t\t\tint res = JOptionPane.showConfirmDialog(\n\t\t\t\t\tthis,\n\t\t\t\t\tNLS.str(\"confirm.save_as_message\", savePath.getFileName()),\n\t\t\t\t\tNLS.str(\"confirm.save_as_title\"),\n\t\t\t\t\tJOptionPane.YES_NO_OPTION);\n\t\t\tif (res == JOptionPane.NO_OPTION) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tproject.saveAs(savePath);\n\t\tsettings.addRecentProject(savePath);\n\t\tupdate();\n\t}\n\n\tpublic void removeInput(Path file) {\n\t\tint dialogResult = JOptionPane.showConfirmDialog(\n\t\t\t\tthis,\n\t\t\t\tNLS.str(\"message.confirm_remove_script\"),\n\t\t\t\tNLS.str(\"msg.warning_title\"),\n\t\t\t\tJOptionPane.YES_NO_OPTION);\n\t\tif (dialogResult == JOptionPane.NO_OPTION) {\n\t\t\treturn;\n\t\t}\n\n\t\tList<Path> inputs = project.getFilePaths();\n\t\tinputs.remove(file);\n\t\trefreshTree(inputs);\n\t}\n\n\tpublic void renameInput(Path file) {\n\t\tString newName = JOptionPane.showInputDialog(this, NLS.str(\"message.enter_new_name\"), file.getFileName().toString());\n\t\tif (newName == null || newName.trim().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tPath targetPath = file.resolveSibling(newName);\n\n\t\tboolean success = FileUtils.renameFile(file, targetPath);\n\t\tif (success) {\n\t\t\tList<Path> inputs = project.getFilePaths();\n\t\t\tinputs.remove(file);\n\t\t\tinputs.add(targetPath);\n\n\t\t\trefreshTree(inputs);\n\t\t} else {\n\t\t\tJOptionPane.showMessageDialog(this,\n\t\t\t\t\tNLS.str(\"message.could_not_rename\"),\n\t\t\t\t\tNLS.str(\"message.errorTitle\"),\n\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t}\n\t}\n\n\tprivate void refreshTree(List<Path> inputs) {\n\t\tproject.setFilePaths(inputs);\n\t\tproject.save();\n\t\treopen();\n\t}\n\n\tpublic void open(Path path) {\n\t\topen(Collections.singletonList(path), UiUtils.EMPTY_RUNNABLE);\n\t}\n\n\tpublic void open(List<Path> paths) {\n\t\topen(paths, UiUtils.EMPTY_RUNNABLE);\n\t}\n\n\tprivate void open(List<Path> paths, Runnable onFinish) {\n\t\tsaveAll();\n\t\tUiUtils.bgRun(() -> {\n\t\t\tcloseAll();\n\t\t\tif (paths.size() == 1 && openSingleFile(paths.get(0), onFinish)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// start new project\n\t\t\tproject = new JadxProject(this);\n\t\t\tproject.setFilePaths(paths);\n\t\t\tshowUndisplayedCharsDialog = false;\n\t\t\tloadFiles(onFinish);\n\t\t});\n\t}\n\n\tprivate boolean openSingleFile(Path singleFile, Runnable onFinish) {\n\t\tif (singleFile.getFileName() == null) {\n\t\t\treturn false;\n\t\t}\n\t\tString fileExtension = CommonFileUtils.getFileExtension(singleFile.getFileName().toString());\n\t\tif (fileExtension != null && fileExtension.equalsIgnoreCase(JadxProject.PROJECT_EXTENSION)) {\n\t\t\topenProject(singleFile, onFinish);\n\t\t\treturn true;\n\t\t}\n\t\t// check if project file already saved with default name\n\t\tPath projectPath = getProjectPathForFile(singleFile);\n\t\tif (Files.exists(projectPath)) {\n\t\t\topenProject(projectPath, onFinish);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static Path getProjectPathForFile(Path loadedFile) {\n\t\tString fileName = loadedFile.getFileName() + \".\" + JadxProject.PROJECT_EXTENSION;\n\t\treturn loadedFile.resolveSibling(fileName);\n\t}\n\n\tpublic void reopen() {\n\t\tLOG.debug(\"starting reopen\");\n\t\tUiUtils.bgRun(() -> {\n\t\t\tgetBackgroundExecutor().waitForComplete();\n\t\t\tsynchronized (ReloadProject.EVENT) {\n\t\t\t\tsaveAll();\n\t\t\t\tcloseAll();\n\t\t\t\tSystem.gc();\n\t\t\t\tloadFiles(() -> {\n\t\t\t\t\tmenuBar.reloadShortcuts();\n\t\t\t\t\tevents().send(ReloadSettingsWindow.INSTANCE);\n\t\t\t\t\tLOG.debug(\"reopen complete\");\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void openProject(Path path, Runnable onFinish) {\n\t\tLOG.debug(\"Loading project: {}\", path);\n\t\tproject = JadxProject.load(this, path);\n\t\tsettings.addRecentProject(path);\n\t\tloadFiles(onFinish);\n\t}\n\n\tprivate void loadFiles(Runnable onFinish) {\n\t\tif (project.getFilePaths().isEmpty()) {\n\t\t\ttabsController.selectTab(new StartPageNode());\n\t\t\tonFinish.run();\n\t\t\treturn;\n\t\t}\n\t\tbackgroundExecutor.execute(NLS.str(\"progress.load\"),\n\t\t\t\t() -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\twrapper.open();\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLOG.error(\"Project load error\", e);\n\t\t\t\t\t\tcloseAll();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tstatus -> {\n\t\t\t\t\tif (status == TaskStatus.CANCEL_BY_MEMORY) {\n\t\t\t\t\t\tshowHeapUsageBar();\n\t\t\t\t\t\tUiUtils.errorMessage(this, NLS.str(\"message.memoryLow\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (status != TaskStatus.COMPLETE) {\n\t\t\t\t\t\tLOG.warn(\"Loading task incomplete, status: {}\", status);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcheckLoadedStatus();\n\t\t\t\t\tonOpen(onFinish);\n\t\t\t\t});\n\t}\n\n\tprivate void saveAll() {\n\t\tsaveOpenTabs();\n\t\tproject.setTreeExpansions(treeExpansionService.save());\n\t\tBreakpointManager.saveAndExit();\n\t}\n\n\tprivate void closeAll() {\n\t\tUiUtils.notUiThreadGuard();\n\t\tcancelBackgroundJobs();\n\t\tUiUtils.uiRunAndWait(() -> {\n\t\t\ttabsController.forceCloseAllTabs();\n\t\t\ttabbedPane.reset();\n\t\t\tnavController.reset();\n\t\t\tshortcutsController.reset();\n\t\t\tclearTree();\n\t\t\tUiUtils.resetClipboardOwner();\n\t\t\tupdate();\n\t\t});\n\t\twrapper.close();\n\t\tLogCollector.getInstance().reset();\n\t\tresetCache();\n\t\tnotifyLoadListeners(false);\n\t}\n\n\tprivate void checkLoadedStatus() {\n\t\tif (!wrapper.getClasses().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tint errors = issuesPanel.getErrorsCount();\n\t\tif (errors > 0) {\n\t\t\tint result = JOptionPane.showConfirmDialog(this,\n\t\t\t\t\tNLS.str(\"message.load_errors\", errors),\n\t\t\t\t\tNLS.str(\"message.errorTitle\"),\n\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION,\n\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t\tif (result == JOptionPane.OK_OPTION) {\n\t\t\t\tshowLogViewer(LogOptions.allWithLevel(Level.ERROR));\n\t\t\t}\n\t\t} else {\n\t\t\tshowLogViewer(LogOptions.allWithLevel(Level.WARN));\n\t\t\tUiUtils.showMessageBox(this, NLS.str(\"message.no_classes\"));\n\t\t}\n\t}\n\n\tprivate void onOpen(Runnable onFinish) {\n\t\tinitTree();\n\t\tupdateLiveReload(project.isEnableLiveReload());\n\t\tBreakpointManager.init(project.getFilePaths().get(0).toAbsolutePath().getParent());\n\t\tList<EditorViewState> openTabs = project.getOpenTabs(this);\n\t\tbackgroundExecutor.startLoading(\n\t\t\t\t() -> preLoadOpenTabs(openTabs),\n\t\t\t\t() -> {\n\t\t\t\t\trestoreOpenTabs(openTabs);\n\t\t\t\t\tupdate();\n\t\t\t\t\tnotifyLoadListeners(true);\n\t\t\t\t\tonFinish.run();\n\t\t\t\t\tcheckIfCodeHasNonPrintableChars();\n\t\t\t\t\trunInitialBackgroundJobs();\n\t\t\t\t});\n\t\t// queue tree state restore after loading task\n\t\ttreeExpansionService.load(project.getTreeExpansions());\n\t}\n\n\tpublic void passesReloaded() {\n\t\tUiUtils.uiThreadGuard();\n\t\ttabbedPane.reloadInactiveTabs();\n\t\treloadTreePreservingState();\n\t}\n\n\tprivate void initEvents() {\n\t\tevents().global().addListener(JadxEvents.RELOAD_PROJECT, ev -> UiUtils.uiRun(this::reopen));\n\t\tRenameService.init(this);\n\t}\n\n\tpublic void updateLiveReload(boolean state) {\n\t\tif (liveReloadWorker.isStarted() == state) {\n\t\t\treturn;\n\t\t}\n\t\tproject.setEnableLiveReload(state);\n\t\tliveReloadMenuItem.setEnabled(false);\n\t\tbackgroundExecutor.execute(\n\t\t\t\t(state ? \"Starting\" : \"Stopping\") + \" live reload\",\n\t\t\t\t() -> liveReloadWorker.updateState(state),\n\t\t\t\ts -> {\n\t\t\t\t\tliveReloadMenuItem.setState(state);\n\t\t\t\t\tliveReloadMenuItem.setEnabled(true);\n\t\t\t\t});\n\t}\n\n\tprivate void addTreeCustomNodes() {\n\t\ttreeRoot.replaceCustomNode(ApkSignatureNode.getApkSignature(wrapper));\n\t\ttreeRoot.replaceCustomNode(new SummaryNode(this));\n\t}\n\n\tprivate boolean ensureProjectIsSaved() {\n\t\tif (project.isSaved() || project.isInitial()) {\n\t\t\treturn true;\n\t\t}\n\t\tif (project.getFilePaths().isEmpty()) {\n\t\t\t// ignore blank project save\n\t\t\treturn true;\n\t\t}\n\t\t// Check if we saved settings that indicate what to do\n\t\tif (settings.getSaveOption() == SaveOptionEnum.NEVER) {\n\t\t\treturn true;\n\t\t}\n\t\tif (settings.getSaveOption() == SaveOptionEnum.ALWAYS) {\n\t\t\tsaveProject();\n\t\t\treturn true;\n\t\t}\n\n\t\tJCheckBox remember = new JCheckBox(NLS.str(\"confirm.remember\"));\n\t\tJLabel message = new JLabel(NLS.str(\"confirm.not_saved_message\"));\n\n\t\tJPanel inner = new JPanel(new BorderLayout());\n\t\tinner.add(remember, BorderLayout.SOUTH);\n\t\tinner.add(message, BorderLayout.NORTH);\n\n\t\tint res = JOptionPane.showConfirmDialog(\n\t\t\t\tthis,\n\t\t\t\tinner,\n\t\t\t\tNLS.str(\"confirm.not_saved_title\"),\n\t\t\t\tJOptionPane.YES_NO_CANCEL_OPTION);\n\t\tswitch (res) {\n\t\t\tcase JOptionPane.YES_OPTION:\n\t\t\t\tif (remember.isSelected()) {\n\t\t\t\t\tsettings.setSaveOption(SaveOptionEnum.ALWAYS);\n\t\t\t\t\tsettings.sync();\n\t\t\t\t}\n\t\t\t\tsaveProject();\n\t\t\t\treturn true;\n\n\t\t\tcase JOptionPane.NO_OPTION:\n\t\t\t\tif (remember.isSelected()) {\n\t\t\t\t\tsettings.setSaveOption(SaveOptionEnum.NEVER);\n\t\t\t\t\tsettings.sync();\n\t\t\t\t}\n\t\t\t\treturn true;\n\n\t\t\tcase JOptionPane.CANCEL_OPTION:\n\t\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic void updateProject(@NotNull JadxProject jadxProject) {\n\t\tthis.project = jadxProject;\n\t\tUiUtils.uiRun(this::update);\n\t}\n\n\tpublic void update() {\n\t\tUiUtils.uiThreadGuard();\n\t\tnewProjectAction.setEnabled(!project.isInitial());\n\t\tsaveProjectAction.setEnabled(loaded && !project.isSaved());\n\t\tdeobfToggleBtn.setSelected(settings.isDeobfuscationOn());\n\t\trenameMappings.onUpdate(loaded);\n\n\t\tPath projectPath = project.getProjectPath();\n\t\tString pathString;\n\t\tif (projectPath == null) {\n\t\t\tpathString = \"\";\n\t\t} else {\n\t\t\tpathString = \" [\" + projectPath.toAbsolutePath().getParent() + ']';\n\t\t}\n\t\tsetTitle((project.isSaved() ? \"\" : '*')\n\t\t\t\t+ project.getName() + pathString + \" - \" + DEFAULT_TITLE);\n\t}\n\n\tprotected void resetCache() {\n\t\tcacheObject.reset();\n\t}\n\n\tsynchronized void runInitialBackgroundJobs() {\n\t\tif (settings.isAutoStartJobs()) {\n\t\t\tnew Timer().schedule(new TimerTask() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\trequestFullDecompilation();\n\t\t\t\t}\n\t\t\t}, 1000);\n\t\t}\n\t}\n\n\tpublic void requestFullDecompilation() {\n\t\tif (cacheObject.isFullDecompilationFinished()) {\n\t\t\treturn;\n\t\t}\n\t\tbackgroundExecutor.execute(new DecompileTask(this));\n\t}\n\n\tpublic void resetCodeCache() {\n\t\tbackgroundExecutor.execute(\n\t\t\t\tNLS.str(\"preferences.cache.task.delete\"),\n\t\t\t\t() -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tgetWrapper().getCurrentDecompiler().ifPresent(jadx -> {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tjadx.getArgs().getCodeCache().close();\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tLOG.error(\"Failed to close code cache\", e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t\tPath cacheDir = project.getCacheDir();\n\t\t\t\t\t\tproject.resetCacheDir();\n\t\t\t\t\t\tFileUtils.deleteDirIfExists(cacheDir);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLOG.error(\"Error during code cache reset\", e);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tstatus -> events().send(ReloadProject.EVENT));\n\t}\n\n\tpublic void cancelBackgroundJobs() {\n\t\tbackgroundExecutor.cancelAll();\n\t}\n\n\tpublic void exportProject() {\n\t\tExportProjectDialog dialog = new ExportProjectDialog(this, props -> {\n\t\t\tJadxArgs args = wrapper.getArgs();\n\t\t\tif (props.isAsGradleMode()) {\n\t\t\t\targs.setExportGradleType(props.getExportGradleType());\n\t\t\t\targs.setSkipSources(false);\n\t\t\t\targs.setSkipResources(false);\n\t\t\t} else {\n\t\t\t\targs.setExportGradleType(null);\n\t\t\t\targs.setSkipSources(props.isSkipSources());\n\t\t\t\targs.setSkipResources(props.isSkipResources());\n\t\t\t}\n\t\t\tbackgroundExecutor.execute(new ExportTask(this, wrapper, new File(props.getExportPath())));\n\t\t});\n\t\tdialog.setVisible(true);\n\t}\n\n\tpublic void initTree() {\n\t\ttreeRoot = new JRoot(this);\n\t\ttreeRoot.setFlatPackages(isFlattenPackage);\n\t\ttreeModel.setRoot(treeRoot);\n\t\taddTreeCustomNodes();\n\t\ttreeRoot.update();\n\t\treloadTree();\n\t}\n\n\tprivate void clearTree() {\n\t\ttreeRoot = null;\n\t\ttreeModel.setRoot(null);\n\t\ttreeModel.reload();\n\t}\n\n\tpublic void reloadTree() {\n\t\ttreeReloading = true;\n\t\ttreeUpdateListener.forEach(listener -> listener.accept(treeRoot));\n\t\ttreeModel.reload();\n\t\ttreeReloading = false;\n\t}\n\n\tpublic void rebuildPackagesTree() {\n\t\ttreeRoot.update();\n\t}\n\n\t/**\n\t * Simple save and restore tree state after renaming.\n\t * TODO: maybe need improve for find and update only changed node\n\t */\n\tpublic void reloadTreePreservingState() {\n\t\tList<String> treePath = treeExpansionService.save();\n\t\treloadTree();\n\t\ttreeExpansionService.load(treePath);\n\t}\n\n\tprivate void toggleFlattenPackage() {\n\t\tsetFlattenPackage(!isFlattenPackage);\n\t}\n\n\tprivate void setFlattenPackage(boolean value) {\n\t\tisFlattenPackage = value;\n\t\tsettings.setFlattenPackage(isFlattenPackage);\n\n\t\tflatPkgButton.setSelected(isFlattenPackage);\n\t\tflatPkgMenuItem.setState(isFlattenPackage);\n\n\t\tObject root = treeModel.getRoot();\n\t\tif (root instanceof JRoot) {\n\t\t\tJRoot treeRoot = (JRoot) root;\n\t\t\ttreeRoot.setFlatPackages(isFlattenPackage);\n\t\t\treloadTree();\n\t\t}\n\t}\n\n\tprivate void toggleDeobfuscation() {\n\t\tboolean deobfOn = !settings.isDeobfuscationOn();\n\t\tsettings.setDeobfuscationOn(deobfOn);\n\t\tsettings.sync();\n\n\t\tdeobfToggleBtn.setSelected(deobfOn);\n\t\tdeobfMenuItem.setState(deobfOn);\n\t\treopen();\n\t}\n\n\tprivate boolean nodeClickAction(@Nullable Object obj) {\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tif (obj instanceof JResource) {\n\t\t\t\tJResource res = (JResource) obj;\n\t\t\t\tResourceFile resFile = res.getResFile();\n\t\t\t\tif (resFile != null) {\n\t\t\t\t\tif (JResource.isOpenInExternalTool(resFile.getType())) {\n\t\t\t\t\t\tFileOpenerHelper.openFile(this, res);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tif (JResource.isSupportedForView(resFile.getType())) {\n\t\t\t\t\t\ttabsController.selectTab(res, true);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (obj instanceof JNode) {\n\t\t\t\tJNode treeNode = (JNode) obj;\n\t\t\t\tif (treeNode.hasContent() || treeNode.getJParent() != null) {\n\t\t\t\t\ttabsController.codeJump(treeNode, true);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Content loading error\", e);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate void treeRightClickAction(MouseEvent e) {\n\t\tJNode node = getJNodeUnderMouse(e);\n\t\tif (node == null) {\n\t\t\treturn;\n\t\t}\n\t\tJPopupMenu menu = node.onTreePopupMenu(this);\n\t\tCommonGuiPluginsContext pluginsContext = getWrapper().getGuiPluginsContext();\n\t\tfor (TreePopupMenuEntry entry : pluginsContext.getTreePopupMenuEntries()) {\n\t\t\tJMenuItem menuItem = entry.buildEntry(node);\n\t\t\tif (menuItem != null) {\n\t\t\t\tif (menu == null) {\n\t\t\t\t\tmenu = new JPopupMenu();\n\t\t\t\t}\n\t\t\t\tmenu.add(menuItem);\n\t\t\t}\n\t\t}\n\t\tif (menu != null) {\n\t\t\tmenu.show(e.getComponent(), e.getX(), e.getY());\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate JNode getJNodeUnderMouse(MouseEvent mouseEvent) {\n\t\tTreeNode treeNode = UiUtils.getTreeNodeUnderMouse(tree, mouseEvent);\n\t\tif (treeNode instanceof JNode) {\n\t\t\treturn (JNode) treeNode;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t// TODO: extract tree component into new class\n\tpublic void selectNodeInTree(JNode node) {\n\t\tif (node.getParent() == null && treeRoot != null) {\n\t\t\t// node not register in tree\n\t\t\tnode = treeRoot.searchNode(node);\n\t\t\tif (node == null) {\n\t\t\t\tLOG.error(\"Class not found in tree\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tTreeNode[] pathNodes = treeModel.getPathToRoot(node);\n\t\tif (pathNodes == null) {\n\t\t\treturn;\n\t\t}\n\t\tTreePath path = new TreePath(pathNodes);\n\t\ttree.setSelectionPath(path);\n\t\ttree.makeVisible(path);\n\t\ttree.scrollPathToVisible(path);\n\t\ttree.requestFocus();\n\t}\n\n\tpublic void textSearch() {\n\t\tContentPanel panel = tabbedPane.getSelectedContentPanel();\n\t\tif (panel instanceof AbstractCodeContentPanel) {\n\t\t\tAbstractCodeArea codeArea = ((AbstractCodeContentPanel) panel).getCodeArea();\n\t\t\tif (codeArea != null) {\n\t\t\t\tString preferText = codeArea.getSelectedText();\n\t\t\t\tif (StringUtils.isEmpty(preferText)) {\n\t\t\t\t\tpreferText = codeArea.getWordUnderCaret();\n\t\t\t\t}\n\t\t\t\tif (!StringUtils.isEmpty(preferText)) {\n\t\t\t\t\tSearchDialog.searchText(MainWindow.this, preferText);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tSearchDialog.search(MainWindow.this, SearchDialog.SearchPreset.TEXT);\n\t}\n\n\tprivate void sendActionsToHexViewer(ActionModel action) {\n\t\tHexPreviewPanel hexPreviewPanel = getCurrentHexViewTab();\n\t\tif (hexPreviewPanel != null) {\n\t\t\tHexInspectorPanel inspector = hexPreviewPanel.getInspector();\n\t\t\tSectCodeArea hexEditor = hexPreviewPanel.getEditor();\n\t\t\tswitch (action) {\n\t\t\t\tcase HEX_VIEWER_SHOW_INSPECTOR:\n\t\t\t\t\thexPreviewPanel.getInspector().setVisible(!inspector.isVisible());\n\t\t\t\t\tbreak;\n\t\t\t\tcase HEX_VIEWER_CHANGE_ENCODING:\n\t\t\t\t\tString result = CharsetDialog.chooseCharset(this, hexEditor.getCharset().name());\n\t\t\t\t\tif (!StringUtils.isEmpty(result)) {\n\t\t\t\t\t\thexEditor.setCharset(Charset.forName(result));\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase HEX_VIEWER_GO_TO_ADDRESS:\n\t\t\t\t\tnew GotoAddressDialog().showSetSelectionDialog(hexEditor);\n\t\t\t\t\tbreak;\n\t\t\t\tcase HEX_VIEWER_FIND:\n\t\t\t\t\thexPreviewPanel.showSearchBar();\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic HexPreviewPanel getCurrentHexViewTab() {\n\t\tContentPanel panel = tabbedPane.getSelectedContentPanel();\n\t\tif (panel instanceof AbstractCodeContentPanel) {\n\t\t\tComponent childrenComponent = ((AbstractCodeContentPanel) panel).getChildrenComponent();\n\t\t\tif (childrenComponent instanceof HexPreviewPanel) {\n\t\t\t\treturn (HexPreviewPanel) childrenComponent;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic void toggleHexViewMenu() {\n\t\thexViewerMenu.setEnabled(getCurrentHexViewTab() != null);\n\t}\n\n\tpublic void goToMainActivity() {\n\t\tAndroidManifestParser parser = new AndroidManifestParser(\n\t\t\t\tAndroidManifestParser.getAndroidManifest(getWrapper().getResources()),\n\t\t\t\tEnumSet.of(AppAttribute.MAIN_ACTIVITY),\n\t\t\t\tgetWrapper().getArgs().getSecurity());\n\t\tif (!parser.isManifestFound()) {\n\t\t\tJOptionPane.showMessageDialog(MainWindow.this,\n\t\t\t\t\tNLS.str(\"error_dialog.not_found\", \"AndroidManifest.xml\"),\n\t\t\t\t\tNLS.str(\"error_dialog.title\"),\n\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tApplicationParams results = parser.parse();\n\t\t\tif (results.getMainActivity() == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Failed to get main activity name from manifest\");\n\t\t\t}\n\t\t\tJavaClass mainActivityClass = results.getMainActivityJavaClass(getWrapper().getDecompiler());\n\t\t\tif (mainActivityClass == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Failed to find main activity class: \" + results.getMainActivity());\n\t\t\t}\n\t\t\ttabsController.codeJump(getCacheObject().getNodeCache().makeFrom(mainActivityClass));\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Main activity not found\", e);\n\t\t\tJOptionPane.showMessageDialog(MainWindow.this,\n\t\t\t\t\tNLS.str(\"error_dialog.not_found\", \"Main Activity\"),\n\t\t\t\t\tNLS.str(\"error_dialog.title\"),\n\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t}\n\t}\n\n\tpublic void goToApplication() {\n\t\tAndroidManifestParser parser = new AndroidManifestParser(\n\t\t\t\tAndroidManifestParser.getAndroidManifest(getWrapper().getResources()),\n\t\t\t\tEnumSet.of(AppAttribute.APPLICATION),\n\t\t\t\tgetWrapper().getArgs().getSecurity());\n\t\tif (!parser.isManifestFound()) {\n\t\t\tJOptionPane.showMessageDialog(MainWindow.this,\n\t\t\t\t\tNLS.str(\"error_dialog.not_found\", \"AndroidManifest.xml\"),\n\t\t\t\t\tNLS.str(\"error_dialog.title\"),\n\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tApplicationParams results = parser.parse();\n\t\t\tif (results.getApplication() == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Failed to get application from manifest\");\n\t\t\t}\n\t\t\tJavaClass applicationClass = results.getApplicationJavaClass(getWrapper().getDecompiler());\n\t\t\tif (applicationClass == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Failed to find application class: \" + results.getApplication());\n\t\t\t}\n\t\t\ttabsController.codeJump(getCacheObject().getNodeCache().makeFrom(applicationClass));\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Application not found\", e);\n\t\t\tJOptionPane.showMessageDialog(MainWindow.this,\n\t\t\t\t\tNLS.str(\"error_dialog.not_found\", \"Application\"),\n\t\t\t\t\tNLS.str(\"error_dialog.title\"),\n\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t}\n\t}\n\n\tpublic void goToAndroidManifest() {\n\t\tResourceFile androidManifest = AndroidManifestParser.getAndroidManifest(getWrapper().getResources());\n\t\tif (androidManifest == null) {\n\t\t\tJOptionPane.showMessageDialog(MainWindow.this,\n\t\t\t\t\tNLS.str(\"error_dialog.not_found\", \"AndroidManifest.xml\"),\n\t\t\t\t\tNLS.str(\"error_dialog.title\"),\n\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t\treturn;\n\t\t}\n\n\t\tJResource res = new JResource(androidManifest, androidManifest.getDeobfName(), JResource.JResType.FILE);\n\t\ttabsController.codeJump(res);\n\t}\n\n\tprivate void initMenuAndToolbar() {\n\t\tJadxGuiAction openAction = new JadxGuiAction(ActionModel.OPEN, this::openFileDialog);\n\t\tJadxGuiAction openProject = new JadxGuiAction(ActionModel.OPEN_PROJECT, this::openProjectDialog);\n\n\t\tJadxGuiAction addFilesAction = new JadxGuiAction(ActionModel.ADD_FILES, () -> addFiles());\n\t\tnewProjectAction = new JadxGuiAction(ActionModel.NEW_PROJECT, this::newProject);\n\t\tsaveProjectAction = new JadxGuiAction(ActionModel.SAVE_PROJECT, this::saveProject);\n\t\tJadxGuiAction saveProjectAsAction = new JadxGuiAction(ActionModel.SAVE_PROJECT_AS, this::saveProjectAs);\n\t\tJadxGuiAction reloadAction = new JadxGuiAction(ActionModel.RELOAD, () -> UiUtils.uiRun(this::reopen));\n\t\tJadxGuiAction liveReloadAction = new JadxGuiAction(ActionModel.LIVE_RELOAD,\n\t\t\t\t() -> updateLiveReload(!project.isEnableLiveReload()));\n\n\t\tliveReloadMenuItem = new JCheckBoxMenuItem(liveReloadAction);\n\t\tliveReloadMenuItem.setState(project.isEnableLiveReload());\n\n\t\tJadxGuiAction exportAction = new JadxGuiAction(ActionModel.EXPORT, this::exportProject);\n\n\t\tJMenu recentProjects = new JadxMenu(NLS.str(\"menu.recent_projects\"), shortcutsController);\n\t\trecentProjects.addMenuListener(new RecentProjectsMenuListener(this, recentProjects));\n\n\t\thexViewerMenu = new JadxMenu(NLS.str(\"menu.hex_viewer\"), shortcutsController);\n\t\tinitHexViewMenu();\n\n\t\tJadxGuiAction prefsAction = new JadxGuiAction(ActionModel.PREFS, () -> openSettings());\n\t\tJadxGuiAction exitAction = new JadxGuiAction(ActionModel.EXIT, this::closeWindow);\n\n\t\tisFlattenPackage = settings.isFlattenPackage();\n\t\tflatPkgMenuItem = new JCheckBoxMenuItem(NLS.str(\"menu.flatten\"), Icons.FLAT_PKG);\n\t\tflatPkgMenuItem.setState(isFlattenPackage);\n\n\t\tJadxGuiAction enablePreviewTabAction = new JadxGuiAction(ActionModel.PREVIEW_TAB, () -> {\n\t\t\tsettings.setEnablePreviewTab(!settings.isEnablePreviewTab());\n\t\t});\n\t\tenablePreviewTabAction.setSelected(settings.isEnablePreviewTab());\n\n\t\tJCheckBoxMenuItem heapUsageBarMenuItem = new JCheckBoxMenuItem(NLS.str(\"menu.heapUsageBar\"));\n\t\theapUsageBarMenuItem.setState(settings.isShowHeapUsageBar());\n\t\theapUsageBarMenuItem.addActionListener(event -> {\n\t\t\tsettings.setShowHeapUsageBar(!settings.isShowHeapUsageBar());\n\t\t\theapUsageBar.setVisible(settings.isShowHeapUsageBar());\n\t\t});\n\n\t\tJCheckBoxMenuItem alwaysSelectOpened = new JCheckBoxMenuItem(NLS.str(\"menu.alwaysSelectOpened\"));\n\t\talwaysSelectOpened.setState(settings.isAlwaysSelectOpened());\n\t\talwaysSelectOpened.addActionListener(event -> {\n\t\t\tsettings.setAlwaysSelectOpened(!settings.isAlwaysSelectOpened());\n\t\t\tif (settings.isAlwaysSelectOpened()) {\n\t\t\t\tthis.editorSyncManager.sync();\n\t\t\t}\n\t\t});\n\n\t\tJCheckBoxMenuItem dockLog = new JCheckBoxMenuItem(NLS.str(\"menu.dock_log\"));\n\t\tdockLog.setState(settings.isDockLogViewer());\n\t\tdockLog.addActionListener(event -> settings.saveDockLogViewer(!settings.isDockLogViewer()));\n\n\t\tActionHandler quickTabsAction = new ActionHandler(ev -> {\n\t\t\tboolean visible = quickTabsTree == null;\n\t\t\tsetQuickTabsVisibility(visible);\n\t\t\tsettings.saveDockQuickTabs(visible);\n\t\t});\n\t\tquickTabsAction.setNameAndDesc(NLS.str(\"menu.dock_quick_tabs\"));\n\t\tquickTabsAction.setIcon(Icons.QUICK_TABS);\n\t\tquickTabsAction.setSelected(settings.isDockQuickTabs());\n\t\tsetQuickTabsVisibility(settings.isDockQuickTabs());\n\n\t\tJadxGuiAction syncAction = new JadxGuiAction(ActionModel.SYNC, this.editorSyncManager::sync);\n\t\tJadxGuiAction textSearchAction = new JadxGuiAction(ActionModel.TEXT_SEARCH, this::textSearch);\n\t\tJadxGuiAction clsSearchAction = new JadxGuiAction(ActionModel.CLASS_SEARCH,\n\t\t\t\t() -> SearchDialog.search(MainWindow.this, SearchDialog.SearchPreset.CLASS));\n\t\tJadxGuiAction commentSearchAction = new JadxGuiAction(ActionModel.COMMENT_SEARCH,\n\t\t\t\t() -> SearchDialog.search(MainWindow.this, SearchDialog.SearchPreset.COMMENT));\n\t\tJadxGuiAction goToMainActivityAction = new JadxGuiAction(ActionModel.GO_TO_MAIN_ACTIVITY,\n\t\t\t\tthis::goToMainActivity);\n\t\tJadxGuiAction goToApplicationAction = new JadxGuiAction(ActionModel.GO_TO_APPLICATION,\n\t\t\t\tthis::goToApplication);\n\t\tJadxGuiAction goToAndroidManifestAction = new JadxGuiAction(ActionModel.GO_TO_ANDROID_MANIFEST, this::goToAndroidManifest);\n\t\tJadxGuiAction decompileAllAction = new JadxGuiAction(ActionModel.DECOMPILE_ALL, this::requestFullDecompilation);\n\t\tJadxGuiAction resetCacheAction = new JadxGuiAction(ActionModel.RESET_CACHE, this::resetCodeCache);\n\t\tJadxGuiAction deobfAction = new JadxGuiAction(ActionModel.DEOBF, this::toggleDeobfuscation);\n\n\t\tdeobfToggleBtn = new JToggleButton(deobfAction);\n\t\tdeobfToggleBtn.setSelected(settings.isDeobfuscationOn());\n\t\tdeobfToggleBtn.setText(\"\");\n\n\t\tdeobfMenuItem = new JCheckBoxMenuItem(deobfAction);\n\t\tdeobfMenuItem.setState(settings.isDeobfuscationOn());\n\n\t\tJadxGuiAction showLogAction = new JadxGuiAction(ActionModel.SHOW_LOG,\n\t\t\t\t() -> showLogViewer(LogOptions.current()));\n\t\tJadxGuiAction aboutAction = new JadxGuiAction(ActionModel.ABOUT, () -> new AboutDialog().setVisible(true));\n\t\tJadxGuiAction backAction = new JadxGuiAction(ActionModel.BACK, navController::navBack);\n\t\tJadxGuiAction backVariantAction = new JadxGuiAction(ActionModel.BACK_V, navController::navBack);\n\t\tJadxGuiAction forwardAction = new JadxGuiAction(ActionModel.FORWARD, navController::navForward);\n\t\tJadxGuiAction forwardVariantAction = new JadxGuiAction(ActionModel.FORWARD_V, navController::navForward);\n\t\tJadxGuiAction quarkAction = new JadxGuiAction(ActionModel.QUARK,\n\t\t\t\t() -> new QuarkDialog(MainWindow.this).setVisible(true));\n\t\tJadxGuiAction debuggerAction = new JadxGuiAction(ActionModel.OPEN_DEVICE,\n\t\t\t\t() -> new ADBDialog(MainWindow.this).setVisible(true));\n\n\t\tJMenu file = new JadxMenu(NLS.str(\"menu.file\"), shortcutsController);\n\t\tfile.setMnemonic(KeyEvent.VK_F);\n\t\tfile.add(openAction);\n\t\tfile.add(openProject);\n\t\tfile.add(addFilesAction);\n\t\tfile.addSeparator();\n\t\tfile.add(newProjectAction);\n\t\tfile.add(saveProjectAction);\n\t\tfile.add(saveProjectAsAction);\n\t\tfile.addSeparator();\n\t\tfile.add(reloadAction);\n\t\tfile.add(liveReloadMenuItem);\n\t\trenameMappings.addMenuActions(file);\n\t\tfile.addSeparator();\n\t\tfile.add(exportAction);\n\t\tfile.addSeparator();\n\t\tfile.add(recentProjects);\n\t\tfile.addSeparator();\n\t\tfile.add(prefsAction);\n\t\tfile.addSeparator();\n\t\tfile.add(exitAction);\n\n\t\tJMenu view = new JadxMenu(NLS.str(\"menu.view\"), shortcutsController);\n\t\tview.setMnemonic(KeyEvent.VK_V);\n\t\tview.add(quickTabsAction.makeCheckBoxMenuItem());\n\t\tview.add(hexViewerMenu);\n\t\tview.add(flatPkgMenuItem);\n\t\tview.addSeparator();\n\t\tview.add(enablePreviewTabAction.makeCheckBoxMenuItem());\n\t\tview.add(syncAction);\n\t\tview.add(alwaysSelectOpened);\n\t\tview.addSeparator();\n\t\tview.add(dockLog);\n\t\tview.add(heapUsageBarMenuItem);\n\n\t\tJMenu nav = new JadxMenu(NLS.str(\"menu.navigation\"), shortcutsController);\n\t\tnav.setMnemonic(KeyEvent.VK_N);\n\t\tnav.add(textSearchAction);\n\t\tnav.add(clsSearchAction);\n\t\tnav.add(commentSearchAction);\n\t\tnav.add(goToMainActivityAction);\n\t\tnav.add(goToApplicationAction);\n\t\tnav.add(goToAndroidManifestAction);\n\t\tnav.addSeparator();\n\t\tnav.add(backAction);\n\t\tnav.add(forwardAction);\n\n\t\tpluginsMenu = new JadxMenu(NLS.str(\"menu.plugins\"), shortcutsController);\n\t\tpluginsMenu.setMnemonic(KeyEvent.VK_P);\n\t\tresetPluginsMenu();\n\n\t\tJMenu tools = new JadxMenu(NLS.str(\"menu.tools\"), shortcutsController);\n\t\ttools.setMnemonic(KeyEvent.VK_T);\n\t\ttools.add(decompileAllAction);\n\t\ttools.add(resetCacheAction);\n\t\ttools.add(deobfMenuItem);\n\t\ttools.add(quarkAction);\n\t\ttools.add(debuggerAction);\n\n\t\tJMenu help = new JadxMenu(NLS.str(\"menu.help\"), shortcutsController);\n\t\thelp.setMnemonic(KeyEvent.VK_H);\n\t\thelp.add(showLogAction);\n\t\tif (JadxSystemInfo.IS_LINUX) {\n\t\t\thelp.add(new JadxGuiAction(ActionModel.CREATE_DESKTOP_ENTRY, this::createDesktopEntry));\n\t\t}\n\t\tif (Jadx.isDevVersion()) {\n\t\t\thelp.add(new AbstractAction(\"Show sample error report\") {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t\tExceptionDialog.throwTestException();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tif (UiUtils.JADX_GUI_DEBUG) {\n\t\t\tJCheckBoxMenuItem uiWatchDog = new JCheckBoxMenuItem(new ActionHandler(\"UI WatchDog\", UIWatchDog::toggle));\n\t\t\tuiWatchDog.setState(UIWatchDog.onStart());\n\t\t\thelp.add(uiWatchDog);\n\t\t}\n\n\t\tif (JadxSystemInfo.IS_MAC) {\n\t\t\tSystem.setProperty(\"apple.laf.useScreenMenuBar\", \"true\");\n\t\t\tDesktop.getDesktop().setAboutHandler(e -> aboutAction.actionPerformed(null));\n\t\t} else {\n\t\t\thelp.add(aboutAction);\n\t\t}\n\n\t\tmenuBar = new JadxMenuBar();\n\t\tmenuBar.add(file);\n\t\tmenuBar.add(view);\n\t\tmenuBar.add(nav);\n\t\tmenuBar.add(tools);\n\t\tmenuBar.add(pluginsMenu);\n\t\tmenuBar.add(help);\n\t\tsetJMenuBar(menuBar);\n\n\t\tflatPkgButton = new JToggleButton(Icons.FLAT_PKG);\n\t\tflatPkgButton.setSelected(isFlattenPackage);\n\t\tActionListener flatPkgAction = e -> toggleFlattenPackage();\n\t\tflatPkgMenuItem.addActionListener(flatPkgAction);\n\t\tflatPkgButton.addActionListener(flatPkgAction);\n\t\tflatPkgButton.setToolTipText(NLS.str(\"menu.flatten\"));\n\n\t\tupdateLink = new Link();\n\t\tupdateLink.setVisible(false);\n\n\t\tJToolBar toolbar = new JToolBar();\n\t\ttoolbar.setFloatable(false);\n\t\ttoolbar.add(openAction);\n\t\ttoolbar.add(addFilesAction);\n\t\ttoolbar.addSeparator();\n\t\ttoolbar.add(reloadAction);\n\t\ttoolbar.addSeparator();\n\t\ttoolbar.add(exportAction);\n\t\ttoolbar.addSeparator();\n\t\ttoolbar.add(syncAction);\n\t\ttoolbar.add(flatPkgButton);\n\t\ttoolbar.add(enablePreviewTabAction.makeToggleButton());\n\t\ttoolbar.add(quickTabsAction.makeToggleButton());\n\t\ttoolbar.addSeparator();\n\t\ttoolbar.add(textSearchAction);\n\t\ttoolbar.add(clsSearchAction);\n\t\ttoolbar.add(commentSearchAction);\n\t\ttoolbar.add(goToMainActivityAction);\n\t\ttoolbar.add(goToApplicationAction);\n\t\ttoolbar.add(goToAndroidManifestAction);\n\t\ttoolbar.addSeparator();\n\t\ttoolbar.add(backAction);\n\t\ttoolbar.add(forwardAction);\n\t\ttoolbar.addSeparator();\n\t\ttoolbar.add(deobfToggleBtn);\n\t\ttoolbar.add(quarkAction);\n\t\ttoolbar.add(debuggerAction);\n\t\ttoolbar.addSeparator();\n\t\ttoolbar.add(showLogAction);\n\t\ttoolbar.addSeparator();\n\t\ttoolbar.add(prefsAction);\n\t\ttoolbar.addSeparator();\n\t\ttoolbar.add(Box.createHorizontalGlue());\n\t\ttoolbar.add(updateLink);\n\n\t\tmainPanel.add(toolbar, BorderLayout.NORTH);\n\n\t\tnav.add(new HiddenMenuItem(backVariantAction));\n\t\tnav.add(new HiddenMenuItem(forwardVariantAction));\n\n\t\tshortcutsController.bind(backVariantAction);\n\t\tshortcutsController.bind(forwardVariantAction);\n\n\t\taddLoadListener(loaded -> {\n\t\t\ttextSearchAction.setEnabled(loaded);\n\t\t\tclsSearchAction.setEnabled(loaded);\n\t\t\tcommentSearchAction.setEnabled(loaded);\n\t\t\tgoToMainActivityAction.setEnabled(loaded);\n\t\t\tgoToApplicationAction.setEnabled(loaded);\n\t\t\tgoToAndroidManifestAction.setEnabled(loaded);\n\t\t\tbackAction.setEnabled(loaded);\n\t\t\tbackVariantAction.setEnabled(loaded);\n\t\t\tforwardAction.setEnabled(loaded);\n\t\t\tforwardVariantAction.setEnabled(loaded);\n\t\t\tsyncAction.setEnabled(loaded);\n\t\t\texportAction.setEnabled(loaded);\n\t\t\tsaveProjectAsAction.setEnabled(loaded);\n\t\t\treloadAction.setEnabled(loaded);\n\t\t\tdecompileAllAction.setEnabled(loaded);\n\t\t\tdeobfAction.setEnabled(loaded);\n\t\t\tquarkAction.setEnabled(loaded);\n\t\t\tdebuggerAction.setEnabled(loaded);\n\t\t\tresetCacheAction.setEnabled(loaded);\n\t\t\treturn false;\n\t\t});\n\t}\n\n\tprivate void initUI() {\n\t\tsetMinimumSize(new Dimension(200, 150));\n\t\tmainPanel = new JPanel(new BorderLayout());\n\t\ttreeSplitPane = new JSplitPane();\n\t\ttreeSplitPane.setResizeWeight(SPLIT_PANE_RESIZE_WEIGHT);\n\t\tmainPanel.add(treeSplitPane);\n\n\t\tDefaultMutableTreeNode treeRootNode = new DefaultMutableTreeNode(NLS.str(\"msg.open_file\"));\n\t\ttreeModel = new DefaultTreeModel(treeRootNode);\n\t\ttree = new JTree(treeModel);\n\t\tToolTipManager.sharedInstance().registerComponent(tree);\n\t\ttree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);\n\t\ttree.setFocusable(false);\n\t\ttree.addFocusListener(new FocusAdapter() {\n\t\t\t@Override\n\t\t\tpublic void focusLost(FocusEvent e) {\n\t\t\t\ttree.setFocusable(false);\n\t\t\t}\n\t\t});\n\t\ttree.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mousePressed(MouseEvent e) {\n\t\t\t\tif (SwingUtilities.isLeftMouseButton(e)) {\n\t\t\t\t\tif (!nodeClickAction(getJNodeUnderMouse(e))) {\n\t\t\t\t\t\t// click ignored -> switch to focusable mode\n\t\t\t\t\t\ttree.setFocusable(true);\n\t\t\t\t\t\ttree.requestFocus();\n\t\t\t\t\t}\n\t\t\t\t} else if (SwingUtilities.isRightMouseButton(e)) {\n\t\t\t\t\ttreeRightClickAction(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\ttree.addKeyListener(new KeyAdapter() {\n\t\t\t@Override\n\t\t\tpublic void keyPressed(KeyEvent e) {\n\t\t\t\tif (e.getKeyCode() == KeyEvent.VK_ENTER) {\n\t\t\t\t\tnodeClickAction(tree.getLastSelectedPathComponent());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\ttree.setCellRenderer(new DefaultTreeCellRenderer() {\n\t\t\t@Override\n\t\t\tpublic Component getTreeCellRendererComponent(JTree tree,\n\t\t\t\t\tObject value, boolean selected, boolean expanded,\n\t\t\t\t\tboolean isLeaf, int row, boolean focused) {\n\t\t\t\tComponent c = super.getTreeCellRendererComponent(tree, value, selected, expanded, isLeaf, row, focused);\n\t\t\t\tif (value instanceof JNode) {\n\t\t\t\t\tJNode jNode = (JNode) value;\n\t\t\t\t\tNodeLabel.disableHtml(this, jNode.disableHtml());\n\t\t\t\t\tsetText(jNode.makeStringHtml());\n\t\t\t\t\tsetIcon(jNode.getIcon());\n\t\t\t\t\tsetToolTipText(jNode.getTooltip());\n\t\t\t\t} else {\n\t\t\t\t\tsetToolTipText(null);\n\t\t\t\t}\n\t\t\t\tif (value instanceof JPackage) {\n\t\t\t\t\tsetEnabled(((JPackage) value).isEnabled());\n\t\t\t\t}\n\t\t\t\treturn c;\n\t\t\t}\n\t\t});\n\t\ttree.addTreeWillExpandListener(new TreeWillExpandListener() {\n\t\t\t@Override\n\t\t\tpublic void treeWillExpand(TreeExpansionEvent event) {\n\t\t\t\tTreePath path = event.getPath();\n\t\t\t\tObject node = path.getLastPathComponent();\n\t\t\t\tif (node instanceof JLoadableNode) {\n\t\t\t\t\tJLoadableNode treeNode = (JLoadableNode) node;\n\t\t\t\t\tIBackgroundTask loadTask = treeNode.getLoadTask();\n\t\t\t\t\tif (loadTask != null) {\n\t\t\t\t\t\tbackgroundExecutor.execute(new TaskWithExtraOnFinish(loadTask,\n\t\t\t\t\t\t\t\tstatus -> {\n\t\t\t\t\t\t\t\t\tif (!treeReloading) {\n\t\t\t\t\t\t\t\t\t\ttreeModel.nodeStructureChanged(treeNode);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void treeWillCollapse(TreeExpansionEvent event) {\n\t\t\t\tif (!treeReloading) {\n\t\t\t\t\tupdate();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tprogressPane = new ProgressPanel(this, true);\n\t\tissuesPanel = new IssuesPanel(this);\n\n\t\tJPanel leftPane = new JPanel(new BorderLayout());\n\t\tJScrollPane treeScrollPane = new JScrollPane(tree);\n\t\ttreeScrollPane.setMinimumSize(new Dimension(100, 150));\n\n\t\tJPanel bottomPane = new JPanel(new BorderLayout());\n\t\tbottomPane.add(issuesPanel, BorderLayout.PAGE_START);\n\t\tbottomPane.add(progressPane, BorderLayout.PAGE_END);\n\n\t\tleftPane.add(treeScrollPane, BorderLayout.CENTER);\n\t\tleftPane.add(bottomPane, BorderLayout.PAGE_END);\n\t\ttreeSplitPane.setLeftComponent(leftPane);\n\n\t\ttabbedPane = new TabbedPane(this, tabsController);\n\t\ttabbedPane.setMinimumSize(new Dimension(150, 150));\n\t\tnew TabDndController(tabbedPane, settings);\n\n\t\tquickTabsAndCodeSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);\n\t\tquickTabsAndCodeSplitPane.setResizeWeight(0.15);\n\t\tquickTabsAndCodeSplitPane.setDividerSize(0);\n\t\tquickTabsAndCodeSplitPane.setRightComponent(tabbedPane);\n\n\t\trightSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);\n\t\trightSplitPane.setTopComponent(quickTabsAndCodeSplitPane);\n\t\trightSplitPane.setResizeWeight(SPLIT_PANE_RESIZE_WEIGHT);\n\n\t\ttreeSplitPane.setRightComponent(rightSplitPane);\n\n\t\tnew DropTarget(this, DnDConstants.ACTION_COPY, new MainDropTarget(this));\n\n\t\theapUsageBar = new HeapUsageBar();\n\t\tmainPanel.add(heapUsageBar, BorderLayout.SOUTH);\n\n\t\tbottomSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);\n\t\tbottomSplitPane.setTopComponent(treeSplitPane);\n\t\tbottomSplitPane.setResizeWeight(SPLIT_PANE_RESIZE_WEIGHT);\n\n\t\tmainPanel.add(bottomSplitPane, BorderLayout.CENTER);\n\t\tsetContentPane(mainPanel);\n\t\tsetTitle(DEFAULT_TITLE);\n\n\t\tif (UiUtils.JADX_GUI_DEBUG) {\n\t\t\tFlatInspector.install(\"ctrl shift alt X\");\n\t\t\tFlatUIDefaultsInspector.install(\"ctrl shift alt Y\");\n\t\t}\n\n\t\tsetDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);\n\t\taddWindowListener(new WindowAdapter() {\n\t\t\t@Override\n\t\t\tpublic void windowClosing(WindowEvent e) {\n\t\t\t\tcloseWindow();\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic void setLocationAndPosition() {\n\t\tif (settings.loadWindowPos(this)) {\n\t\t\treturn;\n\t\t}\n\t\tGraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();\n\t\tDisplayMode mode = gd.getDisplayMode();\n\t\tAffineTransform trans = gd.getDefaultConfiguration().getDefaultTransform();\n\t\tint w = (int) (mode.getWidth() / trans.getScaleX());\n\t\tint h = (int) (mode.getHeight() / trans.getScaleY());\n\t\tsetBounds((int) (w * BORDER_RATIO), (int) (h * BORDER_RATIO),\n\t\t\t\t(int) (w * WINDOW_RATIO), (int) (h * WINDOW_RATIO));\n\t\tsetLocationRelativeTo(null);\n\t}\n\n\tprivate void openSettings() {\n\t\topenSettings(null);\n\t}\n\n\tprivate void openSettings(@Nullable String navigateTo) {\n\t\tsettingsOpen = true;\n\n\t\tJadxSettingsWindow settingsWindow = new JadxSettingsWindow(MainWindow.this, settings);\n\t\tsettingsWindow.addWindowListener(new WindowAdapter() {\n\t\t\t@Override\n\t\t\tpublic void windowClosed(WindowEvent e) {\n\t\t\t\tsettingsOpen = false;\n\t\t\t}\n\t\t});\n\t\tif (navigateTo != null) {\n\t\t\tsettingsWindow.activatePage(navigateTo);\n\t\t}\n\t\tsettingsWindow.setVisible(true);\n\t}\n\n\tpublic boolean isSettingsOpen() {\n\t\treturn settingsOpen;\n\t}\n\n\tpublic void loadSettings() {\n\t\t// queue update to not interrupt current UI tasks\n\t\tUiUtils.uiRun(this::updateUiSettings);\n\t}\n\n\tprivate void updateUiSettings() {\n\t\tboolean needUpdateUI = false;\n\t\tFont defaultUiFont = UIManager.getFont(\"defaultFont\");\n\t\tFont uiFont = settings.getUiFont();\n\t\tif (!uiFont.equals(defaultUiFont)) {\n\t\t\tUIManager.put(\"defaultFont\", uiFont);\n\t\t\tsetFont(uiFont);\n\t\t\tneedUpdateUI = true;\n\t\t}\n\t\tif (LafManager.updateLaf(settings)) {\n\t\t\tneedUpdateUI = true;\n\t\t}\n\t\teditorThemeManager.setTheme(settings.getEditorTheme());\n\n\t\tif (UIScale.setZoomFactor(settings.getUiZoom())) {\n\t\t\tneedUpdateUI = true;\n\t\t}\n\t\ttree.setFont(settings.getCodeFont());\n\t\ttree.setRowHeight(-1);\n\n\t\ttabbedPane.loadSettings();\n\t\tif (logPanel != null) {\n\t\t\tlogPanel.loadSettings();\n\t\t}\n\t\tif (quickTabsTree != null) {\n\t\t\tquickTabsTree.loadSettings();\n\t\t}\n\t\tshortcutsController.loadSettings();\n\t\tif (needUpdateUI) {\n\t\t\tFlatLaf.updateUI();\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"finally\")\n\tprivate void closeWindow() {\n\t\tsaveAll();\n\t\tif (!ensureProjectIsSaved()) {\n\t\t\treturn;\n\t\t}\n\t\tUiUtils.bgRun(() -> {\n\t\t\ttry {\n\t\t\t\tsettings.setTreeWidth(treeSplitPane.getDividerLocation());\n\t\t\t\tsettings.saveWindowPos(this);\n\t\t\t\tsettings.setMainWindowExtendedState(getExtendedState());\n\t\t\t\tif (debuggerPanel != null) {\n\t\t\t\t\tsaveSplittersInfo();\n\t\t\t\t}\n\t\t\t\t// block UI thread to avoid settings data changes during sync\n\t\t\t\tUiUtils.uiRunAndWait(settings::sync);\n\n\t\t\t\tcloseAll();\n\t\t\t\tUiUtils.uiRunAndWait(() -> {\n\t\t\t\t\theapUsageBar.reset();\n\t\t\t\t\teditorThemeManager.unload();\n\t\t\t\t\tdispose();\n\t\t\t\t});\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Close window error\", e);\n\t\t\t} finally {\n\t\t\t\tSystem.exit(0);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void saveOpenTabs() {\n\t\tproject.saveOpenTabs(tabsController.getEditorViewStates());\n\t}\n\n\tprivate void restoreOpenTabs(List<EditorViewState> openTabs) {\n\t\tUiUtils.uiThreadGuard();\n\t\tif (openTabs.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (EditorViewState viewState : openTabs) {\n\t\t\ttabsController.restoreEditorViewState(viewState);\n\t\t}\n\t\ttabsController.notifyRestoreEditorViewStateDone();\n\t}\n\n\tprivate void preLoadOpenTabs(List<EditorViewState> openTabs) {\n\t\tUiUtils.notUiThreadGuard();\n\t\tfor (EditorViewState tabState : openTabs) {\n\t\t\tif (tabState.isHidden()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tJNode node = tabState.getNode();\n\t\t\ttry {\n\t\t\t\tnode.getCodeInfo();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.warn(\"Failed to preload code for node: {}\", node, e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void saveSplittersInfo() {\n\t\tsettings.setMainWindowVerticalSplitterLoc(bottomSplitPane.getDividerLocation());\n\t\tif (debuggerPanel != null) {\n\t\t\tsettings.setDebuggerStackFrameSplitterLoc(debuggerPanel.getLeftSplitterLocation());\n\t\t\tsettings.setDebuggerVarTreeSplitterLoc(debuggerPanel.getRightSplitterLocation());\n\t\t}\n\t}\n\n\tpublic void addLoadListener(ILoadListener loadListener) {\n\t\tthis.loadListeners.add(loadListener);\n\t\t// set initial value\n\t\tloadListener.update(loaded);\n\t}\n\n\tpublic void notifyLoadListeners(boolean loaded) {\n\t\tthis.loaded = loaded;\n\t\tloadListeners.removeIf(listener -> listener.update(loaded));\n\t}\n\n\tpublic void addTreeUpdateListener(Consumer<JRoot> listener) {\n\t\ttreeUpdateListener.add(listener);\n\t}\n\n\tpublic JadxWrapper getWrapper() {\n\t\treturn wrapper;\n\t}\n\n\tpublic JadxProject getProject() {\n\t\treturn project;\n\t}\n\n\tpublic TabbedPane getTabbedPane() {\n\t\treturn tabbedPane;\n\t}\n\n\tpublic TabsController getTabsController() {\n\t\treturn tabsController;\n\t}\n\n\tpublic NavigationController getNavController() {\n\t\treturn navController;\n\t}\n\n\tpublic JadxSettings getSettings() {\n\t\treturn settings;\n\t}\n\n\tpublic CacheObject getCacheObject() {\n\t\treturn cacheObject;\n\t}\n\n\tpublic BackgroundExecutor getBackgroundExecutor() {\n\t\treturn backgroundExecutor;\n\t}\n\n\tpublic JRoot getTreeRoot() {\n\t\treturn treeRoot;\n\t}\n\n\tpublic JDebuggerPanel getDebuggerPanel() {\n\t\tinitDebuggerPanel();\n\t\treturn debuggerPanel;\n\t}\n\n\tpublic ShortcutsController getShortcutsController() {\n\t\treturn shortcutsController;\n\t}\n\n\tpublic void showDebuggerPanel() {\n\t\tinitDebuggerPanel();\n\t}\n\n\tpublic void destroyDebuggerPanel() {\n\t\tsaveSplittersInfo();\n\t\tif (debuggerPanel != null) {\n\t\t\tdebuggerPanel.setVisible(false);\n\t\t\tdebuggerPanel = null;\n\t\t}\n\t}\n\n\tpublic void showHeapUsageBar() {\n\t\tsettings.setShowHeapUsageBar(true);\n\t\theapUsageBar.setVisible(true);\n\t}\n\n\tprivate void initDebuggerPanel() {\n\t\tif (debuggerPanel == null) {\n\t\t\tdebuggerPanel = new JDebuggerPanel(this);\n\t\t\tdebuggerPanel.loadSettings();\n\t\t\tbottomSplitPane.setBottomComponent(debuggerPanel);\n\t\t\tint loc = settings.getMainWindowVerticalSplitterLoc();\n\t\t\tif (loc == 0) {\n\t\t\t\tloc = 300;\n\t\t\t}\n\t\t\tbottomSplitPane.setDividerLocation(loc);\n\t\t}\n\t}\n\n\tpublic void showLogViewer(LogOptions logOptions) {\n\t\tUiUtils.uiRun(() -> {\n\t\t\tif (settings.isDockLogViewer()) {\n\t\t\t\tshowDockedLog(logOptions);\n\t\t\t} else {\n\t\t\t\tLogViewerDialog.open(this, logOptions);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void showDockedLog(LogOptions logOptions) {\n\t\tif (logPanel != null) {\n\t\t\tlogPanel.applyLogOptions(logOptions);\n\t\t\treturn;\n\t\t}\n\t\tRunnable undock = () -> {\n\t\t\thideDockedLog();\n\t\t\tsettings.saveDockLogViewer(false);\n\t\t\tLogViewerDialog.open(this, logOptions);\n\t\t};\n\t\tlogPanel = new LogPanel(this, logOptions, undock, this::hideDockedLog);\n\t\trightSplitPane.setBottomComponent(logPanel);\n\t}\n\n\tprivate void hideDockedLog() {\n\t\tif (logPanel == null) {\n\t\t\treturn;\n\t\t}\n\t\tlogPanel.dispose();\n\t\tlogPanel = null;\n\t\trightSplitPane.setBottomComponent(null);\n\t}\n\n\tprivate void setQuickTabsVisibility(boolean visible) {\n\t\tif (visible) {\n\t\t\tif (quickTabsTree == null) {\n\t\t\t\tquickTabsTree = new QuickTabsTree(this);\n\t\t\t}\n\n\t\t\tquickTabsAndCodeSplitPane.setLeftComponent(quickTabsTree);\n\t\t\tquickTabsAndCodeSplitPane.setDividerSize(5);\n\t\t} else {\n\t\t\tquickTabsAndCodeSplitPane.setLeftComponent(null);\n\t\t\tquickTabsAndCodeSplitPane.setDividerSize(0);\n\n\t\t\tif (quickTabsTree != null) {\n\t\t\t\tquickTabsTree.dispose();\n\t\t\t\tquickTabsTree = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic JMenu getPluginsMenu() {\n\t\treturn pluginsMenu;\n\t}\n\n\tpublic void resetPluginsMenu() {\n\t\tpluginsMenu.removeAll();\n\t\tpluginsMenu.add(new ActionHandler(() -> openSettings(\"PluginSettingsGroup.class\"))\n\t\t\t\t.withNameAndDesc(NLS.str(\"preferences.plugins.manage\")));\n\t}\n\n\tpublic void addToPluginsMenu(Action item) {\n\t\tif (pluginsMenu.getMenuComponentCount() == 1) {\n\t\t\tpluginsMenu.addSeparator();\n\t\t}\n\t\tpluginsMenu.add(item);\n\t}\n\n\tprivate void createDesktopEntry() {\n\t\tif (DesktopEntryUtils.createDesktopEntry()) {\n\t\t\tJOptionPane.showMessageDialog(this, NLS.str(\"message.desktop_entry_creation_success\"),\n\t\t\t\t\tNLS.str(\"message.success_title\"), JOptionPane.INFORMATION_MESSAGE);\n\t\t} else {\n\t\t\tJOptionPane.showMessageDialog(this, NLS.str(\"message.desktop_entry_creation_error\"),\n\t\t\t\t\tNLS.str(\"message.errorTitle\"), JOptionPane.ERROR_MESSAGE);\n\t\t}\n\t}\n\n\tprivate void checkIfCodeHasNonPrintableChars() {\n\t\tif (getSettings().isRenamePrintable() || getSettings().isDeobfuscationOn()) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (showUndisplayedCharsDialog) {\n\t\t\treturn;\n\t\t}\n\n\t\tStringBuilder nonDisplayString = new StringBuilder();\n\n\t\tList<ClassNode> classes = wrapper.getRootNode().getClasses(true);\n\t\tFont font = getSettings().getCodeFont();\n\t\tboolean hasNonDisplayable = false;\n\n\t\tfor (ClassNode cls : classes) {\n\t\t\tString className = cls.getRawName();\n\t\t\tif (!FontUtils.canStringBeDisplayed(className, font)) {\n\t\t\t\thasNonDisplayable = true;\n\t\t\t\tnonDisplayString.append(className);\n\t\t\t\tnonDisplayString.append(\"\\n\");\n\t\t\t}\n\n\t\t\tfor (MethodNode methodNode : cls.getMethods()) {\n\t\t\t\tString methodName = methodNode.getName();\n\t\t\t\tif (!FontUtils.canStringBeDisplayed(methodName, font)) {\n\t\t\t\t\thasNonDisplayable = true;\n\t\t\t\t\tnonDisplayString.append(methodName);\n\t\t\t\t\tnonDisplayString.append(\"\\n\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (FieldNode fieldNode : cls.getFields()) {\n\t\t\t\tString fieldName = fieldNode.getName();\n\t\t\t\tif (!FontUtils.canStringBeDisplayed(fieldName, font)) {\n\t\t\t\t\thasNonDisplayable = true;\n\t\t\t\t\tnonDisplayString.append(fieldName);\n\t\t\t\t\tnonDisplayString.append(\"\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (hasNonDisplayable) {\n\t\t\tshowUndisplayedCharsDialog = true;\n\t\t\tint dialogResult = JOptionPane.showConfirmDialog(this,\n\t\t\t\t\tNLS.str(\"msg.non_displayable_chars\", font.getFontName()),\n\t\t\t\t\tNLS.str(\"msg.warning_title\"),\n\t\t\t\t\tJOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);\n\t\t\tif (dialogResult == JOptionPane.YES_OPTION) {\n\t\t\t\ttabsController.selectTab(new UndisplayedStringsNode(nonDisplayString.toString()));\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic RenameMappingsGui getRenameMappings() {\n\t\treturn renameMappings;\n\t}\n\n\tpublic CacheManager getCacheManager() {\n\t\treturn cacheManager;\n\t}\n\n\tpublic EditorThemeManager getEditorThemeManager() {\n\t\treturn editorThemeManager;\n\t}\n\n\tpublic JadxGuiEventsImpl events() {\n\t\treturn events;\n\t}\n\n\tprivate void initHexViewMenu() {\n\t\thexViewerMenu.setEnabled(false);\n\n\t\tJadxGuiAction showInspectorAction = new JadxGuiAction(ActionModel.HEX_VIEWER_SHOW_INSPECTOR,\n\t\t\t\t() -> sendActionsToHexViewer(ActionModel.HEX_VIEWER_SHOW_INSPECTOR));\n\t\tJCheckBoxMenuItem showInspectorMenuItem = new JCheckBoxMenuItem(showInspectorAction);\n\n\t\tJadxGuiAction changeEncoding = new JadxGuiAction(ActionModel.HEX_VIEWER_CHANGE_ENCODING,\n\t\t\t\t() -> sendActionsToHexViewer(ActionModel.HEX_VIEWER_CHANGE_ENCODING));\n\t\tJadxGuiAction goToAddress = new JadxGuiAction(ActionModel.HEX_VIEWER_GO_TO_ADDRESS,\n\t\t\t\t() -> sendActionsToHexViewer(ActionModel.HEX_VIEWER_GO_TO_ADDRESS));\n\n\t\tJadxGuiAction findAction = new JadxGuiAction(ActionModel.HEX_VIEWER_FIND,\n\t\t\t\t() -> sendActionsToHexViewer(ActionModel.HEX_VIEWER_FIND));\n\n\t\thexViewerMenu.add(showInspectorMenuItem);\n\t\thexViewerMenu.add(changeEncoding);\n\t\thexViewerMenu.add(goToAddress);\n\t\thexViewerMenu.addSeparator();\n\t\thexViewerMenu.add(findAction);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/ActionCategory.java",
    "content": "package jadx.gui.ui.action;\n\nimport jadx.gui.utils.NLS;\n\npublic enum ActionCategory {\n\tMENU_TOOLBAR(\"action_category.menu_toolbar\"),\n\tCODE_AREA(\"action_category.code_area\"),\n\tPLUGIN_SCRIPT(\"action_category.plugin_script\"),\n\tHEX_VIEWER_MENU(\"action_category.hex_viewer\");\n\n\tprivate final String nameRes;\n\n\tActionCategory(String nameRes) {\n\t\tthis.nameRes = nameRes;\n\t}\n\n\tpublic String getName() {\n\t\tif (nameRes != null) {\n\t\t\treturn NLS.str(nameRes);\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/ActionModel.java",
    "content": "package jadx.gui.ui.action;\n\nimport java.awt.event.InputEvent;\nimport java.awt.event.KeyEvent;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport javax.swing.ImageIcon;\n\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.shortcut.Shortcut;\n\nimport static jadx.gui.ui.action.ActionCategory.*;\n\npublic enum ActionModel {\n\tABOUT(MENU_TOOLBAR, \"menu.about\", \"menu.about\", \"ui/showInfos\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_F1)),\n\tOPEN(MENU_TOOLBAR, \"file.open_action\", \"file.open_action\", \"ui/openDisk\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_O, KeyEvent.CTRL_DOWN_MASK)),\n\tOPEN_PROJECT(MENU_TOOLBAR, \"file.open_project\", \"file.open_project\", \"ui/projectDirectory\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_O, InputEvent.SHIFT_DOWN_MASK | UiUtils.ctrlButton())),\n\tADD_FILES(MENU_TOOLBAR, \"file.add_files_action\", \"file.add_files_action\", \"ui/addFile\",\n\t\t\tShortcut.none()),\n\tNEW_PROJECT(MENU_TOOLBAR, \"file.new_project\", \"file.new_project\", \"ui/newFolder\",\n\t\t\tShortcut.none()),\n\tSAVE_PROJECT(MENU_TOOLBAR, \"file.save_project\", \"file.save_project\", null,\n\t\t\tShortcut.none()),\n\tSAVE_PROJECT_AS(MENU_TOOLBAR, \"file.save_project_as\", \"file.save_project_as\", null,\n\t\t\tShortcut.none()),\n\tRELOAD(MENU_TOOLBAR, \"file.reload\", \"file.reload\", \"ui/refresh\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_F5)),\n\tLIVE_RELOAD(MENU_TOOLBAR, \"file.live_reload\", \"file.live_reload_desc\", null,\n\t\t\tShortcut.keyboard(KeyEvent.VK_F5, InputEvent.SHIFT_DOWN_MASK)),\n\tSAVE_ALL(MENU_TOOLBAR, \"file.save_all\", \"file.save_all\", \"ui/menu-saveall\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_E, UiUtils.ctrlButton())),\n\tEXPORT(MENU_TOOLBAR, \"file.export\", \"file.export\", \"ui/export\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_E, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK)),\n\tPREFS(MENU_TOOLBAR, \"menu.preferences\", \"menu.preferences\", \"ui/settings\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_P, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK)),\n\tEXIT(MENU_TOOLBAR, \"file.exit\", \"file.exit\", \"ui/exit\",\n\t\t\tShortcut.none()),\n\tSYNC(MENU_TOOLBAR, \"menu.sync\", \"menu.sync\", \"ui/locate\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_T, UiUtils.ctrlButton())),\n\tTEXT_SEARCH(MENU_TOOLBAR, \"menu.text_search\", \"menu.text_search\", \"ui/find\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_F, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK)),\n\n\tCLASS_SEARCH(MENU_TOOLBAR, \"menu.class_search\", \"menu.class_search\", \"ui/ejbFinderMethod\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_N, UiUtils.ctrlButton())),\n\tCOMMENT_SEARCH(MENU_TOOLBAR, \"menu.comment_search\", \"menu.comment_search\", \"ui/usagesFinder\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_SEMICOLON, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK)),\n\tGO_TO_MAIN_ACTIVITY(MENU_TOOLBAR, \"menu.go_to_main_activity\", \"menu.go_to_main_activity\", \"ui/home\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_M, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK)),\n\tGO_TO_APPLICATION(MENU_TOOLBAR, \"menu.go_to_application\", \"menu.go_to_application\", \"ui/application\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_A, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK)),\n\tGO_TO_ANDROID_MANIFEST(MENU_TOOLBAR, \"menu.go_to_android_manifest\", \"menu.go_to_android_manifest\",\n\t\t\t\"ui/androidManifest\",\n\t\t\tShortcut.none()),\n\tPREVIEW_TAB(MENU_TOOLBAR, \"menu.enable_preview_tab\", \"menu.enable_preview_tab\", \"ui/editorPreview\",\n\t\t\tShortcut.none()),\n\tDECOMPILE_ALL(MENU_TOOLBAR, \"menu.decompile_all\", \"menu.decompile_all\", \"ui/runAll\",\n\t\t\tShortcut.none()),\n\tRESET_CACHE(MENU_TOOLBAR, \"menu.reset_cache\", \"menu.reset_cache\", \"ui/reset\",\n\t\t\tShortcut.none()),\n\tDEOBF(MENU_TOOLBAR, \"menu.deobfuscation\", \"preferences.deobfuscation\", \"ui/helmChartLock\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_D, UiUtils.ctrlButton() | KeyEvent.ALT_DOWN_MASK)),\n\tSHOW_LOG(MENU_TOOLBAR, \"menu.log\", \"menu.log\", \"ui/logVerbose\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_L, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK)),\n\tCREATE_DESKTOP_ENTRY(MENU_TOOLBAR, \"menu.create_desktop_entry\", \"menu.create_desktop_entry\", null, Shortcut.none()),\n\tBACK(MENU_TOOLBAR, \"nav.back\", \"nav.back\", \"ui/left\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_ESCAPE)),\n\tBACK_V(MENU_TOOLBAR, \"nav.back\", \"nav.back\", \"ui/left\",\n\t\t\tShortcut.none()),\n\tFORWARD(MENU_TOOLBAR, \"nav.forward\", \"nav.forward\", \"ui/right\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_RIGHT, KeyEvent.ALT_DOWN_MASK)),\n\tFORWARD_V(MENU_TOOLBAR, \"nav.forward\", \"nav.forward\", \"ui/right\",\n\t\t\tShortcut.none()),\n\tQUARK(MENU_TOOLBAR, \"menu.quark\", \"menu.quark\", \"ui/quark\",\n\t\t\tShortcut.none()),\n\tOPEN_DEVICE(MENU_TOOLBAR, \"debugger.process_selector\", \"debugger.process_selector\", \"ui/startDebugger\",\n\t\t\tShortcut.none()),\n\n\tFIND_USAGE(CODE_AREA, \"popup.find_usage\", \"popup.find_usage\", null,\n\t\t\tShortcut.keyboard(KeyEvent.VK_X)),\n\tFIND_USAGE_PLUS(CODE_AREA, \"popup.usage_dialog_plus\", \"popup.usage_dialog_plus\", null,\n\t\t\tShortcut.keyboard(KeyEvent.VK_C)),\n\tGOTO_DECLARATION(CODE_AREA, \"popup.go_to_declaration\", \"popup.go_to_declaration\", null,\n\t\t\tShortcut.keyboard(KeyEvent.VK_D)),\n\tCONVERT_NUMBER(CODE_AREA, \"popup.convert_number\", \"popup.convert_number\", null, Shortcut.none()),\n\tVIEW_CLASS_INHERITANCE_GRAPH(CODE_AREA, \"popup.view_class_graph\", \"popup.view_class_graph_description\", null,\n\t\t\tShortcut.none()),\n\tVIEW_CLASS_METHOD_GRAPH(CODE_AREA, \"popup.view_class_method_graph\", \"popup.view_class_method_graph_description\",\n\t\t\tnull, Shortcut.none()),\n\tVIEW_CALL_GRAPH(CODE_AREA, \"popup.view_call_graph\", \"popup.view_call_graph_description\", null, Shortcut.none()),\n\tVIEW_CONTROL_FLOW_GRAPH(CODE_AREA, \"popup.view_cfg\", \"popup.view_cfg_description\", null, Shortcut.none()),\n\tVIEW_RAW_CONTROL_FLOW_GRAPH(CODE_AREA, \"popup.view_raw_cfg\", \"popup.view_raw_cfg_description\", null, Shortcut.none()),\n\tVIEW_REGION_CONTROL_FLOW_GRAPH(CODE_AREA, \"popup.view_region_cfg\", \"popup.view_region_cfg_description\", null, Shortcut.none()),\n\n\tCODE_COMMENT(CODE_AREA, \"popup.add_comment\", \"popup.add_comment\", null,\n\t\t\tShortcut.keyboard(KeyEvent.VK_SEMICOLON)),\n\tCODE_COMMENT_SEARCH(CODE_AREA, \"popup.search_comment\", \"popup.search_comment\", null,\n\t\t\tShortcut.keyboard(KeyEvent.VK_SEMICOLON, UiUtils.ctrlButton())),\n\tCODE_RENAME(CODE_AREA, \"popup.rename\", \"popup.rename\", null,\n\t\t\tShortcut.keyboard(KeyEvent.VK_N)),\n\tFRIDA_COPY(CODE_AREA, \"popup.frida\", \"popup.frida\", null,\n\t\t\tShortcut.keyboard(KeyEvent.VK_F)),\n\tXPOSED_COPY(CODE_AREA, \"popup.xposed\", \"popup.xposed\", null,\n\t\t\tShortcut.keyboard(KeyEvent.VK_Y)),\n\tJSON_PRETTIFY(CODE_AREA, \"popup.json_prettify\", \"popup.json_prettify\", null,\n\t\t\tShortcut.none()),\n\n\tSCRIPT_RUN(PLUGIN_SCRIPT, \"script.run\", \"script.run\", \"ui/run\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_F8)),\n\tSCRIPT_SAVE(PLUGIN_SCRIPT, \"script.save\", \"script.save\", \"ui/menu-saveall\",\n\t\t\tShortcut.keyboard(KeyEvent.VK_S, UiUtils.ctrlButton())),\n\tSCRIPT_AUTO_COMPLETE(PLUGIN_SCRIPT, \"script.auto_complete\", \"script.auto_complete\", null,\n\t\t\tShortcut.keyboard(KeyEvent.VK_SPACE, UiUtils.ctrlButton())),\n\n\tHEX_VIEWER_SHOW_INSPECTOR(HEX_VIEWER_MENU, \"hex_viewer.show_inspector\", \"hex_viewer.show_inspector\",\n\t\t\tnull, Shortcut.none()),\n\tHEX_VIEWER_CHANGE_ENCODING(HEX_VIEWER_MENU, \"hex_viewer.change_encoding\", \"hex_viewer.change_encoding\",\n\t\t\tnull, Shortcut.none()),\n\tHEX_VIEWER_GO_TO_ADDRESS(HEX_VIEWER_MENU, \"hex_viewer.goto_address\", \"hex_viewer.goto_address\",\n\t\t\tnull, Shortcut.keyboard(KeyEvent.VK_J, UiUtils.ctrlButton())),\n\tHEX_VIEWER_FIND(HEX_VIEWER_MENU, \"hex_viewer.find\", \"hex_viewer.find\",\n\t\t\tnull, Shortcut.keyboard(KeyEvent.VK_F, UiUtils.ctrlButton()));\n\n\tprivate final ActionCategory category;\n\tprivate final String nameRes;\n\tprivate final String descRes;\n\tprivate final String iconPath;\n\tprivate final Shortcut defaultShortcut;\n\n\tActionModel(ActionCategory category, String nameRes, String descRes, String iconPath, Shortcut defaultShortcut) {\n\t\tthis.category = category;\n\t\tthis.nameRes = nameRes;\n\t\tthis.descRes = descRes;\n\t\tthis.iconPath = iconPath;\n\t\tthis.defaultShortcut = defaultShortcut;\n\t}\n\n\tpublic static List<ActionModel> select(ActionCategory category) {\n\t\treturn Arrays.stream(values())\n\t\t\t\t.filter(actionModel -> actionModel.category == category)\n\t\t\t\t.collect(Collectors.toUnmodifiableList());\n\t}\n\n\tpublic ActionCategory getCategory() {\n\t\treturn category;\n\t}\n\n\tpublic String getName() {\n\t\tif (nameRes != null) {\n\t\t\tString name = NLS.str(nameRes);\n\t\t\tif (name().endsWith(\"_V\")) {\n\t\t\t\tname = NLS.str(\"action.variant\", name);\n\t\t\t}\n\t\t\treturn name;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic String getDescription() {\n\t\tif (descRes != null) {\n\t\t\treturn NLS.str(descRes);\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic ImageIcon getIcon() {\n\t\tif (iconPath != null) {\n\t\t\treturn UiUtils.openSvgIcon(iconPath);\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic Shortcut getDefaultShortcut() {\n\t\treturn defaultShortcut;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/CodeAreaAction.java",
    "content": "package jadx.gui.ui.action;\n\nimport jadx.gui.ui.codearea.CodeArea;\n\npublic class CodeAreaAction extends JadxGuiAction {\n\tprotected transient CodeArea codeArea;\n\n\tpublic CodeAreaAction(ActionModel actionModel, CodeArea codeArea) {\n\t\tsuper(actionModel);\n\t\tthis.codeArea = codeArea;\n\t\tsetShortcutComponent(codeArea);\n\t}\n\n\tpublic CodeAreaAction(String id, CodeArea codeArea) {\n\t\tsuper(id);\n\t\tthis.codeArea = codeArea;\n\t\tsetShortcutComponent(codeArea);\n\t}\n\n\tpublic void dispose() {\n\t\tcodeArea = null;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/CommentSearchAction.java",
    "content": "package jadx.gui.ui.action;\n\nimport java.awt.event.ActionEvent;\n\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.dialog.SearchDialog;\n\npublic class CommentSearchAction extends CodeAreaAction {\n\tprivate static final long serialVersionUID = -3646341661734961590L;\n\n\tpublic CommentSearchAction(CodeArea codeArea) {\n\t\tsuper(ActionModel.CODE_COMMENT_SEARCH, codeArea);\n\t}\n\n\t@Override\n\tpublic void actionPerformed(ActionEvent e) {\n\t\tstartSearch();\n\t}\n\n\tprivate void startSearch() {\n\t\tSearchDialog.searchInActiveTab(codeArea.getMainWindow(), SearchDialog.SearchPreset.COMMENT);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/FindUsageAction.java",
    "content": "package jadx.gui.ui.action;\n\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.dialog.UsageDialog;\n\npublic final class FindUsageAction extends JNodeAction {\n\tprivate static final long serialVersionUID = 4692546569977976384L;\n\n\tpublic FindUsageAction(CodeArea codeArea) {\n\t\tsuper(ActionModel.FIND_USAGE, codeArea);\n\t}\n\n\t@Override\n\tpublic void runAction(JNode node) {\n\t\tUsageDialog.open(getCodeArea().getMainWindow(), node);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/FridaAction.java",
    "content": "package jadx.gui.ui.action;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport javax.swing.JOptionPane;\nimport javax.swing.SwingUtilities;\n\nimport org.apache.commons.text.StringEscapeUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JavaClass;\nimport jadx.api.JavaField;\nimport jadx.api.JavaMethod;\nimport jadx.api.metadata.annotations.VarNode;\nimport jadx.core.codegen.TypeGen;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JField;\nimport jadx.gui.treemodel.JMethod;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.dialog.MethodsDialog;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic final class FridaAction extends JNodeAction {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(FridaAction.class);\n\tprivate static final long serialVersionUID = -3084073927621269039L;\n\n\tpublic FridaAction(CodeArea codeArea) {\n\t\tsuper(ActionModel.FRIDA_COPY, codeArea);\n\t}\n\n\t@Override\n\tpublic void runAction(JNode node) {\n\t\ttry {\n\t\t\tgenerateFridaSnippet(node);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to generate Frida code snippet\", e);\n\t\t\tJOptionPane.showMessageDialog(getCodeArea().getMainWindow(), e.getLocalizedMessage(), NLS.str(\"error_dialog.title\"),\n\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isActionEnabled(JNode node) {\n\t\treturn node instanceof JMethod || node instanceof JClass || node instanceof JField;\n\t}\n\n\tprivate void generateFridaSnippet(JNode node) {\n\t\tString fridaSnippet;\n\t\tif (node instanceof JMethod) {\n\t\t\tfridaSnippet = generateMethodSnippet((JMethod) node);\n\t\t\tcopySnipped(fridaSnippet);\n\t\t} else if (node instanceof JField) {\n\t\t\tfridaSnippet = generateFieldSnippet((JField) node);\n\t\t\tcopySnipped(fridaSnippet);\n\t\t} else if (node instanceof JClass) {\n\t\t\tSwingUtilities.invokeLater(() -> showMethodSelectionDialog((JClass) node));\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(\"Unsupported node type: \" + (node != null ? node.getClass() : \"null\"));\n\t\t}\n\n\t}\n\n\tprivate void copySnipped(String fridaSnippet) {\n\t\tif (!StringUtils.isEmpty(fridaSnippet)) {\n\t\t\tLOG.info(\"Frida snippet:\\n{}\", fridaSnippet);\n\t\t\tUiUtils.copyToClipboard(fridaSnippet);\n\t\t}\n\t}\n\n\tprivate String generateMethodSnippet(JMethod jMth) {\n\t\tString classSnippet = generateClassSnippet(jMth.getJParent());\n\t\tString methodSnippet = getMethodSnippet(jMth.getJavaMethod(), jMth.getJParent());\n\t\treturn String.format(\"%s\\n%s\", classSnippet, methodSnippet);\n\t}\n\n\tprivate String generateMethodSnippet(JavaMethod javaMethod, JClass jc) {\n\t\treturn getMethodSnippet(javaMethod, jc);\n\t}\n\n\tprivate String getMethodSnippet(JavaMethod javaMethod, JClass jc) {\n\t\tMethodNode mth = javaMethod.getMethodNode();\n\t\tMethodInfo methodInfo = mth.getMethodInfo();\n\t\tString methodName;\n\t\tString newMethodName;\n\t\tif (methodInfo.isConstructor()) {\n\t\t\tmethodName = \"$init\";\n\t\t\tnewMethodName = methodName;\n\t\t} else {\n\t\t\tmethodName = StringEscapeUtils.escapeEcmaScript(methodInfo.getName());\n\t\t\tnewMethodName = StringEscapeUtils.escapeEcmaScript(methodInfo.getAlias());\n\t\t}\n\t\tString overload;\n\t\tif (isOverloaded(mth)) {\n\t\t\tString overloadArgs = methodInfo.getArgumentsTypes().stream()\n\t\t\t\t\t.map(this::parseArgType).collect(Collectors.joining(\", \"));\n\t\t\toverload = \".overload(\" + overloadArgs + \")\";\n\t\t} else {\n\t\t\toverload = \"\";\n\t\t}\n\t\tList<String> argNames = mth.collectArgNodes().stream()\n\t\t\t\t.map(VarNode::getName).collect(Collectors.toList());\n\t\tString args = String.join(\", \", argNames);\n\t\tString logArgs;\n\t\tif (argNames.isEmpty()) {\n\t\t\tlogArgs = \"\";\n\t\t} else {\n\t\t\tlogArgs = \": \" + argNames.stream().map(arg -> arg + \"=${\" + arg + \"}\").collect(Collectors.joining(\", \"));\n\t\t}\n\t\tString shortClassName = mth.getParentClass().getAlias();\n\t\tif (methodInfo.isConstructor() || methodInfo.getReturnType() == ArgType.VOID) {\n\t\t\t// no return value\n\t\t\treturn shortClassName + \"[\\\"\" + methodName + \"\\\"]\" + overload + \".implementation = function (\" + args + \") {\\n\"\n\t\t\t\t\t+ \"    console.log(`\" + shortClassName + \".\" + newMethodName + \" is called\" + logArgs + \"`);\\n\"\n\t\t\t\t\t+ \"    this[\\\"\" + methodName + \"\\\"](\" + args + \");\\n\"\n\t\t\t\t\t+ \"};\";\n\t\t}\n\t\treturn shortClassName + \"[\\\"\" + methodName + \"\\\"]\" + overload + \".implementation = function (\" + args + \") {\\n\"\n\t\t\t\t+ \"    console.log(`\" + shortClassName + \".\" + newMethodName + \" is called\" + logArgs + \"`);\\n\"\n\t\t\t\t+ \"    let result = this[\\\"\" + methodName + \"\\\"](\" + args + \");\\n\"\n\t\t\t\t+ \"    console.log(`\" + shortClassName + \".\" + newMethodName + \" result=${result}`);\\n\"\n\t\t\t\t+ \"    return result;\\n\"\n\t\t\t\t+ \"};\";\n\t}\n\n\tprivate String generateClassSnippet(JClass jc) {\n\t\tJavaClass javaClass = jc.getCls();\n\t\tString rawClassName = StringEscapeUtils.escapeEcmaScript(javaClass.getRawName());\n\t\tString shortClassName = javaClass.getName();\n\t\treturn String.format(\"var %s = Java.use(\\\"%s\\\");\", shortClassName, rawClassName);\n\t}\n\n\tprivate void showMethodSelectionDialog(JClass jc) {\n\t\tJavaClass javaClass = jc.getCls();\n\t\tnew MethodsDialog(getCodeArea().getMainWindow(), javaClass.getMethods(), (result) -> {\n\t\t\tString fridaSnippet = generateClassAllMethodSnippet(jc, result);\n\t\t\tcopySnipped(fridaSnippet);\n\t\t});\n\t}\n\n\tprivate String generateClassAllMethodSnippet(JClass jc, List<JavaMethod> methodList) {\n\t\tStringBuilder result = new StringBuilder();\n\t\tString classSnippet = generateClassSnippet(jc);\n\t\tresult.append(classSnippet).append(\"\\n\");\n\t\tfor (JavaMethod javaMethod : methodList) {\n\t\t\tresult.append(generateMethodSnippet(javaMethod, jc)).append(\"\\n\");\n\t\t}\n\t\treturn result.toString();\n\t}\n\n\tprivate String generateFieldSnippet(JField jf) {\n\t\tJavaField javaField = jf.getJavaField();\n\t\tString rawFieldName = StringEscapeUtils.escapeEcmaScript(javaField.getRawName());\n\t\tString fieldName = javaField.getName();\n\n\t\tList<MethodNode> methodNodes = javaField.getFieldNode().getParentClass().getMethods();\n\t\tfor (MethodNode methodNode : methodNodes) {\n\t\t\tif (methodNode.getName().equals(rawFieldName)) {\n\t\t\t\trawFieldName = \"_\" + rawFieldName;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tJClass jc = jf.getRootClass();\n\t\tString classSnippet = generateClassSnippet(jc);\n\t\treturn String.format(\"%s\\n%s = %s.%s.value;\", classSnippet, fieldName, jc.getName(), rawFieldName);\n\t}\n\n\tpublic Boolean isOverloaded(MethodNode methodNode) {\n\t\treturn methodNode.getParentClass().getMethods().stream()\n\t\t\t\t.anyMatch(m -> m.getName().equals(methodNode.getName())\n\t\t\t\t\t\t&& !Objects.equals(methodNode.getMethodInfo().getShortId(), m.getMethodInfo().getShortId()));\n\t}\n\n\tprivate String parseArgType(ArgType x) {\n\t\tString typeStr;\n\t\tif (x.isArray()) {\n\t\t\ttypeStr = TypeGen.signature(x).replace(\"/\", \".\");\n\t\t} else {\n\t\t\ttypeStr = x.toString();\n\t\t}\n\t\treturn \"'\" + typeStr + \"'\";\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/GoToDeclarationAction.java",
    "content": "package jadx.gui.ui.action;\n\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.codearea.CodeArea;\n\npublic final class GoToDeclarationAction extends JNodeAction {\n\tprivate static final long serialVersionUID = -1186470538894941301L;\n\n\tpublic GoToDeclarationAction(CodeArea codeArea) {\n\t\tsuper(ActionModel.GOTO_DECLARATION, codeArea);\n\t}\n\n\t@Override\n\tpublic void runAction(JNode node) {\n\t\tgetCodeArea().getContentPanel().getTabsController().codeJump(node);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/IShortcutAction.java",
    "content": "package jadx.gui.ui.action;\n\nimport javax.swing.JComponent;\n\nimport jadx.gui.utils.shortcut.Shortcut;\n\npublic interface IShortcutAction {\n\tActionModel getActionModel();\n\n\tJComponent getShortcutComponent();\n\n\tvoid performAction();\n\n\tvoid setShortcut(Shortcut shortcut);\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/JNodeAction.java",
    "content": "package jadx.gui.ui.action;\n\nimport java.awt.event.ActionEvent;\nimport java.beans.PropertyChangeListener;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.codearea.CodeArea;\n\n/**\n * Add menu and key binding actions for JNode in code area\n */\npublic abstract class JNodeAction extends CodeAreaAction {\n\tprivate static final long serialVersionUID = -2600154727884853550L;\n\n\tprivate transient @Nullable JNode node;\n\n\tpublic JNodeAction(ActionModel actionModel, CodeArea codeArea) {\n\t\tsuper(actionModel, codeArea);\n\t}\n\n\tpublic JNodeAction(String id, CodeArea codeArea) {\n\t\tsuper(id, codeArea);\n\t}\n\n\tpublic abstract void runAction(JNode node);\n\n\tpublic boolean isActionEnabled(@Nullable JNode node) {\n\t\treturn node != null;\n\t}\n\n\t@Override\n\tpublic void actionPerformed(ActionEvent e) {\n\t\tif (JadxGuiAction.isSource(e)) {\n\t\t\tnode = codeArea.getNodeUnderCaret();\n\t\t\tif (isActionEnabled(node)) {\n\t\t\t\trunAction(node);\n\t\t\t}\n\t\t} else {\n\t\t\trunAction(node);\n\t\t}\n\t}\n\n\tpublic void changeNode(@Nullable JNode node) {\n\t\tthis.node = node;\n\t\tsetEnabled(isActionEnabled(node));\n\t}\n\n\tpublic CodeArea getCodeArea() {\n\t\treturn codeArea;\n\t}\n\n\t@Override\n\tpublic void dispose() {\n\t\tsuper.dispose();\n\t\tnode = null;\n\t\tfor (PropertyChangeListener changeListener : getPropertyChangeListeners()) {\n\t\t\tremovePropertyChangeListener(changeListener);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/JadxAutoCompletion.java",
    "content": "package jadx.gui.ui.action;\n\nimport java.awt.event.ActionEvent;\nimport java.awt.event.KeyEvent;\n\nimport javax.swing.JComponent;\nimport javax.swing.KeyStroke;\n\nimport org.fife.ui.autocomplete.AutoCompletion;\nimport org.fife.ui.autocomplete.CompletionProvider;\n\nimport jadx.gui.utils.shortcut.Shortcut;\n\npublic class JadxAutoCompletion extends AutoCompletion\n\t\timplements IShortcutAction {\n\tpublic static final String COMMAND = \"JadxAutoCompletion.Command\";\n\n\t/**\n\t * Constructor.\n\t *\n\t * @param provider The completion provider. This cannot be <code>null</code>\n\t */\n\tpublic JadxAutoCompletion(CompletionProvider provider) {\n\t\tsuper(provider);\n\t}\n\n\t@Override\n\tpublic ActionModel getActionModel() {\n\t\treturn ActionModel.SCRIPT_AUTO_COMPLETE;\n\t}\n\n\t@Override\n\tpublic JComponent getShortcutComponent() {\n\t\treturn getTextComponent();\n\t}\n\n\t@Override\n\tpublic void performAction() {\n\t\tcreateAutoCompleteAction().actionPerformed(\n\t\t\t\tnew ActionEvent(this, ActionEvent.ACTION_PERFORMED, COMMAND));\n\t}\n\n\t@Override\n\tpublic void setShortcut(Shortcut shortcut) {\n\t\tif (shortcut != null && shortcut.isKeyboard()) {\n\t\t\tsetTriggerKey(shortcut.toKeyStroke());\n\t\t} else {\n\t\t\tsetTriggerKey(KeyStroke.getKeyStroke(KeyEvent.VK_UNDEFINED, 0));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/JadxGuiAction.java",
    "content": "package jadx.gui.ui.action;\n\nimport java.awt.event.ActionEvent;\nimport java.util.function.Consumer;\n\nimport javax.swing.ImageIcon;\nimport javax.swing.JComponent;\nimport javax.swing.KeyStroke;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.gui.ui.menu.JadxMenu;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.shortcut.Shortcut;\nimport jadx.gui.utils.ui.ActionHandler;\n\npublic class JadxGuiAction extends ActionHandler implements IShortcutAction {\n\tprivate static final String COMMAND_PREFIX = \"JadxGuiAction.Command.\";\n\n\tprivate final ActionModel actionModel;\n\tprivate final String id;\n\tprivate JComponent shortcutComponent = null;\n\tprivate KeyStroke addedKeyStroke = null;\n\tprivate Shortcut shortcut;\n\n\tpublic JadxGuiAction(ActionModel actionModel) {\n\t\tthis.actionModel = actionModel;\n\t\tthis.id = actionModel.name();\n\n\t\tupdateProperties();\n\t}\n\n\tpublic JadxGuiAction(ActionModel actionModel, Runnable action) {\n\t\tsuper(action);\n\t\tthis.actionModel = actionModel;\n\t\tthis.id = actionModel.name();\n\n\t\tupdateProperties();\n\t}\n\n\tpublic JadxGuiAction(ActionModel actionModel, Consumer<ActionEvent> consumer) {\n\t\tsuper(consumer);\n\t\tthis.actionModel = actionModel;\n\t\tthis.id = actionModel.name();\n\n\t\tupdateProperties();\n\t}\n\n\tpublic JadxGuiAction(String id) {\n\t\tthis.actionModel = null;\n\t\tthis.id = id;\n\n\t\tupdateProperties();\n\t}\n\n\tprivate void updateProperties() {\n\t\tif (actionModel == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tString name = actionModel.getName();\n\t\tString description = actionModel.getDescription();\n\t\tImageIcon icon = actionModel.getIcon();\n\t\tif (name != null) {\n\t\t\tsetName(name);\n\t\t}\n\t\tif (description != null) {\n\t\t\tsetShortDescription(description);\n\t\t}\n\t\tif (icon != null) {\n\t\t\tsetIcon(icon);\n\t\t}\n\t}\n\n\t@Nullable\n\tpublic ActionModel getActionModel() {\n\t\treturn actionModel;\n\t}\n\n\t@Override\n\tpublic void setShortcut(Shortcut shortcut) {\n\t\tthis.shortcut = shortcut;\n\t\tif (shortcut != null) {\n\t\t\tsetKeyBinding(shortcut.toKeyStroke());\n\t\t} else {\n\t\t\tsetKeyBinding(null);\n\t\t}\n\t}\n\n\tpublic void setShortcutComponent(JComponent component) {\n\t\tthis.shortcutComponent = component;\n\t}\n\n\t@Override\n\tpublic JComponent getShortcutComponent() {\n\t\treturn shortcutComponent;\n\t}\n\n\t@Override\n\tpublic void actionPerformed(ActionEvent e) {\n\t\tsuper.actionPerformed(e);\n\t}\n\n\t@Override\n\tpublic void performAction() {\n\t\tif (shortcutComponent != null) {\n\t\t\tif (shortcutComponent == JadxMenu.JADX_MENU_COMPONENT) {\n\t\t\t\t// always enabled\n\t\t\t} else if (!shortcutComponent.isShowing()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tString shortcutType = shortcut != null ? shortcut.getTypeString() : \"null\";\n\t\tactionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, COMMAND_PREFIX + shortcutType));\n\t}\n\n\tpublic static boolean isSource(ActionEvent event) {\n\t\tString command = event.getActionCommand();\n\t\treturn command != null && command.startsWith(COMMAND_PREFIX);\n\t}\n\n\t@Override\n\tpublic void setKeyBinding(KeyStroke keyStroke) {\n\t\tif (shortcutComponent == null) {\n\t\t\tsuper.setKeyBinding(keyStroke);\n\t\t} else {\n\t\t\t// We just set the keyStroke for it to appear in the menu item\n\t\t\t// (grayed out in the right)\n\t\t\tsuper.setKeyBinding(keyStroke);\n\n\t\t\tif (addedKeyStroke != null) {\n\t\t\t\tUiUtils.removeKeyBinding(shortcutComponent, addedKeyStroke, id);\n\t\t\t}\n\t\t\tUiUtils.addKeyBinding(shortcutComponent, keyStroke, id, this::performAction);\n\t\t\taddedKeyStroke = keyStroke;\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"JadxGuiAction{\" + id + \", component: \" + shortcutComponent + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/JsonPrettifyAction.java",
    "content": "package jadx.gui.ui.action;\n\nimport com.google.gson.Gson;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonParser;\n\nimport jadx.core.utils.GsonUtils;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.codearea.CodeArea;\n\npublic class JsonPrettifyAction extends JNodeAction {\n\tprivate static final long serialVersionUID = -2682529369671695550L;\n\n\tprivate static final Gson GSON = GsonUtils.buildGson();\n\n\tpublic JsonPrettifyAction(CodeArea codeArea) {\n\t\tsuper(ActionModel.JSON_PRETTIFY, codeArea);\n\t}\n\n\t@Override\n\tpublic void runAction(JNode node) {\n\t\tString originString = getCodeArea().getCodeInfo().getCodeStr();\n\t\tJsonElement je = JsonParser.parseString(originString);\n\t\tString prettyString = GSON.toJson(je);\n\t\tgetCodeArea().setText(prettyString);\n\t}\n\n\t@Override\n\tpublic boolean isActionEnabled(JNode node) {\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/RenameAction.java",
    "content": "package jadx.gui.ui.action;\n\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.treemodel.JRenameNode;\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.dialog.RenameDialog;\n\npublic final class RenameAction extends JNodeAction {\n\tprivate static final long serialVersionUID = -4680872086148463289L;\n\n\tpublic RenameAction(CodeArea codeArea) {\n\t\tsuper(ActionModel.CODE_RENAME, codeArea);\n\t}\n\n\t@Override\n\tpublic boolean isActionEnabled(JNode node) {\n\t\tif (node == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (node instanceof JRenameNode) {\n\t\t\treturn ((JRenameNode) node).canRename();\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void runAction(JNode node) {\n\t\tRenameDialog.rename(getCodeArea().getMainWindow(), (JRenameNode) node);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/ViewCallGraphAction.java",
    "content": "package jadx.gui.ui.action;\n\nimport javax.swing.JOptionPane;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.treemodel.JMethod;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.dialog.CallGraphDialog;\nimport jadx.gui.utils.NLS;\n\npublic final class ViewCallGraphAction extends JNodeAction {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ViewCallGraphAction.class);\n\tprivate static final long serialVersionUID = -11122327621269039L;\n\n\tpublic ViewCallGraphAction(CodeArea codeArea) {\n\t\tsuper(ActionModel.VIEW_CALL_GRAPH, codeArea);\n\t}\n\n\t@Override\n\tpublic void runAction(JNode node) {\n\t\ttry {\n\n\t\t\tJMethod methodNode;\n\n\t\t\tif (node instanceof JMethod) {\n\t\t\t\tmethodNode = (JMethod) node;\n\t\t\t} else {\n\t\t\t\tthrow new JadxRuntimeException(\"Unsupported node type: \" + (node != null ? node.getClass() : \"null\"));\n\t\t\t}\n\n\t\t\tCallGraphDialog.open(getCodeArea().getMainWindow(), methodNode);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to view graph\", e);\n\t\t\tJOptionPane.showMessageDialog(getCodeArea().getMainWindow(), e.getLocalizedMessage(), NLS.str(\"error_dialog.title\"),\n\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isActionEnabled(JNode node) {\n\t\treturn node instanceof JMethod;\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/ViewClassInheritanceGraphAction.java",
    "content": "package jadx.gui.ui.action;\n\nimport javax.swing.JOptionPane;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JField;\nimport jadx.gui.treemodel.JMethod;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.dialog.ClassInheritanceGraphDialog;\nimport jadx.gui.utils.NLS;\n\npublic final class ViewClassInheritanceGraphAction extends JNodeAction {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ViewClassInheritanceGraphAction.class);\n\tprivate static final long serialVersionUID = -331826691076655264L;\n\n\tpublic ViewClassInheritanceGraphAction(CodeArea codeArea) {\n\t\tsuper(ActionModel.VIEW_CLASS_INHERITANCE_GRAPH, codeArea);\n\t}\n\n\t@Override\n\tpublic void runAction(JNode node) {\n\t\ttry {\n\n\t\t\tJClass classNode;\n\n\t\t\tif (node instanceof JMethod) {\n\t\t\t\tclassNode = node.getJParent();\n\t\t\t} else if (node instanceof JField) {\n\t\t\t\tclassNode = node.getJParent();\n\t\t\t} else if (node instanceof JClass) {\n\t\t\t\tclassNode = (JClass) node;\n\t\t\t} else {\n\t\t\t\tthrow new JadxRuntimeException(\"Unsupported node type: \" + (node != null ? node.getClass() : \"null\"));\n\t\t\t}\n\n\t\t\tClassInheritanceGraphDialog.open(getCodeArea().getMainWindow(), classNode);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to view graph\", e);\n\t\t\tJOptionPane.showMessageDialog(getCodeArea().getMainWindow(), e.getLocalizedMessage(), NLS.str(\"error_dialog.title\"),\n\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isActionEnabled(JNode node) {\n\t\treturn node instanceof JMethod || node instanceof JClass || node instanceof JField;\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/ViewClassMethodGraphAction.java",
    "content": "package jadx.gui.ui.action;\n\nimport javax.swing.JOptionPane;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JField;\nimport jadx.gui.treemodel.JMethod;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.dialog.ClassMethodGraphDialog;\nimport jadx.gui.utils.NLS;\n\npublic final class ViewClassMethodGraphAction extends JNodeAction {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ViewClassMethodGraphAction.class);\n\tprivate static final long serialVersionUID = -331826691076655264L;\n\n\tpublic ViewClassMethodGraphAction(CodeArea codeArea) {\n\t\tsuper(ActionModel.VIEW_CLASS_METHOD_GRAPH, codeArea);\n\t}\n\n\t@Override\n\tpublic void runAction(JNode node) {\n\t\ttry {\n\n\t\t\tJClass classNode;\n\n\t\t\tif (node instanceof JMethod) {\n\t\t\t\tclassNode = node.getJParent();\n\t\t\t} else if (node instanceof JField) {\n\t\t\t\tclassNode = node.getJParent();\n\t\t\t} else if (node instanceof JClass) {\n\t\t\t\tclassNode = (JClass) node;\n\t\t\t} else {\n\t\t\t\tthrow new JadxRuntimeException(\"Unsupported node type: \" + (node != null ? node.getClass() : \"null\"));\n\t\t\t}\n\n\t\t\tClassMethodGraphDialog.open(getCodeArea().getMainWindow(), classNode);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to view graph\", e);\n\t\t\tJOptionPane.showMessageDialog(getCodeArea().getMainWindow(), e.getLocalizedMessage(), NLS.str(\"error_dialog.title\"),\n\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isActionEnabled(JNode node) {\n\t\treturn node instanceof JMethod || node instanceof JClass || node instanceof JField;\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/ViewControlFlowGraphAction.java",
    "content": "package jadx.gui.ui.action;\n\nimport java.io.File;\n\nimport javax.swing.JOptionPane;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.DotGraphUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.treemodel.JMethod;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.dialog.ControlFlowGraphDialog;\nimport jadx.gui.utils.NLS;\n\npublic final class ViewControlFlowGraphAction extends JNodeAction {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ViewControlFlowGraphAction.class);\n\tprivate static final long serialVersionUID = -490213655L;\n\n\tpublic ViewControlFlowGraphAction(CodeArea codeArea) {\n\t\tsuper(ActionModel.VIEW_CONTROL_FLOW_GRAPH, codeArea);\n\t}\n\n\t@Override\n\tpublic void runAction(JNode node) {\n\t\ttry {\n\n\t\t\tJMethod methodNode;\n\n\t\t\tif (node instanceof JMethod) {\n\t\t\t\tmethodNode = (JMethod) node;\n\t\t\t} else {\n\t\t\t\tthrow new JadxRuntimeException(\"Unsupported node type: \" + (node != null ? node.getClass() : \"null\"));\n\t\t\t}\n\n\t\t\tControlFlowGraphDialog.open(getCodeArea().getMainWindow(), methodNode, false, false);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to view graph\", e);\n\t\t\tJOptionPane.showMessageDialog(getCodeArea().getMainWindow(), e.getLocalizedMessage(), NLS.str(\"error_dialog.title\"),\n\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isActionEnabled(JNode node) {\n\t\tif (!(node instanceof JMethod)) {\n\t\t\treturn false;\n\t\t}\n\t\tMethodNode mth = ((JMethod) node).getJavaMethod().getMethodNode();\n\t\tFile file = new DotGraphUtils(false, false).getFullFile(mth);\n\n\t\treturn file.exists() && !file.isDirectory();\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/ViewRawControlFlowGraphAction.java",
    "content": "package jadx.gui.ui.action;\n\nimport java.io.File;\n\nimport javax.swing.JOptionPane;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.DotGraphUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.treemodel.JMethod;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.dialog.ControlFlowGraphDialog;\nimport jadx.gui.utils.NLS;\n\npublic final class ViewRawControlFlowGraphAction extends JNodeAction {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ViewRawControlFlowGraphAction.class);\n\tprivate static final long serialVersionUID = -535703386523657L;\n\n\tpublic ViewRawControlFlowGraphAction(CodeArea codeArea) {\n\t\tsuper(ActionModel.VIEW_RAW_CONTROL_FLOW_GRAPH, codeArea);\n\t}\n\n\t@Override\n\tpublic void runAction(JNode node) {\n\t\ttry {\n\n\t\t\tJMethod methodNode;\n\n\t\t\tif (node instanceof JMethod) {\n\t\t\t\tmethodNode = (JMethod) node;\n\t\t\t} else {\n\t\t\t\tthrow new JadxRuntimeException(\"Unsupported node type: \" + (node != null ? node.getClass() : \"null\"));\n\t\t\t}\n\n\t\t\tControlFlowGraphDialog.open(getCodeArea().getMainWindow(), methodNode, false, true);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to view graph\", e);\n\t\t\tJOptionPane.showMessageDialog(getCodeArea().getMainWindow(), e.getLocalizedMessage(), NLS.str(\"error_dialog.title\"),\n\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isActionEnabled(JNode node) {\n\t\tif (!(node instanceof JMethod)) {\n\t\t\treturn false;\n\t\t}\n\t\tMethodNode mth = ((JMethod) node).getJavaMethod().getMethodNode();\n\t\tFile file = new DotGraphUtils(false, true).getFullFile(mth);\n\n\t\treturn file.exists() && !file.isDirectory();\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/ViewRegionControlFlowGraphAction.java",
    "content": "package jadx.gui.ui.action;\n\nimport java.io.File;\n\nimport javax.swing.JOptionPane;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.DotGraphUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.treemodel.JMethod;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.dialog.ControlFlowGraphDialog;\nimport jadx.gui.utils.NLS;\n\npublic final class ViewRegionControlFlowGraphAction extends JNodeAction {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ViewRegionControlFlowGraphAction.class);\n\tprivate static final long serialVersionUID = -14970352087936L;\n\n\tpublic ViewRegionControlFlowGraphAction(CodeArea codeArea) {\n\t\tsuper(ActionModel.VIEW_REGION_CONTROL_FLOW_GRAPH, codeArea);\n\t}\n\n\t@Override\n\tpublic void runAction(JNode node) {\n\t\ttry {\n\n\t\t\tJMethod methodNode;\n\n\t\t\tif (node instanceof JMethod) {\n\t\t\t\tmethodNode = (JMethod) node;\n\t\t\t} else {\n\t\t\t\tthrow new JadxRuntimeException(\"Unsupported node type: \" + (node != null ? node.getClass() : \"null\"));\n\t\t\t}\n\n\t\t\tControlFlowGraphDialog.open(getCodeArea().getMainWindow(), methodNode, true, false);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to view graph\", e);\n\t\t\tJOptionPane.showMessageDialog(getCodeArea().getMainWindow(), e.getLocalizedMessage(), NLS.str(\"error_dialog.title\"),\n\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isActionEnabled(JNode node) {\n\t\tif (!(node instanceof JMethod)) {\n\t\t\treturn false;\n\t\t}\n\t\tMethodNode mth = ((JMethod) node).getJavaMethod().getMethodNode();\n\t\tFile file = new DotGraphUtils(true, false).getFullFile(mth);\n\n\t\treturn file.exists() && !file.isDirectory();\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/action/XposedAction.kt",
    "content": "package jadx.gui.ui.action\n\nimport jadx.core.dex.instructions.args.ArgType\nimport jadx.core.dex.instructions.args.PrimitiveType\nimport jadx.core.utils.exceptions.JadxRuntimeException\nimport jadx.gui.settings.XposedCodegenLanguage\nimport jadx.gui.treemodel.JClass\nimport jadx.gui.treemodel.JField\nimport jadx.gui.treemodel.JMethod\nimport jadx.gui.treemodel.JNode\nimport jadx.gui.ui.codearea.CodeArea\nimport jadx.gui.utils.NLS\nimport jadx.gui.utils.UiUtils\nimport org.slf4j.Logger\nimport org.slf4j.LoggerFactory\nimport javax.swing.JOptionPane\n\nclass XposedAction(codeArea: CodeArea) : JNodeAction(ActionModel.XPOSED_COPY, codeArea) {\n\toverride fun runAction(node: JNode) {\n\t\ttry {\n\t\t\tval xposedSnippet = generateXposedSnippet(node)\n\t\t\tLOG.info(\"Xposed snippet:\\n{}\", xposedSnippet)\n\t\t\tUiUtils.copyToClipboard(xposedSnippet)\n\t\t} catch (e: Exception) {\n\t\t\tLOG.error(\"Failed to generate Xposed code snippet\", e)\n\t\t\tJOptionPane.showMessageDialog(\n\t\t\t\tgetCodeArea().mainWindow,\n\t\t\t\te.localizedMessage,\n\t\t\t\tNLS.str(\"error_dialog.title\"),\n\t\t\t\tJOptionPane.ERROR_MESSAGE,\n\t\t\t)\n\t\t}\n\t}\n\n\toverride fun isActionEnabled(node: JNode?): Boolean {\n\t\treturn node is JMethod || node is JClass || node is JField\n\t}\n\n\tprivate fun generateXposedSnippet(node: JNode): String {\n\t\treturn when (node) {\n\t\t\tis JMethod -> generateMethodSnippet(node)\n\t\t\tis JClass -> generateClassSnippet(node)\n\t\t\tis JField -> generateFieldSnippet(node)\n\t\t\telse -> throw JadxRuntimeException(\"Unsupported node type: \" + node.javaClass)\n\t\t}\n\t}\n\n\tprivate fun generateMethodSnippet(jMethod: JMethod): String {\n\t\tval javaMethod = jMethod.javaMethod\n\t\tval methodNode = javaMethod.methodNode\n\t\tval methodInfo = methodNode.methodInfo\n\n\t\tval xposedMethod: String\n\t\tvar args = methodInfo.argumentsTypes.map(::fixTypeContent)\n\t\tval rawClassName = javaMethod.declaringClass.rawName\n\n\t\tif (methodNode.isConstructor) {\n\t\t\txposedMethod = \"findAndHookConstructor\"\n\t\t} else {\n\t\t\txposedMethod = \"findAndHookMethod\"\n\t\t\targs = listOf(\"\\\"${methodInfo.name}\\\"\") + args\n\t\t}\n\n\t\tval template = when (language) {\n\t\t\tXposedCodegenLanguage.JAVA ->\n\t\t\t\t\"\"\"XposedHelpers.%s(\"%s\", classLoader, %s, new XC_MethodHook() {\n    @Override\n    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n        super.beforeHookedMethod(param);\n    }\n    @Override\n    protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n        super.afterHookedMethod(param);\n    }\n});\"\"\"\n\t\t\tXposedCodegenLanguage.KOTLIN ->\n\t\t\t\t\"\"\"XposedHelpers.%s(\"%s\", classLoader, %s, object : XC_MethodHook() {\n    override fun beforeHookedMethod(param: MethodHookParam) {\n        super.beforeHookedMethod(param)\n    }\n\n    override fun afterHookedMethod(param: MethodHookParam) {\n        super.afterHookedMethod(param)\n    }\n})\"\"\"\n\t\t}\n\n\t\treturn String.format(template, xposedMethod, rawClassName, args.joinToString(\", \"))\n\t}\n\n\tprivate fun fixTypeContent(type: ArgType): String {\n\t\treturn when {\n\t\t\ttype.isGeneric -> \"\\\"${type.`object`}\\\"\"\n\t\t\ttype.isGenericType && type.isObject && type.isTypeKnown -> \"java.lang.Object\"\n\t\t\ttype.isPrimitive -> when (language) {\n\t\t\t\tXposedCodegenLanguage.JAVA -> \"$type.class\"\n\t\t\t\tXposedCodegenLanguage.KOTLIN -> when (type.primitiveType) {\n\t\t\t\t\tPrimitiveType.BOOLEAN -> \"Boolean::class.javaPrimitiveType\"\n\t\t\t\t\tPrimitiveType.CHAR -> \"Char::class.javaPrimitiveType\"\n\t\t\t\t\tPrimitiveType.BYTE -> \"Byte::class.javaPrimitiveType\"\n\t\t\t\t\tPrimitiveType.SHORT -> \"Short::class.javaPrimitiveType\"\n\t\t\t\t\tPrimitiveType.INT -> \"Int::class.javaPrimitiveType\"\n\t\t\t\t\tPrimitiveType.FLOAT -> \"Float::class.javaPrimitiveType\"\n\t\t\t\t\tPrimitiveType.LONG -> \"Long::class.javaPrimitiveType\"\n\t\t\t\t\tPrimitiveType.DOUBLE -> \"Double::class.javaPrimitiveType\"\n\t\t\t\t\tPrimitiveType.OBJECT -> \"Any::class.java\"\n\t\t\t\t\tPrimitiveType.ARRAY -> \"Array::class.java\"\n\t\t\t\t\tPrimitiveType.VOID -> \"Void::class.javaPrimitiveType\"\n\t\t\t\t\telse -> throw JadxRuntimeException(\"Unknown or null primitive type: $type\")\n\t\t\t\t}\n\t\t\t}\n\t\t\telse -> \"\\\"$type\\\"\"\n\t\t}\n\t}\n\n\tprivate fun generateClassSnippet(jClass: JClass): String {\n\t\tval javaClass = jClass.cls\n\t\tval rawClassName = javaClass.rawName\n\t\tval className = javaClass.name\n\n\t\tval template = when (language) {\n\t\t\tXposedCodegenLanguage.JAVA -> \"Class<?> %sClass = classLoader.loadClass(\\\"%s\\\");\"\n\t\t\tXposedCodegenLanguage.KOTLIN -> \"val %sClass = classLoader.loadClass(\\\"%s\\\")\"\n\t\t}\n\n\t\treturn String.format(template, className, rawClassName)\n\t}\n\n\tprivate fun generateFieldSnippet(jField: JField): String {\n\t\tval javaField = jField.javaField\n\t\tval static = if (javaField.accessFlags.isStatic) \"Static\" else \"\"\n\t\tval type = PRIMITIVE_TYPE_MAPPING.getOrDefault(javaField.fieldNode.type.toString(), \"Object\")\n\t\tval xposedMethod = \"XposedHelpers.get${static}${type}Field\"\n\n\t\tval template = when (language) {\n\t\t\tXposedCodegenLanguage.JAVA -> \"%s(/*runtimeObject*/, \\\"%s\\\");\"\n\t\t\tXposedCodegenLanguage.KOTLIN -> \"%s(/*runtimeObject*/, \\\"%s\\\")\"\n\t\t}\n\n\t\treturn String.format(template, xposedMethod, javaField.fieldNode.fieldInfo.name)\n\t}\n\n\tprivate val language: XposedCodegenLanguage\n\t\tget() = getCodeArea().mainWindow.settings.xposedCodegenLanguage\n\n\tcompanion object {\n\t\tprivate val LOG: Logger = LoggerFactory.getLogger(XposedAction::class.java)\n\t\tprivate const val serialVersionUID = 2641585141624592578L\n\n\t\tprivate val PRIMITIVE_TYPE_MAPPING = mapOf(\n\t\t\t\"int\" to \"Int\",\n\t\t\t\"byte\" to \"Byte\",\n\t\t\t\"short\" to \"Short\",\n\t\t\t\"long\" to \"Long\",\n\t\t\t\"float\" to \"Float\",\n\t\t\t\"double\" to \"Double\",\n\t\t\t\"char\" to \"Char\",\n\t\t\t\"boolean\" to \"Boolean\",\n\t\t)\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/cellrenders/MethodRenderHelper.java",
    "content": "package jadx.gui.ui.cellrenders;\n\nimport java.util.Iterator;\n\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\n\nimport jadx.api.JavaMethod;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.OverlayIcon;\nimport jadx.gui.utils.UiUtils;\n\npublic class MethodRenderHelper {\n\n\tprivate static final ImageIcon ICON_METHOD_ABSTRACT = UiUtils.openSvgIcon(\"nodes/abstractMethod\");\n\tprivate static final ImageIcon ICON_METHOD_PRIVATE = UiUtils.openSvgIcon(\"nodes/privateMethod\");\n\tprivate static final ImageIcon ICON_METHOD_PROTECTED = UiUtils.openSvgIcon(\"nodes/protectedMethod\");\n\tprivate static final ImageIcon ICON_METHOD_PUBLIC = UiUtils.openSvgIcon(\"nodes/publicMethod\");\n\tprivate static final ImageIcon ICON_METHOD_CONSTRUCTOR = UiUtils.openSvgIcon(\"nodes/constructorMethod\");\n\tprivate static final ImageIcon ICON_METHOD_SYNC = UiUtils.openSvgIcon(\"nodes/methodReference\");\n\n\tpublic static Icon getIcon(JavaMethod mth) {\n\t\tAccessInfo accessFlags = mth.getAccessFlags();\n\t\tIcon icon = Icons.METHOD;\n\t\tif (accessFlags.isAbstract()) {\n\t\t\ticon = ICON_METHOD_ABSTRACT;\n\t\t}\n\t\tif (accessFlags.isConstructor()) {\n\t\t\ticon = ICON_METHOD_CONSTRUCTOR;\n\t\t}\n\t\tif (accessFlags.isPublic()) {\n\t\t\ticon = ICON_METHOD_PUBLIC;\n\t\t}\n\t\tif (accessFlags.isPrivate()) {\n\t\t\ticon = ICON_METHOD_PRIVATE;\n\t\t}\n\t\tif (accessFlags.isProtected()) {\n\t\t\ticon = ICON_METHOD_PROTECTED;\n\t\t}\n\t\tif (accessFlags.isSynchronized()) {\n\t\t\ticon = ICON_METHOD_SYNC;\n\t\t}\n\n\t\tOverlayIcon overIcon = new OverlayIcon(icon);\n\t\tif (accessFlags.isFinal()) {\n\t\t\toverIcon.add(Icons.FINAL);\n\t\t}\n\t\tif (accessFlags.isStatic()) {\n\t\t\toverIcon.add(Icons.STATIC);\n\t\t}\n\n\t\treturn overIcon;\n\t}\n\n\tpublic static String makeBaseString(JavaMethod mth) {\n\t\tif (mth.isClassInit()) {\n\t\t\treturn \"{...}\";\n\t\t}\n\t\tStringBuilder base = new StringBuilder();\n\t\tif (mth.isConstructor()) {\n\t\t\tbase.append(mth.getDeclaringClass().getName());\n\t\t} else {\n\t\t\tbase.append(mth.getName());\n\t\t}\n\t\tbase.append('(');\n\t\tfor (Iterator<ArgType> it = mth.getArguments().iterator(); it.hasNext();) {\n\t\t\tbase.append(UiUtils.typeStr(it.next()));\n\t\t\tif (it.hasNext()) {\n\t\t\t\tbase.append(\", \");\n\t\t\t}\n\t\t}\n\t\tbase.append(')');\n\t\treturn base.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/cellrenders/MethodsListRenderer.java",
    "content": "package jadx.gui.ui.cellrenders;\n\nimport java.awt.BorderLayout;\nimport java.awt.Component;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.JCheckBox;\nimport javax.swing.JLabel;\nimport javax.swing.JList;\nimport javax.swing.JPanel;\nimport javax.swing.ListCellRenderer;\n\nimport jadx.api.JavaMethod;\nimport jadx.gui.utils.UiUtils;\n\npublic class MethodsListRenderer extends JPanel implements ListCellRenderer<JavaMethod> {\n\tprivate final JCheckBox checkBox;\n\tprivate final JLabel label;\n\n\tpublic MethodsListRenderer() {\n\t\tsetLayout(new BorderLayout(5, 0));\n\t\tcheckBox = new JCheckBox();\n\t\tlabel = new JLabel();\n\n\t\tsetBorder(BorderFactory.createEmptyBorder(1, 5, 1, 5));\n\n\t\tadd(checkBox, BorderLayout.WEST);\n\t\tadd(label, BorderLayout.CENTER);\n\n\t\tsetOpaque(true);\n\n\t\tcheckBox.setOpaque(false);\n\t\tlabel.setOpaque(false);\n\t}\n\n\t@Override\n\tpublic Component getListCellRendererComponent(JList<? extends JavaMethod> list,\n\t\t\tJavaMethod value,\n\t\t\tint index,\n\t\t\tboolean isSelected,\n\t\t\tboolean cellHasFocus) {\n\t\tlabel.setText(UiUtils.typeFormatHtml(MethodRenderHelper.makeBaseString(value), value.getReturnType()));\n\t\tlabel.setIcon(MethodRenderHelper.getIcon(value));\n\n\t\tcheckBox.setSelected(isSelected);\n\n\t\tsetBackground(isSelected ? list.getSelectionBackground() : list.getBackground());\n\t\tsetForeground(isSelected ? list.getSelectionForeground() : list.getForeground());\n\t\tlabel.setForeground(isSelected ? list.getSelectionForeground() : list.getForeground());\n\n\t\treturn this;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/cellrenders/PathHighlightTreeCellRenderer.java",
    "content": "package jadx.gui.ui.cellrenders;\n\nimport java.awt.Color;\nimport java.awt.Component;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.JTree;\nimport javax.swing.UIManager;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.DefaultTreeCellRenderer;\nimport javax.swing.tree.TreePath;\n\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.utils.UiUtils;\n\npublic class PathHighlightTreeCellRenderer extends DefaultTreeCellRenderer {\n\n\tprivate final boolean isDarkTheme;\n\n\tpublic PathHighlightTreeCellRenderer() {\n\t\tsuper();\n\t\tColor themeBackground = UIManager.getColor(\"Panel.background\");\n\t\tisDarkTheme = UiUtils.isDarkTheme(themeBackground);\n\t}\n\n\t@Override\n\tpublic Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected,\n\t\t\tboolean expanded, boolean leaf, int row, boolean hasFocus) {\n\n\t\tComponent comp = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);\n\n\t\tDefaultMutableTreeNode node = (DefaultMutableTreeNode) value;\n\t\tObject userObject = node.getUserObject();\n\n\t\t// Calculate the node level\n\t\tint level = node.getLevel();\n\t\t// Set different colors according to the level\n\t\tfloat hue = (level * 0.1f) % 1.0f; // Hue cycle\n\t\tColor levelColor = Color.getHSBColor(hue, 0.1f, 0.95f);\n\n\t\t// Check if it is on the selected path\n\t\tboolean onSelectionPath = false;\n\t\tTreePath selectionPath = tree.getSelectionPath();\n\t\tif (selectionPath != null) {\n\t\t\t// Check if the current node is on the selected path (whether it is part of the selected path)\n\t\t\tObject[] selectedPathNodes = selectionPath.getPath();\n\t\t\tfor (Object pathNode : selectedPathNodes) {\n\t\t\t\tif (pathNode == node) {\n\t\t\t\t\tonSelectionPath = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (onSelectionPath && !selected) {\n\t\t\t// If it is on the selected path but not the selected node, use a special foreground\n\t\t\tsetForeground(isDarkTheme ? Color.decode(\"#70AEFF\") : Color.decode(\"#0033B3\"));\n\t\t} else if (!selected) {\n\t\t\t// Only apply the background color when it is not selected\n\t\t\tsetBackground(levelColor);\n\t\t\t// Normal border\n\t\t\tsetBorder(BorderFactory.createEmptyBorder(2, level * 2 + 1, 2, 1));\n\t\t} else {\n\t\t\t// The selected node also adds a border\n\t\t\tsetBorder(BorderFactory.createEmptyBorder(2, level * 2 + 1, 2, 1));\n\t\t}\n\n\t\tif (userObject instanceof JNode) {\n\t\t\tJNode jNode = (JNode) userObject;\n\t\t\tsetText(jNode.makeLongString());\n\t\t\tsetIcon(jNode.getIcon());\n\t\t\tsetToolTipText(jNode.getTooltip());\n\t\t}\n\t\treturn comp;\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.Insets;\nimport java.awt.Point;\nimport java.awt.Rectangle;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.FocusEvent;\nimport java.awt.event.FocusListener;\nimport java.awt.event.KeyAdapter;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.MouseListener;\nimport java.awt.event.MouseMotionListener;\nimport java.util.Objects;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport javax.swing.AbstractAction;\nimport javax.swing.Action;\nimport javax.swing.JCheckBoxMenuItem;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPopupMenu;\nimport javax.swing.JViewport;\nimport javax.swing.SwingUtilities;\nimport javax.swing.event.CaretEvent;\nimport javax.swing.event.CaretListener;\nimport javax.swing.event.PopupMenuEvent;\nimport javax.swing.event.PopupMenuListener;\nimport javax.swing.text.BadLocationException;\nimport javax.swing.text.Caret;\nimport javax.swing.text.DefaultCaret;\n\nimport org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;\nimport org.fife.ui.rsyntaxtextarea.Token;\nimport org.fife.ui.rsyntaxtextarea.TokenMakerFactory;\nimport org.fife.ui.rsyntaxtextarea.TokenTypes;\nimport org.fife.ui.rtextarea.Gutter;\nimport org.fife.ui.rtextarea.SearchContext;\nimport org.fife.ui.rtextarea.SearchEngine;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.jobs.IBackgroundTask;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JEditableNode;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.action.JNodeAction;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.utils.DefaultPopupMenuListener;\nimport jadx.gui.utils.JumpPosition;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.ui.DocumentUpdateListener;\nimport jadx.gui.utils.ui.ZoomActions;\n\npublic abstract class AbstractCodeArea extends RSyntaxTextArea {\n\tprivate static final long serialVersionUID = -3980354865216031972L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(AbstractCodeArea.class);\n\n\tpublic static final String SYNTAX_STYLE_SMALI = \"text/smali\";\n\n\tstatic {\n\t\tTokenMakerFactory tokenMakerFactory = TokenMakerFactory.getDefaultInstance();\n\t\tif (tokenMakerFactory instanceof AbstractTokenMakerFactory) {\n\t\t\tAbstractTokenMakerFactory atmf = (AbstractTokenMakerFactory) tokenMakerFactory;\n\t\t\tatmf.putMapping(SYNTAX_STYLE_SMALI, \"jadx.gui.ui.codearea.SmaliTokenMaker\");\n\t\t\t// use simple token maker instead default PlainTextTokenMaker to avoid parse errors\n\t\t\tatmf.putMapping(SYNTAX_STYLE_NONE, \"jadx.gui.ui.codearea.SimpleTokenMaker\");\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(\"Unexpected TokenMakerFactory instance: \" + tokenMakerFactory.getClass());\n\t\t}\n\n\t\tSmaliFoldParser.register();\n\t}\n\n\tprotected ContentPanel contentPanel;\n\tprotected JNode node;\n\n\tprivate final AtomicBoolean loaded = new AtomicBoolean(false);\n\n\tpublic AbstractCodeArea(ContentPanel contentPanel, JNode node) {\n\t\tthis.contentPanel = contentPanel;\n\t\tthis.node = Objects.requireNonNull(node);\n\n\t\tsetMarkOccurrences(false);\n\t\tsetFadeCurrentLineHighlight(true);\n\t\tsetAntiAliasingEnabled(true);\n\t\tapplyEditableProperties(node);\n\t\tloadSettings();\n\n\t\tJadxSettings settings = contentPanel.getMainWindow().getSettings();\n\t\tsetLineWrap(settings.isCodeAreaLineWrap());\n\n\t\tZoomActions.register(this, settings, this::loadSettings);\n\n\t\tif (node instanceof JEditableNode) {\n\t\t\tJEditableNode editableNode = (JEditableNode) node;\n\t\t\taddSaveActions(editableNode);\n\t\t\taddChangeUpdates(editableNode);\n\t\t} else {\n\t\t\taddCaretActions();\n\t\t\taddFastCopyAction();\n\t\t}\n\t}\n\n\tprivate void applyEditableProperties(JNode node) {\n\t\tboolean editable = node.isEditable();\n\t\tsetEditable(editable);\n\t\tif (editable) {\n\t\t\tsetCloseCurlyBraces(true);\n\t\t\tsetCloseMarkupTags(true);\n\t\t\tsetAutoIndentEnabled(true);\n\t\t\tsetClearWhitespaceLinesEnabled(true);\n\t\t}\n\t}\n\n\t@Override\n\tprotected JPopupMenu createPopupMenu() {\n\t\tJPopupMenu menu = new JPopupMenu();\n\t\tif (node.isEditable()) {\n\t\t\tmenu.add(createPopupMenuItem(getAction(UNDO_ACTION)));\n\t\t\tmenu.add(createPopupMenuItem(getAction(REDO_ACTION)));\n\t\t\tmenu.addSeparator();\n\t\t\tmenu.add(createPopupMenuItem(cutAction));\n\t\t\tmenu.add(createPopupMenuItem(copyAction));\n\t\t\tmenu.add(createPopupMenuItem(getAction(PASTE_ACTION)));\n\t\t\tmenu.add(createPopupMenuItem(getAction(DELETE_ACTION)));\n\t\t\tmenu.addSeparator();\n\t\t\tmenu.add(createPopupMenuItem(getAction(SELECT_ALL_ACTION)));\n\t\t} else {\n\t\t\tmenu.add(createPopupMenuItem(copyAction));\n\t\t\tmenu.add(createPopupMenuItem(getAction(SELECT_ALL_ACTION)));\n\t\t}\n\t\tappendFoldingMenu(menu);\n\t\tappendWrapLineMenu(menu);\n\t\treturn menu;\n\t}\n\n\t@Override\n\tprotected void appendFoldingMenu(JPopupMenu popup) {\n\t\t// append code folding popup menu entry only if enabled\n\t\tif (isCodeFoldingEnabled()) {\n\t\t\tsuper.appendFoldingMenu(popup);\n\t\t}\n\t}\n\n\tprivate void appendWrapLineMenu(JPopupMenu popupMenu) {\n\t\tJadxSettings settings = contentPanel.getMainWindow().getSettings();\n\t\tpopupMenu.addSeparator();\n\t\tJCheckBoxMenuItem wrapItem = new JCheckBoxMenuItem(NLS.str(\"popup.line_wrap\"), getLineWrap());\n\t\twrapItem.setAction(new AbstractAction(NLS.str(\"popup.line_wrap\")) {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tboolean wrap = !getLineWrap();\n\t\t\t\tsettings.setCodeAreaLineWrap(wrap);\n\t\t\t\tsettings.sync();\n\t\t\t\tcontentPanel.getTabbedPane().getTabs().forEach(v -> {\n\t\t\t\t\tif (v instanceof AbstractCodeContentPanel) {\n\t\t\t\t\t\tAbstractCodeArea codeArea = ((AbstractCodeContentPanel) v).getCodeArea();\n\t\t\t\t\t\tsetCodeAreaLineWrap(codeArea, wrap);\n\t\t\t\t\t\tif (v instanceof ClassCodeContentPanel) {\n\t\t\t\t\t\t\tcodeArea = ((ClassCodeContentPanel) v).getSmaliCodeArea();\n\t\t\t\t\t\t\tsetCodeAreaLineWrap(codeArea, wrap);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t\tpopupMenu.add(wrapItem);\n\t\tpopupMenu.addPopupMenuListener(new DefaultPopupMenuListener() {\n\t\t\t@Override\n\t\t\tpublic void popupMenuWillBecomeVisible(PopupMenuEvent e) {\n\t\t\t\twrapItem.setState(getLineWrap());\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void setCodeAreaLineWrap(AbstractCodeArea codeArea, boolean wrap) {\n\t\tcodeArea.setLineWrap(wrap);\n\t\tif (codeArea.isVisible()) {\n\t\t\tcodeArea.repaint();\n\t\t}\n\t}\n\n\tprivate void addCaretActions() {\n\t\tCaret caret = getCaret();\n\t\tif (caret instanceof DefaultCaret) {\n\t\t\t((DefaultCaret) caret).setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);\n\t\t}\n\t\tthis.addFocusListener(new FocusListener() {\n\t\t\t// fix caret missing bug.\n\t\t\t// when lost focus set visible to false,\n\t\t\t// and when regained set back to true will force\n\t\t\t// the caret to be repainted.\n\t\t\t@Override\n\t\t\tpublic void focusGained(FocusEvent e) {\n\t\t\t\tcaret.setVisible(true);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void focusLost(FocusEvent e) {\n\t\t\t\tcaret.setVisible(false);\n\t\t\t}\n\t\t});\n\t\taddCaretListener(new CaretListener() {\n\t\t\tint lastPos = -1;\n\t\t\tString lastText = \"\";\n\n\t\t\t@Override\n\t\t\tpublic void caretUpdate(CaretEvent e) {\n\t\t\t\tint pos = getCaretPosition();\n\t\t\t\tif (lastPos != pos) {\n\t\t\t\t\tlastPos = pos;\n\t\t\t\t\tlastText = highlightCaretWord(lastText, pos);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Ctrl+C will copy highlighted word\n\t */\n\tprivate void addFastCopyAction() {\n\t\taddKeyListener(new KeyAdapter() {\n\t\t\t@Override\n\t\t\tpublic void keyPressed(KeyEvent e) {\n\t\t\t\tif (e.getKeyCode() == KeyEvent.VK_C && UiUtils.isCtrlDown(e)) {\n\t\t\t\t\tUiUtils.copyToClipboard(getSelectedTokenOrWord());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * If the user has selected an individual word, for example by clicking and dragging\n\t * the mouse, then get that. Otherwise get the token underneath the cursor.\n\t * This is useful when the token is a string or comment and we want to control or copy\n\t * the word rather than the whole thing.\n\t *\n\t * @return The word or the token text\n\t */\n\tpublic @Nullable String getSelectedTokenOrWord() {\n\t\tfinal String rc = getSelectedText();\n\t\tif (rc == null) {\n\t\t\treturn getWordUnderCaret();\n\t\t}\n\t\tif (StringUtils.isEmpty(rc)) {\n\t\t\treturn getWordUnderCaret();\n\t\t}\n\t\treturn rc;\n\t}\n\n\tprivate void addSaveActions(JEditableNode node) {\n\t\taddKeyListener(new KeyAdapter() {\n\t\t\t@Override\n\t\t\tpublic void keyPressed(KeyEvent e) {\n\t\t\t\tif (e.getKeyCode() == KeyEvent.VK_S && UiUtils.isCtrlDown(e)) {\n\t\t\t\t\tnode.save(AbstractCodeArea.this.getText());\n\t\t\t\t\tnode.setChanged(false);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void addChangeUpdates(JEditableNode editableNode) {\n\t\tgetDocument().addDocumentListener(new DocumentUpdateListener(ev -> {\n\t\t\tif (loaded.get()) {\n\t\t\t\teditableNode.setChanged(true);\n\t\t\t}\n\t\t}));\n\t}\n\n\tprivate String highlightCaretWord(String lastText, int pos) {\n\t\tString text = getWordByPosition(pos);\n\t\tif (StringUtils.isEmpty(text)) {\n\t\t\thighlightAllMatches(null);\n\t\t\tlastText = \"\";\n\t\t} else if (!lastText.equals(text)) {\n\t\t\thighlightAllMatches(text);\n\t\t\tlastText = text;\n\t\t}\n\t\treturn lastText;\n\t}\n\n\t@Nullable\n\tpublic String getWordUnderCaret() {\n\t\treturn getWordByPosition(getCaretPosition());\n\t}\n\n\tpublic @Nullable String getWordByPosition(int offset) {\n\t\tToken token = getWordTokenAtOffset(offset);\n\t\tif (token == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString str = token.getLexeme();\n\t\tint len = str.length();\n\t\tif (len > 2 && str.startsWith(\"\\\"\") && str.endsWith(\"\\\"\")) {\n\t\t\treturn str.substring(1, len - 1);\n\t\t}\n\t\treturn str;\n\t}\n\n\t/**\n\t * Return any word token (not whitespace or special symbol) at offset.\n\t * Select the previous token if the cursor at word end (current token already is whitespace)\n\t */\n\tpublic @Nullable Token getWordTokenAtOffset(int offset) {\n\t\ttry {\n\t\t\tint line = this.getLineOfOffset(offset);\n\t\t\tToken lineTokens = this.getTokenListForLine(line);\n\t\t\tToken token = null;\n\t\t\tToken prevToken = null;\n\t\t\tfor (Token t = lineTokens; t != null && t.isPaintable(); t = t.getNextToken()) {\n\t\t\t\tif (t.containsPosition(offset)) {\n\t\t\t\t\ttoken = t;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tprevToken = t;\n\t\t\t}\n\t\t\tif (token == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (isWordToken(token)) {\n\t\t\t\treturn token;\n\t\t\t}\n\t\t\tif (isWordToken(prevToken)) {\n\t\t\t\treturn prevToken;\n\t\t\t}\n\t\t\treturn null;\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to get token at pos: {}\", offset, e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static boolean isWordToken(@Nullable Token token) {\n\t\tif (token == null) {\n\t\t\treturn false;\n\t\t}\n\t\tswitch (token.getType()) {\n\t\t\tcase TokenTypes.NULL:\n\t\t\tcase TokenTypes.WHITESPACE:\n\t\t\tcase TokenTypes.SEPARATOR:\n\t\t\tcase TokenTypes.OPERATOR:\n\t\t\tcase TokenTypes.FUNCTION:\n\t\t\t\treturn false;\n\n\t\t\tcase TokenTypes.IDENTIFIER:\n\t\t\t\tif (token.length() == 1) {\n\t\t\t\t\tchar ch = token.charAt(0);\n\t\t\t\t\treturn ch != ';' && ch != '.' && ch != ',';\n\t\t\t\t}\n\t\t\t\treturn true;\n\n\t\t\tdefault:\n\t\t\t\treturn true;\n\t\t}\n\t}\n\n\tpublic abstract ICodeInfo getCodeInfo();\n\n\tpublic void load() {\n\t\tif (isLoaded()) {\n\t\t\treturn;\n\t\t}\n\t\tIBackgroundTask loadTask = getLoadTask();\n\t\tcontentPanel.getMainWindow().getBackgroundExecutor().execute(loadTask);\n\t}\n\n\t/**\n\t * Implement in this method the code that loads and sets the content to be displayed\n\t * Call `setLoaded()` on load finish.\n\t */\n\tpublic abstract IBackgroundTask getLoadTask();\n\n\tpublic void setLoaded() {\n\t\tthis.loaded.set(true);\n\t\tdiscardAllEdits(); // disable 'undo' action to empty state (before load)\n\t}\n\n\tpublic void setUnLoaded() {\n\t\tthis.loaded.set(false);\n\t}\n\n\tpublic boolean isLoaded() {\n\t\treturn loaded.get();\n\t}\n\n\t/**\n\t * Implement in this method the code that reloads node from cache and sets the new content to be\n\t * displayed\n\t */\n\tpublic abstract void refresh();\n\n\tpublic static RSyntaxTextArea getDefaultArea(MainWindow mainWindow) {\n\t\tRSyntaxTextArea area = new RSyntaxTextArea();\n\t\tarea.setEditable(false);\n\t\tarea.setCodeFoldingEnabled(false);\n\t\tarea.setAntiAliasingEnabled(true);\n\t\tloadCommonSettings(mainWindow, area);\n\t\treturn area;\n\t}\n\n\tpublic static void loadCommonSettings(MainWindow mainWindow, RSyntaxTextArea area) {\n\t\tJadxSettings settings = mainWindow.getSettings();\n\t\tmainWindow.getEditorThemeManager().apply(area);\n\t\tarea.setFont(settings.getCodeFont());\n\t\tGutter gutter = RSyntaxUtilities.getGutter(area);\n\t\tif (gutter != null) {\n\t\t\tgutter.setLineNumberFont(settings.getCodeFont());\n\t\t}\n\t}\n\n\tpublic void loadSettings() {\n\t\tloadCommonSettings(contentPanel.getMainWindow(), this);\n\t}\n\n\tpublic void scrollToPos(int pos) {\n\t\ttry {\n\t\t\tsetCaretPosition(pos);\n\t\t\tcenterCurrentLine();\n\t\t\tforceCurrentLineHighlightRepaint();\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Can't scroll to position {}\", pos, e);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void centerCurrentLine() {\n\t\tJViewport viewport = (JViewport) SwingUtilities.getAncestorOfClass(JViewport.class, this);\n\t\tif (viewport == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tRectangle r = modelToView(getCaretPosition());\n\t\t\tif (r == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tint extentHeight = viewport.getExtentSize().height;\n\t\t\tDimension viewSize = viewport.getViewSize();\n\t\t\tif (viewSize == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tint viewHeight = viewSize.height;\n\n\t\t\tint y = Math.max(0, r.y - extentHeight / 2);\n\t\t\ty = Math.min(y, viewHeight - extentHeight);\n\n\t\t\tviewport.setViewPosition(new Point(0, y));\n\t\t} catch (BadLocationException e) {\n\t\t\tLOG.debug(\"Can't center current line\", e);\n\t\t}\n\t}\n\n\t/**\n\t * @param str - if null -> reset current highlights\n\t */\n\tprivate void highlightAllMatches(@Nullable String str) {\n\t\ttry {\n\t\t\tSearchContext context = new SearchContext(str);\n\t\t\tcontext.setMarkAll(true);\n\t\t\tcontext.setMatchCase(true);\n\t\t\tcontext.setWholeWord(true);\n\t\t\tSearchEngine.markAll(this, context);\n\t\t} catch (Throwable e) {\n\t\t\t// syntax parsing can fail for incorrect code\n\t\t\tLOG.debug(\"Search highlight failed\", e);\n\t\t}\n\t}\n\n\tpublic @Nullable JumpPosition getCurrentPosition() {\n\t\tint pos = getCaretPosition();\n\t\tif (pos == 0) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new JumpPosition(node, pos);\n\t}\n\n\tpublic int getLineStartFor(int pos) throws BadLocationException {\n\t\treturn getLineStartOffset(getLineOfOffset(pos));\n\t}\n\n\tpublic String getLineAt(int pos) throws BadLocationException {\n\t\treturn getLineText(getLineOfOffset(pos) + 1);\n\t}\n\n\tpublic String getLineText(int line) throws BadLocationException {\n\t\tint lineNum = line - 1;\n\t\tint startOffset = getLineStartOffset(lineNum);\n\t\tint endOffset = getLineEndOffset(lineNum);\n\t\treturn getText(startOffset, endOffset - startOffset);\n\t}\n\n\tpublic ContentPanel getContentPanel() {\n\t\treturn contentPanel;\n\t}\n\n\tpublic JNode getNode() {\n\t\treturn node;\n\t}\n\n\t@Nullable\n\tpublic JClass getJClass() {\n\t\tif (node instanceof JClass) {\n\t\t\treturn (JClass) node;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic boolean isDisposed() {\n\t\treturn node == null;\n\t}\n\n\tpublic void dispose() {\n\t\t// clear internals\n\t\ttry {\n\t\t\tsetIgnoreRepaint(true);\n\t\t\tsetText(\"\");\n\t\t\tsetEnabled(false);\n\t\t\tsetSyntaxEditingStyle(SYNTAX_STYLE_NONE);\n\t\t\tsetLinkGenerator(null);\n\t\t\tfor (MouseListener mouseListener : getMouseListeners()) {\n\t\t\t\tremoveMouseListener(mouseListener);\n\t\t\t}\n\t\t\tfor (MouseMotionListener mouseMotionListener : getMouseMotionListeners()) {\n\t\t\t\tremoveMouseMotionListener(mouseMotionListener);\n\t\t\t}\n\t\t\tJPopupMenu popupMenu = getPopupMenu();\n\t\t\tfor (PopupMenuListener popupMenuListener : popupMenu.getPopupMenuListeners()) {\n\t\t\t\tpopupMenu.removePopupMenuListener(popupMenuListener);\n\t\t\t}\n\t\t\tfor (Component component : popupMenu.getComponents()) {\n\t\t\t\tif (component instanceof JMenuItem) {\n\t\t\t\t\tAction action = ((JMenuItem) component).getAction();\n\t\t\t\t\tif (action instanceof JNodeAction) {\n\t\t\t\t\t\t((JNodeAction) action).dispose();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tpopupMenu.removeAll();\n\t\t} catch (Throwable e) {\n\t\t\tLOG.debug(\"Error on code area dispose\", e);\n\t\t}\n\t\t// code area reference can still be used somewhere in UI objects,\n\t\t// reset node reference to allow to GC jadx objects tree\n\t\tnode = null;\n\t\tcontentPanel = null;\n\t}\n\n\t@Override\n\tpublic Dimension getPreferredSize() {\n\t\ttry {\n\t\t\treturn super.getPreferredSize();\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to calculate preferred size for code area\", e);\n\t\t\t// copied from javax.swing.JTextArea.getPreferredSize (super call above)\n\t\t\t// as a fallback for returned null size\n\t\t\tDimension d = new Dimension(400, 400);\n\t\t\tInsets insets = getInsets();\n\t\t\tif (getColumns() != 0) {\n\t\t\t\td.width = Math.max(d.width, getColumns() * getColumnWidth() + insets.left + insets.right);\n\t\t\t}\n\t\t\tif (getRows() != 0) {\n\t\t\t\td.height = Math.max(d.height, getRows() * getRowHeight() + insets.top + insets.bottom);\n\t\t\t}\n\t\t\treturn d;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeContentPanel.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.awt.Component;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.tab.TabbedPane;\n\n/**\n * The abstract base class for a content panel that show text based code or a.g. a resource\n */\npublic abstract class AbstractCodeContentPanel extends ContentPanel {\n\tprivate static final long serialVersionUID = 4685846894279064422L;\n\n\tprotected AbstractCodeContentPanel(TabbedPane panel, JNode jnode) {\n\t\tsuper(panel, jnode);\n\t}\n\n\tpublic abstract @Nullable AbstractCodeArea getCodeArea();\n\n\tpublic abstract Component getChildrenComponent();\n\n\tpublic void scrollToPos(int pos) {\n\t\tAbstractCodeArea codeArea = getCodeArea();\n\t\tif (codeArea != null) {\n\t\t\tcodeArea.requestFocus();\n\t\t\tcodeArea.scrollToPos(pos);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/BinaryContentPanel.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.awt.BorderLayout;\nimport java.awt.Component;\nimport java.nio.charset.StandardCharsets;\n\nimport javax.swing.JSplitPane;\nimport javax.swing.JTabbedPane;\nimport javax.swing.SwingUtilities;\nimport javax.swing.border.EmptyBorder;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ResourcesLoader;\nimport jadx.api.resources.ResourceContentType;\nimport jadx.gui.jobs.BackgroundExecutor;\nimport jadx.gui.jobs.IBackgroundTask;\nimport jadx.gui.jobs.TaskWithExtraOnFinish;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.settings.LineNumbersMode;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.treemodel.JResource;\nimport jadx.gui.ui.hexviewer.HexPreviewPanel;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.UiUtils;\n\npublic class BinaryContentPanel extends AbstractCodeContentPanel {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(BinaryContentPanel.class);\n\tprivate final transient CodePanel textCodePanel;\n\tprivate final transient HexPreviewPanel hexPreviewPanel;\n\tprivate final transient JTabbedPane areaTabbedPane;\n\n\tpublic BinaryContentPanel(TabbedPane panel, JNode jnode, boolean supportsText) {\n\t\tsuper(panel, jnode);\n\t\tsetLayout(new BorderLayout());\n\t\tsetBorder(new EmptyBorder(0, 0, 0, 0));\n\t\tif (supportsText) {\n\t\t\ttextCodePanel = new CodePanel(new CodeArea(this, jnode));\n\t\t} else {\n\t\t\ttextCodePanel = null;\n\t\t}\n\t\thexPreviewPanel = new HexPreviewPanel(getSettings());\n\t\thexPreviewPanel.getInspector().setVisible(false);\n\n\t\tareaTabbedPane = buildTabbedPane();\n\t\tadd(areaTabbedPane);\n\n\t\tSwingUtilities.invokeLater(this::loadSelectedPanel);\n\t}\n\n\tprivate void loadHexView() {\n\t\tif (hexPreviewPanel.isDataLoaded()) {\n\t\t\treturn;\n\t\t}\n\t\tUiUtils.notUiThreadGuard();\n\t\tbyte[] bytes = getNodeBytes();\n\t\tUiUtils.uiRunAndWait(() -> hexPreviewPanel.setData(bytes));\n\t}\n\n\tprivate byte[] getNodeBytes() {\n\t\tJNode binaryNode = getNode();\n\t\tif (binaryNode instanceof JResource) {\n\t\t\tJResource jResource = (JResource) binaryNode;\n\t\t\ttry {\n\t\t\t\treturn ResourcesLoader.decodeStream(jResource.getResFile(), (size, is) -> is.readAllBytes());\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to directly load resource binary data {}: {}\", jResource.getName(), e.getMessage());\n\t\t\t}\n\t\t}\n\t\treturn binaryNode.getCodeInfo().getCodeStr().getBytes(StandardCharsets.US_ASCII);\n\t}\n\n\tprivate JTabbedPane buildTabbedPane() {\n\t\tJTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);\n\t\ttabbedPane.setBorder(new EmptyBorder(0, 0, 0, 0));\n\t\ttabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);\n\t\tif (textCodePanel != null) {\n\t\t\ttabbedPane.add(textCodePanel, \"Text\");\n\t\t}\n\t\ttabbedPane.add(hexPreviewPanel, \"Hex\");\n\t\ttabbedPane.addChangeListener(e -> {\n\t\t\tgetMainWindow().toggleHexViewMenu();\n\t\t\tloadSelectedPanel();\n\t\t});\n\t\treturn tabbedPane;\n\t}\n\n\tprivate void loadSelectedPanel() {\n\t\tBackgroundExecutor bgExec = getMainWindow().getBackgroundExecutor();\n\t\tComponent codePanel = getSelectedPanel();\n\t\tif (codePanel instanceof CodeArea) {\n\t\t\tCodeArea codeArea = (CodeArea) codePanel;\n\t\t\tcodeArea.load();\n\t\t} else {\n\t\t\tbgExec.startLoading(this::loadHexView);\n\t\t}\n\t}\n\n\t@Override\n\tpublic AbstractCodeArea getCodeArea() {\n\t\tif (textCodePanel != null) {\n\t\t\treturn textCodePanel.getCodeArea();\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void scrollToPos(int pos) {\n\t\tUiUtils.uiThreadGuard();\n\t\tBackgroundExecutor bgExec = getMainWindow().getBackgroundExecutor();\n\t\tif (getNode().getContentType() == ResourceContentType.CONTENT_TEXT) {\n\t\t\tareaTabbedPane.setSelectedComponent(textCodePanel);\n\t\t\tAbstractCodeArea codeArea = textCodePanel.getCodeArea();\n\t\t\tif (codeArea.isLoaded()) {\n\t\t\t\tcodeArea.scrollToPos(pos);\n\t\t\t} else {\n\t\t\t\tIBackgroundTask loadTask = codeArea.getLoadTask();\n\t\t\t\tbgExec.execute(new TaskWithExtraOnFinish(loadTask, () -> codeArea.scrollToPos(pos)));\n\t\t\t}\n\t\t} else {\n\t\t\tareaTabbedPane.setSelectedComponent(hexPreviewPanel);\n\t\t\tbgExec.startLoading(this::loadHexView, () -> hexPreviewPanel.scrollToOffset(pos));\n\t\t}\n\t}\n\n\t@Override\n\tpublic Component getChildrenComponent() {\n\t\treturn getSelectedPanel();\n\t}\n\n\t@Override\n\tpublic void loadSettings() {\n\t\tif (textCodePanel != null) {\n\t\t\ttextCodePanel.loadSettings();\n\t\t}\n\t\tupdateUI();\n\t}\n\n\t@Override\n\tpublic JadxSettings getSettings() {\n\t\tJadxSettings settings = super.getSettings();\n\t\tsettings.setLineNumbersMode(LineNumbersMode.NORMAL);\n\t\treturn settings;\n\t}\n\n\tprivate Component getSelectedPanel() {\n\t\tComponent selectedComponent = areaTabbedPane.getSelectedComponent();\n\t\tComponent selectedPanel;\n\t\tif (selectedComponent instanceof CodePanel) {\n\t\t\tselectedPanel = ((CodePanel) selectedComponent).getCodeArea();\n\t\t} else if (selectedComponent instanceof JSplitPane) {\n\t\t\tselectedPanel = ((JSplitPane) selectedComponent).getLeftComponent();\n\t\t} else if (selectedComponent instanceof HexPreviewPanel) {\n\t\t\tselectedPanel = selectedComponent;\n\t\t} else {\n\t\t\tthrow new RuntimeException(\"tabbedPane.getSelectedComponent returned a Component \"\n\t\t\t\t\t+ \"of unexpected type \" + selectedComponent);\n\t\t}\n\t\treturn selectedPanel;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/ClassCodeContentPanel.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.awt.BorderLayout;\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.Point;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport javax.swing.JCheckBox;\nimport javax.swing.JSplitPane;\nimport javax.swing.JTabbedPane;\nimport javax.swing.JToolBar;\nimport javax.swing.SwingUtilities;\nimport javax.swing.border.EmptyBorder;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.DecompilationMode;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.ui.codearea.mode.JCodeMode;\nimport jadx.gui.ui.codearea.sync.CodePanelSyncee;\nimport jadx.gui.ui.codearea.sync.CodePanelSyncer;\nimport jadx.gui.ui.codearea.sync.CodePanelSyncerAbstractFactory;\nimport jadx.gui.ui.codearea.sync.fallback.FallbackSyncer;\nimport jadx.gui.ui.panel.IViewStateSupport;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.NLS;\n\nimport static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_TRAILING_COMPONENT;\n\n/**\n * Displays one class with two different views:\n *\n * <ul>\n * <li>Java source code of the selected class (default)</li>\n * <li>Smali source code of the selected class</li>\n * </ul>\n */\npublic final class ClassCodeContentPanel extends AbstractCodeContentPanel implements IViewStateSupport {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ClassCodeContentPanel.class);\n\tprivate static final long serialVersionUID = -7229931102504634591L;\n\n\tprivate final transient CodePanel javaCodePanel;\n\tprivate final transient CodePanel smaliCodePanel;\n\tprivate final transient JTabbedPane areaTabbedPane;\n\tprivate final AtomicBoolean syncInProgress = new AtomicBoolean(false);\n\n\tprivate boolean splitView = false;\n\tprivate final JCheckBox splitCheckboxNormal;\n\n\tpublic ClassCodeContentPanel(TabbedPane panel, JClass jCls) {\n\t\tsuper(panel, jCls);\n\n\t\tjavaCodePanel = new CodePanel(new CodeArea(this, jCls));\n\t\tsmaliCodePanel = new CodePanel(new SmaliArea(this, jCls, false));\n\t\tareaTabbedPane = buildTabbedPane(jCls);\n\t\tsplitCheckboxNormal = addCustomControls(areaTabbedPane, false);\n\n\t\tjavaCodePanel.load();\n\t\tinitView(false);\n\t}\n\n\tprivate void initView(boolean splitViewEnabled) {\n\t\tsplitView = splitViewEnabled;\n\t\tremoveAll();\n\t\tsetLayout(new BorderLayout());\n\t\tsetBorder(new EmptyBorder(0, 0, 0, 0));\n\t\tif (splitViewEnabled) {\n\t\t\tsetupSplitPane();\n\t\t} else {\n\t\t\tjavaCodePanel.load();\n\t\t\tsmaliCodePanel.load();\n\t\t\tattachSyncListeners(javaCodePanel, smaliCodePanel);\n\t\t\tareaTabbedPane.setSelectedIndex(0); // default to Java\n\t\t\tsplitCheckboxNormal.setSelected(false);\n\t\t\tadd(areaTabbedPane);\n\t\t}\n\t\trevalidate();\n\t\trepaint();\n\t}\n\n\tprivate void attachSyncListeners(CodePanel javaPanel, CodePanel smaliPanel) {\n\t\tjavaPanel.getCodeArea().addCaretListener(e -> {\n\t\t\tif (syncInProgress.get()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsyncInProgress.set(true);\n\t\t\tsyncToMethod(javaPanel, smaliPanel);\n\t\t\tsyncInProgress.set(false);\n\t\t});\n\n\t\tsmaliPanel.getCodeArea().addCaretListener(e -> {\n\t\t\tif (syncInProgress.get()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsyncInProgress.set(true);\n\t\t\tsyncToMethod(smaliPanel, javaPanel);\n\t\t\tsyncInProgress.set(false);\n\t\t});\n\t}\n\n\tprivate void setupSplitPane() {\n\t\tJTabbedPane leftTabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);\n\t\tJTabbedPane rightTabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);\n\n\t\tCodePanel[] leftPanels = {\n\t\t\t\tnew CodePanel(new CodeArea(this, (JClass) node)), // Java\n\t\t\t\tnew CodePanel(new SmaliArea(this, (JClass) node, false)), // Smali\n\t\t\t\tnew CodePanel(new SmaliArea(this, (JClass) node, true)), // Smali with Dalvik\n\t\t\t\tnew CodePanel(new CodeArea(this, new JCodeMode((JClass) node, DecompilationMode.SIMPLE))), // Simple\n\t\t\t\tnew CodePanel(new CodeArea(this, new JCodeMode((JClass) node, DecompilationMode.FALLBACK))) // Fallback\n\t\t};\n\n\t\tCodePanel[] rightPanels = {\n\t\t\t\tnew CodePanel(new SmaliArea(this, (JClass) node, false)), // Smali\n\t\t\t\tnew CodePanel(new SmaliArea(this, (JClass) node, true)), // Smali with Dalvik\n\t\t\t\tnew CodePanel(new CodeArea(this, (JClass) node)), // Java\n\t\t\t\tnew CodePanel(new CodeArea(this, new JCodeMode((JClass) node, DecompilationMode.SIMPLE))), // Simple\n\t\t\t\tnew CodePanel(new CodeArea(this, new JCodeMode((JClass) node, DecompilationMode.FALLBACK))) // Fallback\n\t\t};\n\n\t\tleftTabbedPane.add(leftPanels[0], NLS.str(\"tabs.code\"));\n\t\tleftTabbedPane.add(leftPanels[1], NLS.str(\"tabs.smali\"));\n\t\tleftTabbedPane.add(leftPanels[2], NLS.str(\"tabs.smali_bytecode\"));\n\t\tleftTabbedPane.add(leftPanels[3], \"Simple\");\n\t\tleftTabbedPane.add(leftPanels[4], \"Fallback\");\n\n\t\trightTabbedPane.add(rightPanels[0], NLS.str(\"tabs.smali\"));\n\t\trightTabbedPane.add(rightPanels[1], NLS.str(\"tabs.smali_bytecode\"));\n\t\trightTabbedPane.add(rightPanels[2], NLS.str(\"tabs.code\"));\n\t\trightTabbedPane.add(rightPanels[3], \"Simple\");\n\t\trightTabbedPane.add(rightPanels[4], \"Fallback\");\n\n\t\tfor (CodePanel p : leftPanels) {\n\t\t\tp.load();\n\t\t}\n\t\tfor (CodePanel p : rightPanels) {\n\t\t\tp.load();\n\t\t}\n\n\t\tleftTabbedPane.addChangeListener(e -> ((CodePanel) leftTabbedPane.getSelectedComponent()).load());\n\t\trightTabbedPane.addChangeListener(e -> ((CodePanel) rightTabbedPane.getSelectedComponent()).load());\n\n\t\t// Attach caret sync between all combinations\n\t\tfor (CodePanel leftPanel : leftPanels) {\n\t\t\tfor (CodePanel rightPanel : rightPanels) {\n\t\t\t\tattachSyncListeners(leftPanel, rightPanel);\n\t\t\t}\n\t\t}\n\n\t\t// Create and configure split pane\n\t\tJSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftTabbedPane, rightTabbedPane);\n\t\tsplitPane.setResizeWeight(0.5);\n\t\tleftTabbedPane.setMinimumSize(new Dimension(200, 200));\n\t\trightTabbedPane.setMinimumSize(new Dimension(200, 200));\n\t\tadd(splitPane);\n\n\t\t// Set divider location after layout\n\t\tSwingUtilities.invokeLater(() -> splitPane.setDividerLocation(0.5));\n\n\t\trightTabbedPane.setSelectedIndex(0);\n\t\taddCustomControls(leftTabbedPane, true);\n\t}\n\n\tprivate JTabbedPane buildTabbedPane(JClass jCls) {\n\t\tJTabbedPane areaTabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);\n\t\tareaTabbedPane.setBorder(new EmptyBorder(0, 0, 0, 0));\n\t\tareaTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);\n\t\tareaTabbedPane.add(javaCodePanel, NLS.str(\"tabs.code\"));\n\t\tareaTabbedPane.add(smaliCodePanel, NLS.str(\"tabs.smali\"));\n\t\tareaTabbedPane.add(new CodePanel(new SmaliArea(this, jCls, true)), NLS.str(\"tabs.smali_bytecode\"));\n\t\tareaTabbedPane.add(new CodePanel(new CodeArea(this, new JCodeMode(jCls, DecompilationMode.SIMPLE))), \"Simple\");\n\t\tareaTabbedPane.add(new CodePanel(new CodeArea(this, new JCodeMode(jCls, DecompilationMode.FALLBACK))), \"Fallback\");\n\t\tareaTabbedPane.addChangeListener(e -> {\n\t\t\tCodePanel selectedPanel = (CodePanel) areaTabbedPane.getSelectedComponent();\n\t\t\t// TODO: to run background load extract ui update to other method\n\t\t\tselectedPanel.load();\n\t\t\t// execInBackground(selectedPanel::load);\n\t\t});\n\t\treturn areaTabbedPane;\n\t}\n\n\tprivate JCheckBox addCustomControls(JTabbedPane tabbedPane, boolean splitCheckboxInitialState) {\n\t\tJCheckBox splitCheckBox = new JCheckBox(\"Split view\", splitCheckboxInitialState);\n\t\tsplitCheckBox.addItemListener(e -> {\n\t\t\tboolean newSplitView = splitCheckBox.isSelected();\n\t\t\tif (splitView != newSplitView) {\n\t\t\t\tthis.initView(newSplitView);\n\t\t\t}\n\t\t});\n\n\t\tJToolBar trailing = new JToolBar();\n\t\ttrailing.setFloatable(false);\n\t\ttrailing.setBorder(null);\n\t\t// trailing.add(Box.createHorizontalGlue());\n\t\ttrailing.addSeparator(new Dimension(50, 1));\n\t\ttrailing.add(splitCheckBox);\n\t\ttabbedPane.putClientProperty(TABBED_PANE_TRAILING_COMPONENT, trailing);\n\t\treturn splitCheckBox;\n\t}\n\n\t@Override\n\tpublic void loadSettings() {\n\t\tjavaCodePanel.loadSettings();\n\t\tsmaliCodePanel.loadSettings();\n\t\tupdateUI();\n\t}\n\n\t@Override\n\tpublic AbstractCodeArea getCodeArea() {\n\t\treturn javaCodePanel.getCodeArea();\n\t}\n\n\t@Override\n\tpublic Component getChildrenComponent() {\n\t\treturn getCodeArea();\n\t}\n\n\tpublic CodePanel getJavaCodePanel() {\n\t\treturn javaCodePanel;\n\t}\n\n\tpublic void switchPanel() {\n\t\tboolean toSmali = areaTabbedPane.getSelectedComponent() == javaCodePanel;\n\t\tareaTabbedPane.setSelectedComponent(toSmali ? smaliCodePanel : javaCodePanel);\n\t}\n\n\tpublic AbstractCodeArea getCurrentCodeArea() {\n\t\treturn ((CodePanel) areaTabbedPane.getSelectedComponent()).getCodeArea();\n\t}\n\n\tpublic AbstractCodeArea getSmaliCodeArea() {\n\t\treturn smaliCodePanel.getCodeArea();\n\t}\n\n\tpublic void showSmaliPane() {\n\t\tareaTabbedPane.setSelectedComponent(smaliCodePanel);\n\t}\n\n\t@Override\n\tpublic void saveEditorViewState(EditorViewState viewState) {\n\t\tCodePanel codePanel = (CodePanel) areaTabbedPane.getSelectedComponent();\n\t\tint caretPos = codePanel.getCodeArea().getCaretPosition();\n\t\tPoint viewPoint = codePanel.getCodeScrollPane().getViewport().getViewPosition();\n\t\tString subPath = codePanel == javaCodePanel ? \"java\" : \"smali\";\n\t\tviewState.setSubPath(subPath);\n\t\tviewState.setCaretPos(caretPos);\n\t\tviewState.setViewPoint(viewPoint);\n\t}\n\n\t@Override\n\tpublic void restoreEditorViewState(EditorViewState viewState) {\n\t\tboolean isJava = viewState.getSubPath().equals(\"java\");\n\t\tCodePanel activePanel = isJava ? javaCodePanel : smaliCodePanel;\n\t\tareaTabbedPane.setSelectedComponent(activePanel);\n\t\ttry {\n\t\t\tactivePanel.getCodeScrollPane().getViewport().setViewPosition(viewState.getViewPoint());\n\t\t} catch (Exception e) {\n\t\t\tLOG.debug(\"Failed to restore view position: {}\", viewState.getViewPoint(), e);\n\t\t}\n\t\tint caretPos = viewState.getCaretPos();\n\t\ttry {\n\t\t\tAbstractCodeArea codeArea = activePanel.getCodeArea();\n\t\t\tint codeLen = codeArea.getDocument().getLength();\n\t\t\tif (caretPos >= 0 && caretPos < codeLen) {\n\t\t\t\tcodeArea.setCaretPosition(caretPos);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.debug(\"Failed to restore caret position: {}\", caretPos, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void dispose() {\n\t\tjavaCodePanel.dispose();\n\t\tsmaliCodePanel.dispose();\n\t\tfor (Component component : areaTabbedPane.getComponents()) {\n\t\t\tif (component instanceof CodePanel) {\n\t\t\t\t((CodePanel) component).dispose();\n\t\t\t}\n\t\t}\n\t\tsuper.dispose();\n\t}\n\n\tprivate void syncToMethod(CodePanel fromPanel, CodePanel toPanel) {\n\t\tif (!fromPanel.isShowing() || !toPanel.isShowing()) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tAbstractCodeArea from = fromPanel.getCodeArea();\n\t\t\tAbstractCodeArea to = toPanel.getCodeArea();\n\t\t\ttoPanel.load();\n\n\t\t\tif (from instanceof CodePanelSyncerAbstractFactory && to instanceof CodePanelSyncee) {\n\t\t\t\tCodePanelSyncer syncer = ((CodePanelSyncerAbstractFactory) from).createCodePanelSyncer();\n\t\t\t\tif (((CodePanelSyncee) to).sync(syncer)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!FallbackSyncer.sync(fromPanel, toPanel)) {\n\t\t\t\tLOG.warn(\"Code pane area sync not possible\");\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tLOG.warn(\"Failed to sync method/class across views: {}\", ex.getLocalizedMessage());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.awt.Point;\nimport java.awt.event.InputEvent;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport javax.swing.JPopupMenu;\nimport javax.swing.event.PopupMenuEvent;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxDocument;\nimport org.fife.ui.rsyntaxtextarea.Token;\nimport org.fife.ui.rsyntaxtextarea.TokenTypes;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.JavaClass;\nimport jadx.api.JavaNode;\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeMetadata;\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.jobs.IBackgroundTask;\nimport jadx.gui.jobs.LoadTask;\nimport jadx.gui.jobs.TaskWithExtraOnFinish;\nimport jadx.gui.settings.JadxProject;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JLoadableNode;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.treemodel.JResource;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.action.CommentSearchAction;\nimport jadx.gui.ui.action.FindUsageAction;\nimport jadx.gui.ui.action.FridaAction;\nimport jadx.gui.ui.action.GoToDeclarationAction;\nimport jadx.gui.ui.action.JNodeAction;\nimport jadx.gui.ui.action.JsonPrettifyAction;\nimport jadx.gui.ui.action.RenameAction;\nimport jadx.gui.ui.action.ViewCallGraphAction;\nimport jadx.gui.ui.action.ViewClassInheritanceGraphAction;\nimport jadx.gui.ui.action.ViewClassMethodGraphAction;\nimport jadx.gui.ui.action.ViewControlFlowGraphAction;\nimport jadx.gui.ui.action.ViewRawControlFlowGraphAction;\nimport jadx.gui.ui.action.ViewRegionControlFlowGraphAction;\nimport jadx.gui.ui.action.XposedAction;\nimport jadx.gui.ui.codearea.mode.JCodeMode;\nimport jadx.gui.ui.codearea.sync.CodePanelSyncee;\nimport jadx.gui.ui.codearea.sync.CodePanelSyncer;\nimport jadx.gui.ui.codearea.sync.CodePanelSyncerAbstractFactory;\nimport jadx.gui.ui.codearea.sync.JavaSyncer;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.utils.CaretPositionFix;\nimport jadx.gui.utils.DefaultPopupMenuListener;\nimport jadx.gui.utils.JNodeCache;\nimport jadx.gui.utils.JumpPosition;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.shortcut.ShortcutsController;\n\n/**\n * The {@link AbstractCodeArea} implementation used for displaying Java code and text based\n * resources (e.g. AndroidManifest.xml)\n */\npublic final class CodeArea extends AbstractCodeArea implements CodePanelSyncerAbstractFactory, CodePanelSyncee {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CodeArea.class);\n\n\tprivate static final long serialVersionUID = 6312736869579635796L;\n\n\tprivate @Nullable ICodeInfo cachedCodeInfo;\n\tprivate @Nullable MouseHoverHighlighter mouseHoverHighlighter;\n\tprivate final ShortcutsController shortcutsController;\n\n\tCodeArea(ContentPanel contentPanel, JNode node) {\n\t\tsuper(contentPanel, node);\n\t\tthis.shortcutsController = getMainWindow().getShortcutsController();\n\n\t\tsetSyntaxEditingStyle(node.getSyntaxName());\n\t\tboolean isJavaCode = isCodeNode();\n\t\tif (isJavaCode) {\n\t\t\t((RSyntaxDocument) getDocument()).setSyntaxStyle(new JadxTokenMaker(this));\n\t\t}\n\n\t\tif (node instanceof JResource && node.makeString().endsWith(\".json\")) {\n\t\t\taddMenuForJsonFile();\n\t\t}\n\n\t\tsetHyperlinksEnabled(true);\n\t\tsetCodeFoldingEnabled(true);\n\t\tsetLinkScanningMask(InputEvent.CTRL_DOWN_MASK);\n\t\tCodeLinkGenerator codeLinkGenerator = new CodeLinkGenerator(this);\n\t\tsetLinkGenerator(codeLinkGenerator);\n\t\taddMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(MouseEvent e) {\n\t\t\t\tif (e.isControlDown() || jumpOnDoubleClick(e)) {\n\t\t\t\t\tnavToDecl(e.getPoint());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tif (isJavaCode) {\n\t\t\tmouseHoverHighlighter = new MouseHoverHighlighter(this, codeLinkGenerator);\n\t\t\taddMouseMotionListener(mouseHoverHighlighter);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void loadSettings() {\n\t\tsuper.loadSettings();\n\t\tif (mouseHoverHighlighter != null) {\n\t\t\tmouseHoverHighlighter.loadSettings();\n\t\t}\n\t}\n\n\tpublic boolean isCodeNode() {\n\t\treturn node instanceof JClass || node instanceof JCodeMode;\n\t}\n\n\tprivate boolean jumpOnDoubleClick(MouseEvent e) {\n\t\treturn e.getClickCount() == 2 && getMainWindow().getSettings().isJumpOnDoubleClick();\n\t}\n\n\tprivate void navToDecl(Point point) {\n\t\tint offs = viewToModel2D(point);\n\t\tJNode node = getJNodeAtOffset(adjustOffsetForWordToken(offs));\n\t\tif (node != null) {\n\t\t\tcontentPanel.getTabsController().codeJump(node);\n\t\t}\n\t}\n\n\t@Override\n\tpublic ICodeInfo getCodeInfo() {\n\t\tif (cachedCodeInfo == null) {\n\t\t\tif (isDisposed()) {\n\t\t\t\tLOG.debug(\"CodeArea used after dispose!\");\n\t\t\t\treturn ICodeInfo.EMPTY;\n\t\t\t}\n\t\t\tcachedCodeInfo = Objects.requireNonNull(node.getCodeInfo());\n\t\t}\n\t\treturn cachedCodeInfo;\n\t}\n\n\t@Override\n\tpublic IBackgroundTask getLoadTask() {\n\t\tif (node instanceof JLoadableNode) {\n\t\t\tIBackgroundTask loadTask = ((JLoadableNode) node).getLoadTask();\n\t\t\tif (loadTask != null) {\n\t\t\t\treturn new TaskWithExtraOnFinish(loadTask, () -> {\n\t\t\t\t\tsetText(getCodeInfo().getCodeStr());\n\t\t\t\t\tsetCaretPosition(0);\n\t\t\t\t\tsetLoaded();\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn new LoadTask<>(\n\t\t\t\t() -> getCodeInfo().getCodeStr(),\n\t\t\t\tcode -> {\n\t\t\t\t\tsetText(code);\n\t\t\t\t\tsetCaretPosition(0);\n\t\t\t\t\tsetLoaded();\n\t\t\t\t});\n\t}\n\n\t@Override\n\tpublic void refresh() {\n\t\tcachedCodeInfo = null;\n\t\tsetText(getCodeInfo().getCodeStr());\n\t}\n\n\t@Override\n\tprotected JPopupMenu createPopupMenu() {\n\t\tJPopupMenu popup = super.createPopupMenu();\n\t\tif (node instanceof JClass) {\n\t\t\tappendCodeMenuItems(popup);\n\t\t}\n\t\treturn popup;\n\t}\n\n\tprivate void appendCodeMenuItems(JPopupMenu popupMenu) {\n\t\tShortcutsController shortcutsController = getMainWindow().getShortcutsController();\n\t\tJNodePopupBuilder popup = new JNodePopupBuilder(this, popupMenu, shortcutsController);\n\t\tpopup.addSeparator();\n\t\tpopup.add(new FindUsageAction(this));\n\t\tpopup.add(new UsageDialogPlusAction(this));\n\t\tpopup.add(new GoToDeclarationAction(this));\n\t\tpopup.add(new CommentAction(this));\n\t\tpopup.add(new CommentSearchAction(this));\n\t\tpopup.add(new RenameAction(this));\n\t\tpopup.addSeparator();\n\t\tpopup.add(new FridaAction(this));\n\t\tpopup.add(new XposedAction(this));\n\t\tpopup.addSeparator();\n\t\tpopup.add(new ViewClassInheritanceGraphAction(this));\n\t\tpopup.add(new ViewClassMethodGraphAction(this));\n\t\tpopup.add(new ViewCallGraphAction(this));\n\t\tpopup.addSubmenu(new JNodeAction[] {\n\t\t\t\tnew ViewControlFlowGraphAction(this),\n\t\t\t\tnew ViewRawControlFlowGraphAction(this),\n\t\t\t\tnew ViewRegionControlFlowGraphAction(this),\n\t\t}, NLS.str(\"popup.cfg_submenu\"));\n\t\tpopup.addSeparator();\n\t\tpopup.add(new ConvertNumberAction(this));\n\n\t\tgetMainWindow().getWrapper().getGuiPluginsContext().appendPopupMenus(this, popup);\n\n\t\t// move caret on mouse right button click\n\t\tpopupMenu.addPopupMenuListener(new DefaultPopupMenuListener() {\n\t\t\t@Override\n\t\t\tpublic void popupMenuWillBecomeVisible(PopupMenuEvent e) {\n\t\t\t\tCodeArea codeArea = CodeArea.this;\n\t\t\t\tif (codeArea.getSelectedText() == null) {\n\t\t\t\t\tint offset = UiUtils.getOffsetAtMousePosition(codeArea);\n\t\t\t\t\tif (offset >= 0) {\n\t\t\t\t\t\tcodeArea.setCaretPosition(offset);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void addMenuForJsonFile() {\n\t\tShortcutsController shortcutsController = getMainWindow().getShortcutsController();\n\t\tJNodePopupBuilder popup = new JNodePopupBuilder(this, getPopupMenu(), shortcutsController);\n\t\tpopup.addSeparator();\n\t\tpopup.add(new JsonPrettifyAction(this));\n\t}\n\n\t/**\n\t * Search start of word token at specified offset\n\t *\n\t * @return -1 if no word token found\n\t */\n\tpublic int adjustOffsetForWordToken(int offset) {\n\t\tToken token = getWordTokenAtOffset(offset);\n\t\tif (token == null) {\n\t\t\treturn -1;\n\t\t}\n\t\tint type = token.getType();\n\t\tif (isCodeNode()) {\n\t\t\tif (type == TokenTypes.IDENTIFIER || type == TokenTypes.FUNCTION) {\n\t\t\t\treturn token.getOffset();\n\t\t\t}\n\t\t\tif (type == TokenTypes.ANNOTATION && token.length() > 1) {\n\t\t\t\treturn token.getOffset() + 1;\n\t\t\t}\n\t\t\tif (type == TokenTypes.RESERVED_WORD && token.length() == 6 && token.getLexeme().equals(\"static\")) {\n\t\t\t\t// maybe a class init method\n\t\t\t\treturn token.getOffset();\n\t\t\t}\n\t\t} else if (type == TokenTypes.MARKUP_TAG_ATTRIBUTE_VALUE) {\n\t\t\treturn token.getOffset() + 1; // skip quote at start (\")\n\t\t}\n\t\treturn -1;\n\t}\n\n\t/**\n\t * Search node by offset in {@code jCls} code and return its definition position\n\t * (useful for jumps from usage)\n\t */\n\t@Nullable\n\tpublic JumpPosition getDefPosForNodeAtOffset(int offset) {\n\t\tif (offset == -1) {\n\t\t\treturn null;\n\t\t}\n\t\tJavaNode foundNode = getJavaNodeAtOffset(offset);\n\t\tif (foundNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (foundNode == node.getJavaNode()) {\n\t\t\t// current node\n\t\t\treturn new JumpPosition(node);\n\t\t}\n\t\tJNode jNode = convertJavaNode(foundNode);\n\t\treturn new JumpPosition(jNode);\n\t}\n\n\tprivate JNode convertJavaNode(JavaNode javaNode) {\n\t\tJNodeCache nodeCache = getMainWindow().getCacheObject().getNodeCache();\n\t\treturn nodeCache.makeFrom(javaNode);\n\t}\n\n\t@Nullable\n\tpublic JNode getNodeUnderCaret() {\n\t\tint caretPos = getCaretPosition();\n\t\treturn getJNodeAtOffset(adjustOffsetForWordToken(caretPos));\n\t}\n\n\t@Nullable\n\tpublic JNode getEnclosingNodeUnderCaret() {\n\t\tint caretPos = getCaretPosition();\n\t\tint start = adjustOffsetForWordToken(caretPos);\n\t\tif (start == -1) {\n\t\t\tstart = caretPos;\n\t\t}\n\t\treturn getEnclosingJNodeAtOffset(start);\n\t}\n\n\t@Nullable\n\tpublic JNode getNodeUnderMouse() {\n\t\tPoint pos = UiUtils.getMousePosition(this);\n\t\treturn getJNodeAtOffset(adjustOffsetForWordToken(viewToModel2D(pos)));\n\t}\n\n\t@Nullable\n\tpublic JNode getEnclosingNodeUnderMouse() {\n\t\tPoint pos = UiUtils.getMousePosition(this);\n\t\treturn getEnclosingJNodeAtOffset(adjustOffsetForWordToken(viewToModel2D(pos)));\n\t}\n\n\t@Nullable\n\tpublic JNode getEnclosingJNodeAtOffset(int offset) {\n\t\tJavaNode javaNode = getEnclosingJavaNode(offset);\n\t\tif (javaNode != null) {\n\t\t\treturn convertJavaNode(javaNode);\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Nullable\n\tpublic JNode getJNodeAtOffset(int offset) {\n\t\tJavaNode javaNode = getJavaNodeAtOffset(offset);\n\t\tif (javaNode != null) {\n\t\t\treturn convertJavaNode(javaNode);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Search referenced java node by offset in {@code jCls} code\n\t */\n\tpublic JavaNode getJavaNodeAtOffset(int offset) {\n\t\tif (offset == -1) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\treturn getJadxWrapper().getDecompiler().getJavaNodeAtPosition(getCodeInfo(), offset);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Can't get java node by offset: {}\", offset, e);\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic JavaNode getClosestJavaNode(int offset) {\n\t\tif (offset == -1) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\treturn getJadxWrapper().getDecompiler().getClosestJavaNode(getCodeInfo(), offset);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Can't get java node by offset: {}\", offset, e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic JavaNode getEnclosingJavaNode(int offset) {\n\t\tif (offset == -1) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\treturn getJadxWrapper().getDecompiler().getEnclosingNode(getCodeInfo(), offset);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Can't get java node by offset: {}\", offset, e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic @Nullable JavaClass getJavaClassIfAtPos(int pos) {\n\t\ttry {\n\t\t\tICodeInfo codeInfo = getCodeInfo();\n\t\t\tif (!codeInfo.hasMetadata()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tICodeAnnotation ann = codeInfo.getCodeMetadata().getAt(pos);\n\t\t\tif (ann == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tswitch (ann.getAnnType()) {\n\t\t\t\tcase CLASS:\n\t\t\t\t\treturn (JavaClass) getJadxWrapper().getDecompiler().getJavaNodeByCodeAnnotation(codeInfo, ann);\n\t\t\t\tcase METHOD:\n\t\t\t\t\t// use class from constructor call\n\t\t\t\t\tJavaNode node = getJadxWrapper().getDecompiler().getJavaNodeByCodeAnnotation(codeInfo, ann);\n\t\t\t\t\treturn node != null ? node.getDeclaringClass() : null;\n\t\t\t\tdefault:\n\t\t\t\t\treturn null;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Can't get java node by offset: {}\", pos, e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic void refreshClass() {\n\t\trefreshClass(false);\n\t}\n\n\tpublic void refreshClass(boolean alreadyReloaded) {\n\t\tif (node instanceof JClass) {\n\t\t\tJClass cls = node.getRootClass();\n\t\t\ttry {\n\t\t\t\tCaretPositionFix caretFix = new CaretPositionFix(this);\n\t\t\t\tcaretFix.save();\n\n\t\t\t\tif (alreadyReloaded) {\n\t\t\t\t\tcachedCodeInfo = cls.getCodeInfo();\n\t\t\t\t} else {\n\t\t\t\t\t// bad. blocks the UI thread for a potentially expensive decomp\n\t\t\t\t\tcachedCodeInfo = cls.reload(getMainWindow().getCacheObject());\n\t\t\t\t}\n\n\t\t\t\tClassCodeContentPanel codeContentPanel = (ClassCodeContentPanel) this.contentPanel;\n\t\t\t\tcodeContentPanel.getTabbedPane().refresh(cls);\n\t\t\t\tcodeContentPanel.getJavaCodePanel().refresh(caretFix);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to reload class: {}\", cls.getFullName(), e);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Refresh the class in the background, updating the UI once the potential decomp is complete.\n\t * Should be called from the UI thread.\n\t */\n\tpublic void backgroundRefreshClass() {\n\t\tUiUtils.uiThreadGuard();\n\t\tthis.getMainWindow().getBackgroundExecutor().execute(\"Refreshing...\", () -> {\n\t\t\tthis.getNode().getRootClass().reload(this.getMainWindow().getCacheObject());\n\t\t\tUiUtils.uiRunAndWait(() -> {\n\t\t\t\tthis.refreshClass(true);\n\t\t\t});\n\t\t});\n\t}\n\n\tpublic MainWindow getMainWindow() {\n\t\treturn contentPanel.getMainWindow();\n\t}\n\n\tpublic JadxWrapper getJadxWrapper() {\n\t\treturn getMainWindow().getWrapper();\n\t}\n\n\tpublic JadxProject getProject() {\n\t\treturn getMainWindow().getProject();\n\t}\n\n\t@Override\n\tpublic void dispose() {\n\t\tshortcutsController.unbindActionsForComponent(this);\n\n\t\tsuper.dispose();\n\t\tcachedCodeInfo = null;\n\t}\n\n\t@Override\n\tpublic CodePanelSyncer createCodePanelSyncer() {\n\t\treturn new JavaSyncer(this);\n\t}\n\n\t@Override\n\tpublic boolean sync(CodePanelSyncer codePanelSyncer) {\n\t\treturn codePanelSyncer.syncTo(this);\n\t}\n\n\t@Nullable\n\tpublic ICodeMetadata getCodeMetadata() {\n\t\tICodeInfo codeInfo = getCodeInfo();\n\t\tif (!codeInfo.hasMetadata()) {\n\t\t\tLOG.warn(\"No code info metadata for {}\", codeInfo.toString());\n\t\t\treturn null;\n\t\t}\n\t\treturn codeInfo.getCodeMetadata();\n\t}\n\n\t/**\n\t * Returns a mapping of 'decompilation output line number' to 'dex debug line number'\n\t * These are 1-indexed line numbers not the line indices of the CodeArea\n\t *\n\t * @return the line mapping\n\t */\n\tpublic Map<Integer, Integer> getLineMappings() {\n\t\tICodeInfo codeInfo = getCodeInfo();\n\t\tif (!codeInfo.hasMetadata()) {\n\t\t\tLOG.debug(\"No code info metadata for {}\", codeInfo.toString());\n\t\t\treturn Map.of();\n\t\t}\n\t\tMap<Integer, Integer> lineMapping = codeInfo.getCodeMetadata().getLineMapping();\n\t\tif (lineMapping.isEmpty()) {\n\t\t\tLOG.debug(\"Line mappings are empty for {}\", codeInfo.toString());\n\t\t\treturn Map.of();\n\t\t}\n\t\treturn lineMapping;\n\t}\n\n\t/**\n\t * Returns the same as {@link #getLineMappings()} but only if each value (dex debug line number)\n\t * appears only once.\n\t * If a value appears more than once then it suggests that methods might share dex debug line\n\t * numbers.\n\t * If this is the case then the line mapping cannot be used for code sync correlation.\n\t *\n\t * @return the line mapping\n\t */\n\tpublic Map<Integer, Integer> getFunctionUniqueLineMappings() {\n\t\tfinal var lineMappings = getLineMappings();\n\t\tfinal boolean isAnyRepeated =\n\t\t\t\tlineMappings.values().stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())).values().stream()\n\t\t\t\t\t\t.filter(v -> v > 1).findAny().isPresent();\n\t\tif (isAnyRepeated) {\n\t\t\tLOG.debug(\"Dex debug line mappings are not unique\");\n\t\t\treturn Map.of();\n\t\t}\n\t\treturn lineMappings;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeContentPanel.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.awt.BorderLayout;\nimport java.awt.Component;\nimport java.awt.Point;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.panel.IViewStateSupport;\nimport jadx.gui.ui.tab.TabbedPane;\n\npublic final class CodeContentPanel extends AbstractCodeContentPanel implements IViewStateSupport {\n\tprivate static final long serialVersionUID = 5310536092010045565L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CodeContentPanel.class);\n\n\tprivate final CodePanel codePanel;\n\n\tpublic CodeContentPanel(TabbedPane panel, JNode jnode) {\n\t\tsuper(panel, jnode);\n\t\tsetLayout(new BorderLayout());\n\t\tcodePanel = new CodePanel(new CodeArea(this, jnode));\n\t\tadd(codePanel, BorderLayout.CENTER);\n\t\tcodePanel.load();\n\t}\n\n\t@Override\n\tpublic void loadSettings() {\n\t\tcodePanel.loadSettings();\n\t\tupdateUI();\n\t}\n\n\tSearchBar getSearchBar() {\n\t\treturn codePanel.getSearchBar();\n\t}\n\n\t@Override\n\tpublic AbstractCodeArea getCodeArea() {\n\t\treturn codePanel.getCodeArea();\n\t}\n\n\t@Override\n\tpublic Component getChildrenComponent() {\n\t\treturn getCodeArea();\n\t}\n\n\t@Override\n\tpublic void saveEditorViewState(EditorViewState viewState) {\n\t\tint caretPos = codePanel.getCodeArea().getCaretPosition();\n\t\tPoint viewPoint = codePanel.getCodeScrollPane().getViewport().getViewPosition();\n\t\tviewState.setCaretPos(caretPos);\n\t\tviewState.setViewPoint(viewPoint);\n\t}\n\n\t@Override\n\tpublic void restoreEditorViewState(EditorViewState viewState) {\n\t\ttry {\n\t\t\tcodePanel.getCodeScrollPane().getViewport().setViewPosition(viewState.getViewPoint());\n\t\t\tcodePanel.getCodeArea().setCaretPosition(viewState.getCaretPos());\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to restore view state\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void dispose() {\n\t\tcodePanel.dispose();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeLinkGenerator.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.util.Objects;\n\nimport javax.swing.event.HyperlinkEvent;\n\nimport org.fife.ui.rsyntaxtextarea.LinkGenerator;\nimport org.fife.ui.rsyntaxtextarea.LinkGeneratorResult;\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JavaNode;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.utils.JumpPosition;\n\npublic class CodeLinkGenerator implements LinkGenerator {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CodeLinkGenerator.class);\n\n\tprivate final CodeArea codeArea;\n\tprivate final JNode jNode;\n\n\tpublic CodeLinkGenerator(CodeArea codeArea) {\n\t\tthis.codeArea = codeArea;\n\t\tthis.jNode = codeArea.getNode();\n\t}\n\n\tpublic JavaNode getNodeAtOffset(int offset) {\n\t\ttry {\n\t\t\tif (!codeArea.getCodeInfo().hasMetadata()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tint sourceOffset = codeArea.adjustOffsetForWordToken(offset);\n\t\t\tif (sourceOffset == -1) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn codeArea.getJavaNodeAtOffset(offset);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"getNodeAtOffset error\", e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, int offset) {\n\t\ttry {\n\t\t\tif (!codeArea.getCodeInfo().hasMetadata()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tint sourceOffset = codeArea.adjustOffsetForWordToken(offset);\n\t\t\tif (sourceOffset == -1) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tJumpPosition defPos = getJumpBySourceOffset(sourceOffset);\n\t\t\tif (defPos == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn new LinkGeneratorResult() {\n\t\t\t\t@Override\n\t\t\t\tpublic HyperlinkEvent execute() {\n\t\t\t\t\treturn new HyperlinkEvent(defPos, HyperlinkEvent.EventType.ACTIVATED, null,\n\t\t\t\t\t\t\tdefPos.getNode().makeLongString());\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic int getSourceOffset() {\n\t\t\t\t\treturn sourceOffset;\n\t\t\t\t}\n\t\t\t};\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"isLinkAtOffset error\", e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate JumpPosition getJumpBySourceOffset(int sourceOffset) {\n\t\tfinal JumpPosition defPos = codeArea.getDefPosForNodeAtOffset(sourceOffset);\n\t\tif (defPos == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (Objects.equals(defPos.getNode().getRootClass(), jNode)\n\t\t\t\t&& defPos.getPos() == sourceOffset) {\n\t\t\t// ignore self jump\n\t\t\treturn null;\n\t\t}\n\t\treturn defPos;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/CodePanel.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.awt.BorderLayout;\nimport java.awt.Component;\nimport java.awt.Point;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.KeyEvent;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.swing.AbstractAction;\nimport javax.swing.Action;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPanel;\nimport javax.swing.JPopupMenu;\nimport javax.swing.JPopupMenu.Separator;\nimport javax.swing.JScrollPane;\nimport javax.swing.JViewport;\nimport javax.swing.KeyStroke;\nimport javax.swing.SwingUtilities;\nimport javax.swing.border.EmptyBorder;\nimport javax.swing.event.PopupMenuEvent;\n\nimport org.fife.ui.rtextarea.LineNumberFormatter;\nimport org.fife.ui.rtextarea.LineNumberList;\nimport org.fife.ui.rtextarea.RTextScrollPane;\n\nimport jadx.api.ICodeInfo;\nimport jadx.core.utils.StringUtils;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.settings.LineNumbersMode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.dialog.SearchDialog;\nimport jadx.gui.utils.CaretPositionFix;\nimport jadx.gui.utils.DefaultPopupMenuListener;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.ui.MousePressedHandler;\n\n/**\n * A panel combining a {@link SearchBar} and a scollable {@link CodeArea}\n */\npublic class CodePanel extends JPanel {\n\tprivate static final long serialVersionUID = 1117721869391885865L;\n\n\tprivate final SearchBar searchBar;\n\tprivate final AbstractCodeArea codeArea;\n\tprivate final RTextScrollPane codeScrollPane;\n\n\tprivate boolean useSourceLines;\n\n\tpublic CodePanel(AbstractCodeArea codeArea) {\n\t\tthis.codeArea = codeArea;\n\t\tthis.searchBar = new SearchBar(codeArea);\n\t\tthis.codeScrollPane = new RTextScrollPane(codeArea);\n\n\t\tsetLayout(new BorderLayout());\n\t\tsetBorder(new EmptyBorder(0, 0, 0, 0));\n\t\tadd(searchBar, BorderLayout.NORTH);\n\t\tadd(codeScrollPane, BorderLayout.CENTER);\n\n\t\tinitLinesModeSwitch();\n\n\t\tKeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_F, UiUtils.ctrlButton());\n\t\tUiUtils.addKeyBinding(codeArea, key, \"SearchAction\", new AbstractAction() {\n\t\t\tprivate static final long serialVersionUID = 71338030532869694L;\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tsearchBar.showAndFocus();\n\t\t\t}\n\t\t});\n\t\tJMenuItem searchItem = new JMenuItem();\n\t\tJMenuItem globalSearchItem = new JMenuItem();\n\t\tAbstractAction searchAction = new AbstractAction(NLS.str(\"popup.search\", \"\")) {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tsearchBar.toggle();\n\t\t\t}\n\t\t};\n\t\tAbstractAction globalSearchAction = new AbstractAction(NLS.str(\"popup.search_global\", \"\")) {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tMainWindow mainWindow = codeArea.getContentPanel().getMainWindow();\n\t\t\t\tSearchDialog.searchText(mainWindow, codeArea.getSelectedText());\n\t\t\t}\n\t\t};\n\t\tsearchItem.setAction(searchAction);\n\t\tglobalSearchItem.setAction(globalSearchAction);\n\t\tSeparator separator = new Separator();\n\t\tJPopupMenu popupMenu = codeArea.getPopupMenu();\n\t\tpopupMenu.addPopupMenuListener(new DefaultPopupMenuListener() {\n\t\t\t@Override\n\t\t\tpublic void popupMenuWillBecomeVisible(PopupMenuEvent e) {\n\t\t\t\tString preferText = codeArea.getSelectedText();\n\t\t\t\tif (!StringUtils.isEmpty(preferText)) {\n\t\t\t\t\tif (preferText.length() >= 23) {\n\t\t\t\t\t\tpreferText = preferText.substring(0, 20) + \" ...\";\n\t\t\t\t\t}\n\t\t\t\t\tsearchAction.putValue(Action.NAME, NLS.str(\"popup.search\", preferText));\n\t\t\t\t\tglobalSearchAction.putValue(Action.NAME, NLS.str(\"popup.search_global\", preferText));\n\t\t\t\t\tpopupMenu.add(separator);\n\t\t\t\t\tpopupMenu.add(globalSearchItem);\n\t\t\t\t\tpopupMenu.add(searchItem);\n\t\t\t\t} else {\n\t\t\t\t\tpopupMenu.remove(separator);\n\t\t\t\t\tpopupMenu.remove(globalSearchItem);\n\t\t\t\t\tpopupMenu.remove(searchItem);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic void loadSettings() {\n\t\tcodeArea.loadSettings();\n\t\tinitLineNumbers();\n\t}\n\n\tpublic void load() {\n\t\tcodeArea.load();\n\t\tinitLineNumbers();\n\t}\n\n\tprivate synchronized void initLineNumbers() {\n\t\tcodeScrollPane.getGutter().setLineNumberFont(getSettings().getCodeFont());\n\t\tLineNumbersMode mode = getLineNumbersMode();\n\t\tif (mode == LineNumbersMode.DISABLE) {\n\t\t\tcodeScrollPane.setLineNumbersEnabled(false);\n\t\t\treturn;\n\t\t}\n\t\tuseSourceLines = mode == LineNumbersMode.DEBUG;\n\t\tapplyLineFormatter();\n\t\tcodeScrollPane.setLineNumbersEnabled(true);\n\t}\n\n\tprivate static final LineNumberFormatter SIMPLE_LINE_FORMATTER = new LineNumberFormatter() {\n\t\t@Override\n\t\tpublic String format(int lineNumber) {\n\t\t\treturn Integer.toString(lineNumber);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getMaxLength(int maxLineNumber) {\n\t\t\treturn SourceLineFormatter.getNumberLength(maxLineNumber);\n\t\t}\n\t};\n\n\tprivate synchronized void applyLineFormatter() {\n\t\tLineNumberFormatter linesFormatter = useSourceLines\n\t\t\t\t? new SourceLineFormatter(codeArea.getCodeInfo())\n\t\t\t\t: SIMPLE_LINE_FORMATTER;\n\t\tcodeScrollPane.getGutter().setLineNumberFormatter(linesFormatter);\n\t}\n\n\tprivate LineNumbersMode getLineNumbersMode() {\n\t\tLineNumbersMode mode = getSettings().getLineNumbersMode();\n\t\tboolean canShowDebugLines = canShowDebugLines();\n\t\tif (mode == LineNumbersMode.AUTO) {\n\t\t\tmode = canShowDebugLines ? LineNumbersMode.DEBUG : LineNumbersMode.NORMAL;\n\t\t} else if (mode == LineNumbersMode.DEBUG && !canShowDebugLines) {\n\t\t\t// nothing to show => hide lines view\n\t\t\tmode = LineNumbersMode.DISABLE;\n\t\t}\n\t\treturn mode;\n\t}\n\n\tprivate boolean canShowDebugLines() {\n\t\tif (codeArea instanceof SmaliArea) {\n\t\t\treturn false;\n\t\t}\n\t\tICodeInfo codeInfo = codeArea.getCodeInfo();\n\t\tif (!codeInfo.hasMetadata()) {\n\t\t\treturn false;\n\t\t}\n\t\tMap<Integer, Integer> lineMapping = codeInfo.getCodeMetadata().getLineMapping();\n\t\tif (lineMapping.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tSet<Integer> uniqueDebugLines = new HashSet<>(lineMapping.values());\n\t\treturn uniqueDebugLines.size() > 3;\n\t}\n\n\tprivate void initLinesModeSwitch() {\n\t\tMousePressedHandler lineModeSwitch = new MousePressedHandler(ev -> {\n\t\t\tuseSourceLines = !useSourceLines;\n\t\t\tapplyLineFormatter();\n\t\t});\n\t\tfor (Component gutterComp : codeScrollPane.getGutter().getComponents()) {\n\t\t\tif (gutterComp instanceof LineNumberList) {\n\t\t\t\tgutterComp.addMouseListener(lineModeSwitch);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic SearchBar getSearchBar() {\n\t\treturn searchBar;\n\t}\n\n\tpublic AbstractCodeArea getCodeArea() {\n\t\treturn codeArea;\n\t}\n\n\tpublic JScrollPane getCodeScrollPane() {\n\t\treturn codeScrollPane;\n\t}\n\n\tpublic void refresh(CaretPositionFix caretFix) {\n\t\tJViewport viewport = getCodeScrollPane().getViewport();\n\t\tPoint viewPosition = viewport.getViewPosition();\n\t\tcodeArea.refresh();\n\t\tinitLineNumbers();\n\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\tviewport.setViewPosition(viewPosition);\n\t\t\tcaretFix.restore();\n\t\t});\n\t}\n\n\tprivate JadxSettings getSettings() {\n\t\treturn this.codeArea.getContentPanel().getTabbedPane()\n\t\t\t\t.getMainWindow().getSettings();\n\t}\n\n\tpublic void dispose() {\n\t\tcodeArea.dispose();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/CommentAction.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.awt.event.ActionEvent;\nimport java.util.Objects;\n\nimport javax.swing.event.PopupMenuEvent;\n\nimport org.fife.ui.rsyntaxtextarea.Token;\nimport org.fife.ui.rsyntaxtextarea.TokenTypes;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.JavaMethod;\nimport jadx.api.JavaNode;\nimport jadx.api.data.ICodeComment;\nimport jadx.api.data.impl.JadxCodeComment;\nimport jadx.api.data.impl.JadxCodeData;\nimport jadx.api.data.impl.JadxCodeRef;\nimport jadx.api.data.impl.JadxNodeRef;\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeAnnotation.AnnType;\nimport jadx.api.metadata.ICodeMetadata;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.metadata.annotations.InsnCodeOffset;\nimport jadx.api.metadata.annotations.NodeDeclareRef;\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.settings.JadxProject;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.ui.action.ActionModel;\nimport jadx.gui.ui.action.CodeAreaAction;\nimport jadx.gui.ui.action.JadxGuiAction;\nimport jadx.gui.ui.dialog.CommentDialog;\nimport jadx.gui.utils.DefaultPopupMenuListener;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic class CommentAction extends CodeAreaAction implements DefaultPopupMenuListener {\n\tprivate static final long serialVersionUID = 4753838562204629112L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CommentAction.class);\n\n\tprotected final boolean enabled;\n\tprivate @Nullable ICodeComment actionComment;\n\tprivate boolean updateComment;\n\n\tpublic CommentAction(CodeArea codeArea) {\n\t\tsuper(ActionModel.CODE_COMMENT, codeArea);\n\t\tthis.enabled = codeArea.getNode() instanceof JClass;\n\t}\n\n\tpublic CommentAction(ActionModel actionModel, CodeArea codeArea) {\n\t\tsuper(actionModel, codeArea);\n\t\tthis.enabled = codeArea.getNode() instanceof JClass;\n\t}\n\n\t@Override\n\tpublic void popupMenuWillBecomeVisible(PopupMenuEvent e) {\n\t\tif (enabled && updateCommentAction(UiUtils.getOffsetAtMousePosition(codeArea))) {\n\t\t\tsetNameAndDesc(updateComment ? NLS.str(\"popup.update_comment\") : NLS.str(\"popup.add_comment\"));\n\t\t\tsetEnabled(true);\n\t\t} else {\n\t\t\tsetEnabled(false);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void popupMenuCanceled(PopupMenuEvent e) {\n\t\tactionComment = null;\n\t\tsetEnabled(false);\n\t}\n\n\tprivate boolean updateCommentAction(int pos) {\n\t\tICodeComment codeComment = getCommentRef(pos);\n\t\tif (codeComment == null) {\n\t\t\tactionComment = null;\n\t\t\treturn false;\n\t\t}\n\t\tICodeComment exitsComment = searchForExistComment(codeComment);\n\t\tif (exitsComment != null) {\n\t\t\tactionComment = exitsComment;\n\t\t\tupdateComment = true;\n\t\t} else {\n\t\t\tactionComment = codeComment;\n\t\t\tupdateComment = false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void actionPerformed(ActionEvent e) {\n\t\tif (!enabled) {\n\t\t\treturn;\n\t\t}\n\t\tif (JadxGuiAction.isSource(e)) {\n\t\t\tupdateCommentAction(codeArea.getCaretPosition());\n\t\t}\n\t\tif (actionComment == null) {\n\t\t\tUiUtils.showMessageBox(codeArea.getMainWindow(), NLS.str(\"msg.cant_add_comment\"));\n\t\t\treturn;\n\t\t}\n\t\tCommentDialog.show(codeArea, actionComment, updateComment);\n\t}\n\n\tprotected @Nullable ICodeComment searchForExistComment(ICodeComment blankComment) {\n\t\ttry {\n\t\t\tJadxProject project = codeArea.getProject();\n\t\t\tJadxCodeData codeData = project.getCodeData();\n\t\t\tif (codeData == null || codeData.getComments().isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tfor (ICodeComment comment : codeData.getComments()) {\n\t\t\t\tif (Objects.equals(comment.getNodeRef(), blankComment.getNodeRef())\n\t\t\t\t\t\t&& Objects.equals(comment.getCodeRef(), blankComment.getCodeRef())) {\n\t\t\t\t\treturn comment;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Error searching for exists comment\", e);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Check if possible insert comment at current line.\n\t *\n\t * @return blank code comment object (comment string empty)\n\t */\n\t@Nullable\n\tprotected ICodeComment getCommentRef(int pos) {\n\t\tif (pos == -1) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\tJadxWrapper wrapper = codeArea.getJadxWrapper();\n\t\t\tICodeInfo codeInfo = codeArea.getCodeInfo();\n\t\t\tICodeMetadata metadata = codeInfo.getCodeMetadata();\n\t\t\tint lineStartPos = codeArea.getLineStartFor(pos);\n\n\t\t\t// add method line comment by instruction offset\n\t\t\tICodeAnnotation offsetAnn = metadata.searchUp(pos, lineStartPos, AnnType.OFFSET);\n\t\t\tif (offsetAnn instanceof InsnCodeOffset) {\n\t\t\t\tJavaNode node = wrapper.getJavaNodeByRef(metadata.getNodeAt(pos));\n\t\t\t\tif (node instanceof JavaMethod) {\n\t\t\t\t\tint rawOffset = ((InsnCodeOffset) offsetAnn).getOffset();\n\t\t\t\t\tJadxNodeRef nodeRef = JadxNodeRef.forMth((JavaMethod) node);\n\t\t\t\t\treturn new JadxCodeComment(nodeRef, JadxCodeRef.forInsn(rawOffset), \"\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// check for definition at this line\n\t\t\tICodeNodeRef nodeDef = metadata.searchUp(pos, (off, ann) -> {\n\t\t\t\tif (lineStartPos <= off && ann.getAnnType() == AnnType.DECLARATION) {\n\t\t\t\t\tICodeNodeRef defRef = ((NodeDeclareRef) ann).getNode();\n\t\t\t\t\tif (defRef.getAnnType() != AnnType.VAR) {\n\t\t\t\t\t\treturn defRef;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t});\n\t\t\tif (nodeDef != null) {\n\t\t\t\tJadxNodeRef nodeRef = JadxNodeRef.forJavaNode(wrapper.getJavaNodeByRef(nodeDef));\n\t\t\t\treturn new JadxCodeComment(nodeRef, \"\");\n\t\t\t}\n\n\t\t\t// check if at comment above node definition\n\t\t\tif (isCommentLine(pos)) {\n\t\t\t\tICodeNodeRef nodeRef = metadata.searchDown(pos, (off, ann) -> {\n\t\t\t\t\tif (off > pos && ann.getAnnType() == AnnType.DECLARATION) {\n\t\t\t\t\t\treturn ((NodeDeclareRef) ann).getNode();\n\t\t\t\t\t}\n\t\t\t\t\treturn null;\n\t\t\t\t});\n\t\t\t\tif (nodeRef != null) {\n\t\t\t\t\tJavaNode defNode = wrapper.getJavaNodeByRef(nodeRef);\n\t\t\t\t\treturn new JadxCodeComment(JadxNodeRef.forJavaNode(defNode), \"\");\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to add comment at: {}\", pos, e);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Check if all tokens are 'comment' in line at 'pos'\n\t */\n\tprotected boolean isCommentLine(int pos) {\n\t\ttry {\n\t\t\tint line = codeArea.getLineOfOffset(pos);\n\t\t\tToken lineTokens = codeArea.getTokenListForLine(line);\n\t\t\tboolean commentFound = false;\n\t\t\tfor (Token t = lineTokens; t != null; t = t.getNextToken()) {\n\t\t\t\tif (t.isComment()) {\n\t\t\t\t\tcommentFound = true;\n\t\t\t\t} else {\n\t\t\t\t\tswitch (t.getType()) {\n\t\t\t\t\t\tcase TokenTypes.WHITESPACE:\n\t\t\t\t\t\tcase TokenTypes.NULL:\n\t\t\t\t\t\t\t// allowed tokens\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn commentFound;\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to check for comment line\", e);\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/ConvertNumberAction.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.awt.event.ActionEvent;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport javax.swing.event.PopupMenuEvent;\nimport javax.swing.text.BadLocationException;\n\nimport org.fife.ui.rsyntaxtextarea.Token;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.data.CommentStyle;\nimport jadx.api.data.ICodeComment;\nimport jadx.api.data.impl.JadxCodeComment;\nimport jadx.api.data.impl.JadxCodeData;\nimport jadx.gui.settings.JadxProject;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.ui.action.ActionModel;\nimport jadx.gui.utils.NLS;\n\npublic class ConvertNumberAction extends CommentAction {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ConvertNumberAction.class);\n\n\tprivate static final String DEFAULT_TEXT = \"\";\n\tprivate final String tooltipText;\n\n\tpublic ConvertNumberAction(CodeArea codeArea) {\n\n\t\tsuper(ActionModel.CONVERT_NUMBER, codeArea);\n\n\t\ttooltipText = NLS.str(\"popup.convert_number\");\n\n\t\t// default menu item to disabled\n\t\tsetEnabled(false);\n\t\tsetNameAndDesc(DEFAULT_TEXT);\n\t}\n\n\t@Override\n\tpublic void popupMenuWillBecomeVisible(PopupMenuEvent e) {\n\n\t\tif (codeArea.getNode() instanceof JClass) {\n\t\t\t// try parse number from word under caret\n\t\t\t// and set text of popup menu dynamically\n\t\t\tString word = getWordByPosition(codeArea.getCaretPosition());\n\t\t\tList<String> conversions = getConversionsFromWord(word);\n\t\t\tif (conversions != null && !conversions.isEmpty()) {\n\t\t\t\tString joined = String.join(\" | \", conversions);\n\t\t\t\tsetName(joined);\n\t\t\t\tsetShortDescription(tooltipText);\n\t\t\t\tsetEnabled(true);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void popupMenuCanceled(PopupMenuEvent e) {\n\t\t// reset menu to disabled on cancel\n\t\tsetEnabled(false);\n\t\tsetNameAndDesc(DEFAULT_TEXT);\n\t}\n\n\t@Override\n\tpublic void actionPerformed(ActionEvent e) {\n\n\t\tif (!super.enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tString newText = e.getActionCommand();\n\t\tif (newText == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tICodeComment comment = getCommentRef(codeArea.getCaretPosition());\n\t\tif (comment == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tICodeComment newComment = new JadxCodeComment(comment.getNodeRef(), comment.getCodeRef(), newText, CommentStyle.LINE);\n\t\tupdateCommentsData(codeArea, list -> list.add(newComment));\n\n\t}\n\n\t/**\n\t * Adds comments to project file and code area\n\t */\n\tprivate static void updateCommentsData(CodeArea codeArea, Consumer<List<ICodeComment>> updater) {\n\t\ttry {\n\t\t\tJadxProject project = codeArea.getProject();\n\t\t\tJadxCodeData codeData = project.getCodeData();\n\t\t\tif (codeData == null) {\n\t\t\t\tcodeData = new JadxCodeData();\n\t\t\t}\n\t\t\tList<ICodeComment> list = new ArrayList<>(codeData.getComments());\n\t\t\tupdater.accept(list);\n\t\t\tCollections.sort(list);\n\t\t\tcodeData.setComments(list);\n\t\t\tproject.setCodeData(codeData);\n\t\t\tcodeArea.getMainWindow().getWrapper().reloadCodeData();\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Comment action failed\", e);\n\t\t}\n\t\ttry {\n\t\t\t// refresh code\n\t\t\tcodeArea.backgroundRefreshClass();\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to reload code\", e);\n\t\t}\n\t}\n\n\t/**\n\t * similar to AbstractCodeArea::getWordByPosition\n\t * but includes \"-\" for negative numbers\n\t */\n\tpublic @Nullable String getWordByPosition(int offset) {\n\t\tToken token = codeArea.getWordTokenAtOffset(offset);\n\t\tif (token == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tString str = token.getLexeme();\n\n\t\ttry {\n\t\t\tString prev = codeArea.getText(token.getOffset() - 1, 1);\n\t\t\tif (prev.equals(\"-\")) {\n\t\t\t\tstr = \"-\" + str;\n\t\t\t}\n\n\t\t} catch (BadLocationException e) {\n\t\t\t// ignore\n\t\t}\n\n\t\tint len = str.length();\n\t\tif (len > 2 && str.startsWith(\"\\\"\") && str.endsWith(\"\\\"\")) {\n\t\t\treturn str.substring(1, len - 1);\n\t\t}\n\t\treturn str;\n\t}\n\n\t/**\n\t * Tries to parse a number from input string,\n\t * returns list of strings of the number converted to different formats.\n\t * e.g. if input number is in hex, converts to decimal and binary.\n\t */\n\tstatic @Nullable List<String> getConversionsFromWord(String word) {\n\n\t\tList<String> conversions = new ArrayList<>();\n\n\t\tif (word == null || word.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tint i32 = 0;\n\t\tlong i64 = 0;\n\t\tint radix = 10;\n\t\tboolean parsedLong = false;\n\n\t\t// handle hex\n\t\tif (word.startsWith(\"0x\")) {\n\t\t\tword = word.substring(2);\n\t\t\tradix = 16;\n\t\t}\n\n\t\t// handle long int syntax like \"12345L\"\n\t\tif (word.endsWith(\"L\")) {\n\t\t\tword = word.substring(0, word.length() - 1);\n\t\t\tparsedLong = true;\n\t\t}\n\n\t\t// try parse int\n\t\ttry {\n\t\t\ti32 = Integer.parseInt(word, radix);\n\t\t\ti64 = i32;\n\n\t\t} catch (NumberFormatException e) {\n\n\t\t\t// try parse long\n\t\t\ttry {\n\t\t\t\ti64 = Long.parseLong(word, radix);\n\t\t\t\tparsedLong = true;\n\n\t\t\t} catch (NumberFormatException ignore) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t// if we parsed decimal, output hex and vice versa\n\t\tif (radix == 10) {\n\t\t\tif (parsedLong) {\n\t\t\t\tconversions.add(String.format(\"0x%x\", i64));\n\t\t\t} else {\n\t\t\t\tconversions.add(String.format(\"0x%x\", i32));\n\t\t\t}\n\n\t\t} else if (radix == 16) {\n\t\t\tconversions.add(String.format(\"%d\", i32));\n\t\t}\n\n\t\t// pad binary in 8-bit groups\n\t\t// int leadingZeros = parsed_long ? : Integer.numberOfLeadingZeros(i32);\n\t\tint padBits = (int) Math.ceil((64 - Long.numberOfLeadingZeros(i64)) / 8.0) * 8;\n\t\tif (padBits < 8) {\n\t\t\tpadBits = 8;\n\t\t}\n\t\tif (!parsedLong && padBits > 32) {\n\t\t\tpadBits = 32;\n\t\t}\n\n\t\t// format padded binary\n\t\tString binaryString = parsedLong ? Long.toBinaryString(i64) : Integer.toBinaryString(i32);\n\t\tString fmt = String.format(\"0b%%%ds\", padBits);\n\t\tconversions.add(String.format(fmt, binaryString).replace(' ', '0'));\n\n\t\t// format printable ascii chars\n\t\tif (i32 >= ' ' && i32 <= '~') {\n\t\t\tconversions.add(String.format(\"'%c'\", (int) i32));\n\t\t}\n\n\t\treturn conversions; // no match\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/EditorViewState.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.awt.Point;\n\nimport jadx.gui.treemodel.JNode;\n\npublic class EditorViewState {\n\tpublic static final Point ZERO = new Point(0, 0);\n\n\tprivate final JNode node;\n\tprivate int caretPos;\n\tprivate Point viewPoint;\n\tprivate String subPath;\n\n\tprivate boolean active;\n\n\tprivate boolean pinned;\n\tprivate boolean bookmarked;\n\tprivate boolean hidden;\n\tprivate boolean previewTab;\n\n\tpublic EditorViewState(JNode node) {\n\t\tthis(node, \"\", 0, EditorViewState.ZERO);\n\t}\n\n\tpublic EditorViewState(JNode node, String subPath, int caretPos, Point viewPoint) {\n\t\tthis.node = node;\n\t\tthis.subPath = subPath;\n\t\tthis.caretPos = caretPos;\n\t\tthis.viewPoint = viewPoint;\n\t}\n\n\tpublic JNode getNode() {\n\t\treturn node;\n\t}\n\n\tpublic int getCaretPos() {\n\t\treturn caretPos;\n\t}\n\n\tpublic void setCaretPos(int caretPos) {\n\t\tthis.caretPos = caretPos;\n\t}\n\n\tpublic Point getViewPoint() {\n\t\treturn viewPoint;\n\t}\n\n\tpublic void setViewPoint(Point viewPoint) {\n\t\tthis.viewPoint = viewPoint;\n\t}\n\n\tpublic String getSubPath() {\n\t\treturn subPath;\n\t}\n\n\tpublic void setSubPath(String subPath) {\n\t\tthis.subPath = subPath;\n\t}\n\n\tpublic boolean isActive() {\n\t\treturn active;\n\t}\n\n\tpublic void setActive(boolean active) {\n\t\tthis.active = active;\n\t}\n\n\tpublic boolean isPinned() {\n\t\treturn pinned;\n\t}\n\n\tpublic void setPinned(boolean pinned) {\n\t\tthis.pinned = pinned;\n\t}\n\n\tpublic boolean isBookmarked() {\n\t\treturn bookmarked;\n\t}\n\n\tpublic void setBookmarked(boolean bookmarked) {\n\t\tthis.bookmarked = bookmarked;\n\t}\n\n\tpublic boolean isHidden() {\n\t\treturn hidden;\n\t}\n\n\tpublic boolean isPreviewTab() {\n\t\treturn previewTab;\n\t}\n\n\tpublic void setPreviewTab(boolean previewTab) {\n\t\tthis.previewTab = previewTab;\n\t}\n\n\tpublic void setHidden(boolean hidden) {\n\t\tthis.hidden = hidden;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"EditorViewState{node=\" + node\n\t\t\t\t+ \", caretPos=\" + caretPos\n\t\t\t\t+ \", viewPoint=\" + viewPoint\n\t\t\t\t+ \", subPath='\" + subPath + '\\''\n\t\t\t\t+ \", active=\" + active\n\t\t\t\t+ '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodePopupBuilder.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport javax.swing.JMenu;\nimport javax.swing.JPopupMenu;\nimport javax.swing.event.PopupMenuListener;\n\nimport jadx.gui.ui.action.JNodeAction;\nimport jadx.gui.ui.action.JadxGuiAction;\nimport jadx.gui.utils.shortcut.ShortcutsController;\n\npublic class JNodePopupBuilder {\n\tprivate final JPopupMenu menu;\n\tprivate final JNodePopupListener popupListener;\n\tprivate final ShortcutsController shortcutsController;\n\n\tpublic JNodePopupBuilder(CodeArea codeArea, JPopupMenu popupMenu, ShortcutsController shortcutsController) {\n\t\tthis.shortcutsController = shortcutsController;\n\t\tmenu = popupMenu;\n\t\tpopupListener = new JNodePopupListener(codeArea);\n\t\tpopupMenu.addPopupMenuListener(popupListener);\n\t}\n\n\tpublic void addSeparator() {\n\t\tmenu.addSeparator();\n\t}\n\n\tpublic void add(JNodeAction nodeAction) {\n\t\t// We set the shortcut immediately for two reasons\n\t\t// - there might be multiple instances of this action with\n\t\t// same ActionModel across different codeAreas, while\n\t\t// ShortcutController only supports one instance\n\t\t// - This action will be recreated when shortcuts are changed,\n\t\t// so no need to bind it\n\t\tif (nodeAction.getActionModel() != null) {\n\t\t\tshortcutsController.bindImmediate(nodeAction);\n\t\t}\n\t\tmenu.add(nodeAction);\n\t\tpopupListener.addActions(nodeAction);\n\t}\n\n\tpublic void addSubmenu(JNodeAction[] nodeActions, String name) {\n\t\tJMenu submenu = new JMenu(name);\n\n\t\tfor (JNodeAction nodeAction : nodeActions) {\n\t\t\tif (nodeAction.getActionModel() != null) {\n\t\t\t\tshortcutsController.bindImmediate(nodeAction);\n\t\t\t}\n\n\t\t\tsubmenu.add(nodeAction);\n\t\t\tpopupListener.addActions(nodeAction);\n\t\t}\n\n\t\tmenu.add(submenu);\n\t}\n\n\tpublic void add(JadxGuiAction action) {\n\t\tif (action.getActionModel() != null) {\n\t\t\tshortcutsController.bindImmediate(action);\n\t\t}\n\t\tmenu.add(action);\n\t\tif (action instanceof PopupMenuListener) {\n\t\t\tmenu.addPopupMenuListener((PopupMenuListener) action);\n\t\t}\n\t}\n\n\tpublic JPopupMenu getMenu() {\n\t\treturn menu;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodePopupListener.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.swing.event.PopupMenuEvent;\nimport javax.swing.event.PopupMenuListener;\n\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.action.JNodeAction;\n\npublic final class JNodePopupListener implements PopupMenuListener {\n\tprivate final CodeArea codeArea;\n\tprivate final List<JNodeAction> actions = new ArrayList<>();\n\n\tpublic JNodePopupListener(CodeArea codeArea) {\n\t\tthis.codeArea = codeArea;\n\t}\n\n\tpublic void addActions(JNodeAction action) {\n\t\tactions.add(action);\n\t}\n\n\tprivate void updateNode(JNode node) {\n\t\tfor (JNodeAction action : actions) {\n\t\t\taction.changeNode(node);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void popupMenuWillBecomeVisible(PopupMenuEvent e) {\n\t\tupdateNode(codeArea.getNodeUnderMouse());\n\t}\n\n\t@Override\n\tpublic void popupMenuWillBecomeInvisible(PopupMenuEvent e) {\n\t\t// this event can be called just before running action, so can't reset node here\n\t}\n\n\t@Override\n\tpublic void popupMenuCanceled(PopupMenuEvent e) {\n\t\tupdateNode(null);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/JadxTokenMaker.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.util.Set;\n\nimport javax.swing.text.Segment;\n\nimport org.fife.ui.rsyntaxtextarea.Token;\nimport org.fife.ui.rsyntaxtextarea.TokenImpl;\nimport org.fife.ui.rsyntaxtextarea.TokenTypes;\nimport org.fife.ui.rsyntaxtextarea.modes.JavaTokenMaker;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JavaClass;\nimport jadx.api.JavaNode;\n\nimport static jadx.api.plugins.utils.Utils.constSet;\n\npublic final class JadxTokenMaker extends JavaTokenMaker {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxTokenMaker.class);\n\n\tprivate final CodeArea codeArea;\n\n\tpublic JadxTokenMaker(CodeArea codeArea) {\n\t\tthis.codeArea = codeArea;\n\t}\n\n\t@Override\n\tpublic Token getTokenList(Segment text, int initialTokenType, int startOffset) {\n\t\tif (codeArea.isDisposed()) {\n\t\t\treturn new TokenImpl();\n\t\t}\n\t\ttry {\n\t\t\tToken tokens = super.getTokenList(text, initialTokenType, startOffset);\n\t\t\tif (tokens != null && tokens.getType() != TokenTypes.NULL) {\n\t\t\t\tprocessTokens(tokens);\n\t\t\t}\n\t\t\treturn tokens;\n\t\t} catch (Throwable e) { // JavaTokenMaker throws 'java.lang.Error' if failed to parse input string\n\t\t\tLOG.error(\"Process tokens failed for text: {}\", text, e);\n\t\t\treturn new TokenImpl();\n\t\t}\n\t}\n\n\tprivate void processTokens(Token tokens) {\n\t\tToken prev = null;\n\t\tToken current = tokens;\n\t\twhile (current != null && current.getType() != TokenTypes.NULL) {\n\t\t\tif (prev != null) {\n\t\t\t\tswitch (current.getType()) {\n\t\t\t\t\tcase TokenTypes.RESERVED_WORD:\n\t\t\t\t\t\tfixContextualKeyword(current);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase TokenTypes.IDENTIFIER:\n\t\t\t\t\t\tcurrent = mergeLongClassNames(prev, current, false);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase TokenTypes.ANNOTATION:\n\t\t\t\t\t\tcurrent = mergeLongClassNames(prev, current, true);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tprev = current;\n\t\t\tcurrent = current.getNextToken();\n\t\t}\n\t}\n\n\tprivate static final Set<String> CONTEXTUAL_KEYWORDS = constSet(\n\t\t\t\"exports\", \"module\", \"non-sealed\", \"open\", \"opens\", \"permits\", \"provides\", \"record\",\n\t\t\t\"requires\", \"sealed\", \"to\", \"transitive\", \"uses\", \"var\", \"with\", \"yield\");\n\n\tprivate static void fixContextualKeyword(Token token) {\n\t\tString lexeme = token.getLexeme(); // TODO: create new string every call, better to avoid\n\t\tif (lexeme != null && CONTEXTUAL_KEYWORDS.contains(lexeme)) {\n\t\t\ttoken.setType(TokenTypes.IDENTIFIER);\n\t\t}\n\t}\n\n\t@NotNull\n\tprivate Token mergeLongClassNames(Token prev, Token current, boolean annotation) {\n\t\tint offset = current.getTextOffset();\n\t\tif (annotation) {\n\t\t\toffset++;\n\t\t}\n\t\tJavaClass javaCls = codeArea.getJavaClassIfAtPos(offset);\n\t\tif (javaCls != null) {\n\t\t\tString name = javaCls.getName();\n\t\t\tString lexeme = current.getLexeme();\n\t\t\tif (annotation && lexeme.length() > 1) {\n\t\t\t\tlexeme = lexeme.substring(1);\n\t\t\t}\n\t\t\tif (!lexeme.equals(name) && isClassNameStart(javaCls, lexeme)) {\n\t\t\t\t// try to replace long class name with one token\n\t\t\t\tToken replace = concatTokensUntil(current, name);\n\t\t\t\tif (replace != null && prev instanceof TokenImpl) {\n\t\t\t\t\tTokenImpl impl = ((TokenImpl) prev);\n\t\t\t\t\timpl.setNextToken(replace);\n\t\t\t\t\tcurrent = replace;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn current;\n\t}\n\n\tprivate boolean isClassNameStart(JavaNode javaNode, String lexeme) {\n\t\tif (javaNode.getFullName().startsWith(lexeme)) {\n\t\t\t// full class name\n\t\t\treturn true;\n\t\t}\n\t\tif (javaNode.getTopParentClass().getName().startsWith(lexeme)) {\n\t\t\t// inner class references from parent class\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Nullable\n\tprivate Token concatTokensUntil(Token start, String endText) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tToken current = start;\n\t\twhile (current != null && current.getType() != TokenTypes.NULL) {\n\t\t\tString text = current.getLexeme();\n\t\t\tif (text != null) {\n\t\t\t\tsb.append(text);\n\t\t\t\tif (text.equals(endText)) {\n\t\t\t\t\tchar[] line = sb.toString().toCharArray();\n\t\t\t\t\tTokenImpl token = new TokenImpl(line, 0, line.length - 1, start.getOffset(),\n\t\t\t\t\t\t\tstart.getType(), start.getLanguageIndex());\n\t\t\t\t\ttoken.setNextToken(current.getNextToken());\n\t\t\t\t\treturn token;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcurrent = current.getNextToken();\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/MouseHoverHighlighter.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.awt.event.MouseEvent;\nimport java.awt.event.MouseMotionAdapter;\n\nimport javax.swing.text.Caret;\n\nimport org.fife.ui.rsyntaxtextarea.Token;\nimport org.fife.ui.rtextarea.SmartHighlightPainter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JavaNode;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.JNodeCache;\n\nclass MouseHoverHighlighter extends MouseMotionAdapter {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(MouseHoverHighlighter.class);\n\n\tprivate final CodeArea codeArea;\n\tprivate final CodeLinkGenerator codeLinkGenerator;\n\tprivate final SmartHighlightPainter highlighter;\n\n\tprivate Object tag;\n\tprivate int highlightedTokenOffset = -1;\n\n\tpublic MouseHoverHighlighter(CodeArea codeArea, CodeLinkGenerator codeLinkGenerator) {\n\t\tthis.codeArea = codeArea;\n\t\tthis.codeLinkGenerator = codeLinkGenerator;\n\t\tthis.highlighter = new SmartHighlightPainter();\n\t\tloadSettings();\n\t}\n\n\tpublic void loadSettings() {\n\t\thighlighter.setPaint(codeArea.getMarkOccurrencesColor());\n\t}\n\n\t@Override\n\tpublic void mouseMoved(MouseEvent e) {\n\t\tif (!addHighlight(e)) {\n\t\t\tremoveHighlight();\n\t\t}\n\t}\n\n\tprivate boolean addHighlight(MouseEvent e) {\n\t\tif (e.getModifiersEx() != 0) {\n\t\t\treturn false;\n\t\t}\n\t\tCaret caret = codeArea.getCaret();\n\t\tif (caret.getDot() != caret.getMark()) {\n\t\t\t// selection in action, highlight will interfere with selection\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tToken token = codeArea.viewToToken(e.getPoint());\n\t\t\tif (token == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tint tokenOffset = token.getOffset();\n\t\t\tif (tokenOffset == highlightedTokenOffset) {\n\t\t\t\t// don't repaint highlight\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tJavaNode nodeAtOffset = codeLinkGenerator.getNodeAtOffset(tokenOffset);\n\t\t\tif (nodeAtOffset == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tremoveHighlight();\n\t\t\ttag = codeArea.getHighlighter().addHighlight(tokenOffset, token.getEndOffset(), this.highlighter);\n\t\t\thighlightedTokenOffset = tokenOffset;\n\t\t\tupdateToolTip(nodeAtOffset);\n\t\t\treturn true;\n\t\t} catch (Exception exc) {\n\t\t\tLOG.error(\"Mouse hover highlight error\", exc);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate void removeHighlight() {\n\t\tif (tag != null) {\n\t\t\tcodeArea.getHighlighter().removeHighlight(tag);\n\t\t\ttag = null;\n\t\t\thighlightedTokenOffset = -1;\n\t\t\tupdateToolTip(null);\n\t\t}\n\t}\n\n\tprivate void updateToolTip(JavaNode node) {\n\t\tMainWindow mainWindow = codeArea.getMainWindow();\n\t\tif (node == null || mainWindow.getSettings().isDisableTooltipOnHover()) {\n\t\t\tcodeArea.setToolTipText(null);\n\t\t\treturn;\n\t\t}\n\t\tJNodeCache nodeCache = mainWindow.getCacheObject().getNodeCache();\n\t\tJNode jNode = nodeCache.makeFrom(node);\n\t\tcodeArea.setToolTipText(jNode.getTooltip());\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/SearchBar.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.awt.Color;\nimport java.awt.event.ActionListener;\nimport java.awt.event.KeyAdapter;\nimport java.awt.event.KeyEvent;\n\nimport javax.swing.JButton;\nimport javax.swing.JLabel;\nimport javax.swing.JTextField;\nimport javax.swing.JToggleButton;\nimport javax.swing.JToolBar;\nimport javax.swing.border.EmptyBorder;\nimport javax.swing.text.BadLocationException;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.fife.ui.rtextarea.SearchContext;\nimport org.fife.ui.rtextarea.SearchEngine;\nimport org.fife.ui.rtextarea.SearchResult;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.formdev.flatlaf.FlatClientProperties;\n\nimport jadx.core.utils.StringUtils;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.TextStandardActions;\n\npublic class SearchBar extends JToolBar {\n\tprivate static final long serialVersionUID = 1836871286618633003L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SearchBar.class);\n\tprivate static final int MAX_RESULT_COUNT = 999;\n\n\tprivate final RSyntaxTextArea rTextArea;\n\n\tprivate final JTextField searchField;\n\tprivate final JLabel resultCountLabel;\n\tprivate final JToggleButton markAllCB;\n\tprivate final JToggleButton regexCB;\n\tprivate final JToggleButton wholeWordCB;\n\tprivate final JToggleButton matchCaseCB;\n\tprivate boolean notFound;\n\n\tpublic SearchBar(RSyntaxTextArea textArea) {\n\t\trTextArea = textArea;\n\n\t\tJLabel findLabel = new JLabel(NLS.str(\"search.find\") + ':');\n\t\tadd(findLabel);\n\n\t\tsearchField = new JTextField(30);\n\t\tsearchField.putClientProperty(FlatClientProperties.TEXT_FIELD_SHOW_CLEAR_BUTTON, true);\n\t\tsearchField.addKeyListener(new KeyAdapter() {\n\t\t\t@Override\n\t\t\tpublic void keyReleased(KeyEvent e) {\n\t\t\t\tswitch (e.getKeyCode()) {\n\t\t\t\t\tcase KeyEvent.VK_ENTER:\n\t\t\t\t\t\t// skip\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase KeyEvent.VK_ESCAPE:\n\t\t\t\t\t\ttoggle();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tsearch(0);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tsearchField.addActionListener(e -> search(1));\n\t\tTextStandardActions.attach(searchField);\n\t\tadd(searchField);\n\n\t\tActionListener forwardListener = e -> search(1);\n\n\t\tresultCountLabel = new JLabel();\n\t\tresultCountLabel.setBorder(new EmptyBorder(0, 10, 0, 10));\n\t\tresultCountLabel.setForeground(Color.GRAY);\n\t\tadd(resultCountLabel);\n\t\tsetResultCount(0);\n\n\t\tmatchCaseCB = new JToggleButton();\n\t\tmatchCaseCB.setIcon(Icons.ICON_MATCH);\n\t\tmatchCaseCB.setSelectedIcon(Icons.ICON_MATCH_SELECTED);\n\t\tmatchCaseCB.setToolTipText(NLS.str(\"search.match_case\"));\n\t\tmatchCaseCB.addActionListener(forwardListener);\n\t\tadd(matchCaseCB);\n\n\t\twholeWordCB = new JToggleButton();\n\t\twholeWordCB.setIcon(Icons.ICON_WORDS);\n\t\twholeWordCB.setSelectedIcon(Icons.ICON_WORDS_SELECTED);\n\t\twholeWordCB.setToolTipText(NLS.str(\"search.whole_word\"));\n\t\twholeWordCB.addActionListener(forwardListener);\n\t\tadd(wholeWordCB);\n\n\t\tregexCB = new JToggleButton();\n\t\tregexCB.setIcon(Icons.ICON_REGEX);\n\t\tregexCB.setSelectedIcon(Icons.ICON_REGEX_SELECTED);\n\t\tregexCB.setToolTipText(NLS.str(\"search.regex\"));\n\t\tregexCB.addActionListener(forwardListener);\n\t\tadd(regexCB);\n\n\t\tJButton prevButton = new JButton();\n\t\tprevButton.setIcon(Icons.ICON_UP);\n\t\tprevButton.setToolTipText(NLS.str(\"search.previous\"));\n\t\tprevButton.addActionListener(e -> search(-1));\n\t\tprevButton.setBorderPainted(false);\n\t\tadd(prevButton);\n\n\t\tJButton nextButton = new JButton();\n\t\tnextButton.setIcon(Icons.ICON_DOWN);\n\t\tnextButton.setToolTipText(NLS.str(\"search.next\"));\n\t\tnextButton.addActionListener(e -> search(1));\n\t\tnextButton.setBorderPainted(false);\n\t\tadd(nextButton);\n\n\t\tmarkAllCB = new JToggleButton();\n\t\tmarkAllCB.setIcon(Icons.ICON_MARK);\n\t\tmarkAllCB.setSelectedIcon(Icons.ICON_MARK_SELECTED);\n\t\tmarkAllCB.setToolTipText(NLS.str(\"search.mark_all\"));\n\t\tmarkAllCB.addActionListener(forwardListener);\n\t\tadd(markAllCB);\n\n\t\tJButton closeButton = new JButton();\n\t\tcloseButton.setIcon(Icons.ICON_CLOSE);\n\t\tcloseButton.addActionListener(e -> toggle());\n\t\tcloseButton.setBorderPainted(false);\n\t\tadd(closeButton);\n\n\t\tsetFloatable(false);\n\t\tsetVisible(false);\n\t}\n\n\t/*\n\t * Replicates IntelliJ's search bar behavior\n\t * 1.1. If the user has selected text, use that as the search text\n\t * 1.2. Otherwise, use the previous search text (or empty if none)\n\t * 2. Select all text in the search bar and give it focus\n\t */\n\tpublic void showAndFocus() {\n\t\tsetVisible(true);\n\n\t\tString selectedText = rTextArea.getSelectedText();\n\t\tif (!StringUtils.isEmpty(selectedText)) {\n\t\t\tsearchField.setText(selectedText);\n\t\t}\n\n\t\tsearchField.selectAll();\n\t\tsearchField.requestFocus();\n\t}\n\n\tpublic void toggle() {\n\t\tboolean visible = !isVisible();\n\t\tsetVisible(visible);\n\n\t\tif (visible) {\n\t\t\tString preferText = rTextArea.getSelectedText();\n\t\t\tif (!StringUtils.isEmpty(preferText)) {\n\t\t\t\tsearchField.setText(preferText);\n\t\t\t}\n\t\t\tsearchField.selectAll();\n\t\t\tsearchField.requestFocus();\n\t\t} else {\n\t\t\trTextArea.requestFocus();\n\t\t}\n\t}\n\n\tprivate void search(int direction) {\n\t\tString searchText = searchField.getText();\n\t\tif (searchText == null\n\t\t\t\t|| searchText.isEmpty()\n\t\t\t\t|| rTextArea.getText() == null) {\n\t\t\tsetResultCount(0);\n\t\t\treturn;\n\t\t}\n\n\t\tboolean forward = direction >= 0;\n\t\tboolean matchCase = matchCaseCB.isSelected();\n\t\tboolean regex = regexCB.isSelected();\n\t\tboolean wholeWord = wholeWordCB.isSelected();\n\n\t\tSearchContext context = new SearchContext();\n\t\tcontext.setSearchFor(searchText);\n\t\tcontext.setMatchCase(matchCase);\n\t\tcontext.setRegularExpression(regex);\n\t\tcontext.setSearchForward(forward);\n\t\tcontext.setWholeWord(wholeWord);\n\t\tcontext.setSearchWrap(true);\n\n\t\t// We enable Mark All even if the corresponding toggle button is off,\n\t\t// this is a bit hackish, but it's the only way to count matches through SearchEngine\n\t\tcontext.setMarkAll(true);\n\n\t\t// TODO hack: move cursor before previous search for not jump to next occurrence\n\t\tif (direction == 0 && !notFound) {\n\t\t\ttry {\n\t\t\t\tint caretPos = rTextArea.getCaretPosition();\n\t\t\t\tint lineNum = rTextArea.getLineOfOffset(caretPos) - 1;\n\t\t\t\tif (lineNum > 1) {\n\t\t\t\t\trTextArea.setCaretPosition(rTextArea.getLineStartOffset(lineNum));\n\t\t\t\t}\n\t\t\t} catch (BadLocationException e) {\n\t\t\t\tLOG.error(\"Caret move error\", e);\n\t\t\t}\n\t\t}\n\n\t\tSearchResult result = SearchEngine.find(rTextArea, context);\n\n\t\tsetResultCount(result.getMarkedCount());\n\n\t\t// Clear the highlighted results if Mark All is disabled\n\t\tif (!markAllCB.isSelected()) {\n\t\t\tcontext.setMarkAll(false);\n\t\t\tSearchEngine.markAll(rTextArea, context);\n\t\t}\n\n\t\tnotFound = !result.wasFound();\n\t\tif (notFound) {\n\t\t\tsearchField.putClientProperty(\"JComponent.outline\", \"error\");\n\t\t} else {\n\t\t\tsearchField.putClientProperty(\"JComponent.outline\", \"\");\n\t\t}\n\t\tsearchField.repaint();\n\t}\n\n\tprivate void setResultCount(int count) {\n\t\tboolean exceedsLimit = count > MAX_RESULT_COUNT;\n\t\tString plusSign = exceedsLimit ? \"+\" : \"\";\n\t\tcount = exceedsLimit ? MAX_RESULT_COUNT : count;\n\n\t\tresultCountLabel.setText(NLS.str(\"search.results\", plusSign, count));\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/SimpleTokenMaker.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport javax.swing.text.Segment;\n\nimport org.fife.ui.rsyntaxtextarea.Token;\nimport org.fife.ui.rsyntaxtextarea.TokenImpl;\nimport org.fife.ui.rsyntaxtextarea.TokenMakerBase;\nimport org.fife.ui.rsyntaxtextarea.TokenTypes;\n\n/**\n * Very simple token maker to use only one token per line without any parsing\n */\n@SuppressWarnings(\"unused\") // class registered by name in {@link AbstractCodeArea}\npublic class SimpleTokenMaker extends TokenMakerBase {\n\tprivate final TokenImpl token;\n\n\tpublic SimpleTokenMaker() {\n\t\ttoken = new TokenImpl();\n\t\ttoken.setType(TokenTypes.IDENTIFIER);\n\t}\n\n\t@Override\n\tpublic Token getTokenList(Segment segment, int initialTokenType, int startOffset) {\n\t\ttoken.text = segment.array;\n\t\ttoken.textOffset = startOffset;\n\t\ttoken.textCount = segment.count;\n\t\ttoken.setOffset(startOffset);\n\t\treturn token;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/SmaliArea.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.awt.Color;\nimport java.awt.Font;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.MouseEvent;\nimport java.beans.PropertyChangeListener;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.swing.AbstractAction;\nimport javax.swing.Icon;\nimport javax.swing.JCheckBoxMenuItem;\nimport javax.swing.KeyStroke;\nimport javax.swing.text.BadLocationException;\nimport javax.swing.text.EditorKit;\nimport javax.swing.text.JTextComponent;\n\nimport org.fife.ui.rsyntaxtextarea.FoldingAwareIconRowHeader;\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.Style;\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.fife.ui.rsyntaxtextarea.SyntaxScheme;\nimport org.fife.ui.rtextarea.Gutter;\nimport org.fife.ui.rtextarea.GutterIconInfo;\nimport org.fife.ui.rtextarea.IconRowHeader;\nimport org.fife.ui.rtextarea.RTextArea;\nimport org.fife.ui.rtextarea.RTextAreaUI;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.gui.device.debugger.BreakpointManager;\nimport jadx.gui.device.debugger.DbgUtils;\nimport jadx.gui.jobs.IBackgroundTask;\nimport jadx.gui.jobs.LoadTask;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.treemodel.TextNode;\nimport jadx.gui.ui.codearea.sync.CodePanelSyncee;\nimport jadx.gui.ui.codearea.sync.CodePanelSyncer;\nimport jadx.gui.ui.codearea.sync.CodePanelSyncerAbstractFactory;\nimport jadx.gui.ui.codearea.sync.SmaliSyncer;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic final class SmaliArea extends AbstractCodeArea implements CodePanelSyncerAbstractFactory, CodePanelSyncee {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SmaliArea.class);\n\n\tprivate static final long serialVersionUID = 1334485631870306494L;\n\n\tprivate static final Icon ICON_BREAKPOINT = UiUtils.openSvgIcon(\"debugger/db_set_breakpoint\");\n\tprivate static final Icon ICON_BREAKPOINT_DISABLED = UiUtils.openSvgIcon(\"debugger/db_disabled_breakpoint\");\n\tprivate static final Color BREAKPOINT_LINE_COLOR = Color.decode(\"#ad103c\");\n\tprivate static final Color DEBUG_LINE_COLOR = Color.decode(\"#9c1138\");\n\n\tprivate final JNode textNode;\n\tprivate final JCheckBoxMenuItem cbUseSmaliV2;\n\tprivate final boolean allowToggleV2 = false; // add to constructor args to change back\n\tprivate final boolean initialDisplayV2;\n\n\tprivate boolean curVersion = false;\n\tprivate SmaliModel model;\n\n\tSmaliArea(ContentPanel contentPanel, JClass node, boolean initialDisplayV2) {\n\t\tsuper(contentPanel, node);\n\t\tthis.textNode = new TextNode(node.getName());\n\t\tthis.initialDisplayV2 = initialDisplayV2;\n\n\t\tsetCodeFoldingEnabled(true);\n\n\t\tcbUseSmaliV2 = new JCheckBoxMenuItem(NLS.str(\"popup.bytecode_col\"), shouldUseSmaliPrinterV2());\n\t\tcbUseSmaliV2.setAction(new AbstractAction(NLS.str(\"popup.bytecode_col\")) {\n\t\t\tprivate static final long serialVersionUID = -1111111202103170737L;\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tJadxSettings settings = getContentPanel().getMainWindow().getSettings();\n\t\t\t\tsettings.setSmaliAreaShowBytecode(!settings.isSmaliAreaShowBytecode());\n\t\t\t\tcontentPanel.getTabbedPane().getTabs().forEach(v -> {\n\t\t\t\t\tif (v instanceof ClassCodeContentPanel) {\n\t\t\t\t\t\tswitchModel();\n\t\t\t\t\t\t((ClassCodeContentPanel) v).getSmaliCodeArea().refresh();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tsettings.sync();\n\t\t\t}\n\t\t});\n\t\tif (allowToggleV2) {\n\t\t\tgetPopupMenu().add(cbUseSmaliV2);\n\t\t}\n\t\tswitchModel();\n\t}\n\n\t@Override\n\tpublic IBackgroundTask getLoadTask() {\n\t\treturn new LoadTask<>(\n\t\t\t\t() -> model.loadCode(),\n\t\t\t\tcode -> {\n\t\t\t\t\tcurVersion = shouldUseSmaliPrinterV2();\n\t\t\t\t\tmodel.loadUI(code);\n\t\t\t\t\tsetCaretPosition(0);\n\t\t\t\t\tsetLoaded();\n\t\t\t\t});\n\t}\n\n\t@Override\n\tpublic ICodeInfo getCodeInfo() {\n\t\treturn ICodeInfo.EMPTY;\n\t}\n\n\t@Override\n\tpublic void refresh() {\n\t\tload();\n\t}\n\n\t@Override\n\tpublic JNode getNode() {\n\t\t// this area contains only smali without other node attributes\n\t\treturn textNode;\n\t}\n\n\tpublic boolean isShowingDalvikBytecode() {\n\t\treturn model instanceof DebugModel;\n\t}\n\n\tpublic JClass getJClass() {\n\t\treturn (JClass) node;\n\t}\n\n\tprivate void switchModel() {\n\t\tif (model != null) {\n\t\t\tmodel.unload();\n\t\t}\n\t\tcurVersion = shouldUseSmaliPrinterV2();\n\t\tmodel = curVersion ? new DebugModel() : new NormalModel(this);\n\t\tsetUnLoaded();\n\t\tload();\n\t}\n\n\tpublic void scrollToDebugPos(int pos) {\n\t\t// don't sync when it's set programmatically.\n\t\tgetContentPanel().getMainWindow().getSettings().setSmaliAreaShowBytecode(true);\n\t\tcbUseSmaliV2.setState(shouldUseSmaliPrinterV2());\n\t\tif (!(model instanceof DebugModel)) {\n\t\t\tswitchModel();\n\t\t\trefresh();\n\t\t}\n\t\tmodel.togglePosHighlight(pos);\n\t}\n\n\t@Override\n\tpublic Font getFont() {\n\t\tif (model == null || isDisposed()) {\n\t\t\treturn super.getFont();\n\t\t}\n\t\treturn model.getFont();\n\t}\n\n\t@Override\n\tpublic Font getFontForTokenType(int type) {\n\t\treturn getFont();\n\t}\n\n\tprivate boolean shouldUseSmaliPrinterV2() {\n\t\treturn getContentPanel().getMainWindow().getSettings().isSmaliAreaShowBytecode();\n\t}\n\n\tprivate abstract class SmaliModel {\n\t\tabstract String loadCode();\n\n\t\tabstract void loadUI(String code);\n\n\t\tabstract void unload();\n\n\t\tFont getFont() {\n\t\t\treturn SmaliArea.super.getFont();\n\t\t}\n\n\t\tFont getFontForTokenType(int type) {\n\t\t\treturn SmaliArea.super.getFontForTokenType(type);\n\t\t}\n\n\t\tvoid setBreakpoint(int off) {\n\t\t}\n\n\t\tvoid togglePosHighlight(int pos) {\n\t\t}\n\t}\n\n\tprivate class NormalModel extends SmaliModel {\n\t\tpublic NormalModel(SmaliArea smaliArea) {\n\t\t\tgetContentPanel().getMainWindow().getEditorThemeManager().apply(smaliArea);\n\t\t\tsetSyntaxEditingStyle(SYNTAX_STYLE_SMALI);\n\t\t}\n\n\t\t@Override\n\t\tpublic String loadCode() {\n\t\t\treturn getJClass().getSmali();\n\t\t}\n\n\t\t@Override\n\t\tpublic void loadUI(String code) {\n\t\t\tsetText(code);\n\t\t}\n\n\t\t@Override\n\t\tpublic void unload() {\n\t\t}\n\t}\n\n\tprivate class DebugModel extends SmaliModel {\n\t\tprivate KeyStroke bpShortcut;\n\t\tprivate Gutter gutter;\n\t\tprivate Object runningHighlightTag = null; // running line\n\t\tprivate final SmaliV2Style smaliV2Style = new SmaliV2Style(SmaliArea.this);\n\t\tprivate final Map<Integer, BreakpointLine> bpMap = new HashMap<>();\n\t\tprivate final PropertyChangeListener schemeListener = evt -> {\n\t\t\tif (smaliV2Style.refreshTheme()) {\n\t\t\t\tsetSyntaxScheme(smaliV2Style);\n\t\t\t}\n\t\t};\n\n\t\tpublic DebugModel() {\n\t\t\tloadV2Style();\n\t\t\tsetSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_ASSEMBLER_6502);\n\t\t\taddPropertyChangeListener(SYNTAX_SCHEME_PROPERTY, schemeListener);\n\t\t\tregBreakpointEvents();\n\t\t}\n\n\t\t@Override\n\t\tString loadCode() {\n\t\t\treturn DbgUtils.getSmaliCode(((JClass) node).getCls().getClassNode());\n\t\t}\n\n\t\t@Override\n\t\tvoid loadUI(String code) {\n\t\t\tif (gutter == null) {\n\t\t\t\tgutter = RSyntaxUtilities.getGutter(SmaliArea.this);\n\t\t\t\tgutter.setBookmarkingEnabled(true);\n\t\t\t\tgutter.setIconRowHeaderInheritsGutterBackground(true);\n\t\t\t\tFont baseFont = SmaliArea.super.getFont();\n\t\t\t\tgutter.setLineNumberFont(baseFont.deriveFont(baseFont.getSize2D() - 1.0f));\n\t\t\t}\n\t\t\tsetText(code);\n\t\t\tloadV2Style();\n\t\t\tloadBreakpoints();\n\t\t}\n\n\t\t@Override\n\t\tpublic void unload() {\n\t\t\tremovePropertyChangeListener(schemeListener);\n\t\t\tremoveLineHighlight(runningHighlightTag);\n\t\t\tUiUtils.removeKeyBinding(SmaliArea.this, bpShortcut, \"set a break point\");\n\t\t\tBreakpointManager.removeListener((JClass) node);\n\t\t\tbpMap.forEach((k, v) -> v.remove());\n\t\t}\n\n\t\t@Override\n\t\tpublic Font getFont() {\n\t\t\treturn smaliV2Style.getFont();\n\t\t}\n\n\t\t@Override\n\t\tpublic Font getFontForTokenType(int type) {\n\t\t\treturn smaliV2Style.getFont();\n\t\t}\n\n\t\tprivate void loadV2Style() {\n\t\t\tsetSyntaxScheme(smaliV2Style);\n\t\t}\n\n\t\tprivate void regBreakpointEvents() {\n\t\t\tbpShortcut = KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0);\n\t\t\tUiUtils.addKeyBinding(SmaliArea.this, bpShortcut, \"set break point\", new AbstractAction() {\n\t\t\t\tprivate static final long serialVersionUID = -1111111202103170738L;\n\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t\tsetBreakpoint(getCaretPosition());\n\t\t\t\t}\n\t\t\t});\n\t\t\tBreakpointManager.addListener((JClass) node, this::setBreakpointDisabled);\n\t\t}\n\n\t\tprivate void loadBreakpoints() {\n\t\t\tList<Integer> posList = BreakpointManager.getPositions((JClass) node);\n\t\t\tfor (Integer integer : posList) {\n\t\t\t\tsetBreakpoint(integer);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void setBreakpoint(int pos) {\n\t\t\tint line;\n\t\t\ttry {\n\t\t\t\tline = getLineOfOffset(pos);\n\t\t\t} catch (BadLocationException e) {\n\t\t\t\tLOG.error(\"Failed to get line by offset: {}\", pos, e);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tBreakpointLine bpLine = bpMap.remove(line);\n\t\t\tif (bpLine == null) {\n\t\t\t\tbpLine = new BreakpointLine(line);\n\t\t\t\tbpLine.setDisabled(false);\n\t\t\t\tbpMap.put(line, bpLine);\n\t\t\t\tif (!BreakpointManager.set((JClass) node, line)) {\n\t\t\t\t\tbpLine.setDisabled(true);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tBreakpointManager.remove((JClass) node, line);\n\t\t\t\tbpLine.remove();\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void togglePosHighlight(int pos) {\n\t\t\tif (runningHighlightTag != null) {\n\t\t\t\tremoveLineHighlight(runningHighlightTag);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tint line = getLineOfOffset(pos);\n\t\t\t\trunningHighlightTag = addLineHighlight(line, DEBUG_LINE_COLOR);\n\t\t\t} catch (BadLocationException e) {\n\t\t\t\tLOG.error(\"Failed to get line by offset: {}\", pos, e);\n\t\t\t}\n\t\t}\n\n\t\tprivate void setBreakpointDisabled(int pos) {\n\t\t\ttry {\n\t\t\t\tint line = getLineOfOffset(pos);\n\t\t\t\tbpMap.computeIfAbsent(line, k -> new BreakpointLine(line)).setDisabled(true);\n\t\t\t} catch (BadLocationException e) {\n\t\t\t\tLOG.error(\"Failed to get line by offset: {}\", pos, e);\n\t\t\t}\n\t\t}\n\n\t\tprivate class SmaliV2Style extends SyntaxScheme {\n\t\t\tpublic SmaliV2Style(SmaliArea smaliArea) {\n\t\t\t\tsuper(true);\n\t\t\t\tsmaliArea.getContentPanel().getMainWindow().getEditorThemeManager().apply(smaliArea);\n\t\t\t\tupdateTheme();\n\t\t\t}\n\n\t\t\tpublic Font getFont() {\n\t\t\t\treturn getContentPanel().getMainWindow().getSettings().getSmaliFont();\n\t\t\t}\n\n\t\t\tpublic boolean refreshTheme() {\n\t\t\t\tboolean refresh = getSyntaxScheme() != this;\n\t\t\t\tif (refresh) {\n\t\t\t\t\tupdateTheme();\n\t\t\t\t}\n\t\t\t\treturn refresh;\n\t\t\t}\n\n\t\t\tprivate void updateTheme() {\n\t\t\t\tStyle[] mainStyles = getSyntaxScheme().getStyles();\n\t\t\t\tStyle[] styles = new Style[mainStyles.length];\n\t\t\t\tfor (int i = 0; i < mainStyles.length; i++) {\n\t\t\t\t\tStyle mainStyle = mainStyles[i];\n\t\t\t\t\tif (mainStyle == null) {\n\t\t\t\t\t\tstyles[i] = new Style();\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// font will be hijacked by getFont & getFontForTokenType,\n\t\t\t\t\t\t// so it doesn't need to be set here.\n\t\t\t\t\t\tstyles[i] = new Style(mainStyle.foreground, mainStyle.background, null);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tsetStyles(styles);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void restoreDefaults(Font baseFont) {\n\t\t\t\trestoreDefaults(baseFont, true);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void restoreDefaults(Font baseFont, boolean fontStyles) {\n\t\t\t\t// Note: it's a hook for continuing using the editor theme, better don't remove it.\n\t\t\t}\n\t\t}\n\n\t\tprivate class BreakpointLine {\n\t\t\tObject highlightTag;\n\t\t\tGutterIconInfo iconInfo;\n\t\t\tboolean disabled;\n\t\t\tfinal int line;\n\n\t\t\tBreakpointLine(int line) {\n\t\t\t\tthis.line = line;\n\t\t\t\tthis.disabled = true;\n\t\t\t}\n\n\t\t\tvoid remove() {\n\t\t\t\tsafeRemoveTrackingIcon(iconInfo);\n\t\t\t\tif (!this.disabled) {\n\t\t\t\t\tremoveLineHighlight(highlightTag);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvoid setDisabled(boolean disabled) {\n\t\t\t\tif (disabled) {\n\t\t\t\t\tif (!this.disabled) {\n\t\t\t\t\t\tsafeRemoveTrackingIcon(iconInfo);\n\t\t\t\t\t\tremoveLineHighlight(highlightTag);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\ticonInfo = gutter.addLineTrackingIcon(line, ICON_BREAKPOINT_DISABLED);\n\t\t\t\t\t\t} catch (BadLocationException e) {\n\t\t\t\t\t\t\tLOG.error(\"Failed to add line tracking icon\", e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (this.disabled) {\n\t\t\t\t\t\tsafeRemoveTrackingIcon(this.iconInfo);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\ticonInfo = gutter.addLineTrackingIcon(line, ICON_BREAKPOINT);\n\t\t\t\t\t\t\thighlightTag = addLineHighlight(line, BREAKPOINT_LINE_COLOR);\n\t\t\t\t\t\t} catch (BadLocationException e) {\n\t\t\t\t\t\t\tLOG.error(\"Failed to remove line tracking icon\", e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.disabled = disabled;\n\t\t\t}\n\t\t}\n\n\t\tprivate void safeRemoveTrackingIcon(GutterIconInfo iconInfo) {\n\t\t\tif (gutter != null && iconInfo != null) {\n\t\t\t\tgutter.removeTrackingIcon(iconInfo);\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Override\n\tprotected RTextAreaUI createRTextAreaUI() {\n\t\t// IconRowHeader won't fire an event when people click on it for adding/removing icons,\n\t\t// so our poor breakpoints won't be set if we don't hijack IconRowHeader.\n\t\treturn new RSyntaxTextAreaUI(this) {\n\t\t\t@Override\n\t\t\tpublic EditorKit getEditorKit(JTextComponent tc) {\n\t\t\t\treturn new RSyntaxTextAreaEditorKit() {\n\t\t\t\t\tprivate static final long serialVersionUID = -1111111202103170740L;\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic IconRowHeader createIconRowHeader(RTextArea textArea) {\n\t\t\t\t\t\treturn new FoldingAwareIconRowHeader((RSyntaxTextArea) textArea) {\n\t\t\t\t\t\t\tprivate static final long serialVersionUID = -1111111202103170739L;\n\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic void mousePressed(MouseEvent e) {\n\t\t\t\t\t\t\t\tint offs = textArea.viewToModel2D(e.getPoint());\n\t\t\t\t\t\t\t\tif (offs > -1) {\n\t\t\t\t\t\t\t\t\tmodel.setBreakpoint(offs);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t};\n\t}\n\n\t@Override\n\tpublic CodePanelSyncer createCodePanelSyncer() {\n\t\treturn new SmaliSyncer(this);\n\t}\n\n\t@Override\n\tpublic boolean sync(CodePanelSyncer codePanelSyncer) {\n\t\treturn codePanelSyncer.syncTo(this);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/SmaliFoldParser.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.NavigableSet;\nimport java.util.TreeSet;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.fife.ui.rsyntaxtextarea.folding.Fold;\nimport org.fife.ui.rsyntaxtextarea.folding.FoldParser;\nimport org.fife.ui.rsyntaxtextarea.folding.FoldParserManager;\nimport org.fife.ui.rsyntaxtextarea.folding.FoldType;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class SmaliFoldParser implements FoldParser {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SmaliFoldParser.class);\n\n\tprivate static final Pattern CLASS_LINE_PATTERN = Pattern.compile(\"^\\\\.class\\\\b\", Pattern.MULTILINE);\n\tprivate static final Pattern ENDMETHOD_LINE_PATTERN = Pattern.compile(\"^\\\\.end method\\\\b\", Pattern.MULTILINE);\n\tprivate static final Pattern STARTMETHOD_LINE_PATTERN = Pattern.compile(\"^\\\\.method\\\\b\", Pattern.MULTILINE);\n\n\tpublic static void register() {\n\t\tFoldParserManager.get().addFoldParserMapping(AbstractCodeArea.SYNTAX_STYLE_SMALI, new SmaliFoldParser());\n\t}\n\n\tprivate SmaliFoldParser() {\n\t}\n\n\t@Override\n\tpublic List<Fold> getFolds(RSyntaxTextArea textArea) {\n\t\tList<Fold> classFolds = new ArrayList<>();\n\t\tString text = textArea.getText();\n\n\t\tList<Integer> classStartOffsets = getClassStartOffsets(text);\n\t\tNavigableSet<Integer> startMethodStartOffsets = getStartMethodStartOffsets(text);\n\t\tNavigableSet<Integer> endMethodEndOffsets = getEndMethodEndOffsets(text);\n\t\tfor (int i = 0; i < classStartOffsets.size(); i++) {\n\t\t\t// Start offset of .class\n\t\t\tint startOffset = classStartOffsets.get(i);\n\n\t\t\tint classLimit;\n\t\t\tif (i < classStartOffsets.size() - 1) {\n\t\t\t\tclassLimit = classStartOffsets.get(i + 1);\n\t\t\t} else {\n\t\t\t\tclassLimit = text.length();\n\t\t\t}\n\n\t\t\t// Get the last \".end method\" before next .class or end of file\n\t\t\tInteger endOffset = endMethodEndOffsets.floor(classLimit);\n\t\t\tif (endOffset != null) {\n\t\t\t\tFold classFold = createFold(textArea, startOffset, endOffset);\n\t\t\t\tif (classFold != null) {\n\t\t\t\t\tclassFolds.add(classFold);\n\n\t\t\t\t\t// Start looking for .method after .class definition\n\t\t\t\t\tInteger startMethodStartOffset = startMethodStartOffsets.ceiling(startOffset);\n\t\t\t\t\twhile (startMethodStartOffset != null && startMethodStartOffset < endOffset) {\n\t\t\t\t\t\tInteger endMethodEndOffset = endMethodEndOffsets.ceiling(startMethodStartOffset);\n\t\t\t\t\t\tif (endMethodEndOffset != null) {\n\t\t\t\t\t\t\taddFold(classFold, startMethodStartOffset, endMethodEndOffset);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Look for next .method starting from last .end method\n\t\t\t\t\t\tstartMethodStartOffset = startMethodStartOffsets.ceiling(endMethodEndOffset);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn classFolds;\n\t}\n\n\tprivate static @Nullable Fold createFold(RSyntaxTextArea textArea, int startOffset, int endOffset) {\n\t\ttry {\n\t\t\tFold fold = new Fold(FoldType.CODE, textArea, startOffset);\n\t\t\tfold.setEndOffset(endOffset);\n\t\t\treturn fold;\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to create code fold\", e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static void addFold(Fold parent, int startOffset, int endOffset) {\n\t\ttry {\n\t\t\tFold fold = parent.createChild(FoldType.CODE, startOffset);\n\t\t\tfold.setEndOffset(endOffset);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to add code fold\", e);\n\t\t}\n\t}\n\n\tprivate List<Integer> getClassStartOffsets(String text) {\n\t\tList<Integer> startOffsets = new ArrayList<>();\n\t\tMatcher matcher = CLASS_LINE_PATTERN.matcher(text);\n\t\twhile (matcher.find()) {\n\t\t\tint startOffset = matcher.start();\n\t\t\tstartOffsets.add(startOffset);\n\t\t}\n\t\treturn startOffsets;\n\t}\n\n\tprivate NavigableSet<Integer> getStartMethodStartOffsets(String text) {\n\t\tNavigableSet<Integer> startOffsets = new TreeSet<>();\n\t\tMatcher matcher = STARTMETHOD_LINE_PATTERN.matcher(text);\n\t\twhile (matcher.find()) {\n\t\t\tint startOffset = matcher.start();\n\t\t\tstartOffsets.add(startOffset);\n\t\t}\n\t\treturn startOffsets;\n\t}\n\n\tprivate NavigableSet<Integer> getEndMethodEndOffsets(String text) {\n\t\tNavigableSet<Integer> endOffsets = new TreeSet<>();\n\t\tMatcher matcher = ENDMETHOD_LINE_PATTERN.matcher(text);\n\t\twhile (matcher.find()) {\n\t\t\tint endOffset = matcher.end();\n\t\t\tendOffsets.add(endOffset);\n\t\t}\n\t\treturn endOffsets;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/SmaliTokenMaker.java",
    "content": "/* The following code was generated by JFlex */\n\npackage jadx.gui.ui.codearea;\n\nimport java.io.*;\n\nimport javax.swing.text.Segment;\n\nimport org.fife.ui.rsyntaxtextarea.*;\n\n/**\n * SmaliTokenMaker\n * MartinKay@qq.com\n */\n\n@SuppressWarnings(\"checkstyle:all\")\npublic class SmaliTokenMaker extends AbstractJFlexCTokenMaker {\n\n\t/** This character denotes the end of file */\n\tpublic static final int YYEOF = -1;\n\n\t/** initial size of the lookahead buffer */\n\tprivate static final int ZZ_BUFFERSIZE = 16384;\n\n\t/** lexical states */\n\tpublic static final int EOL_COMMENT = 1;\n\tpublic static final int YYINITIAL = 0;\n\n\t/**\n\t * Translates characters to character classes\n\t */\n\tprivate static final String ZZ_CMAP_PACKED =\n\t\t\t\"\\11\\0\\1\\21\\1\\10\\1\\0\\1\\21\\1\\17\\22\\0\\1\\77\\1\\54\\1\\15\" +\n\t\t\t\t\t\"\\1\\20\\1\\1\\1\\35\\1\\35\\1\\7\\1\\37\\1\\37\\1\\35\\1\\40\\1\\35\" +\n\t\t\t\t\t\"\\1\\25\\1\\23\\1\\41\\1\\4\\1\\66\\1\\16\\1\\72\\1\\71\\1\\6\\1\\67\" +\n\t\t\t\t\t\"\\1\\6\\1\\76\\1\\3\\1\\45\\1\\50\\1\\55\\1\\54\\1\\56\\1\\35\\1\\36\" +\n\t\t\t\t\t\"\\1\\5\\1\\53\\1\\53\\1\\53\\1\\5\\1\\53\\2\\1\\1\\52\\1\\52\\1\\1\" +\n\t\t\t\t\t\"\\1\\47\\6\\1\\1\\52\\2\\1\\1\\51\\3\\1\\1\\52\\1\\37\\1\\11\\1\\37\" +\n\t\t\t\t\t\"\\1\\17\\1\\2\\1\\0\\1\\31\\1\\14\\1\\60\\1\\61\\1\\24\\1\\30\\1\\62\" +\n\t\t\t\t\t\"\\1\\42\\1\\44\\1\\70\\1\\73\\1\\32\\1\\65\\1\\13\\1\\63\\1\\43\\1\\74\" +\n\t\t\t\t\t\"\\1\\27\\1\\33\\1\\26\\1\\12\\1\\57\\1\\46\\1\\22\\1\\64\\1\\75\\1\\34\" +\n\t\t\t\t\t\"\\1\\17\\1\\34\\1\\35\\uff81\\0\";\n\n\t/**\n\t * Translates characters to character classes\n\t */\n\tprivate static final char[] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED);\n\n\t/**\n\t * Translates DFA states to action switch labels.\n\t */\n\tprivate static final int[] ZZ_ACTION = zzUnpackAction();\n\n\tprivate static final String ZZ_ACTION_PACKED_0 =\n\t\t\t\"\\2\\0\\2\\1\\2\\2\\1\\3\\1\\4\\3\\1\\1\\5\\1\\6\" +\n\t\t\t\t\t\"\\1\\7\\12\\1\\1\\10\\1\\11\\1\\12\\4\\1\\2\\13\\1\\12\" +\n\t\t\t\t\t\"\\5\\1\\1\\14\\1\\15\\3\\14\\1\\0\\1\\16\\1\\0\\2\\16\" +\n\t\t\t\t\t\"\\1\\3\\1\\17\\1\\0\\1\\3\\5\\1\\2\\5\\1\\20\\1\\21\" +\n\t\t\t\t\t\"\\12\\0\\1\\1\\1\\10\\23\\1\\1\\12\\5\\1\\6\\0\\1\\13\" +\n\t\t\t\t\t\"\\1\\0\\15\\1\\5\\0\\1\\21\\1\\0\\1\\22\\1\\3\\1\\23\" +\n\t\t\t\t\t\"\\2\\3\\1\\17\\1\\3\\5\\1\\1\\24\\1\\1\\1\\5\\1\\25\" +\n\t\t\t\t\t\"\\1\\5\\20\\0\\35\\1\\7\\0\\10\\1\\1\\0\\2\\1\\5\\0\" +\n\t\t\t\t\t\"\\1\\3\\2\\0\\1\\1\\1\\0\\1\\1\\1\\5\\22\\0\\1\\26\" +\n\t\t\t\t\t\"\\1\\27\\3\\1\\1\\0\\7\\1\\1\\24\\1\\1\\1\\0\\2\\1\" +\n\t\t\t\t\t\"\\1\\0\\6\\1\\1\\0\\2\\1\\11\\0\\4\\1\\1\\0\\3\\1\" +\n\t\t\t\t\t\"\\1\\24\\2\\0\\1\\1\\1\\24\\2\\0\\1\\30\\1\\0\\1\\3\" +\n\t\t\t\t\t\"\\6\\0\\1\\1\\1\\5\\6\\0\\1\\31\\13\\0\\2\\1\\3\\0\" +\n\t\t\t\t\t\"\\2\\1\\1\\0\\3\\1\\3\\0\\2\\1\\1\\0\\6\\1\\1\\0\" +\n\t\t\t\t\t\"\\2\\1\\1\\24\\5\\0\\3\\1\\1\\24\\1\\0\\2\\1\\3\\0\" +\n\t\t\t\t\t\"\\1\\1\\5\\0\\1\\3\\6\\0\\1\\5\\7\\0\\1\\31\\4\\0\" +\n\t\t\t\t\t\"\\1\\31\\1\\1\\1\\24\\4\\0\\1\\1\\1\\0\\2\\1\\10\\0\" +\n\t\t\t\t\t\"\\1\\1\\1\\0\\3\\1\\1\\0\\2\\1\\1\\24\\3\\0\\1\\32\" +\n\t\t\t\t\t\"\\2\\1\\2\\0\\1\\1\\1\\0\\2\\1\\3\\0\\1\\24\\1\\1\" +\n\t\t\t\t\t\"\\23\\0\\1\\1\\7\\0\\2\\1\\10\\0\\1\\24\\1\\1\\1\\24\" +\n\t\t\t\t\t\"\\1\\0\\2\\1\\1\\0\\1\\1\\11\\0\\1\\1\\1\\0\\1\\1\" +\n\t\t\t\t\t\"\\2\\0\\1\\1\\22\\0\\1\\24\\3\\0\\1\\1\\12\\0\\1\\1\" +\n\t\t\t\t\t\"\\1\\0\\1\\1\\15\\0\\1\\1\\1\\0\\1\\1\\25\\0\\1\\1\" +\n\t\t\t\t\t\"\\22\\0\\1\\1\\10\\0\\1\\24\\26\\0\\1\\24\\2\\0\\1\\1\" +\n\t\t\t\t\t\"\\35\\0\\1\\24\\3\\0\\1\\24\\6\\0\\1\\24\\50\\0\\1\\26\";\n\n\tprivate static int[] zzUnpackAction() {\n\t\tint[] result = new int[701];\n\t\tint offset = 0;\n\t\toffset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);\n\t\treturn result;\n\t}\n\n\tprivate static int zzUnpackAction(String packed, int offset, int[] result) {\n\t\tint i = 0; /* index in packed string */\n\t\tint j = offset; /* index in unpacked array */\n\t\tint l = packed.length();\n\t\twhile (i < l) {\n\t\t\tint count = packed.charAt(i++);\n\t\t\tint value = packed.charAt(i++);\n\t\t\tdo\n\t\t\t\tresult[j++] = value;\n\t\t\twhile (--count > 0);\n\t\t}\n\t\treturn j;\n\t}\n\n\t/**\n\t * Translates a state to a row index in the transition table\n\t */\n\tprivate static final int[] ZZ_ROWMAP = zzUnpackRowMap();\n\n\tprivate static final String ZZ_ROWMAP_PACKED_0 =\n\t\t\t\"\\0\\0\\0\\100\\0\\200\\0\\300\\0\\u0100\\0\\u0140\\0\\u0180\\0\\200\" +\n\t\t\t\t\t\"\\0\\u01c0\\0\\u0200\\0\\u0240\\0\\u0280\\0\\200\\0\\u02c0\\0\\u0300\\0\\u0340\" +\n\t\t\t\t\t\"\\0\\u0380\\0\\u03c0\\0\\u0400\\0\\u0440\\0\\u0480\\0\\u04c0\\0\\u0500\\0\\u0540\" +\n\t\t\t\t\t\"\\0\\200\\0\\200\\0\\u0580\\0\\u05c0\\0\\u0600\\0\\u0640\\0\\u0680\\0\\300\" +\n\t\t\t\t\t\"\\0\\u06c0\\0\\u0700\\0\\u0740\\0\\u0780\\0\\u07c0\\0\\u0800\\0\\u0840\\0\\u0880\" +\n\t\t\t\t\t\"\\0\\200\\0\\u08c0\\0\\u0900\\0\\u0940\\0\\u0980\\0\\u09c0\\0\\u0a00\\0\\u0a40\" +\n\t\t\t\t\t\"\\0\\u0a80\\0\\u0ac0\\0\\200\\0\\u0b00\\0\\u0b40\\0\\u0b80\\0\\u0bc0\\0\\u0c00\" +\n\t\t\t\t\t\"\\0\\u0c40\\0\\u0c80\\0\\u0cc0\\0\\u0d00\\0\\200\\0\\u0d40\\0\\u0d80\\0\\u0dc0\" +\n\t\t\t\t\t\"\\0\\u0e00\\0\\u0e40\\0\\u0e80\\0\\u0ec0\\0\\u0f00\\0\\u0f40\\0\\u0f80\\0\\u0fc0\" +\n\t\t\t\t\t\"\\0\\u1000\\0\\u1040\\0\\u1080\\0\\u10c0\\0\\u1100\\0\\u1140\\0\\u1180\\0\\u11c0\" +\n\t\t\t\t\t\"\\0\\u1200\\0\\u1240\\0\\u1280\\0\\u12c0\\0\\u1300\\0\\u1340\\0\\u1380\\0\\u13c0\" +\n\t\t\t\t\t\"\\0\\u1400\\0\\u1440\\0\\u1480\\0\\u14c0\\0\\u1500\\0\\u1540\\0\\u1580\\0\\u15c0\" +\n\t\t\t\t\t\"\\0\\u1600\\0\\u1640\\0\\u1680\\0\\u16c0\\0\\u1700\\0\\u1740\\0\\u1780\\0\\u17c0\" +\n\t\t\t\t\t\"\\0\\u1800\\0\\200\\0\\u1840\\0\\u06c0\\0\\u1880\\0\\u18c0\\0\\u1900\\0\\u1940\" +\n\t\t\t\t\t\"\\0\\u1980\\0\\u19c0\\0\\u1a00\\0\\u1a40\\0\\u1a80\\0\\u1ac0\\0\\u1b00\\0\\u1b40\" +\n\t\t\t\t\t\"\\0\\u1b80\\0\\u1bc0\\0\\u1c00\\0\\u1c40\\0\\u1c80\\0\\u1cc0\\0\\u1d00\\0\\u0a80\" +\n\t\t\t\t\t\"\\0\\u1d40\\0\\200\\0\\u1d80\\0\\u1dc0\\0\\u0b00\\0\\u1e00\\0\\u1e40\\0\\u1e80\" +\n\t\t\t\t\t\"\\0\\u1ec0\\0\\u1f00\\0\\u1f40\\0\\300\\0\\u1f80\\0\\u1fc0\\0\\200\\0\\u2000\" +\n\t\t\t\t\t\"\\0\\u2040\\0\\u2080\\0\\u20c0\\0\\u2100\\0\\u2140\\0\\u2180\\0\\u21c0\\0\\u2200\" +\n\t\t\t\t\t\"\\0\\u2240\\0\\u2280\\0\\u22c0\\0\\u2300\\0\\u2340\\0\\u2380\\0\\u23c0\\0\\u2400\" +\n\t\t\t\t\t\"\\0\\u2440\\0\\u2480\\0\\u24c0\\0\\u2500\\0\\u2540\\0\\u2580\\0\\u25c0\\0\\u2600\" +\n\t\t\t\t\t\"\\0\\u2640\\0\\u2680\\0\\u26c0\\0\\u2700\\0\\u2740\\0\\u2780\\0\\u27c0\\0\\u2800\" +\n\t\t\t\t\t\"\\0\\u2840\\0\\u2880\\0\\u28c0\\0\\u2900\\0\\u2940\\0\\u2980\\0\\u29c0\\0\\u2a00\" +\n\t\t\t\t\t\"\\0\\u2a40\\0\\u2a80\\0\\u2ac0\\0\\u2b00\\0\\u2b40\\0\\u2b80\\0\\u2bc0\\0\\u2c00\" +\n\t\t\t\t\t\"\\0\\u2c40\\0\\u2c80\\0\\u2cc0\\0\\u2d00\\0\\u2d40\\0\\u2d80\\0\\u2dc0\\0\\u2e00\" +\n\t\t\t\t\t\"\\0\\u2e40\\0\\u2e80\\0\\u2ec0\\0\\u2f00\\0\\u2f40\\0\\u2f80\\0\\u2fc0\\0\\u3000\" +\n\t\t\t\t\t\"\\0\\u3040\\0\\u3080\\0\\u30c0\\0\\u3100\\0\\u3140\\0\\u3180\\0\\u31c0\\0\\u3200\" +\n\t\t\t\t\t\"\\0\\u3240\\0\\u3280\\0\\u32c0\\0\\u3300\\0\\u3340\\0\\u3380\\0\\u33c0\\0\\u3400\" +\n\t\t\t\t\t\"\\0\\u3440\\0\\u3480\\0\\u34c0\\0\\u3500\\0\\u3540\\0\\u3580\\0\\u35c0\\0\\u3600\" +\n\t\t\t\t\t\"\\0\\u3640\\0\\u3680\\0\\u36c0\\0\\u3700\\0\\u3740\\0\\300\\0\\300\\0\\u3780\" +\n\t\t\t\t\t\"\\0\\u37c0\\0\\u3800\\0\\u3840\\0\\u3880\\0\\u38c0\\0\\u3900\\0\\u3940\\0\\u3980\" +\n\t\t\t\t\t\"\\0\\u39c0\\0\\u3a00\\0\\u3a40\\0\\u3a80\\0\\u3ac0\\0\\u3b00\\0\\u3b40\\0\\u3b80\" +\n\t\t\t\t\t\"\\0\\u3bc0\\0\\u3c00\\0\\u3c40\\0\\u3c80\\0\\u3cc0\\0\\u3d00\\0\\u3d40\\0\\u3d80\" +\n\t\t\t\t\t\"\\0\\u3dc0\\0\\u3e00\\0\\u3e40\\0\\u3e80\\0\\u3ec0\\0\\u3f00\\0\\u3f40\\0\\u3f80\" +\n\t\t\t\t\t\"\\0\\u3fc0\\0\\u4000\\0\\u4040\\0\\u4080\\0\\u40c0\\0\\u4100\\0\\u4140\\0\\u4180\" +\n\t\t\t\t\t\"\\0\\u41c0\\0\\u4200\\0\\u4240\\0\\u4280\\0\\u42c0\\0\\u4300\\0\\u4340\\0\\u4380\" +\n\t\t\t\t\t\"\\0\\u43c0\\0\\u4400\\0\\u4440\\0\\u4480\\0\\u44c0\\0\\u4500\\0\\u4540\\0\\u4580\" +\n\t\t\t\t\t\"\\0\\u45c0\\0\\u4600\\0\\u4640\\0\\u4680\\0\\u46c0\\0\\u4700\\0\\u4740\\0\\u4780\" +\n\t\t\t\t\t\"\\0\\u47c0\\0\\u4800\\0\\200\\0\\u4840\\0\\u4880\\0\\u48c0\\0\\u4900\\0\\u4940\" +\n\t\t\t\t\t\"\\0\\u4980\\0\\u49c0\\0\\u4a00\\0\\u4a40\\0\\u4a80\\0\\u4ac0\\0\\u4b00\\0\\u4b40\" +\n\t\t\t\t\t\"\\0\\u4b80\\0\\u4bc0\\0\\u4c00\\0\\u4c40\\0\\u4c80\\0\\u4cc0\\0\\u4d00\\0\\u4d40\" +\n\t\t\t\t\t\"\\0\\u4d80\\0\\u4dc0\\0\\u4e00\\0\\u4e40\\0\\u4e80\\0\\u4ec0\\0\\u4f00\\0\\u4f40\" +\n\t\t\t\t\t\"\\0\\u4f80\\0\\u4fc0\\0\\u5000\\0\\u5040\\0\\u5080\\0\\u50c0\\0\\u5100\\0\\u5140\" +\n\t\t\t\t\t\"\\0\\u5180\\0\\u51c0\\0\\u5200\\0\\u5240\\0\\u5280\\0\\u52c0\\0\\u5300\\0\\u5340\" +\n\t\t\t\t\t\"\\0\\u5380\\0\\u53c0\\0\\u5400\\0\\u5440\\0\\u5480\\0\\u54c0\\0\\u5500\\0\\u5540\" +\n\t\t\t\t\t\"\\0\\u5580\\0\\u55c0\\0\\u5600\\0\\u5640\\0\\u4400\\0\\u5680\\0\\u56c0\\0\\u5700\" +\n\t\t\t\t\t\"\\0\\u5740\\0\\u5780\\0\\u57c0\\0\\u5800\\0\\u5840\\0\\u5880\\0\\u58c0\\0\\u5900\" +\n\t\t\t\t\t\"\\0\\u5940\\0\\u5980\\0\\u59c0\\0\\u5a00\\0\\u5a40\\0\\u4a80\\0\\u5a80\\0\\u5ac0\" +\n\t\t\t\t\t\"\\0\\u5b00\\0\\u5b40\\0\\u5b80\\0\\u5bc0\\0\\u5c00\\0\\u5c40\\0\\u5c80\\0\\u5cc0\" +\n\t\t\t\t\t\"\\0\\u5d00\\0\\u5d40\\0\\u5d80\\0\\u5dc0\\0\\u5e00\\0\\u5e40\\0\\u5e80\\0\\u5ec0\" +\n\t\t\t\t\t\"\\0\\u5f00\\0\\u5f40\\0\\u5f80\\0\\u5fc0\\0\\u6000\\0\\u6040\\0\\u6080\\0\\u60c0\" +\n\t\t\t\t\t\"\\0\\u6100\\0\\u6140\\0\\u6180\\0\\u61c0\\0\\u6200\\0\\200\\0\\u6240\\0\\u6280\" +\n\t\t\t\t\t\"\\0\\u62c0\\0\\u6300\\0\\u6340\\0\\u6380\\0\\u63c0\\0\\u6400\\0\\u6440\\0\\u6480\" +\n\t\t\t\t\t\"\\0\\u64c0\\0\\u6500\\0\\u6540\\0\\u6580\\0\\u65c0\\0\\u6600\\0\\u6640\\0\\u6680\" +\n\t\t\t\t\t\"\\0\\u66c0\\0\\u6700\\0\\u6740\\0\\u6780\\0\\u67c0\\0\\u6800\\0\\u6840\\0\\u6880\" +\n\t\t\t\t\t\"\\0\\u68c0\\0\\u6900\\0\\u6940\\0\\u6980\\0\\u69c0\\0\\u6a00\\0\\u6a40\\0\\u6a80\" +\n\t\t\t\t\t\"\\0\\u6ac0\\0\\u6b00\\0\\u6b40\\0\\u6b80\\0\\u6bc0\\0\\u6c00\\0\\u6c40\\0\\u6c80\" +\n\t\t\t\t\t\"\\0\\u6cc0\\0\\u6d00\\0\\u6d40\\0\\u6d80\\0\\u6dc0\\0\\u6e00\\0\\u6e40\\0\\u6e80\" +\n\t\t\t\t\t\"\\0\\u6ec0\\0\\u6f00\\0\\u6f40\\0\\u6f80\\0\\u6fc0\\0\\u7000\\0\\u7040\\0\\u7080\" +\n\t\t\t\t\t\"\\0\\u70c0\\0\\u7100\\0\\u7140\\0\\u7180\\0\\u71c0\\0\\u7200\\0\\u7240\\0\\u7280\" +\n\t\t\t\t\t\"\\0\\u72c0\\0\\u7300\\0\\u7340\\0\\u7380\\0\\u73c0\\0\\u7400\\0\\u7440\\0\\u7480\" +\n\t\t\t\t\t\"\\0\\u74c0\\0\\u7500\\0\\u7540\\0\\u7580\\0\\u75c0\\0\\u7600\\0\\u7640\\0\\u7680\" +\n\t\t\t\t\t\"\\0\\u76c0\\0\\u7700\\0\\u7740\\0\\u7780\\0\\u77c0\\0\\u7800\\0\\u7840\\0\\u7880\" +\n\t\t\t\t\t\"\\0\\u78c0\\0\\u7900\\0\\u7940\\0\\u7980\\0\\u79c0\\0\\u7a00\\0\\u7a40\\0\\u7a80\" +\n\t\t\t\t\t\"\\0\\u7ac0\\0\\u7b00\\0\\u7b40\\0\\u7b80\\0\\u7bc0\\0\\u7c00\\0\\u7c40\\0\\u7c80\" +\n\t\t\t\t\t\"\\0\\u7cc0\\0\\u7d00\\0\\u7d40\\0\\u7d80\\0\\u7dc0\\0\\u7e00\\0\\u7e40\\0\\u7e80\" +\n\t\t\t\t\t\"\\0\\u7ec0\\0\\u7f00\\0\\u7f40\\0\\u7f80\\0\\u7fc0\\0\\u8000\\0\\u8040\\0\\u8080\" +\n\t\t\t\t\t\"\\0\\u80c0\\0\\u8100\\0\\u8140\\0\\u8180\\0\\u81c0\\0\\u8200\\0\\u8240\\0\\u8280\" +\n\t\t\t\t\t\"\\0\\u82c0\\0\\u8300\\0\\u8340\\0\\u8380\\0\\u83c0\\0\\u8400\\0\\u8440\\0\\u8480\" +\n\t\t\t\t\t\"\\0\\u84c0\\0\\u8500\\0\\u8540\\0\\u8580\\0\\u85c0\\0\\u8600\\0\\u8640\\0\\u8680\" +\n\t\t\t\t\t\"\\0\\u86c0\\0\\u8700\\0\\u8740\\0\\u8780\\0\\u87c0\\0\\u8800\\0\\u8840\\0\\u8880\" +\n\t\t\t\t\t\"\\0\\u88c0\\0\\u8900\\0\\u8940\\0\\u8980\\0\\u89c0\\0\\u8a00\\0\\u8a40\\0\\u8a80\" +\n\t\t\t\t\t\"\\0\\u8ac0\\0\\u8b00\\0\\u8b40\\0\\u8b80\\0\\u8bc0\\0\\u8c00\\0\\u8c40\\0\\u8c80\" +\n\t\t\t\t\t\"\\0\\u8cc0\\0\\u8d00\\0\\u8d40\\0\\u8d80\\0\\u8dc0\\0\\u8e00\\0\\u8e40\\0\\u8e80\" +\n\t\t\t\t\t\"\\0\\u8ec0\\0\\u8f00\\0\\u8f40\\0\\u8f80\\0\\u8fc0\\0\\u9000\\0\\u9040\\0\\u9080\" +\n\t\t\t\t\t\"\\0\\u90c0\\0\\u9100\\0\\u9140\\0\\u9180\\0\\u91c0\\0\\u9200\\0\\u9240\\0\\u9280\" +\n\t\t\t\t\t\"\\0\\u92c0\\0\\u9300\\0\\u9340\\0\\u9380\\0\\u93c0\\0\\u9400\\0\\u9440\\0\\u9480\" +\n\t\t\t\t\t\"\\0\\u94c0\\0\\u9500\\0\\u9540\\0\\u9580\\0\\u95c0\\0\\u9600\\0\\u9640\\0\\u9680\" +\n\t\t\t\t\t\"\\0\\u96c0\\0\\u9700\\0\\u9740\\0\\u9780\\0\\u97c0\\0\\u9800\\0\\u9840\\0\\u9880\" +\n\t\t\t\t\t\"\\0\\u98c0\\0\\u9900\\0\\u9940\\0\\u9980\\0\\u99c0\\0\\u9a00\\0\\u9a40\\0\\u9a80\" +\n\t\t\t\t\t\"\\0\\u9ac0\\0\\u9b00\\0\\u9b40\\0\\u9b80\\0\\u9bc0\\0\\u9c00\\0\\u9c40\\0\\u9c80\" +\n\t\t\t\t\t\"\\0\\u9cc0\\0\\u9d00\\0\\u9d40\\0\\u9d80\\0\\u9dc0\\0\\u9e00\\0\\u9e40\\0\\u9e80\" +\n\t\t\t\t\t\"\\0\\u9ec0\\0\\u9f00\\0\\u9f40\\0\\u9f80\\0\\u9fc0\\0\\ua000\\0\\ua040\\0\\ua080\" +\n\t\t\t\t\t\"\\0\\ua0c0\\0\\ua100\\0\\ua140\\0\\ua180\\0\\ua1c0\\0\\ua200\\0\\ua240\\0\\ua280\" +\n\t\t\t\t\t\"\\0\\ua2c0\\0\\ua300\\0\\ua340\\0\\ua380\\0\\ua3c0\\0\\ua400\\0\\ua440\\0\\ua480\" +\n\t\t\t\t\t\"\\0\\ua4c0\\0\\ua500\\0\\ua540\\0\\ua580\\0\\ua5c0\\0\\ua600\\0\\ua640\\0\\ua680\" +\n\t\t\t\t\t\"\\0\\ua6c0\\0\\ua700\\0\\ua740\\0\\ua780\\0\\ua7c0\\0\\ua800\\0\\ua840\\0\\ua880\" +\n\t\t\t\t\t\"\\0\\ua8c0\\0\\ua900\\0\\ua940\\0\\ua980\\0\\200\";\n\n\tprivate static int[] zzUnpackRowMap() {\n\t\tint[] result = new int[701];\n\t\tint offset = 0;\n\t\toffset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);\n\t\treturn result;\n\t}\n\n\tprivate static int zzUnpackRowMap(String packed, int offset, int[] result) {\n\t\tint i = 0; /* index in packed string */\n\t\tint j = offset; /* index in unpacked array */\n\t\tint l = packed.length();\n\t\twhile (i < l) {\n\t\t\tint high = packed.charAt(i++) << 16;\n\t\t\tresult[j++] = high | packed.charAt(i++);\n\t\t}\n\t\treturn j;\n\t}\n\n\t/**\n\t * The transition table of the DFA\n\t */\n\tprivate static final int[] ZZ_TRANS = zzUnpackTrans();\n\n\tprivate static final String ZZ_TRANS_PACKED_0 =\n\t\t\t\"\\1\\3\\2\\4\\1\\5\\1\\6\\1\\4\\1\\5\\1\\7\\1\\10\" +\n\t\t\t\t\t\"\\1\\3\\1\\11\\1\\12\\1\\13\\1\\14\\1\\5\\1\\3\\1\\15\" +\n\t\t\t\t\t\"\\1\\16\\1\\17\\1\\20\\1\\21\\1\\22\\1\\23\\1\\24\\1\\25\" +\n\t\t\t\t\t\"\\1\\26\\1\\27\\1\\30\\1\\31\\1\\3\\1\\32\\1\\31\\1\\3\" +\n\t\t\t\t\t\"\\1\\32\\1\\4\\1\\33\\1\\34\\1\\35\\1\\4\\1\\36\\1\\37\" +\n\t\t\t\t\t\"\\1\\40\\2\\41\\3\\32\\1\\42\\1\\43\\1\\44\\1\\45\\1\\46\" +\n\t\t\t\t\t\"\\1\\4\\1\\47\\2\\5\\1\\4\\2\\5\\3\\4\\1\\5\\1\\16\" +\n\t\t\t\t\t\"\\10\\50\\1\\51\\17\\50\\1\\52\\11\\50\\1\\53\\3\\50\\1\\54\" +\n\t\t\t\t\t\"\\31\\50\\101\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\1\\0\\3\\56\" +\n\t\t\t\t\t\"\\2\\5\\1\\56\\1\\5\\2\\0\\4\\56\\1\\0\\1\\5\\1\\0\" +\n\t\t\t\t\t\"\\1\\56\\1\\0\\1\\56\\1\\57\\1\\60\\1\\0\\6\\56\\2\\0\" +\n\t\t\t\t\t\"\\1\\56\\3\\0\\3\\56\\1\\0\\2\\56\\1\\0\\3\\56\\3\\0\" +\n\t\t\t\t\t\"\\7\\56\\2\\5\\1\\56\\2\\5\\3\\56\\1\\5\\1\\0\\3\\56\" +\n\t\t\t\t\t\"\\2\\5\\1\\56\\1\\5\\2\\0\\4\\56\\1\\0\\1\\5\\1\\0\" +\n\t\t\t\t\t\"\\1\\56\\1\\0\\1\\61\\1\\57\\1\\60\\1\\0\\6\\56\\2\\0\" +\n\t\t\t\t\t\"\\1\\56\\3\\0\\3\\56\\1\\0\\2\\56\\1\\0\\3\\56\\3\\0\" +\n\t\t\t\t\t\"\\7\\56\\2\\5\\1\\56\\2\\5\\3\\56\\1\\5\\1\\0\\7\\62\" +\n\t\t\t\t\t\"\\1\\63\\1\\64\\1\\65\\66\\62\\1\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\5\\4\\1\\66\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\67\\1\\0\\3\\4\\1\\70\" +\n\t\t\t\t\t\"\\2\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\4\\4\\1\\71\\13\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\72\\4\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\1\\0\\10\\14\\1\\73\\1\\74\\3\\14\\1\\75\" +\n\t\t\t\t\t\"\\62\\14\\21\\0\\1\\16\\55\\0\\1\\16\\1\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\4\\4\\1\\46\\13\\4\\4\\0\\2\\76\\1\\0\\1\\76\" +\n\t\t\t\t\t\"\\7\\0\\1\\76\\5\\0\\1\\77\\2\\0\\1\\100\\1\\101\\1\\102\" +\n\t\t\t\t\t\"\\1\\103\\1\\104\\7\\0\\1\\105\\1\\106\\13\\0\\1\\107\\4\\0\" +\n\t\t\t\t\t\"\\1\\110\\2\\76\\1\\0\\2\\76\\3\\0\\1\\76\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\1\\4\\1\\111\\1\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\57\\0\\1\\112\\22\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\1\\4\\1\\113\\4\\4\\6\\0\\1\\114\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\115\\1\\0\\5\\4\\1\\116\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\1\\117\\1\\120\\1\\4\\6\\0\\2\\4\\1\\121\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\1\\4\\1\\122\\1\\123\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\1\\4\\1\\124\\4\\4\\6\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\125\\1\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\126\\1\\127\\14\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\4\\4\\1\\130\\13\\4\\2\\0\\6\\4\\2\\0\\1\\55\\1\\131\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\132\\5\\4\\6\\0\\1\\133\\1\\134\\1\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\3\\4\\1\\127\\1\\4\\1\\135\\12\\4\" +\n\t\t\t\t\t\"\\2\\0\\2\\4\\2\\136\\1\\4\\1\\136\\2\\0\\1\\55\\1\\137\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\1\\136\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\140\\1\\4\\1\\141\\2\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\7\\4\\2\\136\\1\\4\\2\\136\" +\n\t\t\t\t\t\"\\3\\4\\1\\136\\2\\0\\6\\4\\2\\0\\1\\55\\1\\4\\1\\142\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\143\\3\\4\\6\\0\\1\\4\\1\\125\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\3\\4\\1\\127\\14\\4\\27\\0\" +\n\t\t\t\t\t\"\\1\\144\\2\\0\\1\\145\\11\\0\\1\\146\\14\\0\\1\\147\\1\\0\" +\n\t\t\t\t\t\"\\1\\150\\16\\0\\6\\36\\2\\0\\1\\55\\3\\36\\1\\0\\1\\36\" +\n\t\t\t\t\t\"\\3\\0\\1\\36\\1\\0\\1\\36\\1\\0\\6\\36\\5\\0\\1\\151\" +\n\t\t\t\t\t\"\\3\\36\\1\\0\\2\\36\\1\\152\\3\\36\\3\\0\\20\\36\\26\\0\" +\n\t\t\t\t\t\"\\1\\153\\53\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\36\\1\\152\\1\\4\\2\\154\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\2\\0\\2\\4\\2\\136\\1\\4\\1\\136\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\136\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\155\\2\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\4\\4\\1\\156\\2\\4\\2\\136\\1\\4\\2\\136\\3\\4\" +\n\t\t\t\t\t\"\\1\\136\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\1\\157\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\4\\4\\1\\160\" +\n\t\t\t\t\t\"\\1\\4\\1\\161\\11\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\162\\1\\0\\6\\4\" +\n\t\t\t\t\t\"\\6\\0\\2\\4\\1\\163\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\4\\4\\1\\164\\13\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\" +\n\t\t\t\t\t\"\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\4\\4\" +\n\t\t\t\t\t\"\\1\\165\\13\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\1\\4\\1\\166\" +\n\t\t\t\t\t\"\\4\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\1\\167\\2\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\4\\4\\1\\170\" +\n\t\t\t\t\t\"\\13\\4\\1\\0\\10\\50\\1\\0\\17\\50\\1\\0\\11\\50\\1\\0\" +\n\t\t\t\t\t\"\\3\\50\\1\\0\\31\\50\\26\\0\\1\\171\\15\\0\\1\\172\\61\\0\" +\n\t\t\t\t\t\"\\1\\173\\117\\0\\1\\174\\43\\0\\1\\175\\65\\0\\7\\56\\2\\0\" +\n\t\t\t\t\t\"\\4\\56\\1\\0\\1\\56\\1\\0\\1\\56\\1\\0\\1\\56\\1\\0\" +\n\t\t\t\t\t\"\\1\\56\\1\\0\\6\\56\\2\\0\\1\\56\\3\\0\\3\\56\\1\\0\" +\n\t\t\t\t\t\"\\2\\56\\1\\0\\3\\56\\3\\0\\20\\56\\4\\0\\2\\76\\1\\0\" +\n\t\t\t\t\t\"\\1\\76\\7\\0\\1\\76\\47\\0\\2\\76\\1\\0\\2\\76\\3\\0\" +\n\t\t\t\t\t\"\\1\\76\\1\\0\\3\\56\\2\\176\\1\\56\\1\\176\\2\\0\\4\\56\" +\n\t\t\t\t\t\"\\1\\0\\1\\176\\1\\0\\1\\56\\1\\0\\1\\56\\1\\0\\1\\56\" +\n\t\t\t\t\t\"\\1\\177\\6\\56\\2\\0\\1\\56\\1\\0\\1\\177\\1\\0\\3\\56\" +\n\t\t\t\t\t\"\\1\\0\\2\\56\\1\\0\\3\\56\\3\\0\\7\\56\\2\\176\\1\\56\" +\n\t\t\t\t\t\"\\2\\176\\3\\56\\1\\176\\1\\0\\3\\56\\4\\200\\2\\0\\3\\56\" +\n\t\t\t\t\t\"\\1\\200\\1\\0\\1\\200\\1\\0\\1\\56\\1\\0\\1\\56\\1\\0\" +\n\t\t\t\t\t\"\\1\\200\\1\\0\\2\\56\\2\\200\\2\\56\\2\\0\\1\\56\\3\\0\" +\n\t\t\t\t\t\"\\3\\56\\1\\0\\2\\56\\1\\0\\2\\56\\1\\200\\3\\0\\1\\56\" +\n\t\t\t\t\t\"\\2\\200\\4\\56\\2\\200\\1\\56\\2\\200\\3\\56\\1\\200\\1\\0\" +\n\t\t\t\t\t\"\\7\\201\\1\\202\\1\\0\\67\\201\\7\\0\\1\\202\\70\\0\\4\\201\" +\n\t\t\t\t\t\"\\1\\203\\1\\201\\1\\204\\1\\205\\1\\0\\1\\62\\1\\206\\3\\62\" +\n\t\t\t\t\t\"\\1\\203\\7\\201\\3\\62\\35\\201\\1\\203\\1\\204\\1\\201\\1\\204\" +\n\t\t\t\t\t\"\\1\\203\\5\\201\\1\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\" +\n\t\t\t\t\t\"\\1\\207\\2\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\210\\1\\4\\1\\0\\3\\4\\3\\0\\3\\4\\1\\211\\14\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\1\\212\\5\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\213\\5\\4\\6\\0\\1\\4\\1\\214\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\6\\4\\6\\0\\2\\4\\1\\215\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\1\\0\\11\\73\\1\\216\\3\\73\" +\n\t\t\t\t\t\"\\1\\217\\66\\73\\1\\14\\1\\73\\2\\14\\1\\0\\1\\14\\1\\220\" +\n\t\t\t\t\t\"\\4\\14\\7\\73\\3\\14\\35\\73\\2\\14\\1\\73\\2\\14\\5\\73\" +\n\t\t\t\t\t\"\\3\\56\\2\\76\\1\\56\\1\\76\\2\\0\\4\\56\\1\\0\\1\\76\" +\n\t\t\t\t\t\"\\1\\0\\1\\56\\1\\0\\1\\56\\1\\0\\1\\60\\1\\0\\6\\56\" +\n\t\t\t\t\t\"\\2\\0\\1\\56\\3\\0\\3\\56\\1\\0\\2\\56\\1\\0\\3\\56\" +\n\t\t\t\t\t\"\\3\\0\\7\\56\\2\\76\\1\\56\\2\\76\\3\\56\\1\\76\\14\\0\" +\n\t\t\t\t\t\"\\1\\221\\110\\0\\1\\222\\117\\0\\1\\223\\46\\0\\1\\224\\13\\0\" +\n\t\t\t\t\t\"\\1\\225\\114\\0\\1\\226\\16\\0\\1\\227\\26\\0\\1\\230\\30\\0\" +\n\t\t\t\t\t\"\\1\\231\\17\\0\\1\\232\\43\\0\\1\\233\\1\\0\\1\\234\\133\\0\" +\n\t\t\t\t\t\"\\1\\235\\43\\0\\1\\236\\1\\237\\71\\0\\1\\240\\54\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\1\\241\\2\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\2\\112\\2\\0\\1\\112\" +\n\t\t\t\t\t\"\\4\\0\\3\\112\\5\\0\\1\\112\\1\\0\\1\\112\\1\\0\\6\\112\" +\n\t\t\t\t\t\"\\6\\0\\3\\112\\1\\0\\2\\112\\1\\0\\3\\112\\1\\0\\11\\112\" +\n\t\t\t\t\t\"\\2\\0\\1\\112\\2\\0\\3\\112\\3\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\1\\242\\2\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\1\\243\\2\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\244\\4\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\1\\245\" +\n\t\t\t\t\t\"\\5\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\6\\4\\1\\246\\11\\4\\2\\0\\6\\4\\2\\0\\1\\55\\1\\247\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\4\\4\\1\\250\\1\\4\" +\n\t\t\t\t\t\"\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\4\\4\\1\\251\\13\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\1\\4\\1\\252\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\4\\4\\1\\253\\1\\4\" +\n\t\t\t\t\t\"\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\1\\4\\1\\254\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\2\\4\\1\\166\" +\n\t\t\t\t\t\"\\15\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\5\\4\\1\\255\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\1\\4\\1\\256\\4\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\1\\257\\2\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\246\\15\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\257\\1\\0\\6\\4\" +\n\t\t\t\t\t\"\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\1\\4\\1\\260\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\2\\4\\1\\261\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\262\\1\\4\\1\\263\\2\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\264\\2\\4\\1\\264\\1\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\1\\257\\2\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\3\\4\\1\\265\\2\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\1\\4\\1\\266\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\2\\4\" +\n\t\t\t\t\t\"\\2\\136\\1\\4\\1\\136\\2\\0\\1\\55\\3\\4\\1\\0\\1\\136\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\7\\4\\2\\136\\1\\4\" +\n\t\t\t\t\t\"\\2\\136\\3\\4\\1\\136\\2\\0\\6\\4\\2\\0\\1\\55\\2\\4\" +\n\t\t\t\t\t\"\\1\\267\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\270\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\4\\4\\1\\271\" +\n\t\t\t\t\t\"\\13\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\1\\4\\1\\272\\16\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\1\\273\\4\\4\\1\\274\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\1\\275\\17\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\276\\6\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\30\\0\\1\\277\\77\\0\" +\n\t\t\t\t\t\"\\1\\300\\103\\0\\1\\301\\75\\0\\1\\302\\31\\0\\1\\303\\77\\0\" +\n\t\t\t\t\t\"\\1\\304\\15\\0\\6\\151\\3\\0\\3\\151\\1\\0\\1\\151\\3\\0\" +\n\t\t\t\t\t\"\\1\\151\\1\\0\\1\\151\\1\\0\\6\\151\\5\\0\\4\\151\\1\\0\" +\n\t\t\t\t\t\"\\2\\151\\1\\152\\3\\151\\3\\0\\20\\151\\57\\0\\1\\32\\22\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\1\\4\\1\\305\\4\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\4\\4\\1\\306\\1\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\307\" +\n\t\t\t\t\t\"\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\1\\4\\1\\310\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\311\\1\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\1\\4\\1\\312\\16\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\1\\246\\17\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\1\\313\\2\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\1\\314\" +\n\t\t\t\t\t\"\\5\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\315\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\4\\4\\1\\246\\1\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\1\\4\\1\\316\\1\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\1\\317\\17\\4\\44\\0\\1\\320\\66\\0\" +\n\t\t\t\t\t\"\\1\\321\\73\\0\\1\\322\\117\\0\\1\\323\\34\\0\\4\\324\\5\\0\" +\n\t\t\t\t\t\"\\1\\324\\1\\0\\1\\324\\5\\0\\1\\324\\3\\0\\2\\324\\21\\0\" +\n\t\t\t\t\t\"\\1\\324\\4\\0\\2\\324\\4\\0\\2\\324\\1\\0\\2\\324\\3\\0\" +\n\t\t\t\t\t\"\\1\\324\\1\\0\\3\\56\\2\\176\\1\\56\\1\\176\\2\\0\\4\\56\" +\n\t\t\t\t\t\"\\1\\0\\1\\176\\1\\0\\1\\56\\1\\0\\1\\56\\1\\0\\1\\56\" +\n\t\t\t\t\t\"\\1\\0\\6\\56\\2\\0\\1\\56\\3\\0\\3\\56\\1\\0\\2\\56\" +\n\t\t\t\t\t\"\\1\\0\\3\\56\\3\\0\\7\\56\\2\\176\\1\\56\\2\\176\\3\\56\" +\n\t\t\t\t\t\"\\1\\176\\4\\0\\2\\176\\1\\0\\1\\176\\7\\0\\1\\176\\47\\0\" +\n\t\t\t\t\t\"\\2\\176\\1\\0\\2\\176\\3\\0\\1\\176\\1\\0\\7\\201\\1\\63\" +\n\t\t\t\t\t\"\\1\\0\\73\\201\\1\\204\\1\\201\\1\\204\\1\\202\\1\\0\\5\\201\" +\n\t\t\t\t\t\"\\1\\204\\47\\201\\2\\204\\1\\201\\2\\204\\11\\201\\1\\62\\1\\201\" +\n\t\t\t\t\t\"\\1\\62\\1\\202\\1\\0\\5\\201\\1\\62\\47\\201\\2\\62\\1\\201\" +\n\t\t\t\t\t\"\\2\\62\\10\\201\\4\\325\\1\\63\\1\\0\\3\\201\\1\\325\\1\\201\" +\n\t\t\t\t\t\"\\1\\325\\5\\201\\1\\325\\3\\201\\2\\325\\21\\201\\1\\325\\4\\201\" +\n\t\t\t\t\t\"\\2\\325\\4\\201\\2\\325\\1\\201\\2\\325\\3\\201\\1\\325\\1\\201\" +\n\t\t\t\t\t\"\\1\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\1\\4\\1\\264\\4\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\326\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\327\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\330\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\331\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\332\\15\\4\\1\\0\\10\\73\\1\\0\\72\\73\\4\\333\" +\n\t\t\t\t\t\"\\2\\73\\1\\216\\2\\73\\1\\333\\1\\217\\1\\333\\5\\73\\1\\333\" +\n\t\t\t\t\t\"\\3\\73\\2\\333\\21\\73\\1\\333\\4\\73\\2\\333\\4\\73\\2\\333\" +\n\t\t\t\t\t\"\\1\\73\\2\\333\\3\\73\\1\\333\\1\\73\\61\\0\\1\\334\\51\\0\" +\n\t\t\t\t\t\"\\1\\335\\26\\0\\1\\336\\41\\0\\1\\337\\66\\0\\1\\340\\113\\0\" +\n\t\t\t\t\t\"\\1\\341\\63\\0\\1\\342\\144\\0\\1\\343\\62\\0\\1\\344\\65\\0\" +\n\t\t\t\t\t\"\\1\\345\\60\\0\\1\\346\\150\\0\\1\\347\\43\\0\\1\\350\\30\\0\" +\n\t\t\t\t\t\"\\1\\351\\62\\0\\1\\352\\62\\0\\1\\353\\102\\0\\1\\354\\74\\0\" +\n\t\t\t\t\t\"\\1\\355\\52\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\6\\4\\1\\356\\11\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\357\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\1\\4\\1\\360\\1\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\4\\4\\1\\361\\13\\4\\2\\0\\6\\4\\2\\0\\1\\55\\1\\362\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\363\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\2\\4\\1\\364\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\5\\4\\1\\242\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\3\\4\\1\\365\" +\n\t\t\t\t\t\"\\2\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\3\\4\\1\\366\\2\\4\" +\n\t\t\t\t\t\"\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\4\\4\\1\\367\\1\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\4\\4\\1\\370\\13\\4\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\371\\5\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\372\\2\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\1\\373\" +\n\t\t\t\t\t\"\\5\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\3\\4\\1\\374\\14\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\375\\6\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\6\\4\\6\\0\\2\\4\\1\\376\\1\\0\\2\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\1\\377\" +\n\t\t\t\t\t\"\\5\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\u0100\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\u0101\\4\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\u0102\\5\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\4\\4\" +\n\t\t\t\t\t\"\\1\\377\\1\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\1\\u0103\\17\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\1\\u0104\\5\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\3\\0\\14\\4\\1\\u0105\\3\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\u0106\" +\n\t\t\t\t\t\"\\1\\u0107\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\1\\u0108\\5\\4\" +\n\t\t\t\t\t\"\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\4\\4\\1\\u0109\\13\\4\\14\\0\" +\n\t\t\t\t\t\"\\1\\u010a\\10\\0\\1\\u010b\\5\\0\\1\\u010c\\27\\0\\1\\u010c\\101\\0\" +\n\t\t\t\t\t\"\\1\\u010d\\42\\0\\1\\u010e\\116\\0\\1\\u010f\\57\\0\\1\\u0110\\64\\0\" +\n\t\t\t\t\t\"\\1\\u0111\\112\\0\\1\\u0112\\52\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\u0113\\2\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\3\\4\\1\\u0114\" +\n\t\t\t\t\t\"\\2\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\1\\4\\1\\u0115\\16\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\5\\4\\1\\u0116\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\u0117\\4\\4\\1\\u0118\\1\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\3\\4\\1\\u0118\\14\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\4\\4\\1\\u0119\\1\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\2\\4\\1\\u011a\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\4\\4\\1\\u011b\\13\\4\\33\\0\\1\\u011c\\11\\0\\1\\u011d\\34\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\2\\4\\1\\u011e\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\u011f\" +\n\t\t\t\t\t\"\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\46\\0\\1\\u0120\\56\\0\\1\\320\\116\\0\\1\\u0121\" +\n\t\t\t\t\t\"\\57\\0\\1\\u0122\\57\\0\\4\\u0123\\5\\0\\1\\u0123\\1\\0\\1\\u0123\" +\n\t\t\t\t\t\"\\5\\0\\1\\u0123\\3\\0\\2\\u0123\\21\\0\\1\\u0123\\4\\0\\2\\u0123\" +\n\t\t\t\t\t\"\\4\\0\\2\\u0123\\1\\0\\2\\u0123\\3\\0\\1\\u0123\\1\\0\\3\\201\" +\n\t\t\t\t\t\"\\4\\u0124\\1\\63\\1\\0\\3\\201\\1\\u0124\\1\\201\\1\\u0124\\5\\201\" +\n\t\t\t\t\t\"\\1\\u0124\\3\\201\\2\\u0124\\21\\201\\1\\u0124\\4\\201\\2\\u0124\\4\\201\" +\n\t\t\t\t\t\"\\2\\u0124\\1\\201\\2\\u0124\\3\\201\\1\\u0124\\1\\201\\31\\0\\1\\u0125\" +\n\t\t\t\t\t\"\\12\\0\\1\\u0126\\63\\0\\1\\u0127\\1\\0\\1\\u0128\\11\\0\\1\\u0129\" +\n\t\t\t\t\t\"\\14\\0\\1\\u012a\\17\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\1\\u012b\\17\\4\" +\n\t\t\t\t\t\"\\33\\0\\1\\u0128\\11\\0\\1\\u0129\\34\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\u012b\\14\\4\\1\\0\\3\\73\\4\\u012c\\2\\73\\1\\216\" +\n\t\t\t\t\t\"\\2\\73\\1\\u012c\\1\\217\\1\\u012c\\5\\73\\1\\u012c\\3\\73\\2\\u012c\" +\n\t\t\t\t\t\"\\21\\73\\1\\u012c\\4\\73\\2\\u012c\\4\\73\\2\\u012c\\1\\73\\2\\u012c\" +\n\t\t\t\t\t\"\\3\\73\\1\\u012c\\1\\73\\77\\0\\1\\u012d\\26\\0\\1\\u012e\\115\\0\" +\n\t\t\t\t\t\"\\1\\u012f\\65\\0\\1\\u0130\\130\\0\\1\\u0131\\45\\0\\1\\u0132\\72\\0\" +\n\t\t\t\t\t\"\\1\\u0133\\104\\0\\1\\u0134\\72\\0\\1\\u0135\\102\\0\\1\\u0136\\77\\0\" +\n\t\t\t\t\t\"\\1\\u0137\\102\\0\\1\\u0138\\76\\0\\1\\u0139\\141\\0\\1\\u013a\\36\\0\" +\n\t\t\t\t\t\"\\1\\u013b\\125\\0\\1\\u013c\\52\\0\\1\\u013d\\106\\0\\1\\u013e\\36\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\5\\4\\1\\u013f\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\1\\214\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\u0140\\4\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\31\\0\\1\\u0141\\1\\0\\1\\u011c\\11\\0\\1\\u011d\" +\n\t\t\t\t\t\"\\14\\0\\1\\u0142\\17\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\u0143\\6\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\1\\u0144\\5\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\4\\4\\1\\356\\1\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\u0145\\1\\u0146\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\1\\u0147\\5\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\1\\4\\1\\u0148\\4\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\3\\0\\5\\4\\1\\u0149\\12\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\u014a\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\u014b\\6\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\31\\0\" +\n\t\t\t\t\t\"\\1\\u0141\\1\\0\\1\\u011c\\11\\0\\1\\u014c\\14\\0\\1\\u0142\\17\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\1\\4\\1\\u014d\\16\\4\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\6\\4\\6\\0\\2\\4\\1\\u014e\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\33\\0\\1\\u011c\\11\\0\\1\\u014f\" +\n\t\t\t\t\t\"\\34\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\5\\4\\1\\u0150\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\6\\4\\6\\0\\1\\u0151\\2\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\u0152\\2\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\u0153\\1\\0\\6\\4\" +\n\t\t\t\t\t\"\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\u0154\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\u0155\\4\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\27\\0\\1\\u0156\\52\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\3\\4\\1\\u0157\\2\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\14\\4\\1\\u0158\\3\\4\\25\\0\\1\\u0159\\147\\0\\1\\u0159\" +\n\t\t\t\t\t\"\\27\\0\\1\\u0159\\1\\0\\1\\u0159\\53\\0\\1\\u015a\\126\\0\\1\\u015b\" +\n\t\t\t\t\t\"\\112\\0\\1\\u015c\\113\\0\\1\\u015d\\100\\0\\1\\u015e\\101\\0\\1\\u015e\" +\n\t\t\t\t\t\"\\15\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\1\\4\\1\\u015f\\4\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\1\\u0160\\5\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\14\\4\\1\\u0161\\3\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\u0162\\5\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\33\\0\\1\\u0128\\46\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\u0163\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\3\\4\\1\\u0164\\2\\4\" +\n\t\t\t\t\t\"\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\4\\4\\1\\u0165\\1\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\6\\4\\5\\0\\1\\u0166\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\64\\0\\1\\u0167\\27\\0\" +\n\t\t\t\t\t\"\\1\\u0168\\65\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\1\\u0169\\5\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\u016a\\6\\4\\5\\0\\1\\u016b\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\42\\0\\1\\u016c\\71\\0\" +\n\t\t\t\t\t\"\\1\\320\\11\\0\\1\\u0120\\33\\0\\1\\u0122\\1\\u016d\\4\\u0122\\1\\u016d\" +\n\t\t\t\t\t\"\\2\\0\\3\\u0122\\1\\0\\1\\u0122\\1\\0\\1\\u016d\\1\\0\\1\\u0122\" +\n\t\t\t\t\t\"\\1\\u016d\\1\\u0122\\1\\u016d\\6\\u0122\\1\\0\\4\\u016d\\4\\u0122\\1\\u016d\" +\n\t\t\t\t\t\"\\2\\u0122\\1\\u016d\\3\\u0122\\1\\u016d\\2\\0\\20\\u0122\\4\\0\\4\\u016e\" +\n\t\t\t\t\t\"\\5\\0\\1\\u016e\\1\\0\\1\\u016e\\5\\0\\1\\u016e\\3\\0\\2\\u016e\" +\n\t\t\t\t\t\"\\21\\0\\1\\u016e\\4\\0\\2\\u016e\\4\\0\\2\\u016e\\1\\0\\2\\u016e\" +\n\t\t\t\t\t\"\\3\\0\\1\\u016e\\1\\0\\3\\201\\4\\u016f\\1\\63\\1\\0\\3\\201\" +\n\t\t\t\t\t\"\\1\\u016f\\1\\201\\1\\u016f\\5\\201\\1\\u016f\\3\\201\\2\\u016f\\21\\201\" +\n\t\t\t\t\t\"\\1\\u016f\\4\\201\\2\\u016f\\4\\201\\2\\u016f\\1\\201\\2\\u016f\\3\\201\" +\n\t\t\t\t\t\"\\1\\u016f\\1\\201\\27\\0\\1\\u0170\\63\\0\\1\\u0171\\116\\0\\1\\u0172\" +\n\t\t\t\t\t\"\\130\\0\\1\\u0173\\27\\0\\1\\u0174\\147\\0\\1\\u0175\\15\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\356\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\3\\0\\20\\4\\1\\0\\3\\73\\4\\u0176\\2\\73\\1\\216\" +\n\t\t\t\t\t\"\\2\\73\\1\\u0176\\1\\217\\1\\u0176\\5\\73\\1\\u0176\\3\\73\\2\\u0176\" +\n\t\t\t\t\t\"\\21\\73\\1\\u0176\\4\\73\\2\\u0176\\4\\73\\2\\u0176\\1\\73\\2\\u0176\" +\n\t\t\t\t\t\"\\3\\73\\1\\u0176\\1\\73\\30\\0\\1\\101\\1\\102\\1\\u0177\\1\\u0178\" +\n\t\t\t\t\t\"\\7\\0\\1\\u0179\\21\\0\\1\\110\\43\\0\\1\\u017a\\101\\0\\1\\u017b\" +\n\t\t\t\t\t\"\\125\\0\\1\\u0133\\44\\0\\1\\u017c\\135\\0\\1\\u017d\\45\\0\\1\\u017e\" +\n\t\t\t\t\t\"\\74\\0\\1\\u0133\\103\\0\\1\\u017f\\124\\0\\1\\342\\102\\0\\1\\u0180\" +\n\t\t\t\t\t\"\\101\\0\\1\\u0133\\36\\0\\1\\u0181\\77\\0\\1\\u0182\\115\\0\\1\\u0183\" +\n\t\t\t\t\t\"\\70\\0\\1\\u0133\\127\\0\\1\\u0130\\15\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\2\\4\\1\\u0184\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\1\\4\\1\\u0185\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\33\\0\\1\\u0186\\130\\0\\1\\u0187\\60\\0\\1\\u0188\\34\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\u0189\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\u018a\\15\\4\\32\\0\\1\\u018b\\47\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\1\\u018c\\2\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\u018d\\2\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\u018e\\6\\4\" +\n\t\t\t\t\t\"\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\15\\0\\1\\u018f\\16\\0\\1\\u0190\\12\\0\\1\\u0191\\11\\0\\1\\u0192\" +\n\t\t\t\t\t\"\\2\\0\\1\\u0193\\42\\0\\1\\u0194\\64\\0\\1\\u0195\\65\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\u0196\\5\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\356\\16\\4\\14\\0\\1\\u0197\\65\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\u0198\" +\n\t\t\t\t\t\"\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\263\\1\\0\\6\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\1\\u012b\\5\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\u0199\\16\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\u0198\\15\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\u019a\\3\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\64\\0\\1\\u019b\\15\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\1\\4\\1\\u019c\\1\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\u019d\\1\\0\\6\\4\" +\n\t\t\t\t\t\"\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\76\\0\\1\\u019e\\26\\0\\1\\303\\6\\0\\1\\u019f\\130\\0\\1\\u015e\" +\n\t\t\t\t\t\"\\41\\0\\1\\u01a0\\113\\0\\1\\u01a1\\37\\0\\1\\u01a2\\76\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\3\\0\\3\\4\\1\\u01a3\\14\\4\\2\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\6\\4\\6\\0\\2\\4\\1\\u01a4\\1\\0\\2\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\u01a5\\6\\4\" +\n\t\t\t\t\t\"\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\u01a6\\1\\4\\1\\u01a7\\4\\4\\5\\0\" +\n\t\t\t\t\t\"\\1\\u01a8\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\31\\0\\1\\u0127\\30\\0\\1\\u012a\\17\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\u01a9\\4\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\u01aa\\1\\0\\6\\4\" +\n\t\t\t\t\t\"\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\67\\0\\1\\u01ab\\3\\0\\1\\u01ac\\20\\0\\1\\u01ad\\112\\0\\1\\u01ae\" +\n\t\t\t\t\t\"\\52\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\4\\4\\1\\u01af\\13\\4\\25\\0\" +\n\t\t\t\t\t\"\\1\\u01b0\\2\\0\\1\\u01b1\\16\\0\\1\\u01b2\\14\\0\\1\\u01b3\\44\\0\" +\n\t\t\t\t\t\"\\1\\u01b4\\35\\0\\1\\u01ab\\52\\0\\1\\u0122\\41\\0\\4\\4\\5\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\5\\0\\1\\4\\3\\0\\2\\4\\21\\0\" +\n\t\t\t\t\t\"\\1\\4\\4\\0\\2\\4\\4\\0\\2\\4\\1\\0\\2\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\3\\201\\4\\62\\1\\63\\1\\0\\3\\201\\1\\62\" +\n\t\t\t\t\t\"\\1\\201\\1\\62\\5\\201\\1\\62\\3\\201\\2\\62\\21\\201\\1\\62\" +\n\t\t\t\t\t\"\\4\\201\\2\\62\\4\\201\\2\\62\\1\\201\\2\\62\\3\\201\\1\\62\" +\n\t\t\t\t\t\"\\1\\201\\27\\0\\1\\u01b5\\103\\0\\1\\u01b6\\127\\0\\1\\u01b7\\27\\0\" +\n\t\t\t\t\t\"\\1\\u01b8\\112\\0\\1\\u019e\\63\\0\\1\\u01b9\\65\\0\\3\\73\\4\\14\" +\n\t\t\t\t\t\"\\2\\73\\1\\216\\2\\73\\1\\14\\1\\217\\1\\14\\5\\73\\1\\14\" +\n\t\t\t\t\t\"\\3\\73\\2\\14\\21\\73\\1\\14\\4\\73\\2\\14\\4\\73\\2\\14\" +\n\t\t\t\t\t\"\\1\\73\\2\\14\\3\\73\\1\\14\\1\\73\\63\\0\\1\\u01ba\\57\\0\" +\n\t\t\t\t\t\"\\1\\231\\65\\0\\1\\234\\75\\0\\1\\u01bb\\76\\0\\1\\u01bc\\102\\0\" +\n\t\t\t\t\t\"\\1\\u01bd\\73\\0\\1\\u01be\\76\\0\\1\\u01bf\\135\\0\\1\\u01c0\\76\\0\" +\n\t\t\t\t\t\"\\1\\u01bf\\103\\0\\1\\u01c1\\43\\0\\1\\u01c2\\47\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\u01c3\" +\n\t\t\t\t\t\"\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\u01c4\\6\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\64\\0\" +\n\t\t\t\t\t\"\\1\\u01c5\\26\\0\\1\\u01c6\\100\\0\\1\\u01c7\\112\\0\\1\\u01c8\\52\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\u01c9\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\30\\0\\1\\u01ca\\51\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\u01cb\\5\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\u01cc\\16\\4\\33\\0\\1\\u01cd\\130\\0\\1\\u01ce\\1\\u01cf\" +\n\t\t\t\t\t\"\\55\\0\\1\\u01d0\\101\\0\\1\\u01d1\\75\\0\\1\\u01d2\\51\\0\\1\\u01d3\" +\n\t\t\t\t\t\"\\146\\0\\1\\u01d4\\42\\0\\1\\u01d5\\52\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\u01d6\\3\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\" +\n\t\t\t\t\t\"\\3\\4\\3\\0\\20\\4\\27\\0\\1\\u01d7\\52\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\u01d8\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\1\\u01d9\\5\\4\" +\n\t\t\t\t\t\"\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\3\\4\\1\\u01da\\2\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\26\\0\" +\n\t\t\t\t\t\"\\1\\u01db\\53\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\1\\4\\1\\u01dc\\16\\4\" +\n\t\t\t\t\t\"\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\u01dd\\6\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\27\\0\\1\\u01de\\131\\0\" +\n\t\t\t\t\t\"\\1\\u01df\\21\\0\\1\\u01a2\\26\\0\\1\\u01e0\\47\\0\\6\\u01a2\\3\\0\" +\n\t\t\t\t\t\"\\3\\u01a2\\1\\0\\1\\u01a2\\3\\0\\1\\u01a2\\1\\0\\1\\u01a2\\1\\0\" +\n\t\t\t\t\t\"\\6\\u01a2\\6\\0\\3\\u01a2\\1\\0\\2\\u01a2\\1\\0\\3\\u01a2\\3\\0\" +\n\t\t\t\t\t\"\\20\\u01a2\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\5\\4\\1\\356\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\2\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\0\\4\\4\\1\\u012b\\1\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\61\\0\\1\\u01e1\" +\n\t\t\t\t\t\"\\52\\0\\1\\u01e2\\12\\0\\1\\u01e3\\11\\0\\1\\u01e4\\4\\0\\1\\u01e5\" +\n\t\t\t\t\t\"\\13\\0\\6\\4\\2\\0\\1\\55\\1\\u01e6\\2\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\43\\0\\1\\u01e7\" +\n\t\t\t\t\t\"\\23\\0\\1\\u01ab\\2\\0\\1\\u019e\\7\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\u01e8\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\u01e9\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\70\\0\\1\\u019e\" +\n\t\t\t\t\t\"\\26\\0\\1\\u019e\\143\\0\\1\\u01d5\\56\\0\\1\\u01ea\\37\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\u01eb\\4\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\23\\0\\1\\u01ec\\101\\0\" +\n\t\t\t\t\t\"\\1\\u01ed\\117\\0\\1\\u01ee\\47\\0\\1\\u01ef\\112\\0\\1\\u01f0\\101\\0\" +\n\t\t\t\t\t\"\\1\\u01f1\\74\\0\\1\\u01f2\\102\\0\\1\\u0174\\130\\0\\1\\u019e\\31\\0\" +\n\t\t\t\t\t\"\\1\\u01f3\\143\\0\\1\\u01f4\\45\\0\\1\\u01f5\\75\\0\\1\\u01f6\\101\\0\" +\n\t\t\t\t\t\"\\1\\u01f7\\132\\0\\1\\u01f8\\43\\0\\1\\u01f9\\64\\0\\1\\342\\111\\0\" +\n\t\t\t\t\t\"\\1\\u01fa\\105\\0\\1\\u01fb\\46\\0\\6\\4\\2\\0\\1\\55\\1\\4\" +\n\t\t\t\t\t\"\\1\\u01cc\\1\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\47\\0\\1\\u0191\\10\\0\\1\\u01fc\\3\\0\\1\\u0193\" +\n\t\t\t\t\t\"\\45\\0\\1\\u0195\\62\\0\\1\\u01fd\\111\\0\\1\\u01fe\\134\\0\\1\\u01ff\" +\n\t\t\t\t\t\"\\27\\0\\1\\u0200\\113\\0\\1\\u0201\\51\\0\\6\\4\\2\\0\\1\\55\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\6\\4\\6\\0\\2\\4\\1\\u0202\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\1\\356\\5\\4\" +\n\t\t\t\t\t\"\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\25\\0\\1\\u0203\\136\\0\\1\\u0204\\42\\0\\1\\u0205\\134\\0\\1\\u0206\" +\n\t\t\t\t\t\"\\75\\0\\1\\u0205\\47\\0\\1\\u0207\\136\\0\\1\\u0208\\34\\0\\1\\u0209\" +\n\t\t\t\t\t\"\\113\\0\\1\\u020a\\37\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\356\\1\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\42\\0\\1\\u020b\\71\\0\\1\\u020c\\45\\0\\6\\4\\2\\0\" +\n\t\t\t\t\t\"\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\u020d\" +\n\t\t\t\t\t\"\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\" +\n\t\t\t\t\t\"\\3\\0\\20\\4\\2\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\1\\4\\1\\u012b\" +\n\t\t\t\t\t\"\\16\\4\\15\\0\\1\\u020e\\13\\0\\1\\u0127\\1\\0\\1\\u0128\\1\\u0190\" +\n\t\t\t\t\t\"\\24\\0\\1\\u0192\\1\\u012a\\17\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\u020f\\1\\0\\6\\4\" +\n\t\t\t\t\t\"\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\34\\0\\1\\u0210\\7\\0\\1\\u0211\\1\\u0212\\12\\0\\1\\u0213\\1\\u0214\" +\n\t\t\t\t\t\"\\1\\u0215\\47\\0\\1\\u0216\\110\\0\\1\\u015e\\67\\0\\1\\u0217\\76\\0\" +\n\t\t\t\t\t\"\\1\\u0218\\74\\0\\1\\u0219\\115\\0\\1\\u021a\\65\\0\\1\\u021b\\71\\0\" +\n\t\t\t\t\t\"\\1\\u021c\\54\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\1\\4\\1\\u021d\\16\\4\" +\n\t\t\t\t\t\"\\45\\0\\1\\u021e\\34\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\2\\4\\1\\u021f\" +\n\t\t\t\t\t\"\\15\\4\\27\\0\\1\\u0220\\67\\0\\1\\u0221\\13\\0\\1\\u0222\\46\\0\" +\n\t\t\t\t\t\"\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\1\\u0223\\6\\4\\6\\0\\3\\4\\1\\0\\2\\4\" +\n\t\t\t\t\t\"\\1\\0\\3\\4\\3\\0\\20\\4\\61\\0\\1\\u0224\\52\\0\\1\\u0225\" +\n\t\t\t\t\t\"\\125\\0\\1\\u0226\\106\\0\\1\\u0227\\72\\0\\1\\u0228\\100\\0\\1\\u019e\" +\n\t\t\t\t\t\"\\44\\0\\1\\u0229\\100\\0\\1\\u0205\\76\\0\\1\\u01fb\\145\\0\\1\\u022a\" +\n\t\t\t\t\t\"\\27\\0\\1\\u013d\\114\\0\\1\\u022b\\64\\0\\1\\u022c\\101\\0\\1\\u022d\" +\n\t\t\t\t\t\"\\57\\0\\1\\u022e\\116\\0\\1\\u0133\\130\\0\\1\\u022f\\46\\0\\1\\u0230\" +\n\t\t\t\t\t\"\\106\\0\\1\\u0231\\63\\0\\1\\u0232\\76\\0\\1\\u0233\\104\\0\\1\\u0234\" +\n\t\t\t\t\t\"\\47\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\4\\4\\1\\u0235\\13\\4\\14\\0\" +\n\t\t\t\t\t\"\\1\\u0236\\116\\0\\1\\u0237\\71\\0\\1\\u019e\\102\\0\\1\\u0174\\77\\0\" +\n\t\t\t\t\t\"\\1\\u019e\\74\\0\\1\\u0238\\103\\0\\1\\u0127\\13\\0\\1\\u0129\\14\\0\" +\n\t\t\t\t\t\"\\1\\u012a\\34\\0\\1\\u0221\\77\\0\\1\\u0221\\13\\0\\1\\u0239\\113\\0\" +\n\t\t\t\t\t\"\\1\\u023a\\32\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\2\\4\\1\\356\\15\\4\" +\n\t\t\t\t\t\"\\65\\0\\1\\u01cf\\14\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\u023b\\6\\4\\6\\0\" +\n\t\t\t\t\t\"\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\13\\0\" +\n\t\t\t\t\t\"\\1\\u023c\\13\\0\\1\\u023d\\134\\0\\1\\u023e\\27\\0\\1\\u023f\\130\\0\" +\n\t\t\t\t\t\"\\1\\u0240\\45\\0\\1\\u0241\\131\\0\\1\\u0242\\62\\0\\1\\u0243\\102\\0\" +\n\t\t\t\t\t\"\\1\\u015e\\100\\0\\1\\u0174\\73\\0\\1\\u0244\\131\\0\\1\\u0245\\47\\0\" +\n\t\t\t\t\t\"\\1\\u0246\\74\\0\\1\\u0247\\52\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\1\\u0248\" +\n\t\t\t\t\t\"\\5\\4\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\" +\n\t\t\t\t\t\"\\20\\4\\63\\0\\1\\u0249\\16\\0\\6\\4\\2\\0\\1\\55\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\\1\\4\\1\\u024a\\6\\4\" +\n\t\t\t\t\t\"\\6\\0\\3\\4\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\" +\n\t\t\t\t\t\"\\64\\0\\1\\u024b\\45\\0\\1\\u024c\\112\\0\\1\\u024d\\57\\0\\1\\u024e\" +\n\t\t\t\t\t\"\\77\\0\\1\\u024f\\65\\0\\1\\u0250\\111\\0\\1\\u0251\\77\\0\\1\\u0252\" +\n\t\t\t\t\t\"\\140\\0\\1\\u0253\\25\\0\\1\\u0254\\116\\0\\1\\u0177\\130\\0\\1\\u0255\" +\n\t\t\t\t\t\"\\42\\0\\1\\u0256\\117\\0\\1\\u0257\\57\\0\\1\\u013d\\115\\0\\1\\u0258\" +\n\t\t\t\t\t\"\\57\\0\\1\\u01d5\\105\\0\\1\\u0239\\77\\0\\1\\u0128\\11\\0\\1\\u0129\" +\n\t\t\t\t\t\"\\14\\0\\1\\u012a\\64\\0\\1\\u0259\\115\\0\\1\\u025a\\14\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\1\\4\\1\\356\\1\\4\\1\\0\\1\\4\\3\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\63\\0\\1\\u025b\\41\\0\" +\n\t\t\t\t\t\"\\1\\u025c\\133\\0\\1\\u0174\\63\\0\\1\\u025d\\77\\0\\1\\u025e\\116\\0\" +\n\t\t\t\t\t\"\\1\\u025f\\57\\0\\1\\u0260\\65\\0\\1\\u0261\\100\\0\\1\\u0262\\73\\0\" +\n\t\t\t\t\t\"\\1\\u0263\\100\\0\\1\\u0264\\103\\0\\1\\u0265\\73\\0\\1\\u0266\\76\\0\" +\n\t\t\t\t\t\"\\1\\u015e\\115\\0\\1\\u0267\\57\\0\\1\\u0268\\106\\0\\1\\u0269\\106\\0\" +\n\t\t\t\t\t\"\\1\\u026a\\36\\0\\6\\4\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\" +\n\t\t\t\t\t\"\\3\\0\\1\\4\\1\\0\\1\\4\\1\\0\\6\\4\\6\\0\\3\\4\" +\n\t\t\t\t\t\"\\1\\0\\2\\4\\1\\0\\3\\4\\3\\0\\4\\4\\1\\u026b\\13\\4\" +\n\t\t\t\t\t\"\\43\\0\\1\\u0253\\70\\0\\1\\u026c\\71\\0\\1\\u026d\\133\\0\\1\\u026e\" +\n\t\t\t\t\t\"\\44\\0\\1\\u026f\\64\\0\\1\\u0270\\6\\0\\1\\u0271\\120\\0\\1\\u0272\" +\n\t\t\t\t\t\"\\66\\0\\1\\u0273\\106\\0\\1\\u016b\\116\\0\\1\\u0274\\105\\0\\1\\u01ab\" +\n\t\t\t\t\t\"\\71\\0\\1\\u0205\\32\\0\\1\\u0133\\115\\0\\1\\u0133\\112\\0\\1\\u0275\" +\n\t\t\t\t\t\"\\114\\0\\1\\u019e\\43\\0\\1\\u0276\\77\\0\\1\\u0277\\100\\0\\1\\u0278\" +\n\t\t\t\t\t\"\\102\\0\\1\\u0279\\74\\0\\1\\u027a\\77\\0\\1\\u027b\\101\\0\\1\\u019e\" +\n\t\t\t\t\t\"\\73\\0\\1\\u027c\\101\\0\\1\\u027d\\135\\0\\1\\u027e\\37\\0\\1\\u027f\" +\n\t\t\t\t\t\"\\101\\0\\1\\u0280\\77\\0\\1\\u0281\\75\\0\\1\\u0282\\66\\0\\1\\u0283\" +\n\t\t\t\t\t\"\\125\\0\\1\\u0284\\71\\0\\1\\u019e\\127\\0\\1\\u0285\\15\\0\\6\\4\" +\n\t\t\t\t\t\"\\2\\0\\1\\55\\3\\4\\1\\0\\1\\4\\3\\0\\1\\4\\1\\0\" +\n\t\t\t\t\t\"\\1\\4\\1\\0\\1\\4\\1\\356\\4\\4\\6\\0\\3\\4\\1\\0\" +\n\t\t\t\t\t\"\\2\\4\\1\\0\\3\\4\\3\\0\\20\\4\\65\\0\\1\\u0286\\43\\0\" +\n\t\t\t\t\t\"\\1\\u0127\\1\\0\\1\\u0128\\11\\0\\1\\u0129\\114\\0\\1\\u0207\\104\\0\" +\n\t\t\t\t\t\"\\1\\u01ab\\7\\0\\1\\u019e\\27\\0\\1\\u0287\\115\\0\\1\\u0174\\61\\0\" +\n\t\t\t\t\t\"\\1\\u0288\\77\\0\\1\\u0289\\77\\0\\1\\u0251\\77\\0\\1\\u028a\\102\\0\" +\n\t\t\t\t\t\"\\1\\u028b\\127\\0\\1\\u028c\\60\\0\\1\\u019e\\50\\0\\1\\u019e\\162\\0\" +\n\t\t\t\t\t\"\\1\\u019e\\61\\0\\1\\u0278\\46\\0\\1\\u028d\\114\\0\\1\\u028e\\120\\0\" +\n\t\t\t\t\t\"\\1\\u028f\\41\\0\\1\\u0290\\62\\0\\1\\u0291\\150\\0\\1\\u0292\\74\\0\" +\n\t\t\t\t\t\"\\1\\u0293\\101\\0\\1\\u0294\\57\\0\\1\\u01e7\\23\\0\\1\\u01ab\\3\\0\" +\n\t\t\t\t\t\"\\1\\u01ac\\66\\0\\1\\u0295\\31\\0\\1\\u0296\\110\\0\\1\\u0207\\117\\0\" +\n\t\t\t\t\t\"\\1\\u0297\\60\\0\\1\\u0298\\132\\0\\1\\u0299\\46\\0\\1\\u029a\\101\\0\" +\n\t\t\t\t\t\"\\1\\u029b\\107\\0\\1\\u029c\\116\\0\\1\\u028d\\102\\0\\1\\u029d\\44\\0\" +\n\t\t\t\t\t\"\\1\\u029e\\100\\0\\1\\u029f\\133\\0\\1\\u028d\\40\\0\\1\\u028d\\112\\0\" +\n\t\t\t\t\t\"\\1\\u02a0\\63\\0\\1\\u02a1\\132\\0\\1\\u02a2\\102\\0\\1\\u0279\\62\\0\" +\n\t\t\t\t\t\"\\1\\u0191\\14\\0\\1\\u0193\\56\\0\\1\\u0133\\64\\0\\1\\u02a3\\76\\0\" +\n\t\t\t\t\t\"\\1\\u02a4\\100\\0\\1\\u02a5\\77\\0\\1\\u02a6\\101\\0\\1\\u02a7\\100\\0\" +\n\t\t\t\t\t\"\\1\\u028d\\135\\0\\1\\u02a8\\35\\0\\1\\u02a9\\13\\0\\1\\u02aa\\77\\0\" +\n\t\t\t\t\t\"\\1\\u02ab\\66\\0\\1\\u02ac\\77\\0\\1\\u019e\\77\\0\\1\\u02ad\\111\\0\" +\n\t\t\t\t\t\"\\1\\u02ae\\114\\0\\1\\u02af\\31\\0\\1\\u02b0\\151\\0\\1\\u02b1\\44\\0\" +\n\t\t\t\t\t\"\\1\\u02b2\\75\\0\\1\\u02b3\\134\\0\\1\\u028d\\26\\0\\1\\u02b4\\126\\0\" +\n\t\t\t\t\t\"\\1\\u027d\\61\\0\\1\\u028d\\140\\0\\1\\u02b5\\55\\0\\1\\u0205\\47\\0\" +\n\t\t\t\t\t\"\\1\\u02b6\\147\\0\\1\\u02b7\\76\\0\\1\\u0205\\31\\0\\1\\u02b8\\144\\0\" +\n\t\t\t\t\t\"\\1\\u01f3\\31\\0\\1\\u02b9\\147\\0\\1\\u019e\\60\\0\\1\\u02ba\\130\\0\" +\n\t\t\t\t\t\"\\1\\u02bb\\26\\0\\1\\u02bc\\134\\0\\1\\u02bd\\16\\0\";\n\n\tprivate static int[] zzUnpackTrans() {\n\t\tint[] result = new int[43456];\n\t\tint offset = 0;\n\t\toffset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result);\n\t\treturn result;\n\t}\n\n\tprivate static int zzUnpackTrans(String packed, int offset, int[] result) {\n\t\tint i = 0; /* index in packed string */\n\t\tint j = offset; /* index in unpacked array */\n\t\tint l = packed.length();\n\t\twhile (i < l) {\n\t\t\tint count = packed.charAt(i++);\n\t\t\tint value = packed.charAt(i++);\n\t\t\tvalue--;\n\t\t\tdo\n\t\t\t\tresult[j++] = value;\n\t\t\twhile (--count > 0);\n\t\t}\n\t\treturn j;\n\t}\n\n\t/* error codes */\n\tprivate static final int ZZ_UNKNOWN_ERROR = 0;\n\tprivate static final int ZZ_NO_MATCH = 1;\n\tprivate static final int ZZ_PUSHBACK_2BIG = 2;\n\n\t/* error messages for the codes above */\n\tprivate static final String ZZ_ERROR_MSG[] = {\n\t\t\t\"Unknown internal scanner error\",\n\t\t\t\"Error: could not match input\",\n\t\t\t\"Error: pushback value was too large\"\n\t};\n\n\t/**\n\t * ZZ_ATTRIBUTE[aState] contains the attributes of state <code>aState</code>\n\t */\n\tprivate static final int[] ZZ_ATTRIBUTE = zzUnpackAttribute();\n\n\tprivate static final String ZZ_ATTRIBUTE_PACKED_0 =\n\t\t\t\"\\2\\0\\1\\11\\4\\1\\1\\11\\4\\1\\1\\11\\13\\1\\2\\11\" +\n\t\t\t\t\t\"\\16\\1\\1\\11\\3\\1\\1\\0\\1\\1\\1\\0\\3\\1\\1\\11\" +\n\t\t\t\t\t\"\\1\\0\\10\\1\\1\\11\\1\\1\\12\\0\\33\\1\\6\\0\\1\\11\" +\n\t\t\t\t\t\"\\1\\0\\15\\1\\5\\0\\1\\1\\1\\0\\2\\1\\1\\11\\14\\1\" +\n\t\t\t\t\t\"\\1\\11\\1\\1\\20\\0\\35\\1\\7\\0\\10\\1\\1\\0\\2\\1\" +\n\t\t\t\t\t\"\\5\\0\\1\\1\\2\\0\\1\\1\\1\\0\\2\\1\\22\\0\\5\\1\" +\n\t\t\t\t\t\"\\1\\0\\11\\1\\1\\0\\2\\1\\1\\0\\6\\1\\1\\0\\2\\1\" +\n\t\t\t\t\t\"\\11\\0\\4\\1\\1\\0\\4\\1\\2\\0\\2\\1\\2\\0\\1\\1\" +\n\t\t\t\t\t\"\\1\\0\\1\\1\\6\\0\\2\\1\\6\\0\\1\\11\\13\\0\\2\\1\" +\n\t\t\t\t\t\"\\3\\0\\2\\1\\1\\0\\3\\1\\3\\0\\2\\1\\1\\0\\6\\1\" +\n\t\t\t\t\t\"\\1\\0\\3\\1\\5\\0\\4\\1\\1\\0\\2\\1\\3\\0\\1\\1\" +\n\t\t\t\t\t\"\\5\\0\\1\\1\\6\\0\\1\\1\\7\\0\\1\\1\\4\\0\\3\\1\" +\n\t\t\t\t\t\"\\4\\0\\1\\1\\1\\0\\2\\1\\10\\0\\1\\1\\1\\0\\3\\1\" +\n\t\t\t\t\t\"\\1\\0\\2\\1\\1\\11\\3\\0\\3\\1\\2\\0\\1\\1\\1\\0\" +\n\t\t\t\t\t\"\\2\\1\\3\\0\\2\\1\\23\\0\\1\\1\\7\\0\\2\\1\\10\\0\" +\n\t\t\t\t\t\"\\3\\1\\1\\0\\2\\1\\1\\0\\1\\1\\11\\0\\1\\1\\1\\0\" +\n\t\t\t\t\t\"\\1\\1\\2\\0\\1\\1\\22\\0\\1\\1\\3\\0\\1\\1\\12\\0\" +\n\t\t\t\t\t\"\\1\\1\\1\\0\\1\\1\\15\\0\\1\\1\\1\\0\\1\\1\\25\\0\" +\n\t\t\t\t\t\"\\1\\1\\22\\0\\1\\1\\10\\0\\1\\1\\26\\0\\1\\1\\2\\0\" +\n\t\t\t\t\t\"\\1\\1\\35\\0\\1\\1\\3\\0\\1\\1\\6\\0\\1\\1\\50\\0\" +\n\t\t\t\t\t\"\\1\\11\";\n\n\tprivate static int[] zzUnpackAttribute() {\n\t\tint[] result = new int[701];\n\t\tint offset = 0;\n\t\toffset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);\n\t\treturn result;\n\t}\n\n\tprivate static int zzUnpackAttribute(String packed, int offset, int[] result) {\n\t\tint i = 0; /* index in packed string */\n\t\tint j = offset; /* index in unpacked array */\n\t\tint l = packed.length();\n\t\twhile (i < l) {\n\t\t\tint count = packed.charAt(i++);\n\t\t\tint value = packed.charAt(i++);\n\t\t\tdo\n\t\t\t\tresult[j++] = value;\n\t\t\twhile (--count > 0);\n\t\t}\n\t\treturn j;\n\t}\n\n\t/** the input device */\n\tprivate Reader zzReader;\n\n\t/** the current state of the DFA */\n\tprivate int zzState;\n\n\t/** the current lexical state */\n\tprivate int zzLexicalState = YYINITIAL;\n\n\t/**\n\t * this buffer contains the current text to be matched and is\n\t * the source of the yytext() string\n\t */\n\tprivate char zzBuffer[];\n\n\t/** the textposition at the last accepting state */\n\tprivate int zzMarkedPos;\n\n\t/** the textposition at the last state to be included in yytext */\n\tprivate int zzPushbackPos;\n\n\t/** the current text position in the buffer */\n\tprivate int zzCurrentPos;\n\n\t/** startRead marks the beginning of the yytext() string in the buffer */\n\tprivate int zzStartRead;\n\n\t/**\n\t * endRead marks the last character in the buffer, that has been read\n\t * from input\n\t */\n\tprivate int zzEndRead;\n\n\t/** number of newlines encountered up to the start of the matched text */\n\tprivate int yyline;\n\n\t/** the number of characters up to the start of the matched text */\n\tprivate int yychar;\n\n\t/**\n\t * the number of characters from the last newline up to the start of the\n\t * matched text\n\t */\n\tprivate int yycolumn;\n\n\t/**\n\t * zzAtBOL == true <=> the scanner is currently at the beginning of a line\n\t */\n\tprivate boolean zzAtBOL = true;\n\n\t/** zzAtEOF == true <=> the scanner is at the EOF */\n\tprivate boolean zzAtEOF;\n\n\t/* user code: */\n\n\t/**\n\t * Constructor. This must be here because JFlex does not generate a\n\t * no-parameter constructor.\n\t */\n\tpublic SmaliTokenMaker() {\n\t}\n\n\t/**\n\t * Adds the token specified to the current linked list of tokens.\n\t *\n\t * @param tokenType The token's type.\n\t * @see #addToken(int, int, int)\n\t */\n\tprivate void addHyperlinkToken(int start, int end, int tokenType) {\n\t\tint so = start + offsetShift;\n\t\taddToken(zzBuffer, start, end, tokenType, so, true);\n\t}\n\n\t/**\n\t * Adds the token specified to the current linked list of tokens.\n\t *\n\t * @param tokenType The token's type.\n\t */\n\tprivate void addToken(int tokenType) {\n\t\taddToken(zzStartRead, zzMarkedPos - 1, tokenType);\n\t}\n\n\t/**\n\t * Adds the token specified to the current linked list of tokens.\n\t *\n\t * @param tokenType The token's type.\n\t * @see #addHyperlinkToken(int, int, int)\n\t */\n\tprivate void addToken(int start, int end, int tokenType) {\n\t\tint so = start + offsetShift;\n\t\taddToken(zzBuffer, start, end, tokenType, so, false);\n\t}\n\n\t/**\n\t * Adds the token specified to the current linked list of tokens.\n\t *\n\t * @param array       The character array.\n\t * @param start       The starting offset in the array.\n\t * @param end         The ending offset in the array.\n\t * @param tokenType   The token's type.\n\t * @param startOffset The offset in the document at which this token\n\t *                    occurs.\n\t * @param hyperlink   Whether this token is a hyperlink.\n\t */\n\tpublic void addToken(char[] array, int start, int end, int tokenType,\n\t\t\tint startOffset, boolean hyperlink) {\n\t\tsuper.addToken(array, start, end, tokenType, startOffset, hyperlink);\n\t\tzzStartRead = zzMarkedPos;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\tpublic String[] getLineCommentStartAndEnd(int languageIndex) {\n\t\treturn new String[] { \"#\", null };\n\t}\n\n\t/**\n\t * Returns the first token in the linked list of tokens generated\n\t * from <code>text</code>. This method must be implemented by\n\t * subclasses so they can correctly implement syntax highlighting.\n\t *\n\t * @param text             The text from which to get tokens.\n\t * @param initialTokenType The token type we should start with.\n\t * @param startOffset      The offset into the document at which\n\t *                         <code>text</code> starts.\n\t * @return The first <code>Token</code> in a linked list representing\n\t *         the syntax highlighted text.\n\t */\n\tpublic Token getTokenList(Segment text, int initialTokenType, int startOffset) {\n\n\t\tresetTokenList();\n\t\tthis.offsetShift = -text.offset + startOffset;\n\n\t\t// Start off in the proper state.\n\t\tint state = Token.NULL;\n\t\tswitch (initialTokenType) {\n\t\t\t/* No multi-line comments */\n\t\t\t/* No documentation comments */\n\t\t\tdefault:\n\t\t\t\tstate = Token.NULL;\n\t\t}\n\n\t\ts = text;\n\t\ttry {\n\t\t\tyyreset(zzReader);\n\t\t\tyybegin(state);\n\t\t\treturn yylex();\n\t\t} catch (IOException ioe) {\n\t\t\tioe.printStackTrace();\n\t\t\treturn new TokenImpl();\n\t\t}\n\n\t}\n\n\t/**\n\t * Refills the input buffer.\n\t *\n\t * @return <code>true</code> if EOF was reached, otherwise\n\t *         <code>false</code>.\n\t */\n\tprivate boolean zzRefill() {\n\t\treturn zzCurrentPos >= s.offset + s.count;\n\t}\n\n\t/**\n\t * Resets the scanner to read from a new input stream.\n\t * Does not close the old reader.\n\t *\n\t * All internal variables are reset, the old input stream\n\t * <b>cannot</b> be reused (internal buffer is discarded and lost).\n\t * Lexical state is set to <code>YY_INITIAL</code>\n\t *\n\t * @param reader the new input stream\n\t */\n\tpublic final void yyreset(Reader reader) {\n\t\t// 's' has been updated.\n\t\tzzBuffer = s.array;\n\t\t/*\n\t\t * We replaced the line below with the two below it because zzRefill\n\t\t * no longer \"refills\" the buffer (since the way we do it, it's always\n\t\t * \"full\" the first time through, since it points to the segment's\n\t\t * array). So, we assign zzEndRead here.\n\t\t */\n\t\t// zzStartRead = zzEndRead = s.offset;\n\t\tzzStartRead = s.offset;\n\t\tzzEndRead = zzStartRead + s.count - 1;\n\t\tzzCurrentPos = zzMarkedPos = zzPushbackPos = s.offset;\n\t\tzzLexicalState = YYINITIAL;\n\t\tzzReader = reader;\n\t\tzzAtBOL = true;\n\t\tzzAtEOF = false;\n\t}\n\n\t/**\n\t * Creates a new scanner\n\t * There is also a java.io.InputStream version of this constructor.\n\t *\n\t * @param in the java.io.Reader to read input from.\n\t */\n\tpublic SmaliTokenMaker(Reader in) {\n\t\tthis.zzReader = in;\n\t}\n\n\t/**\n\t * Creates a new scanner.\n\t * There is also java.io.Reader version of this constructor.\n\t *\n\t * @param in the java.io.Inputstream to read input from.\n\t */\n\tpublic SmaliTokenMaker(InputStream in) {\n\t\tthis(new InputStreamReader(in));\n\t}\n\n\t/**\n\t * Unpacks the compressed character translation table.\n\t *\n\t * @param packed the packed character translation table\n\t * @return the unpacked character translation table\n\t */\n\tprivate static char[] zzUnpackCMap(String packed) {\n\t\tchar[] map = new char[0x10000];\n\t\tint i = 0; /* index in packed string */\n\t\tint j = 0; /* index in unpacked array */\n\t\twhile (i < 188) {\n\t\t\tint count = packed.charAt(i++);\n\t\t\tchar value = packed.charAt(i++);\n\t\t\tdo\n\t\t\t\tmap[j++] = value;\n\t\t\twhile (--count > 0);\n\t\t}\n\t\treturn map;\n\t}\n\n\t/**\n\t * Closes the input stream.\n\t */\n\tpublic final void yyclose() throws IOException {\n\t\tzzAtEOF = true; /* indicate end of file */\n\t\tzzEndRead = zzStartRead; /* invalidate buffer */\n\n\t\tif (zzReader != null)\n\t\t\tzzReader.close();\n\t}\n\n\t/**\n\t * Enters a new lexical state\n\t *\n\t * @param newState the new lexical state\n\t */\n\tpublic final void yybegin(int newState) {\n\t\tzzLexicalState = newState;\n\t}\n\n\tpublic final int yystate() {\n\t\treturn zzLexicalState;\n\t}\n\n\t/**\n\t * Returns the text matched by the current regular expression.\n\t */\n\tpublic final String yytext() {\n\t\treturn new String(zzBuffer, zzStartRead, zzMarkedPos - zzStartRead);\n\t}\n\n\t/**\n\t * Returns the character at position<code>pos</code> from the\n\t * matched text.\n\t *\n\t * It is equivalent to yytext().charAt(pos), but faster\n\t *\n\t * @param pos the position of the character to fetch.\n\t *            A value from 0 to yylength()-1.\n\t *\n\t * @return the character at position pos\n\t */\n\tpublic final char yycharat(int pos) {\n\t\treturn zzBuffer[zzStartRead + pos];\n\t}\n\n\t/**\n\t * Returns the length of the matched text region.\n\t */\n\tpublic final int yylength() {\n\t\treturn zzMarkedPos - zzStartRead;\n\t}\n\n\t/**\n\t * Reports an error that occurred while scanning.\n\t *\n\t * In a wellformed scanner (no or only correct usage of\n\t * yypushback(int) and a match-all fallback rule) this method\n\t * will only be called with things that \"Can't Possibly Happen\".\n\t * If this method is called, something is seriously wrong\n\t * (e.g. a JFlex bug producing a faulty scanner etc.).\n\t *\n\t * Usual syntax/scanner level error handling should be done\n\t * in error fallback rules.\n\t *\n\t * @param errorCode the code of the errormessage to display\n\t */\n\tprivate void zzScanError(int errorCode) {\n\t\tString message;\n\t\ttry {\n\t\t\tmessage = ZZ_ERROR_MSG[errorCode];\n\t\t} catch (ArrayIndexOutOfBoundsException e) {\n\t\t\tmessage = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR];\n\t\t}\n\n\t\tthrow new Error(message);\n\t}\n\n\t/**\n\t * Pushes the specified amount of characters back into the input stream.\n\t *\n\t * They will be read again by then next call of the scanning method\n\t *\n\t * @param number the number of characters to be read again.\n\t *               This number must not be greater than yylength()!\n\t */\n\tpublic void yypushback(int number) {\n\t\tif (number > yylength())\n\t\t\tzzScanError(ZZ_PUSHBACK_2BIG);\n\n\t\tzzMarkedPos -= number;\n\t}\n\n\t/**\n\t * Resumes scanning until the next regular expression is matched,\n\t * the end of input is encountered or an I/O-Error occurs.\n\t *\n\t * @return the next token\n\t * @exception IOException if any I/O-Error occurs\n\t */\n\tpublic Token yylex() throws IOException {\n\t\tint zzInput;\n\t\tint zzAction;\n\n\t\t// cached fields:\n\t\tint zzCurrentPosL;\n\t\tint zzMarkedPosL;\n\t\tint zzEndReadL = zzEndRead;\n\t\tchar[] zzBufferL = zzBuffer;\n\t\tchar[] zzCMapL = ZZ_CMAP;\n\n\t\tint[] zzTransL = ZZ_TRANS;\n\t\tint[] zzRowMapL = ZZ_ROWMAP;\n\t\tint[] zzAttrL = ZZ_ATTRIBUTE;\n\n\t\twhile (true) {\n\t\t\tzzMarkedPosL = zzMarkedPos;\n\n\t\t\tzzAction = -1;\n\n\t\t\tzzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;\n\n\t\t\tzzState = zzLexicalState;\n\n\t\t\tzzForAction: {\n\t\t\t\twhile (true) {\n\n\t\t\t\t\tif (zzCurrentPosL < zzEndReadL)\n\t\t\t\t\t\tzzInput = zzBufferL[zzCurrentPosL++];\n\t\t\t\t\telse if (zzAtEOF) {\n\t\t\t\t\t\tzzInput = YYEOF;\n\t\t\t\t\t\tbreak zzForAction;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// store back cached positions\n\t\t\t\t\t\tzzCurrentPos = zzCurrentPosL;\n\t\t\t\t\t\tzzMarkedPos = zzMarkedPosL;\n\t\t\t\t\t\tboolean eof = zzRefill();\n\t\t\t\t\t\t// get translated positions and possibly new buffer\n\t\t\t\t\t\tzzCurrentPosL = zzCurrentPos;\n\t\t\t\t\t\tzzMarkedPosL = zzMarkedPos;\n\t\t\t\t\t\tzzBufferL = zzBuffer;\n\t\t\t\t\t\tzzEndReadL = zzEndRead;\n\t\t\t\t\t\tif (eof) {\n\t\t\t\t\t\t\tzzInput = YYEOF;\n\t\t\t\t\t\t\tbreak zzForAction;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tzzInput = zzBufferL[zzCurrentPosL++];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tint zzNext = zzTransL[zzRowMapL[zzState] + zzCMapL[zzInput]];\n\t\t\t\t\tif (zzNext == -1)\n\t\t\t\t\t\tbreak zzForAction;\n\t\t\t\t\tzzState = zzNext;\n\n\t\t\t\t\tint zzAttributes = zzAttrL[zzState];\n\t\t\t\t\tif ((zzAttributes & 1) == 1) {\n\t\t\t\t\t\tzzAction = zzState;\n\t\t\t\t\t\tzzMarkedPosL = zzCurrentPosL;\n\t\t\t\t\t\tif ((zzAttributes & 8) == 8)\n\t\t\t\t\t\t\tbreak zzForAction;\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// store back cached position\n\t\t\tzzMarkedPos = zzMarkedPosL;\n\n\t\t\tswitch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) {\n\t\t\t\tcase 4: {\n\t\t\t\t\taddNullToken();\n\t\t\t\t\treturn firstToken;\n\t\t\t\t}\n\t\t\t\tcase 27:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 19: {\n\t\t\t\t\taddToken(Token.LITERAL_CHAR);\n\t\t\t\t}\n\t\t\t\tcase 28:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 26: {\n\t\t\t\t\taddToken(Token.MARKUP_TAG_NAME);\n\t\t\t\t}\n\t\t\t\tcase 29:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 7: {\n\t\t\t\t\taddToken(Token.WHITESPACE);\n\t\t\t\t}\n\t\t\t\tcase 30:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 18: {\n\t\t\t\t\taddToken(Token.LITERAL_NUMBER_HEXADECIMAL);\n\t\t\t\t}\n\t\t\t\tcase 31:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 21: {\n\t\t\t\t\taddToken(Token.ERROR_STRING_DOUBLE);\n\t\t\t\t}\n\t\t\t\tcase 32:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 17: {\n\t\t\t\t\taddToken(Token.LITERAL_NUMBER_FLOAT);\n\t\t\t\t}\n\t\t\t\tcase 33:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 22: {\n\t\t\t\t\taddToken(Token.RESERVED_WORD);\n\t\t\t\t}\n\t\t\t\tcase 34:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 8: {\n\t\t\t\t\taddToken(Token.SEPARATOR);\n\t\t\t\t}\n\t\t\t\tcase 35:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 10: {\n\t\t\t\t\taddToken(Token.VARIABLE);\n\t\t\t\t}\n\t\t\t\tcase 36:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1: {\n\t\t\t\t\taddToken(Token.IDENTIFIER);\n\t\t\t\t}\n\t\t\t\tcase 37:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 13: {\n\t\t\t\t\taddToken(start, zzStartRead - 1, Token.COMMENT_EOL);\n\t\t\t\t\taddNullToken();\n\t\t\t\t\treturn firstToken;\n\t\t\t\t}\n\t\t\t\tcase 38:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 20: {\n\t\t\t\t\taddToken(Token.FUNCTION);\n\t\t\t\t}\n\t\t\t\tcase 39:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3: {\n\t\t\t\t\taddToken(Token.ERROR_CHAR);\n\t\t\t\t\taddNullToken();\n\t\t\t\t\treturn firstToken;\n\t\t\t\t}\n\t\t\t\tcase 40:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 5: {\n\t\t\t\t\taddToken(Token.ERROR_STRING_DOUBLE);\n\t\t\t\t\taddNullToken();\n\t\t\t\t\treturn firstToken;\n\t\t\t\t}\n\t\t\t\tcase 41:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 11: {\n\t\t\t\t\taddToken(Token.DATA_TYPE);\n\t\t\t\t}\n\t\t\t\tcase 42:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 15: {\n\t\t\t\t\taddToken(Token.ERROR_CHAR);\n\t\t\t\t}\n\t\t\t\tcase 43:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 23: {\n\t\t\t\t\taddToken(Token.LITERAL_BOOLEAN);\n\t\t\t\t}\n\t\t\t\tcase 44:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 16: {\n\t\t\t\t\taddToken(Token.LITERAL_STRING_DOUBLE_QUOTE);\n\t\t\t\t}\n\t\t\t\tcase 45:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 24: {\n\t\t\t\t\tint temp = zzStartRead;\n\t\t\t\t\taddToken(start, zzStartRead - 1, Token.COMMENT_EOL);\n\t\t\t\t\taddHyperlinkToken(temp, zzMarkedPos - 1, Token.COMMENT_EOL);\n\t\t\t\t\tstart = zzMarkedPos;\n\t\t\t\t}\n\t\t\t\tcase 46:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 25: {\n\t\t\t\t\taddToken(Token.RESERVED_WORD_2);\n\t\t\t\t}\n\t\t\t\tcase 47:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 14: {\n\t\t\t\t\taddToken(Token.ERROR_NUMBER_FORMAT);\n\t\t\t\t}\n\t\t\t\tcase 48:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 6: {\n\t\t\t\t\tstart = zzMarkedPos - 1;\n\t\t\t\t\tyybegin(EOL_COMMENT);\n\t\t\t\t}\n\t\t\t\tcase 49:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2: {\n\t\t\t\t\taddToken(Token.LITERAL_NUMBER_DECIMAL_INT);\n\t\t\t\t}\n\t\t\t\tcase 50:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 9: {\n\t\t\t\t\taddToken(Token.OPERATOR);\n\t\t\t\t}\n\t\t\t\tcase 51:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 12: {\n\t\t\t\t}\n\t\t\t\tcase 52:\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tif (zzInput == YYEOF && zzStartRead == zzCurrentPos) {\n\t\t\t\t\t\tzzAtEOF = true;\n\t\t\t\t\t\tswitch (zzLexicalState) {\n\t\t\t\t\t\t\tcase EOL_COMMENT: {\n\t\t\t\t\t\t\t\taddToken(start, zzStartRead - 1, Token.COMMENT_EOL);\n\t\t\t\t\t\t\t\taddNullToken();\n\t\t\t\t\t\t\t\treturn firstToken;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase 702:\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase YYINITIAL: {\n\t\t\t\t\t\t\t\taddNullToken();\n\t\t\t\t\t\t\t\treturn firstToken;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase 703:\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tzzScanError(ZZ_NO_MATCH);\n\t\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/SourceLineFormatter.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport org.fife.ui.rtextarea.LineNumberFormatter;\n\nimport jadx.api.ICodeInfo;\n\npublic class SourceLineFormatter implements LineNumberFormatter {\n\tprivate final ICodeInfo codeInfo;\n\tprivate final int maxLength;\n\n\tpublic SourceLineFormatter(ICodeInfo codeInfo) {\n\t\tthis.codeInfo = codeInfo;\n\t\tthis.maxLength = calcMaxLength(codeInfo);\n\t}\n\n\t@Override\n\tpublic String format(int lineNumber) {\n\t\tInteger sourceLine = codeInfo.getCodeMetadata().getLineMapping().get(lineNumber);\n\t\tif (sourceLine == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn String.valueOf(sourceLine);\n\t}\n\n\t@Override\n\tpublic int getMaxLength(int maxLineNumber) {\n\t\treturn maxLength;\n\t}\n\n\tprivate static int calcMaxLength(ICodeInfo codeInfo) {\n\t\tint maxLine = codeInfo.getCodeMetadata().getLineMapping()\n\t\t\t\t.values().stream()\n\t\t\t\t.mapToInt(Integer::intValue)\n\t\t\t\t.max().orElse(1);\n\t\treturn getNumberLength(maxLine);\n\t}\n\n\tpublic static int getNumberLength(int num) {\n\t\treturn num < 10 ? 1 : 1 + (int) Math.log10(num);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/UsageDialogPlusAction.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.action.ActionModel;\nimport jadx.gui.ui.action.JNodeAction;\nimport jadx.gui.ui.dialog.UsageDialogPlus;\n\npublic final class UsageDialogPlusAction extends JNodeAction {\n\tprivate static final long serialVersionUID = 4692546569977976384L;\n\n\tpublic UsageDialogPlusAction(CodeArea codeArea) {\n\t\tsuper(ActionModel.FIND_USAGE_PLUS, codeArea);\n\t}\n\n\t@Override\n\tpublic void runAction(JNode node) {\n\t\tUsageDialogPlus.open(getCodeArea().getMainWindow(), node);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/mode/JCodeMode.java",
    "content": "package jadx.gui.ui.codearea.mode;\n\nimport javax.swing.Icon;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.DecompilationMode;\nimport jadx.api.ICodeInfo;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\n\npublic class JCodeMode extends JNode {\n\n\tprivate final JClass jCls;\n\tprivate final DecompilationMode mode;\n\n\tprivate @Nullable ICodeInfo codeInfo;\n\n\tpublic JCodeMode(JClass jClass, DecompilationMode mode) {\n\t\tthis.jCls = jClass;\n\t\tthis.mode = mode;\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn jCls.getJParent();\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn jCls.getIcon();\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn jCls.makeString();\n\t}\n\n\t@Override\n\tpublic ICodeInfo getCodeInfo() {\n\t\tif (codeInfo != null) {\n\t\t\treturn codeInfo;\n\t\t}\n\t\tClassNode cls = jCls.getCls().getClassNode();\n\t\tcodeInfo = cls.decompileWithMode(mode);\n\t\treturn codeInfo;\n\t}\n\n\t@Override\n\tpublic String getSyntaxName() {\n\t\treturn SyntaxConstants.SYNTAX_STYLE_JAVA;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn jCls.getName();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/CodeMetadataRange.java",
    "content": "package jadx.gui.ui.codearea.sync;\n\nimport java.util.Map;\n\nimport jadx.api.metadata.ICodeAnnotation;\n\n/**\n * Marks the start and end of annotation within a CodeMetadataStorage\n */\npublic class CodeMetadataRange {\n\t// Use Map.Entry here because Java has no built in tuple/pair utility\n\tprivate final Map.Entry<Integer, ICodeAnnotation> start;\n\tprivate final Map.Entry<Integer, ICodeAnnotation> end;\n\n\tCodeMetadataRange(\n\t\t\tMap.Entry<Integer, ICodeAnnotation> start,\n\t\t\tMap.Entry<Integer, ICodeAnnotation> end) {\n\t\tthis.start = start;\n\t\tthis.end = end;\n\t}\n\n\tMap.Entry<Integer, ICodeAnnotation> getStart() {\n\t\treturn start;\n\t}\n\n\tMap.Entry<Integer, ICodeAnnotation> getEnd() {\n\t\treturn end;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"CodeMetadataRange{start=\"\n\t\t\t\t+ start.getKey()\n\t\t\t\t+ \"->\"\n\t\t\t\t+ start.getValue()\n\t\t\t\t+ \",end=\"\n\t\t\t\t+ end.getKey()\n\t\t\t\t+ \"->\"\n\t\t\t\t+ end.getValue()\n\t\t\t\t+ \"}\";\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/CodePanelSyncee.java",
    "content": "package jadx.gui.ui.codearea.sync;\n\n/**\n * Accepts a code panel syncer for syncing code areas\n */\npublic interface CodePanelSyncee {\n\tboolean sync(CodePanelSyncer syncer);\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/CodePanelSyncer.java",
    "content": "package jadx.gui.ui.codearea.sync;\n\npublic interface CodePanelSyncer extends IToJavaSyncStrategy, IToSmaliSyncStrategy {\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/CodePanelSyncerAbstractFactory.java",
    "content": "package jadx.gui.ui.codearea.sync;\n\npublic interface CodePanelSyncerAbstractFactory {\n\tCodePanelSyncer createCodePanelSyncer();\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/CodeSyncHighlighter.java",
    "content": "package jadx.gui.ui.codearea.sync;\n\nimport java.awt.Color;\n\nimport javax.swing.Timer;\nimport javax.swing.UIManager;\nimport javax.swing.text.BadLocationException;\nimport javax.swing.text.DefaultHighlighter;\nimport javax.swing.text.Highlighter;\nimport javax.swing.text.Highlighter.HighlightPainter;\n\nimport jadx.gui.ui.codearea.AbstractCodeArea;\n\n/**\n * Highlighting and scrolling utility into a CodeArea for a given color\n */\npublic class CodeSyncHighlighter {\n\tprivate final Color color;\n\n\tpublic CodeSyncHighlighter(Color color) {\n\t\tthis.color = color;\n\t}\n\n\tpublic void highlightAndScrollToLine(AbstractCodeArea area, int lineIndex) throws BadLocationException {\n\t\thighlightLine(area, lineIndex);\n\t\tarea.scrollToPos(area.getLineStartOffset(lineIndex));\n\t}\n\n\tpublic void highlightLine(AbstractCodeArea area, int lineIndex) throws BadLocationException {\n\t\tint startOffset = area.getLineStartOffset(lineIndex);\n\t\tint endOffset = area.getLineEndOffset(lineIndex);\n\t\thighlightRange(area, startOffset, endOffset);\n\t}\n\n\t// Highlight range in code area with a temporary yellow highlight\n\tpublic void highlightRange(AbstractCodeArea area, int startOffset, int endOffset) throws BadLocationException {\n\t\tHighlighter hl = area.getHighlighter();\n\t\tHighlightPainter painter =\n\t\t\t\tnew DefaultHighlighter.DefaultHighlightPainter(this.color);\n\t\tObject tag = hl.addHighlight(startOffset, endOffset, painter);\n\t\tnew Timer(1000, e -> hl.removeHighlight(tag)).start();\n\t}\n\n\tpublic static CodeSyncHighlighter defaultHighlighter() {\n\t\treturn new CodeSyncHighlighter(UIManager.getColor(\"TabbedPane.hoverColor\"));\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/DebugLineJavaSyncer.java",
    "content": "package jadx.gui.ui.codearea.sync;\n\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.codearea.SmaliArea;\n\n/**\n * Use debug line info from dex to correlate from java to java/smali\n */\npublic class DebugLineJavaSyncer implements IToSmaliSyncStrategy, IToJavaSyncStrategy {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DebugLineJavaSyncer.class);\n\n\tprivate final CodeArea from;\n\n\tpublic DebugLineJavaSyncer(CodeArea area) {\n\t\tthis.from = area;\n\t}\n\n\t@Override\n\tpublic boolean syncTo(CodeArea to) {\n\t\t// This might be any combination between java/simple/fallback\n\t\t// We cannot just rely on the current line.\n\t\t// Instead try to correlate with line mappings.\n\t\ttry {\n\t\t\tint lineIndex = from.getCaretLineNumber();\n\t\t\tMap<Integer, Integer> toLineMapping = to.getFunctionUniqueLineMappings();\n\t\t\t// lineIndex is 0-indexed whereas the line mappings are based off a 1-index.\n\t\t\tInteger sourceLine = getClosestSourceLine(lineIndex + 1);\n\t\t\tif (sourceLine == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// find the equivalent linenumber in the 'to' by a reverse lookup from the source line\n\t\t\tfor (Map.Entry<Integer, Integer> entry : toLineMapping.entrySet()) {\n\t\t\t\tint toLine = entry.getKey();\n\t\t\t\tint candidateSourceLine = entry.getValue();\n\t\t\t\tif (sourceLine == candidateSourceLine) {\n\t\t\t\t\t// we have the mapped line we target the lineIndex which is a 0-index\n\t\t\t\t\tCodeSyncHighlighter.defaultHighlighter().highlightAndScrollToLine(to, toLine - 1);\n\t\t\t\t\tLOG.info(\"{} - successful sync of code to code\", LOG.getName());\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"{} - Failed to sync from CodeArea to CodeArea: {}\", LOG.getName(), e.getLocalizedMessage());\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean syncTo(SmaliArea to) {\n\t\ttry {\n\t\t\tint lineIndex = from.getCaretLineNumber();\n\n\t\t\t// lineIndex is 0-indexed but the line mappings are based of 1-indexed line numbers.\n\t\t\tint lineNum = lineIndex + 1;\n\t\t\tInteger sourceLine = getClosestSourceLine(lineNum);\n\t\t\tif (sourceLine == null) {\n\t\t\t\tto.removeAllLineHighlights();\n\t\t\t\tLOG.debug(\"decompiled line {} not mapped to source line\", lineNum);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// find the smali line where \".line <sourceLine>\" is\n\t\t\tLOG.debug(\"Finding \\\".line {}\\\" in smali\", sourceLine);\n\t\t\tint smaliLine = findSmaliLineIndex(to, sourceLine);\n\t\t\tif (smaliLine < 0) {\n\t\t\t\tLOG.warn(\"{} - Source line {} not annotated in Smali\", LOG.getName(), sourceLine);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tCodeSyncHighlighter.defaultHighlighter().highlightAndScrollToLine(to, smaliLine);\n\t\t\tLOG.info(\"{} - successful sync of code to smali\", LOG.getName());\n\t\t\treturn true;\n\t\t} catch (Exception ex) {\n\t\t\tLOG.error(\"{} - Failed to sync CodeArea to SmaliArea: {}\", LOG.getName(), ex.getLocalizedMessage());\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate @Nullable Integer getClosestSourceLine(int lineNum) {\n\t\t// get the line mappings of the Java/Simple/Fallback code\n\t\tMap<Integer, Integer> lineMapping = from.getFunctionUniqueLineMappings();\n\t\tif (lineMapping == null || lineMapping.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\t// get the source line from the decomp line\n\t\tInteger sourceLine = null;\n\t\t// Some of the intermediate lines are not mapped so keep going back until we find one\n\t\t// e.g. multiple instruction lines in the 'Simple' view belong to a single source line\n\t\twhile (lineNum >= 0 && (sourceLine = lineMapping.get(lineNum)) == null) {\n\t\t\t--lineNum;\n\t\t}\n\t\treturn sourceLine;\n\t}\n\n\t/**\n\t * find the \".line \\d+\" line in the smali\n\t */\n\tprivate static int findSmaliLineIndex(SmaliArea smaliArea, int sourceLine) {\n\t\tString line = \".line \" + Integer.toString(sourceLine);\n\t\tString[] smaliLines = smaliArea.getText().split(\"\\\\R\");\n\t\tfor (int i = 0; i < smaliLines.length; ++i) {\n\t\t\tString l = smaliLines[i];\n\t\t\tif (l.trim().equals(line)) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/DebugLineSmaliSyncer.java",
    "content": "package jadx.gui.ui.codearea.sync;\n\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.codearea.SmaliArea;\n\n/**\n * Use Debug lines in smali from dex debug info to correlate with code\n */\npublic class DebugLineSmaliSyncer implements IToJavaSyncStrategy {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DebugLineSmaliSyncer.class);\n\n\tprivate final SmaliArea from;\n\n\tpublic DebugLineSmaliSyncer(SmaliArea area) {\n\t\tthis.from = area;\n\t}\n\n\t@Override\n\tpublic boolean syncTo(CodeArea to) {\n\t\ttry {\n\t\t\t// Get the from lines and currentline index\n\t\t\tint lineIndex = from.getCaretLineNumber();\n\t\t\tString[] fromLines = from.getText().split(\"\\\\R\");\n\t\t\tif (lineIndex >= fromLines.length) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// find an Anchor to guide what to look for and highlight in the CodeArea\n\t\t\tAnchor anchor = findNearestAnchor(lineIndex, fromLines);\n\t\t\tif (anchor == null) {\n\t\t\t\tLOG.error(\"{} - No Smali Anchor found\", LOG.getName());\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (anchor.getType() == Anchor.Type.SOURCE_LINE) {\n\t\t\t\tLOG.debug(anchor.toString());\n\t\t\t\tMap<Integer, Integer> toDecompToSourceMapping = to.getFunctionUniqueLineMappings();\n\t\t\t\tfor (Map.Entry<Integer, Integer> entry : toDecompToSourceMapping.entrySet()) {\n\t\t\t\t\tint decompLine = entry.getKey();\n\t\t\t\t\tint sourceLine = entry.getValue();\n\t\t\t\t\tif (anchor.getCodeMappedLineNumber() == sourceLine) {\n\t\t\t\t\t\tint decompLineIndex = decompLine - 1;\n\t\t\t\t\t\tLOG.debug(\"Highlighting {} on {}\", decompLine, to);\n\t\t\t\t\t\tCodeSyncHighlighter.defaultHighlighter().highlightAndScrollToLine(to, decompLineIndex);\n\t\t\t\t\t\tLOG.info(\"{} - successful sync of smali to code\", LOG.getName());\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tto.removeAllLineHighlights();\n\t\t} catch (Exception ex) {\n\t\t\tLOG.error(\"{} - Failed to sync from Smali to Code\", LOG.getName(), ex);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Nullable\n\tprivate Anchor findNearestAnchor(int smaliLineNumber, String[] lines) {\n\t\tfor (int i = smaliLineNumber; i >= 0; i--) {\n\t\t\tString trimmedLine = lines[i].trim();\n\t\t\tif (trimmedLine.startsWith(\".line\")) {\n\t\t\t\treturn new Anchor(Anchor.Type.SOURCE_LINE, trimmedLine, i);\n\t\t\t}\n\t\t\tif (trimmedLine.startsWith(\".method\")) {\n\t\t\t\treturn new Anchor(Anchor.Type.METHOD_START, trimmedLine, i);\n\t\t\t}\n\t\t\tif (trimmedLine.startsWith(\".end\")) {\n\t\t\t\treturn new Anchor(Anchor.Type.METHOD_END, trimmedLine, i);\n\t\t\t}\n\t\t\tif (trimmedLine.startsWith(\".field\")) {\n\t\t\t\treturn new Anchor(Anchor.Type.FIELD, trimmedLine, i);\n\t\t\t}\n\t\t\tif (trimmedLine.startsWith(\".class\")) {\n\t\t\t\treturn new Anchor(Anchor.Type.CLASS, trimmedLine, smaliLineNumber);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Line in the smali that can be used to find a section to highlight in the code area\n\t */\n\tprivate static class Anchor {\n\t\tpublic enum Type {\n\t\t\tSOURCE_LINE,\n\t\t\tMETHOD_START,\n\t\t\tMETHOD_END,\n\t\t\tFIELD,\n\t\t\tCLASS\n\t\t}\n\n\t\tprivate final Type type;\n\t\tprivate final String line;\n\t\tprivate final int smaliLineNumber;\n\t\tprivate int codeMappedLineNumber = -1;\n\n\t\tpublic Anchor(Type type, String line, int smaliLineNumber) {\n\t\t\tthis.type = type;\n\t\t\tthis.line = line;\n\t\t\tthis.smaliLineNumber = smaliLineNumber;\n\t\t\tthis.map();\n\t\t}\n\n\t\tpublic Type getType() {\n\t\t\treturn type;\n\t\t}\n\n\t\tpublic int getCodeMappedLineNumber() {\n\t\t\treturn codeMappedLineNumber;\n\t\t}\n\n\t\tprivate void map() {\n\t\t\tswitch (type) {\n\t\t\t\tcase SOURCE_LINE:\n\t\t\t\t\tPattern p = Pattern.compile(\"(\\\\.line\\\\s)(\\\\d+)\");\n\t\t\t\t\tMatcher m = p.matcher(line);\n\t\t\t\t\tif (m.find()) {\n\t\t\t\t\t\tcodeMappedLineNumber = Integer.parseInt(m.group(2));\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tcodeMappedLineNumber = -1;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn String.format(\"Anchor %s, %d, %d\", type.name(), smaliLineNumber, codeMappedLineNumber);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/IToJavaSyncStrategy.java",
    "content": "package jadx.gui.ui.codearea.sync;\n\nimport jadx.gui.ui.codearea.CodeArea;\n\npublic interface IToJavaSyncStrategy {\n\tboolean syncTo(CodeArea area);\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/IToSmaliSyncStrategy.java",
    "content": "package jadx.gui.ui.codearea.sync;\n\nimport jadx.gui.ui.codearea.SmaliArea;\n\npublic interface IToSmaliSyncStrategy {\n\tboolean syncTo(SmaliArea area);\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/InsnOffsetJavaSyncer.java",
    "content": "package jadx.gui.ui.codearea.sync;\n\nimport java.util.AbstractMap.SimpleEntry;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.metadata.annotations.InsnCodeOffset;\nimport jadx.api.metadata.annotations.NodeDeclareRef;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.gui.device.debugger.DbgUtils;\nimport jadx.gui.device.debugger.smali.SmaliMethodNode;\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.codearea.SmaliArea;\n\n/**\n * Use insn code offsets to sync code panel area to code/smali\n * This only works for Smali when SmaliArea is showing the dalvik bytecode.\n */\npublic class InsnOffsetJavaSyncer implements IToJavaSyncStrategy, IToSmaliSyncStrategy {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(InsnOffsetJavaSyncer.class);\n\n\tprivate final CodeArea from;\n\n\tpublic InsnOffsetJavaSyncer(CodeArea area) {\n\t\tthis.from = area;\n\t}\n\n\t@Override\n\tpublic boolean syncTo(SmaliArea to) {\n\t\tif (!to.isShowingDalvikBytecode()) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// 1. Find the Method start and end boundaries enclosing the caret position in the code metadata\n\t\t// 2. Find the closest InsnCodeOffset range within the method boundary corresponding to the caret\n\t\t// position\n\t\t// 3. Get all of the smali lines which fall within the InsnCodeOffset range.\n\t\t// 4. Highlight those found in 3. and scroll to the first one.\n\t\tint caretPos = from.getCaretPosition();\n\t\tCodeMetadataRange mthRange = findEnclosingMethodRange(caretPos);\n\t\tif (mthRange == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tInteger mthDefPos = mthRange.getStart().getKey();\n\t\tInteger mthEndPos = mthRange.getEnd().getKey();\n\n\t\tLOG.debug(\"InsnOffsetJavaSyncer caretPos = {}\", caretPos);\n\t\tLOG.debug(\"InsnOffsetJavaSyncer mthDefPos = {}\", mthDefPos);\n\t\tLOG.debug(\"InsnOffsetJavaSyncer mthEndPos = {}\", mthEndPos);\n\n\t\tCodeMetadataRange insnOffsetRange = findOffsetRange(caretPos, mthDefPos, mthEndPos);\n\t\tif (insnOffsetRange == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tString mthID = getMthRawFullID(mthDefPos);\n\t\tSmaliMethodNode smaliMthNode = DbgUtils.getSmaliMethodNode(to.getJClass(), mthID);\n\t\tif (smaliMthNode == null) {\n\t\t\tLOG.error(\"{} - mth ID {} not mapped to a SmaliMethodNode\", LOG.getName(), mthID);\n\t\t\treturn false;\n\t\t}\n\n\t\tList<Integer> smaliLines = getMappedSmaliLines(smaliMthNode, insnOffsetRange);\n\t\tif (smaliLines.size() < 2) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tCodeSyncHighlighter.defaultHighlighter().highlightAndScrollToLine(to, smaliLines.get(0));\n\t\t\tfor (int i = 1; i < smaliLines.size(); ++i) {\n\t\t\t\tCodeSyncHighlighter.defaultHighlighter().highlightLine(to, smaliLines.get(i));\n\t\t\t}\n\t\t\tLOG.info(\"{} - successful sync of code to smali\", LOG.getName());\n\t\t\treturn true;\n\t\t} catch (Exception ex) {\n\t\t\tLOG.error(\"{} - Failed to sync code to smali with instruction offsets \", LOG.getName(), ex);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean syncTo(CodeArea to) {\n\t\tint caretPos = from.getCaretPosition();\n\t\tCodeMetadataRange fromMthRange = findEnclosingMethodRange(caretPos);\n\t\tif (fromMthRange == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tInteger mthDefPos = fromMthRange.getStart().getKey();\n\t\tInteger mthEndPos = fromMthRange.getEnd().getKey();\n\n\t\tLOG.debug(\"InsnOffsetJavaSyncer caretPos = {}\", caretPos);\n\t\tLOG.debug(\"InsnOffsetJavaSyncer mthDefPos = {}\", mthDefPos);\n\t\tLOG.debug(\"InsnOffsetJavaSyncer mthEndPos = {}\", mthEndPos);\n\n\t\tCodeMetadataRange fromInsnOffsetRange = findOffsetRange(caretPos, mthDefPos, mthEndPos);\n\t\tif (fromInsnOffsetRange == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tString mthID = getMthRawFullID(mthDefPos);\n\n\t\t// now search for this range within the target area\n\t\tCodeMetadataRange toMthRange = findMethodRange(mthID, to);\n\t\tif (toMthRange == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// search for the first insn offset\n\t\tint firstInsnOffset = ((InsnCodeOffset) fromInsnOffsetRange.getStart().getValue()).getOffset();\n\t\tInteger highlightPosStart = to.getCodeMetadata().searchDown(toMthRange.getStart().getKey(), (offset, ann) -> {\n\t\t\tif (ann.getAnnType() != ICodeAnnotation.AnnType.OFFSET) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tint pos = ((InsnCodeOffset) ann).getOffset();\n\t\t\tif (pos != firstInsnOffset) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn offset;\n\t\t});\n\n\t\tif (highlightPosStart == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// search for the second insn offset\n\t\tint secondInsnOffset = ((InsnCodeOffset) fromInsnOffsetRange.getEnd().getValue()).getOffset();\n\t\tInteger highlightPosEnd = to.getCodeMetadata().searchDown(highlightPosStart, (offset, ann) -> {\n\t\t\tif (ann.getAnnType() != ICodeAnnotation.AnnType.OFFSET) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tint pos = ((InsnCodeOffset) ann).getOffset();\n\t\t\tif (pos != secondInsnOffset) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn offset;\n\t\t});\n\n\t\tif (highlightPosEnd == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tto.scrollToPos(highlightPosStart);\n\t\ttry {\n\t\t\tCodeSyncHighlighter.defaultHighlighter().highlightRange(to, highlightPosStart, highlightPosEnd);\n\t\t\tLOG.info(\"{} - successful sync of code to code\", LOG.getName());\n\t\t\treturn true;\n\t\t} catch (Exception ex) {\n\t\t\tLOG.error(\"{} - Unable to highlight code area from insn offset mappings {} -> {}\", LOG.getName(), highlightPosStart,\n\t\t\t\t\thighlightPosEnd);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Nullable\n\tprivate static CodeMetadataRange findMethodRange(String mthFullRawID, CodeArea area) {\n\t\tMap.Entry<Integer, ICodeAnnotation> toMthDecl = area.getCodeMetadata().searchDown(0, (offset, ann) -> {\n\t\t\tif (ann.getAnnType() != ICodeAnnotation.AnnType.DECLARATION) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tNodeDeclareRef decl = (NodeDeclareRef) ann;\n\t\t\tICodeNodeRef node = decl.getNode();\n\t\t\tif (node.getAnnType() != ICodeAnnotation.AnnType.METHOD) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tMethodNode mth = (MethodNode) node;\n\t\t\tif (!mth.getMethodInfo().getRawFullId().equals(mthFullRawID)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn new SimpleEntry<>(offset, ann);\n\t\t});\n\n\t\tif (toMthDecl == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tMap.Entry<Integer, ICodeAnnotation> toMthEnd = area.getCodeMetadata().searchDown(toMthDecl.getKey(), (offset, ann) -> {\n\t\t\tif (ann.getAnnType() != ICodeAnnotation.AnnType.END) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn new SimpleEntry<>(offset, ann);\n\t\t});\n\n\t\tif (toMthEnd == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn new CodeMetadataRange(toMthDecl, toMthEnd);\n\t}\n\n\t@Nullable\n\tprivate CodeMetadataRange findEnclosingMethodRange(Integer startPos) {\n\t\tMap.Entry<Integer, ICodeAnnotation> mthDef = from.getCodeMetadata().searchUp(startPos, (offset, ann) -> {\n\t\t\tif (ann.getAnnType() != ICodeAnnotation.AnnType.DECLARATION) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tNodeDeclareRef decl = (NodeDeclareRef) ann;\n\t\t\tICodeNodeRef node = decl.getNode();\n\t\t\tif (node.getAnnType() != ICodeAnnotation.AnnType.METHOD) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn new SimpleEntry<>(offset, ann);\n\t\t});\n\n\t\tif (mthDef == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tMap.Entry<Integer, ICodeAnnotation> mthEnd = from.getCodeMetadata().searchDown(startPos, (offset, ann) -> {\n\t\t\tif (ann.getAnnType() != ICodeAnnotation.AnnType.END) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn new SimpleEntry<>(offset, ann);\n\t\t});\n\n\t\tif (mthEnd == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn new CodeMetadataRange(mthDef, mthEnd);\n\t}\n\n\t/**\n\t * Gets a CodeMetadataRange for the from CodeArea where start and end\n\t * are InsnCodeOffsets whose offsets are monotonically increasing.\n\t *\n\t * @param - startPos the starting position to start searching from\n\t * @param - mthDefPos the method node decl position enclosing the range\n\t * @param - mthEndPos the method end position enclosing the range\n\t */\n\t@Nullable\n\tprivate CodeMetadataRange findOffsetRange(Integer startPos, Integer mthDefPos, Integer mthEndPos) {\n\t\tMap.Entry<Integer, ICodeAnnotation> first = findInsnOffsetBeforePos(startPos, mthDefPos);\n\t\tMap.Entry<Integer, ICodeAnnotation> second = findInsnOffsetAfterPos(startPos, mthEndPos);\n\t\tif (first == null || second == null) {\n\t\t\tLOG.warn(\"{} - Unable to find InsnCodeOffsets between {} -> {}\", LOG.getName(), mthDefPos, mthEndPos);\n\t\t\treturn null;\n\t\t}\n\t\tint startOffset = ((InsnCodeOffset) first.getValue()).getOffset();\n\t\tint endOffset = ((InsnCodeOffset) second.getValue()).getOffset();\n\t\tif (startOffset > endOffset) {\n\t\t\tLOG.warn(\"{} - insn startOffset={} is greater than insn endOffset={} - cannot construct range\", LOG.getName(), startOffset,\n\t\t\t\t\tendOffset);\n\t\t\treturn null;\n\t\t}\n\t\treturn new CodeMetadataRange(first, second);\n\t}\n\n\t@Nullable\n\tprivate Map.Entry<Integer, ICodeAnnotation> findInsnOffsetBeforePos(Integer startPos, Integer limit) {\n\t\treturn from.getCodeMetadata().searchUp(startPos, (offset, ann) -> {\n\t\t\tif (offset <= limit) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (ann.getAnnType() != ICodeAnnotation.AnnType.OFFSET) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn new SimpleEntry<Integer, ICodeAnnotation>(offset, ann);\n\t\t});\n\t}\n\n\t@Nullable\n\tprivate Map.Entry<Integer, ICodeAnnotation> findInsnOffsetAfterPos(Integer startPos, Integer limit) {\n\t\treturn from.getCodeMetadata().searchDown(startPos, (offset, ann) -> {\n\t\t\tif (offset >= limit) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (ann.getAnnType() != ICodeAnnotation.AnnType.OFFSET) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn new SimpleEntry<Integer, ICodeAnnotation>(offset, ann);\n\t\t});\n\t}\n\n\t/**\n\t * Assumes that there is a NodeDeclareRef{MethodNode{}} annotation at mthDefPos in the `from`\n\t * CodeInfoMetadata\n\t */\n\tprivate String getMthRawFullID(Integer mthDefPos) {\n\t\tICodeAnnotation ann = from.getCodeMetadata().getAt(mthDefPos);\n\t\tNodeDeclareRef ref = (NodeDeclareRef) ann;\n\t\tMethodNode mth = (MethodNode) ref.getNode();\n\t\treturn mth.getMethodInfo().getRawFullId();\n\t}\n\n\t/**\n\t * Gets the mapped smali line indices for the code offsets of interest\n\t *\n\t * @param smaliMethodNode     - method of interest\n\t * @param insnCodeOffsetRange - code offset range from the caret pos\n\t * @return\n\t */\n\tprivate static List<Integer> getMappedSmaliLines(\n\t\t\tSmaliMethodNode smaliMethodNode,\n\t\t\tCodeMetadataRange insnCodeOffsetRange) {\n\t\tList<Integer> lines = new ArrayList<>();\n\t\tint startInsnCodeOffset = ((InsnCodeOffset) insnCodeOffsetRange.getStart().getValue()).getOffset();\n\t\tint endInsnCodeOffset = ((InsnCodeOffset) insnCodeOffsetRange.getEnd().getValue()).getOffset();\n\t\t// Line mappings are Line index -> Code offset\n\t\tMap<Integer, Integer> smaliLineMapping = smaliMethodNode.getLineMapping();\n\t\tLOG.debug(\"startInsnPos={}, endInsnPos={}\", startInsnCodeOffset, endInsnCodeOffset);\n\t\tfor (Map.Entry<Integer, Integer> lineToCodeOffset : smaliLineMapping.entrySet()) {\n\t\t\tLOG.debug(\"line={} -> codeOffset={}\", lineToCodeOffset.getKey(), lineToCodeOffset.getValue());\n\t\t\t// Asume code offsets from smali debug utils are the same as those in the code metadata\n\t\t\tif (lineToCodeOffset.getValue() == startInsnCodeOffset || lineToCodeOffset.getValue() == endInsnCodeOffset) {\n\t\t\t\tlines.add(lineToCodeOffset.getKey());\n\t\t\t}\n\t\t}\n\t\tCollections.sort(lines); // only two elements\n\t\treturn lines;\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/InsnOffsetSmaliSyncer.java",
    "content": "package jadx.gui.ui.codearea.sync;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.NavigableMap;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeMetadata;\nimport jadx.api.metadata.annotations.InsnCodeOffset;\nimport jadx.api.metadata.annotations.NodeDeclareRef;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.gui.device.debugger.DbgUtils;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.codearea.SmaliArea;\n\n/*\n * Use insn code offsets to sync smali to code panel area\n * This only works for Smali when the SmaliArea is showing the dalvik bytecode\n */\npublic class InsnOffsetSmaliSyncer implements IToJavaSyncStrategy {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(InsnOffsetSmaliSyncer.class);\n\n\tprivate final SmaliArea from;\n\n\tpublic InsnOffsetSmaliSyncer(SmaliArea area) {\n\t\tthis.from = area;\n\t}\n\n\t@Override\n\tpublic boolean syncTo(CodeArea to) {\n\t\tif (!from.isShowingDalvikBytecode()) {\n\t\t\t// This strategy can only be used when the debug model has been used to generate the smali.\n\t\t\t// This populates the code offsets by line as opposed to just text.\n\t\t\treturn false;\n\t\t}\n\t\t// 1. Get the code offset from the Smali caret line number\n\t\t// 2. Find the appropriate NodeDeclareRef for the method enclosed in the CodeArea annotations\n\t\t// 3. Find all code offset range intervals in the map which contain the code offset\n\t\t// 4. Get the CodeArea positions of these intervals and hightlight them in the code area\n\t\t// 5. Scroll to the first one.\n\t\tJClass jclass = from.getJClass();\n\t\tMap.Entry<String, Integer> lineInfo = DbgUtils.getCodeOffsetInfoByLine(jclass, from.getCaretLineNumber());\n\t\tif (lineInfo == null) {\n\t\t\treturn false;\n\t\t}\n\t\tInteger lineInfoPos = lineInfo.getValue();\n\t\tLOG.debug(\"lineInfo key {}, lineInfo value {}, caretLineNumber {}\", lineInfo.getKey(), lineInfo.getValue(),\n\t\t\t\tfrom.getCaretLineNumber());\n\t\tICodeMetadata toMetadata = to.getCodeMetadata();\n\t\tNavigableMap<Integer, ICodeAnnotation> codeAreaAnnotationMap =\n\t\t\t\t(NavigableMap<Integer, ICodeAnnotation>) toMetadata.getAsMap();\n\t\tIterator<NavigableMap.Entry<Integer, ICodeAnnotation>> methodDecl =\n\t\t\t\tfindMethodDeclAnnotation(codeAreaAnnotationMap, lineInfo.getKey());\n\t\tif (methodDecl == null) {\n\t\t\tLOG.warn(\"{} - No NodeDeclareRef exists for {}\", LOG.getName(), lineInfo.getKey());\n\t\t\treturn false;\n\t\t}\n\t\t// Looking through the annotations in order from the Method declaration to its end\n\t\t// compare every adjacent pair of instruction offsets where the second is greater than the first.\n\t\t// Highlight if the smali offset falls between the second and the first.\n\t\tIterator<NavigableMap.Entry<Integer, ICodeAnnotation>> it = methodDecl;\n\t\tNavigableMap.Entry<Integer, ICodeAnnotation> prev = null;\n\t\tList<CodeMetadataRange> offsetBoundariesToHighlight = new ArrayList<>();\n\t\twhile (it.hasNext()) {\n\t\t\tNavigableMap.Entry<Integer, ICodeAnnotation> entry = it.next();\n\t\t\tif (entry.getValue().getAnnType() == ICodeAnnotation.AnnType.END) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (entry.getValue().getAnnType() != ICodeAnnotation.AnnType.OFFSET) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (prev != null) {\n\t\t\t\tInsnCodeOffset currentInsnOffset = (InsnCodeOffset) entry.getValue();\n\t\t\t\tInsnCodeOffset prevInsnOffset = (InsnCodeOffset) prev.getValue();\n\t\t\t\tif (prevInsnOffset.getOffset() <= lineInfoPos && lineInfoPos <= currentInsnOffset.getOffset()) {\n\t\t\t\t\toffsetBoundariesToHighlight.add(new CodeMetadataRange(prev, entry));\n\t\t\t\t}\n\t\t\t}\n\t\t\tprev = entry;\n\t\t}\n\n\t\tif (offsetBoundariesToHighlight.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tto.scrollToPos(offsetBoundariesToHighlight.get(0).getStart().getKey());\n\n\t\ttry {\n\t\t\tfor (CodeMetadataRange cmr : offsetBoundariesToHighlight) {\n\t\t\t\tLOG.debug(\"Highlighting {}\", cmr);\n\t\t\t\tCodeSyncHighlighter.defaultHighlighter().highlightRange(to, cmr.getStart().getKey(), cmr.getEnd().getKey());\n\t\t\t}\n\t\t\tLOG.info(\"{} - successful sync of smali to code\", LOG.getName());\n\t\t\treturn true;\n\t\t} catch (Exception ex) {\n\t\t\tLOG.error(\"{} - Unable to highlight smali -> code insn offset range: {}\", LOG.getName(), ex.getLocalizedMessage());\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Find the NodeDeclareRef annotation of the method identified by smaliLineMthFullID\n\t *\n\t * @param map                the annotation map from the CodeArea\n\t * @param smaliLineMthFullID the raw full method ID to look for\n\t * @return iterator to the entry in the annotation map\n\t */\n\t@Nullable\n\tprivate static Iterator<NavigableMap.Entry<Integer, ICodeAnnotation>> findMethodDeclAnnotation(\n\t\t\tNavigableMap<Integer, ICodeAnnotation> map,\n\t\t\tString smaliLineMthFullID) {\n\t\t// Ensure we use NavigableMap here to get ordering guarantee from iterator call\n\t\tIterator<NavigableMap.Entry<Integer, ICodeAnnotation>> it = map.descendingMap().entrySet().iterator();\n\t\twhile (it.hasNext()) {\n\t\t\tNavigableMap.Entry<Integer, ICodeAnnotation> entry = it.next();\n\t\t\tif (entry.getValue() instanceof NodeDeclareRef) {\n\t\t\t\tNodeDeclareRef nodeDeclareRef = (NodeDeclareRef) entry.getValue();\n\t\t\t\tif (nodeDeclareRef.getNode() instanceof MethodNode) {\n\t\t\t\t\tMethodNode mth = (MethodNode) nodeDeclareRef.getNode();\n\t\t\t\t\tif (mth.getMethodInfo().getRawFullId().equals(smaliLineMthFullID)) {\n\t\t\t\t\t\treturn it;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/JavaSyncer.java",
    "content": "package jadx.gui.ui.codearea.sync;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.codearea.SmaliArea;\n\n/**\n * Syncs a Java code panel area (Java/Simple/Fallback) to another area\n */\npublic class JavaSyncer implements CodePanelSyncer {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JavaSyncer.class);\n\n\tprivate final DebugLineJavaSyncer debugLineSyncer;\n\tprivate final InsnOffsetJavaSyncer insnOffsetSyncer;\n\n\tpublic JavaSyncer(CodeArea area) {\n\t\tthis.debugLineSyncer = new DebugLineJavaSyncer(area);\n\t\tthis.insnOffsetSyncer = new InsnOffsetJavaSyncer(area);\n\t}\n\n\t@Override\n\tpublic boolean syncTo(CodeArea to) {\n\t\treturn debugLineSyncer.syncTo(to) || insnOffsetSyncer.syncTo(to);\n\t}\n\n\t@Override\n\tpublic boolean syncTo(SmaliArea to) {\n\t\treturn debugLineSyncer.syncTo(to) || insnOffsetSyncer.syncTo(to);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/SmaliSyncer.java",
    "content": "package jadx.gui.ui.codearea.sync;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.codearea.SmaliArea;\n\n/**\n * Syncs a Smali code panel area to another area\n */\npublic class SmaliSyncer implements CodePanelSyncer {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SmaliSyncer.class);\n\n\tprivate final SmaliArea from;\n\tprivate final InsnOffsetSmaliSyncer insnOffsetSyncer;\n\tprivate final DebugLineSmaliSyncer debugLineSyncer;\n\n\tpublic SmaliSyncer(SmaliArea area) {\n\t\tthis.from = area;\n\t\tthis.insnOffsetSyncer = new InsnOffsetSmaliSyncer(area);\n\t\tthis.debugLineSyncer = new DebugLineSmaliSyncer(area);\n\t}\n\n\t@Override\n\tpublic boolean syncTo(CodeArea to) {\n\t\t// first try debug lines then insn offsets\n\t\treturn debugLineSyncer.syncTo(to) || insnOffsetSyncer.syncTo(to);\n\t}\n\n\t@Override\n\tpublic boolean syncTo(SmaliArea to) {\n\t\tif (from.isShowingDalvikBytecode() == to.isShowingDalvikBytecode()) {\n\t\t\t// smali -> smali just highlight the current line but only if content is the same\n\t\t\tto.scrollToPos(from.getLineStartOffsetOfCurrentLine());\n\t\t}\n\t\treturn true; // Prevent fallback syncing\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/fallback/AbstractCodeAreaLine.java",
    "content": "package jadx.gui.ui.codearea.sync.fallback;\n\nimport javax.swing.text.BadLocationException;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.gui.ui.codearea.AbstractCodeArea;\n\nabstract class AbstractCodeAreaLine {\n\tprivate final AbstractCodeArea area;\n\tprivate final int lineIndex;\n\tprivate final String line;\n\n\tprotected AbstractCodeAreaLine(AbstractCodeArea area, int lineIndex) throws BadLocationException {\n\t\tthis.area = area;\n\t\tthis.lineIndex = lineIndex;\n\t\tthis.line = this.area.getText().split(\"\\\\R\")[lineIndex];\n\t}\n\n\tpublic AbstractCodeArea getArea() {\n\t\treturn area;\n\t}\n\n\tpublic int getLineIndex() {\n\t\treturn lineIndex;\n\t}\n\n\tpublic String getStr() {\n\t\treturn line;\n\t}\n\n\tpublic String getTrimmedStr() {\n\t\treturn line.trim();\n\t}\n\n\tpublic abstract AbstractCodeAreaLine getLineAt(int lineIndex) throws BadLocationException;\n\n\tpublic abstract boolean isClassDeclaration();\n\n\tpublic abstract boolean isMethodOrConstructorDeclaration();\n\n\tpublic abstract boolean isFieldDeclaration();\n\n\t@Nullable\n\tpublic abstract String extractDeclaredMethodName();\n\n\t@Nullable\n\tpublic abstract String extractDeclaredClassName();\n\n\tprotected abstract MethodDeclaration createMethodDeclaration() throws FallbackSyncException;\n\n\t/**\n\t * This could be itself or:\n\t * - the enclosing method delcaration if line is in a method\n\t * - the enclosing class declaration if line is a field declaration\n\t */\n\tpublic IDeclaration getEnclosingScopeDeclaration() throws BadLocationException, FallbackSyncException {\n\t\tIDeclaration decl = this.getDeclaration();\n\t\tif (decl != null) {\n\t\t\treturn decl;\n\t\t}\n\t\tfor (int i = lineIndex - 1; i >= 0; i--) {\n\t\t\tAbstractCodeAreaLine line = getLineAt(i);\n\t\t\tboolean enclosingDecl = line.isScopeDeclarationLine();\n\t\t\tif (enclosingDecl) {\n\t\t\t\treturn line.getDeclaration();\n\t\t\t}\n\t\t}\n\t\tthrow new FallbackSyncException(\"No enclosing declaration found for \" + this);\n\t}\n\n\tpublic boolean isScopeDeclarationLine() {\n\t\treturn isClassDeclaration() || isMethodOrConstructorDeclaration();\n\t}\n\n\tpublic boolean isDeclarationLine() {\n\t\treturn isScopeDeclarationLine() || isFieldDeclaration();\n\t}\n\n\t@Nullable\n\tpublic IDeclaration getDeclaration() throws FallbackSyncException {\n\t\tif (isClassDeclaration()) {\n\t\t\treturn new ClassDeclaration(this);\n\t\t}\n\t\tif (isMethodOrConstructorDeclaration()) {\n\t\t\treturn createMethodDeclaration();\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn line;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/fallback/AbstractCodeAreaToken.java",
    "content": "package jadx.gui.ui.codearea.sync.fallback;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport javax.swing.text.BadLocationException;\n\nimport jadx.gui.ui.codearea.AbstractCodeArea;\n\npublic abstract class AbstractCodeAreaToken {\n\tprotected final AbstractCodeArea area;\n\tprivate final int atPos;\n\tprotected int startPos;\n\tprotected int length;\n\n\tprotected AbstractCodeAreaToken(AbstractCodeArea area, int at) throws BadLocationException, FallbackSyncException {\n\t\tthis.area = area;\n\t\tthis.atPos = at;\n\t\tthis.extractTokenAt();\n\t}\n\n\tpublic int getAtPos() {\n\t\treturn atPos;\n\t}\n\n\tpublic String getStr() throws BadLocationException {\n\t\treturn area.getText(this.startPos, this.length);\n\t}\n\n\tpublic boolean isMethodConstructorDeclarationOrCall() throws BadLocationException {\n\t\treturn area.getText(this.startPos + this.length, 1).equals(\"(\");\n\t}\n\n\t// Class field reference within a method\n\tpublic abstract boolean isFieldReference() throws BadLocationException;\n\n\t// Class field token in class field declaration\n\tpublic abstract boolean isClassField() throws BadLocationException;\n\n\tpublic abstract AbstractCodeAreaLine getLine() throws BadLocationException;\n\n\t// Helper to extract token under caret (at pos)\n\tprivate void extractTokenAt() throws FallbackSyncException, BadLocationException {\n\t\tString text = area.getText();\n\t\tif (text == null || text.isEmpty()) {\n\t\t\tthrow new FallbackSyncException(\"text area is null or empty\");\n\t\t}\n\t\t// Find word boundaries around caretPos\n\t\tint start = atPos;\n\t\tint end = atPos;\n\n\t\twhile (start > 0 && Character.isJavaIdentifierPart(text.charAt(start - 1))) {\n\t\t\tstart--;\n\t\t}\n\t\twhile (end < text.length() && Character.isJavaIdentifierPart(text.charAt(end))) {\n\t\t\tend++;\n\t\t}\n\t\tif (start == end) {\n\t\t\t// No identifier found, try string literal at caret line\n\t\t\tint line = area.getLineOfOffset(atPos);\n\t\t\tString lineText = area.getText(area.getLineStartOffset(line), area.getLineEndOffset(line) - area.getLineStartOffset(line));\n\t\t\tPattern p = Pattern.compile(\"\\\"([^\\\"]*)\\\"\");\n\t\t\tMatcher m = p.matcher(lineText);\n\t\t\twhile (m.find()) {\n\t\t\t\tint litStart = area.getLineStartOffset(line) + m.start(1);\n\t\t\t\tint litEnd = area.getLineStartOffset(line) + m.end(1);\n\t\t\t\tif (atPos >= litStart && atPos <= litEnd) {\n\t\t\t\t\tthis.startPos = m.start(1);\n\t\t\t\t\tthis.length = m.end(1) - m.start(1);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new FallbackSyncException(\"Unable to extract token at position \" + atPos);\n\t\t}\n\t\tthis.startPos = start;\n\t\tthis.length = end - start;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/fallback/ClassDeclaration.java",
    "content": "package jadx.gui.ui.codearea.sync.fallback;\n\nimport java.util.Objects;\n\npublic class ClassDeclaration implements IDeclaration {\n\tprivate final AbstractCodeAreaLine line;\n\tprivate final String name;\n\n\tpublic ClassDeclaration(AbstractCodeAreaLine line) throws FallbackSyncException {\n\t\tthis.name = line.extractDeclaredClassName();\n\t\tif (this.name == null) {\n\t\t\tthrow new FallbackSyncException(\"line does not declare a class: \" + toString());\n\t\t}\n\t\tthis.line = line;\n\t}\n\n\t@Override\n\tpublic String getIdentifyingName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic AbstractCodeAreaLine getLine() {\n\t\treturn line;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (o instanceof ClassDeclaration) {\n\t\t\tClassDeclaration cd = (ClassDeclaration) o;\n\t\t\treturn this.getIdentifyingName().equals(cd.getIdentifyingName());\n\t\t}\n\t\treturn false;\n\t}\n\n\t// Not necessary but removes checkstyle warning\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(line, name);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/fallback/FallbackSyncException.java",
    "content": "package jadx.gui.ui.codearea.sync.fallback;\n\npublic class FallbackSyncException extends Exception {\n\tpublic FallbackSyncException(String msg) {\n\t\tsuper(msg);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/fallback/FallbackSyncer.java",
    "content": "package jadx.gui.ui.codearea.sync.fallback;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport javax.swing.text.BadLocationException;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.ui.codearea.AbstractCodeArea;\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.ui.codearea.CodePanel;\nimport jadx.gui.ui.codearea.SmaliArea;\nimport jadx.gui.ui.codearea.sync.CodeSyncHighlighter;\n\n/**\n * Regex/String based sync strategy of toPanel when clicking in fromPanel\n * Summary of syncing strategy:\n * 1) Look for an identifying class member token under the caret position.\n * 2) If found look for the enclosing method or class declaration.\n * 3) If the line is a declaration line, find the equivalent line in the other code panel.\n * 4) Otherwise find the nth occurence of the token in the enclosing method/class in the other code\n * panel.\n * The following are not yet supported:\n * - generic classes/methods\n * - anonymous classes\n * - lambda functions\n * - constructors\n */\npublic class FallbackSyncer {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(FallbackSyncer.class);\n\n\tpublic static boolean sync(CodePanel fromPanel, CodePanel toPanel) throws BadLocationException, Exception {\n\t\tLOG.debug(\"FALLBACK SYNC START\");\n\t\ttry {\n\t\t\tAbstractCodeArea from = fromPanel.getCodeArea();\n\t\t\tAbstractCodeArea to = toPanel.getCodeArea();\n\n\t\t\tint caretPos = from.getCaretPosition();\n\t\t\tint lineIndex = from.getLineOfOffset(caretPos);\n\t\t\tString[] fromLines = from.getText().split(\"\\\\R\");\n\t\t\tif (lineIndex >= fromLines.length) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tString caretLine = fromLines[lineIndex];\n\t\t\tLOG.debug(\"Caret line [{}]: {}\", caretPos, caretLine);\n\n\t\t\t// Extract token under caret (string literal or identifier)\n\t\t\tAbstractCodeAreaToken areaToken = FallbackSyncer.getToken(from, caretPos);\n\t\t\tString token = areaToken.getStr();\n\t\t\tLOG.debug(\"Token at caret: '{}'\", token);\n\t\t\tif (token == null || token.isEmpty()) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (!allowSync(areaToken)) {\n\t\t\t\tLOG.debug(\"Fallback matching only applicable for variable, classname, field or method tokens\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn syncToIdentifyingNthOccurence(areaToken, to);\n\t\t} finally {\n\t\t\tLOG.debug(\"FALLBACK SYNC END\");\n\t\t}\n\t}\n\n\t// This function just serves as a way to create the correct Token type\n\t// FallbackSyncer should be refactored to use CodePanelSyncer\n\tprivate static AbstractCodeAreaToken getToken(AbstractCodeArea from, int caretPos) throws BadLocationException, FallbackSyncException {\n\t\tif (from instanceof SmaliArea) {\n\t\t\treturn new SmaliAreaToken((SmaliArea) from, caretPos);\n\t\t}\n\t\tif (from instanceof CodeArea) {\n\t\t\treturn new JavaCodeAreaToken((CodeArea) from, caretPos);\n\t\t}\n\t\tthrow new FallbackSyncException(\"Unknown AbstractCodeArea type for \" + from);\n\t}\n\n\t/**\n\t * Looks for the nth occurence of the token in the enclosing class/method scope in the `to` area.\n\t * If found, sync to it in the `to` area.\n\t */\n\tprivate static boolean syncToIdentifyingNthOccurence(AbstractCodeAreaToken sourceToken, AbstractCodeArea to)\n\t\t\tthrows BadLocationException, FallbackSyncException {\n\t\tAbstractCodeAreaLine tokenLine = sourceToken.getLine();\n\n\t\t// Locate the method/class declaration line for context\n\t\tIDeclaration fromDeclaration = tokenLine.getEnclosingScopeDeclaration();\n\t\tif (fromDeclaration == null) {\n\t\t\tLOG.warn(\"Unable to find declaration line above {}\", tokenLine);\n\t\t\treturn false;\n\t\t}\n\t\tAbstractCodeAreaLine fromDeclaringLine = fromDeclaration.getLine();\n\n\t\tAbstractCodeArea from = fromDeclaringLine.getArea();\n\t\tString declarationLineStr = fromDeclaringLine.getStr();\n\t\tLOG.debug(\"Found declaration line: {}\", declarationLineStr);\n\t\tString nameToFind = fromDeclaration.getIdentifyingName();\n\t\tif (nameToFind == null || nameToFind.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Determine whether we're matching a class or method\n\t\tboolean isClass = fromDeclaringLine.isClassDeclaration();\n\t\tString regex = isClass\n\t\t\t\t? generateClassRegex(nameToFind)\n\t\t\t\t: generateMethodRegex(nameToFind);\n\n\t\t// Find the declaration in target text\n\t\tMatcher matcher = Pattern.compile(regex).matcher(to.getText());\n\t\tLOG.debug(\"Searching for {} in targetText, isClass {}\", nameToFind, isClass);\n\t\tAbstractCodeAreaLine targetDeclLine = findTargetDeclaringLine(to, matcher, fromDeclaration);\n\t\tif (targetDeclLine == null) {\n\t\t\tLOG.debug(\"Cannot find target declaration line\");\n\t\t\treturn false;\n\t\t}\n\t\tint targetDeclarationLineIndex = targetDeclLine.getLineIndex();\n\t\tLOG.debug(\"Target declaration line {}\", targetDeclLine.getStr());\n\t\tif (tokenLine.isScopeDeclarationLine()) {\n\t\t\tCodeSyncHighlighter.defaultHighlighter().highlightAndScrollToLine(to, targetDeclarationLineIndex);\n\t\t\tLOG.info(\"{} - Highlighted target declaration line\", LOG.getName(), targetDeclLine.getStr());\n\t\t\treturn true;\n\t\t}\n\n\t\t// Extract the method/class body from target\n\t\tString methodBody = extractMethodBody(to, matcher.start());\n\n\t\t// Find nth occurrence of token in source method\n\t\t// Extract method body from source (to count occurrences)\n\t\tMatcher fromMatcher = Pattern.compile(regex).matcher(from.getText());\n\t\tif (!fromMatcher.find()) {\n\t\t\tLOG.debug(\"No method/class match found in source for regex: {}\", regex);\n\t\t\treturn false;\n\t\t}\n\t\tString sourceMethodBody = extractMethodBody(from, fromMatcher.start());\n\n\t\t// Count which occurrence of token the caret corresponds to in the source method body\n\t\tString tokenStr = sourceToken.getStr();\n\t\tint caretPos = sourceToken.getAtPos();\n\t\tint caretOffsetInMethod = caretPos - fromMatcher.start();\n\t\tint nthOccurrence = 0;\n\t\tPattern tokenPattern = Pattern.compile(\"\\\"\" + Pattern.quote(tokenStr) + \"\\\"|\\\\b\" + Pattern.quote(tokenStr) + \"\\\\b\");\n\t\tMatcher tokenMatcher = tokenPattern.matcher(sourceMethodBody);\n\n\t\twhile (tokenMatcher.find()) {\n\t\t\tif (tokenMatcher.start() > caretOffsetInMethod) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tnthOccurrence++;\n\t\t}\n\n\t\tLOG.debug(\"Caret is at occurrence number: {}\", nthOccurrence);\n\n\t\t// Now find nth occurrence of token in target method body\n\t\ttokenMatcher = tokenPattern.matcher(methodBody);\n\t\tint occurrenceCount = 0;\n\t\twhile (tokenMatcher.find()) {\n\t\t\toccurrenceCount++;\n\t\t\tif (occurrenceCount == nthOccurrence) {\n\t\t\t\t// Find absolute offset of this line in targetText\n\t\t\t\tint tokenPosInMethod = tokenMatcher.start();\n\t\t\t\tint absoluteOffset = matcher.start() + tokenPosInMethod;\n\n\t\t\t\t// Find line start and end offset in target\n\t\t\t\tint tokenLineIndex = to.getLineOfOffset(absoluteOffset);\n\t\t\t\tCodeSyncHighlighter.defaultHighlighter().highlightAndScrollToLine(to, tokenLineIndex);\n\t\t\t\tLOG.info(\"{} - Highlighted token '{}' at nth occurrence: {}\", LOG.getName(), tokenStr, nthOccurrence);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tLOG.debug(\"No matching token or instruction found in method: {}\", nameToFind);\n\t\treturn false;\n\t}\n\n\tprivate static AbstractCodeAreaLine findTargetDeclaringLine(\n\t\t\tAbstractCodeArea to, // target area\n\t\t\tMatcher matcher, // matcher to search for method/ctor name\n\t\t\tIDeclaration sourceDecl // source decl to match against\n\t) throws BadLocationException, FallbackSyncException {\n\t\t// Find the declaration in target text\n\t\twhile (matcher.find()) {\n\t\t\tLOG.debug(\"Match found at offset: {}\", matcher.start());\n\t\t\tint targetDeclarationLineIndex = to.getLineOfOffset(matcher.start());\n\t\t\tAbstractCodeAreaLine toDeclCandidate = getLine(to, targetDeclarationLineIndex);\n\t\t\tif (!toDeclCandidate.isScopeDeclarationLine()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tIDeclaration targetDecl = toDeclCandidate.getDeclaration();\n\t\t\tif (sourceDecl.equals(targetDecl)) {\n\t\t\t\treturn toDeclCandidate;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t// Similar with the function above if refactored to use the CodePanelSyncer Abstraction we can\n\t// remove this.\n\tprivate static AbstractCodeAreaLine getLine(AbstractCodeArea area, int lineIndex) throws BadLocationException, FallbackSyncException {\n\t\tif (area instanceof SmaliArea) {\n\t\t\treturn new SmaliAreaLine((SmaliArea) area, lineIndex);\n\t\t}\n\t\tif (area instanceof CodeArea) {\n\t\t\treturn new JavaCodeAreaLine((CodeArea) area, lineIndex);\n\t\t}\n\t\tthrow new FallbackSyncException(\"Unknown AbstractCodeArea type for \" + area);\n\t}\n\n\tprivate static boolean allowSync(AbstractCodeAreaToken areaToken) throws BadLocationException {\n\t\tboolean isOnDeclarationLine = areaToken.getLine().isDeclarationLine();\n\t\treturn isOnDeclarationLine\n\t\t\t\t|| areaToken.isClassField()\n\t\t\t\t|| areaToken.isFieldReference()\n\t\t\t\t|| areaToken.isMethodConstructorDeclarationOrCall();\n\t}\n\n\tprivate static String generateClassRegex(String name) {\n\t\treturn \"\\\\b(class|interface|enum)\\\\s+\" + Pattern.quote(name) + \"\\\\b\" // java\n\t\t\t\t+ \"|\"\n\t\t\t\t+ \"\\\\.class.*L.*\" + Pattern.quote(name) + \";\" // smali text\n\t\t\t\t+ \"|\"\n\t\t\t\t+ \"Class:\\\\sL.*\" + Pattern.quote(name) + \";\"; // smali + dalvik\n\t}\n\n\tprivate static String generateMethodRegex(String name) {\n\t\treturn \"\\\\b\" + Pattern.quote(name) + \"\\\\s*\\\\(\" // java like\n\t\t\t\t+ \"|\"\n\t\t\t\t+ \"\\\\.method.*\" + Pattern.quote(name) + \"\\\\s*\\\\(\"; // smali\n\t}\n\n\tprivate static String extractMethodBody(AbstractCodeArea area, int startIndex) {\n\t\tString text = area.getText();\n\t\tif (area instanceof SmaliArea) {\n\t\t\tint end = text.indexOf(\".end method\", startIndex);\n\t\t\treturn end != -1 ? text.substring(startIndex, end + \".end method\".length()) : text.substring(startIndex);\n\t\t} else {\n\t\t\tint brace = 0;\n\t\t\tboolean inMethod = false;\n\t\t\tfor (int i = startIndex; i < text.length(); i++) {\n\t\t\t\tchar c = text.charAt(i);\n\t\t\t\tif (c == '{') {\n\t\t\t\t\tbrace++;\n\t\t\t\t\tinMethod = true;\n\t\t\t\t} else if (c == '}') {\n\t\t\t\t\tbrace--;\n\t\t\t\t\tif (brace == 0 && inMethod) {\n\t\t\t\t\t\treturn text.substring(startIndex, i + 1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn text.substring(startIndex);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/fallback/IDeclaration.java",
    "content": "package jadx.gui.ui.codearea.sync.fallback;\n\ninterface IDeclaration {\n\tString getIdentifyingName();\n\n\tAbstractCodeAreaLine getLine();\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/fallback/JavaCodeAreaLine.java",
    "content": "package jadx.gui.ui.codearea.sync.fallback;\n\nimport javax.swing.text.BadLocationException;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.ui.codearea.CodeArea;\n\npublic class JavaCodeAreaLine extends AbstractCodeAreaLine {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JavaCodeAreaLine.class);\n\n\tpublic JavaCodeAreaLine(CodeArea area, int lineIndex) throws BadLocationException {\n\t\tsuper(area, lineIndex);\n\t}\n\n\t@Override\n\tpublic AbstractCodeAreaLine getLineAt(int lineIndex) throws BadLocationException {\n\t\treturn new JavaCodeAreaLine((CodeArea) getArea(), lineIndex);\n\t}\n\n\t@Override\n\tpublic boolean isClassDeclaration() {\n\t\treturn getTrimmedStr().matches(\".*\\\\b(class|interface|enum)\\\\b.*\\\\{\");\n\t}\n\n\t@Override\n\tpublic boolean isMethodOrConstructorDeclaration() {\n\t\tString l = getTrimmedStr();\n\t\t// Skip control-flow constructs (to avoid matching 'if', 'for', etc.)\n\t\t// WARNING - we are relying on the code gen format output of jadx here and that it is trimmed.\n\t\t// it also assumes that jadx will never output two statements on the same line separated by ';'\n\t\tif (l.startsWith(\"if \")\n\t\t\t\t|| l.startsWith(\"for \")\n\t\t\t\t|| l.startsWith(\"while \")\n\t\t\t\t|| l.startsWith(\"switch \")\n\t\t\t\t|| l.startsWith(\"case \")\n\t\t\t\t|| l.startsWith(\"break \")\n\t\t\t\t|| l.startsWith(\"default \")\n\t\t\t\t|| l.startsWith(\"} else if \")\n\t\t\t\t|| l.startsWith(\"} else \")\n\t\t\t\t|| l.startsWith(\"try \")\n\t\t\t\t|| l.startsWith(\"} catch \")\n\t\t\t\t|| l.startsWith(\"} finally \")\n\t\t\t\t|| l.startsWith(\"throw \")\n\t\t\t\t|| l.startsWith(\"do \")\n\t\t\t\t|| l.startsWith(\"synchronized \")) {\n\t\t\treturn false;\n\t\t}\n\t\tboolean hasParens = l.contains(\"(\") && l.contains(\")\");\n\t\tboolean isDefined = l.endsWith(\"{\");\n\t\tboolean isAbstract = l.contains(\"abstract\") && l.endsWith(\";\");\n\t\treturn hasParens && (isDefined || isAbstract);\n\t}\n\n\t@Override\n\tpublic boolean isFieldDeclaration() {\n\t\ttry {\n\t\t\tIDeclaration enclosingDeclaration = getEnclosingScopeDeclaration();\n\t\t\tif (!(enclosingDeclaration instanceof ClassDeclaration)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tString line = getTrimmedStr();\n\t\t\t// This may also include fields which are anonymous classes or lambdas\n\t\t\treturn line.endsWith(\";\") || line.contains(\" = \");\n\t\t} catch (Exception ex) {\n\t\t\tLOG.error(\"{} - Unable to determine if line is a field declaration\", LOG.getName(), ex);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic final @Nullable String extractDeclaredClassName() {\n\t\tif (!isClassDeclaration()) {\n\t\t\treturn null;\n\t\t}\n\t\tString[] tokens = getTrimmedStr().split(\"\\\\s+\");\n\t\tfor (int i = 0; i < tokens.length; i++) {\n\t\t\tif (tokens[i].equals(\"class\") || tokens[i].equals(\"interface\") || tokens[i].equals(\"enum\")) {\n\t\t\t\tif (i + 1 < tokens.length) {\n\t\t\t\t\treturn tokens[i + 1];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic @Nullable String extractDeclaredMethodName() {\n\t\tif (!isMethodOrConstructorDeclaration()) {\n\t\t\treturn null;\n\t\t}\n\t\tint paren = getTrimmedStr().indexOf('(');\n\t\tString before = getTrimmedStr().substring(0, paren).trim();\n\t\tString[] parts = before.split(\"\\\\s+\");\n\t\treturn parts[parts.length - 1]; // last token\n\t}\n\n\t@Override\n\tprotected MethodDeclaration createMethodDeclaration() throws FallbackSyncException {\n\t\treturn MethodDeclaration.create(this);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/fallback/JavaCodeAreaToken.java",
    "content": "package jadx.gui.ui.codearea.sync.fallback;\n\nimport javax.swing.text.BadLocationException;\n\nimport jadx.gui.ui.codearea.CodeArea;\n\npublic class JavaCodeAreaToken extends AbstractCodeAreaToken {\n\tpublic JavaCodeAreaToken(CodeArea area, int at) throws BadLocationException, FallbackSyncException {\n\t\tsuper(area, at);\n\t}\n\n\t@Override\n\tpublic boolean isClassField() throws BadLocationException {\n\t\tAbstractCodeAreaLine line = getLine();\n\t\tif (!line.isFieldDeclaration()) {\n\t\t\treturn false;\n\t\t}\n\t\t// assignment immediately follows the token\n\t\tif (line.getStr().contains(\"=\")) {\n\t\t\treturn area.getText(this.startPos + this.length, 2).equals(\" =\");\n\t\t}\n\t\t// ends with ';'\n\t\treturn area.getText(this.startPos + this.length, 1).equals(\";\");\n\t}\n\n\t@Override\n\tpublic boolean isFieldReference() throws BadLocationException {\n\t\treturn area.getText(this.startPos - 5, 5).equals(\"this.\");\n\t}\n\n\t@Override\n\tpublic AbstractCodeAreaLine getLine() throws BadLocationException {\n\t\treturn new JavaCodeAreaLine((CodeArea) area, area.getLineOfOffset(getAtPos()));\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/fallback/MethodDeclaration.java",
    "content": "package jadx.gui.ui.codearea.sync.fallback;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport jadx.core.utils.Utils;\n\nclass MethodDeclaration implements IDeclaration {\n\tprivate final AbstractCodeAreaLine line;\n\tprivate final Type returnType;\n\tprivate final List<Type> argTypes;\n\tprivate final String name;\n\n\tboolean isStatic;\n\n\tpublic static MethodDeclaration create(JavaCodeAreaLine line) throws FallbackSyncException {\n\t\tString methodName = line.extractDeclaredMethodName();\n\t\tif (methodName == null) {\n\t\t\tthrow new FallbackSyncException(\"no method name found in java declaration\");\n\t\t}\n\n\t\t// Get the return string\n\t\tString trimmed = line.getTrimmedStr();\n\t\tint methodNameStartPos = trimmed.indexOf(methodName);\n\t\t// -2 to jump to last char of return type\n\t\t// +1 to get to first char of return type\n\t\tint returnTypeStartPos = trimmed.lastIndexOf(' ', methodNameStartPos - 2) + 1;\n\t\treturnTypeStartPos = returnTypeStartPos > -1 ? returnTypeStartPos : 0;\n\t\tString returnStr = trimmed.substring(returnTypeStartPos, methodNameStartPos - 1);\n\n\t\t// Get the arg types\n\t\tString argString = trimmed.substring(trimmed.indexOf('(') + 1, trimmed.indexOf(')'));\n\t\tString[] argStringParts = argString.split(\", \");\n\t\tList<String> argTypeStrings = new ArrayList<>();\n\t\tfor (int i = 0; i < argStringParts.length; i++) {\n\t\t\tString part = argStringParts[i];\n\t\t\tif (part.isEmpty()) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\targTypeStrings.add(part.substring(0, part.indexOf(\" \")));\n\t\t}\n\n\t\tboolean isStatic = trimmed.contains(\"static \");\n\n\t\tList<Type> argTypes = argTypeStrings.stream().map(s -> Type.fromJavaName(s)).collect(Collectors.toList());\n\t\treturn new MethodDeclaration(line, Type.fromJavaName(returnStr), argTypes, isStatic, methodName);\n\t}\n\n\tpublic static MethodDeclaration create(SmaliAreaLine line) throws FallbackSyncException {\n\t\tString methodName = line.extractDeclaredMethodName();\n\t\tif (methodName == null) {\n\t\t\tthrow new FallbackSyncException(\"no method name found in smali declaration\");\n\t\t}\n\n\t\t// Get the return string\n\t\tString trimmed = line.getTrimmedStr();\n\t\tString returnStr = trimmed.substring(trimmed.indexOf(')') + 1);\n\t\treturnStr = returnStr.endsWith(\";\") ? returnStr.substring(0, returnStr.length() - 1) : returnStr;\n\n\t\tboolean isStatic = trimmed.contains(\"static \");\n\n\t\treturn new MethodDeclaration(line, Type.fromSmaliName(returnStr), parseSmaliArgs(trimmed), isStatic, methodName);\n\t}\n\n\tprivate MethodDeclaration(AbstractCodeAreaLine line, Type returnType, List<Type> argTypes, boolean isStatic, String name) {\n\t\tthis.line = line;\n\t\tthis.returnType = returnType;\n\t\tthis.argTypes = argTypes;\n\t\tthis.isStatic = isStatic;\n\t\tthis.name = name;\n\t}\n\n\t@Override\n\tpublic String getIdentifyingName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic AbstractCodeAreaLine getLine() {\n\t\treturn line;\n\t}\n\n\tprivate static List<Type> parseSmaliArgs(String lineStr) {\n\t\tList<String> argTypeStrings = new ArrayList<>();\n\t\tString argString = lineStr.substring(lineStr.indexOf('(') + 1, lineStr.indexOf(')'));\n\t\tfor (int i = 0; i < argString.length();) {\n\t\t\tchar c = argString.charAt(i);\n\t\t\tif (c == 'L') {\n\t\t\t\tint j = i;\n\t\t\t\tfor (; j < argString.length(); ++j) {\n\t\t\t\t\tif (argString.charAt(j) == ';') {\n\t\t\t\t\t\targTypeStrings.add(argString.substring(i, j + 1));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti = j + 1;\n\t\t\t} else if (c == '[') {\n\t\t\t\targTypeStrings.add(argString.substring(i, i + 2));\n\t\t\t\ti += 2;\n\t\t\t} else if (c != ' ') {\n\t\t\t\targTypeStrings.add(argString.substring(i, i + 1));\n\t\t\t\t++i;\n\t\t\t} else {\n\t\t\t\t++i;\n\t\t\t}\n\t\t}\n\t\treturn argTypeStrings.stream().map(s -> Type.fromSmaliName(s)).collect(Collectors.toList());\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (o instanceof MethodDeclaration) {\n\t\t\tMethodDeclaration decl = (MethodDeclaration) o;\n\t\t\tif (!decl.name.equals(this.name)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (decl.isStatic != this.isStatic) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!decl.returnType.equals(this.returnType)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (decl.argTypes.size() != this.argTypes.size()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfor (int i = 0; i < decl.argTypes.size(); ++i) {\n\t\t\t\tif (!decl.argTypes.get(i).equals(this.argTypes.get(i))) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t// Not necessary but removes checkstyle warning\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(name, isStatic, returnType, argTypes);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"NAME=\").append(name).append(\"+++\")\n\t\t\t\t.append(\"RETURN=\").append(returnType).append(\"+++\")\n\t\t\t\t.append(\"ARGS=\");\n\t\tfor (final var a : argTypes) {\n\t\t\tsb.append(a).append(\",\");\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tprivate static class Type {\n\t\tprivate String smaliName;\n\t\tprivate String javaName;\n\n\t\tpublic static Type fromJavaName(String name) {\n\t\t\treturn new Type(Utils.javaNameToSmaliName(name), name);\n\t\t}\n\n\t\tpublic static Type fromSmaliName(String name) {\n\t\t\treturn new Type(name, Utils.smaliNameToJavaName(name));\n\t\t}\n\n\t\tprivate Type(String smaliName, String javaName) {\n\t\t\tthis.smaliName = smaliName;\n\t\t\tthis.javaName = javaName;\n\t\t}\n\n\t\tprivate boolean isNonPrimitive() {\n\t\t\treturn smaliName.startsWith(\"L\");\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (o instanceof Type) {\n\t\t\t\tType t = (Type) o;\n\t\t\t\tif (t.isNonPrimitive() || this.isNonPrimitive()) {\n\t\t\t\t\t// One of them might be missing the package prefix\n\t\t\t\t\treturn t.javaName.endsWith(this.javaName)\n\t\t\t\t\t\t\t|| this.javaName.endsWith(t.javaName);\n\t\t\t\t}\n\t\t\t\treturn t.javaName.equals(this.javaName)\n\t\t\t\t\t\t|| t.smaliName.equals(this.smaliName);\n\t\t\t\t// Slightly less strict - should think about this more\n\t\t\t\t// && t.smaliName.equals(this.smaliName);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t// Not necessary but removes checkstyle warning\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(this, javaName, smaliName);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"@\" + smaliName + \"-OR-\" + javaName + \"@\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/fallback/SmaliAreaLine.java",
    "content": "package jadx.gui.ui.codearea.sync.fallback;\n\nimport javax.swing.text.BadLocationException;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.gui.ui.codearea.SmaliArea;\n\npublic class SmaliAreaLine extends AbstractCodeAreaLine {\n\tpublic SmaliAreaLine(SmaliArea area, int lineIndex) throws BadLocationException {\n\t\tsuper(area, lineIndex);\n\t}\n\n\t@Override\n\tpublic AbstractCodeAreaLine getLineAt(int lineIndex) throws BadLocationException {\n\t\treturn new SmaliAreaLine((SmaliArea) getArea(), lineIndex);\n\t}\n\n\t@Override\n\tpublic boolean isClassDeclaration() {\n\t\treturn getTrimmedStr().startsWith(\"Class: \") || getTrimmedStr().startsWith(\".class \");\n\t}\n\n\t@Override\n\tpublic boolean isMethodOrConstructorDeclaration() {\n\t\treturn getTrimmedStr().startsWith(\".method\");\n\t}\n\n\t@Override\n\tpublic boolean isFieldDeclaration() {\n\t\treturn getTrimmedStr().startsWith(\".field\");\n\t}\n\n\t@Override\n\tpublic final @Nullable String extractDeclaredClassName() {\n\t\tif (!isClassDeclaration()) {\n\t\t\treturn null;\n\t\t}\n\t\tString[] parts = getTrimmedStr().split(\"\\\\s+\");\n\t\tfor (String part : parts) {\n\t\t\tif (part.startsWith(\"L\") && part.endsWith(\";\")) {\n\t\t\t\tString fileClassName;\n\t\t\t\tif (part.contains(\"/\")) {\n\t\t\t\t\tfileClassName = part.substring(part.lastIndexOf('/') + 1, part.length() - 1);\n\t\t\t\t} else {\n\t\t\t\t\tfileClassName = part.substring(1, part.length() - 1); // remove leading 'L' and trailing ';'\n\t\t\t\t}\n\t\t\t\tif (fileClassName.contains(\"$\")) { // inner class\n\t\t\t\t\treturn fileClassName.substring(fileClassName.lastIndexOf('$') + 1);\n\t\t\t\t}\n\t\t\t\treturn fileClassName;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic final @Nullable String extractDeclaredMethodName() {\n\t\tif (!isMethodOrConstructorDeclaration()) {\n\t\t\treturn null;\n\t\t}\n\t\tint parenIndex = getTrimmedStr().indexOf('(');\n\t\tif (parenIndex > 0) {\n\t\t\tString beforeParen = getTrimmedStr().substring(0, parenIndex).trim();\n\t\t\tString[] tokens = beforeParen.split(\"\\\\s+\");\n\t\t\treturn tokens[tokens.length - 1];\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tprotected MethodDeclaration createMethodDeclaration() throws FallbackSyncException {\n\t\treturn MethodDeclaration.create(this);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/sync/fallback/SmaliAreaToken.java",
    "content": "package jadx.gui.ui.codearea.sync.fallback;\n\nimport javax.swing.text.BadLocationException;\n\nimport jadx.gui.ui.codearea.SmaliArea;\n\npublic class SmaliAreaToken extends AbstractCodeAreaToken {\n\tpublic SmaliAreaToken(SmaliArea area, int at) throws BadLocationException, FallbackSyncException {\n\t\tsuper(area, at);\n\t}\n\n\t@Override\n\tpublic boolean isFieldReference() throws BadLocationException {\n\t\treturn area.getText(this.startPos - 2, 2).equals(\"->\");\n\t}\n\n\t@Override\n\tpublic boolean isClassField() throws BadLocationException {\n\t\tAbstractCodeAreaLine line = this.getLine();\n\t\tboolean startsWithField = line.isFieldDeclaration();\n\t\tif (startsWithField) {\n\t\t\tString tokenStr = getStr();\n\t\t\tString trimmedLine = line.getTrimmedStr();\n\t\t\tint lineTokenStartPos = trimmedLine.indexOf(tokenStr);\n\t\t\tint lineTokenAfterPos = lineTokenStartPos + this.length;\n\t\t\tfor (int i = lineTokenAfterPos; i < trimmedLine.length(); ++i) {\n\t\t\t\tchar c = trimmedLine.charAt(i);\n\t\t\t\tswitch (c) {\n\t\t\t\t\tcase ' ':\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ':':\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic AbstractCodeAreaLine getLine() throws BadLocationException {\n\t\treturn new SmaliAreaLine((SmaliArea) area, area.getLineOfOffset(getAtPos()));\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/theme/DynamicCodeAreaTheme.java",
    "content": "package jadx.gui.ui.codearea.theme;\n\nimport java.awt.Color;\n\nimport javax.swing.UIManager;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;\nimport org.fife.ui.rsyntaxtextarea.SyntaxScheme;\nimport org.fife.ui.rsyntaxtextarea.Token;\nimport org.fife.ui.rtextarea.Gutter;\n\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\n/**\n * Mix current UI theme colors and apply to code area theme.\n */\npublic class DynamicCodeAreaTheme implements IEditorTheme {\n\n\t@Override\n\tpublic String getId() {\n\t\treturn \"DynamicCodeAreaTheme\";\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn NLS.str(\"preferences.dynamic_editor_theme\");\n\t}\n\n\tpublic void apply(RSyntaxTextArea textArea) {\n\t\t// Get the current colors from UIManager\n\t\tColor themeBackground = UIManager.getColor(\"Panel.background\");\n\t\tColor themeForeground = UIManager.getColor(\"Panel.foreground\");\n\t\tColor separatorForeground = UIManager.getColor(\"Separator.foreground\");\n\t\tColor editorSelectionBackground = UIManager.getColor(\"EditorPane.selectionBackground\");\n\t\tColor caretForeground = UIManager.getColor(\"EditorPane.caretForeground\");\n\n\t\tSyntaxScheme scheme = textArea.getSyntaxScheme();\n\n\t\tboolean isDarkTheme = UiUtils.isDarkTheme(themeBackground);\n\n\t\t// Background colors based on the theme\n\t\tColor editorBackground = isDarkTheme ? themeBackground : Color.WHITE; // Use white for light theme\n\t\tColor lineHighlight = isDarkTheme\n\t\t\t\t? UiUtils.adjustBrightness(themeBackground, 1.2f)\n\t\t\t\t: Color.decode(\"#EBECF0\"); // Light gray for light theme\n\t\tColor lineNumberForeground = UIManager.getColor(\"Label.foreground\");\n\n\t\t// Add these lines after setting the background colors\n\t\tColor selectionColor = isDarkTheme\n\t\t\t\t? new Color(51, 153, 255, 90) // Semi-transparent blue for dark theme\n\t\t\t\t: new Color(51, 153, 255, 50); // Lighter blue for light theme\n\n\t\tColor markAllHighlightColor = isDarkTheme ? Color.decode(\"#32593D\") : Color.decode(\"#ffc800\");\n\t\tColor matchedBracketBackground = isDarkTheme ? UiUtils.adjustBrightness(Color.decode(\"#3B514D\"), 1.2f) : Color.decode(\"#93D9D9\");\n\t\tColor markOccurrencesColor = UiUtils.adjustBrightness(editorSelectionBackground, isDarkTheme ? 0.6f : 1.4f);\n\n\t\t// Set the syntax colors for the theme\n\t\tif (isDarkTheme) {\n\t\t\tColor dataTypeColor = Color.decode(\"#4EC9B0\");\n\t\t\tscheme.getStyle(Token.COMMENT_EOL).foreground = Color.decode(\"#57A64A\");\n\t\t\tscheme.getStyle(Token.COMMENT_MULTILINE).foreground = Color.decode(\"#57A64A\");\n\t\t\tscheme.getStyle(Token.COMMENT_DOCUMENTATION).foreground = Color.decode(\"#57A64A\");\n\t\t\tscheme.getStyle(Token.COMMENT_KEYWORD).foreground = Color.decode(\"#57A64A\");\n\t\t\tscheme.getStyle(Token.COMMENT_MARKUP).foreground = Color.decode(\"#57A64A\");\n\t\t\tscheme.getStyle(Token.RESERVED_WORD).foreground = Color.decode(\"#569CD6\");\n\t\t\tscheme.getStyle(Token.RESERVED_WORD_2).foreground = dataTypeColor;\n\t\t\tscheme.getStyle(Token.FUNCTION).foreground = Color.decode(\"#DCDCAA\");\n\t\t\tscheme.getStyle(Token.ANNOTATION).foreground = Color.decode(\"#B3AE60\");\n\t\t\tscheme.getStyle(Token.LITERAL_NUMBER_DECIMAL_INT).foreground = Color.decode(\"#D7BA7D\");\n\t\t\tscheme.getStyle(Token.LITERAL_NUMBER_FLOAT).foreground = Color.decode(\"#D7BA7D\");\n\t\t\tscheme.getStyle(Token.LITERAL_NUMBER_HEXADECIMAL).foreground = Color.decode(\"#D7BA7D\");\n\t\t\tscheme.getStyle(Token.LITERAL_BOOLEAN).foreground = Color.decode(\"#569CD6\");\n\t\t\tscheme.getStyle(Token.LITERAL_CHAR).foreground = Color.decode(\"#CE9178\");\n\t\t\tscheme.getStyle(Token.LITERAL_STRING_DOUBLE_QUOTE).foreground = Color.decode(\"#CE9178\");\n\t\t\tscheme.getStyle(Token.DATA_TYPE).foreground = dataTypeColor;\n\t\t\tscheme.getStyle(Token.OPERATOR).foreground = Color.WHITE;\n\t\t\tscheme.getStyle(Token.SEPARATOR).foreground = Color.WHITE;\n\t\t\tscheme.getStyle(Token.IDENTIFIER).foreground = themeForeground;\n\t\t\t// XML-specific colors for dark theme\n\t\t\tscheme.getStyle(Token.MARKUP_TAG_DELIMITER).foreground = Color.decode(\"#808080\"); // Gray for < > /\n\t\t\tscheme.getStyle(Token.MARKUP_TAG_NAME).foreground = Color.decode(\"#569CD6\"); // Blue for tag names\n\t\t\tscheme.getStyle(Token.MARKUP_TAG_ATTRIBUTE).foreground = Color.decode(\"#9CDCFE\"); // Light blue for attributes\n\t\t\tscheme.getStyle(Token.MARKUP_TAG_ATTRIBUTE_VALUE).foreground = Color.decode(\"#CE9178\"); // Orange for values\n\t\t} else {\n\t\t\tColor dataTypeColor = Color.decode(\"#267F99\");\n\t\t\tscheme.getStyle(Token.COMMENT_EOL).foreground = Color.decode(\"#008000\");\n\t\t\tscheme.getStyle(Token.COMMENT_MULTILINE).foreground = Color.decode(\"#008000\");\n\t\t\tscheme.getStyle(Token.COMMENT_DOCUMENTATION).foreground = Color.decode(\"#008000\");\n\t\t\tscheme.getStyle(Token.COMMENT_KEYWORD).foreground = Color.decode(\"#008000\");\n\t\t\tscheme.getStyle(Token.COMMENT_MARKUP).foreground = Color.decode(\"#008000\");\n\t\t\tscheme.getStyle(Token.RESERVED_WORD).foreground = Color.decode(\"#0000FF\");\n\t\t\tscheme.getStyle(Token.RESERVED_WORD_2).foreground = dataTypeColor;\n\t\t\tscheme.getStyle(Token.FUNCTION).foreground = Color.decode(\"#795E26\");\n\t\t\tscheme.getStyle(Token.ANNOTATION).foreground = Color.decode(\"#9E8809\");\n\t\t\tscheme.getStyle(Token.LITERAL_NUMBER_DECIMAL_INT).foreground = Color.decode(\"#098658\");\n\t\t\tscheme.getStyle(Token.LITERAL_NUMBER_FLOAT).foreground = Color.decode(\"#098658\");\n\t\t\tscheme.getStyle(Token.LITERAL_NUMBER_HEXADECIMAL).foreground = Color.decode(\"#098658\");\n\t\t\tscheme.getStyle(Token.LITERAL_BOOLEAN).foreground = Color.decode(\"#0451A5\");\n\t\t\tscheme.getStyle(Token.LITERAL_CHAR).foreground = Color.decode(\"#067d17\");\n\t\t\tscheme.getStyle(Token.LITERAL_STRING_DOUBLE_QUOTE).foreground = Color.decode(\"#067d17\"); // Soft blue for values\n\t\t\tscheme.getStyle(Token.DATA_TYPE).foreground = dataTypeColor;\n\t\t\tscheme.getStyle(Token.OPERATOR).foreground = Color.decode(\"#333333\");\n\t\t\tscheme.getStyle(Token.SEPARATOR).foreground = Color.decode(\"#333333\");\n\t\t\tscheme.getStyle(Token.IDENTIFIER).foreground = themeForeground;\n\t\t\t// XML-specific colors for light theme\n\t\t\tscheme.getStyle(Token.MARKUP_TAG_DELIMITER).foreground = Color.decode(\"#800000\"); // Dark red for < > /\n\t\t\tscheme.getStyle(Token.MARKUP_TAG_NAME).foreground = Color.decode(\"#4A7A4F\"); // Soft green for tag names (keys)\n\t\t\tscheme.getStyle(Token.MARKUP_TAG_ATTRIBUTE).foreground = Color.decode(\"#FF0000\"); // Red for attributes\n\t\t\tscheme.getStyle(Token.MARKUP_TAG_ATTRIBUTE_VALUE).foreground = Color.decode(\"#0000FF\"); // Blue for values\n\t\t}\n\n\t\ttextArea.setBackground(editorBackground);\n\t\ttextArea.setCaretColor(caretForeground);\n\t\ttextArea.setSelectionColor(selectionColor);\n\t\ttextArea.setCurrentLineHighlightColor(lineHighlight);\n\t\ttextArea.setMarkAllHighlightColor(markAllHighlightColor);\n\t\ttextArea.setMarkOccurrencesColor(markOccurrencesColor);\n\t\ttextArea.setHyperlinkForeground(editorSelectionBackground);\n\t\ttextArea.setMatchedBracketBGColor(matchedBracketBackground);\n\t\ttextArea.setMatchedBracketBorderColor(lineNumberForeground);\n\n\t\ttextArea.setPaintMatchedBracketPair(true);\n\t\ttextArea.setAnimateBracketMatching(false);\n\t\ttextArea.setFadeCurrentLineHighlight(true);\n\n\t\t// Reset gutter colors directly to ensure the change applies\n\t\tGutter gutter = RSyntaxUtilities.getGutter(textArea);\n\t\tif (gutter != null) {\n\t\t\tgutter.setBackground(editorBackground);\n\t\t\tgutter.setBorderColor(separatorForeground);\n\t\t\tgutter.setLineNumberColor(lineNumberForeground);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/theme/EditorThemeManager.java",
    "content": "package jadx.gui.ui.codearea.theme;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.StringUtils;\nimport jadx.gui.settings.JadxSettings;\n\npublic class EditorThemeManager {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(EditorThemeManager.class);\n\n\tprivate final List<IEditorTheme> themes = new ArrayList<>();\n\tprivate final Map<String, IEditorTheme> themesMap = new HashMap<>();\n\n\tprivate IEditorTheme currentTheme = new FallbackEditorTheme();\n\n\tpublic EditorThemeManager(JadxSettings settings) {\n\t\tregisterThemes();\n\t\tif (StringUtils.isEmpty(settings.getEditorTheme())) {\n\t\t\t// set default theme\n\t\t\tIEditorTheme defaultTheme = themes.get(0);\n\t\t\tsettings.setEditorTheme(defaultTheme.getId());\n\t\t}\n\t}\n\n\tprivate void registerThemes() {\n\t\tregisterTheme(new DynamicCodeAreaTheme());\n\t\tregisterTheme(new RSTABundledTheme(\"default\"));\n\t\tregisterTheme(new RSTABundledTheme(\"eclipse\"));\n\t\tregisterTheme(new RSTABundledTheme(\"idea\"));\n\t\tregisterTheme(new RSTABundledTheme(\"vs\"));\n\t\tregisterTheme(new RSTABundledTheme(\"dark\"));\n\t\tregisterTheme(new RSTABundledTheme(\"monokai\"));\n\t\tregisterTheme(new RSTABundledTheme(\"druid\"));\n\t}\n\n\tpublic void registerTheme(IEditorTheme editorTheme) {\n\t\tIEditorTheme prev = themesMap.put(editorTheme.getId(), editorTheme);\n\t\tif (prev != null) {\n\t\t\tthemes.remove(prev);\n\t\t}\n\t\tthemes.add(editorTheme);\n\t}\n\n\tpublic synchronized void setTheme(String id) {\n\t\tif (currentTheme.getId().equals(id)) {\n\t\t\t// already set\n\t\t\treturn;\n\t\t}\n\t\t// resolve new\n\t\tIEditorTheme newTheme = themesMap.get(id);\n\t\tif (newTheme == null) {\n\t\t\tLOG.warn(\"Failed to resolve editor theme: {}\", id);\n\t\t\treturn;\n\t\t}\n\t\t// unload current\n\t\tunload();\n\n\t\t// load new\n\t\ttry {\n\t\t\tnewTheme.load();\n\t\t} catch (Throwable t) {\n\t\t\tLOG.warn(\"Failed to load editor theme: {}\", id, t);\n\t\t}\n\t\tcurrentTheme = newTheme;\n\t}\n\n\tpublic void apply(RSyntaxTextArea textArea) {\n\t\tthis.currentTheme.apply(textArea);\n\t}\n\n\tpublic ThemeIdAndName[] getThemeIdNameArray() {\n\t\treturn themes.stream()\n\t\t\t\t.map(EditorThemeManager::toThemeIdAndName)\n\t\t\t\t.toArray(ThemeIdAndName[]::new);\n\t}\n\n\tpublic ThemeIdAndName getCurrentThemeIdName() {\n\t\treturn toThemeIdAndName(currentTheme);\n\t}\n\n\tprivate static ThemeIdAndName toThemeIdAndName(IEditorTheme t) {\n\t\treturn new ThemeIdAndName(t.getId(), t.getName());\n\t}\n\n\tpublic void unload() {\n\t\ttry {\n\t\t\tcurrentTheme.unload();\n\t\t} catch (Throwable t) {\n\t\t\tLOG.warn(\"Failed to unload editor theme: {}\", currentTheme.getId(), t);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/theme/FallbackEditorTheme.java",
    "content": "package jadx.gui.ui.codearea.theme;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.fife.ui.rsyntaxtextarea.Theme;\n\npublic class FallbackEditorTheme implements IEditorTheme {\n\tprivate Theme baseTheme;\n\n\t@Override\n\tpublic String getId() {\n\t\treturn \"fallback\";\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"Fallback\";\n\t}\n\n\t@Override\n\tpublic void load() {\n\t\tbaseTheme = new Theme(new RSyntaxTextArea());\n\t}\n\n\t@Override\n\tpublic void apply(RSyntaxTextArea textArea) {\n\t\tbaseTheme.apply(textArea);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/theme/IEditorTheme.java",
    "content": "package jadx.gui.ui.codearea.theme;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\n\npublic interface IEditorTheme {\n\n\tString getId();\n\n\tString getName();\n\n\tdefault void load() {\n\t\t// optional method\n\t}\n\n\tvoid apply(RSyntaxTextArea textArea);\n\n\tdefault void unload() {\n\t\t// optional method\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/theme/RSTABundledTheme.java",
    "content": "package jadx.gui.ui.codearea.theme;\n\nimport java.io.InputStream;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.fife.ui.rsyntaxtextarea.Theme;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class RSTABundledTheme implements IEditorTheme {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(RSTABundledTheme.class);\n\n\tprivate static final String RSTA_THEME_PATH = \"/org/fife/ui/rsyntaxtextarea/themes/\";\n\n\tprivate final String name;\n\n\tprivate Theme loadedTheme;\n\n\tpublic RSTABundledTheme(String name) {\n\t\tthis.name = name;\n\t}\n\n\t@Override\n\tpublic String getId() {\n\t\treturn \"RSTA:\" + name;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic void load() {\n\t\tString path = RSTA_THEME_PATH + name + \".xml\";\n\t\ttry {\n\t\t\ttry (InputStream is = RSTABundledTheme.class.getResourceAsStream(path)) {\n\t\t\t\tloadedTheme = Theme.load(is);\n\t\t\t}\n\t\t} catch (Throwable t) {\n\t\t\tLOG.error(\"Failed to load editor theme: {}\", path, t);\n\t\t\tloadedTheme = new Theme(new RSyntaxTextArea());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void apply(RSyntaxTextArea textArea) {\n\t\tloadedTheme.apply(textArea);\n\t}\n\n\t@Override\n\tpublic void unload() {\n\t\tloadedTheme = null;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/theme/RSTAThemeXML.java",
    "content": "package jadx.gui.ui.codearea.theme;\n\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.fife.ui.rsyntaxtextarea.Theme;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class RSTAThemeXML implements IEditorTheme {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(RSTAThemeXML.class);\n\n\tprivate final Path themePath;\n\tprivate final String name;\n\n\tprivate Theme loadedTheme;\n\n\tpublic RSTAThemeXML(Path themeXmlPath, String name) {\n\t\tthis.themePath = themeXmlPath;\n\t\tthis.name = name;\n\t}\n\n\t@Override\n\tpublic String getId() {\n\t\treturn \"file:\" + themePath;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic void load() {\n\t\ttry {\n\t\t\ttry (InputStream is = Files.newInputStream(themePath)) {\n\t\t\t\tloadedTheme = Theme.load(is);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to load editor theme: {}\", themePath, e);\n\t\t\tloadedTheme = new Theme(new RSyntaxTextArea());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void apply(RSyntaxTextArea textArea) {\n\t\tloadedTheme.apply(textArea);\n\t}\n\n\t@Override\n\tpublic void unload() {\n\t\tloadedTheme = null;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/codearea/theme/ThemeIdAndName.java",
    "content": "package jadx.gui.ui.codearea.theme;\n\npublic class ThemeIdAndName {\n\tprivate final String id;\n\tprivate final String name;\n\n\tpublic ThemeIdAndName(String id, String name) {\n\t\tthis.id = id;\n\t\tthis.name = name;\n\t}\n\n\tpublic String getId() {\n\t\treturn id;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic final boolean equals(Object other) {\n\t\tif (!(other instanceof ThemeIdAndName)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn id.equals(((ThemeIdAndName) other).id);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn id.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn name;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/ADBDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.BorderLayout;\nimport java.awt.Color;\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.GridLayout;\nimport java.awt.Label;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.io.File;\nimport java.net.Socket;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.BoxLayout;\nimport javax.swing.ImageIcon;\nimport javax.swing.JButton;\nimport javax.swing.JDialog;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTextField;\nimport javax.swing.JTree;\nimport javax.swing.SwingUtilities;\nimport javax.swing.WindowConstants;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.DefaultTreeCellRenderer;\nimport javax.swing.tree.DefaultTreeModel;\nimport javax.swing.tree.TreePath;\nimport javax.swing.tree.TreeSelectionModel;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.commons.app.JadxSystemInfo;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.device.debugger.DbgUtils;\nimport jadx.gui.device.debugger.DebugSettings;\nimport jadx.gui.device.protocol.ADB;\nimport jadx.gui.device.protocol.ADBDevice;\nimport jadx.gui.device.protocol.ADBDeviceInfo;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.panel.IDebugController;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.JDWPProcessListener {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ADBDialog.class);\n\n\tprivate static final long serialVersionUID = -1111111202102181630L;\n\tprivate static final ImageIcon ICON_DEVICE = UiUtils.openSvgIcon(\"adb/androidDevice\");\n\tprivate static final ImageIcon ICON_PROCESS = UiUtils.openSvgIcon(\"adb/addToWatch\");\n\n\tprivate final transient MainWindow mainWindow;\n\tprivate transient Label tipLabel;\n\tprivate transient JTextField pathTextField;\n\tprivate transient JTextField hostTextField;\n\tprivate transient JTextField portTextField;\n\tprivate transient DefaultTreeModel procTreeModel;\n\tprivate transient DefaultMutableTreeNode procTreeRoot;\n\tprivate transient JTree procTree;\n\tprivate Socket deviceSocket;\n\tprivate transient List<DeviceNode> deviceNodes = new ArrayList<>();\n\tprivate transient DeviceNode lastSelectedDeviceNode;\n\n\tpublic ADBDialog(MainWindow mainWindow) {\n\t\tsuper(mainWindow);\n\t\tthis.mainWindow = mainWindow;\n\t\tinitUI();\n\t\tpathTextField.setText(mainWindow.getSettings().getAdbDialogPath());\n\t\thostTextField.setText(mainWindow.getSettings().getAdbDialogHost());\n\t\tportTextField.setText(mainWindow.getSettings().getAdbDialogPort());\n\n\t\tif (pathTextField.getText().isEmpty()) {\n\t\t\tdetectADBPath();\n\t\t} else {\n\t\t\tpathTextField.setText(\"\");\n\t\t}\n\n\t\tSwingUtilities.invokeLater(this::connectToADB);\n\t\tUiUtils.addEscapeShortCutToDispose(this);\n\t}\n\n\tprivate void initUI() {\n\t\tpathTextField = new JTextField();\n\t\tportTextField = new JTextField();\n\t\thostTextField = new JTextField();\n\n\t\tJPanel adbPanel = new JPanel(new BorderLayout(5, 5));\n\t\tadbPanel.add(new JLabel(NLS.str(\"adb_dialog.path\")), BorderLayout.WEST);\n\t\tadbPanel.add(pathTextField, BorderLayout.CENTER);\n\n\t\tJPanel portPanel = new JPanel(new BorderLayout(5, 0));\n\t\tportPanel.add(new JLabel(NLS.str(\"adb_dialog.port\")), BorderLayout.WEST);\n\t\tportPanel.add(portTextField, BorderLayout.CENTER);\n\n\t\tJPanel hostPanel = new JPanel(new BorderLayout(5, 0));\n\t\thostPanel.add(new JLabel(NLS.str(\"adb_dialog.addr\")), BorderLayout.WEST);\n\t\thostPanel.add(hostTextField, BorderLayout.CENTER);\n\n\t\tJPanel wrapperPanel = new JPanel(new GridLayout(1, 2, 5, 0));\n\t\twrapperPanel.add(hostPanel);\n\t\twrapperPanel.add(portPanel);\n\t\tadbPanel.add(wrapperPanel, BorderLayout.SOUTH);\n\n\t\tprocTree = new JTree();\n\t\tJScrollPane scrollPane = new JScrollPane(procTree);\n\t\tscrollPane.setMinimumSize(new Dimension(100, 150));\n\t\tscrollPane.setBorder(BorderFactory.createLineBorder(Color.black));\n\n\t\tprocTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);\n\t\tprocTreeRoot = new DefaultMutableTreeNode(NLS.str(\"adb_dialog.device_node\"));\n\t\tprocTreeModel = new DefaultTreeModel(procTreeRoot);\n\t\tprocTree.setModel(procTreeModel);\n\t\tprocTree.setRowHeight(-1);\n\t\tprocTree.setFont(mainWindow.getSettings().getCodeFont());\n\n\t\tprocTree.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(MouseEvent e) {\n\t\t\t\tif (e.getClickCount() == 2) {\n\t\t\t\t\tprocessSelected(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tprocTree.setCellRenderer(new DefaultTreeCellRenderer() {\n\t\t\tprivate static final long serialVersionUID = -1111111202103170735L;\n\n\t\t\t@Override\n\t\t\tpublic Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf,\n\t\t\t\t\tint row, boolean hasFocus) {\n\t\t\t\tComponent c = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);\n\t\t\t\tif (value instanceof DeviceTreeNode || value == procTreeRoot) {\n\t\t\t\t\tsetIcon(ICON_DEVICE);\n\t\t\t\t} else {\n\t\t\t\t\tsetIcon(ICON_PROCESS);\n\t\t\t\t}\n\t\t\t\treturn c;\n\t\t\t}\n\t\t});\n\n\t\tprocTree.addTreeSelectionListener(event -> {\n\t\t\tObject selectedNode = procTree.getLastSelectedPathComponent();\n\t\t\tif (selectedNode instanceof DeviceTreeNode) {\n\t\t\t\tlastSelectedDeviceNode = deviceNodes.stream()\n\t\t\t\t\t\t.filter(item -> item.tNode == selectedNode)\n\t\t\t\t\t\t.findFirst().orElse(null);\n\t\t\t}\n\t\t});\n\n\t\tJPanel btnPane = new JPanel();\n\t\tBoxLayout boxLayout = new BoxLayout(btnPane, BoxLayout.LINE_AXIS);\n\t\tbtnPane.setLayout(boxLayout);\n\t\ttipLabel = new Label(NLS.str(\"adb_dialog.waiting\"));\n\t\tbtnPane.add(tipLabel);\n\t\tJButton refreshBtn = new JButton(NLS.str(\"adb_dialog.refresh\"));\n\t\tJButton startServerBtn = new JButton(NLS.str(\"adb_dialog.start_server\"));\n\t\tJButton launchAppBtn = new JButton(NLS.str(\"adb_dialog.launch_app\"));\n\t\tbtnPane.add(launchAppBtn);\n\t\tbtnPane.add(startServerBtn);\n\t\tbtnPane.add(refreshBtn);\n\t\trefreshBtn.addActionListener(e -> {\n\t\t\tclear();\n\t\t\tprocTreeRoot.removeAllChildren();\n\t\t\tprocTreeModel.reload(procTreeRoot);\n\t\t\tSwingUtilities.invokeLater(this::connectToADB);\n\t\t});\n\n\t\tstartServerBtn.addActionListener(e -> startADBServer());\n\t\tlaunchAppBtn.addActionListener(e -> launchApp());\n\n\t\tJPanel mainPane = new JPanel(new BorderLayout(5, 5));\n\t\tmainPane.add(adbPanel, BorderLayout.NORTH);\n\t\tmainPane.add(scrollPane, BorderLayout.CENTER);\n\t\tmainPane.add(btnPane, BorderLayout.SOUTH);\n\t\tmainPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));\n\n\t\tgetContentPane().add(mainPane);\n\n\t\tpack();\n\t\tsetSize(800, 500);\n\t\tsetLocationRelativeTo(null);\n\t\tsetDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\n\t\tsetModalityType(ModalityType.MODELESS);\n\t}\n\n\tprivate void clear() {\n\t\tif (deviceSocket != null) {\n\t\t\ttry {\n\t\t\t\tdeviceSocket.close();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to close device socket\", e);\n\t\t\t}\n\t\t\tdeviceSocket = null;\n\t\t}\n\t\tfor (DeviceNode deviceNode : deviceNodes) {\n\t\t\tdeviceNode.device.stopListenForJDWP();\n\t\t}\n\t\tdeviceNodes.clear();\n\t}\n\n\tprivate void detectADBPath() {\n\t\tboolean isWinOS = JadxSystemInfo.IS_WINDOWS;\n\t\tString slash = isWinOS ? \"\\\\\" : \"/\";\n\t\tString adbName = isWinOS ? \"adb.exe\" : \"adb\";\n\t\tString sdkPath = System.getenv(\"ANDROID_HOME\");\n\t\tif (!StringUtils.isEmpty(sdkPath)) {\n\t\t\tif (!sdkPath.endsWith(slash)) {\n\t\t\t\tsdkPath += slash;\n\t\t\t}\n\t\t\tsdkPath += \"platform-tools\" + slash + adbName;\n\t\t\tif ((new File(sdkPath)).exists()) {\n\t\t\t\tpathTextField.setText(sdkPath);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tString envPath = System.getenv(\"PATH\");\n\t\tString[] paths = envPath.split(isWinOS ? \";\" : \":\");\n\t\tfor (String path : paths) {\n\t\t\tif (!path.endsWith(slash)) {\n\t\t\t\tpath += slash;\n\t\t\t}\n\t\t\tpath = path + adbName;\n\t\t\tif (new File(path).exists()) {\n\t\t\t\tpathTextField.setText(path);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void startADBServer() {\n\t\tString path = pathTextField.getText();\n\t\tif (path.isEmpty()) {\n\t\t\tUiUtils.showMessageBox(mainWindow, NLS.str(\"adb_dialog.missing_path\"));\n\t\t\treturn;\n\t\t}\n\t\tString tip;\n\t\ttry {\n\t\t\tif (ADB.startServer(path, Integer.parseInt(portTextField.getText()))) {\n\t\t\t\ttip = NLS.str(\"adb_dialog.start_okay\", portTextField.getText());\n\t\t\t} else {\n\t\t\t\ttip = NLS.str(\"adb_dialog.start_fail\", portTextField.getText());\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to start adb server\", e);\n\t\t\ttip = e.getMessage();\n\t\t}\n\t\tUiUtils.showMessageBox(mainWindow, tip);\n\t\ttipLabel.setText(tip);\n\t}\n\n\tprivate void connectToADB() {\n\t\tString tip;\n\t\ttry {\n\t\t\tString host = hostTextField.getText().trim();\n\t\t\tString port = portTextField.getText().trim();\n\t\t\ttipLabel.setText(NLS.str(\"adb_dialog.connecting\", host, port));\n\t\t\tdeviceSocket = ADB.listenForDeviceState(this, host, Integer.parseInt(port));\n\t\t\tif (deviceSocket != null) {\n\t\t\t\ttip = NLS.str(\"adb_dialog.connect_okay\", host, port);\n\t\t\t\tthis.setTitle(tip);\n\t\t\t} else {\n\t\t\t\ttip = NLS.str(\"adb_dialog.connect_fail\");\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to connect to adb\", e);\n\t\t\ttip = e.getMessage();\n\t\t\tUiUtils.showMessageBox(mainWindow, tip);\n\t\t}\n\t\ttipLabel.setText(tip);\n\t}\n\n\t@Override\n\tpublic void onDeviceStatusChange(List<ADBDeviceInfo> deviceInfoList) {\n\t\tLOG.debug(\"onDeviceStatusChange {}\", deviceInfoList);\n\t\tList<DeviceNode> nodes = new ArrayList<>(deviceInfoList.size());\n\t\tinfo_loop: for (ADBDeviceInfo info : deviceInfoList) {\n\t\t\tfor (DeviceNode deviceNode : deviceNodes) {\n\t\t\t\tif (deviceNode.device.updateDeviceInfo(info)) {\n\t\t\t\t\tdeviceNode.refresh();\n\t\t\t\t\tnodes.add(deviceNode);\n\t\t\t\t\tcontinue info_loop;\n\t\t\t\t}\n\t\t\t}\n\t\t\tADBDevice device = new ADBDevice(info);\n\t\t\tdevice.getAndroidReleaseVersion();\n\t\t\tnodes.add(new DeviceNode(device));\n\t\t\tlistenJDWP(device);\n\t\t}\n\t\tdeviceNodes = nodes;\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\ttipLabel.setText(NLS.str(\"adb_dialog.tip_devices\", deviceNodes.size()));\n\t\t\tprocTreeRoot.removeAllChildren();\n\t\t\tdeviceNodes.forEach(n -> procTreeRoot.add(n.tNode));\n\t\t\tprocTreeModel.reload(procTreeRoot);\n\t\t\tfor (DeviceNode deviceNode : deviceNodes) {\n\t\t\t\tprocTree.expandPath(new TreePath(deviceNode.tNode.getPath()));\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void processSelected(MouseEvent e) {\n\t\tTreePath path = procTree.getPathForLocation(e.getX(), e.getY());\n\t\tif (path != null) {\n\t\t\tDefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();\n\t\t\tString pid = getPid((String) node.getUserObject());\n\t\t\tif (StringUtils.isEmpty(pid)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (mainWindow.getDebuggerPanel() != null && mainWindow.getDebuggerPanel().getDbgController().isDebugging()) {\n\t\t\t\tif (JOptionPane.showConfirmDialog(mainWindow,\n\t\t\t\t\t\tNLS.str(\"adb_dialog.restart_while_debugging_msg\"),\n\t\t\t\t\t\tNLS.str(\"adb_dialog.restart_while_debugging_title\"),\n\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION) != JOptionPane.CANCEL_OPTION) {\n\t\t\t\t\tIDebugController ctrl = mainWindow.getDebuggerPanel().getDbgController();\n\t\t\t\t\tif (launchForDebugging(mainWindow, ctrl.getProcessName(), true)) {\n\t\t\t\t\t\tdispose();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tDeviceNode deviceNode = getDeviceNode((DefaultMutableTreeNode) node.getParent());\n\t\t\tif (deviceNode == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!setupArgs(deviceNode.device, pid, (String) node.getUserObject())) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (DebugSettings.INSTANCE.isBeingDebugged()) {\n\t\t\t\tif (JOptionPane.showConfirmDialog(mainWindow,\n\t\t\t\t\t\tNLS.str(\"adb_dialog.being_debugged_msg\"),\n\t\t\t\t\t\tNLS.str(\"adb_dialog.being_debugged_title\"),\n\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\ttipLabel.setText(NLS.str(\"adb_dialog.starting_debugger\"));\n\t\t\tif (!attachProcess(mainWindow)) {\n\t\t\t\ttipLabel.setText(NLS.str(\"adb_dialog.init_dbg_fail\"));\n\t\t\t} else {\n\t\t\t\tdispose();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean attachProcess(MainWindow mainWindow) {\n\t\tDebugSettings debugSettings = DebugSettings.INSTANCE;\n\t\tdebugSettings.clearForward();\n\t\tString rst = debugSettings.forwardJDWP();\n\t\tif (!rst.isEmpty()) {\n\t\t\tUiUtils.showMessageBox(mainWindow, rst);\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\treturn mainWindow.getDebuggerPanel().showDebugger(\n\t\t\t\t\tdebugSettings.getName(),\n\t\t\t\t\tdebugSettings.getDevice().getDeviceInfo().getAdbHost(),\n\t\t\t\t\tdebugSettings.getForwardTcpPort(),\n\t\t\t\t\tdebugSettings.getVer(),\n\t\t\t\t\tdebugSettings.getDevice(),\n\t\t\t\t\tdebugSettings.getPid());\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to attach to process\", e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic static boolean launchForDebugging(MainWindow mainWindow, String fullAppPath, boolean autoAttach) {\n\t\tDebugSettings debugSettings = DebugSettings.INSTANCE;\n\t\tdebugSettings.setAutoAttachPkg(autoAttach);\n\t\ttry {\n\t\t\tint pid = debugSettings.getDevice().launchApp(fullAppPath);\n\t\t\tif (pid != -1) {\n\t\t\t\tdebugSettings.setPid(String.valueOf(pid))\n\t\t\t\t\t\t.setName(fullAppPath);\n\t\t\t\treturn attachProcess(mainWindow);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to launch app\", e);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate String getPid(String nodeText) {\n\t\tif (nodeText.startsWith(\"[pid:\")) {\n\t\t\tint pos = nodeText.indexOf(']', \"[pid:\".length());\n\t\t\tif (pos != -1) {\n\t\t\t\treturn nodeText.substring(\"[pid:\".length(), pos).trim();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate DeviceNode getDeviceNode(DefaultMutableTreeNode node) {\n\t\tfor (DeviceNode deviceNode : deviceNodes) {\n\t\t\tif (deviceNode.tNode == node) {\n\t\t\t\treturn deviceNode;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate DeviceNode getDeviceNode(ADBDevice device) {\n\t\tfor (DeviceNode deviceNode : deviceNodes) {\n\t\t\tif (deviceNode.device.equals(device)) {\n\t\t\t\treturn deviceNode;\n\t\t\t}\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Unexpected device: \" + device);\n\t}\n\n\tprivate void listenJDWP(ADBDevice device) {\n\t\ttry {\n\t\t\tdevice.listenForJDWP(this);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed listen for JDWP\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void dispose() {\n\t\tclear();\n\t\tJadxSettings settings = mainWindow.getSettings();\n\t\tboolean changed = !settings.getAdbDialogPath().equals(pathTextField.getText());\n\t\tchanged |= !settings.getAdbDialogHost().equals(hostTextField.getText());\n\t\tchanged |= !settings.getAdbDialogPort().equals(portTextField.getText());\n\t\tif (changed) {\n\t\t\tsettings.setAdbDialogPath(pathTextField.getText());\n\t\t\tsettings.setAdbDialogHost(hostTextField.getText());\n\t\t\tsettings.setAdbDialogPort(portTextField.getText());\n\t\t\tsettings.sync();\n\t\t}\n\t\tsuper.dispose();\n\t}\n\n\t@Override\n\tpublic void adbDisconnected() {\n\t\tdeviceSocket = null;\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\ttipLabel.setText(NLS.str(\"adb_dialog.disconnected\"));\n\t\t\tthis.setTitle(\"\");\n\t\t});\n\t}\n\n\t@Override\n\tpublic void jdwpProcessOccurred(ADBDevice device, Set<String> id) {\n\t\tList<ADB.Process> procs;\n\t\ttry {\n\t\t\tThread.sleep(40); /*\n\t\t\t\t\t\t\t\t * wait for a moment, let the new processes on remote be fully initialized,\n\t\t\t\t\t\t\t\t * otherwise we may not get its real name but the <pre-initialized> state text.\n\t\t\t\t\t\t\t\t */\n\t\t\tprocs = device.getProcessList();\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to get device process list\", e);\n\t\t\tprocs = Collections.emptyList();\n\t\t}\n\t\tList<String> procList = new ArrayList<>(id.size());\n\t\tif (procs.isEmpty()) {\n\t\t\tprocList.addAll(id);\n\t\t} else {\n\t\t\tfor (ADB.Process proc : procs) {\n\t\t\t\tif (id.contains(proc.pid)) {\n\t\t\t\t\tprocList.add(String.format(\"[pid: %-6s] %s\", proc.pid, proc.name));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tCollections.reverse(procList);\n\t\tDeviceNode node;\n\t\ttry {\n\t\t\tnode = getDeviceNode(device);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to find device\", e);\n\t\t\treturn;\n\t\t}\n\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\tnode.tNode.removeAllChildren();\n\t\t\tDefaultMutableTreeNode foundNode = null;\n\t\t\tDebugSettings debugSettings = DebugSettings.INSTANCE;\n\t\t\tfor (String procStr : procList) {\n\t\t\t\tDefaultMutableTreeNode pnode = new DefaultMutableTreeNode(procStr);\n\t\t\t\tnode.tNode.add(pnode);\n\t\t\t\tif (!debugSettings.getExpectPkg().isEmpty() && procStr.endsWith(debugSettings.getExpectPkg())) {\n\t\t\t\t\tif (debugSettings.isAutoAttachPkg() && debugSettings.getDevice().equals(node.device)) {\n\t\t\t\t\t\tdebugSettings.set(node.device, debugSettings.getVer(), getPid(procStr), procStr);\n\t\t\t\t\t\tif (attachProcess(mainWindow)) {\n\t\t\t\t\t\t\tdispose();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfoundNode = pnode;\n\t\t\t\t}\n\t\t\t}\n\t\t\tprocTreeModel.reload(node.tNode);\n\t\t\tprocTree.expandPath(new TreePath(node.tNode.getPath()));\n\t\t\tif (foundNode != null) {\n\t\t\t\tTreePath thePath = new TreePath(foundNode.getPath());\n\t\t\t\tprocTree.scrollPathToVisible(thePath);\n\t\t\t\tprocTree.setSelectionPath(thePath);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void launchApp() {\n\t\tif (deviceNodes.isEmpty()) {\n\t\t\tUiUtils.showMessageBox(mainWindow, NLS.str(\"adb_dialog.no_devices\"));\n\t\t\treturn;\n\t\t}\n\t\tDbgUtils.AppData appData = DbgUtils.parseAppData(mainWindow);\n\t\tif (appData == null) {\n\t\t\t// error already reported\n\t\t\treturn;\n\t\t}\n\t\tif (scrollToProcNode(appData.getAppPackage())) {\n\t\t\treturn;\n\t\t}\n\t\tString processName = appData.getProcessName();\n\t\tADBDevice device = lastSelectedDeviceNode == null ? deviceNodes.get(0).device : lastSelectedDeviceNode.device;\n\t\tif (device != null) {\n\t\t\ttry {\n\t\t\t\tdevice.launchApp(processName);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to launch app: {}\", processName, e);\n\t\t\t\tUiUtils.showMessageBox(mainWindow, e.getMessage());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate boolean scrollToProcNode(String pkg) {\n\t\tif (pkg.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tDebugSettings.INSTANCE.setExpectPkg(\" \" + pkg);\n\t\tfor (int i = 0; i < procTreeRoot.getChildCount(); i++) {\n\t\t\tDefaultMutableTreeNode rn = (DefaultMutableTreeNode) procTreeRoot.getChildAt(i);\n\t\t\tfor (int j = 0; j < rn.getChildCount(); j++) {\n\t\t\t\tDefaultMutableTreeNode n = (DefaultMutableTreeNode) rn.getChildAt(j);\n\t\t\t\tString pName = (String) n.getUserObject();\n\t\t\t\tif (pName.endsWith(DebugSettings.INSTANCE.getExpectPkg())) {\n\t\t\t\t\tTreePath path = new TreePath(n.getPath());\n\t\t\t\t\tprocTree.scrollPathToVisible(path);\n\t\t\t\t\tprocTree.setSelectionPath(path);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void jdwpListenerClosed(ADBDevice device) {\n\n\t}\n\n\tprivate static class DeviceTreeNode extends DefaultMutableTreeNode {\n\t\tprivate static final long serialVersionUID = -1111111202103131112L;\n\t}\n\n\tprivate static class DeviceNode {\n\t\tADBDevice device;\n\t\tDeviceTreeNode tNode;\n\n\t\tDeviceNode(ADBDevice adbDevice) {\n\t\t\tthis.device = adbDevice;\n\t\t\ttNode = new DeviceTreeNode();\n\t\t\trefresh();\n\t\t}\n\n\t\tvoid refresh() {\n\t\t\tADBDeviceInfo info = device.getDeviceInfo();\n\t\t\tString text = info.getModel();\n\t\t\tif (text != null) {\n\t\t\t\tif (!text.equals(info.getSerial())) {\n\t\t\t\t\ttext += String.format(\" [serial: %s]\", info.getSerial());\n\t\t\t\t}\n\t\t\t\ttext += String.format(\" [state: %s]\", info.isOnline() ? \"online\" : \"offline\");\n\t\t\t\ttNode.setUserObject(text);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate boolean setupArgs(ADBDevice device, String pid, String name) {\n\t\tString ver = device.getAndroidReleaseVersion();\n\t\tif (StringUtils.isEmpty(ver)) {\n\t\t\tif (JOptionPane.showConfirmDialog(mainWindow,\n\t\t\t\t\tNLS.str(\"adb_dialog.unknown_android_ver\"),\n\t\t\t\t\t\"\",\n\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tver = \"8\";\n\t\t}\n\t\tver = getMajorVer(ver);\n\t\tDebugSettings.INSTANCE.set(device, Integer.parseInt(ver), pid, name);\n\t\treturn true;\n\t}\n\n\tprivate String getMajorVer(String ver) {\n\t\tint pos = ver.indexOf('.');\n\t\tif (pos != -1) {\n\t\t\tver = ver.substring(0, pos);\n\t\t}\n\t\treturn ver;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/AboutDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.BorderLayout;\nimport java.awt.Container;\nimport java.awt.Dimension;\nimport java.net.URL;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\nimport javax.swing.JButton;\nimport javax.swing.JDialog;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.SwingConstants;\n\nimport jadx.api.JadxDecompiler;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic class AboutDialog extends JDialog {\n\tprivate static final long serialVersionUID = 5763493590584039096L;\n\n\tpublic AboutDialog() {\n\t\tinitUI();\n\t}\n\n\tpublic final void initUI() {\n\t\tURL logoURL = getClass().getResource(\"/logos/jadx-logo-48px.png\");\n\t\tIcon logo = new ImageIcon(logoURL, \"JADX logo\");\n\n\t\tJLabel name = new JLabel(\"JADX\", logo, SwingConstants.CENTER);\n\t\tname.setAlignmentX(0.5f);\n\n\t\tJLabel desc = new JLabel(\"Dex to Java decompiler\");\n\t\tdesc.setAlignmentX(0.5f);\n\n\t\tJLabel version = new JLabel(\"JADX version: \" + JadxDecompiler.getVersion());\n\t\tversion.setAlignmentX(0.5f);\n\n\t\tString javaVm = System.getProperty(\"java.vm.name\");\n\t\tString javaVer = System.getProperty(\"java.version\");\n\n\t\tjavaVm = javaVm == null ? \"\" : javaVm;\n\n\t\tJLabel javaVmLabel = new JLabel(\"Java VM: \" + javaVm);\n\t\tjavaVmLabel.setAlignmentX(0.5f);\n\n\t\tjavaVer = javaVer == null ? \"\" : javaVer;\n\t\tJLabel javaVerLabel = new JLabel(\"Java version: \" + javaVer);\n\t\tjavaVerLabel.setAlignmentX(0.5f);\n\n\t\tJPanel textPane = new JPanel();\n\t\ttextPane.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));\n\t\ttextPane.setLayout(new BoxLayout(textPane, BoxLayout.PAGE_AXIS));\n\t\ttextPane.add(Box.createRigidArea(new Dimension(0, 10)));\n\t\ttextPane.add(name);\n\t\ttextPane.add(Box.createRigidArea(new Dimension(0, 10)));\n\t\ttextPane.add(desc);\n\t\ttextPane.add(Box.createRigidArea(new Dimension(0, 10)));\n\t\ttextPane.add(version);\n\t\ttextPane.add(Box.createRigidArea(new Dimension(0, 20)));\n\t\ttextPane.add(javaVmLabel);\n\t\ttextPane.add(javaVerLabel);\n\t\ttextPane.add(Box.createRigidArea(new Dimension(0, 20)));\n\n\t\tJButton close = new JButton(NLS.str(\"tabs.close\"));\n\t\tclose.addActionListener(event -> dispose());\n\t\tclose.setAlignmentX(0.5f);\n\n\t\tContainer contentPane = getContentPane();\n\t\tcontentPane.add(textPane, BorderLayout.CENTER);\n\t\tcontentPane.add(close, BorderLayout.PAGE_END);\n\n\t\tUiUtils.setWindowIcons(this);\n\n\t\tsetModalityType(ModalityType.APPLICATION_MODAL);\n\n\t\tsetTitle(NLS.str(\"about_dialog.title\"));\n\t\tpack();\n\t\tsetDefaultCloseOperation(DISPOSE_ON_CLOSE);\n\t\tsetLocationRelativeTo(null);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/CallGraphDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.BorderLayout;\nimport java.awt.Color;\nimport java.awt.Dimension;\nimport java.awt.FlowLayout;\nimport java.util.Formatter;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JCheckBox;\nimport javax.swing.JLabel;\nimport javax.swing.JMenuBar;\nimport javax.swing.JPanel;\nimport javax.swing.JSpinner;\nimport javax.swing.SpinnerNumberModel;\nimport javax.swing.SwingConstants;\nimport javax.swing.SwingUtilities;\nimport javax.swing.UIManager;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JavaMethod;\nimport jadx.api.JavaNode;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.core.utils.DotGraphUtils;\nimport jadx.gui.treemodel.JMethod;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.layout.WrapLayout;\n\npublic class CallGraphDialog extends GraphDialog {\n\n\tprivate static final long serialVersionUID = -850803763322590708L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CallGraphDialog.class);\n\n\tprivate static final String FONT = \"fontname=\\\"Courier\\\" fontsize=12\";\n\tprivate int callerDepthLimit = 3;\n\tprivate int calleeDepthLimit = 3;\n\tprivate int nextNodeID;\n\tprivate Map<JavaMethod, Integer> methodToNodeID;\n\tprivate Map<IMethodRef, Integer> unresolvedMethodToNodeID;\n\tprivate Set<Edge> edges;\n\tprivate JavaMethod javaMethod;\n\tprivate boolean longNames = false;\n\n\tpublic CallGraphDialog(MainWindow mainWindow, JavaMethod javaMethod) {\n\t\tsuper(mainWindow,\n\t\t\t\tString.format(\"%s: %s\", NLS.str(\"graph_viewer.call_graph.title\"), DotGraphUtils.methodFormatName(javaMethod, false)));\n\t\tthis.javaMethod = javaMethod;\n\t}\n\n\tpublic JMenuBar addMenuBar() {\n\t\tJMenuBar menuBar = super.addMenuBar();\n\n\t\t// Long names checkbox\n\t\tJCheckBox showLongNames = new JCheckBox(NLS.str(\"graph_viewer.long_names\"));\n\t\tshowLongNames.setSelected(false);\n\t\tshowLongNames.addItemListener(e -> {\n\t\t\tlongNames = showLongNames.isSelected();\n\t\t\treload();\n\t\t});\n\n\t\t// Calee spinner\n\t\tSpinnerNumberModel calleeDepthSpinnerModel = new SpinnerNumberModel(3, 0, 100, 1);\n\t\tJSpinner calleeDepthSpinner = new JSpinner(calleeDepthSpinnerModel);\n\t\tcalleeDepthSpinner.addChangeListener(e -> {\n\t\t\tcalleeDepthLimit = (int) calleeDepthSpinner.getValue();\n\t\t\treload();\n\t\t});\n\n\t\t// Callee label\n\t\tJLabel calleeLbl = new JLabel(NLS.str(\"graph_viewer.callee_depth\"));\n\t\tcalleeLbl.setLabelFor(calleeDepthSpinner);\n\t\tcalleeLbl.setHorizontalAlignment(SwingConstants.LEFT);\n\n\t\t// Assemble callee panel\n\t\tJPanel calleePanel = new JPanel();\n\t\tcalleePanel.setOpaque(false);\n\t\tcalleePanel.setLayout(new BoxLayout(calleePanel, BoxLayout.LINE_AXIS));\n\t\tcalleePanel.add(calleeLbl);\n\t\tcalleePanel.add(Box.createRigidArea(new Dimension(3, 0)));\n\t\tcalleePanel.add(calleeDepthSpinner);\n\n\t\t// Caller spinner\n\t\tSpinnerNumberModel callerDepthSpinnerModel = new SpinnerNumberModel(3, 0, 100, 1);\n\t\tJSpinner callerDepthSpinner = new JSpinner(callerDepthSpinnerModel);\n\t\tcallerDepthSpinner.addChangeListener(e -> {\n\t\t\tcallerDepthLimit = (int) callerDepthSpinner.getValue();\n\t\t\treload();\n\t\t});\n\n\t\t// Caller label\n\t\tJLabel callerLbl = new JLabel(NLS.str(\"graph_viewer.caller_depth\"));\n\t\tcallerLbl.setLabelFor(callerDepthSpinner);\n\t\tcallerLbl.setHorizontalAlignment(SwingConstants.LEFT);\n\n\t\t// Assemble caller panel\n\t\tJPanel callerPanel = new JPanel();\n\t\tcallerPanel.setOpaque(false);\n\t\tcallerPanel.setLayout(new BoxLayout(callerPanel, BoxLayout.LINE_AXIS));\n\t\tcallerPanel.add(callerLbl);\n\t\tcallerPanel.add(Box.createRigidArea(new Dimension(3, 0)));\n\t\tcallerPanel.add(callerDepthSpinner);\n\n\t\t// Assemble menubar panel\n\t\tJPanel menuBarPanel = new JPanel();\n\t\tmenuBarPanel.setOpaque(false);\n\t\tmenuBarPanel.setLayout(new WrapLayout(FlowLayout.LEFT));\n\t\tmenuBarPanel.add(showLongNames, BorderLayout.PAGE_START);\n\t\tmenuBarPanel.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tmenuBarPanel.add(calleePanel);\n\t\tmenuBarPanel.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tmenuBarPanel.add(callerPanel);\n\n\t\t// Add menubar panel to menuBar\n\t\tmenuBar.add(menuBarPanel);\n\t\treturn menuBar;\n\t}\n\n\tpublic static void open(MainWindow window, JMethod method) {\n\n\t\tJavaMethod javaMethod = method.getJavaMethod();\n\t\tCallGraphDialog graphDialog = new CallGraphDialog(window, javaMethod);\n\t\tgraphDialog.addMenuBar();\n\n\t\tgraphDialog.setVisible(true);\n\t\tgraphDialog.reload();\n\t}\n\n\tpublic void reload() {\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\tString graph = generateGraph(javaMethod);\n\t\t\tgetPanel().setGraph(graph);\n\n\t\t});\n\t}\n\n\tprivate String generateGraph(JavaMethod javaMethod) {\n\t\tStringBuilder sb = new StringBuilder();\n\n\t\tColor themeBackground = UIManager.getColor(\"Panel.background\");\n\t\tColor themeForeground = UIManager.getColor(\"Label.foreground\");\n\t\tColor themeHighlight = UIManager.getColor(\"Component.focusedBorderColor\");\n\t\tColor themeShade = UIManager.getColor(\"TextArea.background\");\n\n\t\tString bgColor =\n\t\t\t\tString.format(\"bgcolor=\\\"#%02x%02x%02x\\\"\", themeBackground.getRed(), themeBackground.getGreen(),\n\t\t\t\t\t\tthemeBackground.getBlue());\n\t\tString lineColor =\n\t\t\t\tString.format(\"color=\\\"#%02x%02x%02x\\\"\", themeForeground.getRed(), themeForeground.getGreen(), themeForeground.getBlue());\n\t\tString fontColor =\n\t\t\t\tString.format(\"fontcolor=\\\"#%02x%02x%02x\\\"\", themeForeground.getRed(), themeForeground.getGreen(),\n\t\t\t\t\t\tthemeForeground.getBlue());\n\t\tString highlightColor =\n\t\t\t\tString.format(\"color=\\\"#%02x%02x%02x\\\"\", themeHighlight.getRed(), themeHighlight.getGreen(),\n\t\t\t\t\t\tthemeHighlight.getBlue());\n\t\tString shadeColor = String.format(\"fillcolor=\\\"#%02x%02x%02x\\\"\", themeShade.getRed(), themeShade.getGreen(), themeShade.getBlue());\n\n\t\ttry (Formatter f = new Formatter(sb)) {\n\n\t\t\t// graph header\n\t\t\tf.format(\"digraph G {\\n\");\n\t\t\tf.format(\"%s\\n\", bgColor);\n\t\t\tf.format(\"node[shape=\\\"record\\\" style=\\\"filled\\\" %s %s %s %s]\\n\", FONT, fontColor, lineColor, shadeColor);\n\t\t\tf.format(\"edge[arrowtail=\\\"onormal\\\" arrowhead=\\\"onormal\\\" %s %s %s]\\n\", FONT, fontColor, lineColor);\n\n\t\t\tnextNodeID = 0;\n\t\t\tmethodToNodeID = new HashMap<>();\n\t\t\tunresolvedMethodToNodeID = new HashMap<>();\n\t\t\tedges = new HashSet<>();\n\n\t\t\taddNode(f, javaMethod, highlightColor);\n\n\t\t\t// add caller relationships\n\t\t\taddCallers(0, f, javaMethod);\n\n\t\t\t// add calee relationships\n\t\t\taddCallees(0, f, javaMethod);\n\n\t\t\t// close graph\n\t\t\tf.format(\"}\");\n\n\t\t\treturn f.toString();\n\t\t}\n\t}\n\n\tprivate void addCallers(int depth, Formatter f, JavaMethod javaMethod) {\n\t\tif (depth >= callerDepthLimit) {\n\t\t\treturn;\n\t\t}\n\n\t\tList<JavaNode> uses = javaMethod.getUseIn();\n\n\t\t// add \"calls\" relationships\n\t\tfor (JavaNode node : uses) {\n\t\t\tif (!(node instanceof JavaMethod)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tJavaMethod caller = (JavaMethod) node;\n\n\t\t\tint nodeID = addNode(f, caller);\n\t\t\taddEdge(f, nodeID, methodToNodeID.get(javaMethod));\n\n\t\t\taddCallers(depth + 1, f, caller);\n\t\t}\n\t}\n\n\tprivate void addCallees(int depth, Formatter f, JavaMethod javaMethod) {\n\t\tif (depth >= calleeDepthLimit) {\n\t\t\treturn;\n\t\t}\n\n\t\tList<JavaNode> used = javaMethod.getUsed();\n\n\t\t// add \"calls\" relationships\n\t\tfor (JavaNode node : used) {\n\t\t\tif (!(node instanceof JavaMethod)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tJavaMethod callee = (JavaMethod) node;\n\n\t\t\tint nodeID = addNode(f, callee);\n\t\t\taddEdge(f, methodToNodeID.get(javaMethod), nodeID);\n\n\t\t\taddCallees(depth + 1, f, callee);\n\t\t}\n\t\taddUnresolvedCallees(depth, f, javaMethod);\n\t}\n\n\tprivate void addUnresolvedCallees(int depth, Formatter f, JavaMethod javaMethod) {\n\t\tif (depth >= calleeDepthLimit) {\n\t\t\treturn;\n\t\t}\n\n\t\tList<IMethodRef> used = javaMethod.getUnresolvedUsed();\n\n\t\t// add \"calls\" relationships\n\t\tfor (IMethodRef callee : used) {\n\t\t\tString name = callee.getName();\n\t\t\tif (name == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tint nodeID = addNode(f, callee);\n\t\t\taddEdge(f, methodToNodeID.get(javaMethod), nodeID);\n\t\t}\n\t}\n\n\tprivate int addNode(Formatter f, JavaMethod method) {\n\t\treturn addNode(f, method, \"\");\n\t}\n\n\t// Add a node representing method to the graph in f. Returns the ID of the new node\n\tprivate int addNode(Formatter f, JavaMethod method, String extra) {\n\t\tint nodeID;\n\t\tif (methodToNodeID.containsKey(method)) {\n\t\t\tnodeID = methodToNodeID.get(method);\n\t\t} else {\n\t\t\tnodeID = nextNodeID;\n\t\t\tnextNodeID++;\n\t\t\tmethodToNodeID.put(method, nodeID);\n\t\t}\n\n\t\tString name = DotGraphUtils.methodFormatName(method, longNames);\n\t\tf.format(\"Node_%d [ label=\\\"{%s}\\\" %s]\\n\", nodeID, UiUtils.toDotNodeName(name), extra);\n\n\t\tif (javaMethod.callsSelf()) {\n\t\t\taddEdge(f, nodeID, nodeID);\n\t\t}\n\n\t\treturn nodeID;\n\t}\n\n\tprivate int addNode(Formatter f, IMethodRef method) {\n\t\treturn addNode(f, method, \"\");\n\t}\n\n\t// Add a node representing an unresolved method to the graph in f. Returns the ID of the new node\n\tprivate int addNode(Formatter f, IMethodRef method, String extra) {\n\t\tint nodeID;\n\t\tif (unresolvedMethodToNodeID.containsKey(method)) {\n\t\t\tnodeID = unresolvedMethodToNodeID.get(method);\n\t\t} else {\n\t\t\tnodeID = nextNodeID;\n\t\t\tnextNodeID++;\n\t\t\tunresolvedMethodToNodeID.put(method, nodeID);\n\t\t}\n\n\t\tString name = DotGraphUtils.unresolvedMethodFormatName(method, longNames);\n\n\t\tColor themeOutOfFocus = UIManager.getColor(\"Component.disabledBorderColor\");\n\t\tString outOfFocus =\n\t\t\t\tString.format(\"color=\\\"#%02x%02x%02x\\\"\", themeOutOfFocus.getRed(), themeOutOfFocus.getGreen(),\n\t\t\t\t\t\tthemeOutOfFocus.getBlue());\n\n\t\tf.format(\"Node_%d [ label=\\\"{%s}\\\" style=dashed %s %s]\\n\", nodeID, UiUtils.toDotNodeName(name), outOfFocus, extra);\n\t\treturn nodeID;\n\t}\n\n\t// Add an edge between sourceID and destID to the graph in f\n\tprivate void addEdge(Formatter f, int sourceID, int destID) {\n\t\tEdge edge = new Edge(sourceID, destID);\n\t\tif (!edges.contains(edge)) {\n\t\t\tf.format(\"Node_%d -> Node_%d\\n\", sourceID, destID);\n\t\t\tedges.add(edge);\n\t\t}\n\t}\n\n\tprivate static class Edge {\n\t\tpublic int source;\n\t\tpublic int dest;\n\n\t\tpublic Edge(int source, int dest) {\n\t\t\tthis.source = source;\n\t\t\tthis.dest = dest;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object otherObject) {\n\t\t\tif (!(otherObject instanceof Edge)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tEdge other = (Edge) otherObject;\n\t\t\treturn (this.source == other.source) && (this.dest == other.dest);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(source, dest);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/CharsetDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.Component;\nimport java.nio.charset.Charset;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport javax.swing.JOptionPane;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.utils.NLS;\n\npublic class CharsetDialog {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CharsetDialog.class);\n\n\tprivate static final Comparator<Charset> CHARSET_COMPARATOR = Comparator.comparing(\n\t\t\tCharset::displayName,\n\t\t\tString::compareToIgnoreCase);\n\n\tpublic static String chooseCharset(Component parent, String currentCharsetName) {\n\t\tCollection<Charset> availableCharsets = Charset.availableCharsets().values();\n\n\t\tList<Charset> sortedCharsets = availableCharsets.stream()\n\t\t\t\t.sorted(CHARSET_COMPARATOR)\n\t\t\t\t.collect(Collectors.toList());\n\n\t\tCharset initialSelection = null;\n\t\ttry {\n\t\t\tif (currentCharsetName != null && Charset.isSupported(currentCharsetName)) {\n\t\t\t\tinitialSelection = Charset.forName(currentCharsetName);\n\t\t\t\tif (!sortedCharsets.contains(initialSelection)) {\n\t\t\t\t\tinitialSelection = null;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to find initial charset '{}'\", currentCharsetName, e);\n\t\t}\n\t\tif (initialSelection == null && !sortedCharsets.isEmpty()) {\n\t\t\tinitialSelection = sortedCharsets.get(0);\n\t\t}\n\t\tCharset[] charsetArray = sortedCharsets.toArray(new Charset[0]);\n\n\t\tObject selectedValue = JOptionPane.showInputDialog(\n\t\t\t\tparent,\n\t\t\t\tNLS.str(\"encoding_dialog.message\"),\n\t\t\t\tNLS.str(\"encoding_dialog.title\"),\n\t\t\t\tJOptionPane.INFORMATION_MESSAGE,\n\t\t\t\tnull,\n\t\t\t\tcharsetArray,\n\t\t\t\tinitialSelection);\n\n\t\tif (selectedValue instanceof Charset) {\n\t\t\treturn ((Charset) selectedValue).name();\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/ClassInheritanceGraphDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.BorderLayout;\nimport java.awt.Color;\nimport java.awt.FlowLayout;\nimport java.util.ArrayList;\nimport java.util.Formatter;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.swing.JCheckBox;\nimport javax.swing.JMenuBar;\nimport javax.swing.JPanel;\nimport javax.swing.SwingUtilities;\nimport javax.swing.UIManager;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.android.apksig.internal.util.Pair;\n\nimport jadx.core.dex.attributes.AType;\nimport jadx.core.dex.attributes.nodes.MethodOverrideAttr;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.IMethodDetails;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.DotGraphUtils;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.layout.WrapLayout;\n\npublic class ClassInheritanceGraphDialog extends GraphDialog {\n\n\tprivate static final long serialVersionUID = 938883901412562913L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ClassInheritanceGraphDialog.class);\n\n\tprivate static final String FONT = \"fontname=\\\"Courier\\\" fontsize=12\";\n\tprivate ClassNode cls;\n\tprivate boolean longNames = false;\n\tprivate boolean overrides = false;\n\n\tprivate Map<Object, Integer> objectToNodeID = new HashMap<>();\n\tprivate int nextNodeID = 0;\n\n\tpublic ClassInheritanceGraphDialog(MainWindow mainWindow, ClassNode cls) {\n\t\tsuper(mainWindow,\n\t\t\t\tString.format(\"%s: %s\", NLS.str(\"graph_viewer.inheritance_graph.title\"), DotGraphUtils.classFormatName(cls, false)));\n\t\tthis.cls = cls;\n\t}\n\n\tpublic JMenuBar addMenuBar() {\n\t\tJMenuBar menuBar = super.addMenuBar();\n\n\t\t// Long names checkbox\n\t\tJCheckBox showLongNames = new JCheckBox(NLS.str(\"graph_viewer.long_names\"));\n\t\tshowLongNames.setSelected(false);\n\t\tshowLongNames.addItemListener(e -> {\n\t\t\tlongNames = showLongNames.isSelected();\n\t\t\treload();\n\t\t});\n\n\t\t// Overrides checkbox\n\t\tJCheckBox showOverrides = new JCheckBox(NLS.str(\"graph_viewer.overrides\"));\n\t\tshowOverrides.setSelected(false);\n\t\tshowOverrides.addItemListener(e -> {\n\t\t\toverrides = showOverrides.isSelected();\n\t\t\treload();\n\t\t});\n\n\t\t// Assemble menubar panel\n\t\tJPanel menuBarPanel = new JPanel();\n\t\tmenuBarPanel.setOpaque(false);\n\t\tmenuBarPanel.setLayout(new WrapLayout(FlowLayout.LEFT));\n\t\tmenuBarPanel.add(showLongNames, BorderLayout.PAGE_START);\n\t\tmenuBarPanel.add(showOverrides, BorderLayout.PAGE_START);\n\n\t\t// Add menubar panel to menuBar\n\t\tmenuBar.add(menuBarPanel);\n\t\treturn menuBar;\n\t}\n\n\tpublic static void open(MainWindow window, JClass node) {\n\n\t\tClassNode cls = node.getCls().getClassNode();\n\n\t\tClassInheritanceGraphDialog graphDialog = new ClassInheritanceGraphDialog(window, cls);\n\t\tgraphDialog.addMenuBar();\n\n\t\tgraphDialog.setVisible(true);\n\t\tgraphDialog.reload();\n\t}\n\n\tpublic void reload() {\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\tString graph = generateGraph(cls);\n\t\t\tgetPanel().setGraph(graph);\n\t\t});\n\t}\n\n\tprivate String generateGraph(ClassNode rootClass) {\n\t\tStringBuilder sb = new StringBuilder();\n\n\t\tClassNode cls = rootClass;\n\n\t\tobjectToNodeID = new HashMap<>();\n\n\t\tColor themeBackground = UIManager.getColor(\"Panel.background\");\n\t\tColor themeForeground = UIManager.getColor(\"Label.foreground\");\n\t\tColor themeHighlight = UIManager.getColor(\"Component.focusedBorderColor\");\n\t\tColor themeShade = UIManager.getColor(\"TextArea.background\");\n\n\t\tString bgColor =\n\t\t\t\tString.format(\"bgcolor=\\\"#%02x%02x%02x\\\"\", themeBackground.getRed(), themeBackground.getGreen(), themeBackground.getBlue());\n\t\tString lineColor =\n\t\t\t\tString.format(\"color=\\\"#%02x%02x%02x\\\"\", themeForeground.getRed(), themeForeground.getGreen(), themeForeground.getBlue());\n\t\tString fontColor =\n\t\t\t\tString.format(\"fontcolor=\\\"#%02x%02x%02x\\\"\", themeForeground.getRed(), themeForeground.getGreen(),\n\t\t\t\t\t\tthemeForeground.getBlue());\n\t\tString highlightColor =\n\t\t\t\tString.format(\"color=\\\"#%02x%02x%02x\\\"\", themeHighlight.getRed(), themeHighlight.getGreen(),\n\t\t\t\t\t\tthemeHighlight.getBlue());\n\t\tString shadeColor = String.format(\"fillcolor=\\\"#%02x%02x%02x\\\"\", themeShade.getRed(), themeShade.getGreen(), themeShade.getBlue());\n\n\t\ttry (Formatter f = new Formatter(sb)) {\n\n\t\t\t// graph header\n\t\t\tf.format(\"digraph G {\\n\");\n\t\t\tf.format(\"%s\\n\", bgColor);\n\t\t\tf.format(\"node[shape=\\\"record\\\" style=\\\"filled\\\" %s %s %s %s]\\n\", FONT, fontColor, lineColor, shadeColor);\n\t\t\tf.format(\"edge[arrowtail=\\\"onormal\\\" arrowhead=\\\"onormal\\\" %s %s %s]\\n\", FONT, fontColor, lineColor);\n\n\t\t\t// add nodes\n\t\t\tprocessClass(f, cls, highlightColor);\n\n\t\t\t// close graph\n\t\t\tf.format(\"}\");\n\n\t\t\treturn f.toString();\n\t\t}\n\t}\n\n\tprivate int processClass(Formatter f, ClassNode cls) {\n\t\treturn processClass(f, cls, \"\");\n\t}\n\n\tprivate int processClass(Formatter f, ClassNode cls, String extra) {\n\t\tif (objectToNodeID.containsKey(cls)) {\n\t\t\t// Don't process a class that has been processed before\n\t\t\treturn objectToNodeID.get(cls);\n\t\t}\n\t\tint classID = addNode(f, cls, extra);\n\n\t\t// add interface relationships\n\t\tList<ArgType> ifaces = cls.getInterfaces();\n\t\tfor (int i = 0; i < ifaces.size(); i++) {\n\t\t\tArgType iface = ifaces.get(i);\n\n\t\t\tint ifaceID;\n\t\t\tClassNode ifaceNode = cls.root().resolveClass(iface);\n\t\t\tif (ifaceNode != null) {\n\t\t\t\tifaceID = processClass(f, ifaceNode);\n\t\t\t\tobjectToNodeID.put(iface, ifaceID);\n\t\t\t} else {\n\t\t\t\tifaceID = addNode(f, iface);\n\t\t\t}\n\t\t\t// Classes implement interfaces, interfaces extend interfaces\n\t\t\tString edgeLabel = cls.getAccessFlags().isInterface() ? \"extends\" : \"implements\";\n\t\t\tf.format(\"Node_%d -> Node_%d [label=\\\"%s\\\" style=\\\"dashed\\\" ]\\n\", classID, ifaceID, edgeLabel);\n\t\t}\n\n\t\t// add superclass relationship\n\t\tArgType superClass = cls.getSuperClass();\n\n\t\tif (superClass != ArgType.OBJECT) {\n\t\t\tint superClsID;\n\t\t\tcls = cls.root().resolveClass(superClass);\n\t\t\tif (cls != null) {\n\t\t\t\tsuperClsID = processClass(f, cls);\n\t\t\t\tobjectToNodeID.put(superClass, superClsID);\n\t\t\t} else {\n\t\t\t\tsuperClsID = addNode(f, superClass);\n\t\t\t}\n\n\t\t\tf.format(\"Node_%d -> Node_%d [label=\\\"extends\\\" ]\\n\", classID, superClsID);\n\t\t}\n\t\treturn classID;\n\t}\n\n\t// Add a node for a class\n\tprivate int addNode(Formatter f, ClassNode cls) {\n\t\treturn addNode(f, cls, \"\");\n\t}\n\n\tprivate int addNode(Formatter f, ClassNode cls, String extra) {\n\t\tint nodeID;\n\t\tif (objectToNodeID.containsKey(cls)) {\n\t\t\tnodeID = objectToNodeID.get(cls);\n\t\t} else {\n\t\t\tnodeID = nextNodeID;\n\t\t\tnextNodeID++;\n\t\t\tobjectToNodeID.put(cls, nodeID);\n\t\t}\n\n\t\tif (cls.getAccessFlags().isInterface()) {\n\t\t\textra += \" style=\\\"dashed, filled\\\"\";\n\t\t}\n\n\t\tString name = DotGraphUtils.classFormatName(cls, longNames);\n\t\tf.format(\"Node_%d [ label=\\\"{%s\\\\ \", nodeID, UiUtils.toDotNodeName(name));\n\n\t\tif (overrides) {\n\t\t\tf.format(\"|\");\n\t\t\tList<Pair<String, String>> table = new ArrayList<>();\n\t\t\tfor (MethodNode method : cls.getMethods()) {\n\t\t\t\tMethodOverrideAttr ovrdAttr = method.get(AType.METHOD_OVERRIDE);\n\t\t\t\tif (ovrdAttr != null) {\n\t\t\t\t\tif (!ovrdAttr.getOverrideList().isEmpty()) {\n\t\t\t\t\t\tString methodName = DotGraphUtils.methodFormatName(method, longNames);\n\t\t\t\t\t\tFormatter details = new Formatter();\n\t\t\t\t\t\tdetails.format(\" overrides \");\n\t\t\t\t\t\tfor (IMethodDetails baseMthDetails : ovrdAttr.getOverrideList()) {\n\t\t\t\t\t\t\tString baseClassName = DotGraphUtils.classFormatName(baseMthDetails.getMethodInfo().getDeclClass(), longNames);\n\t\t\t\t\t\t\tdetails.format(\"%s, \", baseClassName);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tString detailsString = details.toString();\n\n\t\t\t\t\t\t// Remove trailing ', '\n\t\t\t\t\t\tdetailsString = detailsString.substring(0, detailsString.length() - 2);\n\n\t\t\t\t\t\ttable.add(Pair.of(methodName, detailsString));\n\t\t\t\t\t\tdetails.close();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!table.isEmpty()) {\n\t\t\t\tint longestLength = table.stream().map(Pair::getFirst).map(String::length).max((a, b) -> a - b).get();\n\t\t\t\tfor (Pair<String, String> entry : table) {\n\t\t\t\t\tf.format(\"%-\" + longestLength + \"s %s\\\\l\", entry.getFirst(), entry.getSecond());\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tf.format(\"No overrides.\");\n\t\t\t}\n\t\t}\n\t\tf.format(\"}\\\" %s]\\n\", extra);\n\n\t\treturn nodeID;\n\t}\n\n\t// Add a node for an unresolved argtype\n\tprivate int addNode(Formatter f, ArgType argType) {\n\t\treturn addNode(f, argType, \"\");\n\t}\n\n\tprivate int addNode(Formatter f, ArgType argType, String extra) {\n\t\tint nodeID;\n\t\tif (objectToNodeID.containsKey(argType)) {\n\t\t\tnodeID = objectToNodeID.get(argType);\n\t\t} else {\n\t\t\tnodeID = nextNodeID;\n\t\t\tnextNodeID++;\n\t\t\tobjectToNodeID.put(argType, nodeID);\n\t\t}\n\n\t\tColor themeOutOfFocus = UIManager.getColor(\"Component.disabledBorderColor\");\n\t\tString outOfFocus =\n\t\t\t\tString.format(\"color=\\\"#%02x%02x%02x\\\"\", themeOutOfFocus.getRed(), themeOutOfFocus.getGreen(), themeOutOfFocus.getBlue());\n\n\t\tString name = DotGraphUtils.interfaceFormatName(argType, cls, longNames);\n\t\tf.format(\"Node_%d [ label=\\\"{%s}\\\" %s %s]\\n\", nodeID, UiUtils.toDotNodeName(name), outOfFocus, extra);\n\n\t\treturn nodeID;\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/ClassMethodGraphDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.BorderLayout;\nimport java.awt.Color;\nimport java.awt.FlowLayout;\nimport java.util.Collections;\nimport java.util.Formatter;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport javax.swing.JCheckBox;\nimport javax.swing.JMenuBar;\nimport javax.swing.JPanel;\nimport javax.swing.SwingUtilities;\nimport javax.swing.UIManager;\n\nimport jadx.api.JavaMethod;\nimport jadx.api.JavaNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.DotGraphUtils;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.layout.WrapLayout;\n\npublic class ClassMethodGraphDialog extends GraphDialog {\n\n\tprivate static final long serialVersionUID = -850803763322590708L;\n\n\tprivate static final String FONT = \"fontname=\\\"Courier\\\" fontsize=12\";\n\tprivate int callerDepthLimit = 10;\n\tprivate int nextNodeID = 0;\n\tprivate Map<JavaMethod, Integer> methodToNodeID;\n\tprivate Set<Edge> edges;\n\tprivate List<JavaMethod> javaMethods = Collections.emptyList();\n\tprivate ClassNode cls;\n\tprivate boolean longNames = false;\n\n\tpublic ClassMethodGraphDialog(MainWindow mainWindow, ClassNode cls) {\n\t\tsuper(mainWindow, String.format(\"%s: %s\", NLS.str(\"graph_viewer.method_graph.title\"), DotGraphUtils.classFormatName(cls, false)));\n\t\tthis.cls = cls;\n\t}\n\n\tpublic JMenuBar addMenuBar() {\n\t\tJMenuBar menuBar = super.addMenuBar();\n\n\t\t// Long names checkbox\n\t\tJCheckBox showLongNames = new JCheckBox(NLS.str(\"graph_viewer.long_names\"));\n\t\tshowLongNames.setSelected(false);\n\t\tshowLongNames.addItemListener(e -> {\n\t\t\tlongNames = showLongNames.isSelected();\n\t\t\treload();\n\t\t});\n\n\t\t// Assemble menubar panel\n\t\tJPanel menuBarPanel = new JPanel();\n\t\tmenuBarPanel.setOpaque(false);\n\t\tmenuBarPanel.setLayout(new WrapLayout(FlowLayout.LEFT));\n\t\tmenuBarPanel.add(showLongNames, BorderLayout.PAGE_START);\n\n\t\t// Add menubar panel to menuBar\n\t\tmenuBar.add(menuBarPanel);\n\t\treturn menuBar;\n\t}\n\n\tpublic static void open(MainWindow window, JClass node) {\n\n\t\tClassNode cls = node.getCls().getClassNode();\n\t\tClassMethodGraphDialog graphDialog = new ClassMethodGraphDialog(window, cls);\n\t\tgraphDialog.addMenuBar();\n\n\t\tgraphDialog.setVisible(true);\n\t\tgraphDialog.reload();\n\t}\n\n\tpublic void reload() {\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\tString graph = generateGraph(cls);\n\t\t\tgetPanel().setGraph(graph);\n\t\t});\n\t}\n\n\tprivate String generateGraph(ClassNode classNode) {\n\t\tStringBuilder sb = new StringBuilder();\n\n\t\tColor themeBackground = UIManager.getColor(\"Panel.background\");\n\t\tColor themeForeground = UIManager.getColor(\"Label.foreground\");\n\t\tColor themeShade = UIManager.getColor(\"TextArea.background\");\n\n\t\tString bgColor =\n\t\t\t\tString.format(\"bgcolor=\\\"#%02x%02x%02x\\\"\", themeBackground.getRed(), themeBackground.getGreen(),\n\t\t\t\t\t\tthemeBackground.getBlue());\n\t\tString lineColor =\n\t\t\t\tString.format(\"color=\\\"#%02x%02x%02x\\\"\", themeForeground.getRed(), themeForeground.getGreen(), themeForeground.getBlue());\n\t\tString fontColor =\n\t\t\t\tString.format(\"fontcolor=\\\"#%02x%02x%02x\\\"\", themeForeground.getRed(), themeForeground.getGreen(),\n\t\t\t\t\t\tthemeForeground.getBlue());\n\t\tString shadeColor = String.format(\"fillcolor=\\\"#%02x%02x%02x\\\"\", themeShade.getRed(), themeShade.getGreen(), themeShade.getBlue());\n\n\t\ttry (Formatter f = new Formatter(sb)) {\n\n\t\t\t// graph header\n\t\t\tf.format(\"digraph G {\\n\");\n\t\t\tf.format(\"%s\\n\", bgColor);\n\t\t\tf.format(\"node[shape=\\\"record\\\" style=\\\"filled\\\" %s %s %s %s]\\n\", FONT, fontColor, lineColor, shadeColor);\n\t\t\tf.format(\"edge[arrowtail=\\\"onormal\\\" arrowhead=\\\"onormal\\\" %s %s %s]\\n\", FONT, fontColor, lineColor);\n\n\t\t\tnextNodeID = 0;\n\t\t\tmethodToNodeID = new HashMap<>();\n\t\t\tedges = new HashSet<>();\n\n\t\t\tList<MethodNode> methods = classNode.getMethods();\n\t\t\tjavaMethods = methods.stream().map(method -> method.getJavaNode()).collect(Collectors.toList());\n\n\t\t\tfor (JavaMethod javaMethod : javaMethods) {\n\n\t\t\t\taddNode(f, javaMethod);\n\n\t\t\t\t// add caller relationships\n\t\t\t\taddCallers(0, f, javaMethod);\n\t\t\t}\n\n\t\t\t// close graph\n\t\t\tf.format(\"}\");\n\n\t\t\treturn f.toString();\n\t\t}\n\t}\n\n\tprivate void addCallers(int depth, Formatter f, JavaMethod javaMethod) {\n\t\tif (depth >= callerDepthLimit) {\n\t\t\treturn;\n\t\t}\n\n\t\tList<JavaNode> uses = javaMethod.getUseIn();\n\n\t\t// add \"calls\" relationships\n\t\tfor (JavaNode node : uses) {\n\t\t\tif (!(node instanceof JavaMethod)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tJavaMethod caller = (JavaMethod) node;\n\n\t\t\t// Do not process callers that are not methods from the class\n\t\t\tif (!javaMethods.contains(node)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tint nodeID = addNode(f, caller);\n\t\t\taddEdge(f, nodeID, methodToNodeID.get(javaMethod));\n\n\t\t\taddCallers(depth + 1, f, caller);\n\t\t}\n\t}\n\n\t// Add a node representing method to the graph in f. Returns the ID of the new node\n\tprivate int addNode(Formatter f, JavaMethod method) {\n\t\tint nodeID;\n\t\tif (methodToNodeID.containsKey(method)) {\n\t\t\tnodeID = methodToNodeID.get(method);\n\t\t} else {\n\t\t\tnodeID = nextNodeID;\n\t\t\tnextNodeID++;\n\t\t\tmethodToNodeID.put(method, nodeID);\n\t\t}\n\n\t\tString name = DotGraphUtils.methodFormatName(method, longNames);\n\t\tf.format(\"Node_%d [ label=\\\"{%s}\\\"]\\n\", nodeID, UiUtils.toDotNodeName(name));\n\n\t\tif (method.callsSelf()) {\n\t\t\taddEdge(f, nodeID, nodeID);\n\t\t}\n\n\t\treturn nodeID;\n\t}\n\n\t// Add an edge between sourceID and destID to the graph in f\n\tprivate void addEdge(Formatter f, int sourceID, int destID) {\n\t\tEdge edge = new Edge(sourceID, destID);\n\t\tif (!edges.contains(edge)) {\n\t\t\tf.format(\"Node_%d -> Node_%d\\n\", sourceID, destID);\n\t\t\tedges.add(edge);\n\t\t}\n\t}\n\n\tprivate static class Edge {\n\t\tpublic int source;\n\t\tpublic int dest;\n\n\t\tpublic Edge(int source, int dest) {\n\t\t\tthis.source = source;\n\t\t\tthis.dest = dest;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object otherObject) {\n\t\t\tif (!(otherObject instanceof Edge)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tEdge other = (Edge) otherObject;\n\t\t\treturn (this.source == other.source) && (this.dest == other.dest);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(source, dest);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/CommentDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.BorderLayout;\nimport java.awt.Component;\nimport java.awt.Container;\nimport java.awt.Dimension;\nimport java.awt.event.KeyAdapter;\nimport java.awt.event.KeyEvent;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\nimport javax.swing.JComboBox;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTextArea;\nimport javax.swing.SwingConstants;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.data.CommentStyle;\nimport jadx.api.data.ICodeComment;\nimport jadx.api.data.impl.JadxCodeComment;\nimport jadx.api.data.impl.JadxCodeData;\nimport jadx.gui.settings.JadxProject;\nimport jadx.gui.ui.codearea.CodeArea;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.TextStandardActions;\n\npublic class CommentDialog extends CommonDialog {\n\tprivate static final long serialVersionUID = -1865682124935757528L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CommentDialog.class);\n\n\tpublic static void show(CodeArea codeArea, ICodeComment comment, boolean updateComment) {\n\t\tCommentDialog dialog = new CommentDialog(codeArea, comment, updateComment);\n\t\tdialog.setVisible(true);\n\t}\n\n\tprivate static void updateCommentsData(CodeArea codeArea, Consumer<List<ICodeComment>> updater) {\n\t\ttry {\n\t\t\tJadxProject project = codeArea.getProject();\n\t\t\tJadxCodeData codeData = project.getCodeData();\n\t\t\tif (codeData == null) {\n\t\t\t\tcodeData = new JadxCodeData();\n\t\t\t}\n\t\t\tList<ICodeComment> list = new ArrayList<>(codeData.getComments());\n\t\t\tupdater.accept(list);\n\t\t\tCollections.sort(list);\n\t\t\tcodeData.setComments(list);\n\t\t\tproject.setCodeData(codeData);\n\t\t\tcodeArea.getMainWindow().getWrapper().reloadCodeData();\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Comment action failed\", e);\n\t\t}\n\t\ttry {\n\t\t\t// refresh code in a background thread to avoid blocking the ui\n\t\t\tcodeArea.backgroundRefreshClass();\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to reload code\", e);\n\t\t}\n\t}\n\n\tprivate final transient CodeArea codeArea;\n\tprivate final transient ICodeComment comment;\n\tprivate final transient boolean updateComment;\n\n\tprivate transient JTextArea commentArea;\n\tprivate transient JComboBox<CommentStyle> styleCombo;\n\n\tpublic CommentDialog(CodeArea codeArea, ICodeComment comment, boolean updateComment) {\n\t\tsuper(codeArea.getMainWindow());\n\t\tthis.codeArea = codeArea;\n\t\tthis.comment = comment;\n\t\tthis.updateComment = updateComment;\n\t\tinitUI();\n\t}\n\n\tprivate void apply() {\n\t\tString newCommentStr = commentArea.getText().trim();\n\t\tif (newCommentStr.isEmpty()) {\n\t\t\tif (updateComment) {\n\t\t\t\tremove();\n\t\t\t} else {\n\t\t\t\tcancel();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tCommentStyle style = (CommentStyle) styleCombo.getSelectedItem();\n\t\tICodeComment newComment = new JadxCodeComment(comment.getNodeRef(), comment.getCodeRef(), newCommentStr, style);\n\t\tif (updateComment) {\n\t\t\tupdateCommentsData(codeArea, list -> {\n\t\t\t\tlist.remove(comment);\n\t\t\t\tlist.add(newComment);\n\t\t\t});\n\t\t} else {\n\t\t\tupdateCommentsData(codeArea, list -> list.add(newComment));\n\t\t}\n\t\tdispose();\n\t}\n\n\tprivate void remove() {\n\t\tupdateCommentsData(codeArea, list -> list.removeIf(c -> c == comment));\n\t\tdispose();\n\t}\n\n\tprivate void cancel() {\n\t\tdispose();\n\t}\n\n\tprivate void initUI() {\n\t\tcommentArea = new JTextArea();\n\t\tTextStandardActions.attach(commentArea);\n\t\tcommentArea.setEditable(true);\n\t\tcommentArea.setFont(mainWindow.getSettings().getCodeFont());\n\t\tcommentArea.setAlignmentX(Component.LEFT_ALIGNMENT);\n\n\t\tcommentArea.addKeyListener(new KeyAdapter() {\n\t\t\t@Override\n\t\t\tpublic void keyPressed(KeyEvent e) {\n\t\t\t\tswitch (e.getKeyCode()) {\n\t\t\t\t\tcase KeyEvent.VK_ENTER:\n\t\t\t\t\t\tif (e.isShiftDown() || e.isControlDown()) {\n\t\t\t\t\t\t\tcommentArea.insert(\"\\n\", commentArea.getCaretPosition());\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tapply();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase KeyEvent.VK_ESCAPE:\n\t\t\t\t\t\tcancel();\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tif (updateComment) {\n\t\t\tcommentArea.setText(comment.getComment());\n\t\t}\n\n\t\tJScrollPane textAreaScrollPane = new JScrollPane(commentArea);\n\t\ttextAreaScrollPane.setAlignmentX(LEFT_ALIGNMENT);\n\n\t\tstyleCombo = new JComboBox<>(CommentStyle.values());\n\t\tstyleCombo.setSelectedItem(comment.getStyle());\n\n\t\tJLabel commentLabel = new JLabel(NLS.str(\"comment_dialog.label\"), SwingConstants.LEFT);\n\t\tJLabel styleLabel = new JLabel(NLS.str(\"comment_dialog.style\"), SwingConstants.LEFT);\n\t\tJLabel usageLabel = new JLabel(NLS.str(\"comment_dialog.usage\"), SwingConstants.LEFT);\n\n\t\tJPanel inputPanel = new JPanel();\n\t\tinputPanel.setLayout(new BoxLayout(inputPanel, BoxLayout.PAGE_AXIS));\n\t\tinputPanel.add(commentLabel);\n\t\tinputPanel.add(Box.createRigidArea(new Dimension(0, 5)));\n\t\tinputPanel.add(textAreaScrollPane);\n\t\tinputPanel.add(Box.createRigidArea(new Dimension(0, 5)));\n\t\tinputPanel.add(usageLabel);\n\n\t\tJPanel stylePanel = new JPanel();\n\t\tstylePanel.setLayout(new BoxLayout(stylePanel, BoxLayout.LINE_AXIS));\n\t\tstylePanel.setAlignmentX(LEFT_ALIGNMENT);\n\t\tstylePanel.add(styleLabel);\n\t\tstylePanel.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tstylePanel.add(styleCombo);\n\n\t\tJPanel mainPanel = new JPanel(new BorderLayout(10, 10));\n\t\tmainPanel.add(inputPanel, BorderLayout.CENTER);\n\t\tmainPanel.add(stylePanel, BorderLayout.PAGE_END);\n\t\tmainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));\n\n\t\tJPanel buttonPane = initButtonsPanel();\n\n\t\tContainer contentPane = getContentPane();\n\t\tcontentPane.add(mainPanel, BorderLayout.CENTER);\n\t\tcontentPane.add(buttonPane, BorderLayout.PAGE_END);\n\n\t\tif (updateComment) {\n\t\t\tsetTitle(NLS.str(\"comment_dialog.title.update\"));\n\t\t} else {\n\t\t\tsetTitle(NLS.str(\"comment_dialog.title.add\"));\n\t\t}\n\t\tcommonWindowInit();\n\t}\n\n\tprotected JPanel initButtonsPanel() {\n\t\tJButton cancelButton = new JButton(NLS.str(\"common_dialog.cancel\"));\n\t\tcancelButton.addActionListener(event -> cancel());\n\n\t\tString applyStr = updateComment ? NLS.str(\"common_dialog.update\") : NLS.str(\"common_dialog.add\");\n\t\tJButton renameBtn = new JButton(applyStr);\n\t\trenameBtn.addActionListener(event -> apply());\n\t\tgetRootPane().setDefaultButton(renameBtn);\n\n\t\tJButton removeBtn;\n\t\tif (updateComment) {\n\t\t\tremoveBtn = new JButton(NLS.str(\"common_dialog.remove\"));\n\t\t\tremoveBtn.addActionListener(event -> remove());\n\t\t} else {\n\t\t\tremoveBtn = null;\n\t\t}\n\n\t\tJPanel buttonPane = new JPanel();\n\t\tbuttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));\n\t\tbuttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));\n\t\tbuttonPane.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tbuttonPane.add(Box.createHorizontalGlue());\n\t\tbuttonPane.add(renameBtn);\n\t\tif (removeBtn != null) {\n\t\t\tbuttonPane.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\t\tbuttonPane.add(removeBtn);\n\t\t}\n\t\tbuttonPane.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tbuttonPane.add(cancelButton);\n\t\treturn buttonPane;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/CommonDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.Dimension;\n\nimport javax.swing.JDialog;\nimport javax.swing.WindowConstants;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.UiUtils;\n\npublic abstract class CommonDialog extends JDialog {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CommonDialog.class);\n\n\tprotected final MainWindow mainWindow;\n\n\tpublic CommonDialog(MainWindow mainWindow) {\n\t\tsuper(mainWindow);\n\t\tthis.mainWindow = mainWindow;\n\t}\n\n\tprotected void commonWindowInit() {\n\t\tsetModalityType(ModalityType.APPLICATION_MODAL);\n\t\tsetDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\n\t\tUiUtils.addEscapeShortCutToDispose(this);\n\t\tsetLocationRelativeTo(null);\n\n\t\tUiUtils.uiRunAndWait(this::pack);\n\t\tDimension minSize = getSize();\n\t\tsetMinimumSize(minSize);\n\t\tif (!mainWindow.getSettings().loadWindowPos(this)) {\n\t\t\tsetSize(incByPercent(minSize.getWidth(), 30), incByPercent(minSize.getHeight(), 30));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void dispose() {\n\t\ttry {\n\t\t\tmainWindow.getSettings().saveWindowPos(this);\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to save window size and position\", e);\n\t\t}\n\t\tsuper.dispose();\n\t}\n\n\tprivate static int incByPercent(double value, int percent) {\n\t\treturn (int) (value * (1 + percent * 0.01));\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/CommonSearchDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.BorderLayout;\nimport java.awt.Color;\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.Font;\nimport java.awt.Rectangle;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.KeyAdapter;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.awt.event.WindowAdapter;\nimport java.awt.event.WindowEvent;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport javax.swing.AbstractAction;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JFrame;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTable;\nimport javax.swing.ListSelectionModel;\nimport javax.swing.SwingConstants;\nimport javax.swing.SwingUtilities;\nimport javax.swing.table.AbstractTableModel;\nimport javax.swing.table.TableCellRenderer;\nimport javax.swing.table.TableColumn;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.fife.ui.rtextarea.SearchContext;\nimport org.fife.ui.rtextarea.SearchEngine;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport ch.qos.logback.classic.Level;\n\nimport jadx.api.JavaNode;\nimport jadx.gui.logs.LogOptions;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.treemodel.JResSearchNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.codearea.AbstractCodeArea;\nimport jadx.gui.ui.panel.ProgressPanel;\nimport jadx.gui.ui.tab.TabsController;\nimport jadx.gui.utils.CacheObject;\nimport jadx.gui.utils.JNodeCache;\nimport jadx.gui.utils.JumpPosition;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.ui.NodeLabel;\n\nimport static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED;\nimport static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED;\n\npublic abstract class CommonSearchDialog extends JFrame {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CommonSearchDialog.class);\n\tprivate static final long serialVersionUID = 8939332306115370276L;\n\n\tprotected final transient TabsController tabsController;\n\tprotected final transient CacheObject cache;\n\tprotected final transient MainWindow mainWindow;\n\tprotected final transient Font codeFont;\n\tprotected final transient String windowTitle;\n\n\tprotected ResultsModel resultsModel;\n\tprotected ResultsTable resultsTable;\n\tprotected JLabel resultsInfoLabel;\n\tprotected JLabel progressInfoLabel;\n\tprotected JLabel warnLabel;\n\tprotected ProgressPanel progressPane;\n\n\tprivate SearchContext highlightContext;\n\n\tpublic CommonSearchDialog(MainWindow mainWindow, String title) {\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.tabsController = mainWindow.getTabsController();\n\t\tthis.cache = mainWindow.getCacheObject();\n\t\tthis.codeFont = mainWindow.getSettings().getCodeFont();\n\t\tthis.windowTitle = title;\n\t\tUiUtils.setWindowIcons(this);\n\t\tupdateTitle(\"\");\n\t}\n\n\tprotected abstract void openInit();\n\n\tprotected abstract void loadFinished();\n\n\tprotected abstract void loadStart();\n\n\tpublic void loadWindowPos() {\n\t\tif (!mainWindow.getSettings().loadWindowPos(this)) {\n\t\t\tsetSize(800, 500);\n\t\t}\n\t}\n\n\tprivate void updateTitle(String searchText) {\n\t\tif (searchText == null || searchText.isEmpty() || searchText.trim().isEmpty()) {\n\t\t\tsetTitle(windowTitle);\n\t\t} else {\n\t\t\tsetTitle(windowTitle + \": \" + searchText);\n\t\t}\n\t}\n\n\tpublic void updateHighlightContext(String text, boolean caseSensitive, boolean regexp, boolean wholeWord) {\n\t\tupdateTitle(text);\n\t\thighlightContext = new SearchContext(text);\n\t\thighlightContext.setMatchCase(caseSensitive);\n\t\thighlightContext.setWholeWord(wholeWord);\n\t\thighlightContext.setRegularExpression(regexp);\n\t\thighlightContext.setMarkAll(true);\n\t}\n\n\tpublic void disableHighlight() {\n\t\thighlightContext = null;\n\t}\n\n\tprotected void registerInitOnOpen() {\n\t\taddWindowListener(new WindowAdapter() {\n\t\t\t@Override\n\t\t\tpublic void windowOpened(WindowEvent e) {\n\t\t\t\tSwingUtilities.invokeLater(CommonSearchDialog.this::openInit);\n\t\t\t}\n\t\t});\n\t}\n\n\tprotected void openSelectedItem() {\n\t\tJNode node = getSelectedNode();\n\t\tif (node == null) {\n\t\t\treturn;\n\t\t}\n\t\topenItem(node);\n\t}\n\n\tprotected void openItem(JNode node) {\n\t\tif (node instanceof JResSearchNode) {\n\t\t\tJumpPosition jmpPos = new JumpPosition(((JResSearchNode) node).getResNode(), node.getPos());\n\t\t\ttabsController.codeJump(jmpPos);\n\t\t} else {\n\t\t\ttabsController.codeJump(node);\n\t\t}\n\t\tif (!mainWindow.getSettings().isKeepCommonDialogOpen()) {\n\t\t\tdispose();\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate JNode getSelectedNode() {\n\t\ttry {\n\t\t\tint selectedId = resultsTable.getSelectedRow();\n\t\t\tif (selectedId == -1 || selectedId >= resultsTable.getRowCount()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn (JNode) resultsModel.getValueAt(selectedId, 0);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to get results table selected object\", e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void dispose() {\n\t\tmainWindow.getSettings().saveWindowPos(this);\n\t\tsuper.dispose();\n\t}\n\n\tprotected void initCommon() {\n\t\tUiUtils.addEscapeShortCutToDispose(this);\n\t}\n\n\tprotected void copyAllSearchResults() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tSet<String> uniqueRefs = new HashSet<>();\n\t\tfor (JNode node : resultsModel.rows) {\n\t\t\tJavaNode javaNode = node.getJavaNode();\n\t\t\tif (javaNode != null) {\n\t\t\t\tString codeNodeRef = javaNode.getCodeNodeRef().toString();\n\t\t\t\tif (uniqueRefs.add(codeNodeRef)) {\n\t\t\t\t\tsb.append(codeNodeRef).append(\"\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tUiUtils.copyToClipboard(sb.toString());\n\t}\n\n\t@NotNull\n\tprotected JPanel initButtonsPanel() {\n\t\tprogressPane = new ProgressPanel(mainWindow, false);\n\n\t\tJButton cancelButton = new JButton(NLS.str(\"search_dialog.cancel\"));\n\t\tcancelButton.addActionListener(event -> dispose());\n\t\tJButton openBtn = new JButton(NLS.str(\"search_dialog.open\"));\n\t\topenBtn.addActionListener(event -> openSelectedItem());\n\t\tgetRootPane().setDefaultButton(openBtn);\n\t\tJButton copyBtn = new JButton(NLS.str(\"search_dialog.copy\"));\n\t\tcopyBtn.addActionListener(event -> copyAllSearchResults());\n\n\t\tJCheckBox cbKeepOpen = new JCheckBox(NLS.str(\"search_dialog.keep_open\"));\n\t\tcbKeepOpen.setSelected(mainWindow.getSettings().isKeepCommonDialogOpen());\n\t\tcbKeepOpen.addActionListener(e -> mainWindow.getSettings().saveKeepCommonDialogOpen(cbKeepOpen.isSelected()));\n\t\tcbKeepOpen.setAlignmentY(Component.CENTER_ALIGNMENT);\n\n\t\tJPanel buttonPane = new JPanel();\n\t\tbuttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));\n\t\tbuttonPane.add(cbKeepOpen);\n\t\tbuttonPane.add(Box.createRigidArea(new Dimension(15, 0)));\n\t\tbuttonPane.add(progressPane);\n\t\tbuttonPane.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tbuttonPane.add(Box.createHorizontalGlue());\n\t\tbuttonPane.add(copyBtn);\n\t\tbuttonPane.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tbuttonPane.add(openBtn);\n\t\tbuttonPane.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tbuttonPane.add(cancelButton);\n\t\treturn buttonPane;\n\t}\n\n\tprotected JPanel initResultsTable() {\n\t\tResultsTableCellRenderer renderer = new ResultsTableCellRenderer();\n\t\tresultsModel = new ResultsModel();\n\t\tresultsModel.addTableModelListener(e -> updateProgressLabel(false));\n\n\t\tresultsTable = new ResultsTable(resultsModel, renderer);\n\t\tresultsTable.setShowHorizontalLines(false);\n\t\tresultsTable.setDragEnabled(false);\n\t\tresultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\n\t\tresultsTable.setColumnSelectionAllowed(false);\n\t\tresultsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);\n\t\tresultsTable.setAutoscrolls(false);\n\n\t\tresultsTable.setDefaultRenderer(Object.class, renderer);\n\t\tEnumeration<TableColumn> columns = resultsTable.getColumnModel().getColumns();\n\t\twhile (columns.hasMoreElements()) {\n\t\t\tTableColumn column = columns.nextElement();\n\t\t\tcolumn.setCellRenderer(renderer);\n\t\t}\n\n\t\tresultsTable.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(MouseEvent evt) {\n\t\t\t\tif (evt.getClickCount() == 2) {\n\t\t\t\t\topenSelectedItem();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tresultsTable.addKeyListener(new KeyAdapter() {\n\t\t\t@Override\n\t\t\tpublic void keyPressed(KeyEvent e) {\n\t\t\t\tif (e.getKeyCode() == KeyEvent.VK_ENTER) {\n\t\t\t\t\topenSelectedItem();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\t// override copy action to copy long string of node column\n\t\tresultsTable.getActionMap().put(\"copy\", new AbstractAction() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tJNode selectedNode = getSelectedNode();\n\t\t\t\tif (selectedNode != null) {\n\t\t\t\t\tUiUtils.copyToClipboard(selectedNode.makeLongString());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\twarnLabel = new JLabel();\n\t\twarnLabel.setForeground(Color.RED);\n\t\twarnLabel.setVisible(false);\n\n\t\tJScrollPane scroll = new JScrollPane(resultsTable, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED);\n\n\t\tresultsInfoLabel = new JLabel(\"\");\n\t\tresultsInfoLabel.setFont(mainWindow.getSettings().getUiFont());\n\n\t\tprogressInfoLabel = new JLabel(\"\");\n\t\tprogressInfoLabel.setFont(mainWindow.getSettings().getUiFont());\n\t\tprogressInfoLabel.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(MouseEvent e) {\n\t\t\t\tmainWindow.showLogViewer(LogOptions.allWithLevel(Level.INFO));\n\t\t\t}\n\t\t});\n\n\t\tJPanel resultsActionsPanel = new JPanel();\n\t\tresultsActionsPanel.setLayout(new BoxLayout(resultsActionsPanel, BoxLayout.LINE_AXIS));\n\t\tresultsActionsPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));\n\t\taddResultsActions(resultsActionsPanel);\n\n\t\tJPanel resultsPanel = new JPanel();\n\t\tresultsPanel.setLayout(new BoxLayout(resultsPanel, BoxLayout.PAGE_AXIS));\n\t\tresultsPanel.add(warnLabel, BorderLayout.PAGE_START);\n\t\tresultsPanel.add(scroll, BorderLayout.CENTER);\n\t\tresultsPanel.add(resultsActionsPanel, BorderLayout.PAGE_END);\n\t\treturn resultsPanel;\n\t}\n\n\tprotected void addResultsActions(JPanel resultsActionsPanel) {\n\t\tresultsActionsPanel.add(Box.createRigidArea(new Dimension(20, 0)));\n\t\tresultsActionsPanel.add(resultsInfoLabel);\n\t\tresultsActionsPanel.add(Box.createRigidArea(new Dimension(20, 0)));\n\t\tresultsActionsPanel.add(progressInfoLabel);\n\t\tresultsActionsPanel.add(Box.createHorizontalGlue());\n\t}\n\n\tprotected void updateProgressLabel(boolean complete) {\n\t\tint count = resultsModel.getRowCount();\n\t\tString statusText;\n\t\tif (complete) {\n\t\t\tstatusText = NLS.str(\"search_dialog.results_complete\", count);\n\t\t} else {\n\t\t\tstatusText = NLS.str(\"search_dialog.results_incomplete\", count);\n\t\t}\n\t\tresultsInfoLabel.setText(statusText);\n\t}\n\n\tprotected void showSearchState() {\n\t\tresultsInfoLabel.setText(NLS.str(\"search_dialog.tip_searching\") + \"...\");\n\t}\n\n\tprotected static final class ResultsTable extends JTable {\n\t\tprivate static final long serialVersionUID = 3901184054736618969L;\n\t\tprivate final transient ResultsModel model;\n\n\t\tpublic ResultsTable(ResultsModel resultsModel, ResultsTableCellRenderer renderer) {\n\t\t\tsuper(resultsModel);\n\t\t\tthis.model = resultsModel;\n\t\t\tsetRowHeight(renderer.getMaxRowHeight());\n\t\t}\n\n\t\tpublic void initColumnWidth() {\n\t\t\tint columnCount = getColumnCount();\n\t\t\tint width = getParent().getWidth();\n\t\t\tint colWidth = model.isAddDescColumn() ? width / 2 : width;\n\t\t\tcolumnModel.getColumn(0).setPreferredWidth(colWidth);\n\t\t\tfor (int col = 1; col < columnCount; col++) {\n\t\t\t\tcolumnModel.getColumn(col).setPreferredWidth(width);\n\t\t\t}\n\t\t}\n\n\t\tpublic void updateTable() {\n\t\t\tUiUtils.uiThreadGuard();\n\t\t\tint rowCount = getRowCount();\n\t\t\tif (rowCount == 0) {\n\t\t\t\tupdateUI();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlong start = System.currentTimeMillis();\n\t\t\tint width = getParent().getWidth();\n\t\t\tTableColumn firstColumn = columnModel.getColumn(0);\n\t\t\tif (model.isAddDescColumn()) {\n\t\t\t\tif (firstColumn.getWidth() > width * 0.8) {\n\t\t\t\t\t// first column too big and hide second column, resize it\n\t\t\t\t\tfirstColumn.setPreferredWidth(width / 2);\n\t\t\t\t}\n\t\t\t\tTableColumn secondColumn = columnModel.getColumn(1);\n\t\t\t\tint columnMaxWidth = width * 2; // set big enough size to skip per row check\n\t\t\t\tif (secondColumn.getWidth() < columnMaxWidth) {\n\t\t\t\t\tsecondColumn.setPreferredWidth(columnMaxWidth);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfirstColumn.setPreferredWidth(width);\n\t\t\t}\n\t\t\tupdateUI();\n\t\t\tif (LOG.isDebugEnabled()) {\n\t\t\t\tLOG.debug(\"Update results table in {}ms, count: {}\", System.currentTimeMillis() - start, rowCount);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getValueAt(int row, int column) {\n\t\t\treturn model.getValueAt(row, column);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {\n\t\t\t// ResultsTable only has two wide columns, the default increment is way too fast\n\t\t\tif (orientation == SwingConstants.HORIZONTAL) {\n\t\t\t\treturn 30;\n\t\t\t}\n\t\t\treturn super.getScrollableUnitIncrement(visibleRect, orientation, direction);\n\t\t}\n\t}\n\n\tprotected static final class ResultsModel extends AbstractTableModel {\n\t\tprivate static final long serialVersionUID = -7821286846923903208L;\n\t\tprivate static final String[] COLUMN_NAMES = { NLS.str(\"search_dialog.col_node\"), NLS.str(\"search_dialog.col_code\") };\n\n\t\tprivate final transient List<JNode> rows = new ArrayList<>();\n\t\tprivate transient boolean addDescColumn;\n\n\t\tpublic void addAll(Collection<? extends JNode> nodes) {\n\t\t\trows.addAll(nodes);\n\t\t\tif (!addDescColumn) {\n\t\t\t\tfor (JNode row : rows) {\n\t\t\t\t\tif (row.hasDescString()) {\n\t\t\t\t\t\taddDescColumn = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic void clear() {\n\t\t\taddDescColumn = false;\n\t\t\trows.clear();\n\t\t}\n\n\t\tpublic void sort() {\n\t\t\tCollections.sort(rows);\n\t\t}\n\n\t\tpublic boolean isAddDescColumn() {\n\t\t\treturn addDescColumn;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getRowCount() {\n\t\t\treturn rows.size();\n\t\t}\n\n\t\t@Override\n\t\tpublic int getColumnCount() {\n\t\t\treturn 2;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getColumnName(int index) {\n\t\t\treturn COLUMN_NAMES[index];\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getValueAt(int rowIndex, int columnIndex) {\n\t\t\treturn rows.get(rowIndex);\n\t\t}\n\t}\n\n\tprotected final class ResultsTableCellRenderer implements TableCellRenderer {\n\t\tprivate final NodeLabel label;\n\t\tprivate final RSyntaxTextArea codeArea;\n\t\tprivate final NodeLabel emptyLabel;\n\t\tprivate final Color codeSelectedColor;\n\t\tprivate final Color codeBackground;\n\n\t\tpublic ResultsTableCellRenderer() {\n\t\t\tcodeArea = AbstractCodeArea.getDefaultArea(mainWindow);\n\t\t\tcodeArea.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));\n\t\t\tcodeArea.setRows(1);\n\t\t\tcodeBackground = codeArea.getBackground();\n\t\t\tcodeSelectedColor = codeArea.getSelectionColor();\n\t\t\tlabel = new NodeLabel();\n\t\t\tlabel.setOpaque(true);\n\t\t\tlabel.setFont(codeArea.getFont());\n\t\t\tlabel.setHorizontalAlignment(SwingConstants.LEFT);\n\t\t\temptyLabel = new NodeLabel();\n\t\t\temptyLabel.setOpaque(true);\n\t\t}\n\n\t\t@Override\n\t\tpublic Component getTableCellRendererComponent(JTable table, Object obj, boolean isSelected, boolean hasFocus, int row,\n\t\t\t\tint column) {\n\t\t\tif (obj == null || table == null) {\n\t\t\t\treturn emptyLabel;\n\t\t\t}\n\t\t\tComponent comp = makeCell((JNode) obj, column);\n\t\t\tupdateSelection(table, comp, column, isSelected);\n\t\t\treturn comp;\n\t\t}\n\n\t\tprivate void updateSelection(JTable table, Component comp, int column, boolean isSelected) {\n\t\t\tif (column == 1) {\n\t\t\t\tif (isSelected) {\n\t\t\t\t\tcomp.setBackground(codeSelectedColor);\n\t\t\t\t} else {\n\t\t\t\t\tcomp.setBackground(codeBackground);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (isSelected) {\n\t\t\t\t\tcomp.setBackground(table.getSelectionBackground());\n\t\t\t\t\tcomp.setForeground(table.getSelectionForeground());\n\t\t\t\t} else {\n\t\t\t\t\tcomp.setBackground(table.getBackground());\n\t\t\t\t\tcomp.setForeground(table.getForeground());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate Component makeCell(JNode node, int column) {\n\t\t\tif (column == 0) {\n\t\t\t\tlabel.disableHtml(node.disableHtml());\n\t\t\t\tlabel.setText(node.makeLongStringHtml());\n\t\t\t\tlabel.setToolTipText(node.getTooltip());\n\t\t\t\tlabel.setIcon(node.getIcon());\n\t\t\t\treturn label;\n\t\t\t}\n\t\t\tif (!node.hasDescString()) {\n\t\t\t\treturn emptyLabel;\n\t\t\t}\n\t\t\tcodeArea.setSyntaxEditingStyle(node.getSyntaxName());\n\t\t\tString descStr = node.makeDescString();\n\t\t\tcodeArea.setText(descStr);\n\t\t\tcodeArea.setColumns(descStr.length() + 1);\n\t\t\tif (highlightContext != null) {\n\t\t\t\tSearchEngine.markAll(codeArea, highlightContext);\n\t\t\t}\n\t\t\treturn codeArea;\n\t\t}\n\n\t\tpublic int getMaxRowHeight() {\n\t\t\tlabel.setText(\"Text\");\n\t\t\tcodeArea.setText(\"Text\");\n\t\t\treturn Math.max(getCompHeight(label), getCompHeight(codeArea));\n\t\t}\n\n\t\tprivate int getCompHeight(Component comp) {\n\t\t\treturn Math.max(comp.getHeight(), comp.getPreferredSize().height);\n\t\t}\n\t}\n\n\tvoid progressStartCommon() {\n\t\tprogressPane.setIndeterminate(true);\n\t\tprogressPane.setVisible(true);\n\t\twarnLabel.setVisible(false);\n\t}\n\n\tvoid progressFinishedCommon() {\n\t\tprogressPane.setVisible(false);\n\t}\n\n\tprotected JNodeCache getNodeCache() {\n\t\treturn mainWindow.getCacheObject().getNodeCache();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/ControlFlowGraphDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.io.File;\nimport java.util.Scanner;\n\nimport javax.swing.SwingUtilities;\n\nimport jadx.api.JavaMethod;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.utils.DotGraphUtils;\nimport jadx.gui.treemodel.JMethod;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\n\npublic class ControlFlowGraphDialog extends GraphDialog {\n\n\tprivate static final long serialVersionUID = -68749445239697710L;\n\n\tpublic ControlFlowGraphDialog(MainWindow mainWindow, String method) {\n\t\tsuper(mainWindow, String.format(\"%s: %s\", NLS.str(\"graph_viewer.cfg.title\"), method));\n\t}\n\n\tpublic static void open(MainWindow window, JMethod method, boolean useRegions, boolean rawInsn) {\n\n\t\tJavaMethod javaMethod = method.getJavaMethod();\n\n\t\tGraphDialog graphDialog = new ControlFlowGraphDialog(window, DotGraphUtils.methodFormatName(javaMethod, false));\n\t\tgraphDialog.addMenuBar();\n\t\tgraphDialog.setVisible(true);\n\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\tString graph = generateGraph(javaMethod, useRegions, rawInsn);\n\t\t\tif (graph != null) {\n\t\t\t\tgraphDialog.getPanel().setGraph(graph);\n\t\t\t} else {\n\t\t\t\tgraphDialog.getPanel().invalidateImage(graphError(NLS.str(\"graph_viewer.file_not_found_error\")));\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate static String generateGraph(JavaMethod javaMethod, boolean useRegions, boolean rawInsn) {\n\t\tMethodNode mth = javaMethod.getMethodNode();\n\t\tFile file = new DotGraphUtils(useRegions, rawInsn).getFullFile(mth);\n\n\t\ttry (Scanner reader = new Scanner(file)) {\n\t\t\tString contents = reader.useDelimiter(\"\\\\Z\").next();\n\t\t\treturn contents;\n\t\t} catch (Exception e) {\n\t\t\treturn null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/ExcludePkgDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.BorderLayout;\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.Font;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JDialog;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTree;\nimport javax.swing.WindowConstants;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.DefaultTreeCellRenderer;\nimport javax.swing.tree.DefaultTreeModel;\nimport javax.swing.tree.TreePath;\n\nimport jadx.api.JavaPackage;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic class ExcludePkgDialog extends JDialog {\n\tprivate static final long serialVersionUID = -1111111202104151030L;\n\n\tprivate final transient MainWindow mainWindow;\n\tprivate transient JTree tree;\n\tprivate transient DefaultMutableTreeNode treeRoot;\n\tprivate final transient List<PkgNode> roots = new ArrayList<>();\n\n\tpublic ExcludePkgDialog(MainWindow mainWindow) {\n\t\tsuper(mainWindow);\n\t\tthis.mainWindow = mainWindow;\n\t\tinitUI();\n\t\tUiUtils.addEscapeShortCutToDispose(this);\n\t\tinitPackageList();\n\t}\n\n\tprivate void initUI() {\n\t\tsetTitle(NLS.str(\"exclude_dialog.title\"));\n\t\ttree = new JTree();\n\t\ttree.setRowHeight(-1);\n\t\ttreeRoot = new DefaultMutableTreeNode(\"Packages\");\n\t\tDefaultTreeModel treeModel = new DefaultTreeModel(treeRoot);\n\t\ttree.setModel(treeModel);\n\t\ttree.setCellRenderer(new PkgListCellRenderer());\n\t\tJScrollPane listPanel = new JScrollPane(tree);\n\t\tlistPanel.setBorder(BorderFactory.createEmptyBorder());\n\t\ttree.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mousePressed(MouseEvent e) {\n\t\t\t\tTreePath path = tree.getPathForLocation(e.getX(), e.getY());\n\t\t\t\tif (path != null && path.getLastPathComponent() instanceof PkgNode) {\n\t\t\t\t\tPkgNode node = (PkgNode) path.getLastPathComponent();\n\t\t\t\t\tnode.toggle();\n\t\t\t\t\trepaint();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tJPanel actionPanel = new JPanel();\n\t\tactionPanel.setLayout(new BoxLayout(actionPanel, BoxLayout.LINE_AXIS));\n\t\tJButton btnOk = new JButton(NLS.str(\"common_dialog.ok\"));\n\t\tJButton btnAll = new JButton(NLS.str(\"exclude_dialog.select_all\"));\n\t\tJButton btnInvert = new JButton(NLS.str(\"exclude_dialog.invert\"));\n\t\tJButton btnDeselect = new JButton(NLS.str(\"exclude_dialog.deselect\"));\n\t\tactionPanel.add(btnDeselect);\n\t\tactionPanel.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tactionPanel.add(btnInvert);\n\t\tactionPanel.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tactionPanel.add(btnAll);\n\t\tactionPanel.add(Box.createHorizontalGlue());\n\t\tactionPanel.add(btnOk, BorderLayout.PAGE_END);\n\n\t\tJPanel mainPane = new JPanel(new BorderLayout(5, 5));\n\t\tmainPane.add(listPanel, BorderLayout.CENTER);\n\t\tmainPane.add(actionPanel, BorderLayout.SOUTH);\n\t\tmainPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));\n\n\t\tgetContentPane().add(mainPane);\n\t\tpack();\n\t\tsetSize(600, 700);\n\t\tsetLocationRelativeTo(null);\n\t\tsetDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\n\t\tsetModalityType(ModalityType.MODELESS);\n\n\t\tbtnOk.addActionListener(e -> {\n\t\t\tmainWindow.getWrapper().setExcludedPackages(getExcludes());\n\t\t\tmainWindow.reopen();\n\t\t\tdispose();\n\t\t});\n\t\tbtnAll.addActionListener(e -> {\n\t\t\troots.forEach(p -> p.setSelected(true));\n\t\t\ttree.updateUI();\n\t\t});\n\t\tbtnDeselect.addActionListener(e -> {\n\t\t\troots.forEach(p -> p.setSelected(false));\n\t\t\ttree.updateUI();\n\t\t});\n\t\tbtnInvert.addActionListener(e -> {\n\t\t\troots.forEach(PkgNode::toggle);\n\t\t\ttree.updateUI();\n\t\t});\n\t}\n\n\tprivate void initPackageList() {\n\t\tList<String> pkgs = mainWindow.getWrapper().getPackages()\n\t\t\t\t.stream()\n\t\t\t\t.map(JavaPackage::getFullName)\n\t\t\t\t.collect(Collectors.toList());\n\t\tgetPackageTree(pkgs).forEach(treeRoot::add);\n\t\tinitCheckbox();\n\t\ttree.expandPath(new TreePath(treeRoot.getPath()));\n\t}\n\n\tprivate List<PkgNode> getPackageTree(List<String> names) {\n\t\tList<PkgNode> roots = new ArrayList<>();\n\t\tSet<String> nameSet = new HashSet<>();\n\t\tMap<String, List<PkgNode>> childMap = new HashMap<>();\n\t\tfor (String name : names) {\n\t\t\tString parent = \"\";\n\t\t\tint last = 0;\n\t\t\tdo {\n\t\t\t\tint pos = name.indexOf('.', last);\n\t\t\t\tif (pos == -1) {\n\t\t\t\t\tpos = name.length();\n\t\t\t\t}\n\t\t\t\tString fullName = name.substring(0, pos);\n\t\t\t\tif (!nameSet.contains(fullName)) {\n\t\t\t\t\tnameSet.add(fullName);\n\t\t\t\t\tPkgNode node = new PkgNode(fullName, name.substring(last, pos));\n\t\t\t\t\tif (!parent.isEmpty()) {\n\t\t\t\t\t\tchildMap.computeIfAbsent(parent, k -> new ArrayList<>())\n\t\t\t\t\t\t\t\t.add(node);\n\t\t\t\t\t} else {\n\t\t\t\t\t\troots.add(node);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tparent = fullName;\n\t\t\t\tlast = pos + 1;\n\t\t\t} while (last < name.length());\n\t\t}\n\t\taddToParent(null, roots, childMap);\n\t\treturn this.roots;\n\t}\n\n\tprivate PkgNode addToParent(PkgNode parent, List<PkgNode> roots, Map<String, List<PkgNode>> childMap) {\n\t\tfor (PkgNode root : roots) {\n\t\t\tString tempFullName = root.getFullName();\n\t\t\tdo {\n\t\t\t\tList<PkgNode> children = childMap.get(tempFullName);\n\t\t\t\tif (children != null) {\n\t\t\t\t\tif (children.size() == 1) {\n\t\t\t\t\t\tPkgNode next = children.get(0);\n\t\t\t\t\t\tnext.name = root.name + \".\" + next.name;\n\t\t\t\t\t\ttempFullName = next.fullName;\n\t\t\t\t\t\tnext.fullName = root.fullName;\n\t\t\t\t\t\troot = next;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t} else {\n\t\t\t\t\t\taddToParent(root, children, childMap);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (parent == null) {\n\t\t\t\t\tthis.roots.add(root);\n\t\t\t\t} else {\n\t\t\t\t\tparent.add(root);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t} while (true);\n\t\t}\n\t\treturn parent;\n\t}\n\n\tprivate List<String> getExcludes() {\n\t\tList<String> excludes = new ArrayList<>();\n\t\twalkTree(true, p -> excludes.add(p.getFullName()));\n\t\treturn excludes;\n\t}\n\n\tprivate void initCheckbox() {\n\t\tFont tmp = mainWindow.getSettings().getCodeFont();\n\t\tFont font = tmp.deriveFont(tmp.getSize() + 1.f);\n\t\tSet<String> excluded = new HashSet<>(mainWindow.getWrapper().getExcludedPackages());\n\t\twalkTree(false, p -> p.initCheckbox(excluded.contains(p.getFullName()), font));\n\t}\n\n\tprivate void walkTree(boolean findSelected, Consumer<PkgNode> consumer) {\n\t\tList<PkgNode> queue = new ArrayList<>(roots);\n\t\tfor (int i = 0; i < queue.size(); i++) {\n\t\t\tPkgNode node = queue.get(i);\n\t\t\tif (findSelected && node.isSelected()) {\n\t\t\t\tconsumer.accept(node);\n\t\t\t} else {\n\t\t\t\tif (!findSelected) {\n\t\t\t\t\tconsumer.accept(node);\n\t\t\t\t}\n\t\t\t\tfor (int j = 0; j < node.getChildCount(); j++) {\n\t\t\t\t\tqueue.add((PkgNode) node.getChildAt(j));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static class PkgNode extends DefaultMutableTreeNode {\n\t\tprivate static final long serialVersionUID = -1111111202104151430L;\n\n\t\tString name;\n\t\tString fullName;\n\t\tJCheckBox checkbox;\n\n\t\tPkgNode(String fullName, String name) {\n\t\t\tthis.name = name;\n\t\t\tthis.fullName = fullName;\n\t\t}\n\n\t\tvoid initCheckbox(boolean select, Font font) {\n\t\t\tif (!select) {\n\t\t\t\tif (getParent() instanceof PkgNode) {\n\t\t\t\t\tselect = ((PkgNode) getParent()).isSelected();\n\t\t\t\t}\n\t\t\t}\n\t\t\tcheckbox = new JCheckBox(name, select);\n\t\t\tcheckbox.setFont(font);\n\t\t}\n\n\t\tboolean toggle() {\n\t\t\tboolean selected = !checkbox.isSelected();\n\t\t\tsetSelected(selected);\n\t\t\ttoggleParents(selected);\n\t\t\treturn selected;\n\t\t}\n\n\t\tvoid toggleParents(boolean select) {\n\t\t\tif (getParent() instanceof PkgNode) {\n\t\t\t\tPkgNode p = ((PkgNode) getParent());\n\t\t\t\tif (select) {\n\t\t\t\t\tselect = p.isChildrenAllSelected();\n\t\t\t\t\tif (select) {\n\t\t\t\t\t\tp.checkbox.setSelected(true);\n\t\t\t\t\t\tp.toggleParents(true);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tp.checkbox.setSelected(false);\n\t\t\t\t\tp.toggleParents(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvoid setSelected(boolean select) {\n\t\t\tcheckbox.setSelected(select);\n\t\t\tfor (int i = 0; i < getChildCount(); i++) {\n\t\t\t\t((PkgNode) getChildAt(i)).setSelected(select);\n\t\t\t}\n\t\t}\n\n\t\tboolean isSelected() {\n\t\t\treturn checkbox.isSelected();\n\t\t}\n\n\t\tString getFullName() {\n\t\t\treturn fullName;\n\t\t}\n\n\t\tString getDisplayName() {\n\t\t\treturn name;\n\t\t}\n\n\t\tboolean isChildrenAllSelected() {\n\t\t\tfor (int i = 0; i < getChildCount(); i++) {\n\t\t\t\tif (!((PkgNode) getChildAt(i)).isSelected()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn name;\n\t\t}\n\t}\n\n\tprivate static class PkgListCellRenderer extends DefaultTreeCellRenderer {\n\t\tprivate static final long serialVersionUID = -1111111202104151235L;\n\n\t\t@Override\n\t\tpublic Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row,\n\t\t\t\tboolean hasFocus) {\n\t\t\tif (value instanceof PkgNode) {\n\t\t\t\tPkgNode node = (PkgNode) value;\n\t\t\t\tnode.checkbox.setBackground(tree.getBackground());\n\t\t\t\tnode.checkbox.setForeground(tree.getForeground());\n\t\t\t\treturn node.checkbox;\n\t\t\t}\n\t\t\tComponent c = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);\n\t\t\tsetIcon(Icons.PACKAGE);\n\t\t\treturn c;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/GotoAddressDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport javax.swing.JOptionPane;\n\nimport org.exbin.bined.swing.section.SectCodeArea;\n\nimport jadx.gui.utils.HexUtils;\nimport jadx.gui.utils.NLS;\n\npublic class GotoAddressDialog {\n\n\tpublic void showSetSelectionDialog(SectCodeArea codeArea) {\n\t\tObject o = JOptionPane.showInputDialog(\n\t\t\t\tcodeArea, NLS.str(\"hex_viewer.enter_address\"), NLS.str(\"hex_viewer.goto_address\"),\n\t\t\t\tJOptionPane.QUESTION_MESSAGE, null, null,\n\t\t\t\tLong.toHexString(codeArea.getDataPosition()));\n\t\tif (o != null) {\n\t\t\tboolean isValidAddress = HexUtils.isValidHexString(toString());\n\t\t\tif (!isValidAddress) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcodeArea.setActiveCaretPosition(Long.parseLong(o.toString(), 16));\n\t\t\tcodeArea.validateCaret();\n\t\t\tcodeArea.revealCursor();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/GraphDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.BorderLayout;\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.FlowLayout;\nimport java.awt.Graphics;\nimport java.awt.Graphics2D;\nimport java.awt.Point;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.awt.event.MouseListener;\nimport java.awt.event.MouseWheelEvent;\nimport java.awt.geom.AffineTransform;\nimport java.awt.image.BufferedImage;\nimport java.io.File;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\n\nimport javax.swing.JButton;\nimport javax.swing.JComponent;\nimport javax.swing.JFileChooser;\nimport javax.swing.JFrame;\nimport javax.swing.JMenuBar;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JTextArea;\nimport javax.swing.SwingUtilities;\nimport javax.swing.WindowConstants;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport guru.nidi.graphviz.engine.Format;\nimport guru.nidi.graphviz.engine.Graphviz;\nimport guru.nidi.graphviz.model.MutableGraph;\nimport guru.nidi.graphviz.parse.Parser;\n\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.layout.WrapLayout;\n\npublic abstract class GraphDialog extends JFrame {\n\n\tprivate static final long serialVersionUID = 5840390965763493590L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(GraphDialog.class);\n\n\tprivate final MainWindow mainWindow;\n\tprivate GraphPanel panel;\n\n\tprivate static final Dimension MIN_WINDOW_SIZE = new Dimension(800, 500);\n\n\tprivate JMenuBar menuBar = null;\n\n\tpublic static JTextArea graphError() {\n\t\treturn graphError(NLS.str(\"graph_viewer.default_error\"));\n\t}\n\n\tpublic static JTextArea graphError(String errorMessage) {\n\t\tJTextArea errorText = new JTextArea();\n\t\terrorText.setText(errorMessage);\n\t\terrorText.setVisible(true);\n\t\terrorText.setEditable(false);\n\t\terrorText.setLineWrap(false);\n\t\treturn errorText;\n\t}\n\n\tpublic static JTextArea graphError(Exception error) {\n\t\tJTextArea errorText = new JTextArea();\n\t\tStringWriter stringWriter = new StringWriter();\n\t\tPrintWriter printWriter = new PrintWriter(stringWriter);\n\t\tstringWriter.write(NLS.str(\"graph_viewer.default_error\"));\n\t\tstringWriter.write(\": \");\n\t\terror.printStackTrace(printWriter);\n\t\terrorText.setText(stringWriter.toString());\n\t\terrorText.setVisible(true);\n\t\terrorText.setEditable(false);\n\t\terrorText.setLineWrap(false);\n\t\treturn errorText;\n\t}\n\n\tpublic GraphDialog(MainWindow mainWindow) {\n\t\tthis(mainWindow, NLS.str(\"graph_viewer.default_title\"));\n\t}\n\n\tpublic JMenuBar addMenuBar() {\n\t\tJMenuBar menuBar = new JMenuBar();\n\t\tmenuBar.setLayout(new WrapLayout(FlowLayout.LEFT));\n\t\tadd(menuBar, BorderLayout.PAGE_START);\n\t\tthis.menuBar = menuBar;\n\n\t\tJFileChooser fileChooser = new JFileChooser();\n\n\t\tJButton saveButton = new JButton(NLS.str(\"graph_viewer.save_graph\"));\n\t\tsaveButton.setEnabled(false);\n\t\tsaveButton.addActionListener(e -> {\n\t\t\ttry {\n\t\t\t\tint option = fileChooser.showSaveDialog(this);\n\n\t\t\t\tif (option == JFileChooser.APPROVE_OPTION) {\n\t\t\t\t\tFile file = fileChooser.getSelectedFile();\n\t\t\t\t\tgetPanel().renderer.render(Format.SVG).toFile(file);\n\n\t\t\t\t}\n\t\t\t} catch (Exception ex) {\n\t\t\t\tLOG.error(\"Failed to save file: \", ex);\n\t\t\t\tJOptionPane.showMessageDialog(this, NLS.str(\"graph_viewer.file_failure\"),\n\t\t\t\t\t\tNLS.str(\"graph_viewer.file_failure\"),\n\t\t\t\t\t\tJOptionPane.INFORMATION_MESSAGE);\n\t\t\t}\n\t\t});\n\n\t\t// Assemble menubar panel\n\t\tJPanel menuBarPanel = new JPanel();\n\t\tmenuBarPanel.setOpaque(false);\n\t\tmenuBarPanel.add(saveButton);\n\n\t\t// Add menubar panel to menuBar\n\t\tmenuBar.add(menuBarPanel);\n\n\t\treturn menuBar;\n\t}\n\n\tprivate void enableMenu() {\n\t\tJMenuBar menu = this.menuBar;\n\t\tsetAllEnabled(true, menu);\n\t}\n\n\tprivate void disableMenu() {\n\t\tJMenuBar menu = this.menuBar;\n\t\tsetAllEnabled(false, menu);\n\t}\n\n\tprivate void setAllEnabled(boolean isEnabled, JComponent component) {\n\t\tcomponent.setEnabled(isEnabled);\n\n\t\tComponent[] components = component.getComponents();\n\t\tfor (Component subComponent : components) {\n\t\t\tif (subComponent instanceof JComponent) {\n\t\t\t\tsetAllEnabled(isEnabled, (JComponent) subComponent);\n\t\t\t} else {\n\t\t\t\tsubComponent.setEnabled(isEnabled);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic GraphDialog(MainWindow mainWindow, String title) {\n\t\tsuper(title);\n\t\tthis.mainWindow = mainWindow;\n\n\t\tsetMinimumSize(MIN_WINDOW_SIZE);\n\n\t\tsetDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\n\t\tUiUtils.addEscapeShortCutToDispose(this);\n\t\tsetLocationRelativeTo(null);\n\n\t\tloadWindowPos();\n\n\t\tLOG.debug(\"Dialog w: {} h: {}\", getWidth(), getHeight());\n\n\t\tLOG.debug(\"cwd: {}\", System.getProperty(\"user.dir\"));\n\n\t\tpanel = new GraphPanel(this);\n\t\tpanel.setFocusable(true);\n\t\tpanel.addMouseListener(new MouseListener() {\n\t\t\tpublic void mouseClicked(MouseEvent e) {\n\t\t\t\trequestFocusInWindow();\n\t\t\t}\n\n\t\t\tpublic void mouseEntered(MouseEvent e) {\n\t\t\t}\n\n\t\t\tpublic void mouseExited(MouseEvent e) {\n\t\t\t}\n\n\t\t\tpublic void mousePressed(MouseEvent e) {\n\t\t\t}\n\n\t\t\tpublic void mouseReleased(MouseEvent e) {\n\t\t\t}\n\t\t});\n\n\t\tsetLayout(new BorderLayout());\n\t\tadd(panel, BorderLayout.CENTER);\n\n\t}\n\n\tpublic void loadWindowPos() {\n\t\tif (!mainWindow.getSettings().loadWindowPos(this)) {\n\t\t\tsetPreferredSize(MIN_WINDOW_SIZE);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void dispose() {\n\t\ttry {\n\t\t\tmainWindow.getSettings().saveWindowPos(this);\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to save window size and position\", e);\n\t\t}\n\t\tsuper.dispose();\n\t}\n\n\tclass GraphPanel extends JPanel {\n\n\t\tprivate Dimension fullImageSize = new Dimension();\n\t\tprivate double scale = 1.0;\n\t\tprivate double minimumScale = 0.01;\n\t\tprivate double maximumScale = 7.0;\n\t\tprivate double translateX = 0;\n\t\tprivate double translateY = 0;\n\t\tprivate Point lastDragPoint = null;\n\n\t\tprivate BufferedImage image;\n\n\t\tprivate Graphviz renderer;\n\n\t\tprivate final GraphDialog parentDialog;\n\n\t\tpublic GraphPanel(GraphDialog parentDialog) {\n\n\t\t\tthis.parentDialog = parentDialog;\n\n\t\t\tMouseAdapter ma = new MouseAdapter() {\n\t\t\t\t@Override\n\t\t\t\tpublic void mousePressed(MouseEvent e) {\n\t\t\t\t\tlastDragPoint = e.getPoint();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void mouseDragged(MouseEvent e) {\n\t\t\t\t\tif (image != null) {\n\t\t\t\t\t\tPoint p = e.getPoint();\n\t\t\t\t\t\ttranslateX += (p.x - lastDragPoint.x) / scale;\n\t\t\t\t\t\ttranslateY += (p.y - lastDragPoint.y) / scale;\n\t\t\t\t\t\tlastDragPoint = p;\n\t\t\t\t\t\trepaint();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void mouseWheelMoved(MouseWheelEvent e) {\n\t\t\t\t\tif (image != null) {\n\t\t\t\t\t\tdouble prevScale = scale;\n\t\t\t\t\t\tscale *= Math.pow(1.1, -e.getWheelRotation());\n\n\t\t\t\t\t\tif (scale > maximumScale) {\n\t\t\t\t\t\t\tscale = maximumScale;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (scale < minimumScale) {\n\t\t\t\t\t\t\tscale = minimumScale;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (scale != prevScale) {\n\t\t\t\t\t\t\tPoint p = e.getPoint();\n\t\t\t\t\t\t\tdouble px = (p.x - translateX * prevScale) / prevScale;\n\t\t\t\t\t\t\tdouble py = (p.y - translateY * prevScale) / prevScale;\n\t\t\t\t\t\t\ttranslateX = (p.x / scale) - px;\n\t\t\t\t\t\t\ttranslateY = (p.y / scale) - py;\n\t\t\t\t\t\t\tLOG.debug(\"Rescaling {}%\", scale * 100);\n\t\t\t\t\t\t\trenderGraphScaled();\n\t\t\t\t\t\t\tif (image == null) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\trepaint();\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t};\n\n\t\t\taddMouseListener(ma);\n\t\t\taddMouseMotionListener(ma);\n\t\t\taddMouseWheelListener(ma);\n\t\t}\n\n\t\t@Override\n\t\tprotected void paintComponent(Graphics g) {\n\t\t\tsuper.paintComponent(g);\n\t\t\tif (image != null) {\n\t\t\t\tGraphics2D g2d = (Graphics2D) g;\n\t\t\t\tAffineTransform transform = new AffineTransform();\n\t\t\t\ttransform.translate(translateX * scale, translateY * scale);\n\t\t\t\tg2d.drawImage(image, transform, null);\n\t\t\t}\n\t\t}\n\n\t\tpublic void setGraph(File dotString) {\n\t\t\ttry {\n\t\t\t\tLOG.debug(\"Parsing DOT file: {} \", dotString.getAbsolutePath());\n\t\t\t\tsetGraph(new Parser().read(dotString));\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Error parsing DOT file\", e);\n\t\t\t\tinvalidateImage(graphError(e));\n\t\t\t}\n\t\t}\n\n\t\tpublic void setGraph(String dotString) {\n\t\t\ttry {\n\t\t\t\tsetGraph(new Parser().read(dotString));\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Error parsing DOT string\", e);\n\t\t\t\tinvalidateImage(graphError(e));\n\t\t\t}\n\t\t}\n\n\t\tpublic void setGraph(MutableGraph g) {\n\n\t\t\trenderer = Graphviz.fromGraph(g);\n\t\t\tparentDialog.enableMenu();\n\n\t\t\tscale = 1.0;\n\n\t\t\t// set initial image scale and posiition\n\t\t\tRunnable doCenter = new Runnable() {\n\t\t\t\tpublic void run() {\n\n\t\t\t\t\trenderGraphFullSize();\n\t\t\t\t\tif (image == null) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tLOG.debug(\"full image w {} h {}\", fullImageSize.width, fullImageSize.height);\n\n\t\t\t\t\t// scale required to fit image to window width or height\n\t\t\t\t\tdouble heightScale = (double) getHeight() / (double) fullImageSize.height;\n\t\t\t\t\tdouble widthScale = (double) getWidth() / (double) fullImageSize.width;\n\t\t\t\t\tif (widthScale < heightScale) {\n\t\t\t\t\t\tscale = widthScale;\n\t\t\t\t\t\tLOG.debug(\"scaling to fit width {}/{} {}\", getWidth(), fullImageSize.width, scale);\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tscale = heightScale;\n\t\t\t\t\t\tLOG.debug(\"scaling to fit height {}/{} {}\", getHeight(), fullImageSize.height, scale);\n\t\t\t\t\t}\n\n\t\t\t\t\tscale = scale * 0.95;\n\t\t\t\t\tmaximumScale = Math.sqrt(Integer.MAX_VALUE / (fullImageSize.width * fullImageSize.height)) / 8;\n\t\t\t\t\tminimumScale = Math.min(scale, maximumScale);\n\n\t\t\t\t\trenderGraphScaled();\n\t\t\t\t\tif (image == null) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// center image in window\n\t\t\t\t\ttranslateY = (getHeight() / 2 - (fullImageSize.height * scale) / 2) / scale;\n\t\t\t\t\ttranslateX = (getWidth() / 2 - (fullImageSize.width * scale) / 2) / scale;\n\n\t\t\t\t\trepaint();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tSwingUtilities.invokeLater(doCenter);\n\n\t\t}\n\n\t\tprivate void renderGraphFullSize() {\n\t\t\ttry {\n\t\t\t\timage = null;\n\t\t\t\timage = renderer.render(Format.SVG).toImage();\n\t\t\t\tif (image.getWidth() == 0 || image.getHeight() == 0) {\n\t\t\t\t\t// If rendered image is too small, calculating the scale would later cause a\n\t\t\t\t\t// division by zero\n\t\t\t\t\tLOG.error(\"Graph render failed, image too small\");\n\t\t\t\t\tinvalidateImage(graphError(NLS.str(\"graph_viewer.image_too_small\")));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tfullImageSize.setSize(image.getWidth(), image.getHeight());\n\n\t\t\t} catch (IllegalArgumentException illegalArgumentException) {\n\t\t\t\t// If rendered image is too large, a Dimension object is passed invalid arguments\n\t\t\t\tLOG.error(\"Graph render failed, illegal arguments: \", illegalArgumentException);\n\t\t\t\tinvalidateImage(graphError(NLS.str(\"graph_viewer.image_too_large\")));\n\n\t\t\t} catch (Exception e) {\n\t\t\t\t// A large image may cause a number of other other exception types caught here along with other\n\t\t\t\t// failure cases\n\t\t\t\tLOG.error(\"Graph render failed: \", e);\n\t\t\t\tinvalidateImage(graphError(e));\n\t\t\t}\n\n\t\t}\n\n\t\tprivate void renderGraphScaled() {\n\t\t\ttry {\n\t\t\t\tif (fullImageSize.width * scale * fullImageSize.height * scale >= Integer.MAX_VALUE) {\n\t\t\t\t\tscale = maximumScale;\n\t\t\t\t}\n\t\t\t\timage = renderer.width((int) (fullImageSize.width * scale)).render(Format.SVG).toImage();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Graph render failed: \", e);\n\t\t\t\tinvalidateImage(graphError(e));\n\t\t\t}\n\n\t\t}\n\n\t\tpublic void invalidateImage(JTextArea errorMsg) {\n\t\t\tthis.add(errorMsg);\n\t\t\timage = null;\n\t\t\tthis.parentDialog.disableMenu();\n\t\t\tthis.revalidate();\n\t\t\trepaint();\n\t\t}\n\n\t}\n\n\tprotected GraphPanel getPanel() {\n\t\treturn this.panel;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/LogViewerDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.BorderLayout;\nimport java.awt.Container;\nimport java.awt.event.WindowAdapter;\nimport java.awt.event.WindowEvent;\n\nimport javax.swing.JFrame;\n\nimport jadx.gui.logs.LogOptions;\nimport jadx.gui.logs.LogPanel;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic class LogViewerDialog extends JFrame {\n\tprivate static final long serialVersionUID = -2188700277429054641L;\n\n\tprivate static LogViewerDialog openLogDialog;\n\n\tprivate final transient JadxSettings settings;\n\tprivate final transient LogPanel logPanel;\n\n\tpublic static void open(MainWindow mainWindow, LogOptions logOptions) {\n\t\tLogViewerDialog logDialog;\n\t\tif (openLogDialog != null) {\n\t\t\tlogDialog = openLogDialog;\n\t\t} else {\n\t\t\tlogDialog = new LogViewerDialog(mainWindow, logOptions);\n\t\t\topenLogDialog = logDialog;\n\t\t}\n\t\tlogDialog.setVisible(true);\n\t\tlogDialog.toFront();\n\t}\n\n\tprivate LogViewerDialog(MainWindow mainWindow, LogOptions logOptions) {\n\t\tsettings = mainWindow.getSettings();\n\t\tUiUtils.setWindowIcons(this);\n\n\t\tRunnable dock = () -> {\n\t\t\tmainWindow.getSettings().saveDockLogViewer(true);\n\t\t\tdispose();\n\t\t\tmainWindow.showLogViewer(LogOptions.current());\n\t\t};\n\t\tlogPanel = new LogPanel(mainWindow, logOptions, dock, this::dispose);\n\t\tContainer contentPane = getContentPane();\n\t\tcontentPane.add(logPanel, BorderLayout.CENTER);\n\n\t\tsetTitle(NLS.str(\"log_viewer.title\"));\n\t\tpack();\n\t\tsetSize(800, 600);\n\t\tsetDefaultCloseOperation(DISPOSE_ON_CLOSE);\n\t\tsetLocationRelativeTo(null);\n\t\tsettings.loadWindowPos(this);\n\t\taddWindowListener(new WindowAdapter() {\n\t\t\t@Override\n\t\t\tpublic void windowClosing(WindowEvent e) {\n\t\t\t\topenLogDialog = null;\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic void dispose() {\n\t\tlogPanel.dispose();\n\t\tsettings.saveWindowPos(this);\n\t\tsuper.dispose();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/MethodsDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.BorderLayout;\nimport java.awt.Dimension;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.DefaultListModel;\nimport javax.swing.DefaultListSelectionModel;\nimport javax.swing.JButton;\nimport javax.swing.JList;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.ListSelectionModel;\nimport javax.swing.WindowConstants;\n\nimport jadx.api.JavaMethod;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.cellrenders.MethodsListRenderer;\nimport jadx.gui.utils.NLS;\n\npublic class MethodsDialog extends CommonDialog {\n\tprivate JList<JavaMethod> methodList;\n\n\tprivate final Consumer<List<JavaMethod>> listConsumer;\n\n\tpublic MethodsDialog(MainWindow mainWindow, List<JavaMethod> methods, Consumer<List<JavaMethod>> listConsumer) {\n\t\tsuper(mainWindow);\n\t\tthis.listConsumer = listConsumer;\n\t\tinitUI(methods);\n\t\tsetVisible(true);\n\t}\n\n\tprivate void initUI(List<JavaMethod> methods) {\n\t\tsetTitle(NLS.str(\"methods_dialog.title\"));\n\n\t\tDefaultListModel<JavaMethod> defaultListModel = new DefaultListModel<>();\n\t\tdefaultListModel.addAll(methods);\n\n\t\tmethodList = new JList<>();\n\t\tmethodList.setModel(defaultListModel);\n\t\tmethodList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\n\t\tmethodList.setCellRenderer(new MethodsListRenderer());\n\t\tmethodList.setSelectionModel(new DefaultListSelectionModel() {\n\t\t\t@Override\n\t\t\tpublic void setSelectionInterval(int index0, int index1) {\n\t\t\t\tif (super.isSelectedIndex(index0)) {\n\t\t\t\t\tsuper.removeSelectionInterval(index0, index1);\n\t\t\t\t} else {\n\t\t\t\t\tsuper.addSelectionInterval(index0, index1);\n\t\t\t\t}\n\t\t\t}\n\n\t\t});\n\n\t\tJScrollPane scrollPane = new JScrollPane(methodList);\n\t\tJPanel buttonPane = initButtonsPanel();\n\n\t\tJPanel contentPanel = new JPanel();\n\t\tcontentPanel.setLayout(new BorderLayout(5, 5));\n\t\tcontentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));\n\t\tcontentPanel.add(scrollPane, BorderLayout.CENTER);\n\t\tcontentPanel.add(buttonPane, BorderLayout.PAGE_END);\n\t\tgetContentPane().add(contentPanel);\n\n\t\tpack();\n\t\tsetSize(500, 300);\n\t\tsetLocationRelativeTo(null);\n\t\tsetDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\n\t}\n\n\tprotected JPanel initButtonsPanel() {\n\t\tJButton cancelButton = new JButton(NLS.str(\"common_dialog.cancel\"));\n\t\tcancelButton.addActionListener(event -> dispose());\n\n\t\tJButton okBtn = new JButton(NLS.str(\"common_dialog.ok\"));\n\t\tokBtn.addActionListener(event -> generateForSelected());\n\t\tgetRootPane().setDefaultButton(okBtn);\n\n\t\tJPanel buttonPane = new JPanel();\n\t\tbuttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));\n\t\tbuttonPane.add(Box.createHorizontalGlue());\n\t\tbuttonPane.add(okBtn);\n\t\tbuttonPane.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tbuttonPane.add(cancelButton);\n\t\treturn buttonPane;\n\t}\n\n\tprivate void generateForSelected() {\n\t\tList<JavaMethod> selectedMethods = methodList.getSelectedValuesList();\n\t\tif (!selectedMethods.isEmpty()) {\n\t\t\tthis.listConsumer.accept(selectedMethods);\n\t\t}\n\t\tdispose();\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/RenameDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.BorderLayout;\nimport java.awt.Container;\nimport java.awt.Dimension;\nimport java.awt.FlowLayout;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\nimport javax.swing.JLabel;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPanel;\nimport javax.swing.JPopupMenu;\nimport javax.swing.JTextField;\nimport javax.swing.SwingUtilities;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.plugins.events.types.NodeRenamedByUser;\nimport jadx.core.utils.Utils;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.treemodel.JPackage;\nimport jadx.gui.treemodel.JRenameNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.TextStandardActions;\nimport jadx.gui.utils.pkgs.JRenamePackage;\nimport jadx.gui.utils.ui.DocumentUpdateListener;\nimport jadx.gui.utils.ui.NodeLabel;\n\npublic class RenameDialog extends CommonDialog {\n\tprivate static final long serialVersionUID = -3269715644416902410L;\n\n\tprivate final transient JRenameNode node;\n\tprivate transient JTextField renameField;\n\tprivate transient JButton renameBtn;\n\n\tpublic static boolean rename(MainWindow mainWindow, JRenameNode node) {\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\tRenameDialog renameDialog = new RenameDialog(mainWindow, node);\n\t\t\trenameDialog.initRenameField();\n\t\t\trenameDialog.setVisible(true);\n\t\t});\n\t\treturn true;\n\t}\n\n\tpublic static JPopupMenu buildRenamePopup(MainWindow mainWindow, JRenameNode node) {\n\t\tJPopupMenu menu = new JPopupMenu();\n\t\tmenu.add(buildRenamePopupMenuItem(mainWindow, node));\n\t\treturn menu;\n\t}\n\n\tpublic static JMenuItem buildRenamePopupMenuItem(MainWindow mainWindow, JRenameNode node) {\n\t\tJMenuItem jmi = new JMenuItem(NLS.str(\"popup.rename\"));\n\t\tjmi.addActionListener(action -> RenameDialog.rename(mainWindow, node));\n\t\tjmi.setEnabled(node.canRename());\n\n\t\treturn jmi;\n\t}\n\n\tprivate RenameDialog(MainWindow mainWindow, JRenameNode node) {\n\t\tsuper(mainWindow);\n\t\tthis.node = node.replace();\n\t\tinitUI();\n\t}\n\n\tprivate void initRenameField() {\n\t\trenameField.setText(node.getName());\n\t\trenameField.selectAll();\n\t}\n\n\tprivate boolean checkNewName(String newName) {\n\t\tif (newName.isEmpty()) {\n\t\t\t// use empty name to reset rename (revert to original)\n\t\t\treturn true;\n\t\t}\n\t\tboolean valid = node.isValidName(newName);\n\t\tif (renameBtn.isEnabled() != valid) {\n\t\t\trenameBtn.setEnabled(valid);\n\t\t\trenameField.putClientProperty(\"JComponent.outline\", valid ? \"\" : \"error\");\n\t\t}\n\t\treturn valid;\n\t}\n\n\tprivate void rename() {\n\t\trename(renameField.getText().trim());\n\t}\n\n\tprivate void resetName() {\n\t\trename(\"\");\n\t}\n\n\tprivate void rename(String newName) {\n\t\tif (!checkNewName(newName)) {\n\t\t\treturn;\n\t\t}\n\t\tString oldName = node.getName();\n\t\tString newNodeName;\n\t\tboolean reset = newName.isEmpty();\n\t\tif (reset) {\n\t\t\tnode.removeAlias();\n\t\t\tnewNodeName = Utils.getOrElse(node.getJavaNode().getName(), \"\");\n\t\t} else {\n\t\t\tnewNodeName = newName;\n\t\t}\n\t\tsendRenameEvent(oldName, newNodeName, reset);\n\t\tdispose();\n\t}\n\n\tprivate void sendRenameEvent(String oldName, String newName, boolean reset) {\n\t\tICodeNodeRef nodeRef = node.getJavaNode().getCodeNodeRef();\n\t\tNodeRenamedByUser event = new NodeRenamedByUser(nodeRef, oldName, newName);\n\t\tevent.setRenameNode(node);\n\t\tevent.setResetName(reset);\n\t\tmainWindow.events().send(event);\n\t}\n\n\t@NotNull\n\tprotected JPanel initButtonsPanel() {\n\t\tJButton resetButton = new JButton(NLS.str(\"common_dialog.reset\"));\n\t\tresetButton.addActionListener(event -> resetName());\n\n\t\tJButton cancelButton = new JButton(NLS.str(\"common_dialog.cancel\"));\n\t\tcancelButton.addActionListener(event -> dispose());\n\n\t\trenameBtn = new JButton(NLS.str(\"common_dialog.ok\"));\n\t\trenameBtn.addActionListener(event -> rename());\n\t\tgetRootPane().setDefaultButton(renameBtn);\n\n\t\tJPanel buttonPane = new JPanel();\n\t\tbuttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));\n\t\tbuttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));\n\t\tbuttonPane.add(resetButton);\n\t\tbuttonPane.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tbuttonPane.add(Box.createHorizontalGlue());\n\t\tbuttonPane.add(renameBtn);\n\t\tbuttonPane.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tbuttonPane.add(cancelButton);\n\t\treturn buttonPane;\n\t}\n\n\tprivate void initUI() {\n\t\tJLabel lbl = new JLabel(NLS.str(\"popup.rename\"));\n\t\tNodeLabel nodeLabel = new NodeLabel(node.getTitle());\n\t\tnodeLabel.setIcon(node.getIcon());\n\t\tif (node instanceof JNode) {\n\t\t\tnodeLabel.disableHtml(((JNode) node).disableHtml());\n\t\t} else if (node instanceof JRenamePackage) {\n\t\t\t// TODO: get from JRenameNode directly\n\t\t\tnodeLabel.disableHtml(!node.getTitle().equals(JPackage.PACKAGE_DEFAULT_HTML_STR));\n\t\t}\n\t\tlbl.setLabelFor(nodeLabel);\n\n\t\trenameField = new JTextField(40);\n\t\trenameField.setFont(mainWindow.getSettings().getCodeFont());\n\t\trenameField.getDocument().addDocumentListener(new DocumentUpdateListener(ev -> checkNewName(renameField.getText())));\n\t\trenameField.addActionListener(e -> rename());\n\t\tnew TextStandardActions(renameField);\n\n\t\tJPanel renamePane = new JPanel();\n\t\trenamePane.setLayout(new FlowLayout(FlowLayout.LEFT));\n\t\trenamePane.add(lbl);\n\t\trenamePane.add(nodeLabel);\n\t\trenamePane.setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 10));\n\n\t\tJPanel textPane = new JPanel();\n\t\ttextPane.setLayout(new BoxLayout(textPane, BoxLayout.PAGE_AXIS));\n\t\ttextPane.add(renameField);\n\t\tif (node instanceof JClass) {\n\t\t\ttextPane.add(new JLabel(NLS.str(\"rename_dialog.class_help\")));\n\t\t}\n\t\ttextPane.setBorder(BorderFactory.createEmptyBorder(5, 10, 10, 10));\n\n\t\tJPanel buttonPane = initButtonsPanel();\n\n\t\tContainer contentPane = getContentPane();\n\t\tcontentPane.add(renamePane, BorderLayout.PAGE_START);\n\t\tcontentPane.add(textPane, BorderLayout.CENTER);\n\t\tcontentPane.add(buttonPane, BorderLayout.PAGE_END);\n\n\t\tsetTitle(NLS.str(\"popup.rename\"));\n\t\tcommonWindowInit();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.BorderLayout;\nimport java.awt.Dimension;\nimport java.awt.FlowLayout;\nimport java.awt.Insets;\nimport java.awt.event.ComponentAdapter;\nimport java.awt.event.ComponentEvent;\nimport java.awt.event.ItemListener;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.ImageIcon;\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JLabel;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPanel;\nimport javax.swing.JPopupMenu;\nimport javax.swing.JSpinner;\nimport javax.swing.JTextField;\nimport javax.swing.JToggleButton;\nimport javax.swing.SpinnerNumberModel;\nimport javax.swing.WindowConstants;\nimport javax.swing.border.TitledBorder;\nimport javax.swing.event.ChangeListener;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.formdev.flatlaf.FlatClientProperties;\nimport com.formdev.flatlaf.icons.FlatSearchWithHistoryIcon;\n\nimport io.reactivex.rxjava3.core.BackpressureStrategy;\nimport io.reactivex.rxjava3.core.Emitter;\nimport io.reactivex.rxjava3.core.Flowable;\nimport io.reactivex.rxjava3.disposables.Disposable;\nimport io.reactivex.rxjava3.schedulers.Schedulers;\n\nimport jadx.api.JavaClass;\nimport jadx.api.JavaPackage;\nimport jadx.api.resources.ResourceContentType;\nimport jadx.core.dex.nodes.PackageNode;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.Utils;\nimport jadx.gui.jobs.ITaskInfo;\nimport jadx.gui.jobs.ITaskProgress;\nimport jadx.gui.search.SearchSettings;\nimport jadx.gui.search.SearchTask;\nimport jadx.gui.search.providers.ClassSearchProvider;\nimport jadx.gui.search.providers.CodeSearchProvider;\nimport jadx.gui.search.providers.CommentSearchProvider;\nimport jadx.gui.search.providers.FieldSearchProvider;\nimport jadx.gui.search.providers.MergedSearchProvider;\nimport jadx.gui.search.providers.MethodSearchProvider;\nimport jadx.gui.search.providers.ResourceFilter;\nimport jadx.gui.search.providers.ResourceSearchProvider;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.treemodel.JResource;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.CacheObject;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.JumpPosition;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.SimpleListener;\nimport jadx.gui.utils.TextStandardActions;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.cache.ValueCache;\nimport jadx.gui.utils.layout.WrapLayout;\nimport jadx.gui.utils.rx.RxUtils;\nimport jadx.gui.utils.ui.DocumentUpdateListener;\n\nimport static jadx.gui.ui.dialog.SearchDialog.SearchOptions.ACTIVE_TAB;\nimport static jadx.gui.ui.dialog.SearchDialog.SearchOptions.CLASS;\nimport static jadx.gui.ui.dialog.SearchDialog.SearchOptions.CODE;\nimport static jadx.gui.ui.dialog.SearchDialog.SearchOptions.COMMENT;\nimport static jadx.gui.ui.dialog.SearchDialog.SearchOptions.FIELD;\nimport static jadx.gui.ui.dialog.SearchDialog.SearchOptions.IGNORE_CASE;\nimport static jadx.gui.ui.dialog.SearchDialog.SearchOptions.METHOD;\nimport static jadx.gui.ui.dialog.SearchDialog.SearchOptions.RESOURCE;\nimport static jadx.gui.ui.dialog.SearchDialog.SearchOptions.USE_REGEX;\n\npublic class SearchDialog extends CommonSearchDialog {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SearchDialog.class);\n\tprivate static final long serialVersionUID = -5105405456969134105L;\n\n\tpublic static void search(MainWindow window, SearchPreset preset) {\n\t\tSearchDialog searchDialog = new SearchDialog(window, preset, Collections.emptySet());\n\t\tshow(searchDialog, window);\n\t}\n\n\tpublic static void searchInActiveTab(MainWindow window, SearchPreset preset) {\n\t\tSearchDialog searchDialog = new SearchDialog(window, preset, EnumSet.of(ACTIVE_TAB));\n\t\tshow(searchDialog, window);\n\t}\n\n\tpublic static void searchText(MainWindow window, String text) {\n\t\tSearchDialog searchDialog = new SearchDialog(window, SearchPreset.TEXT, Collections.emptySet());\n\t\tsearchDialog.initSearchText = text;\n\t\tshow(searchDialog, window);\n\t}\n\n\tpublic static void searchPackage(MainWindow window, String packageName) {\n\t\tSearchDialog searchDialog = new SearchDialog(window, SearchPreset.TEXT, Collections.emptySet());\n\t\tsearchDialog.initSearchPackage = packageName;\n\t\tshow(searchDialog, window);\n\t}\n\n\tprivate static void show(SearchDialog searchDialog, MainWindow mw) {\n\t\tmw.addLoadListener(loaded -> {\n\t\t\tif (!loaded) {\n\t\t\t\tsearchDialog.dispose();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t});\n\t\tsearchDialog.setVisible(true);\n\t}\n\n\tpublic enum SearchPreset {\n\t\tTEXT, CLASS, COMMENT\n\t}\n\n\tpublic enum SearchOptions {\n\t\tCLASS,\n\t\tMETHOD,\n\t\tFIELD,\n\t\tCODE,\n\t\tRESOURCE,\n\t\tCOMMENT,\n\n\t\tIGNORE_CASE,\n\t\tUSE_REGEX,\n\t\tACTIVE_TAB\n\t}\n\n\tprivate final transient SearchPreset searchPreset;\n\tprivate final transient Set<SearchOptions> options;\n\tprivate final transient SimpleListener<Set<SearchOptions>> optionsListener = new SimpleListener<>();\n\n\tprivate transient JTextField searchField;\n\tprivate transient JTextField packageField;\n\tprivate transient JTextField resExtField;\n\tprivate transient JSpinner resSizeLimit;\n\n\tprivate transient @Nullable SearchTask searchTask;\n\tprivate transient JButton loadAllButton;\n\tprivate transient JButton loadMoreButton;\n\tprivate transient JButton stopBtn;\n\tprivate transient JButton sortBtn;\n\n\tprivate transient Disposable searchDisposable;\n\tprivate transient SearchEventEmitter searchEmitter;\n\tprivate transient ChangeListener activeTabListener;\n\n\tprivate transient String initSearchText = null;\n\tprivate transient String initSearchPackage = null;\n\n\t// temporal list for pending results\n\tprivate final List<JNode> pendingResults = new ArrayList<>();\n\n\t/**\n\t * Use single thread to do all background work, so additional synchronization not needed\n\t */\n\tprivate final Executor searchBackgroundExecutor = Executors.newSingleThreadExecutor();\n\n\t// save values between searches\n\tprivate final ValueCache<String, List<JavaClass>> includedClsCache = new ValueCache<>();\n\tprivate final ValueCache<List<JavaClass>, List<List<JavaClass>>> batchesCache = new ValueCache<>();\n\n\tprivate SearchDialog(MainWindow mainWindow, SearchPreset preset, Set<SearchOptions> additionalOptions) {\n\t\tsuper(mainWindow, NLS.str(\"menu.text_search\"));\n\t\tthis.searchPreset = preset;\n\t\tthis.options = buildOptions(preset);\n\t\tthis.options.addAll(additionalOptions);\n\n\t\tloadWindowPos();\n\t\tinitUI();\n\t\tinitSearchEvents();\n\t\tregisterInitOnOpen();\n\t\tregisterActiveTabListener();\n\t}\n\n\t@Override\n\tpublic void dispose() {\n\t\tif (searchDisposable != null && !searchDisposable.isDisposed()) {\n\t\t\tsearchDisposable.dispose();\n\t\t}\n\t\tresultsModel.clear();\n\t\tremoveActiveTabListener();\n\t\tsearchBackgroundExecutor.execute(() -> {\n\t\t\tstopSearchTask();\n\t\t\tunloadTempData();\n\t\t});\n\t\tsuper.dispose();\n\t}\n\n\tprivate Set<SearchOptions> buildOptions(SearchPreset preset) {\n\t\tSet<SearchOptions> searchOptions = cache.getLastSearchOptions().get(preset);\n\t\tif (searchOptions == null) {\n\t\t\tsearchOptions = EnumSet.noneOf(SearchOptions.class);\n\t\t}\n\t\tswitch (preset) {\n\t\t\tcase TEXT:\n\t\t\t\tif (searchOptions.isEmpty()) {\n\t\t\t\t\tsearchOptions.add(SearchOptions.CODE);\n\t\t\t\t\tsearchOptions.add(IGNORE_CASE);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase CLASS:\n\t\t\t\tsearchOptions.add(CLASS);\n\t\t\t\tbreak;\n\n\t\t\tcase COMMENT:\n\t\t\t\tsearchOptions.add(COMMENT);\n\t\t\t\tsearchOptions.remove(ACTIVE_TAB);\n\t\t\t\tbreak;\n\t\t}\n\t\treturn searchOptions;\n\t}\n\n\t@Override\n\tprotected void openInit() {\n\t\tString searchText = initSearchText != null ? initSearchText : cache.getLastSearch();\n\t\tif (searchText != null) {\n\t\t\tsearchField.setText(searchText);\n\t\t\tsearchField.selectAll();\n\t\t}\n\t\tString searchPackage = initSearchPackage != null ? initSearchPackage : cache.getLastSearchPackage();\n\t\tif (searchPackage != null) {\n\t\t\tpackageField.setText(searchPackage);\n\t\t}\n\t\tsearchField.requestFocus();\n\t\tresultsTable.initColumnWidth();\n\n\t\tif (options.contains(COMMENT)) {\n\t\t\t// show all comments on empty input\n\t\t\tsearchEmitter.emitSearch();\n\t\t}\n\t}\n\n\tprivate void initUI() {\n\t\tsearchField = new JTextField();\n\t\tTextStandardActions.attach(searchField);\n\t\taddSearchHistoryButton();\n\t\tsearchField.putClientProperty(FlatClientProperties.TEXT_FIELD_SHOW_CLEAR_BUTTON, true);\n\n\t\tboolean autoSearch = mainWindow.getSettings().isUseAutoSearch();\n\t\tJButton searchBtn = new JButton(NLS.str(\"search_dialog.search_button\"));\n\t\tsearchBtn.setVisible(!autoSearch);\n\t\tsearchBtn.addActionListener(ev -> searchEmitter.emitSearch());\n\n\t\tJCheckBox autoSearchCB = new JCheckBox(NLS.str(\"search_dialog.auto_search\"));\n\t\tautoSearchCB.setSelected(autoSearch);\n\t\tautoSearchCB.addActionListener(ev -> {\n\t\t\tboolean newValue = autoSearchCB.isSelected();\n\t\t\tmainWindow.getSettings().saveUseAutoSearch(newValue);\n\t\t\tsearchBtn.setVisible(!newValue);\n\t\t\tinitSearchEvents();\n\t\t\tif (newValue) {\n\t\t\t\tsearchEmitter.emitSearch();\n\t\t\t}\n\t\t});\n\n\t\tJPanel searchButtons = new JPanel();\n\t\tsearchButtons.setLayout(new BoxLayout(searchButtons, BoxLayout.LINE_AXIS));\n\t\tsearchButtons.add(searchBtn);\n\t\tsearchButtons.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tsearchButtons.add(makeOptionsToggleButton(NLS.str(\"search_dialog.ignorecase\"), Icons.ICON_MATCH, Icons.ICON_MATCH_SELECTED,\n\t\t\t\tSearchOptions.IGNORE_CASE));\n\t\tsearchButtons.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tsearchButtons.add(makeOptionsToggleButton(NLS.str(\"search_dialog.regex\"), Icons.ICON_REGEX, Icons.ICON_REGEX_SELECTED,\n\t\t\t\tSearchOptions.USE_REGEX));\n\t\tsearchButtons.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tsearchButtons.add(makeOptionsToggleButton(NLS.str(\"search_dialog.active_tab\"), Icons.ICON_ACTIVE_TAB,\n\t\t\t\tIcons.ICON_ACTIVE_TAB_SELECTED, SearchOptions.ACTIVE_TAB));\n\t\tsearchButtons.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tsearchButtons.add(autoSearchCB);\n\n\t\tJPanel searchFieldPanel = new JPanel();\n\t\tsearchFieldPanel.setLayout(new BorderLayout(5, 5));\n\t\tsearchFieldPanel.add(new JLabel(NLS.str(\"search_dialog.open_by_name\")), BorderLayout.LINE_START);\n\t\tsearchFieldPanel.add(searchField, BorderLayout.CENTER);\n\t\tsearchFieldPanel.add(searchButtons, BorderLayout.LINE_END);\n\n\t\tJPanel searchInPanel = new JPanel();\n\t\tsearchInPanel.setLayout(new BoxLayout(searchInPanel, BoxLayout.LINE_AXIS));\n\t\tsearchInPanel.setBorder(BorderFactory.createTitledBorder(NLS.str(\"search_dialog.search_in\")));\n\t\tsearchInPanel.add(makeOptionsCheckBox(NLS.str(\"search_dialog.class\"), SearchOptions.CLASS));\n\t\tsearchInPanel.add(makeOptionsCheckBox(NLS.str(\"search_dialog.method\"), SearchOptions.METHOD));\n\t\tsearchInPanel.add(makeOptionsCheckBox(NLS.str(\"search_dialog.field\"), SearchOptions.FIELD));\n\t\tsearchInPanel.add(makeOptionsCheckBox(NLS.str(\"search_dialog.code\"), SearchOptions.CODE));\n\t\tsearchInPanel.add(makeOptionsCheckBox(NLS.str(\"search_dialog.resource\"), SearchOptions.RESOURCE));\n\t\tsearchInPanel.add(makeOptionsCheckBox(NLS.str(\"search_dialog.comments\"), SearchOptions.COMMENT));\n\n\t\tpackageField = new JTextField(Math.min(100, getMaxPkgLen()));\n\t\tTextStandardActions.attach(packageField);\n\t\tpackageField.putClientProperty(FlatClientProperties.TEXT_FIELD_SHOW_CLEAR_BUTTON, true);\n\t\tpackageField.setToolTipText(NLS.str(\"search_dialog.limit_package\"));\n\n\t\tJPanel searchPackagePanel = new JPanel(new BorderLayout());\n\t\tsearchPackagePanel.setBorder(BorderFactory.createTitledBorder(NLS.str(\"search_dialog.limit_package\")));\n\t\tsearchPackagePanel.add(packageField, BorderLayout.CENTER);\n\t\tDimension minPanelSize = calcMinSizeForTitledBorder(searchPackagePanel);\n\t\tsearchPackagePanel\n\t\t\t\t.setPreferredSize(new Dimension(Math.max(packageField.getPreferredSize().width, minPanelSize.width), minPanelSize.height));\n\n\t\tresExtField = new JTextField(30);\n\t\tTextStandardActions.attach(resExtField);\n\t\tresExtField.putClientProperty(FlatClientProperties.TEXT_FIELD_SHOW_CLEAR_BUTTON, true);\n\t\tresExtField.setToolTipText(NLS.str(\"preferences.res_file_ext\"));\n\t\tString resFilterStr = mainWindow.getProject().getSearchResourcesFilter();\n\t\tresExtField.setText(resFilterStr);\n\n\t\tResourceFilter resFilter = ResourceFilter.parse(resFilterStr);\n\n\t\tJCheckBox textResBox = new JCheckBox(NLS.str(\"search_dialog.res_text\"));\n\t\ttextResBox.setSelected(resFilter.getContentTypes().contains(ResourceContentType.CONTENT_TEXT));\n\t\tJCheckBox binResBox = new JCheckBox(NLS.str(\"search_dialog.res_binary\"));\n\t\tbinResBox.setSelected(resFilter.getContentTypes().contains(ResourceContentType.CONTENT_BINARY));\n\n\t\tItemListener resContentTypeListener = ev -> {\n\t\t\ttry {\n\t\t\t\tSet<ResourceContentType> contentTypes = EnumSet.noneOf(ResourceContentType.class);\n\t\t\t\tif (textResBox.isSelected()) {\n\t\t\t\t\tcontentTypes.add(ResourceContentType.CONTENT_TEXT);\n\t\t\t\t}\n\t\t\t\tif (binResBox.isSelected()) {\n\t\t\t\t\tcontentTypes.add(ResourceContentType.CONTENT_BINARY);\n\t\t\t\t}\n\t\t\t\tString newStr = ResourceFilter.withContentType(resExtField.getText(), contentTypes);\n\t\t\t\tif (!newStr.equals(resExtField.getText())) {\n\t\t\t\t\tresExtField.setText(newStr);\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t};\n\t\ttextResBox.addItemListener(resContentTypeListener);\n\t\tbinResBox.addItemListener(resContentTypeListener);\n\n\t\tresExtField.getDocument().addDocumentListener(new DocumentUpdateListener(ev -> UiUtils.uiRun(() -> {\n\t\t\ttry {\n\t\t\t\tResourceFilter filter = ResourceFilter.parse(resExtField.getText());\n\t\t\t\ttextResBox.setSelected(filter.getContentTypes().contains(ResourceContentType.CONTENT_TEXT));\n\t\t\t\tbinResBox.setSelected(filter.getContentTypes().contains(ResourceContentType.CONTENT_BINARY));\n\t\t\t} catch (Exception e) {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t})));\n\n\t\tJPanel resExtFilePanel = new JPanel();\n\t\tresExtFilePanel.setLayout(new BoxLayout(resExtFilePanel, BoxLayout.LINE_AXIS));\n\t\tresExtFilePanel.setBorder(BorderFactory.createTitledBorder(NLS.str(\"preferences.res_file_ext\")));\n\t\tresExtFilePanel.add(resExtField);\n\t\tresExtFilePanel.add(textResBox);\n\t\tresExtFilePanel.add(binResBox);\n\t\tresExtFilePanel.setPreferredSize(calcMinSizeForTitledBorder(resExtFilePanel));\n\n\t\tresSizeLimit = new JSpinner(new SpinnerNumberModel(mainWindow.getProject().getSearchResourcesSizeLimit(), 0, Integer.MAX_VALUE, 1));\n\t\tresSizeLimit.setToolTipText(NLS.str(\"preferences.res_skip_file\"));\n\n\t\tJPanel sizeLimitPanel = new JPanel(new BorderLayout());\n\t\tsizeLimitPanel.setBorder(BorderFactory.createTitledBorder(NLS.str(\"preferences.res_skip_file\")));\n\t\tsizeLimitPanel.add(resSizeLimit, BorderLayout.CENTER);\n\t\tsizeLimitPanel.setPreferredSize(calcMinSizeForTitledBorder(sizeLimitPanel));\n\n\t\tJPanel optionsPanel = new JPanel(new WrapLayout(FlowLayout.LEFT));\n\t\toptionsPanel.add(searchInPanel);\n\t\toptionsPanel.add(searchPackagePanel);\n\t\toptionsPanel.add(resExtFilePanel);\n\t\toptionsPanel.add(sizeLimitPanel);\n\n\t\toptionsListener.addListener(searchOptions -> {\n\t\t\tboolean codeSearch = Utils.isSetContainsAny(searchOptions, EnumSet.of(CODE, CLASS, METHOD, FIELD, COMMENT));\n\t\t\tsearchPackagePanel.setVisible(codeSearch);\n\t\t\tboolean resSearch = searchOptions.contains(RESOURCE);\n\t\t\tresExtFilePanel.setVisible(resSearch);\n\t\t\tsizeLimitPanel.setVisible(resSearch);\n\t\t\toptionsPanel.revalidate();\n\t\t\toptionsPanel.repaint();\n\t\t});\n\n\t\tJPanel searchPane = new JPanel();\n\t\tsearchPane.setLayout(new BoxLayout(searchPane, BoxLayout.PAGE_AXIS));\n\t\tsearchPane.add(searchFieldPanel);\n\t\tsearchPane.add(Box.createRigidArea(new Dimension(0, 5)));\n\t\tsearchPane.add(optionsPanel);\n\n\t\tinitCommon();\n\t\tJPanel resultsPanel = initResultsTable();\n\t\tJPanel buttonPane = initButtonsPanel();\n\n\t\tJPanel contentPanel = new JPanel();\n\t\tcontentPanel.setLayout(new BorderLayout(5, 5));\n\t\tcontentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));\n\t\tcontentPanel.add(searchPane, BorderLayout.PAGE_START);\n\t\tcontentPanel.add(resultsPanel, BorderLayout.CENTER);\n\t\tcontentPanel.add(buttonPane, BorderLayout.PAGE_END);\n\t\tgetContentPane().add(contentPanel);\n\n\t\taddComponentListener(new ComponentAdapter() {\n\t\t\t@Override\n\t\t\tpublic void componentResized(ComponentEvent e) {\n\t\t\t\toptionsPanel.revalidate();\n\t\t\t\toptionsPanel.repaint();\n\t\t\t}\n\t\t});\n\t\tsetLocationRelativeTo(null);\n\t\tsetDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\n\t}\n\n\tprivate int getMaxPkgLen() {\n\t\tCacheObject cacheObject = mainWindow.getCacheObject();\n\t\tint maxPkgLength = cacheObject.getMaxPkgLength();\n\t\tif (maxPkgLength != 0) {\n\t\t\treturn maxPkgLength;\n\t\t}\n\t\tint max = 1;\n\t\tfor (PackageNode pkg : mainWindow.getWrapper().getRootNode().getPackages()) {\n\t\t\tint len = pkg.getPkgInfo().getFullName().length();\n\t\t\tif (len > max) {\n\t\t\t\tmax = len;\n\t\t\t}\n\t\t}\n\t\tcacheObject.setMaxPkgLength(max);\n\t\treturn max;\n\t}\n\n\t/**\n\t * Workaround to calculate minimum size for a panel with titled border\n\t */\n\tprivate Dimension calcMinSizeForTitledBorder(JPanel panel) {\n\t\tTitledBorder border = (TitledBorder) panel.getBorder();\n\t\tInsets borderInsets = border.getBorderInsets(panel);\n\t\tint insets = 2 * (borderInsets.left + borderInsets.right);\n\t\tdouble titleWidth = panel.getFontMetrics(border.getTitleFont()).stringWidth(border.getTitle());\n\t\treturn new Dimension((int) titleWidth + insets, panel.getPreferredSize().height);\n\t}\n\n\tprivate void addSearchHistoryButton() {\n\t\tJButton searchHistoryButton = new JButton(new FlatSearchWithHistoryIcon(true));\n\t\tsearchHistoryButton.setToolTipText(NLS.str(\"search_dialog.search_history\"));\n\t\tsearchHistoryButton.addActionListener(e -> {\n\t\t\tJPopupMenu popupMenu = new JPopupMenu();\n\t\t\tList<String> searchHistory = mainWindow.getProject().getSearchHistory();\n\t\t\tif (searchHistory.isEmpty()) {\n\t\t\t\tpopupMenu.add(\"(empty)\");\n\t\t\t} else {\n\t\t\t\tfor (String str : searchHistory) {\n\t\t\t\t\tJMenuItem item = popupMenu.add(str);\n\t\t\t\t\titem.addActionListener(ev -> searchField.setText(str));\n\t\t\t\t}\n\t\t\t}\n\t\t\tpopupMenu.show(searchHistoryButton, 0, searchHistoryButton.getHeight());\n\t\t});\n\t\tsearchField.putClientProperty(FlatClientProperties.TEXT_FIELD_LEADING_COMPONENT, searchHistoryButton);\n\t}\n\n\tprotected void addResultsActions(JPanel resultsActionsPanel) {\n\t\tloadAllButton = new JButton(NLS.str(\"search_dialog.load_all\"));\n\t\tloadAllButton.addActionListener(e -> loadMoreResults(true));\n\t\tloadAllButton.setEnabled(false);\n\n\t\tloadMoreButton = new JButton(NLS.str(\"search_dialog.load_more\"));\n\t\tloadMoreButton.addActionListener(e -> loadMoreResults(false));\n\t\tloadMoreButton.setEnabled(false);\n\n\t\tstopBtn = new JButton(NLS.str(\"search_dialog.stop\"));\n\t\tstopBtn.addActionListener(e -> pauseSearch());\n\t\tstopBtn.setEnabled(false);\n\n\t\tsortBtn = new JButton(NLS.str(\"search_dialog.sort_results\"));\n\t\tsortBtn.addActionListener(e -> {\n\t\t\tsynchronized (pendingResults) {\n\t\t\t\tresultsModel.sort();\n\t\t\t\tresultsTable.updateTable();\n\t\t\t}\n\t\t});\n\t\tsortBtn.setEnabled(false);\n\n\t\tresultsActionsPanel.add(loadAllButton);\n\t\tresultsActionsPanel.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tresultsActionsPanel.add(loadMoreButton);\n\t\tresultsActionsPanel.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tresultsActionsPanel.add(stopBtn);\n\t\tresultsActionsPanel.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tresultsActionsPanel.add(stopBtn);\n\t\tsuper.addResultsActions(resultsActionsPanel);\n\t\tresultsActionsPanel.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tresultsActionsPanel.add(sortBtn);\n\t}\n\n\tprivate class SearchEventEmitter {\n\t\tprivate final Flowable<String> flowable;\n\t\tprivate Emitter<String> emitter;\n\n\t\tpublic SearchEventEmitter() {\n\t\t\tflowable = Flowable.create(this::saveEmitter, BackpressureStrategy.LATEST);\n\t\t}\n\n\t\tpublic Flowable<String> getFlowable() {\n\t\t\treturn flowable;\n\t\t}\n\n\t\tprivate void saveEmitter(Emitter<String> emitter) {\n\t\t\tthis.emitter = emitter;\n\t\t}\n\n\t\tpublic synchronized void emitSearch() {\n\t\t\tthis.emitter.onNext(searchField.getText());\n\t\t}\n\t}\n\n\tprivate void initSearchEvents() {\n\t\tif (searchDisposable != null) {\n\t\t\tsearchDisposable.dispose();\n\t\t\tsearchDisposable = null;\n\t\t}\n\t\tsearchEmitter = new SearchEventEmitter();\n\t\tFlowable<String> searchEvents;\n\t\tif (mainWindow.getSettings().isUseAutoSearch()) {\n\t\t\tsearchEvents = Flowable.merge(List.of(\n\t\t\t\t\tRxUtils.textFieldChanges(searchField),\n\t\t\t\t\tRxUtils.textFieldEnterPress(searchField),\n\t\t\t\t\tRxUtils.textFieldChanges(packageField),\n\t\t\t\t\tRxUtils.textFieldEnterPress(packageField),\n\t\t\t\t\tRxUtils.textFieldChanges(resExtField),\n\t\t\t\t\tRxUtils.textFieldEnterPress(resExtField),\n\t\t\t\t\tRxUtils.spinnerChanges(resSizeLimit),\n\t\t\t\t\tRxUtils.spinnerEnterPress(resSizeLimit),\n\t\t\t\t\tsearchEmitter.getFlowable()));\n\t\t} else {\n\t\t\tsearchEvents = Flowable.merge(List.of(\n\t\t\t\t\tRxUtils.textFieldEnterPress(searchField),\n\t\t\t\t\tRxUtils.textFieldEnterPress(packageField),\n\t\t\t\t\tRxUtils.textFieldEnterPress(resExtField),\n\t\t\t\t\tRxUtils.spinnerEnterPress(resSizeLimit),\n\t\t\t\t\tsearchEmitter.getFlowable()));\n\t\t}\n\t\tsearchDisposable = searchEvents\n\t\t\t\t.debounce(100, TimeUnit.MILLISECONDS)\n\t\t\t\t.observeOn(Schedulers.from(searchBackgroundExecutor))\n\t\t\t\t.subscribe(t -> this.search(searchField.getText()));\n\n\t\t// set initial values\n\t\toptionsListener.sendUpdate(options);\n\t}\n\n\tprivate void search(String text) {\n\t\tUiUtils.notUiThreadGuard();\n\t\tstopSearchTask();\n\t\tUiUtils.uiRun(this::resetSearch);\n\t\tsearchTask = prepareSearch(text);\n\t\tif (searchTask == null) {\n\t\t\treturn;\n\t\t}\n\t\tUiUtils.uiRunAndWait(() -> {\n\t\t\tupdateTableHighlight();\n\t\t\tprepareForSearch();\n\t\t});\n\t\tthis.searchTask.setResultsLimit(mainWindow.getSettings().getSearchResultsPerPage());\n\t\tthis.searchTask.setProgressListener(this::updateProgress);\n\t\tthis.searchTask.fetchResults();\n\t\tLOG.debug(\"Total search items count estimation: {}\", this.searchTask.getTaskProgress().total());\n\t}\n\n\tprivate SearchTask prepareSearch(String text) {\n\t\tif (text == null || options.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\t// allow empty text for search in comments\n\t\tif (text.isEmpty() && !options.contains(COMMENT)) {\n\t\t\treturn null;\n\t\t}\n\t\tLOG.debug(\"Building search for '{}', options: {}\", text, options);\n\t\tSearchSettings searchSettings = new SearchSettings(text);\n\t\tsearchSettings.setIgnoreCase(options.contains(IGNORE_CASE));\n\t\tsearchSettings.setUseRegex(options.contains(USE_REGEX));\n\t\tsearchSettings.setSearchPkgStr(packageField.getText().trim());\n\t\tsearchSettings.setResFilterStr(resExtField.getText().trim());\n\t\tsearchSettings.setResSizeLimit((Integer) resSizeLimit.getValue());\n\n\t\tString error = searchSettings.prepare(mainWindow);\n\t\tUiUtils.highlightAsErrorField(searchField, !StringUtils.isEmpty(error));\n\t\tif (!StringUtils.isEmpty(error)) {\n\t\t\tresultsInfoLabel.setText(error);\n\t\t\treturn null;\n\t\t}\n\n\t\tSearchTask newSearchTask = new SearchTask(mainWindow, this::addSearchResult, this::searchFinished);\n\t\tif (!buildSearch(newSearchTask, text, searchSettings)) {\n\t\t\tUiUtils.highlightAsErrorField(searchField, true);\n\t\t\treturn null;\n\t\t}\n\t\t// save search settings\n\t\tmainWindow.getProject().setSearchResourcesFilter(resExtField.getText().trim());\n\t\tmainWindow.getProject().setSearchResourcesSizeLimit(searchSettings.getResSizeLimit());\n\t\treturn newSearchTask;\n\t}\n\n\tprivate boolean buildSearch(SearchTask newSearchTask, String text, SearchSettings searchSettings) {\n\t\tList<JavaClass> searchClasses;\n\t\tif (options.contains(ACTIVE_TAB)) {\n\t\t\tJumpPosition currentPos = mainWindow.getTabbedPane().getCurrentPosition();\n\t\t\tif (currentPos == null) {\n\t\t\t\tresultsInfoLabel.setText(\"Can't search in current tab\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tJNode currentNode = currentPos.getNode();\n\t\t\tif (currentNode instanceof JClass) {\n\t\t\t\tJClass activeCls = currentNode.getRootClass();\n\t\t\t\tsearchSettings.setActiveCls(activeCls);\n\t\t\t\tsearchClasses = Collections.singletonList(activeCls.getCls());\n\t\t\t} else if (currentNode instanceof JResource) {\n\t\t\t\tsearchSettings.setActiveResource((JResource) currentNode);\n\t\t\t\tsearchClasses = Collections.emptyList();\n\t\t\t} else {\n\t\t\t\tresultsInfoLabel.setText(\"Can't search in current tab\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else {\n\t\t\tsearchClasses = includedClsCache.get(mainWindow.getSettings().getExcludedPackages(),\n\t\t\t\t\texc -> mainWindow.getWrapper().getIncludedClassesWithInners());\n\t\t}\n\t\tJavaPackage searchPkg = searchSettings.getSearchPackage();\n\t\tif (searchPkg != null) {\n\t\t\tsearchClasses = searchClasses.stream()\n\t\t\t\t\t.filter(searchSettings::isInSearchPkg)\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t}\n\t\tif (text.isEmpty() && options.contains(COMMENT)) {\n\t\t\t// allow empty text for comment search\n\t\t\tnewSearchTask.addProviderJob(new CommentSearchProvider(mainWindow, searchSettings, searchClasses));\n\t\t\treturn true;\n\t\t}\n\t\tif (!searchClasses.isEmpty()) {\n\t\t\t// using ordered execution for fast tasks\n\t\t\tMergedSearchProvider merged = new MergedSearchProvider();\n\t\t\tif (options.contains(CLASS)) {\n\t\t\t\tmerged.add(new ClassSearchProvider(mainWindow, searchSettings, searchClasses));\n\t\t\t}\n\t\t\tif (options.contains(METHOD)) {\n\t\t\t\tmerged.add(new MethodSearchProvider(mainWindow, searchSettings, searchClasses));\n\t\t\t}\n\t\t\tif (options.contains(FIELD)) {\n\t\t\t\tmerged.add(new FieldSearchProvider(mainWindow, searchSettings, searchClasses));\n\t\t\t}\n\t\t\tif (!merged.isEmpty()) {\n\t\t\t\tmerged.prepare();\n\t\t\t\tnewSearchTask.addProviderJob(merged);\n\t\t\t}\n\n\t\t\tif (options.contains(CODE)) {\n\t\t\t\tint clsCount = searchClasses.size();\n\t\t\t\tif (clsCount == 1) {\n\t\t\t\t\tnewSearchTask.addProviderJob(new CodeSearchProvider(mainWindow, searchSettings, searchClasses, null));\n\t\t\t\t} else if (clsCount > 1) {\n\t\t\t\t\tList<JavaClass> topClasses = ListUtils.filter(searchClasses, c -> !c.isInner());\n\t\t\t\t\tList<List<JavaClass>> batches = batchesCache.get(topClasses,\n\t\t\t\t\t\t\tclsList -> mainWindow.getWrapper().buildDecompileBatches(clsList));\n\t\t\t\t\tSet<JavaClass> includedClasses = new HashSet<>(topClasses);\n\t\t\t\t\tfor (List<JavaClass> batch : batches) {\n\t\t\t\t\t\tnewSearchTask.addProviderJob(new CodeSearchProvider(mainWindow, searchSettings, batch, includedClasses));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (options.contains(COMMENT)) {\n\t\t\t\tnewSearchTask.addProviderJob(new CommentSearchProvider(mainWindow, searchSettings, searchClasses));\n\t\t\t}\n\t\t}\n\t\tif (options.contains(RESOURCE)) {\n\t\t\tnewSearchTask.addProviderJob(new ResourceSearchProvider(mainWindow, searchSettings, this));\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tprotected void openItem(JNode node) {\n\t\tif (mainWindow.getSettings().isUseAutoSearch()) {\n\t\t\t// for auto search save only searches which leads to node opening\n\t\t\tmainWindow.getProject().addToSearchHistory(searchField.getText());\n\t\t}\n\t\tsuper.openItem(node);\n\t}\n\n\tprivate void pauseSearch() {\n\t\tstopBtn.setEnabled(false);\n\t\tsearchBackgroundExecutor.execute(() -> {\n\t\t\tif (searchTask != null) {\n\t\t\t\tsearchTask.cancel();\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void stopSearchTask() {\n\t\tUiUtils.notUiThreadGuard();\n\t\tif (searchTask != null) {\n\t\t\tsearchTask.cancel();\n\t\t\tsearchTask.waitTask();\n\t\t\tsearchTask = null;\n\t\t}\n\t}\n\n\tprivate void loadMoreResults(boolean all) {\n\t\tsearchBackgroundExecutor.execute(() -> {\n\t\t\tif (searchTask == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsearchTask.cancel();\n\t\t\tsearchTask.waitTask();\n\t\t\tUiUtils.uiRunAndWait(this::prepareForSearch);\n\t\t\tif (all) {\n\t\t\t\tsearchTask.setResultsLimit(0);\n\t\t\t}\n\t\t\tsearchTask.fetchResults();\n\t\t});\n\t}\n\n\tprivate void resetSearch() {\n\t\tUiUtils.uiThreadGuard();\n\t\tresultsModel.clear();\n\t\tresultsTable.updateTable();\n\t\tsynchronized (pendingResults) {\n\t\t\tpendingResults.clear();\n\t\t}\n\t\tupdateProgressLabel(\"\");\n\t\tprogressPane.setVisible(false);\n\t\twarnLabel.setVisible(false);\n\t\tloadAllButton.setEnabled(false);\n\t\tloadMoreButton.setEnabled(false);\n\t}\n\n\tprivate void prepareForSearch() {\n\t\tUiUtils.uiThreadGuard();\n\t\tstopBtn.setEnabled(true);\n\t\tsortBtn.setEnabled(false);\n\t\tshowSearchState();\n\t\tprogressStartCommon();\n\t}\n\n\tprivate void addSearchResult(JNode node) {\n\t\tObjects.requireNonNull(node);\n\t\tsynchronized (pendingResults) {\n\t\t\tUiUtils.notUiThreadGuard();\n\t\t\tpendingResults.add(node);\n\t\t}\n\t}\n\n\tprivate void updateTable() {\n\t\tsynchronized (pendingResults) {\n\t\t\tUiUtils.uiThreadGuard();\n\t\t\tCollections.sort(pendingResults);\n\t\t\tresultsModel.addAll(pendingResults);\n\t\t\tpendingResults.clear();\n\t\t\tresultsTable.updateTable();\n\t\t}\n\t}\n\n\tprivate void updateTableHighlight() {\n\t\tString text = searchField.getText();\n\t\tupdateHighlightContext(text, !options.contains(IGNORE_CASE), options.contains(USE_REGEX), false);\n\t\tcache.setLastSearch(text);\n\t\tcache.setLastSearchPackage(packageField.getText());\n\t\tcache.getLastSearchOptions().put(searchPreset, options);\n\t\tif (!mainWindow.getSettings().isUseAutoSearch()) {\n\t\t\tmainWindow.getProject().addToSearchHistory(text);\n\t\t}\n\t}\n\n\tprivate void updateProgress(ITaskProgress progress) {\n\t\tUiUtils.uiRun(() -> {\n\t\t\tprogressPane.setProgress(progress);\n\t\t\tupdateTable();\n\t\t});\n\t}\n\n\tpublic void updateProgressLabel(String text) {\n\t\tUiUtils.uiRun(() -> progressInfoLabel.setText(text));\n\t}\n\n\tprivate void searchFinished(ITaskInfo status, Boolean complete) {\n\t\tUiUtils.uiThreadGuard();\n\t\tLOG.debug(\"Search complete: {}, complete: {}\", status, complete);\n\t\tloadAllButton.setEnabled(!complete);\n\t\tloadMoreButton.setEnabled(!complete);\n\t\tstopBtn.setEnabled(false);\n\t\tprogressFinishedCommon();\n\t\tupdateTable();\n\t\tupdateProgressLabel(complete);\n\t\tsortBtn.setEnabled(resultsModel.getRowCount() != 0);\n\t}\n\n\tprivate void unloadTempData() {\n\t\tmainWindow.getWrapper().unloadClasses();\n\t\tSystem.gc();\n\t}\n\n\tprivate JCheckBox makeOptionsCheckBox(String name, final SearchOptions opt) {\n\t\tfinal JCheckBox chBox = new JCheckBox(name);\n\t\tchBox.setSelected(options.contains(opt));\n\t\tchBox.addItemListener(e -> {\n\t\t\tif (chBox.isSelected()) {\n\t\t\t\toptions.add(opt);\n\t\t\t} else {\n\t\t\t\toptions.remove(opt);\n\t\t\t}\n\t\t\toptionsListener.sendUpdate(options);\n\t\t\tsearchEmitter.emitSearch();\n\t\t});\n\t\treturn chBox;\n\t}\n\n\tprivate JToggleButton makeOptionsToggleButton(String name, ImageIcon icon, ImageIcon selectedIcon, final SearchOptions opt) {\n\t\tfinal JToggleButton toggleButton = new JToggleButton();\n\t\ttoggleButton.setToolTipText(name);\n\t\ttoggleButton.setIcon(icon);\n\t\ttoggleButton.setSelectedIcon(selectedIcon);\n\t\ttoggleButton.setSelected(options.contains(opt));\n\t\ttoggleButton.addItemListener(e -> {\n\t\t\tif (toggleButton.isSelected()) {\n\t\t\t\toptions.add(opt);\n\t\t\t} else {\n\t\t\t\toptions.remove(opt);\n\t\t\t}\n\t\t\toptionsListener.sendUpdate(options);\n\t\t\tsearchEmitter.emitSearch();\n\t\t});\n\t\treturn toggleButton;\n\t}\n\n\t@Override\n\tprotected void loadFinished() {\n\t\tresultsTable.setEnabled(true);\n\t\tsearchField.setEnabled(true);\n\t\tsearchEmitter.emitSearch();\n\t}\n\n\t@Override\n\tprotected void loadStart() {\n\t\tresultsTable.setEnabled(false);\n\t\tsearchField.setEnabled(false);\n\t}\n\n\tprivate void registerActiveTabListener() {\n\t\tremoveActiveTabListener();\n\t\tactiveTabListener = e -> {\n\t\t\tif (options.contains(ACTIVE_TAB)) {\n\t\t\t\tLOG.debug(\"active tab change event received\");\n\t\t\t\tsearchEmitter.emitSearch();\n\t\t\t}\n\t\t};\n\t\tmainWindow.getTabbedPane().addChangeListener(activeTabListener);\n\t}\n\n\tprivate void removeActiveTabListener() {\n\t\tif (activeTabListener != null) {\n\t\t\tmainWindow.getTabbedPane().removeChangeListener(activeTabListener);\n\t\t\tactiveTabListener = null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/SetValueDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.BorderLayout;\nimport java.awt.Label;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.KeyEvent;\nimport java.util.AbstractMap.SimpleEntry;\nimport java.util.ArrayList;\nimport java.util.Map.Entry;\n\nimport javax.swing.AbstractAction;\nimport javax.swing.BorderFactory;\nimport javax.swing.BoxLayout;\nimport javax.swing.ButtonGroup;\nimport javax.swing.JButton;\nimport javax.swing.JDialog;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JRadioButton;\nimport javax.swing.JTextField;\nimport javax.swing.KeyStroke;\nimport javax.swing.WindowConstants;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.panel.JDebuggerPanel.ValueTreeNode;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.TextStandardActions;\nimport jadx.gui.utils.UiUtils;\n\npublic class SetValueDialog extends JDialog {\n\tprivate static final long serialVersionUID = -1111111202103121002L;\n\n\tprivate final transient MainWindow mainWindow;\n\tprivate final transient ValueTreeNode valNode;\n\n\tpublic SetValueDialog(MainWindow mainWindow, ValueTreeNode valNode) {\n\t\tsuper(mainWindow);\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.valNode = valNode;\n\t\tinitUI();\n\t\tUiUtils.addEscapeShortCutToDispose(this);\n\t\tsetTitle(valNode.toString());\n\t}\n\n\tprivate void initUI() {\n\t\tJTextField valField = new JTextField();\n\t\tTextStandardActions.attach(valField);\n\t\tJPanel valPane = new JPanel(new BorderLayout(5, 5));\n\t\tvalPane.add(new JLabel(NLS.str(\"set_value_dialog.label_value\")), BorderLayout.WEST);\n\t\tvalPane.add(valField, BorderLayout.CENTER);\n\n\t\tJPanel btnPane = new JPanel();\n\t\tbtnPane.setLayout(new BoxLayout(btnPane, BoxLayout.LINE_AXIS));\n\t\tJButton setValueBtn = new JButton(NLS.str(\"set_value_dialog.btn_set\"));\n\t\tbtnPane.add(new Label());\n\t\tbtnPane.add(setValueBtn);\n\n\t\tUiUtils.addKeyBinding(valField, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), \"set value\",\n\t\t\t\tnew AbstractAction() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t\t\tsetValueBtn.doClick();\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\tJPanel typePane = new JPanel();\n\t\ttypePane.setLayout(new BoxLayout(typePane, BoxLayout.LINE_AXIS));\n\t\tjava.util.List<JRadioButton> rbs = new ArrayList<>(6);\n\t\trbs.add(new JRadioButton(\"int\"));\n\t\trbs.add(new JRadioButton(\"String\"));\n\t\trbs.add(new JRadioButton(\"long\"));\n\t\trbs.add(new JRadioButton(\"float\"));\n\t\trbs.add(new JRadioButton(\"double\"));\n\t\trbs.add(new JRadioButton(\"Object id\"));\n\t\trbs.get(0).setSelected(true); // select int radio\n\n\t\tButtonGroup rbGroup = new ButtonGroup();\n\t\trbs.forEach(rbGroup::add);\n\t\trbs.forEach(typePane::add);\n\n\t\tJPanel mainPane = new JPanel(new BorderLayout(5, 5));\n\t\tmainPane.add(typePane, BorderLayout.NORTH);\n\t\tmainPane.add(valPane, BorderLayout.CENTER);\n\t\tmainPane.add(btnPane, BorderLayout.SOUTH);\n\t\tmainPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));\n\t\tgetContentPane().add(mainPane);\n\n\t\tthis.setTitle(NLS.str(\"set_value_dialog.title\"));\n\n\t\tpack();\n\t\tsetSize(480, 160);\n\t\tsetLocationRelativeTo(null);\n\t\tsetDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\n\t\tsetModalityType(ModalityType.MODELESS);\n\t\tUiUtils.addEscapeShortCutToDispose(this);\n\n\t\tsetValueBtn.addActionListener(new AbstractAction() {\n\t\t\tprivate static final long serialVersionUID = -1111111202103260220L;\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tboolean ok;\n\t\t\t\ttry {\n\t\t\t\t\tEntry<ArgType, Object> type = getType();\n\t\t\t\t\tif (type != null) {\n\t\t\t\t\t\tok = mainWindow\n\t\t\t\t\t\t\t\t.getDebuggerPanel()\n\t\t\t\t\t\t\t\t.getDbgController()\n\t\t\t\t\t\t\t\t.modifyRegValue(valNode, type.getKey(), type.getValue());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tUiUtils.showMessageBox(mainWindow, NLS.str(\"set_value_dialog.sel_type\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} catch (JadxRuntimeException except) {\n\t\t\t\t\tUiUtils.showMessageBox(mainWindow, except.getMessage());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (ok) {\n\t\t\t\t\tdispose();\n\t\t\t\t} else {\n\t\t\t\t\tUiUtils.showMessageBox(mainWindow, NLS.str(\"set_value_dialog.neg_msg\"));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tprivate Entry<ArgType, Object> getType() {\n\t\t\t\tString val = valField.getText();\n\t\t\t\tfor (JRadioButton rb : rbs) {\n\t\t\t\t\tif (rb.isSelected()) {\n\t\t\t\t\t\tswitch (rb.getText()) {\n\t\t\t\t\t\t\tcase \"int\":\n\t\t\t\t\t\t\t\treturn new SimpleEntry<>(ArgType.INT, Integer.valueOf(val));\n\t\t\t\t\t\t\tcase \"String\":\n\t\t\t\t\t\t\t\treturn new SimpleEntry<>(ArgType.STRING, val);\n\t\t\t\t\t\t\tcase \"long\":\n\t\t\t\t\t\t\t\treturn new SimpleEntry<>(ArgType.LONG, Long.valueOf(val));\n\t\t\t\t\t\t\tcase \"float\":\n\t\t\t\t\t\t\t\treturn new SimpleEntry<>(ArgType.FLOAT, Float.valueOf(val));\n\t\t\t\t\t\t\tcase \"double\":\n\t\t\t\t\t\t\t\treturn new SimpleEntry<>(ArgType.DOUBLE, Double.valueOf(val));\n\t\t\t\t\t\t\tcase \"Object id\":\n\t\t\t\t\t\t\t\treturn new SimpleEntry<>(ArgType.OBJECT, Long.valueOf(val));\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tthrow new JadxRuntimeException(\"Unexpected type: \" + rb.getText());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/UsageDialog.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.BorderLayout;\nimport java.awt.FlowLayout;\nimport java.awt.Font;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.WindowConstants;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.JavaClass;\nimport jadx.api.JavaMethod;\nimport jadx.api.JavaNode;\nimport jadx.api.utils.CodeUtils;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.visitors.prepare.CollectConstValues;\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.jobs.TaskStatus;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.treemodel.CodeNode;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JField;\nimport jadx.gui.treemodel.JMethod;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.JNodeCache;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.ui.NodeLabel;\n\npublic class UsageDialog extends CommonSearchDialog {\n\tprivate static final long serialVersionUID = -5105405789969134105L;\n\n\tprivate final transient JNode node;\n\n\tprivate transient List<CodeNode> usageList;\n\n\tpublic static void open(MainWindow mainWindow, JNode node) {\n\t\tUsageDialog usageDialog = new UsageDialog(mainWindow, node);\n\t\tmainWindow.addLoadListener(loaded -> {\n\t\t\tif (!loaded) {\n\t\t\t\tusageDialog.dispose();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t});\n\t\tusageDialog.setVisible(true);\n\t}\n\n\tprivate UsageDialog(MainWindow mainWindow, JNode node) {\n\t\tsuper(mainWindow, NLS.str(\"usage_dialog.title\"));\n\t\tthis.node = node;\n\n\t\tinitUI();\n\t\tregisterInitOnOpen();\n\t\tloadWindowPos();\n\t}\n\n\t@Override\n\tprotected void openInit() {\n\t\tprogressStartCommon();\n\t\tprepareUsageData();\n\t\tmainWindow.getBackgroundExecutor().execute(NLS.str(\"progress.load\"),\n\t\t\t\tthis::collectUsageData,\n\t\t\t\t(status) -> {\n\t\t\t\t\tif (status == TaskStatus.CANCEL_BY_MEMORY) {\n\t\t\t\t\t\tmainWindow.showHeapUsageBar();\n\t\t\t\t\t\tUiUtils.errorMessage(this, NLS.str(\"message.memoryLow\"));\n\t\t\t\t\t}\n\t\t\t\t\tprogressFinishedCommon();\n\t\t\t\t\tloadFinished();\n\t\t\t\t});\n\t}\n\n\tprivate void prepareUsageData() {\n\t\tif (mainWindow.getSettings().isReplaceConsts() && node instanceof JField) {\n\t\t\tFieldNode fld = ((JField) node).getJavaField().getFieldNode();\n\t\t\tboolean constField = CollectConstValues.getFieldConstValue(fld) != null;\n\t\t\tif (constField && !fld.getAccessFlags().isPrivate()) {\n\t\t\t\t// run full decompilation to prepare for full code scan\n\t\t\t\tmainWindow.requestFullDecompilation();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void collectUsageData() {\n\t\tusageList = new ArrayList<>();\n\t\tbuildUsageQuery().forEach(\n\t\t\t\t(searchNode, useNodes) -> useNodes.stream()\n\t\t\t\t\t\t.map(JavaNode::getTopParentClass)\n\t\t\t\t\t\t.distinct()\n\t\t\t\t\t\t.forEach(u -> processUsage(searchNode, u)));\n\t}\n\n\t/**\n\t * Return mapping of 'node to search' to 'use places'\n\t */\n\tprivate Map<JavaNode, List<? extends JavaNode>> buildUsageQuery() {\n\t\tMap<JavaNode, List<? extends JavaNode>> map = new HashMap<>();\n\t\tif (node instanceof JMethod) {\n\t\t\tJavaMethod javaMethod = ((JMethod) node).getJavaMethod();\n\t\t\tfor (JavaMethod mth : getMethodWithOverrides(javaMethod)) {\n\t\t\t\tmap.put(mth, mth.getUseIn());\n\t\t\t}\n\t\t\treturn map;\n\t\t}\n\t\tif (node instanceof JClass) {\n\t\t\tJavaClass javaCls = ((JClass) node).getCls();\n\t\t\tmap.put(javaCls, javaCls.getUseIn());\n\t\t\t// add constructors usage into class usage\n\t\t\tfor (JavaMethod javaMth : javaCls.getMethods()) {\n\t\t\t\tif (javaMth.isConstructor()) {\n\t\t\t\t\tmap.put(javaMth, javaMth.getUseIn());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn map;\n\t\t}\n\t\tif (node instanceof JField && mainWindow.getSettings().isReplaceConsts()) {\n\t\t\tFieldNode fld = ((JField) node).getJavaField().getFieldNode();\n\t\t\tboolean constField = CollectConstValues.getFieldConstValue(fld) != null;\n\t\t\tif (constField && !fld.getAccessFlags().isPrivate()) {\n\t\t\t\t// search all classes to collect usage of replaced constants\n\t\t\t\tmap.put(fld.getJavaNode(), mainWindow.getWrapper().getIncludedClasses());\n\t\t\t\treturn map;\n\t\t\t}\n\t\t}\n\t\tJavaNode javaNode = node.getJavaNode();\n\t\tmap.put(javaNode, javaNode.getUseIn());\n\t\treturn map;\n\t}\n\n\tprivate List<JavaMethod> getMethodWithOverrides(JavaMethod javaMethod) {\n\t\tList<JavaMethod> relatedMethods = javaMethod.getOverrideRelatedMethods();\n\t\tif (!relatedMethods.isEmpty()) {\n\t\t\treturn relatedMethods;\n\t\t}\n\t\treturn Collections.singletonList(javaMethod);\n\t}\n\n\tprivate void processUsage(JavaNode searchNode, JavaClass topUseClass) {\n\t\tICodeInfo codeInfo = topUseClass.getCodeInfo();\n\t\tList<Integer> usePositions = topUseClass.getUsePlacesFor(codeInfo, searchNode);\n\t\tif (usePositions.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tString code = codeInfo.getCodeStr();\n\t\tJadxWrapper wrapper = mainWindow.getWrapper();\n\t\tfor (int pos : usePositions) {\n\t\t\tString line = CodeUtils.getLineForPos(code, pos);\n\t\t\tif (line.startsWith(\"import \")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tJNodeCache nodeCache = getNodeCache();\n\t\t\tJavaNode enclosingNode = wrapper.getEnclosingNode(codeInfo, pos);\n\t\t\tJClass rootJCls = nodeCache.makeFrom(topUseClass);\n\t\t\tJNode usageJNode = enclosingNode == null ? rootJCls : nodeCache.makeFrom(enclosingNode);\n\t\t\tusageList.add(new CodeNode(rootJCls, usageJNode, line.trim(), pos));\n\t\t}\n\t}\n\n\t@Override\n\tprotected void loadFinished() {\n\t\tresultsTable.setEnabled(true);\n\t\tresultsModel.clear();\n\n\t\tCollections.sort(usageList);\n\t\tresultsModel.addAll(usageList);\n\t\tupdateHighlightContext(node.getName(), true, false, true);\n\t\tresultsTable.initColumnWidth();\n\t\tresultsTable.updateTable();\n\t\tupdateProgressLabel(true);\n\t}\n\n\t@Override\n\tprotected void loadStart() {\n\t\tresultsTable.setEnabled(false);\n\t}\n\n\tprivate void initUI() {\n\t\tJadxSettings settings = mainWindow.getSettings();\n\t\tFont codeFont = settings.getCodeFont();\n\t\tJLabel lbl = new JLabel(NLS.str(\"usage_dialog.label\"));\n\t\tlbl.setFont(codeFont);\n\t\tJLabel nodeLabel = NodeLabel.longName(node);\n\t\tnodeLabel.setFont(codeFont);\n\t\tlbl.setLabelFor(nodeLabel);\n\n\t\tJPanel searchPane = new JPanel();\n\t\tsearchPane.setLayout(new FlowLayout(FlowLayout.LEFT));\n\t\tsearchPane.add(lbl);\n\t\tsearchPane.add(nodeLabel);\n\t\tsearchPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));\n\n\t\tinitCommon();\n\t\tJPanel resultsPanel = initResultsTable();\n\t\tJPanel buttonPane = initButtonsPanel();\n\n\t\tJPanel contentPanel = new JPanel();\n\t\tcontentPanel.setLayout(new BorderLayout(5, 5));\n\t\tcontentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));\n\t\tcontentPanel.add(searchPane, BorderLayout.PAGE_START);\n\t\tcontentPanel.add(resultsPanel, BorderLayout.CENTER);\n\t\tcontentPanel.add(buttonPane, BorderLayout.PAGE_END);\n\t\tgetContentPane().add(contentPanel);\n\n\t\tpack();\n\t\tsetSize(800, 500);\n\t\tsetLocationRelativeTo(null);\n\t\tsetDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/dialog/UsageDialogPlus.java",
    "content": "package jadx.gui.ui.dialog;\n\nimport java.awt.BorderLayout;\nimport java.awt.Color;\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.FlowLayout;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JLabel;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPanel;\nimport javax.swing.JPopupMenu;\nimport javax.swing.JScrollPane;\nimport javax.swing.JSplitPane;\nimport javax.swing.JTree;\nimport javax.swing.SwingUtilities;\nimport javax.swing.WindowConstants;\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 javax.swing.tree.TreeSelectionModel;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.JavaClass;\nimport jadx.api.JavaMethod;\nimport jadx.api.JavaNode;\nimport jadx.api.utils.CodeUtils;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.visitors.prepare.CollectConstValues;\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.jobs.TaskStatus;\nimport jadx.gui.treemodel.CodeNode;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JField;\nimport jadx.gui.treemodel.JMethod;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.cellrenders.PathHighlightTreeCellRenderer;\nimport jadx.gui.ui.panel.ProgressPanel;\nimport jadx.gui.ui.panel.SimpleCodePanel;\nimport jadx.gui.utils.JNodeCache;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic class UsageDialogPlus extends CommonSearchDialog {\n\tprivate static final long serialVersionUID = -5105405789969134107L;\n\n\tprivate final JPanel mainPanel;\n\tprivate final JSplitPane splitPane;\n\tprivate final SimpleCodePanel simpleCodePanel;\n\tprivate final JTree usageTree;\n\tprivate final DefaultTreeModel treeModel;\n\tprivate final DefaultMutableTreeNode rootNode;\n\tprivate final ProgressPanel localProgressPanel;\n\tprivate final JLabel resultsInfoLabel;\n\tprivate final JLabel progressInfoLabel;\n\tprivate final JNode initialNode;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(UsageDialogPlus.class);\n\n\tpublic static void open(MainWindow mainWindow, JNode node) {\n\t\tUsageDialogPlus usageDialog = new UsageDialogPlus(mainWindow, node);\n\t\tmainWindow.addLoadListener(loaded -> {\n\t\t\tif (!loaded) {\n\t\t\t\tusageDialog.dispose();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t});\n\t\tusageDialog.setVisible(true);\n\n\t\t// Set the position of the split panel after the window is displayed.\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\tint width = usageDialog.splitPane.getWidth();\n\t\t\tif (width > 0) {\n\t\t\t\tusageDialog.splitPane.setDividerLocation((int) (width * 0.3));\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate UsageDialogPlus(MainWindow mainWindow, JNode node) {\n\t\tsuper(mainWindow, NLS.str(\"usage_dialog_plus.title\"));\n\t\tthis.initialNode = node;\n\n\t\t// Initialize the progress panel and warning label\n\t\tprogressPane = new ProgressPanel(mainWindow, false);\n\t\twarnLabel = new JLabel();\n\t\twarnLabel.setForeground(Color.RED);\n\t\twarnLabel.setVisible(false);\n\n\t\t// Initialize result and progress info labels\n\t\tresultsInfoLabel = new JLabel();\n\t\tprogressInfoLabel = new JLabel();\n\t\tlocalProgressPanel = new ProgressPanel(mainWindow, false);\n\n\t\tmainPanel = new JPanel();\n\t\tmainPanel.setLayout(new BorderLayout());\n\n\t\t// Create the code panel\n\t\tsimpleCodePanel = new SimpleCodePanel(mainWindow);\n\n\t\t// Create the tree\n\t\trootNode = new DefaultMutableTreeNode(node);\n\t\ttreeModel = new DefaultTreeModel(rootNode);\n\t\tusageTree = new JTree(treeModel);\n\t\tusageTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);\n\t\tusageTree.setRootVisible(true);\n\t\tusageTree.setShowsRootHandles(true);\n\t\tusageTree.putClientProperty(\"JTree.lineStyle\", \"Horizontal\");\n\t\tusageTree.setRowHeight(22);\n\t\tusageTree.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\n\t\tusageTree.setFont(mainWindow.getSettings().getCodeFont());\n\n\t\t// Use a custom renderer instead of a custom UI\n\t\tusageTree.setCellRenderer(new PathHighlightTreeCellRenderer());\n\n\t\t// Add tree listeners\n\t\tusageTree.addTreeSelectionListener(e -> {\n\t\t\tDefaultMutableTreeNode node1 = (DefaultMutableTreeNode) usageTree.getLastSelectedPathComponent();\n\t\t\tif (node1 == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tObject nodeInfo = node1.getUserObject();\n\t\t\tif (nodeInfo instanceof CodeNode) {\n\t\t\t\tCodeNode codeNode = (CodeNode) nodeInfo;\n\t\t\t\tsimpleCodePanel.showCode(codeNode, codeNode.makeDescString());\n\t\t\t} else if (nodeInfo instanceof JNode) {\n\t\t\t\tJNode jNode = (JNode) nodeInfo;\n\t\t\t\tsimpleCodePanel.showCode(jNode, jNode.makeDescString());\n\t\t\t}\n\n\t\t\t// Update the result information to display the number of child nodes of the currently selected node\n\t\t\tupdateResultsInfo(node1);\n\t\t});\n\n\t\tusageTree.addTreeExpansionListener(new TreeExpansionListener() {\n\t\t\t@Override\n\t\t\tpublic void treeExpanded(TreeExpansionEvent event) {\n\t\t\t\tTreePath path = event.getPath();\n\t\t\t\tDefaultMutableTreeNode expandedNode = (DefaultMutableTreeNode) path.getLastPathComponent();\n\n\t\t\t\t// Load only when the node has no child nodes.\n\t\t\t\tif (expandedNode.getChildCount() == 0) {\n\t\t\t\t\tObject userObject = expandedNode.getUserObject();\n\t\t\t\t\tif (userObject instanceof JNode) {\n\t\t\t\t\t\tJNode nodeToUse = (JNode) userObject;\n\t\t\t\t\t\t// If it is a CodeNode, first convert it to an actual JNode and then search for its usage.\n\t\t\t\t\t\tif (nodeToUse.getClass() == CodeNode.class) {\n\t\t\t\t\t\t\tnodeToUse = getNodeFromCodeNode((CodeNode) nodeToUse);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (nodeToUse != null) {\n\t\t\t\t\t\t\tloadNodeUsages(nodeToUse, expandedNode);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void treeCollapsed(TreeExpansionEvent event) {\n\t\t\t\t// No need to process\n\t\t\t}\n\t\t});\n\n\t\tusageTree.addMouseListener(new MouseAdapter() {\n\t\t\tprivate long lastClickTime = 0;\n\t\t\tprivate TreePath lastClickPath = null;\n\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(MouseEvent e) {\n\t\t\t\tTreePath path = usageTree.getPathForLocation(e.getX(), e.getY());\n\t\t\t\tif (path == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Set the selected path\n\t\t\t\tusageTree.setSelectionPath(path);\n\n\t\t\t\t// Handle the right-click menu\n\t\t\t\tif (SwingUtilities.isRightMouseButton(e)) {\n\t\t\t\t\tDefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) path.getLastPathComponent();\n\t\t\t\t\tObject userObject = selectedNode.getUserObject();\n\n\t\t\t\t\tif (userObject instanceof JNode || userObject instanceof CodeNode) {\n\t\t\t\t\t\tJNode nodeForMenu = (userObject instanceof JNode) ? (JNode) userObject : getNodeFromCodeNode((CodeNode) userObject);\n\t\t\t\t\t\tshowPopupMenu(e, nodeForMenu, path);\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Handle left-click single/double click\n\t\t\t\tif (SwingUtilities.isLeftMouseButton(e)) {\n\t\t\t\t\tlong clickTime = System.currentTimeMillis();\n\t\t\t\t\t// Double-click interval\n\t\t\t\t\tlong doubleClickInterval = 300;\n\n\t\t\t\t\tDefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) path.getLastPathComponent();\n\n\t\t\t\t\t// If the time interval between two clicks is less than the threshold and the same node is clicked,\n\t\t\t\t\t// it is considered a double-click\n\t\t\t\t\tif ((clickTime - lastClickTime) < doubleClickInterval && path.equals(lastClickPath)) {\n\t\t\t\t\t\t// Double-click processing - switch between expanded/collapsed states\n\t\t\t\t\t\tif (usageTree.isExpanded(path)) {\n\t\t\t\t\t\t\tusageTree.collapsePath(path);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tusageTree.expandPath(path);\n\t\t\t\t\t\t\t// If the node has no child nodes and is a JNode, load its usage\n\t\t\t\t\t\t\tif (selectedNode.getChildCount() == 0 && selectedNode.getUserObject() instanceof JNode) {\n\t\t\t\t\t\t\t\tJNode nodeToUse = (JNode) selectedNode.getUserObject();\n\t\t\t\t\t\t\t\t// If it is a CodeNode, first convert it to an actual JNode and then search for its usage\n\t\t\t\t\t\t\t\tif (nodeToUse.getClass() == CodeNode.class) {\n\t\t\t\t\t\t\t\t\tnodeToUse = getNodeFromCodeNode((CodeNode) nodeToUse);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (nodeToUse != null) {\n\t\t\t\t\t\t\t\t\tloadNodeUsages(nodeToUse, selectedNode);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Update the result information to display the number of child nodes of the currently selected node\n\t\t\t\t\t\tupdateResultsInfo(selectedNode);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Single-click processing - if the node is not expanded, expand it\n\t\t\t\t\t\tif (!usageTree.isExpanded(path)) {\n\t\t\t\t\t\t\tusageTree.expandPath(path);\n\t\t\t\t\t\t\t// If the node has no child nodes and is a JNode, then load its usage.\n\t\t\t\t\t\t\tif (selectedNode.getChildCount() == 0 && selectedNode.getUserObject() instanceof JNode) {\n\t\t\t\t\t\t\t\tJNode nodeToUse = (JNode) selectedNode.getUserObject();\n\t\t\t\t\t\t\t\t// If it is a CodeNode, first convert it to an actual JNode and then search for its usage\n\t\t\t\t\t\t\t\tif (nodeToUse.getClass() == CodeNode.class) {\n\t\t\t\t\t\t\t\t\tnodeToUse = getNodeFromCodeNode((CodeNode) nodeToUse);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (nodeToUse != null) {\n\t\t\t\t\t\t\t\t\tloadNodeUsages(nodeToUse, selectedNode);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Update the result information to display the number of child nodes of the currently selected node\n\t\t\t\t\t\tupdateResultsInfo(selectedNode);\n\t\t\t\t\t}\n\n\t\t\t\t\tlastClickTime = clickTime;\n\t\t\t\t\tlastClickPath = path;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t// Create the split panel\n\t\tsplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);\n\t\tsplitPane.setOneTouchExpandable(true);\n\t\tsplitPane.setContinuousLayout(true);\n\t\tsplitPane.setResizeWeight(0.3); // Left 30%\n\t\tsplitPane.setDividerSize(10); // Increase the width of the divider for easier dragging\n\n\t\t// Add tree to the scroll pane\n\t\tJScrollPane treeScrollPane = new JScrollPane(usageTree);\n\t\ttreeScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);\n\t\ttreeScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);\n\n\t\t// Create status panel\n\t\tJPanel statusPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));\n\t\tstatusPanel.add(resultsInfoLabel);\n\t\tstatusPanel.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tstatusPanel.add(progressInfoLabel);\n\t\tstatusPanel.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tstatusPanel.add(localProgressPanel);\n\n\t\t// Add components to the main panel\n\t\tJPanel leftPanel = new JPanel(new BorderLayout());\n\t\tleftPanel.add(treeScrollPane, BorderLayout.CENTER);\n\t\tleftPanel.add(statusPanel, BorderLayout.SOUTH);\n\n\t\tsplitPane.setLeftComponent(leftPanel);\n\t\tsplitPane.setRightComponent(simpleCodePanel);\n\n\t\tmainPanel.add(splitPane, BorderLayout.CENTER);\n\n\t\tinitUI();\n\t\tregisterInitOnOpen();\n\t\tloadWindowPos();\n\t}\n\n\tprivate void initUI() {\n\t\tinitCommon();\n\t\tJPanel buttonPane = initButtonsPanel();\n\n\t\tJPanel contentPanel = new JPanel();\n\t\tcontentPanel.setLayout(new BorderLayout(5, 5));\n\t\tcontentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));\n\t\tcontentPanel.add(mainPanel, BorderLayout.CENTER);\n\t\tcontentPanel.add(buttonPane, BorderLayout.PAGE_END);\n\t\tgetContentPane().add(contentPanel);\n\n\t\tpack();\n\t\tsetSize(1300, 700);\n\t\tsetLocationRelativeTo(null);\n\t\tsetDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\n\n\t\t// Add a window size change listener to ensure the divider position adapts to window size changes\n\t\taddComponentListener(new java.awt.event.ComponentAdapter() {\n\t\t\t@Override\n\t\t\tpublic void componentResized(java.awt.event.ComponentEvent e) {\n\t\t\t\t// Keep the left-right ratio\n\t\t\t\tint width = splitPane.getWidth();\n\t\t\t\tif (width > 0) {\n\t\t\t\t\tint currentDividerLocation = splitPane.getDividerLocation();\n\t\t\t\t\tdouble ratio = (double) currentDividerLocation / width;\n\n\t\t\t\t\t// If the ratio is too small or too large, reset to a reasonable value\n\t\t\t\t\tif (ratio < 0.2 || ratio > 0.5) {\n\t\t\t\t\t\tsplitPane.setDividerLocation((int) (width * 0.3)); // Use pixel values\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tprotected void openInit() {\n\t\tprepareUsageData(initialNode);\n\n\t\tlocalProgressPanel.setIndeterminate(true);\n\t\tlocalProgressPanel.setVisible(true);\n\t\tprogressInfoLabel.setText(NLS.str(\"search_dialog.tip_searching\"));\n\n\t\t// Load the usage of the root node\n\t\tloadNodeUsages(initialNode, rootNode);\n\t}\n\n\tprivate void loadNodeUsages(JNode node, DefaultMutableTreeNode treeNode) {\n\t\t// When loading starts, display the searching state\n\t\tlocalProgressPanel.setIndeterminate(true);\n\t\tlocalProgressPanel.setVisible(true);\n\t\tprogressInfoLabel.setText(NLS.str(\"search_dialog.tip_searching\"));\n\n\t\tmainWindow.getBackgroundExecutor().execute(NLS.str(\"progress.load\"),\n\t\t\t\t() -> collectUsageData(node, treeNode),\n\t\t\t\t(status) -> {\n\t\t\t\t\tif (status == TaskStatus.CANCEL_BY_MEMORY) {\n\t\t\t\t\t\tmainWindow.showHeapUsageBar();\n\t\t\t\t\t\tUiUtils.errorMessage(UsageDialogPlus.this, NLS.str(\"message.memoryLow\"));\n\t\t\t\t\t}\n\t\t\t\t\tlocalProgressPanel.setVisible(false);\n\t\t\t\t\tprogressInfoLabel.setText(NLS.str(\"usage_dialog_plus.search_complete\"));\n\t\t\t\t\t// Update the result information - always display the number of child nodes of the currently\n\t\t\t\t\t// selected node\n\t\t\t\t\tupdateResultsInfo(treeNode);\n\n\t\t\t\t\t// Expand the root node\n\t\t\t\t\tif (treeNode == rootNode) {\n\t\t\t\t\t\tusageTree.expandPath(new TreePath(rootNode.getPath()));\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\tprivate void updateResultsInfo(DefaultMutableTreeNode node) {\n\t\tif (node != null) {\n\t\t\tint childCount = node.getChildCount();\n\t\t\tresultsInfoLabel.setText(NLS.str(\"search_dialog.results_complete\", childCount));\n\t\t}\n\t}\n\n\tprivate int getTotalChildCount(DefaultMutableTreeNode node) {\n\t\tint count = node.getChildCount();\n\t\tfor (Enumeration<TreeNode> e = node.children(); e.hasMoreElements();) {\n\t\t\tDefaultMutableTreeNode child = (DefaultMutableTreeNode) e.nextElement();\n\t\t\tcount += getTotalChildCount(child);\n\t\t}\n\t\treturn count;\n\t}\n\n\tprivate void prepareUsageData(JNode node) {\n\t\tif (mainWindow.getSettings().isReplaceConsts() && node instanceof JField) {\n\t\t\tFieldNode fld = ((JField) node).getJavaField().getFieldNode();\n\t\t\tboolean constField = CollectConstValues.getFieldConstValue(fld) != null;\n\t\t\tif (constField && !fld.getAccessFlags().isPrivate()) {\n\t\t\t\t// run full decompilation to prepare for full code scan\n\t\t\t\tmainWindow.requestFullDecompilation();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void collectUsageData(JNode node, DefaultMutableTreeNode treeNode) {\n\t\tList<CodeNode> usageList = new ArrayList<>();\n\t\tbuildUsageQuery(node).forEach(\n\t\t\t\t(searchNode, useNodes) -> useNodes.stream()\n\t\t\t\t\t\t.map(JavaNode::getTopParentClass)\n\t\t\t\t\t\t.distinct()\n\t\t\t\t\t\t.forEach(u -> processUsage(searchNode, u, usageList)));\n\n\t\t// Sort and add to the tree node\n\t\tCollections.sort(usageList);\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\tfor (CodeNode codeNode : usageList) {\n\t\t\t\tDefaultMutableTreeNode usageTreeNode = new DefaultMutableTreeNode(codeNode);\n\t\t\t\ttreeModel.insertNodeInto(usageTreeNode, treeNode, treeNode.getChildCount());\n\t\t\t}\n\t\t\ttreeModel.nodeStructureChanged(treeNode);\n\t\t});\n\t}\n\n\tprivate Map<JavaNode, List<? extends JavaNode>> buildUsageQuery(JNode node) {\n\t\tMap<JavaNode, List<? extends JavaNode>> map = new HashMap<>();\n\t\tif (node instanceof JMethod) {\n\t\t\tJavaMethod javaMethod = ((JMethod) node).getJavaMethod();\n\t\t\tfor (JavaMethod mth : getMethodWithOverrides(javaMethod)) {\n\t\t\t\tmap.put(mth, mth.getUseIn());\n\t\t\t}\n\t\t\treturn map;\n\t\t}\n\t\tif (node instanceof JClass) {\n\t\t\tJavaClass javaCls = ((JClass) node).getCls();\n\t\t\tmap.put(javaCls, javaCls.getUseIn());\n\t\t\t// add constructors usage into class usage\n\t\t\tfor (JavaMethod javaMth : javaCls.getMethods()) {\n\t\t\t\tif (javaMth.isConstructor()) {\n\t\t\t\t\tmap.put(javaMth, javaMth.getUseIn());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn map;\n\t\t}\n\t\tif (node instanceof JField && mainWindow.getSettings().isReplaceConsts()) {\n\t\t\tFieldNode fld = ((JField) node).getJavaField().getFieldNode();\n\t\t\tboolean constField = CollectConstValues.getFieldConstValue(fld) != null;\n\t\t\tif (constField && !fld.getAccessFlags().isPrivate()) {\n\t\t\t\t// search all classes to collect usage of replaced constants\n\t\t\t\tmap.put(fld.getJavaNode(), mainWindow.getWrapper().getIncludedClasses());\n\t\t\t\treturn map;\n\t\t\t}\n\t\t}\n\t\tJavaNode javaNode = node.getJavaNode();\n\t\tmap.put(javaNode, javaNode.getUseIn());\n\t\treturn map;\n\t}\n\n\tprivate List<JavaMethod> getMethodWithOverrides(JavaMethod javaMethod) {\n\t\tList<JavaMethod> relatedMethods = javaMethod.getOverrideRelatedMethods();\n\t\tif (!relatedMethods.isEmpty()) {\n\t\t\treturn relatedMethods;\n\t\t}\n\t\treturn Collections.singletonList(javaMethod);\n\t}\n\n\tprivate void processUsage(JavaNode searchNode, JavaClass topUseClass, List<CodeNode> usageList) {\n\t\tICodeInfo codeInfo = topUseClass.getCodeInfo();\n\t\tList<Integer> usePositions = topUseClass.getUsePlacesFor(codeInfo, searchNode);\n\t\tif (usePositions.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tString code = codeInfo.getCodeStr();\n\t\tJadxWrapper wrapper = mainWindow.getWrapper();\n\t\tfor (int pos : usePositions) {\n\t\t\tString line = CodeUtils.getLineForPos(code, pos);\n\t\t\tif (line.startsWith(\"import \")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tJNodeCache nodeCache = getNodeCache();\n\t\t\tJavaNode enclosingNode = wrapper.getEnclosingNode(codeInfo, pos);\n\t\t\tJClass rootJCls = nodeCache.makeFrom(topUseClass);\n\t\t\tJNode usageJNode = enclosingNode == null ? rootJCls : nodeCache.makeFrom(enclosingNode);\n\n\t\t\t// Create CodeNode and add to list\n\t\t\tCodeNode codeNode = new CodeNode(rootJCls, usageJNode, line.trim(), pos);\n\t\t\tusageList.add(codeNode);\n\t\t}\n\t}\n\n\tprivate JNode getNodeFromCodeNode(CodeNode codeNode) {\n\t\tif (codeNode != null) {\n\t\t\ttry {\n\t\t\t\t// Try to get the actual node referenced by the CodeNode\n\t\t\t\tJavaNode javaNode = codeNode.getJavaNode();\n\t\t\t\tJNodeCache nodeCache = getNodeCache();\n\t\t\t\tJNode node = nodeCache.makeFrom(javaNode);\n\n\t\t\t\t// If it cannot be obtained directly, try to get it from jParent\n\t\t\t\tif (node == null) {\n\t\t\t\t\tnode = codeNode.getJParent();\n\t\t\t\t}\n\n\t\t\t\t// Record the log for debugging\n\t\t\t\tif (node != null) {\n\t\t\t\t\tLOG.debug(\"Converted CodeNode to {} of type {}\", node.getName(), node.getClass().getSimpleName());\n\t\t\t\t} else {\n\t\t\t\t\tLOG.debug(\"Failed to convert CodeNode: {}\", codeNode.getName());\n\t\t\t\t}\n\n\t\t\t\treturn node;\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Error converting CodeNode to JNode\", e);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate void showPopupMenu(MouseEvent e, JNode node, TreePath path) {\n\t\tif (node == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tJPopupMenu popup = new JPopupMenu();\n\n\t\t// Add the expand/load usage menu item\n\t\tDefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) path.getLastPathComponent();\n\t\tJMenuItem expandItem = new JMenuItem(NLS.str(\"usage_dialog_plus.expand_usages\"));\n\t\texpandItem.addActionListener(evt -> {\n\t\t\t// Expand the node and load the usage\n\t\t\tif (treeNode.getChildCount() == 0) {\n\t\t\t\tJNode nodeToUse = node;\n\t\t\t\t// If it is a CodeNode, first convert it to an actual JNode and then search for its usage\n\t\t\t\tif (node.getClass() == CodeNode.class) {\n\t\t\t\t\tnodeToUse = getNodeFromCodeNode((CodeNode) node);\n\t\t\t\t}\n\t\t\t\tif (nodeToUse != null) {\n\t\t\t\t\tloadNodeUsages(nodeToUse, treeNode);\n\t\t\t\t}\n\t\t\t}\n\t\t\tusageTree.expandPath(path);\n\t\t});\n\n\t\tJMenuItem jumpToItem = new JMenuItem(NLS.str(\"usage_dialog_plus.jump_to\"));\n\t\tjumpToItem.addActionListener(evt -> openItem(node));\n\n\t\tJMenuItem copyPathItem = new JMenuItem(NLS.str(\"usage_dialog_plus.copy_path\"));\n\t\tcopyPathItem.addActionListener(evt -> copyUsagePath(path));\n\n\t\tpopup.add(expandItem);\n\t\tpopup.addSeparator();\n\t\tpopup.add(jumpToItem);\n\t\tpopup.add(copyPathItem);\n\t\tpopup.show(e.getComponent(), e.getX(), e.getY());\n\t}\n\n\tprivate void copyUsagePath(TreePath path) {\n\t\tif (path != null) {\n\t\t\tStringBuilder pathBuilder = new StringBuilder();\n\t\t\tObject[] nodes = path.getPath();\n\n\t\t\t// Actually reverse the node order, from the leaf node (currently selected node) to the root node\n\t\t\tfor (int i = nodes.length - 1; i >= 0; i--) {\n\t\t\t\tDefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) nodes[i];\n\t\t\t\tObject userObject = treeNode.getUserObject();\n\n\t\t\t\tif (i < nodes.length - 1) {\n\t\t\t\t\tpathBuilder.append(\"\\n\");\n\t\t\t\t\t// Add indentation - reverse calculate the indentation level\n\t\t\t\t\tint indentLevel = nodes.length - 1 - i;\n\t\t\t\t\tfor (int j = 0; j < indentLevel; j++) {\n\t\t\t\t\t\tpathBuilder.append(\" \");\n\t\t\t\t\t}\n\t\t\t\t\tpathBuilder.append(\"-> \");\n\t\t\t\t}\n\n\t\t\t\t// Add node information\n\t\t\t\tif (userObject instanceof JNode) {\n\t\t\t\t\tpathBuilder.append(((JNode) userObject).getJavaNode().getCodeNodeRef().toString());\n\t\t\t\t} else if (userObject instanceof CodeNode) {\n\t\t\t\t\tCodeNode codeNode = (CodeNode) userObject;\n\t\t\t\t\tpathBuilder.append(codeNode.getJavaNode().getCodeNodeRef().toString());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Copy to clipboard\n\t\t\tUiUtils.copyToClipboard(pathBuilder.toString());\n\t\t}\n\t}\n\n\t@NotNull\n\t@Override\n\tprotected JPanel initButtonsPanel() {\n\t\tprogressPane = new ProgressPanel(mainWindow, false);\n\n\t\tJButton cancelButton = new JButton(NLS.str(\"search_dialog.cancel\"));\n\t\tcancelButton.addActionListener(event -> dispose());\n\t\tJButton openBtn = new JButton(NLS.str(\"search_dialog.open\"));\n\t\topenBtn.addActionListener(event -> openSelectedItem());\n\t\tgetRootPane().setDefaultButton(openBtn);\n\n\t\tJCheckBox cbKeepOpen = new JCheckBox(NLS.str(\"search_dialog.keep_open\"));\n\t\tcbKeepOpen.setSelected(mainWindow.getSettings().isKeepCommonDialogOpen());\n\t\tcbKeepOpen.addActionListener(e -> mainWindow.getSettings().saveKeepCommonDialogOpen(cbKeepOpen.isSelected()));\n\t\tcbKeepOpen.setAlignmentY(Component.CENTER_ALIGNMENT);\n\n\t\tJPanel buttonPane = new JPanel();\n\t\tbuttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));\n\t\tbuttonPane.add(cbKeepOpen);\n\t\tbuttonPane.add(Box.createRigidArea(new Dimension(15, 0)));\n\t\tbuttonPane.add(progressPane);\n\t\tbuttonPane.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tbuttonPane.add(Box.createHorizontalGlue());\n\t\tbuttonPane.add(openBtn);\n\t\tbuttonPane.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tbuttonPane.add(cancelButton);\n\t\treturn buttonPane;\n\t}\n\n\t@Override\n\tprotected void openSelectedItem() {\n\t\t// Get the currently selected node\n\t\tJNode node = getSelectedNode();\n\t\tif (node == null) {\n\t\t\treturn;\n\t\t}\n\t\topenItem(node);\n\t}\n\n\t@Override\n\tprotected void loadFinished() {\n\t\t// The tree loading is already handled\n\t}\n\n\t@Override\n\tprotected void loadStart() {\n\t\t// The tree loading is already handled\n\t}\n\n\t@Nullable\n\tprivate JNode getSelectedNode() {\n\t\ttry {\n\t\t\tDefaultMutableTreeNode node = (DefaultMutableTreeNode) usageTree.getLastSelectedPathComponent();\n\t\t\tif (node == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tObject userObject = node.getUserObject();\n\t\t\tif (userObject instanceof JNode) {\n\t\t\t\treturn (JNode) userObject;\n\t\t\t} else if (userObject instanceof CodeNode) {\n\t\t\t\tCodeNode codeNode = (CodeNode) userObject;\n\t\t\t\treturn getNodeFromCodeNode(codeNode);\n\t\t\t}\n\t\t\treturn null;\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to get selected node\", e);\n\t\t\treturn null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/export/ExportProjectDialog.java",
    "content": "package jadx.gui.ui.export;\n\nimport java.awt.BorderLayout;\nimport java.awt.Dimension;\nimport java.awt.event.ItemEvent;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JComboBox;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JTextField;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.export.ExportGradle;\nimport jadx.core.export.ExportGradleType;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.dialog.CommonDialog;\nimport jadx.gui.ui.filedialog.FileDialogWrapper;\nimport jadx.gui.ui.filedialog.FileOpenMode;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.TextStandardActions;\nimport jadx.gui.utils.ui.DocumentUpdateListener;\n\npublic class ExportProjectDialog extends CommonDialog {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ExportProjectDialog.class);\n\n\tprivate final ExportProjectProperties exportProjectProperties = new ExportProjectProperties();\n\tprivate final Consumer<ExportProjectProperties> exportListener;\n\n\tpublic ExportProjectDialog(MainWindow mainWindow, Consumer<ExportProjectProperties> exportListener) {\n\t\tsuper(mainWindow);\n\t\tthis.exportListener = exportListener;\n\t\tinitUI();\n\t}\n\n\tprivate void initUI() {\n\t\tJPanel contentPanel = new JPanel();\n\t\tcontentPanel.setLayout(new BorderLayout(5, 5));\n\t\tcontentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));\n\t\tcontentPanel.add(makeContentPane(), BorderLayout.PAGE_START);\n\t\tcontentPanel.add(initButtonsPanel(), BorderLayout.PAGE_END);\n\t\tgetContentPane().add(contentPanel);\n\n\t\tsetTitle(NLS.str(\"export_dialog.title\"));\n\t\tcommonWindowInit();\n\t}\n\n\tprivate JPanel makeContentPane() {\n\t\tJLabel pathLbl = new JLabel(NLS.str(\"export_dialog.save_path\"));\n\t\tJTextField pathField = new JTextField();\n\t\tpathField.getDocument().addDocumentListener(new DocumentUpdateListener(ev -> setExportProjectPath(pathField)));\n\t\tpathField.setText(mainWindow.getSettings().getLastSaveFilePath().toString());\n\t\tTextStandardActions.attach(pathField);\n\n\t\tJButton browseButton = makeEditorBrowseButton(pathField);\n\n\t\tJCheckBox resourceDecode = new JCheckBox(NLS.str(\"preferences.skipResourcesDecode\"));\n\t\tresourceDecode.setSelected(mainWindow.getSettings().isSkipResources());\n\t\tresourceDecode.addItemListener(e -> {\n\t\t\texportProjectProperties.setSkipResources(e.getStateChange() == ItemEvent.SELECTED);\n\t\t});\n\n\t\tJCheckBox skipSources = new JCheckBox(NLS.str(\"preferences.skipSourcesDecode\"));\n\t\tskipSources.setSelected(mainWindow.getSettings().isSkipSources());\n\t\tskipSources.addItemListener(e -> {\n\t\t\texportProjectProperties.setSkipSources(e.getStateChange() == ItemEvent.SELECTED);\n\t\t});\n\n\t\tJLabel exportTypeLbl = new JLabel(NLS.str(\"export_dialog.export_gradle_type\"));\n\t\tJComboBox<ExportGradleType> exportTypeComboBox = new JComboBox<>(ExportGradleType.values());\n\t\texportTypeLbl.setLabelFor(exportTypeComboBox);\n\t\tExportGradleType initialExportType = getExportGradleType();\n\t\texportProjectProperties.setExportGradleType(initialExportType);\n\t\texportTypeComboBox.setSelectedItem(initialExportType);\n\t\texportTypeComboBox.addItemListener(e -> {\n\t\t\texportProjectProperties.setExportGradleType((ExportGradleType) e.getItem());\n\t\t});\n\t\texportTypeComboBox.setEnabled(false);\n\n\t\tJCheckBox exportAsGradleProject = new JCheckBox(NLS.str(\"export_dialog.export_gradle\"));\n\t\texportAsGradleProject.addItemListener(e -> {\n\t\t\tboolean enableGradle = e.getStateChange() == ItemEvent.SELECTED;\n\t\t\texportProjectProperties.setAsGradleMode(enableGradle);\n\t\t\texportTypeComboBox.setEnabled(enableGradle);\n\t\t\tresourceDecode.setEnabled(!enableGradle);\n\t\t\tskipSources.setEnabled(!enableGradle);\n\t\t});\n\n\t\tJPanel pathPanel = new JPanel();\n\t\tpathPanel.setLayout(new BoxLayout(pathPanel, BoxLayout.LINE_AXIS));\n\t\tpathPanel.setAlignmentX(LEFT_ALIGNMENT);\n\t\tpathPanel.add(pathLbl);\n\t\tpathPanel.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tpathPanel.add(pathField);\n\t\tpathPanel.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tpathPanel.add(browseButton);\n\n\t\tJPanel typePanel = new JPanel();\n\t\ttypePanel.setLayout(new BoxLayout(typePanel, BoxLayout.LINE_AXIS));\n\t\ttypePanel.setAlignmentX(LEFT_ALIGNMENT);\n\t\ttypePanel.add(Box.createRigidArea(new Dimension(20, 0)));\n\t\ttypePanel.add(exportTypeLbl);\n\t\ttypePanel.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\ttypePanel.add(exportTypeComboBox);\n\t\ttypePanel.add(Box.createHorizontalGlue());\n\n\t\tJPanel exportOptionsPanel = new JPanel();\n\t\texportOptionsPanel.setBorder(BorderFactory.createTitledBorder(NLS.str(\"export_dialog.export_options\")));\n\t\texportOptionsPanel.setLayout(new BoxLayout(exportOptionsPanel, BoxLayout.PAGE_AXIS));\n\t\texportOptionsPanel.add(exportAsGradleProject);\n\t\texportOptionsPanel.add(typePanel);\n\t\texportOptionsPanel.add(resourceDecode);\n\t\texportOptionsPanel.add(skipSources);\n\n\t\tJPanel mainPanel = new JPanel();\n\t\tmainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS));\n\t\tmainPanel.add(pathPanel);\n\t\tmainPanel.add(Box.createRigidArea(new Dimension(0, 10)));\n\t\tmainPanel.add(exportOptionsPanel);\n\t\treturn mainPanel;\n\t}\n\n\tprivate ExportGradleType getExportGradleType() {\n\t\ttry {\n\t\t\tJadxWrapper wrapper = mainWindow.getWrapper();\n\t\t\treturn ExportGradle.detectExportType(wrapper.getRootNode(), wrapper.getResources());\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to detect export type\", e);\n\t\t\treturn ExportGradleType.AUTO;\n\t\t}\n\t}\n\n\tprivate void setExportProjectPath(JTextField field) {\n\t\tString path = field.getText();\n\t\tif (!path.isEmpty()) {\n\t\t\texportProjectProperties.setExportPath(field.getText());\n\t\t}\n\t}\n\n\tprotected JPanel initButtonsPanel() {\n\t\tJButton cancelButton = new JButton(NLS.str(\"common_dialog.cancel\"));\n\t\tcancelButton.addActionListener(event -> dispose());\n\n\t\tJButton exportProjectButton = new JButton(NLS.str(\"common_dialog.ok\"));\n\t\texportProjectButton.addActionListener(event -> exportProject());\n\t\tgetRootPane().setDefaultButton(exportProjectButton);\n\n\t\tJPanel buttonPane = new JPanel();\n\t\tbuttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));\n\t\tbuttonPane.add(Box.createHorizontalGlue());\n\t\tbuttonPane.add(exportProjectButton);\n\t\tbuttonPane.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tbuttonPane.add(cancelButton);\n\t\treturn buttonPane;\n\t}\n\n\tprivate JButton makeEditorBrowseButton(final JTextField textField) {\n\t\tJButton button = new JButton(NLS.str(\"export_dialog.browse\"));\n\t\tbutton.addActionListener(e -> {\n\t\t\tFileDialogWrapper fileDialog = new FileDialogWrapper(mainWindow, FileOpenMode.EXPORT);\n\t\t\tmainWindow.getSettings().setLastSaveFilePath(fileDialog.getCurrentDir());\n\t\t\tList<Path> saveDirs = fileDialog.show();\n\t\t\tif (saveDirs.isEmpty()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString path = saveDirs.get(0).toString();\n\t\t\ttextField.setText(path);\n\t\t});\n\t\treturn button;\n\t}\n\n\tprivate void exportProject() {\n\t\tString exportPathStr = exportProjectProperties.getExportPath();\n\t\tif (!validateAndMakeDir(exportPathStr)) {\n\t\t\tJOptionPane.showMessageDialog(this, NLS.str(\"message.enter_valid_path\"),\n\t\t\t\t\tNLS.str(\"message.errorTitle\"), JOptionPane.WARNING_MESSAGE);\n\t\t\treturn;\n\t\t}\n\t\tmainWindow.getSettings().setLastSaveFilePath(Path.of(exportPathStr));\n\t\tLOG.debug(\"Export properties: {}\", exportProjectProperties);\n\t\texportListener.accept(exportProjectProperties);\n\t\tdispose();\n\t}\n\n\tprivate static boolean validateAndMakeDir(String exportPath) {\n\t\tif (exportPath == null || exportPath.isBlank()) {\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tPath path = Path.of(exportPath);\n\t\t\tif (Files.isRegularFile(path)) {\n\t\t\t\t// dir exists as a file\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tFileUtils.makeDirs(path);\n\t\t\treturn true;\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Export path validate error, path string:{}\", exportPath, e);\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/export/ExportProjectProperties.java",
    "content": "package jadx.gui.ui.export;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.export.ExportGradleType;\n\npublic class ExportProjectProperties {\n\tprivate boolean skipSources;\n\tprivate boolean skipResources;\n\tprivate boolean asGradleMode;\n\tprivate @Nullable ExportGradleType exportGradleType;\n\tprivate String exportPath;\n\n\tpublic boolean isSkipSources() {\n\t\treturn skipSources;\n\t}\n\n\tpublic void setSkipSources(boolean skipSources) {\n\t\tthis.skipSources = skipSources;\n\t}\n\n\tpublic boolean isSkipResources() {\n\t\treturn skipResources;\n\t}\n\n\tpublic void setSkipResources(boolean skipResources) {\n\t\tthis.skipResources = skipResources;\n\t}\n\n\tpublic boolean isAsGradleMode() {\n\t\treturn asGradleMode;\n\t}\n\n\tpublic void setAsGradleMode(boolean asGradleMode) {\n\t\tthis.asGradleMode = asGradleMode;\n\t}\n\n\tpublic @Nullable ExportGradleType getExportGradleType() {\n\t\treturn exportGradleType;\n\t}\n\n\tpublic void setExportGradleType(@Nullable ExportGradleType exportGradleType) {\n\t\tthis.exportGradleType = exportGradleType;\n\t}\n\n\tpublic String getExportPath() {\n\t\treturn exportPath;\n\t}\n\n\tpublic void setExportPath(String exportPath) {\n\t\tthis.exportPath = exportPath;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ExportProjectProperties{exportPath='\" + exportPath + '\\''\n\t\t\t\t+ \", asGradleMode=\" + asGradleMode\n\t\t\t\t+ \", exportGradleType=\" + exportGradleType\n\t\t\t\t+ \", skipSources=\" + skipSources\n\t\t\t\t+ \", skipResources=\" + skipResources\n\t\t\t\t+ '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/filedialog/CustomFileChooser.java",
    "content": "package jadx.gui.ui.filedialog;\n\nimport java.awt.Component;\nimport java.awt.HeadlessException;\nimport java.awt.event.WindowAdapter;\nimport java.awt.event.WindowEvent;\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport javax.swing.JDialog;\nimport javax.swing.JFileChooser;\nimport javax.swing.JOptionPane;\nimport javax.swing.UIManager;\n\nimport jadx.api.plugins.utils.CommonFileUtils;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\n\nclass CustomFileChooser extends JFileChooser {\n\n\tstatic {\n\t\t// disable left shortcut panel, can crush in \"Win32ShellFolderManager2.getNetwork()\" or similar call\n\t\tUIManager.put(\"FileChooser.noPlacesBar\", Boolean.TRUE);\n\t}\n\n\tprivate final FileDialogWrapper data;\n\n\tpublic CustomFileChooser(FileDialogWrapper data) {\n\t\tsuper(data.getCurrentDir() == null ? CommonFileUtils.CWD : data.getCurrentDir().toFile());\n\t\tputClientProperty(\"FileChooser.useShellFolder\", Boolean.FALSE);\n\t\tthis.data = data;\n\t}\n\n\tpublic List<Path> showDialog() {\n\t\tsetToolTipText(data.getTitle());\n\t\tsetFileSelectionMode(data.getSelectionMode());\n\t\tsetMultiSelectionEnabled(data.isOpen());\n\t\tsetAcceptAllFileFilterUsed(true);\n\t\tList<String> fileExtList = data.getFileExtList();\n\t\tif (Utils.notEmpty(fileExtList)) {\n\t\t\tList<String> validFileExtList = fileExtList.stream()\n\t\t\t\t\t.filter(StringUtils::notBlank)\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t\tif (Utils.notEmpty(validFileExtList)) {\n\t\t\t\tString description = NLS.str(\"file_dialog.supported_files\") + \": (\" + Utils.listToString(validFileExtList) + ')';\n\t\t\t\tsetFileFilter(new FileNameMultiExtensionFilter(description, validFileExtList.toArray(new String[0])));\n\t\t\t}\n\t\t}\n\t\tif (data.getSelectedFile() != null) {\n\t\t\tsetSelectedFile(data.getSelectedFile().toFile());\n\t\t}\n\t\tMainWindow mainWindow = data.getMainWindow();\n\t\tint ret = data.isOpen() ? showOpenDialog(mainWindow) : showSaveDialog(mainWindow);\n\t\tif (ret != JFileChooser.APPROVE_OPTION) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tdata.setCurrentDir(getCurrentDirectory().toPath());\n\t\tFile[] selectedFiles = getSelectedFiles();\n\t\tif (selectedFiles.length != 0) {\n\t\t\treturn FileUtils.toPathsWithTrim(selectedFiles);\n\t\t}\n\t\tFile chosenFile = getSelectedFile();\n\t\tif (chosenFile != null) {\n\t\t\treturn Collections.singletonList(FileUtils.toPathWithTrim(chosenFile));\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\t@Override\n\tprotected JDialog createDialog(Component parent) throws HeadlessException {\n\t\tJDialog dialog = super.createDialog(parent);\n\t\tdialog.setTitle(data.getTitle());\n\t\tdialog.setLocationRelativeTo(null);\n\t\tdata.getMainWindow().getSettings().loadWindowPos(dialog);\n\t\tdialog.addWindowListener(new WindowAdapter() {\n\t\t\t@Override\n\t\t\tpublic void windowClosed(WindowEvent e) {\n\t\t\t\tdata.getMainWindow().getSettings().saveWindowPos(dialog);\n\t\t\t\tsuper.windowClosed(e);\n\t\t\t}\n\t\t});\n\t\treturn dialog;\n\t}\n\n\t@Override\n\tpublic void approveSelection() {\n\t\tif (data.getSelectionMode() == FILES_AND_DIRECTORIES) {\n\t\t\tFile currentFile = getSelectedFile();\n\t\t\tif (currentFile.isDirectory()) {\n\t\t\t\tint option = JOptionPane.showConfirmDialog(\n\t\t\t\t\t\tdata.getMainWindow(),\n\t\t\t\t\t\tNLS.str(\"file_dialog.load_dir_confirm\") + \"\\n \" + currentFile,\n\t\t\t\t\t\tNLS.str(\"file_dialog.load_dir_title\"),\n\t\t\t\t\t\tJOptionPane.YES_NO_OPTION);\n\t\t\t\tif (option != JOptionPane.YES_OPTION) {\n\t\t\t\t\tthis.setCurrentDirectory(currentFile);\n\t\t\t\t\tthis.updateUI();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tsuper.approveSelection();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/filedialog/CustomFileDialog.java",
    "content": "package jadx.gui.ui.filedialog;\n\nimport java.awt.FileDialog;\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.files.FileUtils;\n\nclass CustomFileDialog {\n\n\tprivate final FileDialogWrapper data;\n\n\tpublic CustomFileDialog(FileDialogWrapper data) {\n\t\tthis.data = data;\n\t}\n\n\tpublic List<Path> showDialog() {\n\t\tFileDialog fileDialog = new FileDialog(data.getMainWindow(), data.getTitle());\n\t\tfileDialog.setMode(data.isOpen() ? FileDialog.LOAD : FileDialog.SAVE);\n\t\tfileDialog.setMultipleMode(true);\n\t\tList<String> fileExtList = data.getFileExtList();\n\t\tif (Utils.notEmpty(fileExtList)) {\n\t\t\tfileDialog.setFilenameFilter((dir, name) -> ListUtils.anyMatch(fileExtList, name::endsWith));\n\t\t}\n\t\tif (data.getSelectedFile() != null) {\n\t\t\tfileDialog.setFile(data.getSelectedFile().toAbsolutePath().toString());\n\t\t}\n\t\tif (data.getCurrentDir() != null) {\n\t\t\tfileDialog.setDirectory(data.getCurrentDir().toAbsolutePath().toString());\n\t\t}\n\t\tfileDialog.setVisible(true);\n\t\tFile[] selectedFiles = fileDialog.getFiles();\n\t\tif (!Utils.isEmpty(selectedFiles)) {\n\t\t\tdata.setCurrentDir(Paths.get(fileDialog.getDirectory()));\n\t\t\treturn FileUtils.toPathsWithTrim(selectedFiles);\n\t\t}\n\t\tif (fileDialog.getFile() != null) {\n\t\t\treturn Collections.singletonList(FileUtils.toPathWithTrim(fileDialog.getFile()));\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/filedialog/FileDialogWrapper.java",
    "content": "package jadx.gui.ui.filedialog;\n\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.swing.JFileChooser;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.gui.settings.JadxProject;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\n\npublic class FileDialogWrapper {\n\n\tprivate static final List<String> OPEN_FILES_EXTS = Arrays.asList(\n\t\t\t\"apk\", \"dex\", \"jar\", \"class\", \"smali\", \"zip\", \"aar\", \"arsc\", \"jadx.kts\", \"xapk\", \"apkm\", \"apks\");\n\n\tprivate final MainWindow mainWindow;\n\n\tprivate boolean isOpen;\n\tprivate String title;\n\tprivate List<String> fileExtList = new ArrayList<>();\n\tprivate int selectionMode = JFileChooser.FILES_AND_DIRECTORIES;\n\tprivate @Nullable Path currentDir;\n\tprivate @Nullable Path selectedFile;\n\n\tpublic FileDialogWrapper(MainWindow mainWindow, FileOpenMode mode) {\n\t\tthis.mainWindow = mainWindow;\n\t\tinitForMode(mode);\n\t}\n\n\tpublic void setTitle(String title) {\n\t\tthis.title = title;\n\t}\n\n\tpublic void setFileExtList(List<String> fileExtList) {\n\t\tthis.fileExtList = fileExtList;\n\t}\n\n\tpublic void setSelectionMode(int selectionMode) {\n\t\tthis.selectionMode = selectionMode;\n\t}\n\n\tpublic void setSelectedFile(Path path) {\n\t\tthis.selectedFile = path;\n\t}\n\n\tpublic void setCurrentDir(Path currentDir) {\n\t\tthis.currentDir = currentDir;\n\t}\n\n\tpublic List<Path> show() {\n\t\tif (mainWindow.getSettings().isUseAlternativeFileDialog()) {\n\t\t\treturn new CustomFileDialog(this).showDialog();\n\t\t} else {\n\t\t\treturn new CustomFileChooser(this).showDialog();\n\t\t}\n\t}\n\n\tprivate void initForMode(FileOpenMode mode) {\n\t\tswitch (mode) {\n\t\t\tcase OPEN_PROJECT:\n\t\t\t\ttitle = NLS.str(\"file.open_title\");\n\t\t\t\tfileExtList = Collections.singletonList(JadxProject.PROJECT_EXTENSION);\n\t\t\t\tselectionMode = JFileChooser.FILES_AND_DIRECTORIES;\n\t\t\t\tcurrentDir = mainWindow.getSettings().getLastOpenFilePath();\n\t\t\t\tisOpen = true;\n\t\t\t\tbreak;\n\n\t\t\tcase OPEN:\n\t\t\t\ttitle = NLS.str(\"file.open_title\");\n\t\t\t\tfileExtList = new ArrayList<>(OPEN_FILES_EXTS);\n\t\t\t\tfileExtList.add(JadxProject.PROJECT_EXTENSION);\n\t\t\t\tfileExtList.add(\"aab\");\n\t\t\t\tselectionMode = JFileChooser.FILES_AND_DIRECTORIES;\n\t\t\t\tcurrentDir = mainWindow.getSettings().getLastOpenFilePath();\n\t\t\t\tisOpen = true;\n\t\t\t\tbreak;\n\n\t\t\tcase ADD:\n\t\t\t\ttitle = NLS.str(\"file.add_files_action\");\n\t\t\t\tfileExtList = new ArrayList<>(OPEN_FILES_EXTS);\n\t\t\t\tfileExtList.add(\"aab\");\n\t\t\t\tselectionMode = JFileChooser.FILES_AND_DIRECTORIES;\n\t\t\t\tcurrentDir = mainWindow.getSettings().getLastOpenFilePath();\n\t\t\t\tisOpen = true;\n\t\t\t\tbreak;\n\n\t\t\tcase SAVE_PROJECT:\n\t\t\t\ttitle = NLS.str(\"file.save_project\");\n\t\t\t\tfileExtList = Collections.singletonList(JadxProject.PROJECT_EXTENSION);\n\t\t\t\tselectionMode = JFileChooser.FILES_ONLY;\n\t\t\t\tcurrentDir = mainWindow.getSettings().getLastSaveFilePath();\n\t\t\t\tisOpen = false;\n\t\t\t\tbreak;\n\n\t\t\tcase EXPORT:\n\t\t\t\ttitle = NLS.str(\"file.save_all_msg\");\n\t\t\t\tfileExtList = Collections.emptyList();\n\t\t\t\tselectionMode = JFileChooser.DIRECTORIES_ONLY;\n\t\t\t\tcurrentDir = mainWindow.getSettings().getLastSaveFilePath();\n\t\t\t\tisOpen = false;\n\t\t\t\tbreak;\n\n\t\t\tcase CUSTOM_SAVE:\n\t\t\t\tisOpen = false;\n\t\t\t\tcurrentDir = mainWindow.getSettings().getLastSaveFilePath();\n\t\t\t\tbreak;\n\n\t\t\tcase CUSTOM_OPEN:\n\t\t\t\tisOpen = true;\n\t\t\t\tcurrentDir = mainWindow.getSettings().getLastOpenFilePath();\n\t\t\t\tbreak;\n\n\t\t\tcase EXPORT_NODE:\n\t\t\t\tisOpen = false;\n\t\t\t\ttitle = NLS.str(\"file.export_node\");\n\t\t\t\tcurrentDir = mainWindow.getSettings().getLastSaveFilePath();\n\t\t\t\tselectionMode = JFileChooser.FILES_ONLY;\n\t\t\t\tbreak;\n\n\t\t\tcase EXPORT_NODE_FOLDER:\n\t\t\t\tisOpen = true;\n\t\t\t\ttitle = NLS.str(\"file.save_all_msg\");\n\t\t\t\tcurrentDir = mainWindow.getSettings().getLastSaveFilePath();\n\t\t\t\tselectionMode = JFileChooser.DIRECTORIES_ONLY;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tpublic Path getCurrentDir() {\n\t\treturn currentDir;\n\t}\n\n\tpublic MainWindow getMainWindow() {\n\t\treturn mainWindow;\n\t}\n\n\tpublic boolean isOpen() {\n\t\treturn isOpen;\n\t}\n\n\tpublic String getTitle() {\n\t\treturn title;\n\t}\n\n\tpublic List<String> getFileExtList() {\n\t\treturn fileExtList;\n\t}\n\n\tpublic int getSelectionMode() {\n\t\treturn selectionMode;\n\t}\n\n\tpublic Path getSelectedFile() {\n\t\treturn selectedFile;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/filedialog/FileNameMultiExtensionFilter.java",
    "content": "package jadx.gui.ui.filedialog;\n\nimport java.io.File;\n\nimport javax.swing.filechooser.FileFilter;\nimport javax.swing.filechooser.FileNameExtensionFilter;\n\n/**\n * Custom file filter for filtering files with multiple extensions.\n * It overcomes the limitation of {@link FileNameExtensionFilter},\n * which treats only the last file extension split by dots as the\n * file extension, and does not support multiple extensions such as\n * {@code .jadx.kts}.\n */\nclass FileNameMultiExtensionFilter extends FileFilter {\n\tprivate final FileNameExtensionFilter delegate;\n\tprivate final String[] extensions;\n\n\tpublic FileNameMultiExtensionFilter(String description, String... extensions) {\n\t\tthis.delegate = new FileNameExtensionFilter(description, extensions[0]);\n\t\tthis.extensions = extensions;\n\t}\n\n\t@Override\n\tpublic boolean accept(File file) {\n\t\tif (file.isDirectory()) {\n\t\t\treturn true;\n\t\t}\n\t\tString fileName = file.getName();\n\t\tfor (String extension : extensions) {\n\t\t\tif (fileName.endsWith(extension)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String getDescription() {\n\t\treturn delegate.getDescription();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/filedialog/FileOpenMode.java",
    "content": "package jadx.gui.ui.filedialog;\n\npublic enum FileOpenMode {\n\tOPEN,\n\tOPEN_PROJECT,\n\tADD,\n\tSAVE_PROJECT,\n\tEXPORT,\n\tCUSTOM_SAVE,\n\tCUSTOM_OPEN,\n\tEXPORT_NODE,\n\tEXPORT_NODE_FOLDER,\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/hexviewer/BinEdCodeAreaAssessor.java",
    "content": "package jadx.gui.ui.hexviewer;\n\n/*\n * Copyright (C) ExBin Project\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * https://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport java.awt.Color;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.exbin.bined.CodeAreaSection;\nimport org.exbin.bined.highlight.swing.NonAsciiCodeAreaColorAssessor;\nimport org.exbin.bined.highlight.swing.NonprintablesCodeAreaAssessor;\nimport org.exbin.bined.highlight.swing.SearchCodeAreaColorAssessor;\nimport org.exbin.bined.swing.CodeAreaCharAssessor;\nimport org.exbin.bined.swing.CodeAreaColorAssessor;\nimport org.exbin.bined.swing.CodeAreaPaintState;\n\n/**\n * Color assessor for binary editor with registrable modifiers.\n *\n * @author ExBin Project (https://exbin.org)\n */\npublic class BinEdCodeAreaAssessor implements CodeAreaColorAssessor, CodeAreaCharAssessor {\n\n\tprivate final List<PositionColorModifier> priorityColorModifiers = new ArrayList<>();\n\tprivate final List<PositionColorModifier> colorModifiers = new ArrayList<>();\n\n\tprivate final CodeAreaColorAssessor parentColorAssessor;\n\tprivate final CodeAreaCharAssessor parentCharAssessor;\n\n\tpublic BinEdCodeAreaAssessor(CodeAreaColorAssessor parentColorAssessor, CodeAreaCharAssessor parentCharAssessor) {\n\t\tNonAsciiCodeAreaColorAssessor nonAsciiCodeAreaColorAssessor = new NonAsciiCodeAreaColorAssessor(parentColorAssessor);\n\t\tNonprintablesCodeAreaAssessor nonprintablesCodeAreaAssessor =\n\t\t\t\tnew NonprintablesCodeAreaAssessor(nonAsciiCodeAreaColorAssessor, parentCharAssessor);\n\t\tSearchCodeAreaColorAssessor searchCodeAreaColorAssessor = new SearchCodeAreaColorAssessor(nonprintablesCodeAreaAssessor);\n\t\tthis.parentColorAssessor = searchCodeAreaColorAssessor;\n\t\tthis.parentCharAssessor = nonprintablesCodeAreaAssessor;\n\t}\n\n\tpublic void addColorModifier(PositionColorModifier colorModifier) {\n\t\tcolorModifiers.add(colorModifier);\n\t}\n\n\tpublic void removeColorModifier(PositionColorModifier colorModifier) {\n\t\tcolorModifiers.remove(colorModifier);\n\t}\n\n\tpublic void addPriorityColorModifier(PositionColorModifier colorModifier) {\n\t\tpriorityColorModifiers.add(colorModifier);\n\t}\n\n\tpublic void removePriorityColorModifier(PositionColorModifier colorModifier) {\n\t\tpriorityColorModifiers.remove(colorModifier);\n\t}\n\n\t@Override\n\tpublic void startPaint(CodeAreaPaintState codeAreaPaintState) {\n\t\tfor (PositionColorModifier colorModifier : priorityColorModifiers) {\n\t\t\tcolorModifier.resetColors();\n\t\t}\n\n\t\tfor (PositionColorModifier colorModifier : colorModifiers) {\n\t\t\tcolorModifier.resetColors();\n\t\t}\n\n\t\tif (parentColorAssessor != null) {\n\t\t\tparentColorAssessor.startPaint(codeAreaPaintState);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Color getPositionBackgroundColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section,\n\t\t\tboolean inSelection) {\n\t\tfor (PositionColorModifier colorModifier : priorityColorModifiers) {\n\t\t\tColor positionBackgroundColor =\n\t\t\t\t\tcolorModifier.getPositionBackgroundColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);\n\t\t\tif (positionBackgroundColor != null) {\n\t\t\t\treturn positionBackgroundColor;\n\t\t\t}\n\t\t}\n\n\t\tif (!inSelection) {\n\t\t\tfor (PositionColorModifier colorModifier : colorModifiers) {\n\t\t\t\tColor positionBackgroundColor =\n\t\t\t\t\t\tcolorModifier.getPositionBackgroundColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);\n\t\t\t\tif (positionBackgroundColor != null) {\n\t\t\t\t\treturn positionBackgroundColor;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (parentColorAssessor != null) {\n\t\t\treturn parentColorAssessor.getPositionBackgroundColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Color getPositionTextColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section, boolean inSelection) {\n\t\tfor (PositionColorModifier colorModifier : priorityColorModifiers) {\n\t\t\tColor positionTextColor = colorModifier.getPositionTextColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);\n\t\t\tif (positionTextColor != null) {\n\t\t\t\treturn positionTextColor;\n\t\t\t}\n\t\t}\n\n\t\tif (!inSelection) {\n\t\t\tfor (PositionColorModifier colorModifier : colorModifiers) {\n\t\t\t\tColor positionTextColor = colorModifier.getPositionTextColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);\n\t\t\t\tif (positionTextColor != null) {\n\t\t\t\t\treturn positionTextColor;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (parentColorAssessor != null) {\n\t\t\treturn parentColorAssessor.getPositionTextColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic char getPreviewCharacter(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section) {\n\t\treturn parentCharAssessor != null ? parentCharAssessor.getPreviewCharacter(rowDataPosition, byteOnRow, charOnRow, section) : ' ';\n\t}\n\n\t@Override\n\tpublic char getPreviewCursorCharacter(long rowDataPosition, int byteOnRow, int charOnRow, byte[] cursorData, int cursorDataLength,\n\t\t\tCodeAreaSection section) {\n\t\treturn parentCharAssessor != null\n\t\t\t\t? parentCharAssessor.getPreviewCursorCharacter(rowDataPosition, byteOnRow, charOnRow, cursorData, cursorDataLength, section)\n\t\t\t\t: ' ';\n\t}\n\n\t@Override\n\tpublic Optional<CodeAreaCharAssessor> getParentCharAssessor() {\n\t\treturn Optional.ofNullable(parentCharAssessor);\n\t}\n\n\t@Override\n\tpublic Optional<CodeAreaColorAssessor> getParentColorAssessor() {\n\t\treturn Optional.ofNullable(parentColorAssessor);\n\t}\n\n\tpublic interface PositionColorModifier {\n\n\t\tColor getPositionBackgroundColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section, boolean inSelection);\n\n\t\tColor getPositionTextColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section, boolean inSelection);\n\n\t\tvoid resetColors();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/hexviewer/HexEditorHeader.java",
    "content": "package jadx.gui.ui.hexviewer;\n\nimport java.awt.Color;\nimport java.awt.Dimension;\nimport java.awt.FontMetrics;\nimport java.awt.Graphics;\nimport java.awt.Graphics2D;\nimport java.awt.Insets;\nimport java.awt.RenderingHints;\n\nimport javax.swing.JComponent;\nimport javax.swing.UIManager;\n\nimport org.exbin.bined.CodeAreaCaretListener;\nimport org.exbin.bined.CodeAreaSection;\nimport org.exbin.bined.DataChangedListener;\nimport org.exbin.bined.SelectionChangedListener;\nimport org.exbin.bined.SelectionRange;\nimport org.exbin.bined.basic.BasicCodeAreaSection;\nimport org.exbin.bined.swing.section.SectCodeArea;\n\npublic class HexEditorHeader extends JComponent {\n\tprivate static final long serialVersionUID = 1L;\n\tprivate static final String HEX_ALPHABET = \"0123456789ABCDEF\";\n\tprivate final SectCodeArea parent;\n\tprivate final DataChangedListener dataChangedListener = this::repaint;\n\tprivate final CodeAreaCaretListener caretMovedListener = caretPosition -> repaint();\n\tprivate final SelectionChangedListener selectionChangedListener = this::repaint;\n\n\tprivate Dimension minimumSize = null;\n\tprivate Dimension preferredSize = null;\n\n\tpublic HexEditorHeader(SectCodeArea parent) {\n\t\tthis.parent = parent;\n\t\tif (this.parent != null) {\n\t\t\tthis.parent.addCaretMovedListener(caretMovedListener);\n\t\t\tthis.parent.addSelectionChangedListener(selectionChangedListener);\n\t\t\tthis.parent.addDataChangedListener(dataChangedListener);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void addNotify() {\n\t\tsuper.addNotify();\n\t\tif (this.parent != null) {\n\t\t\tthis.parent.addCaretMovedListener(caretMovedListener);\n\t\t\tthis.parent.addSelectionChangedListener(selectionChangedListener);\n\t\t\tthis.parent.addDataChangedListener(dataChangedListener);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeNotify() {\n\t\tif (this.parent != null) {\n\t\t\tthis.parent.removeCaretMovedListener(caretMovedListener);\n\t\t\tthis.parent.removeSelectionChangedListener(selectionChangedListener);\n\t\t\tthis.parent.removeDataChangedListener(dataChangedListener);\n\t\t}\n\t\tsuper.removeNotify();\n\t}\n\n\t@Override\n\tpublic Dimension getMinimumSize() {\n\t\tif (minimumSize != null) {\n\t\t\treturn minimumSize;\n\t\t}\n\t\tInsets i = getInsets();\n\t\tFontMetrics fm = getFontMetrics(parent.getFont());\n\t\tif (fm == null) {\n\t\t\treturn new Dimension(100, 20); // Fallback\n\t\t}\n\t\tint ch = fm.getHeight() + 2; // Row height\n\t\tint cw = fm.stringWidth(HEX_ALPHABET) / 16; // Char width estimate\n\n\t\tString sampleText = \"Sel: 00000000:00000000 Len: 00000000/00000000 TXT UTF-8\";\n\t\tint minTextWidth = fm.stringWidth(sampleText);\n\t\tint minimumWidth = minTextWidth + cw * 5 + i.left + i.right;\n\n\t\tint minimumHeight = ch + 5 + i.top + i.bottom;\n\n\t\treturn new Dimension(minimumWidth, minimumHeight);\n\t}\n\n\t@Override\n\tpublic void setMinimumSize(Dimension minimumSize) {\n\t\tthis.minimumSize = minimumSize;\n\t\trevalidate();\n\t}\n\n\t@Override\n\tpublic Dimension getPreferredSize() {\n\t\tif (preferredSize != null) {\n\t\t\treturn preferredSize;\n\t\t}\n\t\tInsets i = getInsets();\n\t\tFontMetrics fm = getFontMetrics(parent.getFont());\n\t\tif (fm == null) {\n\t\t\treturn getMinimumSize(); // Fallback\n\t\t}\n\t\tint ch = fm.getHeight() + 2;\n\t\tint cw = fm.stringWidth(HEX_ALPHABET) / 16;\n\n\t\tString sampleText = \"Sel: 00000000:00000000 Len: 00000000/00000000 TXT UTF-8\";\n\t\tint preferredTextWidth = fm.stringWidth(sampleText);\n\t\tint preferredWidth = preferredTextWidth + cw * 10 + i.left + i.right;\n\t\tint preferredHeight = ch + 5 + i.top + i.bottom;\n\n\t\treturn new Dimension(preferredWidth, preferredHeight);\n\t}\n\n\t@Override\n\tpublic void setPreferredSize(Dimension preferredSize) {\n\t\tthis.preferredSize = preferredSize;\n\t\trevalidate();\n\t}\n\n\t@Override\n\tprotected void paintComponent(Graphics g) {\n\t\t// Standard Graphics2D setup\n\t\tif (g instanceof Graphics2D) {\n\t\t\tGraphics2D g2 = (Graphics2D) g;\n\t\t\tg2.setRenderingHint(\n\t\t\t\t\tRenderingHints.KEY_TEXT_ANTIALIASING,\n\t\t\t\t\tRenderingHints.VALUE_TEXT_ANTIALIAS_ON);\n\t\t}\n\n\t\t// Get insets, width, height\n\t\tInsets i = getInsets();\n\t\tint fw = getWidth();\n\t\tint fh = getHeight();\n\t\tint w = fw - i.left - i.right;\n\t\tint h = fh - i.top - i.bottom;\n\n\t\t// Get font metrics\n\t\tg.setFont(parent.getFont());\n\t\tFontMetrics fm = g.getFontMetrics();\n\t\tif (fm == null) {\n\t\t\treturn; // Cannot paint without font metrics\n\t\t}\n\t\tint ca = fm.getAscent() + 1; // Character ascent for baseline\n\t\tint ch = fm.getHeight() + 2; // Row height including padding\n\t\tint cw = fm.stringWidth(HEX_ALPHABET) / 16; // Character width estimate\n\t\tif (cw <= 0) {\n\t\t\tcw = 1; // Avoid division by zero or incorrect calculations\n\t\t}\n\n\t\t// Get colors and status from parent editor\n\t\tColor separatorForeground = UIManager.getColor(\"Separator.foreground\");\n\t\tColor themeBackground = UIManager.getColor(\"Panel.background\");\n\t\tColor themeForeground = UIManager.getColor(\"Panel.foreground\");\n\n\t\tSelectionRange selectionRange = parent.getSelection();\n\t\tlong ss = selectionRange.getStart();\n\t\tlong se = selectionRange.getEnd();\n\t\tlong sl = selectionRange.getLength();\n\t\tlong length = parent.getDataSize();\n\n\t\t// Vertical position for the text baseline (centered vertically)\n\t\tint ty = i.top + ((h - ch) / 2) + ca; // Calculate middle Y for the single line of text\n\n\t\t// Draw Background\n\t\tg.setColor(themeBackground);\n\t\tg.fillRect(i.left, i.top, w, h);\n\n\t\t// Draw Text Elements (Sel, Len, Status)\n\t\tg.setColor(themeForeground);\n\n\t\t// Start drawing position (adjust for left inset)\n\t\tint currentX = i.left + cw / 2; // Start with a small left padding\n\n\t\t// Selection Range (Sel: start:end\n\t\tString selLabel = \"Sel:\";\n\t\tg.drawString(selLabel, currentX, ty);\n\t\tcurrentX += fm.stringWidth(selLabel) + cw; // \"Sel:\" + space\n\n\t\tString sss = addressString(ss);\n\t\tg.drawString(sss, currentX, ty);\n\t\tcurrentX += fm.stringWidth(sss); // start\n\n\t\tString separator1 = \":\";\n\t\tg.drawString(separator1, currentX, ty);\n\t\tcurrentX += fm.stringWidth(separator1); // :\n\n\t\tString ses = addressString(se);\n\t\tg.drawString(ses, currentX, ty);\n\t\tcurrentX += fm.stringWidth(ses) + cw; // end + larger space\n\n\t\t// Draw Divider After Sel Range\n\t\tint dividerTopY = i.top;\n\t\tint dividerHeight = h;\n\t\tg.setColor(separatorForeground);\n\t\tg.fillRect(currentX, dividerTopY, 1, dividerHeight);\n\t\tcurrentX += cw; // Add larger space after the divider\n\n\t\t// Length Information (Len: selected/total-\n\t\tg.setColor(themeForeground);\n\t\tString lenLabel = \"Len:\";\n\t\tg.drawString(lenLabel, currentX, ty);\n\t\tcurrentX += fm.stringWidth(lenLabel) + cw; // \"Len:\" + space\n\n\t\tString sls = addressString(sl);\n\t\tg.drawString(sls, currentX, ty);\n\t\tcurrentX += fm.stringWidth(sls); // selected length\n\n\t\tString separator2 = \"/\";\n\t\tg.drawString(separator2, currentX, ty);\n\t\tcurrentX += fm.stringWidth(separator2);\n\n\t\tString ls = addressString(length);\n\t\tg.drawString(ls, currentX, ty);\n\t\tcurrentX += fm.stringWidth(ls) + cw; // total length + larger space\n\n\t\t// Draw Divider After Len Info\n\t\tg.setColor(separatorForeground);\n\t\tg.fillRect(currentX, dividerTopY, 1, dividerHeight);\n\t\tcurrentX += cw; // Add larger space after the divider\n\n\t\t// Status Indicators (TXT/HEX, RO/RW, OVR/INS, LE/BE, Charset)\n\t\tg.setColor(themeForeground); // Ensure color is set for text\n\n\t\t// Draw TXT/HEX status\n\t\tString statusTxtHex = \"HEX\";\n\t\tCodeAreaSection section = parent.getActiveSection();\n\t\tif (section == BasicCodeAreaSection.TEXT_PREVIEW) {\n\t\t\tstatusTxtHex = \"TXT\";\n\t\t}\n\n\t\tg.drawString(statusTxtHex, currentX, ty);\n\t\tcurrentX += fm.stringWidth(statusTxtHex) + cw; // TXT/HEX\n\n\t\t// Draw Divider After TXT/HEX\n\t\tg.setColor(separatorForeground);\n\t\tg.fillRect(currentX, dividerTopY, 1, dividerHeight);\n\t\tcurrentX += cw; // Add larger space after the divider\n\n\t\tg.setColor(themeForeground); // Restore text color\n\n\t\t// Draw Charset\n\t\tString statusCharset = parent.getCharset().name();\n\t\tg.drawString(statusCharset, currentX, ty);\n\t\t// No divider or space needed after the last element\n\n\t\t// Draw Bottom Border\n\t\tg.setColor(separatorForeground); // Use headerDivider color for the border\n\t\tg.fillRect(i.left, i.top + h - 1, w, 1);\n\n\t}\n\n\tpublic String addressString(long address) {\n\t\treturn String.format(\"%08X\", address);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/hexviewer/HexInspectorPanel.java",
    "content": "package jadx.gui.ui.hexviewer;\n\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.Insets;\nimport java.awt.event.ItemEvent;\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport javax.swing.JCheckBox;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JTextField;\n\nimport org.apache.commons.lang3.ArrayUtils;\n\npublic class HexInspectorPanel extends JPanel {\n\tprivate final List<ValueFormatter> formatters = new ArrayList<>();\n\n\tprivate byte[] bytes = null;\n\tprivate Integer offset = null;\n\tprivate boolean isLittleEndian;\n\tprivate int row = 0;\n\n\tpublic HexInspectorPanel() {\n\t\tsetLayout(new GridBagLayout());\n\t\taddValueFormat(\"Signed 8 bit\", 1, b -> Integer.toString(b.get()));\n\t\taddValueFormat(\"Unsigned 8 bit\", 1, b -> Integer.toString(b.get() & 0xFF));\n\t\taddValueFormat(\"Signed 16 bit\", 2, b -> Short.toString(b.getShort()));\n\t\taddValueFormat(\"Unsigned 16 bit\", 2, b -> Integer.toString(b.getShort() & 0xFFFF));\n\t\taddValueFormat(\"Float 32 bit\", 4, b -> Float.toString(b.getFloat()));\n\t\taddValueFormat(\"Signed 32 bit\", 4, b -> Integer.toString(b.getInt()));\n\t\taddValueFormat(\"Unsigned 32 bit\", 4, b -> Integer.toUnsignedString(b.getInt()));\n\t\taddValueFormat(\"Signed 64 bit\", 8, b -> Long.toString(b.getLong()));\n\t\taddValueFormat(\"Float 64 bit\", 8, b -> Double.toString(b.getDouble()));\n\t\taddValueFormat(\"Unsigned 64 bit\", 8, b -> Long.toUnsignedString(b.getLong()));\n\t\taddValueFormat(\"Hexadecimal\", 1, b -> Integer.toString(b.get(), 16));\n\t\taddValueFormat(\"Octal\", 1, b -> Integer.toString(b.get(), 8));\n\t\taddValueFormat(\"Binary\", 1, b -> Integer.toString(b.get(), 2));\n\n\t\tGridBagConstraints constraints;\n\t\tconstraints = getConstraints();\n\t\tconstraints.gridwidth = 2;\n\t\tJCheckBox littleEndianCheckBox = new JCheckBox(\"Little endian\", false);\n\t\tlittleEndianCheckBox.addItemListener(ev -> {\n\t\t\tisLittleEndian = ev.getStateChange() == ItemEvent.SELECTED;\n\t\t\treloadOffset();\n\t\t});\n\t\tadd(littleEndianCheckBox, constraints);\n\n\t\t// Workaround to force widgets to start from the top (otherwise centered)\n\t\tconstraints = getConstraints();\n\t\tconstraints.weighty = 1;\n\t\tadd(new JLabel(\" \"), constraints);\n\t}\n\n\tpublic void setOffset(int offset) {\n\t\tthis.offset = offset;\n\t\treloadOffset();\n\t}\n\n\tpublic void setBytes(byte[] bytes) {\n\t\tthis.bytes = bytes;\n\t}\n\n\tprivate void reloadOffset() {\n\t\tif (bytes == null || offset == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (int i = 0; i < formatters.size(); i++) {\n\t\t\tValueFormatter formatter = formatters.get(i);\n\t\t\tif (canDisplay(offset, formatter.dataSize)) {\n\t\t\t\tByteBuffer buffer = decodeByteArray(offset, formatter.dataSize);\n\t\t\t\tString value = formatter.function.apply(buffer);\n\t\t\t\t((JTextField) getComponent(i * 2 + 1)).setText(value);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate GridBagConstraints getConstraints() {\n\t\tGridBagConstraints constraints = new GridBagConstraints();\n\t\tconstraints.insets = new Insets(5, 5, 5, 5);\n\t\tconstraints.gridy = row;\n\t\trow++;\n\t\treturn constraints;\n\t}\n\n\tprivate void addValueFormat(String name, int dataSize, Function<ByteBuffer, String> formatter) {\n\t\tformatters.add(new ValueFormatter(dataSize, formatter));\n\n\t\tGridBagConstraints constraints = getConstraints();\n\t\tconstraints.gridx = 0;\n\t\tconstraints.anchor = GridBagConstraints.WEST;\n\t\tadd(new JLabel(name), constraints);\n\n\t\tconstraints.fill = GridBagConstraints.HORIZONTAL;\n\t\tconstraints.gridx = 1;\n\n\t\tJTextField textField = new JTextField();\n\t\ttextField.setEditable(false);\n\n\t\tadd(textField, constraints);\n\t}\n\n\tprivate boolean canDisplay(int offset, int size) {\n\t\treturn offset + size <= bytes.length;\n\t}\n\n\tprivate ByteBuffer decodeByteArray(int offset, int size) {\n\t\tbyte[] chunk = sliceBytes(offset, size);\n\t\tif (isLittleEndian) {\n\t\t\tArrayUtils.reverse(chunk);\n\t\t}\n\t\treturn ByteBuffer.wrap(chunk);\n\t}\n\n\tprivate byte[] sliceBytes(int offset, int size) {\n\t\tbyte[] slice = new byte[size];\n\t\tSystem.arraycopy(bytes, offset, slice, 0, size);\n\t\treturn slice;\n\t}\n\n\tprivate static class ValueFormatter {\n\t\tpublic final int dataSize;\n\t\tpublic final Function<ByteBuffer, String> function;\n\n\t\tpublic ValueFormatter(int dataSize, Function<ByteBuffer, String> function) {\n\t\t\tthis.dataSize = dataSize;\n\t\t\tthis.function = function;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/hexviewer/HexPreviewPanel.java",
    "content": "package jadx.gui.ui.hexviewer;\n\nimport java.awt.BorderLayout;\nimport java.awt.Color;\nimport java.awt.Component;\nimport java.awt.event.FocusEvent;\nimport java.awt.event.FocusListener;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Objects;\n\nimport javax.swing.JMenu;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPanel;\nimport javax.swing.JPopupMenu;\n\nimport org.exbin.auxiliary.binary_data.BinaryData;\nimport org.exbin.auxiliary.binary_data.array.ByteArrayEditableData;\nimport org.exbin.bined.CodeAreaCaretListener;\nimport org.exbin.bined.CodeAreaCaretPosition;\nimport org.exbin.bined.CodeAreaUtils;\nimport org.exbin.bined.CodeCharactersCase;\nimport org.exbin.bined.CodeType;\nimport org.exbin.bined.EditMode;\nimport org.exbin.bined.SelectionRange;\nimport org.exbin.bined.basic.BasicCodeAreaZone;\nimport org.exbin.bined.color.CodeAreaBasicColors;\nimport org.exbin.bined.highlight.swing.color.CodeAreaMatchColorType;\nimport org.exbin.bined.swing.CodeAreaPainter;\nimport org.exbin.bined.swing.basic.DefaultCodeAreaCommandHandler;\nimport org.exbin.bined.swing.capability.CharAssessorPainterCapable;\nimport org.exbin.bined.swing.capability.ColorAssessorPainterCapable;\nimport org.exbin.bined.swing.section.SectCodeArea;\nimport org.exbin.bined.swing.section.color.SectionCodeAreaColorProfile;\n\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic class HexPreviewPanel extends JPanel {\n\tprivate static final long serialVersionUID = 3261685857479120073L;\n\tprivate static final int CACHE_SIZE = 250;\n\n\tprivate final byte[] valuesCache = new byte[CACHE_SIZE];\n\tprivate final SectCodeArea hexCodeArea;\n\tprivate final SectionCodeAreaColorProfile defaultColors;\n\tprivate final HexEditorHeader header;\n\tprivate final HexSearchBar searchBar;\n\tprivate final HexInspectorPanel inspector;\n\n\tprivate JPopupMenu popupMenu;\n\tprivate JMenuItem cutAction;\n\tprivate JMenuItem copyAction;\n\tprivate JMenuItem copyHexAction;\n\tprivate JMenuItem copyStringAction;\n\tprivate JMenuItem pasteAction;\n\tprivate JMenuItem deleteAction;\n\tprivate JMenuItem selectAllAction;\n\tprivate JMenuItem copyOffsetItem;\n\tprivate BasicCodeAreaZone popupMenuPositionZone = BasicCodeAreaZone.UNKNOWN;\n\n\tpublic HexPreviewPanel(JadxSettings settings) {\n\t\thexCodeArea = new SectCodeArea();\n\t\thexCodeArea.setCodeFont(settings.getSmaliFont());\n\t\thexCodeArea.setEditMode(EditMode.READ_ONLY);\n\t\thexCodeArea.setCharset(StandardCharsets.UTF_8);\n\t\thexCodeArea.setComponentPopupMenu(new JPopupMenu() {\n\t\t\t@Override\n\t\t\tpublic void show(Component invoker, int x, int y) {\n\t\t\t\tpopupMenuPositionZone = hexCodeArea.getPainter().getPositionZone(x, y);\n\t\t\t\tcreatePopupMenu();\n\t\t\t\tif (popupMenu != null && popupMenuPositionZone != BasicCodeAreaZone.HEADER\n\t\t\t\t\t\t&& popupMenuPositionZone != BasicCodeAreaZone.ROW_POSITIONS) {\n\t\t\t\t\tupdatePopupActionStates();\n\t\t\t\t\tpopupMenu.show(invoker, x, y);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tinspector = new HexInspectorPanel();\n\t\tsearchBar = new HexSearchBar(hexCodeArea);\n\t\theader = new HexEditorHeader(hexCodeArea);\n\t\theader.setFont(settings.getUiFont());\n\n\t\tCodeAreaPainter painter = hexCodeArea.getPainter();\n\t\tdefaultColors = (SectionCodeAreaColorProfile) hexCodeArea.getColorsProfile();\n\n\t\thexCodeArea.setColorsProfile(getColorsProfile());\n\n\t\tBinEdCodeAreaAssessor codeAreaAssessor = new BinEdCodeAreaAssessor(((ColorAssessorPainterCapable) painter).getColorAssessor(),\n\t\t\t\t((CharAssessorPainterCapable) painter).getCharAssessor());\n\t\t((ColorAssessorPainterCapable) painter).setColorAssessor(codeAreaAssessor);\n\t\t((CharAssessorPainterCapable) painter).setCharAssessor(codeAreaAssessor);\n\n\t\tsetLayout(new BorderLayout());\n\t\tadd(searchBar, BorderLayout.PAGE_START);\n\t\tadd(hexCodeArea, BorderLayout.CENTER);\n\t\tadd(header, BorderLayout.PAGE_END);\n\t\tadd(inspector, BorderLayout.EAST);\n\n\t\tsetFocusable(true);\n\t\taddFocusListener(new FocusListener() {\n\t\t\t@Override\n\t\t\tpublic void focusGained(FocusEvent e) {\n\t\t\t\thexCodeArea.requestFocusInWindow();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void focusLost(FocusEvent e) {\n\n\t\t\t}\n\t\t});\n\t\tcreateActions();\n\t\tenableUpdate();\n\t}\n\n\tpublic SectionCodeAreaColorProfile getColorsProfile() {\n\t\tboolean isDarkTheme = UiUtils.isDarkTheme(Objects.requireNonNull(defaultColors.getColor(CodeAreaBasicColors.TEXT_BACKGROUND)));\n\t\tColor markAllHighlightColor = isDarkTheme ? Color.decode(\"#32593D\") : Color.decode(\"#ffc800\");\n\t\tColor editorSelectionBackground = Objects.requireNonNull(defaultColors.getColor(CodeAreaBasicColors.SELECTION_BACKGROUND));\n\t\tColor currentMatchColor = UiUtils.adjustBrightness(editorSelectionBackground, isDarkTheme ? 0.6f : 1.4f);\n\t\tdefaultColors.setColor(CodeAreaMatchColorType.MATCH_BACKGROUND, markAllHighlightColor);\n\t\tdefaultColors.setColor(CodeAreaMatchColorType.CURRENT_MATCH_BACKGROUND, currentMatchColor);\n\t\treturn defaultColors;\n\t}\n\n\tpublic boolean isDataLoaded() {\n\t\treturn !hexCodeArea.getContentData().isEmpty();\n\t}\n\n\tpublic void setData(byte[] data) {\n\t\tif (data != null) {\n\t\t\thexCodeArea.setContentData(new ByteArrayEditableData(data));\n\t\t\tinspector.setBytes(data);\n\t\t}\n\t}\n\n\tpublic void scrollToOffset(int pos) {\n\t\thexCodeArea.setSelection(pos, pos + 1);\n\t\thexCodeArea.setActiveCaretPosition(pos);\n\t\thexCodeArea.centerOnPosition(hexCodeArea.getActiveCaretPosition());\n\t}\n\n\tpublic void enableUpdate() {\n\t\tCodeAreaCaretListener caretMovedListener = (CodeAreaCaretPosition caretPosition) -> updateValues();\n\t\thexCodeArea.addCaretMovedListener(caretMovedListener);\n\t}\n\n\tprivate void updateValues() {\n\t\tCodeAreaCaretPosition caretPosition = hexCodeArea.getActiveCaretPosition();\n\t\tlong dataPosition = caretPosition.getDataPosition();\n\t\tlong dataSize = hexCodeArea.getDataSize();\n\n\t\tif (dataPosition < dataSize) {\n\t\t\tint availableData = dataSize - dataPosition >= CACHE_SIZE ? CACHE_SIZE : (int) (dataSize - dataPosition);\n\t\t\tBinaryData contentData = hexCodeArea.getContentData();\n\t\t\tcontentData.copyToArray(dataPosition, valuesCache, 0, availableData);\n\t\t\tif (availableData < CACHE_SIZE) {\n\t\t\t\tArrays.fill(valuesCache, availableData, CACHE_SIZE, (byte) 0);\n\t\t\t}\n\t\t}\n\n\t\tinspector.setOffset((int) dataPosition);\n\t}\n\n\tprivate void createActions() {\n\t\tcutAction = new JMenuItem(NLS.str(\"popup.cut\"));\n\t\tcutAction.addActionListener(e -> performCut());\n\n\t\tcopyAction = new JMenuItem(NLS.str(\"popup.copy\"));\n\t\tcopyAction.addActionListener(e -> performCopy());\n\n\t\tcopyHexAction = new JMenuItem(NLS.str(\"popup.copy_as_hex\"));\n\t\tcopyHexAction.addActionListener(e -> performCopyAsCode());\n\n\t\tcopyStringAction = new JMenuItem(NLS.str(\"popup.copy_as_string\"));\n\t\tcopyStringAction.addActionListener(e -> performCopy());\n\n\t\tpasteAction = new JMenuItem(NLS.str(\"popup.paste\"));\n\t\tpasteAction.addActionListener(e -> performPaste());\n\n\t\tdeleteAction = new JMenuItem(NLS.str(\"popup.delete\"));\n\t\tdeleteAction.addActionListener(e -> {\n\t\t\tif (!isEditable()) {\n\t\t\t\tperformDelete();\n\t\t\t}\n\t\t});\n\n\t\tselectAllAction = new JMenuItem(NLS.str(\"popup.select_all\"));\n\t\tselectAllAction.addActionListener(e -> performSelectAll());\n\n\t\tcopyOffsetItem = new JMenuItem(NLS.str(\"popup.copy_offset\"));\n\t\tcopyOffsetItem.addActionListener(e -> copyOffset());\n\t}\n\n\tprivate void createPopupMenu() {\n\t\tboolean isEditable = isEditable();\n\t\tpopupMenu = new JPopupMenu();\n\t\tpopupMenu.add(copyAction);\n\n\t\tif (isEditable) {\n\t\t\tpopupMenu.add(cutAction);\n\t\t\tpopupMenu.add(pasteAction);\n\t\t\tpopupMenu.add(deleteAction);\n\t\t\tpopupMenu.addSeparator();\n\t\t}\n\n\t\tJMenu copyMenu = new JMenu(NLS.str(\"popup.copy_as\"));\n\t\tcopyMenu.add(copyHexAction);\n\t\tcopyMenu.add(copyStringAction);\n\t\tpopupMenu.add(copyMenu);\n\t\tpopupMenu.add(copyOffsetItem);\n\t\tpopupMenu.add(selectAllAction);\n\t}\n\n\tprivate void updatePopupActionStates() {\n\n\t\tboolean selectionExists = isSelection();\n\t\tboolean isEditable = !isEditable();\n\n\t\tcutAction.setEnabled(isEditable && selectionExists);\n\t\tcopyAction.setEnabled(selectionExists);\n\t\tcopyHexAction.setEnabled(selectionExists);\n\t\tcopyStringAction.setEnabled(selectionExists);\n\t\tdeleteAction.setEnabled(isEditable && selectionExists);\n\n\t\tselectAllAction.setEnabled(hexCodeArea.getDataSize() > 0);\n\t}\n\n\tpublic SectCodeArea getEditor() {\n\t\treturn this.hexCodeArea;\n\t}\n\n\tpublic HexEditorHeader getHeader() {\n\t\treturn this.header;\n\t}\n\n\tpublic HexInspectorPanel getInspector() {\n\t\treturn this.inspector;\n\t}\n\n\tpublic HexSearchBar getSearchBar() {\n\t\treturn this.searchBar;\n\t}\n\n\tpublic void showSearchBar() {\n\t\tsearchBar.showAndFocus();\n\t}\n\n\tpublic void performCut() {\n\t\thexCodeArea.cut();\n\t}\n\n\tpublic void performCopy() {\n\t\thexCodeArea.copy();\n\t}\n\n\tpublic void performCopyAsCode() {\n\t\t((DefaultCodeAreaCommandHandler) hexCodeArea.getCommandHandler()).copyAsCode();\n\t}\n\n\tpublic void performPaste() {\n\t\thexCodeArea.paste();\n\t}\n\n\tpublic void performDelete() {\n\t\thexCodeArea.delete();\n\t}\n\n\tpublic void performSelectAll() {\n\t\thexCodeArea.selectAll();\n\t}\n\n\tpublic boolean isSelection() {\n\t\treturn hexCodeArea.hasSelection();\n\t}\n\n\tpublic boolean isEditable() {\n\t\treturn hexCodeArea.isEditable();\n\t}\n\n\tpublic boolean canPaste() {\n\t\treturn hexCodeArea.canPaste();\n\t}\n\n\tpublic static String getSelectionData(SectCodeArea core) {\n\t\tSelectionRange selection = core.getSelection();\n\t\tif (!selection.isEmpty()) {\n\t\t\tlong first = selection.getFirst();\n\t\t\tlong last = selection.getLast();\n\n\t\t\tBinaryData copy = core.getContentData().copy(first, last - first + 1);\n\n\t\t\tCodeType codeType = core.getCodeType();\n\t\t\tCodeCharactersCase charactersCase = core.getCodeCharactersCase();\n\n\t\t\tint charsPerByte = codeType.getMaxDigitsForByte() + 1;\n\t\t\tint textLength = (int) (copy.getDataSize() * charsPerByte);\n\t\t\tif (textLength > 0) {\n\t\t\t\ttextLength--;\n\t\t\t}\n\n\t\t\tchar[] targetData = new char[textLength];\n\t\t\tArrays.fill(targetData, ' ');\n\t\t\tfor (int i = 0; i < (int) copy.getDataSize(); i++) {\n\t\t\t\tCodeAreaUtils.byteToCharsCode(copy.getByte(i), codeType, targetData, i * charsPerByte, charactersCase);\n\t\t\t}\n\t\t\treturn new String(targetData);\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic void copyOffset() {\n\t\tString str = header.addressString(hexCodeArea.getSelection().getStart());\n\t\tUiUtils.copyToClipboard(str);\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/hexviewer/HexSearchBar.java",
    "content": "package jadx.gui.ui.hexviewer;\n\nimport java.awt.Color;\nimport java.awt.event.ActionListener;\nimport java.awt.event.KeyAdapter;\nimport java.awt.event.KeyEvent;\n\nimport javax.swing.JButton;\nimport javax.swing.JLabel;\nimport javax.swing.JTextField;\nimport javax.swing.JToggleButton;\nimport javax.swing.JToolBar;\nimport javax.swing.border.EmptyBorder;\n\nimport org.exbin.auxiliary.binary_data.array.ByteArrayEditableData;\nimport org.exbin.bined.CodeAreaUtils;\nimport org.exbin.bined.swing.section.SectCodeArea;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.formdev.flatlaf.FlatClientProperties;\n\nimport jadx.core.utils.StringUtils;\nimport jadx.gui.ui.hexviewer.search.BinarySearch;\nimport jadx.gui.ui.hexviewer.search.SearchCondition;\nimport jadx.gui.ui.hexviewer.search.SearchParameters;\nimport jadx.gui.ui.hexviewer.search.service.BinarySearchServiceImpl;\nimport jadx.gui.utils.HexUtils;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.TextStandardActions;\nimport jadx.gui.utils.UiUtils;\n\npublic class HexSearchBar extends JToolBar {\n\tprivate static final long serialVersionUID = 1836871286618633003L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(HexSearchBar.class);\n\tprivate final SectCodeArea hexCodeArea;\n\n\tprivate final JTextField searchField;\n\tprivate final JLabel resultCountLabel;\n\tprivate final JToggleButton markAllCB;\n\tprivate final JToggleButton findTypeCB;\n\tprivate final JToggleButton matchCaseCB;\n\tprivate final JButton nextMatchButton;\n\tprivate final JButton prevMatchButton;\n\n\tprivate Control control = null;\n\n\tpublic HexSearchBar(SectCodeArea textArea) {\n\t\thexCodeArea = textArea;\n\n\t\tJLabel findLabel = new JLabel(NLS.str(\"search.find\") + ':');\n\t\tadd(findLabel);\n\n\t\tsearchField = new JTextField(30);\n\t\tsearchField.putClientProperty(FlatClientProperties.TEXT_FIELD_SHOW_CLEAR_BUTTON, true);\n\t\tsearchField.addKeyListener(new KeyAdapter() {\n\t\t\t@Override\n\t\t\tpublic void keyReleased(KeyEvent e) {\n\t\t\t\tswitch (e.getKeyCode()) {\n\t\t\t\t\tcase KeyEvent.VK_ENTER:\n\t\t\t\t\t\t// skip\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase KeyEvent.VK_ESCAPE:\n\t\t\t\t\t\ttoggle();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tcontrol.performFind();\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tsearchField.addActionListener(e -> control.notifySearchChanging());\n\t\tTextStandardActions.attach(searchField);\n\t\tadd(searchField);\n\n\t\tActionListener searchSettingListener = e -> control.notifySearchChanged();\n\n\t\tresultCountLabel = new JLabel();\n\t\tresultCountLabel.setBorder(new EmptyBorder(0, 10, 0, 10));\n\t\tresultCountLabel.setForeground(Color.GRAY);\n\t\tadd(resultCountLabel);\n\n\t\tmatchCaseCB = new JToggleButton();\n\t\tmatchCaseCB.setIcon(Icons.ICON_MATCH);\n\t\tmatchCaseCB.setSelectedIcon(Icons.ICON_MATCH_SELECTED);\n\t\tmatchCaseCB.setToolTipText(NLS.str(\"search.match_case\"));\n\t\tmatchCaseCB.addActionListener(searchSettingListener);\n\t\tadd(matchCaseCB);\n\n\t\tfindTypeCB = new JToggleButton();\n\t\tfindTypeCB.setIcon(Icons.ICON_FIND_TYPE_TXT);\n\t\tfindTypeCB.setSelectedIcon(Icons.ICON_FIND_TYPE_HEX);\n\t\tif (findTypeCB.isSelected()) {\n\t\t\tfindTypeCB.setToolTipText(NLS.str(\"search.find_type_hex\"));\n\t\t} else {\n\t\t\tfindTypeCB.setToolTipText(NLS.str(\"search.find_type_text\"));\n\t\t}\n\t\tfindTypeCB.addActionListener(e -> {\n\t\t\tsearchField.setText(\"\");\n\t\t\tupdateFindStatus();\n\t\t\tcontrol.notifySearchChanged();\n\t\t});\n\t\tadd(findTypeCB);\n\n\t\tprevMatchButton = new JButton();\n\t\tprevMatchButton.setIcon(Icons.ICON_UP);\n\t\tprevMatchButton.setToolTipText(NLS.str(\"search.previous\"));\n\t\tprevMatchButton.addActionListener(e -> control.prevMatch());\n\t\tprevMatchButton.setBorderPainted(false);\n\t\tadd(prevMatchButton);\n\n\t\tnextMatchButton = new JButton();\n\t\tnextMatchButton.setIcon(Icons.ICON_DOWN);\n\t\tnextMatchButton.setToolTipText(NLS.str(\"search.next\"));\n\t\tnextMatchButton.addActionListener(e -> control.nextMatch());\n\t\tnextMatchButton.setBorderPainted(false);\n\t\tadd(nextMatchButton);\n\n\t\tmarkAllCB = new JToggleButton();\n\t\tmarkAllCB.setIcon(Icons.ICON_MARK);\n\t\tmarkAllCB.setSelectedIcon(Icons.ICON_MARK_SELECTED);\n\t\tmarkAllCB.setToolTipText(NLS.str(\"search.mark_all\"));\n\t\tmarkAllCB.setSelected(true);\n\t\tmarkAllCB.addActionListener(searchSettingListener);\n\t\tadd(markAllCB);\n\n\t\tJButton closeButton = new JButton();\n\t\tcloseButton.setIcon(Icons.ICON_CLOSE);\n\t\tcloseButton.addActionListener(e -> toggle());\n\t\tcloseButton.setBorderPainted(false);\n\t\tadd(closeButton);\n\n\t\tBinarySearch binarySearch = new BinarySearch(this);\n\t\tbinarySearch.setBinarySearchService(new BinarySearchServiceImpl(hexCodeArea));\n\t\tsetFloatable(false);\n\t\tsetVisible(false);\n\n\t}\n\n\t/*\n\t * Replicates IntelliJ's search bar behavior\n\t * 1.1. If the user has selected text, use that as the search text\n\t * 1.2. Otherwise, use the previous search text (or empty if none)\n\t * 2. Select all text in the search bar and give it focus\n\t */\n\tpublic void showAndFocus() {\n\t\tsetVisible(true);\n\n\t\tif (hexCodeArea.hasSelection()) {\n\t\t\tsearchField.setText(hexCodeArea.getActiveSection().toString());\n\t\t}\n\t\tString selectedText = HexPreviewPanel.getSelectionData(hexCodeArea);\n\t\tif (!StringUtils.isEmpty(selectedText)) {\n\t\t\tsearchField.setText(selectedText);\n\t\t\tmakeFindByHexButton();\n\t\t}\n\n\t\tsearchField.selectAll();\n\t\tsearchField.requestFocus();\n\t}\n\n\tpublic void toggle() {\n\t\tboolean visible = !isVisible();\n\t\tsetVisible(visible);\n\n\t\tif (visible) {\n\t\t\tString preferText = HexPreviewPanel.getSelectionData(hexCodeArea);\n\t\t\tif (!StringUtils.isEmpty(preferText)) {\n\t\t\t\tsearchField.setText(preferText);\n\t\t\t\tmakeFindByHexButton();\n\t\t\t}\n\t\t\tsearchField.selectAll();\n\t\t\tsearchField.requestFocus();\n\t\t} else {\n\t\t\tcontrol.performEscape();\n\t\t\thexCodeArea.requestFocus();\n\t\t}\n\t}\n\n\tpublic void setInfoLabel(String text) {\n\t\tresultCountLabel.setText(text);\n\t}\n\n\tpublic void updateMatchCount(boolean hasMatches, boolean prevMatchAvailable, boolean nextMatchAvailable) {\n\t\tprevMatchButton.setEnabled(prevMatchAvailable);\n\t\tnextMatchButton.setEnabled(nextMatchAvailable);\n\t}\n\n\tpublic void setControl(Control control) {\n\t\tthis.control = control;\n\t}\n\n\tpublic void clearSearch() {\n\t\tsetInfoLabel(\"\");\n\t\tsearchField.setText(\"\");\n\t}\n\n\tpublic SearchParameters getSearchParameters() {\n\t\tSearchParameters searchParameters = new SearchParameters();\n\t\tsearchParameters.setMatchCase(matchCaseCB.isSelected());\n\t\tsearchParameters.setMatchMode(SearchParameters.MatchMode.fromBoolean(markAllCB.isSelected()));\n\t\tSearchParameters.SearchDirection searchDirection = control.getSearchDirection();\n\t\tsearchParameters.setSearchDirection(searchDirection);\n\n\t\tlong startPosition;\n\t\tif (searchParameters.isSearchFromCursor()) {\n\t\t\tstartPosition = hexCodeArea.getActiveCaretPosition().getDataPosition();\n\t\t} else {\n\t\t\tswitch (searchDirection) {\n\t\t\t\tcase FORWARD: {\n\t\t\t\t\tstartPosition = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase BACKWARD: {\n\t\t\t\t\tstartPosition = hexCodeArea.getDataSize() - 1;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tthrow CodeAreaUtils.getInvalidTypeException(searchDirection);\n\t\t\t}\n\t\t}\n\t\tsearchParameters.setStartPosition(startPosition);\n\n\t\tsearchParameters.setCondition(new SearchCondition(makeSearchCondition()));\n\t\treturn searchParameters;\n\t}\n\n\tprivate SearchCondition makeSearchCondition() {\n\t\tSearchCondition condition = new SearchCondition();\n\t\tif (findTypeCB.isSelected()) {\n\t\t\tcondition.setSearchMode(SearchCondition.SearchMode.BINARY);\n\t\t} else {\n\t\t\tcondition.setSearchMode(SearchCondition.SearchMode.TEXT);\n\t\t}\n\t\tif (!StringUtils.isEmpty(searchField.getText())) {\n\t\t\tif (condition.getSearchMode() == SearchCondition.SearchMode.TEXT) {\n\t\t\t\tcondition.setSearchText(searchField.getText());\n\t\t\t} else {\n\t\t\t\tString hexBytes = searchField.getText();\n\t\t\t\tboolean isValidHexInput = HexUtils.isValidHexString(hexBytes);\n\t\t\t\tUiUtils.highlightAsErrorField(searchField, !isValidHexInput);\n\t\t\t\tif (isValidHexInput) {\n\t\t\t\t\tcondition.setBinaryData(new ByteArrayEditableData(HexUtils.hexStringToByteArray(hexBytes)));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn condition;\n\t}\n\n\tpublic void updateFindStatus() {\n\t\tUiUtils.highlightAsErrorField(searchField, false);\n\t\tSearchCondition condition = makeSearchCondition();\n\t\tif (condition.getSearchMode() == SearchCondition.SearchMode.TEXT) {\n\t\t\tfindTypeCB.setSelected(false);\n\t\t\tfindTypeCB.setToolTipText(NLS.str(\"search.find_type_text\"));\n\t\t\tmatchCaseCB.setEnabled(true);\n\t\t} else {\n\t\t\tmakeFindByHexButton();\n\t\t\tmatchCaseCB.setEnabled(false);\n\t\t}\n\t}\n\n\tprivate void makeFindByHexButton() {\n\t\tfindTypeCB.setSelected(true);\n\t\tfindTypeCB.setToolTipText(NLS.str(\"search.find_type_hex\"));\n\t}\n\n\tpublic interface Control {\n\n\t\tvoid prevMatch();\n\n\t\tvoid nextMatch();\n\n\t\tvoid performEscape();\n\n\t\tvoid performFind();\n\n\t\t/**\n\t\t * Parameters of search have changed.\n\t\t */\n\t\tvoid notifySearchChanged();\n\n\t\t/**\n\t\t * Parameters of search are changing which might not lead to immediate\n\t\t * search change.\n\t\t * <p>\n\t\t * Typically, text typing.\n\t\t */\n\t\tvoid notifySearchChanging();\n\n\t\tSearchParameters.SearchDirection getSearchDirection();\n\n\t\tvoid close();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/hexviewer/search/BinarySearch.java",
    "content": "package jadx.gui.ui.hexviewer.search;\n\n/*\n * Copyright (C) ExBin Project\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * https://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport org.exbin.auxiliary.binary_data.EditableBinaryData;\nimport org.exbin.auxiliary.binary_data.array.ByteArrayEditableData;\n\nimport jadx.gui.ui.hexviewer.HexSearchBar;\nimport jadx.gui.ui.hexviewer.search.service.BinarySearchService;\nimport jadx.gui.utils.NLS;\n\n/**\n * Binary search.\n *\n * @author ExBin Project (https://exbin.org)\n */\npublic class BinarySearch {\n\n\tprivate static final int DEFAULT_DELAY = 500;\n\n\tprivate InvokeSearchThread invokeSearchThread;\n\tprivate SearchThread searchThread;\n\n\tprivate SearchOperation currentSearchOperation = SearchOperation.FIND;\n\tprivate SearchParameters.SearchDirection currentSearchDirection = SearchParameters.SearchDirection.FORWARD;\n\tprivate final SearchParameters currentSearchParameters = new SearchParameters();\n\tprivate BinarySearchService.FoundMatches foundMatches = new BinarySearchService.FoundMatches();\n\n\tprivate BinarySearchService binarySearchService;\n\tprivate final BinarySearchService.SearchStatusListener searchStatusListener;\n\tprivate HexSearchBar binarySearchPanel;\n\n\tpublic BinarySearch(HexSearchBar binarySearchPanel) {\n\t\tthis.binarySearchPanel = binarySearchPanel;\n\n\t\tsearchStatusListener = new BinarySearchService.SearchStatusListener() {\n\t\t\t@Override\n\t\t\tpublic void setStatus(BinarySearchService.FoundMatches foundMatches, SearchParameters.MatchMode matchMode) {\n\t\t\t\tBinarySearch.this.foundMatches = foundMatches;\n\t\t\t\tswitch (foundMatches.getMatchesCount()) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tbinarySearchPanel.setInfoLabel(NLS.str(\"search.match_not_found\"));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\tbinarySearchPanel.setInfoLabel(\n\t\t\t\t\t\t\t\tmatchMode == SearchParameters.MatchMode.MULTIPLE\n\t\t\t\t\t\t\t\t\t\t? NLS.str(\"search.single_match\")\n\t\t\t\t\t\t\t\t\t\t: NLS.str(\"search.match_found\"));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbinarySearchPanel.setInfoLabel(String.format(NLS.str(\"search.match_of\"),\n\t\t\t\t\t\t\t\tfoundMatches.getMatchPosition() + 1, foundMatches.getMatchesCount()));\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tupdateMatchStatus();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void clearStatus() {\n\t\t\t\tbinarySearchPanel.setInfoLabel(\"\");\n\t\t\t\tBinarySearch.this.foundMatches = new BinarySearchService.FoundMatches();\n\t\t\t\tupdateMatchStatus();\n\t\t\t}\n\n\t\t\tprivate void updateMatchStatus() {\n\t\t\t\tint matchesCount = foundMatches.getMatchesCount();\n\t\t\t\tint matchPosition = foundMatches.getMatchPosition();\n\t\t\t\tbinarySearchPanel.updateMatchCount(matchesCount > 0,\n\t\t\t\t\t\tmatchesCount > 1 && matchPosition > 0,\n\t\t\t\t\t\tmatchPosition < matchesCount - 1);\n\t\t\t}\n\t\t};\n\t\tbinarySearchPanel.setControl(new HexSearchBar.Control() {\n\t\t\t@Override\n\t\t\tpublic void prevMatch() {\n\t\t\t\tfoundMatches.prev();\n\t\t\t\tbinarySearchService.setMatchPosition(foundMatches.getMatchPosition());\n\t\t\t\tsearchStatusListener.setStatus(foundMatches, binarySearchService.getLastSearchParameters().getMatchMode());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void nextMatch() {\n\t\t\t\tfoundMatches.next();\n\t\t\t\tbinarySearchService.setMatchPosition(foundMatches.getMatchPosition());\n\t\t\t\tsearchStatusListener.setStatus(foundMatches, binarySearchService.getLastSearchParameters().getMatchMode());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void performEscape() {\n\t\t\t\tcancelSearch();\n\t\t\t\tclose();\n\t\t\t\tclearSearch();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void performFind() {\n\t\t\t\tinvokeSearch(SearchOperation.FIND);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void notifySearchChanged() {\n\t\t\t\tif (currentSearchOperation == SearchOperation.FIND) {\n\t\t\t\t\tinvokeSearch(SearchOperation.FIND);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void notifySearchChanging() {\n\t\t\t\tif (currentSearchOperation != SearchOperation.FIND) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tSearchCondition condition = currentSearchParameters.getCondition();\n\t\t\t\tSearchCondition updatedSearchCondition = binarySearchPanel.getSearchParameters().getCondition();\n\n\t\t\t\tswitch (updatedSearchCondition.getSearchMode()) {\n\t\t\t\t\tcase TEXT: {\n\t\t\t\t\t\tString searchText = updatedSearchCondition.getSearchText();\n\t\t\t\t\t\tif (searchText.isEmpty()) {\n\t\t\t\t\t\t\tcondition.setSearchText(searchText);\n\t\t\t\t\t\t\tclearSearch();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (searchText.equals(condition.getSearchText())) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcondition.setSearchText(searchText);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase BINARY: {\n\t\t\t\t\t\tEditableBinaryData searchData = (EditableBinaryData) updatedSearchCondition.getBinaryData();\n\t\t\t\t\t\tif (searchData == null || searchData.isEmpty()) {\n\t\t\t\t\t\t\tcondition.setBinaryData(null);\n\t\t\t\t\t\t\tclearSearch();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (searchData.equals(condition.getBinaryData())) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tByteArrayEditableData data = new ByteArrayEditableData();\n\t\t\t\t\t\tdata.insert(0, searchData);\n\t\t\t\t\t\tcondition.setBinaryData(data);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tBinarySearch.this.invokeSearch(SearchOperation.FIND, DEFAULT_DELAY);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic SearchParameters.SearchDirection getSearchDirection() {\n\t\t\t\treturn currentSearchDirection;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void close() {\n\t\t\t\tcancelSearch();\n\t\t\t\tclearSearch();\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic void setBinarySearchService(BinarySearchService binarySearchService) {\n\t\tthis.binarySearchService = binarySearchService;\n\t}\n\n\tpublic void setTargetComponent(HexSearchBar targetComponent) {\n\t\tbinarySearchPanel = targetComponent;\n\t}\n\n\tpublic BinarySearchService.SearchStatusListener getSearchStatusListener() {\n\t\treturn searchStatusListener;\n\t}\n\n\tprivate void invokeSearch(SearchOperation searchOperation) {\n\t\tinvokeSearch(searchOperation, binarySearchPanel.getSearchParameters(), 0);\n\t}\n\n\tprivate void invokeSearch(SearchOperation searchOperation, final int delay) {\n\t\tinvokeSearch(searchOperation, binarySearchPanel.getSearchParameters(), delay);\n\t}\n\n\tprivate void invokeSearch(SearchOperation searchOperation, SearchParameters searchParameters) {\n\t\tinvokeSearch(searchOperation, searchParameters, 0);\n\t}\n\n\tprivate void invokeSearch(SearchOperation searchOperation, SearchParameters searchParameters, final int delay) {\n\t\tif (invokeSearchThread != null) {\n\t\t\tinvokeSearchThread.interrupt();\n\t\t}\n\t\tinvokeSearchThread = new InvokeSearchThread();\n\t\tinvokeSearchThread.delay = delay;\n\t\tcurrentSearchOperation = searchOperation;\n\t\tcurrentSearchParameters.setFromParameters(searchParameters);\n\t\tinvokeSearchThread.start();\n\t}\n\n\tpublic void cancelSearch() {\n\t\tif (invokeSearchThread != null) {\n\t\t\tinvokeSearchThread.interrupt();\n\t\t}\n\t\tif (searchThread != null) {\n\t\t\tsearchThread.interrupt();\n\t\t}\n\t}\n\n\tpublic void clearSearch() {\n\t\tSearchCondition condition = currentSearchParameters.getCondition();\n\t\tcondition.clear();\n\t\tbinarySearchPanel.clearSearch();\n\t\tbinarySearchService.clearMatches();\n\t\tsearchStatusListener.clearStatus();\n\t}\n\n\tpublic HexSearchBar getPanel() {\n\t\treturn binarySearchPanel;\n\t}\n\n\tpublic void dataChanged() {\n\t\tbinarySearchService.clearMatches();\n\t\tinvokeSearch(currentSearchOperation, DEFAULT_DELAY);\n\t}\n\n\tprivate class InvokeSearchThread extends Thread {\n\n\t\tprivate int delay = DEFAULT_DELAY;\n\n\t\tpublic InvokeSearchThread() {\n\t\t\tsuper(\"InvokeSearchThread\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\ttry {\n\t\t\t\tThread.sleep(delay);\n\t\t\t\tif (searchThread != null) {\n\t\t\t\t\tsearchThread.interrupt();\n\t\t\t\t}\n\t\t\t\tsearchThread = new SearchThread();\n\t\t\t\tsearchThread.start();\n\t\t\t} catch (InterruptedException ex) {\n\t\t\t\t// don't search\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate class SearchThread extends Thread {\n\n\t\tpublic SearchThread() {\n\t\t\tsuper(\"SearchThread\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tswitch (currentSearchOperation) {\n\t\t\t\tcase FIND:\n\t\t\t\t\tbinarySearchService.performFind(currentSearchParameters, searchStatusListener);\n\t\t\t\t\tbreak;\n\t\t\t\tcase FIND_AGAIN:\n\t\t\t\t\tbinarySearchService.performFindAgain(searchStatusListener);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new UnsupportedOperationException(\"Not supported yet.\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate enum SearchOperation {\n\t\tFIND,\n\t\tFIND_AGAIN\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/hexviewer/search/SearchCondition.java",
    "content": "package jadx.gui.ui.hexviewer.search;\n\nimport java.util.Objects;\n\nimport org.exbin.auxiliary.binary_data.BinaryData;\nimport org.exbin.auxiliary.binary_data.EditableBinaryData;\nimport org.exbin.auxiliary.binary_data.array.ByteArrayEditableData;\nimport org.exbin.bined.CodeAreaUtils;\n\n/**\n * Parameters for action to search for occurrences of text or data.\n *\n * @author ExBin Project (https://exbin.org)\n */\n\npublic class SearchCondition {\n\n\tprivate SearchMode searchMode = SearchMode.TEXT;\n\tprivate String searchText = \"\";\n\tprivate EditableBinaryData binaryData;\n\n\tpublic SearchCondition() {\n\t}\n\n\t/**\n\t * This is copy constructor.\n\t *\n\t * @param source source condition\n\t */\n\tpublic SearchCondition(SearchCondition source) {\n\t\tsearchMode = source.getSearchMode();\n\t\tsearchText = source.getSearchText();\n\t\tbinaryData = new ByteArrayEditableData();\n\t\tif (source.getBinaryData() != null) {\n\t\t\tbinaryData.insert(0, source.getBinaryData());\n\t\t}\n\t}\n\n\tpublic SearchMode getSearchMode() {\n\t\treturn searchMode;\n\t}\n\n\tpublic void setSearchMode(SearchMode searchMode) {\n\t\tthis.searchMode = searchMode;\n\t}\n\n\tpublic String getSearchText() {\n\t\treturn searchText;\n\t}\n\n\tpublic void setSearchText(String searchText) {\n\t\tthis.searchText = searchText;\n\t}\n\n\tpublic BinaryData getBinaryData() {\n\t\treturn binaryData;\n\t}\n\n\tpublic void setBinaryData(EditableBinaryData binaryData) {\n\t\tthis.binaryData = binaryData;\n\t}\n\n\tpublic boolean isEmpty() {\n\t\tswitch (searchMode) {\n\t\t\tcase TEXT: {\n\t\t\t\treturn searchText == null || searchText.isEmpty();\n\t\t\t}\n\t\t\tcase BINARY: {\n\t\t\t\treturn binaryData == null || binaryData.isEmpty();\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tthrow CodeAreaUtils.getInvalidTypeException(searchMode);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint hash = 3;\n\t\treturn hash;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tfinal SearchCondition other = (SearchCondition) obj;\n\t\tif (this.searchMode != other.searchMode) {\n\t\t\treturn false;\n\t\t}\n\t\tif (searchMode == SearchMode.TEXT) {\n\t\t\treturn Objects.equals(this.searchText, other.searchText);\n\t\t} else {\n\t\t\treturn Objects.equals(this.binaryData, other.binaryData);\n\t\t}\n\t}\n\n\tpublic void clear() {\n\t\tsearchText = \"\";\n\t\tif (binaryData != null) {\n\t\t\tbinaryData.clear();\n\t\t}\n\t}\n\n\tpublic enum SearchMode {\n\t\tTEXT, BINARY\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/hexviewer/search/SearchParameters.java",
    "content": "package jadx.gui.ui.hexviewer.search;\n\n/*\n * Copyright (C) ExBin Project\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * https://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Parameters for action to search for occurrences of text or data.\n *\n * @author ExBin Project (https://exbin.org)\n */\n\npublic class SearchParameters {\n\n\tprivate SearchCondition condition = new SearchCondition();\n\tprivate long startPosition;\n\tprivate boolean searchFromCursor;\n\tprivate boolean matchCase = true;\n\tprivate MatchMode matchMode = MatchMode.MULTIPLE;\n\tprivate SearchDirection searchDirection = SearchDirection.FORWARD;\n\n\tpublic SearchParameters() {\n\t}\n\n\tpublic SearchCondition getCondition() {\n\t\treturn condition;\n\t}\n\n\tpublic void setCondition(SearchCondition condition) {\n\t\tthis.condition = condition;\n\t}\n\n\tpublic long getStartPosition() {\n\t\treturn startPosition;\n\t}\n\n\tpublic void setStartPosition(long startPosition) {\n\t\tthis.startPosition = startPosition;\n\t}\n\n\tpublic boolean isSearchFromCursor() {\n\t\treturn searchFromCursor;\n\t}\n\n\tpublic void setSearchFromCursor(boolean searchFromCursor) {\n\t\tthis.searchFromCursor = searchFromCursor;\n\t}\n\n\tpublic boolean isMatchCase() {\n\t\treturn matchCase;\n\t}\n\n\tpublic void setMatchCase(boolean matchCase) {\n\t\tthis.matchCase = matchCase;\n\t}\n\n\tpublic MatchMode getMatchMode() {\n\t\treturn matchMode;\n\t}\n\n\tpublic void setMatchMode(MatchMode matchMode) {\n\t\tthis.matchMode = matchMode;\n\t}\n\n\tpublic SearchDirection getSearchDirection() {\n\t\treturn searchDirection;\n\t}\n\n\tpublic void setSearchDirection(SearchDirection searchDirection) {\n\t\tthis.searchDirection = searchDirection;\n\t}\n\n\tpublic void setFromParameters(SearchParameters searchParameters) {\n\t\tcondition = searchParameters.getCondition();\n\t\tstartPosition = searchParameters.getStartPosition();\n\t\tsearchFromCursor = searchParameters.isSearchFromCursor();\n\t\tmatchCase = searchParameters.isMatchCase();\n\t\tmatchMode = searchParameters.getMatchMode();\n\t\tsearchDirection = searchParameters.getSearchDirection();\n\t}\n\n\tpublic enum SearchDirection {\n\t\tFORWARD, BACKWARD\n\t}\n\n\tpublic enum MatchMode {\n\t\tSINGLE, MULTIPLE;\n\n\t\tpublic static MatchMode fromBoolean(boolean multipleMatches) {\n\t\t\treturn multipleMatches ? MULTIPLE : SINGLE;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/hexviewer/search/service/BinarySearchService.java",
    "content": "package jadx.gui.ui.hexviewer.search.service;\n\nimport jadx.gui.ui.hexviewer.search.SearchParameters;\n\npublic interface BinarySearchService {\n\n\tvoid performFind(SearchParameters dialogSearchParameters, SearchStatusListener searchStatusListener);\n\n\tvoid setMatchPosition(int matchPosition);\n\n\tvoid performFindAgain(SearchStatusListener searchStatusListener);\n\n\tSearchParameters getLastSearchParameters();\n\n\tvoid clearMatches();\n\n\tpublic interface SearchStatusListener {\n\n\t\tvoid setStatus(FoundMatches foundMatches, SearchParameters.MatchMode matchMode);\n\n\t\tvoid clearStatus();\n\t}\n\n\tpublic static class FoundMatches {\n\n\t\tprivate int matchesCount;\n\t\tprivate int matchPosition;\n\n\t\tpublic FoundMatches() {\n\t\t\tmatchesCount = 0;\n\t\t\tmatchPosition = -1;\n\t\t}\n\n\t\tpublic FoundMatches(int matchesCount, int matchPosition) {\n\t\t\tif (matchPosition >= matchesCount) {\n\t\t\t\tthrow new IllegalStateException(\"Match position is out of range\");\n\t\t\t}\n\n\t\t\tthis.matchesCount = matchesCount;\n\t\t\tthis.matchPosition = matchPosition;\n\t\t}\n\n\t\tpublic int getMatchesCount() {\n\t\t\treturn matchesCount;\n\t\t}\n\n\t\tpublic int getMatchPosition() {\n\t\t\treturn matchPosition;\n\t\t}\n\n\t\tpublic void setMatchesCount(int matchesCount) {\n\t\t\tthis.matchesCount = matchesCount;\n\t\t}\n\n\t\tpublic void setMatchPosition(int matchPosition) {\n\t\t\tthis.matchPosition = matchPosition;\n\t\t}\n\n\t\tpublic void next() {\n\t\t\tif (matchPosition == matchesCount - 1) {\n\t\t\t\tthrow new IllegalStateException(\"Cannot find next on last match\");\n\t\t\t}\n\n\t\t\tmatchPosition++;\n\t\t}\n\n\t\tpublic void prev() {\n\t\t\tif (matchPosition == 0) {\n\t\t\t\tthrow new IllegalStateException(\"Cannot find previous on first match\");\n\t\t\t}\n\n\t\t\tmatchPosition--;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/hexviewer/search/service/BinarySearchServiceImpl.java",
    "content": "package jadx.gui.ui.hexviewer.search.service;\n\nimport java.nio.charset.Charset;\nimport java.nio.charset.CharsetEncoder;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.exbin.auxiliary.binary_data.BinaryData;\nimport org.exbin.bined.CharsetStreamTranslator;\nimport org.exbin.bined.CodeAreaUtils;\nimport org.exbin.bined.highlight.swing.SearchCodeAreaColorAssessor;\nimport org.exbin.bined.highlight.swing.SearchMatch;\nimport org.exbin.bined.swing.CodeAreaSwingUtils;\nimport org.exbin.bined.swing.capability.ColorAssessorPainterCapable;\nimport org.exbin.bined.swing.section.SectCodeArea;\n\nimport jadx.gui.ui.hexviewer.search.SearchCondition;\nimport jadx.gui.ui.hexviewer.search.SearchParameters;\n\n/**\n * Binary search service.\n *\n * @author ExBin Project (https://exbin.org)\n */\n\npublic class BinarySearchServiceImpl implements BinarySearchService {\n\n\tprivate static final int MAX_MATCHES_COUNT = 100;\n\tprivate final SectCodeArea codeArea;\n\tprivate final SearchParameters lastSearchParameters = new SearchParameters();\n\n\tpublic BinarySearchServiceImpl(SectCodeArea codeArea) {\n\t\tthis.codeArea = codeArea;\n\t}\n\n\t@Override\n\tpublic void performFind(SearchParameters searchParameters, SearchStatusListener searchStatusListener) {\n\t\tSearchCodeAreaColorAssessor searchAssessor = CodeAreaSwingUtils\n\t\t\t\t.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), SearchCodeAreaColorAssessor.class);\n\t\tSearchCondition condition = searchParameters.getCondition();\n\t\tsearchStatusListener.clearStatus();\n\t\tif (condition.isEmpty()) {\n\t\t\tsearchAssessor.clearMatches();\n\t\t\tcodeArea.repaint();\n\t\t\treturn;\n\t\t}\n\n\t\tlong position;\n\t\tswitch (searchParameters.getSearchDirection()) {\n\t\t\tcase FORWARD: {\n\t\t\t\tif (searchParameters.isSearchFromCursor()) {\n\t\t\t\t\tposition = codeArea.getActiveCaretPosition().getDataPosition();\n\t\t\t\t} else {\n\t\t\t\t\tposition = 0;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase BACKWARD: {\n\t\t\t\tif (searchParameters.isSearchFromCursor()) {\n\t\t\t\t\tposition = codeArea.getActiveCaretPosition().getDataPosition() - 1;\n\t\t\t\t} else {\n\t\t\t\t\tlong searchDataSize;\n\t\t\t\t\tswitch (condition.getSearchMode()) {\n\t\t\t\t\t\tcase TEXT: {\n\t\t\t\t\t\t\tsearchDataSize = condition.getSearchText().length();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase BINARY: {\n\t\t\t\t\t\t\tsearchDataSize = condition.getBinaryData().getDataSize();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tthrow CodeAreaUtils.getInvalidTypeException(condition.getSearchMode());\n\t\t\t\t\t}\n\t\t\t\t\tposition = codeArea.getDataSize() - searchDataSize;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tthrow CodeAreaUtils.getInvalidTypeException(searchParameters.getSearchDirection());\n\t\t}\n\t\tsearchParameters.setStartPosition(position);\n\n\t\tswitch (condition.getSearchMode()) {\n\t\t\tcase TEXT: {\n\t\t\t\tsearchForText(searchParameters, searchStatusListener);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase BINARY: {\n\t\t\t\tsearchForBinaryData(searchParameters, searchStatusListener);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tthrow CodeAreaUtils.getInvalidTypeException(condition.getSearchMode());\n\t\t}\n\t}\n\n\t/**\n\t * Performs search by binary data.\n\t */\n\tprivate void searchForBinaryData(SearchParameters searchParameters, SearchStatusListener searchStatusListener) {\n\t\tSearchCodeAreaColorAssessor searchAssessor = CodeAreaSwingUtils\n\t\t\t\t.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), SearchCodeAreaColorAssessor.class);\n\t\tSearchCondition condition = searchParameters.getCondition();\n\t\tlong position = searchParameters.getStartPosition();\n\n\t\tBinaryData searchData = condition.getBinaryData();\n\t\tlong searchDataSize = searchData.getDataSize();\n\t\tBinaryData data = codeArea.getContentData();\n\n\t\tList<SearchMatch> foundMatches = new ArrayList<>();\n\n\t\tlong dataSize = data.getDataSize();\n\t\twhile (position >= 0 && position <= dataSize - searchDataSize) {\n\t\t\tlong matchLength = 0;\n\t\t\twhile (matchLength < searchDataSize) {\n\t\t\t\tif (data.getByte(position + matchLength) != searchData.getByte(matchLength)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tmatchLength++;\n\t\t\t}\n\n\t\t\tif (matchLength == searchDataSize) {\n\t\t\t\tSearchMatch match = new SearchMatch();\n\t\t\t\tmatch.setPosition(position);\n\t\t\t\tmatch.setLength(searchDataSize);\n\t\t\t\tif (searchParameters.getSearchDirection() == SearchParameters.SearchDirection.BACKWARD) {\n\t\t\t\t\tfoundMatches.add(0, match);\n\t\t\t\t} else {\n\t\t\t\t\tfoundMatches.add(match);\n\t\t\t\t}\n\n\t\t\t\tif (foundMatches.size() == MAX_MATCHES_COUNT || searchParameters.getMatchMode() == SearchParameters.MatchMode.SINGLE) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tposition++;\n\t\t}\n\n\t\tsearchAssessor.setMatches(foundMatches);\n\t\tif (!foundMatches.isEmpty()) {\n\t\t\tif (searchParameters.getSearchDirection() == SearchParameters.SearchDirection.BACKWARD) {\n\t\t\t\tsearchAssessor.setCurrentMatchIndex(foundMatches.size() - 1);\n\t\t\t} else {\n\t\t\t\tsearchAssessor.setCurrentMatchIndex(0);\n\t\t\t}\n\t\t\tSearchMatch firstMatch = Objects.requireNonNull(searchAssessor.getCurrentMatch());\n\t\t\tcodeArea.revealPosition(firstMatch.getPosition(), 0, codeArea.getActiveSection());\n\t\t}\n\t\tlastSearchParameters.setFromParameters(searchParameters);\n\t\tsearchStatusListener.setStatus(\n\t\t\t\tnew FoundMatches(foundMatches.size(), foundMatches.isEmpty() ? -1 : searchAssessor.getCurrentMatchIndex()),\n\t\t\t\tsearchParameters.getMatchMode());\n\t\tcodeArea.repaint();\n\t}\n\n\t/**\n\t * Performs search by text/characters.\n\t */\n\tprivate void searchForText(SearchParameters searchParameters, SearchStatusListener searchStatusListener) {\n\t\tSearchCodeAreaColorAssessor searchAssessor = CodeAreaSwingUtils\n\t\t\t\t.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), SearchCodeAreaColorAssessor.class);\n\t\tSearchCondition condition = searchParameters.getCondition();\n\n\t\tlong position = searchParameters.getStartPosition();\n\t\tString findText;\n\t\tif (searchParameters.isMatchCase()) {\n\t\t\tfindText = condition.getSearchText();\n\t\t} else {\n\t\t\tfindText = condition.getSearchText().toLowerCase();\n\t\t}\n\t\tlong searchDataSize = findText.length();\n\t\tBinaryData data = codeArea.getContentData();\n\n\t\tList<SearchMatch> foundMatches = new ArrayList<>();\n\n\t\tCharset charset = codeArea.getCharset();\n\t\tint maxBytesPerChar;\n\t\ttry {\n\t\t\tCharsetEncoder encoder = charset.newEncoder();\n\t\t\tmaxBytesPerChar = (int) encoder.maxBytesPerChar();\n\t\t} catch (UnsupportedOperationException ex) {\n\t\t\tmaxBytesPerChar = CharsetStreamTranslator.DEFAULT_MAX_BYTES_PER_CHAR;\n\t\t}\n\t\tbyte[] charData = new byte[maxBytesPerChar];\n\t\tlong dataSize = data.getDataSize();\n\t\tlong lastPosition = position;\n\t\twhile (position >= 0 && position <= dataSize - searchDataSize) {\n\t\t\tint matchCharLength = 0;\n\t\t\tint matchLength = 0;\n\t\t\twhile (matchCharLength < (int) searchDataSize) {\n\t\t\t\tif (Thread.interrupted()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlong searchPosition = position + (long) matchLength;\n\t\t\t\tint bytesToUse = maxBytesPerChar;\n\t\t\t\tif (searchPosition + bytesToUse > dataSize) {\n\t\t\t\t\tbytesToUse = (int) (dataSize - searchPosition);\n\t\t\t\t}\n\n\t\t\t\tif (searchPosition == lastPosition + 1) {\n\t\t\t\t\tSystem.arraycopy(charData, 1, charData, 0, maxBytesPerChar - 1);\n\t\t\t\t\tcharData[bytesToUse - 1] = data.getByte(searchPosition + bytesToUse - 1);\n\t\t\t\t} else if (searchPosition == lastPosition - 1) {\n\t\t\t\t\tSystem.arraycopy(charData, 0, charData, 1, maxBytesPerChar - 1);\n\t\t\t\t\tcharData[0] = data.getByte(searchPosition);\n\t\t\t\t} else {\n\t\t\t\t\tdata.copyToArray(searchPosition, charData, 0, bytesToUse);\n\t\t\t\t}\n\t\t\t\tif (bytesToUse < maxBytesPerChar) {\n\t\t\t\t\tArrays.fill(charData, bytesToUse, maxBytesPerChar, (byte) 0);\n\t\t\t\t}\n\t\t\t\tlastPosition = searchPosition;\n\t\t\t\tchar singleChar = new String(charData, charset).charAt(0);\n\n\t\t\t\tif (searchParameters.isMatchCase()) {\n\t\t\t\t\tif (singleChar != findText.charAt(matchCharLength)) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t} else if (Character.toLowerCase(singleChar) != findText.charAt(matchCharLength)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tint characterLength = String.valueOf(singleChar).getBytes(charset).length;\n\t\t\t\tmatchCharLength++;\n\t\t\t\tmatchLength += characterLength;\n\t\t\t}\n\n\t\t\tif (matchCharLength == findText.length()) {\n\t\t\t\tSearchMatch match = new SearchMatch();\n\t\t\t\tmatch.setPosition(position);\n\t\t\t\tmatch.setLength(matchLength);\n\t\t\t\tif (searchParameters.getSearchDirection() == SearchParameters.SearchDirection.BACKWARD) {\n\t\t\t\t\tfoundMatches.add(0, match);\n\t\t\t\t} else {\n\t\t\t\t\tfoundMatches.add(match);\n\t\t\t\t}\n\n\t\t\t\tif (foundMatches.size() == MAX_MATCHES_COUNT || searchParameters.getMatchMode() == SearchParameters.MatchMode.SINGLE) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tswitch (searchParameters.getSearchDirection()) {\n\t\t\t\tcase FORWARD: {\n\t\t\t\t\tposition++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase BACKWARD: {\n\t\t\t\t\tposition--;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tthrow CodeAreaUtils.getInvalidTypeException(searchParameters.getSearchDirection());\n\t\t\t}\n\t\t}\n\n\t\tif (Thread.interrupted()) {\n\t\t\treturn;\n\t\t}\n\n\t\tsearchAssessor.setMatches(foundMatches);\n\t\tif (!foundMatches.isEmpty()) {\n\t\t\tif (searchParameters.getSearchDirection() == SearchParameters.SearchDirection.BACKWARD) {\n\t\t\t\tsearchAssessor.setCurrentMatchIndex(foundMatches.size() - 1);\n\t\t\t} else {\n\t\t\t\tsearchAssessor.setCurrentMatchIndex(0);\n\t\t\t}\n\t\t\tSearchMatch firstMatch = searchAssessor.getCurrentMatch();\n\t\t\tcodeArea.revealPosition(firstMatch.getPosition(), 0, codeArea.getActiveSection());\n\t\t}\n\t\tlastSearchParameters.setFromParameters(searchParameters);\n\t\tsearchStatusListener.setStatus(\n\t\t\t\tnew FoundMatches(foundMatches.size(), foundMatches.isEmpty() ? -1 : searchAssessor.getCurrentMatchIndex()),\n\t\t\t\tsearchParameters.getMatchMode());\n\t\tcodeArea.repaint();\n\t}\n\n\t@Override\n\tpublic void setMatchPosition(int matchPosition) {\n\t\tSearchCodeAreaColorAssessor searchAssessor = CodeAreaSwingUtils\n\t\t\t\t.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), SearchCodeAreaColorAssessor.class);\n\t\tsearchAssessor.setCurrentMatchIndex(matchPosition);\n\t\tSearchMatch currentMatch = searchAssessor.getCurrentMatch();\n\t\tcodeArea.revealPosition(currentMatch.getPosition(), 0, codeArea.getActiveSection());\n\t\tcodeArea.repaint();\n\t}\n\n\t@Override\n\tpublic void performFindAgain(SearchStatusListener searchStatusListener) {\n\t\tSearchCodeAreaColorAssessor searchAssessor = CodeAreaSwingUtils\n\t\t\t\t.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), SearchCodeAreaColorAssessor.class);\n\t\tList<SearchMatch> foundMatches = searchAssessor.getMatches();\n\t\tint matchesCount = foundMatches.size();\n\t\tif (matchesCount > 0) {\n\t\t\tswitch (lastSearchParameters.getMatchMode()) {\n\t\t\t\tcase MULTIPLE:\n\t\t\t\t\tif (matchesCount > 1) {\n\t\t\t\t\t\tint currentMatchIndex = searchAssessor.getCurrentMatchIndex();\n\t\t\t\t\t\tsetMatchPosition(currentMatchIndex < matchesCount - 1 ? currentMatchIndex + 1 : 0);\n\t\t\t\t\t\tsearchStatusListener.setStatus(new FoundMatches(foundMatches.size(), searchAssessor.getCurrentMatchIndex()),\n\t\t\t\t\t\t\t\tlastSearchParameters.getMatchMode());\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\tcase SINGLE:\n\t\t\t\t\tswitch (lastSearchParameters.getSearchDirection()) {\n\t\t\t\t\t\tcase FORWARD:\n\t\t\t\t\t\t\tlastSearchParameters.setStartPosition(foundMatches.get(0).getPosition() + 1);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase BACKWARD:\n\t\t\t\t\t\t\tSearchMatch match = foundMatches.get(0);\n\t\t\t\t\t\t\tlastSearchParameters.setStartPosition(match.getPosition() - 1);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tSearchCondition condition = lastSearchParameters.getCondition();\n\t\t\t\t\tswitch (condition.getSearchMode()) {\n\t\t\t\t\t\tcase TEXT: {\n\t\t\t\t\t\t\tsearchForText(lastSearchParameters, searchStatusListener);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase BINARY: {\n\t\t\t\t\t\t\tsearchForBinaryData(lastSearchParameters, searchStatusListener);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tthrow CodeAreaUtils.getInvalidTypeException(condition.getSearchMode());\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic SearchParameters getLastSearchParameters() {\n\t\treturn lastSearchParameters;\n\t}\n\n\t@Override\n\tpublic void clearMatches() {\n\t\tSearchCodeAreaColorAssessor searchAssessor = CodeAreaSwingUtils\n\t\t\t\t.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), SearchCodeAreaColorAssessor.class);\n\t\tsearchAssessor.clearMatches();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/menu/HiddenMenuItem.java",
    "content": "package jadx.gui.ui.menu;\n\nimport java.awt.Dimension;\nimport java.awt.Graphics;\n\nimport javax.swing.Action;\nimport javax.swing.JMenuItem;\n\npublic class HiddenMenuItem extends JMenuItem {\n\tpublic HiddenMenuItem(Action a) {\n\t\tsuper(a);\n\t}\n\n\t@Override\n\tprotected void paintComponent(Graphics g) {\n\t}\n\n\t@Override\n\tpublic Dimension getPreferredSize() {\n\t\treturn new Dimension(0, 0);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/menu/JadxMenu.java",
    "content": "package jadx.gui.ui.menu;\n\nimport javax.swing.Action;\nimport javax.swing.JComponent;\nimport javax.swing.JMenu;\nimport javax.swing.JMenuItem;\n\nimport jadx.gui.ui.action.ActionModel;\nimport jadx.gui.ui.action.JadxGuiAction;\nimport jadx.gui.utils.shortcut.Shortcut;\nimport jadx.gui.utils.shortcut.ShortcutsController;\n\npublic class JadxMenu extends JMenu {\n\t// fake component to fill action shortcut component property\n\tpublic static final JComponent JADX_MENU_COMPONENT = new JComponent() {\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"JADX_MENU_COMPONENT\";\n\t\t}\n\t};\n\n\tprivate final ShortcutsController shortcutsController;\n\n\tpublic JadxMenu(String name, ShortcutsController shortcutsController) {\n\t\tsuper(name);\n\t\tthis.shortcutsController = shortcutsController;\n\t}\n\n\t@Override\n\tpublic JMenuItem add(JMenuItem menuItem) {\n\t\tAction action = menuItem.getAction();\n\t\tbindAction(action);\n\t\treturn super.add(menuItem);\n\t}\n\n\t@Override\n\tpublic JMenuItem add(Action action) {\n\t\tbindAction(action);\n\t\treturn super.add(action);\n\t}\n\n\tpublic void bindAction(Action action) {\n\t\tif (action instanceof JadxGuiAction) {\n\t\t\tJadxGuiAction guiAction = (JadxGuiAction) action;\n\t\t\tJComponent shortcutComponent = guiAction.getShortcutComponent();\n\t\t\tif (shortcutComponent == null) {\n\t\t\t\tguiAction.setShortcutComponent(JADX_MENU_COMPONENT);\n\t\t\t}\n\t\t\tshortcutsController.bind(guiAction);\n\t\t}\n\t}\n\n\tpublic void reloadShortcuts() {\n\t\tfor (int i = 0; i < getItemCount(); i++) {\n\t\t\t// TODO only repaint the items whose shortcut changed\n\t\t\tJMenuItem item = getItem(i);\n\t\t\tif (item == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tAction action = item.getAction();\n\t\t\tif (!(action instanceof JadxGuiAction) || ((JadxGuiAction) action).getActionModel() == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tActionModel actionModel = ((JadxGuiAction) action).getActionModel();\n\t\t\tShortcut shortcut = shortcutsController.get(actionModel);\n\t\t\tif (shortcut != null) {\n\t\t\t\titem.setAccelerator(shortcut.toKeyStroke());\n\t\t\t} else {\n\t\t\t\titem.setAccelerator(null);\n\t\t\t}\n\t\t\titem.repaint();\n\t\t\titem.revalidate();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/menu/JadxMenuBar.java",
    "content": "package jadx.gui.ui.menu;\n\nimport javax.swing.JMenu;\nimport javax.swing.JMenuBar;\n\npublic class JadxMenuBar extends JMenuBar {\n\tpublic void reloadShortcuts() {\n\t\tfor (int i = 0; i < getMenuCount(); i++) {\n\t\t\tJMenu menu = getMenu(i);\n\t\t\tif (menu instanceof JadxMenu) {\n\t\t\t\t((JadxMenu) menu).reloadShortcuts();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/panel/ContentPanel.java",
    "content": "package jadx.gui.ui.panel;\n\nimport javax.swing.JPanel;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.ui.tab.TabsController;\n\npublic abstract class ContentPanel extends JPanel {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ContentPanel.class);\n\tprivate static final long serialVersionUID = 3237031760631677822L;\n\n\tprotected TabbedPane tabbedPane;\n\tprotected JNode node;\n\n\tprotected ContentPanel(TabbedPane panel, JNode node) {\n\t\ttabbedPane = panel;\n\t\tthis.node = node;\n\t}\n\n\tpublic abstract void loadSettings();\n\n\tpublic TabbedPane getTabbedPane() {\n\t\treturn tabbedPane;\n\t}\n\n\tpublic TabsController getTabsController() {\n\t\treturn tabbedPane.getTabsController();\n\t}\n\n\tpublic MainWindow getMainWindow() {\n\t\treturn tabbedPane.getMainWindow();\n\t}\n\n\tpublic JNode getNode() {\n\t\treturn node;\n\t}\n\n\tpublic void scrollToPos(int pos) {\n\t\tLOG.warn(\"ContentPanel.scrollToPos method not implemented, class: {}\", getClass().getSimpleName());\n\t}\n\n\tpublic JadxSettings getSettings() {\n\t\treturn tabbedPane.getMainWindow().getSettings();\n\t}\n\n\tpublic boolean supportsQuickTabs() {\n\t\treturn getNode().supportsQuickTabs();\n\t}\n\n\tpublic void dispose() {\n\t\ttabbedPane = null;\n\t\tnode = null;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/panel/FontPanel.java",
    "content": "package jadx.gui.ui.panel;\n\nimport java.awt.BorderLayout;\nimport java.awt.Font;\nimport java.awt.FontFormatException;\nimport java.io.ByteArrayInputStream;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\n\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourcesLoader;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.xmlgen.ResContainer;\nimport jadx.gui.treemodel.JResource;\nimport jadx.gui.ui.codearea.AbstractCodeArea;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.NLS;\n\npublic class FontPanel extends ContentPanel {\n\tprivate static final long serialVersionUID = 695370628262996993L;\n\tprivate static final String DEFAULT_PREVIEW_STRING = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\\n\"\n\t\t\t+ \"abcdefghijklmnopqrstuvwxyz\\n\"\n\t\t\t+ \"1234567890!@#$%^&*()_-=+[]{}<,.>\";\n\n\tpublic FontPanel(TabbedPane panel, JResource res) {\n\t\tsuper(panel, res);\n\t\tsetLayout(new BorderLayout());\n\t\tRSyntaxTextArea textArea = AbstractCodeArea.getDefaultArea(panel.getMainWindow());\n\t\tadd(textArea, BorderLayout.CENTER);\n\t\ttry {\n\t\t\tFont selectedFont = loadFont(res);\n\t\t\tif (selectedFont.canDisplay(DEFAULT_PREVIEW_STRING.codePointAt(0))) {\n\t\t\t\ttextArea.setFont(selectedFont);\n\t\t\t\ttextArea.setText(DEFAULT_PREVIEW_STRING);\n\t\t\t} else {\n\t\t\t\ttextArea.setText(NLS.str(\"message.unable_preview_font\"));\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\ttextArea.setText(\"Font load error:\\n\" + Utils.getStackTrace(e));\n\t\t}\n\t}\n\n\tprivate Font loadFont(JResource res) {\n\t\tResourceFile resFile = res.getResFile();\n\t\tResContainer resContainer = resFile.loadContent();\n\t\tResContainer.DataType dataType = resContainer.getDataType();\n\t\tif (dataType == ResContainer.DataType.DECODED_DATA) {\n\t\t\ttry {\n\t\t\t\treturn Font.createFont(Font.TRUETYPE_FONT, new ByteArrayInputStream(resContainer.getDecodedData())).deriveFont(12f);\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new JadxRuntimeException(\"Failed to load font\", e);\n\t\t\t}\n\t\t} else if (dataType == ResContainer.DataType.RES_LINK) {\n\t\t\ttry {\n\t\t\t\treturn ResourcesLoader.decodeStream(resFile, (size, is) -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn Font.createFont(Font.TRUETYPE_FONT, is).deriveFont(12f);\n\t\t\t\t\t} catch (FontFormatException e) {\n\t\t\t\t\t\tthrow new JadxRuntimeException(\"Failed to load font\", e);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new JadxRuntimeException(\"Failed to load font\", e);\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(\"Unsupported resource font data type: \" + resFile);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void loadSettings() {\n\t\t// no op\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/panel/HtmlPanel.java",
    "content": "package jadx.gui.ui.panel;\n\nimport java.awt.BorderLayout;\nimport java.awt.Graphics;\nimport java.awt.Graphics2D;\nimport java.awt.RenderingHints;\n\nimport javax.swing.JEditorPane;\nimport javax.swing.JScrollPane;\n\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.ui.ZoomActions;\n\npublic final class HtmlPanel extends ContentPanel {\n\tprivate static final long serialVersionUID = -6251262855835426245L;\n\n\tprivate final JHtmlPane textArea;\n\n\tpublic HtmlPanel(TabbedPane panel, JNode jnode) {\n\t\tsuper(panel, jnode);\n\t\tsetLayout(new BorderLayout());\n\t\ttextArea = new JHtmlPane();\n\t\tloadSettings();\n\t\tloadContent(jnode);\n\t\ttextArea.setEditable(false);\n\t\tJScrollPane sp = new JScrollPane(textArea);\n\t\tadd(sp);\n\n\t\tZoomActions.register(textArea, panel.getMainWindow().getSettings(), this::loadSettings);\n\t}\n\n\t@Override\n\tpublic void loadSettings() {\n\t\tJadxSettings settings = getMainWindow().getSettings();\n\t\ttextArea.setFont(settings.getUiFont());\n\t}\n\n\tpublic void loadContent(JNode jnode) {\n\t\ttextArea.setText(jnode.getCodeInfo().getCodeStr());\n\t\ttextArea.setCaretPosition(0); // otherwise the start view will be the last line\n\t}\n\n\tpublic JEditorPane getHtmlArea() {\n\t\treturn textArea;\n\t}\n\n\tprivate static final class JHtmlPane extends JEditorPane {\n\t\tprivate static final long serialVersionUID = 6886040384052136157L;\n\n\t\tpublic JHtmlPane() {\n\t\t\tsetContentType(\"text/html\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void paint(Graphics g) {\n\t\t\tGraphics2D g2d = (Graphics2D) g.create();\n\t\t\ttry {\n\t\t\t\tg2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);\n\t\t\t\tsuper.paint(g2d);\n\t\t\t} finally {\n\t\t\t\tg2d.dispose();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/panel/IDebugController.java",
    "content": "package jadx.gui.ui.panel;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.gui.ui.panel.JDebuggerPanel.ValueTreeNode;\n\npublic interface IDebugController {\n\tboolean startDebugger(JDebuggerPanel debuggerPanel, String adbHost, int adbPort, int androidVer);\n\n\tboolean run();\n\n\tboolean stepOver();\n\n\tboolean stepInto();\n\n\tboolean stepOut();\n\n\tboolean pause();\n\n\tboolean stop();\n\n\tboolean exit();\n\n\tboolean isSuspended();\n\n\tboolean isDebugging();\n\n\tboolean modifyRegValue(ValueTreeNode node, ArgType type, Object val);\n\n\tString getProcessName();\n\n\tvoid setStateListener(StateListener l);\n\n\tinterface StateListener {\n\t\tvoid onStateChanged(boolean suspended, boolean stopped);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/panel/IViewStateSupport.java",
    "content": "package jadx.gui.ui.panel;\n\nimport jadx.gui.ui.codearea.EditorViewState;\n\npublic interface IViewStateSupport {\n\n\tvoid saveEditorViewState(EditorViewState viewState);\n\n\tvoid restoreEditorViewState(EditorViewState viewState);\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/panel/ImagePanel.java",
    "content": "package jadx.gui.ui.panel;\n\nimport java.awt.BorderLayout;\nimport java.awt.image.BufferedImage;\nimport java.io.ByteArrayInputStream;\n\nimport javax.imageio.ImageIO;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\n\nimport hu.kazocsaba.imageviewer.ImageViewer;\n\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourcesLoader;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.xmlgen.ResContainer;\nimport jadx.gui.treemodel.JResource;\nimport jadx.gui.ui.codearea.AbstractCodeArea;\nimport jadx.gui.ui.tab.TabbedPane;\n\npublic class ImagePanel extends ContentPanel {\n\tprivate static final long serialVersionUID = 4071356367073142688L;\n\n\tpublic ImagePanel(TabbedPane panel, JResource res) {\n\t\tsuper(panel, res);\n\t\tsetLayout(new BorderLayout());\n\t\ttry {\n\t\t\tBufferedImage img = loadImage(res);\n\t\t\tImageViewer imageViewer = new ImageViewer(img);\n\t\t\tadd(imageViewer.getComponent());\n\t\t} catch (Exception e) {\n\t\t\tRSyntaxTextArea textArea = AbstractCodeArea.getDefaultArea(panel.getMainWindow());\n\t\t\ttextArea.setText(\"Image load error:\\n\" + Utils.getStackTrace(e));\n\t\t\tadd(textArea);\n\t\t}\n\t}\n\n\tprivate BufferedImage loadImage(JResource res) {\n\t\tResourceFile resFile = res.getResFile();\n\t\tResContainer resContainer = resFile.loadContent();\n\t\tResContainer.DataType dataType = resContainer.getDataType();\n\t\tif (dataType == ResContainer.DataType.DECODED_DATA) {\n\t\t\ttry {\n\t\t\t\treturn ImageIO.read(new ByteArrayInputStream(resContainer.getDecodedData()));\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new JadxRuntimeException(\"Failed to load image\", e);\n\t\t\t}\n\t\t} else if (dataType == ResContainer.DataType.RES_LINK) {\n\t\t\ttry {\n\t\t\t\treturn ResourcesLoader.decodeStream(resFile, (size, is) -> ImageIO.read(is));\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new JadxRuntimeException(\"Failed to load image\", e);\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(\"Unsupported resource image data type: \" + resFile);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void loadSettings() {\n\t\t// no op\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/panel/IssuesPanel.java",
    "content": "package jadx.gui.ui.panel;\n\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.ImageIcon;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\n\nimport ch.qos.logback.classic.Level;\n\nimport jadx.gui.logs.IssuesListener;\nimport jadx.gui.logs.LogCollector;\nimport jadx.gui.logs.LogOptions;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic class IssuesPanel extends JPanel {\n\tprivate static final long serialVersionUID = -7720576036668459218L;\n\n\tprivate static final ImageIcon ERROR_ICON = UiUtils.openSvgIcon(\"ui/error\");\n\tprivate static final ImageIcon WARN_ICON = UiUtils.openSvgIcon(\"ui/warning\");\n\n\tprivate final MainWindow mainWindow;\n\tprivate final IssuesListener issuesListener;\n\tprivate JLabel errorLabel;\n\tprivate JLabel warnLabel;\n\n\tpublic IssuesPanel(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t\tinitUI();\n\t\tthis.issuesListener = new IssuesListener(this);\n\t\tLogCollector.getInstance().registerListener(issuesListener);\n\t}\n\n\tpublic int getErrorsCount() {\n\t\treturn issuesListener.getErrors();\n\t}\n\n\tprivate void initUI() {\n\t\tJLabel label = new JLabel(NLS.str(\"issues_panel.label\"));\n\t\terrorLabel = new JLabel(ERROR_ICON);\n\t\twarnLabel = new JLabel(WARN_ICON);\n\n\t\tString toolTipText = NLS.str(\"issues_panel.tooltip\");\n\t\terrorLabel.setToolTipText(toolTipText);\n\t\twarnLabel.setToolTipText(toolTipText);\n\n\t\terrorLabel.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(MouseEvent e) {\n\t\t\t\tmainWindow.showLogViewer(LogOptions.allWithLevel(Level.ERROR));\n\t\t\t}\n\t\t});\n\t\twarnLabel.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(MouseEvent e) {\n\t\t\t\tmainWindow.showLogViewer(LogOptions.allWithLevel(Level.WARN));\n\t\t\t}\n\t\t});\n\n\t\tsetBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\n\t\tsetLayout(new BoxLayout(this, BoxLayout.X_AXIS));\n\t\tsetVisible(false);\n\t\tadd(label);\n\t\tadd(Box.createHorizontalGlue());\n\t\tadd(errorLabel);\n\t\tadd(Box.createHorizontalGlue());\n\t\tadd(warnLabel);\n\t}\n\n\tpublic void onUpdate(int error, int warnings) {\n\t\tif (error == 0 && warnings == 0) {\n\t\t\tsetVisible(false);\n\t\t\treturn;\n\t\t}\n\t\tsetVisible(true);\n\t\terrorLabel.setText(NLS.str(\"issues_panel.errors\", error));\n\t\terrorLabel.setVisible(error != 0);\n\t\twarnLabel.setText(NLS.str(\"issues_panel.warnings\", warnings));\n\t\twarnLabel.setVisible(warnings != 0);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/panel/JDebuggerPanel.java",
    "content": "package jadx.gui.ui.panel;\n\nimport java.awt.BorderLayout;\nimport java.awt.CardLayout;\nimport java.awt.Color;\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.Font;\nimport java.awt.KeyEventDispatcher;\nimport java.awt.KeyboardFocusManager;\nimport java.awt.Label;\nimport java.awt.Point;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.util.List;\n\nimport javax.swing.AbstractAction;\nimport javax.swing.Action;\nimport javax.swing.Box;\nimport javax.swing.DefaultComboBoxModel;\nimport javax.swing.DefaultListModel;\nimport javax.swing.ImageIcon;\nimport javax.swing.JButton;\nimport javax.swing.JComboBox;\nimport javax.swing.JList;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JSplitPane;\nimport javax.swing.JTabbedPane;\nimport javax.swing.JTextArea;\nimport javax.swing.JToolBar;\nimport javax.swing.JTree;\nimport javax.swing.SwingUtilities;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.DefaultTreeCellRenderer;\nimport javax.swing.tree.DefaultTreeModel;\nimport javax.swing.tree.TreeNode;\nimport javax.swing.tree.TreePath;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.StringUtils;\nimport jadx.gui.device.debugger.DebugController;\nimport jadx.gui.device.protocol.ADBDevice;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.codearea.SmaliArea;\nimport jadx.gui.ui.dialog.ADBDialog;\nimport jadx.gui.ui.popupmenu.VarTreePopupMenu;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic class JDebuggerPanel extends JPanel {\n\tprivate static final long serialVersionUID = -1111111202102181631L;\n\tprivate static final Logger LOG = LoggerFactory.getLogger(LogcatPanel.class);\n\n\tprivate static final ImageIcon ICON_RUN = UiUtils.openSvgIcon(\"debugger/execute\");\n\tprivate static final ImageIcon ICON_RERUN = UiUtils.openSvgIcon(\"debugger/rerun\");\n\tprivate static final ImageIcon ICON_PAUSE = UiUtils.openSvgIcon(\"debugger/threadFrozen\");\n\tprivate static final ImageIcon ICON_STOP = UiUtils.openSvgIcon(\"debugger/suspend\");\n\tprivate static final ImageIcon ICON_STOP_GRAY = UiUtils.openSvgIcon(\"debugger/suspendGray\");\n\tprivate static final ImageIcon ICON_STEP_INTO = UiUtils.openSvgIcon(\"debugger/traceInto\");\n\tprivate static final ImageIcon ICON_STEP_OVER = UiUtils.openSvgIcon(\"debugger/traceOver\");\n\tprivate static final ImageIcon ICON_STEP_OUT = UiUtils.openSvgIcon(\"debugger/stepOut\");\n\n\tprivate final transient MainWindow mainWindow;\n\tprivate final transient JList<IListElement> stackFrameList;\n\tprivate final transient JComboBox<IListElement> threadBox;\n\tprivate final transient JTextArea logger;\n\tprivate final transient JTree variableTree;\n\tprivate final transient DefaultTreeModel variableTreeModel;\n\tprivate final transient DefaultMutableTreeNode rootTreeNode;\n\tprivate final transient DefaultMutableTreeNode thisTreeNode;\n\tprivate final transient DefaultMutableTreeNode regTreeNode;\n\n\tprivate final transient JSplitPane rightSplitter;\n\tprivate final transient JSplitPane leftSplitter;\n\tprivate final transient IDebugController controller;\n\tprivate final LogcatPanel logcatPanel;\n\n\tprivate final transient VarTreePopupMenu varTreeMenu;\n\tprivate transient KeyEventDispatcher controllerShortCutDispatcher;\n\n\tpublic JDebuggerPanel(MainWindow mainWindow) {\n\t\tUiUtils.uiThreadGuard();\n\t\tthis.mainWindow = mainWindow;\n\t\tcontroller = new DebugController();\n\t\tthis.setLayout(new BorderLayout());\n\t\tthis.setMinimumSize(new Dimension(100, 150));\n\n\t\tleftSplitter = new JSplitPane();\n\t\trightSplitter = new JSplitPane();\n\n\t\tleftSplitter.setDividerLocation(mainWindow.getSettings().getDebuggerStackFrameSplitterLoc());\n\t\trightSplitter.setDividerLocation(mainWindow.getSettings().getDebuggerVarTreeSplitterLoc());\n\n\t\tJPanel stackFramePanel = new JPanel(new BorderLayout());\n\t\tthreadBox = new JComboBox<>();\n\t\tstackFrameList = new JList<>();\n\t\tthreadBox.setModel(new DefaultComboBoxModel<>());\n\t\tstackFrameList.setModel(new DefaultListModel<>());\n\n\t\tstackFramePanel.add(threadBox, BorderLayout.NORTH);\n\t\tstackFramePanel.add(new JScrollPane(stackFrameList), BorderLayout.CENTER);\n\n\t\tJPanel variablePanel = new JPanel(new CardLayout());\n\t\tvariableTree = new JTree();\n\t\tvariablePanel.add(new JScrollPane(variableTree));\n\n\t\trootTreeNode = new DefaultMutableTreeNode();\n\t\tthisTreeNode = new DefaultMutableTreeNode(\"this\");\n\t\tregTreeNode = new DefaultMutableTreeNode(\"var\");\n\t\trootTreeNode.add(thisTreeNode);\n\t\trootTreeNode.add(regTreeNode);\n\t\tvariableTreeModel = new DefaultTreeModel(rootTreeNode);\n\t\tvariableTree.setModel(variableTreeModel);\n\t\tvariableTree.expandPath(new TreePath(rootTreeNode.getPath()));\n\t\tvariableTree.setCellRenderer(new DefaultTreeCellRenderer() {\n\t\t\tprivate static final long serialVersionUID = -1111111202103170725L;\n\n\t\t\t@Override\n\t\t\tpublic Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row,\n\t\t\t\t\tboolean hasFocus) {\n\t\t\t\tComponent c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);\n\t\t\t\tif (value instanceof ValueTreeNode) {\n\t\t\t\t\tif (((ValueTreeNode) value).isUpdated()) {\n\t\t\t\t\t\tsetForeground(Color.RED);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn c;\n\t\t\t}\n\t\t});\n\n\t\tvarTreeMenu = new VarTreePopupMenu(mainWindow);\n\n\t\tJTabbedPane loggerPanel = new JTabbedPane();\n\t\tlogger = new JTextArea();\n\t\tlogger.setEditable(false);\n\t\tlogger.setLineWrap(true);\n\t\tJScrollPane loggerScroll = new JScrollPane(logger);\n\t\tloggerPanel.addTab(\"Debugger Log\", null, loggerScroll, null);\n\t\tthis.logcatPanel = new LogcatPanel(this);\n\t\tloggerPanel.addTab(NLS.str(\"logcat.logcat\"), null, logcatPanel, null);\n\n\t\tleftSplitter.setLeftComponent(stackFramePanel);\n\t\tleftSplitter.setRightComponent(rightSplitter);\n\t\tleftSplitter.setResizeWeight(MainWindow.SPLIT_PANE_RESIZE_WEIGHT);\n\n\t\trightSplitter.setLeftComponent(variablePanel);\n\t\trightSplitter.setRightComponent(loggerPanel);\n\t\trightSplitter.setResizeWeight(MainWindow.SPLIT_PANE_RESIZE_WEIGHT);\n\n\t\tJPanel headerPanel = new JPanel(new BorderLayout());\n\t\theaderPanel.add(new Label(), BorderLayout.WEST);\n\t\theaderPanel.add(initToolBar(), BorderLayout.CENTER);\n\t\tJButton closeBtn = new JButton(UiUtils.openSvgIcon(\"ui/close\"));\n\t\tcloseBtn.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(MouseEvent e) {\n\t\t\t\tif (controller.isDebugging()) {\n\t\t\t\t\tint what = JOptionPane.showConfirmDialog(mainWindow,\n\t\t\t\t\t\t\tNLS.str(\"debugger.cfm_dialog_msg\"),\n\t\t\t\t\t\t\tNLS.str(\"debugger.cfm_dialog_title\"),\n\t\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION);\n\t\t\t\t\tif (what == JOptionPane.OK_OPTION) {\n\t\t\t\t\t\tcontroller.exit();\n\t\t\t\t\t\tlogcatPanel.exit();\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tmainWindow.destroyDebuggerPanel();\n\t\t\t\t}\n\t\t\t\tunregShortcuts();\n\t\t\t}\n\t\t});\n\t\theaderPanel.add(closeBtn, BorderLayout.EAST);\n\n\t\tthis.add(headerPanel, BorderLayout.NORTH);\n\t\tthis.add(leftSplitter, BorderLayout.CENTER);\n\t\tlistenUIEvents();\n\t}\n\n\tpublic MainWindow getMainWindow() {\n\t\treturn mainWindow;\n\t}\n\n\tprivate void listenUIEvents() {\n\t\tstackFrameList.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(MouseEvent e) {\n\t\t\t\tif (e.getClickCount() % 2 == 0) {\n\t\t\t\t\tstackFrameSelected(e.getPoint());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tvariableTree.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(MouseEvent e) {\n\t\t\t\tif (SwingUtilities.isRightMouseButton(e)) {\n\t\t\t\t\ttreeNodeRightClicked(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate JToolBar initToolBar() {\n\t\tAbstractAction stepOver = new AbstractAction(NLS.str(\"debugger.step_over\"), ICON_STEP_OVER) {\n\t\t\tprivate static final long serialVersionUID = -1111111202103170726L;\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tcontroller.stepOver();\n\t\t\t}\n\t\t};\n\t\tstepOver.putValue(Action.SHORT_DESCRIPTION, NLS.str(\"debugger.step_over\"));\n\n\t\tAbstractAction stepInto = new AbstractAction(NLS.str(\"debugger.step_into\"), ICON_STEP_INTO) {\n\t\t\tprivate static final long serialVersionUID = -1111111202103170727L;\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tcontroller.stepInto();\n\t\t\t}\n\t\t};\n\t\tstepInto.putValue(Action.SHORT_DESCRIPTION, NLS.str(\"debugger.step_into\"));\n\n\t\tAbstractAction stepOut = new AbstractAction(NLS.str(\"debugger.step_out\"), ICON_STEP_OUT) {\n\t\t\tprivate static final long serialVersionUID = -1111111202103170728L;\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tcontroller.stepOut();\n\t\t\t}\n\t\t};\n\t\tstepOut.putValue(Action.SHORT_DESCRIPTION, NLS.str(\"debugger.step_out\"));\n\n\t\tAbstractAction stop = new AbstractAction(NLS.str(\"debugger.stop\"), ICON_STOP_GRAY) {\n\t\t\tprivate static final long serialVersionUID = -1111111202103170728L;\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tcontroller.stop();\n\t\t\t}\n\t\t};\n\t\tstop.putValue(Action.SHORT_DESCRIPTION, NLS.str(\"debugger.stop\"));\n\n\t\tAbstractAction run = new AbstractAction(NLS.str(\"debugger.run\"), ICON_RUN) {\n\t\t\tprivate static final long serialVersionUID = -1111111202103170728L;\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tif (controller.isDebugging()) {\n\t\t\t\t\tif (controller.isSuspended()) {\n\t\t\t\t\t\tcontroller.run();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcontroller.pause();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\trun.putValue(Action.SHORT_DESCRIPTION, NLS.str(\"debugger.run\"));\n\n\t\tAbstractAction rerun = new AbstractAction(NLS.str(\"debugger.rerun\"), ICON_RERUN) {\n\t\t\tprivate static final long serialVersionUID = -1111111202103210433L;\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tif (controller.isDebugging()) {\n\t\t\t\t\tcontroller.stop();\n\t\t\t\t}\n\t\t\t\tString pkgName = controller.getProcessName();\n\t\t\t\tif (pkgName.isEmpty() || !ADBDialog.launchForDebugging(mainWindow, pkgName, true)) {\n\t\t\t\t\t(new ADBDialog(mainWindow)).setVisible(true);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\trerun.putValue(Action.SHORT_DESCRIPTION, NLS.str(\"debugger.rerun\"));\n\n\t\tcontroller.setStateListener(new DebugController.StateListener() {\n\t\t\tboolean isGray = true;\n\n\t\t\t@Override\n\t\t\tpublic void onStateChanged(boolean suspended, boolean stopped) {\n\t\t\t\tUiUtils.uiRun(() -> {\n\t\t\t\t\tif (!stopped) {\n\t\t\t\t\t\tif (isGray) {\n\t\t\t\t\t\t\tstop.putValue(Action.SMALL_ICON, ICON_STOP);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstop.putValue(Action.SMALL_ICON, ICON_STOP_GRAY);\n\t\t\t\t\t\trun.putValue(Action.SMALL_ICON, ICON_RUN);\n\t\t\t\t\t\trun.putValue(Action.SHORT_DESCRIPTION, NLS.str(\"debugger.run\"));\n\t\t\t\t\t\tisGray = true;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (suspended) {\n\t\t\t\t\t\trun.putValue(Action.SMALL_ICON, ICON_RUN);\n\t\t\t\t\t\trun.putValue(Action.SHORT_DESCRIPTION, NLS.str(\"debugger.run\"));\n\t\t\t\t\t} else {\n\t\t\t\t\t\trun.putValue(Action.SMALL_ICON, ICON_PAUSE);\n\t\t\t\t\t\trun.putValue(Action.SHORT_DESCRIPTION, NLS.str(\"debugger.pause\"));\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\tJToolBar toolBar = new JToolBar();\n\t\ttoolBar.add(new Label());\n\t\ttoolBar.add(Box.createHorizontalGlue());\n\t\ttoolBar.add(rerun);\n\t\ttoolBar.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\ttoolBar.add(stop);\n\t\ttoolBar.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\ttoolBar.add(run);\n\t\ttoolBar.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\ttoolBar.add(stepOver);\n\t\ttoolBar.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\ttoolBar.add(stepInto);\n\t\ttoolBar.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\ttoolBar.add(stepOut);\n\t\ttoolBar.add(Box.createHorizontalGlue());\n\t\ttoolBar.add(new Label());\n\t\tregShortcuts();\n\t\treturn toolBar;\n\t}\n\n\tprivate void unregShortcuts() {\n\t\tKeyboardFocusManager\n\t\t\t\t.getCurrentKeyboardFocusManager()\n\t\t\t\t.removeKeyEventDispatcher(controllerShortCutDispatcher);\n\t}\n\n\tprivate void regShortcuts() {\n\t\tcontrollerShortCutDispatcher = new KeyEventDispatcher() {\n\t\t\t@Override\n\t\t\tpublic boolean dispatchKeyEvent(KeyEvent e) {\n\t\t\t\tif (e.getID() == KeyEvent.KEY_PRESSED\n\t\t\t\t\t\t&& mainWindow.getTabbedPane().getFocusedComp() instanceof SmaliArea) {\n\t\t\t\t\tif (e.getModifiersEx() == KeyEvent.SHIFT_DOWN_MASK\n\t\t\t\t\t\t\t&& e.getKeyCode() == KeyEvent.VK_F8) {\n\t\t\t\t\t\tcontroller.stepOut();\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tswitch (e.getKeyCode()) {\n\t\t\t\t\t\tcase KeyEvent.VK_F7:\n\t\t\t\t\t\t\tcontroller.stepInto();\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\tcase KeyEvent.VK_F8:\n\t\t\t\t\t\t\tcontroller.stepOver();\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\tcase KeyEvent.VK_F9:\n\t\t\t\t\t\t\tcontroller.run();\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\t\tKeyboardFocusManager.getCurrentKeyboardFocusManager()\n\t\t\t\t.addKeyEventDispatcher(controllerShortCutDispatcher);\n\t}\n\n\tprivate void treeNodeRightClicked(MouseEvent e) {\n\t\tTreePath path = variableTree.getPathForLocation(e.getX(), e.getY());\n\t\tif (path != null) {\n\t\t\tObject node = path.getLastPathComponent();\n\t\t\tif (node instanceof ValueTreeNode) {\n\t\t\t\tvarTreeMenu.show((ValueTreeNode) node, e.getComponent(), e.getX(), e.getY());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void stackFrameSelected(Point p) {\n\t\tint loc = stackFrameList.locationToIndex(p);\n\t\tif (loc > -1) {\n\t\t\tIListElement ele = stackFrameList.getModel().getElementAt(loc);\n\t\t\tif (ele != null) {\n\t\t\t\tele.onSelected();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic boolean showDebugger(String procName, String host, int port, int androidVer, ADBDevice device, String pid) {\n\t\tboolean ok = controller.startDebugger(this, host, port, androidVer);\n\t\tif (ok) {\n\t\t\tUiUtils.uiRun(() -> {\n\t\t\t\tlog(String.format(\"Attached %s %s:%d\", procName, host, port));\n\t\t\t\ttry {\n\t\t\t\t\tlogcatPanel.init(device, pid);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog(NLS.str(\"logcat.error_fail_start\"));\n\t\t\t\t\tLOG.error(\"Logcat failed to start\", e);\n\t\t\t\t}\n\t\t\t\tleftSplitter.setDividerLocation(mainWindow.getSettings().getDebuggerStackFrameSplitterLoc());\n\t\t\t\trightSplitter.setDividerLocation(mainWindow.getSettings().getDebuggerVarTreeSplitterLoc());\n\t\t\t\tmainWindow.showDebuggerPanel();\n\t\t\t});\n\t\t}\n\t\treturn ok;\n\t}\n\n\tpublic IDebugController getDbgController() {\n\t\treturn controller;\n\t}\n\n\tpublic int getLeftSplitterLocation() {\n\t\treturn leftSplitter.getDividerLocation();\n\t}\n\n\tpublic int getRightSplitterLocation() {\n\t\treturn rightSplitter.getDividerLocation();\n\t}\n\n\tpublic void loadSettings() {\n\t\tUiUtils.uiThreadGuard();\n\n\t\tFont font = mainWindow.getSettings().getCodeFont();\n\t\tvariableTree.setFont(font.deriveFont(font.getSize() + 1.f));\n\t\tvariableTree.setRowHeight(-1);\n\t\tstackFrameList.setFont(font);\n\t\tthreadBox.setFont(font);\n\t\tlogger.setFont(font);\n\t}\n\n\tpublic void resetUI() {\n\t\tUiUtils.uiThreadGuard();\n\n\t\tthisTreeNode.removeAllChildren();\n\t\tregTreeNode.removeAllChildren();\n\n\t\tclearFrameAndThreadList();\n\n\t\tthreadBox.updateUI();\n\t\tstackFrameList.updateUI();\n\t\tvariableTreeModel.reload(rootTreeNode);\n\t\tvariableTree.expandPath(new TreePath(rootTreeNode.getPath()));\n\t\tlogger.setText(\"\");\n\t}\n\n\tpublic void scrollToSmaliLine(JClass cls, int pos, boolean debugMode) {\n\t\tSwingUtilities.invokeLater(() -> getMainWindow().getTabsController().smaliJump(cls, pos, debugMode));\n\t}\n\n\tpublic void resetAllDebuggingInfo() {\n\t\tUiUtils.uiThreadGuard();\n\t\tclearFrameAndThreadList();\n\t\tresetRegTreeNodes();\n\t\tresetThisTreeNodes();\n\t}\n\n\tpublic void resetThisTreeNodes() {\n\t\tthisTreeNode.removeAllChildren();\n\t\tSwingUtilities.invokeLater(() -> variableTreeModel.reload(thisTreeNode));\n\t}\n\n\tpublic void resetRegTreeNodes() {\n\t\tregTreeNode.removeAllChildren();\n\t\tSwingUtilities.invokeLater(() -> variableTreeModel.reload(regTreeNode));\n\t}\n\n\tpublic void updateRegTreeNodes(List<? extends ValueTreeNode> nodes) {\n\t\tnodes.forEach(regTreeNode::add);\n\t}\n\n\tpublic void updateThisFieldNodes(List<? extends ValueTreeNode> nodes) {\n\t\tnodes.forEach(thisTreeNode::add);\n\t}\n\n\tpublic void refreshThreadBox(List<? extends IListElement> elements) {\n\t\tUiUtils.uiRun(() -> {\n\t\t\tif (!elements.isEmpty()) {\n\t\t\t\tDefaultComboBoxModel<IListElement> model = (DefaultComboBoxModel<IListElement>) threadBox.getModel();\n\t\t\t\telements.forEach(model::addElement);\n\t\t\t}\n\t\t\tthreadBox.updateUI();\n\t\t\tstackFrameList.setFont(mainWindow.getSettings().getCodeFont());\n\t\t});\n\t}\n\n\tpublic void refreshStackFrameList(List<? extends IListElement> elements) {\n\t\tUiUtils.uiRun(() -> {\n\t\t\tif (!elements.isEmpty()) {\n\t\t\t\tDefaultListModel<IListElement> model =\n\t\t\t\t\t\t(DefaultListModel<IListElement>) stackFrameList.getModel();\n\t\t\t\tmodel.addAll(elements);\n\t\t\t\tstackFrameList.setFont(mainWindow.getSettings().getCodeFont());\n\t\t\t}\n\t\t\tstackFrameList.repaint();\n\t\t});\n\t}\n\n\tpublic void refreshRegisterTree() {\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\tvariableTreeModel.reload(regTreeNode);\n\t\t\tvariableTree.expandPath(new TreePath(regTreeNode.getPath()));\n\t\t});\n\t}\n\n\tpublic void refreshThisFieldTree() {\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\tboolean expanded = variableTree.isExpanded(new TreePath(thisTreeNode.getPath()));\n\t\t\tvariableTreeModel.reload(thisTreeNode);\n\t\t\tif (expanded) {\n\t\t\t\tvariableTree.expandPath(new TreePath(regTreeNode.getPath()));\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic void clearFrameAndThreadList() {\n\t\t((DefaultListModel<IListElement>) stackFrameList.getModel()).removeAllElements();\n\t\t((DefaultComboBoxModel<IListElement>) threadBox.getModel()).removeAllElements();\n\t}\n\n\tpublic void log(String msg) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\" > \")\n\t\t\t\t.append(StringUtils.getDateText())\n\t\t\t\t.append(\" \")\n\t\t\t\t.append(msg)\n\t\t\t\t.append(\"\\n\");\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\tlogger.append(sb.toString());\n\t\t});\n\t}\n\n\tpublic void updateRegTree(ValueTreeNode node) {\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\tvariableTreeModel.reload(regTreeNode);\n\t\t\tscrollToUpdatedNode(node);\n\t\t});\n\t}\n\n\tpublic void updateThisTree(ValueTreeNode node) {\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\tvariableTreeModel.reload(thisTreeNode);\n\t\t\tscrollToUpdatedNode(node);\n\t\t});\n\t}\n\n\tpublic void scrollToUpdatedNode(ValueTreeNode node) {\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\tTreeNode[] path = node.getPath();\n\t\t\tvariableTree.scrollPathToVisible(new TreePath(path));\n\t\t});\n\t}\n\n\tpublic abstract static class ValueTreeNode extends DefaultMutableTreeNode {\n\t\tprivate static final long serialVersionUID = -1111111202103122236L;\n\n\t\tprivate boolean updated;\n\n\t\tpublic void setUpdated(boolean updated) {\n\t\t\tthis.updated = updated;\n\t\t}\n\n\t\tpublic boolean isUpdated() {\n\t\t\treturn updated;\n\t\t}\n\n\t\tpublic abstract String getName();\n\n\t\t@Nullable\n\t\tpublic abstract String getValue();\n\n\t\t@Nullable\n\t\tpublic abstract String getType();\n\n\t\tpublic abstract long getTypeID();\n\n\t\tpublic abstract ValueTreeNode updateValue(String val);\n\n\t\tpublic abstract ValueTreeNode updateType(String val);\n\n\t\tpublic abstract ValueTreeNode updateTypeID(long id);\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tStringBuilder sb = new StringBuilder(64);\n\t\t\tsb.append(getName());\n\t\t\tString val = getValue();\n\t\t\tif (val != null) {\n\t\t\t\tsb.append(\" val: \").append(val).append(\",\");\n\t\t\t}\n\t\t\tString type = getType();\n\t\t\tif (type != null) {\n\t\t\t\tsb.append(\" type: \").append(getType());\n\t\t\t\tlong id = getTypeID();\n\t\t\t\tif (id > 0) {\n\t\t\t\t\tsb.append(\"@\").append(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (val == null && type == null) {\n\t\t\t\tsb.append(\" undefined\");\n\t\t\t}\n\t\t\treturn sb.toString();\n\t\t}\n\t}\n\n\tpublic interface IListElement {\n\t\tvoid onSelected();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/panel/LogcatPanel.java",
    "content": "package jadx.gui.ui.panel;\n\nimport java.awt.BorderLayout;\nimport java.awt.Color;\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.EventQueue;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.Insets;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport javax.swing.AbstractAction;\nimport javax.swing.Action;\nimport javax.swing.BoundedRangeModel;\nimport javax.swing.Box;\nimport javax.swing.ImageIcon;\nimport javax.swing.JCheckBox;\nimport javax.swing.JComboBox;\nimport javax.swing.JLabel;\nimport javax.swing.JList;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPanel;\nimport javax.swing.JPopupMenu;\nimport javax.swing.JScrollBar;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTextPane;\nimport javax.swing.JToolBar;\nimport javax.swing.ListCellRenderer;\nimport javax.swing.text.AttributeSet;\nimport javax.swing.text.SimpleAttributeSet;\nimport javax.swing.text.StyleConstants;\nimport javax.swing.text.StyleContext;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.device.debugger.LogcatController;\nimport jadx.gui.device.protocol.ADB;\nimport jadx.gui.device.protocol.ADBDevice;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.ui.NodeLabel;\n\npublic class LogcatPanel extends JPanel {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(LogcatPanel.class);\n\n\tStyleContext sc = StyleContext.getDefaultStyleContext();\n\tprivate final AttributeSet defaultAset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.decode(\"#6c71c4\"));\n\tprivate final AttributeSet verboseAset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.decode(\"#2aa198\"));\n\tprivate final AttributeSet debugAset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.decode(\"#859900\"));\n\tprivate final AttributeSet infoAset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.decode(\"#586e75\"));\n\tprivate final AttributeSet warningAset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.decode(\"#b58900\"));\n\tprivate final AttributeSet errorAset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.decode(\"#dc322f\"));\n\tprivate final AttributeSet fatalAset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.decode(\"#d33682\"));\n\tprivate final AttributeSet silentAset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.decode(\"#002b36\"));\n\n\tprivate final Map<Byte, AttributeSet> asetMap =\n\t\t\tMap.of((byte) 1, defaultAset, (byte) 2, verboseAset, (byte) 3, debugAset, (byte) 4, infoAset, (byte) 5, warningAset,\n\t\t\t\t\t(byte) 6, errorAset, (byte) 7, fatalAset, (byte) 8, silentAset);\n\n\tprivate static final ImageIcon ICON_PAUSE = UiUtils.openSvgIcon(\"debugger/threadFrozen\");\n\tprivate static final ImageIcon ICON_RUN = UiUtils.openSvgIcon(\"debugger/execute\");\n\tprivate static final ImageIcon CLEAR_LOGCAT = UiUtils.openSvgIcon(\"debugger/trash\");\n\n\tprivate transient JTextPane logcatPane;\n\tprivate final transient JDebuggerPanel debugPanel;\n\tprivate LogcatController logcatController;\n\tprivate boolean ready = false;\n\tprivate List<ADB.Process> procs;\n\n\tpublic LogcatPanel(JDebuggerPanel debugPanel) {\n\t\tthis.debugPanel = debugPanel;\n\t}\n\n\tprivate List<Integer> pids;\n\tprivate JScrollPane logcatScroll;\n\tprivate int pid;\n\n\tprivate final AbstractAction pauseButton = new AbstractAction(NLS.str(\"logcat.pause\"), ICON_PAUSE) {\n\t\t@Override\n\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\ttoggleLogcat();\n\t\t}\n\t};\n\n\tprivate final AbstractAction clearButton = new AbstractAction(NLS.str(\"logcat.clear\"), CLEAR_LOGCAT) {\n\t\t@Override\n\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\tclearLogcat();\n\t\t}\n\t};\n\n\tpublic boolean showLogcat() {\n\t\tthis.removeAll();\n\n\t\tList<String> pkgs = new ArrayList<>();\n\t\tpids = new ArrayList<>();\n\t\tJPanel procBox;\n\t\tfor (ADB.Process proc : procs.subList(1, procs.size())) { // skipping first element because it contains the column label\n\t\t\tpkgs.add(String.format(\"[pid: %-6s] %s\", proc.pid, proc.name));\n\t\t\tpids.add(Integer.valueOf(proc.pid));\n\t\t}\n\n\t\tString[] msgTypes = {\n\t\t\t\tNLS.str(\"logcat.default\"),\n\t\t\t\tNLS.str(\"logcat.verbose\"),\n\t\t\t\tNLS.str(\"logcat.debug\"),\n\t\t\t\tNLS.str(\"logcat.info\"),\n\t\t\t\tNLS.str(\"logcat.warn\"),\n\t\t\t\tNLS.str(\"logcat.error\"),\n\t\t\t\tNLS.str(\"logcat.fatal\"),\n\t\t\t\tNLS.str(\"logcat.silent\") };\n\t\tInteger[] msgIndex = { 1, 2, 3, 4, 5, 6, 7, 8 };\n\n\t\tthis.setLayout(new BorderLayout());\n\t\tlogcatPane = new JTextPane();\n\t\tlogcatPane.setEditable(false);\n\t\tlogcatScroll = new JScrollPane(logcatPane);\n\t\tJToolBar menuPanel = new JToolBar();\n\n\t\tCheckCombo procObj = new CheckCombo(NLS.str(\"logcat.process\"), 1, pids.toArray(new Integer[0]), pkgs.toArray(new String[0]));\n\t\tprocBox = procObj.getContent();\n\t\tprocObj.selectAllBut(this.pids.indexOf(this.pid));\n\n\t\tJPanel msgTypeBox = new CheckCombo(NLS.str(\"logcat.level\"), 2, msgIndex, msgTypes).getContent();\n\n\t\tmenuPanel.add(procBox);\n\t\tmenuPanel.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tmenuPanel.add(msgTypeBox);\n\t\tmenuPanel.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tmenuPanel.add(pauseButton);\n\t\tmenuPanel.add(Box.createRigidArea(new Dimension(5, 0)));\n\t\tmenuPanel.add(clearButton);\n\n\t\tthis.add(menuPanel, BorderLayout.NORTH);\n\t\tthis.add(logcatScroll, BorderLayout.CENTER);\n\n\t\treturn true;\n\t}\n\n\tpublic boolean clearLogcatArea() {\n\t\tlogcatPane.setText(\"\");\n\t\treturn true;\n\t}\n\n\tpublic boolean init(ADBDevice device, String pid) {\n\t\tthis.pid = Integer.parseInt(pid);\n\t\ttry {\n\t\t\tthis.logcatController = new LogcatController(this, device);\n\t\t\tthis.procs = device.getProcessList();\n\t\t\tif (!this.showLogcat()) {\n\t\t\t\tdebugPanel.log(NLS.str(\"logcat.error_fail_start\"));\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthis.ready = false;\n\t\t\tLOG.error(\"Failed to start logcat\", e);\n\t\t\treturn false;\n\t\t}\n\t\tthis.ready = true;\n\t\treturn true;\n\t}\n\n\tprivate void toggleLogcat() {\n\t\tif (Objects.equals(this.logcatController.getStatus(), \"running\")) {\n\t\t\tthis.logcatController.stopLogcat();\n\t\t\tthis.pauseButton.putValue(Action.SMALL_ICON, ICON_RUN);\n\t\t\tthis.pauseButton.putValue(Action.NAME, NLS.str(\"logcat.start\"));\n\t\t} else if (Objects.equals(this.logcatController.getStatus(), \"stopped\")) {\n\t\t\tthis.logcatController.startLogcat();\n\t\t\tthis.pauseButton.putValue(Action.SMALL_ICON, ICON_PAUSE);\n\t\t\tthis.pauseButton.putValue(Action.NAME, NLS.str(\"logcat.pause\"));\n\t\t}\n\t}\n\n\tprivate void clearLogcat() {\n\t\tboolean running = false;\n\t\tif (Objects.equals(this.logcatController.getStatus(), \"running\")) {\n\t\t\tthis.logcatController.stopLogcat();\n\t\t\trunning = true;\n\t\t}\n\t\tthis.logcatController.clearLogcat();\n\t\tclearLogcatArea();\n\t\tthis.debugPanel.log(this.logcatController.getStatus());\n\t\tif (running) {\n\t\t\tthis.logcatController.startLogcat();\n\t\t}\n\t}\n\n\tpublic boolean isReady() {\n\t\treturn this.ready;\n\t}\n\n\tprivate boolean isAtBottom(JScrollBar scrollbar) {\n\t\tBoundedRangeModel model = scrollbar.getModel();\n\t\treturn (model.getExtent() + model.getValue()) == model.getMaximum();\n\t}\n\n\tpublic void log(LogcatController.LogcatInfo logcatInfo) {\n\t\tint len = logcatPane.getDocument().getLength();\n\t\tJScrollBar scrollbar = logcatScroll.getVerticalScrollBar();\n\t\tboolean atBottom = isAtBottom(scrollbar);\n\n\t\tString logString = \" > \" + logcatInfo.getTimestamp() + \" [pid: \" + logcatInfo.getPid() + \"] \"\n\t\t\t\t+ logcatInfo.getMsgTypeString() + \": \" + logcatInfo.getMsg() + \"\\n\";\n\n\t\tif (logcatInfo.getMsgType() == 0) {\n\t\t\treturn; // ignore unknown\n\t\t}\n\n\t\tAttributeSet attrSet = asetMap.get(logcatInfo.getMsgType());\n\n\t\tUiUtils.uiRun(() -> {\n\t\t\ttry {\n\t\t\t\tlogcatPane.getDocument().insertString(len, logString, attrSet);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to add logcat message\", e);\n\t\t\t}\n\t\t\tif (atBottom) {\n\t\t\t\tEventQueue.invokeLater(() -> scrollbar.setValue(scrollbar.getMaximum()));\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic void exit() {\n\t\tlogcatController.exit();\n\t\tclearLogcatArea();\n\t\tthis.logcatController.clearEvents();\n\n\t}\n\n\tclass CheckCombo implements ActionListener {\n\t\tprivate final String[] ids;\n\t\tprivate final int type;\n\t\tprivate final String label;\n\t\tprivate final Integer[] index;\n\t\tprivate JComboBox<CheckComboStore> combo;\n\n\t\tpublic CheckCombo(String label, int type, Integer[] index, String[] ids) {\n\t\t\tthis.ids = ids;\n\t\t\tthis.type = type;\n\t\t\tthis.label = label;\n\t\t\tthis.index = index;\n\n\t\t}\n\n\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\tJComboBox<?> cb = (JComboBox<?>) e.getSource();\n\t\t\tCheckComboStore store = (CheckComboStore) cb.getSelectedItem();\n\t\t\tCheckComboRenderer ccr = (CheckComboRenderer) cb.getRenderer();\n\t\t\tstore.state = !store.state;\n\t\t\tccr.checkBox.setSelected(store.state);\n\n\t\t\tswitch (this.type) {\n\t\t\t\tcase 1: // process\n\t\t\t\t\tlogcatController.getFilter().togglePid(store.index, store.state);\n\t\t\t\t\tlogcatController.reload();\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2: // label\n\t\t\t\t\tlogcatController.getFilter().toggleMsgType((byte) store.index, store.state);\n\t\t\t\t\tlogcatController.reload();\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tLOG.error(\"Invalid Logcat Filter Type\");\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tpublic JPanel getContent() {\n\t\t\tJLabel label = NodeLabel.noHtml(this.label + \": \");\n\t\t\tCheckComboStore[] stores = new CheckComboStore[ids.length];\n\t\t\tfor (int j = 0; j < ids.length; j++) {\n\t\t\t\tstores[j] = new CheckComboStore(index[j], ids[j], Boolean.TRUE);\n\t\t\t}\n\t\t\tcombo = new JComboBox<>(stores);\n\t\t\tcombo.setRenderer(new CheckComboRenderer());\n\t\t\tJPanel panel = new JPanel();\n\t\t\tpanel.setLayout(new GridBagLayout());\n\t\t\tGridBagConstraints c = new GridBagConstraints();\n\t\t\tc.weightx = 0;\n\t\t\tc.gridwidth = 1;\n\t\t\tc.insets = new Insets(0, 1, 0, 1);\n\t\t\tpanel.add(label, c);\n\t\t\tc.weightx = 1;\n\t\t\tc.gridwidth = GridBagConstraints.REMAINDER;\n\t\t\tc.anchor = GridBagConstraints.WEST;\n\t\t\tc.insets = new Insets(0, 1, 0, 1);\n\t\t\tpanel.add(combo, c);\n\t\t\tcombo.addActionListener(this);\n\t\t\tcombo.addMouseListener(new FilterClickListener(this));\n\t\t\treturn panel;\n\t\t}\n\n\t\tpublic void toggleAll(boolean checked) {\n\t\t\tfor (int i = 0; i < combo.getItemCount(); i++) {\n\t\t\t\tCheckComboStore ccs = combo.getItemAt(i);\n\t\t\t\tccs.state = checked;\n\t\t\t\tswitch (type) {\n\t\t\t\t\tcase 1: // process\n\t\t\t\t\t\tlogcatController.getFilter().togglePid(ccs.index, checked);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2: // level\n\t\t\t\t\t\tlogcatController.getFilter().toggleMsgType((byte) ccs.index, checked);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tLOG.error(\"Invalid Logcat Toggle Filter Encountered\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tlogcatController.reload();\n\t\t}\n\n\t\tpublic void selectAllBut(int ind) {\n\t\t\tfor (int i = 0; i < combo.getItemCount(); i++) {\n\t\t\t\tCheckComboStore ccs = combo.getItemAt(i);\n\t\t\t\tccs.state = (i == ind);\n\t\t\t\tswitch (type) {\n\t\t\t\t\tcase 1: // process\n\t\t\t\t\t\tlogcatController.getFilter().togglePid(ccs.index, ccs.state);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2: // level\n\t\t\t\t\t\tlogcatController.getFilter().toggleMsgType((byte) ccs.index, ccs.state);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tLOG.error(\"Invalid Logcat selectAllBut filter encountered\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tlogcatController.reload();\n\t\t}\n\t}\n\n\tprivate static class CheckComboRenderer implements ListCellRenderer {\n\t\tprivate final JCheckBox checkBox = new JCheckBox();\n\n\t\tpublic Component getListCellRendererComponent(JList list, Object value,\n\t\t\t\tint index, boolean isSelected, boolean cellHasFocus) {\n\t\t\tCheckComboStore store = (CheckComboStore) value;\n\t\t\tcheckBox.setText(store.id);\n\t\t\tcheckBox.setSelected(store.state);\n\t\t\treturn checkBox;\n\t\t}\n\t}\n\n\tstatic class CheckComboStore {\n\t\tString id;\n\t\tBoolean state;\n\t\tint index;\n\n\t\tpublic CheckComboStore(int index, String id, Boolean state) {\n\t\t\tthis.id = id;\n\t\t\tthis.state = state;\n\t\t\tthis.index = index;\n\t\t}\n\t}\n\n\tclass FilterClickListener extends MouseAdapter {\n\t\tCheckCombo combo;\n\n\t\tpublic FilterClickListener(CheckCombo combo) {\n\t\t\tthis.combo = combo;\n\t\t}\n\n\t\tpublic void mousePressed(MouseEvent e) {\n\t\t\tif (e.isPopupTrigger()) {\n\t\t\t\tdoPop(e);\n\t\t\t}\n\t\t}\n\n\t\tpublic void mouseReleased(MouseEvent e) {\n\t\t\tif (e.isPopupTrigger()) {\n\t\t\t\tdoPop(e);\n\t\t\t}\n\t\t}\n\n\t\tprivate void doPop(MouseEvent e) {\n\t\t\tFilterPopup menu = new FilterPopup(combo);\n\t\t\tmenu.show(e.getComponent(), e.getX(), e.getY());\n\t\t}\n\t}\n\n\tclass FilterPopup extends JPopupMenu {\n\t\tCheckCombo combo;\n\t\tJMenuItem selectAll;\n\t\tJMenuItem unselectAll;\n\t\tJMenuItem selectAttached;\n\n\t\tpublic FilterPopup(CheckCombo combo) {\n\t\t\tthis.combo = combo;\n\t\t\tselectAll = new JMenuItem(NLS.str(\"logcat.select_all\"));\n\t\t\tselectAll.addActionListener(actionEvent -> combo.toggleAll(true));\n\n\t\t\tunselectAll = new JMenuItem(NLS.str(\"logcat.unselect_all\"));\n\t\t\tunselectAll.addActionListener(actionEvent -> combo.toggleAll(false));\n\n\t\t\tif (combo.type == 1) {\n\t\t\t\tselectAttached = new JMenuItem(NLS.str(\"logcat.select_attached\"));\n\t\t\t\tselectAttached.addActionListener(actionEvent -> combo.selectAllBut(pids.indexOf(pid)));\n\t\t\t\tadd(selectAttached);\n\t\t\t}\n\n\t\t\tadd(selectAll);\n\t\t\tadd(unselectAll);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/panel/ProgressPanel.java",
    "content": "package jadx.gui.ui.panel;\n\nimport java.awt.Dimension;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.BoxLayout;\nimport javax.swing.Icon;\nimport javax.swing.JButton;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JProgressBar;\n\nimport jadx.gui.jobs.ITaskProgress;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.UiUtils;\n\npublic class ProgressPanel extends JPanel {\n\tprivate static final long serialVersionUID = -3238438119672015733L;\n\n\tprivate final JProgressBar progressBar;\n\tprivate final JLabel progressLabel;\n\tprivate final JButton cancelButton;\n\tprivate final boolean showCancelButton;\n\n\tpublic ProgressPanel(final MainWindow mainWindow, boolean showCancelButton) {\n\t\tthis.showCancelButton = showCancelButton;\n\n\t\tprogressLabel = new JLabel();\n\t\tprogressBar = new JProgressBar(0, 100);\n\t\tprogressBar.setIndeterminate(true);\n\t\tprogressBar.setStringPainted(false);\n\t\tprogressLabel.setLabelFor(progressBar);\n\n\t\tsetBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));\n\t\tsetLayout(new BoxLayout(this, BoxLayout.X_AXIS));\n\t\tsetVisible(false);\n\t\tadd(progressLabel);\n\t\tadd(progressBar);\n\n\t\tIcon cancelIcon = Icons.ICON_CLOSE;\n\t\tcancelButton = new JButton(cancelIcon);\n\t\tcancelButton.setPreferredSize(new Dimension(cancelIcon.getIconWidth(), cancelIcon.getIconHeight()));\n\t\tcancelButton.setToolTipText(\"Cancel background jobs\");\n\t\tcancelButton.setBorderPainted(false);\n\t\tcancelButton.setFocusPainted(false);\n\t\tcancelButton.setContentAreaFilled(false);\n\t\tcancelButton.addActionListener(e -> mainWindow.cancelBackgroundJobs());\n\t\tcancelButton.setVisible(showCancelButton);\n\t\tadd(cancelButton);\n\t}\n\n\tpublic void reset() {\n\t\tcancelButton.setVisible(showCancelButton);\n\t\tprogressBar.setIndeterminate(true);\n\t\tprogressBar.setValue(0);\n\t\tprogressBar.setString(\"\");\n\t\tprogressBar.setStringPainted(true);\n\t}\n\n\tpublic void setProgress(ITaskProgress taskProgress) {\n\t\tint progress = taskProgress.progress();\n\t\tint total = taskProgress.total();\n\t\tif (progress == 0 || total == 0) {\n\t\t\tprogressBar.setIndeterminate(true);\n\t\t} else {\n\t\t\tif (progressBar.isIndeterminate()) {\n\t\t\t\tprogressBar.setIndeterminate(false);\n\t\t\t}\n\t\t\tsetProgress(UiUtils.calcProgress(progress, total));\n\t\t}\n\t}\n\n\tprivate void setProgress(int progress) {\n\t\tprogressBar.setIndeterminate(false);\n\t\tprogressBar.setValue(progress);\n\t\tprogressBar.setString(progress + \"%\");\n\t\tprogressBar.setStringPainted(true);\n\t}\n\n\tpublic void setLabel(String label) {\n\t\tprogressLabel.setText(label);\n\t}\n\n\tpublic void setIndeterminate(boolean newValue) {\n\t\tprogressBar.setIndeterminate(newValue);\n\t}\n\n\tpublic void setCancelButtonVisible(boolean visible) {\n\t\tcancelButton.setVisible(visible);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/panel/SimpleCodePanel.java",
    "content": "package jadx.gui.ui.panel;\n\nimport java.awt.BorderLayout;\nimport java.awt.Dimension;\nimport java.awt.Point;\nimport java.awt.Rectangle;\nimport java.awt.geom.Rectangle2D;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.SwingUtilities;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.fife.ui.rtextarea.RTextScrollPane;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.settings.LineNumbersMode;\nimport jadx.gui.treemodel.CodeNode;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.codearea.AbstractCodeArea;\nimport jadx.gui.utils.NLS;\n\n// The code panel class is used to display the code of the selected node.\npublic class SimpleCodePanel extends JPanel {\n\tprivate static final long serialVersionUID = -4073178549744330905L;\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SimpleCodePanel.class);\n\n\tprivate final RSyntaxTextArea codeArea;\n\tprivate final RTextScrollPane codeScrollPane;\n\tprivate final JLabel titleLabel;\n\n\tpublic SimpleCodePanel(MainWindow mainWindow) {\n\t\tJadxSettings settings = mainWindow.getSettings();\n\n\t\tsetLayout(new BorderLayout(5, 5));\n\t\tsetBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\n\n\t\t// Set the minimum size to ensure the panel is not completely minimized\n\t\tsetMinimumSize(new Dimension(300, 400));\n\t\tsetPreferredSize(new Dimension(800, 600));\n\n\t\t// The title label\n\t\ttitleLabel = new JLabel(NLS.str(\"usage_dialog_plus.code_view\"));\n\t\ttitleLabel.setFont(settings.getCodeFont());\n\t\ttitleLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 10, 5));\n\n\t\t// The code area\n\t\tcodeArea = AbstractCodeArea.getDefaultArea(mainWindow);\n\t\tcodeArea.setText(\"// \" + NLS.str(\"usage_dialog_plus.select_node\"));\n\n\t\tcodeScrollPane = new RTextScrollPane(codeArea);\n\t\tcodeScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);\n\t\tcodeScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);\n\n\t\tadd(titleLabel, BorderLayout.NORTH);\n\t\tadd(codeScrollPane, BorderLayout.CENTER);\n\n\t\tapplySettings(settings);\n\t}\n\n\tprivate void applySettings(JadxSettings settings) {\n\t\tcodeScrollPane.setLineNumbersEnabled(settings.getLineNumbersMode() != LineNumbersMode.DISABLE);\n\t\tcodeScrollPane.getGutter().setLineNumberFont(settings.getCodeFont());\n\t\tcodeArea.setFont(settings.getCodeFont());\n\t}\n\n\tpublic void showCode(JNode node, String codeLine) {\n\t\tif (node != null) {\n\t\t\ttitleLabel.setText(NLS.str(\"usage_dialog_plus.code_for\", node.makeLongString()));\n\t\t\tcodeArea.setSyntaxEditingStyle(node.getSyntaxName());\n\n\t\t\t// Get the complete code\n\t\t\tString contextCode = getContextCode(node, codeLine);\n\t\t\tcodeArea.setText(contextCode);\n\n\t\t\t// Highlight the key line and scroll to that position\n\t\t\tscrollToCodeLine(codeArea, codeLine);\n\n\t\t\t// If it is a CodeNode, we can get a more precise position\n\t\t\tif (node instanceof CodeNode) {\n\t\t\t\tCodeNode codeNode = (CodeNode) node;\n\t\t\t\tint pos = codeNode.getPos();\n\t\t\t\tif (pos > 0) {\n\t\t\t\t\t// Try to use the position information to more accurately locate\n\t\t\t\t\ttry {\n\t\t\t\t\t\tString text = codeArea.getText();\n\t\t\t\t\t\tint lineNum = 0;\n\t\t\t\t\t\tint curPos = 0;\n\t\t\t\t\t\t// Calculate the line number corresponding to the position\n\t\t\t\t\t\tfor (int i = 0; i < text.length() && curPos <= pos; i++) {\n\t\t\t\t\t\t\tif (text.charAt(i) == '\\n') {\n\t\t\t\t\t\t\t\tlineNum++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcurPos++;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (lineNum > 0) {\n\t\t\t\t\t\t\t// Scroll to the calculated line number\n\t\t\t\t\t\t\tint finalLineNum = lineNum;\n\t\t\t\t\t\t\tSwingUtilities.invokeLater(() -> {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tRectangle2D lineRect = codeArea\n\t\t\t\t\t\t\t\t\t\t\t.modelToView2D(codeArea.getLineStartOffset(finalLineNum));\n\t\t\t\t\t\t\t\t\tif (lineRect != null) {\n\t\t\t\t\t\t\t\t\t\tJScrollPane scrollPane = (JScrollPane) codeArea.getParent().getParent();\n\t\t\t\t\t\t\t\t\t\tRectangle viewRect = scrollPane.getViewport().getViewRect();\n\t\t\t\t\t\t\t\t\t\tint y = (int) (lineRect.getY() - (viewRect.height - lineRect.getHeight()) / 2);\n\t\t\t\t\t\t\t\t\t\tif (y < 0) {\n\t\t\t\t\t\t\t\t\t\t\ty = 0;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tscrollPane.getViewport().setViewPosition(new Point(0, y));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t\t// Fall back to using string matching\n\t\t\t\t\t\t\t\t\tscrollToCodeLine(codeArea, codeLine);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t// Fall back to using string matching\n\t\t\t\t\t\tscrollToCodeLine(codeArea, codeLine);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// If there is no position information, use string matching\n\t\t\t\t\tscrollToCodeLine(codeArea, codeLine);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Not a CodeNode, use string matching\n\t\t\t\tscrollToCodeLine(codeArea, codeLine);\n\t\t\t}\n\t\t} else {\n\t\t\ttitleLabel.setText(NLS.str(\"usage_dialog_plus.code_view\"));\n\t\t\tcodeArea.setText(\"// \" + NLS.str(\"usage_dialog_plus.select_node\"));\n\t\t}\n\t}\n\n\tprivate String getContextCode(JNode node, String codeLine) {\n\t\t// Always try to get the complete code\n\t\tif (node instanceof CodeNode) {\n\t\t\tCodeNode codeNode = (CodeNode) node;\n\t\t\tJNode usageJNode = codeNode.getJParent();\n\t\t\tif (usageJNode != null) {\n\t\t\t\t// Try to get the complete code of the method or class\n\t\t\t\tString fullCode = getFullNodeCode(usageJNode);\n\t\t\t\tif (fullCode != null && !fullCode.isEmpty()) {\n\t\t\t\t\treturn fullCode;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If you cannot get more context, at least add some empty lines and comments\n\t\treturn \"// Unable to get complete context, only display related lines\\n\\n\" + codeLine;\n\t}\n\n\tprivate String getFullNodeCode(JNode node) {\n\t\tif (node != null) {\n\t\t\t// Get the code information of the node\n\t\t\tICodeInfo codeInfo = node.getCodeInfo();\n\t\t\tif (codeInfo != null && !codeInfo.equals(ICodeInfo.EMPTY)) {\n\t\t\t\treturn codeInfo.getCodeStr();\n\t\t\t}\n\n\t\t\t// If it is a class node, try to get the class code\n\t\t\tif (node instanceof JClass) {\n\t\t\t\tJClass jClass = (JClass) node;\n\t\t\t\treturn jClass.getCodeInfo().getCodeStr();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate void scrollToCodeLine(RSyntaxTextArea textArea, String lineToHighlight) {\n\t\t// Try to find and highlight a specific line in the code and scroll to that position\n\t\ttry {\n\t\t\tString fullText = textArea.getText();\n\t\t\tint lineIndex = fullText.indexOf(lineToHighlight);\n\t\t\tif (lineIndex >= 0) {\n\t\t\t\t// Ensure the text area has updated the layout\n\t\t\t\ttextArea.revalidate();\n\n\t\t\t\t// Highlight the code line\n\t\t\t\ttextArea.setCaretPosition(lineIndex);\n\t\t\t\tint endIndex = lineIndex + lineToHighlight.length();\n\t\t\t\ttextArea.select(lineIndex, endIndex);\n\t\t\t\ttextArea.getCaret().setSelectionVisible(true);\n\n\t\t\t\t// Use SwingUtilities.invokeLater to ensure the scroll is executed after the UI is updated\n\t\t\t\tSwingUtilities.invokeLater(() -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Get the line number\n\t\t\t\t\t\tint lineNum = textArea.getLineOfOffset(lineIndex);\n\t\t\t\t\t\t// Ensure the line is centered in the view\n\t\t\t\t\t\tRectangle2D lineRect = textArea.modelToView2D(textArea.getLineStartOffset(lineNum));\n\t\t\t\t\t\tif (lineRect != null) {\n\t\t\t\t\t\t\t// Calculate the center point of the view\n\t\t\t\t\t\t\tJScrollPane scrollPane = (JScrollPane) textArea.getParent().getParent();\n\t\t\t\t\t\t\tRectangle viewRect = scrollPane.getViewport().getViewRect();\n\t\t\t\t\t\t\tint y = (int) (lineRect.getY() - (viewRect.height - lineRect.getHeight()) / 2);\n\t\t\t\t\t\t\tif (y < 0) {\n\t\t\t\t\t\t\t\ty = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Scroll to the calculated position\n\t\t\t\t\t\t\tscrollPane.getViewport().setViewPosition(new Point(0, y));\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLOG.debug(\"Error scrolling to line: {}\", e.getMessage());\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tLOG.debug(\"Could not find line to highlight: {}\", lineToHighlight);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.debug(\"Error highlighting line: {}\", e.getMessage());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/panel/UndisplayedStringsPanel.java",
    "content": "package jadx.gui.ui.panel;\n\nimport java.awt.BorderLayout;\nimport java.awt.Font;\n\nimport javax.swing.BorderFactory;\n\nimport org.drjekyll.fontchooser.FontChooser;\nimport org.drjekyll.fontchooser.model.FontSelectionModel;\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.fife.ui.rtextarea.RTextScrollPane;\n\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.settings.LineNumbersMode;\nimport jadx.gui.settings.ui.font.FontChooserHack;\nimport jadx.gui.ui.codearea.AbstractCodeArea;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.ui.treenodes.UndisplayedStringsNode;\n\npublic class UndisplayedStringsPanel extends ContentPanel {\n\tprivate static final long serialVersionUID = 695370628262996993L;\n\n\tprivate final RSyntaxTextArea textPane;\n\tprivate final RTextScrollPane codeScrollPane;\n\n\tpublic UndisplayedStringsPanel(TabbedPane panel, UndisplayedStringsNode node) {\n\t\tsuper(panel, node);\n\t\tsetLayout(new BorderLayout());\n\t\ttextPane = AbstractCodeArea.getDefaultArea(panel.getMainWindow());\n\n\t\tJadxSettings settings = getSettings();\n\t\tFont selectedFont = settings.getCodeFont();\n\n\t\tFontChooser fontChooser = new FontChooser();\n\t\tfontChooser.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\n\t\tfontChooser.setSelectedFont(selectedFont);\n\t\tFontChooserHack.hidePreview(fontChooser);\n\n\t\tfontChooser.addChangeListener(event -> {\n\t\t\tFontSelectionModel model = (FontSelectionModel) event.getSource();\n\t\t\tsettings.setCodeFont(model.getSelectedFont());\n\t\t\tgetMainWindow().loadSettings();\n\t\t});\n\n\t\tcodeScrollPane = new RTextScrollPane(textPane);\n\n\t\tadd(codeScrollPane, BorderLayout.CENTER);\n\t\tadd(fontChooser, BorderLayout.EAST);\n\n\t\tapplySettings();\n\t\tshowData(node.makeDescString());\n\t}\n\n\tprivate void applySettings() {\n\t\tcodeScrollPane.setLineNumbersEnabled(getSettings().getLineNumbersMode() != LineNumbersMode.DISABLE);\n\t\tcodeScrollPane.getGutter().setLineNumberFont(getSettings().getCodeFont());\n\t\ttextPane.setFont(getSettings().getCodeFont());\n\t}\n\n\tprivate void showData(String data) {\n\t\ttextPane.setText(data);\n\t\ttextPane.setCaretPosition(0);\n\t}\n\n\t@Override\n\tpublic void loadSettings() {\n\t\tapplySettings();\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/popupmenu/JClassExportType.java",
    "content": "package jadx.gui.ui.popupmenu;\n\npublic enum JClassExportType {\n\tCode(\"java\"),\n\tSmali(\"smali\"),\n\tSimple(\"java\"),\n\tFallback(\"java\");\n\n\tfinal String extension;\n\n\tJClassExportType(String extension) {\n\t\tthis.extension = extension;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/popupmenu/JClassPopupMenu.java",
    "content": "package jadx.gui.ui.popupmenu;\n\nimport java.io.Writer;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Locale;\n\nimport javax.swing.JMenu;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPopupMenu;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.DecompilationMode;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.codearea.mode.JCodeMode;\nimport jadx.gui.ui.dialog.RenameDialog;\nimport jadx.gui.ui.filedialog.FileDialogWrapper;\nimport jadx.gui.ui.filedialog.FileOpenMode;\nimport jadx.gui.utils.NLS;\n\npublic class JClassPopupMenu extends JPopupMenu {\n\tprivate static final long serialVersionUID = -7781009781149260806L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JClassPopupMenu.class);\n\n\tprivate final transient MainWindow mainWindow;\n\n\tpublic JClassPopupMenu(MainWindow mainWindow, JClass jClass) {\n\t\tthis.mainWindow = mainWindow;\n\n\t\tadd(RenameDialog.buildRenamePopupMenuItem(mainWindow, jClass));\n\t\tadd(makeExportSubMenu(jClass));\n\t}\n\n\tprivate JMenuItem makeExportSubMenu(JClass jClass) {\n\t\tJMenu exportSubMenu = new JMenu(NLS.str(\"popup.export\"));\n\n\t\texportSubMenu.add(makeExportMenuItem(jClass, NLS.str(\"tabs.code\"), JClassExportType.Code));\n\t\texportSubMenu.add(makeExportMenuItem(jClass, NLS.str(\"tabs.smali\"), JClassExportType.Smali));\n\t\texportSubMenu.add(makeExportMenuItem(jClass, \"Simple\", JClassExportType.Simple));\n\t\texportSubMenu.add(makeExportMenuItem(jClass, \"Fallback\", JClassExportType.Fallback));\n\n\t\treturn exportSubMenu;\n\t}\n\n\tpublic JMenuItem makeExportMenuItem(JClass jClass, String label, JClassExportType exportType) {\n\t\tJMenuItem exportMenuItem = new JMenuItem(label);\n\t\texportMenuItem.addActionListener(event -> {\n\t\t\tString fileName = jClass.getName() + \".\" + exportType.extension;\n\n\t\t\tFileDialogWrapper fileDialog = new FileDialogWrapper(mainWindow, FileOpenMode.EXPORT_NODE);\n\t\t\tfileDialog.setFileExtList(Collections.singletonList(exportType.extension));\n\t\t\tPath currentDir = fileDialog.getCurrentDir();\n\t\t\tif (currentDir != null) {\n\t\t\t\tfileDialog.setSelectedFile(currentDir.resolve(fileName));\n\t\t\t}\n\n\t\t\tList<Path> selectedPaths = fileDialog.show();\n\t\t\tif (selectedPaths.size() != 1) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tPath selectedPath = selectedPaths.get(0);\n\t\t\tPath savePath;\n\t\t\t// Append file extension if missing\n\t\t\tif (!selectedPath.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(exportType.extension)) {\n\t\t\t\tsavePath = selectedPath.resolveSibling(selectedPath.getFileName() + \".\" + exportType.extension);\n\t\t\t} else {\n\t\t\t\tsavePath = selectedPath;\n\t\t\t}\n\n\t\t\tsaveJClass(jClass, savePath, exportType);\n\n\t\t\tLOG.info(\"Done saving {}\", savePath);\n\t\t});\n\n\t\treturn exportMenuItem;\n\t}\n\n\tpublic static void saveJClass(JClass jClass, Path savePath, JClassExportType exportType) {\n\t\ttry (Writer writer = Files.newBufferedWriter(savePath, StandardCharsets.UTF_8)) {\n\t\t\twriter.write(getCode(jClass, exportType));\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Error saving project\", e);\n\t\t}\n\t}\n\n\tprivate static String getCode(JClass jClass, JClassExportType exportType) {\n\t\tswitch (exportType) {\n\t\t\tcase Code:\n\t\t\t\treturn jClass.getCodeInfo().getCodeStr();\n\t\t\tcase Smali:\n\t\t\t\treturn jClass.getSmali();\n\t\t\tcase Simple:\n\t\t\t\tJNode jClassSimple = new JCodeMode(jClass, DecompilationMode.SIMPLE);\n\t\t\t\treturn jClassSimple.getCodeInfo().getCodeStr();\n\t\t\tcase Fallback:\n\t\t\t\tJNode jClassFallback = new JCodeMode(jClass, DecompilationMode.FALLBACK);\n\t\t\t\treturn jClassFallback.getCodeInfo().getCodeStr();\n\t\t\tdefault:\n\t\t\t\tthrow new RuntimeException(\"Unsupported JClassExportType \" + exportType);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/popupmenu/JPackagePopupMenu.java",
    "content": "package jadx.gui.ui.popupmenu;\n\nimport java.awt.event.ActionEvent;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport javax.swing.AbstractAction;\nimport javax.swing.JCheckBoxMenuItem;\nimport javax.swing.JMenu;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPopupMenu;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JPackage;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.dialog.ExcludePkgDialog;\nimport jadx.gui.ui.dialog.RenameDialog;\nimport jadx.gui.ui.dialog.SearchDialog;\nimport jadx.gui.ui.filedialog.FileDialogWrapper;\nimport jadx.gui.ui.filedialog.FileOpenMode;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.pkgs.JRenamePackage;\nimport jadx.gui.utils.pkgs.PackageHelper;\n\npublic class JPackagePopupMenu extends JPopupMenu {\n\tprivate static final long serialVersionUID = -7781009781149224131L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JPackagePopupMenu.class);\n\n\tprivate final transient MainWindow mainWindow;\n\n\tpublic JPackagePopupMenu(MainWindow mainWindow, JPackage pkg) {\n\t\tthis.mainWindow = mainWindow;\n\n\t\tadd(makeExcludeItem(pkg));\n\t\tadd(makeExcludeItem());\n\t\tadd(makeRenameMenuItem(pkg));\n\t\tadd(makeExportSubMenu(pkg));\n\t\tadd(makeSearchItem(pkg));\n\t}\n\n\tprivate JMenuItem makeRenameMenuItem(JPackage pkg) {\n\t\tJMenuItem renameSubMenu = new JMenu(NLS.str(\"popup.rename\"));\n\t\tPackageHelper packageHelper = mainWindow.getCacheObject().getPackageHelper();\n\t\tList<JRenamePackage> nodes = packageHelper.getRenameNodes(pkg);\n\t\tfor (JRenamePackage node : nodes) {\n\t\t\tJMenuItem pkgPartItem = new JMenuItem(node.getTitle(), node.getIcon());\n\t\t\tpkgPartItem.addActionListener(e -> rename(node));\n\t\t\trenameSubMenu.add(pkgPartItem);\n\t\t}\n\t\treturn renameSubMenu;\n\t}\n\n\tprivate void rename(JRenamePackage pkg) {\n\t\tLOG.debug(\"Renaming package: {}\", pkg);\n\t\tRenameDialog.rename(mainWindow, pkg);\n\t}\n\n\tprivate JMenuItem makeExcludeItem(JPackage pkg) {\n\t\tJMenuItem excludeItem = new JCheckBoxMenuItem(NLS.str(\"popup.exclude\"));\n\t\texcludeItem.setSelected(!pkg.isEnabled());\n\t\texcludeItem.addItemListener(e -> {\n\t\t\tJadxWrapper wrapper = mainWindow.getWrapper();\n\t\t\tString fullName = pkg.getPkg().getFullName();\n\t\t\tif (excludeItem.isSelected()) {\n\t\t\t\twrapper.addExcludedPackage(fullName);\n\t\t\t} else {\n\t\t\t\twrapper.removeExcludedPackage(fullName);\n\t\t\t}\n\t\t\tmainWindow.reopen();\n\t\t});\n\t\treturn excludeItem;\n\t}\n\n\tprivate JMenuItem makeExportSubMenu(JPackage pkg) {\n\t\tJMenu exportSubMenu = new JMenu(NLS.str(\"popup.export\"));\n\n\t\texportSubMenu.add(makeExportMenuItem(pkg, NLS.str(\"tabs.code\"), JClassExportType.Code));\n\t\texportSubMenu.add(makeExportMenuItem(pkg, NLS.str(\"tabs.smali\"), JClassExportType.Smali));\n\t\texportSubMenu.add(makeExportMenuItem(pkg, \"Simple\", JClassExportType.Simple));\n\t\texportSubMenu.add(makeExportMenuItem(pkg, \"Fallback\", JClassExportType.Fallback));\n\n\t\treturn exportSubMenu;\n\t}\n\n\tpublic JMenuItem makeExportMenuItem(JPackage pkg, String label, JClassExportType exportType) {\n\t\tJMenuItem exportMenuItem = new JMenuItem(label);\n\t\texportMenuItem.addActionListener(event -> {\n\t\t\tFileDialogWrapper fileDialog = new FileDialogWrapper(mainWindow, FileOpenMode.EXPORT_NODE_FOLDER);\n\n\t\t\tList<Path> selectedPaths = fileDialog.show();\n\t\t\tif (selectedPaths.size() != 1) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tPath savePath = selectedPaths.get(0);\n\t\t\tsaveJPackage(pkg, savePath, exportType);\n\t\t});\n\n\t\treturn exportMenuItem;\n\t}\n\n\tprivate static void saveJPackage(JPackage pkg, Path savePath, JClassExportType exportType) {\n\t\tPath subSavePath = savePath.resolve(pkg.getName());\n\t\ttry {\n\t\t\tif (!Files.isDirectory(subSavePath)) {\n\t\t\t\tFiles.createDirectory(subSavePath);\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t\tfor (JClass jClass : pkg.getClasses()) {\n\t\t\tString fileName = jClass.getName() + \".\" + exportType.extension;\n\t\t\tJClassPopupMenu.saveJClass(jClass, subSavePath.resolve(fileName), exportType);\n\t\t}\n\t\tfor (JPackage subPkg : pkg.getSubPackages()) {\n\t\t\tsaveJPackage(subPkg, subSavePath, exportType);\n\t\t}\n\t}\n\n\tprivate JMenuItem makeExcludeItem() {\n\t\treturn new JMenuItem(new AbstractAction(NLS.str(\"popup.exclude_packages\")) {\n\t\t\tprivate static final long serialVersionUID = -1111111202104151028L;\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tnew ExcludePkgDialog(mainWindow).setVisible(true);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate JMenuItem makeSearchItem(JPackage pkg) {\n\t\tJMenuItem searchItem = new JMenuItem(NLS.str(\"menu.text_search\"));\n\t\tsearchItem.addActionListener(e -> {\n\t\t\tString fullName = pkg.getPkg().getFullName();\n\t\t\tLOG.debug(\"Searching package: {}\", fullName);\n\t\t\tSearchDialog.searchPackage(mainWindow, fullName);\n\t\t});\n\t\treturn searchItem;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/popupmenu/JResourcePopupMenu.java",
    "content": "package jadx.gui.ui.popupmenu;\n\nimport java.io.IOException;\nimport java.io.Writer;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Locale;\n\nimport javax.swing.JMenuItem;\nimport javax.swing.JPopupMenu;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.utils.CommonFileUtils;\nimport jadx.gui.treemodel.JResource;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.filedialog.FileDialogWrapper;\nimport jadx.gui.ui.filedialog.FileOpenMode;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.ui.FileOpenerHelper;\n\npublic class JResourcePopupMenu extends JPopupMenu {\n\tprivate static final long serialVersionUID = -7781009781149260806L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JResourcePopupMenu.class);\n\n\tprivate final transient MainWindow mainWindow;\n\n\tpublic JResourcePopupMenu(MainWindow mainWindow, JResource resource) {\n\t\tthis.mainWindow = mainWindow;\n\n\t\tif (resource.getType() != JResource.JResType.ROOT) {\n\t\t\tadd(makeExportMenuItem(resource));\n\t\t}\n\t}\n\n\tprivate JMenuItem makeExportMenuItem(JResource resource) {\n\t\tJMenuItem exportMenu = new JMenuItem(NLS.str(\"popup.export\"));\n\t\texportMenu.addActionListener(event -> {\n\t\t\tPath savePath = null;\n\t\t\tswitch (resource.getType()) {\n\t\t\t\tcase ROOT:\n\t\t\t\tcase DIR:\n\t\t\t\t\tsavePath = getSaveDirPath(resource);\n\t\t\t\t\tbreak;\n\t\t\t\tcase FILE:\n\t\t\t\t\tsavePath = getSaveFilePath(resource);\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (savePath == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsaveJResource(resource, savePath, true);\n\n\t\t\tLOG.info(\"Done saving {}\", savePath);\n\t\t});\n\t\treturn exportMenu;\n\t}\n\n\tprivate Path getSaveFilePath(JResource resource) {\n\t\tString extension = CommonFileUtils.getFileExtension(resource.getName());\n\n\t\tFileDialogWrapper fileDialog = new FileDialogWrapper(mainWindow, FileOpenMode.EXPORT_NODE);\n\t\tif (extension != null) {\n\t\t\tfileDialog.setFileExtList(Collections.singletonList(extension));\n\t\t}\n\t\tPath currentDir = fileDialog.getCurrentDir();\n\t\tif (currentDir != null) {\n\t\t\tfileDialog.setSelectedFile(currentDir.resolve(resource.getName()));\n\t\t}\n\n\t\tList<Path> selectedPaths = fileDialog.show();\n\t\tif (selectedPaths.size() != 1) {\n\t\t\treturn null;\n\t\t}\n\n\t\tPath selectedPath = selectedPaths.get(0);\n\t\tPath savePath;\n\t\t// Append file extension if missing\n\t\tif (extension != null\n\t\t\t\t&& !selectedPath.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(extension)) {\n\t\t\tsavePath = selectedPath.resolveSibling(selectedPath.getFileName() + \".\" + extension);\n\t\t} else {\n\t\t\tsavePath = selectedPath;\n\t\t}\n\n\t\treturn savePath;\n\t}\n\n\tprivate Path getSaveDirPath(JResource resource) {\n\t\tFileDialogWrapper fileDialog = new FileDialogWrapper(mainWindow, FileOpenMode.EXPORT_NODE_FOLDER);\n\n\t\tList<Path> selectedPaths = fileDialog.show();\n\t\tif (selectedPaths.size() != 1) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn selectedPaths.get(0);\n\t}\n\n\tprivate static void saveJResource(JResource resource, Path savePath, boolean comingFromDialog) {\n\t\tswitch (resource.getType()) {\n\t\t\tcase ROOT:\n\t\t\tcase DIR:\n\t\t\t\tsaveJResourceDir(resource, savePath, comingFromDialog);\n\t\t\t\tbreak;\n\t\t\tcase FILE:\n\t\t\t\tsaveJResourceFile(resource, savePath, comingFromDialog);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate static void saveJResourceDir(JResource resource, Path savePath, boolean comingFromDialog) {\n\t\tPath subSavePath = savePath.resolve(resource.getShortName());\n\t\ttry {\n\t\t\tif (!Files.isDirectory(subSavePath)) {\n\t\t\t\tFiles.createDirectories(subSavePath);\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t\tfor (JResource subResource : resource.getSubNodes()) {\n\t\t\tsaveJResource(subResource, subSavePath, false);\n\t\t}\n\t}\n\n\tprivate static void saveJResourceFile(JResource resource, Path savePath, boolean comingFromDialog) {\n\t\tif (!comingFromDialog) {\n\t\t\tPath fileName = Path.of(resource.getName()).getFileName();\n\t\t\tsavePath = savePath.resolve(fileName);\n\t\t}\n\t\tswitch (resource.getResFile().getType()) {\n\t\t\tcase MANIFEST:\n\t\t\tcase XML:\n\t\t\t\texportString(resource, savePath);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tFileOpenerHelper.exportBinary(resource, savePath);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate static void exportString(JResource resource, Path savePath) {\n\t\ttry (Writer writer = Files.newBufferedWriter(savePath, StandardCharsets.UTF_8)) {\n\t\t\twriter.write(resource.getCodeInfo().getCodeStr());\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Error saving file \" + resource.getName(), e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/popupmenu/RecentProjectsMenuListener.java",
    "content": "package jadx.gui.ui.popupmenu;\n\nimport java.nio.file.Path;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport javax.swing.JMenu;\nimport javax.swing.JMenuItem;\nimport javax.swing.event.MenuEvent;\nimport javax.swing.event.MenuListener;\n\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\n\npublic class RecentProjectsMenuListener implements MenuListener {\n\tprivate final MainWindow mainWindow;\n\tprivate final JMenu menu;\n\n\tpublic RecentProjectsMenuListener(MainWindow mainWindow, JMenu menu) {\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.menu = menu;\n\t}\n\n\t@Override\n\tpublic void menuSelected(MenuEvent menuEvent) {\n\t\tSet<Path> current = new HashSet<>(mainWindow.getProject().getFilePaths());\n\t\tList<JMenuItem> items = mainWindow.getSettings().getRecentProjects()\n\t\t\t\t.stream()\n\t\t\t\t.filter(path -> !current.contains(path))\n\t\t\t\t.map(path -> {\n\t\t\t\t\tJMenuItem menuItem = new JMenuItem(path.toAbsolutePath().toString());\n\t\t\t\t\tmenuItem.addActionListener(e -> mainWindow.open(Collections.singletonList(path)));\n\t\t\t\t\treturn menuItem;\n\t\t\t\t}).collect(Collectors.toList());\n\n\t\tmenu.removeAll();\n\t\tif (items.isEmpty()) {\n\t\t\tmenu.add(new JMenuItem(NLS.str(\"menu.no_recent_projects\")));\n\t\t} else {\n\t\t\titems.forEach(menu::add);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void menuDeselected(MenuEvent e) {\n\t}\n\n\t@Override\n\tpublic void menuCanceled(MenuEvent e) {\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/popupmenu/VarTreePopupMenu.java",
    "content": "package jadx.gui.ui.popupmenu;\n\nimport java.awt.Component;\nimport java.awt.Toolkit;\nimport java.awt.datatransfer.Clipboard;\nimport java.awt.datatransfer.StringSelection;\nimport java.awt.event.ActionEvent;\n\nimport javax.swing.AbstractAction;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPopupMenu;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.dialog.SetValueDialog;\nimport jadx.gui.ui.panel.JDebuggerPanel.ValueTreeNode;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic class VarTreePopupMenu extends JPopupMenu {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(VarTreePopupMenu.class);\n\n\tprivate static final long serialVersionUID = -1111111202103170724L;\n\n\tprivate final MainWindow mainWindow;\n\tprivate ValueTreeNode valNode;\n\n\tpublic VarTreePopupMenu(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t\taddItems();\n\t}\n\n\tpublic void show(ValueTreeNode treeNode, Component invoker, int x, int y) {\n\t\tvalNode = treeNode;\n\t\tsuper.show(invoker, x, y);\n\t}\n\n\tprivate void addItems() {\n\t\tJMenuItem copyValItem = new JMenuItem(new AbstractAction(NLS.str(\"debugger.popup_copy_value\")) {\n\t\t\tprivate static final long serialVersionUID = -1111111202103171118L;\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tString val = valNode.getValue();\n\t\t\t\tif (val != null) {\n\t\t\t\t\tif (val.startsWith(\"\\\"\") && val.endsWith(\"\\\"\")) {\n\t\t\t\t\t\tval = val.substring(1, val.length() - 1);\n\t\t\t\t\t}\n\t\t\t\t\tStringSelection stringSelection = new StringSelection(val);\n\t\t\t\t\tClipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();\n\t\t\t\t\tclipboard.setContents(stringSelection, null);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tJMenuItem setValItem = new JMenuItem(new AbstractAction(NLS.str(\"debugger.popup_set_value\")) {\n\t\t\tprivate static final long serialVersionUID = -1111111202103171119L;\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t(new SetValueDialog(mainWindow, valNode)).setVisible(true);\n\t\t\t}\n\t\t});\n\n\t\tJMenuItem zeroItem = new JMenuItem(new AbstractAction(NLS.str(\"debugger.popup_change_to_zero\")) {\n\t\t\tprivate static final long serialVersionUID = -1111111202103171120L;\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent event) {\n\t\t\t\ttry {\n\t\t\t\t\tmainWindow.getDebuggerPanel()\n\t\t\t\t\t\t\t.getDbgController()\n\t\t\t\t\t\t\t.modifyRegValue(valNode, ArgType.INT, 0);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.error(\"Change to zero failed\", e);\n\t\t\t\t\tUiUtils.showMessageBox(mainWindow, e.getMessage());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tJMenuItem oneItem = new JMenuItem(new AbstractAction(NLS.str(\"debugger.popup_change_to_one\")) {\n\t\t\tprivate static final long serialVersionUID = -1111111202103171121L;\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent event) {\n\t\t\t\ttry {\n\t\t\t\t\tmainWindow.getDebuggerPanel()\n\t\t\t\t\t\t\t.getDbgController()\n\t\t\t\t\t\t\t.modifyRegValue(valNode, ArgType.INT, 1);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.error(\"Change to one failed\", e);\n\t\t\t\t\tUiUtils.showMessageBox(mainWindow, e.getMessage());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tthis.add(copyValItem);\n\t\tthis.add(new Separator());\n\t\tthis.add(setValItem);\n\t\tthis.add(zeroItem);\n\t\tthis.add(oneItem);\n\t\tthis.add(zeroItem);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/startpage/RecentProjectItem.java",
    "content": "package jadx.gui.ui.startpage;\n\nimport java.nio.file.Path;\nimport java.util.Objects;\n\nimport jadx.api.plugins.utils.CommonFileUtils;\n\n/**\n * Represents an item in the recent projects list.\n */\npublic class RecentProjectItem {\n\tprivate final Path path;\n\n\tpublic RecentProjectItem(Path path) {\n\t\tthis.path = Objects.requireNonNull(path);\n\t}\n\n\tpublic Path getPath() {\n\t\treturn path;\n\t}\n\n\tpublic String getProjectName() {\n\t\treturn CommonFileUtils.removeFileExtension(path.getFileName().toString());\n\t}\n\n\tpublic String getAbsolutePath() {\n\t\treturn path.toAbsolutePath().toString();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getProjectName();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tRecentProjectItem that = (RecentProjectItem) o;\n\t\treturn Objects.equals(path, that.path);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(path);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/startpage/RecentProjectListCellRenderer.java",
    "content": "package jadx.gui.ui.startpage;\n\nimport java.awt.BorderLayout;\nimport java.awt.Color;\nimport java.awt.Component;\nimport java.awt.Font;\nimport java.awt.Graphics;\nimport java.awt.Rectangle;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.JButton;\nimport javax.swing.JLabel;\nimport javax.swing.JList;\nimport javax.swing.JPanel;\nimport javax.swing.ListCellRenderer;\nimport javax.swing.UIManager;\nimport javax.swing.plaf.basic.BasicButtonUI;\n\nimport jadx.gui.utils.Icons;\n\npublic class RecentProjectListCellRenderer extends JPanel implements ListCellRenderer<RecentProjectItem> {\n\tprivate static final long serialVersionUID = 5550591869239586857L;\n\n\tprivate final JLabel fileNameLabel;\n\tprivate final JLabel pathLabel;\n\tprivate final JButton removeProjectBtn;\n\n\tprivate final Color defaultBackground;\n\tprivate final Color defaultForeground;\n\n\tprivate final Color selectedBackground;\n\tprivate final Color selectedForeground;\n\n\tprivate Rectangle removeIconBounds;\n\n\tpublic RecentProjectListCellRenderer(Font baseFont) {\n\t\tsuper(new BorderLayout(5, 0));\n\t\tsetOpaque(true);\n\t\tsetBorder(BorderFactory.createEmptyBorder(5, 10, 5, 5));\n\n\t\tthis.fileNameLabel = new JLabel();\n\t\tfileNameLabel.setFont(baseFont.deriveFont(Font.BOLD, baseFont.getSize()));\n\n\t\tthis.pathLabel = new JLabel();\n\t\tpathLabel.setFont(baseFont.deriveFont(baseFont.getSize() - 2f));\n\t\tpathLabel.setForeground(UIManager.getColor(\"Label.disabledForeground\"));\n\n\t\tJPanel textPanel = new JPanel(new BorderLayout());\n\t\ttextPanel.setOpaque(false);\n\t\ttextPanel.add(fileNameLabel, BorderLayout.NORTH);\n\t\ttextPanel.add(pathLabel, BorderLayout.SOUTH);\n\n\t\tremoveProjectBtn = new JButton();\n\t\tremoveProjectBtn.setIcon(Icons.CLOSE_INACTIVE);\n\t\tremoveProjectBtn.setOpaque(false);\n\t\tremoveProjectBtn.setUI(new BasicButtonUI());\n\t\tremoveProjectBtn.setContentAreaFilled(false);\n\t\tremoveProjectBtn.setFocusable(false);\n\t\tremoveProjectBtn.setBorder(null);\n\t\tremoveProjectBtn.setBorderPainted(false);\n\n\t\tadd(textPanel, BorderLayout.CENTER);\n\t\tadd(removeProjectBtn, BorderLayout.EAST);\n\n\t\tdefaultBackground = UIManager.getColor(\"List.background\");\n\t\tdefaultForeground = UIManager.getColor(\"List.foreground\");\n\n\t\tselectedBackground = UIManager.getColor(\"List.selectionBackground\");\n\t\tselectedForeground = UIManager.getColor(\"List.selectionForeground\");\n\t}\n\n\t@Override\n\tpublic Component getListCellRendererComponent(JList<? extends RecentProjectItem> list,\n\t\t\tRecentProjectItem value, int index, boolean isSelected, boolean cellHasFocus) {\n\n\t\tfileNameLabel.setText(value.getProjectName());\n\t\tpathLabel.setText(value.getAbsolutePath());\n\n\t\tboolean isThisRemoveButtonHovered = (index == StartPagePanel.hoveredRemoveBtnIndex);\n\t\tremoveProjectBtn.setIcon(isThisRemoveButtonHovered ? Icons.CLOSE : Icons.CLOSE_INACTIVE);\n\t\tremoveProjectBtn.setRolloverEnabled(isThisRemoveButtonHovered);\n\n\t\tif (isSelected) {\n\t\t\tsetBackground(selectedBackground);\n\t\t\tfileNameLabel.setForeground(selectedForeground);\n\t\t\tpathLabel.setForeground(selectedForeground.darker());\n\t\t\tremoveProjectBtn.setForeground(selectedForeground);\n\t\t} else {\n\t\t\tsetBackground(defaultBackground);\n\t\t\tfileNameLabel.setForeground(defaultForeground);\n\t\t\tpathLabel.setForeground(UIManager.getColor(\"Label.disabledForeground\"));\n\t\t\tremoveProjectBtn.setForeground(defaultForeground);\n\t\t}\n\n\t\tsetToolTipText(value.getAbsolutePath());\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Overriding paint to calculate the bounds of the remove button.\n\t * This is crucial for the MouseListener on the JList to determine if a click/hover was on the\n\t * button.\n\t */\n\t@Override\n\tpublic void paint(Graphics g) {\n\t\tsuper.paint(g);\n\t\t// Ensure the button's layout is valid before getting bounds\n\t\tremoveProjectBtn.doLayout();\n\t\t// Calculate bounds of the remove button relative to this renderer panel\n\t\tint x = getWidth() - removeProjectBtn.getWidth() - getBorder().getBorderInsets(this).right;\n\t\tint y = (getHeight() - removeProjectBtn.getHeight()) / 2;\n\t\tremoveIconBounds = new Rectangle(x, y, removeProjectBtn.getWidth(), removeProjectBtn.getHeight());\n\t}\n\n\t/**\n\t * Returns the bounds of the remove button within the renderer component's coordinate system.\n\t * This is crucial for the MouseListener on the JList to determine if a click was on the icon.\n\t *\n\t * @return Rectangle representing the bounds of the remove icon.\n\t */\n\tpublic Rectangle getRemoveIconBounds() {\n\t\treturn removeIconBounds;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/startpage/StartPageNode.java",
    "content": "package jadx.gui.ui.startpage;\n\nimport javax.swing.Icon;\n\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.NLS;\n\npublic class StartPageNode extends JNode {\n\tprivate static final long serialVersionUID = 8983134608645736174L;\n\n\t@Override\n\tpublic boolean hasContent() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic ContentPanel getContentPanel(TabbedPane tabbedPane) {\n\t\treturn new StartPagePanel(tabbedPane, this);\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn NLS.str(\"start_page.title\");\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn Icons.START_PAGE;\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean supportsQuickTabs() {\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/startpage/StartPagePanel.java",
    "content": "package jadx.gui.ui.startpage;\n\nimport java.awt.BorderLayout;\nimport java.awt.Dimension;\nimport java.awt.Font;\nimport java.awt.Rectangle;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.awt.event.MouseMotionAdapter;\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.DefaultListModel;\nimport javax.swing.JButton;\nimport javax.swing.JList;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPanel;\nimport javax.swing.JPopupMenu;\nimport javax.swing.JScrollPane;\nimport javax.swing.ListSelectionModel;\nimport javax.swing.ScrollPaneConstants;\nimport javax.swing.SwingUtilities;\nimport javax.swing.border.Border;\nimport javax.swing.border.TitledBorder;\n\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.NLS;\n\npublic class StartPagePanel extends ContentPanel {\n\tprivate static final long serialVersionUID = 2457805175218770732L;\n\n\tprivate final RecentProjectsJList recentList;\n\tprivate final DefaultListModel<RecentProjectItem> recentListModel;\n\tprivate final MainWindow mainWindow;\n\tprivate final JadxSettings settings;\n\n\tpublic static int hoveredRemoveBtnIndex = -1;\n\n\tpublic StartPagePanel(TabbedPane tabbedPane, StartPageNode node) {\n\t\tsuper(tabbedPane, node);\n\t\tthis.mainWindow = tabbedPane.getMainWindow();\n\t\tthis.settings = mainWindow.getSettings();\n\t\tFont baseFont = settings.getUiFont();\n\n\t\tJButton openFile = new JButton(NLS.str(\"file.open_title\"), Icons.OPEN);\n\t\topenFile.addActionListener(ev -> mainWindow.openFileDialog());\n\n\t\tJButton openProject = new JButton(NLS.str(\"file.open_project\"), Icons.OPEN_PROJECT);\n\t\topenProject.addActionListener(ev -> mainWindow.openProjectDialog());\n\n\t\tJPanel start = new JPanel();\n\t\tstart.setBorder(sectionFrame(NLS.str(\"start_page.start\"), baseFont));\n\t\tstart.setLayout(new BoxLayout(start, BoxLayout.LINE_AXIS));\n\t\tstart.add(openFile);\n\t\tstart.add(Box.createRigidArea(new Dimension(10, 0)));\n\t\tstart.add(openProject);\n\t\tstart.add(Box.createHorizontalGlue());\n\n\t\tthis.recentListModel = new DefaultListModel<>();\n\t\tthis.recentList = new RecentProjectsJList(recentListModel);\n\t\tthis.recentList.setCellRenderer(new RecentProjectListCellRenderer(baseFont));\n\t\tthis.recentList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\n\n\t\tJScrollPane scrollPane = new JScrollPane(recentList);\n\t\tscrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);\n\t\tscrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);\n\t\tscrollPane.setPreferredSize(new Dimension(400, 250));\n\t\tscrollPane.setBorder(BorderFactory.createEmptyBorder());\n\n\t\trecentList.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(MouseEvent e) {\n\t\t\t\tint index = recentList.locationToIndex(e.getPoint());\n\t\t\t\tif (index == -1) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tRecentProjectItem item = recentListModel.getElementAt(index);\n\t\t\t\tif (item == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tRecentProjectListCellRenderer renderer = (RecentProjectListCellRenderer) recentList.getCellRenderer()\n\t\t\t\t\t\t.getListCellRendererComponent(recentList, item, index, false, false);\n\n\t\t\t\tRectangle cellBounds = recentList.getCellBounds(index, index);\n\t\t\t\tif (cellBounds != null) {\n\t\t\t\t\tint xInCell = e.getX() - cellBounds.x;\n\t\t\t\t\tint yInCell = e.getY() - cellBounds.y;\n\n\t\t\t\t\tRectangle removeIconBounds = renderer.getRemoveIconBounds();\n\t\t\t\t\tif (removeIconBounds != null && removeIconBounds.contains(xInCell, yInCell)) {\n\t\t\t\t\t\tremoveRecentProject(item.getPath());\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e)) {\n\t\t\t\t\topenRecentProject(item.getPath());\n\t\t\t\t} else if (SwingUtilities.isRightMouseButton(e)) {\n\t\t\t\t\trecentList.setSelectedIndex(index);\n\t\t\t\t\tshowRecentProjectContextMenu(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\trecentList.addMouseMotionListener(new MouseMotionAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseMoved(MouseEvent e) {\n\t\t\t\tint oldHoveredRemoveBtnIndex = hoveredRemoveBtnIndex;\n\t\t\t\thoveredRemoveBtnIndex = -1;\n\n\t\t\t\tint currentCellIndex = recentList.locationToIndex(e.getPoint());\n\n\t\t\t\tif (currentCellIndex != -1) {\n\t\t\t\t\tRecentProjectItem item = recentListModel.getElementAt(currentCellIndex);\n\t\t\t\t\tRecentProjectListCellRenderer renderer = (RecentProjectListCellRenderer) recentList.getCellRenderer()\n\t\t\t\t\t\t\t.getListCellRendererComponent(recentList, item, currentCellIndex, recentList.isSelectedIndex(currentCellIndex),\n\t\t\t\t\t\t\t\t\tfalse);\n\n\t\t\t\t\tRectangle cellBounds = recentList.getCellBounds(currentCellIndex, currentCellIndex);\n\t\t\t\t\tif (cellBounds != null) {\n\t\t\t\t\t\tint xInCell = e.getX() - cellBounds.x;\n\t\t\t\t\t\tint yInCell = e.getY() - cellBounds.y;\n\n\t\t\t\t\t\tRectangle removeIconBounds = renderer.getRemoveIconBounds();\n\t\t\t\t\t\tif (removeIconBounds != null && removeIconBounds.contains(xInCell, yInCell)) {\n\t\t\t\t\t\t\thoveredRemoveBtnIndex = currentCellIndex;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (oldHoveredRemoveBtnIndex != hoveredRemoveBtnIndex) {\n\t\t\t\t\tif (oldHoveredRemoveBtnIndex != -1) {\n\t\t\t\t\t\tRectangle bounds = recentList.getCellBounds(oldHoveredRemoveBtnIndex, oldHoveredRemoveBtnIndex);\n\t\t\t\t\t\tif (bounds != null) {\n\t\t\t\t\t\t\trecentList.repaint(bounds);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (hoveredRemoveBtnIndex != -1) {\n\t\t\t\t\t\tRectangle bounds = recentList.getCellBounds(hoveredRemoveBtnIndex, hoveredRemoveBtnIndex);\n\t\t\t\t\t\tif (bounds != null) {\n\t\t\t\t\t\t\trecentList.repaint(bounds);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tJPanel recent = new JPanel();\n\t\trecent.setBorder(sectionFrame(NLS.str(\"start_page.recent\"), baseFont));\n\t\trecent.setLayout(new BoxLayout(recent, BoxLayout.PAGE_AXIS));\n\t\trecent.add(scrollPane);\n\n\t\tJPanel center = new JPanel();\n\t\tcenter.setLayout(new BorderLayout(10, 10));\n\t\tcenter.add(start, BorderLayout.PAGE_START);\n\t\tcenter.add(recent, BorderLayout.CENTER);\n\t\tcenter.setMaximumSize(new Dimension(700, 600));\n\t\tcenter.setAlignmentX(CENTER_ALIGNMENT);\n\n\t\tsetLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n\t\tsetBorder(BorderFactory.createEmptyBorder(50, 50, 50, 50));\n\t\tadd(Box.createVerticalGlue());\n\t\tadd(center);\n\t\tadd(Box.createVerticalGlue());\n\n\t\tfillRecentProjectsList();\n\t}\n\n\tprivate void fillRecentProjectsList() {\n\t\trecentListModel.clear();\n\t\tList<Path> recentPaths = settings.getRecentProjects();\n\t\tfor (Path path : recentPaths) {\n\t\t\trecentListModel.addElement(new RecentProjectItem(path));\n\t\t}\n\t\trecentList.revalidate();\n\t\trecentList.repaint();\n\t}\n\n\tprivate void openRecentProject(Path path) {\n\t\tmainWindow.open(path);\n\t}\n\n\tprivate void removeRecentProject(Path path) {\n\t\tsettings.removeRecentProject(path);\n\t\tfillRecentProjectsList();\n\t\tif (hoveredRemoveBtnIndex != -1 && hoveredRemoveBtnIndex >= recentListModel.size()) {\n\t\t\thoveredRemoveBtnIndex = -1;\n\t\t}\n\t}\n\n\tprivate void showRecentProjectContextMenu(MouseEvent e) {\n\t\tJPopupMenu popupMenu = new JPopupMenu();\n\t\tRecentProjectItem selectedItem = recentList.getSelectedValue();\n\n\t\tif (selectedItem != null) {\n\t\t\tJMenuItem openItem = new JMenuItem(NLS.str(\"file.open_project\"));\n\t\t\topenItem.addActionListener(actionEvent -> openRecentProject(selectedItem.getPath()));\n\t\t\tpopupMenu.add(openItem);\n\n\t\t\tJMenuItem removeItem = new JMenuItem(NLS.str(\"start_page.list.delete_recent_project\"));\n\t\t\tremoveItem.addActionListener(actionEvent -> removeRecentProject(selectedItem.getPath()));\n\t\t\tpopupMenu.add(removeItem);\n\t\t}\n\n\t\tpopupMenu.show(e.getComponent(), e.getX(), e.getY());\n\t}\n\n\tprivate static Border sectionFrame(String title, Font font) {\n\t\tTitledBorder titledBorder = BorderFactory.createTitledBorder(title);\n\t\ttitledBorder.setTitleFont(font.deriveFont(Font.BOLD, font.getSize() + 1));\n\t\tBorder spacing = BorderFactory.createEmptyBorder(10, 10, 10, 10);\n\t\treturn BorderFactory.createCompoundBorder(titledBorder, spacing);\n\t}\n\n\t@Override\n\tpublic void loadSettings() {\n\t}\n\n\t/**\n\t * Inner class: Custom JList to override getToolTipText method.\n\t * This allows displaying specific tooltips based on mouse position within a cell.\n\t */\n\tprivate static class RecentProjectsJList extends JList<RecentProjectItem> {\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\tpublic RecentProjectsJList(DefaultListModel<RecentProjectItem> model) {\n\t\t\tsuper(model);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getToolTipText(MouseEvent event) {\n\t\t\tint index = locationToIndex(event.getPoint());\n\t\t\tif (index == -1) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tRecentProjectItem item = getModel().getElementAt(index);\n\t\t\tif (item == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tRecentProjectListCellRenderer renderer = (RecentProjectListCellRenderer) getCellRenderer()\n\t\t\t\t\t.getListCellRendererComponent(this, item, index, isSelectedIndex(index), false);\n\n\t\t\tRectangle cellBounds = getCellBounds(index, index);\n\t\t\tif (cellBounds != null) {\n\t\t\t\tint xInCell = event.getX() - cellBounds.x;\n\t\t\t\tint yInCell = event.getY() - cellBounds.y;\n\n\t\t\t\tRectangle removeIconBounds = renderer.getRemoveIconBounds();\n\t\t\t\tif (removeIconBounds != null && removeIconBounds.contains(xInCell, yInCell)) {\n\t\t\t\t\treturn NLS.str(\"start_page.list.delete_recent_project.tooltip\");\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn item.getAbsolutePath();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/EditorSyncManager.java",
    "content": "package jadx.gui.ui.tab;\n\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.panel.ContentPanel;\n\npublic class EditorSyncManager implements ITabStatesListener {\n\tprivate final MainWindow mainWindow;\n\tprivate final TabbedPane tabbedPane;\n\n\tpublic EditorSyncManager(MainWindow mainWindow, TabbedPane tabbedPane) {\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.tabbedPane = tabbedPane;\n\t\tmainWindow.getTabsController().addListener(this);\n\t}\n\n\tpublic void sync() {\n\t\tContentPanel selectedContentPanel = tabbedPane.getSelectedContentPanel();\n\t\tif (selectedContentPanel != null) {\n\t\t\tmainWindow.selectNodeInTree(selectedContentPanel.getNode());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onTabSelect(TabBlueprint blueprint) {\n\t\tmainWindow.toggleHexViewMenu();\n\t\tif (mainWindow.getSettings().isAlwaysSelectOpened()) {\n\t\t\t// verify that tab opened for this blueprint (some nodes don't open tab with content)\n\t\t\tContentPanel selectedContentPanel = tabbedPane.getSelectedContentPanel();\n\t\t\tif (selectedContentPanel != null && selectedContentPanel.getNode().equals(blueprint.getNode())) {\n\t\t\t\tsync();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onTabClose(TabBlueprint blueprint) {\n\t\tITabStatesListener.super.onTabClose(blueprint);\n\t\tmainWindow.toggleHexViewMenu();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/ITabStatesListener.java",
    "content": "package jadx.gui.ui.tab;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.gui.ui.codearea.EditorViewState;\nimport jadx.gui.utils.JumpPosition;\n\n/**\n * Tabbed pane events listener\n */\npublic interface ITabStatesListener {\n\n\t/**\n\t * Tab added to tabbed pane without become active (selected)\n\t */\n\tdefault void onTabOpen(TabBlueprint blueprint) {\n\t}\n\n\t/**\n\t * Tab become active (selected)\n\t */\n\tdefault void onTabSelect(TabBlueprint blueprint) {\n\t}\n\n\t/**\n\t * Caret position changes.\n\t *\n\t * @param prevPos previous caret position; can be null if unknown; can be from another tab\n\t * @param newPos  new caret position, node refer to jump target node\n\t */\n\tdefault void onTabCodeJump(TabBlueprint blueprint, @Nullable JumpPosition prevPos, JumpPosition newPos) {\n\t}\n\n\tdefault void onTabSmaliJump(TabBlueprint blueprint, int pos, boolean debugMode) {\n\t}\n\n\tdefault void onTabClose(TabBlueprint blueprint) {\n\t}\n\n\tdefault void onTabPositionFirst(TabBlueprint blueprint) {\n\t}\n\n\tdefault void onTabPinChange(TabBlueprint blueprint) {\n\t}\n\n\tdefault void onTabBookmarkChange(TabBlueprint blueprint) {\n\t}\n\n\tdefault void onTabVisibilityChange(TabBlueprint blueprint) {\n\t}\n\n\tdefault void onTabRestore(TabBlueprint blueprint, EditorViewState viewState) {\n\t}\n\n\tdefault void onTabsRestoreDone() {\n\t}\n\n\tdefault void onTabsReorder(List<TabBlueprint> blueprints) {\n\t}\n\n\tdefault void onTabSave(TabBlueprint blueprint, EditorViewState viewState) {\n\t}\n\n\tdefault void onTabPreviewChange(TabBlueprint blueprint) {\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/LogTabStates.java",
    "content": "package jadx.gui.ui.tab;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.ui.codearea.EditorViewState;\nimport jadx.gui.utils.JumpPosition;\n\n/**\n * Utility class to log events from TabsController by implementing ITabStatesListener.\n */\npublic class LogTabStates implements ITabStatesListener {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(LogTabStates.class);\n\n\t@Override\n\tpublic void onTabBookmarkChange(TabBlueprint blueprint) {\n\t\tLOG.debug(\"onTabBookmarkChange: blueprint={}\", blueprint);\n\t}\n\n\t@Override\n\tpublic void onTabClose(TabBlueprint blueprint) {\n\t\tLOG.debug(\"onTabClose: blueprint={}\", blueprint);\n\t}\n\n\t@Override\n\tpublic void onTabCodeJump(TabBlueprint blueprint, @Nullable JumpPosition prevPos, JumpPosition newPos) {\n\t\tLOG.debug(\"onTabCodeJump: blueprint={}, prevPos={}, newPos={}\", blueprint, prevPos, newPos);\n\t}\n\n\t@Override\n\tpublic void onTabOpen(TabBlueprint blueprint) {\n\t\tLOG.debug(\"onTabOpen: blueprint={}\", blueprint);\n\t}\n\n\t@Override\n\tpublic void onTabPinChange(TabBlueprint blueprint) {\n\t\tLOG.debug(\"onTabPinChange: blueprint={}\", blueprint);\n\t}\n\n\t@Override\n\tpublic void onTabPositionFirst(TabBlueprint blueprint) {\n\t\tLOG.debug(\"onTabPositionFirst: blueprint={}\", blueprint);\n\t}\n\n\t@Override\n\tpublic void onTabRestore(TabBlueprint blueprint, EditorViewState viewState) {\n\t\tLOG.debug(\"onTabRestore: blueprint={}, viewState={}\", blueprint, viewState);\n\t}\n\n\t@Override\n\tpublic void onTabSave(TabBlueprint blueprint, EditorViewState viewState) {\n\t\tLOG.debug(\"onTabSave: blueprint={}, viewState={}\", blueprint, viewState);\n\t}\n\n\t@Override\n\tpublic void onTabSelect(TabBlueprint blueprint) {\n\t\tLOG.debug(\"onTabSelect: blueprint={}\", blueprint);\n\t}\n\n\t@Override\n\tpublic void onTabSmaliJump(TabBlueprint blueprint, int pos, boolean debugMode) {\n\t\tLOG.debug(\"onTabSmaliJump: blueprint={}, pos={}, debugMode={}\", blueprint, pos, debugMode);\n\t}\n\n\t@Override\n\tpublic void onTabsReorder(List<TabBlueprint> blueprints) {\n\t\tLOG.debug(\"onTabsReorder: blueprints={}\", blueprints);\n\t}\n\n\t@Override\n\tpublic void onTabsRestoreDone() {\n\t\tLOG.debug(\"onTabsRestoreDone\");\n\t}\n\n\t@Override\n\tpublic void onTabVisibilityChange(TabBlueprint blueprint) {\n\t\tLOG.debug(\"onTabVisibilityChange: blueprint={}\", blueprint);\n\t}\n\n\t@Override\n\tpublic void onTabPreviewChange(TabBlueprint blueprint) {\n\t\tLOG.debug(\"onTabPreviewChange: blueprint={}\", blueprint);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/NavigationController.java",
    "content": "package jadx.gui.ui.tab;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.JumpManager;\nimport jadx.gui.utils.JumpPosition;\n\n/**\n * TODO: Save jumps history into project file to restore after reload or reopen\n */\npublic class NavigationController implements ITabStatesListener {\n\tprivate final transient MainWindow mainWindow;\n\n\tprivate final transient JumpManager jumps = new JumpManager();\n\n\tpublic NavigationController(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t\tmainWindow.getTabsController().addListener(this);\n\t}\n\n\tpublic void navBack() {\n\t\tjump(jumps.getPrev());\n\t}\n\n\tpublic void navForward() {\n\t\tjump(jumps.getNext());\n\t}\n\n\tprivate void jump(@Nullable JumpPosition pos) {\n\t\tif (pos != null) {\n\t\t\tmainWindow.getTabsController().codeJump(pos);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onTabCodeJump(TabBlueprint blueprint, @Nullable JumpPosition prevPos, JumpPosition newPos) {\n\t\tif (newPos.equals(jumps.getCurrent())) {\n\t\t\t// ignore self-initiated jumps\n\t\t\treturn;\n\t\t}\n\t\tjumps.addPosition(prevPos);\n\t\tjumps.addPosition(newPos);\n\t}\n\n\t@Override\n\tpublic void onTabSmaliJump(TabBlueprint blueprint, int pos, boolean debugMode) {\n\t\t// TODO: save smali jump\n\t}\n\n\tpublic void reset() {\n\t\tjumps.reset();\n\t}\n\n\tpublic void dispose() {\n\t\treset();\n\t\tmainWindow.getTabsController().removeListener(this);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/QuickTabsBaseNode.java",
    "content": "package jadx.gui.ui.tab;\n\nimport javax.swing.Icon;\nimport javax.swing.JPopupMenu;\nimport javax.swing.tree.DefaultMutableTreeNode;\n\nimport jadx.gui.ui.MainWindow;\n\nabstract class QuickTabsBaseNode extends DefaultMutableTreeNode {\n\tJPopupMenu onTreePopupMenu(MainWindow mainWindow) {\n\t\treturn null;\n\t}\n\n\tIcon getIcon() {\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/QuickTabsBookmarkParentNode.java",
    "content": "package jadx.gui.ui.tab;\n\nimport javax.swing.Icon;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPopupMenu;\n\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.NLS;\n\npublic class QuickTabsBookmarkParentNode extends QuickTabsParentNode {\n\tprotected QuickTabsBookmarkParentNode(TabsController tabsController) {\n\t\tsuper(tabsController);\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn NLS.str(\"tree.bookmarked_tabs\");\n\t}\n\n\t@Override\n\tIcon getIcon() {\n\t\treturn Icons.BOOKMARK_DARK;\n\t}\n\n\t@Override\n\tJPopupMenu onTreePopupMenu(MainWindow mainWindow) {\n\t\tif (getChildCount() == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tJPopupMenu menu = new JPopupMenu();\n\t\tJMenuItem unbookmarkAll = new JMenuItem(NLS.str(\"tabs.unbookmark_all\"));\n\t\tunbookmarkAll.addActionListener(e -> getTabsController().unbookmarkAllTabs());\n\t\tmenu.add(unbookmarkAll);\n\n\t\treturn menu;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/QuickTabsChildNode.java",
    "content": "package jadx.gui.ui.tab;\n\nimport javax.swing.Icon;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPopupMenu;\n\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.NLS;\n\npublic class QuickTabsChildNode extends QuickTabsBaseNode {\n\tprivate final JNode node;\n\n\tpublic QuickTabsChildNode(JNode node) {\n\t\tthis.node = node;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn node.toString();\n\t}\n\n\tpublic JNode getJNode() {\n\t\treturn node;\n\t}\n\n\t@Override\n\tpublic JPopupMenu onTreePopupMenu(MainWindow mainWindow) {\n\t\tJPopupMenu menu = node.onTreePopupMenu(mainWindow);\n\n\t\tif (node.supportsQuickTabs()) {\n\t\t\tif (getParent() instanceof QuickTabsPinParentNode) {\n\t\t\t\tif (menu == null) {\n\t\t\t\t\tmenu = new JPopupMenu();\n\t\t\t\t}\n\n\t\t\t\tJMenuItem closeAction = new JMenuItem(NLS.str(\"tabs.close\"));\n\t\t\t\tcloseAction.addActionListener(e -> mainWindow.getTabsController().closeTab(node, true));\n\t\t\t\tmenu.add(closeAction, 0);\n\t\t\t\tmenu.add(new JPopupMenu.Separator(), 1);\n\t\t\t}\n\t\t\tif (getParent() instanceof QuickTabsPinParentNode) {\n\t\t\t\tif (menu == null) {\n\t\t\t\t\tmenu = new JPopupMenu();\n\t\t\t\t}\n\n\t\t\t\tJMenuItem unpinAction = new JMenuItem(NLS.str(\"tabs.unpin\"));\n\t\t\t\tunpinAction.addActionListener(e -> mainWindow.getTabsController().setTabPinned(node, false));\n\t\t\t\tmenu.add(unpinAction, 0);\n\t\t\t\tmenu.add(new JPopupMenu.Separator(), 1);\n\t\t\t}\n\t\t\tif (getParent() instanceof QuickTabsBookmarkParentNode) {\n\t\t\t\tif (menu == null) {\n\t\t\t\t\tmenu = new JPopupMenu();\n\t\t\t\t}\n\n\t\t\t\tJMenuItem unbookmarkAction = new JMenuItem(NLS.str(\"tabs.unbookmark\"));\n\t\t\t\tunbookmarkAction.addActionListener(e -> mainWindow.getTabsController().setTabBookmarked(node, false));\n\t\t\t\tmenu.add(unbookmarkAction, 0);\n\t\t\t\tmenu.add(new JPopupMenu.Separator(), 1);\n\t\t\t}\n\t\t}\n\n\t\treturn menu;\n\t}\n\n\t@Override\n\tIcon getIcon() {\n\t\treturn node.getIcon();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/QuickTabsOpenParentNode.java",
    "content": "package jadx.gui.ui.tab;\n\nimport javax.swing.Icon;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPopupMenu;\n\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.NLS;\n\npublic class QuickTabsOpenParentNode extends QuickTabsParentNode {\n\tprotected QuickTabsOpenParentNode(TabsController tabsController) {\n\t\tsuper(tabsController);\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn NLS.str(\"tree.open_tabs\");\n\t}\n\n\t@Override\n\tIcon getIcon() {\n\t\treturn Icons.FOLDER;\n\t}\n\n\t@Override\n\tJPopupMenu onTreePopupMenu(MainWindow mainWindow) {\n\t\tif (getChildCount() == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tJPopupMenu menu = new JPopupMenu();\n\t\tJMenuItem closeAll = new JMenuItem(NLS.str(\"tabs.closeAll\"));\n\t\tcloseAll.addActionListener(e -> getTabsController().closeAllTabs(true));\n\t\tmenu.add(closeAll);\n\n\t\treturn menu;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/QuickTabsParentNode.java",
    "content": "package jadx.gui.ui.tab;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.swing.JPopupMenu;\n\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\n\nabstract class QuickTabsParentNode extends QuickTabsBaseNode {\n\tprivate final TabsController tabsController;\n\tprivate final Map<JNode, QuickTabsChildNode> childrenMap = new HashMap<>();\n\n\tprotected QuickTabsParentNode(TabsController tabsController) {\n\t\tsuper();\n\n\t\tthis.tabsController = tabsController;\n\t}\n\n\tpublic boolean addJNode(JNode node) {\n\t\tif (childrenMap.containsKey(node)) {\n\t\t\treturn false;\n\t\t}\n\t\tQuickTabsChildNode childNode = new QuickTabsChildNode(node);\n\t\tchildrenMap.put(node, childNode);\n\t\tadd(childNode);\n\t\treturn true;\n\t}\n\n\tpublic boolean removeJNode(JNode node) {\n\t\tQuickTabsChildNode childNode = childrenMap.remove(node);\n\t\tif (childNode == null) {\n\t\t\treturn false;\n\t\t}\n\t\tremove(childNode);\n\t\treturn true;\n\t}\n\n\tpublic void removeAllNodes() {\n\t\tremoveAllChildren();\n\t}\n\n\tpublic QuickTabsChildNode getQuickTabsNode(JNode node) {\n\t\treturn childrenMap.get(node);\n\t}\n\n\tpublic TabsController getTabsController() {\n\t\treturn tabsController;\n\t}\n\n\tabstract String getTitle();\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getTitle();\n\t}\n\n\t@Override\n\tJPopupMenu onTreePopupMenu(MainWindow mainWindow) {\n\t\treturn super.onTreePopupMenu(mainWindow);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/QuickTabsPinParentNode.java",
    "content": "package jadx.gui.ui.tab;\n\nimport javax.swing.Icon;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPopupMenu;\n\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.NLS;\n\npublic class QuickTabsPinParentNode extends QuickTabsParentNode {\n\tprotected QuickTabsPinParentNode(TabsController tabsController) {\n\t\tsuper(tabsController);\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn NLS.str(\"tree.pinned_tabs\");\n\t}\n\n\t@Override\n\tIcon getIcon() {\n\t\treturn Icons.PIN;\n\t}\n\n\t@Override\n\tJPopupMenu onTreePopupMenu(MainWindow mainWindow) {\n\t\tif (getChildCount() == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tJPopupMenu menu = new JPopupMenu();\n\t\tJMenuItem unpinAll = new JMenuItem(NLS.str(\"tabs.unpin_all\"));\n\t\tunpinAll.addActionListener(e -> getTabsController().unpinAllTabs());\n\t\tmenu.add(unpinAll);\n\n\t\treturn menu;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/QuickTabsTree.java",
    "content": "package jadx.gui.ui.tab;\n\nimport java.awt.Component;\nimport java.awt.event.KeyAdapter;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\n\nimport javax.swing.JPopupMenu;\nimport javax.swing.JTree;\nimport javax.swing.SwingUtilities;\nimport javax.swing.event.TreeSelectionEvent;\nimport javax.swing.event.TreeSelectionListener;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.DefaultTreeCellRenderer;\nimport javax.swing.tree.DefaultTreeModel;\nimport javax.swing.tree.TreeNode;\n\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.UiUtils;\n\npublic class QuickTabsTree extends JTree implements ITabStatesListener, TreeSelectionListener {\n\tprivate final MainWindow mainWindow;\n\tprivate final DefaultTreeModel treeModel;\n\n\tprivate final QuickTabsParentNode openParentNode;\n\tprivate final QuickTabsParentNode pinParentNode;\n\tprivate final QuickTabsParentNode bookmarkParentNode;\n\n\tpublic QuickTabsTree(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\n\t\tmainWindow.getTabsController().addListener(this);\n\n\t\tRoot root = new Root();\n\t\tpinParentNode = new QuickTabsPinParentNode(mainWindow.getTabsController());\n\t\topenParentNode = new QuickTabsOpenParentNode(mainWindow.getTabsController());\n\t\tbookmarkParentNode = new QuickTabsBookmarkParentNode(mainWindow.getTabsController());\n\t\troot.add(openParentNode);\n\t\troot.add(pinParentNode);\n\t\troot.add(bookmarkParentNode);\n\n\t\ttreeModel = new DefaultTreeModel(root);\n\t\tsetModel(treeModel);\n\t\tsetCellRenderer(new CellRenderer());\n\t\tsetRootVisible(false);\n\t\tsetShowsRootHandles(true);\n\n\t\taddMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mousePressed(MouseEvent e) {\n\t\t\t\tTreeNode pressedNode = UiUtils.getTreeNodeUnderMouse(QuickTabsTree.this, e);\n\t\t\t\tif (SwingUtilities.isLeftMouseButton(e)) {\n\t\t\t\t\tif (nodeClickAction(pressedNode)) {\n\t\t\t\t\t\tsetFocusable(true);\n\t\t\t\t\t\trequestFocus();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (SwingUtilities.isRightMouseButton(e)) {\n\t\t\t\t\ttriggerRightClickAction(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\taddKeyListener(new KeyAdapter() {\n\t\t\t@Override\n\t\t\tpublic void keyPressed(KeyEvent e) {\n\t\t\t\tif (e.getKeyCode() == KeyEvent.VK_ENTER) {\n\t\t\t\t\tnodeClickAction(getLastSelectedPathComponent());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tloadSettings();\n\n\t\tfillOpenParentNode();\n\t\tfillPinParentNode();\n\t\tfillBookmarkParentNode();\n\t}\n\n\tprivate void triggerRightClickAction(MouseEvent e) {\n\t\tTreeNode treeNode = UiUtils.getTreeNodeUnderMouse(this, e);\n\t\tif (!(treeNode instanceof QuickTabsBaseNode)) {\n\t\t\treturn;\n\t\t}\n\n\t\tQuickTabsBaseNode quickTabsNode = (QuickTabsBaseNode) treeNode;\n\t\tJPopupMenu menu = quickTabsNode.onTreePopupMenu(mainWindow);\n\t\tif (menu != null) {\n\t\t\tmenu.show(e.getComponent(), e.getX(), e.getY());\n\t\t}\n\t}\n\n\tprivate boolean nodeClickAction(Object pressedNode) {\n\t\tif (pressedNode == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (pressedNode instanceof QuickTabsChildNode) {\n\t\t\tQuickTabsChildNode childNode = (QuickTabsChildNode) pressedNode;\n\t\t\tmainWindow.getTabsController().selectTab(childNode.getJNode());\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate void fillOpenParentNode() {\n\t\tmainWindow.getTabsController().getOpenTabs().forEach(this::onTabOpen);\n\t}\n\n\tprivate void fillPinParentNode() {\n\t\tmainWindow.getTabsController().getPinnedTabs().forEach(this::onTabPinChange);\n\t}\n\n\tprivate void fillBookmarkParentNode() {\n\t\tmainWindow.getTabsController().getBookmarkedTabs().forEach(this::onTabBookmarkChange);\n\t}\n\n\tprivate void clearParentNode(QuickTabsParentNode parentNode) {\n\t\tint[] childIndices = new int[parentNode.getChildCount()];\n\t\tObject[] objects = new Object[parentNode.getChildCount()];\n\t\tfor (int i = 0; i < childIndices.length; i++) {\n\t\t\tchildIndices[i] = i;\n\t\t\tobjects[i] = parentNode.getChildAt(i);\n\t\t}\n\t\tparentNode.removeAllNodes();\n\t\ttreeModel.nodesWereRemoved(parentNode, childIndices, objects);\n\t}\n\n\tprivate void addJNode(QuickTabsParentNode parentNode, JNode node) {\n\t\tif (parentNode.addJNode(node)) {\n\t\t\ttreeModel.nodesWereInserted(parentNode, new int[] { parentNode.getChildCount() - 1 });\n\t\t}\n\t}\n\n\tprivate void removeJNode(QuickTabsParentNode parentNode, JNode node) {\n\t\tQuickTabsChildNode child = parentNode.getQuickTabsNode(node);\n\t\tif (child != null) {\n\t\t\tint removedIndex = parentNode.getIndex(child);\n\t\t\tif (parentNode.removeJNode(node)) {\n\t\t\t\ttreeModel.nodesWereRemoved(parentNode, new int[] { removedIndex }, new Object[] { child });\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void valueChanged(TreeSelectionEvent event) {\n\t\tDefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) getLastSelectedPathComponent();\n\t\tif (selectedNode != null) {\n\t\t\tif (selectedNode instanceof QuickTabsChildNode) {\n\t\t\t\tQuickTabsChildNode childNode = (QuickTabsChildNode) selectedNode;\n\t\t\t\tJNode jNode = childNode.getJNode();\n\n\t\t\t\tTabsController tabsController = mainWindow.getTabsController();\n\t\t\t\ttabsController.selectTab(jNode);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void loadSettings() {\n\t\tsetFont(mainWindow.getSettings().getCodeFont());\n\t}\n\n\tpublic void dispose() {\n\t\tmainWindow.getTabsController().removeListener(this);\n\t}\n\n\t@Override\n\tpublic void onTabOpen(TabBlueprint blueprint) {\n\t\tif (!blueprint.isHidden() && blueprint.getNode().supportsQuickTabs()) {\n\t\t\taddJNode(openParentNode, blueprint.getNode());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onTabClose(TabBlueprint blueprint) {\n\t\tremoveJNode(openParentNode, blueprint.getNode());\n\t\tremoveJNode(pinParentNode, blueprint.getNode());\n\t\tremoveJNode(bookmarkParentNode, blueprint.getNode());\n\t}\n\n\t@Override\n\tpublic void onTabPinChange(TabBlueprint blueprint) {\n\t\tJNode node = blueprint.getNode();\n\t\tif (blueprint.isPinned()) {\n\t\t\taddJNode(pinParentNode, node);\n\t\t} else {\n\t\t\tremoveJNode(pinParentNode, node);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onTabBookmarkChange(TabBlueprint blueprint) {\n\t\tJNode node = blueprint.getNode();\n\t\tif (blueprint.isBookmarked()) {\n\t\t\taddJNode(bookmarkParentNode, node);\n\t\t} else {\n\t\t\tremoveJNode(bookmarkParentNode, node);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onTabVisibilityChange(TabBlueprint blueprint) {\n\t\tJNode node = blueprint.getNode();\n\t\tif (!blueprint.isHidden()) {\n\t\t\taddJNode(openParentNode, node);\n\t\t} else {\n\t\t\tremoveJNode(openParentNode, node);\n\t\t}\n\t}\n\n\tprivate class Root extends DefaultMutableTreeNode {\n\n\t}\n\n\tprivate class CellRenderer extends DefaultTreeCellRenderer {\n\t\t@Override\n\t\tpublic Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row,\n\t\t\t\tboolean hasFocus) {\n\t\t\tComponent c = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);\n\t\t\tif (value instanceof QuickTabsBaseNode) {\n\t\t\t\tQuickTabsBaseNode quickTabsNode = (QuickTabsBaseNode) value;\n\t\t\t\tsetIcon(quickTabsNode.getIcon());\n\t\t\t}\n\t\t\treturn c;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/TabBlueprint.java",
    "content": "package jadx.gui.ui.tab;\n\nimport java.util.Objects;\n\nimport jadx.gui.treemodel.JNode;\n\npublic class TabBlueprint {\n\tprivate final JNode node;\n\tprivate boolean created;\n\tprivate boolean pinned;\n\tprivate boolean bookmarked;\n\tprivate boolean hidden;\n\tprivate boolean previewTab;\n\n\tpublic TabBlueprint(JNode node) {\n\t\tthis.node = Objects.requireNonNull(node);\n\t}\n\n\tpublic JNode getNode() {\n\t\treturn node;\n\t}\n\n\tpublic boolean isCreated() {\n\t\treturn created;\n\t}\n\n\tpublic void setCreated(boolean created) {\n\t\tthis.created = created;\n\t}\n\n\tpublic boolean isPinned() {\n\t\treturn pinned;\n\t}\n\n\tpublic void setPinned(boolean pinned) {\n\t\tthis.pinned = pinned;\n\t}\n\n\tpublic boolean isBookmarked() {\n\t\treturn bookmarked;\n\t}\n\n\tpublic void setBookmarked(boolean bookmarked) {\n\t\tthis.bookmarked = bookmarked;\n\t}\n\n\tpublic boolean supportsQuickTabs() {\n\t\treturn node.supportsQuickTabs();\n\t}\n\n\tpublic boolean isReferenced() {\n\t\treturn isBookmarked();\n\t}\n\n\tpublic boolean isHidden() {\n\t\treturn hidden;\n\t}\n\n\tpublic void setHidden(boolean hidden) {\n\t\tthis.hidden = hidden;\n\t}\n\n\tpublic boolean isPreviewTab() {\n\t\treturn previewTab;\n\t}\n\n\tpublic void setPreviewTab(boolean previewTab) {\n\t\tthis.previewTab = previewTab;\n\t}\n\n\t@Override\n\tpublic final boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof TabBlueprint)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn node.equals(((TabBlueprint) o).node);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn node.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"TabBlueprint{\" + \"node=\"\n\t\t\t\t+ node + \", pinned=\"\n\t\t\t\t+ pinned + \", bookmarked=\"\n\t\t\t\t+ bookmarked + \", hidden=\"\n\t\t\t\t+ hidden + \", previewTab=\"\n\t\t\t\t+ previewTab + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/TabComponent.java",
    "content": "package jadx.gui.ui.tab;\n\nimport java.awt.FlowLayout;\nimport java.awt.Font;\nimport java.awt.Point;\nimport java.awt.dnd.DnDConstants;\nimport java.awt.dnd.DragGestureEvent;\nimport java.awt.dnd.DragGestureListener;\nimport java.awt.dnd.DragSource;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.util.List;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.JButton;\nimport javax.swing.JLabel;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPanel;\nimport javax.swing.JPopupMenu;\nimport javax.swing.SwingUtilities;\nimport javax.swing.plaf.basic.BasicButtonUI;\n\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JEditableNode;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.action.ActionModel;\nimport jadx.gui.ui.action.JadxGuiAction;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.tab.dnd.TabDndGestureListener;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.OverlayIcon;\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.ui.NodeLabel;\n\npublic class TabComponent extends JPanel {\n\tprivate static final long serialVersionUID = -8147035487543610321L;\n\n\tprivate static final int TAB_TITLE_MAX_LENGTH = 30;\n\n\tprivate final TabbedPane tabbedPane;\n\tprivate final TabsController tabsController;\n\tprivate final ContentPanel contentPanel;\n\n\tprivate OverlayIcon icon;\n\tprivate JLabel label;\n\tprivate JButton pinBtn;\n\tprivate JButton closeBtn;\n\n\tpublic TabComponent(TabbedPane tabbedPane, ContentPanel contentPanel) {\n\t\tthis.tabbedPane = tabbedPane;\n\t\tthis.tabsController = tabbedPane.getMainWindow().getTabsController();\n\t\tthis.contentPanel = contentPanel;\n\n\t\tinit();\n\t}\n\n\tpublic void loadSettings() {\n\t\tlabel.setFont(getLabelFont());\n\t\tif (tabbedPane.getDnd() != null) {\n\t\t\ttabbedPane.getDnd().loadSettings();\n\t\t}\n\t}\n\n\tprivate Font getLabelFont() {\n\t\tFont font = tabsController.getMainWindow().getSettings().getCodeFont();\n\t\tint style = font.getStyle();\n\t\tstyle |= Font.BOLD;\n\t\tif (getBlueprint().isPreviewTab()) {\n\t\t\tstyle ^= Font.ITALIC; // flip italic bit to distinguish preview\n\t\t}\n\t\treturn font.deriveFont(style);\n\t}\n\n\tprivate void init() {\n\t\tsetLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));\n\t\tsetOpaque(false);\n\n\t\tJNode node = getNode();\n\t\ticon = new OverlayIcon(node.getIcon());\n\n\t\tlabel = new NodeLabel(buildTabTitle(node), node.disableHtml());\n\t\tString toolTip = contentPanel.getNode().getTooltip();\n\t\tif (toolTip != null) {\n\t\t\tsetToolTipText(toolTip);\n\t\t}\n\t\tlabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));\n\t\tlabel.setIcon(icon);\n\t\tif (node instanceof JEditableNode) {\n\t\t\t((JEditableNode) node).addChangeListener(c -> label.setText(buildTabTitle(node)));\n\t\t}\n\n\t\tpinBtn = new JButton();\n\t\tpinBtn.setIcon(Icons.PIN);\n\t\tpinBtn.setRolloverIcon(Icons.PIN_HOVERED);\n\t\tpinBtn.setRolloverEnabled(true);\n\t\tpinBtn.setOpaque(false);\n\t\tpinBtn.setUI(new BasicButtonUI());\n\t\tpinBtn.setContentAreaFilled(false);\n\t\tpinBtn.setBorder(null);\n\t\tpinBtn.setBorderPainted(false);\n\t\tpinBtn.addActionListener(e -> togglePin());\n\n\t\tcloseBtn = new JButton();\n\t\tcloseBtn.setIcon(Icons.CLOSE_INACTIVE);\n\t\tcloseBtn.setRolloverIcon(Icons.CLOSE);\n\t\tcloseBtn.setRolloverEnabled(true);\n\t\tcloseBtn.setOpaque(false);\n\t\tcloseBtn.setUI(new BasicButtonUI());\n\t\tcloseBtn.setContentAreaFilled(false);\n\t\tcloseBtn.setFocusable(false);\n\t\tcloseBtn.setBorder(null);\n\t\tcloseBtn.setBorderPainted(false);\n\t\tcloseBtn.addActionListener(e -> {\n\t\t\ttabsController.closeTab(node, true);\n\t\t});\n\n\t\tMouseAdapter clickAdapter = new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mousePressed(MouseEvent e) {\n\t\t\t\tif (SwingUtilities.isMiddleMouseButton(e)) {\n\t\t\t\t\ttabsController.closeTab(node, true);\n\t\t\t\t} else if (SwingUtilities.isRightMouseButton(e)) {\n\t\t\t\t\tJPopupMenu menu = createTabPopupMenu();\n\t\t\t\t\tmenu.show(e.getComponent(), e.getX(), e.getY());\n\t\t\t\t} else if (SwingUtilities.isLeftMouseButton(e)) {\n\t\t\t\t\ttabsController.selectTab(node);\n\t\t\t\t\tif (e.getClickCount() == 2) {\n\t\t\t\t\t\ttabsController.setTabPreview(node, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\taddMouseListener(clickAdapter);\n\t\taddListenerForDnd();\n\n\t\tadd(label);\n\t\tsetBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));\n\n\t\tupdate();\n\t}\n\n\tpublic void update() {\n\t\tupdateCloseOrPinButton();\n\t\tupdateBookmarkIcon();\n\t\tupdateFont();\n\t}\n\n\tprivate void updateCloseOrPinButton() {\n\t\tif (getBlueprint().isPinned()) {\n\t\t\tif (closeBtn.isShowing()) {\n\t\t\t\tremove(closeBtn);\n\t\t\t}\n\t\t\tif (!pinBtn.isShowing()) {\n\t\t\t\tadd(pinBtn);\n\t\t\t}\n\t\t} else {\n\t\t\tif (pinBtn.isShowing()) {\n\t\t\t\tremove(pinBtn);\n\t\t\t}\n\t\t\tif (!closeBtn.isShowing()) {\n\t\t\t\tadd(closeBtn);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void updateBookmarkIcon() {\n\t\ticon.clear();\n\n\t\tif (getBlueprint().isBookmarked()) {\n\t\t\ticon.add(Icons.BOOKMARK_OVERLAY_DARK);\n\t\t}\n\t\tlabel.repaint();\n\t}\n\n\tprivate void togglePin() {\n\t\tboolean pinned = !getBlueprint().isPinned();\n\t\ttabsController.setTabPinned(getNode(), pinned);\n\n\t\tif (pinned) {\n\t\t\ttabsController.setTabPositionFirst(getNode());\n\t\t}\n\t}\n\n\tprivate void toggleBookmark() {\n\t\tboolean bookmarked = !getBlueprint().isBookmarked();\n\t\ttabsController.setTabBookmarked(getNode(), bookmarked);\n\t}\n\n\tprivate void updateFont() {\n\t\tlabel.setFont(getLabelFont());\n\t}\n\n\tprivate void addListenerForDnd() {\n\t\tif (tabbedPane.getDnd() == null) {\n\t\t\treturn;\n\t\t}\n\t\tTabComponent comp = this;\n\t\tDragGestureListener dgl = new TabDndGestureListener(tabbedPane.getDnd()) {\n\t\t\t@Override\n\t\t\tprotected Point getDragOrigin(DragGestureEvent e) {\n\t\t\t\treturn SwingUtilities.convertPoint(comp, e.getDragOrigin(), tabbedPane);\n\t\t\t}\n\t\t};\n\t\tDragSource.getDefaultDragSource()\n\t\t\t\t.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE, dgl);\n\t}\n\n\tprivate String buildTabTitle(JNode node) {\n\t\tString tabTitle = node.makeStringHtml();\n\t\tif (tabbedPane.tabWithTitleExists(tabTitle)) {\n\t\t\ttabTitle = node.makeLongString();\n\t\t}\n\t\tString newTabTitle = UiUtils.limitStringLength(tabTitle, TAB_TITLE_MAX_LENGTH);\n\t\tif (!newTabTitle.equals(tabTitle)) {\n\t\t\tif (tabbedPane.tabWithTitleExists(newTabTitle)) {\n\t\t\t\t// shorter version also exist => make longer version (last try)\n\t\t\t\ttabTitle = UiUtils.limitStringLength(tabTitle, (int) (TAB_TITLE_MAX_LENGTH * 1.2));\n\t\t\t} else {\n\t\t\t\ttabTitle = newTabTitle;\n\t\t\t}\n\t\t}\n\t\tif (node instanceof JEditableNode) {\n\t\t\tif (((JEditableNode) node).isChanged()) {\n\t\t\t\treturn \"*\" + tabTitle;\n\t\t\t}\n\t\t}\n\t\treturn tabTitle;\n\t}\n\n\tprivate JPopupMenu createTabPopupMenu() {\n\t\tJPopupMenu menu = new JPopupMenu();\n\n\t\tString nodeFullName = getNodeFullName(contentPanel);\n\t\tif (nodeFullName != null) {\n\t\t\tJMenuItem copyRootClassName = new JMenuItem(NLS.str(\"tabs.copy_class_name\"));\n\t\t\tcopyRootClassName.addActionListener(actionEvent -> UiUtils.setClipboardString(nodeFullName));\n\t\t\tmenu.add(copyRootClassName);\n\t\t\tmenu.addSeparator();\n\t\t}\n\n\t\tif (getBlueprint().supportsQuickTabs()) {\n\t\t\tString pinTitle = getBlueprint().isPinned() ? NLS.str(\"tabs.unpin\") : NLS.str(\"tabs.pin\");\n\t\t\tJMenuItem pinTab = new JMenuItem(pinTitle);\n\t\t\tpinTab.addActionListener(e -> togglePin());\n\t\t\tmenu.add(pinTab);\n\n\t\t\tJMenuItem unpinAll = new JMenuItem(NLS.str(\"tabs.unpin_all\"));\n\t\t\tunpinAll.addActionListener(e -> tabsController.unpinAllTabs());\n\t\t\tmenu.add(unpinAll);\n\n\t\t\tString bookmarkTitle = getBlueprint().isBookmarked() ? NLS.str(\"tabs.unbookmark\") : NLS.str(\"tabs.bookmark\");\n\t\t\tJMenuItem bookmarkTab = new JMenuItem(bookmarkTitle);\n\t\t\tbookmarkTab.addActionListener(e -> toggleBookmark());\n\t\t\tmenu.add(bookmarkTab);\n\n\t\t\tJMenuItem unbookmarkAll = new JMenuItem(NLS.str(\"tabs.unbookmark_all\"));\n\t\t\tunbookmarkAll.addActionListener(e -> tabsController.unbookmarkAllTabs());\n\t\t\tmenu.add(unbookmarkAll);\n\t\t\tmenu.addSeparator();\n\t\t}\n\n\t\tif (nodeFullName != null) {\n\t\t\tMainWindow mainWindow = tabsController.getMainWindow();\n\t\t\tJadxGuiAction selectInTree = new JadxGuiAction(ActionModel.SYNC, () -> mainWindow.selectNodeInTree(getNode()));\n\t\t\t// attach shortcut without bind only to show current keybinding\n\t\t\tselectInTree.setShortcut(mainWindow.getShortcutsController().get(ActionModel.SYNC));\n\t\t\tmenu.add(selectInTree);\n\t\t\tmenu.addSeparator();\n\t\t}\n\n\t\tJMenuItem closeTab = new JMenuItem(NLS.str(\"tabs.close\"));\n\t\tcloseTab.addActionListener(e -> tabsController.closeTab(getNode(), true));\n\t\tif (getBlueprint().isPinned()) {\n\t\t\tcloseTab.setEnabled(false);\n\t\t}\n\t\tmenu.add(closeTab);\n\n\t\tList<TabBlueprint> tabs = tabsController.getOpenTabs();\n\t\tif (tabs.size() > 1) {\n\t\t\tJMenuItem closeOther = new JMenuItem(NLS.str(\"tabs.closeOthers\"));\n\t\t\tcloseOther.addActionListener(e -> {\n\t\t\t\tJNode currentNode = getNode();\n\t\t\t\tfor (TabBlueprint tab : tabs) {\n\t\t\t\t\tif (tab.getNode() != currentNode) {\n\t\t\t\t\t\ttabsController.closeTab(tab, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\tmenu.add(closeOther);\n\n\t\t\tJMenuItem closeAll = new JMenuItem(NLS.str(\"tabs.closeAll\"));\n\t\t\tcloseAll.addActionListener(e -> tabsController.closeAllTabs(true));\n\t\t\tmenu.add(closeAll);\n\n\t\t\t// We don't use TabsController here because tabs position is\n\t\t\t// specific to TabbedPane\n\t\t\tList<ContentPanel> contentPanels = tabbedPane.getTabs();\n\t\t\tint currentIndex = contentPanels.indexOf(contentPanel);\n\t\t\tif (currentIndex > 0) { // Add item only if there are tabs on the left (index > 0)\n\t\t\t\tJMenuItem closeAllLeft = new JMenuItem(NLS.str(\"tabs.closeAllLeft\"));\n\t\t\t\tcloseAllLeft.addActionListener(e -> {\n\t\t\t\t\t// Iterate in reverse order from the index before the current tab to the beginning\n\t\t\t\t\tfor (int i = currentIndex - 1; i >= 0; i--) {\n\t\t\t\t\t\tContentPanel panelToClose = contentPanels.get(i);\n\t\t\t\t\t\ttabsController.closeTab(panelToClose.getNode(), true);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tmenu.add(closeAllLeft);\n\t\t\t}\n\t\t\tif (contentPanel != ListUtils.last(contentPanels)) {\n\t\t\t\tJMenuItem closeAllRight = new JMenuItem(NLS.str(\"tabs.closeAllRight\"));\n\t\t\t\tcloseAllRight.addActionListener(e -> {\n\t\t\t\t\tboolean pastCurrentPanel = false;\n\t\t\t\t\tfor (ContentPanel panel : contentPanels) {\n\t\t\t\t\t\tif (!pastCurrentPanel) {\n\t\t\t\t\t\t\tif (panel == contentPanel) {\n\t\t\t\t\t\t\t\tpastCurrentPanel = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttabsController.closeTab(panel.getNode(), true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tmenu.add(closeAllRight);\n\t\t\t}\n\t\t\tmenu.addSeparator();\n\n\t\t\tTabBlueprint selectedTab = tabsController.getSelectedTab();\n\t\t\tfor (TabBlueprint tab : tabs) {\n\t\t\t\tif (tab == selectedTab) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tJNode node = tab.getNode();\n\t\t\t\tfinal String clsName = node.makeLongString();\n\t\t\t\tJMenuItem item = new JMenuItem(clsName);\n\t\t\t\titem.addActionListener(e -> tabsController.codeJump(node));\n\t\t\t\titem.setIcon(node.getIcon());\n\t\t\t\tmenu.add(item);\n\t\t\t}\n\t\t}\n\t\treturn menu;\n\t}\n\n\tprivate String getNodeFullName(ContentPanel contentPanel) {\n\t\tJNode node = contentPanel.getNode();\n\t\tJClass jClass = node.getRootClass();\n\t\tif (jClass != null) {\n\t\t\treturn jClass.getFullName();\n\t\t}\n\t\treturn node.getName();\n\t}\n\n\tpublic ContentPanel getContentPanel() {\n\t\treturn contentPanel;\n\t}\n\n\tpublic TabBlueprint getBlueprint() {\n\t\tJNode node = contentPanel.getNode();\n\t\tTabBlueprint blueprint = tabsController.getTabByNode(node);\n\t\tif (blueprint == null) {\n\t\t\tthrow new JadxRuntimeException(\"TabComponent does not have a corresponding TabBlueprint, node: \" + node);\n\t\t}\n\t\treturn blueprint;\n\t}\n\n\tpublic JNode getNode() {\n\t\treturn contentPanel.getNode();\n\t}\n\n\tpublic String getTabTitle() {\n\t\treturn label.getText();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/TabbedPane.java",
    "content": "package jadx.gui.ui.tab;\n\nimport java.awt.Component;\nimport java.awt.KeyEventDispatcher;\nimport java.awt.KeyboardFocusManager;\nimport java.awt.event.FocusEvent;\nimport java.awt.event.FocusListener;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.swing.JTabbedPane;\nimport javax.swing.SwingUtilities;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.jobs.SilentTask;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.codearea.AbstractCodeArea;\nimport jadx.gui.ui.codearea.AbstractCodeContentPanel;\nimport jadx.gui.ui.codearea.ClassCodeContentPanel;\nimport jadx.gui.ui.codearea.EditorViewState;\nimport jadx.gui.ui.codearea.SmaliArea;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.panel.FontPanel;\nimport jadx.gui.ui.panel.HtmlPanel;\nimport jadx.gui.ui.panel.IViewStateSupport;\nimport jadx.gui.ui.panel.ImagePanel;\nimport jadx.gui.ui.tab.dnd.TabDndController;\nimport jadx.gui.utils.JumpPosition;\nimport jadx.gui.utils.UiUtils;\n\npublic class TabbedPane extends JTabbedPane implements ITabStatesListener {\n\tprivate static final long serialVersionUID = -8833600618794570904L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(TabbedPane.class);\n\n\tprivate final transient MainWindow mainWindow;\n\tprivate final transient TabsController controller;\n\tprivate final transient Map<JNode, ContentPanel> tabsMap = new HashMap<>();\n\n\tprivate transient ContentPanel curTab;\n\tprivate transient ContentPanel lastTab;\n\n\tprivate transient TabDndController dnd;\n\n\tpublic TabbedPane(MainWindow window, TabsController controller) {\n\t\tthis.mainWindow = window;\n\t\tthis.controller = controller;\n\n\t\tcontroller.addListener(this);\n\t\tsetTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);\n\n\t\tMouseAdapter clickAdapter = new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mousePressed(MouseEvent e) {\n\t\t\t\tint tabIndex = indexAtLocation(e.getX(), e.getY());\n\t\t\t\tif (tabIndex == -1 || tabIndex > getTabCount()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tTabComponent tab = (TabComponent) getTabComponentAt(tabIndex);\n\t\t\t\ttab.dispatchEvent(e);\n\t\t\t}\n\t\t};\n\t\taddMouseListener(clickAdapter);\n\n\t\taddMouseWheelListener(event -> {\n\t\t\tif (dnd != null && dnd.isDragging()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tint direction = event.getWheelRotation();\n\t\t\tif (getTabCount() == 0 || direction == 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdirection = (direction < 0) ? -1 : 1; // normalize direction\n\t\t\tint index = getSelectedIndex();\n\t\t\tint maxIndex = getTabCount() - 1;\n\t\t\tindex += direction;\n\t\t\tindex = Math.max(0, Math.min(maxIndex, index));\n\t\t\ttry {\n\t\t\t\tsetSelectedIndex(index);\n\t\t\t} catch (IndexOutOfBoundsException e) {\n\t\t\t\t// ignore error\n\t\t\t}\n\t\t});\n\t\tinterceptTabKey();\n\t\tinterceptCloseKey();\n\t\tenableSwitchingTabs();\n\t}\n\n\tprivate void interceptTabKey() {\n\t\tKeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() {\n\t\t\tprivate static final int ctrlDown = KeyEvent.CTRL_DOWN_MASK;\n\t\t\tprivate long ctrlInterval = 0;\n\n\t\t\t@Override\n\t\t\tpublic boolean dispatchKeyEvent(KeyEvent e) {\n\t\t\t\tlong cur = System.currentTimeMillis();\n\t\t\t\tif (!FocusManager.isActive()) {\n\t\t\t\t\treturn false; // don't do nothing when tab is not on focus.\n\t\t\t\t}\n\t\t\t\tint code = e.getKeyCode();\n\t\t\t\tboolean consume = code == KeyEvent.VK_TAB; // consume Tab key event anyway\n\t\t\t\tboolean isReleased = e.getID() == KeyEvent.KEY_RELEASED;\n\t\t\t\tif (isReleased) {\n\t\t\t\t\tif (code == KeyEvent.VK_CONTROL) {\n\t\t\t\t\t\tctrlInterval = cur;\n\t\t\t\t\t} else if (code == KeyEvent.VK_TAB) {\n\t\t\t\t\t\tboolean doSwitch = false;\n\t\t\t\t\t\tif ((e.getModifiersEx() & ctrlDown) != 0) {\n\t\t\t\t\t\t\tdoSwitch = lastTab != null && getTabCount() > 1;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// the gap of the release of ctrl and tab is very close, nearly the same time,\n\t\t\t\t\t\t\t// but ctrl released first.\n\t\t\t\t\t\t\tctrlInterval = cur - ctrlInterval;\n\t\t\t\t\t\t\tif (ctrlInterval <= 90) {\n\t\t\t\t\t\t\t\tdoSwitch = lastTab != null && getTabCount() > 1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (doSwitch) {\n\t\t\t\t\t\t\tselectTab(lastTab);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (consume && (e.getModifiersEx() & ctrlDown) == 0) {\n\t\t\t\t\t// switch between source and smali\n\t\t\t\t\tif (curTab instanceof ClassCodeContentPanel) {\n\t\t\t\t\t\t((ClassCodeContentPanel) curTab).switchPanel();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn consume;\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void interceptCloseKey() {\n\t\tKeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() {\n\t\t\tprivate static final int closeKey = KeyEvent.VK_W;\n\t\t\tprivate boolean canClose = true;\n\n\t\t\t@Override\n\t\t\tpublic boolean dispatchKeyEvent(KeyEvent e) {\n\t\t\t\tif (!FocusManager.isActive()) {\n\t\t\t\t\treturn false; // do nothing when tab is not on focus.\n\t\t\t\t}\n\t\t\t\tif (e.getKeyCode() != closeKey) {\n\t\t\t\t\treturn false; // only intercept the events of the close key\n\t\t\t\t}\n\t\t\t\tif (e.getID() == KeyEvent.KEY_RELEASED) {\n\t\t\t\t\tcanClose = true; // after the close key is lifted we allow to use it again\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (e.isControlDown() && canClose) {\n\t\t\t\t\t// close the current tab\n\t\t\t\t\tcloseCodePanel(curTab);\n\t\t\t\t\tcanClose = false; // make sure we dont close more tabs until the close key is lifted\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void enableSwitchingTabs() {\n\t\taddChangeListener(e -> {\n\t\t\tContentPanel tab = getSelectedContentPanel();\n\t\t\tif (tab == null) { // all closed\n\t\t\t\tcurTab = null;\n\t\t\t\tlastTab = null;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tFocusManager.focusOnCodePanel(tab);\n\t\t\tif (tab == curTab) { // a tab was closed by not the current one.\n\t\t\t\tif (lastTab != null && indexOfComponent(lastTab) == -1) { // lastTab was closed\n\t\t\t\t\tsetLastTabAdjacentToCurTab();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (tab == lastTab) {\n\t\t\t\tif (indexOfComponent(curTab) == -1) { // curTab was closed and lastTab is the current one.\n\t\t\t\t\tcurTab = lastTab;\n\t\t\t\t\tsetLastTabAdjacentToCurTab();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// it's switching between lastTab and curTab.\n\t\t\t}\n\t\t\tlastTab = curTab;\n\t\t\tcurTab = tab;\n\t\t});\n\t}\n\n\tprivate void setLastTabAdjacentToCurTab() {\n\t\tif (getTabCount() < 2) {\n\t\t\tlastTab = null;\n\t\t\treturn;\n\t\t}\n\t\tint idx = indexOfComponent(curTab);\n\t\tif (idx == 0) {\n\t\t\tlastTab = (ContentPanel) getComponentAt(idx + 1);\n\t\t} else {\n\t\t\tlastTab = (ContentPanel) getComponentAt(idx - 1);\n\t\t}\n\t}\n\n\tpublic MainWindow getMainWindow() {\n\t\treturn mainWindow;\n\t}\n\n\tpublic TabsController getTabsController() {\n\t\treturn controller;\n\t}\n\n\tprivate @Nullable ContentPanel showCode(JumpPosition jumpPos) {\n\t\tUiUtils.uiThreadGuard();\n\t\tJNode jumpNode = jumpPos.getNode();\n\t\tContentPanel contentPanel = getTabByNode(jumpNode);\n\t\tif (contentPanel == null) {\n\t\t\treturn null;\n\t\t}\n\t\tselectTab(contentPanel);\n\t\tint pos = jumpPos.getPos();\n\t\tif (pos <= 0) {\n\t\t\tLOG.warn(\"Invalid jump: {}\", jumpPos, new JadxRuntimeException());\n\t\t\tpos = Math.max(0, jumpNode.getPos());\n\t\t}\n\t\tcontentPanel.scrollToPos(pos);\n\t\treturn contentPanel;\n\t}\n\n\tpublic void selectTab(ContentPanel contentPanel) {\n\t\tcontroller.selectTab(contentPanel.getNode());\n\t}\n\n\tprivate void smaliJump(JClass cls, int pos, boolean debugMode) {\n\t\tContentPanel panel = getTabByNode(cls);\n\t\tif (panel == null) {\n\t\t\tpanel = showCode(new JumpPosition(cls, 1));\n\t\t\tif (panel == null) {\n\t\t\t\tthrow new JadxRuntimeException(\"Failed to open panel for JClass: \" + cls);\n\t\t\t}\n\t\t} else {\n\t\t\tselectTab(panel);\n\t\t}\n\t\tClassCodeContentPanel codePane = (ClassCodeContentPanel) panel;\n\t\tcodePane.showSmaliPane();\n\t\tSmaliArea smaliArea = (SmaliArea) codePane.getSmaliCodeArea();\n\t\tif (debugMode) {\n\t\t\tsmaliArea.scrollToDebugPos(pos);\n\t\t}\n\t\tsmaliArea.scrollToPos(pos);\n\t\tsmaliArea.requestFocus();\n\t}\n\n\tpublic @Nullable JumpPosition getCurrentPosition() {\n\t\tContentPanel selectedCodePanel = getSelectedContentPanel();\n\t\tif (selectedCodePanel instanceof AbstractCodeContentPanel) {\n\t\t\tAbstractCodeArea codeArea = ((AbstractCodeContentPanel) selectedCodePanel).getCodeArea();\n\t\t\tif (codeArea != null) {\n\t\t\t\treturn codeArea.getCurrentPosition();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate void addContentPanel(ContentPanel contentPanel) {\n\t\ttabsMap.put(contentPanel.getNode(), contentPanel);\n\t\tint tabCount = getTabCount();\n\t\tadd(contentPanel, tabCount);\n\t\tsetTabComponentAt(tabCount, makeTabComponent(contentPanel));\n\t}\n\n\tpublic void closeCodePanel(ContentPanel contentPanel) {\n\t\tcloseCodePanel(contentPanel, false);\n\t}\n\n\tpublic void closeCodePanel(ContentPanel contentPanel, boolean considerPins) {\n\t\tcontroller.closeTab(contentPanel.getNode(), considerPins);\n\t}\n\n\tpublic List<ContentPanel> getTabs() {\n\t\tList<ContentPanel> list = new ArrayList<>(getTabCount());\n\t\tfor (int i = 0; i < getTabCount(); i++) {\n\t\t\tlist.add((ContentPanel) getComponentAt(i));\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic @Nullable ContentPanel getTabByNode(JNode node) {\n\t\treturn tabsMap.get(node);\n\t}\n\n\tpublic @Nullable TabComponent getTabComponentByNode(JNode node) {\n\t\tContentPanel contentPanel = getTabByNode(node);\n\t\tif (contentPanel == null) {\n\t\t\treturn null;\n\t\t}\n\t\tint index = indexOfComponent(contentPanel);\n\t\tif (index == -1) {\n\t\t\treturn null;\n\t\t}\n\t\tComponent component = getTabComponentAt(index);\n\t\tif (!(component instanceof TabComponent)) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn (TabComponent) component;\n\t}\n\n\tpublic boolean tabWithTitleExists(String tabTitle) {\n\t\ttry {\n\t\t\tfor (int i = 0; i < getTabCount(); i++) {\n\t\t\t\tComponent component = getTabComponentAt(i);\n\t\t\t\tif (component instanceof TabComponent) {\n\t\t\t\t\tif (((TabComponent) component).getTabTitle().equals(tabTitle)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to check tabs titles\", e);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic void refresh(JNode node) {\n\t\tContentPanel panel = getTabByNode(node);\n\t\tif (panel != null) {\n\t\t\tsetTabComponentAt(indexOfComponent(panel), makeTabComponent(panel));\n\t\t\tfireStateChanged();\n\t\t}\n\t}\n\n\tpublic void reloadInactiveTabs() {\n\t\tUiUtils.uiThreadGuard();\n\t\tint tabCount = getTabCount();\n\t\tif (tabCount == 1) {\n\t\t\treturn;\n\t\t}\n\t\tint current = getSelectedIndex();\n\t\tfor (int i = 0; i < tabCount; i++) {\n\t\t\tif (i == current) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tContentPanel oldPanel = (ContentPanel) getComponentAt(i);\n\t\t\tTabBlueprint tab = controller.getTabByNode(oldPanel.getNode());\n\t\t\tif (tab == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tEditorViewState viewState = controller.getEditorViewState(tab);\n\t\t\tJNode node = oldPanel.getNode();\n\t\t\tContentPanel panel = node.getContentPanel(this);\n\t\t\tFocusManager.listen(panel);\n\t\t\ttabsMap.put(node, panel);\n\t\t\tsetComponentAt(i, panel);\n\t\t\tsetTabComponentAt(i, makeTabComponent(panel));\n\t\t\tcontroller.restoreEditorViewState(viewState);\n\t\t}\n\t\tfireStateChanged();\n\t}\n\n\t@Nullable\n\tpublic ContentPanel getSelectedContentPanel() {\n\t\treturn (ContentPanel) getSelectedComponent();\n\t}\n\n\tprivate Component makeTabComponent(final ContentPanel contentPanel) {\n\t\treturn new TabComponent(this, contentPanel);\n\t}\n\n\tpublic void closeAllTabs() {\n\t\tcloseAllTabs(false);\n\t}\n\n\tpublic void closeAllTabs(boolean considerPins) {\n\t\tfor (ContentPanel panel : getTabs()) {\n\t\t\tcloseCodePanel(panel, considerPins);\n\t\t}\n\t}\n\n\tpublic void loadSettings() {\n\t\tfor (int i = 0; i < getTabCount(); i++) {\n\t\t\t((ContentPanel) getComponentAt(i)).loadSettings();\n\t\t\t((TabComponent) getTabComponentAt(i)).loadSettings();\n\t\t}\n\t}\n\n\tpublic void reset() {\n\t\tcloseAllTabs();\n\t\ttabsMap.clear();\n\t\tcurTab = null;\n\t\tlastTab = null;\n\t\tFocusManager.reset();\n\t}\n\n\t@Nullable\n\tpublic Component getFocusedComp() {\n\t\treturn FocusManager.getFocusedComp();\n\t}\n\n\tpublic TabDndController getDnd() {\n\t\treturn dnd;\n\t}\n\n\tpublic void setDnd(TabDndController dnd) {\n\t\tthis.dnd = dnd;\n\t}\n\n\t@Override\n\tpublic void onTabOpen(TabBlueprint blueprint) {\n\t\tif (blueprint.isHidden()) {\n\t\t\treturn;\n\t\t}\n\t\tJNode node = blueprint.getNode();\n\t\tContentPanel newPanel = node.getContentPanel(this);\n\t\tif (newPanel != null) {\n\t\t\tif (node != newPanel.getNode()) {\n\t\t\t\tthrow new JadxRuntimeException(\"Incorrect node found in content panel\");\n\t\t\t}\n\t\t\tFocusManager.listen(newPanel);\n\t\t\taddContentPanel(newPanel);\n\t\t\tblueprint.setCreated(true);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onTabSelect(TabBlueprint blueprint) {\n\t\tContentPanel contentPanel = getTabByNode(blueprint.getNode());\n\t\tif (contentPanel != null) {\n\t\t\tsetSelectedComponent(contentPanel);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onTabCodeJump(TabBlueprint blueprint, @Nullable JumpPosition prevPos, JumpPosition position) {\n\t\t// queue task to wait completion of loading tasks\n\t\tmainWindow.getBackgroundExecutor().execute(new SilentTask(() -> {\n\t\t\tUiUtils.uiRun(() -> showCode(position));\n\t\t}));\n\t}\n\n\t@Override\n\tpublic void onTabSmaliJump(TabBlueprint blueprint, int pos, boolean debugMode) {\n\t\tJNode node = blueprint.getNode();\n\t\tif (node instanceof JClass) {\n\t\t\tsmaliJump((JClass) node, pos, debugMode);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onTabClose(TabBlueprint blueprint) {\n\t\tContentPanel contentPanelToClose = getTabByNode(blueprint.getNode());\n\t\tif (contentPanelToClose == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tContentPanel currentContentPanel = getSelectedContentPanel();\n\t\tif (currentContentPanel == contentPanelToClose) {\n\t\t\tif (lastTab != null && lastTab.getNode() != null) {\n\t\t\t\tselectTab(lastTab);\n\t\t\t} else if (getTabCount() > 1) {\n\t\t\t\tint removalIdx = indexOfComponent(contentPanelToClose);\n\t\t\t\tif (removalIdx > 0) { // select left tab\n\t\t\t\t\tsetSelectedIndex(removalIdx - 1);\n\t\t\t\t} else if (removalIdx == 0) { // select right tab\n\t\t\t\t\tsetSelectedIndex(removalIdx + 1);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// no other tabs => inform controller to reset selection\n\t\t\t\tcontroller.deselectTab();\n\t\t\t}\n\t\t}\n\n\t\ttabsMap.remove(contentPanelToClose.getNode());\n\t\tremove(contentPanelToClose);\n\t\tcontentPanelToClose.dispose();\n\t}\n\n\t@Override\n\tpublic void onTabPositionFirst(TabBlueprint blueprint) {\n\t\tContentPanel contentPanel = getTabByNode(blueprint.getNode());\n\t\tif (contentPanel == null) {\n\t\t\treturn;\n\t\t}\n\t\tsetTabPosition(contentPanel, 0);\n\t}\n\n\tprivate void setTabPosition(ContentPanel contentPanel, int position) {\n\t\tTabComponent tabComponent = getTabComponentByNode(contentPanel.getNode());\n\t\tif (tabComponent == null) {\n\t\t\treturn;\n\t\t}\n\t\tboolean restoreSelection = contentPanel == getSelectedContentPanel();\n\t\tremove(contentPanel);\n\t\tadd(contentPanel, position);\n\t\tsetTabComponentAt(position, tabComponent);\n\t\tif (restoreSelection) {\n\t\t\tsetSelectedIndex(position);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onTabPinChange(TabBlueprint blueprint) {\n\t\tTabComponent tabComponent = getTabComponentByNode(blueprint.getNode());\n\t\tif (tabComponent == null) {\n\t\t\treturn;\n\t\t}\n\t\ttabComponent.update();\n\t}\n\n\t@Override\n\tpublic void onTabBookmarkChange(TabBlueprint blueprint) {\n\t\tTabComponent tabComponent = getTabComponentByNode(blueprint.getNode());\n\t\tif (tabComponent == null) {\n\t\t\treturn;\n\t\t}\n\t\ttabComponent.update();\n\t}\n\n\t@Override\n\tpublic void onTabVisibilityChange(TabBlueprint blueprint) {\n\t\tif (!blueprint.isHidden() && !tabsMap.containsKey(blueprint.getNode())) {\n\t\t\tonTabOpen(blueprint);\n\t\t}\n\t\tif (blueprint.isHidden() && tabsMap.containsKey(blueprint.getNode())) {\n\t\t\tonTabClose(blueprint);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onTabPreviewChange(TabBlueprint blueprint) {\n\t\tTabComponent tabComponent = getTabComponentByNode(blueprint.getNode());\n\t\tif (tabComponent == null) {\n\t\t\treturn;\n\t\t}\n\t\ttabComponent.update();\n\t}\n\n\t@Override\n\tpublic void onTabRestore(TabBlueprint blueprint, EditorViewState viewState) {\n\t\tContentPanel contentPanel = getTabByNode(blueprint.getNode());\n\t\tif (contentPanel instanceof IViewStateSupport) {\n\t\t\t((IViewStateSupport) contentPanel).restoreEditorViewState(viewState);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onTabsReorder(List<TabBlueprint> blueprints) {\n\t\tList<TabBlueprint> newBlueprints = new ArrayList<>(blueprints.size());\n\t\tfor (ContentPanel contentPanel : getTabs()) {\n\t\t\tTabBlueprint blueprint = controller.getTabByNode(contentPanel.getNode());\n\t\t\tif (blueprint != null) {\n\t\t\t\tnewBlueprints.add(blueprint);\n\t\t\t}\n\t\t}\n\t\t// Add back hidden tabs\n\t\tSet<TabBlueprint> set = new LinkedHashSet<>(blueprints);\n\t\tnewBlueprints.forEach(set::remove);\n\t\tnewBlueprints.addAll(set);\n\n\t\tblueprints.clear();\n\t\tblueprints.addAll(newBlueprints);\n\t}\n\n\t@Override\n\tpublic void onTabSave(TabBlueprint blueprint, EditorViewState viewState) {\n\t\tContentPanel contentPanel = getTabByNode(blueprint.getNode());\n\t\tif (contentPanel instanceof IViewStateSupport) {\n\t\t\t((IViewStateSupport) contentPanel).saveEditorViewState(viewState);\n\t\t}\n\t}\n\n\tprivate static class FocusManager implements FocusListener {\n\t\tprivate static final FocusManager INSTANCE = new FocusManager();\n\t\tprivate static @Nullable Component focusedComp;\n\n\t\tstatic boolean isActive() {\n\t\t\treturn focusedComp != null;\n\t\t}\n\n\t\tstatic void reset() {\n\t\t\tfocusedComp = null;\n\t\t}\n\n\t\tstatic Component getFocusedComp() {\n\t\t\treturn focusedComp;\n\t\t}\n\n\t\t@Override\n\t\tpublic void focusGained(FocusEvent e) {\n\t\t\tfocusedComp = (Component) e.getSource();\n\t\t}\n\n\t\t@Override\n\t\tpublic void focusLost(FocusEvent e) {\n\t\t\tfocusedComp = null;\n\t\t}\n\n\t\tstatic void listen(ContentPanel pane) {\n\t\t\tif (pane instanceof ClassCodeContentPanel) {\n\t\t\t\t((ClassCodeContentPanel) pane).getCodeArea().addFocusListener(INSTANCE);\n\t\t\t\t((ClassCodeContentPanel) pane).getSmaliCodeArea().addFocusListener(INSTANCE);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (pane instanceof AbstractCodeContentPanel) {\n\t\t\t\t((AbstractCodeContentPanel) pane).getChildrenComponent().addFocusListener(INSTANCE);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (pane instanceof HtmlPanel) {\n\t\t\t\t((HtmlPanel) pane).getHtmlArea().addFocusListener(INSTANCE);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (pane instanceof ImagePanel) {\n\t\t\t\tpane.addFocusListener(INSTANCE);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (pane instanceof FontPanel) {\n\t\t\t\tpane.addFocusListener(INSTANCE);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// throw new JadxRuntimeException(\"Add the new ContentPanel to TabbedPane.FocusManager: \" + pane);\n\t\t}\n\n\t\tstatic void focusOnCodePanel(ContentPanel pane) {\n\t\t\tif (pane instanceof ClassCodeContentPanel) {\n\t\t\t\tSwingUtilities.invokeLater(() -> ((ClassCodeContentPanel) pane).getCurrentCodeArea().requestFocus());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (pane instanceof AbstractCodeContentPanel) {\n\t\t\t\tSwingUtilities.invokeLater(() -> ((AbstractCodeContentPanel) pane).getChildrenComponent().requestFocus());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (pane instanceof HtmlPanel) {\n\t\t\t\tSwingUtilities.invokeLater(() -> ((HtmlPanel) pane).getHtmlArea().requestFocusInWindow());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (pane instanceof ImagePanel) {\n\t\t\t\tSwingUtilities.invokeLater(((ImagePanel) pane)::requestFocusInWindow);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (pane instanceof FontPanel) {\n\t\t\t\tSwingUtilities.invokeLater(((FontPanel) pane)::requestFocusInWindow);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// throw new JadxRuntimeException(\"Add the new ContentPanel to TabbedPane.FocusManager: \" + pane);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/TabsController.java",
    "content": "package jadx.gui.ui.tab;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JavaClass;\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.metadata.annotations.NodeDeclareRef;\nimport jadx.gui.jobs.SimpleTask;\nimport jadx.gui.jobs.TaskWithExtraOnFinish;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.codearea.EditorViewState;\nimport jadx.gui.utils.JumpPosition;\nimport jadx.gui.utils.UiUtils;\n\npublic class TabsController {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(TabsController.class);\n\n\tprivate final MainWindow mainWindow;\n\tprivate final Map<JNode, TabBlueprint> tabsMap = new HashMap<>();\n\tprivate final List<ITabStatesListener> listeners = new ArrayList<>();\n\n\tprivate boolean forceClose;\n\tprivate @Nullable TabBlueprint selectedTab;\n\n\tpublic TabsController(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t\tif (UiUtils.JADX_GUI_DEBUG) {\n\t\t\taddListener(new LogTabStates());\n\t\t}\n\t}\n\n\tpublic MainWindow getMainWindow() {\n\t\treturn mainWindow;\n\t}\n\n\tpublic void addListener(ITabStatesListener listener) {\n\t\tlisteners.add(listener);\n\t}\n\n\tpublic void removeListener(ITabStatesListener listener) {\n\t\tlisteners.remove(listener);\n\t}\n\n\tpublic @Nullable TabBlueprint getTabByNode(JNode node) {\n\t\treturn tabsMap.get(node);\n\t}\n\n\tpublic TabBlueprint openTab(JNode node) {\n\t\treturn openTab(node, false, false);\n\t}\n\n\tpublic TabBlueprint openTab(JNode node, boolean hidden, boolean preview) {\n\t\tif (!node.hasContent()) {\n\t\t\tLOG.warn(\"Can't open tab for node without content, node: {}\", node);\n\t\t}\n\t\tTabBlueprint blueprint = getTabByNode(node);\n\t\tif (blueprint == null) {\n\t\t\tTabBlueprint newBlueprint = new TabBlueprint(node);\n\t\t\tnewBlueprint.setHidden(hidden);\n\t\t\tnewBlueprint.setPreviewTab(preview);\n\t\t\ttabsMap.put(node, newBlueprint);\n\t\t\tlisteners.forEach(l -> l.onTabOpen(newBlueprint));\n\t\t\tif (hidden) {\n\t\t\t\tlisteners.forEach(l -> l.onTabVisibilityChange(newBlueprint));\n\t\t\t}\n\t\t\tblueprint = newBlueprint;\n\t\t}\n\t\tsetTabHiddenInternal(blueprint, hidden);\n\t\tif (!blueprint.isCreated()) {\n\t\t\tLOG.warn(\"No content panel for node: {}\", node);\n\t\t\tcloseTabForce(blueprint);\n\t\t}\n\t\treturn blueprint;\n\t}\n\n\tpublic TabBlueprint previewTab(JNode node) {\n\t\tTabBlueprint blueprint = getPreviewTab();\n\t\tif (blueprint != null) {\n\t\t\tcloseTab(blueprint.getNode());\n\t\t}\n\t\treturn openTab(node, false, true);\n\t}\n\n\tpublic void selectTab(JNode node) {\n\t\tselectTab(node, false);\n\t}\n\n\tpublic void selectTab(JNode node, boolean fromTree) {\n\t\tif (selectedTab != null && selectedTab.getNode() == node) {\n\t\t\t// already selected\n\t\t\treturn;\n\t\t}\n\t\tif (mainWindow.getSettings().isEnablePreviewTab() && fromTree) {\n\t\t\tselectedTab = previewTab(node);\n\t\t} else {\n\t\t\tselectedTab = openTab(node);\n\t\t}\n\t\tlisteners.forEach(l -> l.onTabSelect(selectedTab));\n\t}\n\n\tpublic void deselectTab() {\n\t\tselectedTab = null;\n\t}\n\n\t/**\n\t * Jump to node definition\n\t */\n\tpublic void codeJump(JNode node) {\n\t\tcodeJump(node, false);\n\t}\n\n\t/**\n\t * Jump to node definition\n\t */\n\tpublic void codeJump(JNode node, boolean fromTree) {\n\t\tJClass parentCls = node.getJParent();\n\t\tif (parentCls != null) {\n\t\t\t// handle jump to inner class, method or field:\n\t\t\t// - load parent\n\t\t\t// - search position and jump to it\n\t\t\tJavaClass cls = node.getJParent().getCls();\n\t\t\tJavaClass origTopCls = cls.getOriginalTopParentClass();\n\t\t\tJavaClass codeParent = cls.getTopParentClass();\n\t\t\tif (!Objects.equals(codeParent, origTopCls)) {\n\t\t\t\tJClass jumpCls = mainWindow.getCacheObject().getNodeCache().makeFrom(codeParent);\n\t\t\t\tloadCodeWithUIAction(jumpCls, () -> jumpToInnerClass(node, codeParent, jumpCls, fromTree));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tJClass clsRootClass = node.getRootClass();\n\t\tif (clsRootClass == null) {\n\t\t\t// not a class, select tab (without position scroll)\n\t\t\tselectTab(node, fromTree);\n\t\t\treturn;\n\t\t}\n\t\tloadCodeWithUIAction(clsRootClass, () -> codeJump(new JumpPosition(node), fromTree));\n\t}\n\n\tprivate void loadCodeWithUIAction(JClass cls, Runnable action) {\n\t\tSimpleTask loadTask = cls.getLoadTask();\n\t\tif (loadTask == null) {\n\t\t\t// already loaded\n\t\t\tUiUtils.uiRun(action);\n\t\t\treturn;\n\t\t}\n\t\tmainWindow.getBackgroundExecutor().execute(new TaskWithExtraOnFinish(loadTask, action));\n\t}\n\n\t/**\n\t * Search and jump to original node in jumpCls\n\t */\n\tprivate void jumpToInnerClass(JNode node, JavaClass codeParent, JClass jumpCls, boolean fromTree) {\n\t\tcodeParent.getCodeInfo().getCodeMetadata().searchDown(0, (pos, ann) -> {\n\t\t\tif (ann.getAnnType() == ICodeAnnotation.AnnType.DECLARATION) {\n\t\t\t\tICodeNodeRef declNode = ((NodeDeclareRef) ann).getNode();\n\t\t\t\tif (declNode.equals(node.getJavaNode().getCodeNodeRef())) {\n\t\t\t\t\tcodeJump(new JumpPosition(jumpCls, pos), fromTree);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t}\n\n\tpublic void codeJump(JumpPosition pos) {\n\t\tcodeJump(pos, false);\n\t}\n\n\t/**\n\t * Prefer {@link TabsController#codeJump(JNode)} method\n\t */\n\tpublic void codeJump(JumpPosition pos, boolean fromTree) {\n\t\tJumpPosition currentPosition = mainWindow.getTabbedPane().getCurrentPosition();\n\t\tif (selectedTab == null || selectedTab.getNode() != pos.getNode()) {\n\t\t\tselectTab(pos.getNode(), fromTree);\n\t\t}\n\t\tlisteners.forEach(l -> l.onTabCodeJump(selectedTab, currentPosition, pos));\n\t}\n\n\tpublic void smaliJump(JClass cls, int pos, boolean debugMode) {\n\t\tselectTab(cls);\n\t\tTabBlueprint blueprint = getTabByNode(cls);\n\t\tlisteners.forEach(l -> l.onTabSmaliJump(blueprint, pos, debugMode));\n\t}\n\n\tpublic void closeTab(JNode node) {\n\t\tcloseTab(node, false);\n\t}\n\n\tpublic void closeTab(JNode node, boolean considerPins) {\n\t\tTabBlueprint blueprint = getTabByNode(node);\n\t\tif (blueprint != null) {\n\t\t\tcloseTab(blueprint, considerPins);\n\t\t}\n\t}\n\n\tpublic void closeTab(TabBlueprint blueprint, boolean considerPins) {\n\t\tif (forceClose) {\n\t\t\tcloseTabForce(blueprint);\n\t\t\treturn;\n\t\t}\n\t\tif (!considerPins || !blueprint.isPinned()) {\n\t\t\tif (!blueprint.isReferenced()) {\n\t\t\t\tcloseTabForce(blueprint);\n\t\t\t} else {\n\t\t\t\tcloseTabSoft(blueprint);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Removes Tab from everywhere\n\t */\n\tprivate void closeTabForce(TabBlueprint blueprint) {\n\t\tlisteners.forEach(l -> l.onTabClose(blueprint));\n\t\ttabsMap.remove(blueprint.getNode());\n\t}\n\n\t/**\n\t * Hides Tab from TabbedPane\n\t */\n\tprivate void closeTabSoft(TabBlueprint blueprint) {\n\t\tsetTabHidden(blueprint.getNode(), true);\n\t}\n\n\tpublic void setTabPositionFirst(JNode node) {\n\t\tTabBlueprint blueprint = openTab(node);\n\t\tlisteners.forEach(l -> l.onTabPositionFirst(blueprint));\n\t}\n\n\tpublic void setTabPinned(JNode node, boolean pinned) {\n\t\tTabBlueprint blueprint = openTab(node);\n\t\tsetTabPinnedInternal(blueprint, pinned);\n\t}\n\n\tpublic void setTabPinnedInternal(TabBlueprint blueprint, boolean pinned) {\n\t\tif (blueprint.isPinned() != pinned) {\n\t\t\tblueprint.setPreviewTab(false);\n\t\t\tblueprint.setPinned(pinned);\n\t\t\tlisteners.forEach(l -> l.onTabPinChange(blueprint));\n\t\t}\n\t}\n\n\tpublic void setTabBookmarked(JNode node, boolean bookmarked) {\n\t\tTabBlueprint blueprint = openTab(node);\n\t\tsetTabBookmarkedInternal(blueprint, bookmarked);\n\t}\n\n\tprivate void setTabBookmarkedInternal(TabBlueprint blueprint, boolean bookmarked) {\n\t\tif (blueprint.isBookmarked() != bookmarked) {\n\t\t\tblueprint.setPreviewTab(false);\n\t\t\tblueprint.setBookmarked(bookmarked);\n\t\t\tlisteners.forEach(l -> l.onTabBookmarkChange(blueprint));\n\t\t\tremoveTabIfNotReferenced(blueprint);\n\t\t}\n\t}\n\n\tpublic void setTabHidden(JNode node, boolean hidden) {\n\t\tTabBlueprint blueprint = getTabByNode(node);\n\t\tsetTabHiddenInternal(blueprint, hidden);\n\t}\n\n\tprivate void setTabHiddenInternal(TabBlueprint blueprint, boolean hidden) {\n\t\tif (blueprint != null && blueprint.isHidden() != hidden) {\n\t\t\tblueprint.setPreviewTab(false);\n\t\t\tblueprint.setHidden(hidden);\n\t\t\tlisteners.forEach(l -> l.onTabVisibilityChange(blueprint));\n\t\t}\n\t}\n\n\tpublic void setTabPreview(JNode node, boolean isPreview) {\n\t\tTabBlueprint blueprint = getTabByNode(node);\n\t\tsetTabPreviewInternal(blueprint, isPreview);\n\t}\n\n\tprivate void setTabPreviewInternal(TabBlueprint blueprint, boolean isPreview) {\n\t\tif (blueprint != null && blueprint.isPreviewTab() != isPreview) {\n\t\t\tblueprint.setPreviewTab(isPreview);\n\t\t\tlisteners.forEach(l -> l.onTabPreviewChange(blueprint));\n\t\t}\n\t}\n\n\tprivate void removeTabIfNotReferenced(TabBlueprint blueprint) {\n\t\tif (blueprint.isHidden() && !blueprint.isReferenced()) {\n\t\t\ttabsMap.remove(blueprint.getNode());\n\t\t}\n\t}\n\n\tpublic void closeAllTabs() {\n\t\tcloseAllTabs(false);\n\t}\n\n\tpublic void forceCloseAllTabs() {\n\t\tforceClose = true;\n\t\tcloseAllTabs();\n\t\tforceClose = false;\n\t\tselectedTab = null;\n\t}\n\n\tpublic boolean isForceClose() {\n\t\treturn forceClose;\n\t}\n\n\tpublic void closeAllTabs(boolean considerPins) {\n\t\tList.copyOf(tabsMap.values()).forEach(t -> closeTab(t.getNode(), considerPins));\n\t}\n\n\tpublic void unpinAllTabs() {\n\t\ttabsMap.values().forEach(t -> setTabPinned(t.getNode(), false));\n\t}\n\n\tpublic void unbookmarkAllTabs() {\n\t\ttabsMap.values().forEach(t -> setTabBookmarked(t.getNode(), false));\n\t}\n\n\tpublic TabBlueprint getSelectedTab() {\n\t\treturn selectedTab;\n\t}\n\n\tpublic List<TabBlueprint> getTabs() {\n\t\treturn List.copyOf(tabsMap.values());\n\t}\n\n\tpublic List<TabBlueprint> getOpenTabs() {\n\t\treturn List.copyOf(tabsMap.values());\n\t}\n\n\tpublic List<TabBlueprint> getPinnedTabs() {\n\t\treturn tabsMap.values().stream()\n\t\t\t\t.filter(TabBlueprint::isPinned)\n\t\t\t\t.collect(Collectors.toUnmodifiableList());\n\t}\n\n\tpublic List<TabBlueprint> getBookmarkedTabs() {\n\t\treturn tabsMap.values().stream()\n\t\t\t\t.filter(TabBlueprint::isBookmarked)\n\t\t\t\t.collect(Collectors.toUnmodifiableList());\n\t}\n\n\tpublic TabBlueprint getPreviewTab() {\n\t\treturn tabsMap.values().stream()\n\t\t\t\t.filter(TabBlueprint::isPreviewTab).findFirst().orElse(null);\n\t}\n\n\tpublic void restoreEditorViewState(EditorViewState viewState) {\n\t\tJNode node = viewState.getNode();\n\t\tTabBlueprint blueprint = openTab(node, viewState.isHidden(), viewState.isPreviewTab());\n\t\tsetTabPinnedInternal(blueprint, viewState.isPinned());\n\t\tsetTabBookmarkedInternal(blueprint, viewState.isBookmarked());\n\t\tlisteners.forEach(l -> l.onTabRestore(blueprint, viewState));\n\t\tif (viewState.isActive()) {\n\t\t\tselectTab(node);\n\t\t}\n\t}\n\n\tpublic void notifyRestoreEditorViewStateDone() {\n\t\tif (selectedTab == null && !tabsMap.isEmpty()) {\n\t\t\tJNode node = tabsMap.values().iterator().next().getNode();\n\t\t\tLOG.warn(\"No active tab found, select {}\", node); // TODO: find the reason of this issue\n\t\t\tselectTab(node);\n\t\t}\n\t\tlisteners.forEach(ITabStatesListener::onTabsRestoreDone);\n\t}\n\n\tpublic List<EditorViewState> getEditorViewStates() {\n\t\tList<TabBlueprint> reorderedTabs = new ArrayList<>(tabsMap.values());\n\t\tlisteners.forEach(l -> l.onTabsReorder(reorderedTabs));\n\t\tList<EditorViewState> states = new ArrayList<>();\n\t\tfor (TabBlueprint blueprint : reorderedTabs) {\n\t\t\tstates.add(getEditorViewState(blueprint));\n\t\t}\n\t\treturn states;\n\t}\n\n\tpublic EditorViewState getEditorViewState(TabBlueprint blueprint) {\n\t\tEditorViewState viewState = new EditorViewState(blueprint.getNode());\n\t\tlisteners.forEach(l -> l.onTabSave(blueprint, viewState));\n\t\tviewState.setActive(blueprint == selectedTab);\n\t\tviewState.setPinned(blueprint.isPinned());\n\t\tviewState.setBookmarked(blueprint.isBookmarked());\n\t\tviewState.setHidden(blueprint.isHidden());\n\t\tviewState.setPreviewTab(blueprint.isPreviewTab());\n\t\treturn viewState;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/dnd/TabDndController.java",
    "content": "/*\n * The MIT License (MIT)\n * Copyright (c) 2015 TERAI Atsuhiro\n * Copyright (c) 2024 Skylot\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 * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\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\npackage jadx.gui.ui.tab.dnd;\n\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.Graphics2D;\nimport java.awt.GraphicsConfiguration;\nimport java.awt.GraphicsDevice;\nimport java.awt.GraphicsEnvironment;\nimport java.awt.Point;\nimport java.awt.Rectangle;\nimport java.awt.dnd.DnDConstants;\nimport java.awt.dnd.DragSource;\nimport java.awt.dnd.DropTarget;\nimport java.awt.image.BufferedImage;\nimport java.util.Objects;\n\nimport javax.swing.Icon;\nimport javax.swing.JButton;\nimport javax.swing.JTabbedPane;\nimport javax.swing.SwingUtilities;\nimport javax.swing.plaf.metal.MetalTabbedPaneUI;\n\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.ui.tab.TabbedPane;\n\npublic class TabDndController {\n\n\tprivate final transient JTabbedPane pane;\n\n\tprivate static final int DROP_TARGET_MARK_SIZE = 4;\n\tprivate static final int SCROLL_AREA_SIZE = 30;\n\tprivate static final int SCROLL_AREA_EXTRA = 30; // Making area with scroll buttons a bit bigger.\n\tprivate static final String ACTION_SCROLL_FORWARD = \"scrollTabsForwardAction\";\n\tprivate static final String ACTION_SCROLL_BACKWARD = \"scrollTabsBackwardAction\";\n\n\tprivate final transient TabDndGhostPane tabDndGhostPane;\n\tprotected int dragTabIndex = -1;\n\n\tprotected boolean drawGhost = true; // Semi-transparent tab copy moving along with cursor.\n\tprotected boolean paintScrollTriggerAreas = false; // For debug purposes.\n\n\tprotected Rectangle rectBackward = new Rectangle();\n\tprotected Rectangle rectForward = new Rectangle();\n\tprivate boolean isDragging = false;\n\n\tpublic TabDndController(TabbedPane pane, JadxSettings settings) {\n\t\tpane.setDnd(this);\n\t\tthis.pane = pane;\n\n\t\ttabDndGhostPane = new TabDndGhostPane(this, settings);\n\n\t\tnew DropTarget(tabDndGhostPane, DnDConstants.ACTION_COPY_OR_MOVE, new TabDndTargetListener(this), true);\n\t\tDragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(pane,\n\t\t\t\tDnDConstants.ACTION_COPY_OR_MOVE,\n\t\t\t\tnew TabDndGestureListener(this));\n\t}\n\n\tpublic static boolean isHorizontalTabPlacement(int tabPlacement) {\n\t\treturn tabPlacement == JTabbedPane.TOP || tabPlacement == JTabbedPane.BOTTOM;\n\t}\n\n\t/**\n\t * Check if dragging near edges and scroll to according\n\t * direction through programmatically clicking system's scroll buttons.\n\t *\n\t * @param glassPt Cursor position in TabbedPane coordinates.\n\t */\n\tpublic void scrollIfNeeded(Point glassPt) {\n\t\tRectangle r = getTabAreaBounds();\n\t\tboolean isHorizontal = isHorizontalTabPlacement(pane.getTabPlacement());\n\n\t\t// Trying to avoid calculating two directions simultaneously. Forward first.\n\t\tif (isHorizontal) {\n\t\t\trectForward.setBounds(r.x + r.width - SCROLL_AREA_SIZE - SCROLL_AREA_EXTRA,\n\t\t\t\t\tr.y,\n\t\t\t\t\tSCROLL_AREA_SIZE + SCROLL_AREA_EXTRA,\n\t\t\t\t\tr.height);\n\t\t} else {\n\t\t\trectForward.setBounds(r.x,\n\t\t\t\t\tr.y + r.height - SCROLL_AREA_SIZE - SCROLL_AREA_EXTRA,\n\t\t\t\t\tr.width,\n\t\t\t\t\tSCROLL_AREA_SIZE + SCROLL_AREA_EXTRA);\n\t\t}\n\t\trectForward = SwingUtilities.convertRectangle(pane.getParent(), rectForward, tabDndGhostPane);\n\t\tif (rectForward.contains(glassPt)) {\n\t\t\tclickScrollButton(ACTION_SCROLL_FORWARD);\n\t\t}\n\n\t\t// Backward.\n\t\tif (isHorizontal) {\n\t\t\trectBackward.setBounds(r.x, r.y, SCROLL_AREA_SIZE, r.height);\n\t\t} else {\n\t\t\trectBackward.setBounds(r.x, r.y, r.width, SCROLL_AREA_SIZE);\n\t\t}\n\t\trectBackward = SwingUtilities.convertRectangle(pane.getParent(), rectBackward, tabDndGhostPane);\n\t\tif (rectBackward.contains(glassPt)) {\n\t\t\tclickScrollButton(ACTION_SCROLL_BACKWARD);\n\t\t}\n\t}\n\n\tprivate void clickScrollButton(String actionKey) {\n\t\tJButton forwardButton = null;\n\t\tJButton backwardButton = null;\n\t\tfor (Component c : pane.getComponents()) {\n\t\t\tif (c instanceof JButton) {\n\t\t\t\tif (Objects.isNull(forwardButton)) {\n\t\t\t\t\tforwardButton = (JButton) c;\n\t\t\t\t} else {\n\t\t\t\t\tbackwardButton = (JButton) c;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tJButton scrollButton = ACTION_SCROLL_FORWARD.equals(actionKey) ? forwardButton : backwardButton;\n\t\tif (scrollButton != null && scrollButton.isEnabled()) {\n\t\t\tscrollButton.doClick();\n\t\t}\n\t}\n\n\t/**\n\t * Finds the tab index by cursor position. If tabs first half contains the point,\n\t * then its index is returned. Second half means inserting at next index.\n\t *\n\t * @param glassPt Cursor position in TabbedPane coordinates.\n\t * @return Tab index.\n\t */\n\tprotected int getTargetTabIndex(Point glassPt) {\n\t\tPoint tabPt = SwingUtilities.convertPoint(tabDndGhostPane, glassPt, pane);\n\t\tboolean isHorizontal = isHorizontalTabPlacement(pane.getTabPlacement());\n\t\tfor (int i = 0; i < pane.getTabCount(); ++i) {\n\t\t\tRectangle r = pane.getBoundsAt(i);\n\n\t\t\t// First half.\n\t\t\tif (isHorizontal) {\n\t\t\t\tr.width = r.width / 2 + 1;\n\t\t\t} else {\n\t\t\t\tr.height = r.height / 2 + 1;\n\t\t\t}\n\t\t\tif (r.contains(tabPt)) {\n\t\t\t\treturn i;\n\t\t\t}\n\n\t\t\t// Second half.\n\t\t\tif (isHorizontal) {\n\t\t\t\tr.x = r.x + r.width;\n\t\t\t} else {\n\t\t\t\tr.y = r.y + r.height;\n\t\t\t}\n\t\t\tif (r.contains(tabPt)) {\n\t\t\t\treturn i + 1;\n\t\t\t}\n\t\t}\n\n\t\tint count = pane.getTabCount();\n\t\tif (count == 0) {\n\t\t\treturn -1;\n\t\t}\n\t\tRectangle lastRect = pane.getBoundsAt(count - 1);\n\t\tPoint d = isHorizontal ? new Point(1, 0) : new Point(0, 1);\n\t\tlastRect.translate(lastRect.width * d.x, lastRect.height * d.y);\n\t\treturn lastRect.contains(tabPt) ? count : -1;\n\t}\n\n\tprotected void swapTabs(int oldIdx, int newIdx) {\n\t\tif (newIdx < 0 || oldIdx == newIdx) {\n\t\t\treturn;\n\t\t}\n\t\tfinal Component cmp = pane.getComponentAt(oldIdx);\n\t\tfinal Component tab = pane.getTabComponentAt(oldIdx);\n\t\tfinal String title = pane.getTitleAt(oldIdx);\n\t\tfinal Icon icon = pane.getIconAt(oldIdx);\n\t\tfinal String tip = pane.getToolTipTextAt(oldIdx);\n\t\tfinal boolean isEnabled = pane.isEnabledAt(oldIdx);\n\t\tnewIdx = oldIdx > newIdx ? newIdx : (newIdx - 1);\n\t\tpane.remove(oldIdx);\n\t\tpane.insertTab(title, icon, cmp, tip, newIdx);\n\t\tpane.setEnabledAt(newIdx, isEnabled);\n\t\tif (isEnabled) {\n\t\t\tpane.setSelectedIndex(newIdx);\n\t\t}\n\t\tpane.setTabComponentAt(newIdx, tab);\n\t}\n\n\tprotected void updateTargetMark(int tabIdx) {\n\t\tboolean isSideNeighbor = tabIdx < 0 || dragTabIndex == tabIdx || tabIdx == dragTabIndex + 1;\n\t\tif (isSideNeighbor) {\n\t\t\ttabDndGhostPane.setTargetRect(0, 0, 0, 0);\n\t\t\treturn;\n\t\t}\n\t\tRectangle boundsRect = pane.getBoundsAt(Math.max(0, tabIdx - 1));\n\t\tfinal Rectangle r = SwingUtilities.convertRectangle(pane, boundsRect, tabDndGhostPane);\n\t\tint a = Math.min(tabIdx, 1);\n\t\tif (isHorizontalTabPlacement(pane.getTabPlacement())) {\n\t\t\ttabDndGhostPane.setTargetRect(r.x + r.width * a - DROP_TARGET_MARK_SIZE / 2,\n\t\t\t\t\tr.y,\n\t\t\t\t\tDROP_TARGET_MARK_SIZE,\n\t\t\t\t\tr.height);\n\t\t} else {\n\t\t\ttabDndGhostPane.setTargetRect(r.x,\n\t\t\t\t\tr.y + r.height * a - DROP_TARGET_MARK_SIZE / 2,\n\t\t\t\t\tr.width,\n\t\t\t\t\tDROP_TARGET_MARK_SIZE);\n\t\t}\n\t}\n\n\tprotected void initGlassPane(Point tabPt) {\n\t\tpane.getRootPane().setGlassPane(tabDndGhostPane);\n\t\tif (drawGhost) {\n\t\t\tComponent c = pane.getTabComponentAt(dragTabIndex);\n\t\t\tif (c == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tDimension d = c.getPreferredSize();\n\t\t\tswitch (tabDndGhostPane.getGhostType()) {\n\t\t\t\tcase IMAGE: {\n\t\t\t\t\tGraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();\n\t\t\t\t\tGraphicsDevice device = env.getDefaultScreenDevice();\n\t\t\t\t\tGraphicsConfiguration config = device.getDefaultConfiguration();\n\t\t\t\t\tBufferedImage image = config.createCompatibleImage(d.width, d.height, BufferedImage.TRANSLUCENT);\n\t\t\t\t\tGraphics2D g2 = image.createGraphics();\n\t\t\t\t\tSwingUtilities.paintComponent(g2, c, tabDndGhostPane, 0, 0, d.width, d.height);\n\t\t\t\t\tg2.dispose();\n\t\t\t\t\ttabDndGhostPane.setGhostImage(image);\n\t\t\t\t\tpane.setTabComponentAt(dragTabIndex, c);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase OUTLINE: {\n\t\t\t\t\ttabDndGhostPane.setGhostSize(d);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase TARGET_MARK:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tPoint glassPt = SwingUtilities.convertPoint(pane, tabPt, tabDndGhostPane);\n\t\ttabDndGhostPane.setPoint(glassPt);\n\t\ttabDndGhostPane.setVisible(true);\n\t}\n\n\tprotected Rectangle getTabAreaBounds() {\n\t\tRectangle tabbedRect = pane.getBounds();\n\t\tRectangle compRect;\n\t\tif (pane.getSelectedComponent() != null) {\n\t\t\tcompRect = pane.getSelectedComponent().getBounds();\n\t\t} else {\n\t\t\tcompRect = new Rectangle();\n\t\t}\n\t\tint tabPlacement = pane.getTabPlacement();\n\t\tif (isHorizontalTabPlacement(tabPlacement)) {\n\t\t\ttabbedRect.height = tabbedRect.height - compRect.height;\n\t\t\tif (tabPlacement == JTabbedPane.BOTTOM) {\n\t\t\t\ttabbedRect.y += compRect.y + compRect.height;\n\t\t\t}\n\t\t} else {\n\t\t\ttabbedRect.width = tabbedRect.width - compRect.width;\n\t\t\tif (tabPlacement == JTabbedPane.RIGHT) {\n\t\t\t\ttabbedRect.x += compRect.x + compRect.width;\n\t\t\t}\n\t\t}\n\t\ttabbedRect.grow(2, 2);\n\t\treturn tabbedRect;\n\t}\n\n\tpublic void onPaintGlassPane(Graphics2D g) {\n\t\tboolean isScrollLayout = pane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT;\n\t\tif (isScrollLayout && paintScrollTriggerAreas) {\n\t\t\tg.setPaint(tabDndGhostPane.getColor());\n\t\t\tg.fill(rectBackward);\n\t\t\tg.fill(rectForward);\n\t\t}\n\t}\n\n\tpublic boolean onStartDrag(Point pt) {\n\t\tsetDragging(true);\n\t\tint idx = pane.indexAtLocation(pt.x, pt.y);\n\t\tint selIdx = pane.getSelectedIndex();\n\t\tboolean isTabRunsRotated =\n\t\t\t\t!(pane.getUI() instanceof MetalTabbedPaneUI) && pane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT && idx != selIdx;\n\t\tdragTabIndex = isTabRunsRotated ? selIdx : idx;\n\t\tif (dragTabIndex >= 0 && pane.isEnabledAt(dragTabIndex)) {\n\t\t\tinitGlassPane(pt);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic void loadSettings() {\n\t\ttabDndGhostPane.loadSettings();\n\t}\n\n\tpublic boolean isDragging() {\n\t\treturn isDragging;\n\t}\n\n\tpublic void setDragging(boolean dragging) {\n\t\tisDragging = dragging;\n\t}\n\n\tpublic TabDndGhostPane getDndGhostPane() {\n\t\treturn tabDndGhostPane;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/dnd/TabDndGestureListener.java",
    "content": "/*\n * The MIT License (MIT)\n * Copyright (c) 2015 TERAI Atsuhiro\n * Copyright (c) 2024 Skylot\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 * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\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\npackage jadx.gui.ui.tab.dnd;\n\nimport java.awt.Point;\nimport java.awt.dnd.DragGestureEvent;\nimport java.awt.dnd.DragGestureListener;\nimport java.awt.dnd.DragSource;\nimport java.awt.dnd.InvalidDnDOperationException;\n\npublic class TabDndGestureListener implements DragGestureListener {\n\n\tprivate final transient TabDndController dnd;\n\n\tpublic TabDndGestureListener(TabDndController dnd) {\n\t\tthis.dnd = dnd;\n\t}\n\n\t@Override\n\tpublic void dragGestureRecognized(DragGestureEvent e) {\n\t\tPoint tabPt = getDragOrigin(e);\n\t\tif (!dnd.onStartDrag(tabPt)) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\te.startDrag(DragSource.DefaultMoveDrop, new TabDndTransferable(), new TabDndSourceListener(dnd));\n\t\t} catch (InvalidDnDOperationException ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t}\n\n\tprotected Point getDragOrigin(DragGestureEvent e) {\n\t\treturn e.getDragOrigin();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/dnd/TabDndGhostPane.java",
    "content": "/*\n * The MIT License (MIT)\n * Copyright (c) 2015 TERAI Atsuhiro\n * Copyright (c) 2024 Skylot\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 * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\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\npackage jadx.gui.ui.tab.dnd;\n\nimport java.awt.AlphaComposite;\nimport java.awt.Color;\nimport java.awt.Dimension;\nimport java.awt.Graphics;\nimport java.awt.Graphics2D;\nimport java.awt.Insets;\nimport java.awt.Point;\nimport java.awt.Rectangle;\nimport java.awt.image.BufferedImage;\n\nimport javax.swing.JComponent;\nimport javax.swing.UIManager;\n\nimport jadx.gui.settings.JadxSettings;\n\npublic class TabDndGhostPane extends JComponent {\n\n\tprivate final TabDndController dnd;\n\tprivate final Rectangle lineRect = new Rectangle();\n\tprivate final Point location = new Point();\n\tprivate transient BufferedImage ghostImage;\n\tprivate JadxSettings settings;\n\tprivate TabDndGhostType tabDndGhostType = TabDndGhostType.OUTLINE;\n\tprivate Dimension ghostSize;\n\tprivate Color ghostColor;\n\tprivate Insets insets;\n\n\tprotected TabDndGhostPane(TabDndController dnd, JadxSettings settings) {\n\t\tsuper();\n\t\tthis.dnd = dnd;\n\t\tthis.settings = settings;\n\t\tloadSettings();\n\t}\n\n\tpublic void loadSettings() {\n\t\tColor systemColor = UIManager.getColor(\"Component.focusColor\");\n\t\tColor fallbackColor = new Color(0, 100, 255);\n\t\tghostColor = systemColor != null ? systemColor : fallbackColor;\n\n\t\tInsets ins = UIManager.getInsets(\"TabbedPane.tabInsets\");\n\t\tinsets = ins != null ? ins : new Insets(0, 0, 0, 0);\n\n\t\ttabDndGhostType = settings.getTabDndGhostType();\n\t}\n\n\tpublic void setTargetRect(int x, int y, int width, int height) {\n\t\tlineRect.setBounds(x, y, width, height);\n\t}\n\n\tpublic void setGhostImage(BufferedImage ghostImage) {\n\t\tthis.ghostImage = ghostImage;\n\t}\n\n\tpublic void setGhostSize(Dimension ghostSize) {\n\t\tghostSize.setSize(ghostSize.width + insets.left + insets.right, ghostSize.height + insets.top + insets.bottom);\n\t\tthis.ghostSize = ghostSize;\n\t}\n\n\tpublic void setGhostType(TabDndGhostType tabDndGhostType) {\n\t\tthis.tabDndGhostType = tabDndGhostType;\n\t}\n\n\tpublic TabDndGhostType getGhostType() {\n\t\treturn this.tabDndGhostType;\n\t}\n\n\tpublic void setColor(Color color) {\n\t\tthis.ghostColor = color;\n\t}\n\n\tpublic Color getColor() {\n\t\treturn this.ghostColor;\n\t}\n\n\tpublic void setPoint(Point pt) {\n\t\tthis.location.setLocation(pt);\n\t}\n\n\t@Override\n\tpublic boolean isOpaque() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void setVisible(boolean v) {\n\t\tsuper.setVisible(v);\n\t\tif (!v) {\n\t\t\tsetTargetRect(0, 0, 0, 0);\n\t\t\tsetGhostImage(null);\n\t\t\tsetGhostSize(new Dimension());\n\t\t}\n\t}\n\n\t@Override\n\tprotected void paintComponent(Graphics g) {\n\t\tGraphics2D g2 = (Graphics2D) g.create();\n\t\tdnd.onPaintGlassPane(g2);\n\t\trenderMark(g2);\n\t\trenderGhost(g2);\n\t\tg2.dispose();\n\t}\n\n\tprivate void renderGhost(Graphics2D g) {\n\t\tswitch (tabDndGhostType) {\n\t\t\tcase IMAGE: {\n\t\t\t\tg.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f));\n\t\t\t\tif (ghostImage == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tdouble x = location.getX() - ghostImage.getWidth(this) / 2d;\n\t\t\t\tdouble y = location.getY() - ghostImage.getHeight(this) / 2d;\n\t\t\t\tg.drawImage(ghostImage, (int) x, (int) y, this);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase OUTLINE: {\n\t\t\t\tg.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .2f));\n\t\t\t\tif (ghostSize == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tdouble x = location.getX() - ghostSize.getWidth() / 2d;\n\t\t\t\tdouble y = location.getY() - ghostSize.getHeight() / 2d;\n\t\t\t\tg.setPaint(ghostColor);\n\t\t\t\tg.fillRect((int) x, (int) y, ghostSize.width, ghostSize.height);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase TARGET_MARK:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate void renderMark(Graphics2D g) {\n\t\tg.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .7f));\n\t\tg.setPaint(ghostColor);\n\t\tg.fill(lineRect);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/dnd/TabDndGhostType.java",
    "content": "package jadx.gui.ui.tab.dnd;\n\npublic enum TabDndGhostType {\n\t/**\n\t * Bitmap is rendered from tabs component and dragged along with cursor.\n\t * May be impactful on performance.\n\t */\n\tIMAGE,\n\n\t/**\n\t * Colored rect of tabs size is dragged along with cursor.\n\t */\n\tOUTLINE,\n\n\t/**\n\t * Only insert mark is rendered.\n\t */\n\tTARGET_MARK,\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/dnd/TabDndSourceListener.java",
    "content": "/*\n * The MIT License (MIT)\n * Copyright (c) 2015 TERAI Atsuhiro\n * Copyright (c) 2024 Skylot\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 * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\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\npackage jadx.gui.ui.tab.dnd;\n\nimport java.awt.Component;\nimport java.awt.dnd.DragSource;\nimport java.awt.dnd.DragSourceDragEvent;\nimport java.awt.dnd.DragSourceDropEvent;\nimport java.awt.dnd.DragSourceEvent;\nimport java.awt.dnd.DragSourceListener;\n\nimport javax.swing.JComponent;\nimport javax.swing.JRootPane;\n\nclass TabDndSourceListener implements DragSourceListener {\n\n\tprivate final transient TabDndController dnd;\n\n\tpublic TabDndSourceListener(TabDndController dnd) {\n\t\tthis.dnd = dnd;\n\t}\n\n\t@Override\n\tpublic void dragEnter(DragSourceDragEvent e) {\n\t\te.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);\n\t}\n\n\t@Override\n\tpublic void dragExit(DragSourceEvent e) {\n\t\te.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);\n\t}\n\n\t@Override\n\tpublic void dragOver(DragSourceDragEvent e) {\n\t}\n\n\t@Override\n\tpublic void dragDropEnd(DragSourceDropEvent e) {\n\t\tdnd.setDragging(false);\n\t\tComponent c = e.getDragSourceContext().getComponent();\n\t\tif (c instanceof JComponent) {\n\t\t\tJRootPane rp = ((JComponent) c).getRootPane();\n\t\t\tif (rp.getGlassPane() != null) {\n\t\t\t\trp.getGlassPane().setVisible(false);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void dropActionChanged(DragSourceDragEvent e) {\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/dnd/TabDndTargetListener.java",
    "content": "/*\n * The MIT License (MIT)\n * Copyright (c) 2015 TERAI Atsuhiro\n * Copyright (c) 2024 Skylot\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 * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\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\npackage jadx.gui.ui.tab.dnd;\n\nimport java.awt.Point;\nimport java.awt.datatransfer.DataFlavor;\nimport java.awt.datatransfer.Transferable;\nimport java.awt.dnd.DropTargetDragEvent;\nimport java.awt.dnd.DropTargetDropEvent;\nimport java.awt.dnd.DropTargetEvent;\nimport java.awt.dnd.DropTargetListener;\n\nclass TabDndTargetListener implements DropTargetListener {\n\tprivate static final Point HIDDEN_POINT = new Point(0, -1000);\n\n\tprivate final transient TabDndController dnd;\n\n\tpublic TabDndTargetListener(TabDndController dnd) {\n\t\tthis.dnd = dnd;\n\t}\n\n\t@Override\n\tpublic void dragEnter(DropTargetDragEvent e) {\n\t\tTabDndGhostPane pane = dnd.getDndGhostPane();\n\t\tif (pane == null || e.getDropTargetContext().getComponent() != pane) {\n\t\t\treturn;\n\t\t}\n\t\tTransferable t = e.getTransferable();\n\t\tDataFlavor[] f = e.getCurrentDataFlavors();\n\t\tif (t.isDataFlavorSupported(f[0])) {\n\t\t\te.acceptDrag(e.getDropAction());\n\t\t} else {\n\t\t\te.rejectDrag();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void dragExit(DropTargetEvent e) {\n\t\tTabDndGhostPane pane = dnd.getDndGhostPane();\n\t\tif (pane == null || e.getDropTargetContext().getComponent() != pane) {\n\t\t\treturn;\n\t\t}\n\t\tpane.setPoint(HIDDEN_POINT);\n\t\tpane.setTargetRect(0, 0, 0, 0);\n\t\tpane.repaint();\n\t}\n\n\t@Override\n\tpublic void dropActionChanged(DropTargetDragEvent e) {\n\t}\n\n\t@Override\n\tpublic void dragOver(DropTargetDragEvent e) {\n\t\tTabDndGhostPane pane = dnd.getDndGhostPane();\n\t\tif (pane == null || e.getDropTargetContext().getComponent() != pane) {\n\t\t\treturn;\n\t\t}\n\t\tPoint glassPt = e.getLocation();\n\t\tdnd.updateTargetMark(dnd.getTargetTabIndex(glassPt));\n\t\tdnd.scrollIfNeeded(glassPt); // backward and forward scrolling\n\t\tpane.setPoint(glassPt);\n\t\tpane.repaint();\n\t}\n\n\t@Override\n\tpublic void drop(DropTargetDropEvent e) {\n\t\tTabDndGhostPane pane = dnd.getDndGhostPane();\n\t\tif (pane == null || e.getDropTargetContext().getComponent() != pane) {\n\t\t\treturn;\n\t\t}\n\t\tTransferable t = e.getTransferable();\n\t\tDataFlavor[] f = t.getTransferDataFlavors();\n\t\tint oldIdx = dnd.dragTabIndex;\n\t\tint newIdx = dnd.getTargetTabIndex(e.getLocation());\n\t\tif (t.isDataFlavorSupported(f[0]) && oldIdx != newIdx) {\n\t\t\tdnd.swapTabs(oldIdx, newIdx);\n\t\t\te.dropComplete(true);\n\t\t} else {\n\t\t\te.dropComplete(false);\n\t\t}\n\t\tpane.setVisible(false);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/tab/dnd/TabDndTransferable.java",
    "content": "/*\n * The MIT License (MIT)\n * Copyright (c) 2015 TERAI Atsuhiro\n * Copyright (c) 2024 Skylot\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 * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\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\npackage jadx.gui.ui.tab.dnd;\n\nimport java.awt.datatransfer.DataFlavor;\nimport java.awt.datatransfer.Transferable;\n\nclass TabDndTransferable implements Transferable {\n\tprivate static final String NAME = \"Transferable Tab\";\n\n\t@Override\n\tpublic Object getTransferData(DataFlavor flavor) {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic DataFlavor[] getTransferDataFlavors() {\n\t\treturn new DataFlavor[] { new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType, NAME) };\n\t}\n\n\t@Override\n\tpublic boolean isDataFlavorSupported(DataFlavor flavor) {\n\t\treturn NAME.equals(flavor.getHumanPresentableName());\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/treenodes/SummaryNode.java",
    "content": "package jadx.gui.ui.treenodes;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.text.StringEscapeUtils;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ResourceFile;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.core.dex.attributes.IAttributeNode;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.ProcessState;\nimport jadx.core.utils.ErrorsCounter;\nimport jadx.core.utils.Utils;\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.panel.HtmlPanel;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.UiUtils;\n\npublic class SummaryNode extends JNode {\n\tprivate static final long serialVersionUID = 4295299814582784805L;\n\n\tprivate static final ImageIcon ICON = UiUtils.openSvgIcon(\"nodes/detailView\");\n\n\tprivate final MainWindow mainWindow;\n\tprivate final JadxWrapper wrapper;\n\n\tpublic SummaryNode(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.wrapper = mainWindow.getWrapper();\n\t}\n\n\t@Override\n\tpublic ICodeInfo getCodeInfo() {\n\t\tStringEscapeUtils.Builder builder = StringEscapeUtils.builder(StringEscapeUtils.ESCAPE_HTML4);\n\t\ttry {\n\t\t\tbuilder.append(\"<html>\");\n\t\t\tbuilder.append(\"<body>\");\n\t\t\twriteInputSummary(builder);\n\t\t\twriteDecompilationSummary(builder);\n\t\t\tbuilder.append(\"</body>\");\n\t\t} catch (Exception e) {\n\t\t\tbuilder.append(\"Error build summary: \");\n\t\t\tbuilder.append(\"<pre>\");\n\t\t\tbuilder.append(Utils.getStackTrace(e));\n\t\t\tbuilder.append(\"</pre>\");\n\t\t}\n\t\treturn new SimpleCodeInfo(builder.toString());\n\t}\n\n\tprivate void writeInputSummary(StringEscapeUtils.Builder builder) throws IOException {\n\t\tbuilder.append(\"<h2>Input</h2>\");\n\t\tbuilder.append(\"<h3>Files</h3>\");\n\t\tbuilder.append(\"<ul>\");\n\t\tfor (File inputFile : wrapper.getArgs().getInputFiles()) {\n\t\t\tbuilder.append(\"<li>\");\n\t\t\tbuilder.escape(inputFile.getCanonicalFile().getAbsolutePath());\n\t\t\tbuilder.append(\"</li>\");\n\t\t}\n\t\tbuilder.append(\"</ul>\");\n\n\t\tList<ClassNode> classes = wrapper.getRootNode().getClasses(true);\n\t\tList<String> codeSources = classes.stream()\n\t\t\t\t.map(ClassNode::getInputFileName)\n\t\t\t\t.distinct()\n\t\t\t\t.sorted(Comparator.naturalOrder())\n\t\t\t\t.collect(Collectors.toList());\n\t\tcodeSources.remove(\"synthetic\");\n\t\tint codeSourcesCount = codeSources.size();\n\t\tbuilder.append(\"<h3>Code sources</h3>\");\n\t\tbuilder.append(\"<ul>\");\n\t\tif (codeSourcesCount != 1) {\n\t\t\tbuilder.append(\"<li>Count: \" + codeSourcesCount + \"</li>\");\n\t\t}\n\t\tfor (String input : codeSources) {\n\t\t\tbuilder.append(\"<li>\");\n\t\t\tbuilder.escape(input);\n\t\t\tbuilder.append(\"</li>\");\n\t\t}\n\t\tbuilder.append(\"</ul>\");\n\n\t\taddNativeLibsInfo(builder);\n\n\t\tint methodsCount = classes.stream().mapToInt(cls -> cls.getMethods().size()).sum();\n\t\tint fieldsCount = classes.stream().mapToInt(cls -> cls.getFields().size()).sum();\n\t\tint insnCount = classes.stream().flatMap(cls -> cls.getMethods().stream()).mapToInt(MethodNode::getInsnsCount).sum();\n\t\tbuilder.append(\"<h3>Counts</h3>\");\n\t\tbuilder.append(\"<ul>\");\n\t\tbuilder.append(\"<li>Classes: \" + classes.size() + \"</li>\");\n\t\tbuilder.append(\"<li>Methods: \" + methodsCount + \"</li>\");\n\t\tbuilder.append(\"<li>Fields: \" + fieldsCount + \"</li>\");\n\t\tbuilder.append(\"<li>Instructions: \" + insnCount + \" (units)</li>\");\n\t\tbuilder.append(\"</ul>\");\n\t}\n\n\tprivate void addNativeLibsInfo(StringEscapeUtils.Builder builder) {\n\t\tList<String> nativeLibs = wrapper.getResources().stream()\n\t\t\t\t.map(ResourceFile::getOriginalName)\n\t\t\t\t.filter(f -> f.endsWith(\".so\"))\n\t\t\t\t.sorted(Comparator.naturalOrder())\n\t\t\t\t.collect(Collectors.toList());\n\t\tbuilder.append(\"<h3>Native libs</h3>\");\n\t\tbuilder.append(\"<ul>\");\n\t\tif (nativeLibs.isEmpty()) {\n\t\t\tbuilder.append(\"<li>Total count: 0</li>\");\n\t\t} else {\n\t\t\tMap<String, Set<String>> libsByArch = new HashMap<>();\n\t\t\tfor (String libFile : nativeLibs) {\n\t\t\t\tString[] parts = StringUtils.split(libFile, '/');\n\t\t\t\tint count = parts.length;\n\t\t\t\tif (count >= 2) {\n\t\t\t\t\tString arch = parts[count - 2];\n\t\t\t\t\tString name = parts[count - 1];\n\t\t\t\t\tlibsByArch.computeIfAbsent(arch, (a) -> new HashSet<>())\n\t\t\t\t\t\t\t.add(name);\n\t\t\t\t}\n\t\t\t}\n\t\t\tString arches = libsByArch.keySet().stream()\n\t\t\t\t\t.sorted(Comparator.naturalOrder())\n\t\t\t\t\t.collect(Collectors.joining(\", \"));\n\t\t\tbuilder.append(\"<li>Arch list: \" + arches + \"</li>\");\n\n\t\t\tString perArchCount = libsByArch.entrySet().stream()\n\t\t\t\t\t.map(entry -> entry.getKey() + \":\" + entry.getValue().size())\n\t\t\t\t\t.sorted(Comparator.naturalOrder())\n\t\t\t\t\t.collect(Collectors.joining(\", \"));\n\t\t\tbuilder.append(\"<li>Per arch count: \" + perArchCount + \"</li>\");\n\n\t\t\tbuilder.append(\"<br>\");\n\t\t\tbuilder.append(\"<li>Total count: \" + nativeLibs.size() + \"</li>\");\n\t\t\tfor (String lib : nativeLibs) {\n\t\t\t\tbuilder.append(\"<li>\");\n\t\t\t\tbuilder.escape(lib);\n\t\t\t\tbuilder.append(\"</li>\");\n\t\t\t}\n\t\t}\n\t\tbuilder.append(\"</ul>\");\n\t}\n\n\tprivate void writeDecompilationSummary(StringEscapeUtils.Builder builder) {\n\t\tbuilder.append(\"<h2>Decompilation</h2>\");\n\t\tList<ClassNode> classes = wrapper.getRootNode().getClassesWithoutInner();\n\t\tint classesCount = classes.size();\n\t\tlong notLoadedClasses = classes.stream().filter(c -> c.getState() == ProcessState.NOT_LOADED).count();\n\t\tlong loadedClasses = classes.stream().filter(c -> c.getState() == ProcessState.LOADED).count();\n\t\tlong processedClasses = classes.stream().filter(c -> c.getState() == ProcessState.PROCESS_COMPLETE).count();\n\t\tlong generatedClasses = classes.stream().filter(c -> c.getState() == ProcessState.GENERATED_AND_UNLOADED).count();\n\t\tbuilder.append(\"<ul>\");\n\t\tbuilder.append(\"<li>Top level classes: \" + classesCount + \"</li>\");\n\t\tbuilder.append(\"<li>Not loaded: \" + valueAndPercent(notLoadedClasses, classesCount) + \"</li>\");\n\t\tbuilder.append(\"<li>Loaded: \" + valueAndPercent(loadedClasses, classesCount) + \"</li>\");\n\t\tbuilder.append(\"<li>Processed: \" + valueAndPercent(processedClasses, classesCount) + \"</li>\");\n\t\tbuilder.append(\"<li>Code generated: \" + valueAndPercent(generatedClasses, classesCount) + \"</li>\");\n\t\tbuilder.append(\"</ul>\");\n\n\t\tErrorsCounter counter = wrapper.getRootNode().getErrorsCounter();\n\t\tSet<IAttributeNode> problemNodes = new HashSet<>();\n\t\tproblemNodes.addAll(counter.getErrorNodes());\n\t\tproblemNodes.addAll(counter.getWarnNodes());\n\t\tlong problemMethods = problemNodes.stream().filter(MethodNode.class::isInstance).count();\n\t\tint methodsCount = classes.stream().mapToInt(cls -> cls.getMethods().size()).sum();\n\t\tdouble methodSuccessRate = (methodsCount - problemMethods) * 100.0 / (double) methodsCount;\n\n\t\tbuilder.append(\"<h3>Issues</h3>\");\n\t\tbuilder.append(\"<ul>\");\n\t\tbuilder.append(\"<li>Errors: \" + counter.getErrorCount() + \"</li>\");\n\t\tbuilder.append(\"<li>Warnings: \" + counter.getWarnsCount() + \"</li>\");\n\t\tbuilder.append(\"<li>Nodes with errors: \" + counter.getErrorNodes().size() + \"</li>\");\n\t\tbuilder.append(\"<li>Nodes with warnings: \" + counter.getWarnNodes().size() + \"</li>\");\n\t\tbuilder.append(\"<li>Total nodes with issues: \" + problemNodes.size() + \"</li>\");\n\t\tbuilder.append(\"<li>Methods with issues: \" + problemMethods + \"</li>\");\n\t\tbuilder.append(\"<li>Methods success rate: \" + String.format(\"%.2f\", methodSuccessRate) + \"%</li>\");\n\t\tbuilder.append(\"</ul>\");\n\t}\n\n\tprivate String valueAndPercent(long value, int total) {\n\t\treturn String.format(\"%d (%.2f%%)\", value, value * 100 / ((double) total));\n\t}\n\n\t@Override\n\tpublic boolean hasContent() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic ContentPanel getContentPanel(TabbedPane tabbedPane) {\n\t\treturn new HtmlPanel(tabbedPane, this);\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn \"Summary\";\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn ICON;\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/ui/treenodes/UndisplayedStringsNode.java",
    "content": "package jadx.gui.ui.treenodes;\n\nimport javax.swing.Icon;\n\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.panel.ContentPanel;\nimport jadx.gui.ui.panel.UndisplayedStringsPanel;\nimport jadx.gui.ui.tab.TabbedPane;\nimport jadx.gui.utils.Icons;\nimport jadx.gui.utils.NLS;\n\npublic class UndisplayedStringsNode extends JNode {\n\tprivate static final long serialVersionUID = 2005158949697898302L;\n\n\tprivate final String undisplayedStings;\n\n\tpublic UndisplayedStringsNode(String undisplayedStings) {\n\t\tthis.undisplayedStings = undisplayedStings;\n\t}\n\n\t@Override\n\tpublic boolean hasContent() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic ContentPanel getContentPanel(TabbedPane tabbedPane) {\n\t\treturn new UndisplayedStringsPanel(tabbedPane, this);\n\t}\n\n\t@Override\n\tpublic String makeString() {\n\t\treturn NLS.str(\"msg.non_displayable_chars.title\");\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn Icons.FONT;\n\t}\n\n\t@Override\n\tpublic JClass getJParent() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String makeDescString() {\n\t\treturn undisplayedStings;\n\t}\n\n\t@Override\n\tpublic boolean supportsQuickTabs() {\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/update/JadxUpdate.kt",
    "content": "package jadx.gui.update\n\nimport com.google.gson.annotations.SerializedName\nimport io.github.oshai.kotlinlogging.KotlinLogging\nimport jadx.core.Jadx\nimport jadx.core.plugins.versions.VersionComparator\nimport jadx.core.utils.GsonUtils.buildGson\nimport jadx.gui.settings.JadxUpdateChannel\nimport java.io.InputStreamReader\nimport java.net.HttpURLConnection\nimport java.net.URI\nimport kotlin.reflect.KClass\n\ndata class Release(var name: String = \"\")\n\ndata class ArtifactList(var artifacts: List<Artifact>? = null)\n\ndata class Artifact(\n\tvar name: String = \"\",\n\t@SerializedName(\"workflow_run\") var workflowRun: WorkflowRun? = null,\n)\n\ndata class WorkflowRun(\n\t@SerializedName(\"head_branch\") var branch: String = \"\",\n)\n\ninterface IUpdateCallback {\n\tfun onUpdate(r: Release)\n}\n\nclass JadxUpdate(private val jadxVersion: String = Jadx.getVersion()) {\n\n\tcompanion object {\n\t\tprivate val LOG = KotlinLogging.logger {}\n\n\t\tconst val JADX_ARTIFACTS_URL = \"https://nightly.link/skylot/jadx/workflows/build-artifacts/master\"\n\t\tconst val JADX_RELEASES_URL = \"https://github.com/skylot/jadx/releases\"\n\n\t\tprivate const val GITHUB_API_URL = \"https://api.github.com/repos/skylot/jadx\"\n\t\tprivate const val GITHUB_LATEST_ARTIFACTS_URL = \"$GITHUB_API_URL/actions/artifacts?per_page=5&page=1\"\n\t\tprivate const val GITHUB_LATEST_RELEASE_URL = \"$GITHUB_API_URL/releases/latest\"\n\t}\n\n\tfun check(updateChannel: JadxUpdateChannel, callback: IUpdateCallback) {\n\t\tif (jadxVersion == Jadx.VERSION_DEV) {\n\t\t\tLOG.debug { \"Ignore update check: development version\" }\n\t\t\treturn\n\t\t}\n\t\tThread {\n\t\t\ttry {\n\t\t\t\tval release = checkForNewRelease(updateChannel)\n\t\t\t\tif (release != null) {\n\t\t\t\t\tcallback.onUpdate(release)\n\t\t\t\t} else {\n\t\t\t\t\tLOG.info { \"No updates found\" }\n\t\t\t\t}\n\t\t\t} catch (e: Exception) {\n\t\t\t\tLOG.warn(e) { \"Jadx update error\" }\n\t\t\t}\n\t\t}.apply {\n\t\t\tname = \"Jadx update thread\"\n\t\t\tpriority = Thread.MIN_PRIORITY\n\t\t\tstart()\n\t\t}\n\t}\n\n\tfun checkForNewRelease(updateChannel: JadxUpdateChannel): Release? {\n\t\tLOG.info { \"Checking for updates...\" }\n\t\tLOG.info { \"Update channel: $updateChannel, current version: $jadxVersion\" }\n\t\treturn when (updateChannel) {\n\t\t\tJadxUpdateChannel.STABLE -> checkForNewStableRelease()\n\t\t\tJadxUpdateChannel.UNSTABLE -> checkForNewUnstableRelease()\n\t\t}\n\t}\n\n\tprivate fun checkForNewStableRelease(): Release? {\n\t\tif (jadxVersion.startsWith(\"r\")) {\n\t\t\t// current version is 'unstable', but update channel set to 'stable'\n\t\t\tLOG.info { \"Skip update check: can't compare unstable and stable versions\" }\n\t\t\treturn null\n\t\t}\n\t\tval latestRelease = getAndParse(GITHUB_LATEST_RELEASE_URL, Release::class) ?: return null\n\t\tif (VersionComparator.checkAndCompare(jadxVersion, latestRelease.name) >= 0) return null\n\t\tLOG.info { \"Found new jadx version: ${latestRelease.name}\" }\n\t\treturn latestRelease\n\t}\n\n\tprivate fun checkForNewUnstableRelease(): Release? {\n\t\tval artifacts = getAndParse(GITHUB_LATEST_ARTIFACTS_URL, ArtifactList::class)\n\t\t\t?.artifacts\n\t\t\t?.filter { it.workflowRun?.branch == \"master\" }\n\t\t\t?: return null\n\t\tif (artifacts.isEmpty()) return null\n\n\t\tval latestVersion = artifacts[0].name.removePrefix(\"jadx-gui-\").removePrefix(\"jadx-\").substringBefore('-')\n\t\tif (VersionComparator.checkAndCompare(jadxVersion, latestVersion) >= 0) return null\n\t\tLOG.info { \"Found new unstable version: $latestVersion\" }\n\t\treturn Release(latestVersion)\n\t}\n\n\tprivate fun <T : Any> getAndParse(url: String, klass: KClass<T>): T? {\n\t\tval con = URI(url).toURL().openConnection() as? HttpURLConnection\n\t\tif (con == null || con.responseCode != 200) {\n\t\t\treturn null\n\t\t}\n\t\treturn con.inputStream.use { stream ->\n\t\t\tInputStreamReader(stream).use { reader ->\n\t\t\t\tbuildGson().fromJson(reader, klass.java)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/CacheObject.java",
    "content": "package jadx.gui.utils;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.ui.dialog.SearchDialog;\nimport jadx.gui.utils.pkgs.PackageHelper;\n\npublic class CacheObject {\n\tprivate final JadxWrapper wrapper;\n\tprivate final JNodeCache jNodeCache;\n\tprivate final PackageHelper packageHelper;\n\n\tprivate String lastSearch;\n\tprivate Map<SearchDialog.SearchPreset, Set<SearchDialog.SearchOptions>> lastSearchOptions;\n\tprivate String lastSearchPackage;\n\tprivate int maxPkgLength;\n\n\tprivate volatile boolean fullDecompilationFinished;\n\n\tpublic CacheObject(JadxWrapper wrapper) {\n\t\tthis.wrapper = wrapper;\n\t\tthis.jNodeCache = new JNodeCache(wrapper);\n\t\tthis.packageHelper = new PackageHelper(wrapper, jNodeCache);\n\t\treset();\n\t}\n\n\tpublic void reset() {\n\t\tlastSearch = null;\n\t\tjNodeCache.reset();\n\t\tlastSearchOptions = new HashMap<>();\n\t\tlastSearchPackage = null;\n\t\tfullDecompilationFinished = false;\n\t}\n\n\t@Nullable\n\tpublic String getLastSearch() {\n\t\treturn lastSearch;\n\t}\n\n\t@Nullable\n\tpublic String getLastSearchPackage() {\n\t\treturn lastSearchPackage;\n\t}\n\n\tpublic void setLastSearch(String lastSearch) {\n\t\tthis.lastSearch = lastSearch;\n\t}\n\n\tpublic void setLastSearchPackage(String lastSearchPackage) {\n\t\tthis.lastSearchPackage = lastSearchPackage;\n\t}\n\n\tpublic int getMaxPkgLength() {\n\t\treturn maxPkgLength;\n\t}\n\n\tpublic void setMaxPkgLength(int maxPkgLength) {\n\t\tthis.maxPkgLength = maxPkgLength;\n\t}\n\n\tpublic JNodeCache getNodeCache() {\n\t\treturn jNodeCache;\n\t}\n\n\tpublic Map<SearchDialog.SearchPreset, Set<SearchDialog.SearchOptions>> getLastSearchOptions() {\n\t\treturn lastSearchOptions;\n\t}\n\n\tpublic PackageHelper getPackageHelper() {\n\t\treturn packageHelper;\n\t}\n\n\tpublic boolean isFullDecompilationFinished() {\n\t\treturn fullDecompilationFinished;\n\t}\n\n\tpublic void setFullDecompilationFinished(boolean fullDecompilationFinished) {\n\t\tthis.fullDecompilationFinished = fullDecompilationFinished;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/CaretPositionFix.java",
    "content": "package jadx.gui.utils;\n\nimport java.util.Map;\n\nimport javax.swing.text.BadLocationException;\n\nimport org.fife.ui.rsyntaxtextarea.Token;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeMetadata;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.metadata.annotations.InsnCodeOffset;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.ui.codearea.AbstractCodeArea;\n\n/**\n * After class refresh (rename, comment, etc) the change of document is undetectable.\n * So use Token index or offset in line to calculate the new caret position.\n */\npublic class CaretPositionFix {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CaretPositionFix.class);\n\n\tprivate final AbstractCodeArea codeArea;\n\n\tprivate int linesCount;\n\tprivate int line;\n\tprivate int pos;\n\tprivate int lineOffset;\n\tprivate TokenInfo tokenInfo;\n\n\tprivate int javaNodePos = -1;\n\tprivate int codeRawOffset = -1;\n\n\tpublic CaretPositionFix(AbstractCodeArea codeArea) {\n\t\tthis.codeArea = codeArea;\n\t}\n\n\t/**\n\t * Save caret position by anchor to token under caret\n\t */\n\tpublic void save() {\n\t\ttry {\n\t\t\tlinesCount = codeArea.getLineCount();\n\t\t\tpos = codeArea.getCaretPosition();\n\t\t\tline = codeArea.getLineOfOffset(pos);\n\t\t\tlineOffset = pos - codeArea.getLineStartOffset(line);\n\n\t\t\ttokenInfo = getTokenInfoByOffset(codeArea.getTokenListForLine(line), pos);\n\n\t\t\tICodeInfo codeInfo = codeArea.getCodeInfo();\n\t\t\tif (codeInfo.hasMetadata()) {\n\t\t\t\tICodeMetadata metadata = codeInfo.getCodeMetadata();\n\t\t\t\tICodeAnnotation ann = metadata.getAt(pos);\n\t\t\t\tif (ann instanceof InsnCodeOffset) {\n\t\t\t\t\tcodeRawOffset = ((InsnCodeOffset) ann).getOffset();\n\t\t\t\t\tICodeNodeRef javaNode = metadata.getNodeAt(pos);\n\t\t\t\t\tif (javaNode != null) {\n\t\t\t\t\t\tjavaNodePos = javaNode.getDefPosition();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tLOG.debug(\"Saved position data: line={}, lineOffset={}, token={}, codeRawOffset={}, javaNodeLine={}\",\n\t\t\t\t\tline, lineOffset, tokenInfo, codeRawOffset, javaNodePos);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to save caret position before refresh\", e);\n\t\t\tline = -1;\n\t\t}\n\t}\n\n\t/**\n\t * Restore caret position in refreshed code.\n\t * Expected to be called in UI thread.\n\t */\n\tpublic void restore() {\n\t\tif (line == -1) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tint newPos = getNewPos();\n\t\t\tint newLine = codeArea.getLineOfOffset(newPos);\n\t\t\tToken token = codeArea.getTokenListForLine(newLine);\n\t\t\tint tokenPos = getOffsetFromTokenInfo(tokenInfo, token);\n\t\t\tif (tokenPos == -1) {\n\t\t\t\tint lineStartOffset = codeArea.getLineStartOffset(newLine);\n\t\t\t\tint lineEndOffset = codeArea.getLineEndOffset(newLine) - 1;\n\t\t\t\tint lineLength = lineEndOffset - lineStartOffset;\n\t\t\t\t// can't restore using token -> just restore by line offset\n\t\t\t\tif (lineOffset < lineLength) {\n\t\t\t\t\ttokenPos = lineStartOffset + lineOffset;\n\t\t\t\t} else {\n\t\t\t\t\t// line truncated -> set caret at line end\n\t\t\t\t\ttokenPos = lineEndOffset;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcodeArea.setCaretPosition(tokenPos);\n\t\t\tLOG.debug(\"Restored caret position: {}\", tokenPos);\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to restore caret position\", e);\n\t\t}\n\t}\n\n\tprivate int getNewPos() throws BadLocationException {\n\t\tint newLinesCount = codeArea.getLineCount();\n\t\tif (linesCount == newLinesCount) {\n\t\t\treturn pos;\n\t\t}\n\t\t// lines count changes, try find line by raw offset\n\t\tICodeInfo codeInfo = codeArea.getCodeInfo();\n\t\tif (javaNodePos != -1 && codeInfo.hasMetadata()) {\n\t\t\tJClass cls = codeArea.getJClass();\n\t\t\tif (cls != null) {\n\t\t\t\tICodeMetadata codeMetadata = codeInfo.getCodeMetadata();\n\t\t\t\tfor (Map.Entry<Integer, ICodeAnnotation> entry : codeMetadata.getAsMap().entrySet()) {\n\t\t\t\t\tint annPos = entry.getKey();\n\t\t\t\t\tif (annPos >= javaNodePos) {\n\t\t\t\t\t\tICodeAnnotation ann = entry.getValue();\n\t\t\t\t\t\tif (ann instanceof InsnCodeOffset\n\t\t\t\t\t\t\t\t&& ((InsnCodeOffset) ann).getOffset() == codeRawOffset) {\n\t\t\t\t\t\t\treturn annPos;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// fallback: assume lines added/removed before caret\n\t\tint newLine = line - (linesCount - newLinesCount);\n\t\treturn codeArea.getLineStartOffset(newLine);\n\t}\n\n\tprivate TokenInfo getTokenInfoByOffset(Token token, int offset) {\n\t\tif (token == null) {\n\t\t\treturn null;\n\t\t}\n\t\tint index = 1;\n\t\twhile (token.getEndOffset() < offset) {\n\t\t\ttoken = token.getNextToken();\n\t\t\tif (token == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tindex++;\n\t\t}\n\t\treturn new TokenInfo(index, token.getType());\n\t}\n\n\tprivate int getOffsetFromTokenInfo(TokenInfo tokenInfo, Token token) {\n\t\tif (tokenInfo == null || token == null) {\n\t\t\treturn -1;\n\t\t}\n\t\tint index = tokenInfo.getIndex();\n\t\tif (index == -1) {\n\t\t\treturn -1;\n\t\t}\n\t\tfor (int i = 0; i < index; i++) {\n\t\t\ttoken = token.getNextToken();\n\t\t\tif (token == null) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t\tif (token.getType() != tokenInfo.getType()) {\n\t\t\treturn -1;\n\t\t}\n\t\treturn token.getOffset();\n\t}\n\n\tprivate static final class TokenInfo {\n\t\tprivate final int index;\n\t\tprivate final int type;\n\n\t\tpublic TokenInfo(int index, int type) {\n\t\t\tthis.index = index;\n\t\t\tthis.type = type;\n\t\t}\n\n\t\tpublic int getIndex() {\n\t\t\treturn index;\n\t\t}\n\n\t\tpublic int getType() {\n\t\t\treturn type;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Token{index=\" + index + \", type=\" + type + '}';\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/CertificateManager.java",
    "content": "package jadx.gui.utils;\n\nimport java.io.InputStream;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.Principal;\nimport java.security.PublicKey;\nimport java.security.cert.Certificate;\nimport java.security.cert.CertificateEncodingException;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\nimport java.security.interfaces.DSAPublicKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class CertificateManager {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(CertificateManager.class);\n\tprivate static final String CERTIFICATE_TYPE_NAME = \"X.509\";\n\n\tprivate final Certificate cert;\n\tprivate X509Certificate x509cert;\n\n\tpublic static String decode(InputStream in) {\n\t\tStringBuilder strBuild = new StringBuilder();\n\t\tCollection<? extends Certificate> certificates = readCertificates(in);\n\t\tif (certificates != null) {\n\t\t\tfor (Certificate cert : certificates) {\n\t\t\t\tCertificateManager certificateManager = new CertificateManager(cert);\n\t\t\t\tstrBuild.append(certificateManager.generateText());\n\t\t\t}\n\t\t}\n\t\treturn strBuild.toString();\n\t}\n\n\tstatic Collection<? extends Certificate> readCertificates(InputStream in) {\n\t\ttry {\n\t\t\tCertificateFactory cf = CertificateFactory.getInstance(CERTIFICATE_TYPE_NAME);\n\t\t\treturn cf.generateCertificates(in);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Certificate read error\", e);\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\tpublic CertificateManager(Certificate cert) {\n\t\tthis.cert = cert;\n\t\tString type = cert.getType();\n\t\tif (type.equals(CERTIFICATE_TYPE_NAME) && cert instanceof X509Certificate) {\n\t\t\tx509cert = (X509Certificate) cert;\n\t\t}\n\t}\n\n\tpublic String generateHeader() {\n\t\tStringBuilder builder = new StringBuilder();\n\t\tappend(builder, NLS.str(\"certificate.cert_type\"), x509cert.getType());\n\t\tappend(builder, NLS.str(\"certificate.serialSigVer\"), ((Integer) x509cert.getVersion()).toString());\n\t\t// serial number\n\t\tappend(builder, NLS.str(\"certificate.serialNumber\"), \"0x\" + x509cert.getSerialNumber().toString(16));\n\n\t\t// Get subject\n\t\tPrincipal subjectDN = x509cert.getSubjectDN();\n\t\tappend(builder, NLS.str(\"certificate.cert_subject\"), subjectDN.getName());\n\n\t\tappend(builder, NLS.str(\"certificate.serialValidFrom\"), x509cert.getNotBefore().toString());\n\t\tappend(builder, NLS.str(\"certificate.serialValidUntil\"), x509cert.getNotAfter().toString());\n\t\treturn builder.toString();\n\t}\n\n\tpublic String generateSignature() {\n\t\tStringBuilder builder = new StringBuilder();\n\t\tappend(builder, NLS.str(\"certificate.serialSigType\"), x509cert.getSigAlgName());\n\t\tappend(builder, NLS.str(\"certificate.serialSigOID\"), x509cert.getSigAlgOID());\n\t\treturn builder.toString();\n\t}\n\n\tpublic String generateFingerprint() {\n\t\tStringBuilder builder = new StringBuilder();\n\t\ttry {\n\t\t\tappend(builder, NLS.str(\"certificate.serialMD5\"), getThumbPrint(x509cert, \"MD5\"));\n\t\t\tappend(builder, NLS.str(\"certificate.serialSHA1\"), getThumbPrint(x509cert, \"SHA-1\"));\n\t\t\tappend(builder, NLS.str(\"certificate.serialSHA256\"), getThumbPrint(x509cert, \"SHA-256\"));\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to parse fingerprint\", e);\n\t\t}\n\t\treturn builder.toString();\n\t}\n\n\tpublic String generatePublicKey() {\n\t\tPublicKey publicKey = x509cert.getPublicKey();\n\t\tif (publicKey instanceof RSAPublicKey) {\n\t\t\treturn generateRSAPublicKey();\n\t\t}\n\t\tif (publicKey instanceof DSAPublicKey) {\n\t\t\treturn generateDSAPublicKey();\n\t\t}\n\t\treturn \"\";\n\t}\n\n\tString generateRSAPublicKey() {\n\t\tRSAPublicKey pub = (RSAPublicKey) cert.getPublicKey();\n\t\tStringBuilder builder = new StringBuilder();\n\n\t\tappend(builder, NLS.str(\"certificate.serialPubKeyType\"), pub.getAlgorithm());\n\t\tappend(builder, NLS.str(\"certificate.serialPubKeyExponent\"), pub.getPublicExponent().toString(10));\n\t\tappend(builder, NLS.str(\"certificate.serialPubKeyModulusSize\"), Integer.toString(\n\t\t\t\tpub.getModulus().toString(2).length()));\n\t\tappend(builder, NLS.str(\"certificate.serialPubKeyModulus\"), pub.getModulus().toString(10));\n\n\t\treturn builder.toString();\n\t}\n\n\tString generateDSAPublicKey() {\n\t\tDSAPublicKey pub = (DSAPublicKey) cert.getPublicKey();\n\t\tStringBuilder builder = new StringBuilder();\n\t\tappend(builder, NLS.str(\"certificate.serialPubKeyType\"), pub.getAlgorithm());\n\t\tappend(builder, NLS.str(\"certificate.serialPubKeyY\"), pub.getY().toString(10));\n\n\t\treturn builder.toString();\n\t}\n\n\tpublic String generateTextForX509() {\n\t\tStringBuilder builder = new StringBuilder();\n\t\tif (x509cert != null) {\n\t\t\tbuilder.append(generateHeader());\n\t\t\tbuilder.append('\\n');\n\n\t\t\tbuilder.append(generatePublicKey());\n\t\t\tbuilder.append('\\n');\n\n\t\t\tbuilder.append(generateSignature());\n\t\t\tbuilder.append('\\n');\n\t\t\tbuilder.append(generateFingerprint());\n\t\t}\n\t\treturn builder.toString();\n\t}\n\n\tpublic String generateText() {\n\t\tStringBuilder str = new StringBuilder();\n\t\tString type = cert.getType();\n\t\tif (type.equals(CERTIFICATE_TYPE_NAME)) {\n\t\t\tstr.append(generateTextForX509());\n\t\t} else {\n\t\t\tstr.append(cert.toString());\n\t\t}\n\t\treturn str.toString();\n\t}\n\n\tstatic void append(StringBuilder str, String name, String value) {\n\t\tstr.append(name).append(\": \").append(value).append('\\n');\n\t}\n\n\tpublic static String getThumbPrint(X509Certificate cert, String type)\n\t\t\tthrows NoSuchAlgorithmException, CertificateEncodingException {\n\t\tMessageDigest md = MessageDigest.getInstance(type); // lgtm [java/weak-cryptographic-algorithm]\n\t\tbyte[] der = cert.getEncoded();\n\t\tmd.update(der);\n\t\tbyte[] digest = md.digest();\n\t\treturn hexify(digest);\n\t}\n\n\tpublic static String hexify(byte[] bytes) {\n\t\tchar[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };\n\t\tStringBuilder buf = new StringBuilder(bytes.length * 3);\n\t\tfor (byte aByte : bytes) {\n\t\t\tbuf.append(hexDigits[(aByte & 0xf0) >> 4]);\n\t\t\tbuf.append(hexDigits[aByte & 0x0f]);\n\t\t\tbuf.append(' ');\n\t\t}\n\t\treturn buf.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/DefaultPopupMenuListener.java",
    "content": "package jadx.gui.utils;\n\nimport javax.swing.event.PopupMenuEvent;\nimport javax.swing.event.PopupMenuListener;\n\npublic interface DefaultPopupMenuListener extends PopupMenuListener {\n\t@Override\n\tdefault void popupMenuWillBecomeVisible(PopupMenuEvent e) {\n\t}\n\n\t@Override\n\tdefault void popupMenuWillBecomeInvisible(PopupMenuEvent e) {\n\t}\n\n\t@Override\n\tdefault void popupMenuCanceled(PopupMenuEvent e) {\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/DesktopEntryUtils.java",
    "content": "package jadx.gui.utils;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.export.TemplateFile;\nimport jadx.core.utils.files.FileUtils;\n\npublic class DesktopEntryUtils {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DesktopEntryUtils.class);\n\tprivate static final Map<Integer, String> SIZE_TO_LOGO_MAP = Map.of(\n\t\t\t16, \"jadx-logo-16px.png\",\n\t\t\t32, \"jadx-logo-32px.png\",\n\t\t\t48, \"jadx-logo-48px.png\",\n\t\t\t252, \"jadx-logo.png\",\n\t\t\t256, \"jadx-logo.png\");\n\tprivate static final Path XDG_DESKTOP_MENU_COMMAND_PATH = findExecutablePath(\"xdg-desktop-menu\");\n\tprivate static final Path XDG_ICON_RESOURCE_COMMAND_PATH = findExecutablePath(\"xdg-icon-resource\");\n\n\tpublic static boolean createDesktopEntry() {\n\t\tif (XDG_DESKTOP_MENU_COMMAND_PATH == null) {\n\t\t\tLOG.error(\"xdg-desktop-menu was not found in $PATH\");\n\t\t\treturn false;\n\t\t}\n\t\tif (XDG_ICON_RESOURCE_COMMAND_PATH == null) {\n\t\t\tLOG.error(\"xdg-icon-resource was not found in $PATH\");\n\t\t\treturn false;\n\t\t}\n\t\tPath desktopTempFile = FileUtils.createTempFileNonPrefixed(\"jadx-gui.desktop\");\n\t\tPath iconTempFolder = FileUtils.createTempDir(\"logos\");\n\t\tLOG.debug(\"Creating desktop with temp files: {}, {}\", desktopTempFile, iconTempFolder);\n\t\ttry {\n\t\t\treturn createDesktopEntry(desktopTempFile, iconTempFolder);\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tFileUtils.deleteFileIfExists(desktopTempFile);\n\t\t\t\tFileUtils.deleteDirIfExists(iconTempFolder);\n\t\t\t} catch (IOException e) {\n\t\t\t\tLOG.error(\"Failed to clean up temp files\", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean createDesktopEntry(Path desktopTempFile, Path iconTempFolder) {\n\t\tString launchScriptPath = getLaunchScriptPath();\n\t\tif (launchScriptPath == null) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (Map.Entry<Integer, String> entry : SIZE_TO_LOGO_MAP.entrySet()) {\n\t\t\tPath path = iconTempFolder.resolve(entry.getKey() + \".png\");\n\t\t\tif (!writeLogoFile(entry.getValue(), path)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!installIcon(entry.getKey(), path)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tif (!writeDesktopFile(launchScriptPath, desktopTempFile)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn installDesktopEntry(desktopTempFile);\n\t}\n\n\tprivate static boolean installDesktopEntry(Path desktopTempFile) {\n\t\ttry {\n\t\t\tProcessBuilder desktopFileInstallCommand = new ProcessBuilder(Objects.requireNonNull(XDG_DESKTOP_MENU_COMMAND_PATH).toString(),\n\t\t\t\t\t\"install\", desktopTempFile.toString());\n\t\t\tProcess process = desktopFileInstallCommand.start();\n\t\t\tint statusCode = process.waitFor();\n\t\t\tif (statusCode != 0) {\n\t\t\t\tLOG.error(\"Got error code {} while installing desktop file\", statusCode);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to install desktop file\", e);\n\t\t\treturn false;\n\t\t}\n\t\tLOG.info(\"Successfully installed desktop file\");\n\t\treturn true;\n\t}\n\n\tprivate static boolean installIcon(int size, Path iconPath) {\n\t\ttry {\n\t\t\tProcessBuilder iconInstallCommand =\n\t\t\t\t\tnew ProcessBuilder(Objects.requireNonNull(XDG_ICON_RESOURCE_COMMAND_PATH).toString(), \"install\", \"--novendor\", \"--size\",\n\t\t\t\t\t\t\tString.valueOf(size), iconPath.toString(),\n\t\t\t\t\t\t\t\"jadx\");\n\t\t\tProcess process = iconInstallCommand.start();\n\t\t\tint statusCode = process.waitFor();\n\t\t\tif (statusCode != 0) {\n\t\t\t\tLOG.error(\"Got error code {} while installing icon of size {}\", statusCode, size);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to install icon of size {}\", size, e);\n\t\t\treturn false;\n\t\t}\n\t\tLOG.info(\"Successfully installed icon of size {}\", size);\n\t\treturn true;\n\t}\n\n\tprivate static Path findExecutablePath(String executableName) {\n\t\tfor (String pathDirectory : System.getenv(\"PATH\").split(File.pathSeparator)) {\n\t\t\tPath path = Paths.get(pathDirectory, executableName);\n\t\t\tif (path.toFile().isFile() && path.toFile().canExecute()) {\n\t\t\t\treturn path;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static boolean writeDesktopFile(String launchScriptPath, Path desktopFilePath) {\n\t\ttry {\n\t\t\tTemplateFile tmpl = TemplateFile.fromResources(\"/files/jadx-gui.desktop.tmpl\");\n\t\t\ttmpl.add(\"launchScriptPath\", launchScriptPath);\n\t\t\tFileUtils.writeFile(desktopFilePath, tmpl.build());\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to save .desktop file at: {}\", desktopFilePath, e);\n\t\t\treturn false;\n\t\t}\n\t\tLOG.debug(\"Wrote .desktop file to {}\", desktopFilePath);\n\t\treturn true;\n\t}\n\n\tprivate static boolean writeLogoFile(String logoFile, Path logoPath) {\n\t\ttry (InputStream is = DesktopEntryUtils.class.getResourceAsStream(\"/logos/\" + logoFile)) {\n\t\t\tFileUtils.writeFile(logoPath, is);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to write logo file at: {}\", logoPath, e);\n\t\t\treturn false;\n\t\t}\n\t\tLOG.debug(\"Wrote logo file to: {}\", logoPath);\n\t\treturn true;\n\t}\n\n\tpublic static @Nullable String getLaunchScriptPath() {\n\t\tString launchScriptPath = System.getProperty(\"jadx.launchScript.path\");\n\t\tif (launchScriptPath == null || launchScriptPath.isEmpty()) {\n\t\t\tLOG.error(\n\t\t\t\t\t\"The jadx.launchScript.path property is not set. Please launch JADX with the bundled launch script or set it to the appropriate value yourself.\");\n\t\t\treturn null;\n\t\t}\n\t\tLOG.debug(\"JADX launch script path: {}\", launchScriptPath);\n\t\treturn launchScriptPath;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/FontUtils.java",
    "content": "package jadx.gui.utils;\n\nimport java.awt.Font;\nimport java.io.InputStream;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class FontUtils {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(FontUtils.class);\n\n\tpublic static Font loadByStr(String fontDesc) {\n\t\tString[] parts = fontDesc.split(\"/\");\n\t\tif (parts.length != 3) {\n\t\t\tthrow new JadxRuntimeException(\"Unsupported font description format: \" + fontDesc);\n\t\t}\n\t\tString family = parts[0];\n\t\tint style = parseFontStyle(parts[1]);\n\t\tint size = Integer.parseInt(parts[2]);\n\n\t\tFont font = FontUtils.getCompositeFont(family, style, size);\n\t\tif (font == null) {\n\t\t\tthrow new JadxRuntimeException(\"Font not found: \" + fontDesc);\n\t\t}\n\t\treturn font;\n\t}\n\n\tpublic static String convertToStr(@Nullable Font font) {\n\t\tif (font == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\tif (font.getSize() < 1) {\n\t\t\tthrow new JadxRuntimeException(\"Bad font size: \" + font.getSize());\n\t\t}\n\t\treturn font.getFamily()\n\t\t\t\t+ '/' + convertFontStyleToString(font.getStyle())\n\t\t\t\t+ '/' + font.getSize();\n\t}\n\n\tpublic static String convertFontStyleToString(int style) {\n\t\tif (style == 0) {\n\t\t\treturn \"plain\";\n\t\t}\n\t\tStringBuilder sb = new StringBuilder();\n\t\tif ((style & Font.BOLD) != 0) {\n\t\t\tsb.append(\"bold\");\n\t\t}\n\t\tif ((style & Font.ITALIC) != 0) {\n\t\t\tsb.append(\" italic\");\n\t\t}\n\t\treturn sb.toString().trim();\n\t}\n\n\tprivate static int parseFontStyle(String str) {\n\t\tint style = 0;\n\t\tif (str.contains(\"bold\")) {\n\t\t\tstyle |= Font.BOLD;\n\t\t}\n\t\tif (str.contains(\"italic\")) {\n\t\t\tstyle |= Font.ITALIC;\n\t\t}\n\t\treturn style;\n\t}\n\n\t@Nullable\n\tpublic static Font openFontTTF(String name) {\n\t\tString fontPath = \"/fonts/\" + name + \".ttf\";\n\t\ttry (InputStream is = UiUtils.class.getResourceAsStream(fontPath)) {\n\t\t\tFont font = Font.createFont(Font.TRUETYPE_FONT, is);\n\t\t\treturn font.deriveFont(12f);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed load font by path: {}\", fontPath, e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static boolean canStringBeDisplayed(String str, Font font) {\n\t\tif (str == null || str.isEmpty()) {\n\t\t\treturn true;\n\t\t}\n\t\tint offset = 0;\n\t\twhile (offset < str.length()) {\n\t\t\tint codePoint = str.codePointAt(offset);\n\t\t\tif (!font.canDisplay(codePoint)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\toffset += Character.charCount(codePoint);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * https://github.com/JFormDesigner/FlatLaf/issues/923\n\t * When changing fonts, use font.deriveFont() or FontUtils.getCompositeFont\n\t * instead of new Font(), otherwise FlatLaf's CJKs support will be lost\n\t */\n\tpublic static Font getCompositeFont(String family, int style, int size) {\n\t\treturn com.formdev.flatlaf.util.FontUtils.getCompositeFont(family, style, size);\n\t}\n\n\tprivate FontUtils() {\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/HexUtils.java",
    "content": "package jadx.gui.utils;\n\npublic class HexUtils {\n\n\tpublic static boolean isValidHexString(String hexString) {\n\t\tString cleanS = hexString.replace(\" \", \"\");\n\t\tint len = cleanS.length();\n\t\ttry {\n\t\t\tboolean isPair = len % 2 == 0;\n\t\t\tif (isPair) {\n\t\t\t\tLong.parseLong(cleanS, 16);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (NumberFormatException ex) {\n\t\t\t// ignore error\n\t\t\treturn false;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static byte[] hexStringToByteArray(String hexString) {\n\t\tif (hexString == null || hexString.isEmpty()) {\n\t\t\treturn new byte[0];\n\t\t}\n\t\tString cleanS = hexString.replace(\" \", \"\");\n\t\tint len = cleanS.length();\n\t\tif (!isValidHexString(hexString)) {\n\t\t\tthrow new IllegalArgumentException(\"Hex string must have even length. Input length: \" + len);\n\t\t}\n\n\t\tbyte[] data = new byte[len / 2];\n\t\tfor (int i = 0; i < len; i += 2) {\n\t\t\tString byteString = cleanS.substring(i, i + 2);\n\t\t\ttry {\n\t\t\t\tint intValue = Integer.parseInt(byteString, 16);\n\t\t\t\tdata[i / 2] = (byte) intValue;\n\t\t\t} catch (NumberFormatException e) {\n\t\t\t\tthrow new IllegalArgumentException(\"Input string contains non-hex characters at index \" + i + \": \" + byteString, e);\n\t\t\t}\n\t\t}\n\t\treturn data;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/ILoadListener.java",
    "content": "package jadx.gui.utils;\n\npublic interface ILoadListener {\n\n\t/**\n\t * Update files/project loaded state\n\t *\n\t * @return true to remove listener\n\t */\n\tboolean update(boolean loaded);\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/IOUtils.java",
    "content": "package jadx.gui.utils;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic class IOUtils {\n\n\t/**\n\t * This method can be deleted once Jadx is Java11+\n\t */\n\t@Nullable\n\tpublic static byte[] readNBytes(InputStream inputStream, int len) throws IOException {\n\t\tbyte[] payload = new byte[len];\n\t\tint readSize = 0;\n\t\twhile (true) {\n\t\t\tint read = inputStream.read(payload, readSize, len - readSize);\n\t\t\tif (read == -1) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treadSize += read;\n\t\t\tif (readSize == len) {\n\t\t\t\treturn payload;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static int read(InputStream inputStream, byte[] buf) throws IOException {\n\t\treturn read(inputStream, buf, 0, buf.length);\n\t}\n\n\tpublic static int read(InputStream inputStream, byte[] buf, int off, int len) throws IOException {\n\t\tint remainingBytes = len;\n\t\twhile (remainingBytes > 0) {\n\t\t\tint start = len - remainingBytes;\n\t\t\tint bytesRead = inputStream.read(buf, off + start, remainingBytes);\n\t\t\tif (bytesRead == -1) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tremainingBytes -= bytesRead;\n\t\t}\n\t\treturn len - remainingBytes;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/Icons.java",
    "content": "package jadx.gui.utils;\n\nimport javax.swing.ImageIcon;\n\nimport static jadx.gui.utils.UiUtils.openSvgIcon;\n\npublic class Icons {\n\n\tpublic static final ImageIcon OPEN = openSvgIcon(\"ui/openDisk\");\n\tpublic static final ImageIcon OPEN_PROJECT = openSvgIcon(\"ui/projectDirectory\");\n\tpublic static final ImageIcon NEW_PROJECT = openSvgIcon(\"ui/newFolder\");\n\n\tpublic static final ImageIcon CLOSE = openSvgIcon(\"ui/closeHovered\");\n\tpublic static final ImageIcon CLOSE_INACTIVE = openSvgIcon(\"ui/close\");\n\n\tpublic static final ImageIcon SAVE_ALL = UiUtils.openSvgIcon(\"ui/menu-saveall\");\n\n\tpublic static final ImageIcon FLAT_PKG = UiUtils.openSvgIcon(\"ui/moduleGroup\");\n\tpublic static final ImageIcon QUICK_TABS = UiUtils.openSvgIcon(\"ui/dataView\");\n\n\tpublic static final ImageIcon PIN = UiUtils.openSvgIcon(\"nodes/pin\");\n\tpublic static final ImageIcon PIN_DARK = UiUtils.openSvgIcon(\"nodes/pin_dark\");\n\tpublic static final ImageIcon PIN_HOVERED = UiUtils.openSvgIcon(\"nodes/pinHovered\");\n\tpublic static final ImageIcon PIN_HOVERED_DARK = UiUtils.openSvgIcon(\"nodes/pinHovered_dark\");\n\tpublic static final ImageIcon BOOKMARK = UiUtils.openSvgIcon(\"nodes/bookmark\");\n\tpublic static final ImageIcon BOOKMARK_OVERLAY = UiUtils.openSvgIcon(\"nodes/bookmark_overlay\");\n\tpublic static final ImageIcon BOOKMARK_DARK = UiUtils.openSvgIcon(\"nodes/bookmark_dark\");\n\tpublic static final ImageIcon BOOKMARK_OVERLAY_DARK = UiUtils.openSvgIcon(\"nodes/bookmark_overlay_dark\");\n\n\tpublic static final ImageIcon STATIC = openSvgIcon(\"nodes/staticMark\");\n\tpublic static final ImageIcon FINAL = openSvgIcon(\"nodes/finalMark\");\n\n\tpublic static final ImageIcon START_PAGE = openSvgIcon(\"nodes/newWindow\");\n\n\tpublic static final ImageIcon FOLDER = UiUtils.openSvgIcon(\"nodes/folder\");\n\tpublic static final ImageIcon FILE = UiUtils.openSvgIcon(\"nodes/file_any_type\");\n\n\tpublic static final ImageIcon PACKAGE = UiUtils.openSvgIcon(\"nodes/package\");\n\tpublic static final ImageIcon CLASS = UiUtils.openSvgIcon(\"nodes/class\");\n\tpublic static final ImageIcon METHOD = UiUtils.openSvgIcon(\"nodes/method\");\n\tpublic static final ImageIcon FIELD = UiUtils.openSvgIcon(\"nodes/field\");\n\tpublic static final ImageIcon PROPERTY = UiUtils.openSvgIcon(\"nodes/property\");\n\tpublic static final ImageIcon PARAMETER = UiUtils.openSvgIcon(\"nodes/parameter\");\n\n\tpublic static final ImageIcon RUN = UiUtils.openSvgIcon(\"ui/run\");\n\tpublic static final ImageIcon CHECK = UiUtils.openSvgIcon(\"ui/checkConstraint\");\n\tpublic static final ImageIcon FORMAT = UiUtils.openSvgIcon(\"ui/toolWindowMessages\");\n\tpublic static final ImageIcon RESET = UiUtils.openSvgIcon(\"ui/reset\");\n\n\tpublic static final ImageIcon FONT = UiUtils.openSvgIcon(\"nodes/fontFile\");\n\tpublic static final ImageIcon ICON_MARK = UiUtils.openSvgIcon(\"search/mark\");\n\tpublic static final ImageIcon ICON_MARK_SELECTED = UiUtils.openSvgIcon(\"search/previewSelected\");\n\tpublic static final ImageIcon ICON_REGEX = UiUtils.openSvgIcon(\"search/regexHovered\");\n\tpublic static final ImageIcon ICON_REGEX_SELECTED = UiUtils.openSvgIcon(\"search/regexSelected\");\n\tpublic static final ImageIcon ICON_WORDS = UiUtils.openSvgIcon(\"search/wordsHovered\");\n\tpublic static final ImageIcon ICON_WORDS_SELECTED = UiUtils.openSvgIcon(\"search/wordsSelected\");\n\tpublic static final ImageIcon ICON_MATCH = UiUtils.openSvgIcon(\"search/matchCaseHovered\");\n\tpublic static final ImageIcon ICON_MATCH_SELECTED = UiUtils.openSvgIcon(\"search/matchCaseSelected\");\n\tpublic static final ImageIcon ICON_UP = UiUtils.openSvgIcon(\"ui/top\");\n\tpublic static final ImageIcon ICON_DOWN = UiUtils.openSvgIcon(\"ui/bottom\");\n\tpublic static final ImageIcon ICON_CLOSE = UiUtils.openSvgIcon(\"ui/close\");\n\tpublic static final ImageIcon ICON_FIND_TYPE_TXT = UiUtils.openSvgIcon(\"search/text\");\n\tpublic static final ImageIcon ICON_FIND_TYPE_HEX = UiUtils.openSvgIcon(\"search/hexSerial\");\n\tpublic static final ImageIcon ICON_ACTIVE_TAB = UiUtils.openSvgIcon(\"search/activeTab\");\n\tpublic static final ImageIcon ICON_ACTIVE_TAB_SELECTED = UiUtils.openSvgIcon(\"search/activeTabSelected\");\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/IconsCache.java",
    "content": "package jadx.gui.utils;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport javax.swing.ImageIcon;\n\npublic class IconsCache {\n\n\tprivate static final Map<String, ImageIcon> SVG_ICONS = new ConcurrentHashMap<>();\n\n\tpublic static ImageIcon getSVGIcon(String name) {\n\t\treturn SVG_ICONS.computeIfAbsent(name, UiUtils::openSvgIcon);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java",
    "content": "package jadx.gui.utils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport jadx.api.JavaClass;\nimport jadx.api.JavaField;\nimport jadx.api.JavaMethod;\nimport jadx.api.JavaNode;\nimport jadx.api.JavaPackage;\nimport jadx.api.JavaVariable;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JField;\nimport jadx.gui.treemodel.JMethod;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.treemodel.JPackage;\nimport jadx.gui.treemodel.JVariable;\n\npublic class JNodeCache {\n\tprivate final JadxWrapper wrapper;\n\tprivate final Map<ICodeNodeRef, JNode> cache = new ConcurrentHashMap<>();\n\n\tpublic JNodeCache(JadxWrapper wrapper) {\n\t\tthis.wrapper = wrapper;\n\t}\n\n\tpublic JNode makeFrom(ICodeNodeRef nodeRef) {\n\t\tif (nodeRef == null) {\n\t\t\treturn null;\n\t\t}\n\t\t// don't use 'computeIfAbsent' method here, it will cause 'Recursive update' exception\n\t\tJNode jNode = cache.get(nodeRef);\n\t\tif (jNode == null || jNode.getJavaNode().getCodeNodeRef() != nodeRef) {\n\t\t\tjNode = convert(nodeRef);\n\t\t\tcache.put(nodeRef, jNode);\n\t\t}\n\t\treturn jNode;\n\t}\n\n\tpublic void put(ICodeNodeRef nodeRef, JNode jNode) {\n\t\tcache.put(nodeRef, jNode);\n\t}\n\n\tpublic void put(JavaNode javaNode, JNode jNode) {\n\t\tcache.put(javaNode.getCodeNodeRef(), jNode);\n\t}\n\n\tpublic JNode makeFrom(JavaNode javaNode) {\n\t\tif (javaNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn makeFrom(javaNode.getCodeNodeRef());\n\t}\n\n\tpublic JClass makeFrom(JavaClass javaCls) {\n\t\tif (javaCls == null) {\n\t\t\treturn null;\n\t\t}\n\t\tICodeNodeRef nodeRef = javaCls.getCodeNodeRef();\n\t\tJClass jCls = (JClass) cache.get(nodeRef);\n\t\tif (jCls == null || jCls.getCls() != javaCls) {\n\t\t\tjCls = convert(javaCls);\n\t\t\tcache.put(nodeRef, jCls);\n\t\t}\n\t\treturn jCls;\n\t}\n\n\tpublic JPackage newJPackage(JavaPackage javaPkg, boolean synthetic, boolean pkgEnabled, List<JClass> classes) {\n\t\tJPackage jPackage = new JPackage(javaPkg, pkgEnabled, classes, new ArrayList<>(), synthetic);\n\t\tput(javaPkg, jPackage);\n\t\treturn jPackage;\n\t}\n\n\tpublic void remove(JavaNode javaNode) {\n\t\tcache.remove(javaNode.getCodeNodeRef());\n\t}\n\n\tpublic void removeWholeClass(JavaClass javaCls) {\n\t\tremove(javaCls);\n\t\t/*\n\t\t * These javaCls.get...() calls require the class to be loaded, or will force it to load, generating\n\t\t * a potentially large decompilation task if needed, before throwing away that work when the class\n\t\t * is unloaded. To avoid this, which is very slow, we only bother to remove things from the cache if\n\t\t * the class is already loaded. If it's not then there either isn't going to be anything relevant in\n\t\t * the node cache or decompilation would regenerate the cache anyway.\n\t\t */\n\t\t// if (true) {\n\t\tif (!javaCls.loadingWouldRequireDecompilation()) {\n\t\t\tjavaCls.getMethods().forEach(this::remove);\n\t\t\tjavaCls.getFields().forEach(this::remove);\n\t\t\tjavaCls.getInnerClasses().forEach(this::remove);\n\t\t\tjavaCls.getInlinedClasses().forEach(this::remove);\n\t\t}\n\t}\n\n\tpublic void reset() {\n\t\tcache.clear();\n\t}\n\n\tprivate JClass convert(JavaClass cls) {\n\t\tJavaClass parentCls = cls.getDeclaringClass();\n\t\tif (parentCls == cls) {\n\t\t\treturn new JClass(cls, null, this);\n\t\t}\n\t\treturn new JClass(cls, makeFrom(parentCls), this);\n\t}\n\n\tprivate JNode convert(ICodeNodeRef nodeRef) {\n\t\tJavaNode javaNode = wrapper.getDecompiler().getJavaNodeByRef(nodeRef);\n\t\treturn convert(javaNode);\n\t}\n\n\tprivate JNode convert(JavaNode node) {\n\t\tif (node == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (node instanceof JavaClass) {\n\t\t\treturn convert((JavaClass) node);\n\t\t}\n\t\tif (node instanceof JavaMethod) {\n\t\t\treturn new JMethod((JavaMethod) node, makeFrom(node.getDeclaringClass()));\n\t\t}\n\t\tif (node instanceof JavaField) {\n\t\t\treturn new JField((JavaField) node, makeFrom(node.getDeclaringClass()));\n\t\t}\n\t\tif (node instanceof JavaVariable) {\n\t\t\tJavaVariable javaVar = (JavaVariable) node;\n\t\t\tJMethod jMth = (JMethod) makeFrom(javaVar.getMth());\n\t\t\treturn new JVariable(jMth, javaVar);\n\t\t}\n\t\tif (node instanceof JavaPackage) {\n\t\t\tthrow new JadxRuntimeException(\"Unexpected JPackage (missing from cache): \" + node);\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Unknown type for JavaNode: \" + node.getClass());\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/JumpManager.java",
    "content": "package jadx.gui.utils;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic class JumpManager {\n\t/**\n\t * Maximum number of elements to store in a jump list\n\t */\n\tprivate static final int MAX_JUMPS = 100;\n\n\t/**\n\t * This number of elements will be removed from the start if a list becomes bigger than MAX_JUMPS.\n\t * List grow most of the time, so removing should be done in big batches to not run very often.\n\t * Because of this, an effective jump history size will vary\n\t * from (MAX_JUMPS - LIST_SHRINK_COUNT) to MAX_JUMPS over time.\n\t */\n\tprivate static final int LIST_SHRINK_COUNT = 50;\n\n\tprivate final List<JumpPosition> list = new ArrayList<>(MAX_JUMPS);\n\tprivate int currentPos = 0;\n\n\tpublic void addPosition(@Nullable JumpPosition pos) {\n\t\tif (pos == null || ignoreJump(pos)) {\n\t\t\treturn;\n\t\t}\n\t\tcurrentPos++;\n\t\tif (currentPos >= list.size()) {\n\t\t\tlist.add(pos);\n\t\t\tif (list.size() >= MAX_JUMPS) {\n\t\t\t\tlist.subList(0, LIST_SHRINK_COUNT).clear();\n\t\t\t}\n\t\t\tcurrentPos = list.size() - 1;\n\t\t} else {\n\t\t\t// discard forward history after navigating back and jumping to a new place\n\t\t\tlist.set(currentPos, pos);\n\t\t\tlist.subList(currentPos + 1, list.size()).clear();\n\t\t}\n\t}\n\n\tpublic int size() {\n\t\treturn list.size();\n\t}\n\n\tprivate boolean ignoreJump(JumpPosition pos) {\n\t\tJumpPosition current = getCurrent();\n\t\tif (current == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn pos.equals(current);\n\t}\n\n\tpublic @Nullable JumpPosition getCurrent() {\n\t\tif (currentPos >= 0 && currentPos < list.size()) {\n\t\t\treturn list.get(currentPos);\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Nullable\n\tpublic JumpPosition getPrev() {\n\t\tif (currentPos == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tcurrentPos--;\n\t\treturn list.get(currentPos);\n\t}\n\n\t@Nullable\n\tpublic JumpPosition getNext() {\n\t\tint size = list.size();\n\t\tif (size == 0) {\n\t\t\tcurrentPos = 0;\n\t\t\treturn null;\n\t\t}\n\t\tint newPos = currentPos + 1;\n\t\tif (newPos >= size) {\n\t\t\tcurrentPos = size - 1;\n\t\t\treturn null;\n\t\t}\n\t\tJumpPosition position = list.get(newPos);\n\t\tif (position == null) {\n\t\t\treturn null;\n\t\t}\n\t\tcurrentPos = newPos;\n\t\treturn position;\n\t}\n\n\tpublic void reset() {\n\t\tlist.clear();\n\t\tcurrentPos = 0;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/JumpPosition.java",
    "content": "package jadx.gui.utils;\n\nimport jadx.core.utils.Utils;\nimport jadx.gui.treemodel.JNode;\n\npublic class JumpPosition {\n\tprivate final JNode node;\n\tprivate int pos;\n\n\tpublic JumpPosition(JNode node) {\n\t\tthis(node, node.getPos());\n\t}\n\n\tpublic JumpPosition(JNode node, int pos) {\n\t\tthis.node = Utils.getOrElse(node.getRootClass(), node);\n\t\tthis.pos = pos;\n\t}\n\n\tpublic int getPos() {\n\t\treturn pos;\n\t}\n\n\tpublic void setPos(int pos) {\n\t\tthis.pos = pos;\n\t}\n\n\tpublic JNode getNode() {\n\t\treturn node;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof JumpPosition)) {\n\t\t\treturn false;\n\t\t}\n\t\tJumpPosition jump = (JumpPosition) obj;\n\t\treturn pos == jump.pos && node.equals(jump.node);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn 31 * node.hashCode() + pos;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Jump{\" + node + \":\" + pos + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/LafManager.java",
    "content": "package jadx.gui.utils;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport javax.swing.UIManager;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.formdev.flatlaf.FlatDarculaLaf;\nimport com.formdev.flatlaf.FlatDarkLaf;\nimport com.formdev.flatlaf.FlatIntelliJLaf;\nimport com.formdev.flatlaf.FlatLightLaf;\nimport com.formdev.flatlaf.intellijthemes.FlatAllIJThemes;\nimport com.formdev.flatlaf.themes.FlatMacDarkLaf;\nimport com.formdev.flatlaf.themes.FlatMacLightLaf;\n\nimport jadx.gui.settings.JadxSettings;\n\npublic class LafManager {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(LafManager.class);\n\n\tpublic static final String INITIAL_THEME_NAME = FlatLightLaf.NAME;\n\n\tprivate static final Map<String, String> THEMES_MAP = initThemesMap();\n\n\tpublic static void init(JadxSettings settings) {\n\t\tString preferredThemeClass = getThemeClass(settings);\n\n\t\t// reset if settings refers to missing theme\n\t\tif (preferredThemeClass == null) {\n\t\t\tsettings.setLafTheme(INITIAL_THEME_NAME);\n\t\t\tpreferredThemeClass = getThemeClass(settings);\n\t\t}\n\n\t\tif (setupLaf(preferredThemeClass)) {\n\t\t\treturn;\n\t\t}\n\t\tsetupLaf(INITIAL_THEME_NAME);\n\t\tsettings.setLafTheme(INITIAL_THEME_NAME);\n\t\tsettings.sync();\n\t}\n\n\tpublic static boolean updateLaf(JadxSettings settings) {\n\t\treturn setupLaf(getThemeClass(settings));\n\t}\n\n\tpublic static String[] getThemes() {\n\t\treturn THEMES_MAP.keySet().toArray(new String[0]);\n\t}\n\n\tprivate static String getThemeClass(JadxSettings settings) {\n\t\treturn THEMES_MAP.get(settings.getLafTheme());\n\t}\n\n\tprivate static boolean setupLaf(String themeClass) {\n\n\t\tif (themeClass != null && !themeClass.isEmpty()) {\n\t\t\treturn applyLaf(themeClass);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static Map<String, String> initThemesMap() {\n\t\tMap<String, String> map = new LinkedHashMap<>();\n\n\t\t// default flatlaf themes\n\t\tmap.put(FlatLightLaf.NAME, FlatLightLaf.class.getName());\n\t\tmap.put(FlatDarkLaf.NAME, FlatDarkLaf.class.getName());\n\t\tmap.put(FlatMacLightLaf.NAME, FlatMacLightLaf.class.getName());\n\t\tmap.put(FlatMacDarkLaf.NAME, FlatMacDarkLaf.class.getName());\n\t\tmap.put(FlatIntelliJLaf.NAME, FlatIntelliJLaf.class.getName());\n\t\tmap.put(FlatDarculaLaf.NAME, FlatDarculaLaf.class.getName());\n\n\t\t// themes from flatlaf-intellij-themes\n\t\tfor (FlatAllIJThemes.FlatIJLookAndFeelInfo themeInfo : FlatAllIJThemes.INFOS) {\n\t\t\tmap.put(themeInfo.getName(), themeInfo.getClassName());\n\t\t}\n\t\treturn map;\n\t}\n\n\tprivate static boolean applyLaf(String theme) {\n\t\ttry {\n\t\t\tUIManager.setLookAndFeel(theme);\n\t\t\treturn true;\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to set laf to {}\", theme, e);\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/LangLocale.java",
    "content": "package jadx.gui.utils;\n\nimport java.util.Locale;\n\npublic class LangLocale {\n\tprivate Locale locale;\n\n\t// Don't remove. Used in json serialization\n\tpublic LangLocale() {\n\t}\n\n\tpublic LangLocale(Locale locale) {\n\t\tthis.locale = locale;\n\t}\n\n\tpublic LangLocale(String l, String c) {\n\t\tthis.locale = new Locale(l, c);\n\t}\n\n\tpublic Locale get() {\n\t\treturn locale;\n\t}\n\n\tpublic Locale getLocale() {\n\t\treturn locale;\n\t}\n\n\tpublic void setLocale(Locale locale) {\n\t\tthis.locale = locale;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn NLS.str(\"language.name\", this);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\treturn obj instanceof LangLocale && locale.equals(((LangLocale) obj).get());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn locale.hashCode();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/Link.java",
    "content": "package jadx.gui.utils;\n\nimport java.awt.Cursor;\nimport java.awt.Desktop;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.util.Map;\n\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JTextArea;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.commons.app.JadxSystemInfo;\n\nimport static java.awt.Desktop.Action;\n\npublic class Link extends JLabel {\n\tprivate static final long serialVersionUID = 3655322136444908178L;\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(Link.class);\n\n\tprivate String url;\n\n\tpublic Link() {\n\t\tsuper();\n\t\tinit();\n\t}\n\n\tpublic Link(String text, String url) {\n\t\tsuper(text);\n\t\tinit();\n\t\tsetUrl(url);\n\t}\n\n\tprivate void init() {\n\t\tsetCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));\n\t\tthis.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(MouseEvent e) {\n\t\t\t\tbrowse();\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic void setUrl(String url) {\n\t\tthis.url = url;\n\t\tsetToolTipText(\"Open \" + url + \" in your browser\");\n\t}\n\n\tprivate void browse() {\n\t\tif (Desktop.isDesktopSupported()) {\n\t\t\tDesktop desktop = Desktop.getDesktop();\n\t\t\tif (desktop.isSupported(Action.BROWSE)) {\n\t\t\t\ttry {\n\t\t\t\t\tdesktop.browse(new java.net.URI(url));\n\t\t\t\t\treturn;\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.debug(\"Open url error\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tif (JadxSystemInfo.IS_WINDOWS) {\n\t\t\t\tnew ProcessBuilder()\n\t\t\t\t\t\t.command(new String[] { \"rundll32\", \"url.dll,FileProtocolHandler\", url })\n\t\t\t\t\t\t.start();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (JadxSystemInfo.IS_MAC) {\n\t\t\t\tnew ProcessBuilder()\n\t\t\t\t\t\t.command(new String[] { \"open\", url })\n\t\t\t\t\t\t.start();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tMap<String, String> env = System.getenv();\n\t\t\tif (env.get(\"BROWSER\") != null) {\n\t\t\t\tnew ProcessBuilder()\n\t\t\t\t\t\t.command(new String[] { env.get(\"BROWSER\"), url })\n\t\t\t\t\t\t.start();\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.debug(\"Open url error\", e);\n\t\t}\n\t\tshowUrlDialog();\n\t}\n\n\tprivate void showUrlDialog() {\n\t\tJTextArea urlArea = new JTextArea(\"Can't open browser. Please browse to:\\n\" + url);\n\t\tJOptionPane.showMessageDialog(null, urlArea);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/NLS.java",
    "content": "package jadx.gui.utils;\n\nimport java.io.InputStreamReader;\nimport java.io.Reader;\nimport java.net.URL;\nimport java.nio.charset.StandardCharsets;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.MissingResourceException;\nimport java.util.PropertyResourceBundle;\nimport java.util.ResourceBundle;\nimport java.util.Vector;\n\nimport jadx.core.utils.exceptions.JadxRuntimeException;\n\npublic class NLS {\n\n\tprivate static final Vector<LangLocale> LANG_LOCALES = new Vector<>();\n\n\tprivate static final Map<LangLocale, ResourceBundle> LANG_LOCALES_MAP = new HashMap<>();\n\n\tprivate static final ResourceBundle FALLBACK_MESSAGES_MAP;\n\tprivate static final LangLocale LOCAL_LOCALE;\n\n\t// Use these two fields to avoid invoking Map.get() method twice.\n\tprivate static ResourceBundle localizedMessagesMap;\n\tprivate static LangLocale currentLocale;\n\n\tstatic {\n\t\tLOCAL_LOCALE = new LangLocale(Locale.getDefault());\n\n\t\tLANG_LOCALES.add(new LangLocale(\"en\", \"US\")); // As default language\n\t\tLANG_LOCALES.add(new LangLocale(\"zh\", \"CN\"));\n\t\tLANG_LOCALES.add(new LangLocale(\"zh\", \"TW\"));\n\t\tLANG_LOCALES.add(new LangLocale(\"es\", \"ES\"));\n\t\tLANG_LOCALES.add(new LangLocale(\"de\", \"DE\"));\n\t\tLANG_LOCALES.add(new LangLocale(\"ko\", \"KR\"));\n\t\tLANG_LOCALES.add(new LangLocale(\"pt\", \"BR\"));\n\t\tLANG_LOCALES.add(new LangLocale(\"ru\", \"RU\"));\n\t\tLANG_LOCALES.add(new LangLocale(\"id\", \"ID\"));\n\n\t\tLANG_LOCALES.forEach(NLS::load);\n\n\t\tLangLocale defLang = LANG_LOCALES.get(0);\n\t\tFALLBACK_MESSAGES_MAP = LANG_LOCALES_MAP.get(defLang);\n\t\tlocalizedMessagesMap = LANG_LOCALES_MAP.get(defLang);\n\t}\n\n\tprivate NLS() {\n\t}\n\n\tprivate static void load(LangLocale lang) {\n\t\tLocale locale = lang.get();\n\t\tString resName = String.format(\"i18n/Messages_%s.properties\", locale.toLanguageTag().replace('-', '_'));\n\t\tURL bundleUrl = NLS.class.getClassLoader().getResource(resName);\n\t\tif (bundleUrl == null) {\n\t\t\tthrow new JadxRuntimeException(\"Locale resource not found: \" + resName);\n\t\t}\n\t\tResourceBundle bundle;\n\t\ttry (Reader reader = new InputStreamReader(bundleUrl.openStream(), StandardCharsets.UTF_8)) {\n\t\t\tbundle = new PropertyResourceBundle(reader);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to load \" + resName, e);\n\t\t}\n\t\tLANG_LOCALES_MAP.put(lang, bundle);\n\t}\n\n\tpublic static String str(String key) {\n\t\ttry {\n\t\t\treturn localizedMessagesMap.getString(key);\n\t\t} catch (Exception e) {\n\t\t\treturn FALLBACK_MESSAGES_MAP.getString(key);\n\t\t}\n\t}\n\n\tpublic static String str(String key, Object... parameters) {\n\t\treturn String.format(str(key), parameters);\n\t}\n\n\tpublic static String str(String key, LangLocale locale) {\n\t\tResourceBundle bundle = LANG_LOCALES_MAP.get(locale);\n\t\tif (bundle != null) {\n\t\t\ttry {\n\t\t\t\treturn bundle.getString(key);\n\t\t\t} catch (MissingResourceException ignored) {\n\t\t\t\t// use fallback string\n\t\t\t}\n\t\t}\n\t\treturn FALLBACK_MESSAGES_MAP.getString(key); // definitely exists\n\t}\n\n\tpublic static void setLocale(LangLocale locale) {\n\t\tif (LANG_LOCALES_MAP.containsKey(locale)) {\n\t\t\tcurrentLocale = locale;\n\t\t} else {\n\t\t\tcurrentLocale = LANG_LOCALES.get(0);\n\t\t}\n\t\tlocalizedMessagesMap = LANG_LOCALES_MAP.get(currentLocale);\n\t}\n\n\tpublic static Vector<LangLocale> getLangLocales() {\n\t\treturn LANG_LOCALES;\n\t}\n\n\tpublic static LangLocale currentLocale() {\n\t\treturn currentLocale;\n\t}\n\n\tpublic static LangLocale defaultLocale() {\n\t\tif (LANG_LOCALES_MAP.containsKey(LOCAL_LOCALE)) {\n\t\t\treturn LOCAL_LOCALE;\n\t\t}\n\t\t// fallback to english if unsupported\n\t\treturn LANG_LOCALES.get(0);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/ObjectPool.java",
    "content": "package jadx.gui.utils;\n\nimport java.lang.ref.WeakReference;\nimport java.util.concurrent.ConcurrentLinkedQueue;\n\npublic class ObjectPool<T> {\n\n\tprivate final ConcurrentLinkedQueue<WeakReference<T>> pool = new ConcurrentLinkedQueue<>();\n\tprivate final Creator<T> creator;\n\n\tpublic interface Creator<T> {\n\t\tT create();\n\t}\n\n\tpublic ObjectPool(Creator<T> creator) {\n\t\tthis.creator = creator;\n\t}\n\n\tpublic T get() {\n\t\tT node;\n\t\tdo {\n\t\t\tWeakReference<T> wNode = pool.poll();\n\t\t\tif (wNode == null) {\n\t\t\t\treturn creator.create();\n\t\t\t}\n\t\t\tnode = wNode.get();\n\t\t} while (node == null);\n\t\treturn node;\n\t}\n\n\tpublic void put(T node) {\n\t\tpool.add(new WeakReference<>(node));\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/OverlayIcon.java",
    "content": "package jadx.gui.utils;\n\nimport java.awt.Component;\nimport java.awt.Graphics;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.swing.Icon;\n\npublic class OverlayIcon implements Icon {\n\n\tprivate final Icon icon;\n\tprivate final List<Icon> icons = new ArrayList<>(4);\n\n\tprivate static final double A = 0.8;\n\tprivate static final double B = 0.2;\n\tprivate static final double[] OVERLAY_POS = new double[] { A, B, B, B, A, A, B, A };\n\n\tpublic OverlayIcon(Icon icon) {\n\t\tthis.icon = icon;\n\t}\n\n\tpublic OverlayIcon(Icon icon, Icon... ovrIcons) {\n\t\tthis.icon = icon;\n\t\tCollections.addAll(icons, ovrIcons);\n\t}\n\n\t@Override\n\tpublic int getIconHeight() {\n\t\treturn icon.getIconHeight();\n\t}\n\n\t@Override\n\tpublic int getIconWidth() {\n\t\treturn icon.getIconWidth();\n\t}\n\n\t@Override\n\tpublic void paintIcon(Component c, Graphics g, int x, int y) {\n\t\tint w = getIconWidth();\n\t\tint h = getIconHeight();\n\n\t\ticon.paintIcon(c, g, x, y);\n\t\tint k = 0;\n\t\tfor (Icon subIcon : icons) {\n\t\t\tint dx = (int) (OVERLAY_POS[k++] * (w - subIcon.getIconWidth()));\n\t\t\tint dy = (int) (OVERLAY_POS[k++] * (h - subIcon.getIconHeight()));\n\t\t\tsubIcon.paintIcon(c, g, x + dx, y + dy);\n\t\t}\n\t}\n\n\tpublic void add(Icon icon) {\n\t\ticons.add(icon);\n\t}\n\n\tpublic void remove(Icon icon) {\n\t\ticons.remove(icon);\n\t}\n\n\tpublic void clear() {\n\t\ticons.clear();\n\t}\n\n\tpublic List<Icon> getIcons() {\n\t\treturn icons;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/PathTypeAdapter.java",
    "content": "package jadx.gui.utils;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport com.google.gson.TypeAdapter;\nimport com.google.gson.stream.JsonReader;\nimport com.google.gson.stream.JsonToken;\nimport com.google.gson.stream.JsonWriter;\n\npublic class PathTypeAdapter {\n\n\tprivate static final TypeAdapter<Path> SINGLETON = new TypeAdapter<>() {\n\t\t@Override\n\t\tpublic void write(JsonWriter out, Path value) throws IOException {\n\t\t\tif (value == null) {\n\t\t\t\tout.nullValue();\n\t\t\t} else {\n\t\t\t\tout.value(value.toAbsolutePath().toString());\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic Path read(JsonReader in) throws IOException {\n\t\t\tif (in.peek() == JsonToken.NULL) {\n\t\t\t\tin.nextNull();\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn Paths.get(in.nextString());\n\t\t}\n\t};\n\n\tpublic static TypeAdapter<Path> singleton() {\n\t\treturn SINGLETON;\n\t}\n\n\tprivate PathTypeAdapter() {\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/RectangleTypeAdapter.java",
    "content": "package jadx.gui.utils;\n\nimport java.awt.Rectangle;\nimport java.io.IOException;\n\nimport com.google.gson.TypeAdapter;\nimport com.google.gson.stream.JsonReader;\nimport com.google.gson.stream.JsonToken;\nimport com.google.gson.stream.JsonWriter;\n\npublic class RectangleTypeAdapter {\n\n\tprivate static final TypeAdapter<Rectangle> SINGLETON = new TypeAdapter<>() {\n\t\t@Override\n\t\tpublic void write(JsonWriter out, Rectangle value) throws IOException {\n\t\t\tif (value == null) {\n\t\t\t\tout.nullValue();\n\t\t\t} else {\n\t\t\t\tout.beginObject();\n\t\t\t\tout.name(\"x\").value(value.getX());\n\t\t\t\tout.name(\"y\").value(value.getY());\n\t\t\t\tout.name(\"width\").value(value.getWidth());\n\t\t\t\tout.name(\"height\").value(value.getHeight());\n\t\t\t\tout.endObject();\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic Rectangle read(JsonReader in) throws IOException {\n\t\t\tif (in.peek() == JsonToken.NULL) {\n\t\t\t\tin.nextNull();\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tin.beginObject();\n\t\t\tRectangle rectangle = new Rectangle();\n\t\t\twhile (in.hasNext()) {\n\t\t\t\tString name = in.nextName();\n\t\t\t\tswitch (name) {\n\t\t\t\t\tcase \"x\":\n\t\t\t\t\t\trectangle.x = in.nextInt();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"y\":\n\t\t\t\t\t\trectangle.y = in.nextInt();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"width\":\n\t\t\t\t\t\trectangle.width = in.nextInt();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"height\":\n\t\t\t\t\t\trectangle.height = in.nextInt();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow new IllegalArgumentException(\"Unknown field in Rectangle: \" + name);\n\t\t\t\t}\n\t\t\t}\n\t\t\tin.endObject();\n\t\t\treturn rectangle;\n\t\t}\n\t};\n\n\tpublic static TypeAdapter<Rectangle> singleton() {\n\t\treturn SINGLETON;\n\t}\n\n\tprivate RectangleTypeAdapter() {\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/RelativePathTypeAdapter.java",
    "content": "package jadx.gui.utils;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Objects;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.TypeAdapter;\nimport com.google.gson.stream.JsonReader;\nimport com.google.gson.stream.JsonToken;\nimport com.google.gson.stream.JsonWriter;\n\npublic class RelativePathTypeAdapter extends TypeAdapter<Path> {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(RelativePathTypeAdapter.class);\n\n\tprivate final Path basePath;\n\n\tpublic RelativePathTypeAdapter(Path basePath) {\n\t\tthis.basePath = Objects.requireNonNull(basePath);\n\t}\n\n\t@Override\n\tpublic void write(JsonWriter out, Path value) throws IOException {\n\t\tif (value == null) {\n\t\t\tout.nullValue();\n\t\t} else {\n\t\t\tvalue = value.toAbsolutePath().normalize();\n\t\t\tPath resultPath;\n\t\t\ttry {\n\t\t\t\tresultPath = basePath.relativize(value);\n\t\t\t} catch (IllegalArgumentException e) {\n\t\t\t\tLOG.warn(\"Unable to build a relative path to {} - using absolute path\", value);\n\t\t\t\tresultPath = value;\n\t\t\t}\n\t\t\tout.value(resultPath.toString());\n\t\t}\n\t}\n\n\t@Override\n\tpublic Path read(JsonReader in) throws IOException {\n\t\tif (in.peek() == JsonToken.NULL) {\n\t\t\tin.nextNull();\n\t\t\treturn null;\n\t\t}\n\t\tPath p = Paths.get(in.nextString());\n\t\tif (p.isAbsolute()) {\n\t\t\treturn p;\n\t\t}\n\t\treturn basePath.resolve(p);\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/SimpleListener.java",
    "content": "package jadx.gui.utils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\n\npublic class SimpleListener<T> {\n\tprivate final List<Consumer<T>> listeners = new ArrayList<>();\n\n\tpublic void sendUpdate(T data) {\n\t\tfor (Consumer<T> listener : listeners) {\n\t\t\tlistener.accept(data);\n\t\t}\n\t}\n\n\tpublic void addListener(Consumer<T> listener) {\n\t\tlisteners.add(listener);\n\t}\n\n\tpublic boolean removeListener(Consumer<T> listener) {\n\t\treturn listeners.remove(listener);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/TextStandardActions.java",
    "content": "package jadx.gui.utils;\n\nimport java.awt.Toolkit;\nimport java.awt.datatransfer.DataFlavor;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\n\nimport javax.swing.AbstractAction;\nimport javax.swing.Action;\nimport javax.swing.JPopupMenu;\nimport javax.swing.KeyStroke;\nimport javax.swing.text.JTextComponent;\nimport javax.swing.undo.UndoManager;\n\npublic class TextStandardActions {\n\n\tprivate final JTextComponent textComponent;\n\n\tprivate final JPopupMenu popup = new JPopupMenu();\n\tprivate final UndoManager undoManager;\n\n\tprivate Action undoAction;\n\tprivate Action redoAction;\n\tprivate Action cutAction;\n\tprivate Action copyAction;\n\tprivate Action pasteAction;\n\tprivate Action deleteAction;\n\tprivate Action selectAllAction;\n\n\tpublic static void attach(JTextComponent textComponent) {\n\t\tnew TextStandardActions(textComponent);\n\t}\n\n\tpublic TextStandardActions(JTextComponent textComponent) {\n\t\tthis.textComponent = textComponent;\n\t\tthis.undoManager = new UndoManager();\n\n\t\tinitActions();\n\t\taddPopupItems();\n\t\taddKeyActions();\n\n\t\tregisterListeners();\n\t}\n\n\tprivate void initActions() {\n\t\tundoAction = new AbstractAction(NLS.str(\"popup.undo\")) {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent ae) {\n\t\t\t\tif (undoManager.canUndo()) {\n\t\t\t\t\tundoManager.undo();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tredoAction = new AbstractAction(NLS.str(\"popup.redo\")) {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent ae) {\n\t\t\t\tif (undoManager.canRedo()) {\n\t\t\t\t\tundoManager.redo();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tcutAction = new AbstractAction(NLS.str(\"popup.cut\")) {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent ae) {\n\t\t\t\ttextComponent.cut();\n\t\t\t}\n\t\t};\n\t\tcopyAction = new AbstractAction(NLS.str(\"popup.copy\")) {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent ae) {\n\t\t\t\ttextComponent.copy();\n\t\t\t}\n\t\t};\n\t\tpasteAction = new AbstractAction(NLS.str(\"popup.paste\")) {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent ae) {\n\t\t\t\ttextComponent.paste();\n\t\t\t}\n\t\t};\n\t\tdeleteAction = new AbstractAction(NLS.str(\"popup.delete\")) {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent ae) {\n\t\t\t\ttextComponent.replaceSelection(\"\");\n\t\t\t}\n\t\t};\n\t\tselectAllAction = new AbstractAction(NLS.str(\"popup.select_all\")) {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent ae) {\n\t\t\t\ttextComponent.selectAll();\n\t\t\t}\n\t\t};\n\t}\n\n\tvoid addPopupItems() {\n\t\tpopup.add(undoAction);\n\t\tpopup.add(redoAction);\n\t\tpopup.addSeparator();\n\t\tpopup.add(cutAction);\n\t\tpopup.add(copyAction);\n\t\tpopup.add(pasteAction);\n\t\tpopup.add(deleteAction);\n\t\tpopup.addSeparator();\n\t\tpopup.add(selectAllAction);\n\t}\n\n\tprivate void addKeyActions() {\n\t\tKeyStroke undoKey = KeyStroke.getKeyStroke(KeyEvent.VK_Z, UiUtils.ctrlButton());\n\t\ttextComponent.getInputMap().put(undoKey, undoAction);\n\t\tKeyStroke redoKey = KeyStroke.getKeyStroke(KeyEvent.VK_R, UiUtils.ctrlButton());\n\t\ttextComponent.getInputMap().put(redoKey, redoAction);\n\t}\n\n\tprivate void registerListeners() {\n\t\ttextComponent.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseReleased(MouseEvent e) {\n\t\t\t\tif (e.getButton() == 3 && e.getSource() == textComponent) {\n\t\t\t\t\tprocess(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\ttextComponent.getDocument().addUndoableEditListener(event -> undoManager.addEdit(event.getEdit()));\n\t}\n\n\tprivate void process(MouseEvent e) {\n\t\ttextComponent.requestFocus();\n\n\t\tboolean enabled = textComponent.isEnabled();\n\t\tboolean editable = textComponent.isEditable();\n\t\tboolean nonempty = !(textComponent.getText() == null || textComponent.getText().isEmpty());\n\t\tboolean marked = textComponent.getSelectedText() != null;\n\t\tboolean pasteAvailable = Toolkit.getDefaultToolkit().getSystemClipboard()\n\t\t\t\t.getContents(null).isDataFlavorSupported(DataFlavor.stringFlavor);\n\n\t\tundoAction.setEnabled(enabled && editable && undoManager.canUndo());\n\t\tredoAction.setEnabled(enabled && editable && undoManager.canRedo());\n\t\tcutAction.setEnabled(enabled && editable && marked);\n\t\tcopyAction.setEnabled(enabled && marked);\n\t\tpasteAction.setEnabled(enabled && editable && pasteAvailable);\n\t\tdeleteAction.setEnabled(enabled && editable && marked);\n\t\tselectAllAction.setEnabled(enabled && nonempty);\n\n\t\tint nx = e.getX();\n\t\tif (nx > 500) {\n\t\t\tnx = nx - popup.getSize().width;\n\t\t}\n\t\tpopup.show(e.getComponent(), nx, e.getY() - popup.getSize().height);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/UiUtils.java",
    "content": "package jadx.gui.utils;\n\nimport java.awt.Color;\nimport java.awt.Component;\nimport java.awt.Image;\nimport java.awt.MouseInfo;\nimport java.awt.Point;\nimport java.awt.Rectangle;\nimport java.awt.Toolkit;\nimport java.awt.Window;\nimport java.awt.datatransfer.Clipboard;\nimport java.awt.datatransfer.StringSelection;\nimport java.awt.datatransfer.Transferable;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.InputEvent;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.MouseEvent;\nimport java.io.File;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Timer;\nimport java.util.TimerTask;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport javax.swing.AbstractAction;\nimport javax.swing.Action;\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\nimport javax.swing.JComponent;\nimport javax.swing.JOptionPane;\nimport javax.swing.JTextField;\nimport javax.swing.JTree;\nimport javax.swing.KeyStroke;\nimport javax.swing.RootPaneContainer;\nimport javax.swing.SwingUtilities;\nimport javax.swing.tree.TreeNode;\nimport javax.swing.tree.TreePath;\n\nimport org.intellij.lang.annotations.MagicConstant;\nimport org.jetbrains.annotations.TestOnly;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.formdev.flatlaf.extras.FlatSVGIcon;\n\nimport jadx.commons.app.JadxCommonEnv;\nimport jadx.commons.app.JadxSystemInfo;\nimport jadx.core.dex.info.AccessInfo;\nimport jadx.core.dex.instructions.args.ArgType;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.jobs.ITaskProgress;\nimport jadx.gui.ui.codearea.AbstractCodeArea;\n\npublic class UiUtils {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(UiUtils.class);\n\n\tpublic static final boolean JADX_GUI_DEBUG = JadxCommonEnv.getBool(\"JADX_GUI_DEBUG\", false);\n\n\t/**\n\t * The minimum about of memory in bytes we are trying to keep free, otherwise the application may\n\t * run out of heap\n\t * which ends up in a Java garbage collector running \"amok\" (CPU utilization 100% for each core and\n\t * the UI is\n\t * not responsive).\n\t * <p>\n\t * We can calculate and store this value here as the maximum heap is fixed for each JVM instance\n\t * and can't be changed at runtime.\n\t */\n\tpublic static final long MIN_FREE_MEMORY = calculateMinFreeMemory();\n\n\tpublic static final Runnable EMPTY_RUNNABLE = new Runnable() {\n\t\t@Override\n\t\tpublic void run() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"EMPTY_RUNNABLE\";\n\t\t}\n\t};\n\n\tprivate static final ExecutorService BACKGROUND_THREAD = Executors.newSingleThreadExecutor(Utils.simpleThreadFactory(\"utils-bg\"));\n\n\tprivate UiUtils() {\n\t}\n\n\tpublic static ImageIcon openSvgIcon(String name) {\n\t\tString iconPath = \"icons/\" + name + \".svg\";\n\t\tFlatSVGIcon icon = new FlatSVGIcon(iconPath);\n\t\tboolean found;\n\t\ttry {\n\t\t\tfound = icon.hasFound();\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to load icon: \" + iconPath, e);\n\t\t}\n\t\tif (!found) {\n\t\t\tthrow new JadxRuntimeException(\"Icon not found: \" + iconPath);\n\t\t}\n\t\treturn icon;\n\t}\n\n\tpublic static ImageIcon openIcon(String name) {\n\t\tString iconPath = \"/icons-16/\" + name + \".png\";\n\t\tURL resource = UiUtils.class.getResource(iconPath);\n\t\tif (resource == null) {\n\t\t\tthrow new JadxRuntimeException(\"Icon not found: \" + iconPath);\n\t\t}\n\t\treturn new ImageIcon(resource);\n\t}\n\n\tpublic static Image openImage(String path) {\n\t\tURL resource = UiUtils.class.getResource(path);\n\t\tif (resource == null) {\n\t\t\tthrow new JadxRuntimeException(\"Image not found: \" + path);\n\t\t}\n\t\treturn Toolkit.getDefaultToolkit().createImage(resource);\n\t}\n\n\tpublic static void addKeyBinding(JComponent comp, KeyStroke key, String id, Runnable action) {\n\t\taddKeyBinding(comp, key, id, new AbstractAction() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\taction.run();\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic static void addKeyBinding(JComponent comp, KeyStroke key, String id, Action action) {\n\t\tcomp.getInputMap().put(key, id);\n\t\tcomp.getActionMap().put(id, action);\n\t}\n\n\tpublic static void removeKeyBinding(JComponent comp, KeyStroke key, String id) {\n\t\tcomp.getInputMap().remove(key);\n\t\tcomp.getActionMap().remove(id);\n\t}\n\n\tpublic static String typeFormat(String name, ArgType type) {\n\t\treturn name + \" \" + typeStr(type);\n\t}\n\n\tpublic static String typeFormatHtml(String name, ArgType type) {\n\t\treturn wrapHtml(escapeHtml(name) + ' ' + fadeHtml(escapeHtml(typeStr(type))));\n\t}\n\n\tpublic static String fadeHtml(String htmlStr) {\n\t\treturn \"<span style='color:#888888;'>\" + htmlStr + \"</span>\"; // TODO: get color from theme\n\t}\n\n\tpublic static String wrapHtml(String htmlStr) {\n\t\treturn \"<html><body><nobr>\" + htmlStr + \"</nobr></body></html>\";\n\t}\n\n\tpublic static String escapeHtml(String str) {\n\t\treturn str.replace(\"<\", \"&lt;\").replace(\">\", \"&gt;\");\n\t}\n\n\tprivate static final String CUT_STR_REPLACE = \"...\";\n\n\tpublic static String limitStringLength(String str, int maxLength) {\n\t\tif (str.length() <= maxLength) {\n\t\t\treturn str;\n\t\t}\n\t\tchar fileSepChar = File.separatorChar;\n\t\tint firstFileSep = str.indexOf(fileSepChar);\n\t\tif (firstFileSep != -1) {\n\t\t\t// remove path parts\n\t\t\tint lastFileSep = str.lastIndexOf(fileSepChar);\n\t\t\tif (firstFileSep == lastFileSep) {\n\t\t\t\t// single path char => cut before\n\t\t\t\tstr = CUT_STR_REPLACE + str.substring(lastFileSep - 1);\n\t\t\t} else {\n\t\t\t\t// cut middle\n\t\t\t\tstr = str.substring(0, firstFileSep + 1) + CUT_STR_REPLACE + str.substring(lastFileSep);\n\t\t\t}\n\t\t\tif (str.length() < maxLength) {\n\t\t\t\treturn str;\n\t\t\t}\n\t\t}\n\t\t// cut end by default\n\t\treturn str.substring(0, maxLength - CUT_STR_REPLACE.length()) + CUT_STR_REPLACE;\n\t}\n\n\tpublic static String typeStr(ArgType type) {\n\t\tif (type == null) {\n\t\t\treturn \"null\";\n\t\t}\n\t\tif (type.isObject()) {\n\t\t\tif (type.isGenericType()) {\n\t\t\t\treturn type.getObject();\n\t\t\t}\n\t\t\tArgType wt = type.getWildcardType();\n\t\t\tif (wt != null) {\n\t\t\t\tArgType.WildcardBound bound = type.getWildcardBound();\n\t\t\t\tif (bound == ArgType.WildcardBound.UNBOUND) {\n\t\t\t\t\treturn bound.getStr();\n\t\t\t\t}\n\t\t\t\treturn bound.getStr() + typeStr(wt);\n\t\t\t}\n\t\t\tString objName = objectShortName(type.getObject());\n\t\t\tArgType outerType = type.getOuterType();\n\t\t\tif (outerType != null) {\n\t\t\t\treturn typeStr(outerType) + '.' + objName;\n\t\t\t}\n\t\t\tList<ArgType> genericTypes = type.getGenericTypes();\n\t\t\tif (genericTypes != null) {\n\t\t\t\tString generics = Utils.listToString(genericTypes, \", \", UiUtils::typeStr);\n\t\t\t\treturn objName + '<' + generics + '>';\n\t\t\t}\n\t\t\treturn objName;\n\t\t}\n\t\tif (type.isArray()) {\n\t\t\treturn typeStr(type.getArrayElement()) + \"[]\";\n\t\t}\n\t\treturn type.toString();\n\t}\n\n\tprivate static String objectShortName(String obj) {\n\t\tint dot = obj.lastIndexOf('.');\n\t\tif (dot != -1) {\n\t\t\treturn obj.substring(dot + 1);\n\t\t}\n\t\treturn obj;\n\t}\n\n\tpublic static OverlayIcon makeIcon(AccessInfo af, Icon pub, Icon pri, Icon pro, Icon def) {\n\t\tIcon icon;\n\t\tif (af.isPublic()) {\n\t\t\ticon = pub;\n\t\t} else if (af.isPrivate()) {\n\t\t\ticon = pri;\n\t\t} else if (af.isProtected()) {\n\t\t\ticon = pro;\n\t\t} else {\n\t\t\ticon = def;\n\t\t}\n\t\tOverlayIcon overIcon = new OverlayIcon(icon);\n\t\tif (af.isFinal()) {\n\t\t\toverIcon.add(Icons.FINAL);\n\t\t}\n\t\tif (af.isStatic()) {\n\t\t\toverIcon.add(Icons.STATIC);\n\t\t}\n\t\treturn overIcon;\n\t}\n\n\t/**\n\t * @return 20% of the maximum heap size limited to 512 MB (bytes)\n\t */\n\tpublic static long calculateMinFreeMemory() {\n\t\tRuntime runtime = Runtime.getRuntime();\n\t\tlong minFree = (long) (runtime.maxMemory() * 0.2);\n\t\treturn Math.min(minFree, 512 * 1024L * 1024L);\n\t}\n\n\tpublic static boolean isFreeMemoryAvailable() {\n\t\tRuntime runtime = Runtime.getRuntime();\n\t\tlong maxMemory = runtime.maxMemory();\n\t\tlong totalFree = runtime.freeMemory() + (maxMemory - runtime.totalMemory());\n\t\treturn totalFree > MIN_FREE_MEMORY;\n\t}\n\n\tpublic static String memoryInfo() {\n\t\tRuntime runtime = Runtime.getRuntime();\n\t\tlong maxMemory = runtime.maxMemory();\n\t\tlong allocatedMemory = runtime.totalMemory();\n\t\tlong freeMemory = runtime.freeMemory();\n\n\t\treturn \"heap: \" + format(allocatedMemory - freeMemory)\n\t\t\t\t+ \", allocated: \" + format(allocatedMemory)\n\t\t\t\t+ \", free: \" + format(freeMemory)\n\t\t\t\t+ \", total free: \" + format(freeMemory + maxMemory - allocatedMemory)\n\t\t\t\t+ \", max: \" + format(maxMemory);\n\t}\n\n\tprivate static String format(long mem) {\n\t\treturn (long) (mem / (double) (1024L * 1024L)) + \"MB\";\n\t}\n\n\tpublic static void setClipboardString(String text) {\n\t\ttry {\n\t\t\tClipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();\n\t\t\tTransferable transferable = new StringSelection(text);\n\t\t\tclipboard.setContents(transferable, null);\n\t\t\tLOG.debug(\"String '{}' copied to clipboard\", text);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed copy string '{}' to clipboard\", text, e);\n\t\t}\n\t}\n\n\tpublic static void setWindowIcons(Window window) {\n\t\tList<Image> icons = new ArrayList<>();\n\t\ticons.add(UiUtils.openImage(\"/logos/jadx-logo-16px.png\"));\n\t\ticons.add(UiUtils.openImage(\"/logos/jadx-logo-32px.png\"));\n\t\ticons.add(UiUtils.openImage(\"/logos/jadx-logo-48px.png\"));\n\t\ticons.add(UiUtils.openImage(\"/logos/jadx-logo.png\"));\n\t\twindow.setIconImages(icons);\n\t}\n\n\tpublic static final int CTRL_BNT_KEY = getCtrlButton();\n\n\t@SuppressWarnings(\"deprecation\")\n\t@MagicConstant(flagsFromClass = InputEvent.class)\n\tprivate static int getCtrlButton() {\n\t\tif (JadxSystemInfo.IS_MAC) {\n\t\t\treturn Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();\n\t\t} else {\n\t\t\treturn InputEvent.CTRL_DOWN_MASK;\n\t\t}\n\t}\n\n\t@MagicConstant(flagsFromClass = InputEvent.class)\n\tpublic static int ctrlButton() {\n\t\treturn CTRL_BNT_KEY;\n\t}\n\n\tpublic static boolean isCtrlDown(KeyEvent keyEvent) {\n\t\treturn keyEvent.getModifiersEx() == CTRL_BNT_KEY;\n\t}\n\n\tpublic static <T extends Window & RootPaneContainer> void addEscapeShortCutToDispose(T window) {\n\t\tKeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);\n\t\twindow.getRootPane().registerKeyboardAction(e -> window.dispose(), stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);\n\t}\n\n\t/**\n\t * Get closest offset at mouse position\n\t *\n\t * @return -1 on error\n\t */\n\t@SuppressWarnings(\"deprecation\")\n\tpublic static int getOffsetAtMousePosition(AbstractCodeArea codeArea) {\n\t\ttry {\n\t\t\tPoint mousePos = getMousePosition(codeArea);\n\t\t\treturn codeArea.viewToModel(mousePos);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to get offset at mouse position\", e);\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\tpublic static Point getMousePosition(Component comp) {\n\t\tPoint pos = MouseInfo.getPointerInfo().getLocation();\n\t\tSwingUtilities.convertPointFromScreen(pos, comp);\n\t\treturn pos;\n\t}\n\n\tpublic static TreeNode getTreeNodeUnderMouse(JTree tree, MouseEvent mouseEvent) {\n\t\tTreePath path = tree.getClosestPathForLocation(mouseEvent.getX(), mouseEvent.getY());\n\t\tif (path == null) {\n\t\t\treturn null;\n\t\t}\n\t\t// allow 'closest' path only at the right of the item row\n\t\tRectangle pathBounds = tree.getPathBounds(path);\n\t\tif (pathBounds != null) {\n\t\t\tint y = mouseEvent.getY();\n\t\t\tif (y < pathBounds.y || y > (pathBounds.y + pathBounds.height)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (mouseEvent.getX() < pathBounds.x) {\n\t\t\t\t// exclude expand/collapse events\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tObject obj = path.getLastPathComponent();\n\t\tif (obj instanceof TreeNode) {\n\t\t\ttree.setSelectionPath(path);\n\t\t\treturn (TreeNode) obj;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static void showMessageBox(Component parent, String msg) {\n\t\tJOptionPane.showMessageDialog(parent, msg);\n\t}\n\n\tpublic static void errorMessage(Component parent, String message) {\n\t\terrorMessage(parent, NLS.str(\"message.errorTitle\"), message);\n\t}\n\n\tpublic static void errorMessage(Component parent, String title, String message) {\n\t\tLOG.error(message);\n\t\tJOptionPane.showMessageDialog(parent, message, title, JOptionPane.ERROR_MESSAGE);\n\t}\n\n\tpublic static void copyToClipboard(String text) {\n\t\tif (StringUtils.isEmpty(text)) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tClipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();\n\t\t\tStringSelection selection = new StringSelection(text);\n\t\t\tclipboard.setContents(selection, selection);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed copy text to clipboard\", e);\n\t\t}\n\t}\n\n\t/**\n\t * Owner field in Clipboard class can store reference to CodeArea.\n\t * This prevents from garbage collection whole jadx object tree and cause memory leak.\n\t * Trying to lost ownership by new empty selection.\n\t */\n\tpublic static void resetClipboardOwner() {\n\t\ttry {\n\t\t\tClipboard clipboard = Toolkit.getDefaultToolkit().getSystemSelection();\n\t\t\tif (clipboard != null) {\n\t\t\t\tStringSelection selection = new StringSelection(\"\");\n\t\t\t\tclipboard.setContents(selection, selection);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to reset clipboard owner\", e);\n\t\t}\n\t}\n\n\tpublic static int calcProgress(ITaskProgress taskProgress) {\n\t\treturn calcProgress(taskProgress.progress(), taskProgress.total());\n\t}\n\n\tpublic static int calcProgress(long done, long total) {\n\t\tif (done > total) {\n\t\t\tLOG.debug(\"Task progress has invalid values: done={}, total={}\", done, total);\n\t\t\treturn 100;\n\t\t}\n\t\treturn Math.round(done * 100 / (float) total);\n\t}\n\n\tpublic static void sleep(int ms) {\n\t\ttry {\n\t\t\tThread.sleep(ms);\n\t\t} catch (InterruptedException e) {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tpublic static void uiRun(Runnable runnable) {\n\t\tSwingUtilities.invokeLater(runnable);\n\t}\n\n\tpublic static void uiRunAndWait(Runnable runnable) {\n\t\tif (SwingUtilities.isEventDispatchThread()) {\n\t\t\trunnable.run();\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tSwingUtilities.invokeAndWait(runnable);\n\t\t} catch (InterruptedException e) {\n\t\t\tLOG.warn(\"UI thread interrupted, runnable: {}\", runnable, e);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\t/**\n\t * Run task in background thread.\n\t * Uses single thread, so all tasks are ordered.\n\t */\n\tpublic static void bgRun(Runnable runnable) {\n\t\tBACKGROUND_THREAD.execute(runnable);\n\t}\n\n\tpublic static void uiThreadGuard() {\n\t\tif (JADX_GUI_DEBUG && !SwingUtilities.isEventDispatchThread()) {\n\t\t\tLOG.warn(\"Expect UI thread, got: {}\", Thread.currentThread(), new JadxRuntimeException());\n\t\t}\n\t}\n\n\tpublic static void notUiThreadGuard() {\n\t\tif (JADX_GUI_DEBUG && SwingUtilities.isEventDispatchThread()) {\n\t\t\tLOG.warn(\"Expect background thread, got: {}\", Thread.currentThread(), new JadxRuntimeException());\n\t\t}\n\t}\n\n\t@TestOnly\n\tpublic static void debugTimer(int periodInSeconds, Runnable action) {\n\t\tif (!LOG.isDebugEnabled()) {\n\t\t\treturn;\n\t\t}\n\t\tTimer timer = new Timer();\n\t\ttimer.scheduleAtFixedRate(new TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\taction.run();\n\t\t\t}\n\t\t}, 0, periodInSeconds * 1000L);\n\t}\n\n\t@TestOnly\n\tpublic static void printStackTrace(String label) {\n\t\tLOG.debug(\"StackTrace: {}\", label, new Exception(label));\n\t}\n\n\tpublic static boolean isDarkTheme(Color background) {\n\t\tdouble brightness = (background.getRed() * 0.299\n\t\t\t\t+ background.getGreen() * 0.587\n\t\t\t\t+ background.getBlue() * 0.114) / 255;\n\t\treturn brightness < 0.5;\n\t}\n\n\t/**\n\t * Adjusts the brightness of a given {@code Color} object without altering its hue or saturation.\n\t *\n\t * <p>\n\t * This method converts the input RGB color to the HSB (Hue, Saturation, Brightness) color model,\n\t * multiplies its brightness component by the specified {@code factor}, and then converts it back\n\t * to a new RGB {@code Color} object.\n\t * </p>\n\t *\n\t * <p>\n\t * The new brightness value is capped at {@code 1.0f} (maximum HSB brightness) to prevent\n\t * colors from becoming invalid or exceeding full brightness.\n\t * </p>\n\t *\n\t * How to use:\n\t * <ul>\n\t * <li>To make a color **brighter**: Use a {@code factor} greater than {@code 1.0f} (e.g.,\n\t * {@code 1.2f}, {@code 1.5f}).</li>\n\t * <li>To make a color **darker**: Use a {@code factor} less than {@code 1.0f} (e.g., {@code 0.8f},\n\t * {@code 0.5f}).</li>\n\t * <li>To keep the brightness **unchanged**: Use a {@code factor} of {@code 1.0f}.</li>\n\t * </ul>\n\t *\n\t * <pre>{@code\n\t * // Example usage:\n\t * Color originalColor = Color.BLUE;\n\t *\n\t * // Make the blue color 50% brighter (factor 1.5)\n\t * Color brighterBlue = adjustBrightness(originalColor, 1.5f);\n\t *\n\t * // Make the blue color 30% darker (factor 0.7)\n\t * Color darkerBlue = adjustBrightness(originalColor, 0.7f);\n\t *\n\t * // Get the brightest possible version of the color (will cap at 1.0 brightness)\n\t * Color maxBrightnessBlue = adjustBrightness(originalColor, 10.0f);\n\t *\n\t * // Get a very dark, almost black version\n\t * Color veryDarkBlue = adjustBrightness(originalColor, 0.1f);\n\t * }</pre>\n\t *\n\t * @param color  The original {@code Color} object whose brightness needs to be adjusted.\n\t * @param factor The multiplier for the brightness.\n\t * @return A new {@code Color} object with the adjusted brightness.\n\t * @see java.awt.Color#RGBtoHSB(int, int, int, float[])\n\t * @see java.awt.Color#getHSBColor(float, float, float)\n\t */\n\tpublic static Color adjustBrightness(Color color, float factor) {\n\t\tfloat[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);\n\t\thsb[2] = Math.min(1.0f, hsb[2] * factor); // Adjust brightness\n\t\treturn Color.getHSBColor(hsb[0], hsb[1], hsb[2]);\n\t}\n\n\tpublic static void highlightAsErrorField(final JTextField field, boolean isError) {\n\t\tif (isError) {\n\t\t\tfield.putClientProperty(\"JComponent.outline\", \"error\");\n\t\t} else {\n\t\t\tfield.putClientProperty(\"JComponent.outline\", \"\");\n\t\t}\n\t\tfield.repaint();\n\t}\n\n\tpublic static boolean nearlyEqual(float a, float b) {\n\t\treturn Math.abs(a - b) < 1E-6f;\n\t}\n\n\t// Formats a string to be in a .DOT node\n\tpublic static String toDotNodeName(String fullName) {\n\t\tString newName = fullName.replace(\"<\", \"\\\\<\");\n\t\tnewName = newName.replace(\">\", \"\\\\>\");\n\t\treturn newName;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/cache/ValueCache.java",
    "content": "package jadx.gui.utils.cache;\n\nimport java.util.function.Function;\n\n/**\n * Simple store for values depending on 'key' object.\n *\n * @param <K> key object type\n * @param <V> stored object type\n */\npublic class ValueCache<K, V> {\n\tprivate K key;\n\tprivate V value;\n\n\t/**\n\t * Return a stored object if key not changed, load a new object overwise.\n\t */\n\tpublic synchronized V get(K requestKey, Function<K, V> loadFunc) {\n\t\tif (key != null && key.equals(requestKey)) {\n\t\t\treturn value;\n\t\t}\n\t\tV newValue = loadFunc.apply(requestKey);\n\t\tkey = requestKey;\n\t\tvalue = newValue;\n\t\treturn newValue;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/dbg/UIWatchDog.java",
    "content": "package jadx.gui.utils.dbg;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport javax.swing.SwingUtilities;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.utils.UiUtils;\n\n/**\n * Watch for UI thread state, if it stuck log a warning with stacktrace\n */\npublic class UIWatchDog {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(UIWatchDog.class);\n\n\tprivate static final int UI_MAX_DELAY_MS = 200;\n\tprivate static final int CHECK_INTERVAL_MS = 50;\n\n\tpublic static boolean onStart() {\n\t\tUiUtils.uiRunAndWait(UIWatchDog::toggle);\n\t\treturn INSTANCE.isEnabled();\n\t}\n\n\tpublic static synchronized void toggle() {\n\t\tif (SwingUtilities.isEventDispatchThread()) {\n\t\t\tINSTANCE.toggleState(Thread.currentThread());\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(\"This method should be called in UI thread\");\n\t\t}\n\t}\n\n\tprivate static final UIWatchDog INSTANCE = new UIWatchDog();\n\n\tprivate final AtomicBoolean enabled = new AtomicBoolean(false);\n\tprivate final ExecutorService executor = Executors.newSingleThreadExecutor();\n\tprivate Future<?> taskFuture;\n\n\tprivate UIWatchDog() {\n\t\t// singleton\n\t}\n\n\tprivate void toggleState(Thread uiThread) {\n\t\tif (enabled.get()) {\n\t\t\t// stop\n\t\t\tenabled.set(false);\n\t\t\tif (taskFuture != null) {\n\t\t\t\ttry {\n\t\t\t\t\ttaskFuture.get(CHECK_INTERVAL_MS * 5, TimeUnit.MILLISECONDS);\n\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\tLOG.warn(\"Stopping UI watchdog error\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// start\n\t\t\tenabled.set(true);\n\t\t\ttaskFuture = executor.submit(() -> start(uiThread));\n\t\t}\n\t}\n\n\tprivate boolean isEnabled() {\n\t\treturn enabled.get();\n\t}\n\n\t@SuppressWarnings(\"BusyWait\")\n\tprivate void start(Thread uiThread) {\n\t\tLOG.debug(\"UI watchdog started\");\n\t\ttry {\n\t\t\tException e = new JadxRuntimeException(\"at\");\n\t\t\tTimeMeasure tm = new TimeMeasure();\n\t\t\tboolean stuck = false;\n\t\t\tlong reportTime = 0;\n\t\t\twhile (enabled.get()) {\n\t\t\t\tif (uiThread.getState() == Thread.State.TIMED_WAITING) {\n\t\t\t\t\tif (!stuck) {\n\t\t\t\t\t\ttm.start();\n\t\t\t\t\t\tstuck = true;\n\t\t\t\t\t\treportTime = UI_MAX_DELAY_MS;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttm.end();\n\t\t\t\t\t\tlong time = tm.getTime();\n\t\t\t\t\t\tif (time > reportTime) {\n\t\t\t\t\t\t\te.setStackTrace(uiThread.getStackTrace());\n\t\t\t\t\t\t\tLOG.warn(\"UI events thread stuck for {}ms\", time, e);\n\t\t\t\t\t\t\treportTime += UI_MAX_DELAY_MS;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tstuck = false;\n\t\t\t\t}\n\t\t\t\tThread.sleep(CHECK_INTERVAL_MS);\n\t\t\t}\n\t\t} catch (Throwable e) {\n\t\t\tLOG.error(\"UI watchdog fail\", e);\n\t\t}\n\t\tLOG.debug(\"UI watchdog stopped\");\n\t}\n\n\tprivate static final class TimeMeasure {\n\t\tprivate long start;\n\t\tprivate long end;\n\n\t\tpublic void start() {\n\t\t\tstart = System.currentTimeMillis();\n\t\t}\n\n\t\tpublic void end() {\n\t\t\tend = System.currentTimeMillis();\n\t\t}\n\n\t\tpublic long getTime() {\n\t\t\treturn end - start;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/files/JadxFiles.java",
    "content": "package jadx.gui.utils.files;\n\nimport java.nio.file.Path;\n\nimport jadx.commons.app.JadxCommonFiles;\n\npublic class JadxFiles {\n\n\tprivate static final Path CONFIG_DIR = JadxCommonFiles.getConfigDir();\n\tpublic static final Path GUI_CONF = CONFIG_DIR.resolve(\"gui.json\");\n\tpublic static final Path CACHES_LIST = CONFIG_DIR.resolve(\"caches.json\");\n\n\tpublic static final Path CACHE_DIR = JadxCommonFiles.getCacheDir();\n\tpublic static final Path PROJECTS_CACHE_DIR = CACHE_DIR.resolve(\"projects\");\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/fileswatcher/FilesWatcher.java",
    "content": "package jadx.gui.utils.fileswatcher;\n\nimport java.io.IOException;\nimport java.nio.file.FileSystems;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.SimpleFileVisitor;\nimport java.nio.file.WatchEvent;\nimport java.nio.file.WatchKey;\nimport java.nio.file.WatchService;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.function.BiConsumer;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.Utils;\n\nimport static java.nio.file.LinkOption.NOFOLLOW_LINKS;\nimport static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;\nimport static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;\nimport static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;\nimport static java.nio.file.StandardWatchEventKinds.OVERFLOW;\n\npublic class FilesWatcher {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(FilesWatcher.class);\n\n\tprivate final WatchService watcher = FileSystems.getDefault().newWatchService();\n\tprivate final Map<WatchKey, Path> keys = new HashMap<>();\n\tprivate final Map<Path, Set<Path>> files = new HashMap<>();\n\tprivate final AtomicBoolean cancel = new AtomicBoolean(false);\n\tprivate final BiConsumer<Path, WatchEvent.Kind<Path>> listener;\n\n\tpublic FilesWatcher(List<Path> paths, BiConsumer<Path, WatchEvent.Kind<Path>> listener) throws IOException {\n\t\tthis.listener = listener;\n\t\tfor (Path path : paths) {\n\t\t\tif (Files.isDirectory(path, NOFOLLOW_LINKS)) {\n\t\t\t\tregisterDirs(path);\n\t\t\t} else {\n\t\t\t\tPath parentDir = path.toAbsolutePath().getParent();\n\t\t\t\tregister(parentDir);\n\t\t\t\tfiles.merge(parentDir, Collections.singleton(path), Utils::mergeSets);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void cancel() {\n\t\tcancel.set(true);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void watch() {\n\t\tcancel.set(false);\n\t\tLOG.debug(\"File watcher started for {} dirs\", keys.size());\n\t\twhile (!cancel.get()) {\n\t\t\tWatchKey key;\n\t\t\ttry {\n\t\t\t\tkey = watcher.take();\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\tLOG.debug(\"File watcher interrupted\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tPath dir = keys.get(key);\n\t\t\tif (dir == null) {\n\t\t\t\tLOG.warn(\"Unknown directory key: {}\", key);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (WatchEvent<?> event : key.pollEvents()) {\n\t\t\t\tif (cancel.get() || Thread.interrupted()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tWatchEvent.Kind<?> kind = event.kind();\n\t\t\t\tif (kind == OVERFLOW) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tPath fileName = ((WatchEvent<Path>) event).context();\n\t\t\t\tPath path = dir.resolve(fileName);\n\n\t\t\t\tSet<Path> files = this.files.get(dir);\n\t\t\t\tif (files == null || files.contains(path)) {\n\t\t\t\t\tlistener.accept(path, (WatchEvent.Kind<Path>) kind);\n\t\t\t\t}\n\t\t\t\tif (kind == ENTRY_CREATE) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (Files.isDirectory(path, NOFOLLOW_LINKS)) {\n\t\t\t\t\t\t\tregisterDirs(path);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLOG.warn(\"Failed to update directory watch: {}\", path, e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tboolean valid = key.reset();\n\t\t\tif (!valid) {\n\t\t\t\tkeys.remove(key);\n\t\t\t\tif (keys.isEmpty()) {\n\t\t\t\t\tLOG.debug(\"File watcher stopped: all watch keys removed\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void registerDirs(Path start) throws IOException {\n\t\tFiles.walkFileTree(start, new SimpleFileVisitor<Path>() {\n\t\t\t@Override\n\t\t\tpublic FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {\n\t\t\t\tregister(dir);\n\t\t\t\treturn FileVisitResult.CONTINUE;\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void register(Path dir) throws IOException {\n\t\tWatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);\n\t\tkeys.put(key, dir);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/fileswatcher/LiveReloadWorker.java",
    "content": "package jadx.gui.utils.fileswatcher;\n\nimport java.nio.file.Path;\nimport java.nio.file.WatchEvent;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.reactivex.rxjava3.processors.PublishProcessor;\n\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.UiUtils;\n\npublic class LiveReloadWorker {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(LiveReloadWorker.class);\n\n\tprivate final MainWindow mainWindow;\n\tprivate final PublishProcessor<Path> processor;\n\tprivate volatile boolean started = false;\n\tprivate ExecutorService executor;\n\tprivate FilesWatcher watcher;\n\n\t@SuppressWarnings(\"ResultOfMethodCallIgnored\")\n\tpublic LiveReloadWorker(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t\tthis.processor = PublishProcessor.create();\n\t\tthis.processor\n\t\t\t\t.debounce(1, TimeUnit.SECONDS)\n\t\t\t\t.subscribe(path -> {\n\t\t\t\t\tLOG.debug(\"Reload triggered\");\n\t\t\t\t\tUiUtils.uiRun(mainWindow::reopen);\n\t\t\t\t});\n\t}\n\n\tpublic boolean isStarted() {\n\t\treturn started;\n\t}\n\n\tpublic synchronized void updateState(boolean enabled) {\n\t\tif (this.started == enabled) {\n\t\t\treturn;\n\t\t}\n\t\tif (enabled) {\n\t\t\tLOG.debug(\"Starting live reload worker\");\n\t\t\tstart();\n\t\t} else {\n\t\t\tLOG.debug(\"Stopping live reload worker\");\n\t\t\tstop();\n\t\t}\n\t}\n\n\tprivate void onUpdate(Path path, WatchEvent.Kind<Path> pathKind) {\n\t\tLOG.debug(\"Path updated: {}\", path);\n\t\tprocessor.onNext(path);\n\t}\n\n\tprivate synchronized void start() {\n\t\ttry {\n\t\t\twatcher = new FilesWatcher(mainWindow.getProject().getFilePaths(), this::onUpdate);\n\t\t\texecutor = Executors.newSingleThreadExecutor();\n\t\t\tstarted = true;\n\t\t\texecutor.submit(watcher::watch);\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to start live reload worker\", e);\n\t\t\tresetState();\n\t\t}\n\t}\n\n\tprivate synchronized void stop() {\n\t\ttry {\n\t\t\twatcher.cancel();\n\t\t\texecutor.shutdownNow();\n\t\t\tboolean canceled = executor.awaitTermination(5, TimeUnit.SECONDS);\n\t\t\tif (!canceled) {\n\t\t\t\tLOG.warn(\"Failed to cancel live reload worker\");\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to stop live reload worker\", e);\n\t\t} finally {\n\t\t\tresetState();\n\t\t}\n\t}\n\n\tprivate void resetState() {\n\t\tstarted = false;\n\t\texecutor = null;\n\t\twatcher = null;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/layout/WrapLayout.java",
    "content": "package jadx.gui.utils.layout;\n\nimport java.awt.Component;\nimport java.awt.Container;\nimport java.awt.Dimension;\nimport java.awt.FlowLayout;\nimport java.awt.Insets;\n\nimport javax.swing.JScrollPane;\nimport javax.swing.SwingUtilities;\n\nimport org.intellij.lang.annotations.MagicConstant;\n\n/**\n * FlowLayout subclass that fully supports wrapping of components.\n */\npublic class WrapLayout extends FlowLayout {\n\tprivate static final long serialVersionUID = 6109752116520941346L;\n\n\tprivate Dimension preferredLayoutSize;\n\n\t/**\n\t * Constructs a new <code>WrapLayout</code> with a left\n\t * alignment and a default 5-unit horizontal and vertical gap.\n\t */\n\tpublic WrapLayout() {\n\t\tsuper();\n\t}\n\n\t/**\n\t * Constructs a new <code>FlowLayout</code> with the specified\n\t * alignment and a default 5-unit horizontal and vertical gap.\n\t * The value of the alignment argument must be one of\n\t * <code>WrapLayout</code>, <code>WrapLayout</code>,\n\t * or <code>WrapLayout</code>.\n\t *\n\t * @param align the alignment value\n\t */\n\tpublic WrapLayout(@MagicConstant(valuesFromClass = FlowLayout.class) int align) {\n\t\tsuper(align);\n\t}\n\n\t/**\n\t * Creates a new flow layout manager with the indicated alignment\n\t * and the indicated horizontal and vertical gaps.\n\t * <p>\n\t * The value of the alignment argument must be one of\n\t * <code>WrapLayout</code>, <code>WrapLayout</code>,\n\t * or <code>WrapLayout</code>.\n\t *\n\t * @param align the alignment value\n\t * @param hgap  the horizontal gap between components\n\t * @param vgap  the vertical gap between components\n\t */\n\tpublic WrapLayout(int align, int hgap, int vgap) {\n\t\tsuper(align, hgap, vgap);\n\t}\n\n\t/**\n\t * Returns the preferred dimensions for this layout given the\n\t * <i>visible</i> components in the specified target container.\n\t *\n\t * @param target the component which needs to be laid out\n\t * @return the preferred dimensions to lay out the\n\t *         subcomponents of the specified container\n\t */\n\t@Override\n\tpublic Dimension preferredLayoutSize(Container target) {\n\t\treturn layoutSize(target, true);\n\t}\n\n\t/**\n\t * Returns the minimum dimensions needed to layout the <i>visible</i>\n\t * components contained in the specified target container.\n\t *\n\t * @param target the component which needs to be laid out\n\t * @return the minimum dimensions to lay out the\n\t *         subcomponents of the specified container\n\t */\n\t@Override\n\tpublic Dimension minimumLayoutSize(Container target) {\n\t\tDimension minimum = layoutSize(target, false);\n\t\tminimum.width -= (getHgap() + 1);\n\t\treturn minimum;\n\t}\n\n\t/**\n\t * Returns the minimum or preferred dimension needed to layout the target\n\t * container.\n\t *\n\t * @param target    target to get layout size for\n\t * @param preferred should preferred size be calculated\n\t * @return the dimension to layout the target container\n\t */\n\tprivate Dimension layoutSize(Container target, boolean preferred) {\n\t\tsynchronized (target.getTreeLock()) {\n\t\t\t// Each row must fit with the width allocated to the container.\n\t\t\t// When the container width = 0, the preferred width of the container\n\t\t\t// has not yet been calculated so lets ask for the maximum.\n\n\t\t\tint targetWidth = target.getSize().width;\n\t\t\tContainer container = target;\n\n\t\t\twhile (container.getSize().width == 0 && container.getParent() != null) {\n\t\t\t\tcontainer = container.getParent();\n\t\t\t}\n\n\t\t\ttargetWidth = container.getSize().width;\n\n\t\t\tif (targetWidth == 0) {\n\t\t\t\ttargetWidth = Integer.MAX_VALUE;\n\t\t\t}\n\n\t\t\tint hgap = getHgap();\n\t\t\tint vgap = getVgap();\n\t\t\tInsets insets = target.getInsets();\n\t\t\tint horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2);\n\t\t\tint maxWidth = targetWidth - horizontalInsetsAndGap;\n\n\t\t\t// Fit components into the allowed width\n\n\t\t\tDimension dim = new Dimension(0, 0);\n\t\t\tint rowWidth = 0;\n\t\t\tint rowHeight = 0;\n\n\t\t\tint nmembers = target.getComponentCount();\n\n\t\t\tfor (int i = 0; i < nmembers; i++) {\n\t\t\t\tComponent m = target.getComponent(i);\n\n\t\t\t\tif (m.isVisible()) {\n\t\t\t\t\tDimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();\n\t\t\t\t\tint width = d.width;\n\n\t\t\t\t\t// Can't add the component to current row. Start a new row.\n\t\t\t\t\tif (rowWidth + width >= maxWidth) {\n\t\t\t\t\t\taddRow(dim, rowWidth, rowHeight);\n\t\t\t\t\t\trowWidth = 0;\n\t\t\t\t\t\trowHeight = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Add a horizontal gap for all components after the first\n\n\t\t\t\t\tif (rowWidth != 0) {\n\t\t\t\t\t\trowWidth += hgap;\n\t\t\t\t\t}\n\n\t\t\t\t\trowWidth += width;\n\t\t\t\t\trowHeight = Math.max(rowHeight, d.height);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\taddRow(dim, rowWidth, rowHeight);\n\n\t\t\tdim.width += horizontalInsetsAndGap;\n\t\t\tdim.height += insets.top + insets.bottom + vgap * 2;\n\n\t\t\t// When using a scroll pane or the DecoratedLookAndFeel we need to\n\t\t\t// make sure the preferred size is less than the size of the\n\t\t\t// target container so shrinking the container size works\n\t\t\t// correctly. Removing the horizontal gap is an easy way to do this.\n\n\t\t\tContainer scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target);\n\n\t\t\tif (scrollPane != null && target.isValid()) {\n\t\t\t\tdim.width -= (hgap + 1);\n\t\t\t}\n\n\t\t\treturn dim;\n\t\t}\n\t}\n\n\t/*\n\t * A new row has been completed. Use the dimensions of this row\n\t * to update the preferred size for the container.\n\t * @param dim update the width and height when appropriate\n\t * @param rowWidth the width of the row to add\n\t * @param rowHeight the height of the row to add\n\t */\n\tprivate void addRow(Dimension dim, int rowWidth, int rowHeight) {\n\t\tdim.width = Math.max(dim.width, rowWidth);\n\n\t\tif (dim.height > 0) {\n\t\t\tdim.height += getVgap();\n\t\t}\n\n\t\tdim.height += rowHeight;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/pkgs/JRenamePackage.java",
    "content": "package jadx.gui.utils.pkgs;\n\nimport java.util.List;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport javax.swing.Icon;\n\nimport org.apache.commons.lang3.StringUtils;\n\nimport jadx.api.JavaNode;\nimport jadx.api.JavaPackage;\nimport jadx.api.data.ICodeRename;\nimport jadx.api.data.impl.JadxCodeRename;\nimport jadx.api.data.impl.JadxNodeRef;\nimport jadx.core.deobf.NameMapper;\nimport jadx.gui.treemodel.JRenameNode;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.utils.Icons;\n\nimport static jadx.core.deobf.NameMapper.VALID_JAVA_IDENTIFIER;\n\npublic class JRenamePackage implements JRenameNode {\n\n\tprivate final JavaPackage refPkg;\n\tprivate final String rawFullName;\n\tprivate final String fullName;\n\tprivate final String name;\n\n\tpublic JRenamePackage(JavaPackage refPkg, String rawFullName, String fullName, String name) {\n\t\tthis.refPkg = refPkg;\n\t\tthis.rawFullName = rawFullName;\n\t\tthis.fullName = fullName;\n\t\tthis.name = name;\n\t}\n\n\t@Override\n\tpublic JavaNode getJavaNode() {\n\t\treturn refPkg;\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn fullName;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic Icon getIcon() {\n\t\treturn Icons.PACKAGE;\n\t}\n\n\t@Override\n\tpublic boolean canRename() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic ICodeRename buildCodeRename(String newName, Set<ICodeRename> renames) {\n\t\treturn new JadxCodeRename(JadxNodeRef.forPkg(rawFullName), newName);\n\t}\n\n\t@Override\n\tpublic boolean isValidName(String newName) {\n\t\treturn isValidPackageName(newName);\n\t}\n\n\tprivate static final Pattern PACKAGE_RENAME_PATTERN = Pattern.compile(\n\t\t\t\"(\\\\.)?PKG(\\\\.PKG)*\".replace(\"PKG\", VALID_JAVA_IDENTIFIER.pattern()));\n\n\tstatic boolean isValidPackageName(String newName) {\n\t\tif (newName == null || newName.isEmpty() || NameMapper.isReserved(newName)) {\n\t\t\treturn false;\n\t\t}\n\t\tMatcher matcher = PACKAGE_RENAME_PATTERN.matcher(newName);\n\t\tif (!matcher.matches()) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (String part : StringUtils.split(newName, '.')) {\n\t\t\tif (NameMapper.isReserved(part)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void removeAlias() {\n\t\trefPkg.removeAlias();\n\t}\n\n\t@Override\n\tpublic void addUpdateNodes(List<JavaNode> toUpdate) {\n\t\trefPkg.addUseIn(toUpdate);\n\t}\n\n\t@Override\n\tpublic void reload(MainWindow mainWindow) {\n\t\tmainWindow.rebuildPackagesTree();\n\t\tmainWindow.reloadTreePreservingState();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn refPkg.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/pkgs/PackageHelper.java",
    "content": "package jadx.gui.utils.pkgs;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JavaPackage;\nimport jadx.core.dex.info.PackageInfo;\nimport jadx.core.utils.ListUtils;\nimport jadx.core.utils.Utils;\nimport jadx.gui.JadxWrapper;\nimport jadx.gui.treemodel.JClass;\nimport jadx.gui.treemodel.JPackage;\nimport jadx.gui.utils.JNodeCache;\n\npublic class PackageHelper {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(PackageHelper.class);\n\n\tprivate static final Comparator<JClass> CLASS_COMPARATOR = Comparator.comparing(JClass::getName, String.CASE_INSENSITIVE_ORDER);\n\tprivate static final Comparator<JPackage> PKG_COMPARATOR = Comparator.comparing(JPackage::getName, String.CASE_INSENSITIVE_ORDER);\n\n\tprivate final JadxWrapper wrapper;\n\tprivate final JNodeCache nodeCache;\n\tprivate List<String> excludedPackages;\n\n\tprivate final Map<PackageInfo, JPackage> pkgInfoMap = new HashMap<>();\n\n\tpublic PackageHelper(JadxWrapper wrapper, JNodeCache jNodeCache) {\n\t\tthis.wrapper = wrapper;\n\t\tthis.nodeCache = jNodeCache;\n\t}\n\n\tpublic List<JPackage> getRoots(boolean flatPackages) {\n\t\texcludedPackages = wrapper.getExcludedPackages();\n\t\tpkgInfoMap.clear();\n\t\tif (flatPackages) {\n\t\t\treturn prepareFlatPackages();\n\t\t}\n\t\tlong start = System.currentTimeMillis();\n\t\tList<JPackage> roots = prepareHierarchyPackages();\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tLOG.debug(\"Prepare hierarchy packages in {} ms\", System.currentTimeMillis() - start);\n\t\t}\n\t\treturn roots;\n\t}\n\n\tpublic List<JRenamePackage> getRenameNodes(JPackage pkg) {\n\t\tList<JRenamePackage> list = new ArrayList<>();\n\t\tPackageInfo pkgInfo = pkg.getPkg().getPkgNode().getAliasPkgInfo();\n\t\tSet<String> added = new HashSet<>();\n\t\tdo {\n\t\t\tJPackage jPkg = pkgInfoMap.get(pkgInfo);\n\t\t\tif (jPkg != null && !jPkg.isSynthetic()) {\n\t\t\t\tJavaPackage javaPkg = jPkg.getPkg();\n\t\t\t\tif (!javaPkg.isDefault()) {\n\t\t\t\t\tJRenamePackage renamePkg = new JRenamePackage(javaPkg,\n\t\t\t\t\t\t\tjavaPkg.getRawFullName(), javaPkg.getFullName(), javaPkg.getName());\n\t\t\t\t\tif (added.add(javaPkg.getFullName())) {\n\t\t\t\t\t\tlist.add(renamePkg);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tpkgInfo = pkgInfo.getParentPkg();\n\t\t} while (pkgInfo != null);\n\t\treturn list;\n\t}\n\n\tprivate List<JPackage> prepareFlatPackages() {\n\t\tList<JPackage> list = new ArrayList<>();\n\t\tfor (JavaPackage javaPkg : wrapper.getPackages()) {\n\t\t\tif (javaPkg.isLeaf() || !javaPkg.getClasses().isEmpty()) {\n\t\t\t\tJPackage pkg = buildJPackage(javaPkg, false);\n\t\t\t\tpkg.setName(javaPkg.getFullName());\n\t\t\t\tlist.add(pkg);\n\t\t\t\tpkgInfoMap.put(javaPkg.getPkgNode().getAliasPkgInfo(), pkg);\n\t\t\t}\n\t\t}\n\t\tlist.sort(PKG_COMPARATOR);\n\t\treturn list;\n\t}\n\n\tprivate List<JPackage> prepareHierarchyPackages() {\n\t\tJPackage root = JPackage.makeTmpRoot();\n\t\tList<JavaPackage> packages = wrapper.getPackages();\n\t\tList<JPackage> jPackages = new ArrayList<>(packages.size());\n\t\t// create nodes for exists packages\n\t\tfor (JavaPackage javaPkg : packages) {\n\t\t\tJPackage jPkg = buildJPackage(javaPkg, false);\n\t\t\tjPackages.add(jPkg);\n\t\t\tPackageInfo aliasPkgInfo = javaPkg.getPkgNode().getAliasPkgInfo();\n\t\t\tjPkg.setName(aliasPkgInfo.getName());\n\t\t\tpkgInfoMap.put(aliasPkgInfo, jPkg);\n\t\t\tif (aliasPkgInfo.isRoot()) {\n\t\t\t\troot.getSubPackages().add(jPkg);\n\t\t\t}\n\t\t}\n\t\t// link subpackages, create missing packages created by renames\n\t\tfor (JPackage jPkg : jPackages) {\n\t\t\tif (jPkg.getPkg().isLeaf()) {\n\t\t\t\tbuildLeafPath(jPkg, root, pkgInfoMap);\n\t\t\t}\n\t\t}\n\t\tList<JPackage> toMerge = new ArrayList<>();\n\t\ttraverseMiddlePackages(root, toMerge);\n\t\tUtils.treeDfsVisit(root, JPackage::getSubPackages, v -> v.getSubPackages().sort(PKG_COMPARATOR));\n\t\treturn root.getSubPackages();\n\t}\n\n\tprivate void buildLeafPath(JPackage jPkg, JPackage root, Map<PackageInfo, JPackage> pkgMap) {\n\t\tJPackage currentJPkg = jPkg;\n\t\tPackageInfo current = jPkg.getPkg().getPkgNode().getAliasPkgInfo();\n\t\twhile (true) {\n\t\t\tcurrent = current.getParentPkg();\n\t\t\tif (current == null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tJPackage parentJPkg = pkgMap.get(current);\n\t\t\tif (parentJPkg == null) {\n\t\t\t\tparentJPkg = buildJPackage(currentJPkg.getPkg(), true);\n\t\t\t\tparentJPkg.setName(current.getName());\n\t\t\t\tpkgMap.put(current, parentJPkg);\n\t\t\t\tif (current.isRoot()) {\n\t\t\t\t\troot.getSubPackages().add(parentJPkg);\n\t\t\t\t}\n\t\t\t}\n\t\t\tList<JPackage> subPackages = parentJPkg.getSubPackages();\n\t\t\tString pkgName = currentJPkg.getName();\n\t\t\tif (ListUtils.noneMatch(subPackages, p -> p.getName().equals(pkgName))) {\n\t\t\t\tsubPackages.add(currentJPkg);\n\t\t\t}\n\t\t\tcurrentJPkg = parentJPkg;\n\t\t}\n\t}\n\n\tprivate static void traverseMiddlePackages(JPackage pkg, List<JPackage> toMerge) {\n\t\tList<JPackage> subPackages = pkg.getSubPackages();\n\t\tint count = subPackages.size();\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tJPackage subPackage = subPackages.get(i);\n\t\t\tJPackage replacePkg = mergeMiddlePackages(subPackage, toMerge);\n\t\t\tif (replacePkg != subPackage) {\n\t\t\t\tsubPackages.set(i, replacePkg);\n\t\t\t}\n\t\t\ttraverseMiddlePackages(replacePkg, toMerge);\n\t\t}\n\t}\n\n\tprivate static JPackage mergeMiddlePackages(JPackage jPkg, List<JPackage> merged) {\n\t\tList<JPackage> subPackages = jPkg.getSubPackages();\n\t\tif (subPackages.size() == 1 && jPkg.getClasses().isEmpty()) {\n\t\t\tmerged.add(jPkg);\n\t\t\tJPackage endPkg = mergeMiddlePackages(subPackages.get(0), merged);\n\t\t\tmerged.clear();\n\t\t\treturn endPkg;\n\t\t}\n\t\tif (!merged.isEmpty()) {\n\t\t\tmerged.add(jPkg);\n\t\t\tjPkg.setName(Utils.listToString(merged, \".\", JPackage::getName));\n\t\t}\n\t\treturn jPkg;\n\t}\n\n\tprivate JPackage buildJPackage(JavaPackage javaPkg, boolean synthetic) {\n\t\tboolean pkgEnabled = isPkgEnabled(javaPkg.getRawFullName(), excludedPackages);\n\t\tList<JClass> classes;\n\t\tif (synthetic) {\n\t\t\tclasses = Collections.emptyList();\n\t\t} else {\n\t\t\tclasses = Utils.collectionMap(javaPkg.getClassesNoDup(), nodeCache::makeFrom);\n\t\t\tclasses.sort(CLASS_COMPARATOR);\n\t\t}\n\t\treturn nodeCache.newJPackage(javaPkg, synthetic, pkgEnabled, classes);\n\t}\n\n\tprivate static boolean isPkgEnabled(String fullPkgName, List<String> excludedPackages) {\n\t\treturn excludedPackages.isEmpty() || excludedPackages.stream().noneMatch(p -> isPkgMatch(fullPkgName, p));\n\t}\n\n\tprivate static boolean isPkgMatch(String fullPkgName, String filterPkg) {\n\t\tif (fullPkgName.equals(filterPkg)) {\n\t\t\treturn true;\n\t\t}\n\t\t// optimized check, same as `fullPkgName.startsWith(filterPkg + '.')`\n\t\tint filterPkgLen = filterPkg.length();\n\t\treturn fullPkgName.length() > filterPkgLen\n\t\t\t\t&& fullPkgName.charAt(filterPkgLen) == '.'\n\t\t\t\t&& fullPkgName.startsWith(filterPkg);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/plugins/CloseablePlugins.java",
    "content": "package jadx.gui.utils.plugins;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.plugins.PluginContext;\n\npublic class CloseablePlugins {\n\tprivate final List<PluginContext> list;\n\tprivate final @Nullable Runnable closeable;\n\n\tpublic CloseablePlugins(List<PluginContext> list, @Nullable Runnable closeable) {\n\t\tthis.list = list;\n\t\tthis.closeable = closeable;\n\t}\n\n\tpublic void close() {\n\t\tif (closeable != null) {\n\t\t\tcloseable.run();\n\t\t}\n\t}\n\n\tpublic @Nullable Runnable getCloseable() {\n\t\treturn closeable;\n\t}\n\n\tpublic List<PluginContext> getList() {\n\t\treturn list;\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/plugins/CollectPlugins.java",
    "content": "package jadx.gui.utils.plugins;\n\nimport java.util.ArrayList;\nimport java.util.Optional;\nimport java.util.SortedSet;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.JadxDecompiler;\nimport jadx.cli.plugins.JadxFilesGetter;\nimport jadx.core.plugins.AppContext;\nimport jadx.core.plugins.JadxPluginManager;\nimport jadx.core.plugins.PluginContext;\nimport jadx.gui.ui.MainWindow;\nimport jadx.plugins.tools.JadxExternalPluginsLoader;\n\n/**\n * Collect all plugins.\n * Init not yet loaded plugins in new temporary context.\n * Support a case if decompiler in wrapper is not initialized yet.\n */\npublic class CollectPlugins {\n\n\tprivate final MainWindow mainWindow;\n\n\tpublic CollectPlugins(MainWindow mainWindow) {\n\t\tthis.mainWindow = mainWindow;\n\t}\n\n\tpublic CloseablePlugins build() {\n\t\tOptional<JadxDecompiler> currentDecompiler = mainWindow.getWrapper().getCurrentDecompiler();\n\t\tif (currentDecompiler.isPresent()) {\n\t\t\tJadxDecompiler decompiler = currentDecompiler.get();\n\t\t\tSortedSet<PluginContext> plugins = decompiler.getPluginManager().getResolvedPluginContexts();\n\t\t\treturn new CloseablePlugins(new ArrayList<>(plugins), null);\n\t\t}\n\t\t// collect and init plugins in new temp context\n\t\tJadxArgs jadxArgs = mainWindow.getSettings().toJadxArgs();\n\t\tjadxArgs.setFilesGetter(JadxFilesGetter.INSTANCE);\n\t\ttry (JadxDecompiler decompiler = new JadxDecompiler(jadxArgs)) {\n\t\t\tJadxPluginManager pluginManager = decompiler.getPluginManager();\n\t\t\tpluginManager.registerAddPluginListener(pluginContext -> {\n\t\t\t\tAppContext appContext = new AppContext();\n\t\t\t\tappContext.setGuiContext(null); // load temp plugins without UI context\n\t\t\t\tappContext.setFilesGetter(jadxArgs.getFilesGetter());\n\t\t\t\tpluginContext.setAppContext(appContext);\n\t\t\t});\n\t\t\tpluginManager.load(new JadxExternalPluginsLoader());\n\t\t\tSortedSet<PluginContext> allPlugins = pluginManager.getAllPluginContexts();\n\t\t\tpluginManager.init(allPlugins);\n\t\t\tRunnable closeable = () -> pluginManager.unload(allPlugins);\n\t\t\treturn new CloseablePlugins(new ArrayList<>(allPlugins), closeable);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/plugins/PluginWithOptions.java",
    "content": "package jadx.gui.utils.plugins;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.api.plugins.JadxPlugin;\nimport jadx.api.plugins.options.JadxPluginOptions;\n\npublic class PluginWithOptions implements Comparable<PluginWithOptions> {\n\tpublic static final PluginWithOptions NULL = new PluginWithOptions(null, null);\n\n\tprivate final JadxPlugin plugin;\n\tprivate final JadxPluginOptions options;\n\n\tpublic PluginWithOptions(JadxPlugin plugin, JadxPluginOptions options) {\n\t\tthis.plugin = plugin;\n\t\tthis.options = options;\n\t}\n\n\tpublic JadxPlugin getPlugin() {\n\t\treturn plugin;\n\t}\n\n\tpublic JadxPluginOptions getOptions() {\n\t\treturn options;\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull PluginWithOptions other) {\n\t\treturn plugin.getClass().getName().compareTo(other.getClass().getName());\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"PluginWithOptions{plugin=\" + plugin + \", options=\" + options + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/plugins/SettingsGroupPluginWrap.java",
    "content": "package jadx.gui.utils.plugins;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.swing.JComponent;\nimport javax.swing.JLabel;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.gui.ISettingsGroup;\n\npublic class SettingsGroupPluginWrap implements ISettingsGroup {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SettingsGroupPluginWrap.class);\n\n\tprivate final String pluginId;\n\tprivate final ISettingsGroup pluginSettingGroup;\n\n\tpublic SettingsGroupPluginWrap(String pluginId, ISettingsGroup pluginSettingGroup) {\n\t\tthis.pluginId = pluginId;\n\t\tthis.pluginSettingGroup = pluginSettingGroup;\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\ttry {\n\t\t\treturn pluginSettingGroup.getTitle();\n\t\t} catch (Throwable t) {\n\t\t\tLOG.warn(\"Failed to get settings group title for plugin: {}\", pluginId, t);\n\t\t\treturn \"<error>\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic JComponent buildComponent() {\n\t\ttry {\n\t\t\treturn pluginSettingGroup.buildComponent();\n\t\t} catch (Throwable t) {\n\t\t\tLOG.warn(\"Failed to build settings group component for plugin: {}\", pluginId, t);\n\t\t\treturn new JLabel(\"<error>\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic List<ISettingsGroup> getSubGroups() {\n\t\ttry {\n\t\t\treturn pluginSettingGroup.getSubGroups();\n\t\t} catch (Throwable t) {\n\t\t\tLOG.warn(\"Failed to get settings group sub-groups for plugin: {}\", pluginId, t);\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void close(boolean save) {\n\t\ttry {\n\t\t\tpluginSettingGroup.close(save);\n\t\t} catch (Throwable t) {\n\t\t\tLOG.warn(\"Failed to close settings group for plugin: {}\", pluginId, t);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/plugins/TreeInputsHelper.java",
    "content": "package jadx.gui.utils.plugins;\n\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.gui.plugins.context.ITreeInputCategory;\nimport jadx.gui.treemodel.JNode;\nimport jadx.gui.ui.MainWindow;\n\npublic class TreeInputsHelper {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(TreeInputsHelper.class);\n\n\tprivate final List<CategoryData> categoryData;\n\tprivate List<Path> simpleFiles;\n\n\tpublic TreeInputsHelper(MainWindow mainWindow) {\n\t\tcategoryData = mainWindow.getWrapper().getGuiPluginsContext()\n\t\t\t\t.getTreeInputCategories()\n\t\t\t\t.stream()\n\t\t\t\t.map(CategoryData::new)\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tpublic void processInputs(List<Path> files) {\n\t\tsimpleFiles = new ArrayList<>(files.size());\n\t\tfor (Path file : files) {\n\t\t\tboolean added = false;\n\t\t\tfor (CategoryData data : categoryData) {\n\t\t\t\tif (data.filesFilter(file)) {\n\t\t\t\t\tadded = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!added) {\n\t\t\t\tsimpleFiles.add(file);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic List<JNode> getCustomNodes() {\n\t\treturn categoryData.stream()\n\t\t\t\t.map(CategoryData::buildInputNode)\n\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tpublic List<Path> getSimpleFiles() {\n\t\treturn simpleFiles;\n\t}\n\n\tprivate static final class CategoryData {\n\t\tprivate final ITreeInputCategory provider;\n\t\tprivate final List<Path> collectedFiles = new ArrayList<>();\n\n\t\tprivate CategoryData(ITreeInputCategory provider) {\n\t\t\tthis.provider = provider;\n\t\t}\n\n\t\tpublic boolean filesFilter(Path file) {\n\t\t\ttry {\n\t\t\t\tif (provider.filesFilter(file)) {\n\t\t\t\t\tcollectedFiles.add(file);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to filter input files\", e);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic @Nullable JNode buildInputNode() {\n\t\t\ttry {\n\t\t\t\treturn provider.buildInputNode(collectedFiles);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to build custom input node\", e);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/res/ResTableHelper.java",
    "content": "package jadx.gui.utils.res;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ResourceFileContent;\nimport jadx.api.ResourceType;\nimport jadx.core.xmlgen.ResContainer;\nimport jadx.gui.treemodel.JResource;\nimport jadx.gui.treemodel.JSubResource;\n\npublic class ResTableHelper {\n\n\t/**\n\t * Build UI tree for resource table container.\n\t *\n\t * @return root nodes\n\t */\n\tpublic static List<JResource> buildTree(JResource resTableRes, ResContainer resTable) {\n\t\tResTableHelper resTableHelper = new ResTableHelper(resTableRes);\n\t\tresTableHelper.process(resTable);\n\t\treturn resTableHelper.roots;\n\t}\n\n\tprivate final List<JResource> roots = new ArrayList<>();\n\tprivate final Map<String, JResource> dirs = new HashMap<>();\n\tprivate final JResource resTableRes;\n\n\tprivate ResTableHelper(JResource resTableRes) {\n\t\tthis.resTableRes = resTableRes;\n\t}\n\n\tprivate void process(ResContainer resTable) {\n\t\tfor (ResContainer subFile : resTable.getSubFiles()) {\n\t\t\tloadSubNodes(subFile);\n\t\t}\n\t}\n\n\tprivate void loadSubNodes(ResContainer rc) {\n\t\tString resName = rc.getName();\n\t\tint split = resName.lastIndexOf('/');\n\t\tString dir;\n\t\tString name;\n\t\tif (split == -1) {\n\t\t\tdir = null;\n\t\t\tname = resName;\n\t\t} else {\n\t\t\tdir = resName.substring(0, split);\n\t\t\tname = resName.substring(split + 1);\n\t\t}\n\t\tICodeInfo code = rc.getText();\n\t\tResourceFileContent fileContent = new ResourceFileContent(name, ResourceType.XML, code);\n\t\tJResource resFile = new JSubResource(resTableRes, fileContent, resName, name, JResource.JResType.FILE);\n\t\taddResFile(dir, resFile);\n\n\t\tfor (ResContainer subFile : rc.getSubFiles()) {\n\t\t\tloadSubNodes(subFile);\n\t\t}\n\t}\n\n\tprivate void addResFile(@Nullable String dir, JResource resFile) {\n\t\tif (dir == null) {\n\t\t\troots.add(resFile);\n\t\t\treturn;\n\t\t}\n\t\tJResource dirRes = dirs.get(dir);\n\t\tif (dirRes != null) {\n\t\t\tdirRes.addSubNode(resFile);\n\t\t\treturn;\n\t\t}\n\t\tJResource parentDir = null;\n\t\tint splitPos = -1;\n\t\twhile (true) {\n\t\t\tint prevStart = splitPos + 1;\n\t\t\tsplitPos = dir.indexOf('/', prevStart);\n\t\t\tboolean last = splitPos == -1;\n\t\t\tString path = last ? dir : dir.substring(0, splitPos);\n\t\t\tJResource curDir = dirs.get(path);\n\t\t\tif (curDir == null) {\n\t\t\t\tString dirName = last ? dir.substring(prevStart) : dir.substring(prevStart, splitPos);\n\t\t\t\tcurDir = new JSubResource(resTableRes, null, path, dirName, JResource.JResType.DIR);\n\t\t\t\tdirs.put(path, curDir);\n\t\t\t\tif (parentDir == null) {\n\t\t\t\t\troots.add(curDir);\n\t\t\t\t} else {\n\t\t\t\t\tparentDir.addSubNode(curDir);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (last) {\n\t\t\t\tcurDir.addSubNode(resFile);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tparentDir = curDir;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/rx/CustomDisposable.java",
    "content": "package jadx.gui.utils.rx;\n\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport io.reactivex.rxjava3.disposables.Disposable;\n\npublic class CustomDisposable implements Disposable {\n\n\tprivate final AtomicBoolean disposed = new AtomicBoolean(false);\n\tprivate final Runnable disposeTask;\n\n\tpublic CustomDisposable(Runnable disposeTask) {\n\t\tthis.disposeTask = disposeTask;\n\t}\n\n\t@Override\n\tpublic void dispose() {\n\t\ttry {\n\t\t\tdisposeTask.run();\n\t\t} finally {\n\t\t\tdisposed.set(true);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDisposed() {\n\t\treturn disposed.get();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/rx/DebounceUpdate.java",
    "content": "package jadx.gui.utils.rx;\n\nimport java.util.concurrent.TimeUnit;\n\nimport io.reactivex.rxjava3.core.BackpressureStrategy;\nimport io.reactivex.rxjava3.core.Flowable;\nimport io.reactivex.rxjava3.core.FlowableEmitter;\nimport io.reactivex.rxjava3.core.FlowableOnSubscribe;\nimport io.reactivex.rxjava3.disposables.Disposable;\n\npublic class DebounceUpdate {\n\n\tprivate FlowableEmitter<Boolean> emitter;\n\tprivate final Disposable disposable;\n\n\tpublic DebounceUpdate(int timeMs, Runnable action) {\n\t\tFlowableOnSubscribe<Boolean> source = emitter -> this.emitter = emitter;\n\t\tdisposable = Flowable.create(source, BackpressureStrategy.LATEST)\n\t\t\t\t.debounce(timeMs, TimeUnit.MILLISECONDS)\n\t\t\t\t.subscribe(v -> action.run());\n\t}\n\n\tpublic void requestUpdate() {\n\t\temitter.onNext(Boolean.TRUE);\n\t}\n\n\tpublic void dispose() {\n\t\tdisposable.dispose();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/rx/RxUtils.java",
    "content": "package jadx.gui.utils.rx;\n\nimport java.awt.event.KeyAdapter;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.KeyListener;\nimport java.util.function.Supplier;\n\nimport javax.swing.JSpinner;\nimport javax.swing.JTextField;\nimport javax.swing.event.ChangeListener;\nimport javax.swing.event.DocumentListener;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport io.reactivex.rxjava3.core.BackpressureStrategy;\nimport io.reactivex.rxjava3.core.Flowable;\nimport io.reactivex.rxjava3.core.FlowableEmitter;\nimport io.reactivex.rxjava3.core.FlowableOnSubscribe;\n\nimport jadx.gui.utils.ui.DocumentUpdateListener;\n\npublic class RxUtils {\n\n\tpublic static Flowable<String> textFieldChanges(final JTextField textField) {\n\t\tFlowableOnSubscribe<String> source = emitter -> {\n\t\t\tDocumentListener listener = new DocumentUpdateListener(ev -> emitter.onNext(textField.getText()));\n\t\t\ttextField.getDocument().addDocumentListener(listener);\n\t\t\temitter.setDisposable(new CustomDisposable(() -> textField.getDocument().removeDocumentListener(listener)));\n\t\t};\n\t\treturn Flowable.create(source, BackpressureStrategy.LATEST).distinctUntilChanged();\n\t}\n\n\tpublic static Flowable<String> textFieldEnterPress(final JTextField textField) {\n\t\tFlowableOnSubscribe<String> source = emitter -> {\n\t\t\tKeyListener keyListener = enterKeyListener(emitter, textField::getText);\n\t\t\ttextField.addKeyListener(keyListener);\n\t\t\temitter.setDisposable(new CustomDisposable(() -> textField.removeKeyListener(keyListener)));\n\t\t};\n\t\treturn Flowable.create(source, BackpressureStrategy.LATEST).distinctUntilChanged();\n\t}\n\n\tpublic static Flowable<String> spinnerChanges(final JSpinner spinner) {\n\t\tFlowableOnSubscribe<String> source = emitter -> {\n\t\t\tChangeListener changeListener = e -> emitter.onNext(String.valueOf(spinner.getValue()));\n\t\t\tspinner.addChangeListener(changeListener);\n\t\t\temitter.setDisposable(new CustomDisposable(() -> spinner.removeChangeListener(changeListener)));\n\t\t};\n\t\treturn Flowable.create(source, BackpressureStrategy.LATEST).distinctUntilChanged();\n\t}\n\n\tpublic static Flowable<String> spinnerEnterPress(final JSpinner spinner) {\n\t\tFlowableOnSubscribe<String> source = emitter -> {\n\t\t\tKeyListener keyListener = enterKeyListener(emitter, () -> String.valueOf(spinner.getValue()));\n\t\t\tspinner.addKeyListener(keyListener);\n\t\t\temitter.setDisposable(new CustomDisposable(() -> spinner.removeKeyListener(keyListener)));\n\t\t};\n\t\treturn Flowable.create(source, BackpressureStrategy.LATEST).distinctUntilChanged();\n\t}\n\n\tprivate static @NotNull KeyListener enterKeyListener(FlowableEmitter<String> emitter, Supplier<String> supplier) {\n\t\treturn new KeyAdapter() {\n\t\t\t@Override\n\t\t\tpublic void keyPressed(KeyEvent ev) {\n\t\t\t\tif (ev.getKeyCode() == KeyEvent.VK_ENTER) {\n\t\t\t\t\temitter.onNext(supplier.get());\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/shortcut/Shortcut.java",
    "content": "package jadx.gui.utils.shortcut;\n\nimport java.awt.event.KeyEvent;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.swing.KeyStroke;\n\nimport jadx.commons.app.JadxSystemInfo;\n\npublic class Shortcut {\n\tprivate static final Set<Integer> FORBIDDEN_KEY_CODES = new HashSet<>(List.of(\n\t\t\tKeyEvent.VK_UNDEFINED, KeyEvent.VK_SHIFT, KeyEvent.VK_ALT, KeyEvent.VK_META, KeyEvent.VK_ALT_GRAPH));\n\tprivate static final Set<Integer> ALLOWED_MODIFIERS = new HashSet<>(List.of(\n\t\t\tKeyEvent.CTRL_DOWN_MASK, KeyEvent.META_DOWN_MASK, KeyEvent.ALT_DOWN_MASK, KeyEvent.ALT_GRAPH_DOWN_MASK,\n\t\t\tKeyEvent.SHIFT_DOWN_MASK));\n\n\tprivate Integer keyCode = null;\n\tprivate Integer modifiers = null;\n\tprivate Integer mouseButton = null;\n\n\tprivate Shortcut() {\n\t}\n\n\tpublic static Shortcut keyboard(int keyCode) {\n\t\treturn keyboard(keyCode, 0);\n\t}\n\n\tpublic static Shortcut keyboard(int keyCode, int modifiers) {\n\t\tShortcut shortcut = new Shortcut();\n\t\tshortcut.keyCode = keyCode;\n\t\tshortcut.modifiers = modifiers;\n\t\treturn shortcut;\n\t}\n\n\tpublic static Shortcut mouse(int mouseButton) {\n\t\tShortcut shortcut = new Shortcut();\n\t\tshortcut.mouseButton = mouseButton;\n\t\treturn shortcut;\n\t}\n\n\tpublic static Shortcut none() {\n\t\tShortcut shortcut = new Shortcut();\n\t\t// Must have at least one non-null attribute in order to be serialized\n\t\t// otherwise will roll back to default Shortcut\n\t\tshortcut.modifiers = 0;\n\t\treturn shortcut;\n\t}\n\n\tpublic Integer getKeyCode() {\n\t\treturn keyCode;\n\t}\n\n\tpublic Integer getModifiers() {\n\t\treturn modifiers;\n\t}\n\n\tpublic Integer getMouseButton() {\n\t\treturn mouseButton;\n\t}\n\n\tpublic boolean isKeyboard() {\n\t\treturn keyCode != null;\n\t}\n\n\tpublic boolean isMouse() {\n\t\treturn mouseButton != null;\n\t}\n\n\tpublic boolean isNone() {\n\t\treturn !isMouse() && !isKeyboard();\n\t}\n\n\tpublic boolean isValidKeyboard() {\n\t\treturn isKeyboard() && !FORBIDDEN_KEY_CODES.contains(keyCode) && isValidModifiers();\n\t}\n\n\tpublic boolean isValidModifiers() {\n\t\tint modifiersTest = modifiers;\n\t\tfor (Integer modifier : ALLOWED_MODIFIERS) {\n\t\t\tmodifiersTest &= ~modifier;\n\t\t}\n\t\treturn modifiersTest == 0;\n\t}\n\n\tpublic KeyStroke toKeyStroke() {\n\t\treturn isKeyboard()\n\t\t\t\t? KeyStroke.getKeyStroke(keyCode, modifiers,\n\t\t\t\t\t\tmodifiers != 0 && JadxSystemInfo.IS_MAC)\n\t\t\t\t: null;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tif (isKeyboard()) {\n\t\t\treturn keyToString();\n\t\t} else if (isMouse()) {\n\t\t\treturn mouseToString();\n\t\t}\n\t\treturn \"NONE\";\n\t}\n\n\tpublic String getTypeString() {\n\t\tif (isKeyboard()) {\n\t\t\treturn \"Keyboard\";\n\t\t} else if (isMouse()) {\n\t\t\treturn \"Mouse\";\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String mouseToString() {\n\t\treturn \"MouseButton\" + mouseButton;\n\t}\n\n\tprivate String keyToString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tif (modifiers != null && modifiers > 0) {\n\t\t\tsb.append(KeyEvent.getModifiersExText(modifiers));\n\t\t\tsb.append('+');\n\t\t}\n\t\tif (keyCode != null && keyCode != 0) {\n\t\t\tsb.append(KeyEvent.getKeyText(keyCode));\n\t\t} else {\n\t\t\tsb.append(\"UNDEFINED\");\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tShortcut shortcut = (Shortcut) o;\n\t\treturn Objects.equals(keyCode, shortcut.keyCode)\n\t\t\t\t&& Objects.equals(modifiers, shortcut.modifiers)\n\t\t\t\t&& Objects.equals(mouseButton, shortcut.mouseButton);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(keyCode, modifiers, mouseButton);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/shortcut/ShortcutsController.java",
    "content": "package jadx.gui.utils.shortcut;\n\nimport java.awt.AWTEvent;\nimport java.awt.Toolkit;\nimport java.awt.event.MouseEvent;\nimport java.util.ArrayList;\nimport java.util.EnumMap;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.swing.JComponent;\nimport javax.swing.KeyStroke;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.settings.data.ShortcutsWrapper;\nimport jadx.gui.ui.MainWindow;\nimport jadx.gui.ui.action.ActionCategory;\nimport jadx.gui.ui.action.ActionModel;\nimport jadx.gui.ui.action.IShortcutAction;\nimport jadx.gui.utils.UiUtils;\n\npublic class ShortcutsController {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ShortcutsController.class);\n\n\tprivate final JadxSettings settings;\n\tprivate final Map<ActionModel, Set<IShortcutAction>> boundActions = new EnumMap<>(ActionModel.class);\n\tprivate final Map<Integer, List<IShortcutAction>> mouseActions = new HashMap<>();\n\n\tprivate ShortcutsWrapper shortcuts;\n\n\tpublic ShortcutsController(JadxSettings settings) {\n\t\tthis.settings = settings;\n\t}\n\n\tpublic void loadSettings() {\n\t\tshortcuts = settings.getShortcuts();\n\t\tindexMouseActions();\n\t\tboundActions.forEach((actionModel, actions) -> {\n\t\t\tif (actions != null) {\n\t\t\t\tShortcut shortcut = get(actionModel);\n\t\t\t\tfor (IShortcutAction action : actions) {\n\t\t\t\t\taction.setShortcut(shortcut);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t@Nullable\n\tpublic Shortcut get(ActionModel actionModel) {\n\t\treturn shortcuts.get(actionModel);\n\t}\n\n\tpublic KeyStroke getKeyStroke(ActionModel actionModel) {\n\t\tShortcut shortcut = get(actionModel);\n\t\tif (shortcut != null && shortcut.isKeyboard()) {\n\t\t\treturn shortcut.toKeyStroke();\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Binds to an action and updates its shortcut every time loadSettings is called\n\t */\n\tpublic void bind(IShortcutAction action) {\n\t\tif (action.getShortcutComponent() == null) {\n\t\t\tLOG.warn(\"No shortcut component in action: {}\", action, new JadxRuntimeException());\n\t\t\treturn;\n\t\t}\n\t\tboundActions.computeIfAbsent(action.getActionModel(), k -> new HashSet<>()).add(action);\n\t}\n\n\t/*\n\t * Immediately sets the shortcut for an action\n\t */\n\tpublic void bindImmediate(IShortcutAction action) {\n\t\tbind(action);\n\t\tShortcut shortcut = get(action.getActionModel());\n\t\taction.setShortcut(shortcut);\n\t}\n\n\tpublic static Map<ActionModel, Shortcut> getDefault() {\n\t\tMap<ActionModel, Shortcut> shortcuts = new HashMap<>();\n\t\tfor (ActionModel actionModel : ActionModel.values()) {\n\t\t\tshortcuts.put(actionModel, actionModel.getDefaultShortcut());\n\t\t}\n\t\treturn shortcuts;\n\t}\n\n\tpublic void registerMouseEventListener(MainWindow mw) {\n\t\tToolkit.getDefaultToolkit().addAWTEventListener(event -> {\n\t\t\tif (mw.isSettingsOpen()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!(event instanceof MouseEvent)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tMouseEvent mouseEvent = (MouseEvent) event;\n\t\t\tif (mouseEvent.getID() != MouseEvent.MOUSE_PRESSED) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tList<IShortcutAction> actions = mouseActions.get(mouseEvent.getButton());\n\t\t\tif (actions != null) {\n\t\t\t\tfor (IShortcutAction action : actions) {\n\t\t\t\t\tif (action != null) {\n\t\t\t\t\t\tmouseEvent.consume();\n\t\t\t\t\t\tUiUtils.uiRun(action::performAction);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}, AWTEvent.MOUSE_EVENT_MASK);\n\t}\n\n\tprivate void indexMouseActions() {\n\t\tmouseActions.clear();\n\t\tfor (ActionModel actionModel : ActionModel.values()) {\n\t\t\tShortcut shortcut = shortcuts.get(actionModel);\n\t\t\tif (shortcut != null && shortcut.isMouse()) {\n\t\t\t\tSet<IShortcutAction> actions = boundActions.get(actionModel);\n\t\t\t\tif (actions != null && !actions.isEmpty()) {\n\t\t\t\t\tmouseActions.computeIfAbsent(shortcut.getMouseButton(), i -> new ArrayList<>())\n\t\t\t\t\t\t\t.addAll(actions);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void unbindActionsForComponent(JComponent component) {\n\t\tfor (Set<IShortcutAction> actions : boundActions.values()) {\n\t\t\tif (actions != null) {\n\t\t\t\tactions.removeIf(action -> action == null\n\t\t\t\t\t\t|| action.getShortcutComponent() == null\n\t\t\t\t\t\t|| action.getShortcutComponent() == component);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Keep only actions bound to the main window.\n\t * Other actions will be added on demand.\n\t */\n\tpublic void reset() {\n\t\tfor (ActionModel actionModel : ActionModel.values()) {\n\t\t\tif (actionModel.getCategory() != ActionCategory.MENU_TOOLBAR) {\n\t\t\t\tboundActions.remove(actionModel);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/tools/SyncNLSLines.java",
    "content": "package jadx.gui.utils.tools;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Automatically synchronizes i18n files with a reference (EN) file.\n * - Adds new lines from the reference file (commented out) to other language files.\n * - Removes lines from other language files that are not present in the reference file.\n * - Updates commented-out empty translations in other files with the reference text (commented).\n */\npublic class SyncNLSLines {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SyncNLSLines.class);\n\n\tprivate static final Path I18N_PATH = Paths.get(\"src/main/resources/i18n/\");\n\tprivate static final String REFERENCE_FILE_NAME = \"Messages_en_US.properties\";\n\n\t/**\n\t * Assumes this tool runs from the project root and jadx-gui is a direct subdirectory.\n\t * If jadx-gui is the project root, this might need adjustment or to be removed\n\t * if I18N_PATH is already relative to jadx-gui.\n\t */\n\tprivate static final String GUI_MODULE_DIR_NAME = \"jadx-gui\";\n\tprivate static final Path GUI_MODULE_PREFIX_PATH = Paths.get(GUI_MODULE_DIR_NAME);\n\n\tpublic static void main(String[] args) {\n\t\ttry {\n\t\t\tprocess();\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to process i18n files\", e);\n\t\t}\n\t}\n\n\tprivate static void process() throws Exception {\n\t\tPath refPath = getRefPath(REFERENCE_FILE_NAME);\n\t\tif (!Files.exists(refPath)) {\n\t\t\tLOG.error(\"Reference i18n file not found: {}\", REFERENCE_FILE_NAME);\n\t\t\treturn;\n\t\t}\n\t\tLOG.info(\"Using reference file: {}\", refPath.toAbsolutePath());\n\t\tPath i18nDir = refPath.toAbsolutePath().getParent();\n\t\tif (i18nDir == null) {\n\t\t\tLOG.error(\"Could not determine i18n directory from reference path: {}\", refPath);\n\t\t\treturn;\n\t\t}\n\t\tList<String> refFileLines = Files.readAllLines(refPath, StandardCharsets.UTF_8);\n\t\ttry (Stream<Path> pathStream = Files.list(i18nDir)) {\n\t\t\tpathStream.filter(path -> {\n\t\t\t\tString fileName = path.getFileName().toString();\n\t\t\t\treturn !fileName.equals(REFERENCE_FILE_NAME)\n\t\t\t\t\t\t&& fileName.startsWith(\"Messages_\")\n\t\t\t\t\t\t&& fileName.endsWith(\".properties\");\n\t\t\t})\n\t\t\t\t\t.forEach(targetPath -> {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tLOG.info(\"Processing target file: {}\", targetPath.toAbsolutePath());\n\t\t\t\t\t\t\tapplySync(refFileLines, targetPath);\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tLOG.error(\"Failed to sync file: {}\", targetPath, e);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t}\n\t\tLOG.info(\"I18N synchronization process finished.\");\n\t}\n\n\t/**\n\t * Parses a list of lines from a properties file into a map of key-value pairs.\n\t * Preserves the full line as value. Comments and empty lines will have null keys.\n\t */\n\tprivate static Map<String, String> parseProperties(List<String> lines) {\n\t\tMap<String, String> properties = new LinkedHashMap<>();\n\t\tfor (String line : lines) {\n\t\t\tString key = extractKey(line);\n\t\t\t// If the key is null, it's a comment or blank line, we might not need these in the map.\n\t\t\t// If we iterate over the original refFileLines later.\n\t\t\t// For simplicity here, we only store actual properties.\n\t\t\tif (key != null) {\n\t\t\t\tproperties.put(key, line);\n\t\t\t}\n\t\t}\n\t\treturn properties;\n\t}\n\n\t/**\n\t * Extracts the key from a properties file line.\n\t * Returns null if the line is a comment, empty, or doesn't seem to be a key-value pair.\n\t */\n\tprivate static String extractKey(String line) {\n\t\tString trimmedLine = line.trim();\n\t\tif (trimmedLine.isEmpty() || trimmedLine.startsWith(\"#\")) {\n\t\t\t// Comment or empty line\n\t\t\treturn null;\n\t\t}\n\t\tint separatorIndex = trimmedLine.indexOf('=');\n\t\tif (separatorIndex == -1) {\n\t\t\t// Line without a separator could be a key with no value (less common in i18n)\n\t\t\t// or just a malformed line. For now, let's consider it a key if it's not empty.\n\t\t\treturn trimmedLine;\n\t\t}\n\t\treturn trimmedLine.substring(0, separatorIndex).trim();\n\t}\n\n\tprivate static void applySync(List<String> refFileLines, Path targetPath) throws IOException {\n\t\tList<String> originalTargetLines = Files.readAllLines(targetPath, StandardCharsets.UTF_8);\n\t\tMap<String, String> targetProperties = parseProperties(originalTargetLines);\n\t\tList<String> newTargetLines = new ArrayList<>(refFileLines.size());\n\t\tboolean updated = false;\n\t\tfor (String refLine : refFileLines) {\n\t\t\tString refKey = extractKey(refLine);\n\t\t\tif (refKey == null) {\n\t\t\t\t// It's a comment or blank line from reference\n\t\t\t\tnewTargetLines.add(refLine);\n\t\t\t} else {\n\t\t\t\t// It's a property line from reference\n\t\t\t\tif (targetProperties.containsKey(refKey)) {\n\t\t\t\t\tString targetLine = targetProperties.get(refKey);\n\t\t\t\t\t// Original logic: if target line is like \"#key=\" (commented, no value)\n\t\t\t\t\t// then use the commented reference value.\n\t\t\t\t\tString trimmed = targetLine.trim();\n\t\t\t\t\tif (trimmed.startsWith(\"#\")\n\t\t\t\t\t\t\t&& trimmed.substring(1).trim().startsWith(refKey) // ensure it's the same key\n\t\t\t\t\t\t\t&& trimmed.endsWith(\"=\")) {\n\t\t\t\t\t\t// Use reference line, commented\n\t\t\t\t\t\tnewTargetLines.add('#' + refLine.trim());\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Use existing target line\n\t\t\t\t\t\tnewTargetLines.add(targetLine);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Key from reference is missing in target, add it commented out\n\t\t\t\t\tnewTargetLines.add('#' + refLine.trim());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Check if files are different\n\t\tif (originalTargetLines.size() != newTargetLines.size()) {\n\t\t\tupdated = true;\n\t\t} else {\n\t\t\tfor (int i = 0; i < originalTargetLines.size(); i++) {\n\t\t\t\tif (!originalTargetLines.get(i).equals(newTargetLines.get(i))) {\n\t\t\t\t\tupdated = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (updated) {\n\t\t\tLOG.info(\"Updating {} ({} lines -> {} lines)\", targetPath.getFileName(), originalTargetLines.size(), newTargetLines.size());\n\t\t\tFiles.write(targetPath, newTargetLines, StandardCharsets.UTF_8);\n\t\t} else {\n\t\t\tLOG.info(\"No changes needed for {}\", targetPath.getFileName());\n\t\t}\n\t}\n\n\tprivate static Path getRefPath(String referenceFileName) {\n\t\t// Path relative to project root (where src/main/resources exists)\n\t\tPath projectRootRelative = I18N_PATH.resolve(referenceFileName);\n\t\tif (Files.exists(projectRootRelative)) {\n\t\t\treturn projectRootRelative.toAbsolutePath();\n\t\t}\n\t\t// Path relative to a module (e.g. jadx-gui/src/main/resources)\n\t\t// This assumes the script is run from one level above GUI_MODULE_DIR_NAME\n\t\tPath moduleRelative = GUI_MODULE_PREFIX_PATH.resolve(I18N_PATH).resolve(referenceFileName);\n\t\tif (Files.exists(moduleRelative)) {\n\t\t\treturn moduleRelative.toAbsolutePath();\n\t\t}\n\t\t// Path if tool runs from within the GUI_MODULE_DIR_NAME itself\n\t\tPath currentDirRelative = Paths.get(\".\").resolve(I18N_PATH).resolve(referenceFileName);\n\t\tif (Files.exists(currentDirRelative)) {\n\t\t\treturn currentDirRelative.toAbsolutePath();\n\t\t}\n\t\tthrow new RuntimeException(\"Can't find reference I18N: \" + referenceFileName);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/ui/ActionHandler.java",
    "content": "package jadx.gui.utils.ui;\n\nimport java.awt.event.ActionEvent;\nimport java.util.function.Consumer;\n\nimport javax.swing.AbstractAction;\nimport javax.swing.Icon;\nimport javax.swing.JButton;\nimport javax.swing.JCheckBoxMenuItem;\nimport javax.swing.JComponent;\nimport javax.swing.JToggleButton;\nimport javax.swing.KeyStroke;\n\nimport jadx.gui.utils.UiUtils;\nimport jadx.gui.utils.shortcut.Shortcut;\n\npublic class ActionHandler extends AbstractAction {\n\n\tprivate final Consumer<ActionEvent> consumer;\n\n\tpublic ActionHandler(Runnable action) {\n\t\tthis.consumer = ev -> action.run();\n\t}\n\n\tpublic ActionHandler(Consumer<ActionEvent> consumer) {\n\t\tthis.consumer = consumer;\n\t}\n\n\tpublic ActionHandler(String name, Runnable action) {\n\t\tthis(action);\n\t\tsetName(name);\n\t}\n\n\tpublic ActionHandler() {\n\t\tthis.consumer = ev -> {\n\t\t};\n\t}\n\n\tpublic void setName(String name) {\n\t\tputValue(NAME, name);\n\t}\n\n\tpublic ActionHandler withNameAndDesc(String name) {\n\t\tsetNameAndDesc(name);\n\t\treturn this;\n\t}\n\n\tpublic void setNameAndDesc(String name) {\n\t\tsetName(name);\n\t\tsetShortDescription(name);\n\t}\n\n\tpublic void setShortDescription(String desc) {\n\t\tputValue(SHORT_DESCRIPTION, desc);\n\t}\n\n\tpublic void setIcon(Icon icon) {\n\t\tputValue(SMALL_ICON, icon);\n\t}\n\n\tpublic void setSelected(boolean selected) {\n\t\tputValue(SELECTED_KEY, selected);\n\t}\n\n\tpublic void setKeyBinding(KeyStroke keyStroke) {\n\t\tputValue(ACCELERATOR_KEY, keyStroke);\n\t}\n\n\tpublic void attachKeyBindingFor(JComponent component, KeyStroke keyStroke) {\n\t\tUiUtils.addKeyBinding(component, keyStroke, \"run\", this);\n\t\tsetKeyBinding(keyStroke);\n\t}\n\n\tpublic void addKeyBindToDescription() {\n\t\tKeyStroke keyStroke = (KeyStroke) getValue(ACCELERATOR_KEY);\n\t\tif (keyStroke != null) {\n\t\t\tString keyText = Shortcut.keyboard(keyStroke.getKeyCode(), keyStroke.getModifiers()).toString();\n\t\t\tString desc = (String) getValue(SHORT_DESCRIPTION);\n\t\t\tsetShortDescription(desc + \" (\" + keyText + \")\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void actionPerformed(ActionEvent e) {\n\t\tconsumer.accept(e);\n\t}\n\n\tpublic JButton makeButton() {\n\t\taddKeyBindToDescription();\n\t\treturn new JButton(this);\n\t}\n\n\tpublic JToggleButton makeToggleButton() {\n\t\tJToggleButton toggleButton = new JToggleButton(this);\n\t\ttoggleButton.setText(\"\");\n\t\treturn toggleButton;\n\t}\n\n\tpublic JCheckBoxMenuItem makeCheckBoxMenuItem() {\n\t\treturn new JCheckBoxMenuItem(this);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/ui/DocumentUpdateListener.java",
    "content": "package jadx.gui.utils.ui;\n\nimport java.util.function.Consumer;\n\nimport javax.swing.event.DocumentEvent;\nimport javax.swing.event.DocumentListener;\n\npublic class DocumentUpdateListener implements DocumentListener {\n\n\tprivate final Consumer<DocumentEvent> listener;\n\n\tpublic DocumentUpdateListener(Consumer<DocumentEvent> listener) {\n\t\tthis.listener = listener;\n\t}\n\n\t@Override\n\tpublic void insertUpdate(DocumentEvent event) {\n\t\tthis.listener.accept(event);\n\t}\n\n\t@Override\n\tpublic void removeUpdate(DocumentEvent event) {\n\t\tthis.listener.accept(event);\n\t}\n\n\t@Override\n\tpublic void changedUpdate(DocumentEvent event) {\n\t\t// ignore attributes change\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/ui/FileOpenerHelper.java",
    "content": "package jadx.gui.utils.ui;\n\nimport java.awt.Desktop;\nimport java.awt.Frame;\nimport java.io.BufferedOutputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourcesLoader;\nimport jadx.core.plugins.files.TempFilesGetter;\nimport jadx.gui.treemodel.JResource;\nimport jadx.gui.utils.NLS;\nimport jadx.gui.utils.UiUtils;\n\npublic class FileOpenerHelper {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(FileOpenerHelper.class);\n\n\tpublic static void exportBinary(JResource resource, Path savePath) {\n\t\ttry (BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(savePath.toFile()))) {\n\t\t\tbyte[] bytes = ResourcesLoader.decodeStream(resource.getResFile(), (size, is) -> is.readAllBytes());\n\n\t\t\tif (bytes == null) {\n\t\t\t\tbytes = new byte[0];\n\t\t\t}\n\t\t\tos.write(bytes);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Error saving file \" + resource.getName(), e);\n\t\t}\n\t}\n\n\tpublic static void openFile(Frame frame, JResource res) {\n\t\tif (Desktop.isDesktopSupported()) {\n\t\t\tDesktop desktop = Desktop.getDesktop();\n\t\t\tPath tempDir = TempFilesGetter.INSTANCE.getTempDir();\n\t\t\tResourceFile resFile = res.getResFile();\n\t\t\tPath path = Paths.get(resFile.getDeobfName());\n\t\t\tPath fileNamePath = path.getFileName();\n\t\t\tPath filePath = tempDir.resolve(fileNamePath);\n\t\t\texportBinary(res, filePath);\n\n\t\t\tif (!Files.exists(filePath)) {\n\t\t\t\tUiUtils.errorMessage(frame, NLS.str(\"error_dialog.not_found_file\", filePath));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (Files.isDirectory(filePath)) {\n\t\t\t\tUiUtils.errorMessage(frame, NLS.str(\"error_dialog.path_is_directory\", filePath));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!Files.isReadable(filePath)) {\n\t\t\t\tUiUtils.errorMessage(frame, NLS.str(\"error_dialog.cannot_read\", filePath));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tdesktop.open(filePath.toFile());\n\t\t\t} catch (IOException ex) {\n\t\t\t\tUiUtils.errorMessage(frame, NLS.str(\"error_dialog.open_failed\", ex.getMessage()));\n\t\t\t\tLOG.error(\"Unable to open file: {0}\", ex);\n\t\t\t} catch (IllegalArgumentException ex) {\n\t\t\t\tUiUtils.errorMessage(frame, NLS.str(\"error_dialog.invalid_path_format\", ex.getMessage()));\n\t\t\t\tLOG.error(\"Invalid file path: {0}\", ex);\n\t\t\t}\n\n\t\t} else {\n\t\t\tUiUtils.errorMessage(frame, NLS.str(\"error_dialog.desktop_unsupported\"));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/ui/MousePressedHandler.java",
    "content": "package jadx.gui.utils.ui;\n\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.util.function.Consumer;\n\npublic class MousePressedHandler extends MouseAdapter {\n\n\tprivate final Consumer<MouseEvent> listener;\n\n\tpublic MousePressedHandler(Consumer<MouseEvent> listener) {\n\t\tthis.listener = listener;\n\t}\n\n\t@Override\n\tpublic void mousePressed(MouseEvent ev) {\n\t\tlistener.accept(ev);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/ui/NodeLabel.java",
    "content": "package jadx.gui.utils.ui;\n\nimport javax.swing.JLabel;\nimport javax.swing.SwingConstants;\n\nimport jadx.gui.treemodel.JNode;\n\npublic class NodeLabel extends JLabel {\n\n\tpublic static NodeLabel longName(JNode node) {\n\t\tNodeLabel label = new NodeLabel(node.makeLongStringHtml(), node.disableHtml());\n\t\tlabel.setIcon(node.getIcon());\n\t\tlabel.setHorizontalAlignment(SwingConstants.LEFT);\n\t\treturn label;\n\t}\n\n\tpublic static NodeLabel noHtml(String label) {\n\t\treturn new NodeLabel(label, true);\n\t}\n\n\tpublic static void disableHtml(JLabel label, boolean disable) {\n\t\tlabel.putClientProperty(\"html.disable\", disable);\n\t}\n\n\tprivate boolean htmlDisabled = false;\n\n\tpublic NodeLabel() {\n\t\tdisableHtml(true);\n\t}\n\n\tpublic NodeLabel(String label) {\n\t\tdisableHtml(true);\n\t\tsetText(label);\n\t}\n\n\tpublic NodeLabel(String label, boolean disableHtml) {\n\t\tdisableHtml(disableHtml);\n\t\tsetText(label);\n\t}\n\n\tpublic void disableHtml(boolean disable) {\n\t\tif (htmlDisabled != disable) {\n\t\t\thtmlDisabled = disable;\n\t\t\tdisableHtml(this, disable);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/ui/SimpleMenuItem.java",
    "content": "package jadx.gui.utils.ui;\n\nimport javax.swing.JMenuItem;\n\npublic class SimpleMenuItem extends JMenuItem {\n\n\tpublic SimpleMenuItem(String text, Runnable action) {\n\t\tsuper(text);\n\t\taddActionListener(ev -> action.run());\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/java/jadx/gui/utils/ui/ZoomActions.java",
    "content": "package jadx.gui.utils.ui;\n\nimport java.awt.Container;\nimport java.awt.Font;\nimport java.awt.event.KeyEvent;\n\nimport javax.swing.ActionMap;\nimport javax.swing.InputMap;\nimport javax.swing.JComponent;\nimport javax.swing.KeyStroke;\n\nimport jadx.gui.settings.JadxSettings;\nimport jadx.gui.settings.font.FontAdapter;\nimport jadx.gui.settings.font.FontSettings;\nimport jadx.gui.ui.codearea.SmaliArea;\nimport jadx.gui.utils.UiUtils;\n\npublic class ZoomActions {\n\tprivate final JComponent component;\n\tprivate final JadxSettings settings;\n\tprivate final Runnable update;\n\n\tpublic static void register(JComponent component, JadxSettings settings, Runnable update) {\n\t\tZoomActions actions = new ZoomActions(component, settings, update);\n\t\tactions.register();\n\t}\n\n\tprivate ZoomActions(JComponent component, JadxSettings settings, Runnable update) {\n\t\tthis.component = component;\n\t\tthis.settings = settings;\n\t\tthis.update = update;\n\t}\n\n\tprivate void register() {\n\t\tString zoomIn = \"TextZoomIn\";\n\t\tString zoomOut = \"TextZoomOut\";\n\t\tint ctrlButton = UiUtils.ctrlButton();\n\t\tInputMap inputMap = component.getInputMap();\n\t\tinputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, ctrlButton), zoomIn);\n\t\tinputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, ctrlButton), zoomIn);\n\t\tinputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, ctrlButton), zoomIn);\n\t\tinputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, ctrlButton), zoomOut);\n\t\tinputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, ctrlButton), zoomOut);\n\t\tActionMap actionMap = component.getActionMap();\n\t\tactionMap.put(zoomIn, new ActionHandler(e -> textZoom(1)));\n\t\tactionMap.put(zoomOut, new ActionHandler(e -> textZoom(-1)));\n\n\t\tcomponent.addMouseWheelListener(e -> {\n\t\t\tif (e.getModifiersEx() == UiUtils.ctrlButton()) {\n\t\t\t\ttextZoom(e.getWheelRotation() < 0 ? 1 : -1);\n\t\t\t\te.consume();\n\t\t\t} else {\n\t\t\t\t// pass event to parent component, needed for scroll in JScrollPane\n\t\t\t\tContainer parent = component.getParent();\n\t\t\t\tif (parent != null) {\n\t\t\t\t\tparent.dispatchEvent(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void textZoom(int change) {\n\t\tFontSettings fontSettings = settings.getFontSettings();\n\t\tFontAdapter fontAdapter;\n\t\tif (component instanceof SmaliArea) {\n\t\t\tfontAdapter = fontSettings.getSmaliFontAdapter();\n\t\t} else {\n\t\t\tfontAdapter = fontSettings.getCodeFontAdapter();\n\t\t}\n\t\tfontAdapter.setFont(changeFontSize(fontAdapter.getFont(), change));\n\n\t\tsettings.sync();\n\t\tupdate.run();\n\t}\n\n\tprivate Font changeFontSize(Font font, int change) {\n\t\tfloat newSize = font.getSize() + change;\n\t\tif (newSize < 2) {\n\t\t\t// ignore change\n\t\t\treturn font;\n\t\t}\n\t\treturn font.deriveFont(newSize);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/main/resources/files/jadx-gui.desktop.tmpl",
    "content": "[Desktop Entry]\nName=JADX GUI\nComment=Dex to Java decompiler\nIcon=jadx\nExec={{launchScriptPath}} %f\nTerminal=false\nType=Application\nCategories=Development;Java;\nKeywords=Java;Decompiler;\nStartupWMClass=jadx-gui-JadxGUI\nMimeType=application/vnd.android.package-archive;application/java-archive;\n"
  },
  {
    "path": "jadx-gui/src/main/resources/i18n/Messages_de_DE.properties",
    "content": "language.name=Deutsch\n\nmenu.file=Datei\nmenu.view=Ansicht\nmenu.recent_projects=Aktuelle Projekte\nmenu.no_recent_projects=Keine aktuellen Projekte\nmenu.preferences=Einstellungen\nmenu.sync=Mit Editor synchronisieren\nmenu.flatten=Codepaket erweitern\n#menu.enable_preview_tab=Enable Preview Tab\nmenu.heapUsageBar=Speicherverbrauchsleiste anzeigen\nmenu.alwaysSelectOpened=Immer geöffnete Datei/Klasse auswählen\nmenu.dock_log=Dock-Protokollanzeige\nmenu.dock_quick_tabs=Dock-Schnellzugriffsleisten\nmenu.navigation=Navigation\nmenu.text_search=Textsuche\nmenu.class_search=Klassen-Suche\nmenu.comment_search=Kommentar suchen\nmenu.go_to_main_activity=Zur Hauptaktivität gehen\nmenu.go_to_application=Zur Anwendung gehen\nmenu.go_to_android_manifest=Zur AndroidManifest.xml gehen\nmenu.tools=Werkzeuge\nmenu.plugins=Plugins\nmenu.decompile_all=Alle Klassen dekompilieren\nmenu.reset_cache=Code-Cache zurücksetzen\nmenu.deobfuscation=Deobfuskierung\nmenu.log=Protokollanzeige\nmenu.create_desktop_entry=Desktop-Eintrag erstellen\nmenu.help=Hilfe\nmenu.about=Über\nmenu.quark=Quark Engine\nmenu.update_label=Neue Version %s verfügbar!\n#menu.hex_viewer=Hex Viewer\n\nfile.open_action=Datei öffnen…\nfile.add_files_action=Dateien hinzufügen…\nfile.open_title=Datei öffnen\nfile.open_project=Projekt öffnen\nfile.new_project=Neues Projekt\nfile.save_project=Projekt speichern\nfile.save_project_as=Projekt speichern als…\nfile.reload=Dateien neu laden\nfile.live_reload=Live nachladen\nfile.live_reload_desc=Dateien bei Änderungen autom. neuladen\nfile.open_mappings=Zuordnungen öffnen...\nfile.save_mappings=Zuordnungen speichern\nfile.save_mappings_as=Zuordnungen speichern als...\nfile.close_mappings=Zuordnungen exportieren als…\nfile.save_all=Alles speichern\nfile.save=Speichern\nfile.export=Projekt exportieren\nfile.save_all_msg=Verzeichnis für das Speichern dekompilierter Ressourcen auswählen\nfile.exit=Beenden\nfile.export_node=Datei exportieren\n\nstart_page.title=Startseite\nstart_page.start=Start\nstart_page.recent=Aktuelle Projekte\n#start_page.list.delete_recent_project=Delete project\n#start_page.list.delete_recent_project.tooltip=Delete project from recent list\n\ntree.inputs_title=Eingaben\ntree.input_files=Dateien\ntree.input_scripts=Skripte\ntree.sources_title=Quellcode\ntree.resources_title=Ressourcen\ntree.loading=Laden…\ntree.pinned_tabs=Angepinnte Registerkarten\ntree.open_tabs=Offene Registerkarten\ntree.bookmarked_tabs=Mit Lesezeichen versehene Registerkarten\n\nprogress.load=Laden\nprogress.save_mappings=Zuordnungen werden gespeichert\nprogress.decompile=Dekompilieren\nprogress.canceling=Breche ab\n\nerror_dialog.title=Fehler\nerror_dialog.not_found=%s nicht gefunden\n#error_dialog.not_found_file=File not found at path:\\n%s\n#error_dialog.path_is_directory=The specified path is a directory:\\n%s\n#error_dialog.cannot_read=Cannot read file:\\n%s\n#error_dialog.open_failed=Failed to open file:\\n%s\n#error_dialog.invalid_path_format=Invalid file path:\\n%s\n#error_dialog.desktop_unsupported=Desktop is not supported in this environment.\n\nsearch.previous=Vorheriges\nsearch.next=Nächstes\nsearch.mark_all=Alles markieren\nsearch.regex=Regex\nsearch.match_case=Groß/Kleinschreibung beachten\nsearch.whole_word=Ganzes Wort\nsearch.find=Finden\nsearch.results=%s%d Ergebnisse\n#search.match_of=Match %d of %d\n#search.match_found=Match found\n#search.match_not_found=No Matches found\n#search.single_match=Single match found\n#search.find_type_text=Find by text\n#search.find_type_hex=Find by hex\n\ntabs.copy_class_name=Klassennamen kopieren\ntabs.close=Schließen\ntabs.closeOthers=Andere schließen\ntabs.unpin=Lösen\ntabs.unpin_all=Alle lösen\ntabs.bookmark=Lesezeichen setzen\ntabs.unbookmark=Lesezeichen entfernen\ntabs.unbookmark_all=Alle Lesezeichen entfernen\ntabs.pin=Anheften\ntabs.closeAll=Alles schließen\ntabs.closeAllRight=Alles rechts schließen\n#tabs.closeAllLeft=Close All Left\ntabs.code=Code\ntabs.smali=Smali\ntabs.smali_bytecode=Smali+Bytecode\n\nnav.back=Zurück\nnav.forward=Vorwärts\n\nmessage.taskTimeout=Aufgabe hat Zeitlimit von %d ms überschritten.\nmessage.userCancelTask=Aufgabe wurde vom Benutzer abgebrochen.\nmessage.memoryLow=Jadx hat zu wenig Speicherplatz. Bitte mit erhöhter maximaler Heap-Größe erneut starten.\nmessage.taskError=Die Aufgabe ist durch Fehler fehlgeschlagen (siehe Protokoll für Details).\nmessage.errorTitle=Fehler\nmessage.load_errors=Laden fehlgeschlagen.\\nAnzahl der Fehler: %d\\nKlicke auf OK, um die Protokollansicht zu öffnen.\nmessage.no_classes=Keine Klassen geladen, nichts zu dekompilieren!\nmessage.enter_valid_path=Gültigen Pfad zum Speichern eingeben!\n\nmessage.saveIncomplete=<html>Speichern unvollständig.<br> %s<br> %d Klassen oder Ressourcen wurden nicht gespeichert!</html>\nmessage.indexIncomplete=<html>Index einiger Klassen übersprungen.<br> %s<br> %d Klassen wurden nicht indiziert und werden nicht in den Suchergebnissen erscheinen!</html>\nmessage.indexingClassesSkipped=<html>Jadx hat nur noch wenig Speicherplatz, daher wurden %d Klassen nicht indiziert.<br>Wenn du möchtest, dass alle Klassen indiziert werden, dann starte Jadx neu mit erhöhter maximaler Heap-Größe.</html>\nmessage.enter_new_name=Neuen Namen eingeben\nmessage.could_not_rename=Die Datei kann nicht umbenannt werden\nmessage.confirm_remove_script=Willst du das Skript wirklich entfernen?\nmessage.desktop_entry_creation_error=Erstellen des Desktop-Eintrags fehlgeschlagen (Details findest du im Protokoll).\nmessage.desktop_entry_creation_success=Desktop-Eintrag erfolgreich erstellt!\nmessage.success_title=Erfolg\n#message.unable_preview_font=Unable preview font\n\nheapUsage.text=JADX-Speicherauslastung: %.2f GB von %.2f GB (%.2f GB Höhepunkt)\n\ncommon_dialog.ok=OK\ncommon_dialog.cancel=Abbrechen\ncommon_dialog.add=Hinzufügen\ncommon_dialog.update=Aktualisieren\ncommon_dialog.remove=Entfernen\ncommon_dialog.reset=Zurücksetzen\n\nfile_dialog.supported_files=Unterstützte Dateien\nfile_dialog.load_dir_title=Verzeichnis laden\nfile_dialog.load_dir_confirm=Alle Dateien aus dem Verzeichnis laden?\n\nsearch_dialog.open=Öffnen\nsearch_dialog.cancel=Beenden\nsearch_dialog.open_by_name=Nach Text suchen:\nsearch_dialog.search_button=Suche\nsearch_dialog.search_history=Suchverlauf\nsearch_dialog.auto_search=Automatische Suche\nsearch_dialog.search_in=Suche in Definitionen von:\nsearch_dialog.class=Klasse\nsearch_dialog.method=Methode\nsearch_dialog.field=Feld\nsearch_dialog.code=Code\nsearch_dialog.ignorecase=Groß/Kleinschreibung ignorieren\nsearch_dialog.load_more=Mehr laden\nsearch_dialog.load_all=Alle laden\nsearch_dialog.stop=Stopp\nsearch_dialog.results_incomplete=%d+ gefunden\nsearch_dialog.results_complete=%d gefunden (komplett)\nsearch_dialog.resources_load_errors=Ladefehler: %d\nsearch_dialog.resources_skip_by_size=Nach Größe übersprungen: %d\nsearch_dialog.resources_check_logs=(Klicken, um die Protokolle zu überprüfen)\nsearch_dialog.col_node=Knoten\nsearch_dialog.col_code=Code\nsearch_dialog.sort_results=Ergebnisse sortieren\nsearch_dialog.regex=Regex\nsearch_dialog.active_tab=Nur aktive Registerkarte\nsearch_dialog.comments=Kommentare\nsearch_dialog.resource=Ressource\nsearch_dialog.keep_open=Offen halten\nsearch_dialog.tip_searching=Suchen…\nsearch_dialog.limit_package=Begrenzung auf Paket:\n#search_dialog.res_text=Text\n#search_dialog.res_binary=Binary\nsearch_dialog.package_not_found=Kein passendes Paket gefunden\nsearch_dialog.copy=alles kopieren\n\nusage_dialog.title=Verwendungssuche\nusage_dialog.label=Verwendungen von:\n\n#usage_dialog_plus.title=Usage tree search\nusage_dialog_plus.jump_to=Zur aktuellen Position springen\nusage_dialog_plus.copy_path=Verwendungspfad kopieren\nusage_dialog_plus.search_complete=Suche abgeschlossen\n#usage_dialog_plus.code_view=Code View\n#usage_dialog_plus.select_node=Select Node\n#usage_dialog_plus.code_for=Code for %s\n#usage_dialog_plus.expand_usages=Expand Data\n\ncomment_dialog.title.add=Code-Kommentar hinzufügen\ncomment_dialog.title.update=Code-Kommentar aktualisieren\ncomment_dialog.label=Kommentar:\ncomment_dialog.style=Stil:\ncomment_dialog.usage=Umschalt + Eingabe verwenden, um eine neue Zeile zu beginnen\n\nrename_dialog.class_help=Gib den vollständigen Namen ein, um die Klasse in ein anderes Paket zu verschieben. Beginne mit '.', um die Klasse in das (leere) Vorgabepaket zu verschieben.\n\nexport_dialog.title=In Quellcode exportieren\nexport_dialog.save_path=Speicherpfad:\nexport_dialog.browse=Durchsuchen\nexport_dialog.export_options=Exportoptionen\nexport_dialog.export_gradle=Als Gradle-Projekt exportieren\n#export_dialog.export_gradle_type=Gradle template:\n\nlog_viewer.title=Protokollanzeige\nlog_viewer.log_level=Protokollstufe:\nlog_viewer.mode=Modus:\nlog_viewer.modes=Alle|Alle Skripte|Aktuelles Skript\nlog_viewer.hide=Ausblenden\nlog_viewer.dock=Andocken\nlog_viewer.undock=Abdocken\nlog_viewer.clear=Löschen\n\nabout_dialog.title=Über JADX\n\npreferences.title=Einstellungen\npreferences.deobfuscation=Deobfuskierung\npreferences.appearance=Erscheinung\npreferences.shortcuts=Tastenkürzel\npreferences.select_shortcuts=Wähle eine bestimmte Tastenkürzelgruppe aus\npreferences.decompile=Dekompilierung\npreferences.plugins=Plugins\npreferences.project=Projekt\npreferences.other=Sonstiges\npreferences.language=Sprache\npreferences.lineNumbersMode=Editor Zeilennummern-Modus\npreferences.jumpOnDoubleClick=Sprung bei Doppelklick aktivieren\npreferences.useAlternativeFileDialog=Alternativen Dateidialog verwenden\npreferences.check_for_updates=Beim Starten nach Updates suchen\npreferences.useDx=dx/d8 zur Konvertierung von Java-Bytecode verwenden\npreferences.decompilationMode=Dekompilierungsmodus\npreferences.codeCacheMode=Cache-Code-Modus\n#preferences.codeCacheMode.memory=Memory\n#preferences.codeCacheMode.memory.desc=Everything in memory: fast search, slow reopen, high memory usage\n#preferences.codeCacheMode.diskWithCache=Disk with cache\n#preferences.codeCacheMode.diskWithCache.desc=Code saved on disk with in memory cache: medium search, fast reopen, medium memory usage\n#preferences.codeCacheMode.disk=Disk\n#preferences.codeCacheMode.disk.desc=Everything on disk: slow search, fast reopen, low memory usage\npreferences.usageCacheMode=Nutzungsdaten-Cache-Modus\npreferences.showInconsistentCode=Inkonsistenten Code anzeigen\npreferences.escapeUnicode=Unicode maskieren\npreferences.replaceConsts=Konstanten ersetzen\npreferences.respectBytecodeAccessModifiers=Modifikatoren für Bytecode-Zugriff beachten\npreferences.useImports=Importanweisungen verwenden\npreferences.useDebugInfo=Debug-Infos verwenden\npreferences.inlineAnonymous=Anonyme Inline-Klassen\npreferences.inlineMethods=Inline-Methoden\n#preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas\n#preferences.moveInnerClasses=Move inner classes into parent\n#preferences.extractFinally=Extract finally block\n#preferences.restoreSwitchOverString=Restore switch over string\npreferences.fsCaseSensitive=Dateisystem unterscheidet zwischen Groß/Kleinschreibung\npreferences.skipResourcesDecode=Ressourcen nicht dekodieren\npreferences.skipSourcesDecode=Quellcode nicht dekompilieren\npreferences.useKotlinMethodsForVarNames=Kotlin-Methoden für die Umbenennung von Variablen verwenden\npreferences.commentsLevel=Stufe der Code-Kommentare\npreferences.saveOption=Einstellungen automatisch speichern\npreferences.threads=Verarbeitungs-Thread-Anzahl\npreferences.excludedPackages=Ausgeschlossene Pakete\npreferences.excludedPackages.tooltip=Liste der durch Leerzeichen getrennten Paketnamen, die nicht dekompiliert oder indiziert werden (spart RAM)\npreferences.excludedPackages.button=Bearbeiten\npreferences.excludedPackages.editDialog=<html>Liste der durch Leerzeichen getrennten Paketnamen, die nicht dekompiliert oder indiziert werden. (spart RAM)<br>z.B. <code>android.support</code></html>\npreferences.cfg=CFG-Grafiken für Methoden generieren (im 'dot'-Format)\npreferences.raw_cfg=RAW CFG-Grafiken generieren\npreferences.xposed_codegen_language=Xposed-Code-Generierungssprache\npreferences.update_channel=Jadx-Updatekanal\n#preferences.disable_tooltip_on_hover=Disable tooltip on hover\npreferences.integerFormat=Ganzzahlformat\n#preferences.typeUpdatesCountLimit=Update type limit count\n#preferences.ui_zoom=UI Zoom factor\n#preferences.apply_ui_zoom_to_fonts=Apply UI zoom to fonts\n#preferences.ui_font=UI font\npreferences.code_font=Schriftart ändern\npreferences.smali_font=Monospaced-Schriftart (Smali/Hex)\npreferences.laf_theme=Thema\n#preferences.dynamic_editor_theme=Use UI theme colors\npreferences.theme=Thema ändern\npreferences.start_jobs=Autom. Hintergrunddekompilierung starten\npreferences.select_font=Ändern\npreferences.deobfuscation_on=Deobfuskierung einschalten\npreferences.generated_renames_mapping_file_mode=Umgang mit Map-Dateien\npreferences.deobfuscation_min_len=Minimale Namenlänge\npreferences.deobfuscation_max_len=Maximale Namenlänge\npreferences.deobfuscation_res_name_source=Bessere Ressourcennamenquelle\n#preferences.deobfuscation_res_use_headers=Use headers for detect resource extensions\npreferences.deobfuscation_whitelist=Pakete und Klassen von Deobfuskierung ausschließen\npreferences.deobfuscation_whitelist.editDialog=Weiße Liste für Deobfuskierung\npreferences.deobfuscation_whitelist.tooltip=Liste der durch ':' getrennten Pakete (Suffix '.*') und Klassenamen, die nicht deobfuskiert werden sollen\npreferences.save=Speichern\npreferences.cancel=Abbrechen\npreferences.reset=Zurücksetzen\npreferences.reset_message=Einstellungen auf Vorgabewerte zurücksetzen?\npreferences.reset_title=Einstellungen zurücksetzen\npreferences.copy=In Zwischenablage kopieren\npreferences.copy_message=Alle Einstellungswerte wurden in die Zwischenablage kopiert\npreferences.rename=Bezeichner umbenennen\npreferences.rename_case=Um Probleme mit der Groß-/Kleinschreibung zu beheben\npreferences.rename_valid=Um sie gültig zu machen\npreferences.rename_printable=Um druckbar zu machen\npreferences.rename_use_source_name_as_class_name_alias=Quelldateiname als Klassennamen-Alias verwenden\npreferences.rename_source_name_repeat_limit=Verwendung des Quellnamens erlauben, wenn dieser seltener vorkommt als\npreferences.search_results_per_page=Ergebnisse pro Seite (0 - keine Begrenzung)\npreferences.res_file_ext=Ressourcendatei-Erweiterungen ('xml|html', * für alle)\npreferences.res_skip_file=Ressourcendateien überspringen, wenn sie größer sind (MB) (0 - ausschalten)\npreferences.tab_dnd_appearance=Erscheinungsbild der Registerkarte \"Verschieben\"\n\n#preferences.plugins.manage=Manage plugins\npreferences.plugins.install=Plugin installieren\npreferences.plugins.install_btn=Installieren\npreferences.plugins.uninstall_btn=Deinstallieren\npreferences.plugins.disable_btn=Ausschalten\npreferences.plugins.enable_btn=Einschalten\npreferences.plugins.location_id_label=Standort-ID:\npreferences.plugins.plugin_jar=Plugin-Jar auswählen\npreferences.plugins.plugin_jar_label=oder\npreferences.plugins.update_all=Alle aktualisieren\npreferences.plugins.details=Plugin-Details\npreferences.plugins.task.installing=Installation des Plugins\npreferences.plugins.task.uninstalling=Deinstallation des Plugins\npreferences.plugins.task.updating=Aktualisierung der Plugins\npreferences.plugins.task.downloading_list=Herunterladen der Plugin-Liste\npreferences.plugins.task.status=Ändern des Plugin-Status\n\npreferences.cache=Cache\npreferences.cache.location=Cache-Speicherort\npreferences.cache.location_default=App-Cache-Systemverzeichnis\npreferences.cache.location_local=Gleiches Verzeichnis wie Projektdatei\npreferences.cache.location_custom=Benutzerdefinierter Speicherort:\npreferences.cache.change_notice=* für vorhandene Caches wird die Änderung nach Cache-Entfernung oder manuellem Zurücksetzen angewendet\npreferences.cache.table.title=Cache-Liste\npreferences.cache.table.project=Cache für Projekt\npreferences.cache.table.size=Datenträgernutzung\npreferences.cache.btn.usage=Nutzung berechnen\npreferences.cache.btn.delete_selected=Ausgewähltes löschen\npreferences.cache.btn.delete_all=Alles löschen\npreferences.cache.task.usage=Berechnung der Cache-Größe\npreferences.cache.task.delete=Löschen der Cache\n\nmsg.open_file=Bitte Datei öffnen\nmsg.saving_sources=Quellcodes werden gespeichert\nmsg.language_changed_title=Sprache geändert\nmsg.language_changed=Die neue Sprache wird beim nächsten Start der Anwendung angezeigt.\nmsg.warning_title=Warnung\nmsg.common_mouse_shortcut=Dies ist eine häufig verwendete Taste. Möchtest du sie wirklich an eine Aktion binden?\nmsg.duplicate_shortcut=Der Tastenkürzel %s ist bereits in der Aktion „%s“ aus der Kategorie „%s“ festgelegt, fortfahren?\nmsg.cmd_select_class_error=Klasse\\n%s auswählen nicht möglich\\nSie existiert nicht.\nmsg.cant_add_comment=Kann hier keinen Kommentar hinzufügen\n#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font \"%s\". Show chars?\n#msg.non_displayable_chars.title=Undisplayed Strings\n\n#methods_dialog.title=Select methods\n\npopup.bytecode_col=Dalvik-Bytecode anzeigen\npopup.line_wrap=Zeilenumbruch\npopup.undo=Rückgängig\npopup.redo=Wiederholen\npopup.cut=Ausschneiden\npopup.copy=Kopieren\npopup.paste=Einfügen\npopup.delete=Löschen\npopup.select_all=Alle auswählen\npopup.frida=Als Frida-Schnipsel kopieren\npopup.xposed=Als Xposed-Schnipsel kopieren\npopup.find_usage=Verwendung suchen\npopup.go_to_declaration=Zur Erklärung gehen\npopup.exclude=Ausschließen\npopup.exclude_packages=Pakete ausschließen\npopup.convert_number=Conversion als Kommentar hinzufügen\n#popup.view_call_graph=View call graph\n#popup.view_call_graph_description=Show call chains to this function\n#popup.view_class_graph=View inheritance graph\n#popup.view_class_graph_description=Show inheritance tree for this class\n#popup.view_class_method_graph=View methods graph\n#popup.view_class_method_graph_description=Show all methods for this class\n#popup.cfg_submenu=View control flow graph\n#popup.view_cfg=Regular\n#popup.view_cfg_description=Show regular control flow graph for this function (enable in File->Preferences->Other)\n#popup.view_raw_cfg=Raw\n#popup.view_raw_cfg_description=Show control flow graph with raw instructions for this function (enable in File->Preferences->Other)\n#popup.view_region_cfg=Region\n#popup.view_region_cfg_description=Show regioned control flow graph for this function (enable in File->Preferences->Other)\npopup.add_comment=Kommentar\npopup.update_comment=Kommentar aktualisieren\npopup.search_comment=Kommentar suchen\npopup.rename=Umbennen\npopup.search=Suche \"%s\"\npopup.search_global=Globale Suche \"%s\"\npopup.remove=Entfernen\npopup.add_files=Dateien hinzufügen\npopup.add_scripts=Skripte hinzufügen\npopup.new_script=Neues Skript\npopup.json_prettify=JSON-Verschönerung\npopup.export=Exportieren\n#popup.usage_dialog_plus=Usage Tree Search\n#popup.copy_as=Copy as...\n#popup.copy_as_hex=Copy as HEX\n#popup.copy_as_string=Copy as String\n#popup.copy_offset=Copy offset\n\nscript.run=Ausführen\nscript.save=Speichern\nscript.auto_complete=Automatisch vervollständigen\nscript.check=Überprüfen\nscript.format=Neu formatieren\nscript.log=Protokoll anzeigen\n\n#encoding_dialog.title=Encoding\n#encoding_dialog.message=Select encoding:\n\nexclude_dialog.title=Paketauswahl\nexclude_dialog.select_all=Alles auswählen\nexclude_dialog.deselect=Abwählen\nexclude_dialog.invert=Invertieren\n\nconfirm.save_as_title=Speichern unter bestätigen\nconfirm.save_as_message=%s existiert bereits.\\nErsetzen?\nconfirm.not_saved_title=Projekt speichern\nconfirm.not_saved_message=Das aktuelle Projekt speichern, bevor du fortfährst?\nconfirm.remember=Meine Entscheidung merken\n\ncertificate.cert_type=Typ\ncertificate.serialSigVer=Version\ncertificate.serialNumber=Seriennummer\ncertificate.cert_subject=Subjekt\ncertificate.serialValidFrom=Gültig ab\ncertificate.serialValidUntil=Gültig bis\ncertificate.serialPubKeyType=Öffentlicher Schlüsseltyp\ncertificate.serialPubKeyExponent=Exponent\ncertificate.serialPubKeyModulus=Modulus\ncertificate.serialPubKeyModulusSize=Modulusgröße (Bits)\ncertificate.serialSigType=Signaturtyp\ncertificate.serialSigOID=Signatur-OID\ncertificate.serialMD5=MD5-Fingerabdruck\ncertificate.serialSHA1=SHA-1-Fingerabdruck\ncertificate.serialSHA256=SHA-256-Fingerabdruck\ncertificate.serialPubKeyY=Y\n\napkSignature.signer=Signierer\n#apkSignature.loading=Loading signature...\napkSignature.verificationSuccess=Signaturprüfung erfolgreich abgeschlossen\napkSignature.verificationFailed=Signaturprüfung fehlgeschlagen\napkSignature.signatureSuccess=Gültige APK-Signatur v%d gefunden\napkSignature.signatureFailed=Ungültige APK-Signatur v%d gefunden\napkSignature.errors=Fehler\napkSignature.warnings=Warnhinweise\napkSignature.exception=APK-Verifizierung fehlgeschlagen\napkSignature.unprotectedEntry=Dateien, die nicht durch eine APK-Signatur v1 geschützt sind. Unbefugte Änderungen an diesen Einträgen werden nur von einer APK-Signatur v2 oder neuer erkannt.\n\nissues_panel.label=Probleme:\nissues_panel.errors=%d Fehler\nissues_panel.warnings=%d Warnungen\nissues_panel.tooltip=In Protokollansicht öffnen\n\ndebugger.process_selector=Zu debuggenden Prozess auswählen\ndebugger.step_into=Einzelschritt (F7)\ndebugger.step_over=Prozedurschritt (F8)\ndebugger.step_out=>Rücksprung (Umschalt + F8)\ndebugger.run=Ausführen (F9)\ndebugger.stop=Debugger anhalten und App beenden\ndebugger.pause=Pause\ndebugger.rerun=Wiederholen\ndebugger.cfm_dialog_title=Während der Fehlersuche beenden\ndebugger.cfm_dialog_msg=Bist du dir sicher, dass du den Debugger beenden willst?\n\ndebugger.popup_set_value=Wert einstellen\ndebugger.popup_change_to_zero=Wechsel zu 0\ndebugger.popup_change_to_one=Wechsel zu 1\ndebugger.popup_copy_value=Wert kopieren\n\nlogcat.pause=Logcat anhalten\nlogcat.start=Logcat fortsetzen\nlogcat.clear=Logcat löschen\n\nlogcat.error_fail_start=Logcat konnte nicht gestartet werden\nlogcat.process=Prozess\nlogcat.level=Level\nlogcat.default=Vorgabe\nlogcat.verbose=Ausführlich\nlogcat.debug=Debug\nlogcat.info=Info\nlogcat.warn=Warnen\nlogcat.error=Fehler\nlogcat.fatal=Fatal\nlogcat.silent=Still\nlogcat.logcat=Logcat\nlogcat.select_attached=Angehängtes auswählen\nlogcat.select_all=Alles auswählen\nlogcat.unselect_all=Alles abwählen\n\nset_value_dialog.label_value=Wert\nset_value_dialog.btn_set=Wert einstellen\nset_value_dialog.title=Wert einstellen\nset_value_dialog.neg_msg=Wert einstellen fehlgeschlagen\nset_value_dialog.sel_type=Wähle einen Typ aus, um einen Wert einzustellen.\n\nadb_dialog.addr=ADB-Adresse\nadb_dialog.port=ADB-Port\nadb_dialog.path=ADB-Pfad\nadb_dialog.launch_app=App starten\nadb_dialog.start_server=ADB-Server starten\nadb_dialog.refresh=Aktualisieren\nadb_dialog.tip_devices=%d Geräte\nadb_dialog.device_node=Gerät\nadb_dialog.missing_path=Muss ADB-Pfad angeben, um einen ADB-Server zu starten.\nadb_dialog.waiting=Auf Verbindung zum ADB-Server warten…\nadb_dialog.connecting=Mit ADB-Server verbinden, Adresse: %s:%s…\nadb_dialog.connect_okay=ADB-Server verbunden, Adresse: %s:%s\nadb_dialog.connect_fail=ADB-Serververbindung fehlgeschlagen.\nadb_dialog.disconnected=ADB-Server getrennt.\nadb_dialog.start_okay=ADB-Server auf Port gestartet: %s.\nadb_dialog.start_fail=ADB-Server auf Port starten fehlgeschlagen: %s!\nadb_dialog.forward_fail=Weiterleitung ist aus verschiedenen Gründen fehlgeschlagen.\nadb_dialog.being_debugged_msg=Dieser Prozess scheint fehlerhaft zu sein, sollen wir fortfahren?\nadb_dialog.unknown_android_ver=Android-Version nicht erhalten, Android 8 als Vorgabe verwenden?\nadb_dialog.being_debugged_title=Es wird debuggt durch andere.\nadb_dialog.init_dbg_fail=Debugger starten fehlgeschlagen.\nadb_dialog.msg_read_mani_fail=AndroidManifest.xml dekodieren fehlgeschlagen\nadb_dialog.no_devices=Konnte kein Gerät finden, um die App zu starten.\nadb_dialog.restart_while_debugging_title=Neustart während der Fehlersuche\nadb_dialog.restart_while_debugging_msg=Du debuggst eine App, möchtest du wirklich eine Sitzung neu starten?\nadb_dialog.starting_debugger=Debugger starten…\n\naction.variant=%s (Variante)\naction_category.menu_toolbar=Menü / Symbolleiste\n#action_category.hex_viewer=View / Hex Viewer\naction_category.code_area=Code-Bereich\naction_category.plugin_script=Plugin-Skript\n\n#hex_viewer.show_inspector=Show Inspector\n#hex_viewer.change_encoding=Change Encoding\n#hex_viewer.goto_address=Go To Address\n#hex_viewer.enter_address=Enter address range:\n#hex_viewer.find=Find\n\n#graph_viewer.long_names=Show full names\n#graph_viewer.overrides=Show overrides\n#graph_viewer.callee_depth=Down depth\n#graph_viewer.caller_depth=Up depth\n#graph_viewer.default_error=Failed to view graph\n#graph_viewer.file_not_found_error=Failed to load graph file\n#graph_viewer.image_too_large=Failed to render graph: graph too large\n#graph_viewer.image_too_small=Failed to render graph: graph too small\n#graph_viewer.file_failure=Error in File Operation\n#graph_viewer.save_graph=Save graph\n\n#graph_viewer.default_title=Graph Viewer\n#graph_viewer.method_graph.title=Methods Graph\n#graph_viewer.call_graph.title=Call Graph\n#graph_viewer.inheritance_graph.title=Inheritance Graph\n#graph_viewer.cfg.title=Control Flow Graph\n"
  },
  {
    "path": "jadx-gui/src/main/resources/i18n/Messages_en_US.properties",
    "content": "language.name=English\n\nmenu.file=File\nmenu.view=View\nmenu.recent_projects=Recent projects\nmenu.no_recent_projects=No recent projects\nmenu.preferences=Preferences\nmenu.sync=Select in Tree\nmenu.flatten=Use Flatten Packages\nmenu.enable_preview_tab=Enable Preview Tab\nmenu.heapUsageBar=Show Memory Usage Bar\nmenu.alwaysSelectOpened=Auto Select in Tree\nmenu.dock_log=Dock Log Viewer\nmenu.dock_quick_tabs=Show Quick Tabs\nmenu.navigation=Navigation\nmenu.text_search=Text search\nmenu.class_search=Class search\nmenu.comment_search=Comment search\nmenu.go_to_main_activity=Go to main Activity\nmenu.go_to_application=Go to Application\nmenu.go_to_android_manifest=Go to AndroidManifest.xml\nmenu.tools=Tools\nmenu.plugins=Plugins\nmenu.decompile_all=Decompile all classes\nmenu.reset_cache=Reset code cache\nmenu.deobfuscation=Deobfuscation\nmenu.log=Log Viewer\nmenu.create_desktop_entry=Create Desktop Entry\nmenu.help=Help\nmenu.about=About\nmenu.quark=Quark Engine\nmenu.update_label=New version %s available!\nmenu.hex_viewer=Hex Viewer\n\nfile.open_action=Open files ...\nfile.add_files_action=Add files\nfile.open_title=Open file\nfile.open_project=Open project\nfile.new_project=New project\nfile.save_project=Save project\nfile.save_project_as=Save project as...\nfile.reload=Reload files\nfile.live_reload=Live reload\nfile.live_reload_desc=Auto reload files on changes\nfile.open_mappings=Open mappings...\nfile.save_mappings=Save mappings\nfile.save_mappings_as=Save mappings as...\nfile.close_mappings=Close mappings\nfile.save_all=Save all\nfile.save=Save\nfile.export=Export project\nfile.save_all_msg=Select directory for save decompiled sources\nfile.exit=Exit\nfile.export_node=Export file\n\nstart_page.title=Start page\nstart_page.start=Start\nstart_page.recent=Recent projects\nstart_page.list.delete_recent_project=Delete project\nstart_page.list.delete_recent_project.tooltip=Delete project from recent list\n\ntree.inputs_title=Inputs\ntree.input_files=Files\ntree.input_scripts=Scripts\ntree.sources_title=Source code\ntree.resources_title=Resources\ntree.loading=Loading...\ntree.pinned_tabs=Pinned Tabs\ntree.open_tabs=Open Tabs\ntree.bookmarked_tabs=Bookmarked Tabs\n\nprogress.load=Loading\nprogress.save_mappings=Saving mappings\nprogress.decompile=Decompiling\nprogress.canceling=Canceling\n\nerror_dialog.title=Error\nerror_dialog.not_found=%s not found\nerror_dialog.not_found_file=File not found at path:\\n%s\nerror_dialog.path_is_directory=The specified path is a directory:\\n%s\nerror_dialog.cannot_read=Cannot read file:\\n%s\nerror_dialog.open_failed=Failed to open file:\\n%s\nerror_dialog.invalid_path_format=Invalid file path:\\n%s\nerror_dialog.desktop_unsupported=Desktop is not supported in this environment.\n\nsearch.previous=Previous\nsearch.next=Next\nsearch.mark_all=Mark All\nsearch.regex=Regex\nsearch.match_case=Match Case\nsearch.whole_word=Whole word\nsearch.find=Find\nsearch.results=%s%d results\nsearch.match_of=Match %d of %d\nsearch.match_found=Match found\nsearch.match_not_found=No Matches found\nsearch.single_match=Single match found\nsearch.find_type_text=Find by text\nsearch.find_type_hex=Find by hex\n\ntabs.copy_class_name=Copy Name\ntabs.close=Close\ntabs.closeOthers=Close Others\ntabs.unpin=Unpin\ntabs.unpin_all=Unpin All\ntabs.bookmark=Bookmark\ntabs.unbookmark=Unbookmark\ntabs.unbookmark_all=Unbookmark All\ntabs.pin=Pin\ntabs.closeAll=Close All\ntabs.closeAllRight=Close All Right\ntabs.closeAllLeft=Close All Left\ntabs.code=Code\ntabs.smali=Smali\ntabs.smali_bytecode=Smali+Bytecode\n\nnav.back=Back\nnav.forward=Forward\n\nmessage.taskTimeout=Task exceeded time limit of %d ms.\nmessage.userCancelTask=Task was canceled by user.\nmessage.memoryLow=Jadx is running low on memory. Please restart with increased maximum heap size.\nmessage.taskError=Task failed with error (check log for details).\nmessage.errorTitle=Error\nmessage.load_errors=Load failed.\\nErrors count: %d\\nClick OK to open log viewer.\nmessage.no_classes=No classes loaded, nothing to decompile!\nmessage.enter_valid_path=Enter valid path to save!\n\nmessage.saveIncomplete=<html>Save incomplete.<br> %s<br> %d classes or resources were not saved!</html>\nmessage.indexIncomplete=<html>Index of some classes skipped.<br> %s<br> %d classes were not indexed and will not appear in search results!</html>\nmessage.indexingClassesSkipped=<html>Jadx is running low on memory. Therefore %d classes were not indexed.<br>If you want all classes to be indexed restart Jadx with increased maximum heap size.</html>\nmessage.enter_new_name=Enter new name\nmessage.could_not_rename=Can't rename the file\nmessage.confirm_remove_script=Do you really want to remove script?\nmessage.desktop_entry_creation_error=Failed to create desktop entry (check log for details).\nmessage.desktop_entry_creation_success=Desktop entry created successfully!\nmessage.success_title=Success\nmessage.unable_preview_font=Unable preview font\n\nheapUsage.text=JADX memory usage: %.2f GB of %.2f GB (%.2f GB peak)\n\ncommon_dialog.ok=OK\ncommon_dialog.cancel=Cancel\ncommon_dialog.add=Add\ncommon_dialog.update=Update\ncommon_dialog.remove=Remove\ncommon_dialog.reset=Reset\n\nfile_dialog.supported_files=Supported files\nfile_dialog.load_dir_title=Load directory\nfile_dialog.load_dir_confirm=Load all files from directory?\n\nsearch_dialog.open=Open\nsearch_dialog.cancel=Cancel\nsearch_dialog.open_by_name=Search for text:\nsearch_dialog.search_button=Search\nsearch_dialog.search_history=Search history\nsearch_dialog.auto_search=Auto search\nsearch_dialog.search_in=Search definitions of:\nsearch_dialog.class=Class\nsearch_dialog.method=Method\nsearch_dialog.field=Field\nsearch_dialog.code=Code\nsearch_dialog.ignorecase=Case-insensitive\nsearch_dialog.load_more=Load more\nsearch_dialog.load_all=Load all\nsearch_dialog.stop=Stop\nsearch_dialog.results_incomplete=Found %d+\nsearch_dialog.results_complete=Found %d (complete)\nsearch_dialog.resources_load_errors=Load errors: %d\nsearch_dialog.resources_skip_by_size=Skipped by size: %d\nsearch_dialog.resources_check_logs=(click to check logs)\nsearch_dialog.col_node=Node\nsearch_dialog.col_code=Code\nsearch_dialog.sort_results=Sort results\nsearch_dialog.regex=Regex\nsearch_dialog.active_tab=Active tab only\nsearch_dialog.comments=Comments\nsearch_dialog.resource=Resource\nsearch_dialog.keep_open=Keep open\nsearch_dialog.tip_searching=Searching\nsearch_dialog.limit_package=Limit to package:\nsearch_dialog.res_text=Text\nsearch_dialog.res_binary=Binary\nsearch_dialog.package_not_found=No matching package found\nsearch_dialog.copy=Copy All\n\nusage_dialog.title=Usage search\nusage_dialog.label=Usage for:\n\nusage_dialog_plus.title=Usage tree search\nusage_dialog_plus.jump_to=Jump to current location\nusage_dialog_plus.copy_path=Copy usage tree path\nusage_dialog_plus.search_complete=Search completed\nusage_dialog_plus.code_view=Code View\nusage_dialog_plus.select_node=Select Node\nusage_dialog_plus.code_for=Code for %s\nusage_dialog_plus.expand_usages=Expand Data\n\ncomment_dialog.title.add=Add code comment\ncomment_dialog.title.update=Update code comment\ncomment_dialog.label=Comment:\ncomment_dialog.style=Style:\ncomment_dialog.usage=Use 'Shift + Enter' to start a new line\n\nrename_dialog.class_help=Enter full name to move class to another package. Start with '.' to move to default (empty) package\n\nexport_dialog.title=Export\nexport_dialog.save_path=Save path:\nexport_dialog.browse=Browse\nexport_dialog.export_options=Export options\nexport_dialog.export_gradle=Export as a Gradle project\nexport_dialog.export_gradle_type=Gradle template:\n\nlog_viewer.title=Log Viewer\nlog_viewer.log_level=Log level:\nlog_viewer.mode=Mode:\nlog_viewer.modes=All|All scripts|Current script\nlog_viewer.hide=Hide\nlog_viewer.dock=Dock\nlog_viewer.undock=Undock\nlog_viewer.clear=Clear\n\nabout_dialog.title=About JADX\n\npreferences.title=Preferences\npreferences.deobfuscation=Deobfuscation\npreferences.appearance=Appearance\npreferences.shortcuts=Shortcuts\npreferences.select_shortcuts=Select a specific shortcuts group\npreferences.decompile=Decompilation\npreferences.plugins=Plugins\npreferences.project=Project\npreferences.other=Other\npreferences.language=Language\npreferences.lineNumbersMode=Editor line numbers mode\npreferences.jumpOnDoubleClick=Enable jump on double click\npreferences.useAlternativeFileDialog=Use alternative file dialog\npreferences.check_for_updates=Check for updates on startup\npreferences.useDx=Use dx/d8 to convert java bytecode\npreferences.decompilationMode=Decompilation mode\npreferences.codeCacheMode=Code cache mode\npreferences.codeCacheMode.memory=Memory\npreferences.codeCacheMode.memory.desc=Everything in memory: fast search, slow reopen, high memory usage\npreferences.codeCacheMode.diskWithCache=Disk with cache\npreferences.codeCacheMode.diskWithCache.desc=Code saved on disk with in memory cache: medium search, fast reopen, medium memory usage\npreferences.codeCacheMode.disk=Disk\npreferences.codeCacheMode.disk.desc=Everything on disk: slow search, fast reopen, low memory usage\npreferences.usageCacheMode=Usage data cache mode\npreferences.showInconsistentCode=Show inconsistent code\npreferences.escapeUnicode=Escape unicode\npreferences.replaceConsts=Replace constants\npreferences.respectBytecodeAccessModifiers=Respect bytecode access modifiers\npreferences.useImports=Use import statements\npreferences.useDebugInfo=Use debug info\npreferences.inlineAnonymous=Inline anonymous classes\npreferences.inlineMethods=Inline methods\npreferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas\npreferences.moveInnerClasses=Move inner classes into parent\npreferences.extractFinally=Extract finally block\npreferences.restoreSwitchOverString=Restore switch over string\npreferences.fsCaseSensitive=File system is case-sensitive\npreferences.skipResourcesDecode=Don't decode resources\npreferences.skipSourcesDecode=Don't decompile source code\npreferences.useKotlinMethodsForVarNames=Use kotlin methods for variables rename\npreferences.commentsLevel=Code comments level\npreferences.saveOption=Auto-save settings\npreferences.threads=Processing threads count\npreferences.excludedPackages=Excluded packages\npreferences.excludedPackages.tooltip=List of space separated package names that will not be decompiled or indexed (saves RAM)\npreferences.excludedPackages.button=Edit\npreferences.excludedPackages.editDialog=<html>List of space separated package names that will not be decompiled or indexed (saves RAM)<br>e.g. <code>android.support</code></html>\npreferences.cfg=Generate methods CFG graphs (in 'dot' format)\npreferences.raw_cfg=Generate RAW CFG graphs\npreferences.xposed_codegen_language=Xposed code generation language\npreferences.update_channel=Jadx update channel\npreferences.disable_tooltip_on_hover=Disable tooltip on hover\npreferences.integerFormat=Integer format\npreferences.typeUpdatesCountLimit=Update type limit count\npreferences.ui_zoom=UI Zoom factor\npreferences.apply_ui_zoom_to_fonts=Apply UI zoom to fonts\npreferences.ui_font=UI font\npreferences.code_font=Editor font\npreferences.smali_font=Monospaced font (Smali/Hex)\npreferences.laf_theme=Theme\npreferences.dynamic_editor_theme=Use UI theme colors\npreferences.theme=Editor theme\npreferences.start_jobs=Auto start background decompilation\npreferences.select_font=Change\npreferences.deobfuscation_on=Enable deobfuscation\npreferences.generated_renames_mapping_file_mode=Map file handle mode\npreferences.deobfuscation_min_len=Minimum name length\npreferences.deobfuscation_max_len=Maximum name length\npreferences.deobfuscation_res_name_source=Better resources name source\npreferences.deobfuscation_res_use_headers=Use headers for detect resource extensions\npreferences.deobfuscation_whitelist=Exclude packages and classes from deobfuscation\npreferences.deobfuscation_whitelist.editDialog=Whitelist for deobfuscation\npreferences.deobfuscation_whitelist.tooltip=List of ':' separated packages (suffix '.*') and class names that will not be deobfuscated\npreferences.save=Save\npreferences.cancel=Cancel\npreferences.reset=Reset\npreferences.reset_message=Reset settings to default values?\npreferences.reset_title=Reset settings\npreferences.copy=Copy to clipboard\npreferences.copy_message=All settings values has been copied to clipboard\npreferences.rename=Rename identifiers\npreferences.rename_case=To fix case sensitivity issues\npreferences.rename_valid=To make them valid\npreferences.rename_printable=To make printable\npreferences.rename_use_source_name_as_class_name_alias=Use source file name as class name alias\npreferences.rename_source_name_repeat_limit=Allow using source name if it appears less than a limit number\npreferences.search_results_per_page=Results per page (0 - no limit)\npreferences.res_file_ext=Resource files extensions ('xml|html', * for all)\npreferences.res_skip_file=Skip resources files if larger (MB) (0 - disable)\npreferences.tab_dnd_appearance=Dragging tab appearance\n\npreferences.plugins.manage=Manage plugins\npreferences.plugins.install=Install plugin\npreferences.plugins.install_btn=Install\npreferences.plugins.uninstall_btn=Uninstall\npreferences.plugins.disable_btn=Disable\npreferences.plugins.enable_btn=Enable\npreferences.plugins.location_id_label=Location id:\npreferences.plugins.plugin_jar=Select Plugin jar\npreferences.plugins.plugin_jar_label=or\npreferences.plugins.update_all=Update All\npreferences.plugins.details=Plugin details\npreferences.plugins.task.installing=Installing plugin\npreferences.plugins.task.uninstalling=Uninstalling plugin\npreferences.plugins.task.updating=Updating plugins\npreferences.plugins.task.downloading_list=Downloading plugins list\npreferences.plugins.task.status=Changing plugin status\n\npreferences.cache=Cache\npreferences.cache.location=Cache location\npreferences.cache.location_default=App cache system directory\npreferences.cache.location_local=Same directory as project file\npreferences.cache.location_custom=Custom location:\npreferences.cache.change_notice=* for exists caches change will be applied after cache remove or manual reset\npreferences.cache.table.title=Caches list\npreferences.cache.table.project=Cache for project\npreferences.cache.table.size=Disk usage\npreferences.cache.btn.usage=Calculate usage\npreferences.cache.btn.delete_selected=Delete Selected\npreferences.cache.btn.delete_all=Delete All\npreferences.cache.task.usage=Calculating cache size\npreferences.cache.task.delete=Deleting caches\n\nmsg.open_file=Please open file\nmsg.saving_sources=Saving sources\nmsg.language_changed_title=Language changed\nmsg.language_changed=New language will be displayed the next time application starts.\nmsg.warning_title=Warning\nmsg.common_mouse_shortcut=This is a commonly used key, are you sure you would like to bind it to an action?\nmsg.duplicate_shortcut=The shortcut %s is already set in action \"%s\" from category \"%s\", continue ?\nmsg.cmd_select_class_error=Failed to select the class\\n%s\\nThe class does not exist.\nmsg.cant_add_comment=Can't add comment here\nmsg.non_displayable_chars=Some characters were found in code are not displayable by currently used font \"%s\". Show chars?\nmsg.non_displayable_chars.title=Undisplayed Strings\n\nmethods_dialog.title=Select methods\n\npopup.bytecode_col=Show Dalvik Bytecode\npopup.line_wrap=Line Wrap\npopup.undo=Undo\npopup.redo=Redo\npopup.cut=Cut\npopup.copy=Copy\npopup.paste=Paste\npopup.delete=Delete\npopup.select_all=Select All\npopup.frida=Copy as frida snippet\npopup.xposed=Copy as xposed snippet\npopup.find_usage=Find Usage\npopup.go_to_declaration=Go to declaration\npopup.exclude=Exclude\npopup.exclude_packages=Exclude packages\npopup.convert_number=Add conversion as comment\npopup.view_call_graph=View call graph\npopup.view_call_graph_description=Show call chains to this function\npopup.view_class_graph=View inheritance graph\npopup.view_class_graph_description=Show inheritance tree for this class\npopup.view_class_method_graph=View methods graph\npopup.view_class_method_graph_description=Show all methods for this class\npopup.cfg_submenu=View control flow graph\npopup.view_cfg=Regular\npopup.view_cfg_description=Show regular control flow graph for this function (enable in File->Preferences->Other)\npopup.view_raw_cfg=Raw\npopup.view_raw_cfg_description=Show control flow graph with raw instructions for this function (enable in File->Preferences->Other)\npopup.view_region_cfg=Region\npopup.view_region_cfg_description=Show regioned control flow graph for this function (enable in File->Preferences->Other)\npopup.add_comment=Comment\npopup.update_comment=Update comment\npopup.search_comment=Search comments\npopup.rename=Rename\npopup.search=Search \"%s\"\npopup.search_global=Global Search \"%s\"\npopup.remove=Remove\npopup.add_files=Add files\npopup.add_scripts=Add scripts\npopup.new_script=New script\npopup.json_prettify=JSON Prettify\npopup.export=Export\npopup.usage_dialog_plus=Usage Tree Search\npopup.copy_as=Copy as...\npopup.copy_as_hex=Copy as HEX\npopup.copy_as_string=Copy as String\npopup.copy_offset=Copy offset\n\nscript.run=Run\nscript.save=Save\nscript.auto_complete=Auto Complete\nscript.check=Check\nscript.format=Reformat\nscript.log=Show log\n\nencoding_dialog.title=Encoding\nencoding_dialog.message=Select encoding:\n\nexclude_dialog.title=Package Selector\nexclude_dialog.select_all=Select all\nexclude_dialog.deselect=Deselect\nexclude_dialog.invert=Invert\n\nconfirm.save_as_title=Confirm Save as\nconfirm.save_as_message=%s already exists.\\nDo you want to replace it?\nconfirm.not_saved_title=Save project\nconfirm.not_saved_message=Save the current project before proceeding?\nconfirm.remember=Remember my decision\n\ncertificate.cert_type=Type\ncertificate.serialSigVer=Version\ncertificate.serialNumber=Serial number\ncertificate.cert_subject=Subject\ncertificate.serialValidFrom=Valid from\ncertificate.serialValidUntil=Valid until\ncertificate.serialPubKeyType=Public key type\ncertificate.serialPubKeyExponent=Exponent\ncertificate.serialPubKeyModulus=Modulus\ncertificate.serialPubKeyModulusSize=Modulus size (bits)\ncertificate.serialSigType=Signature type\ncertificate.serialSigOID=Signature OID\ncertificate.serialMD5=MD5 Fingerprint\ncertificate.serialSHA1=SHA-1 Fingerprint\ncertificate.serialSHA256=SHA-256 Fingerprint\ncertificate.serialPubKeyY=Y\n\napkSignature.signer=Signer\napkSignature.loading=Loading signature...\napkSignature.verificationSuccess=Signature verification succeeded\napkSignature.verificationFailed=Signature verification failed\napkSignature.signatureSuccess=Valid APK signature v%d found\napkSignature.signatureFailed=Invalid APK signature v%d found\napkSignature.errors=Errors\napkSignature.warnings=Warnings\napkSignature.exception=APK verification failed\napkSignature.unprotectedEntry=Files that are not protected by APK signature v1. Unauthorized modifications to these entries can only be detected by APK signature v2 and higher.\n\nissues_panel.label=Issues:\nissues_panel.errors=%d errors\nissues_panel.warnings=%d warnings\nissues_panel.tooltip=Open in log viewer\n\ndebugger.process_selector=Select a process to debug\ndebugger.step_into=Step Into (F7)\ndebugger.step_over=Step Over (F8)\ndebugger.step_out=Step Out (Shift + F8)\ndebugger.run=Run (F9)\ndebugger.stop=Stop debugger and kill app\ndebugger.pause=Pause\ndebugger.rerun=Rerun\ndebugger.cfm_dialog_title=Exit while debugging\ndebugger.cfm_dialog_msg=Are you sure to terminate debugger?\n\ndebugger.popup_set_value=Set Value\ndebugger.popup_change_to_zero=Change to 0\ndebugger.popup_change_to_one=Change to 1\ndebugger.popup_copy_value=Copy Value\n\nlogcat.pause=Pause Logcat\nlogcat.start=Resume Logcat\nlogcat.clear=Clear Logcat\n\nlogcat.error_fail_start=Failed to start logcat\nlogcat.process=Process\nlogcat.level=Level\nlogcat.default=Default\nlogcat.verbose=Verbose\nlogcat.debug=Debug\nlogcat.info=Info\nlogcat.warn=Warn\nlogcat.error=Error\nlogcat.fatal=Fatal\nlogcat.silent=Silent\nlogcat.logcat=Logcat\nlogcat.select_attached=Select Attached\nlogcat.select_all=Select All\nlogcat.unselect_all=Unselect All\n\nset_value_dialog.label_value=Value\nset_value_dialog.btn_set=Set Value\nset_value_dialog.title=Set Value\nset_value_dialog.neg_msg=Failed to set value.\nset_value_dialog.sel_type=Select a type to set value.\n\nadb_dialog.addr=ADB Addr\nadb_dialog.port=ADB Port\nadb_dialog.path=ADB Path\nadb_dialog.launch_app=Launch App\nadb_dialog.start_server=Start ADB Server\nadb_dialog.refresh=Refresh\nadb_dialog.tip_devices=%d devices\nadb_dialog.device_node=Device\nadb_dialog.missing_path=Must provide the ADB path to start an ADB server.\nadb_dialog.waiting=Waiting to connect to ADB server...\nadb_dialog.connecting=Connecting to ADB server, addr: %s:%s...\nadb_dialog.connect_okay=ADB server connected, addr: %s:%s\nadb_dialog.connect_fail=Failed to connect to ADB server.\nadb_dialog.disconnected=ADB server disconnected.\nadb_dialog.start_okay=ADB server started on port: %s.\nadb_dialog.start_fail=Failed to start ADB server on port: %s!\nadb_dialog.forward_fail=Failed to forward for some reasons.\nadb_dialog.being_debugged_msg=This process seems like it's being debugged, should we proceed?\nadb_dialog.unknown_android_ver=Failed to get Android release version, use Android 8 as default?\nadb_dialog.being_debugged_title=It's Debugging by other.\nadb_dialog.init_dbg_fail=Failed to init debugger.\nadb_dialog.msg_read_mani_fail=Failed to decode AndroidManifest.xml\nadb_dialog.no_devices=Can't found any device to start app.\nadb_dialog.restart_while_debugging_title=Restart while debugging\nadb_dialog.restart_while_debugging_msg=You're debugging an app, are you sure to restart a session?\nadb_dialog.starting_debugger=Starting debugger...\n\naction.variant=%s (variant)\naction_category.menu_toolbar=Menu / Toolbar\naction_category.hex_viewer=View / Hex Viewer\naction_category.code_area=Code Area\naction_category.plugin_script=Plugin Script\n\nhex_viewer.show_inspector=Show Inspector\nhex_viewer.change_encoding=Change Encoding\nhex_viewer.goto_address=Go To Address\nhex_viewer.enter_address=Enter address range:\nhex_viewer.find=Find\n\ngraph_viewer.long_names=Show full names\ngraph_viewer.overrides=Show overrides\ngraph_viewer.callee_depth=Down depth\ngraph_viewer.caller_depth=Up depth\ngraph_viewer.default_error=Failed to view graph\ngraph_viewer.file_not_found_error=Failed to load graph file\ngraph_viewer.image_too_large=Failed to render graph: graph too large\ngraph_viewer.image_too_small=Failed to render graph: graph too small\ngraph_viewer.file_failure=Error in File Operation\ngraph_viewer.save_graph=Save graph\n\ngraph_viewer.default_title=Graph Viewer\ngraph_viewer.method_graph.title=Methods Graph\ngraph_viewer.call_graph.title=Call Graph\ngraph_viewer.inheritance_graph.title=Inheritance Graph\ngraph_viewer.cfg.title=Control Flow Graph\n"
  },
  {
    "path": "jadx-gui/src/main/resources/i18n/Messages_es_ES.properties",
    "content": "language.name=Español\n\nmenu.file=Archivo\nmenu.view=Vista\n#menu.recent_projects=Recent projects\n#menu.no_recent_projects=No recent projects\nmenu.preferences=Preferencias\nmenu.sync=Sincronizar con el editor\nmenu.flatten=Mostrar paquetes en vista plana\n#menu.enable_preview_tab=Enable Preview Tab\n#menu.heapUsageBar=Show Memory Usage Bar\n#menu.alwaysSelectOpened=Auto Select in Tree\n#menu.dock_log=Dock Log Viewer\n#menu.dock_quick_tabs=Show Quick Tabs\nmenu.navigation=Navegación\nmenu.text_search=Buscar texto\nmenu.class_search=Buscar clase\n#menu.comment_search=Comment search\n#menu.go_to_main_activity=Go to main Activity\n#menu.go_to_application=Go to Application\n#menu.go_to_android_manifest=Go to AndroidManifest.xml\nmenu.tools=Herramientas\n#menu.plugins=Plugins\n#menu.decompile_all=Decompile all classes\n#menu.reset_cache=Reset code cache\nmenu.deobfuscation=Desofuscación\nmenu.log=Visor log\n#menu.create_desktop_entry=Create Desktop Entry\nmenu.help=Ayuda\nmenu.about=Acerca de...\n#menu.quark=Quark Engine\nmenu.update_label=¡Nueva versión %s disponible!\n#menu.hex_viewer=Hex Viewer\n\nfile.open_action=Abrir archivo...\n#file.add_files_action=Add files\nfile.open_title=Abrir archivo\n#file.open_project=Open project\n#file.new_project=New project\n#file.save_project=Save project\n#file.save_project_as=Save project as...\n#file.reload=Reload files\n#file.live_reload=Live reload\n#file.live_reload_desc=Auto reload files on changes\n#file.open_mappings=Open mappings...\n#file.save_mappings=Save mappings\n#file.save_mappings_as=Save mappings as...\n#file.close_mappings=Close mappings\nfile.save_all=Guardar todo\n#file.save=Save\n#file.export=Export project\nfile.save_all_msg=Seleccionar carpeta para guardar fuentes descompiladas\nfile.exit=Salir\n#file.export_node=Export file\n\n#start_page.title=Start page\n#start_page.start=Start\n#start_page.recent=Recent projects\n#start_page.list.delete_recent_project=Delete project\n#start_page.list.delete_recent_project.tooltip=Delete project from recent list\n\n#tree.inputs_title=Inputs\n#tree.input_files=Files\n#tree.input_scripts=Scripts\ntree.sources_title=Código fuente\ntree.resources_title=Recursos\ntree.loading=Cargando...\n#tree.pinned_tabs=Pinned Tabs\n#tree.open_tabs=Open Tabs\n#tree.bookmarked_tabs=Bookmarked Tabs\n\nprogress.load=Cargando\n#progress.save_mappings=Saving mappings\nprogress.decompile=Decompiling\n#progress.canceling=Canceling\n\n#error_dialog.title=Error\n#error_dialog.not_found=%s not found\n#error_dialog.not_found_file=File not found at path:\\n%s\n#error_dialog.path_is_directory=The specified path is a directory:\\n%s\n#error_dialog.cannot_read=Cannot read file:\\n%s\n#error_dialog.open_failed=Failed to open file:\\n%s\n#error_dialog.invalid_path_format=Invalid file path:\\n%s\n#error_dialog.desktop_unsupported=Desktop is not supported in this environment.\n\nsearch.previous=Anterior\nsearch.next=Siguiente\nsearch.mark_all=Marcar todo\nsearch.regex=Regex\nsearch.match_case=Sensible a minúsculas/mayúsculas\nsearch.whole_word=Palabra entera\nsearch.find=Buscar\n#search.results=%s%d results\n#search.match_of=Match %d of %d\n#search.match_found=Match found\n#search.match_not_found=No Matches found\n#search.single_match=Single match found\n#search.find_type_text=Find by text\n#search.find_type_hex=Find by hex\n\ntabs.copy_class_name=Copy Name\ntabs.close=Cerrar\ntabs.closeOthers=Cerrar otros\n#tabs.unpin=Unpin\n#tabs.unpin_all=Unpin All\n#tabs.bookmark=Bookmark\n#tabs.unbookmark=Unbookmark\n#tabs.unbookmark_all=Unbookmark All\n#tabs.pin=Pin\ntabs.closeAll=Cerrar todo\ntabs.closeAllRight=Cierra todo a la derecha\n#tabs.closeAllLeft=Close All Left\n#tabs.code=Code\n#tabs.smali=Smali\n#tabs.smali_bytecode=Smali+Bytecode\n\nnav.back=Atrás\nnav.forward=Adelante\n\n#message.taskTimeout=Task exceeded time limit of %d ms.\n#message.userCancelTask=Task was canceled by user.\n#message.memoryLow=Jadx is running low on memory. Please restart with increased maximum heap size.\n#message.taskError=Task failed with error (check log for details).\n#message.errorTitle=Error\n#message.load_errors=Load failed.\\nErrors count: %d\\nClick OK to open log viewer.\n#message.no_classes=No classes loaded, nothing to decompile!\n#message.enter_valid_path=Enter valid path to save!\n\n#message.saveIncomplete=<html>Save incomplete.<br> %s<br> %d classes or resources were not saved!</html>\n#message.indexIncomplete=<html>Index of some classes skipped.<br> %s<br> %d classes were not indexed and will not appear in search results!</html>\n#message.indexingClassesSkipped=<html>Jadx is running low on memory. Therefore %d classes were not indexed.<br>If you want all classes to be indexed restart Jadx with increased maximum heap size.</html>\n#message.enter_new_name=Enter new name\n#message.could_not_rename=Can't rename the file\n#message.confirm_remove_script=Do you really want to remove script?\n#message.desktop_entry_creation_error=Failed to create desktop entry (check log for details).\n#message.desktop_entry_creation_success=Desktop entry created successfully!\n#message.success_title=Success\n#message.unable_preview_font=Unable preview font\n\n#heapUsage.text=JADX memory usage: %.2f GB of %.2f GB (%.2f GB peak)\n\n#common_dialog.ok=OK\n#common_dialog.cancel=Cancel\n#common_dialog.add=Add\n#common_dialog.update=Update\n#common_dialog.remove=Remove\n#common_dialog.reset=Reset\n\n#file_dialog.supported_files=Supported files\n#file_dialog.load_dir_title=Load directory\n#file_dialog.load_dir_confirm=Load all files from directory?\n\nsearch_dialog.open=Abrir\nsearch_dialog.cancel=Cancelar\nsearch_dialog.open_by_name=Buscar texto:\n#search_dialog.search_button=Search\n#search_dialog.search_history=Search history\n#search_dialog.auto_search=Auto search\nsearch_dialog.search_in=Buscar definiciones de:\nsearch_dialog.class=Clase\nsearch_dialog.method=Método\nsearch_dialog.field=Campo\nsearch_dialog.code=Código\nsearch_dialog.ignorecase=Ignorar minúsculas/mayúsculas\n#search_dialog.load_more=Load more\n#search_dialog.load_all=Load all\n#search_dialog.stop=Stop\n#search_dialog.results_incomplete=Found %d+\n#search_dialog.results_complete=Found %d (complete)\n#search_dialog.resources_load_errors=Load errors: %d\n#search_dialog.resources_skip_by_size=Skipped by size: %d\n#search_dialog.resources_check_logs=(click to check logs)\nsearch_dialog.col_node=Nodo\nsearch_dialog.col_code=Código\n#search_dialog.sort_results=Sort results\nsearch_dialog.regex=Regex\n#search_dialog.active_tab=Active tab only\n#search_dialog.comments=Comments\n#search_dialog.resource=Resource\n#search_dialog.keep_open=Keep open\n#search_dialog.tip_searching=Searching\n#search_dialog.limit_package=Limit to package:\n#search_dialog.res_text=Text\n#search_dialog.res_binary=Binary\n#search_dialog.package_not_found=No matching package found\nsearch_dialog.copy=copiar todo\n\nusage_dialog.title=Usage search\nusage_dialog.label=Usage for:\n\n#usage_dialog_plus.title=Usage tree search\n#usage_dialog_plus.jump_to=Jump to current location\n#usage_dialog_plus.copy_path=Copy usage tree path\n#usage_dialog_plus.search_complete=Search completed\n#usage_dialog_plus.code_view=Code View\n#usage_dialog_plus.select_node=Select Node\n#usage_dialog_plus.code_for=Code for %s\n#usage_dialog_plus.expand_usages=Expand Data\n\n#comment_dialog.title.add=Add code comment\n#comment_dialog.title.update=Update code comment\n#comment_dialog.label=Comment:\n#comment_dialog.style=Style:\n#comment_dialog.usage=Use 'Shift + Enter' to start a new line\n\n#rename_dialog.class_help=Enter full name to move class to another package. Start with '.' to move to default (empty) package\n\n#export_dialog.title=Export\n#export_dialog.save_path=Save path:\n#export_dialog.browse=Browse\n#export_dialog.export_options=Export options\n#export_dialog.export_gradle=Export as a Gradle project\n#export_dialog.export_gradle_type=Gradle template:\n\nlog_viewer.title=Visor log\nlog_viewer.log_level=Nivel log:\n#log_viewer.mode=Mode:\n#log_viewer.modes=All|All scripts|Current script\n#log_viewer.hide=Hide\n#log_viewer.dock=Dock\n#log_viewer.undock=Undock\n#log_viewer.clear=Clear\n\nabout_dialog.title=Sobre JADX\n\npreferences.title=Preferencias\npreferences.deobfuscation=Desofuscación\n#preferences.appearance=Appearance\n#preferences.shortcuts=Shortcuts\n#preferences.select_shortcuts=Select a specific shortcuts group\npreferences.decompile=Descompilación\n#preferences.plugins=Plugins\n#preferences.project=Project\npreferences.other=Otros\npreferences.language=Idioma\n#preferences.lineNumbersMode=Editor line numbers mode\n#preferences.jumpOnDoubleClick=Enable jump on double click\n#preferences.useAlternativeFileDialog=Use alternative file dialog\npreferences.check_for_updates=Buscar actualizaciones al iniciar\n#preferences.useDx=Use dx/d8 to convert java bytecode\n#preferences.decompilationMode=Decompilation mode\n#preferences.codeCacheMode=Code cache mode\n#preferences.codeCacheMode.memory=Memory\n#preferences.codeCacheMode.memory.desc=Everything in memory: fast search, slow reopen, high memory usage\n#preferences.codeCacheMode.diskWithCache=Disk with cache\n#preferences.codeCacheMode.diskWithCache.desc=Code saved on disk with in memory cache: medium search, fast reopen, medium memory usage\n#preferences.codeCacheMode.disk=Disk\n#preferences.codeCacheMode.disk.desc=Everything on disk: slow search, fast reopen, low memory usage\n#preferences.usageCacheMode=Usage data cache mode\npreferences.showInconsistentCode=Mostrar código inconsistente\npreferences.escapeUnicode=Escape unicode\npreferences.replaceConsts=Reemplazar constantes\n#preferences.respectBytecodeAccessModifiers=Respect bytecode access modifiers\n#preferences.useImports=Use import statements\n#preferences.useDebugInfo=Use debug info\n#preferences.inlineAnonymous=Inline anonymous classes\n#preferences.inlineMethods=Inline methods\n#preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas\n#preferences.moveInnerClasses=Move inner classes into parent\n#preferences.extractFinally=Extract finally block\n#preferences.restoreSwitchOverString=Restore switch over string\n#preferences.fsCaseSensitive=File system is case-sensitive\npreferences.skipResourcesDecode=No descodificar recursos\n#preferences.skipSourcesDecode=Don't decompile source code\n#preferences.useKotlinMethodsForVarNames=Use kotlin methods for variables rename\n#preferences.commentsLevel=Code comments level\n#preferences.saveOption=Auto-save settings\npreferences.threads=Número de hilos a procesar\n#preferences.excludedPackages=Excluded packages\n#preferences.excludedPackages.tooltip=List of space separated package names that will not be decompiled or indexed (saves RAM)\n#preferences.excludedPackages.button=Edit\n#preferences.excludedPackages.editDialog=<html>List of space separated package names that will not be decompiled or indexed (saves RAM)<br>e.g. <code>android.support</code></html>\npreferences.cfg=Generar methods CFG graphs (in 'dot' format)\npreferences.raw_cfg=Generate RAW CFG graphs\n#preferences.xposed_codegen_language=Xposed code generation language\n#preferences.update_channel=Jadx update channel\n#preferences.disable_tooltip_on_hover=Disable tooltip on hover\n#preferences.integerFormat=Integer format\n#preferences.typeUpdatesCountLimit=Update type limit count\n#preferences.ui_zoom=UI Zoom factor\n#preferences.apply_ui_zoom_to_fonts=Apply UI zoom to fonts\n#preferences.ui_font=UI font\npreferences.code_font=Fuente del editor\n#preferences.smali_font=Monospaced font (Smali/Hex)\n#preferences.laf_theme=Theme\n#preferences.dynamic_editor_theme=Use UI theme colors\npreferences.theme=Tema del editor\npreferences.start_jobs=Inicio autom. descompilación de fondo\npreferences.select_font=Seleccionar\npreferences.deobfuscation_on=Activar desobfuscación\n#preferences.generated_renames_mapping_file_mode=Map file handle mode\npreferences.deobfuscation_min_len=Longitud mínima del nombre\npreferences.deobfuscation_max_len=Longitud máxima del nombre\n#preferences.deobfuscation_res_name_source=Better resources name source\n#preferences.deobfuscation_res_use_headers=Use headers for detect resource extensions\n#preferences.deobfuscation_whitelist=Exclude packages and classes from deobfuscation\n#preferences.deobfuscation_whitelist.editDialog=Whitelist for deobfuscation\n#preferences.deobfuscation_whitelist.tooltip=List of ':' separated packages (suffix '.*') and class names that will not be deobfuscated\npreferences.save=Guardar\npreferences.cancel=Cancelar\npreferences.reset=Reestablecer\npreferences.reset_message=¿Reestablecer preferencias a valores por defecto?\npreferences.reset_title=Reestablecer preferencias\n#preferences.copy=Copy to clipboard\n#preferences.copy_message=All settings values has been copied to clipboard\n#preferences.rename=Rename identifiers\n#preferences.rename_case=To fix case sensitivity issues\n#preferences.rename_valid=To make them valid\n#preferences.rename_printable=To make printable\npreferences.rename_use_source_name_as_class_name_alias=Usar el nombre del source como alias para la clase\n#preferences.rename_source_name_repeat_limit=Allow using source name if it appears less than a limit number\n#preferences.search_results_per_page=Results per page (0 - no limit)\n#preferences.res_file_ext=Resource files extensions ('xml|html', * for all)\n#preferences.res_skip_file=Skip resources files if larger (MB) (0 - disable)\n#preferences.tab_dnd_appearance=Dragging tab appearance\n\n#preferences.plugins.manage=Manage plugins\n#preferences.plugins.install=Install plugin\n#preferences.plugins.install_btn=Install\n#preferences.plugins.uninstall_btn=Uninstall\n#preferences.plugins.disable_btn=Disable\n#preferences.plugins.enable_btn=Enable\n#preferences.plugins.location_id_label=Location id:\n#preferences.plugins.plugin_jar=Select Plugin jar\n#preferences.plugins.plugin_jar_label=or\n#preferences.plugins.update_all=Update All\n#preferences.plugins.details=Plugin details\n#preferences.plugins.task.installing=Installing plugin\n#preferences.plugins.task.uninstalling=Uninstalling plugin\n#preferences.plugins.task.updating=Updating plugins\n#preferences.plugins.task.downloading_list=Downloading plugins list\n#preferences.plugins.task.status=Changing plugin status\n\n#preferences.cache=Cache\n#preferences.cache.location=Cache location\n#preferences.cache.location_default=App cache system directory\n#preferences.cache.location_local=Same directory as project file\n#preferences.cache.location_custom=Custom location:\n#preferences.cache.change_notice=* for exists caches change will be applied after cache remove or manual reset\n#preferences.cache.table.title=Caches list\n#preferences.cache.table.project=Cache for project\n#preferences.cache.table.size=Disk usage\n#preferences.cache.btn.usage=Calculate usage\n#preferences.cache.btn.delete_selected=Delete Selected\n#preferences.cache.btn.delete_all=Delete All\n#preferences.cache.task.usage=Calculating cache size\n#preferences.cache.task.delete=Deleting caches\n\nmsg.open_file=Por favor, abra un archivo\nmsg.saving_sources=Guardando fuente\nmsg.language_changed_title=Idioma cambiado\nmsg.language_changed=El nuevo idioma se mostrará la próxima vez que la aplicación se inicie.\n#msg.warning_title=Warning\n#msg.common_mouse_shortcut=This is a commonly used key, are you sure you would like to bind it to an action?\n#msg.duplicate_shortcut=The shortcut %s is already set in action \"%s\" from category \"%s\", continue ?\n#msg.cmd_select_class_error=Failed to select the class\\n%s\\nThe class does not exist.\n#msg.cant_add_comment=Can't add comment here\n#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font \"%s\". Show chars?\n#msg.non_displayable_chars.title=Undisplayed Strings\n\n#methods_dialog.title=Select methods\n\n#popup.bytecode_col=Show Dalvik Bytecode\n#popup.line_wrap=Line Wrap\npopup.undo=Deshacer\npopup.redo=Rehacer\npopup.cut=Cortar\npopup.copy=Copiar\npopup.paste=Pegar\npopup.delete=Borrar\npopup.select_all=Seleccionar todo\npopup.frida=Copiar como fragmento de frida\npopup.xposed=Copiar como fragmento de xposed\n#popup.find_usage=Find Usage\n#popup.go_to_declaration=Go to declaration\n#popup.exclude=Exclude\n#popup.exclude_packages=Exclude packages\n#popup.convert_number=Add conversion as comment\n#popup.view_call_graph=View call graph\n#popup.view_call_graph_description=Show call chains to this function\n#popup.view_class_graph=View inheritance graph\n#popup.view_class_graph_description=Show inheritance tree for this class\n#popup.view_class_method_graph=View methods graph\n#popup.view_class_method_graph_description=Show all methods for this class\n#popup.cfg_submenu=View control flow graph\n#popup.view_cfg=Regular\n#popup.view_cfg_description=Show regular control flow graph for this function (enable in File->Preferences->Other)\n#popup.view_raw_cfg=Raw\n#popup.view_raw_cfg_description=Show control flow graph with raw instructions for this function (enable in File->Preferences->Other)\n#popup.view_region_cfg=Region\n#popup.view_region_cfg_description=Show regioned control flow graph for this function (enable in File->Preferences->Other)\n#popup.add_comment=Comment\n#popup.update_comment=Update comment\n#popup.search_comment=Search comments\npopup.rename=Renombrar\n#popup.search=Search \"%s\"\n#popup.search_global=Global Search \"%s\"\n#popup.remove=Remove\n#popup.add_files=Add files\n#popup.add_scripts=Add scripts\n#popup.new_script=New script\n#popup.json_prettify=JSON Prettify\n#popup.export=Export\n#popup.usage_dialog_plus=Usage Tree Search\n#popup.copy_as=Copy as...\n#popup.copy_as_hex=Copy as HEX\n#popup.copy_as_string=Copy as String\n#popup.copy_offset=Copy offset\n\n#script.run=Run\n#script.save=Save\n#script.auto_complete=Auto Complete\n#script.check=Check\n#script.format=Reformat\n#script.log=Show log\n\n#encoding_dialog.title=Encoding\n#encoding_dialog.message=Select encoding:\n\n#exclude_dialog.title=Package Selector\n#exclude_dialog.select_all=Select all\n#exclude_dialog.deselect=Deselect\n#exclude_dialog.invert=Invert\n\n#confirm.save_as_title=Confirm Save as\n#confirm.save_as_message=%s already exists.\\nDo you want to replace it?\n#confirm.not_saved_title=Save project\n#confirm.not_saved_message=Save the current project before proceeding?\n#confirm.remember=Remember my decision\n\ncertificate.cert_type=Tipo\ncertificate.serialSigVer=Versión\ncertificate.serialNumber=Número de serial\ncertificate.cert_subject=Subject\ncertificate.serialValidFrom=Válido desde\ncertificate.serialValidUntil=Válido hasta\ncertificate.serialPubKeyType=Tipo de clave pública\ncertificate.serialPubKeyExponent=Exponente\ncertificate.serialPubKeyModulus=Módulo\n#certificate.serialPubKeyModulusSize=Modulus size (bits)\ncertificate.serialSigType=Tipo de firma\ncertificate.serialSigOID=Firma OID\ncertificate.serialMD5=Huella MD5\ncertificate.serialSHA1=Huella SHA-1\ncertificate.serialSHA256=Huella SHA-256\ncertificate.serialPubKeyY=Y\n\n#apkSignature.signer=Signer\n#apkSignature.loading=Loading signature...\n#apkSignature.verificationSuccess=Signature verification succeeded\n#apkSignature.verificationFailed=Signature verification failed\n#apkSignature.signatureSuccess=Valid APK signature v%d found\n#apkSignature.signatureFailed=Invalid APK signature v%d found\n#apkSignature.errors=Errors\n#apkSignature.warnings=Warnings\n#apkSignature.exception=APK verification failed\n#apkSignature.unprotectedEntry=Files that are not protected by APK signature v1. Unauthorized modifications to these entries can only be detected by APK signature v2 and higher.\n\n#issues_panel.label=Issues:\n#issues_panel.errors=%d errors\n#issues_panel.warnings=%d warnings\n#issues_panel.tooltip=Open in log viewer\n\n#debugger.process_selector=Select a process to debug\n#debugger.step_into=Step Into (F7)\n#debugger.step_over=Step Over (F8)\n#debugger.step_out=Step Out (Shift + F8)\n#debugger.run=Run (F9)\n#debugger.stop=Stop debugger and kill app\n#debugger.pause=Pause\n#debugger.rerun=Rerun\n#debugger.cfm_dialog_title=Exit while debugging\n#debugger.cfm_dialog_msg=Are you sure to terminate debugger?\n\n#debugger.popup_set_value=Set Value\n#debugger.popup_change_to_zero=Change to 0\n#debugger.popup_change_to_one=Change to 1\n#debugger.popup_copy_value=Copy Value\n\n#logcat.pause=Pause Logcat\n#logcat.start=Resume Logcat\n#logcat.clear=Clear Logcat\n\n#logcat.error_fail_start=Failed to start logcat\n#logcat.process=Process\n#logcat.level=Level\n#logcat.default=Default\n#logcat.verbose=Verbose\n#logcat.debug=Debug\n#logcat.info=Info\n#logcat.warn=Warn\n#logcat.error=Error\n#logcat.fatal=Fatal\n#logcat.silent=Silent\n#logcat.logcat=Logcat\n#logcat.select_attached=Select Attached\n#logcat.select_all=Select All\n#logcat.unselect_all=Unselect All\n\n#set_value_dialog.label_value=Value\n#set_value_dialog.btn_set=Set Value\n#set_value_dialog.title=Set Value\n#set_value_dialog.neg_msg=Failed to set value.\n#set_value_dialog.sel_type=Select a type to set value.\n\n#adb_dialog.addr=ADB Addr\n#adb_dialog.port=ADB Port\n#adb_dialog.path=ADB Path\n#adb_dialog.launch_app=Launch App\n#adb_dialog.start_server=Start ADB Server\n#adb_dialog.refresh=Refresh\n#adb_dialog.tip_devices=%d devices\n#adb_dialog.device_node=Device\n#adb_dialog.missing_path=Must provide the ADB path to start an ADB server.\n#adb_dialog.waiting=Waiting to connect to ADB server...\n#adb_dialog.connecting=Connecting to ADB server, addr: %s:%s...\n#adb_dialog.connect_okay=ADB server connected, addr: %s:%s\n#adb_dialog.connect_fail=Failed to connect to ADB server.\n#adb_dialog.disconnected=ADB server disconnected.\n#adb_dialog.start_okay=ADB server started on port: %s.\n#adb_dialog.start_fail=Failed to start ADB server on port: %s!\n#adb_dialog.forward_fail=Failed to forward for some reasons.\n#adb_dialog.being_debugged_msg=This process seems like it's being debugged, should we proceed?\n#adb_dialog.unknown_android_ver=Failed to get Android release version, use Android 8 as default?\n#adb_dialog.being_debugged_title=It's Debugging by other.\n#adb_dialog.init_dbg_fail=Failed to init debugger.\n#adb_dialog.msg_read_mani_fail=Failed to decode AndroidManifest.xml\n#adb_dialog.no_devices=Can't found any device to start app.\n#adb_dialog.restart_while_debugging_title=Restart while debugging\n#adb_dialog.restart_while_debugging_msg=You're debugging an app, are you sure to restart a session?\n#adb_dialog.starting_debugger=Starting debugger...\n\n#action.variant=%s (variant)\n#action_category.menu_toolbar=Menu / Toolbar\n#action_category.hex_viewer=View / Hex Viewer\n#action_category.code_area=Code Area\n#action_category.plugin_script=Plugin Script\n\n#hex_viewer.show_inspector=Show Inspector\n#hex_viewer.change_encoding=Change Encoding\n#hex_viewer.goto_address=Go To Address\n#hex_viewer.enter_address=Enter address range:\n#hex_viewer.find=Find\n\n#graph_viewer.long_names=Show full names\n#graph_viewer.overrides=Show overrides\n#graph_viewer.callee_depth=Down depth\n#graph_viewer.caller_depth=Up depth\n#graph_viewer.default_error=Failed to view graph\n#graph_viewer.file_not_found_error=Failed to load graph file\n#graph_viewer.image_too_large=Failed to render graph: graph too large\n#graph_viewer.image_too_small=Failed to render graph: graph too small\n#graph_viewer.file_failure=Error in File Operation\n#graph_viewer.save_graph=Save graph\n\n#graph_viewer.default_title=Graph Viewer\n#graph_viewer.method_graph.title=Methods Graph\n#graph_viewer.call_graph.title=Call Graph\n#graph_viewer.inheritance_graph.title=Inheritance Graph\n#graph_viewer.cfg.title=Control Flow Graph\n"
  },
  {
    "path": "jadx-gui/src/main/resources/i18n/Messages_id_ID.properties",
    "content": "language.name=Indonesian\n\nmenu.file=Berkas\nmenu.view=Tampilan\nmenu.recent_projects=Proyek Terbaru\nmenu.no_recent_projects=Tidak ada proyek terbaru\nmenu.preferences=Preferensi\nmenu.sync=Sinkronisasi dengan editor\nmenu.flatten=Tampilkan paket yang diratakan\n#menu.enable_preview_tab=Enable Preview Tab\nmenu.heapUsageBar=Tampilkan penggunaan memori\nmenu.alwaysSelectOpened=Selalu Pilih Berkas/Kelas yang Terbuka\nmenu.dock_log=Kaitkan pemantau log\n#menu.dock_quick_tabs=Show Quick Tabs\nmenu.navigation=Navigasi\nmenu.text_search=Pencarian Teks\nmenu.class_search=Pencarian Kelas\nmenu.comment_search=Pencarian Komentar\nmenu.go_to_main_activity=Pergi ke Activitas Utama\n#menu.go_to_application=Go to Application\n#menu.go_to_android_manifest=Go to AndroidManifest.xml\nmenu.tools=Alat\nmenu.plugins=Plugin\nmenu.decompile_all=Deskompilasi semua kelas\nmenu.reset_cache=Reset cache kode\nmenu.deobfuscation=Deobfikasi\nmenu.log=Pemantau Log\n#menu.create_desktop_entry=Create Desktop Entry\nmenu.help=Bantuan\nmenu.about=Tentang\nmenu.quark=Mesin Quark\nmenu.update_label=Versi baru %s tersedia!\n#menu.hex_viewer=Hex Viewer\n\nfile.open_action=Buka berkas ...\nfile.add_files_action=Tambahkan berkas\nfile.open_title=Buka berkas\nfile.open_project=Buka proyek\nfile.new_project=Proyek baru\nfile.save_project=Simpan proyek\nfile.save_project_as=Simpan proyek sebagai...\nfile.reload=Muat ulang berkas\nfile.live_reload=Muat ulang otomatis\nfile.live_reload_desc=Muat ulang berkas secara otomatis saat ada perubahan\nfile.open_mappings=Buka pemetaan...\nfile.save_mappings=Simpan pemetaan\nfile.save_mappings_as=Simpan pemetaan sebagai...\nfile.close_mappings=Tutup pemetaan\nfile.save_all=Simpan semua\nfile.save=Simpan\n#file.export=Export project\nfile.save_all_msg=Pilih direktori untuk menyimpan sumber yang telah didekompilasi\nfile.exit=Keluar\n#file.export_node=Export file\n\nstart_page.title=Halaman Awal\nstart_page.start=Mulai\nstart_page.recent=Proyek Terbaru\n#start_page.list.delete_recent_project=Delete project\n#start_page.list.delete_recent_project.tooltip=Delete project from recent list\n\ntree.inputs_title=Input\ntree.input_files=Berkas\ntree.input_scripts=Skrip\ntree.sources_title=Kode Sumber\ntree.resources_title=Sumber Daya\ntree.loading=Memuat...\n#tree.pinned_tabs=Pinned Tabs\n#tree.open_tabs=Open Tabs\n#tree.bookmarked_tabs=Bookmarked Tabs\n\nprogress.load=Memuat\nprogress.save_mappings=Menyimpan pemetaan\nprogress.decompile=Deskompilasi\nprogress.canceling=Membatalkan\n\nerror_dialog.title=Kesalahan\nerror_dialog.not_found=%s tidak ditemukan\n#error_dialog.not_found_file=File not found at path:\\n%s\n#error_dialog.path_is_directory=The specified path is a directory:\\n%s\n#error_dialog.cannot_read=Cannot read file:\\n%s\n#error_dialog.open_failed=Failed to open file:\\n%s\n#error_dialog.invalid_path_format=Invalid file path:\\n%s\n#error_dialog.desktop_unsupported=Desktop is not supported in this environment.\n\nsearch.previous=Sebelumnya\nsearch.next=Berikutnya\nsearch.mark_all=Tandai Semua\nsearch.regex=Regex\nsearch.match_case=Sama huruf\nsearch.whole_word=Seluruh kata\nsearch.find=Cari\nsearch.results=%s%d hasil\n#search.match_of=Match %d of %d\n#search.match_found=Match found\n#search.match_not_found=No Matches found\n#search.single_match=Single match found\n#search.find_type_text=Find by text\n#search.find_type_hex=Find by hex\n\ntabs.copy_class_name=Salin Nama\ntabs.close=Tutup\ntabs.closeOthers=Tutup yang Lain\n#tabs.unpin=Unpin\n#tabs.unpin_all=Unpin All\n#tabs.bookmark=Bookmark\n#tabs.unbookmark=Unbookmark\n#tabs.unbookmark_all=Unbookmark All\n#tabs.pin=Pin\ntabs.closeAll=Tutup Semua\ntabs.closeAllRight=Tutup Semua yang Kanan\n#tabs.closeAllLeft=Close All Left\ntabs.code=Kode\ntabs.smali=Smali\ntabs.smali_bytecode=Smali+Bytecode\n\nnav.back=Kembali\nnav.forward=Maju\n\nmessage.taskTimeout=Tugas melebihi batas waktu %d ms.\nmessage.userCancelTask=Tugas dibatalkan oleh pengguna.\nmessage.memoryLow=JADX kekurangan memori. Harap restart dengan peningkatan ukuran heap maksimum.\nmessage.taskError=Tugas gagal dengan kesalahan (periksa log untuk detailnya).\nmessage.errorTitle=Kesalahan\nmessage.load_errors=Pemuatan gagal.\\nJumlah kesalahan: %d\\nKlik OK untuk membuka penampil log.\nmessage.no_classes=Tidak ada kelas yang dimuat, tidak ada yang dapat didekompilasi!\n#message.enter_valid_path=Enter valid path to save!\n\nmessage.saveIncomplete=<html>Simpan tidak lengkap.<br> %s<br> %d kelas atau sumber daya tidak disimpan!</html>\nmessage.indexIncomplete=<html>Indeks beberapa kelas dilewati.<br> %s<br> %d kelas tidak diindeks dan tidak akan muncul dalam hasil pencarian!</html>\nmessage.indexingClassesSkipped=<html>JADX kekurangan memori. Oleh karena itu %d kelas tidak diindeks.<br>Jika Anda ingin semua kelas diindeks, restart JADX dengan ukuran heap maksimum yang lebih besar.</html>\n#message.enter_new_name=Enter new name\n#message.could_not_rename=Can't rename the file\n#message.confirm_remove_script=Do you really want to remove script?\n#message.desktop_entry_creation_error=Failed to create desktop entry (check log for details).\n#message.desktop_entry_creation_success=Desktop entry created successfully!\n#message.success_title=Success\n#message.unable_preview_font=Unable preview font\n\nheapUsage.text=Penggunaan memori JADX: %.2f GB dari %.2f GB (%.2f GB tertinggi)\n\ncommon_dialog.ok=OK\ncommon_dialog.cancel=Batal\ncommon_dialog.add=Tambah\ncommon_dialog.update=Perbarui\ncommon_dialog.remove=Hapus\ncommon_dialog.reset=Reset\n\nfile_dialog.supported_files=Berkas yang didukung\nfile_dialog.load_dir_title=Muat direktori\nfile_dialog.load_dir_confirm=Muat semua berkas dari direktori?\n\nsearch_dialog.open=Buka\nsearch_dialog.cancel=Batal\nsearch_dialog.open_by_name=Cari teks:\nsearch_dialog.search_button=Cari\nsearch_dialog.search_history=Riwayat pencarian\nsearch_dialog.auto_search=Pencarian otomatis\nsearch_dialog.search_in=Cari definisi dari:\nsearch_dialog.class=Kelas\nsearch_dialog.method=Metode\nsearch_dialog.field=Bidang\nsearch_dialog.code=Kode\nsearch_dialog.ignorecase=Non-ketetapan huruf besar kecil\nsearch_dialog.load_more=Muat lebih banyak\nsearch_dialog.load_all=Muat semua\nsearch_dialog.stop=Berhenti\nsearch_dialog.results_incomplete=Ditemukan %d+\nsearch_dialog.results_complete=Ditemukan %d (lengkap)\nsearch_dialog.resources_load_errors=Kesalahan pemuatan: %d\nsearch_dialog.resources_skip_by_size=Dilewati karena ukuran: %d\nsearch_dialog.resources_check_logs=(klik untuk memeriksa log)\nsearch_dialog.col_node=Node\nsearch_dialog.col_code=Kode\nsearch_dialog.sort_results=Sortir hasil\nsearch_dialog.regex=Regex\nsearch_dialog.active_tab=Hanya tab aktif\nsearch_dialog.comments=Komentar\nsearch_dialog.resource=Sumber daya\nsearch_dialog.keep_open=Tetap terbuka\nsearch_dialog.tip_searching=Mencari\n#search_dialog.limit_package=Limit to package:\n#search_dialog.res_text=Text\n#search_dialog.res_binary=Binary\n#search_dialog.package_not_found=No matching package found\nsearch_dialog.copy=salin semua\n\nusage_dialog.title=Pencarian penggunaan\nusage_dialog.label=Penggunaan untuk:\n\n#usage_dialog_plus.title=Usage tree search\n#usage_dialog_plus.jump_to=Jump to current location\n#usage_dialog_plus.copy_path=Copy usage tree path\n#usage_dialog_plus.search_complete=Search completed\n#usage_dialog_plus.code_view=Code View\n#usage_dialog_plus.select_node=Select Node\n#usage_dialog_plus.code_for=Code for %s\n#usage_dialog_plus.expand_usages=Expand Data\n\ncomment_dialog.title.add=Tambahkan komentar kode\ncomment_dialog.title.update=Perbarui komentar kode\ncomment_dialog.label=Komentar:\n#comment_dialog.style=Style:\ncomment_dialog.usage=Gunakan Shift + Enter untuk memulai baris baru\n\n#rename_dialog.class_help=Enter full name to move class to another package. Start with '.' to move to default (empty) package\n\n#export_dialog.title=Export\n#export_dialog.save_path=Save path:\n#export_dialog.browse=Browse\n#export_dialog.export_options=Export options\n#export_dialog.export_gradle=Export as a Gradle project\n#export_dialog.export_gradle_type=Gradle template:\n\nlog_viewer.title=Pemantau Log\nlog_viewer.log_level=Tingkat log:\nlog_viewer.mode=Mode:\nlog_viewer.modes=Semua|Semua skrip|Skrip saat ini\nlog_viewer.hide=Sembunyikan\nlog_viewer.dock=Kaitkan\nlog_viewer.undock=Lepas kaitan\nlog_viewer.clear=Bersihkan\n\nabout_dialog.title=Tentang JADX\n\npreferences.title=Preferensi\npreferences.deobfuscation=Deobfikasi\npreferences.appearance=Tampilan\npreferences.shortcuts=Pintas\npreferences.select_shortcuts=Pilih grup pintas tertentu\npreferences.decompile=Deskompilasi\npreferences.plugins=Plugin\npreferences.project=Proyek\npreferences.other=Lainnya\npreferences.language=Bahasa\npreferences.lineNumbersMode=Mode nomor baris editor\npreferences.jumpOnDoubleClick=Aktifkan lompat saat dua kali klik\npreferences.useAlternativeFileDialog=Gunakan dialog berkas alternatif\npreferences.check_for_updates=Periksa pembaruan saat memulai\npreferences.useDx=Gunakan dx/d8 untuk mengonversi bytecode Java\npreferences.decompilationMode=Mode deskompilasi\npreferences.codeCacheMode=Mode cache kode\n#preferences.codeCacheMode.memory=Memory\n#preferences.codeCacheMode.memory.desc=Everything in memory: fast search, slow reopen, high memory usage\n#preferences.codeCacheMode.diskWithCache=Disk with cache\n#preferences.codeCacheMode.diskWithCache.desc=Code saved on disk with in memory cache: medium search, fast reopen, medium memory usage\n#preferences.codeCacheMode.disk=Disk\n#preferences.codeCacheMode.disk.desc=Everything on disk: slow search, fast reopen, low memory usage\npreferences.usageCacheMode=Mode cache data penggunaan\npreferences.showInconsistentCode=Tampilkan kode yang tidak konsisten\npreferences.escapeUnicode=Escape unicode\npreferences.replaceConsts=Ganti konstanta\npreferences.respectBytecodeAccessModifiers=Hormati pengubah akses bytecode\npreferences.useImports=Gunakan pernyataan impor\npreferences.useDebugInfo=Gunakan info debug\npreferences.inlineAnonymous=Inline kelas anonim\npreferences.inlineMethods=Inline metode\npreferences.inlineKotlinLambdas=Izinkan untuk menggantikan Lambdas Kotlin\npreferences.moveInnerClasses=Pindahkan kelas dalam ke kelas induk\npreferences.extractFinally=Ekstrak blok finally\n#preferences.restoreSwitchOverString=Restore switch over string\npreferences.fsCaseSensitive=File sistem bersifat sensitif huruf besar-kecil\npreferences.skipResourcesDecode=Jangan dekode sumber daya\n#preferences.skipSourcesDecode=Don't decompile source code\npreferences.useKotlinMethodsForVarNames=Gunakan metode Kotlin untuk mengganti nama variabel\npreferences.commentsLevel=Tingkat komentar kode\n#preferences.saveOption=Auto-save settings\npreferences.threads=Jumlah utas pemrosesan\npreferences.excludedPackages=Package yang dikecualikan\npreferences.excludedPackages.tooltip=Daftar nama paket yang dipisahkan spasi yang tidak akan di deskompilasi atau diindeks (menghemat RAM)\npreferences.excludedPackages.button=Edit\npreferences.excludedPackages.editDialog=<html>Daftar nama paket yang dipisahkan spasi yang tidak akan di deskompilasi atau diindeks (menghemat RAM)<br>contoh: <code>android.support</code></html>\npreferences.cfg=Hasilkan grafik CFG metode (dalam format 'dot')\npreferences.raw_cfg=Hasilkan grafik CFG mentah\n#preferences.xposed_codegen_language=Xposed code generation language\n#preferences.update_channel=Jadx update channel\n#preferences.disable_tooltip_on_hover=Disable tooltip on hover\npreferences.integerFormat=Format bilangan bulat\n#preferences.typeUpdatesCountLimit=Update type limit count\n#preferences.ui_zoom=UI Zoom factor\n#preferences.apply_ui_zoom_to_fonts=Apply UI zoom to fonts\n#preferences.ui_font=UI font\npreferences.code_font=Font editor\npreferences.smali_font=Font monospasi (Smali/Hex)\npreferences.laf_theme=Tema\n#preferences.dynamic_editor_theme=Use UI theme colors\npreferences.theme=Tema editor\npreferences.start_jobs=Deskompilasi latar belakang otomatis\npreferences.select_font=Ubah\npreferences.deobfuscation_on=Aktifkan deobfikasi\npreferences.generated_renames_mapping_file_mode=Mode penanganan file pemetaan\npreferences.deobfuscation_min_len=Panjang nama minimum\npreferences.deobfuscation_max_len=Panjang nama maksimum\npreferences.deobfuscation_res_name_source=Sumber nama sumber daya yang lebih baik\n#preferences.deobfuscation_res_use_headers=Use headers for detect resource extensions\n#preferences.deobfuscation_whitelist=Exclude packages and classes from deobfuscation\n#preferences.deobfuscation_whitelist.editDialog=Whitelist for deobfuscation\n#preferences.deobfuscation_whitelist.tooltip=List of ':' separated packages (suffix '.*') and class names that will not be deobfuscated\npreferences.save=Simpan\npreferences.cancel=Batal\npreferences.reset=Reset\npreferences.reset_message=Reset pengaturan ke nilai default?\npreferences.reset_title=Reset pengaturan\npreferences.copy=Salin ke papan klip\npreferences.copy_message=Semua nilai pengaturan telah disalin ke papan klip\npreferences.rename=Ganti nama identitas\npreferences.rename_case=Untuk memperbaiki masalah sensitivitas huruf besar-kecil\npreferences.rename_valid=Untuk membuatnya valid\npreferences.rename_printable=Untuk membuatnya dapat dicetak\npreferences.rename_use_source_name_as_class_name_alias=Gunakan nama berkas sumber sebagai alias nama kelas\n#preferences.rename_source_name_repeat_limit=Allow using source name if it appears less than a limit number\npreferences.search_results_per_page=Hasil per halaman (0 - tanpa batas)\npreferences.res_file_ext=Ekstensi berkas sumber daya ('xml|html', * untuk semua)\npreferences.res_skip_file=Lewati berkas sumber daya jika lebih besar (MB) (0 - nonaktifkan)\npreferences.tab_dnd_appearance=Dragging tab appearance\n\n#preferences.plugins.manage=Manage plugins\npreferences.plugins.install=Instal plugin\npreferences.plugins.install_btn=Instal\npreferences.plugins.uninstall_btn=Uninstal\n#preferences.plugins.disable_btn=Disable\n#preferences.plugins.enable_btn=Enable\npreferences.plugins.location_id_label=Lokasi id:\npreferences.plugins.plugin_jar=Pilih berkas jar Plugin\npreferences.plugins.plugin_jar_label=atau\npreferences.plugins.update_all=Perbarui Semua\npreferences.plugins.details=Detail Plugin\npreferences.plugins.task.installing=Menginstal plugin\npreferences.plugins.task.uninstalling=Menguninstal plugin\npreferences.plugins.task.updating=Mengperbarui plugin\npreferences.plugins.task.downloading_list=Mengunduh daftar plugin\n#preferences.plugins.task.status=Changing plugin status\n\npreferences.cache=Cache\npreferences.cache.location=Lokasi cache\npreferences.cache.location_default=Direktori sistem cache aplikasi\npreferences.cache.location_local=Direktori yang sama dengan berkas proyek\npreferences.cache.location_custom=Lokasi kustom:\npreferences.cache.change_notice=* untuk cache yang sudah ada, perubahan akan diterapkan setelah cache dihapus atau direset secara manual\npreferences.cache.table.title=Daftar Cache\npreferences.cache.table.project=Cache untuk proyek\npreferences.cache.table.size=Penggunaan disk\npreferences.cache.btn.usage=Hitung penggunaan\npreferences.cache.btn.delete_selected=Hapus yang Dipilih\npreferences.cache.btn.delete_all=Hapus Semua\npreferences.cache.task.usage=Menghitung ukuran cache\npreferences.cache.task.delete=Menghapus cache\n\nmsg.open_file=Harap buka berkas\nmsg.saving_sources=Menyimpan sumber\nmsg.language_changed_title=Bahasa diubah\nmsg.language_changed=Bahasa baru akan ditampilkan saat aplikasi dimulai kembali.\nmsg.warning_title=Peringatan\nmsg.common_mouse_shortcut=Ini adalah tombol yang sering digunakan, apakah Anda yakin ingin mengaitkannya dengan tindakan?\nmsg.duplicate_shortcut=Pintas %s sudah diatur pada tindakan \"%s\" dari kategori \"%s\", lanjutkan?\nmsg.cmd_select_class_error=Gagal memilih kelas\\n%s\\nKelas tidak ada.\nmsg.cant_add_comment=Tidak dapat menambahkan komentar di sini\n#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font \"%s\". Show chars?\n#msg.non_displayable_chars.title=Undisplayed Strings\n\n#methods_dialog.title=Select methods\n\npopup.bytecode_col=Tampilkan Bytecode Dalvik\npopup.line_wrap=Baris Wrap\npopup.undo=Kembalikan\npopup.redo=Ulang\npopup.cut=Potong\npopup.copy=Salin\npopup.paste=Tempel\npopup.delete=Hapus\npopup.select_all=Pilih Semua\npopup.frida=Salin sebagai potongan Frida\npopup.xposed=Salin sebagai potongan Xposed\npopup.find_usage=Cari Penggunaan\npopup.go_to_declaration=Pergi ke Deklarasi\npopup.exclude=Kecualikan\npopup.exclude_packages=Kecualikan paket\n#popup.convert_number=Add conversion as comment\n#popup.view_call_graph=View call graph\n#popup.view_call_graph_description=Show call chains to this function\n#popup.view_class_graph=View inheritance graph\n#popup.view_class_graph_description=Show inheritance tree for this class\n#popup.view_class_method_graph=View methods graph\n#popup.view_class_method_graph_description=Show all methods for this class\n#popup.cfg_submenu=View control flow graph\n#popup.view_cfg=Regular\n#popup.view_cfg_description=Show regular control flow graph for this function (enable in File->Preferences->Other)\n#popup.view_raw_cfg=Raw\n#popup.view_raw_cfg_description=Show control flow graph with raw instructions for this function (enable in File->Preferences->Other)\n#popup.view_region_cfg=Region\n#popup.view_region_cfg_description=Show regioned control flow graph for this function (enable in File->Preferences->Other)\npopup.add_comment=Komentar\n#popup.update_comment=Update comment\npopup.search_comment=Cari komentar\npopup.rename=Ganti nama\npopup.search=Cari \"%s\"\npopup.search_global=Cari Global \"%s\"\npopup.remove=Hapus\npopup.add_files=Tambahkan berkas\npopup.add_scripts=Tambahkan skrip\npopup.new_script=Skrip baru\npopup.json_prettify=JSON Prettify\n#popup.export=Export\n#popup.usage_dialog_plus=Usage Tree Search\n#popup.copy_as=Copy as...\n#popup.copy_as_hex=Copy as HEX\n#popup.copy_as_string=Copy as String\n#popup.copy_offset=Copy offset\n\nscript.run=Jalankan\nscript.save=Simpan\nscript.auto_complete=Auto Lengkapi\nscript.check=Periksa\nscript.format=Format Ulang\nscript.log=Tampilkan log\n\n#encoding_dialog.title=Encoding\n#encoding_dialog.message=Select encoding:\n\nexclude_dialog.title=Selektor Paket\nexclude_dialog.select_all=Pilih semua\nexclude_dialog.deselect=Batal pilih\nexclude_dialog.invert=Balik pilihan\n\nconfirm.save_as_title=Konfirmasi Simpan Sebagai\nconfirm.save_as_message=%s sudah ada.\\nApakah Anda ingin menggantinya?\nconfirm.not_saved_title=Simpan proyek\nconfirm.not_saved_message=Simpan proyek saat ini sebelum melanjutkan?\n#confirm.remember=Remember my decision\n\ncertificate.cert_type=Tipe\ncertificate.serialSigVer=Versi Tanda Tangan\ncertificate.serialNumber=Nomor Seri\ncertificate.cert_subject=Subjek\ncertificate.serialValidFrom=Berlaku dari\ncertificate.serialValidUntil=Berlaku hingga\ncertificate.serialPubKeyType=Tipe Kunci Publik\ncertificate.serialPubKeyExponent=Eksponen\ncertificate.serialPubKeyModulus=Modulus\ncertificate.serialPubKeyModulusSize=Ukuran Modulus (bit)\ncertificate.serialSigType=Tipe Tanda Tangan\ncertificate.serialSigOID=OID Tanda Tangan\ncertificate.serialMD5=Sidik Jari MD5\ncertificate.serialSHA1=Sidik Jari SHA-1\ncertificate.serialSHA256=Sidik Jari SHA-256\ncertificate.serialPubKeyY=Y\n\napkSignature.signer=Penandatangan\n#apkSignature.loading=Loading signature...\napkSignature.verificationSuccess=Verifikasi tanda tangan berhasil\napkSignature.verificationFailed=Verifikasi tanda tangan gagal\napkSignature.signatureSuccess=Tanda tangan APK yang valid v%d ditemukan\napkSignature.signatureFailed=Tanda tangan APK yang tidak valid v%d ditemukan\napkSignature.errors=Kesalahan\napkSignature.warnings=Peringatan\napkSignature.exception=Verifikasi APK gagal\napkSignature.unprotectedEntry=Berkas yang tidak dilindungi oleh tanda tangan APK v1. Modifikasi yang tidak sah pada entri-entri ini hanya dapat terdeteksi oleh tanda tangan APK v2 dan yang lebih tinggi.\n\nissues_panel.label=Masalah:\nissues_panel.errors=%d kesalahan\nissues_panel.warnings=%d peringatan\nissues_panel.tooltip=Buka di penampil log\n\ndebugger.process_selector=Pilih proses untuk debugging\ndebugger.step_into=Langkah Masuk (F7)\ndebugger.step_over=Langkah Atas (F8)\ndebugger.step_out=Langkah Keluar (Shift + F8)\ndebugger.run=Jalankan (F9)\ndebugger.stop=Hentikan debugger dan hentikan aplikasi\ndebugger.pause=Jeda\ndebugger.rerun=Jalankan Ulang\ndebugger.cfm_dialog_title=Keluar saat debugging\ndebugger.cfm_dialog_msg=Apakah Anda yakin ingin mengakhiri debugger?\n\ndebugger.popup_set_value=Tetapkan Nilai\ndebugger.popup_change_to_zero=Ubah menjadi 0\ndebugger.popup_change_to_one=Ubah menjadi 1\ndebugger.popup_copy_value=Salin Nilai\n\nlogcat.pause=Jeda Logcat\nlogcat.start=Lanjutkan Logcat\nlogcat.clear=Kosongkan Logcat\n\nlogcat.error_fail_start=Gagal memulai Logcat\nlogcat.process=Proses\nlogcat.level=Tingkat\nlogcat.default=Bawaan\nlogcat.verbose=Verbose\nlogcat.debug=Debug\nlogcat.info=Info\nlogcat.warn=Peringatan\nlogcat.error=Kesalahan\nlogcat.fatal=Fatal\nlogcat.silent=Diam\nlogcat.logcat=Logcat\nlogcat.select_attached=Pilih Terlampir\nlogcat.select_all=Pilih Semua\nlogcat.unselect_all=Batal Pilih Semua\n\nset_value_dialog.label_value=Nilai\nset_value_dialog.btn_set=Tetapkan Nilai\nset_value_dialog.title=Tetapkan Nilai\nset_value_dialog.neg_msg=Gagal menetapkan nilai.\nset_value_dialog.sel_type=Pilih jenis untuk menetapkan nilai.\n\nadb_dialog.addr=Alamat ADB\nadb_dialog.port=Port ADB\nadb_dialog.path=Path ADB\nadb_dialog.launch_app=Jalankan Aplikasi\nadb_dialog.start_server=Menajalankan Server ADB\nadb_dialog.refresh=Segarkan\nadb_dialog.tip_devices=%d perangkat\nadb_dialog.device_node=Perangkat\nadb_dialog.missing_path=Harus memberikan path ADB untuk menjalankan server ADB.\nadb_dialog.waiting=Menunggu untuk terhubung ke server ADB...\nadb_dialog.connecting=Menghubungkan ke server ADB, alamat: %s:%s...\nadb_dialog.connect_okay=Server ADB terhubung, alamat: %s:%s\nadb_dialog.connect_fail=Gagal menghubungkan ke server ADB.\nadb_dialog.disconnected=Server ADB terputus.\nadb_dialog.start_okay=Server ADB dijalankan pada port: %s.\nadb_dialog.start_fail=Gagal menjalankan server ADB pada port: %s!\nadb_dialog.forward_fail=Gagal meneruskan dengan alasan tertentu.\nadb_dialog.being_debugged_msg=Proses ini sepertinya sedang di-debug, apakah kita harus melanjutkan?\nadb_dialog.unknown_android_ver=Gagal mendapatkan versi rilis Android, gunakan Android 8 sebagai default?\nadb_dialog.being_debugged_title=Sedang Didebug oleh Pihak Lain.\nadb_dialog.init_dbg_fail=Gagal menginisialisasi debugger.\nadb_dialog.msg_read_mani_fail=Gagal mendekode AndroidManifest.xml\nadb_dialog.no_devices=Tidak dapat menemukan perangkat apa pun untuk memulai aplikasi.\nadb_dialog.restart_while_debugging_title=Memulai Ulang Saat Debugging\nadb_dialog.restart_while_debugging_msg=Anda sedang debugging sebuah aplikasi, apakah Anda yakin ingin memulai ulang sesi?\nadb_dialog.starting_debugger=Memulai debugger...\n\naction.variant=%s (variant)\naction_category.menu_toolbar=Menu / Toolbar\n#action_category.hex_viewer=View / Hex Viewer\naction_category.code_area=Area Kode\naction_category.plugin_script=Plugin Script\n\n#hex_viewer.show_inspector=Show Inspector\n#hex_viewer.change_encoding=Change Encoding\n#hex_viewer.goto_address=Go To Address\n#hex_viewer.enter_address=Enter address range:\n#hex_viewer.find=Find\n\n#graph_viewer.long_names=Show full names\n#graph_viewer.overrides=Show overrides\n#graph_viewer.callee_depth=Down depth\n#graph_viewer.caller_depth=Up depth\n#graph_viewer.default_error=Failed to view graph\n#graph_viewer.file_not_found_error=Failed to load graph file\n#graph_viewer.image_too_large=Failed to render graph: graph too large\n#graph_viewer.image_too_small=Failed to render graph: graph too small\n#graph_viewer.file_failure=Error in File Operation\n#graph_viewer.save_graph=Save graph\n\n#graph_viewer.default_title=Graph Viewer\n#graph_viewer.method_graph.title=Methods Graph\n#graph_viewer.call_graph.title=Call Graph\n#graph_viewer.inheritance_graph.title=Inheritance Graph\n#graph_viewer.cfg.title=Control Flow Graph\n"
  },
  {
    "path": "jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties",
    "content": "language.name=한국어\n\nmenu.file=파일\nmenu.view=보기\nmenu.recent_projects=최근 프로젝트\nmenu.no_recent_projects=최근 프로젝트 없음\nmenu.preferences=설정\nmenu.sync=에디터와 동기화\nmenu.flatten=플랫 패키지 표시\n#menu.enable_preview_tab=Enable Preview Tab\nmenu.heapUsageBar=메모리 사용량 표시\nmenu.alwaysSelectOpened=항상 열린 파일/클래스 선택\n#menu.dock_log=Dock Log Viewer\n#menu.dock_quick_tabs=Show Quick Tabs\nmenu.navigation=네비게이션\nmenu.text_search=텍스트 검색\nmenu.class_search=클래스 검색\nmenu.comment_search=주석 검색\n#menu.go_to_main_activity=Go to main Activity\n#menu.go_to_application=Go to Application\n#menu.go_to_android_manifest=Go to AndroidManifest.xml\nmenu.tools=도구\n#menu.plugins=Plugins\n#menu.decompile_all=Decompile all classes\n#menu.reset_cache=Reset code cache\nmenu.deobfuscation=난독화 해제\nmenu.log=로그 뷰어\n#menu.create_desktop_entry=Create Desktop Entry\nmenu.help=도움말\nmenu.about=정보\n#menu.quark=Quark Engine\nmenu.update_label=새 버전 %s 이(가) 존재합니다!\n#menu.hex_viewer=Hex Viewer\n\nfile.open_action=파일 열기 ...\nfile.add_files_action=파일 추가\nfile.open_title=파일 열기\nfile.open_project=프로젝트 열기\nfile.new_project=새 프로젝트\nfile.save_project=프로젝트 저장\nfile.save_project_as=다른 이름으로 프로젝트 저장...\nfile.reload=파일 다시 로드\nfile.live_reload=라이브 로드\nfile.live_reload_desc=파일 내용 변경 시 자동으로 다시 로드\n#file.open_mappings=Open mappings...\nfile.save_mappings=다른 이름으로 매핑 내보내기...\n#file.save_mappings_as=Save mappings as...\n#file.close_mappings=Close mappings\nfile.save_all=모두 저장\n#file.save=Save\n#file.export=Export project\nfile.save_all_msg=디컴파일된 소스를 저장할 디렉토리 선택\nfile.exit=나가기\n#file.export_node=Export file\n\nstart_page.title=페이지 시작\nstart_page.start=시작\nstart_page.recent=최근 프로젝트\n#start_page.list.delete_recent_project=Delete project\n#start_page.list.delete_recent_project.tooltip=Delete project from recent list\n\n#tree.inputs_title=Inputs\n#tree.input_files=Files\n#tree.input_scripts=Scripts\ntree.sources_title=소스코드\ntree.resources_title=리소스\ntree.loading=로딩중...\n#tree.pinned_tabs=Pinned Tabs\n#tree.open_tabs=Open Tabs\n#tree.bookmarked_tabs=Bookmarked Tabs\n\nprogress.load=로딩중\nprogress.save_mappings=매핑 내보내는 중\nprogress.decompile=디컴파일 중\nprogress.canceling=취소 중\n\nerror_dialog.title=오류\n#error_dialog.not_found=%s not found\n#error_dialog.not_found_file=File not found at path:\\n%s\n#error_dialog.path_is_directory=The specified path is a directory:\\n%s\n#error_dialog.cannot_read=Cannot read file:\\n%s\n#error_dialog.open_failed=Failed to open file:\\n%s\n#error_dialog.invalid_path_format=Invalid file path:\\n%s\n#error_dialog.desktop_unsupported=Desktop is not supported in this environment.\n\nsearch.previous=이전\nsearch.next=다음\nsearch.mark_all=모두 선택\nsearch.regex=정규식\nsearch.match_case=매치 케이스\nsearch.whole_word=전체 단어\nsearch.find=찾기\n#search.results=%s%d results\n#search.match_of=Match %d of %d\n#search.match_found=Match found\n#search.match_not_found=No Matches found\n#search.single_match=Single match found\n#search.find_type_text=Find by text\n#search.find_type_hex=Find by hex\n\ntabs.copy_class_name=이름 복사\ntabs.close=닫기\ntabs.closeOthers=이 탭을 제외하고 닫기\n#tabs.unpin=Unpin\n#tabs.unpin_all=Unpin All\n#tabs.bookmark=Bookmark\n#tabs.unbookmark=Unbookmark\n#tabs.unbookmark_all=Unbookmark All\n#tabs.pin=Pin\ntabs.closeAll=모두 닫기\ntabs.closeAllRight=오른쪽의 모든 것을 닫으십시오\n#tabs.closeAllLeft=Close All Left\ntabs.code=코드\ntabs.smali=Smali\ntabs.smali_bytecode=Smali+Bytecode\n\nnav.back=뒤로\nnav.forward=앞으로\n\nmessage.taskTimeout=작업이 %d ms의 시간 제한을 초과했습니다.\nmessage.userCancelTask=사용자가 작업을 취소했습니다.\nmessage.memoryLow=Jadx의 메모리가 부족합니다. 최대 힙 크기를 늘린 후 다시 시작하십시오.\nmessage.taskError=작업이 오류로 실패했습니다. (자세한 내용은 로그 확인)\nmessage.errorTitle=오류\nmessage.load_errors=로드하지 못했습니다.\\n오류 수: %d\\n로그 뷰어를 열려면 확인을 클릭하십시오.\nmessage.no_classes=로드된 클래스 없음, 디컴파일 대상 존재하지 않음\n#message.enter_valid_path=Enter valid path to save!\n\nmessage.saveIncomplete=<html>저장이 완료되지 않았습니다.<br> %s<br> %d개의 클래스 또는 리소스가 저장되지 않았습니다!</html>\nmessage.indexIncomplete=<html>일부 클래스의 색인을 건너뛰었습니다.<br> %s<br> %d개의 클래스는 색인이 생성되지 않았으며 검색 결과에 나타나지 않습니다.</html>\nmessage.indexingClassesSkipped=<html>Jadx의 메모리가 부족합니다. 따라서 %d 개의 클래스가 인덱싱되지 않았습니다. <br> 모든 클래스를 인덱싱하려면 최대 힙 크기를 늘린 상태로 Jadx를 다시 시작하십시오.</html>\n#message.enter_new_name=Enter new name\n#message.could_not_rename=Can't rename the file\n#message.confirm_remove_script=Do you really want to remove script?\n#message.desktop_entry_creation_error=Failed to create desktop entry (check log for details).\n#message.desktop_entry_creation_success=Desktop entry created successfully!\n#message.success_title=Success\n#message.unable_preview_font=Unable preview font\n\nheapUsage.text=JADX 메모리 사용량 : %.2f GB / %.2f GB (%.2f GB 첨단)\n\ncommon_dialog.ok=확인\ncommon_dialog.cancel=취소\ncommon_dialog.add=추가\ncommon_dialog.update=업데이트\ncommon_dialog.remove=삭제\n#common_dialog.reset=Reset\n\nfile_dialog.supported_files=지원되는 파일\nfile_dialog.load_dir_title=디렉토리 불러오기\nfile_dialog.load_dir_confirm=디렉토리에서 모든 파일을 불러오시겠습니까?\n\nsearch_dialog.open=열기\nsearch_dialog.cancel=취소\nsearch_dialog.open_by_name=텍스트 검색 :\nsearch_dialog.search_button=검색\nsearch_dialog.search_history=검색 기록\nsearch_dialog.auto_search=자동 검색\nsearch_dialog.search_in=정의 검색 :\nsearch_dialog.class=클래스\nsearch_dialog.method=메소드\nsearch_dialog.field=필드\nsearch_dialog.code=코드\nsearch_dialog.ignorecase=대소문자 구분 안함\nsearch_dialog.load_more=더보기\nsearch_dialog.load_all=모두 로드\nsearch_dialog.stop=정지\nsearch_dialog.results_incomplete=%d+개 찾음\nsearch_dialog.results_complete=%d개 찾음 (검색 완료)\n#search_dialog.resources_load_errors=Load errors: %d\n#search_dialog.resources_skip_by_size=Skipped by size: %d\n#search_dialog.resources_check_logs=(click to check logs)\nsearch_dialog.col_node=노드\nsearch_dialog.col_code=코드\nsearch_dialog.sort_results=결과 정렬\nsearch_dialog.regex=정규식\nsearch_dialog.active_tab=열려 있는 탭에서만 검색\nsearch_dialog.comments=주석\nsearch_dialog.resource=리소스\nsearch_dialog.keep_open=열어 두기\nsearch_dialog.tip_searching=검색 중...\n#search_dialog.limit_package=Limit to package:\n#search_dialog.res_text=Text\n#search_dialog.res_binary=Binary\n#search_dialog.package_not_found=No matching package found\nsearch_dialog.copy=모두 복사\n\nusage_dialog.title=사용 검색\nusage_dialog.label=다음의 사용 검색 결과:\n\n#usage_dialog_plus.title=Usage tree search\n#usage_dialog_plus.jump_to=Jump to current location\n#usage_dialog_plus.copy_path=Copy usage tree path\n#usage_dialog_plus.search_complete=Search completed\n#usage_dialog_plus.code_view=Code View\n#usage_dialog_plus.select_node=Select Node\n#usage_dialog_plus.code_for=Code for %s\n#usage_dialog_plus.expand_usages=Expand Data\n\ncomment_dialog.title.add=주석 추가\ncomment_dialog.title.update=주석 업데이트\ncomment_dialog.label=주석:\n#comment_dialog.style=Style:\ncomment_dialog.usage=Shift + Enter 를 입력해 새 라인에 입력\n\n#rename_dialog.class_help=Enter full name to move class to another package. Start with '.' to move to default (empty) package\n\n#export_dialog.title=Export\n#export_dialog.save_path=Save path:\n#export_dialog.browse=Browse\n#export_dialog.export_options=Export options\n#export_dialog.export_gradle=Export as a Gradle project\n#export_dialog.export_gradle_type=Gradle template:\n\nlog_viewer.title=로그 뷰어\nlog_viewer.log_level=로그 레벨:\n#log_viewer.mode=Mode:\n#log_viewer.modes=All|All scripts|Current script\n#log_viewer.hide=Hide\n#log_viewer.dock=Dock\n#log_viewer.undock=Undock\n#log_viewer.clear=Clear\n\nabout_dialog.title=JADX 정보\n\npreferences.title=설정\npreferences.deobfuscation=난독화 해제\npreferences.appearance=외관\n#preferences.shortcuts=Shortcuts\n#preferences.select_shortcuts=Select a specific shortcuts group\npreferences.decompile=디컴파일\npreferences.plugins=플러그인\npreferences.project=프로젝트\npreferences.other=기타\npreferences.language=언어\npreferences.lineNumbersMode=편집기 줄 번호 모드\npreferences.jumpOnDoubleClick=더블 클릭 시 점프 활성화\n#preferences.useAlternativeFileDialog=Use alternative file dialog\npreferences.check_for_updates=시작시 업데이트 확인\npreferences.useDx=dx/d8을 사용하여 Java 바이트 코드 변환\npreferences.decompilationMode=디컴파일 모드\npreferences.codeCacheMode=코드 캐시 모드\n#preferences.codeCacheMode.memory=Memory\n#preferences.codeCacheMode.memory.desc=Everything in memory: fast search, slow reopen, high memory usage\n#preferences.codeCacheMode.diskWithCache=Disk with cache\n#preferences.codeCacheMode.diskWithCache.desc=Code saved on disk with in memory cache: medium search, fast reopen, medium memory usage\n#preferences.codeCacheMode.disk=Disk\n#preferences.codeCacheMode.disk.desc=Everything on disk: slow search, fast reopen, low memory usage\n#preferences.usageCacheMode=Usage data cache mode\npreferences.showInconsistentCode=디컴파일 안된 코드 표시\npreferences.escapeUnicode=유니코드 이스케이프\npreferences.replaceConsts=상수 바꾸기\npreferences.respectBytecodeAccessModifiers=바이트코드 액세스 수정자 존중\npreferences.useImports=import 문 사용\npreferences.useDebugInfo=디버그 정보 사용\npreferences.inlineAnonymous=인라인 익명 클래스\npreferences.inlineMethods=인라인 메서드\n#preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas\n#preferences.moveInnerClasses=Move inner classes into parent\npreferences.extractFinally=finally 블록 추출\n#preferences.restoreSwitchOverString=Restore switch over string\npreferences.fsCaseSensitive=파일 시스템 대소문자 구별\npreferences.skipResourcesDecode=리소스 디코딩 하지 않기\n#preferences.skipSourcesDecode=Don't decompile source code\npreferences.useKotlinMethodsForVarNames=변수 이름 바꾸기에 kotlin 메서드 사용\npreferences.commentsLevel=코드 주석 수준\n#preferences.saveOption=Auto-save settings\npreferences.threads=처리 스레드 수\npreferences.excludedPackages=제외할 패키지\npreferences.excludedPackages.tooltip=RAM 절약을 위해 디컴파일되거나 인덱싱하지 않을 패키지 이름 목록 (공백으로 항목 구분)\npreferences.excludedPackages.button=Edit\npreferences.excludedPackages.editDialog=<html>RAM 절약을 위해 디컴파일되거나 인덱싱하지 않을 패키지 이름 목록 (공백으로 항목 구분)<br>예: <code>android.support</code></html>\npreferences.cfg=메소드 CFG 그래프 생성 ('dot' 포맷)\npreferences.raw_cfg=RAW CFG 그래프 생성\n#preferences.xposed_codegen_language=Xposed code generation language\n#preferences.update_channel=Jadx update channel\n#preferences.disable_tooltip_on_hover=Disable tooltip on hover\n#preferences.integerFormat=Integer format\n#preferences.typeUpdatesCountLimit=Update type limit count\n#preferences.ui_zoom=UI Zoom factor\n#preferences.apply_ui_zoom_to_fonts=Apply UI zoom to fonts\n#preferences.ui_font=UI font\npreferences.code_font=에디터 글씨체\n#preferences.smali_font=Monospaced font (Smali/Hex)\npreferences.laf_theme=테마\n#preferences.dynamic_editor_theme=Use UI theme colors\npreferences.theme=에디터 테마\npreferences.start_jobs=백그라운드에서 디컴파일 자동 시작\npreferences.select_font=변경\npreferences.deobfuscation_on=난독 해제 활성화\npreferences.generated_renames_mapping_file_mode=맵 파일 처리 모드\npreferences.deobfuscation_min_len=최소 이름 길이\npreferences.deobfuscation_max_len=최대 이름 길이\npreferences.deobfuscation_res_name_source=더 나은 리소스 이름 소스\n#preferences.deobfuscation_res_use_headers=Use headers for detect resource extensions\n#preferences.deobfuscation_whitelist=Exclude packages and classes from deobfuscation\n#preferences.deobfuscation_whitelist.editDialog=Whitelist for deobfuscation\n#preferences.deobfuscation_whitelist.tooltip=List of ':' separated packages (suffix '.*') and class names that will not be deobfuscated\npreferences.save=저장\npreferences.cancel=취소\npreferences.reset=재설정\npreferences.reset_message=설정을 기본값으로 재설정 하시겠습니까?\npreferences.reset_title=재설정\npreferences.copy=클립보드에 복사\npreferences.copy_message=모든 설정 값이 클립 보드에 복사되었습니다.\npreferences.rename=이름 바꾸기\npreferences.rename_case=시스템 대소문자 구분\npreferences.rename_valid=유효한 식별자로 바꾸기\npreferences.rename_printable=출력 가능하게 바꾸기\npreferences.rename_use_source_name_as_class_name_alias=소스 파일 이름을 클래스 이름 별칭으로 사용\n#preferences.rename_source_name_repeat_limit=Allow using source name if it appears less than a limit number\n#preferences.search_results_per_page=Results per page (0 - no limit)\npreferences.res_file_ext=파일 확장자 (예: .xml|.html) (* 은 전체를 의미)\npreferences.res_skip_file=이 옵션보다 큰 파일 건너 뛰기 (MB)\npreferences.tab_dnd_appearance=Dragging tab appearance\n\n#preferences.plugins.manage=Manage plugins\n#preferences.plugins.install=Install plugin\n#preferences.plugins.install_btn=Install\n#preferences.plugins.uninstall_btn=Uninstall\n#preferences.plugins.disable_btn=Disable\n#preferences.plugins.enable_btn=Enable\n#preferences.plugins.location_id_label=Location id:\n#preferences.plugins.plugin_jar=Select Plugin jar\n#preferences.plugins.plugin_jar_label=or\n#preferences.plugins.update_all=Update All\n#preferences.plugins.details=Plugin details\n#preferences.plugins.task.installing=Installing plugin\n#preferences.plugins.task.uninstalling=Uninstalling plugin\n#preferences.plugins.task.updating=Updating plugins\n#preferences.plugins.task.downloading_list=Downloading plugins list\n#preferences.plugins.task.status=Changing plugin status\n\n#preferences.cache=Cache\n#preferences.cache.location=Cache location\n#preferences.cache.location_default=App cache system directory\n#preferences.cache.location_local=Same directory as project file\n#preferences.cache.location_custom=Custom location:\n#preferences.cache.change_notice=* for exists caches change will be applied after cache remove or manual reset\n#preferences.cache.table.title=Caches list\n#preferences.cache.table.project=Cache for project\n#preferences.cache.table.size=Disk usage\n#preferences.cache.btn.usage=Calculate usage\n#preferences.cache.btn.delete_selected=Delete Selected\n#preferences.cache.btn.delete_all=Delete All\n#preferences.cache.task.usage=Calculating cache size\n#preferences.cache.task.delete=Deleting caches\n\nmsg.open_file=파일을 여십시오\nmsg.saving_sources=소스 저장 중\nmsg.language_changed_title=언어 변경됨\nmsg.language_changed=다음에 응용 프로그램이 시작되면 새 언어가 표시됩니다.\n#msg.warning_title=Warning\n#msg.common_mouse_shortcut=This is a commonly used key, are you sure you would like to bind it to an action?\n#msg.duplicate_shortcut=The shortcut %s is already set in action \"%s\" from category \"%s\", continue ?\nmsg.cmd_select_class_error=클래스를 선택하지 못했습니다.\\n%s\\n클래스가 없습니다.\nmsg.cant_add_comment=여기에 주석을 추가할수 없음\n#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font \"%s\". Show chars?\n#msg.non_displayable_chars.title=Undisplayed Strings\n\n#methods_dialog.title=Select methods\n\npopup.bytecode_col=Dalvik Bytecode 보이기\npopup.line_wrap=줄 바꿈\npopup.undo=실행 취소\npopup.redo=다시 실행\npopup.cut=자르기\npopup.copy=붙여넣기\npopup.paste=복사\npopup.delete=삭제\npopup.select_all=모두 선택\npopup.frida=frida 스니펫으로 복사\npopup.xposed=xposed 스니펫으로 복사\npopup.find_usage=사용 찾기\npopup.go_to_declaration=선언문으로 이동\npopup.exclude=제외\npopup.exclude_packages=패키지 제외\n#popup.convert_number=Add conversion as comment\n#popup.view_call_graph=View call graph\n#popup.view_call_graph_description=Show call chains to this function\n#popup.view_class_graph=View inheritance graph\n#popup.view_class_graph_description=Show inheritance tree for this class\n#popup.view_class_method_graph=View methods graph\n#popup.view_class_method_graph_description=Show all methods for this class\n#popup.cfg_submenu=View control flow graph\n#popup.view_cfg=Regular\n#popup.view_cfg_description=Show regular control flow graph for this function (enable in File->Preferences->Other)\n#popup.view_raw_cfg=Raw\n#popup.view_raw_cfg_description=Show control flow graph with raw instructions for this function (enable in File->Preferences->Other)\n#popup.view_region_cfg=Region\n#popup.view_region_cfg_description=Show regioned control flow graph for this function (enable in File->Preferences->Other)\npopup.add_comment=주석\n#popup.update_comment=Update comment\npopup.search_comment=주석 검색\npopup.rename=이름 바꾸기\npopup.search=\"%s\" 검색\npopup.search_global=\"%s\" 전역 검색\n#popup.remove=Remove\n#popup.add_files=Add files\n#popup.add_scripts=Add scripts\n#popup.new_script=New script\n#popup.json_prettify=JSON Prettify\n#popup.export=Export\n#popup.usage_dialog_plus=Usage Tree Search\n#popup.copy_as=Copy as...\n#popup.copy_as_hex=Copy as HEX\n#popup.copy_as_string=Copy as String\n#popup.copy_offset=Copy offset\n\n#script.run=Run\n#script.save=Save\n#script.auto_complete=Auto Complete\n#script.check=Check\n#script.format=Reformat\n#script.log=Show log\n\n#encoding_dialog.title=Encoding\n#encoding_dialog.message=Select encoding:\n\nexclude_dialog.title=패키지 선택기\nexclude_dialog.select_all=모두 선택\nexclude_dialog.deselect=선택 해제\nexclude_dialog.invert=반전\n\nconfirm.save_as_title=다른 이름으로 저장 확인\nconfirm.save_as_message=%s이(가) 이미 있습니다.\\n바꾸시겠습니까?\nconfirm.not_saved_title=프로젝트 저장\nconfirm.not_saved_message=계속하기 전에 현재 프로젝트를 저장 하시겠습니까?\n#confirm.remember=Remember my decision\n\ncertificate.cert_type=유형\ncertificate.serialSigVer=버전\ncertificate.serialNumber=시리얼 번호\ncertificate.cert_subject=소유자\ncertificate.serialValidFrom=유효 시작 시각\ncertificate.serialValidUntil=유효 종료 시각\ncertificate.serialPubKeyType=공개키 타입\ncertificate.serialPubKeyExponent=지수\ncertificate.serialPubKeyModulus=모듈러스\ncertificate.serialPubKeyModulusSize=모듈러스 크기 (비트)\ncertificate.serialSigType=서명 유형\ncertificate.serialSigOID=서명 OID\ncertificate.serialMD5=MD5 지문\ncertificate.serialSHA1=SHA-1 지문\ncertificate.serialSHA256=SHA-256 지문\ncertificate.serialPubKeyY=Y\n\napkSignature.signer=서명자\n#apkSignature.loading=Loading signature...\napkSignature.verificationSuccess=서명 검증 성공\napkSignature.verificationFailed=서명 검증 실패\napkSignature.signatureSuccess=유효한 APK 서명 v%d을(를) 찾았습니다.\napkSignature.signatureFailed=유효하지 않은 APK 서명 v%d을(를) 찾았습니다.\napkSignature.errors=오류\napkSignature.warnings=경고\napkSignature.exception=APK 검증 실패\napkSignature.unprotectedEntry=APK 서명 v1에 의해 보호되지 않는 파일. 이러한 항목에 대한 승인되지 않은 수정은 APK 서명 v2 이상에서만 감지할 수 있습니다.\n\nissues_panel.label=이슈:\nissues_panel.errors=오류 %d개\nissues_panel.warnings=경고 %d개\nissues_panel.tooltip=로그 뷰어에서 열기\n\ndebugger.process_selector=디버깅 할 프로세스 선택\ndebugger.step_into=한 단계씩 코드 실행 (F7)\ndebugger.step_over=프로시저 단위 실행 (F8)\ndebugger.step_out=프로시저 나가기 (Shift + F8)\ndebugger.run=실행 (F9)\ndebugger.stop=디버거 중지 후 앱 종료\ndebugger.pause=일시 중지\ndebugger.rerun=재실행\ndebugger.cfm_dialog_title=디버깅 중 종료\ndebugger.cfm_dialog_msg=디버거를 종료 하시겠습니까?\n\ndebugger.popup_set_value=값 설정\ndebugger.popup_change_to_zero=0으로 변경\ndebugger.popup_change_to_one=1로 변경\ndebugger.popup_copy_value=값 복사\n\n#logcat.pause=Pause Logcat\n#logcat.start=Resume Logcat\n#logcat.clear=Clear Logcat\n\n#logcat.error_fail_start=Failed to start logcat\n#logcat.process=Process\n#logcat.level=Level\n#logcat.default=Default\n#logcat.verbose=Verbose\n#logcat.debug=Debug\n#logcat.info=Info\n#logcat.warn=Warn\n#logcat.error=Error\n#logcat.fatal=Fatal\n#logcat.silent=Silent\n#logcat.logcat=Logcat\n#logcat.select_attached=Select Attached\n#logcat.select_all=Select All\n#logcat.unselect_all=Unselect All\n\nset_value_dialog.label_value=값\nset_value_dialog.btn_set=값 설정\nset_value_dialog.title=값 설정\nset_value_dialog.neg_msg=값 설정에 실패했습니다.\nset_value_dialog.sel_type=값을 설정할 유형을 선택하십시오.\n\nadb_dialog.addr=ADB 주소\nadb_dialog.port=ADB 포트\nadb_dialog.path=ADB 경로\nadb_dialog.launch_app=앱 실행\nadb_dialog.start_server=ADB 서버 시작\nadb_dialog.refresh=새로고침\nadb_dialog.tip_devices=기기 %d 대\nadb_dialog.device_node=기기\nadb_dialog.missing_path=ADB 서버를 시작하려면 ADB 경로를 제공해야합니다.\nadb_dialog.waiting=ADB 서버 연결 대기 중 ...\nadb_dialog.connecting=ADB 서버에 연결 중입니다. 주소: %s:%s...\nadb_dialog.connect_okay=ADB 서버가 연결되었습니다. 주소: %s:%s\nadb_dialog.connect_fail=ADB 서버에 연결하지 못했습니다.\nadb_dialog.disconnected=ADB 서버 연결이 끊어졌습니다.\nadb_dialog.start_okay=ADB 서버가 포트 %s 에서 시작되었습니다.\nadb_dialog.start_fail=포트 %s 에서 ADB 서버를 시작하지 못했습니다.\nadb_dialog.forward_fail=알수 없는 이유로 전달하지 못했습니다.\nadb_dialog.being_debugged_msg=이 프로세스는 디버깅중인 것 같습니다. 계속 진행합니까?\nadb_dialog.unknown_android_ver=Android 릴리스 버전을 가져 오지 못했습니다. Android 8을 기본값으로 사용 하시겠습니까?\nadb_dialog.being_debugged_title=이미 다른 세션에 의해 디버깅중인 프로세스입니다.\nadb_dialog.init_dbg_fail=디버거를 초기화하는데 실패했습니다.\nadb_dialog.msg_read_mani_fail=AndroidManifest.xml을 디코딩하는데 실패했습니다.\nadb_dialog.no_devices=앱을 실행할 기기를 찾을 수 없습니다.\nadb_dialog.restart_while_debugging_title=디버깅중 다시 시작\nadb_dialog.restart_while_debugging_msg=앱을 디버깅하고 있습니다. 세션을 다시 시작 하시겠습니까?\nadb_dialog.starting_debugger=디버거 시작 중 ...\n\n#action.variant=%s (variant)\n#action_category.menu_toolbar=Menu / Toolbar\n#action_category.hex_viewer=View / Hex Viewer\n#action_category.code_area=Code Area\n#action_category.plugin_script=Plugin Script\n\n#hex_viewer.show_inspector=Show Inspector\n#hex_viewer.change_encoding=Change Encoding\n#hex_viewer.goto_address=Go To Address\n#hex_viewer.enter_address=Enter address range:\n#hex_viewer.find=Find\n\n#graph_viewer.long_names=Show full names\n#graph_viewer.overrides=Show overrides\n#graph_viewer.callee_depth=Down depth\n#graph_viewer.caller_depth=Up depth\n#graph_viewer.default_error=Failed to view graph\n#graph_viewer.file_not_found_error=Failed to load graph file\n#graph_viewer.image_too_large=Failed to render graph: graph too large\n#graph_viewer.image_too_small=Failed to render graph: graph too small\n#graph_viewer.file_failure=Error in File Operation\n#graph_viewer.save_graph=Save graph\n\n#graph_viewer.default_title=Graph Viewer\n#graph_viewer.method_graph.title=Methods Graph\n#graph_viewer.call_graph.title=Call Graph\n#graph_viewer.inheritance_graph.title=Inheritance Graph\n#graph_viewer.cfg.title=Control Flow Graph\n"
  },
  {
    "path": "jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties",
    "content": "language.name=Português\n\nmenu.file=Arquivo\nmenu.view=Ver\nmenu.recent_projects=Projetos recentes\nmenu.no_recent_projects=Nenhum projeto recente\nmenu.preferences=Preferências\nmenu.sync=Sincronizar com editor\nmenu.flatten=Mostrar pacotes achatados\n#menu.enable_preview_tab=Enable Preview Tab\nmenu.heapUsageBar=Mostrar uso de memória\nmenu.alwaysSelectOpened=Sempre selecionar arquivo/classe aberta\n#menu.dock_log=Dock Log Viewer\n#menu.dock_quick_tabs=Show Quick Tabs\nmenu.navigation=Navegação\nmenu.text_search=Buscar por texto\nmenu.class_search=Buscar por classe\nmenu.comment_search=Busca por comentário\n#menu.go_to_main_activity=Go to main Activity\n#menu.go_to_application=Go to Application\n#menu.go_to_android_manifest=Go to AndroidManifest.xml\nmenu.tools=Ferramentas\n#menu.plugins=Plugins\n#menu.decompile_all=Decompile all classes\n#menu.reset_cache=Reset code cache\nmenu.deobfuscation=Desofuscar\nmenu.log=Visualizador de log\n#menu.create_desktop_entry=Create Desktop Entry\nmenu.help=Ajuda\nmenu.about=Sobre\n#menu.quark=Quark Engine\nmenu.update_label=Nova versão %s disponível!\n#menu.hex_viewer=Hex Viewer\n\nfile.open_action=Abrir arquivos...\nfile.add_files_action=Adicionar arquivos\nfile.open_title=Abrir arquivo\nfile.open_project=Abrir projeto\nfile.new_project=Novo projeto\nfile.save_project=Salvar projeto\nfile.save_project_as=Salvar projeto como...\nfile.reload=Recarregar arquivos\nfile.live_reload=Recarregar em tempo real\nfile.live_reload_desc=Recarregar arquivos automaticamente ao serem alterados\n#file.open_mappings=Open mappings...\n#file.save_mappings=Save mappings\n#file.save_mappings_as=Save mappings as...\n#file.close_mappings=Close mappings\nfile.save_all=Salvar tudo\n#file.save=Save\n#file.export=Export project\nfile.save_all_msg=Selecionar diretório para salvar arquivos descompilados\nfile.exit=Sair\n#file.export_node=Export file\n\nstart_page.title=Página inicial\nstart_page.start=Começar\nstart_page.recent=Projetos recentes\n#start_page.list.delete_recent_project=Delete project\n#start_page.list.delete_recent_project.tooltip=Delete project from recent list\n\n#tree.inputs_title=Inputs\n#tree.input_files=Files\n#tree.input_scripts=Scripts\ntree.sources_title=Código fonte\ntree.resources_title=Recursos\ntree.loading=Carregando...\n#tree.pinned_tabs=Pinned Tabs\n#tree.open_tabs=Open Tabs\n#tree.bookmarked_tabs=Bookmarked Tabs\n\nprogress.load=Carregando\n#progress.save_mappings=Saving mappings\nprogress.decompile=Descompilando\nprogress.canceling=Cancelando\n\nerror_dialog.title=Erro\n#error_dialog.not_found=%s not found\n#error_dialog.not_found_file=File not found at path:\\n%s\n#error_dialog.path_is_directory=The specified path is a directory:\\n%s\n#error_dialog.cannot_read=Cannot read file:\\n%s\n#error_dialog.open_failed=Failed to open file:\\n%s\n#error_dialog.invalid_path_format=Invalid file path:\\n%s\n#error_dialog.desktop_unsupported=Desktop is not supported in this environment.\n\nsearch.previous=Anterior\nsearch.next=Próximo\nsearch.mark_all=Marcar todos\nsearch.regex=Regex\nsearch.match_case=Match Case\nsearch.whole_word=Palavra inteira\nsearch.find=Encontrar\n#search.results=%s%d results\n#search.match_of=Match %d of %d\n#search.match_found=Match found\n#search.match_not_found=No Matches found\n#search.single_match=Single match found\n#search.find_type_text=Find by text\n#search.find_type_hex=Find by hex\n\ntabs.copy_class_name=Copiar nome\ntabs.close=Fechar\ntabs.closeOthers=Fechar outros\n#tabs.unpin=Unpin\n#tabs.unpin_all=Unpin All\n#tabs.bookmark=Bookmark\n#tabs.unbookmark=Unbookmark\n#tabs.unbookmark_all=Unbookmark All\n#tabs.pin=Pin\ntabs.closeAll=Fechar todos\ntabs.closeAllRight=Feche tudo à direita\n#tabs.closeAllLeft=Close All Left\ntabs.code=Código\ntabs.smali=Smali\ntabs.smali_bytecode=Smali+Bytecode\n\nnav.back=Voltar\nnav.forward=Avançar\n\nmessage.taskTimeout=Tarefa excedeu limite de tempo de %d ms.\nmessage.userCancelTask=Tarefa foi cancelada pelo usuário.\nmessage.memoryLow=Jadx está rodando com pouca memória. Por favor reinicie com um limite de memória heap maior.\nmessage.taskError=Tarefa falhou com erro (cheque o log para detalhes).\nmessage.errorTitle=Erro\nmessage.load_errors=Carregamento falhou.\\nNúmero de erros: %d\\nClique em ok para abrir o visualizador de log.\nmessage.no_classes=Nenhuma classe carregada, nada para ser descompilado!\n#message.enter_valid_path=Enter valid path to save!\n\nmessage.saveIncomplete=<html>Operação não foi completa.<br> %s<br> %d classes ou recursos não foram salvos!</html>\nmessage.indexIncomplete=<html>Indexação de algumas classes foram ignoradas.<br> %s<br> %d classes não foram indexadas não vão aparecer nos resultados de busca!</html>\nmessage.indexingClassesSkipped=<html>Jadx está rodando com pouca memória. Por conta disso, %d classes não foram indexadas.<br>Se você deseja que todas classes sejam indexadas, reinicie com um limite de memória heap maior.</html>\n#message.enter_new_name=Enter new name\n#message.could_not_rename=Can't rename the file\n#message.confirm_remove_script=Do you really want to remove script?\n#message.desktop_entry_creation_error=Failed to create desktop entry (check log for details).\n#message.desktop_entry_creation_success=Desktop entry created successfully!\n#message.success_title=Success\n#message.unable_preview_font=Unable preview font\n\nheapUsage.text=Uso de memória do JADX: %.2f GB of %.2f GB (%.2f GB pico)\n\ncommon_dialog.ok=Ok\ncommon_dialog.cancel=Cancelar\ncommon_dialog.add=Adicionar\ncommon_dialog.update=Atualizar\ncommon_dialog.remove=Remover\n#common_dialog.reset=Reset\n\nfile_dialog.supported_files=Arquivos suportados\nfile_dialog.load_dir_title=Carregar diretório\nfile_dialog.load_dir_confirm=Carregar todos arquivos do diretório?\n\nsearch_dialog.open=Abrir\nsearch_dialog.cancel=Cancelar\nsearch_dialog.open_by_name=Buscar por texto:\nsearch_dialog.search_button=Buscar\nsearch_dialog.search_history=Buscar histórico\nsearch_dialog.auto_search=Busca automática\nsearch_dialog.search_in=Buscar definições de:\nsearch_dialog.class=Classe\nsearch_dialog.method=Método\nsearch_dialog.field=Propriedade\nsearch_dialog.code=Código\nsearch_dialog.ignorecase=Não diferencia maiúsculas de minúsculas\nsearch_dialog.load_more=Carregar mais\nsearch_dialog.load_all=Carregar todas\nsearch_dialog.stop=Parar\nsearch_dialog.results_incomplete=Encontradas %d+\nsearch_dialog.results_complete=Encontradas %d (completos)\n#search_dialog.resources_load_errors=Load errors: %d\n#search_dialog.resources_skip_by_size=Skipped by size: %d\n#search_dialog.resources_check_logs=(click to check logs)\nsearch_dialog.col_node=Nó\nsearch_dialog.col_code=Código\nsearch_dialog.sort_results=Ordenar resultados\nsearch_dialog.regex=Expressão regular\nsearch_dialog.active_tab=Apenas abas ativas\nsearch_dialog.comments=Comentários\nsearch_dialog.resource=Recursos\nsearch_dialog.keep_open=Manter aberto\nsearch_dialog.tip_searching=Buscando\n#search_dialog.limit_package=Limit to package:\n#search_dialog.res_text=Text\n#search_dialog.res_binary=Binary\n#search_dialog.package_not_found=No matching package found\nsearch_dialog.copy=skopiuj wszystko\n\nusage_dialog.title=Busca por utilização\nusage_dialog.label=Usado por:\n\n#usage_dialog_plus.title=Usage tree search\n#usage_dialog_plus.jump_to=Jump to current location\n#usage_dialog_plus.copy_path=Copy usage tree path\n#usage_dialog_plus.search_complete=Search completed\n#usage_dialog_plus.code_view=Code View\n#usage_dialog_plus.select_node=Select Node\n#usage_dialog_plus.code_for=Code for %s\n#usage_dialog_plus.expand_usages=Expand Data\n\ncomment_dialog.title.add=Adicionar comentário ao código\ncomment_dialog.title.update=Atualizar comentário do código\ncomment_dialog.label=Comentário:\n#comment_dialog.style=Style:\ncomment_dialog.usage=Use Shift + Enter para pular uma linha\n\n#rename_dialog.class_help=Enter full name to move class to another package. Start with '.' to move to default (empty) package\n\n#export_dialog.title=Export\n#export_dialog.save_path=Save path:\n#export_dialog.browse=Browse\n#export_dialog.export_options=Export options\n#export_dialog.export_gradle=Export as a Gradle project\n#export_dialog.export_gradle_type=Gradle template:\n\nlog_viewer.title=Visualizador de log\nlog_viewer.log_level=Nível do log:\n#log_viewer.mode=Mode:\n#log_viewer.modes=All|All scripts|Current script\n#log_viewer.hide=Hide\n#log_viewer.dock=Dock\n#log_viewer.undock=Undock\n#log_viewer.clear=Clear\n\nabout_dialog.title=Sobre o JADX\n\npreferences.title=Preferências\npreferences.deobfuscation=Desofuscar\npreferences.appearance=Aparência\n#preferences.shortcuts=Shortcuts\n#preferences.select_shortcuts=Select a specific shortcuts group\npreferences.decompile=Descompilação\npreferences.plugins=Plugins\npreferences.project=Projeto\npreferences.other=Outro\npreferences.language=Idioma\npreferences.lineNumbersMode=Modo do contador de linhas do editor\npreferences.jumpOnDoubleClick=Ativar salto no duplo clique\n#preferences.useAlternativeFileDialog=Use alternative file dialog\npreferences.check_for_updates=Verificar por atualizações ao inicializar\npreferences.useDx=Usar dx/d8 para converter bytecode Java\npreferences.decompilationMode=Modo de descompilação\npreferences.codeCacheMode=Modo de cachê do código\n#preferences.codeCacheMode.memory=Memory\n#preferences.codeCacheMode.memory.desc=Everything in memory: fast search, slow reopen, high memory usage\n#preferences.codeCacheMode.diskWithCache=Disk with cache\n#preferences.codeCacheMode.diskWithCache.desc=Code saved on disk with in memory cache: medium search, fast reopen, medium memory usage\n#preferences.codeCacheMode.disk=Disk\n#preferences.codeCacheMode.disk.desc=Everything on disk: slow search, fast reopen, low memory usage\n#preferences.usageCacheMode=Usage data cache mode\npreferences.showInconsistentCode=Mostrar código inconsistent\npreferences.escapeUnicode=Escapar unicode\npreferences.replaceConsts=Substituir constantes\npreferences.respectBytecodeAccessModifiers=Respeitar modificadores de acesso do bytecode\npreferences.useImports=Utilizar declaração de imports\npreferences.useDebugInfo=Utilizar informação de depuração\npreferences.inlineAnonymous=Classes anônimas de uma linha\npreferences.inlineMethods=Métodos de uma linha\n#preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas\n#preferences.moveInnerClasses=Move inner classes into parent\npreferences.extractFinally=Extrair blocos finally\n#preferences.restoreSwitchOverString=Restore switch over string\npreferences.fsCaseSensitive=Sistema de arquivo diferencia maiúsculas de minúsculas\npreferences.skipResourcesDecode=Não decodificar recursos\n#preferences.skipSourcesDecode=Don't decompile source code\npreferences.useKotlinMethodsForVarNames=Usar métodos do kotlin para renomear variáveis\npreferences.commentsLevel=Nível de comentários do código\n#preferences.saveOption=Auto-save settings\npreferences.threads=Número de threads no processo\npreferences.excludedPackages=Pacotes ignorados\npreferences.excludedPackages.tooltip=Lista espaço de pacotes que não vão ser descompilados ou indexados (economiza RAM)\npreferences.excludedPackages.button=Editar\npreferences.excludedPackages.editDialog=<html>Lista espaço de pacotes que não vão ser descompilados ou indexados (economiza RAM)<br>ex: <code>android.support</code></html>\npreferences.cfg=Gera gráficos de métodos CFG no formato de pontos ('dot')\npreferences.raw_cfg=Gera gráficos CFG no formato RAW\n#preferences.xposed_codegen_language=Xposed code generation language\n#preferences.update_channel=Jadx update channel\n#preferences.disable_tooltip_on_hover=Disable tooltip on hover\n#preferences.integerFormat=Integer format\n#preferences.typeUpdatesCountLimit=Update type limit count\n#preferences.ui_zoom=UI Zoom factor\n#preferences.apply_ui_zoom_to_fonts=Apply UI zoom to fonts\n#preferences.ui_font=UI font\npreferences.code_font=Fonte do editor\n#preferences.smali_font=Monospaced font (Smali/Hex)\npreferences.laf_theme=Tema\n#preferences.dynamic_editor_theme=Use UI theme colors\npreferences.theme=Tema do editor\npreferences.start_jobs=Inicializar descompilação automaticamente em segundo-plano\npreferences.select_font=Alterar\npreferences.deobfuscation_on=Ativar desofuscação\n#preferences.generated_renames_mapping_file_mode=Map file handle mode\npreferences.deobfuscation_min_len=Tamanho mínimo do nome\npreferences.deobfuscation_max_len=Tamanho máximo do nome\npreferences.deobfuscation_res_name_source=Melhora nome da fonte dos recursos\n#preferences.deobfuscation_res_use_headers=Use headers for detect resource extensions\n#preferences.deobfuscation_whitelist=Exclude packages and classes from deobfuscation\n#preferences.deobfuscation_whitelist.editDialog=Whitelist for deobfuscation\n#preferences.deobfuscation_whitelist.tooltip=List of ':' separated packages (suffix '.*') and class names that will not be deobfuscated\npreferences.save=Salvar\npreferences.cancel=Cancelar\npreferences.reset=Redefinir\npreferences.reset_message=Redefinir configurações para o padrão?\npreferences.reset_title=Redefinir configurações\npreferences.copy=Copy to clipboard\npreferences.copy_message=Todas configurações foram copiadas para área de transferência\npreferences.rename=Renomear identificadores\npreferences.rename_case=Corrigir problemas de capitalização (case sensitivity)\npreferences.rename_valid=Deixá-las válidas\npreferences.rename_printable=Deixá-las imprimíveis (printable)\npreferences.rename_use_source_name_as_class_name_alias=Utilizar nome do arquivo como apelido da classe\n#preferences.rename_source_name_repeat_limit=Allow using source name if it appears less than a limit number\n#preferences.search_results_per_page=Results per page (0 - no limit)\npreferences.res_file_ext=Extensões de arquivos (ex: .xml|.html), * significa todas\npreferences.res_skip_file=Pular arquivos excedidos\npreferences.tab_dnd_appearance=Dragging tab appearance\n\n#preferences.plugins.manage=Manage plugins\n#preferences.plugins.install=Install plugin\n#preferences.plugins.install_btn=Install\n#preferences.plugins.uninstall_btn=Uninstall\n#preferences.plugins.disable_btn=Disable\n#preferences.plugins.enable_btn=Enable\n#preferences.plugins.location_id_label=Location id:\n#preferences.plugins.plugin_jar=Select Plugin jar\n#preferences.plugins.plugin_jar_label=or\n#preferences.plugins.update_all=Update All\n#preferences.plugins.details=Plugin details\n#preferences.plugins.task.installing=Installing plugin\n#preferences.plugins.task.uninstalling=Uninstalling plugin\n#preferences.plugins.task.updating=Updating plugins\n#preferences.plugins.task.downloading_list=Downloading plugins list\n#preferences.plugins.task.status=Changing plugin status\n\n#preferences.cache=Cache\n#preferences.cache.location=Cache location\n#preferences.cache.location_default=App cache system directory\n#preferences.cache.location_local=Same directory as project file\n#preferences.cache.location_custom=Custom location:\n#preferences.cache.change_notice=* for exists caches change will be applied after cache remove or manual reset\n#preferences.cache.table.title=Caches list\n#preferences.cache.table.project=Cache for project\n#preferences.cache.table.size=Disk usage\n#preferences.cache.btn.usage=Calculate usage\n#preferences.cache.btn.delete_selected=Delete Selected\n#preferences.cache.btn.delete_all=Delete All\n#preferences.cache.task.usage=Calculating cache size\n#preferences.cache.task.delete=Deleting caches\n\nmsg.open_file=Abra um arquivo\nmsg.saving_sources=Salvando recursos\nmsg.language_changed_title=Idioma alterado\nmsg.language_changed=Novo idioma será mostrado na próxima inicialização.\n#msg.warning_title=Warning\n#msg.common_mouse_shortcut=This is a commonly used key, are you sure you would like to bind it to an action?\n#msg.duplicate_shortcut=The shortcut %s is already set in action \"%s\" from category \"%s\", continue ?\nmsg.cmd_select_class_error=Falha ao selecionar classe\\n%s\\nA classe não existe.\nmsg.cant_add_comment=Não é possível adicionar comentários aqui\n#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font \"%s\". Show chars?\n#msg.non_displayable_chars.title=Undisplayed Strings\n\n#methods_dialog.title=Select methods\n\npopup.bytecode_col=Mostrar Dalvik Bytecode\npopup.line_wrap=Quebra de linha\npopup.undo=Desfazer\npopup.redo=Refazer\npopup.cut=Cortar\npopup.copy=Copiar\npopup.paste=Colar\npopup.delete=Apagar\npopup.select_all=Selecionar todos\npopup.frida=Copiar como snippet frida\npopup.xposed=Copiar como snippet xposed\npopup.find_usage=Buscar uso\npopup.go_to_declaration=Ir para declaração\npopup.exclude=Ignorar\npopup.exclude_packages=Pacotes ignorados\n#popup.convert_number=Add conversion as comment\n#popup.view_call_graph=View call graph\n#popup.view_call_graph_description=Show call chains to this function\n#popup.view_class_graph=View inheritance graph\n#popup.view_class_graph_description=Show inheritance tree for this class\n#popup.view_class_method_graph=View methods graph\n#popup.view_class_method_graph_description=Show all methods for this class\n#popup.cfg_submenu=View control flow graph\n#popup.view_cfg=Regular\n#popup.view_cfg_description=Show regular control flow graph for this function (enable in File->Preferences->Other)\n#popup.view_raw_cfg=Raw\n#popup.view_raw_cfg_description=Show control flow graph with raw instructions for this function (enable in File->Preferences->Other)\n#popup.view_region_cfg=Region\n#popup.view_region_cfg_description=Show regioned control flow graph for this function (enable in File->Preferences->Other)\npopup.add_comment=Comentar\n#popup.update_comment=Update comment\npopup.search_comment=Buscar comentários\npopup.rename=Renomear\npopup.search=Buscar \"%s\"\npopup.search_global=Busca global \"%s\"\n#popup.remove=Remove\n#popup.add_files=Add files\n#popup.add_scripts=Add scripts\n#popup.new_script=New script\n#popup.json_prettify=JSON Prettify\n#popup.export=Export\n#popup.usage_dialog_plus=Usage Tree Search\n#popup.copy_as=Copy as...\n#popup.copy_as_hex=Copy as HEX\n#popup.copy_as_string=Copy as String\n#popup.copy_offset=Copy offset\n\n#script.run=Run\n#script.save=Save\n#script.auto_complete=Auto Complete\n#script.check=Check\n#script.format=Reformat\n#script.log=Show log\n\n#encoding_dialog.title=Encoding\n#encoding_dialog.message=Select encoding:\n\nexclude_dialog.title=Selecionar pacote\nexclude_dialog.select_all=Selecionar tudo\nexclude_dialog.deselect=Remover seleção\nexclude_dialog.invert=Inverter\n\nconfirm.save_as_title=Confirmar operação\nconfirm.save_as_message=%s Já existe.\\nVocê deseja substituir?\nconfirm.not_saved_title=Salvar projeto\nconfirm.not_saved_message=Salvar projeto atual antes de continuar?\n#confirm.remember=Remember my decision\n\ncertificate.cert_type=Tipo\ncertificate.serialSigVer=Versão\ncertificate.serialNumber=Número de série (serial)\ncertificate.cert_subject=Remetente\ncertificate.serialValidFrom=Válido de\ncertificate.serialValidUntil=Válido até\ncertificate.serialPubKeyType=Tipo de chave pública\ncertificate.serialPubKeyExponent=Expoente\ncertificate.serialPubKeyModulus=Módulo\ncertificate.serialPubKeyModulusSize=Tamanho do módulo (bits)\ncertificate.serialSigType=Tipo de assinatura\ncertificate.serialSigOID=Assinatura OID\ncertificate.serialMD5=Assinatura digital MD5\ncertificate.serialSHA1=Assinatura digital SHA-1\ncertificate.serialSHA256=Assinatura digital SHA-256\ncertificate.serialPubKeyY=Y\n\napkSignature.signer=Assinador\n#apkSignature.loading=Loading signature...\napkSignature.verificationSuccess=Verificação de assinatura bem-sucedida\napkSignature.verificationFailed=Verificação de assinatura falhou\napkSignature.signatureSuccess=Assinatura válida de apk v%d encontrada\napkSignature.signatureFailed=Assinatura inválida de apk v%d encontrada\napkSignature.errors=Erros\napkSignature.warnings=Avisos\napkSignature.exception=Verificação do APK falhou\napkSignature.unprotectedEntry=Arquivos que não são protegidos pela assinatura v1. Modificações não autorizadas a essas entradas só podem ser detectadas por uma assinatura de versão 2 ou maior.\n\nissues_panel.label=Problemas:\nissues_panel.errors=%d error\nissues_panel.warnings=%d avisos\nissues_panel.tooltip=Abrir no visualizador de log\n\ndebugger.process_selector=Selecionar um processo para depurar\ndebugger.step_into=Passar para próxima chamada de função (F7)\ndebugger.step_over=Passar sobre próxima chamada de função (F8)\ndebugger.step_out=Sair da função atual (Shift + F8)\ndebugger.run=Executar (F9)\ndebugger.stop=Parar depurador e matar o aplicativo\ndebugger.pause=Pausar\ndebugger.rerun=Reexecutar\ndebugger.cfm_dialog_title=Sair enquanto depura\ndebugger.cfm_dialog_msg=Você tem certeza de que deseja terminar o depurador?\n\ndebugger.popup_set_value=Definir valor\ndebugger.popup_change_to_zero=Alterar para 0\ndebugger.popup_change_to_one=Alterar para 1\ndebugger.popup_copy_value=Copiar valor\n\n#logcat.pause=Pause Logcat\n#logcat.start=Resume Logcat\n#logcat.clear=Clear Logcat\n\n#logcat.error_fail_start=Failed to start logcat\n#logcat.process=Process\n#logcat.level=Level\n#logcat.default=Default\n#logcat.verbose=Verbose\n#logcat.debug=Debug\n#logcat.info=Info\n#logcat.warn=Warn\n#logcat.error=Error\n#logcat.fatal=Fatal\n#logcat.silent=Silent\n#logcat.logcat=Logcat\n#logcat.select_attached=Select Attached\n#logcat.select_all=Select All\n#logcat.unselect_all=Unselect All\n\nset_value_dialog.label_value=Valor\nset_value_dialog.btn_set=Definir Valor\nset_value_dialog.title=Definir Valor\nset_value_dialog.neg_msg=Falha ao definir valor.\nset_value_dialog.sel_type=Selecionar um tipo para definir valor.\n\nadb_dialog.addr=Endereço do ADB\nadb_dialog.port=Porta do ADB\nadb_dialog.path=Caminho do ADB\nadb_dialog.launch_app=Iniciar Aplicativo\nadb_dialog.start_server=Iniciar servidor ADB\nadb_dialog.refresh=Recarregar\nadb_dialog.tip_devices=%d dispositivos\nadb_dialog.device_node=dispositivo\nadb_dialog.missing_path=Caminho do ADB deve ser especificado para iniciar o servidor.\nadb_dialog.waiting=Aguardando para conectar no servidor ADB...\nadb_dialog.connecting=Conectando ao servidor ADB, endereço: %s:%s...\nadb_dialog.connect_okay=Servidor ADB conectado, endereço: %s:%s\nadb_dialog.connect_fail=Falha ao se conectar ao servidor ADB.\nadb_dialog.disconnected=Servidor ADB desconectado.\nadb_dialog.start_okay=Servidor ADB inicializado na porta: %s.\nadb_dialog.start_fail=Falha ao iniciar servidor ADB na porta: %s!\nadb_dialog.forward_fail=Falha ao encaminhar por um motivo não especificado.\nadb_dialog.being_debugged_msg=Esse processo parece estar sendo depurado, deseja continuar?\nadb_dialog.unknown_android_ver=Falha ao obter versão do Android, deseja usar o Android 8 como padrão?\nadb_dialog.being_debugged_title=Está sendo depurado por outro processo.\nadb_dialog.init_dbg_fail=Falha ao iniciar depurador.\nadb_dialog.msg_read_mani_fail=Falha ao decodificar AndroidManifest.xml\nadb_dialog.no_devices=Não foi possível encontrar nenhum dispositivo para iniciar o aplicativo.\nadb_dialog.restart_while_debugging_title=Reiniciar enquanto depura\nadb_dialog.restart_while_debugging_msg=Você está depurando um aplicativo, você tem certeza que deseja reiniciar a sessão?\nadb_dialog.starting_debugger=Iniciando depurador...\n\n#action.variant=%s (variant)\n#action_category.menu_toolbar=Menu / Toolbar\n#action_category.hex_viewer=View / Hex Viewer\n#action_category.code_area=Code Area\n#action_category.plugin_script=Plugin Script\n\n#hex_viewer.show_inspector=Show Inspector\n#hex_viewer.change_encoding=Change Encoding\n#hex_viewer.goto_address=Go To Address\n#hex_viewer.enter_address=Enter address range:\n#hex_viewer.find=Find\n\n#graph_viewer.long_names=Show full names\n#graph_viewer.overrides=Show overrides\n#graph_viewer.callee_depth=Down depth\n#graph_viewer.caller_depth=Up depth\n#graph_viewer.default_error=Failed to view graph\n#graph_viewer.file_not_found_error=Failed to load graph file\n#graph_viewer.image_too_large=Failed to render graph: graph too large\n#graph_viewer.image_too_small=Failed to render graph: graph too small\n#graph_viewer.file_failure=Error in File Operation\n#graph_viewer.save_graph=Save graph\n\n#graph_viewer.default_title=Graph Viewer\n#graph_viewer.method_graph.title=Methods Graph\n#graph_viewer.call_graph.title=Call Graph\n#graph_viewer.inheritance_graph.title=Inheritance Graph\n#graph_viewer.cfg.title=Control Flow Graph\n"
  },
  {
    "path": "jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties",
    "content": "language.name=Русский\n\nmenu.file=Файл\nmenu.view=Вид\nmenu.recent_projects=Недавние проекты\nmenu.no_recent_projects=Нет недавно открытых проектов\nmenu.preferences=Параметры\nmenu.sync=Синхр. файлы с редактором\nmenu.flatten=Плоская структура пакетов\n#menu.enable_preview_tab=Enable Preview Tab\nmenu.heapUsageBar=Использование ОЗУ\nmenu.alwaysSelectOpened=Выбирать открытый файл/класс\nmenu.dock_log=Просмотр логов в панели\n#menu.dock_quick_tabs=Show Quick Tabs\nmenu.navigation=Навигация\nmenu.text_search=Поиск строк\nmenu.class_search=Поиск классов\nmenu.comment_search=Поиск комментариев\nmenu.go_to_main_activity=Найти главное Activity\n#menu.go_to_application=Go to Application\n#menu.go_to_android_manifest=Go to AndroidManifest.xml\nmenu.tools=Инструменты\nmenu.plugins=Плагины\nmenu.decompile_all=Декомпилировать все\nmenu.reset_cache=Сбросить кэш\nmenu.deobfuscation=Деобфускация\nmenu.log=Просмотр логов\n#menu.create_desktop_entry=Create Desktop Entry\nmenu.help=Помощь\nmenu.about=О программе\n#menu.quark=Quark Engine\nmenu.update_label=Версия %s уже доступна!\n#menu.hex_viewer=Hex Viewer\n\nfile.open_action=Открыть файлы...\nfile.add_files_action=Добавить файл\nfile.open_title=Открыть файл\nfile.open_project=Открыть проект\nfile.new_project=Новый проект\nfile.save_project=Сохранить проект\nfile.save_project_as=Сохранить проект как...\nfile.reload=Пересканировать файлы\nfile.live_reload=Автосканирование\nfile.live_reload_desc=Автоматически перезагружать файлы при внешних изменениях\nfile.open_mappings=Открыть маппинги...\nfile.save_mappings=Сохранить маппинги\nfile.save_mappings_as=Сохранить маппинги как...\nfile.close_mappings=Закрыть маппинги\nfile.save_all=Сохранить все\nfile.save=Сохранить\n#file.export=Export project\nfile.save_all_msg=Выбрать папку для декомпилированных проектов\nfile.exit=Выход\n#file.export_node=Export file\n\nstart_page.title=Начальная страница\nstart_page.start=Начать\nstart_page.recent=Недавние проекты\n#start_page.list.delete_recent_project=Delete project\n#start_page.list.delete_recent_project.tooltip=Delete project from recent list\n\ntree.inputs_title=Входные файлы\ntree.input_files=Файлы\ntree.input_scripts=Скрипты\ntree.sources_title=Исходный код\ntree.resources_title=Ресурсы\ntree.loading=Загрузка...\n#tree.pinned_tabs=Pinned Tabs\n#tree.open_tabs=Open Tabs\n#tree.bookmarked_tabs=Bookmarked Tabs\n\nprogress.load=Загрузка\nprogress.save_mappings=Сохранить маппинги\nprogress.decompile=Декомпиляция\nprogress.canceling=Прерывание\n\nerror_dialog.title=Ошибка\nerror_dialog.not_found= %s не найден\n#error_dialog.not_found_file=File not found at path:\\n%s\n#error_dialog.path_is_directory=The specified path is a directory:\\n%s\n#error_dialog.cannot_read=Cannot read file:\\n%s\n#error_dialog.open_failed=Failed to open file:\\n%s\n#error_dialog.invalid_path_format=Invalid file path:\\n%s\n#error_dialog.desktop_unsupported=Desktop is not supported in this environment.\n\nsearch.previous=Назад\nsearch.next=Вперед\nsearch.mark_all=Выбрать все\nsearch.regex=Регулярные выражения\nsearch.match_case=Учитывать регистр\nsearch.whole_word=Поиск по словам\nsearch.find=Найти\nsearch.results=%s%d результатов\n#search.match_of=Match %d of %d\n#search.match_found=Match found\n#search.match_not_found=No Matches found\n#search.single_match=Single match found\n#search.find_type_text=Find by text\n#search.find_type_hex=Find by hex\n\ntabs.copy_class_name=Копировать имя\ntabs.close=Закрыть\ntabs.closeOthers=Закрыть другие\n#tabs.unpin=Unpin\n#tabs.unpin_all=Unpin All\n#tabs.bookmark=Bookmark\n#tabs.unbookmark=Unbookmark\n#tabs.unbookmark_all=Unbookmark All\n#tabs.pin=Pin\ntabs.closeAll=Закрыть все\ntabs.closeAllRight=Закройте все справа\n#tabs.closeAllLeft=Close All Left\ntabs.code=Код\ntabs.smali=Smali\ntabs.smali_bytecode=Smali+Bytecode\n\nnav.back=Назад\nnav.forward=Вперед\n\nmessage.taskTimeout=Задача отменена по таймауту в %d мс.\nmessage.userCancelTask=Задача прервана пользователем.\nmessage.memoryLow=Jadx запущен с малым количеством доступной оперативной памяти. Перезапустите увеличив Heap Size\nmessage.taskError=Выполнение задачи закончено с ошибкой (смотрите лог для подробностей).\nmessage.errorTitle=Ошибка\nmessage.load_errors=Ошибка прогрузки.\\nКоличество ошибок: %d\\nНажмите OK, чтобы открыть лог.\nmessage.no_classes=Классы не найдены, нечего декомпилировать!\n#message.enter_valid_path=Enter valid path to save!\n\nmessage.saveIncomplete=<html>Сохранение не завершено.<br> %s<br> %d классов или ресурсов не сохранено!</html>\nmessage.indexIncomplete=<html>Индексирование некоторых классов пропущено.<br> %s<br> %d классов не индексировано, и не будет отображаться в результатах поиска!</html>\nmessage.indexingClassesSkipped=<html>JaDX запущен с малым количеством ОЗУ. %d классов не индексировано.<br>Если вы хотите их индексировать, перезапустите JaDX с большим Heap Size.</html>\n#message.enter_new_name=Enter new name\n#message.could_not_rename=Can't rename the file\n#message.confirm_remove_script=Do you really want to remove script?\n#message.desktop_entry_creation_error=Failed to create desktop entry (check log for details).\n#message.desktop_entry_creation_success=Desktop entry created successfully!\n#message.success_title=Success\n#message.unable_preview_font=Unable preview font\n\nheapUsage.text=JADX использует: %.2f ГБ из %.2f ГБ (%.2f GB пикпик)\n\ncommon_dialog.ok=Ok\ncommon_dialog.cancel=Отмена\ncommon_dialog.add=Добавить\ncommon_dialog.update=Обновить\ncommon_dialog.remove=Убрать\ncommon_dialog.reset=Сброс\n\nfile_dialog.supported_files=Поддерж. файлы\nfile_dialog.load_dir_title=Импортировать папку\nfile_dialog.load_dir_confirm=Импортировать все файлы из директории?\n\nsearch_dialog.open=Открыть\nsearch_dialog.cancel=Отмена\nsearch_dialog.open_by_name=Поиск по тексту:\nsearch_dialog.search_button=Поиск\nsearch_dialog.search_history=История поиска\nsearch_dialog.auto_search=Автопоиск\nsearch_dialog.search_in=Поиск вхождений:\nsearch_dialog.class=Класс\nsearch_dialog.method=Метод\nsearch_dialog.field=Поле\nsearch_dialog.code=Код\nsearch_dialog.ignorecase=Игнорировать регистр\nsearch_dialog.load_more=Загрузить еще\nsearch_dialog.load_all=Загрузить все\nsearch_dialog.stop=Стоп\nsearch_dialog.results_incomplete=Найдено %d+\nsearch_dialog.results_complete=Найдено %d (поиск завершен)\nsearch_dialog.resources_load_errors=Ошибки загрузки: %d\nsearch_dialog.resources_skip_by_size=Пропущено из-за размера: %d\nsearch_dialog.resources_check_logs=(нажмите для просмотра логов)\nsearch_dialog.col_node=Вхождения\nsearch_dialog.col_code=Код\nsearch_dialog.sort_results=Сортировка результатов\nsearch_dialog.regex=Регулярные выражения\nsearch_dialog.active_tab=Только активные вкладки\nsearch_dialog.comments=Комментарии\nsearch_dialog.resource=Ресурсы\nsearch_dialog.keep_open=Оставлять поиск открытым\nsearch_dialog.tip_searching=Поиск...\n#search_dialog.limit_package=Limit to package:\n#search_dialog.res_text=Text\n#search_dialog.res_binary=Binary\n#search_dialog.package_not_found=No matching package found\nsearch_dialog.copy=скопировать все\n\nusage_dialog.title=Поиск использований\nusage_dialog.label=Использования:\n\n#usage_dialog_plus.title=Usage tree search\n#usage_dialog_plus.jump_to=Jump to current location\n#usage_dialog_plus.copy_path=Copy usage tree path\n#usage_dialog_plus.search_complete=Search completed\n#usage_dialog_plus.code_view=Code View\n#usage_dialog_plus.select_node=Select Node\n#usage_dialog_plus.code_for=Code for %s\n#usage_dialog_plus.expand_usages=Expand Data\n\ncomment_dialog.title.add=Добавить комментарий\ncomment_dialog.title.update=Обновить\ncomment_dialog.label=Комментарий:\ncomment_dialog.style=Стиль:\ncomment_dialog.usage=Используйте Shift + Enter для переноса строки\n\nrename_dialog.class_help=Введите полный путь к пакету, в который вы хотите переместить этот класс. Введите '.' для перемещения в пакет по умолчанию (пустой) пакет\n\n#export_dialog.title=Export\n#export_dialog.save_path=Save path:\n#export_dialog.browse=Browse\n#export_dialog.export_options=Export options\n#export_dialog.export_gradle=Export as a Gradle project\n#export_dialog.export_gradle_type=Gradle template:\n\nlog_viewer.title=Просмотр логов\nlog_viewer.log_level=Уровень лога:\nlog_viewer.mode=Режим:\nlog_viewer.modes=Все|Все скрипты|Текущий скрипт\nlog_viewer.hide=Скрыть\nlog_viewer.dock=Прикрепить к панели\nlog_viewer.undock=Открепить от панели\nlog_viewer.clear=Очистить\n\nabout_dialog.title=О программе JADX\n\npreferences.title=Параметры\npreferences.deobfuscation=Деобфускация\npreferences.appearance=Внешний вид\npreferences.shortcuts=Горячие клавиши\npreferences.select_shortcuts=Выбрать горячие клавиши\npreferences.decompile=Декомпиляция\npreferences.plugins=Плагины\npreferences.project=Проект\npreferences.other=Прочее\npreferences.language=Язык\npreferences.lineNumbersMode=Тип переноса строк\npreferences.jumpOnDoubleClick=Переход по двойному клику\npreferences.useAlternativeFileDialog=Использовать альтернативный файлпикер\npreferences.check_for_updates=Проверять наличие новых версий\npreferences.useDx=DX/D8 для конвертации java байткода\npreferences.decompilationMode=Режим декомпиляции\npreferences.codeCacheMode=Кеширование кода\n#preferences.codeCacheMode.memory=Memory\n#preferences.codeCacheMode.memory.desc=Everything in memory: fast search, slow reopen, high memory usage\n#preferences.codeCacheMode.diskWithCache=Disk with cache\n#preferences.codeCacheMode.diskWithCache.desc=Code saved on disk with in memory cache: medium search, fast reopen, medium memory usage\n#preferences.codeCacheMode.disk=Disk\n#preferences.codeCacheMode.disk.desc=Everything on disk: slow search, fast reopen, low memory usage\npreferences.usageCacheMode=Использование кэша\npreferences.showInconsistentCode=Показывать некорректный код\npreferences.escapeUnicode=Кодирование unicode\npreferences.replaceConsts=Замена констант\npreferences.respectBytecodeAccessModifiers=Исходные модификаторы доступа\npreferences.useImports=Использовать импорты\npreferences.useDebugInfo=Отладочная информация\npreferences.inlineAnonymous=Объединять анонимные классы\npreferences.inlineMethods=Объединять методы\npreferences.inlineKotlinLambdas=Разрешить инлайнить Kotlin лямбды\npreferences.moveInnerClasses=Помещать вложенные классы внутрь родительских\npreferences.extractFinally=Вычленять finally блоки\n#preferences.restoreSwitchOverString=Restore switch over string\npreferences.fsCaseSensitive=Учитывать регистр в файловой системе\npreferences.skipResourcesDecode=Не декодировать ресурсы\n#preferences.skipSourcesDecode=Don't decompile source code\npreferences.useKotlinMethodsForVarNames=Kotlin методы как имена полей\npreferences.commentsLevel=Уровень лога операций\npreferences.saveOption=Автосохранение настроек\npreferences.threads=Количество используемых потоков\npreferences.excludedPackages=Исключенные пакеты\npreferences.excludedPackages.tooltip=Список пакетов, которые не будут декомпилироваться и индексироваться (экономит ОЗУ)\npreferences.excludedPackages.button=Изменить\npreferences.excludedPackages.editDialog=<html>Список пакетов, которые не будут декомпилироваться и индексироваться (экономит ОЗУ)<br>например: <code>android.support</code><br>Разделитель - одинарный пробел</html>\npreferences.cfg=Методы генерации графиков CFG (в \"dot\" формате)\npreferences.raw_cfg=Генерировать необработанные графики CFG\npreferences.xposed_codegen_language=Язык генерации Xposed хуков\npreferences.update_channel=Канал обновления Jadx\n#preferences.disable_tooltip_on_hover=Disable tooltip on hover\npreferences.integerFormat=Формат чисел\n#preferences.typeUpdatesCountLimit=Update type limit count\n#preferences.ui_zoom=UI Zoom factor\n#preferences.apply_ui_zoom_to_fonts=Apply UI zoom to fonts\n#preferences.ui_font=UI font\npreferences.code_font=Шрифт редактора Java\npreferences.smali_font=Шрифт smali/HEX редактора\npreferences.laf_theme=Тема приложения\n#preferences.dynamic_editor_theme=Use UI theme colors\npreferences.theme=Тема редактора\npreferences.start_jobs=Автоматическая декомпиляция\npreferences.select_font=Изменить\npreferences.deobfuscation_on=Включить деобфускацию\npreferences.generated_renames_mapping_file_mode=Режим обработки маппингов\npreferences.deobfuscation_min_len=Минимальная длина имени\npreferences.deobfuscation_max_len=Максимальная длина имени\npreferences.deobfuscation_res_name_source=Расшифровка имен ресурсов\n#preferences.deobfuscation_res_use_headers=Use headers for detect resource extensions\npreferences.deobfuscation_whitelist=Исключить пакеты и классы из деобфускации\npreferences.deobfuscation_whitelist.editDialog=Белый список деобфускации\npreferences.deobfuscation_whitelist.tooltip=Разделяйте пакеты через ':' (суффикс '.*') и имя класса которое не будет деобфусцировано\npreferences.save=Сохранить\npreferences.cancel=Отмена\npreferences.reset=Сброс\npreferences.reset_message=Сбросить настройки на значения по умолчанию?\npreferences.reset_title=Сбросить настройки\npreferences.copy=Скопировать в буфер обмена\npreferences.copy_message=Все настройки скопированы в буфер обмена\npreferences.rename=Переименовать идентификаторы\npreferences.rename_case=И исправить проблемы именования\npreferences.rename_valid=И сделать их верными\npreferences.rename_printable=И сделать их доступными для печати\npreferences.rename_use_source_name_as_class_name_alias=Иcпользовать атрибут SOURCE\n#preferences.rename_source_name_repeat_limit=Allow using source name if it appears less than a limit number\npreferences.search_results_per_page=Результатов на страницу (0 - без лимита)\npreferences.res_file_ext=Расширения файлов ресурсов ('xml|html', * для всех)\npreferences.res_skip_file=Пропускать ресурсы больше чем (в МБ)\npreferences.tab_dnd_appearance=Dragging tab appearance\n\n#preferences.plugins.manage=Manage plugins\npreferences.plugins.install=Установить плагин\npreferences.plugins.install_btn=Установить\npreferences.plugins.uninstall_btn=Удалить\n#preferences.plugins.disable_btn=Disable\n#preferences.plugins.enable_btn=Enable\npreferences.plugins.location_id_label=ID источика:\npreferences.plugins.plugin_jar=Выберите jar-файл плагина\npreferences.plugins.plugin_jar_label=или\npreferences.plugins.update_all=Обновить все\npreferences.plugins.details=О плагине\npreferences.plugins.task.installing=Установка плагина\npreferences.plugins.task.uninstalling=Удаление плагина\npreferences.plugins.task.updating=Обновление плагинов\npreferences.plugins.task.downloading_list=Загрузка списка плагинов\n#preferences.plugins.task.status=Changing plugin status\n\npreferences.cache=Кэш\npreferences.cache.location=Директория кэша\npreferences.cache.location_default=Системная папка приложения\npreferences.cache.location_local=Совпадает с папкой проекта\npreferences.cache.location_custom=Кастомная папка:\npreferences.cache.change_notice=* для существующего кеша, потребуется удаление или ручной сброс\npreferences.cache.table.title=Список папок с кэшем\npreferences.cache.table.project=Кэш проекта\npreferences.cache.table.size=Место на диске\npreferences.cache.btn.usage=Подсчитать место\npreferences.cache.btn.delete_selected=Удалить выбранное\npreferences.cache.btn.delete_all=Удалить все\npreferences.cache.task.usage=Подсчет занимаемого места\npreferences.cache.task.delete=Удаление кэша\n\nmsg.open_file=Пожалуйста, откройте файл\nmsg.saving_sources=Сохранение ресурсов\nmsg.language_changed_title=Язык изменен\nmsg.language_changed=Новый язык применится при следующем запуске программы\nmsg.warning_title=Внимание\nmsg.common_mouse_shortcut=Это часто распространенная комбинация, вы действительно хотите назначить ее?\nmsg.duplicate_shortcut=Действие %s уже выполняет \"%s\" в категории \"%s\", продолжить?\nmsg.cmd_select_class_error=Ошибка выбора класса\\n%s\\nЭтот класс не существует.\nmsg.cant_add_comment=Невозможно добавить комментарий сюда\n#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font \"%s\". Show chars?\n#msg.non_displayable_chars.title=Undisplayed Strings\n\n#methods_dialog.title=Select methods\n\npopup.bytecode_col=Показать Dalvik байткод\npopup.line_wrap=Перенос строк\npopup.undo=Отменить\npopup.redo=Вернуть\npopup.cut=Вырезать\npopup.copy=Копировать\npopup.paste=Вставить\npopup.delete=Удалить\npopup.select_all=Выбрать все\npopup.frida=Копировать как хук Frida\npopup.xposed=Копировать как хук Xposed\npopup.find_usage=Найти использования\npopup.go_to_declaration=Перейти к объявлению\npopup.exclude=Исключить\npopup.exclude_packages=Исключить пакеты\n#popup.convert_number=Add conversion as comment\n#popup.view_call_graph=View call graph\n#popup.view_call_graph_description=Show call chains to this function\n#popup.view_class_graph=View inheritance graph\n#popup.view_class_graph_description=Show inheritance tree for this class\n#popup.view_class_method_graph=View methods graph\n#popup.view_class_method_graph_description=Show all methods for this class\n#popup.cfg_submenu=View control flow graph\n#popup.view_cfg=Regular\n#popup.view_cfg_description=Show regular control flow graph for this function (enable in File->Preferences->Other)\n#popup.view_raw_cfg=Raw\n#popup.view_raw_cfg_description=Show control flow graph with raw instructions for this function (enable in File->Preferences->Other)\n#popup.view_region_cfg=Region\n#popup.view_region_cfg_description=Show regioned control flow graph for this function (enable in File->Preferences->Other)\npopup.add_comment=Комментарий\n#popup.update_comment=Update comment\npopup.search_comment=Поиск комментариев\npopup.rename=Переименовать\npopup.search=Найти \"%s\"\npopup.search_global=Глобальный поиск \"%s\"\npopup.remove=Удалить\npopup.add_files=Добавить файлы\npopup.add_scripts=Добавить скрипты\npopup.new_script=Новый скрипт\npopup.json_prettify=Форматировать JSON\n#popup.export=Export\n#popup.usage_dialog_plus=Usage Tree Search\n#popup.copy_as=Copy as...\n#popup.copy_as_hex=Copy as HEX\n#popup.copy_as_string=Copy as String\n#popup.copy_offset=Copy offset\n\nscript.run=Запустить\nscript.save=Сохранить\nscript.auto_complete=Автозавершение\nscript.check=Проверить\nscript.format=Форматировать\nscript.log=Показать лог\n\n#encoding_dialog.title=Encoding\n#encoding_dialog.message=Select encoding:\n\nexclude_dialog.title=Выбор пакетов\nexclude_dialog.select_all=Выбрать все\nexclude_dialog.deselect=Убрать\nexclude_dialog.invert=Инвертировать\n\nconfirm.save_as_title=Подтверджение сохранения\nconfirm.save_as_message=%s уже существует.\\nВы хотите его перезаписать?\nconfirm.not_saved_title=Сохранить проект\nconfirm.not_saved_message=Сохранить текущий проект перед выходом?\nconfirm.remember=Запомнить выбор\n\ncertificate.cert_type=Type\ncertificate.serialSigVer=Version\ncertificate.serialNumber=Serial number\ncertificate.cert_subject=Subject\ncertificate.serialValidFrom=Valid from\ncertificate.serialValidUntil=Valid until\ncertificate.serialPubKeyType=Public key type\ncertificate.serialPubKeyExponent=Exponent\ncertificate.serialPubKeyModulus=Modulus\ncertificate.serialPubKeyModulusSize=Modulus size (bits)\ncertificate.serialSigType=Signature type\ncertificate.serialSigOID=Signature OID\ncertificate.serialMD5=MD5 Fingerprint\ncertificate.serialSHA1=SHA-1 Fingerprint\ncertificate.serialSHA256=SHA-256 Fingerprint\ncertificate.serialPubKeyY=Y\n\napkSignature.signer=Signer\n#apkSignature.loading=Loading signature...\napkSignature.verificationSuccess=Signature verification succeeded\napkSignature.verificationFailed=Signature verification failed\napkSignature.signatureSuccess=Valid APK signature v%d found\napkSignature.signatureFailed=Invalid APK signature v%d found\napkSignature.errors=Errors\napkSignature.warnings=Warnings\napkSignature.exception=APK verification failed\napkSignature.unprotectedEntry=Files that are not protected by APK signature v1. Unauthorized modifications to these entries can only be detected by APK signature v2 and higher.\n\nissues_panel.label=Проблемы:\nissues_panel.errors=Ошибки: %d\nissues_panel.warnings=Предупреждения: %d\nissues_panel.tooltip=Открыть просмотр логов\n\ndebugger.process_selector=Выбрать процесс для отладки\ndebugger.step_into=Перейти (F7)\ndebugger.step_over=Прыжок (F8)\ndebugger.step_out=Выйти (Shift + F8)\ndebugger.run=Запустить (F9)\ndebugger.stop=Остановить\ndebugger.pause=Пауза\ndebugger.rerun=Повторить\ndebugger.cfm_dialog_title=Выйти во время отладки\ndebugger.cfm_dialog_msg=Вы действительно хотите остановить дебаггер?\n\ndebugger.popup_set_value=Задать значение\ndebugger.popup_change_to_zero=Изменить на 0\ndebugger.popup_change_to_one=Изменить на 1\ndebugger.popup_copy_value=Копировать значение\n\nlogcat.pause=Остановить\nlogcat.start=Возобновить\nlogcat.clear=Очистить\n\nlogcat.error_fail_start=Ошибка запуска логгера\nlogcat.process=Процесс\nlogcat.level=Уровень лога\nlogcat.default=По умолчанию\nlogcat.verbose=Verbose\nlogcat.debug=Debug\nlogcat.info=Info\nlogcat.warn=Warn\nlogcat.error=Error\nlogcat.fatal=Fatal\nlogcat.silent=Silent\nlogcat.logcat=Лог\nlogcat.select_attached=Прикрепленный процесс\nlogcat.select_all=Выделить все\nlogcat.unselect_all=Отменить выделение\n\nset_value_dialog.label_value=Значение\nset_value_dialog.btn_set=Присвоить\nset_value_dialog.title=Присвоить значение\nset_value_dialog.neg_msg=Невозможно присвоить значение\nset_value_dialog.sel_type=Выберите тип значения\n\nadb_dialog.addr=Адрес\nadb_dialog.port=Порт\nadb_dialog.path=Путь к ADB\nadb_dialog.launch_app=Запустить приложение\nadb_dialog.start_server=Запустить ADB сервер\nadb_dialog.refresh=Обновить\nadb_dialog.tip_devices=%d устройств\nadb_dialog.device_node=Устройство\nadb_dialog.missing_path=Вы должны указать путь к ADB, для того чтобы запустить сервер\nadb_dialog.waiting=Ожидание подключения к ADB...\nadb_dialog.connecting=Подключение к ADB серверу %s на порту %s...\nadb_dialog.connect_okay=Подключено к серверу, адрес: %s:%s\nadb_dialog.connect_fail=Невозможно подключиться к ADB\nadb_dialog.disconnected=ADB сервер отключен\nadb_dialog.start_okay=ADB запущен на порту: %s.\nadb_dialog.start_fail=Ошибка запуска ADB сервера на порту: %s!\nadb_dialog.forward_fail=Не удалось перейти из-за неизвестной ошибки\nadb_dialog.being_debugged_msg=Похоже, что к этому процессу уже подключен отладчик. Вы хотите продолжить?\nadb_dialog.unknown_android_ver=Ошибка парсинга версии Android. Использовать Android 8 по умолчанию?\nadb_dialog.being_debugged_title=Этот процесс уже занят\nadb_dialog.init_dbg_fail=Ошибка инициализации отладчика\nadb_dialog.msg_read_mani_fail=Ошибка парсинга AndroidManifest.xml\nadb_dialog.no_devices=Нет устройств для запуска приложения\nadb_dialog.restart_while_debugging_title=Перезапустить отладчик\nadb_dialog.restart_while_debugging_msg=Запущен процесс отдадки приложения. Вы действительно хотите перезапустить сессию?\nadb_dialog.starting_debugger=Запуск отладки...\n\naction.variant=%s (вариант)\naction_category.menu_toolbar=Панель инструментов\n#action_category.hex_viewer=View / Hex Viewer\naction_category.code_area=Редактор кода\naction_category.plugin_script=Скрипты и плагины\n\n#hex_viewer.show_inspector=Show Inspector\n#hex_viewer.change_encoding=Change Encoding\n#hex_viewer.goto_address=Go To Address\n#hex_viewer.enter_address=Enter address range:\n#hex_viewer.find=Find\n\n#graph_viewer.long_names=Show full names\n#graph_viewer.overrides=Show overrides\n#graph_viewer.callee_depth=Down depth\n#graph_viewer.caller_depth=Up depth\n#graph_viewer.default_error=Failed to view graph\n#graph_viewer.file_not_found_error=Failed to load graph file\n#graph_viewer.image_too_large=Failed to render graph: graph too large\n#graph_viewer.image_too_small=Failed to render graph: graph too small\n#graph_viewer.file_failure=Error in File Operation\n#graph_viewer.save_graph=Save graph\n\n#graph_viewer.default_title=Graph Viewer\n#graph_viewer.method_graph.title=Methods Graph\n#graph_viewer.call_graph.title=Call Graph\n#graph_viewer.inheritance_graph.title=Inheritance Graph\n#graph_viewer.cfg.title=Control Flow Graph\n"
  },
  {
    "path": "jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties",
    "content": "language.name=中文（简体）\n\nmenu.file=文件\nmenu.view=视图\nmenu.recent_projects=最近的项目\nmenu.no_recent_projects=没有最近的项目\nmenu.preferences=首选项\nmenu.sync=与编辑器同步\nmenu.flatten=展开显示代码包\nmenu.enable_preview_tab=启用预览标签页\nmenu.heapUsageBar=显示内存使用栏\nmenu.alwaysSelectOpened=始终选中打开的文件/类\nmenu.dock_log=停靠日志查看器\nmenu.dock_quick_tabs=停靠快速标签\nmenu.navigation=导航\nmenu.text_search=文本搜索\nmenu.class_search=类名搜索\nmenu.comment_search=注释搜索\nmenu.go_to_main_activity=前往 主Activity\nmenu.go_to_application=前往 Application\nmenu.go_to_android_manifest=前往 AndroidManifest\nmenu.tools=工具\nmenu.plugins=插件\nmenu.decompile_all=反编译所有类\nmenu.reset_cache=重置代码缓存\nmenu.deobfuscation=反混淆\nmenu.log=日志查看器\nmenu.create_desktop_entry=创建桌面入口\nmenu.help=帮助\nmenu.about=关于\nmenu.quark=Quark 引擎\nmenu.update_label=发现新版本 %s！\nmenu.hex_viewer=Hex 查看器\n\nfile.open_action=打开文件…\nfile.add_files_action=添加文件\nfile.open_title=打开文件\nfile.open_project=打开项目\nfile.new_project=新建项目\nfile.save_project=保存项目\nfile.save_project_as=另存项目为…\nfile.reload=重新加载文件\nfile.live_reload=实时重加载\nfile.live_reload_desc=文件变动时自动重载\nfile.open_mappings=打开映射…\nfile.save_mappings=保存映射\nfile.save_mappings_as=映射另存为…\nfile.close_mappings=关闭映射\nfile.save_all=保存伪代码\nfile.save=保存\nfile.export=导出项目\nfile.save_all_msg=请选择保存反编译资源的目录\nfile.exit=退出\nfile.export_node=导出文件\n\nstart_page.title=开始页面\nstart_page.start=开始\nstart_page.recent=最近项目\nstart_page.list.delete_recent_project=删除项目\nstart_page.list.delete_recent_project.tooltip=从最近项目列表中删除项目\n\ntree.inputs_title=输入\ntree.input_files=文件\ntree.input_scripts=脚本\ntree.sources_title=源代码\ntree.resources_title=资源文件\ntree.loading=加载中…\ntree.pinned_tabs=固定标签\ntree.open_tabs=打开标签\ntree.bookmarked_tabs=收藏标签\n\nprogress.load=正在加载\nprogress.save_mappings=导出映射\nprogress.decompile=反编译中\nprogress.canceling=正在取消\n\nerror_dialog.title=错误\nerror_dialog.not_found=未找到 %s\nerror_dialog.not_found_file=未找到指定路径的文件：\\n%s\nerror_dialog.path_is_directory=指定路径是目录：\\n%s\nerror_dialog.cannot_read=无法读取文件：\\n%s\nerror_dialog.open_failed=文件打开失败：\\n%s\nerror_dialog.invalid_path_format=文件路径无效：\\n%s\nerror_dialog.desktop_unsupported=桌面在此环境中不受支持。\n\nsearch.previous=上一个\nsearch.next=下一个\nsearch.mark_all=标记全部\nsearch.regex=正则表达式\nsearch.match_case=区分大小写\nsearch.whole_word=全词匹配\nsearch.find=查找\nsearch.results=%s %d 个结果\nsearch.match_of=匹配 %d / %d\nsearch.match_found=找到匹配项\nsearch.match_not_found=未找到匹配项\nsearch.single_match=找到单个匹配项\nsearch.find_type_text=通过文本搜寻\nsearch.find_type_hex=通过HEX搜寻\n\ntabs.copy_class_name=复制名称\ntabs.close=关闭\ntabs.closeOthers=关闭其他\ntabs.unpin=取消固定\ntabs.unpin_all=取消所有固定\ntabs.bookmark=收藏\ntabs.unbookmark=取消收藏\ntabs.unbookmark_all=取消所有收藏\ntabs.pin=固定\ntabs.closeAll=关闭全部\ntabs.closeAllRight=关闭右边的所有\ntabs.closeAllLeft=关闭左边的所有\ntabs.code=代码\ntabs.smali=Smali\ntabs.smali_bytecode=Smali+Bytecode\n\nnav.back=后退\nnav.forward=前进\n\nmessage.taskTimeout=任务超过了 %d 毫秒时间限制。\nmessage.userCancelTask=任务被用户取消。\nmessage.memoryLow=Jadx内存不足，请增加最大堆空间后重新启动。\nmessage.taskError=任务失败，出现错误（详情请查看日志）。\nmessage.errorTitle=错误\nmessage.load_errors=加载失败。\\n错误数：%d\\n点击确定查看日志\nmessage.no_classes=无类被加载，没什么可反编译！\nmessage.enter_valid_path=输入有效的保存路径！\n\nmessage.saveIncomplete=<html>保存未完成。<br> %s<br> %d 类或资源未被保存！</html>\nmessage.indexIncomplete=<html>已跳过某些类索引。<br> %s<br> %d 类未被索引，不会出现在搜索结果中！</html>\nmessage.indexingClassesSkipped=<html>Jadx 内存不足，因此有 %d 个类未编入索引。<br>如果要将所有类编入索引，请增大最大堆空间后重启 Jadx。</html>\nmessage.enter_new_name=输入新名称\nmessage.could_not_rename=无法重命名文件\nmessage.confirm_remove_script=您真的要删除脚本吗？\nmessage.desktop_entry_creation_error=创建桌面入口失败（请检查日志以了解详细信息）。\nmessage.desktop_entry_creation_success=创建桌面入口成功！\nmessage.success_title=成功\nmessage.unable_preview_font=无法预览字体\n\nheapUsage.text=JADX 内存使用率：%.2f GB / %.2f GB (%.2f GB 顶点)\n\ncommon_dialog.ok=确定\ncommon_dialog.cancel=取消\ncommon_dialog.add=添加\ncommon_dialog.update=更新\ncommon_dialog.remove=移除\ncommon_dialog.reset=重置\n\nfile_dialog.supported_files=支持的文件\nfile_dialog.load_dir_title=加载目录\nfile_dialog.load_dir_confirm=从目录中加载所有文件？\n\nsearch_dialog.open=转到\nsearch_dialog.cancel=取消\nsearch_dialog.open_by_name=搜索文本：\nsearch_dialog.search_button=搜索\nsearch_dialog.search_history=搜索历史\nsearch_dialog.auto_search=自动搜索\nsearch_dialog.search_in=在以下位置搜索：\nsearch_dialog.class=类名\nsearch_dialog.method=方法名\nsearch_dialog.field=字段名\nsearch_dialog.code=代码\nsearch_dialog.ignorecase=忽略大小写\nsearch_dialog.load_more=加载更多\nsearch_dialog.load_all=加载所有\nsearch_dialog.stop=停止\nsearch_dialog.results_incomplete=已找到 %d+\nsearch_dialog.results_complete=全部找到 %d\nsearch_dialog.resources_load_errors=加载错误：%d\nsearch_dialog.resources_skip_by_size=按大小跳过：%d\nsearch_dialog.resources_check_logs=（点击查看日志）\nsearch_dialog.col_node=节点\nsearch_dialog.col_code=代码\nsearch_dialog.sort_results=结果排序\nsearch_dialog.regex=正则表达式\nsearch_dialog.active_tab=只在当前页搜索\nsearch_dialog.comments=注释\nsearch_dialog.resource=资源\nsearch_dialog.keep_open=保持窗口\nsearch_dialog.tip_searching=搜索中…\nsearch_dialog.limit_package=限制package：\nsearch_dialog.res_text=文本\nsearch_dialog.res_binary=字节码\nsearch_dialog.package_not_found=没有找到匹配的package\nsearch_dialog.copy=复制全部\n\nusage_dialog.title=查找\nusage_dialog.label=查找用例：\n\nusage_dialog_plus.title=树状用例查找\nusage_dialog_plus.jump_to=跳转到当前位置\nusage_dialog_plus.copy_path=复制用例路径\nusage_dialog_plus.search_complete=搜索完成\nusage_dialog_plus.code_view=代码预览\nusage_dialog_plus.select_node=选择节点\nusage_dialog_plus.code_for=代码信息 %s\nusage_dialog_plus.expand_usages=展开数据\n\ncomment_dialog.title.add=添加代码注释\ncomment_dialog.title.update=更新代码注释\ncomment_dialog.label=注释：\ncomment_dialog.style=格式：\ncomment_dialog.usage=使用 Shift + Enter 换行\n\nrename_dialog.class_help=输入包名全路径，将类移动到另一个包。以‘.’开始则移到默认（空）包下。\n\nexport_dialog.title=导出为源代码\nexport_dialog.save_path=保存路径：\nexport_dialog.browse=浏览\nexport_dialog.export_options=导出选项\nexport_dialog.export_gradle=导出为 Gradle 项目\nexport_dialog.export_gradle_type=Gradle 模板：\n\nlog_viewer.title=日志查看器\nlog_viewer.log_level=日志级别：\nlog_viewer.mode=模式：\nlog_viewer.modes=所有|所有脚本|当前脚本\nlog_viewer.hide=隐藏\nlog_viewer.dock=停靠\nlog_viewer.undock=取消停靠\nlog_viewer.clear=清除\n\nabout_dialog.title=关于 JADX\n\npreferences.title=首选项\npreferences.deobfuscation=反混淆\npreferences.appearance=界面\npreferences.shortcuts=快捷键\npreferences.select_shortcuts=选择特定的快捷键组合\npreferences.decompile=反编译\npreferences.plugins=插件\npreferences.project=项目\npreferences.other=其他\npreferences.language=语言\npreferences.lineNumbersMode=编辑器行号模式\npreferences.jumpOnDoubleClick=启用双击跳转\npreferences.useAlternativeFileDialog=使用选择文件对话框\npreferences.check_for_updates=启动时检查更新\npreferences.useDx=使用 dx/d8 来转换java字节码\npreferences.decompilationMode=反编译模式\npreferences.codeCacheMode=代码缓存模式\npreferences.codeCacheMode.memory=内存\npreferences.codeCacheMode.memory.desc=全部保存在内存中：搜索快，重新打开慢，内存占用高\npreferences.codeCacheMode.diskWithCache=磁盘加缓存\npreferences.codeCacheMode.diskWithCache.desc=代码保存在磁盘中并带有内存缓存：搜索速度中等，重新打开快，内存占用中等\npreferences.codeCacheMode.disk=磁盘\npreferences.codeCacheMode.disk.desc=全部保存在磁盘中：搜索慢，重新打开快，内存占用低\npreferences.usageCacheMode=数据缓存模式\npreferences.showInconsistentCode=显示不一致的代码\npreferences.escapeUnicode=Unicode 字符转义\npreferences.replaceConsts=替换常量\npreferences.respectBytecodeAccessModifiers=遵守字节码访问修饰符\npreferences.useImports=使用 import 语句\npreferences.useDebugInfo=启用调试信息\npreferences.inlineAnonymous=内联匿名类\npreferences.inlineMethods=内联方法\npreferences.inlineKotlinLambdas=允许内联Kotlin Lambda\npreferences.moveInnerClasses=将内部类移到父类\npreferences.extractFinally=提取finally块\npreferences.restoreSwitchOverString=恢复switch字符串\npreferences.fsCaseSensitive=文件系统区分大小写\npreferences.skipResourcesDecode=不反编译资源文件\npreferences.skipSourcesDecode=不反编译源代码\npreferences.useKotlinMethodsForVarNames=使用Kotlin方法来重命名变量\npreferences.commentsLevel=代码注释级别\npreferences.saveOption=自动保存设置\npreferences.threads=并行线程数\npreferences.excludedPackages=排除的包\npreferences.excludedPackages.tooltip=排除于反编译或索引的以空格分隔的包名列表（节省 RAM）\npreferences.excludedPackages.button=编辑\npreferences.excludedPackages.editDialog=<html>排除于反编译或索引的以空格分隔的包名列表（节省 RAM）<br>例如<code>android.support</code></html>\npreferences.cfg=生成方法的 CFG 图（‘.dot’）\npreferences.raw_cfg=生成原始的 CFG 图\npreferences.xposed_codegen_language=Xposed代码生成语言\npreferences.update_channel=Jadx 更新通道\npreferences.disable_tooltip_on_hover=禁用悬停提示\npreferences.integerFormat=数值格式化\npreferences.typeUpdatesCountLimit=类型更新次数上限\npreferences.ui_zoom=UI 缩放系数\npreferences.apply_ui_zoom_to_fonts=将 UI 缩放应用到字体\npreferences.ui_font=UI 字体\npreferences.code_font=编辑器字体\npreferences.smali_font=等宽字体（Smali/Hex）\npreferences.laf_theme=主题\npreferences.dynamic_editor_theme=使用 UI 主题色彩\npreferences.theme=编辑器主题\npreferences.start_jobs=自动进行后台反编译\npreferences.select_font=修改\npreferences.deobfuscation_on=启用反混淆\npreferences.generated_renames_mapping_file_mode=映射文件句柄模式\npreferences.deobfuscation_min_len=最小命名长度\npreferences.deobfuscation_max_len=最大命名长度\npreferences.deobfuscation_res_name_source=更好的资源名称来源\npreferences.deobfuscation_res_use_headers=使用文件头检测资源扩展\npreferences.deobfuscation_whitelist=从反混淆中排除包和类\npreferences.deobfuscation_whitelist.editDialog=反混淆白名单\npreferences.deobfuscation_whitelist.tooltip=以‘:’分隔的包（后缀‘.*’）和类名列表，它们不会被反混淆。\npreferences.save=保存\npreferences.cancel=取消\npreferences.reset=重置\npreferences.reset_message=要恢复为默认设置吗？\npreferences.reset_title=重置设置\npreferences.copy=复制到剪切板\npreferences.copy_message=所有设置都已复制\npreferences.rename=重命名标识符\npreferences.rename_case=标识符要能够区分大小写\npreferences.rename_valid=标识符应该符合标准规范\npreferences.rename_printable=标识符必须要能正常显示\npreferences.rename_use_source_name_as_class_name_alias=使用资源名作为类的别名\npreferences.rename_source_name_repeat_limit=如果源名称少于限制数，则允许使用源名称\npreferences.search_results_per_page=每页结果数（0 - 无限制）\npreferences.res_file_ext=文件扩展名（比如 .xml|.html），* 表示所有\npreferences.res_skip_file=跳过文件大小（MB）\npreferences.tab_dnd_appearance=拖拽选项卡外观\n\npreferences.plugins.manage=管理插件\npreferences.plugins.install=安装插件\npreferences.plugins.install_btn=安装\npreferences.plugins.uninstall_btn=卸载\npreferences.plugins.disable_btn=禁用\npreferences.plugins.enable_btn=启用\npreferences.plugins.location_id_label=位置ID：\npreferences.plugins.plugin_jar=选择插件 jar\npreferences.plugins.plugin_jar_label=或\npreferences.plugins.update_all=更新所有\npreferences.plugins.details=插件详情\npreferences.plugins.task.installing=安装插件中\npreferences.plugins.task.uninstalling=卸载插件中\npreferences.plugins.task.updating=更新插件中\npreferences.plugins.task.downloading_list=正在下载插件列表\npreferences.plugins.task.status=更改插件状态\n\npreferences.cache=缓存\npreferences.cache.location=缓存位置\npreferences.cache.location_default=应用缓存的系统目录\npreferences.cache.location_local=与项目文件同级的目录\npreferences.cache.location_custom=自定义目录：\npreferences.cache.change_notice=* 对于已存在缓存，改动将在缓存删除或手动重置后生效\npreferences.cache.table.title=缓存列表\npreferences.cache.table.project=项目缓存\npreferences.cache.table.size=磁盘使用率\npreferences.cache.btn.usage=计算使用率\npreferences.cache.btn.delete_selected=删除所选\npreferences.cache.btn.delete_all=删除所有\npreferences.cache.task.usage=计算缓存大小中\npreferences.cache.task.delete=删除缓存中\n\nmsg.open_file=请打开文件\nmsg.saving_sources=正在导出源代码\nmsg.language_changed_title=语言已更改\nmsg.language_changed=新的语言将在下次应用程序启动时显示。\nmsg.warning_title=警告\nmsg.common_mouse_shortcut=这是常用按键，您确定要绑定操作吗？\nmsg.duplicate_shortcut=快捷键 %s 已被操作“%s”（类别“%s”）占用，是否继续？\nmsg.cmd_select_class_error=无法选择类\\n%s\\n该类不存在。\nmsg.cant_add_comment=无法在此添加注释\nmsg.non_displayable_chars=部分字符在当前使用的字体“%s”中无法显示，是否显示这些字符？\nmsg.non_displayable_chars.title=未显示的字符串\n\nmethods_dialog.title=选择方法\n\npopup.bytecode_col=显示Dalvik字节码\npopup.line_wrap=自动换行\npopup.undo=撤销\npopup.redo=重做\npopup.cut=剪切\npopup.copy=复制\npopup.paste=粘贴\npopup.delete=删除\npopup.select_all=全选\npopup.frida=复制为 frida 片段\npopup.xposed=复制为 xposed 片段\npopup.find_usage=查找用例\npopup.go_to_declaration=跳到声明\npopup.exclude=排除此包\npopup.exclude_packages=排除包\npopup.convert_number=将数值转换添加为注释\npopup.view_call_graph=查看调用图\npopup.view_call_graph_description=显示到此函数的调用链\npopup.view_class_graph=查看继承图\npopup.view_class_graph_description=显示该类的继承树\npopup.view_class_method_graph=查看方法图\npopup.view_class_method_graph_description=显示该类的所有方法\npopup.cfg_submenu=查看控制图\npopup.view_cfg=常规\npopup.view_cfg_description=显示该函数的常规控制流图（可在 文件->首选项->其他 中启用）\npopup.view_raw_cfg=原始\npopup.view_raw_cfg_description=显示该函数包含原始指令的控制流图（可在 文件->首选项->其他 中启用）\npopup.view_region_cfg=区域\npopup.view_region_cfg_description=显示该函数的区域化控制流图（可在 文件->首选项->其他 中启用）\npopup.add_comment=添加注释\npopup.update_comment=更新注释\npopup.search_comment=搜索注释\npopup.rename=重命名\npopup.search=搜索 “%s”\npopup.search_global=全局搜索 “%s”\npopup.remove=移除\npopup.add_files=添加文件\npopup.add_scripts=添加脚本\npopup.new_script=新建脚本\npopup.json_prettify=JSON 格式化\npopup.export=导出\npopup.usage_dialog_plus=树状用例查找\npopup.copy_as=另存为…\npopup.copy_as_hex=另存为 HEX\npopup.copy_as_string=另存为字符串\npopup.copy_offset=复制偏移\n\nscript.run=运行\nscript.save=保存\nscript.auto_complete=自动补全\nscript.check=检查\nscript.format=重新格式化\nscript.log=显示日志\n\nencoding_dialog.title=编码\nencoding_dialog.message=选择编码：\n\nexclude_dialog.title=选择要排除的包\nexclude_dialog.select_all=全选\nexclude_dialog.deselect=取消选择\nexclude_dialog.invert=反选\n\nconfirm.save_as_title=确认另存为\nconfirm.save_as_message=%s 已存在。\\n你想替换它吗？\nconfirm.not_saved_title=保存项目\nconfirm.not_saved_message=在继续之前保存当前项目？\nconfirm.remember=记住我的决定\n\ncertificate.cert_type=类型\ncertificate.serialSigVer=版本\ncertificate.serialNumber=序列号\ncertificate.cert_subject=主题\ncertificate.serialValidFrom=有效期始\ncertificate.serialValidUntil=有效期至\ncertificate.serialPubKeyType=公钥类型\ncertificate.serialPubKeyExponent=指数\ncertificate.serialPubKeyModulus=模数\ncertificate.serialPubKeyModulusSize=模数大小（位）\ncertificate.serialSigType=签名算法\ncertificate.serialSigOID=签名 OID\ncertificate.serialMD5=MD5 签名\ncertificate.serialSHA1=SHA-1 签名\ncertificate.serialSHA256=SHA-256 签名\ncertificate.serialPubKeyY=Y\n\napkSignature.signer=签名者\napkSignature.loading=加载签名中…\napkSignature.verificationSuccess=签名验证成功\napkSignature.verificationFailed=签名验证失败\napkSignature.signatureSuccess=找到有效的 APK 签名 v%d\napkSignature.signatureFailed=找到无效的 APK 签名 v%d\napkSignature.errors=错误\napkSignature.warnings=警告\napkSignature.exception=APK 验证失败\napkSignature.unprotectedEntry=未受 v1签名 保护的文件。对这些条目的未经授权的修改，只能通过 v2签名 或更高版本来检测。\n\nissues_panel.label=问题：\nissues_panel.errors=%d 错误\nissues_panel.warnings=%d 警告\nissues_panel.tooltip=点击查看日志\n\ndebugger.process_selector=选择要调试的进程\ndebugger.step_into=步入（F7）\ndebugger.step_over=步过（F8）\ndebugger.step_out=步出（Shift + F8）\ndebugger.run=运行（F9）\ndebugger.stop=停止调试并终止应用\ndebugger.pause=暂停\ndebugger.rerun=重新运行\ndebugger.cfm_dialog_title=在调试时退出\ndebugger.cfm_dialog_msg=您确定要终止调试器吗？\n\ndebugger.popup_set_value=修改值\ndebugger.popup_change_to_zero=修改为0\ndebugger.popup_change_to_one=修改为1\ndebugger.popup_copy_value=复制值\n\nlogcat.pause=暂停 Logcat\nlogcat.start=恢复 Logcat\nlogcat.clear=清空 Logcat\n\nlogcat.error_fail_start=无法启动 Logcat\nlogcat.process=进程\nlogcat.level=级别\nlogcat.default=默认\nlogcat.verbose=Verbose\nlogcat.debug=Debug\nlogcat.info=Info\nlogcat.warn=Warn\nlogcat.error=Error\nlogcat.fatal=Fatal\nlogcat.silent=Silent\nlogcat.logcat=Logcat\nlogcat.select_attached=选择附加\nlogcat.select_all=选择所有\nlogcat.unselect_all=反选所有\n\nset_value_dialog.label_value=值\nset_value_dialog.btn_set=修改\nset_value_dialog.title=设置新值\nset_value_dialog.neg_msg=修改数值失败。\nset_value_dialog.sel_type=选择要修改值的类型。\n\nadb_dialog.addr=ADB IP\nadb_dialog.port=ADB 端口\nadb_dialog.path=ADB 路径\nadb_dialog.launch_app=运行APP\nadb_dialog.start_server=启动ADB服务\nadb_dialog.refresh=刷新\nadb_dialog.tip_devices=%d 台设备\nadb_dialog.device_node=设备\nadb_dialog.missing_path=必须提供ADB路径才能启动ADB服务。\nadb_dialog.waiting=正在等待连接到ADB服务…\nadb_dialog.connecting=正在连接ADB服务，地址：%s:%s…\nadb_dialog.connect_okay=已连接ADB服务，地址：%s:%s\nadb_dialog.connect_fail=连接ADB服务失败。\nadb_dialog.disconnected=ADB服务已断开连接。\nadb_dialog.start_okay=ADB服务启动成功 端口：%s。\nadb_dialog.start_fail=ADB服务启动失败 端口：%s！\nadb_dialog.forward_fail=由于某些原因无法转发。\nadb_dialog.being_debugged_msg=这个进程似乎正在调试，是否继续？\nadb_dialog.unknown_android_ver=获取安卓版本失败，是否默认使用Android 8？\nadb_dialog.being_debugged_title=它正在由其他人调试。\nadb_dialog.init_dbg_fail=初始化调试器失败。\nadb_dialog.msg_read_mani_fail=解码AndroidManifest.xml失败\nadb_dialog.no_devices=找不到任何设备用来启动APP。\nadb_dialog.restart_while_debugging_title=调试时重新启动\nadb_dialog.restart_while_debugging_msg=你正在调试一个APP，确定要重新启动一个会话吗？\nadb_dialog.starting_debugger=正在启动调试器…\n\naction.variant=%s（次要）\naction_category.menu_toolbar=菜单/工具栏\naction_category.hex_viewer=查看器/HEX查看器\naction_category.code_area=代码区\naction_category.plugin_script=插件脚本\n\nhex_viewer.show_inspector=显示检查器\nhex_viewer.change_encoding=更改编码\nhex_viewer.goto_address=跳转到地址\nhex_viewer.enter_address=输入地址范围：\nhex_viewer.find=查找\n\ngraph_viewer.long_names=显示完整名称\ngraph_viewer.overrides=显示重写关系\ngraph_viewer.callee_depth=向下深度\ngraph_viewer.caller_depth=向上深度\ngraph_viewer.default_error=图形显示失败\ngraph_viewer.file_not_found_error=图形文件加载失败\ngraph_viewer.image_too_large=图形渲染失败：图过大\ngraph_viewer.image_too_small=图形渲染失败：图过小\ngraph_viewer.file_failure=文件操作错误\ngraph_viewer.save_graph=保存图形\n\ngraph_viewer.default_title=图形查看器\ngraph_viewer.method_graph.title=方法图\ngraph_viewer.call_graph.title=调用图\ngraph_viewer.inheritance_graph.title=继承图\ngraph_viewer.cfg.title=控制图\n"
  },
  {
    "path": "jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties",
    "content": "language.name=中文 (繁體)\n\nmenu.file=檔案\nmenu.view=檢視\nmenu.recent_projects=近期專案\nmenu.no_recent_projects=無近期專案\nmenu.preferences=選項\nmenu.sync=與編輯器同步\nmenu.flatten=展開顯示套件\n#menu.enable_preview_tab=Enable Preview Tab\nmenu.heapUsageBar=顯示記憶體使用率條\nmenu.alwaysSelectOpened=總是選擇已開啟的檔案/類別\nmenu.dock_log=固定記錄檔檢視器\nmenu.dock_quick_tabs=固定快速分頁\nmenu.navigation=瀏覽\nmenu.text_search=文字搜尋\nmenu.class_search=類別搜尋\nmenu.comment_search=註解搜尋\nmenu.go_to_main_activity=前往主 Activity\nmenu.go_to_application=前往應用程式\nmenu.go_to_android_manifest=前往 AndroidManifest.xml\nmenu.tools=工具\nmenu.plugins=外掛程式\nmenu.decompile_all=反編譯所有類別\nmenu.reset_cache=重設程式碼快取\nmenu.deobfuscation=去模糊化\nmenu.log=記錄檔檢視器\nmenu.create_desktop_entry=建立桌面項目\nmenu.help=幫助\nmenu.about=關於\nmenu.quark= Quark 引擎\nmenu.update_label=新版本 %s 可供下載！\n#menu.hex_viewer=Hex Viewer\n\nfile.open_action=開啟檔案...\nfile.add_files_action=新增檔案\nfile.open_title=開啟檔案\nfile.open_project=開啟專案\nfile.new_project=新建專案\nfile.save_project=儲存專案\nfile.save_project_as=另存專案...\nfile.reload=重新載入檔案\nfile.live_reload=實時重新載入\nfile.live_reload_desc=更動後自動重新載入檔案\nfile.open_mappings=開啟對映檔\nfile.save_mappings=儲存對映檔\nfile.save_mappings_as=儲存對映檔為...\nfile.close_mappings=關閉對映檔\nfile.save_all=全部儲存\nfile.save=儲存\nfile.export=匯出專案\nfile.save_all_msg=選擇儲存反編譯原始碼的路徑\nfile.exit=離開\nfile.export_node=匯出檔案\n\nstart_page.title=開始頁面\nstart_page.start=開始\nstart_page.recent=近期專案\nstart_page.list.delete_recent_project=刪除項目\nstart_page.list.delete_recent_project.tooltip=從最近列表中刪除\n\ntree.inputs_title=輸入\ntree.input_files=檔案\ntree.input_scripts=腳本\ntree.sources_title=原始碼\ntree.resources_title=資源\ntree.loading=載入中...\ntree.pinned_tabs=已釘選的分頁\ntree.open_tabs=已開啟的分頁\ntree.bookmarked_tabs=已加入書籤的分頁\n\nprogress.load=載入中\nprogress.save_mappings=正在匯出對應\nprogress.decompile=正在反編譯\nprogress.canceling=正在取消\n\nerror_dialog.title=錯誤\nerror_dialog.not_found=找不到 %s\nerror_dialog.not_found_file=找不到檔案：\\n%s\nerror_dialog.path_is_directory=指定路徑是目錄：\\n%s\nerror_dialog.cannot_read=無法讀取檔案：\\n%s\nerror_dialog.open_failed=開啟檔案失敗：\\n%s\nerror_dialog.invalid_path_format=檔案路徑格式無效：\\n%s\nerror_dialog.desktop_unsupported=此環境不支援桌面操作。\n\nsearch.previous=上一個\nsearch.next=下一個\nsearch.mark_all=全部標記\nsearch.regex=Regex\nsearch.match_case=大小寫須相符\nsearch.whole_word=全字拼寫須相符\nsearch.find=尋找\nsearch.results=%s%d 個結果\nsearch.match_of=第 %d 個，共 %d 個\nsearch.match_found=找到相符結果\nsearch.match_not_found=為找到相符結果\nsearch.single_match=找到一個相符結果\n#search.find_type_text=Find by text\n#search.find_type_hex=Find by hex\n\ntabs.copy_class_name=複製名稱\ntabs.close=關閉\ntabs.closeOthers=關閉其他\ntabs.unpin=取消釘選\ntabs.unpin_all=全部取消釘選\ntabs.bookmark=加上書籤\ntabs.unbookmark=移除書籤\ntabs.unbookmark_all=全部移除書籤\ntabs.pin=釘選\ntabs.closeAll=關閉全部\ntabs.closeAllRight=關閉右邊的所有\n#tabs.closeAllLeft=Close All Left\ntabs.code=程式碼\ntabs.smali=Smali\ntabs.smali_bytecode=Smali+Bytecode\n\nnav.back=返回\nnav.forward=向前\n\nmessage.taskTimeout=工作超過時間限制 (%d 毫秒)。\nmessage.userCancelTask=工作被使用者取消了。\nmessage.memoryLow=Jadx 的記憶體不足。請增加最大堆疊大小並重新啟動。\nmessage.taskError=工作發生錯誤 (查看記錄檔以了解詳情)。\nmessage.errorTitle=錯誤\nmessage.load_errors=載入失敗。\\n錯誤數：%d\\n點擊 OK 以開啟記錄檔檢視器\nmessage.no_classes=未載入任何類別，沒有東西可以反編譯！\nmessage.enter_valid_path=輸入有效的路徑以儲存！\n\nmessage.saveIncomplete=<html>儲存未完成。<br> %s<br> %d 個類別或資源尚未儲存！</html>\nmessage.indexIncomplete=<html>某些類別的索引被略過。<br> %s<br> %d 個類別未被索引，故不會出現在搜尋結果中。</html>\nmessage.indexingClassesSkipped=<html>Jadx 的記憶體不足。故 %d 個類別未被索引。<br>如果您想要索引所有類別，請增加最大堆疊大小並重新啟動。</html>\nmessage.enter_new_name=輸入新名稱\nmessage.could_not_rename=無法重新命名檔案\nmessage.confirm_remove_script=您確定要移除指令碼嗎？\nmessage.desktop_entry_creation_error=建立桌面項目失敗（詳情請查閱日誌）。\nmessage.desktop_entry_creation_success=成功建立桌面項目！\nmessage.success_title=成功\n#message.unable_preview_font=Unable preview font\n\nheapUsage.text=JADX 記憶體使用率：%.2f GB / %.2f GB (%.2f GB 潼)\n\ncommon_dialog.ok=Ok\ncommon_dialog.cancel=取消\ncommon_dialog.add=新增\ncommon_dialog.update=更新\ncommon_dialog.remove=移除\ncommon_dialog.reset=重設\n\nfile_dialog.supported_files=支援的檔案\nfile_dialog.load_dir_title=載入目錄\nfile_dialog.load_dir_confirm=要載入目錄中的所有檔案嗎？\n\nsearch_dialog.open=開啟\nsearch_dialog.cancel=取消\nsearch_dialog.open_by_name=搜尋文字：\nsearch_dialog.search_button=搜尋\nsearch_dialog.search_history=搜尋記錄\nsearch_dialog.auto_search=自動搜尋\nsearch_dialog.search_in=搜尋定義：\nsearch_dialog.class=類別\nsearch_dialog.method=方法\nsearch_dialog.field=欄位\nsearch_dialog.code=程式碼\nsearch_dialog.ignorecase=不區分大小寫\nsearch_dialog.load_more=載入更多\nsearch_dialog.load_all=載入全部\nsearch_dialog.stop=停止\nsearch_dialog.results_incomplete=找到 %d+\nsearch_dialog.results_complete=找到 %d (完整)\nsearch_dialog.resources_load_errors=載入錯誤：%d\nsearch_dialog.resources_skip_by_size=因大小跳過：%d\nsearch_dialog.resources_check_logs=(點擊以查看記錄檔)\nsearch_dialog.col_node=無\nsearch_dialog.col_code=程式碼\nsearch_dialog.sort_results=排序結果\nsearch_dialog.regex=Regex\nsearch_dialog.active_tab=僅使用中分頁\nsearch_dialog.comments=註解\nsearch_dialog.resource=資源\nsearch_dialog.keep_open=保持開啟\nsearch_dialog.tip_searching=正在搜尋\nsearch_dialog.limit_package=限制至套件：\n#search_dialog.res_text=Text\n#search_dialog.res_binary=Binary\nsearch_dialog.package_not_found=找不到符合的套件\nsearch_dialog.copy=複製全部\n\nusage_dialog.title=使用情況搜尋\nusage_dialog.label=使用情況：\n\nusage_dialog_plus.title=使用情況搜尋\nusage_dialog_plus.jump_to=前往目前位置\nusage_dialog_plus.copy_path=複製使用情況路徑\nusage_dialog_plus.search_complete=搜尋完成\nusage_dialog_plus.code_view=程式碼預覽\nusage_dialog_plus.select_node=選擇節點\nusage_dialog_plus.code_for=程式碼資訊 %s\nusage_dialog_plus.expand_usages=展開資料\n\ncomment_dialog.title.add=新增程式碼註解\ncomment_dialog.title.update=更新程式碼註解\ncomment_dialog.label=註解：\ncomment_dialog.style=風格：\ncomment_dialog.usage=按下 Shift + Enter 來換行\n\nrename_dialog.class_help=輸入全名以將類別移動至其他套件。以 '.' 開頭將移動至預設（空）套件。\n\nexport_dialog.title=匯出至原始碼\nexport_dialog.save_path=儲存路徑：\nexport_dialog.browse=瀏覽\nexport_dialog.export_options=匯出選項\nexport_dialog.export_gradle=匯出成 Gradle 專案\nexport_dialog.export_gradle_type=Gradle 模板:\n\nlog_viewer.title=記錄檔檢視器\nlog_viewer.log_level=記錄層級：\nlog_viewer.mode=模式：\nlog_viewer.modes=所有|所有腳本|目前腳本\nlog_viewer.hide=隱藏\nlog_viewer.dock=固定\nlog_viewer.undock=解除固定\nlog_viewer.clear=清除\n\nabout_dialog.title=關於 JADX\n\npreferences.title=選項\npreferences.deobfuscation=去模糊化\npreferences.appearance=外觀\npreferences.shortcuts=快捷鍵\npreferences.select_shortcuts=選擇特定的快捷鍵群組\npreferences.decompile=反編譯\npreferences.plugins=外掛程式\npreferences.project=專案\npreferences.other=其他\npreferences.language=語言\npreferences.lineNumbersMode=編輯器行號模式\npreferences.jumpOnDoubleClick=啟用點擊兩下時跳躍\npreferences.useAlternativeFileDialog=使用替代檔案對話框\npreferences.check_for_updates=啟動時檢查更新\npreferences.useDx=使用 dx/d8 來轉換 Java 位元組碼\npreferences.decompilationMode=反編譯模式\npreferences.codeCacheMode=程式碼快取模式\n#preferences.codeCacheMode.memory=Memory\n#preferences.codeCacheMode.memory.desc=Everything in memory: fast search, slow reopen, high memory usage\n#preferences.codeCacheMode.diskWithCache=Disk with cache\n#preferences.codeCacheMode.diskWithCache.desc=Code saved on disk with in memory cache: medium search, fast reopen, medium memory usage\n#preferences.codeCacheMode.disk=Disk\n#preferences.codeCacheMode.disk.desc=Everything on disk: slow search, fast reopen, low memory usage\npreferences.usageCacheMode=使用資料快取模式\npreferences.showInconsistentCode=顯示不一致的程式碼\npreferences.escapeUnicode=Unicode 逸出\npreferences.replaceConsts=替換常數\npreferences.respectBytecodeAccessModifiers=遵守位元組碼存取修飾詞\npreferences.useImports=使用 import 陳述式\npreferences.useDebugInfo=使用除錯資訊\npreferences.inlineAnonymous=內嵌匿名類別\npreferences.inlineMethods=內嵌方法\npreferences.inlineKotlinLambdas=允許內嵌 Kotlin 匿名函式\npreferences.moveInnerClasses=將內部類別移動至父類別\npreferences.extractFinally=擷取 finally 區塊\npreferences.restoreSwitchOverString=將字串復原成 switch\npreferences.fsCaseSensitive=檔案系統區分大小寫\npreferences.skipResourcesDecode=不要為資源解碼\npreferences.skipSourcesDecode=不要反編譯原始碼\npreferences.useKotlinMethodsForVarNames=使用 Kotlin 方法來為變數重新命名\npreferences.commentsLevel=程式碼註解層級\npreferences.saveOption=自動儲存設定\npreferences.threads=執行緒數\npreferences.excludedPackages=被排除的套件\npreferences.excludedPackages.tooltip=排除於索引或反編譯外的套件列表 (以空格分隔) (節省 RAM)\npreferences.excludedPackages.button=編輯\npreferences.excludedPackages.editDialog=<html>排除於索引或反編譯外的套件列表 (以空格分隔) (節省 RAM) <br>例如 <code>android.support</code></html>\npreferences.cfg=產生方法 CFG 圖表 ('dot' 格式)\npreferences.raw_cfg=產生 RAW CFG 圖表\npreferences.xposed_codegen_language=Xposed 程式碼產生語言\npreferences.update_channel=Jadx 更新頻道\n#preferences.disable_tooltip_on_hover=Disable tooltip on hover\npreferences.integerFormat=整數模式\n#preferences.typeUpdatesCountLimit=Update type limit count\n#preferences.ui_zoom=UI Zoom factor\n#preferences.apply_ui_zoom_to_fonts=Apply UI zoom to fonts\n#preferences.ui_font=UI font\npreferences.code_font=編輯器字型\npreferences.smali_font=等寬字型 (Smali/Hex)\npreferences.laf_theme=主題\n#preferences.dynamic_editor_theme=Use UI theme colors\npreferences.theme=編輯器主題\npreferences.start_jobs=自動開始背景反編譯\npreferences.select_font=變更\npreferences.deobfuscation_on=啟用去模糊化\npreferences.generated_renames_mapping_file_mode=Map 檔案處理模式\npreferences.deobfuscation_min_len=最小名稱長度\npreferences.deobfuscation_max_len=最大名稱長度\npreferences.deobfuscation_res_name_source=較佳的資源名稱來源\n#preferences.deobfuscation_res_use_headers=Use headers for detect resource extensions\npreferences.deobfuscation_whitelist=去模糊化時排除套件和類別\npreferences.deobfuscation_whitelist.editDialog=去模糊化白名單\npreferences.deobfuscation_whitelist.tooltip=將不會被去模糊化的套件（後綴 '.*'）和類別名稱清單，以 ':' 分隔\npreferences.save=儲存\npreferences.cancel=取消\npreferences.reset=重設\npreferences.reset_message=要將設定重設為預設值嗎？\npreferences.reset_title=重設設定\npreferences.copy=複製至剪貼簿\npreferences.copy_message=已將所有設定值複製至剪貼簿\npreferences.rename=重新命名識別碼\npreferences.rename_case=以修復區分大小寫問題\npreferences.rename_valid=以使其有效\npreferences.rename_printable=以使其可列印\npreferences.rename_use_source_name_as_class_name_alias=將原始檔案名稱作為類別別名\npreferences.rename_source_name_repeat_limit=若出現次數少於限制數便允許使用來源名稱\npreferences.search_results_per_page=每頁的搜尋結果數 (0 - 無限制)\npreferences.res_file_ext=副檔名 (e.g. .xml|.html), * 表示全部\npreferences.res_skip_file=略過大於此值的檔案 (MB)\npreferences.tab_dnd_appearance=拖動分頁時外觀\n\npreferences.plugins.manage=管理外掛程式\npreferences.plugins.install=安裝外掛程式\npreferences.plugins.install_btn=安裝\npreferences.plugins.uninstall_btn=解除安裝\npreferences.plugins.disable_btn=停用\npreferences.plugins.enable_btn=啟用\npreferences.plugins.location_id_label=位置 id：\npreferences.plugins.plugin_jar=選擇外掛程式 jar\npreferences.plugins.plugin_jar_label=或\npreferences.plugins.update_all=全部更新\npreferences.plugins.details=外掛程式詳細資訊\npreferences.plugins.task.installing=正在安裝外掛程式\npreferences.plugins.task.uninstalling=正在解除安裝外掛程式\npreferences.plugins.task.updating=正在更新外掛程式\npreferences.plugins.task.downloading_list=正在下載外掛程式列表\npreferences.plugins.task.status=正在變更外掛程式狀態\n\npreferences.cache=快取\npreferences.cache.location=快取位置\npreferences.cache.location_default=應用程式快取系統目錄\npreferences.cache.location_local=與專案檔案同目錄\npreferences.cache.location_custom=自訂位置：\npreferences.cache.change_notice=* 現有的快取將在刪除或手動重設後套用變更\npreferences.cache.table.title=快取列表\npreferences.cache.table.project=專案快取\npreferences.cache.table.size=硬碟使用量\npreferences.cache.btn.usage=計算使用量\npreferences.cache.btn.delete_selected=刪除所選\npreferences.cache.btn.delete_all=全部刪除\npreferences.cache.task.usage=正在計算快取大小\npreferences.cache.task.delete=正在刪除快取\n\nmsg.open_file=請開啟檔案\nmsg.saving_sources=正在儲存原始碼\nmsg.language_changed_title=已更改語言\nmsg.language_changed=新語言將於下次應用程式啟動時套用。\nmsg.warning_title=警告\nmsg.common_mouse_shortcut=這是常用按鍵，您確定要綁定操作嗎？\nmsg.duplicate_shortcut=快捷鍵 %s 已設為操作 \"%s\"，位於群組 \"%s\"。確定要繼續嗎？\nmsg.cmd_select_class_error=無法選擇類別\\n%s\\n類別不存在。\nmsg.cant_add_comment=無法在此新增註解\n#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font \"%s\". Show chars?\n#msg.non_displayable_chars.title=Undisplayed Strings\n\nmethods_dialog.title=選擇方法\n\npopup.bytecode_col=顯示 Dalvik 位元組碼\npopup.line_wrap=自動換行\npopup.undo=復原\npopup.redo=重做\npopup.cut=剪下\npopup.copy=複製\npopup.paste=貼上\npopup.delete=刪除\npopup.select_all=全選\npopup.frida=複製為 frida 片段\npopup.xposed=複製為 xposed 片段\npopup.find_usage=尋找使用情況\npopup.go_to_declaration=前往宣告\npopup.exclude=排除\npopup.exclude_packages=排除套件\n#popup.convert_number=Add conversion as comment\n#popup.view_call_graph=View call graph\n#popup.view_call_graph_description=Show call chains to this function\n#popup.view_class_graph=View inheritance graph\n#popup.view_class_graph_description=Show inheritance tree for this class\n#popup.view_class_method_graph=View methods graph\n#popup.view_class_method_graph_description=Show all methods for this class\n#popup.cfg_submenu=View control flow graph\n#popup.view_cfg=Regular\n#popup.view_cfg_description=Show regular control flow graph for this function (enable in File->Preferences->Other)\n#popup.view_raw_cfg=Raw\n#popup.view_raw_cfg_description=Show control flow graph with raw instructions for this function (enable in File->Preferences->Other)\n#popup.view_region_cfg=Region\n#popup.view_region_cfg_description=Show regioned control flow graph for this function (enable in File->Preferences->Other)\npopup.add_comment=註解\npopup.update_comment=更新註解\npopup.search_comment=搜尋註解\npopup.rename=重新命名\npopup.search=搜尋 \"%s\"\npopup.search_global=全域搜尋 \"%s\"\npopup.remove=移除\npopup.add_files=新增檔案\npopup.add_scripts=加入腳本\npopup.new_script=新增腳本\npopup.json_prettify=JSON 格式化\npopup.export=匯出\npopup.usage_dialog_plus=查找引用\npopup.copy_as=另存為...\npopup.copy_as_hex=另存爲 HEX\npopup.copy_as_string=另存爲 字符串\npopup.copy_offset=複製 偏移量\n\nscript.run=執行\nscript.save=儲存\nscript.auto_complete=自動完成\nscript.check=檢查\nscript.format=重新格式化\nscript.log=顯示記錄檔\n\nencoding_dialog.title=編碼\nencoding_dialog.message=選擇編碼:\n\nexclude_dialog.title=套件選擇\nexclude_dialog.select_all=全選\nexclude_dialog.deselect=取消選取\nexclude_dialog.invert=反轉選取\n\nconfirm.save_as_title=確認另存為\nconfirm.save_as_message=%s 已存在。\\n您要覆寫它嗎？\nconfirm.not_saved_title=儲存專案\nconfirm.not_saved_message=在繼續進行前要先儲存目前專案嗎？\nconfirm.remember=記住我的選擇\n\ncertificate.cert_type=類型\ncertificate.serialSigVer=版本\ncertificate.serialNumber=序號\ncertificate.cert_subject=主體\ncertificate.serialValidFrom=有效期自\ncertificate.serialValidUntil=有效期限\ncertificate.serialPubKeyType=公鑰類型\ncertificate.serialPubKeyExponent=指數\ncertificate.serialPubKeyModulus=模數\ncertificate.serialPubKeyModulusSize=模數大小 (位元)\ncertificate.serialSigType=簽名類型\ncertificate.serialSigOID=簽名 OID\ncertificate.serialMD5=MD5 指紋\ncertificate.serialSHA1=SHA-1 指紋\ncertificate.serialSHA256=SHA-256 指紋\ncertificate.serialPubKeyY=Y\n\napkSignature.signer=簽署者\napkSignature.loading=載入簽名中...\napkSignature.verificationSuccess=簽名驗證成功\napkSignature.verificationFailed=簽名驗證失敗\napkSignature.signatureSuccess=找到可用的 APK 簽名 v%d\napkSignature.signatureFailed=找到無效的 APK 簽名 v%d\napkSignature.errors=錯誤\napkSignature.warnings=警告\napkSignature.exception=APK 驗證失敗\napkSignature.unprotectedEntry=未被 APK 簽名 v1 保護的檔案。對這些項目進行未經授權的更動僅能由 APK 簽名 v2 及以上版本偵測。\n\nissues_panel.label=問題：\nissues_panel.errors=%d 錯誤\nissues_panel.warnings=%d 警告\nissues_panel.tooltip=在記錄檔檢視器中開啟\n\ndebugger.process_selector=選擇要偵錯的處理程序\ndebugger.step_into=逐步執行 (F7)\ndebugger.step_over=不進入函式 (F8)\ndebugger.step_out=跳離函式 (Shift + F8)\ndebugger.run=執行 (F9)\ndebugger.stop=停止偵錯工具並終止應用程式\ndebugger.pause=暫停\ndebugger.rerun=重新執行\ndebugger.cfm_dialog_title=偵錯時離開\ndebugger.cfm_dialog_msg=您確定要終止偵錯工具嗎？\n\ndebugger.popup_set_value=設置數值\ndebugger.popup_change_to_zero=修改為 0\ndebugger.popup_change_to_one=修改為 1\ndebugger.popup_copy_value=複製數值\n\nlogcat.pause=暫停 Logcat\nlogcat.start=繼續 Logcat\nlogcat.clear=清除 Logcat\n\nlogcat.error_fail_start=無法啟動 Logcat\nlogcat.process=程序\nlogcat.level=等級\nlogcat.default=預設\nlogcat.verbose=詳細資訊\nlogcat.debug=偵錯\nlogcat.info=資訊\nlogcat.warn=警告\nlogcat.error=錯誤\nlogcat.fatal=嚴重錯誤\nlogcat.silent=靜音\nlogcat.logcat=Logcat\nlogcat.select_attached=選取附件\nlogcat.select_all=全選\nlogcat.unselect_all=取消全選\n\nset_value_dialog.label_value=數值\nset_value_dialog.btn_set=設置數值\nset_value_dialog.title=設置數值\nset_value_dialog.neg_msg=設置數值失敗。\nset_value_dialog.sel_type=選擇要設置數值的類型。\n\nadb_dialog.addr=ADB 位址\nadb_dialog.port=ADB 連接埠\nadb_dialog.path=ADB 路徑\nadb_dialog.launch_app=啟動應用程式\nadb_dialog.start_server=啟動 ADB 伺服器\nadb_dialog.refresh=重新整理\nadb_dialog.tip_devices=%d 裝置\nadb_dialog.device_node=裝置\nadb_dialog.missing_path=必須提供 ADB 路徑才能啟動 ADB 伺服器。\nadb_dialog.waiting=正在等待連接至 ADB 伺服器...\nadb_dialog.connecting=正在連接至 ADB 伺服器，位址：%s:%s...\nadb_dialog.connect_okay=已連接至 ADB 伺服器，位址：%s:%s\nadb_dialog.connect_fail=無法連接至 ADB 伺服器。\nadb_dialog.disconnected=已與 ADB 伺服器斷開連線。\nadb_dialog.start_okay=ADB 伺服器已於此連接埠啟動：%s。\nadb_dialog.start_fail=無法於此連接埠啟動 ADB 伺服器：%s！\nadb_dialog.forward_fail=因某些原因而無法轉發。\nadb_dialog.being_debugged_msg=此執行程序貌似已在偵錯，要繼續嗎？\nadb_dialog.unknown_android_ver=無法取得 Android 發行版本，是否要使用預設版本 Android 8？\nadb_dialog.being_debugged_title=正在由他人偵錯。\nadb_dialog.init_dbg_fail=無法初始化偵錯工具。\nadb_dialog.msg_read_mani_fail=無法解碼 AndroidManifest.xml\nadb_dialog.no_devices=無法找到任何可以啟動應用程式的裝置。\nadb_dialog.restart_while_debugging_title=偵錯時重新啟動\nadb_dialog.restart_while_debugging_msg=您正在為應用程式偵錯，您確定要重新啟動工作階段嗎？\nadb_dialog.starting_debugger=正在啟動偵錯工具...\n\naction.variant=%s (variant)\naction_category.menu_toolbar=選單 / 工具列\n#action_category.hex_viewer=View / Hex Viewer\naction_category.code_area=程式碼區域\naction_category.plugin_script=外掛程式腳本\n\n#hex_viewer.show_inspector=Show Inspector\n#hex_viewer.change_encoding=Change Encoding\n#hex_viewer.goto_address=Go To Address\n#hex_viewer.enter_address=Enter address range:\n#hex_viewer.find=Find\n\n#graph_viewer.long_names=Show full names\n#graph_viewer.overrides=Show overrides\n#graph_viewer.callee_depth=Down depth\n#graph_viewer.caller_depth=Up depth\n#graph_viewer.default_error=Failed to view graph\n#graph_viewer.file_not_found_error=Failed to load graph file\n#graph_viewer.image_too_large=Failed to render graph: graph too large\n#graph_viewer.image_too_small=Failed to render graph: graph too small\n#graph_viewer.file_failure=Error in File Operation\n#graph_viewer.save_graph=Save graph\n\n#graph_viewer.default_title=Graph Viewer\n#graph_viewer.method_graph.title=Methods Graph\n#graph_viewer.call_graph.title=Call Graph\n#graph_viewer.inheritance_graph.title=Inheritance Graph\n#graph_viewer.cfg.title=Control Flow Graph\n"
  },
  {
    "path": "jadx-gui/src/test/java/jadx/gui/TestI18n.java",
    "content": "package jadx.gui;\n\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\nimport jadx.gui.utils.LangLocale;\nimport jadx.gui.utils.NLS;\n\nimport static java.nio.file.Paths.get;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\npublic class TestI18n {\n\tprivate static final String DEFAULT_LANG_FILE = \"Messages_en_US.properties\";\n\n\tprivate static Path i18nPath;\n\tprivate static Path refPath;\n\tprivate static Path guiJavaPath;\n\n\t@BeforeAll\n\tpublic static void init() {\n\t\ti18nPath = get(\"src/main/resources/i18n\");\n\t\tassertThat(i18nPath).exists();\n\t\trefPath = i18nPath.resolve(DEFAULT_LANG_FILE);\n\t\tassertThat(refPath).exists();\n\t\tguiJavaPath = get(\"src/main/java\");\n\t\tassertThat(guiJavaPath).exists();\n\t}\n\n\t@Test\n\tpublic void verifyLocales() {\n\t\tfor (LangLocale lang : NLS.getLangLocales()) {\n\t\t\tLocale locale = lang.get();\n\t\t\tSystem.out.println(\"Language: \" + locale.getLanguage() + \" - \" + locale.getDisplayLanguage()\n\t\t\t\t\t+ \", country: \" + locale.getCountry() + \" - \" + locale.getDisplayCountry()\n\t\t\t\t\t+ \", language tag: \" + locale.toLanguageTag());\n\t\t}\n\t}\n\n\t@Test\n\tpublic void filesExactlyMatch() throws IOException {\n\t\tList<String> reference = Files.readAllLines(refPath)\n\t\t\t\t.stream()\n\t\t\t\t.map(TestI18n::getPrefix)\n\t\t\t\t.collect(Collectors.toList());\n\t\ttry (Stream<Path> list = Files.list(i18nPath)) {\n\t\t\tlist.filter(p -> !p.equals(refPath))\n\t\t\t\t\t.forEach(path -> compareToReference(path, reference));\n\t\t}\n\t}\n\n\t/**\n\t * Extract prefix: 'key='\n\t */\n\tprivate static String getPrefix(String line) {\n\t\tif (line.isBlank()) {\n\t\t\treturn \"\";\n\t\t}\n\t\tint sep = line.indexOf('=');\n\t\tif (sep == -1) {\n\t\t\treturn line;\n\t\t}\n\t\tif (line.startsWith(\"#\")) {\n\t\t\tfail(DEFAULT_LANG_FILE + \" shouldn't contain commented values: \" + line);\n\t\t}\n\t\treturn line.substring(0, sep + 1);\n\t}\n\n\tprivate void compareToReference(Path path, List<String> reference) {\n\t\ttry {\n\t\t\tList<String> lines = Files.readAllLines(path);\n\t\t\tfor (int i = 0; i < reference.size(); i++) {\n\t\t\t\tString prefix = reference.get(i);\n\t\t\t\tif (prefix.isEmpty()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (i >= lines.size()) {\n\t\t\t\t\tfail(\"File '\" + path.getFileName() + \"' contains unexpected lines at end\");\n\t\t\t\t}\n\t\t\t\tString line = lines.get(i);\n\t\t\t\tif (!trimComment(line).startsWith(prefix)) {\n\t\t\t\t\tfailLine(path, i + 1);\n\t\t\t\t}\n\t\t\t\tif (line.startsWith(\"#\")) {\n\t\t\t\t\tint sep = line.indexOf('=');\n\t\t\t\t\tif (line.substring(sep + 1).isBlank()) {\n\t\t\t\t\t\tfail(\"File '\" + path.getFileName() + \"' has empty ref text at line \" + (i + 1) + \": \" + line);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (lines.size() != reference.size()) {\n\t\t\t\tfailLine(path, reference.size());\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tfail(\"Process error \", e);\n\t\t}\n\t}\n\n\tprivate static String trimComment(String string) {\n\t\treturn string.startsWith(\"#\") ? string.substring(1) : string;\n\t}\n\n\tprivate void failLine(Path path, int line) {\n\t\tfail(\"I18n file: \" + path.getFileName() + \" and \" + DEFAULT_LANG_FILE + \" differ in line \" + line);\n\t}\n\n\t/**\n\t * Temporary solution to allow use I18N strings in plugins until proper API implemented\n\t */\n\tprivate static final List<String> EXCLUDED_KEYS = Arrays.asList(\n\t\t\t// keys from `jadx-script-kotlin`\n\t\t\t\"file.save\",\n\t\t\t\"tree.input_scripts\",\n\t\t\t\"popup.new_script\",\n\t\t\t\"popup.add_scripts\",\n\t\t\t\"script.log\",\n\t\t\t\"script.format\",\n\t\t\t\"script.check\");\n\n\t@Test\n\tpublic void keyIsUsed() throws IOException {\n\t\tProperties properties = new Properties();\n\t\ttry (Reader reader = Files.newBufferedReader(i18nPath.resolve(DEFAULT_LANG_FILE))) {\n\t\t\tproperties.load(reader);\n\t\t}\n\t\tEXCLUDED_KEYS.forEach(properties.keySet()::remove);\n\t\tSet<String> keys = new HashSet<>();\n\t\tfor (Object key : properties.keySet()) {\n\t\t\tkeys.add(\"\\\"\" + key + '\"');\n\t\t}\n\t\ttry (Stream<Path> walk = Files.walk(guiJavaPath)) {\n\t\t\twalk.filter(Files::isRegularFile).forEach(p -> {\n\t\t\t\ttry {\n\t\t\t\t\tfor (String line : Files.readAllLines(p)) {\n\t\t\t\t\t\tkeys.removeIf(line::contains);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tassertThat(keys).as(\"keys not used\").isEmpty();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/test/java/jadx/gui/device/debugger/smali/DbgSmaliTest.java",
    "content": "package jadx.gui.device.debugger.smali;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.tests.api.SmaliTest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass DbgSmaliTest extends SmaliTest {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DbgSmaliTest.class);\n\n\t@BeforeEach\n\tpublic void initProject() {\n\t\tsetCurrentProject(\"jadx-gui\");\n\t}\n\n\t@Test\n\tvoid testSwitch() {\n\t\tdisableCompilation();\n\t\tClassNode cls = getClassNodeFromSmali(\"switch\", \"SwitchTest\");\n\t\tSmali disasm = Smali.disassemble(cls);\n\t\tLOG.debug(\"{}\", disasm.getCode());\n\t}\n\n\t@Test\n\tvoid testParams() {\n\t\tdisableCompilation();\n\t\tClassNode cls = getClassNodeFromSmali(\"params\", \"ParamsTest\");\n\t\tSmali disasm = Smali.disassemble(cls);\n\t\tString code = disasm.getCode();\n\t\tLOG.debug(\"{}\", code);\n\t\tassertThat(code)\n\t\t\t\t.doesNotContain(\"Failed to write method\")\n\t\t\t\t.doesNotContain(\".param p1\")\n\t\t\t\t.contains(\".local p1, \\\"arg0\\\":Landroid/widget/AdapterView;, \\\"Landroid/widget/AdapterView<*>;\\\"\");\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/test/java/jadx/gui/ui/codearea/ConvertNumberActionTest.java",
    "content": "package jadx.gui.ui.codearea;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class ConvertNumberActionTest {\n\n\t@Test\n\tpublic void nonNumeric() {\n\t\tassertThat(ConvertNumberAction.getConversionsFromWord(\"non-numeric\")).isNullOrEmpty();\n\t\tassertThat(ConvertNumberAction.getConversionsFromWord(\"0xnon-numeric\")).isNullOrEmpty();\n\t\tassertThat(ConvertNumberAction.getConversionsFromWord(\"non-numericL\")).isNullOrEmpty();\n\t\tassertThat(ConvertNumberAction.getConversionsFromWord(\"-non-numeric\")).isNullOrEmpty();\n\t\tassertThat(ConvertNumberAction.getConversionsFromWord(\"ABCD\")).isNullOrEmpty();\n\t}\n\n\t@Test\n\tpublic void simpleDecimalToHex() {\n\n\t\tList<String> expected = new ArrayList<String>();\n\t\texpected.add(\"0x7b\");\n\t\texpected.add(\"0b01111011\");\n\t\texpected.add(\"'{'\");\n\n\t\tList<String> result = ConvertNumberAction.getConversionsFromWord(\"123\");\n\n\t\tassertThat(result).isNotEmpty();\n\n\t\texpected.removeAll(result);\n\t\tassertThat(expected).isEmpty();\n\t}\n\n\t@Test\n\tpublic void negativeDecimalToHex() {\n\n\t\tList<String> expected = new ArrayList<String>();\n\t\texpected.add(\"0xffffff85\");\n\t\texpected.add(\"0b11111111111111111111111110000101\");\n\n\t\tList<String> result = ConvertNumberAction.getConversionsFromWord(\"-123\");\n\n\t\tassertThat(result).isNotEmpty();\n\n\t\texpected.removeAll(result);\n\t\tassertThat(expected).isEmpty();\n\t}\n\n\t@Test\n\tpublic void negativeLongDecimalToHex() {\n\n\t\tList<String> expected = new ArrayList<String>();\n\t\texpected.add(\"0xFFFFFFE8B7891800\".toLowerCase());\n\t\texpected.add(\"0b1111111111111111111111111110100010110111100010010001100000000000\");\n\n\t\tList<String> result = ConvertNumberAction.getConversionsFromWord(\"-100000000000\");\n\n\t\tassertThat(result).isNotEmpty();\n\n\t\texpected.removeAll(result);\n\t\tassertThat(expected).isEmpty();\n\t}\n\n\t@Test\n\tpublic void simpleHexToDecimal() {\n\n\t\tList<String> expected = new ArrayList<String>();\n\t\texpected.add(\"123\");\n\t\texpected.add(\"0b01111011\");\n\t\texpected.add(\"'{'\");\n\n\t\tList<String> result = ConvertNumberAction.getConversionsFromWord(\"0x7b\");\n\n\t\tassertThat(result).isNotEmpty();\n\n\t\texpected.removeAll(result);\n\t\tassertThat(expected).isEmpty();\n\t}\n\n\t@Test\n\tpublic void zeroToHex() {\n\n\t\tList<String> expected = new ArrayList<String>();\n\t\texpected.add(\"0x0\");\n\t\texpected.add(\"0b00000000\");\n\n\t\tList<String> result = ConvertNumberAction.getConversionsFromWord(Integer.toString(0));\n\n\t\tassertThat(result).isNotEmpty();\n\n\t\texpected.removeAll(result);\n\t\tassertThat(expected).isEmpty();\n\n\t}\n\n\t@Test\n\tpublic void minIntToHex() {\n\n\t\tList<String> expected = new ArrayList<String>();\n\t\texpected.add(\"0x80000000\");\n\t\texpected.add(\"0b10000000000000000000000000000000\");\n\n\t\tList<String> result = ConvertNumberAction.getConversionsFromWord(Integer.toString(Integer.MIN_VALUE));\n\n\t\tassertThat(result).isNotEmpty();\n\n\t\texpected.removeAll(result);\n\t\tassertThat(expected).isEmpty();\n\n\t}\n\n\t@Test\n\tpublic void maxIntToHex() {\n\n\t\tList<String> expected = new ArrayList<String>();\n\t\texpected.add(\"0x7fffffff\");\n\t\texpected.add(\"0b01111111111111111111111111111111\");\n\n\t\tList<String> result = ConvertNumberAction.getConversionsFromWord(Integer.toString(Integer.MAX_VALUE));\n\n\t\tassertThat(result).isNotEmpty();\n\n\t\texpected.removeAll(result);\n\t\tassertThat(expected).isEmpty();\n\n\t}\n\n\t@Test\n\tpublic void minLongToHex() {\n\n\t\tList<String> expected = new ArrayList<String>();\n\t\texpected.add(\"0x8000000000000000\");\n\t\texpected.add(\"0b1000000000000000000000000000000000000000000000000000000000000000\");\n\n\t\tList<String> result = ConvertNumberAction.getConversionsFromWord(Long.toString(Long.MIN_VALUE));\n\n\t\tassertThat(result).isNotEmpty();\n\n\t\texpected.removeAll(result);\n\t\tassertThat(expected).isEmpty();\n\n\t}\n\n\t@Test\n\tpublic void maxLongToHex() {\n\n\t\tList<String> expected = new ArrayList<String>();\n\t\texpected.add(\"0x7fffffffffffffff\");\n\t\texpected.add(\"0b0111111111111111111111111111111111111111111111111111111111111111\");\n\n\t\tList<String> result = ConvertNumberAction.getConversionsFromWord(Long.toString(Long.MAX_VALUE));\n\n\t\tassertThat(result).isNotEmpty();\n\n\t\texpected.removeAll(result);\n\t\tassertThat(expected).isEmpty();\n\n\t}\n\n\t@Test\n\tpublic void simpleLongSuffix() {\n\n\t\tList<String> expected = new ArrayList<String>();\n\t\texpected.add(\"0x7b\");\n\t\texpected.add(\"0b01111011\");\n\t\texpected.add(\"'{'\");\n\n\t\tList<String> result = ConvertNumberAction.getConversionsFromWord(\"123L\");\n\n\t\tassertThat(result).isNotEmpty();\n\n\t\texpected.removeAll(result);\n\t\tassertThat(expected).isEmpty();\n\n\t}\n\n\t@Test\n\tpublic void binaryPadding() {\n\n\t\tassertThat(ConvertNumberAction.getConversionsFromWord(\"0\")).containsOnlyOnce(\"0b00000000\");\n\t\tassertThat(ConvertNumberAction.getConversionsFromWord(\"1\")).containsOnlyOnce(\"0b00000001\");\n\t\tassertThat(ConvertNumberAction.getConversionsFromWord(\"127\")).containsOnlyOnce(\"0b01111111\");\n\t\tassertThat(ConvertNumberAction.getConversionsFromWord(\"0xff\")).containsOnlyOnce(\"0b11111111\");\n\t\tassertThat(ConvertNumberAction.getConversionsFromWord(\"0x7fff\")).containsOnlyOnce(\"0b0111111111111111\");\n\t\tassertThat(ConvertNumberAction.getConversionsFromWord(\"0xffff\")).containsOnlyOnce(\"0b1111111111111111\");\n\t\tassertThat(ConvertNumberAction.getConversionsFromWord(\"0x10000\")).containsOnlyOnce(\"0b000000010000000000000000\");\n\t\tassertThat(ConvertNumberAction.getConversionsFromWord(\"0xffffffff\")).containsOnlyOnce(\"0b11111111111111111111111111111111\");\n\n\t\tassertThat(ConvertNumberAction.getConversionsFromWord(\"0xffffffffffff\"))\n\t\t\t\t.containsOnlyOnce(\"0b111111111111111111111111111111111111111111111111\");\n\n\t\tassertThat(ConvertNumberAction.getConversionsFromWord(\"0x7fffffffffff\"))\n\t\t\t\t.containsOnlyOnce(\"0b011111111111111111111111111111111111111111111111\");\n\n\t\tassertThat(ConvertNumberAction.getConversionsFromWord(\"0x7fffffffffffffff\"))\n\t\t\t\t.containsOnlyOnce(\"0b0111111111111111111111111111111111111111111111111111111111111111\");\n\n\t}\n\n\t@Test\n\tpublic void printableAscii() {\n\n\t\tfor (int i = 32; i < 127; i++) {\n\t\t\tString printed = String.format(\"'%c'\", i);\n\t\t\tassertThat(ConvertNumberAction.getConversionsFromWord(Integer.toString(i))).containsOnlyOnce(printed);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jadx-gui/src/test/java/jadx/gui/update/TestJadxUpdate.kt",
    "content": "package jadx.gui.update\n\nimport jadx.gui.settings.JadxUpdateChannel\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Test\n\n/**\n * Test updates fetch.\n * All tests disabled because of network requests, run manually on JadxUpdate changes\n */\n@Disabled(\"Network requests\")\nclass TestJadxUpdate {\n\n\t@Test\n\tfun testStableCheck() {\n\t\tJadxUpdate(\"1.5.0\").checkForNewRelease(JadxUpdateChannel.STABLE)?.let {\n\t\t\tprintln(\"Latest release: $it\")\n\t\t}\n\t}\n\n\t@Test\n\tfun testUnstableCheck() {\n\t\tJadxUpdate(\"r2000\").checkForNewRelease(JadxUpdateChannel.UNSTABLE)?.let {\n\t\t\tprintln(\"Latest unstable: $it\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/test/java/jadx/gui/utils/CertificateManagerTest.java",
    "content": "package jadx.gui.utils;\n\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.security.cert.Certificate;\nimport java.util.Collection;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;\n\npublic class CertificateManagerTest {\n\tprivate static final String CERTIFICATE_TEST_DIR = \"certificate-test/\";\n\tprivate static final String DSA = \"CERT.DSA\";\n\tprivate static final String RSA = \"CERT.RSA\";\n\tprivate static final String EMPTY = \"EMPTY.txt\";\n\n\tprivate String emptyPath;\n\tprivate CertificateManager certificateManagerRSA;\n\tprivate CertificateManager certificateManagerDSA;\n\n\tprivate CertificateManager getCertificateManger(String resName) {\n\t\tString certPath = getResourcePath(resName);\n\t\ttry (InputStream in = new FileInputStream(certPath)) {\n\t\t\tCollection<? extends Certificate> certificates = CertificateManager.readCertificates(in);\n\t\t\tCertificate cert = certificates.iterator().next();\n\t\t\treturn new CertificateManager(cert);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to create CertificateManager\");\n\t\t}\n\t}\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\temptyPath = getResourcePath(EMPTY);\n\t\tcertificateManagerRSA = getCertificateManger(RSA);\n\t\tcertificateManagerDSA = getCertificateManger(DSA);\n\t}\n\n\t@Test\n\tpublic void decodeNotCertificateFile() throws IOException {\n\t\ttry (InputStream in = new FileInputStream(emptyPath)) {\n\t\t\tString result = CertificateManager.decode(in);\n\t\t\tassertThat(result).isEmpty();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void decodeRSAKeyHeader() {\n\t\tassertThat(certificateManagerRSA.generateHeader())\n\t\t\t\t.contains(\"X.509\")\n\t\t\t\t.contains(\"0x4bd68052\")\n\t\t\t\t.contains(\"CN=test cert, OU=test unit, O=OOO TestOrg, L=St.Peterburg, ST=Russia, C=123456\");\n\t}\n\n\t@Test\n\tpublic void decodeDSAKeyHeader() {\n\t\tassertThat(certificateManagerDSA.generateHeader())\n\t\t\t\t.contains(\"X.509\")\n\t\t\t\t.contains(\"0x16420ba2\")\n\t\t\t\t.contains(\"O=\\\"UJMRFVV CN=EDCVBGT C=TG\\\"\");\n\t}\n\n\t@Test\n\tpublic void decodeRSAKeySignature() {\n\t\tassertThat(certificateManagerRSA.generateSignature())\n\t\t\t\t.contains(\"SHA256withRSA\")\n\t\t\t\t.contains(\"1.2.840.113549.1.1.11\");\n\t}\n\n\t@Test\n\tpublic void decodeDSAKeySignature() {\n\t\tassertThat(certificateManagerDSA.generateSignature())\n\t\t\t\t.contains(\"SHA1withDSA\")\n\t\t\t\t.contains(\"1.2.840.10040.4.3\");\n\t}\n\n\t@Test\n\tpublic void decodeRSAFingerprint() {\n\t\tassertThat(certificateManagerRSA.generateFingerprint())\n\t\t\t\t.contains(\"61 18 0A 71 3F C9 55 16 4E 04 E3 C5 45 08 D9 11\")\n\t\t\t\t.contains(\"A0 6E A6 06 DB 2C 6F 3A 16 56 7F 75 97 7B AE 85 C2 13 09 37\")\n\t\t\t\t.contains(\"12 53 E8 BB C8 AA 27 A8 49 9B F8 0D 6E 68 CE 32 35 50 DE 55 A7 E7 8C 29 51 00 96 D7 56 F4 54 44\");\n\t}\n\n\t@Test\n\tpublic void decodeDSAFingerprint() {\n\t\tassertThat(certificateManagerDSA.generateFingerprint())\n\t\t\t\t.contains(\"D9 06 A6 2D 1F 79 8C 9D A6 EF 40 C7 2E C2 EA 0B\")\n\t\t\t\t.contains(\"18 E9 9C D4 A1 40 8F 63 FA EC 2E 62 A0 F2 AE B7 3F C3 C2 04\")\n\t\t\t\t.contains(\"74 F9 48 64 EE AC 92 26 53 2C 7A 0E 55 BE 5E D8 2F A7 D9 A9 99 F5 D5 21 2C 51 21 C4 31 AD 73 40\");\n\t}\n\n\t@Test\n\tpublic void decodeRSAPubKey() {\n\t\tassertThat(certificateManagerRSA.generatePublicKey())\n\t\t\t\t.contains(\"RSA\")\n\t\t\t\t.contains(\"65537\")\n\t\t\t\t.contains(\"1681953129031804462554643735709908030601939275292568895111488068832920121318010916\"\n\t\t\t\t\t\t+ \"889038430576806710152191447376363866950356097752126932858298006033288814768019331823126004318941179\"\n\t\t\t\t\t\t+ \"4465899645633586173494259691101582064441956032924396850221679489313043628562082670183392670094163371\"\n\t\t\t\t\t\t+ \"8586841184804093747497905514737738452134274762361473284344272721776230189352829291523087538543142199\"\n\t\t\t\t\t\t+ \"8761760403746876947208990209024335828599173964217021197086277312193991177728010193707324300633538463\"\n\t\t\t\t\t\t+ \"6193260583579409760790138329893534549366882523130765297472656435892831796545149793228897111760122091\"\n\t\t\t\t\t\t+ \"442123535919361963075454640516520743\");\n\t}\n\n\t@Test\n\tpublic void decodeDSAPubKey() {\n\t\tassertThat(certificateManagerDSA.generatePublicKey())\n\t\t\t\t.contains(\"DSA\")\n\t\t\t\t.contains(\"193233676050581546825633012823454532222793121048898990016982096262547255815113\"\n\t\t\t\t\t\t+ \"7546996381246109049596383861577383286736433045701055397423798599190480095839416942148507037843474\"\n\t\t\t\t\t\t+ \"67923797088055637932532829952742936211625049432875384559446523443782422268975073691469424116922209\"\n\t\t\t\t\t\t+ \"22477368782490423187845815262510366\");\n\t}\n\n\tprivate String getResourcePath(String resName) {\n\t\tURL resource = getClass().getClassLoader().getResource(CERTIFICATE_TEST_DIR + resName);\n\t\tif (resource == null) {\n\t\t\tthrow new RuntimeException(\"Resource not found: \" + resName);\n\t\t}\n\t\treturn resource.getPath();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/test/java/jadx/gui/utils/JumpManagerTest.java",
    "content": "package jadx.gui.utils;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport jadx.gui.treemodel.TextNode;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass JumpManagerTest {\n\tprivate JumpManager jm;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tjm = new JumpManager();\n\t}\n\n\t@Test\n\tpublic void testEmptyHistory() {\n\t\tassertThat(jm.getPrev()).isNull();\n\t\tassertThat(jm.getNext()).isNull();\n\t}\n\n\t@Test\n\tpublic void testEmptyHistory2() {\n\t\tassertThat(jm.getPrev()).isNull();\n\t\tassertThat(jm.getNext()).isNull();\n\t\tassertThat(jm.getPrev()).isNull();\n\t\tassertThat(jm.getNext()).isNull();\n\t\tassertThat(jm.getPrev()).isNull();\n\t}\n\n\t@Test\n\tpublic void testOneElement() {\n\t\tjm.addPosition(makeJumpPos());\n\n\t\tassertThat(jm.getPrev()).isNull();\n\t\tassertThat(jm.getNext()).isNull();\n\t}\n\n\t@Test\n\tpublic void testTwoElements() {\n\t\tJumpPosition pos1 = makeJumpPos();\n\t\tjm.addPosition(pos1);\n\t\tJumpPosition pos2 = makeJumpPos();\n\t\tjm.addPosition(pos2);\n\n\t\tassertThat(jm.getPrev()).isSameAs(pos1);\n\t\tassertThat(jm.getPrev()).isNull();\n\t\tassertThat(jm.getNext()).isSameAs(pos2);\n\t\tassertThat(jm.getNext()).isNull();\n\t}\n\n\t@Test\n\tpublic void testNavigation() {\n\t\tJumpPosition pos1 = makeJumpPos();\n\t\tjm.addPosition(pos1);\n\t\t// 1@\n\t\tJumpPosition pos2 = makeJumpPos();\n\t\tjm.addPosition(pos2);\n\t\t// 1 - 2@\n\t\tassertThat(jm.getPrev()).isSameAs(pos1);\n\t\t// 1@ - 2\n\t\tJumpPosition pos3 = makeJumpPos();\n\t\tjm.addPosition(pos3);\n\t\t// 1 - 3@\n\t\tassertThat(jm.getNext()).isNull();\n\t\tassertThat(jm.getPrev()).isSameAs(pos1);\n\t\t// 1@ - 3\n\t\tassertThat(jm.getNext()).isSameAs(pos3);\n\t}\n\n\t@Test\n\tpublic void testNavigation2() {\n\t\tJumpPosition pos1 = makeJumpPos();\n\t\tjm.addPosition(pos1);\n\t\t// 1@\n\t\tJumpPosition pos2 = makeJumpPos();\n\t\tjm.addPosition(pos2);\n\t\t// 1 - 2@\n\t\tJumpPosition pos3 = makeJumpPos();\n\t\tjm.addPosition(pos3);\n\t\t// 1 - 2 - 3@\n\t\tJumpPosition pos4 = makeJumpPos();\n\t\tjm.addPosition(pos4);\n\t\t// 1 - 2 - 3 - 4@\n\t\tassertThat(jm.getPrev()).isSameAs(pos3);\n\t\t// 1 - 2 - 3@ - 4\n\t\tassertThat(jm.getPrev()).isSameAs(pos2);\n\t\t// 1 - 2@ - 3 - 4\n\t\tJumpPosition pos5 = makeJumpPos();\n\t\tjm.addPosition(pos5);\n\t\t// 1 - 2 - 5@\n\t\tassertThat(jm.getNext()).isNull();\n\t\tassertThat(jm.getNext()).isNull();\n\t\tassertThat(jm.getPrev()).isSameAs(pos2);\n\t\t// 1 - 2@ - 5\n\t\tassertThat(jm.getPrev()).isSameAs(pos1);\n\t\t// 1@ - 2 - 5\n\t\tassertThat(jm.getPrev()).isNull();\n\t\tassertThat(jm.getNext()).isSameAs(pos2);\n\t\t// 1 - 2@ - 5\n\t\tassertThat(jm.getNext()).isSameAs(pos5);\n\t\t// 1 - 2 - 5@\n\t\tassertThat(jm.getNext()).isNull();\n\t}\n\n\t@Test\n\tpublic void addSame() {\n\t\tJumpPosition pos = makeJumpPos();\n\t\tjm.addPosition(pos);\n\t\tjm.addPosition(pos);\n\n\t\tassertThat(jm.getPrev()).isNull();\n\t\tassertThat(jm.getNext()).isNull();\n\t}\n\n\tprivate JumpPosition makeJumpPos() {\n\t\treturn new JumpPosition(new TextNode(\"\"), 0);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/test/java/jadx/gui/utils/cache/code/DiskCodeCacheTest.java",
    "content": "package jadx.gui.utils.cache.code;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.impl.NoOpCodeCache;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.gui.cache.code.disk.DiskCodeCache;\nimport jadx.tests.api.IntegrationTest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass DiskCodeCacheTest extends IntegrationTest {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DiskCodeCacheTest.class);\n\n\t@TempDir\n\tpublic Path tempDir;\n\n\t@Test\n\tpublic void test() throws IOException {\n\t\tdisableCompilation();\n\t\tgetArgs().setCodeCache(NoOpCodeCache.INSTANCE);\n\t\tClassNode clsNode = getClassNode(DiskCodeCacheTest.class);\n\t\tICodeInfo codeInfo = clsNode.getCode();\n\n\t\tDiskCodeCache cache = new DiskCodeCache(clsNode.root(), tempDir);\n\n\t\tString clsKey = clsNode.getFullName();\n\t\tcache.add(clsKey, codeInfo);\n\n\t\tICodeInfo readCodeInfo = cache.get(clsKey);\n\n\t\tassertThat(readCodeInfo).isNotNull();\n\t\tassertThat(readCodeInfo.getCodeStr()).isEqualTo(codeInfo.getCodeStr());\n\t\tassertThat(readCodeInfo.getCodeMetadata().getLineMapping()).isEqualTo(codeInfo.getCodeMetadata().getLineMapping());\n\t\tLOG.info(\"Disk code annotations: {}\", readCodeInfo.getCodeMetadata().getAsMap());\n\t\tassertThat(readCodeInfo.getCodeMetadata().getAsMap()).hasSameSizeAs(codeInfo.getCodeMetadata().getAsMap());\n\n\t\tcache.close();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/test/java/jadx/gui/utils/cache/code/disk/adapters/DataAdapterHelperTest.java",
    "content": "package jadx.gui.utils.cache.code.disk.adapters;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.DataInput;\nimport java.io.DataInputStream;\nimport java.io.DataOutputStream;\nimport java.io.IOException;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.gui.cache.code.disk.adapters.DataAdapterHelper;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass DataAdapterHelperTest {\n\n\t@Test\n\tvoid uvInt() throws IOException {\n\t\tcheckUVIntFor(0);\n\t\tcheckUVIntFor(7);\n\t\tcheckUVIntFor(0x7f);\n\t\tcheckUVIntFor(0x80);\n\t\tcheckUVIntFor(0x256);\n\t\tcheckUVIntFor(Byte.MAX_VALUE);\n\t\tcheckUVIntFor(Short.MAX_VALUE);\n\t\tcheckUVIntFor(Integer.MAX_VALUE);\n\t}\n\n\tprivate void checkUVIntFor(int val) throws IOException {\n\t\tassertThat(writeReadUVInt(val)).isEqualTo(val);\n\t}\n\n\tprivate int writeReadUVInt(int val) throws IOException {\n\t\tByteArrayOutputStream byteOut = new ByteArrayOutputStream();\n\t\tDataOutputStream out = new DataOutputStream(byteOut);\n\t\tDataAdapterHelper.writeUVInt(out, val);\n\n\t\tDataInput in = new DataInputStream(new ByteArrayInputStream(byteOut.toByteArray()));\n\t\treturn DataAdapterHelper.readUVInt(in);\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/test/java/jadx/gui/utils/pkgs/TestJRenamePackage.java",
    "content": "package jadx.gui.utils.pkgs;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass TestJRenamePackage {\n\n\t@Test\n\tvoid isValidName() {\n\t\tvalid(\"foo\");\n\t\tvalid(\"foo.bar\");\n\t\tvalid(\".bar\");\n\n\t\tinvalid(\"\");\n\t\tinvalid(\"0foo\");\n\t\tinvalid(\"foo.\");\n\t\tinvalid(\"do\");\n\t\tinvalid(\"foo.if\");\n\t\tinvalid(\"foo.if.bar\");\n\t}\n\n\tprivate void valid(String name) {\n\t\tassertThat(JRenamePackage.isValidPackageName(name))\n\t\t\t\t.as(\"expect valid: %s\", name)\n\t\t\t\t.isTrue();\n\t}\n\n\tprivate void invalid(String name) {\n\t\tassertThat(JRenamePackage.isValidPackageName(name))\n\t\t\t\t.as(\"expect invalid: %s\", name)\n\t\t\t\t.isFalse();\n\t}\n}\n"
  },
  {
    "path": "jadx-gui/src/test/resources/certificate-test/EMPTY.txt",
    "content": "test\n"
  },
  {
    "path": "jadx-gui/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t\t<encoder>\n\t\t\t<pattern>%d{HH:mm:ss} %-5level - %msg%n</pattern>\n\t\t</encoder>\n\t</appender>\n\n\t<root level=\"DEBUG\">\n\t\t<appender-ref ref=\"STDOUT\"/>\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "jadx-gui/src/test/smali/params.smali",
    "content": ".class LParamsTest;\n.super Ljava/lang/Object;\n\n.method public test(Landroid/widget/AdapterView;Landroid/view/View;IJ)V\n    .registers 10\n    .param p2, \"arg1\"    # Landroid/view/View;\n    .param p3, \"arg2\"    # I\n    .param p4, \"arg3\"    # J\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Landroid/widget/AdapterView\",\n            \"<*>;\",\n            \"Landroid/view/View;\",\n            \"IJ)V\"\n        }\n    .end annotation\n\n    .prologue\n    .line 69\n    .local p1, \"arg0\":Landroid/widget/AdapterView;, \"Landroid/widget/AdapterView<*>;\"\n    iget-object v2, p0, LParamsTest;->this$0:Ltest/ColorListActivity;\n\n    .line 72\n    iget-object v2, v2, Ltest/ColorListActivity;->mSortedColorList:[Ljava/lang/String;\n\n    .line 75\n    aget-object v0, v2, p3\n\n    .line 80\n    .local v0, \"colorString\":Ljava/lang/String;\n    new-instance v1, Landroid/content/Intent;\n\n    .line 83\n    iget-object v2, p0, LParamsTest;->this$0:Ltest/ColorListActivity;\n\n    .line 86\n    const-class v3, Ltest/ColorItemActivity;\n\n    .line 89\n    invoke-direct {v1, v2, v3}, Landroid/content/Intent;-><init>(Landroid/content/Context;Ljava/lang/Class;)V\n\n    .line 94\n    .local v1, \"intent\":Landroid/content/Intent;\n    const-string v2, \"colorString\"\n\n    .line 97\n    invoke-virtual {v1, v2, v0}, Landroid/content/Intent;->putExtra(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;\n\n    .line 101\n    iget-object v2, p0, LParamsTest;->this$0:Ltest/ColorListActivity;\n\n    .line 104\n    invoke-virtual {v2, v1}, Ltest/ColorListActivity;->startActivity(Landroid/content/Intent;)V\n\n    .line 108\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-gui/src/test/smali/switch.smali",
    "content": ".class public final LSwitchTest;\n.super Ljava/lang/Object;\n\n.field public static final synthetic a:LSwitchTest;\n.field public static final synthetic b:LSwitchTest;\n\n.field private final synthetic c:I\n\n.method public final test(Ljava/lang/Runnable;)Ljava/lang/Thread;\n    .registers 4\n    const v0, 0xa\n    const v1, 0xa\n    add-int v0, v0, v1\n    rem-int v0, v0, v1\n    if-gtz v0, :cond_f\n    goto/32 :goto_d2\n\n    :cond_f\n    :goto_f\n    goto/32 :goto_bf\n    :goto_18\n    const-string v1, \"A\"\n    goto/32 :goto_68\n    :goto_26\n    const-string v1, \"B\"\n    goto/32 :goto_7c\n    :goto_33\n    return-object v0\n\n    :pswitch_38\n    goto/32 :goto_4e\n    :goto_41\n    new-instance v0, Labo;\n    goto/32 :goto_84\n    :goto_4e\n    new-instance v0, Lwf;\n    goto/32 :goto_ab\n    :goto_5b\n    new-instance v0, Ljava/lang/Thread;\n    goto/32 :goto_18\n    :goto_68\n    invoke-direct {v0, p1, v1}, Ljava/lang/Thread;-><init>(Ljava/lang/Runnable;Ljava/lang/String;)V\n    goto/32 :goto_71\n    :goto_71\n    return-object v0\n    :pswitch_75\n    goto/32 :goto_b3\n    :goto_7c\n    invoke-direct {v0, p1, v1}, Ljava/lang/Thread;-><init>(Ljava/lang/Runnable;Ljava/lang/String;)V\n    goto/32 :goto_33\n    :goto_84\n    invoke-direct {v0, p1}, Labo;-><init>(Ljava/lang/Runnable;)V\n    goto/32 :goto_90\n    :goto_90\n    return-object v0\n\n    :pswitch_data_96\n    .packed-switch 0x0\n        :pswitch_a3\n        :pswitch_38\n        :pswitch_75\n    .end packed-switch\n\n    :goto_a0\n    return-object v0\n    :pswitch_a3\n    goto/32 :goto_41\n    :goto_ab\n    invoke-direct {v0, p1}, Lwf;-><init>(Ljava/lang/Runnable;)V\n    goto/32 :goto_a0\n    :goto_b3\n    new-instance v0, Ljava/lang/Thread;\n    goto/32 :goto_26\n    :goto_bf\n    iget v0, p0, LSwitchTest;->c:I\n\n    packed-switch v0, :pswitch_data_96\n    goto/32 :goto_5b\n    :goto_d2\n    goto/32 :goto_f\n.end method\n"
  },
  {
    "path": "jadx-plugins/jadx-aab-input/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-library\")\n}\n\ndependencies {\n\tcompileOnly(project(\":jadx-core\"))\n\n\timplementation(\"com.android.tools.build:aapt2-proto:8.13.2-14304508\")\n\timplementation(\"com.google.protobuf:protobuf-java\") {\n\t\tversion {\n\t\t\trequire(\"3.25.3\") // version 4 conflict with `aapt2-proto`\n\t\t}\n\t}\n\n\timplementation(\"com.android.tools.build:bundletool:1.18.3\") {\n\t\t// All of this is unnecessary for parsing BundleConfig.pb except for protobuf\n\t\texclude(group = \"com.android.tools.build\")\n\t\texclude(group = \"com.google.protobuf\")\n\t\texclude(group = \"com.google.guava\")\n\t\texclude(group = \"org.bitbucket.b_c\")\n\t\texclude(group = \"org.slf4j\")\n\t\texclude(group = \"com.google.auto.value\")\n\t\texclude(group = \"com.google.dagger\")\n\t\texclude(group = \"com.google.errorprone\")\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/AabInputPlugin.java",
    "content": "package jadx.plugins.input.aab;\n\nimport jadx.api.plugins.JadxPlugin;\nimport jadx.api.plugins.JadxPluginContext;\nimport jadx.api.plugins.JadxPluginInfo;\nimport jadx.api.plugins.resources.IResourcesLoader;\nimport jadx.plugins.input.aab.factories.ProtoAppDependenciesResContainerFactory;\nimport jadx.plugins.input.aab.factories.ProtoAssetsConfigResContainerFactory;\nimport jadx.plugins.input.aab.factories.ProtoBundleConfigResContainerFactory;\nimport jadx.plugins.input.aab.factories.ProtoNativeConfigResContainerFactory;\nimport jadx.plugins.input.aab.factories.ProtoTableResContainerFactory;\nimport jadx.plugins.input.aab.factories.ProtoXmlResContainerFactory;\n\npublic class AabInputPlugin implements JadxPlugin {\n\tpublic static final String PLUGIN_ID = \"aab-input\";\n\n\t@Override\n\tpublic JadxPluginInfo getPluginInfo() {\n\t\treturn new JadxPluginInfo(\n\t\t\t\tPLUGIN_ID,\n\t\t\t\t\".AAB Input\",\n\t\t\t\t\"Loads .AAB files.\");\n\t}\n\n\t@Override\n\tpublic synchronized void init(JadxPluginContext context) {\n\t\tIResourcesLoader resourcesLoader = context.getResourcesLoader();\n\t\tResTableProtoParserProvider tableParserProvider = new ResTableProtoParserProvider();\n\t\tresourcesLoader.addResTableParserProvider(tableParserProvider);\n\n\t\tresourcesLoader.addResContainerFactory(new ProtoTableResContainerFactory(tableParserProvider));\n\t\tresourcesLoader.addResContainerFactory(new ProtoXmlResContainerFactory());\n\t\tresourcesLoader.addResContainerFactory(new ProtoBundleConfigResContainerFactory());\n\t\tresourcesLoader.addResContainerFactory(new ProtoAssetsConfigResContainerFactory());\n\t\tresourcesLoader.addResContainerFactory(new ProtoNativeConfigResContainerFactory());\n\t\tresourcesLoader.addResContainerFactory(new ProtoAppDependenciesResContainerFactory());\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/ResTableProtoParserProvider.java",
    "content": "package jadx.plugins.input.aab;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ResourceFile;\nimport jadx.api.plugins.resources.IResTableParserProvider;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.xmlgen.IResTableParser;\nimport jadx.plugins.input.aab.parsers.ResTableProtoParser;\n\npublic class ResTableProtoParserProvider implements IResTableParserProvider {\n\tprivate RootNode root;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tthis.root = root;\n\t}\n\n\t@Override\n\tpublic @Nullable IResTableParser getParser(ResourceFile resFile) {\n\t\tString fileName = resFile.getOriginalName();\n\t\tif (!fileName.endsWith(\"resources.pb\")) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new ResTableProtoParser(root);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/factories/ProtoAppDependenciesResContainerFactory.java",
    "content": "package jadx.plugins.input.aab.factories;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport com.android.bundle.AppDependenciesOuterClass;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ResourceFile;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.api.plugins.resources.IResContainerFactory;\nimport jadx.core.xmlgen.ResContainer;\n\npublic class ProtoAppDependenciesResContainerFactory implements IResContainerFactory {\n\n\t@Override\n\tpublic @Nullable ResContainer create(ResourceFile resFile, InputStream inputStream) throws IOException {\n\t\tif (!resFile.getOriginalName().endsWith(\"BUNDLE-METADATA/com.android.tools.build.libraries/dependencies.pb\")) {\n\t\t\treturn null;\n\t\t}\n\n\t\tAppDependenciesOuterClass.AppDependencies appDependencies = AppDependenciesOuterClass.AppDependencies.parseFrom(inputStream);\n\t\tICodeInfo content = new SimpleCodeInfo(appDependencies.toString());\n\t\treturn ResContainer.textResource(resFile.getDeobfName(), content);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/factories/ProtoAssetsConfigResContainerFactory.java",
    "content": "package jadx.plugins.input.aab.factories;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport com.android.bundle.Files;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ResourceFile;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.api.plugins.resources.IResContainerFactory;\nimport jadx.core.xmlgen.ResContainer;\n\npublic class ProtoAssetsConfigResContainerFactory implements IResContainerFactory {\n\n\t@Override\n\tpublic @Nullable ResContainer create(ResourceFile resFile, InputStream inputStream) throws IOException {\n\t\tif (!resFile.getOriginalName().endsWith(\"assets.pb\")) {\n\t\t\treturn null;\n\t\t}\n\n\t\tFiles.Assets assetsConfig = Files.Assets.parseFrom(inputStream);\n\t\tICodeInfo content = new SimpleCodeInfo(assetsConfig.toString());\n\t\treturn ResContainer.textResource(resFile.getDeobfName(), content);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/factories/ProtoBundleConfigResContainerFactory.java",
    "content": "package jadx.plugins.input.aab.factories;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport com.android.bundle.Config.BundleConfig;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ResourceFile;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.api.plugins.resources.IResContainerFactory;\nimport jadx.core.xmlgen.ResContainer;\n\npublic class ProtoBundleConfigResContainerFactory implements IResContainerFactory {\n\n\t@Override\n\tpublic @Nullable ResContainer create(ResourceFile resFile, InputStream inputStream) throws IOException {\n\t\tif (!resFile.getOriginalName().endsWith(\"BundleConfig.pb\")) {\n\t\t\treturn null;\n\t\t}\n\t\tBundleConfig bundleConfig = BundleConfig.parseFrom(inputStream);\n\t\tICodeInfo content = new SimpleCodeInfo(bundleConfig.toString());\n\t\treturn ResContainer.textResource(resFile.getDeobfName(), content);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/factories/ProtoNativeConfigResContainerFactory.java",
    "content": "package jadx.plugins.input.aab.factories;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport com.android.bundle.Files;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ResourceFile;\nimport jadx.api.impl.SimpleCodeInfo;\nimport jadx.api.plugins.resources.IResContainerFactory;\nimport jadx.core.xmlgen.ResContainer;\n\npublic class ProtoNativeConfigResContainerFactory implements IResContainerFactory {\n\n\t@Override\n\tpublic @Nullable ResContainer create(ResourceFile resFile, InputStream inputStream) throws IOException {\n\t\tif (!resFile.getOriginalName().endsWith(\"native.pb\")) {\n\t\t\treturn null;\n\t\t}\n\n\t\tFiles.NativeLibraries nativeConfig = Files.NativeLibraries.parseFrom(inputStream);\n\t\tICodeInfo content = new SimpleCodeInfo(nativeConfig.toString());\n\t\treturn ResContainer.textResource(resFile.getDeobfName(), content);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/factories/ProtoTableResContainerFactory.java",
    "content": "package jadx.plugins.input.aab.factories;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourceType;\nimport jadx.api.plugins.resources.IResContainerFactory;\nimport jadx.api.plugins.resources.IResTableParserProvider;\nimport jadx.core.xmlgen.IResTableParser;\nimport jadx.core.xmlgen.ResContainer;\n\npublic class ProtoTableResContainerFactory implements IResContainerFactory {\n\tprivate final IResTableParserProvider provider;\n\n\tpublic ProtoTableResContainerFactory(IResTableParserProvider provider) {\n\t\tthis.provider = provider;\n\t}\n\n\t@Override\n\tpublic @Nullable ResContainer create(ResourceFile resFile, InputStream inputStream) throws IOException {\n\t\tif (!resFile.getOriginalName().endsWith(\".pb\") || resFile.getType() != ResourceType.ARSC) {\n\t\t\treturn null;\n\t\t}\n\t\tIResTableParser parser = provider.getParser(resFile);\n\t\tif (parser == null) {\n\t\t\treturn null;\n\t\t}\n\t\tparser.decode(inputStream);\n\t\treturn parser.decodeFiles();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/factories/ProtoXmlResContainerFactory.java",
    "content": "package jadx.plugins.input.aab.factories;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourceType;\nimport jadx.api.plugins.resources.IResContainerFactory;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.xmlgen.ResContainer;\nimport jadx.plugins.input.aab.parsers.ResXmlProtoParser;\nimport jadx.zip.IZipEntry;\n\npublic class ProtoXmlResContainerFactory implements IResContainerFactory {\n\tprivate ResXmlProtoParser xmlParser;\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\txmlParser = new ResXmlProtoParser(root);\n\t}\n\n\t@Override\n\tpublic @Nullable ResContainer create(ResourceFile resFile, InputStream inputStream) throws IOException {\n\t\tResourceType type = resFile.getType();\n\t\tif (type != ResourceType.XML && type != ResourceType.MANIFEST) {\n\t\t\treturn null;\n\t\t}\n\t\tIZipEntry zipEntry = resFile.getZipEntry();\n\t\tif (zipEntry == null) {\n\t\t\treturn null;\n\t\t}\n\t\tboolean isFromAab = zipEntry.getZipFile().getPath().toLowerCase().endsWith(\".aab\");\n\t\tif (!isFromAab) {\n\t\t\treturn null;\n\t\t}\n\t\tICodeInfo content = xmlParser.parse(inputStream);\n\t\treturn ResContainer.textResource(resFile.getDeobfName(), content);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/parsers/CommonProtoParser.java",
    "content": "package jadx.plugins.input.aab.parsers;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.android.aapt.ConfigurationOuterClass;\nimport com.android.aapt.Resources;\n\nimport jadx.core.xmlgen.ParserConstants;\nimport jadx.core.xmlgen.XmlGenUtils;\nimport jadx.core.xmlgen.entry.EntryConfig;\nimport jadx.core.xmlgen.entry.ProtoValue;\n\npublic class CommonProtoParser extends ParserConstants {\n\tprotected ProtoValue parse(Resources.Style s) {\n\t\tList<ProtoValue> namedValues = new ArrayList<>(s.getEntryCount());\n\t\tString parent = s.getParent().getName();\n\t\tif (parent.isEmpty()) {\n\t\t\tparent = null;\n\t\t} else {\n\t\t\tparent = '@' + parent;\n\t\t}\n\t\tfor (int i = 0; i < s.getEntryCount(); i++) {\n\t\t\tResources.Style.Entry entry = s.getEntry(i);\n\t\t\tString name = entry.getKey().getName();\n\t\t\tString value = parse(entry.getItem());\n\t\t\tnamedValues.add(new ProtoValue(value).setName(name));\n\t\t}\n\t\treturn new ProtoValue().setNamedValues(namedValues).setParent(parent);\n\t}\n\n\tprotected ProtoValue parse(Resources.Styleable s) {\n\t\tList<ProtoValue> namedValues = new ArrayList<>(s.getEntryCount());\n\t\tfor (int i = 0; i < s.getEntryCount(); i++) {\n\t\t\tResources.Styleable.Entry e = s.getEntry(i);\n\t\t\tnamedValues.add(new ProtoValue('@' + e.getAttr().getName()));\n\t\t}\n\t\treturn new ProtoValue().setNamedValues(namedValues);\n\t}\n\n\tprotected ProtoValue parse(Resources.Array a) {\n\t\tList<ProtoValue> namedValues = new ArrayList<>(a.getElementCount());\n\t\tfor (int i = 0; i < a.getElementCount(); i++) {\n\t\t\tResources.Array.Element e = a.getElement(i);\n\t\t\tString value = parse(e.getItem());\n\t\t\tnamedValues.add(new ProtoValue(value));\n\t\t}\n\t\treturn new ProtoValue().setNamedValues(namedValues);\n\t}\n\n\tprotected ProtoValue parse(Resources.Attribute a) {\n\t\tString format = XmlGenUtils.getAttrTypeAsString(a.getFormatFlags());\n\t\tList<ProtoValue> namedValues = new ArrayList<>(a.getSymbolCount());\n\t\tfor (int i = 0; i < a.getSymbolCount(); i++) {\n\t\t\tResources.Attribute.Symbol s = a.getSymbol(i);\n\t\t\tint type = s.getType();\n\t\t\tString name = s.getName().getName();\n\t\t\tString value = String.valueOf(s.getValue());\n\t\t\tnamedValues.add(new ProtoValue(value).setName(name).setType(type));\n\t\t}\n\t\treturn new ProtoValue(format).setNamedValues(namedValues);\n\t}\n\n\tprotected ProtoValue parse(Resources.Plural p) {\n\t\tList<ProtoValue> namedValues = new ArrayList<>(p.getEntryCount());\n\t\tfor (int i = 0; i < p.getEntryCount(); i++) {\n\t\t\tResources.Plural.Entry e = p.getEntry(i);\n\t\t\tString name = e.getArity().name();\n\t\t\tString value = parse(e.getItem());\n\t\t\tnamedValues.add(new ProtoValue(value).setName(name));\n\t\t}\n\t\treturn new ProtoValue().setNamedValues(namedValues);\n\t}\n\n\tprotected ProtoValue parse(Resources.CompoundValue c) {\n\t\tswitch (c.getValueCase()) {\n\t\t\tcase STYLE:\n\t\t\t\treturn parse(c.getStyle());\n\t\t\tcase STYLEABLE:\n\t\t\t\treturn parse(c.getStyleable());\n\t\t\tcase ARRAY:\n\t\t\t\treturn parse(c.getArray());\n\t\t\tcase ATTR:\n\t\t\t\treturn parse(c.getAttr());\n\t\t\tcase PLURAL:\n\t\t\t\treturn parse(c.getPlural());\n\t\t\tdefault:\n\t\t\t\treturn new ProtoValue(\"Unresolved value\");\n\t\t}\n\t}\n\n\tprotected String parse(ConfigurationOuterClass.Configuration c) {\n\t\tchar[] language = c.getLocale().toCharArray();\n\t\tif (language.length == 0) {\n\t\t\tlanguage = new char[] { '\\00' };\n\t\t}\n\t\tshort mcc = (short) c.getMcc();\n\t\tshort mnc = (short) c.getMnc();\n\t\tbyte orientation = (byte) c.getOrientationValue();\n\t\tshort screenWidth = (short) c.getScreenWidth();\n\t\tshort screenHeight = (short) c.getScreenHeight();\n\t\tshort screenWidthDp = (short) c.getScreenWidthDp();\n\t\tshort screenHeightDp = (short) c.getScreenHeightDp();\n\t\tshort smallestScreenWidthDp = (short) c.getSmallestScreenWidthDp();\n\t\tshort sdkVersion = (short) c.getSdkVersion();\n\t\tbyte keyboard = (byte) c.getKeyboardValue();\n\t\tbyte touchscreen = (byte) c.getTouchscreenValue();\n\t\tint density = c.getDensity();\n\t\tbyte screenLayout = (byte) c.getScreenLayoutLongValue();\n\t\tbyte colorMode = (byte) (c.getHdrValue() | c.getWideColorGamutValue());\n\t\tbyte screenLayout2 = (byte) (c.getLayoutDirectionValue() | c.getScreenRoundValue());\n\t\tbyte navigation = (byte) c.getNavigationValue();\n\t\tbyte inputFlags = (byte) (c.getKeysHiddenValue() | c.getNavHiddenValue());\n\t\tbyte grammaticalInflection = (byte) c.getGrammaticalGenderValue();\n\t\tint size = c.getSerializedSize();\n\t\tbyte uiMode = (byte) (c.getUiModeNightValue() | c.getUiModeTypeValue());\n\n\t\tc.getScreenLayoutSize(); // unknown field\n\t\tc.getProduct(); // unknown field\n\n\t\treturn new EntryConfig(mcc, mnc, language, new char[] { '\\00' },\n\t\t\t\torientation, touchscreen, density, keyboard, navigation,\n\t\t\t\tinputFlags, grammaticalInflection, screenWidth, screenHeight, sdkVersion,\n\t\t\t\tscreenLayout, uiMode, smallestScreenWidthDp, screenWidthDp,\n\t\t\t\tscreenHeightDp, new char[] { '\\00' }, new char[] { '\\00' }, screenLayout2,\n\t\t\t\tcolorMode, false, size).getQualifiers();\n\t}\n\n\tprotected String parse(Resources.Item i) {\n\t\tif (i.hasRawStr()) {\n\t\t\treturn i.getRawStr().getValue();\n\t\t}\n\t\tif (i.hasStr()) {\n\t\t\treturn i.getStr().getValue();\n\t\t}\n\t\tif (i.hasStyledStr()) {\n\t\t\treturn i.getStyledStr().getValue();\n\t\t}\n\t\tif (i.hasPrim()) {\n\t\t\tResources.Primitive prim = i.getPrim();\n\t\t\tswitch (prim.getOneofValueCase()) {\n\t\t\t\tcase NULL_VALUE:\n\t\t\t\t\treturn null;\n\t\t\t\tcase INT_DECIMAL_VALUE:\n\t\t\t\t\treturn String.valueOf(prim.getIntDecimalValue());\n\t\t\t\tcase INT_HEXADECIMAL_VALUE:\n\t\t\t\t\treturn Integer.toHexString(prim.getIntHexadecimalValue());\n\t\t\t\tcase BOOLEAN_VALUE:\n\t\t\t\t\treturn String.valueOf(prim.getBooleanValue());\n\t\t\t\tcase FLOAT_VALUE:\n\t\t\t\t\treturn String.valueOf(prim.getFloatValue());\n\t\t\t\tcase COLOR_ARGB4_VALUE:\n\t\t\t\t\treturn String.format(\"#%04x\", prim.getColorArgb4Value());\n\t\t\t\tcase COLOR_ARGB8_VALUE:\n\t\t\t\t\treturn String.format(\"#%08x\", prim.getColorArgb8Value());\n\t\t\t\tcase COLOR_RGB4_VALUE:\n\t\t\t\t\treturn String.format(\"#%03x\", prim.getColorRgb4Value());\n\t\t\t\tcase COLOR_RGB8_VALUE:\n\t\t\t\t\treturn String.format(\"#%06x\", prim.getColorRgb8Value());\n\t\t\t\tcase DIMENSION_VALUE:\n\t\t\t\t\treturn XmlGenUtils.decodeComplex(prim.getDimensionValue(), false);\n\t\t\t\tcase FRACTION_VALUE:\n\t\t\t\t\treturn XmlGenUtils.decodeComplex(prim.getDimensionValue(), true);\n\t\t\t\tcase EMPTY_VALUE:\n\t\t\t\tdefault:\n\t\t\t\t\treturn \"\";\n\t\t\t}\n\t\t}\n\t\tif (i.hasRef()) {\n\t\t\tResources.Reference ref = i.getRef();\n\t\t\tString value = ref.getName();\n\t\t\tif (value.isEmpty()) {\n\t\t\t\tvalue = \"id/\" + ref.getId();\n\t\t\t}\n\t\t\treturn '@' + value;\n\t\t}\n\t\tif (i.hasFile()) {\n\t\t\treturn i.getFile().getPath();\n\t\t}\n\t\treturn \"\";\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/parsers/ResTableProtoParser.java",
    "content": "package jadx.plugins.input.aab.parsers;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.List;\n\nimport com.android.aapt.Resources.ConfigValue;\nimport com.android.aapt.Resources.Entry;\nimport com.android.aapt.Resources.Package;\nimport com.android.aapt.Resources.ResourceTable;\nimport com.android.aapt.Resources.Type;\nimport com.android.aapt.Resources.Value;\n\nimport jadx.api.ICodeInfo;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.core.xmlgen.BinaryXMLStrings;\nimport jadx.core.xmlgen.IResTableParser;\nimport jadx.core.xmlgen.ResContainer;\nimport jadx.core.xmlgen.ResXmlGen;\nimport jadx.core.xmlgen.ResourceStorage;\nimport jadx.core.xmlgen.XmlGenUtils;\nimport jadx.core.xmlgen.entry.ProtoValue;\nimport jadx.core.xmlgen.entry.ResourceEntry;\nimport jadx.core.xmlgen.entry.ValuesParser;\n\npublic class ResTableProtoParser extends CommonProtoParser implements IResTableParser {\n\tprivate final RootNode root;\n\tprivate ResourceStorage resStorage;\n\tprivate String baseFileName = \"\";\n\n\tpublic ResTableProtoParser(RootNode root) {\n\t\tthis.root = root;\n\t}\n\n\t@Override\n\tpublic void setBaseFileName(String fileName) {\n\t\tthis.baseFileName = fileName;\n\t}\n\n\t@Override\n\tpublic void decode(InputStream inputStream) throws IOException {\n\t\tresStorage = new ResourceStorage(root.getArgs().getSecurity());\n\t\tResourceTable table = ResourceTable.parseFrom(FileUtils.streamToByteArray(inputStream));\n\t\tfor (Package p : table.getPackageList()) {\n\t\t\tparse(p);\n\t\t}\n\t\tresStorage.finish();\n\t}\n\n\t@Override\n\tpublic synchronized ResContainer decodeFiles() {\n\t\tValuesParser vp = new ValuesParser(new BinaryXMLStrings(), resStorage.getResourcesNames());\n\t\tResXmlGen resGen = new ResXmlGen(resStorage, vp, root.initManifestAttributes());\n\t\tICodeInfo content = XmlGenUtils.makeXmlDump(root.makeCodeWriter(), resStorage);\n\t\tList<ResContainer> xmlFiles = resGen.makeResourcesXml(root.getArgs());\n\t\treturn ResContainer.resourceTable(baseFileName, xmlFiles, content);\n\t}\n\n\tprivate void parse(Package p) {\n\t\tString packageName = p.getPackageName();\n\t\tresStorage.setAppPackage(packageName);\n\t\tList<Type> types = p.getTypeList();\n\n\t\tfor (Type type : types) {\n\t\t\tString typeName = type.getName();\n\t\t\tfor (Entry entry : type.getEntryList()) {\n\t\t\t\tint id = p.getPackageId().getId() << 24 | type.getTypeId().getId() << 16 | entry.getEntryId().getId();\n\t\t\t\tString entryName = entry.getName();\n\t\t\t\tfor (ConfigValue configValue : entry.getConfigValueList()) {\n\t\t\t\t\tString config = parse(configValue.getConfig());\n\t\t\t\t\tResourceEntry resEntry = new ResourceEntry(id, packageName, typeName, entryName, config);\n\t\t\t\t\tresStorage.add(resEntry);\n\n\t\t\t\t\tProtoValue protoValue;\n\t\t\t\t\tif (configValue.getValue().getValueCase() == Value.ValueCase.ITEM) {\n\t\t\t\t\t\tprotoValue = new ProtoValue(parse(configValue.getValue().getItem()));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tprotoValue = parse(configValue.getValue().getCompoundValue());\n\t\t\t\t\t}\n\t\t\t\t\tresEntry.setProtoValue(protoValue);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic ResourceStorage getResStorage() {\n\t\treturn resStorage;\n\t}\n\n\t@Override\n\tpublic BinaryXMLStrings getStrings() {\n\t\treturn new BinaryXMLStrings();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/parsers/ResXmlProtoParser.java",
    "content": "package jadx.plugins.input.aab.parsers;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.Set;\n\nimport com.android.aapt.Resources.XmlAttribute;\nimport com.android.aapt.Resources.XmlElement;\nimport com.android.aapt.Resources.XmlNamespace;\nimport com.android.aapt.Resources.XmlNode;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.ICodeWriter;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.android.AndroidResourcesMap;\nimport jadx.core.xmlgen.XMLChar;\nimport jadx.core.xmlgen.XmlDeobf;\nimport jadx.core.xmlgen.XmlGenUtils;\n\npublic class ResXmlProtoParser extends CommonProtoParser {\n\tprivate Map<String, String> nsMap;\n\tprivate final Map<String, String> tagAttrDeobfNames = new HashMap<>();\n\n\tprivate ICodeWriter writer;\n\n\tprivate final RootNode rootNode;\n\tprivate String currentTag;\n\tprivate String appPackageName;\n\tprivate final boolean isPrettyPrint;\n\n\tpublic ResXmlProtoParser(RootNode rootNode) {\n\t\tthis.rootNode = rootNode;\n\t\tthis.isPrettyPrint = !rootNode.getArgs().isSkipXmlPrettyPrint();\n\t}\n\n\tpublic synchronized ICodeInfo parse(InputStream inputStream) throws IOException {\n\t\tnsMap = new HashMap<>();\n\t\twriter = rootNode.makeCodeWriter();\n\t\twriter.add(\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\");\n\t\tdecode(decodeProto(inputStream));\n\t\tnsMap = null;\n\t\treturn writer.finish();\n\t}\n\n\tprivate void decode(XmlNode n) throws IOException {\n\t\tif (n.hasSource()) {\n\t\t\twriter.attachSourceLine(n.getSource().getLineNumber());\n\t\t}\n\t\twriter.add(StringUtils.escapeXML(n.getText().trim()));\n\t\tif (n.hasElement()) {\n\t\t\tdecode(n.getElement());\n\t\t}\n\t}\n\n\tprivate void decode(XmlElement e) throws IOException {\n\t\tString tag = deobfClassName(e.getName());\n\t\ttag = getValidTagAttributeName(tag);\n\t\tcurrentTag = tag;\n\t\twriter.startLine('<').add(tag);\n\n\t\tdecodeNamespaces(e);\n\t\tdecodeAttributes(e);\n\n\t\tif (e.getChildCount() > 0) {\n\t\t\twriter.add('>');\n\t\t\twriter.incIndent();\n\t\t\tfor (int i = 0; i < e.getChildCount(); i++) {\n\t\t\t\tMap<String, String> oldNsMap = new HashMap<>(nsMap);\n\t\t\t\tdecode(e.getChild(i));\n\t\t\t\tnsMap = oldNsMap;\n\t\t\t}\n\t\t\twriter.decIndent();\n\t\t\twriter.startLine(\"</\").add(tag).add('>');\n\t\t} else {\n\t\t\twriter.add(\" />\");\n\t\t}\n\t}\n\n\tprivate void decodeNamespaces(XmlElement e) {\n\t\tint nsCount = e.getNamespaceDeclarationCount();\n\t\tboolean newLine = nsCount != 1 && isPrettyPrint;\n\t\tif (nsCount > 0) {\n\t\t\twriter.add(' ');\n\t\t}\n\t\tfor (int i = 0; i < nsCount; i++) {\n\t\t\tdecodeNamespace(e.getNamespaceDeclaration(i), newLine, i == nsCount - 1);\n\t\t}\n\t}\n\n\tprivate void decodeNamespace(XmlNamespace n, boolean newLine, boolean isLastElement) {\n\t\tString prefix = n.getPrefix();\n\t\tString uri = n.getUri();\n\t\tnsMap.put(uri, prefix);\n\t\twriter.add(\"xmlns:\").add(prefix).add(\"=\\\"\").add(uri).add('\"');\n\t\tif (isLastElement) {\n\t\t\treturn;\n\t\t}\n\t\tif (newLine) {\n\t\t\twriter.startLine().addIndent();\n\t\t} else {\n\t\t\twriter.add(' ');\n\t\t}\n\t}\n\n\tprivate void decodeAttributes(XmlElement e) {\n\t\tint attrsCount = e.getAttributeCount();\n\t\tboolean newLine = attrsCount != 1 && isPrettyPrint;\n\t\tif (attrsCount > 0) {\n\t\t\twriter.add(' ');\n\t\t\tif (isPrettyPrint) {\n\t\t\t\twriter.startLine().addIndent();\n\t\t\t}\n\t\t}\n\t\tSet<String> attrCache = new HashSet<>();\n\t\tfor (int i = 0; i < attrsCount; i++) {\n\t\t\tdecodeAttribute(e.getAttribute(i), attrCache, newLine, i == attrsCount - 1);\n\t\t}\n\t}\n\n\tprivate void decodeAttribute(XmlAttribute a, Set<String> attrCache, boolean newLine, boolean isLastElement) {\n\t\tString name = getAttributeFullName(a);\n\t\tif (XmlDeobf.isDuplicatedAttr(name, attrCache)) {\n\t\t\treturn;\n\t\t}\n\t\tString value = deobfClassName(getAttributeValue(a));\n\t\twriter.add(name).add(\"=\\\"\").add(StringUtils.escapeXML(value)).add('\\\"');\n\t\tmemorizePackageName(name, value);\n\t\tif (isLastElement) {\n\t\t\treturn;\n\t\t}\n\t\tif (newLine) {\n\t\t\twriter.startLine().addIndent();\n\t\t} else {\n\t\t\twriter.add(' ');\n\t\t}\n\t}\n\n\tprivate String getAttributeFullName(XmlAttribute a) {\n\t\tString namespaceUri = a.getNamespaceUri();\n\t\tString namespace = null;\n\t\tif (!namespaceUri.isEmpty()) {\n\t\t\tnamespace = nsMap.get(namespaceUri);\n\t\t}\n\n\t\tString attrName = a.getName();\n\t\tif (attrName.isEmpty()) {\n\t\t\t// some optimization tools clear the name because the Android platform doesn't need it\n\t\t\tint resId = a.getResourceId();\n\t\t\tString str = AndroidResourcesMap.getResName(resId);\n\t\t\tif (str != null) {\n\t\t\t\tnamespace = nsMap.get(ANDROID_NS_URL);\n\t\t\t\t// cut type before /\n\t\t\t\tint typeEnd = str.indexOf('/');\n\t\t\t\tif (typeEnd != -1) {\n\t\t\t\t\tattrName = str.substring(typeEnd + 1);\n\t\t\t\t} else {\n\t\t\t\t\tattrName = str;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tattrName = \"_unknown_\";\n\t\t\t}\n\t\t}\n\n\t\treturn namespace != null ? namespace + \":\" + attrName : attrName;\n\t}\n\n\tprivate String getAttributeValue(XmlAttribute a) {\n\t\tif (!a.getValue().isEmpty()) {\n\t\t\treturn a.getValue();\n\t\t}\n\t\treturn parse(a.getCompiledItem());\n\t}\n\n\tprivate void memorizePackageName(String attrName, String attrValue) {\n\t\tif (\"manifest\".equals(currentTag) && \"package\".equals(attrName)) {\n\t\t\tappPackageName = attrValue;\n\t\t}\n\t}\n\n\tprivate String deobfClassName(String className) {\n\t\tString newName = XmlDeobf.deobfClassName(rootNode, className, appPackageName);\n\t\tif (newName != null) {\n\t\t\treturn newName;\n\t\t}\n\t\treturn className;\n\t}\n\n\tprivate String getValidTagAttributeName(String originalName) {\n\t\tif (XMLChar.isValidName(originalName)) {\n\t\t\treturn originalName;\n\t\t}\n\t\tif (tagAttrDeobfNames.containsKey(originalName)) {\n\t\t\treturn tagAttrDeobfNames.get(originalName);\n\t\t}\n\t\tString generated;\n\t\tdo {\n\t\t\tgenerated = generateTagAttrName();\n\t\t} while (tagAttrDeobfNames.containsValue(generated));\n\t\ttagAttrDeobfNames.put(originalName, generated);\n\t\treturn generated;\n\t}\n\n\tprivate static String generateTagAttrName() {\n\t\tfinal int length = 6;\n\t\tRandom r = new Random();\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (int i = 1; i <= length; i++) {\n\t\t\tsb.append((char) (r.nextInt(26) + 'a'));\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tprivate XmlNode decodeProto(InputStream inputStream) throws IOException {\n\t\treturn XmlNode.parseFrom(XmlGenUtils.readData(inputStream));\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-aab-input/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin",
    "content": "jadx.plugins.input.aab.AabInputPlugin\n"
  },
  {
    "path": "jadx-plugins/jadx-apkm-input/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-library\")\n\tid(\"jadx-kotlin\")\n}\n\ndependencies {\n\tapi(project(\":jadx-core\"))\n\n\timplementation(project(\":jadx-plugins:jadx-dex-input\"))\n\timplementation(\"com.google.code.gson:gson:2.13.2\")\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-apkm-input/src/main/java/jadx/plugins/input/apkm/ApkmCustomCodeInput.kt",
    "content": "package jadx.plugins.input.apkm\n\nimport jadx.api.plugins.input.ICodeLoader\nimport jadx.api.plugins.input.JadxCodeInput\nimport jadx.api.plugins.utils.CommonFileUtils\nimport jadx.plugins.input.dex.DexInputPlugin\nimport jadx.zip.ZipReader\nimport java.io.File\nimport java.nio.file.Path\n\nclass ApkmCustomCodeInput(\n\tprivate val dexInputPlugin: DexInputPlugin,\n\tprivate val zipReader: ZipReader,\n) : JadxCodeInput {\n\n\toverride fun loadFiles(input: List<Path>): ICodeLoader {\n\t\tval apkFiles = mutableListOf<File>()\n\t\tfor (file in input.map { it.toFile() }) {\n\t\t\tif (!file.name.endsWith(\".apkm\")) continue\n\n\t\t\t// Check if this is a valid APKM file\n\t\t\tval manifest = ApkmUtils.getManifest(file, zipReader) ?: continue\n\t\t\tif (!ApkmUtils.isSupported(manifest)) continue\n\n\t\t\t// Load all files ending with .apk\n\t\t\tzipReader.visitEntries<Any>(file) { entry ->\n\t\t\t\tif (entry.name.endsWith(\".apk\")) {\n\t\t\t\t\tval tmpFile = entry.inputStream.use {\n\t\t\t\t\t\tCommonFileUtils.saveToTempFile(it, \".apk\").toFile()\n\t\t\t\t\t}\n\t\t\t\t\tapkFiles.add(tmpFile)\n\t\t\t\t}\n\t\t\t\tnull\n\t\t\t}\n\t\t}\n\n\t\tval codeLoader = dexInputPlugin.loadFiles(apkFiles.map { it.toPath() })\n\n\t\tapkFiles.forEach { CommonFileUtils.safeDeleteFile(it) }\n\n\t\treturn codeLoader\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-apkm-input/src/main/java/jadx/plugins/input/apkm/ApkmCustomResourcesLoader.kt",
    "content": "package jadx.plugins.input.apkm\n\nimport jadx.api.ResourceFile\nimport jadx.api.ResourcesLoader\nimport jadx.api.plugins.CustomResourcesLoader\nimport jadx.api.plugins.utils.CommonFileUtils\nimport jadx.zip.ZipReader\nimport java.io.File\n\nclass ApkmCustomResourcesLoader(\n\tprivate val zipReader: ZipReader,\n) : CustomResourcesLoader {\n\tprivate val tmpFiles = mutableListOf<File>()\n\n\toverride fun load(loader: ResourcesLoader, list: MutableList<ResourceFile>, file: File): Boolean {\n\t\tif (!file.name.endsWith(\".apkm\")) return false\n\n\t\t// Check if this is a valid APKM file\n\t\tval manifest = ApkmUtils.getManifest(file, zipReader) ?: return false\n\t\tif (!ApkmUtils.isSupported(manifest)) return false\n\n\t\t// Load all files ending with .apk\n\t\tzipReader.visitEntries<Any>(file) { entry ->\n\t\t\tif (entry.name.endsWith(\".apk\")) {\n\t\t\t\tval tmpFile = entry.inputStream.use {\n\t\t\t\t\tCommonFileUtils.saveToTempFile(it, \".apk\").toFile()\n\t\t\t\t}\n\t\t\t\tloader.defaultLoadFile(list, tmpFile, entry.name + \"/\")\n\t\t\t\ttmpFiles += tmpFile\n\t\t\t}\n\t\t\tnull\n\t\t}\n\t\treturn true\n\t}\n\n\toverride fun close() {\n\t\ttmpFiles.forEach(CommonFileUtils::safeDeleteFile)\n\t\ttmpFiles.clear()\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-apkm-input/src/main/java/jadx/plugins/input/apkm/ApkmInputPlugin.kt",
    "content": "package jadx.plugins.input.apkm\n\nimport jadx.api.plugins.JadxPlugin\nimport jadx.api.plugins.JadxPluginContext\nimport jadx.api.plugins.JadxPluginInfo\nimport jadx.plugins.input.dex.DexInputPlugin\n\nclass ApkmInputPlugin : JadxPlugin {\n\n\toverride fun getPluginInfo() = JadxPluginInfo(\n\t\t\"apkm-input\",\n\t\t\"APKM Input\",\n\t\t\"Load .apkm files\",\n\t)\n\n\toverride fun init(context: JadxPluginContext) {\n\t\tval dexInputPlugin = context.plugins().getInstance(DexInputPlugin::class.java)\n\t\tcontext.addCodeInput(ApkmCustomCodeInput(dexInputPlugin, context.zipReader))\n\t\tcontext.decompiler.addCustomResourcesLoader(ApkmCustomResourcesLoader(context.zipReader))\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-apkm-input/src/main/java/jadx/plugins/input/apkm/ApkmManifest.kt",
    "content": "package jadx.plugins.input.apkm\n\nimport com.google.gson.annotations.SerializedName\n\ndata class ApkmManifest(\n\t@SerializedName(\"apkm_version\")\n\tvar apkmVersion: Int = -1,\n)\n"
  },
  {
    "path": "jadx-plugins/jadx-apkm-input/src/main/java/jadx/plugins/input/apkm/ApkmUtils.kt",
    "content": "package jadx.plugins.input.apkm\n\nimport jadx.core.utils.GsonUtils.buildGson\nimport jadx.core.utils.files.FileUtils\nimport jadx.zip.ZipReader\nimport java.io.File\nimport java.io.InputStreamReader\n\nobject ApkmUtils {\n\tfun getManifest(file: File, zipReader: ZipReader): ApkmManifest? {\n\t\tif (!FileUtils.isZipFile(file)) return null\n\t\ttry {\n\t\t\tzipReader.open(file).use { zip ->\n\t\t\t\tval manifestEntry = zip.searchEntry(\"info.json\") ?: return null\n\t\t\t\treturn InputStreamReader(manifestEntry.inputStream).use {\n\t\t\t\t\tbuildGson().fromJson(it, ApkmManifest::class.java)\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (e: Exception) {\n\t\t\treturn null\n\t\t}\n\t}\n\n\tfun isSupported(manifest: ApkmManifest): Boolean {\n\t\treturn manifest.apkmVersion != -1\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-apkm-input/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin",
    "content": "jadx.plugins.input.apkm.ApkmInputPlugin\n"
  },
  {
    "path": "jadx-plugins/jadx-apks-input/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-library\")\n\tid(\"jadx-kotlin\")\n}\n\ndependencies {\n\tapi(project(\":jadx-core\"))\n\n\timplementation(project(\":jadx-plugins:jadx-dex-input\"))\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-apks-input/src/main/java/jadx/plugins/input/apks/ApksCustomCodeInput.kt",
    "content": "package jadx.plugins.input.apks\n\nimport jadx.api.plugins.input.ICodeLoader\nimport jadx.api.plugins.input.JadxCodeInput\nimport jadx.api.plugins.utils.CommonFileUtils\nimport jadx.plugins.input.dex.DexInputPlugin\nimport jadx.zip.ZipReader\nimport java.io.File\nimport java.nio.file.Path\n\nclass ApksCustomCodeInput(\n\tprivate val dexInputPlugin: DexInputPlugin,\n\tprivate val zipReader: ZipReader,\n) : JadxCodeInput {\n\toverride fun loadFiles(input: List<Path>): ICodeLoader {\n\t\tval apkFiles = mutableListOf<File>()\n\t\tfor (file in input.map { it.toFile() }) {\n\t\t\tif (!file.name.endsWith(\".apks\")) continue\n\n\t\t\t// Load all files ending with .apk\n\t\t\tzipReader.visitEntries<Any>(file) { entry ->\n\t\t\t\tif (entry.name.endsWith(\".apk\")) {\n\t\t\t\t\tval tmpFile = entry.inputStream.use {\n\t\t\t\t\t\tCommonFileUtils.saveToTempFile(it, \".apk\").toFile()\n\t\t\t\t\t}\n\t\t\t\t\tapkFiles.add(tmpFile)\n\t\t\t\t}\n\t\t\t\tnull\n\t\t\t}\n\t\t}\n\n\t\tval codeLoader = dexInputPlugin.loadFiles(apkFiles.map { it.toPath() })\n\n\t\tapkFiles.forEach { CommonFileUtils.safeDeleteFile(it) }\n\n\t\treturn codeLoader\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-apks-input/src/main/java/jadx/plugins/input/apks/ApksCustomResourcesLoader.kt",
    "content": "package jadx.plugins.input.apks\n\nimport jadx.api.ResourceFile\nimport jadx.api.ResourcesLoader\nimport jadx.api.plugins.CustomResourcesLoader\nimport jadx.api.plugins.utils.CommonFileUtils\nimport jadx.zip.ZipReader\nimport java.io.File\n\nclass ApksCustomResourcesLoader(\n\tprivate val zipReader: ZipReader,\n) : CustomResourcesLoader {\n\tprivate val tmpFiles = mutableListOf<File>()\n\n\toverride fun load(loader: ResourcesLoader, list: MutableList<ResourceFile>, file: File): Boolean {\n\t\tif (!file.name.endsWith(\".apks\")) return false\n\n\t\t// Load all files ending with .apk\n\t\tzipReader.visitEntries<Any>(file) { entry ->\n\t\t\tif (entry.name.endsWith(\".apk\")) {\n\t\t\t\tval tmpFile = entry.inputStream.use {\n\t\t\t\t\tCommonFileUtils.saveToTempFile(it, \".apk\").toFile()\n\t\t\t\t}\n\t\t\t\tloader.defaultLoadFile(list, tmpFile, entry.name + \"/\")\n\t\t\t\ttmpFiles += tmpFile\n\t\t\t}\n\t\t\tnull\n\t\t}\n\t\treturn true\n\t}\n\n\toverride fun close() {\n\t\ttmpFiles.forEach(CommonFileUtils::safeDeleteFile)\n\t\ttmpFiles.clear()\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-apks-input/src/main/java/jadx/plugins/input/apks/ApksInputPlugin.kt",
    "content": "package jadx.plugins.input.apks\n\nimport jadx.api.plugins.JadxPlugin\nimport jadx.api.plugins.JadxPluginContext\nimport jadx.api.plugins.JadxPluginInfo\nimport jadx.plugins.input.dex.DexInputPlugin\n\nclass ApksInputPlugin : JadxPlugin {\n\toverride fun getPluginInfo() = JadxPluginInfo(\n\t\t\"apks-input\",\n\t\t\"APKS Input\",\n\t\t\"Load .apks files\",\n\t)\n\n\toverride fun init(context: JadxPluginContext) {\n\t\tval dexInputPlugin = context.plugins().getInstance(DexInputPlugin::class.java)\n\t\tcontext.addCodeInput(ApksCustomCodeInput(dexInputPlugin, context.zipReader))\n\t\tcontext.decompiler.addCustomResourcesLoader(ApksCustomResourcesLoader(context.zipReader))\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-apks-input/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin",
    "content": "jadx.plugins.input.apks.ApksInputPlugin\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-library\")\n}\n\ndependencies {\n\tapi(project(\":jadx-core\"))\n\n\t// TODO: finish own smali printer\n\timplementation(\"com.android.tools.smali:smali-baksmali:3.0.9\") {\n\t\texclude(group = \"com.beust\", module = \"jcommander\") // exclude old jcommander namespace\n\t}\n\timplementation(\"com.google.guava:guava:33.5.0-jre\") // force the latest version for smali\n\n\t// compile smali files in tests\n\ttestImplementation(\"com.android.tools.smali:smali:3.0.9\") {\n\t\texclude(group = \"com.beust\", module = \"jcommander\") // exclude old jcommander namespace\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexException.java",
    "content": "package jadx.plugins.input.dex;\n\npublic class DexException extends RuntimeException {\n\tprivate static final long serialVersionUID = -5575702801815409269L;\n\n\tpublic DexException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n\tpublic DexException(String message) {\n\t\tsuper(message);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexFileLoader.java",
    "content": "package jadx.plugins.input.dex;\n\nimport java.io.BufferedInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.utils.CommonFileUtils;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.plugins.input.dex.sections.DexConsts;\nimport jadx.plugins.input.dex.sections.DexHeaderV41;\nimport jadx.plugins.input.dex.utils.DexCheckSum;\nimport jadx.zip.IZipEntry;\nimport jadx.zip.ZipContent;\nimport jadx.zip.ZipReader;\n\npublic class DexFileLoader {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DexFileLoader.class);\n\n\t// sharing between all instances (can be used in other plugins) // TODO:\n\tprivate static int dexUniqId = 1;\n\n\tprivate final DexInputOptions options;\n\n\tprivate ZipReader zipReader = new ZipReader();\n\n\tpublic DexFileLoader(DexInputOptions options) {\n\t\tthis.options = options;\n\t}\n\n\tpublic void setZipReader(ZipReader zipReader) {\n\t\tthis.zipReader = zipReader;\n\t}\n\n\tpublic List<DexReader> collectDexFiles(List<Path> pathsList) {\n\t\treturn pathsList.stream()\n\t\t\t\t.map(Path::toFile)\n\t\t\t\t.map(this::loadDexFromFile)\n\t\t\t\t.filter(list -> !list.isEmpty())\n\t\t\t\t.flatMap(Collection::stream)\n\t\t\t\t.peek(dr -> LOG.debug(\"Loading dex: {}\", dr))\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tprivate List<DexReader> loadDexFromFile(File file) {\n\t\ttry (InputStream inputStream = new FileInputStream(file)) {\n\t\t\treturn load(file, inputStream, file.getAbsolutePath());\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"File open error: {}\", file.getAbsolutePath(), e);\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t}\n\n\tprivate List<DexReader> load(@Nullable File file, InputStream inputStream, String fileName) throws IOException {\n\t\ttry (InputStream in = inputStream.markSupported() ? inputStream : new BufferedInputStream(inputStream)) {\n\t\t\tbyte[] magic = new byte[DexConsts.MAX_MAGIC_SIZE];\n\t\t\tin.mark(magic.length);\n\t\t\tif (in.read(magic) != magic.length) {\n\t\t\t\treturn Collections.emptyList();\n\t\t\t}\n\t\t\tif (isStartWithBytes(magic, DexConsts.DEX_FILE_MAGIC)) {\n\t\t\t\tin.reset();\n\t\t\t\tbyte[] content = readAllBytes(in);\n\t\t\t\treturn loadDexReaders(fileName, content);\n\t\t\t}\n\t\t\tif (fileName.endsWith(\".dex\")) {\n\t\t\t\t// report invalid magic in '.dex' file\n\t\t\t\tString hex = FileUtils.bytesToHex(magic);\n\t\t\t\tString str = new String(magic, StandardCharsets.US_ASCII);\n\t\t\t\tLOG.warn(\"Invalid DEX magic: 0x{}(\\\"{}\\\") in file: {}\", hex, str, fileName);\n\t\t\t}\n\t\t\tif (file != null) {\n\t\t\t\t// allow only top level zip files\n\t\t\t\tif (isStartWithBytes(magic, DexConsts.ZIP_FILE_MAGIC) || CommonFileUtils.isZipFileExt(fileName)) {\n\t\t\t\t\treturn collectDexFromZip(file);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t}\n\n\tprivate List<DexReader> loadFromZipEntry(byte[] content, String fileName) {\n\t\tif (isStartWithBytes(content, DexConsts.DEX_FILE_MAGIC) || fileName.endsWith(\".dex\")) {\n\t\t\treturn loadDexReaders(fileName, content);\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\tpublic List<DexReader> loadDexReaders(String fileName, byte[] content) {\n\t\tDexHeaderV41 dexHeaderV41 = DexHeaderV41.readIfPresent(content);\n\t\tif (dexHeaderV41 != null) {\n\t\t\treturn DexHeaderV41.readSubDexOffsets(content, dexHeaderV41)\n\t\t\t\t\t.stream()\n\t\t\t\t\t.map(offset -> loadSingleDex(fileName, content, offset))\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t}\n\t\tDexReader dexReader = loadSingleDex(fileName, content, 0);\n\t\treturn Collections.singletonList(dexReader);\n\t}\n\n\tprivate DexReader loadSingleDex(String fileName, byte[] content, int offset) {\n\t\tif (options.isVerifyChecksum()) {\n\t\t\tDexCheckSum.verify(fileName, content, offset);\n\t\t}\n\t\treturn new DexReader(getNextUniqId(), fileName, content, offset);\n\t}\n\n\t/**\n\t * Since DEX v41, several sub DEX structures can be stored inside container of a single DEX file\n\t * Use {@link DexFileLoader#loadDexReaders(String, byte[])} instead.\n\t */\n\t@Deprecated\n\tpublic DexReader loadDexReader(String fileName, byte[] content) {\n\t\treturn loadSingleDex(fileName, content, 0);\n\t}\n\n\tprivate List<DexReader> collectDexFromZip(File file) {\n\t\tList<DexReader> result = new ArrayList<>();\n\t\ttry (ZipContent zip = zipReader.open(file)) {\n\t\t\tfor (IZipEntry entry : zip.getEntries()) {\n\t\t\t\tif (entry.isDirectory()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tList<DexReader> readers;\n\t\t\t\t\tif (entry.preferBytes()) {\n\t\t\t\t\t\treaders = loadFromZipEntry(entry.getBytes(), entry.getName());\n\t\t\t\t\t} else {\n\t\t\t\t\t\treaders = load(null, entry.getInputStream(), entry.getName());\n\t\t\t\t\t}\n\t\t\t\t\tif (!readers.isEmpty()) {\n\t\t\t\t\t\tresult.addAll(readers);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.error(\"Failed to read zip entry: {}\", entry, e);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to process zip file: {}\", file.getAbsolutePath(), e);\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate static boolean isStartWithBytes(byte[] fileMagic, byte[] expectedBytes) {\n\t\tint len = expectedBytes.length;\n\t\tif (fileMagic.length < len) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tif (fileMagic[i] != expectedBytes[i]) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static byte[] readAllBytes(InputStream in) throws IOException {\n\t\tByteArrayOutputStream buf = new ByteArrayOutputStream();\n\t\tbyte[] data = new byte[8192];\n\t\twhile (true) {\n\t\t\tint read = in.read(data);\n\t\t\tif (read == -1) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbuf.write(data, 0, read);\n\t\t}\n\t\treturn buf.toByteArray();\n\t}\n\n\tprivate static synchronized int getNextUniqId() {\n\t\tdexUniqId++;\n\t\tif (dexUniqId >= 0xFFFF) {\n\t\t\tdexUniqId = 1;\n\t\t}\n\t\treturn dexUniqId;\n\t}\n\n\tprivate static synchronized void resetDexUniqId() {\n\t\tdexUniqId = 1;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexInputOptions.java",
    "content": "package jadx.plugins.input.dex;\n\nimport jadx.api.plugins.options.impl.BasePluginOptionsBuilder;\n\npublic class DexInputOptions extends BasePluginOptionsBuilder {\n\n\tprivate boolean verifyChecksum;\n\n\t@Override\n\tpublic void registerOptions() {\n\t\tboolOption(DexInputPlugin.PLUGIN_ID + \".verify-checksum\")\n\t\t\t\t.description(\"verify dex file checksum before load\")\n\t\t\t\t.defaultValue(true)\n\t\t\t\t.setter(v -> verifyChecksum = v);\n\t}\n\n\tpublic boolean isVerifyChecksum() {\n\t\treturn verifyChecksum;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexInputPlugin.java",
    "content": "package jadx.plugins.input.dex;\n\nimport java.io.Closeable;\nimport java.io.InputStream;\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.JadxPlugin;\nimport jadx.api.plugins.JadxPluginContext;\nimport jadx.api.plugins.JadxPluginInfo;\nimport jadx.api.plugins.input.ICodeLoader;\nimport jadx.api.plugins.input.data.impl.EmptyCodeLoader;\nimport jadx.api.plugins.utils.CommonFileUtils;\nimport jadx.plugins.input.dex.utils.IDexData;\n\npublic class DexInputPlugin implements JadxPlugin {\n\tpublic static final String PLUGIN_ID = \"dex-input\";\n\n\tprivate final DexInputOptions options = new DexInputOptions();\n\tprivate final DexFileLoader loader = new DexFileLoader(options);\n\n\t@Override\n\tpublic JadxPluginInfo getPluginInfo() {\n\t\treturn new JadxPluginInfo(PLUGIN_ID, \"Dex Input\", \"Load .dex and .apk files\");\n\t}\n\n\t@Override\n\tpublic void init(JadxPluginContext context) {\n\t\tcontext.registerOptions(options);\n\t\tcontext.addCodeInput(this::loadFiles);\n\t\tloader.setZipReader(context.getZipReader());\n\t}\n\n\tpublic ICodeLoader loadFiles(List<Path> input) {\n\t\treturn loadFiles(input, null);\n\t}\n\n\tpublic ICodeLoader loadFiles(List<Path> inputFiles, @Nullable Closeable closeable) {\n\t\tList<DexReader> dexReaders = loader.collectDexFiles(inputFiles);\n\t\tif (dexReaders.isEmpty()) {\n\t\t\treturn EmptyCodeLoader.INSTANCE;\n\t\t}\n\t\treturn new DexLoadResult(dexReaders, closeable);\n\t}\n\n\tpublic ICodeLoader loadDex(byte[] content, @Nullable String fileName) {\n\t\tString fileLabel = fileName == null ? \"input.dex\" : fileName;\n\t\tList<DexReader> dexReaders = loader.loadDexReaders(fileLabel, content);\n\t\treturn new DexLoadResult(dexReaders, null);\n\t}\n\n\tpublic ICodeLoader loadDexFromInputStream(InputStream in, @Nullable String fileLabel) {\n\t\ttry {\n\t\t\treturn loadDex(CommonFileUtils.loadBytes(in), fileLabel);\n\t\t} catch (Exception e) {\n\t\t\tthrow new DexException(\"Failed to read input stream\", e);\n\t\t}\n\t}\n\n\tpublic ICodeLoader loadDexData(List<IDexData> list) {\n\t\tList<DexReader> readers = list.stream()\n\t\t\t\t.flatMap(data -> loader.loadDexReaders(data.getFileName(), data.getContent()).stream())\n\t\t\t\t.collect(Collectors.toList());\n\t\treturn new DexLoadResult(readers, null);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexLoadResult.java",
    "content": "package jadx.plugins.input.dex;\n\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.ICodeLoader;\nimport jadx.api.plugins.input.data.IClassData;\n\npublic class DexLoadResult implements ICodeLoader {\n\tprivate final List<DexReader> dexReaders;\n\t@Nullable\n\tprivate final Closeable closeable;\n\n\tpublic DexLoadResult(List<DexReader> dexReaders, @Nullable Closeable closeable) {\n\t\tthis.dexReaders = dexReaders;\n\t\tthis.closeable = closeable;\n\t}\n\n\t@Override\n\tpublic void visitClasses(Consumer<IClassData> consumer) {\n\t\tfor (DexReader dexReader : dexReaders) {\n\t\t\tdexReader.visitClasses(consumer);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\tif (closeable != null) {\n\t\t\tcloseable.close();\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isEmpty() {\n\t\treturn dexReaders.isEmpty();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexReader.java",
    "content": "package jadx.plugins.input.dex;\n\nimport java.nio.ByteBuffer;\nimport java.util.function.Consumer;\n\nimport jadx.api.plugins.input.data.IClassData;\nimport jadx.plugins.input.dex.sections.DexClassData;\nimport jadx.plugins.input.dex.sections.DexHeader;\nimport jadx.plugins.input.dex.sections.SectionReader;\nimport jadx.plugins.input.dex.sections.annotations.AnnotationsParser;\n\npublic class DexReader {\n\tprivate final int uniqId;\n\tprivate final String inputFileName;\n\tprivate final ByteBuffer buf;\n\tprivate final DexHeader header;\n\n\tpublic DexReader(int uniqId, String inputFileName, byte[] content, int offset) {\n\t\tthis.uniqId = uniqId;\n\t\tthis.inputFileName = inputFileName;\n\t\tthis.buf = ByteBuffer.wrap(content);\n\t\tthis.header = new DexHeader(new SectionReader(this, offset));\n\t}\n\n\tpublic void visitClasses(Consumer<IClassData> consumer) {\n\t\tint count = header.getClassDefsSize();\n\t\tif (count == 0) {\n\t\t\treturn;\n\t\t}\n\t\tint classDefsOff = header.getClassDefsOff();\n\t\tSectionReader in = new SectionReader(this, classDefsOff);\n\t\tAnnotationsParser annotationsParser = new AnnotationsParser(in.copy(), in.copy());\n\t\tDexClassData classData = new DexClassData(in, annotationsParser);\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tconsumer.accept(classData);\n\t\t\tin.shiftOffset(DexClassData.SIZE);\n\t\t}\n\t}\n\n\tpublic ByteBuffer getBuf() {\n\t\treturn buf;\n\t}\n\n\tpublic DexHeader getHeader() {\n\t\treturn header;\n\t}\n\n\tpublic String getInputFileName() {\n\t\treturn inputFileName;\n\t}\n\n\tpublic int getUniqId() {\n\t\treturn uniqId;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn inputFileName;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexInsnData.java",
    "content": "package jadx.plugins.input.dex.insns;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.ICallSite;\nimport jadx.api.plugins.input.data.IFieldRef;\nimport jadx.api.plugins.input.data.IMethodHandle;\nimport jadx.api.plugins.input.data.IMethodProto;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.api.plugins.input.insns.InsnIndexType;\nimport jadx.api.plugins.input.insns.Opcode;\nimport jadx.api.plugins.input.insns.custom.ICustomPayload;\nimport jadx.plugins.input.dex.sections.DexCodeReader;\nimport jadx.plugins.input.dex.sections.SectionReader;\n\npublic class DexInsnData implements InsnData {\n\tprivate final DexCodeReader codeData;\n\tprivate final SectionReader externalReader;\n\tprivate final SectionReader secondExtReader;\n\n\tprivate DexInsnInfo insnInfo;\n\tprivate boolean decoded;\n\tprivate int opcodeUnit;\n\tprivate int length;\n\tprivate int insnStart;\n\n\tprivate int offset;\n\tprivate int[] argsReg = new int[5];\n\tprivate int regsCount;\n\tprivate long literal;\n\tprivate int target;\n\tprivate int index;\n\t@Nullable\n\tprivate ICustomPayload payload;\n\n\tpublic DexInsnData(DexCodeReader codeData, SectionReader externalReader) {\n\t\tthis.codeData = codeData;\n\t\tthis.externalReader = externalReader;\n\t\tthis.secondExtReader = externalReader.copy();\n\t}\n\n\t@Override\n\tpublic void decode() {\n\t\tif (insnInfo != null && !decoded) {\n\t\t\tcodeData.decode(this);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int getOffset() {\n\t\treturn offset;\n\t}\n\n\t@Override\n\tpublic int getFileOffset() {\n\t\treturn insnStart;\n\t}\n\n\t@Override\n\tpublic Opcode getOpcode() {\n\t\tDexInsnInfo info = this.insnInfo;\n\t\tif (info == null) {\n\t\t\treturn Opcode.UNKNOWN;\n\t\t}\n\t\treturn info.getApiOpcode();\n\t}\n\n\t@Override\n\tpublic String getOpcodeMnemonic() {\n\t\treturn DexInsnMnemonics.get(opcodeUnit);\n\t}\n\n\t@Override\n\tpublic byte[] getByteCode() {\n\t\treturn externalReader.getByteCode(insnStart, length * 2); // a unit is 2 bytes\n\t}\n\n\t@Override\n\tpublic int getRawOpcodeUnit() {\n\t\treturn opcodeUnit;\n\t}\n\n\t@Override\n\tpublic int getRegsCount() {\n\t\treturn regsCount;\n\t}\n\n\t@Override\n\tpublic int getReg(int argNum) {\n\t\treturn argsReg[argNum];\n\t}\n\n\t@Override\n\tpublic int getResultReg() {\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic long getLiteral() {\n\t\treturn literal;\n\t}\n\n\t@Override\n\tpublic int getTarget() {\n\t\treturn target;\n\t}\n\n\t@Override\n\tpublic int getIndex() {\n\t\treturn index;\n\t}\n\n\t@Override\n\tpublic InsnIndexType getIndexType() {\n\t\treturn insnInfo.getIndexType();\n\t}\n\n\t@Override\n\tpublic String getIndexAsString() {\n\t\treturn externalReader.getString(index);\n\t}\n\n\t@Override\n\tpublic String getIndexAsType() {\n\t\treturn externalReader.getType(index);\n\t}\n\n\t@Override\n\tpublic IFieldRef getIndexAsField() {\n\t\treturn externalReader.getFieldRef(index);\n\t}\n\n\t@Override\n\tpublic IMethodRef getIndexAsMethod() {\n\t\treturn externalReader.getMethodRef(index);\n\t}\n\n\t@Override\n\tpublic ICallSite getIndexAsCallSite() {\n\t\treturn externalReader.getCallSite(index, secondExtReader);\n\t}\n\n\t/**\n\t * Currently, protoIndex is either being stored at index or target, index for const-method-type,\n\t * target for invoke-polymorphic(/range)\n\t */\n\t@Override\n\tpublic IMethodProto getIndexAsProto(int protoIndex) {\n\t\treturn externalReader.getMethodProto(protoIndex);\n\t}\n\n\t@Override\n\tpublic IMethodHandle getIndexAsMethodHandle() {\n\t\treturn externalReader.getMethodHandle(index);\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic ICustomPayload getPayload() {\n\t\treturn payload;\n\t}\n\n\tpublic int[] getArgsReg() {\n\t\treturn argsReg;\n\t}\n\n\tpublic void setArgsReg(int[] argsReg) {\n\t\tthis.argsReg = argsReg;\n\t}\n\n\tpublic void setRegsCount(int regsCount) {\n\t\tthis.regsCount = regsCount;\n\t}\n\n\tpublic int getLength() {\n\t\treturn length;\n\t}\n\n\tpublic void setLength(int length) {\n\t\tthis.length = length;\n\t}\n\n\tpublic void setInsnStart(int start) {\n\t\tthis.insnStart = start;\n\t}\n\n\tpublic void setLiteral(long literal) {\n\t\tthis.literal = literal;\n\t}\n\n\tpublic void setTarget(int target) {\n\t\tthis.target = target;\n\t}\n\n\tpublic void setIndex(int index) {\n\t\tthis.index = index;\n\t}\n\n\tpublic boolean isDecoded() {\n\t\treturn decoded;\n\t}\n\n\tpublic void setDecoded(boolean decoded) {\n\t\tthis.decoded = decoded;\n\t}\n\n\tpublic void setOffset(int offset) {\n\t\tthis.offset = offset;\n\t}\n\n\tpublic DexInsnInfo getInsnInfo() {\n\t\treturn insnInfo;\n\t}\n\n\tpublic void setInsnInfo(DexInsnInfo insnInfo) {\n\t\tthis.insnInfo = insnInfo;\n\t}\n\n\tpublic DexCodeReader getCodeData() {\n\t\treturn codeData;\n\t}\n\n\tpublic int getOpcodeUnit() {\n\t\treturn opcodeUnit;\n\t}\n\n\tpublic void setOpcodeUnit(int opcodeUnit) {\n\t\tthis.opcodeUnit = opcodeUnit;\n\t}\n\n\tpublic void setPayload(ICustomPayload payload) {\n\t\tthis.payload = payload;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(String.format(\"0x%04X\", offset));\n\t\tsb.append(\": \").append(getOpcode());\n\t\tif (insnInfo == null) {\n\t\t\tsb.append(String.format(\"(0x%04X)\", opcodeUnit));\n\t\t} else {\n\t\t\tint regsCount = getRegsCount();\n\t\t\tif (isDecoded()) {\n\t\t\t\tsb.append(' ');\n\t\t\t\tfor (int i = 0; i < regsCount; i++) {\n\t\t\t\t\tif (i != 0) {\n\t\t\t\t\t\tsb.append(\", \");\n\t\t\t\t\t}\n\t\t\t\t\tsb.append(\"r\").append(argsReg[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexInsnFormat.java",
    "content": "package jadx.plugins.input.dex.insns;\n\nimport jadx.api.plugins.input.insns.custom.impl.SwitchPayload;\nimport jadx.plugins.input.dex.DexException;\nimport jadx.plugins.input.dex.insns.payloads.DexArrayPayload;\nimport jadx.plugins.input.dex.sections.SectionReader;\n\npublic abstract class DexInsnFormat {\n\tpublic static final DexInsnFormat FORMAT_10X = new DexInsnFormat(1, 0) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\t// no op\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_12X = new DexInsnFormat(1, 2) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = nibble2(opcodeUnit);\n\t\t\tregs[1] = nibble3(opcodeUnit);\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_11N = new DexInsnFormat(1, 1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = nibble2(opcodeUnit);\n\t\t\tinsn.setLiteral(signedNibble3(opcodeUnit));\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_11X = new DexInsnFormat(1, 1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = byte1(opcodeUnit);\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_10T = new DexInsnFormat(1, 0) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tinsn.setTarget(insn.getOffset() + signedByte1(opcodeUnit));\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_20T = new DexInsnFormat(2, 0) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tinsn.setTarget(insn.getOffset() + in.readShort());\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_20BC = new DexInsnFormat(2, 0) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tinsn.setLiteral(byte1(opcodeUnit));\n\t\t\tinsn.setIndex(in.readUShort());\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_22X = new DexInsnFormat(2, 2) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = byte1(opcodeUnit);\n\t\t\tregs[1] = in.readUShort();\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_21T = new DexInsnFormat(2, 1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = byte1(opcodeUnit);\n\t\t\tinsn.setTarget(insn.getOffset() + in.readShort());\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_21S = new DexInsnFormat(2, 1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = byte1(opcodeUnit);\n\t\t\tinsn.setLiteral(in.readShort());\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_21H = new DexInsnFormat(2, 1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = byte1(opcodeUnit);\n\n\t\t\tlong literal = in.readShort();\n\t\t\tliteral <<= byte0(opcodeUnit) == DexOpcodes.CONST_HIGH16 ? 16 : 48;\n\t\t\tinsn.setLiteral(literal);\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_21C = new DexInsnFormat(2, 1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = byte1(opcodeUnit);\n\t\t\tinsn.setIndex(in.readUShort());\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_23X = new DexInsnFormat(2, 3) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = byte1(opcodeUnit);\n\t\t\tint next = in.readUShort();\n\t\t\tregs[1] = byte0(next);\n\t\t\tregs[2] = byte1(next);\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_22B = new DexInsnFormat(2, 2) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = byte1(opcodeUnit);\n\t\t\tint next = in.readUShort();\n\t\t\tregs[1] = byte0(next);\n\t\t\tinsn.setLiteral(signedByte1(next));\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_22T = new DexInsnFormat(2, 2) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = nibble2(opcodeUnit);\n\t\t\tregs[1] = nibble3(opcodeUnit);\n\t\t\tinsn.setTarget(insn.getOffset() + in.readShort());\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_22S = new DexInsnFormat(2, 2) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = nibble2(opcodeUnit);\n\t\t\tregs[1] = nibble3(opcodeUnit);\n\t\t\tinsn.setLiteral(in.readShort());\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_22C = new DexInsnFormat(2, 2) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = nibble2(opcodeUnit);\n\t\t\tregs[1] = nibble3(opcodeUnit);\n\t\t\tinsn.setIndex(in.readUShort());\n\t\t\tinsn.setLiteral(0L);\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_22CS = FORMAT_22C;\n\n\tpublic static final DexInsnFormat FORMAT_30T = new DexInsnFormat(3, 0) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tinsn.setTarget(insn.getOffset() + in.readInt());\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_32X = new DexInsnFormat(3, 2) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = in.readUShort();\n\t\t\tregs[1] = in.readUShort();\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_31I = new DexInsnFormat(3, 1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = byte1(opcodeUnit);\n\t\t\tinsn.setLiteral(in.readInt());\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_31T = new DexInsnFormat(3, 1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = byte1(opcodeUnit);\n\t\t\tinsn.setTarget(insn.getOffset() + in.readInt());\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_31C = new DexInsnFormat(3, 1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = byte1(opcodeUnit);\n\t\t\tinsn.setIndex(in.readInt());\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_35C = new DexInsnFormat(3, -1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\treadRegsList(insn, opcodeUnit, in);\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_35MS = FORMAT_35C;\n\tpublic static final DexInsnFormat FORMAT_35MI = FORMAT_35C;\n\n\tpublic static final DexInsnFormat FORMAT_3RC = new DexInsnFormat(3, -1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\treadRegsRange(insn, opcodeUnit, in);\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_3RMS = FORMAT_3RC;\n\tpublic static final DexInsnFormat FORMAT_3RMI = FORMAT_3RC;\n\n\tpublic static final DexInsnFormat FORMAT_45CC = new DexInsnFormat(4, -1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\treadRegsList(insn, opcodeUnit, in);\n\t\t\tinsn.setTarget(in.readUShort());\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_4RCC = new DexInsnFormat(4, -1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\treadRegsRange(insn, opcodeUnit, in);\n\t\t\tinsn.setTarget(in.readUShort());\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_51I = new DexInsnFormat(5, 1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint[] regs = insn.getArgsReg();\n\t\t\tregs[0] = byte1(opcodeUnit);\n\t\t\tinsn.setLiteral(in.readLong());\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_PACKED_SWITCH_PAYLOAD = new DexInsnFormat(-1, -1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint size = in.readUShort();\n\t\t\tint firstKey = in.readInt();\n\t\t\tint[] keys = new int[size];\n\t\t\tint[] targets = new int[size];\n\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\ttargets[i] = in.readInt();\n\t\t\t\tkeys[i] = firstKey + i;\n\t\t\t}\n\t\t\tinsn.setPayload(new SwitchPayload(size, keys, targets));\n\t\t\tinsn.setLength(size * 2 + 4);\n\t\t}\n\n\t\t@Override\n\t\tpublic void skip(DexInsnData insn, SectionReader in) {\n\t\t\tint size = in.readUShort();\n\t\t\tin.skip(4 + size * 4);\n\t\t\tinsn.setLength(size * 2 + 4);\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_SPARSE_SWITCH_PAYLOAD = new DexInsnFormat(-1, -1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint size = in.readUShort();\n\t\t\tint[] keys = new int[size];\n\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\tkeys[i] = in.readInt();\n\t\t\t}\n\t\t\tint[] targets = new int[size];\n\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\ttargets[i] = in.readInt();\n\t\t\t}\n\t\t\tinsn.setPayload(new SwitchPayload(size, keys, targets));\n\t\t\tinsn.setLength(size * 4 + 2);\n\t\t}\n\n\t\t@Override\n\t\tpublic void skip(DexInsnData insn, SectionReader in) {\n\t\t\tint size = in.readUShort();\n\t\t\tin.skip(size * 8);\n\t\t\tinsn.setLength(size * 4 + 2);\n\t\t}\n\t};\n\n\tpublic static final DexInsnFormat FORMAT_FILL_ARRAY_DATA_PAYLOAD = new DexInsnFormat(-1, -1) {\n\t\t@Override\n\t\tpublic void decode(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\t\tint elemSize = in.readUShort();\n\t\t\tint size = in.readInt();\n\t\t\tObject data;\n\t\t\tswitch (elemSize) {\n\t\t\t\tcase 1: {\n\t\t\t\t\tdata = in.readByteArray(size);\n\t\t\t\t\tif (size % 2 != 0) {\n\t\t\t\t\t\tin.readUByte();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 2: {\n\t\t\t\t\tshort[] array = new short[size];\n\t\t\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\t\t\tarray[i] = (short) in.readShort();\n\t\t\t\t\t}\n\t\t\t\t\tdata = array;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 4: {\n\t\t\t\t\tint[] array = new int[size];\n\t\t\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\t\t\tarray[i] = in.readInt();\n\t\t\t\t\t}\n\t\t\t\t\tdata = array;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 8: {\n\t\t\t\t\tlong[] array = new long[size];\n\t\t\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\t\t\tarray[i] = in.readLong();\n\t\t\t\t\t}\n\t\t\t\t\tdata = array;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 0: {\n\t\t\t\t\tdata = new byte[0];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new DexException(\"Unexpected element size in FILL_ARRAY_DATA_PAYLOAD: \" + elemSize);\n\t\t\t}\n\t\t\tinsn.setLength((size * elemSize + 1) / 2 + 4);\n\t\t\tinsn.setPayload(new DexArrayPayload(size, elemSize, data));\n\t\t}\n\n\t\t@Override\n\t\tpublic void skip(DexInsnData insn, SectionReader in) {\n\t\t\tint elemSize = in.readUShort();\n\t\t\tint size = in.readInt();\n\t\t\tif (elemSize == 1) {\n\t\t\t\tin.skip(size + size % 2);\n\t\t\t} else {\n\t\t\t\tin.skip(size * elemSize);\n\t\t\t}\n\t\t\tinsn.setLength((size * elemSize + 1) / 2 + 4);\n\t\t}\n\t};\n\n\tprotected void readRegsList(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\tint regsCount1 = nibble3(opcodeUnit);\n\t\tint index = in.readUShort();\n\t\tint rs = in.readUShort();\n\n\t\tint[] regs = insn.getArgsReg();\n\t\tregs[0] = nibble0(rs);\n\t\tregs[1] = nibble1(rs);\n\t\tregs[2] = nibble2(rs);\n\t\tregs[3] = nibble3(rs);\n\t\tregs[4] = nibble2(opcodeUnit);\n\n\t\tinsn.setRegsCount(regsCount1);\n\t\tinsn.setIndex(index);\n\t}\n\n\tprotected void readRegsRange(DexInsnData insn, int opcodeUnit, SectionReader in) {\n\t\tint regsCount = byte1(opcodeUnit);\n\t\tint index = in.readUShort();\n\t\tint startReg = in.readUShort();\n\n\t\tint[] regs = insn.getArgsReg();\n\t\tif (regs.length < regsCount) {\n\t\t\tregs = new int[regsCount];\n\t\t\tinsn.setArgsReg(regs);\n\t\t}\n\t\tint regNum = startReg;\n\t\tfor (int i = 0; i < regsCount; i++) {\n\t\t\tregs[i] = regNum;\n\t\t\tregNum++;\n\t\t}\n\t\tinsn.setRegsCount(regsCount);\n\t\tinsn.setIndex(index);\n\t}\n\n\tprivate final int length;\n\tprivate final int regsCount;\n\n\tprotected DexInsnFormat(int length, int regsCount) {\n\t\tthis.length = length;\n\t\tthis.regsCount = regsCount;\n\t}\n\n\tpublic abstract void decode(DexInsnData insn, int opcodeUnit, SectionReader in);\n\n\tpublic void skip(DexInsnData insn, SectionReader in) {\n\t\tint len = this.length;\n\t\tif (len == 1) {\n\t\t\treturn;\n\t\t}\n\t\tin.skip((len - 1) * 2);\n\t}\n\n\tpublic int getLength() {\n\t\treturn length;\n\t}\n\n\tpublic int getRegsCount() {\n\t\treturn regsCount;\n\t}\n\n\tprivate static int byte0(int value) {\n\t\treturn value & 0xFF;\n\t}\n\n\tprivate static int byte1(int value) {\n\t\treturn (value >> 8) & 0xFF;\n\t}\n\n\tprivate static int signedByte1(int value) {\n\t\treturn (byte) (value >> 8);\n\t}\n\n\tprivate static int nibble0(int value) {\n\t\treturn value & 0xF;\n\t}\n\n\tprivate static int nibble1(int value) {\n\t\treturn (value >> 4) & 0xF;\n\t}\n\n\tprivate static int nibble2(int value) {\n\t\treturn (value >> 8) & 0xF;\n\t}\n\n\tprivate static int nibble3(int value) {\n\t\treturn (value >> 12) & 0xF;\n\t}\n\n\tprivate static int signedNibble3(int value) {\n\t\treturn (((value >> 12) & 0xF) << 28) >> 28;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexInsnInfo.java",
    "content": "package jadx.plugins.input.dex.insns;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.insns.InsnIndexType;\nimport jadx.api.plugins.input.insns.Opcode;\n\npublic class DexInsnInfo {\n\n\tprivate static final DexInsnInfo[] INSN_INFO;\n\tprivate static final Map<Integer, DexInsnInfo> PAYLOAD_INFO;\n\n\tstatic {\n\t\tDexInsnInfo[] arr = new DexInsnInfo[0x100];\n\t\tINSN_INFO = arr;\n\t\tregister(arr, DexOpcodes.NOP, Opcode.NOP, DexInsnFormat.FORMAT_10X);\n\n\t\tregister(arr, DexOpcodes.MOVE, Opcode.MOVE, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.MOVE_FROM16, Opcode.MOVE, DexInsnFormat.FORMAT_22X);\n\t\tregister(arr, DexOpcodes.MOVE_16, Opcode.MOVE, DexInsnFormat.FORMAT_32X);\n\n\t\tregister(arr, DexOpcodes.MOVE_WIDE, Opcode.MOVE_WIDE, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.MOVE_WIDE_FROM16, Opcode.MOVE_WIDE, DexInsnFormat.FORMAT_22X);\n\t\tregister(arr, DexOpcodes.MOVE_WIDE_16, Opcode.MOVE_WIDE, DexInsnFormat.FORMAT_32X);\n\n\t\tregister(arr, DexOpcodes.MOVE_OBJECT, Opcode.MOVE_OBJECT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.MOVE_OBJECT_FROM16, Opcode.MOVE_OBJECT, DexInsnFormat.FORMAT_22X);\n\t\tregister(arr, DexOpcodes.MOVE_OBJECT_16, Opcode.MOVE_OBJECT, DexInsnFormat.FORMAT_32X);\n\n\t\tregister(arr, DexOpcodes.MOVE_RESULT, Opcode.MOVE_RESULT, DexInsnFormat.FORMAT_11X);\n\t\tregister(arr, DexOpcodes.MOVE_RESULT_WIDE, Opcode.MOVE_RESULT, DexInsnFormat.FORMAT_11X);\n\t\tregister(arr, DexOpcodes.MOVE_RESULT_OBJECT, Opcode.MOVE_RESULT, DexInsnFormat.FORMAT_11X);\n\n\t\tregister(arr, DexOpcodes.MOVE_EXCEPTION, Opcode.MOVE_EXCEPTION, DexInsnFormat.FORMAT_11X);\n\n\t\tregister(arr, DexOpcodes.RETURN_VOID, Opcode.RETURN_VOID, DexInsnFormat.FORMAT_10X);\n\t\tregister(arr, DexOpcodes.RETURN, Opcode.RETURN, DexInsnFormat.FORMAT_11X);\n\t\tregister(arr, DexOpcodes.RETURN_WIDE, Opcode.RETURN, DexInsnFormat.FORMAT_11X);\n\t\tregister(arr, DexOpcodes.RETURN_OBJECT, Opcode.RETURN, DexInsnFormat.FORMAT_11X);\n\n\t\tregister(arr, DexOpcodes.CONST_4, Opcode.CONST, DexInsnFormat.FORMAT_11N);\n\t\tregister(arr, DexOpcodes.CONST_16, Opcode.CONST, DexInsnFormat.FORMAT_21S);\n\t\tregister(arr, DexOpcodes.CONST, Opcode.CONST, DexInsnFormat.FORMAT_31I);\n\t\tregister(arr, DexOpcodes.CONST_HIGH16, Opcode.CONST, DexInsnFormat.FORMAT_21H);\n\n\t\tregister(arr, DexOpcodes.CONST_WIDE_16, Opcode.CONST_WIDE, DexInsnFormat.FORMAT_21S);\n\t\tregister(arr, DexOpcodes.CONST_WIDE_32, Opcode.CONST_WIDE, DexInsnFormat.FORMAT_31I);\n\t\tregister(arr, DexOpcodes.CONST_WIDE, Opcode.CONST_WIDE, DexInsnFormat.FORMAT_51I);\n\t\tregister(arr, DexOpcodes.CONST_WIDE_HIGH16, Opcode.CONST_WIDE, DexInsnFormat.FORMAT_21H);\n\n\t\tregister(arr, DexOpcodes.CONST_STRING, Opcode.CONST_STRING, DexInsnFormat.FORMAT_21C, InsnIndexType.STRING_REF);\n\t\tregister(arr, DexOpcodes.CONST_STRING_JUMBO, Opcode.CONST_STRING, DexInsnFormat.FORMAT_31C, InsnIndexType.STRING_REF);\n\n\t\tregister(arr, DexOpcodes.CONST_CLASS, Opcode.CONST_CLASS, DexInsnFormat.FORMAT_21C, InsnIndexType.TYPE_REF);\n\n\t\tregister(arr, DexOpcodes.MONITOR_ENTER, Opcode.MONITOR_ENTER, DexInsnFormat.FORMAT_11X);\n\t\tregister(arr, DexOpcodes.MONITOR_EXIT, Opcode.MONITOR_EXIT, DexInsnFormat.FORMAT_11X);\n\n\t\tregister(arr, DexOpcodes.CHECK_CAST, Opcode.CHECK_CAST, DexInsnFormat.FORMAT_21C, InsnIndexType.TYPE_REF);\n\t\tregister(arr, DexOpcodes.INSTANCE_OF, Opcode.INSTANCE_OF, DexInsnFormat.FORMAT_22C, InsnIndexType.TYPE_REF);\n\t\tregister(arr, DexOpcodes.ARRAY_LENGTH, Opcode.ARRAY_LENGTH, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.NEW_INSTANCE, Opcode.NEW_INSTANCE, DexInsnFormat.FORMAT_21C, InsnIndexType.TYPE_REF);\n\t\tregister(arr, DexOpcodes.NEW_ARRAY, Opcode.NEW_ARRAY, DexInsnFormat.FORMAT_22C, InsnIndexType.TYPE_REF);\n\n\t\tregister(arr, DexOpcodes.FILLED_NEW_ARRAY, Opcode.FILLED_NEW_ARRAY, DexInsnFormat.FORMAT_35C, InsnIndexType.TYPE_REF);\n\t\tregister(arr, DexOpcodes.FILLED_NEW_ARRAY_RANGE, Opcode.FILLED_NEW_ARRAY_RANGE, DexInsnFormat.FORMAT_3RC, InsnIndexType.TYPE_REF);\n\t\tregister(arr, DexOpcodes.FILL_ARRAY_DATA, Opcode.FILL_ARRAY_DATA, DexInsnFormat.FORMAT_31T);\n\n\t\tregister(arr, DexOpcodes.THROW, Opcode.THROW, DexInsnFormat.FORMAT_11X);\n\n\t\tregister(arr, DexOpcodes.GOTO, Opcode.GOTO, DexInsnFormat.FORMAT_10T);\n\t\tregister(arr, DexOpcodes.GOTO_16, Opcode.GOTO, DexInsnFormat.FORMAT_20T);\n\t\tregister(arr, DexOpcodes.GOTO_32, Opcode.GOTO, DexInsnFormat.FORMAT_30T);\n\n\t\tregister(arr, DexOpcodes.PACKED_SWITCH, Opcode.PACKED_SWITCH, DexInsnFormat.FORMAT_31T);\n\t\tregister(arr, DexOpcodes.SPARSE_SWITCH, Opcode.SPARSE_SWITCH, DexInsnFormat.FORMAT_31T);\n\n\t\tregister(arr, DexOpcodes.CMPL_FLOAT, Opcode.CMPL_FLOAT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.CMPG_FLOAT, Opcode.CMPG_FLOAT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.CMPL_DOUBLE, Opcode.CMPL_DOUBLE, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.CMPG_DOUBLE, Opcode.CMPG_DOUBLE, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.CMP_LONG, Opcode.CMP_LONG, DexInsnFormat.FORMAT_23X);\n\n\t\tregister(arr, DexOpcodes.IF_EQ, Opcode.IF_EQ, DexInsnFormat.FORMAT_22T);\n\t\tregister(arr, DexOpcodes.IF_NE, Opcode.IF_NE, DexInsnFormat.FORMAT_22T);\n\t\tregister(arr, DexOpcodes.IF_LT, Opcode.IF_LT, DexInsnFormat.FORMAT_22T);\n\t\tregister(arr, DexOpcodes.IF_GE, Opcode.IF_GE, DexInsnFormat.FORMAT_22T);\n\t\tregister(arr, DexOpcodes.IF_GT, Opcode.IF_GT, DexInsnFormat.FORMAT_22T);\n\t\tregister(arr, DexOpcodes.IF_LE, Opcode.IF_LE, DexInsnFormat.FORMAT_22T);\n\n\t\tregister(arr, DexOpcodes.IF_EQZ, Opcode.IF_EQZ, DexInsnFormat.FORMAT_21T);\n\t\tregister(arr, DexOpcodes.IF_NEZ, Opcode.IF_NEZ, DexInsnFormat.FORMAT_21T);\n\t\tregister(arr, DexOpcodes.IF_LTZ, Opcode.IF_LTZ, DexInsnFormat.FORMAT_21T);\n\t\tregister(arr, DexOpcodes.IF_GEZ, Opcode.IF_GEZ, DexInsnFormat.FORMAT_21T);\n\t\tregister(arr, DexOpcodes.IF_GTZ, Opcode.IF_GTZ, DexInsnFormat.FORMAT_21T);\n\t\tregister(arr, DexOpcodes.IF_LEZ, Opcode.IF_LEZ, DexInsnFormat.FORMAT_21T);\n\n\t\tregister(arr, DexOpcodes.AGET, Opcode.AGET, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.AGET_WIDE, Opcode.AGET_WIDE, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.AGET_OBJECT, Opcode.AGET_OBJECT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.AGET_BOOLEAN, Opcode.AGET_BOOLEAN, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.AGET_BYTE, Opcode.AGET_BYTE, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.AGET_CHAR, Opcode.AGET_CHAR, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.AGET_SHORT, Opcode.AGET_SHORT, DexInsnFormat.FORMAT_23X);\n\n\t\tregister(arr, DexOpcodes.APUT, Opcode.APUT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.APUT_WIDE, Opcode.APUT_WIDE, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.APUT_OBJECT, Opcode.APUT_OBJECT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.APUT_BOOLEAN, Opcode.APUT_BOOLEAN, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.APUT_BYTE, Opcode.APUT_BYTE, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.APUT_CHAR, Opcode.APUT_CHAR, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.APUT_SHORT, Opcode.APUT_SHORT, DexInsnFormat.FORMAT_23X);\n\n\t\tregister(arr, DexOpcodes.IGET, Opcode.IGET, DexInsnFormat.FORMAT_22C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.IGET_WIDE, Opcode.IGET, DexInsnFormat.FORMAT_22C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.IGET_OBJECT, Opcode.IGET, DexInsnFormat.FORMAT_22C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.IGET_BOOLEAN, Opcode.IGET, DexInsnFormat.FORMAT_22C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.IGET_BYTE, Opcode.IGET, DexInsnFormat.FORMAT_22C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.IGET_CHAR, Opcode.IGET, DexInsnFormat.FORMAT_22C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.IGET_SHORT, Opcode.IGET, DexInsnFormat.FORMAT_22C, InsnIndexType.FIELD_REF);\n\n\t\tregister(arr, DexOpcodes.IPUT, Opcode.IPUT, DexInsnFormat.FORMAT_22C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.IPUT_WIDE, Opcode.IPUT, DexInsnFormat.FORMAT_22C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.IPUT_OBJECT, Opcode.IPUT, DexInsnFormat.FORMAT_22C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.IPUT_BOOLEAN, Opcode.IPUT, DexInsnFormat.FORMAT_22C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.IPUT_BYTE, Opcode.IPUT, DexInsnFormat.FORMAT_22C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.IPUT_CHAR, Opcode.IPUT, DexInsnFormat.FORMAT_22C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.IPUT_SHORT, Opcode.IPUT, DexInsnFormat.FORMAT_22C, InsnIndexType.FIELD_REF);\n\n\t\tregister(arr, DexOpcodes.SGET, Opcode.SGET, DexInsnFormat.FORMAT_21C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.SGET_WIDE, Opcode.SGET, DexInsnFormat.FORMAT_21C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.SGET_OBJECT, Opcode.SGET, DexInsnFormat.FORMAT_21C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.SGET_BOOLEAN, Opcode.SGET, DexInsnFormat.FORMAT_21C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.SGET_BYTE, Opcode.SGET, DexInsnFormat.FORMAT_21C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.SGET_CHAR, Opcode.SGET, DexInsnFormat.FORMAT_21C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.SGET_SHORT, Opcode.SGET, DexInsnFormat.FORMAT_21C, InsnIndexType.FIELD_REF);\n\n\t\tregister(arr, DexOpcodes.SPUT, Opcode.SPUT, DexInsnFormat.FORMAT_21C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.SPUT_WIDE, Opcode.SPUT, DexInsnFormat.FORMAT_21C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.SPUT_OBJECT, Opcode.SPUT, DexInsnFormat.FORMAT_21C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.SPUT_BOOLEAN, Opcode.SPUT, DexInsnFormat.FORMAT_21C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.SPUT_BYTE, Opcode.SPUT, DexInsnFormat.FORMAT_21C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.SPUT_CHAR, Opcode.SPUT, DexInsnFormat.FORMAT_21C, InsnIndexType.FIELD_REF);\n\t\tregister(arr, DexOpcodes.SPUT_SHORT, Opcode.SPUT, DexInsnFormat.FORMAT_21C, InsnIndexType.FIELD_REF);\n\n\t\tregister(arr, DexOpcodes.INVOKE_VIRTUAL, Opcode.INVOKE_VIRTUAL, DexInsnFormat.FORMAT_35C, InsnIndexType.METHOD_REF);\n\t\tregister(arr, DexOpcodes.INVOKE_SUPER, Opcode.INVOKE_SUPER, DexInsnFormat.FORMAT_35C, InsnIndexType.METHOD_REF);\n\t\tregister(arr, DexOpcodes.INVOKE_DIRECT, Opcode.INVOKE_DIRECT, DexInsnFormat.FORMAT_35C, InsnIndexType.METHOD_REF);\n\t\tregister(arr, DexOpcodes.INVOKE_STATIC, Opcode.INVOKE_STATIC, DexInsnFormat.FORMAT_35C, InsnIndexType.METHOD_REF);\n\t\tregister(arr, DexOpcodes.INVOKE_INTERFACE, Opcode.INVOKE_INTERFACE, DexInsnFormat.FORMAT_35C, InsnIndexType.METHOD_REF);\n\n\t\tregister(arr, DexOpcodes.INVOKE_VIRTUAL_RANGE, Opcode.INVOKE_VIRTUAL_RANGE, DexInsnFormat.FORMAT_3RC, InsnIndexType.METHOD_REF);\n\t\tregister(arr, DexOpcodes.INVOKE_SUPER_RANGE, Opcode.INVOKE_SUPER_RANGE, DexInsnFormat.FORMAT_3RC, InsnIndexType.METHOD_REF);\n\t\tregister(arr, DexOpcodes.INVOKE_DIRECT_RANGE, Opcode.INVOKE_DIRECT_RANGE, DexInsnFormat.FORMAT_3RC, InsnIndexType.METHOD_REF);\n\t\tregister(arr, DexOpcodes.INVOKE_STATIC_RANGE, Opcode.INVOKE_STATIC_RANGE, DexInsnFormat.FORMAT_3RC, InsnIndexType.METHOD_REF);\n\t\tregister(arr, DexOpcodes.INVOKE_INTERFACE_RANGE, Opcode.INVOKE_INTERFACE_RANGE, DexInsnFormat.FORMAT_3RC, InsnIndexType.METHOD_REF);\n\n\t\tregister(arr, DexOpcodes.NEG_INT, Opcode.NEG_INT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.NOT_INT, Opcode.NOT_INT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.NEG_LONG, Opcode.NEG_LONG, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.NOT_LONG, Opcode.NOT_LONG, DexInsnFormat.FORMAT_12X);\n\n\t\tregister(arr, DexOpcodes.NEG_FLOAT, Opcode.NEG_FLOAT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.NEG_DOUBLE, Opcode.NEG_DOUBLE, DexInsnFormat.FORMAT_12X);\n\n\t\tregister(arr, DexOpcodes.INT_TO_LONG, Opcode.INT_TO_LONG, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.INT_TO_FLOAT, Opcode.INT_TO_FLOAT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.INT_TO_DOUBLE, Opcode.INT_TO_DOUBLE, DexInsnFormat.FORMAT_12X);\n\n\t\tregister(arr, DexOpcodes.LONG_TO_INT, Opcode.LONG_TO_INT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.LONG_TO_FLOAT, Opcode.LONG_TO_FLOAT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.LONG_TO_DOUBLE, Opcode.LONG_TO_DOUBLE, DexInsnFormat.FORMAT_12X);\n\n\t\tregister(arr, DexOpcodes.FLOAT_TO_INT, Opcode.FLOAT_TO_INT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.FLOAT_TO_LONG, Opcode.FLOAT_TO_LONG, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.FLOAT_TO_DOUBLE, Opcode.FLOAT_TO_DOUBLE, DexInsnFormat.FORMAT_12X);\n\n\t\tregister(arr, DexOpcodes.DOUBLE_TO_INT, Opcode.DOUBLE_TO_INT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.DOUBLE_TO_LONG, Opcode.DOUBLE_TO_LONG, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.DOUBLE_TO_FLOAT, Opcode.DOUBLE_TO_FLOAT, DexInsnFormat.FORMAT_12X);\n\n\t\tregister(arr, DexOpcodes.INT_TO_BYTE, Opcode.INT_TO_BYTE, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.INT_TO_CHAR, Opcode.INT_TO_CHAR, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.INT_TO_SHORT, Opcode.INT_TO_SHORT, DexInsnFormat.FORMAT_12X);\n\n\t\tregister(arr, DexOpcodes.ADD_INT, Opcode.ADD_INT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.SUB_INT, Opcode.SUB_INT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.MUL_INT, Opcode.MUL_INT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.DIV_INT, Opcode.DIV_INT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.REM_INT, Opcode.REM_INT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.AND_INT, Opcode.AND_INT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.OR_INT, Opcode.OR_INT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.XOR_INT, Opcode.XOR_INT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.SHL_INT, Opcode.SHL_INT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.SHR_INT, Opcode.SHR_INT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.USHR_INT, Opcode.USHR_INT, DexInsnFormat.FORMAT_23X);\n\n\t\tregister(arr, DexOpcodes.ADD_LONG, Opcode.ADD_LONG, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.SUB_LONG, Opcode.SUB_LONG, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.MUL_LONG, Opcode.MUL_LONG, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.DIV_LONG, Opcode.DIV_LONG, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.REM_LONG, Opcode.REM_LONG, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.AND_LONG, Opcode.AND_LONG, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.OR_LONG, Opcode.OR_LONG, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.XOR_LONG, Opcode.XOR_LONG, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.SHL_LONG, Opcode.SHL_LONG, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.SHR_LONG, Opcode.SHR_LONG, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.USHR_LONG, Opcode.USHR_LONG, DexInsnFormat.FORMAT_23X);\n\n\t\tregister(arr, DexOpcodes.ADD_FLOAT, Opcode.ADD_FLOAT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.SUB_FLOAT, Opcode.SUB_FLOAT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.MUL_FLOAT, Opcode.MUL_FLOAT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.DIV_FLOAT, Opcode.DIV_FLOAT, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.REM_FLOAT, Opcode.REM_FLOAT, DexInsnFormat.FORMAT_23X);\n\n\t\tregister(arr, DexOpcodes.ADD_DOUBLE, Opcode.ADD_DOUBLE, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.SUB_DOUBLE, Opcode.SUB_DOUBLE, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.MUL_DOUBLE, Opcode.MUL_DOUBLE, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.DIV_DOUBLE, Opcode.DIV_DOUBLE, DexInsnFormat.FORMAT_23X);\n\t\tregister(arr, DexOpcodes.REM_DOUBLE, Opcode.REM_DOUBLE, DexInsnFormat.FORMAT_23X);\n\n\t\tregister(arr, DexOpcodes.ADD_INT_2ADDR, Opcode.ADD_INT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.SUB_INT_2ADDR, Opcode.SUB_INT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.MUL_INT_2ADDR, Opcode.MUL_INT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.DIV_INT_2ADDR, Opcode.DIV_INT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.REM_INT_2ADDR, Opcode.REM_INT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.AND_INT_2ADDR, Opcode.AND_INT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.OR_INT_2ADDR, Opcode.OR_INT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.XOR_INT_2ADDR, Opcode.XOR_INT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.SHL_INT_2ADDR, Opcode.SHL_INT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.SHR_INT_2ADDR, Opcode.SHR_INT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.USHR_INT_2ADDR, Opcode.USHR_INT, DexInsnFormat.FORMAT_12X);\n\n\t\tregister(arr, DexOpcodes.ADD_LONG_2ADDR, Opcode.ADD_LONG, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.SUB_LONG_2ADDR, Opcode.SUB_LONG, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.MUL_LONG_2ADDR, Opcode.MUL_LONG, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.DIV_LONG_2ADDR, Opcode.DIV_LONG, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.REM_LONG_2ADDR, Opcode.REM_LONG, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.AND_LONG_2ADDR, Opcode.AND_LONG, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.OR_LONG_2ADDR, Opcode.OR_LONG, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.XOR_LONG_2ADDR, Opcode.XOR_LONG, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.SHL_LONG_2ADDR, Opcode.SHL_LONG, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.SHR_LONG_2ADDR, Opcode.SHR_LONG, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.USHR_LONG_2ADDR, Opcode.USHR_LONG, DexInsnFormat.FORMAT_12X);\n\n\t\tregister(arr, DexOpcodes.ADD_FLOAT_2ADDR, Opcode.ADD_FLOAT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.SUB_FLOAT_2ADDR, Opcode.SUB_FLOAT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.MUL_FLOAT_2ADDR, Opcode.MUL_FLOAT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.DIV_FLOAT_2ADDR, Opcode.DIV_FLOAT, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.REM_FLOAT_2ADDR, Opcode.REM_FLOAT, DexInsnFormat.FORMAT_12X);\n\n\t\tregister(arr, DexOpcodes.ADD_DOUBLE_2ADDR, Opcode.ADD_DOUBLE, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.SUB_DOUBLE_2ADDR, Opcode.SUB_DOUBLE, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.MUL_DOUBLE_2ADDR, Opcode.MUL_DOUBLE, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.DIV_DOUBLE_2ADDR, Opcode.DIV_DOUBLE, DexInsnFormat.FORMAT_12X);\n\t\tregister(arr, DexOpcodes.REM_DOUBLE_2ADDR, Opcode.REM_DOUBLE, DexInsnFormat.FORMAT_12X);\n\n\t\tregister(arr, DexOpcodes.ADD_INT_LIT16, Opcode.ADD_INT_LIT, DexInsnFormat.FORMAT_22S);\n\t\tregister(arr, DexOpcodes.RSUB_INT, Opcode.RSUB_INT, DexInsnFormat.FORMAT_22S);\n\t\tregister(arr, DexOpcodes.MUL_INT_LIT16, Opcode.MUL_INT_LIT, DexInsnFormat.FORMAT_22S);\n\t\tregister(arr, DexOpcodes.DIV_INT_LIT16, Opcode.DIV_INT_LIT, DexInsnFormat.FORMAT_22S);\n\t\tregister(arr, DexOpcodes.REM_INT_LIT16, Opcode.REM_INT_LIT, DexInsnFormat.FORMAT_22S);\n\t\tregister(arr, DexOpcodes.AND_INT_LIT16, Opcode.AND_INT_LIT, DexInsnFormat.FORMAT_22S);\n\t\tregister(arr, DexOpcodes.OR_INT_LIT16, Opcode.OR_INT_LIT, DexInsnFormat.FORMAT_22S);\n\t\tregister(arr, DexOpcodes.XOR_INT_LIT16, Opcode.XOR_INT_LIT, DexInsnFormat.FORMAT_22S);\n\n\t\tregister(arr, DexOpcodes.ADD_INT_LIT8, Opcode.ADD_INT_LIT, DexInsnFormat.FORMAT_22B);\n\t\tregister(arr, DexOpcodes.RSUB_INT_LIT8, Opcode.RSUB_INT, DexInsnFormat.FORMAT_22B);\n\t\tregister(arr, DexOpcodes.MUL_INT_LIT8, Opcode.MUL_INT_LIT, DexInsnFormat.FORMAT_22B);\n\t\tregister(arr, DexOpcodes.DIV_INT_LIT8, Opcode.DIV_INT_LIT, DexInsnFormat.FORMAT_22B);\n\t\tregister(arr, DexOpcodes.REM_INT_LIT8, Opcode.REM_INT_LIT, DexInsnFormat.FORMAT_22B);\n\t\tregister(arr, DexOpcodes.AND_INT_LIT8, Opcode.AND_INT_LIT, DexInsnFormat.FORMAT_22B);\n\t\tregister(arr, DexOpcodes.OR_INT_LIT8, Opcode.OR_INT_LIT, DexInsnFormat.FORMAT_22B);\n\t\tregister(arr, DexOpcodes.XOR_INT_LIT8, Opcode.XOR_INT_LIT, DexInsnFormat.FORMAT_22B);\n\t\tregister(arr, DexOpcodes.SHL_INT_LIT8, Opcode.SHL_INT_LIT, DexInsnFormat.FORMAT_22B);\n\t\tregister(arr, DexOpcodes.SHR_INT_LIT8, Opcode.SHR_INT_LIT, DexInsnFormat.FORMAT_22B);\n\t\tregister(arr, DexOpcodes.USHR_INT_LIT8, Opcode.USHR_INT_LIT, DexInsnFormat.FORMAT_22B);\n\n\t\tregister(arr, DexOpcodes.INVOKE_POLYMORPHIC, Opcode.INVOKE_POLYMORPHIC, DexInsnFormat.FORMAT_45CC, InsnIndexType.METHOD_REF);\n\t\tregister(arr, DexOpcodes.INVOKE_POLYMORPHIC_RANGE, Opcode.INVOKE_POLYMORPHIC_RANGE, DexInsnFormat.FORMAT_4RCC,\n\t\t\t\tInsnIndexType.METHOD_REF);\n\n\t\tregister(arr, DexOpcodes.INVOKE_CUSTOM, Opcode.INVOKE_CUSTOM, DexInsnFormat.FORMAT_35C, InsnIndexType.CALL_SITE);\n\t\tregister(arr, DexOpcodes.INVOKE_CUSTOM_RANGE, Opcode.INVOKE_CUSTOM_RANGE, DexInsnFormat.FORMAT_3RC, InsnIndexType.CALL_SITE);\n\n\t\tregister(arr, DexOpcodes.CONST_METHOD_HANDLE, Opcode.CONST_METHOD_HANDLE, DexInsnFormat.FORMAT_21C);\n\t\tregister(arr, DexOpcodes.CONST_METHOD_TYPE, Opcode.CONST_METHOD_TYPE, DexInsnFormat.FORMAT_21C);\n\n\t\tPAYLOAD_INFO = new ConcurrentHashMap<>(3);\n\t\tregisterPayload(DexOpcodes.PACKED_SWITCH_PAYLOAD, Opcode.PACKED_SWITCH_PAYLOAD, DexInsnFormat.FORMAT_PACKED_SWITCH_PAYLOAD);\n\t\tregisterPayload(DexOpcodes.SPARSE_SWITCH_PAYLOAD, Opcode.SPARSE_SWITCH_PAYLOAD, DexInsnFormat.FORMAT_SPARSE_SWITCH_PAYLOAD);\n\t\tregisterPayload(DexOpcodes.FILL_ARRAY_DATA_PAYLOAD, Opcode.FILL_ARRAY_DATA_PAYLOAD, DexInsnFormat.FORMAT_FILL_ARRAY_DATA_PAYLOAD);\n\t}\n\n\tprivate static void register(DexInsnInfo[] arr, int opcode, Opcode apiOpcode, DexInsnFormat format) {\n\t\tarr[opcode] = new DexInsnInfo(opcode, apiOpcode, format, InsnIndexType.NONE);\n\t}\n\n\tprivate static void register(DexInsnInfo[] arr, int opcode, Opcode apiOpcode, DexInsnFormat format, InsnIndexType indexType) {\n\t\tarr[opcode] = new DexInsnInfo(opcode, apiOpcode, format, indexType);\n\t}\n\n\tprivate static void registerPayload(int opcode, Opcode apiOpcode, DexInsnFormat format) {\n\t\tPAYLOAD_INFO.put(opcode, new DexInsnInfo(opcode, apiOpcode, format, InsnIndexType.NONE));\n\t}\n\n\t@Nullable\n\tpublic static DexInsnInfo get(int opcodeUnit) {\n\t\tint opcode = opcodeUnit & 0xFF;\n\t\tif (opcode == 0 && opcodeUnit != 0) {\n\t\t\treturn PAYLOAD_INFO.get(opcodeUnit);\n\t\t}\n\t\treturn INSN_INFO[opcode];\n\t}\n\n\tprivate final int opcode;\n\tprivate final Opcode apiOpcode;\n\tprivate final DexInsnFormat format;\n\tprivate final InsnIndexType indexType;\n\n\tpublic DexInsnInfo(int opcode, Opcode apiOpcode, DexInsnFormat format, InsnIndexType indexType) {\n\t\tthis.opcode = opcode;\n\t\tthis.apiOpcode = apiOpcode;\n\t\tthis.format = format;\n\t\tthis.indexType = indexType;\n\t}\n\n\tpublic int getOpcode() {\n\t\treturn opcode;\n\t}\n\n\tpublic Opcode getApiOpcode() {\n\t\treturn apiOpcode;\n\t}\n\n\tpublic DexInsnFormat getFormat() {\n\t\treturn format;\n\t}\n\n\tpublic InsnIndexType getIndexType() {\n\t\treturn indexType;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn String.format(\"0x%X :%d%d\", opcode, format.getLength(), format.getRegsCount());\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexInsnMnemonics.java",
    "content": "package jadx.plugins.input.dex.insns;\n\npublic class DexInsnMnemonics {\n\n\tpublic static String get(int opcode) {\n\t\treturn MNEMONICS[opcode & 0xFF];\n\t}\n\n\tprivate static final String[] MNEMONICS = new String[] {\n\t\t\t\"nop\",\n\t\t\t\"move\", \"move/from16\", \"move/16\", \"move-wide\",\n\t\t\t\"move-wide/from16\", \"move-wide/16\", \"move-object\", \"move-object/from16\", \"move-object/16\",\n\t\t\t\"move-result\", \"move-result-wide\", \"move-result-object\", \"move-exception\", \"return-void\",\n\t\t\t\"return\", \"return-wide\", \"return-object\",\n\t\t\t\"const/4\", \"const/16\", \"const\", \"const/high16\", \"const-wide/16\", \"const-wide/32\", \"const-wide\",\n\t\t\t\"const-wide/high16\", \"const-string\", \"const-string/jumbo\", \"const-class\", \"monitor-enter\",\n\t\t\t\"monitor-exit\", \"check-cast\", \"instance-of\", \"array-length\", \"new-instance\",\n\t\t\t\"new-array\", \"filled-new-array\", \"filled-new-array/range\", \"fill-array-data\", \"throw\",\n\t\t\t\"goto\", \"goto/16\", \"goto/32\",\n\t\t\t\"packed-switch\", \"sparse-switch\",\n\t\t\t\"cmpl-float\", \"cmpg-float\", \"cmpl-double\", \"cmpg-double\", \"cmp-long\",\n\t\t\t\"if-eq\", \"if-ne\", \"if-lt\", \"if-ge\", \"if-gt\", \"if-le\",\n\t\t\t\"if-eqz\", \"if-nez\", \"if-ltz\", \"if-gez\", \"if-gtz\", \"if-lez\",\n\t\t\t\"(unused)\", \"(unused)\", \"(unused)\", \"(unused)\", \"(unused)\", \"(unused)\",\n\t\t\t\"aget\", \"aget-wide\",\n\t\t\t\"aget-object\", \"aget-boolean\", \"aget-byte\", \"aget-char\", \"aget-short\",\n\t\t\t\"aput\", \"aput-wide\", \"aput-object\", \"aput-boolean\", \"aput-byte\",\n\t\t\t\"aput-char\", \"aput-short\", \"iget\", \"iget-wide\", \"iget-object\",\n\t\t\t\"iget-boolean\", \"iget-byte\", \"iget-char\", \"iget-short\", \"iput\",\n\t\t\t\"iput-wide\", \"iput-object\", \"iput-boolean\", \"iput-byte\", \"iput-char\",\n\t\t\t\"iput-short\", \"sget\", \"sget-wide\", \"sget-object\", \"sget-boolean\",\n\t\t\t\"sget-byte\", \"sget-char\", \"sget-short\", \"sput\", \"sput-wide\",\n\t\t\t\"sput-object\", \"sput-boolean\", \"sput-byte\", \"sput-char\", \"sput-short\",\n\t\t\t\"invoke-virtual\", \"invoke-super\", \"invoke-direct\", \"invoke-static\", \"invoke-interface\",\n\t\t\t\"(unused)\", \"invoke-virtual/range\", \"invoke-super/range\", \"invoke-direct/range\", \"invoke-static/range\",\n\t\t\t\"invoke-interface/range\", \"(unused)\", \"(unused)\", \"neg-int\", \"not-int\",\n\t\t\t\"neg-long\", \"not-long\", \"neg-float\", \"neg-double\", \"int-to-long\",\n\t\t\t\"int-to-float\", \"int-to-double\", \"long-to-int\", \"long-to-float\", \"long-to-double\",\n\t\t\t\"float-to-int\", \"float-to-long\", \"float-to-double\", \"double-to-int\", \"double-to-long\",\n\t\t\t\"double-to-float\", \"int-to-byte\", \"int-to-char\", \"int-to-short\", \"add-int\",\n\t\t\t\"sub-int\", \"mul-int\", \"div-int\", \"rem-int\", \"and-int\",\n\t\t\t\"or-int\", \"xor-int\", \"shl-int\", \"shr-int\", \"ushr-int\",\n\t\t\t\"add-long\", \"sub-long\", \"mul-long\", \"div-long\", \"rem-long\",\n\t\t\t\"and-long\", \"or-long\", \"xor-long\", \"shl-long\", \"shr-long\",\n\t\t\t\"ushr-long\", \"add-float\", \"sub-float\", \"mul-float\", \"div-float\",\n\t\t\t\"rem-float\", \"add-double\", \"sub-double\", \"mul-double\", \"div-double\",\n\t\t\t\"rem-double\", \"add-int/2addr\", \"sub-int/2addr\", \"mul-int/2addr\", \"div-int/2addr\",\n\t\t\t\"rem-int/2addr\", \"and-int/2addr\", \"or-int/2addr\", \"xor-int/2addr\", \"shl-int/2addr\",\n\t\t\t\"shr-int/2addr\", \"ushr-int/2addr\", \"add-long/2addr\", \"sub-long/2addr\", \"mul-long/2addr\",\n\t\t\t\"div-long/2addr\", \"rem-long/2addr\", \"and-long/2addr\", \"or-long/2addr\", \"xor-long/2addr\",\n\t\t\t\"shl-long/2addr\", \"shr-long/2addr\", \"ushr-long/2addr\", \"add-float/2addr\", \"sub-float/2addr\",\n\t\t\t\"mul-float/2addr\", \"div-float/2addr\", \"rem-float/2addr\", \"add-double/2addr\", \"sub-double/2addr\",\n\t\t\t\"mul-double/2addr\", \"div-double/2addr\", \"rem-double/2addr\", \"add-int/lit16\", \"rsub-int\",\n\t\t\t\"mul-int/lit16\", \"div-int/lit16\", \"rem-int/lit16\", \"and-int/lit16\", \"or-int/lit16\",\n\t\t\t\"xor-int/lit16\", \"add-int/lit8\", \"rsub-int/lit8\", \"mul-int/lit8\", \"div-int/lit8\",\n\t\t\t\"rem-int/lit8\", \"and-int/lit8\", \"or-int/lit8\", \"xor-int/lit8\", \"shl-int/lit8\",\n\t\t\t\"shr-int/lit8\", \"ushr-int/lit8\", \"(unused)\", \"(unused)\", \"(unused)\",\n\t\t\t\"(unused)\", \"(unused)\", \"(unused)\", \"(unused)\", \"(unused)\",\n\t\t\t\"(unused)\", \"(unused)\", \"(unused)\", \"(unused)\", \"(unused)\",\n\t\t\t\"(unused)\", \"(unused)\", \"(unused)\", \"(unused)\", \"(unused)\",\n\t\t\t\"(unused)\", \"(unused)\", \"(unused)\", \"(unused)\", \"(unused)\",\n\t\t\t\"invoke-polymorphic\",\n\t\t\t\"invoke-polymorphic/range\",\n\t\t\t\"invoke-custom\",\n\t\t\t\"invoke-custom/range\",\n\t\t\t\"const-method-handle\",\n\t\t\t\"const-method-type\" };\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/DexOpcodes.java",
    "content": "package jadx.plugins.input.dex.insns;\n\npublic class DexOpcodes {\n\tpublic static final int NOP = 0x00;\n\tpublic static final int MOVE = 0x01;\n\tpublic static final int MOVE_FROM16 = 0x02;\n\tpublic static final int MOVE_16 = 0x03;\n\tpublic static final int MOVE_WIDE = 0x04;\n\tpublic static final int MOVE_WIDE_FROM16 = 0x05;\n\tpublic static final int MOVE_WIDE_16 = 0x06;\n\tpublic static final int MOVE_OBJECT = 0x07;\n\tpublic static final int MOVE_OBJECT_FROM16 = 0x08;\n\tpublic static final int MOVE_OBJECT_16 = 0x09;\n\tpublic static final int MOVE_RESULT = 0x0a;\n\tpublic static final int MOVE_RESULT_WIDE = 0x0b;\n\tpublic static final int MOVE_RESULT_OBJECT = 0x0c;\n\tpublic static final int MOVE_EXCEPTION = 0x0d;\n\tpublic static final int RETURN_VOID = 0x0e;\n\tpublic static final int RETURN = 0x0f;\n\tpublic static final int RETURN_WIDE = 0x10;\n\tpublic static final int RETURN_OBJECT = 0x11;\n\tpublic static final int CONST_4 = 0x12;\n\tpublic static final int CONST_16 = 0x13;\n\tpublic static final int CONST = 0x14;\n\tpublic static final int CONST_HIGH16 = 0x15;\n\tpublic static final int CONST_WIDE_16 = 0x16;\n\tpublic static final int CONST_WIDE_32 = 0x17;\n\tpublic static final int CONST_WIDE = 0x18;\n\tpublic static final int CONST_WIDE_HIGH16 = 0x19;\n\tpublic static final int CONST_STRING = 0x1a;\n\tpublic static final int CONST_STRING_JUMBO = 0x1b;\n\tpublic static final int CONST_CLASS = 0x1c;\n\tpublic static final int MONITOR_ENTER = 0x1d;\n\tpublic static final int MONITOR_EXIT = 0x1e;\n\tpublic static final int CHECK_CAST = 0x1f;\n\tpublic static final int INSTANCE_OF = 0x20;\n\tpublic static final int ARRAY_LENGTH = 0x21;\n\tpublic static final int NEW_INSTANCE = 0x22;\n\tpublic static final int NEW_ARRAY = 0x23;\n\tpublic static final int FILLED_NEW_ARRAY = 0x24;\n\tpublic static final int FILLED_NEW_ARRAY_RANGE = 0x25;\n\tpublic static final int FILL_ARRAY_DATA = 0x26;\n\tpublic static final int THROW = 0x27;\n\tpublic static final int GOTO = 0x28;\n\tpublic static final int GOTO_16 = 0x29;\n\tpublic static final int GOTO_32 = 0x2a;\n\tpublic static final int PACKED_SWITCH = 0x2b;\n\tpublic static final int SPARSE_SWITCH = 0x2c;\n\tpublic static final int CMPL_FLOAT = 0x2d;\n\tpublic static final int CMPG_FLOAT = 0x2e;\n\tpublic static final int CMPL_DOUBLE = 0x2f;\n\tpublic static final int CMPG_DOUBLE = 0x30;\n\tpublic static final int CMP_LONG = 0x31;\n\tpublic static final int IF_EQ = 0x32;\n\tpublic static final int IF_NE = 0x33;\n\tpublic static final int IF_LT = 0x34;\n\tpublic static final int IF_GE = 0x35;\n\tpublic static final int IF_GT = 0x36;\n\tpublic static final int IF_LE = 0x37;\n\tpublic static final int IF_EQZ = 0x38;\n\tpublic static final int IF_NEZ = 0x39;\n\tpublic static final int IF_LTZ = 0x3a;\n\tpublic static final int IF_GEZ = 0x3b;\n\tpublic static final int IF_GTZ = 0x3c;\n\tpublic static final int IF_LEZ = 0x3d;\n\tpublic static final int AGET = 0x44;\n\tpublic static final int AGET_WIDE = 0x45;\n\tpublic static final int AGET_OBJECT = 0x46;\n\tpublic static final int AGET_BOOLEAN = 0x47;\n\tpublic static final int AGET_BYTE = 0x48;\n\tpublic static final int AGET_CHAR = 0x49;\n\tpublic static final int AGET_SHORT = 0x4a;\n\tpublic static final int APUT = 0x4b;\n\tpublic static final int APUT_WIDE = 0x4c;\n\tpublic static final int APUT_OBJECT = 0x4d;\n\tpublic static final int APUT_BOOLEAN = 0x4e;\n\tpublic static final int APUT_BYTE = 0x4f;\n\tpublic static final int APUT_CHAR = 0x50;\n\tpublic static final int APUT_SHORT = 0x51;\n\tpublic static final int IGET = 0x52;\n\tpublic static final int IGET_WIDE = 0x53;\n\tpublic static final int IGET_OBJECT = 0x54;\n\tpublic static final int IGET_BOOLEAN = 0x55;\n\tpublic static final int IGET_BYTE = 0x56;\n\tpublic static final int IGET_CHAR = 0x57;\n\tpublic static final int IGET_SHORT = 0x58;\n\tpublic static final int IPUT = 0x59;\n\tpublic static final int IPUT_WIDE = 0x5a;\n\tpublic static final int IPUT_OBJECT = 0x5b;\n\tpublic static final int IPUT_BOOLEAN = 0x5c;\n\tpublic static final int IPUT_BYTE = 0x5d;\n\tpublic static final int IPUT_CHAR = 0x5e;\n\tpublic static final int IPUT_SHORT = 0x5f;\n\tpublic static final int SGET = 0x60;\n\tpublic static final int SGET_WIDE = 0x61;\n\tpublic static final int SGET_OBJECT = 0x62;\n\tpublic static final int SGET_BOOLEAN = 0x63;\n\tpublic static final int SGET_BYTE = 0x64;\n\tpublic static final int SGET_CHAR = 0x65;\n\tpublic static final int SGET_SHORT = 0x66;\n\tpublic static final int SPUT = 0x67;\n\tpublic static final int SPUT_WIDE = 0x68;\n\tpublic static final int SPUT_OBJECT = 0x69;\n\tpublic static final int SPUT_BOOLEAN = 0x6a;\n\tpublic static final int SPUT_BYTE = 0x6b;\n\tpublic static final int SPUT_CHAR = 0x6c;\n\tpublic static final int SPUT_SHORT = 0x6d;\n\tpublic static final int INVOKE_VIRTUAL = 0x6e;\n\tpublic static final int INVOKE_SUPER = 0x6f;\n\tpublic static final int INVOKE_DIRECT = 0x70;\n\tpublic static final int INVOKE_STATIC = 0x71;\n\tpublic static final int INVOKE_INTERFACE = 0x72;\n\tpublic static final int INVOKE_VIRTUAL_RANGE = 0x74;\n\tpublic static final int INVOKE_SUPER_RANGE = 0x75;\n\tpublic static final int INVOKE_DIRECT_RANGE = 0x76;\n\tpublic static final int INVOKE_STATIC_RANGE = 0x77;\n\tpublic static final int INVOKE_INTERFACE_RANGE = 0x78;\n\tpublic static final int NEG_INT = 0x7b;\n\tpublic static final int NOT_INT = 0x7c;\n\tpublic static final int NEG_LONG = 0x7d;\n\tpublic static final int NOT_LONG = 0x7e;\n\tpublic static final int NEG_FLOAT = 0x7f;\n\tpublic static final int NEG_DOUBLE = 0x80;\n\tpublic static final int INT_TO_LONG = 0x81;\n\tpublic static final int INT_TO_FLOAT = 0x82;\n\tpublic static final int INT_TO_DOUBLE = 0x83;\n\tpublic static final int LONG_TO_INT = 0x84;\n\tpublic static final int LONG_TO_FLOAT = 0x85;\n\tpublic static final int LONG_TO_DOUBLE = 0x86;\n\tpublic static final int FLOAT_TO_INT = 0x87;\n\tpublic static final int FLOAT_TO_LONG = 0x88;\n\tpublic static final int FLOAT_TO_DOUBLE = 0x89;\n\tpublic static final int DOUBLE_TO_INT = 0x8a;\n\tpublic static final int DOUBLE_TO_LONG = 0x8b;\n\tpublic static final int DOUBLE_TO_FLOAT = 0x8c;\n\tpublic static final int INT_TO_BYTE = 0x8d;\n\tpublic static final int INT_TO_CHAR = 0x8e;\n\tpublic static final int INT_TO_SHORT = 0x8f;\n\tpublic static final int ADD_INT = 0x90;\n\tpublic static final int SUB_INT = 0x91;\n\tpublic static final int MUL_INT = 0x92;\n\tpublic static final int DIV_INT = 0x93;\n\tpublic static final int REM_INT = 0x94;\n\tpublic static final int AND_INT = 0x95;\n\tpublic static final int OR_INT = 0x96;\n\tpublic static final int XOR_INT = 0x97;\n\tpublic static final int SHL_INT = 0x98;\n\tpublic static final int SHR_INT = 0x99;\n\tpublic static final int USHR_INT = 0x9a;\n\tpublic static final int ADD_LONG = 0x9b;\n\tpublic static final int SUB_LONG = 0x9c;\n\tpublic static final int MUL_LONG = 0x9d;\n\tpublic static final int DIV_LONG = 0x9e;\n\tpublic static final int REM_LONG = 0x9f;\n\tpublic static final int AND_LONG = 0xa0;\n\tpublic static final int OR_LONG = 0xa1;\n\tpublic static final int XOR_LONG = 0xa2;\n\tpublic static final int SHL_LONG = 0xa3;\n\tpublic static final int SHR_LONG = 0xa4;\n\tpublic static final int USHR_LONG = 0xa5;\n\tpublic static final int ADD_FLOAT = 0xa6;\n\tpublic static final int SUB_FLOAT = 0xa7;\n\tpublic static final int MUL_FLOAT = 0xa8;\n\tpublic static final int DIV_FLOAT = 0xa9;\n\tpublic static final int REM_FLOAT = 0xaa;\n\tpublic static final int ADD_DOUBLE = 0xab;\n\tpublic static final int SUB_DOUBLE = 0xac;\n\tpublic static final int MUL_DOUBLE = 0xad;\n\tpublic static final int DIV_DOUBLE = 0xae;\n\tpublic static final int REM_DOUBLE = 0xaf;\n\tpublic static final int ADD_INT_2ADDR = 0xb0;\n\tpublic static final int SUB_INT_2ADDR = 0xb1;\n\tpublic static final int MUL_INT_2ADDR = 0xb2;\n\tpublic static final int DIV_INT_2ADDR = 0xb3;\n\tpublic static final int REM_INT_2ADDR = 0xb4;\n\tpublic static final int AND_INT_2ADDR = 0xb5;\n\tpublic static final int OR_INT_2ADDR = 0xb6;\n\tpublic static final int XOR_INT_2ADDR = 0xb7;\n\tpublic static final int SHL_INT_2ADDR = 0xb8;\n\tpublic static final int SHR_INT_2ADDR = 0xb9;\n\tpublic static final int USHR_INT_2ADDR = 0xba;\n\tpublic static final int ADD_LONG_2ADDR = 0xbb;\n\tpublic static final int SUB_LONG_2ADDR = 0xbc;\n\tpublic static final int MUL_LONG_2ADDR = 0xbd;\n\tpublic static final int DIV_LONG_2ADDR = 0xbe;\n\tpublic static final int REM_LONG_2ADDR = 0xbf;\n\tpublic static final int AND_LONG_2ADDR = 0xc0;\n\tpublic static final int OR_LONG_2ADDR = 0xc1;\n\tpublic static final int XOR_LONG_2ADDR = 0xc2;\n\tpublic static final int SHL_LONG_2ADDR = 0xc3;\n\tpublic static final int SHR_LONG_2ADDR = 0xc4;\n\tpublic static final int USHR_LONG_2ADDR = 0xc5;\n\tpublic static final int ADD_FLOAT_2ADDR = 0xc6;\n\tpublic static final int SUB_FLOAT_2ADDR = 0xc7;\n\tpublic static final int MUL_FLOAT_2ADDR = 0xc8;\n\tpublic static final int DIV_FLOAT_2ADDR = 0xc9;\n\tpublic static final int REM_FLOAT_2ADDR = 0xca;\n\tpublic static final int ADD_DOUBLE_2ADDR = 0xcb;\n\tpublic static final int SUB_DOUBLE_2ADDR = 0xcc;\n\tpublic static final int MUL_DOUBLE_2ADDR = 0xcd;\n\tpublic static final int DIV_DOUBLE_2ADDR = 0xce;\n\tpublic static final int REM_DOUBLE_2ADDR = 0xcf;\n\tpublic static final int ADD_INT_LIT16 = 0xd0;\n\tpublic static final int RSUB_INT = 0xd1;\n\tpublic static final int MUL_INT_LIT16 = 0xd2;\n\tpublic static final int DIV_INT_LIT16 = 0xd3;\n\tpublic static final int REM_INT_LIT16 = 0xd4;\n\tpublic static final int AND_INT_LIT16 = 0xd5;\n\tpublic static final int OR_INT_LIT16 = 0xd6;\n\tpublic static final int XOR_INT_LIT16 = 0xd7;\n\tpublic static final int ADD_INT_LIT8 = 0xd8;\n\tpublic static final int RSUB_INT_LIT8 = 0xd9;\n\tpublic static final int MUL_INT_LIT8 = 0xda;\n\tpublic static final int DIV_INT_LIT8 = 0xdb;\n\tpublic static final int REM_INT_LIT8 = 0xdc;\n\tpublic static final int AND_INT_LIT8 = 0xdd;\n\tpublic static final int OR_INT_LIT8 = 0xde;\n\tpublic static final int XOR_INT_LIT8 = 0xdf;\n\tpublic static final int SHL_INT_LIT8 = 0xe0;\n\tpublic static final int SHR_INT_LIT8 = 0xe1;\n\tpublic static final int USHR_INT_LIT8 = 0xe2;\n\tpublic static final int INVOKE_POLYMORPHIC = 0xfa;\n\tpublic static final int INVOKE_POLYMORPHIC_RANGE = 0xfb;\n\tpublic static final int INVOKE_CUSTOM = 0xfc;\n\tpublic static final int INVOKE_CUSTOM_RANGE = 0xfd;\n\tpublic static final int CONST_METHOD_HANDLE = 0xfe;\n\tpublic static final int CONST_METHOD_TYPE = 0xff;\n\n\t// payload pseudo-instructions\n\tpublic static final int PACKED_SWITCH_PAYLOAD = 0x0100;\n\tpublic static final int SPARSE_SWITCH_PAYLOAD = 0x0200;\n\tpublic static final int FILL_ARRAY_DATA_PAYLOAD = 0x0300;\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/insns/payloads/DexArrayPayload.java",
    "content": "package jadx.plugins.input.dex.insns.payloads;\n\nimport jadx.api.plugins.input.insns.custom.IArrayPayload;\n\npublic class DexArrayPayload implements IArrayPayload {\n\n\tprivate final int size;\n\tprivate final int elemSize;\n\tprivate final Object data;\n\n\tpublic DexArrayPayload(int size, int elemSize, Object data) {\n\t\tthis.size = size;\n\t\tthis.elemSize = elemSize;\n\t\tthis.data = data;\n\t}\n\n\t@Override\n\tpublic int getSize() {\n\t\treturn size;\n\t}\n\n\t@Override\n\tpublic int getElementSize() {\n\t\treturn elemSize;\n\t}\n\n\t@Override\n\tpublic Object getData() {\n\t\treturn data;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexAnnotationsConvert.java",
    "content": "package jadx.plugins.input.dex.sections;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.input.data.annotations.AnnotationVisibility;\nimport jadx.api.plugins.input.data.annotations.EncodedType;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationDefaultClassAttr;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationsAttr;\nimport jadx.api.plugins.input.data.attributes.types.ExceptionsAttr;\nimport jadx.api.plugins.input.data.attributes.types.InnerClassesAttr;\nimport jadx.api.plugins.input.data.attributes.types.InnerClsInfo;\nimport jadx.api.plugins.input.data.attributes.types.MethodParametersAttr;\nimport jadx.api.plugins.input.data.attributes.types.SignatureAttr;\nimport jadx.api.plugins.utils.Utils;\nimport jadx.plugins.input.dex.sections.annotations.AnnotationsUtils;\n\npublic class DexAnnotationsConvert {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DexAnnotationsConvert.class);\n\n\tpublic static void forClass(String cls, List<IJadxAttribute> list, List<IAnnotation> annotationList) {\n\t\tappendAnnotations(cls, list, annotationList);\n\t}\n\n\tpublic static void forMethod(List<IJadxAttribute> list, List<IAnnotation> annotationList) {\n\t\tappendAnnotations(null, list, annotationList);\n\t}\n\n\tpublic static void forField(List<IJadxAttribute> list, List<IAnnotation> annotationList) {\n\t\tappendAnnotations(null, list, annotationList);\n\t}\n\n\tprivate static void appendAnnotations(@Nullable String cls, List<IJadxAttribute> attributes, List<IAnnotation> annotations) {\n\t\tif (annotations.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (IAnnotation annotation : annotations) {\n\t\t\tif (annotation.getVisibility() == AnnotationVisibility.SYSTEM) {\n\t\t\t\tconvertSystemAnnotations(cls, attributes, annotation);\n\t\t\t}\n\t\t}\n\t\tUtils.addToList(attributes, AnnotationsAttr.pack(annotations));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static void convertSystemAnnotations(@Nullable String cls, List<IJadxAttribute> attributes, IAnnotation annotation) {\n\t\tswitch (annotation.getAnnotationClass()) {\n\t\t\tcase \"Ldalvik/annotation/Signature;\":\n\t\t\t\tattributes.add(new SignatureAttr(extractSignature(annotation)));\n\t\t\t\tbreak;\n\n\t\t\tcase \"Ldalvik/annotation/InnerClass;\":\n\t\t\t\ttry {\n\t\t\t\t\tString name = AnnotationsUtils.getValue(annotation, \"name\", EncodedType.ENCODED_STRING, null);\n\t\t\t\t\tint accFlags = AnnotationsUtils.getValue(annotation, \"accessFlags\", EncodedType.ENCODED_INT, 0);\n\t\t\t\t\tif (name != null || accFlags != 0) {\n\t\t\t\t\t\tInnerClsInfo innerClsInfo = new InnerClsInfo(cls, null, name, accFlags);\n\t\t\t\t\t\tattributes.add(new InnerClassesAttr(Collections.singletonMap(cls, innerClsInfo)));\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.warn(\"Failed to parse annotation: {}\", annotation, e);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"Ldalvik/annotation/AnnotationDefault;\":\n\t\t\t\tEncodedValue annValue = annotation.getDefaultValue();\n\t\t\t\tif (annValue != null && annValue.getType() == EncodedType.ENCODED_ANNOTATION) {\n\t\t\t\t\tIAnnotation defAnnotation = (IAnnotation) annValue.getValue();\n\t\t\t\t\tattributes.add(new AnnotationDefaultClassAttr(defAnnotation.getValues()));\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"Ldalvik/annotation/Throws;\":\n\t\t\t\ttry {\n\t\t\t\t\tEncodedValue defaultValue = annotation.getDefaultValue();\n\t\t\t\t\tif (defaultValue != null) {\n\t\t\t\t\t\tList<String> excs = ((List<EncodedValue>) defaultValue.getValue())\n\t\t\t\t\t\t\t\t.stream()\n\t\t\t\t\t\t\t\t.map(ev -> ((String) ev.getValue()))\n\t\t\t\t\t\t\t\t.collect(Collectors.toList());\n\t\t\t\t\t\tattributes.add(new ExceptionsAttr(excs));\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.warn(\"Failed to convert dalvik throws annotation\", e);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"Ldalvik/annotation/MethodParameters;\":\n\t\t\t\ttry {\n\t\t\t\t\tList<EncodedValue> names = AnnotationsUtils.getArray(annotation, \"names\");\n\t\t\t\t\tList<EncodedValue> accFlags = AnnotationsUtils.getArray(annotation, \"accessFlags\");\n\t\t\t\t\tif (!names.isEmpty() && names.size() == accFlags.size()) {\n\t\t\t\t\t\tint size = names.size();\n\t\t\t\t\t\tList<MethodParametersAttr.Info> list = new ArrayList<>(size);\n\t\t\t\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\t\t\t\tString name = (String) names.get(i).getValue();\n\t\t\t\t\t\t\tint accFlag = (int) accFlags.get(i).getValue();\n\t\t\t\t\t\t\tlist.add(new MethodParametersAttr.Info(accFlag, name));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tattributes.add(new MethodParametersAttr(list));\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.warn(\"Failed to parse annotation: {}\", annotation, e);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t@SuppressWarnings({ \"unchecked\", \"ConstantConditions\" })\n\tprivate static String extractSignature(IAnnotation annotation) {\n\t\tList<EncodedValue> values = (List<EncodedValue>) annotation.getDefaultValue().getValue();\n\t\tif (values.size() == 1) {\n\t\t\treturn (String) values.get(0).getValue();\n\t\t}\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (EncodedValue part : values) {\n\t\t\tsb.append((String) part.getValue());\n\t\t}\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexClassData.java",
    "content": "package jadx.plugins.input.dex.sections;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.input.data.IClassData;\nimport jadx.api.plugins.input.data.IFieldData;\nimport jadx.api.plugins.input.data.IMethodData;\nimport jadx.api.plugins.input.data.ISeqConsumer;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.api.plugins.input.data.attributes.types.SourceFileAttr;\nimport jadx.plugins.input.dex.sections.annotations.AnnotationsParser;\nimport jadx.plugins.input.dex.utils.SmaliUtils;\n\npublic class DexClassData implements IClassData {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DexClassData.class);\n\tpublic static final int SIZE = 8 * 4;\n\n\tprivate final SectionReader in;\n\tprivate final AnnotationsParser annotationsParser;\n\tprivate final int inputFileOffset;\n\n\tpublic DexClassData(SectionReader sectionReader, AnnotationsParser annotationsParser) {\n\t\tthis.in = sectionReader;\n\t\tthis.annotationsParser = annotationsParser;\n\t\tthis.inputFileOffset = this.in.getOffset();\n\t}\n\n\t@Override\n\tpublic int getInputFileOffset() {\n\t\treturn this.inputFileOffset;\n\t}\n\n\t@Override\n\tpublic IClassData copy() {\n\t\treturn new DexClassData(in.copy(), annotationsParser.copy());\n\t}\n\n\t@Override\n\tpublic String getType() {\n\t\tint typeIdx = in.pos(0).readInt();\n\t\tString clsType = in.getType(typeIdx);\n\t\tif (clsType == null) {\n\t\t\tthrow new NullPointerException(\"Unknown class type\");\n\t\t}\n\t\treturn clsType;\n\t}\n\n\t@Override\n\tpublic int getAccessFlags() {\n\t\treturn in.pos(4).readInt();\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic String getSuperType() {\n\t\tint typeIdx = in.pos(2 * 4).readInt();\n\t\treturn in.getType(typeIdx);\n\t}\n\n\t@Override\n\tpublic List<String> getInterfacesTypes() {\n\t\tint offset = in.pos(3 * 4).readInt();\n\t\tif (offset == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn in.absPos(offset).readTypeList();\n\t}\n\n\t@Nullable\n\tprivate String getSourceFile() {\n\t\tint strIdx = in.pos(4 * 4).readInt();\n\t\treturn in.getString(strIdx);\n\t}\n\n\t@Override\n\tpublic String getInputFileName() {\n\t\treturn in.getDexReader().getInputFileName();\n\t}\n\n\tpublic int getAnnotationsOff() {\n\t\treturn in.pos(5 * 4).readInt();\n\t}\n\n\tpublic int getClassDataOff() {\n\t\treturn in.pos(6 * 4).readInt();\n\t}\n\n\tpublic int getStaticValuesOff() {\n\t\treturn in.pos(7 * 4).readInt();\n\t}\n\n\t@Override\n\tpublic void visitFieldsAndMethods(ISeqConsumer<IFieldData> fieldConsumer, ISeqConsumer<IMethodData> mthConsumer) {\n\t\tint classDataOff = getClassDataOff();\n\t\tif (classDataOff == 0) {\n\t\t\treturn;\n\t\t}\n\t\tSectionReader data = in.copy(classDataOff);\n\t\tint staticFieldsCount = data.readUleb128();\n\t\tint instanceFieldsCount = data.readUleb128();\n\t\tint directMthCount = data.readUleb128();\n\t\tint virtualMthCount = data.readUleb128();\n\n\t\tfieldConsumer.init(staticFieldsCount + instanceFieldsCount);\n\t\tmthConsumer.init(directMthCount + virtualMthCount);\n\n\t\tannotationsParser.setOffset(getAnnotationsOff());\n\t\tvisitFields(fieldConsumer, data, staticFieldsCount, instanceFieldsCount);\n\t\tvisitMethods(mthConsumer, data, directMthCount, virtualMthCount);\n\t}\n\n\tprivate void visitFields(Consumer<IFieldData> fieldConsumer, SectionReader data, int staticFieldsCount, int instanceFieldsCount) {\n\t\tMap<Integer, Integer> annotationOffsetMap = annotationsParser.readFieldsAnnotationOffsetMap();\n\t\tDexFieldData fieldData = new DexFieldData(annotationsParser);\n\t\tfieldData.setParentClassType(getType());\n\t\treadFields(fieldConsumer, data, fieldData, staticFieldsCount, annotationOffsetMap, true);\n\t\treadFields(fieldConsumer, data, fieldData, instanceFieldsCount, annotationOffsetMap, false);\n\t}\n\n\tprivate void readFields(Consumer<IFieldData> fieldConsumer, SectionReader data, DexFieldData fieldData, int count,\n\t\t\tMap<Integer, Integer> annOffsetMap, boolean staticFields) {\n\t\tList<EncodedValue> constValues = staticFields ? getStaticFieldInitValues(data.copy()) : null;\n\t\tint fieldId = 0;\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tfieldId += data.readUleb128();\n\t\t\tint accFlags = data.readUleb128();\n\t\t\tin.fillFieldData(fieldData, fieldId);\n\t\t\tfieldData.setAccessFlags(accFlags);\n\t\t\tfieldData.setAnnotationsOffset(getOffsetFromMap(fieldId, annOffsetMap));\n\t\t\tfieldData.setConstValue(staticFields && i < constValues.size() ? constValues.get(i) : null);\n\t\t\tfieldConsumer.accept(fieldData);\n\t\t}\n\t}\n\n\tprivate void visitMethods(Consumer<IMethodData> mthConsumer, SectionReader data, int directMthCount, int virtualMthCount) {\n\t\tDexMethodData methodData = new DexMethodData(annotationsParser);\n\t\tmethodData.setMethodRef(new DexMethodRef());\n\t\tMap<Integer, Integer> annotationOffsetMap = annotationsParser.readMethodsAnnotationOffsetMap();\n\t\tMap<Integer, Integer> paramsAnnOffsetMap = annotationsParser.readMethodParamsAnnRefOffsetMap();\n\n\t\treadMethods(mthConsumer, data, methodData, directMthCount, annotationOffsetMap, paramsAnnOffsetMap);\n\t\treadMethods(mthConsumer, data, methodData, virtualMthCount, annotationOffsetMap, paramsAnnOffsetMap);\n\t}\n\n\tprivate void readMethods(Consumer<IMethodData> mthConsumer, SectionReader data, DexMethodData methodData, int count,\n\t\t\tMap<Integer, Integer> annotationOffsetMap, Map<Integer, Integer> paramsAnnOffsetMap) {\n\t\tDexCodeReader dexCodeReader = new DexCodeReader(in.copy());\n\t\tint mthIdx = 0;\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tmthIdx += data.readUleb128();\n\t\t\tint accFlags = data.readUleb128();\n\t\t\tint codeOff = data.readUleb128();\n\n\t\t\tDexMethodRef methodRef = methodData.getMethodRef();\n\t\t\tmethodRef.reset();\n\t\t\tin.initMethodRef(mthIdx, methodRef);\n\t\t\tmethodData.setAccessFlags(accFlags);\n\t\t\tif (codeOff == 0) {\n\t\t\t\tmethodData.setCodeReader(null);\n\t\t\t} else {\n\t\t\t\tdexCodeReader.setMthId(mthIdx);\n\t\t\t\tdexCodeReader.setOffset(codeOff);\n\t\t\t\tmethodData.setCodeReader(dexCodeReader);\n\t\t\t}\n\t\t\tmethodData.setAnnotationsOffset(getOffsetFromMap(mthIdx, annotationOffsetMap));\n\t\t\tmethodData.setParamAnnotationsOffset(getOffsetFromMap(mthIdx, paramsAnnOffsetMap));\n\t\t\tmthConsumer.accept(methodData);\n\t\t}\n\t}\n\n\tprivate static int getOffsetFromMap(int idx, Map<Integer, Integer> annOffsetMap) {\n\t\tInteger offset = annOffsetMap.get(idx);\n\t\treturn offset != null ? offset : 0;\n\t}\n\n\tprivate List<EncodedValue> getStaticFieldInitValues(SectionReader reader) {\n\t\tint staticValuesOff = getStaticValuesOff();\n\t\tif (staticValuesOff == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treader.absPos(staticValuesOff);\n\t\treturn annotationsParser.parseEncodedArray(reader);\n\t}\n\n\tprivate List<IAnnotation> getAnnotations() {\n\t\tannotationsParser.setOffset(getAnnotationsOff());\n\t\treturn annotationsParser.readClassAnnotations();\n\t}\n\n\t@Override\n\tpublic List<IJadxAttribute> getAttributes() {\n\t\tList<IJadxAttribute> list = new ArrayList<>();\n\t\tString sourceFile = getSourceFile();\n\t\tif (sourceFile != null && !sourceFile.isEmpty()) {\n\t\t\tlist.add(new SourceFileAttr(sourceFile));\n\t\t}\n\t\tDexAnnotationsConvert.forClass(getType(), list, getAnnotations());\n\t\treturn list;\n\t}\n\n\tpublic int getClassDefOffset() {\n\t\treturn in.pos(0).getAbsPos();\n\t}\n\n\t@Override\n\tpublic String getDisassembledCode() {\n\t\tbyte[] dexBuf = in.getDexReader().getBuf().array();\n\t\treturn SmaliUtils.getSmaliCode(dexBuf, getClassDefOffset());\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getType();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexCodeReader.java",
    "content": "package jadx.plugins.input.dex.sections;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.ICatch;\nimport jadx.api.plugins.input.data.ICodeReader;\nimport jadx.api.plugins.input.data.IDebugInfo;\nimport jadx.api.plugins.input.data.ITry;\nimport jadx.api.plugins.input.data.impl.CatchData;\nimport jadx.api.plugins.input.data.impl.TryData;\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.core.utils.exceptions.InvalidDataException;\nimport jadx.plugins.input.dex.DexException;\nimport jadx.plugins.input.dex.insns.DexInsnData;\nimport jadx.plugins.input.dex.insns.DexInsnFormat;\nimport jadx.plugins.input.dex.insns.DexInsnInfo;\nimport jadx.plugins.input.dex.sections.debuginfo.DebugInfoParser;\n\npublic class DexCodeReader implements ICodeReader {\n\n\tprivate final SectionReader in;\n\tprivate int mthId;\n\n\tpublic DexCodeReader(SectionReader in) {\n\t\tthis.in = in;\n\t}\n\n\t@Override\n\tpublic DexCodeReader copy() {\n\t\tDexCodeReader copy = new DexCodeReader(in.copy());\n\t\tcopy.setMthId(this.getMthId());\n\t\treturn copy;\n\t}\n\n\tpublic void setOffset(int offset) {\n\t\tthis.in.setOffset(offset);\n\t}\n\n\t@Override\n\tpublic int getRegistersCount() {\n\t\treturn in.pos(0).readUShort();\n\t}\n\n\t@Override\n\tpublic int getArgsStartReg() {\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic int getUnitsCount() {\n\t\treturn in.pos(12).readInt();\n\t}\n\n\t@Override\n\tpublic void visitInstructions(Consumer<InsnData> insnConsumer) {\n\t\tDexInsnData insnData = new DexInsnData(this, in.copy());\n\t\tin.pos(12);\n\t\tint size = in.readInt();\n\t\tint offset = 0; // in code units (2 byte)\n\t\twhile (offset < size) {\n\t\t\tint insnStart = in.getAbsPos();\n\t\t\tint opcodeUnit = in.readUShort();\n\t\t\tDexInsnInfo insnInfo = DexInsnInfo.get(opcodeUnit);\n\t\t\tinsnData.setInsnStart(insnStart);\n\t\t\tinsnData.setOffset(offset);\n\t\t\tinsnData.setInsnInfo(insnInfo);\n\t\t\tinsnData.setOpcodeUnit(opcodeUnit);\n\t\t\tinsnData.setPayload(null);\n\t\t\tinsnData.setDecoded(false);\n\t\t\tif (insnInfo != null) {\n\t\t\t\tDexInsnFormat format = insnInfo.getFormat();\n\t\t\t\tinsnData.setRegsCount(format.getRegsCount());\n\t\t\t\tinsnData.setLength(format.getLength());\n\t\t\t} else {\n\t\t\t\tinsnData.setRegsCount(0);\n\t\t\t\tinsnData.setLength(1);\n\t\t\t}\n\n\t\t\tinsnConsumer.accept(insnData);\n\n\t\t\tif (!insnData.isDecoded()) {\n\t\t\t\tskip(insnData);\n\t\t\t}\n\t\t\toffset += insnData.getLength();\n\t\t}\n\t}\n\n\tpublic void decode(DexInsnData insn) {\n\t\tDexInsnFormat format = insn.getInsnInfo().getFormat();\n\t\tformat.decode(insn, insn.getOpcodeUnit(), insn.getCodeData().in);\n\t\tinsn.setDecoded(true);\n\t}\n\n\tpublic void skip(DexInsnData insn) {\n\t\tDexInsnInfo insnInfo = insn.getInsnInfo();\n\t\tif (insnInfo != null) {\n\t\t\tDexCodeReader codeReader = insn.getCodeData();\n\t\t\tinsnInfo.getFormat().skip(insn, codeReader.in);\n\t\t}\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic IDebugInfo getDebugInfo() {\n\t\tint debugOff = in.pos(8).readInt();\n\t\tif (debugOff == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tif (debugOff < 0 || debugOff > in.size()) {\n\t\t\tthrow new InvalidDataException(\"Invalid debug info offset\");\n\t\t}\n\t\tint regsCount = getRegistersCount();\n\t\tDebugInfoParser debugInfoParser = new DebugInfoParser(in, regsCount, getUnitsCount());\n\t\tdebugInfoParser.initMthArgs(regsCount, in.getMethodParamTypes(mthId));\n\t\treturn debugInfoParser.process(debugOff);\n\t}\n\n\tprivate int getTriesCount() {\n\t\treturn in.pos(6).readUShort();\n\t}\n\n\tprivate int getTriesOffset() {\n\t\tint triesCount = getTriesCount();\n\t\tif (triesCount == 0) {\n\t\t\treturn -1;\n\t\t}\n\t\tint insnsCount = getUnitsCount();\n\t\tint padding = insnsCount % 2 == 1 ? 2 : 0;\n\t\treturn 4 * 4 + insnsCount * 2 + padding;\n\t}\n\n\t@Override\n\tpublic List<ITry> getTries() {\n\t\tint triesOffset = getTriesOffset();\n\t\tif (triesOffset == -1) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tint triesCount = getTriesCount();\n\t\tMap<Integer, ICatch> catchHandlers = getCatchHandlers(triesOffset + 8 * triesCount, in.copy());\n\t\tin.pos(triesOffset);\n\t\tList<ITry> triesList = new ArrayList<>(triesCount);\n\t\tfor (int i = 0; i < triesCount; i++) {\n\t\t\tint startAddr = in.readInt();\n\t\t\tint insnsCount = in.readUShort();\n\t\t\tint handlerOff = in.readUShort();\n\t\t\tICatch catchHandler = catchHandlers.get(handlerOff);\n\t\t\tif (catchHandler == null) {\n\t\t\t\tthrow new DexException(\"Catch handler not found by byte offset: \" + handlerOff);\n\t\t\t}\n\t\t\ttriesList.add(new TryData(startAddr, startAddr + insnsCount - 1, catchHandler));\n\t\t}\n\t\treturn triesList;\n\t}\n\n\tprivate Map<Integer, ICatch> getCatchHandlers(int offset, SectionReader ext) {\n\t\tin.pos(offset);\n\t\tint byteOffsetStart = in.getAbsPos();\n\t\tint size = in.readUleb128();\n\t\tMap<Integer, ICatch> map = new HashMap<>(size);\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tint byteIndex = in.getAbsPos() - byteOffsetStart;\n\t\t\tint sizeAndType = in.readSleb128();\n\t\t\tint handlersLen = Math.abs(sizeAndType);\n\t\t\tint[] addr = new int[handlersLen];\n\t\t\tString[] types = new String[handlersLen];\n\t\t\tfor (int h = 0; h < handlersLen; h++) {\n\t\t\t\ttypes[h] = ext.getType(in.readUleb128());\n\t\t\t\taddr[h] = in.readUleb128();\n\t\t\t}\n\t\t\tint catchAllAddr;\n\t\t\tif (sizeAndType <= 0) {\n\t\t\t\tcatchAllAddr = in.readUleb128();\n\t\t\t} else {\n\t\t\t\tcatchAllAddr = -1;\n\t\t\t}\n\t\t\tmap.put(byteIndex, new CatchData(addr, types, catchAllAddr));\n\t\t}\n\t\treturn map;\n\t}\n\n\t@Override\n\tpublic int getCodeOffset() {\n\t\treturn in.getOffset();\n\t}\n\n\tpublic void setMthId(int mthId) {\n\t\tthis.mthId = mthId;\n\t}\n\n\tpublic int getMthId() {\n\t\treturn mthId;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexConsts.java",
    "content": "package jadx.plugins.input.dex.sections;\n\npublic class DexConsts {\n\n\tpublic static final byte[] DEX_FILE_MAGIC = { 0x64, 0x65, 0x78, 0x0a }; // 'dex\\n'\n\n\tpublic static final byte[] ZIP_FILE_MAGIC = { 0x50, 0x4B, 0x03, 0x04 };\n\n\tpublic static final int MAX_MAGIC_SIZE = 4;\n\n\tpublic static final int ENDIAN_CONSTANT = 0x12345678;\n\n\tpublic static final int NO_INDEX = -1;\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexFieldData.java",
    "content": "package jadx.plugins.input.dex.sections;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.IFieldData;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.api.plugins.utils.Utils;\nimport jadx.plugins.input.dex.sections.annotations.AnnotationsParser;\n\npublic class DexFieldData implements IFieldData {\n\t@Nullable\n\tprivate final AnnotationsParser annotationsParser;\n\n\tprivate String parentClassType;\n\tprivate String type;\n\tprivate String name;\n\tprivate int accessFlags;\n\tprivate int annotationsOffset;\n\tprivate EncodedValue constValue;\n\n\tpublic DexFieldData(@Nullable AnnotationsParser parser) {\n\t\tthis.annotationsParser = parser;\n\t}\n\n\t@Override\n\tpublic String getParentClassType() {\n\t\treturn parentClassType;\n\t}\n\n\tpublic void setParentClassType(String parentClassType) {\n\t\tthis.parentClassType = parentClassType;\n\t}\n\n\t@Override\n\tpublic String getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setType(String type) {\n\t\tthis.type = type;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\t@Override\n\tpublic int getAccessFlags() {\n\t\treturn accessFlags;\n\t}\n\n\tpublic void setAccessFlags(int accessFlags) {\n\t\tthis.accessFlags = accessFlags;\n\t}\n\n\tpublic void setAnnotationsOffset(int annotationsOffset) {\n\t\tthis.annotationsOffset = annotationsOffset;\n\t}\n\n\tpublic void setConstValue(EncodedValue constValue) {\n\t\tthis.constValue = constValue;\n\t}\n\n\tprivate List<IAnnotation> getAnnotations() {\n\t\tif (annotationsParser == null) {\n\t\t\tthrow new NullPointerException(\"Annotation parser not initialized\");\n\t\t}\n\t\treturn annotationsParser.readAnnotationList(annotationsOffset);\n\t}\n\n\t@Override\n\tpublic List<IJadxAttribute> getAttributes() {\n\t\tList<IJadxAttribute> list = new ArrayList<>(2);\n\t\tUtils.addToList(list, constValue);\n\t\tDexAnnotationsConvert.forField(list, getAnnotations());\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getParentClassType() + \"->\" + getName() + \":\" + getType();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexHeader.java",
    "content": "package jadx.plugins.input.dex.sections;\n\nimport jadx.plugins.input.dex.DexException;\n\npublic class DexHeader {\n\tprivate final String version;\n\tprivate final int classDefsSize;\n\tprivate final int classDefsOff;\n\tprivate final int stringIdsOff;\n\tprivate final int typeIdsOff;\n\tprivate final int typeIdsSize;\n\tprivate final int fieldIdsSize;\n\tprivate final int fieldIdsOff;\n\tprivate final int protoIdsSize;\n\tprivate final int protoIdsOff;\n\tprivate final int methodIdsOff;\n\tprivate final int methodIdsSize;\n\n\tprivate int callSiteOff;\n\tprivate int methodHandleOff;\n\n\tpublic DexHeader(SectionReader buf) {\n\t\tbyte[] magic = buf.readByteArray(4);\n\t\tversion = buf.readString(3);\n\t\tbuf.skip(1);\n\t\tint checksum = buf.readInt();\n\t\tbyte[] signature = buf.readByteArray(20);\n\t\tint fileSize = buf.readInt();\n\t\tint headerSize = buf.readInt();\n\t\tint endianTag = buf.readInt();\n\t\tif (endianTag != DexConsts.ENDIAN_CONSTANT) {\n\t\t\tthrow new DexException(\"Unexpected endian tag: 0x\" + Integer.toHexString(endianTag));\n\t\t}\n\t\tint linkSize = buf.readInt();\n\t\tint linkOff = buf.readInt();\n\t\tint mapListOff = buf.readInt();\n\t\tint stringIdsSize = buf.readInt();\n\t\tstringIdsOff = buf.readInt();\n\t\ttypeIdsSize = buf.readInt();\n\t\ttypeIdsOff = buf.readInt();\n\t\tprotoIdsSize = buf.readInt();\n\t\tprotoIdsOff = buf.readInt();\n\t\tfieldIdsSize = buf.readInt();\n\t\tfieldIdsOff = buf.readInt();\n\t\tmethodIdsSize = buf.readInt();\n\t\tmethodIdsOff = buf.readInt();\n\t\tclassDefsSize = buf.readInt();\n\t\tclassDefsOff = buf.readInt();\n\t\tint dataSize = buf.readInt();\n\t\tint dataOff = buf.readInt();\n\n\t\treadMapList(buf, mapListOff);\n\t}\n\n\tprivate void readMapList(SectionReader buf, int mapListOff) {\n\t\tbuf.absPos(mapListOff);\n\t\tint size = buf.readInt();\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tint type = buf.readUShort();\n\t\t\tbuf.skip(6);\n\t\t\tint offset = buf.readInt();\n\n\t\t\tswitch (type) {\n\t\t\t\tcase 0x0007:\n\t\t\t\t\tcallSiteOff = offset;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 0x0008:\n\t\t\t\t\tmethodHandleOff = offset;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic String getVersion() {\n\t\treturn version;\n\t}\n\n\tpublic int getClassDefsSize() {\n\t\treturn classDefsSize;\n\t}\n\n\tpublic int getClassDefsOff() {\n\t\treturn classDefsOff;\n\t}\n\n\tpublic int getStringIdsOff() {\n\t\treturn stringIdsOff;\n\t}\n\n\tpublic int getTypeIdsOff() {\n\t\treturn typeIdsOff;\n\t}\n\n\tpublic int getTypeIdsSize() {\n\t\treturn typeIdsSize;\n\t}\n\n\tpublic int getFieldIdsSize() {\n\t\treturn fieldIdsSize;\n\t}\n\n\tpublic int getFieldIdsOff() {\n\t\treturn fieldIdsOff;\n\t}\n\n\tpublic int getProtoIdsSize() {\n\t\treturn protoIdsSize;\n\t}\n\n\tpublic int getProtoIdsOff() {\n\t\treturn protoIdsOff;\n\t}\n\n\tpublic int getMethodIdsOff() {\n\t\treturn methodIdsOff;\n\t}\n\n\tpublic int getMethodIdsSize() {\n\t\treturn methodIdsSize;\n\t}\n\n\tpublic int getCallSiteOff() {\n\t\treturn callSiteOff;\n\t}\n\n\tpublic int getMethodHandleOff() {\n\t\treturn methodHandleOff;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexHeaderV41.java",
    "content": "package jadx.plugins.input.dex.sections;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport static jadx.plugins.input.dex.utils.DataReader.readU4;\n\npublic class DexHeaderV41 {\n\n\tpublic static @Nullable DexHeaderV41 readIfPresent(byte[] content) {\n\t\tint headerSize = readU4(content, 36);\n\t\tif (headerSize < 120) {\n\t\t\treturn null;\n\t\t}\n\t\tint fileSize = readU4(content, 32);\n\t\tint containerSize = readU4(content, 112);\n\t\tint headerOffset = readU4(content, 116);\n\t\treturn new DexHeaderV41(fileSize, containerSize, headerOffset);\n\t}\n\n\tpublic static List<Integer> readSubDexOffsets(byte[] content, DexHeaderV41 header) {\n\t\tint start = 0;\n\t\tint end = header.getFileSize();\n\t\tint limit = Math.min(header.getContainerSize(), content.length);\n\t\tList<Integer> list = new ArrayList<>();\n\t\twhile (true) {\n\t\t\tlist.add(start);\n\t\t\tstart = end;\n\t\t\tif (start >= limit) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tint nextFileSize = readU4(content, start + 32);\n\t\t\tend = start + nextFileSize;\n\t\t}\n\t\treturn list;\n\t}\n\n\tprivate final int fileSize;\n\tprivate final int containerSize;\n\tprivate final int headerOffset;\n\n\tpublic DexHeaderV41(int fileSize, int containerSize, int headerOffset) {\n\t\tthis.fileSize = fileSize;\n\t\tthis.containerSize = containerSize;\n\t\tthis.headerOffset = headerOffset;\n\t}\n\n\tpublic int getFileSize() {\n\t\treturn fileSize;\n\t}\n\n\tpublic int getContainerSize() {\n\t\treturn containerSize;\n\t}\n\n\tpublic int getHeaderOffset() {\n\t\treturn headerOffset;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexMethodData.java",
    "content": "package jadx.plugins.input.dex.sections;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.ICodeReader;\nimport jadx.api.plugins.input.data.IMethodData;\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationMethodParamsAttr;\nimport jadx.api.plugins.utils.Utils;\nimport jadx.plugins.input.dex.sections.annotations.AnnotationsParser;\nimport jadx.plugins.input.dex.smali.SmaliPrinter;\n\npublic class DexMethodData implements IMethodData {\n\t@Nullable\n\tprivate final AnnotationsParser annotationsParser;\n\n\tprivate DexMethodRef methodRef;\n\n\tprivate int accessFlags;\n\tprivate int annotationsOffset;\n\tprivate int paramAnnotationsOffset;\n\n\t@Nullable\n\tprivate DexCodeReader codeReader;\n\n\tpublic DexMethodData(@Nullable AnnotationsParser annotationsParser) {\n\t\tthis.annotationsParser = annotationsParser;\n\t}\n\n\t@Override\n\tpublic DexMethodRef getMethodRef() {\n\t\treturn methodRef;\n\t}\n\n\tpublic void setMethodRef(DexMethodRef methodRef) {\n\t\tthis.methodRef = methodRef;\n\t}\n\n\t@Override\n\tpublic int getAccessFlags() {\n\t\treturn accessFlags;\n\t}\n\n\tpublic void setAccessFlags(int accessFlags) {\n\t\tthis.accessFlags = accessFlags;\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic ICodeReader getCodeReader() {\n\t\treturn codeReader;\n\t}\n\n\tpublic void setCodeReader(@Nullable DexCodeReader codeReader) {\n\t\tthis.codeReader = codeReader;\n\t}\n\n\t@Override\n\tpublic String disassembleMethod() {\n\t\treturn SmaliPrinter.printMethod(this);\n\t}\n\n\tpublic void setAnnotationsOffset(int annotationsOffset) {\n\t\tthis.annotationsOffset = annotationsOffset;\n\t}\n\n\tpublic void setParamAnnotationsOffset(int paramAnnotationsOffset) {\n\t\tthis.paramAnnotationsOffset = paramAnnotationsOffset;\n\t}\n\n\tprivate List<IAnnotation> getAnnotations() {\n\t\treturn getAnnotationsParser().readAnnotationList(annotationsOffset);\n\t}\n\n\tprivate List<List<IAnnotation>> getParamsAnnotations() {\n\t\treturn getAnnotationsParser().readAnnotationRefList(paramAnnotationsOffset);\n\t}\n\n\t@Override\n\tpublic List<IJadxAttribute> getAttributes() {\n\t\tList<IJadxAttribute> list = new ArrayList<>();\n\t\tDexAnnotationsConvert.forMethod(list, getAnnotations());\n\t\tUtils.addToList(list, AnnotationMethodParamsAttr.pack(getParamsAnnotations()));\n\t\treturn list;\n\t}\n\n\tprivate AnnotationsParser getAnnotationsParser() {\n\t\tif (annotationsParser == null) {\n\t\t\tthrow new NullPointerException(\"Annotation parser not initialized\");\n\t\t}\n\t\treturn annotationsParser;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getMethodRef().toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexMethodProto.java",
    "content": "package jadx.plugins.input.dex.sections;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.IMethodProto;\nimport jadx.api.plugins.utils.Utils;\n\npublic class DexMethodProto implements IMethodProto {\n\tprivate final List<String> argTypes;\n\tprivate final String returnType;\n\n\tpublic DexMethodProto(List<String> argTypes, String returnType) {\n\t\tthis.returnType = returnType;\n\t\tthis.argTypes = argTypes;\n\t}\n\n\t@Override\n\tpublic List<String> getArgTypes() {\n\t\treturn argTypes;\n\t}\n\n\t@Override\n\tpublic String getReturnType() {\n\t\treturn returnType;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object other) {\n\t\tif (this == other) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(other instanceof IMethodProto)) {\n\t\t\treturn false;\n\t\t}\n\t\tIMethodProto that = (IMethodProto) other;\n\t\treturn argTypes.equals(that.getArgTypes())\n\t\t\t\t&& returnType.equals(that.getReturnType());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn 31 * argTypes.hashCode() + returnType.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"(\" + Utils.listToStr(argTypes) + \")\" + returnType;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/DexMethodRef.java",
    "content": "package jadx.plugins.input.dex.sections;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.utils.Utils;\nimport jadx.plugins.input.dex.DexReader;\n\npublic class DexMethodRef implements IMethodRef {\n\n\tprivate int uniqId;\n\tprivate String name;\n\tprivate String parentClassType;\n\tprivate String returnType;\n\tprivate List<String> argTypes;\n\n\t// lazy loading info\n\tprivate int dexIdx;\n\tprivate SectionReader sectionReader;\n\n\tpublic void initUniqId(DexReader dexReader, int idx) {\n\t\tthis.uniqId = (dexReader.getUniqId() & 0xFFFF) << 16 | (idx & 0xFFFF);\n\t}\n\n\t@Override\n\tpublic void load() {\n\t\tif (sectionReader != null) {\n\t\t\tsectionReader.loadMethodRef(this, dexIdx);\n\t\t\tsectionReader = null;\n\t\t}\n\t}\n\n\tpublic void setDexIdx(int dexIdx) {\n\t\tthis.dexIdx = dexIdx;\n\t}\n\n\tpublic void setSectionReader(SectionReader sectionReader) {\n\t\tthis.sectionReader = sectionReader;\n\t}\n\n\t@Override\n\tpublic int getUniqId() {\n\t\treturn uniqId;\n\t}\n\n\tpublic void reset() {\n\t\tname = null;\n\t\tparentClassType = null;\n\t\treturnType = null;\n\t\targTypes = null;\n\t}\n\n\t@Override\n\tpublic String getParentClassType() {\n\t\treturn parentClassType;\n\t}\n\n\tpublic void setParentClassType(String parentClassType) {\n\t\tthis.parentClassType = parentClassType;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\t@Override\n\tpublic String getReturnType() {\n\t\treturn returnType;\n\t}\n\n\tpublic void setReturnType(String returnType) {\n\t\tthis.returnType = returnType;\n\t}\n\n\t@Override\n\tpublic List<String> getArgTypes() {\n\t\treturn argTypes;\n\t}\n\n\tpublic void setArgTypes(List<String> argTypes) {\n\t\tthis.argTypes = argTypes;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tif (name == null) {\n\t\t\t// not loaded\n\t\t\treturn Integer.toHexString(uniqId);\n\t\t}\n\t\treturn getParentClassType() + \"->\" + getName() + '(' + Utils.listToStr(getArgTypes()) + \")\" + getReturnType();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/SectionReader.java",
    "content": "package jadx.plugins.input.dex.sections;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.ICallSite;\nimport jadx.api.plugins.input.data.IFieldRef;\nimport jadx.api.plugins.input.data.IMethodHandle;\nimport jadx.api.plugins.input.data.MethodHandleType;\nimport jadx.api.plugins.input.data.impl.CallSite;\nimport jadx.api.plugins.input.data.impl.FieldRefHandle;\nimport jadx.api.plugins.input.data.impl.MethodRefHandle;\nimport jadx.plugins.input.dex.DexReader;\nimport jadx.plugins.input.dex.sections.annotations.EncodedValueParser;\nimport jadx.plugins.input.dex.utils.Leb128;\nimport jadx.plugins.input.dex.utils.MUtf8;\n\nimport static jadx.plugins.input.dex.sections.DexConsts.NO_INDEX;\n\npublic class SectionReader {\n\tprivate final ByteBuffer buf;\n\tprivate final DexReader dexReader;\n\tprivate int offset;\n\n\tpublic SectionReader(DexReader dexReader, int off) {\n\t\tthis.dexReader = dexReader;\n\t\tthis.offset = off;\n\t\tthis.buf = duplicate(dexReader.getBuf(), off);\n\t}\n\n\tprivate SectionReader(SectionReader sectionReader, int off) {\n\t\tthis(sectionReader.dexReader, off);\n\t}\n\n\tpublic SectionReader copy() {\n\t\treturn new SectionReader(this, offset);\n\t}\n\n\tpublic SectionReader copy(int off) {\n\t\treturn new SectionReader(this, off);\n\t}\n\n\tpublic byte[] getByteCode(int start, int len) {\n\t\tint pos = buf.position();\n\t\tbuf.position(start);\n\t\tbyte[] bytes = readByteArray(len);\n\t\tbuf.position(pos);\n\t\treturn bytes;\n\t}\n\n\tprivate static ByteBuffer duplicate(ByteBuffer baseBuffer, int off) {\n\t\tByteBuffer dupBuf = baseBuffer.duplicate();\n\t\tdupBuf.order(ByteOrder.LITTLE_ENDIAN);\n\t\tdupBuf.position(off);\n\t\treturn dupBuf;\n\t}\n\n\tpublic void setOffset(int offset) {\n\t\tthis.offset = offset;\n\t}\n\n\tpublic int getOffset() {\n\t\treturn offset;\n\t}\n\n\tpublic void shiftOffset(int shift) {\n\t\tthis.offset += shift;\n\t}\n\n\tpublic SectionReader pos(int pos) {\n\t\tbuf.position(offset + pos);\n\t\treturn this;\n\t}\n\n\tpublic SectionReader absPos(int pos) {\n\t\tbuf.position(pos);\n\t\treturn this;\n\t}\n\n\tpublic int getAbsPos() {\n\t\treturn buf.position();\n\t}\n\n\tpublic void skip(int skip) {\n\t\tint pos = buf.position();\n\t\tbuf.position(pos + skip);\n\t}\n\n\tpublic int readInt() {\n\t\treturn buf.getInt();\n\t}\n\n\tpublic long readLong() {\n\t\treturn buf.getLong();\n\t}\n\n\tpublic byte readByte() {\n\t\treturn buf.get();\n\t}\n\n\tpublic int readUByte() {\n\t\treturn buf.get() & 0xFF;\n\t}\n\n\tpublic int readUShort() {\n\t\treturn buf.getShort() & 0xFFFF;\n\t}\n\n\tpublic int readShort() {\n\t\treturn buf.getShort();\n\t}\n\n\tpublic byte[] readByteArray(int len) {\n\t\tbyte[] arr = new byte[len];\n\t\tbuf.get(arr);\n\t\treturn arr;\n\t}\n\n\tpublic int[] readUShortArray(int size) {\n\t\tint[] arr = new int[size];\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tarr[i] = readUShort();\n\t\t}\n\t\treturn arr;\n\t}\n\n\tpublic String readString(int len) {\n\t\treturn new String(readByteArray(len), StandardCharsets.US_ASCII);\n\t}\n\n\tprivate List<String> readTypeListAt(int paramsOff) {\n\t\tif (paramsOff == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn absPos(paramsOff).readTypeList();\n\t}\n\n\tpublic List<String> readTypeList() {\n\t\tint size = readInt();\n\t\tif (size == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tint[] typeIds = readUShortArray(size);\n\t\tList<String> types = new ArrayList<>(size);\n\t\tfor (int typeId : typeIds) {\n\t\t\ttypes.add(getType(typeId));\n\t\t}\n\t\treturn types;\n\t}\n\n\t@Nullable\n\tpublic String getType(int idx) {\n\t\tif (idx == NO_INDEX) {\n\t\t\treturn null;\n\t\t}\n\t\tint typeIdsOff = dexReader.getHeader().getTypeIdsOff();\n\t\tabsPos(typeIdsOff + idx * 4);\n\t\tint strIdx = readInt();\n\t\treturn getString(strIdx);\n\t}\n\n\t@Nullable\n\tpublic String getString(int idx) {\n\t\tif (idx == NO_INDEX) {\n\t\t\treturn null;\n\t\t}\n\t\t// TODO: make string pool cache?\n\t\tint stringIdsOff = dexReader.getHeader().getStringIdsOff();\n\t\tabsPos(stringIdsOff + idx * 4);\n\t\tint strOff = readInt();\n\t\tabsPos(strOff);\n\t\treturn MUtf8.decode(this);\n\t}\n\n\tpublic IFieldRef getFieldRef(int idx) {\n\t\tDexFieldData fieldData = new DexFieldData(null);\n\t\tint clsTypeIdx = fillFieldData(fieldData, idx);\n\t\tfieldData.setParentClassType(getType(clsTypeIdx));\n\t\treturn fieldData;\n\t}\n\n\tpublic int fillFieldData(DexFieldData fieldData, int idx) {\n\t\tint fieldIdsOff = dexReader.getHeader().getFieldIdsOff();\n\t\tabsPos(fieldIdsOff + idx * 8);\n\t\tint classTypeIdx = readUShort();\n\t\tint typeIdx = readUShort();\n\t\tint nameIdx = readInt();\n\t\tfieldData.setType(getType(typeIdx));\n\t\tfieldData.setName(getString(nameIdx));\n\t\treturn classTypeIdx;\n\t}\n\n\tpublic DexMethodRef getMethodRef(int idx) {\n\t\tDexMethodRef methodRef = new DexMethodRef();\n\t\tinitMethodRef(idx, methodRef);\n\t\treturn methodRef;\n\t}\n\n\tpublic ICallSite getCallSite(int idx, SectionReader ext) {\n\t\tint callSiteOff = dexReader.getHeader().getCallSiteOff();\n\t\tabsPos(callSiteOff + idx * 4);\n\t\tabsPos(readInt());\n\t\treturn new CallSite(EncodedValueParser.parseEncodedArray(this, ext));\n\t}\n\n\tpublic IMethodHandle getMethodHandle(int idx) {\n\t\tint methodHandleOff = dexReader.getHeader().getMethodHandleOff();\n\t\tabsPos(methodHandleOff + idx * 8);\n\t\tMethodHandleType handleType = getMethodHandleType(readUShort());\n\t\tskip(2);\n\t\tint refId = readUShort();\n\t\tif (handleType.isField()) {\n\t\t\treturn new FieldRefHandle(handleType, getFieldRef(refId));\n\t\t}\n\t\treturn new MethodRefHandle(handleType, getMethodRef(refId));\n\t}\n\n\tprivate MethodHandleType getMethodHandleType(int type) {\n\t\tswitch (type) {\n\t\t\tcase 0x00:\n\t\t\t\treturn MethodHandleType.STATIC_PUT;\n\t\t\tcase 0x01:\n\t\t\t\treturn MethodHandleType.STATIC_GET;\n\t\t\tcase 0x02:\n\t\t\t\treturn MethodHandleType.INSTANCE_PUT;\n\t\t\tcase 0x03:\n\t\t\t\treturn MethodHandleType.INSTANCE_GET;\n\t\t\tcase 0x04:\n\t\t\t\treturn MethodHandleType.INVOKE_STATIC;\n\t\t\tcase 0x05:\n\t\t\t\treturn MethodHandleType.INVOKE_INSTANCE;\n\t\t\tcase 0x06:\n\t\t\t\treturn MethodHandleType.INVOKE_CONSTRUCTOR;\n\t\t\tcase 0x07:\n\t\t\t\treturn MethodHandleType.INVOKE_DIRECT;\n\t\t\tcase 0x08:\n\t\t\t\treturn MethodHandleType.INVOKE_INTERFACE;\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"Unknown method handle type: 0x\" + Integer.toHexString(type));\n\t\t}\n\t}\n\n\tpublic void initMethodRef(int idx, DexMethodRef methodRef) {\n\t\tmethodRef.initUniqId(dexReader, idx);\n\t\tmethodRef.setDexIdx(idx);\n\t\tmethodRef.setSectionReader(this);\n\t}\n\n\tpublic void loadMethodRef(DexMethodRef methodRef, int idx) {\n\t\tDexHeader header = dexReader.getHeader();\n\t\tint methodIdsOff = header.getMethodIdsOff();\n\t\tabsPos(methodIdsOff + idx * 8);\n\t\tint classTypeIdx = readUShort();\n\t\tint protoIdx = readUShort();\n\t\tint nameIdx = readInt();\n\n\t\tint protoIdsOff = header.getProtoIdsOff();\n\t\tabsPos(protoIdsOff + protoIdx * 12);\n\t\tskip(4); // shortyIdx\n\t\tint returnTypeIdx = readInt();\n\t\tint paramsOff = readInt();\n\n\t\tList<String> argTypes = readTypeListAt(paramsOff);\n\t\tmethodRef.setParentClassType(getType(classTypeIdx));\n\t\tmethodRef.setName(getString(nameIdx));\n\t\tmethodRef.setReturnType(getType(returnTypeIdx));\n\t\tmethodRef.setArgTypes(argTypes);\n\t}\n\n\tpublic DexMethodProto getMethodProto(int idx) {\n\t\tint protoIdsOff = dexReader.getHeader().getProtoIdsOff();\n\t\tabsPos(protoIdsOff + idx * 12);\n\t\tskip(4); // shortyIdx\n\t\tint returnTypeIdx = readInt();\n\t\tint paramsOff = readInt();\n\t\treturn new DexMethodProto(readTypeListAt(paramsOff), getType(returnTypeIdx));\n\t}\n\n\tpublic List<String> getMethodParamTypes(int idx) {\n\t\tDexHeader header = dexReader.getHeader();\n\t\tint methodIdsOff = header.getMethodIdsOff();\n\t\tabsPos(methodIdsOff + idx * 8 + 2);\n\t\tint protoIdx = readUShort();\n\n\t\tint protoIdsOff = header.getProtoIdsOff();\n\t\tabsPos(protoIdsOff + protoIdx * 12 + 8);\n\t\tint paramsOff = readInt();\n\n\t\tif (paramsOff == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn absPos(paramsOff).readTypeList();\n\t}\n\n\tpublic DexReader getDexReader() {\n\t\treturn dexReader;\n\t}\n\n\tpublic int readUleb128() {\n\t\treturn Leb128.readUnsignedLeb128(this);\n\t}\n\n\tpublic int readUleb128p1() {\n\t\treturn Leb128.readUnsignedLeb128(this) - 1;\n\t}\n\n\tpublic int readSleb128() {\n\t\treturn Leb128.readSignedLeb128(this);\n\t}\n\n\tpublic int size() {\n\t\treturn buf.capacity();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"SectionReader{buf=\" + buf + \", offset=\" + offset + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/annotations/AnnotationsParser.java",
    "content": "package jadx.plugins.input.dex.sections.annotations;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport jadx.api.plugins.input.data.annotations.AnnotationVisibility;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.annotations.JadxAnnotation;\nimport jadx.plugins.input.dex.DexException;\nimport jadx.plugins.input.dex.sections.SectionReader;\n\npublic class AnnotationsParser {\n\tprivate final SectionReader in;\n\tprivate final SectionReader ext;\n\n\tprivate int offset;\n\tprivate int fieldsCount;\n\tprivate int methodsCount;\n\tprivate int paramsRefCount;\n\n\tpublic AnnotationsParser(SectionReader in, SectionReader ext) {\n\t\tthis.in = in;\n\t\tthis.ext = ext;\n\t}\n\n\tpublic AnnotationsParser copy() {\n\t\treturn new AnnotationsParser(in.copy(), ext.copy());\n\t}\n\n\tpublic void setOffset(int offset) {\n\t\tthis.offset = offset;\n\t\tif (offset == 0) {\n\t\t\tthis.fieldsCount = 0;\n\t\t\tthis.methodsCount = 0;\n\t\t\tthis.paramsRefCount = 0;\n\t\t} else {\n\t\t\tin.setOffset(offset);\n\t\t\tin.pos(4);\n\t\t\tthis.fieldsCount = in.readInt();\n\t\t\tthis.methodsCount = in.readInt();\n\t\t\tthis.paramsRefCount = in.readInt();\n\t\t}\n\t}\n\n\tpublic List<IAnnotation> readClassAnnotations() {\n\t\tif (offset == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tint classAnnotationsOffset = in.absPos(offset).readInt();\n\t\treturn readAnnotationList(classAnnotationsOffset);\n\t}\n\n\tpublic Map<Integer, Integer> readFieldsAnnotationOffsetMap() {\n\t\tif (fieldsCount == 0) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tin.pos(4 * 4);\n\t\tMap<Integer, Integer> map = new HashMap<>(fieldsCount);\n\t\tfor (int i = 0; i < fieldsCount; i++) {\n\t\t\tint fieldIdx = in.readInt();\n\t\t\tint fieldAnnOffset = in.readInt();\n\t\t\tmap.put(fieldIdx, fieldAnnOffset);\n\t\t}\n\t\treturn map;\n\t}\n\n\tpublic Map<Integer, Integer> readMethodsAnnotationOffsetMap() {\n\t\tif (methodsCount == 0) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tin.pos(4 * 4 + fieldsCount * 2 * 4);\n\t\tMap<Integer, Integer> map = new HashMap<>(methodsCount);\n\t\tfor (int i = 0; i < methodsCount; i++) {\n\t\t\tint methodIdx = in.readInt();\n\t\t\tint methodAnnOffset = in.readInt();\n\t\t\tmap.put(methodIdx, methodAnnOffset);\n\t\t}\n\t\treturn map;\n\t}\n\n\tpublic Map<Integer, Integer> readMethodParamsAnnRefOffsetMap() {\n\t\tif (paramsRefCount == 0) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tin.pos(4 * 4 + fieldsCount * 2 * 4 + methodsCount * 2 * 4);\n\t\tMap<Integer, Integer> map = new HashMap<>(paramsRefCount);\n\t\tfor (int i = 0; i < paramsRefCount; i++) {\n\t\t\tint methodIdx = in.readInt();\n\t\t\tint methodAnnRefOffset = in.readInt();\n\t\t\tmap.put(methodIdx, methodAnnRefOffset);\n\t\t}\n\t\treturn map;\n\t}\n\n\tpublic List<IAnnotation> readAnnotationList(int offset) {\n\t\tif (offset == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tin.absPos(offset);\n\t\tint size = in.readInt();\n\t\tif (size == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<IAnnotation> list = new ArrayList<>(size);\n\t\tint pos = in.getAbsPos();\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tin.absPos(pos + i * 4);\n\t\t\tint annOffset = in.readInt();\n\t\t\tin.absPos(annOffset);\n\t\t\tlist.add(readAnnotation(in, ext, true));\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic List<List<IAnnotation>> readAnnotationRefList(int offset) {\n\t\tif (offset == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tin.absPos(offset);\n\t\tint size = in.readInt();\n\t\tif (size == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<List<IAnnotation>> list = new ArrayList<>(size);\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tint refOff = in.readInt();\n\t\t\tint pos = in.getAbsPos();\n\t\t\tlist.add(readAnnotationList(refOff));\n\t\t\tin.absPos(pos);\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic static IAnnotation readAnnotation(SectionReader in, SectionReader ext, boolean readVisibility) {\n\t\tAnnotationVisibility visibility = null;\n\t\tif (readVisibility) {\n\t\t\tint v = in.readUByte();\n\t\t\tvisibility = getVisibilityValue(v);\n\t\t}\n\t\tint typeIndex = in.readUleb128();\n\t\tint size = in.readUleb128();\n\t\tMap<String, EncodedValue> values = new LinkedHashMap<>(size);\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tString name = ext.getString(in.readUleb128());\n\t\t\tvalues.put(name, EncodedValueParser.parseValue(in, ext));\n\t\t}\n\t\tString type = ext.getType(typeIndex);\n\t\treturn new JadxAnnotation(visibility, type, values);\n\t}\n\n\tprivate static AnnotationVisibility getVisibilityValue(int value) {\n\t\tswitch (value) {\n\t\t\tcase 0:\n\t\t\t\treturn AnnotationVisibility.BUILD;\n\t\t\tcase 1:\n\t\t\t\treturn AnnotationVisibility.RUNTIME;\n\t\t\tcase 2:\n\t\t\t\treturn AnnotationVisibility.SYSTEM;\n\t\t\tdefault:\n\t\t\t\tthrow new DexException(\"Unknown annotation visibility value: \" + value);\n\t\t}\n\t}\n\n\tpublic EncodedValue parseEncodedValue(SectionReader in) {\n\t\treturn EncodedValueParser.parseValue(in, ext);\n\t}\n\n\tpublic List<EncodedValue> parseEncodedArray(SectionReader in) {\n\t\treturn EncodedValueParser.parseEncodedArray(in, ext);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/annotations/AnnotationsUtils.java",
    "content": "package jadx.plugins.input.dex.sections.annotations;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.annotations.EncodedType;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\n\npublic class AnnotationsUtils {\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static <T> T getValue(IAnnotation ann, String name, EncodedType type, T defValue) {\n\t\tif (ann == null || ann.getValues() == null || ann.getValues().isEmpty()) {\n\t\t\treturn defValue;\n\t\t}\n\t\tEncodedValue encodedValue = ann.getValues().get(name);\n\t\tif (encodedValue == null || encodedValue.getType() != type) {\n\t\t\treturn defValue;\n\t\t}\n\t\treturn (T) encodedValue.getValue();\n\t}\n\n\t@Nullable\n\tpublic static Object getValue(IAnnotation ann, String name, EncodedType type) {\n\t\tif (ann == null || ann.getValues() == null || ann.getValues().isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tEncodedValue encodedValue = ann.getValues().get(name);\n\t\tif (encodedValue == null || encodedValue.getType() != type) {\n\t\t\treturn null;\n\t\t}\n\t\treturn encodedValue.getValue();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static List<EncodedValue> getArray(IAnnotation ann, String name) {\n\t\tif (ann == null || ann.getValues() == null || ann.getValues().isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tEncodedValue encodedValue = ann.getValues().get(name);\n\t\tif (encodedValue == null || encodedValue.getType() != EncodedType.ENCODED_ARRAY) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn (List<EncodedValue>) encodedValue.getValue();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/annotations/EncodedValueParser.java",
    "content": "package jadx.plugins.input.dex.sections.annotations;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.annotations.EncodedType;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.plugins.input.dex.DexException;\nimport jadx.plugins.input.dex.sections.SectionReader;\n\npublic class EncodedValueParser {\n\n\tprivate static final int ENCODED_BYTE = 0x00;\n\tprivate static final int ENCODED_SHORT = 0x02;\n\tprivate static final int ENCODED_CHAR = 0x03;\n\tprivate static final int ENCODED_INT = 0x04;\n\tprivate static final int ENCODED_LONG = 0x06;\n\tprivate static final int ENCODED_FLOAT = 0x10;\n\tprivate static final int ENCODED_DOUBLE = 0x11;\n\tprivate static final int ENCODED_METHOD_TYPE = 0x15;\n\tprivate static final int ENCODED_METHOD_HANDLE = 0x16;\n\tprivate static final int ENCODED_STRING = 0x17;\n\tprivate static final int ENCODED_TYPE = 0x18;\n\tprivate static final int ENCODED_FIELD = 0x19;\n\tprivate static final int ENCODED_ENUM = 0x1b;\n\tprivate static final int ENCODED_METHOD = 0x1a;\n\tprivate static final int ENCODED_ARRAY = 0x1c;\n\tprivate static final int ENCODED_ANNOTATION = 0x1d;\n\tprivate static final int ENCODED_NULL = 0x1e;\n\tprivate static final int ENCODED_BOOLEAN = 0x1f;\n\n\tstatic EncodedValue parseValue(SectionReader in, SectionReader ext) {\n\t\tint argAndType = in.readUByte();\n\t\tint type = argAndType & 0x1F;\n\t\tint arg = (argAndType & 0xE0) >> 5;\n\t\tint size = arg + 1;\n\n\t\tswitch (type) {\n\t\t\tcase ENCODED_NULL:\n\t\t\t\treturn EncodedValue.NULL;\n\n\t\t\tcase ENCODED_BOOLEAN:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_BOOLEAN, arg == 1);\n\t\t\tcase ENCODED_BYTE:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_BYTE, in.readByte());\n\n\t\t\tcase ENCODED_SHORT:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_SHORT, (short) parseNumber(in, size, true));\n\t\t\tcase ENCODED_CHAR:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_CHAR, (char) parseUnsignedInt(in, size));\n\t\t\tcase ENCODED_INT:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_INT, (int) parseNumber(in, size, true));\n\t\t\tcase ENCODED_LONG:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_LONG, parseNumber(in, size, true));\n\n\t\t\tcase ENCODED_FLOAT:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_FLOAT, Float.intBitsToFloat((int) parseNumber(in, size, false, 4)));\n\t\t\tcase ENCODED_DOUBLE:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_DOUBLE, Double.longBitsToDouble(parseNumber(in, size, false, 8)));\n\n\t\t\tcase ENCODED_STRING:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_STRING, ext.getString(parseUnsignedInt(in, size)));\n\n\t\t\tcase ENCODED_TYPE:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_TYPE, ext.getType(parseUnsignedInt(in, size)));\n\n\t\t\tcase ENCODED_FIELD:\n\t\t\tcase ENCODED_ENUM:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_FIELD, ext.getFieldRef(parseUnsignedInt(in, size)));\n\n\t\t\tcase ENCODED_ARRAY:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_ARRAY, parseEncodedArray(in, ext));\n\n\t\t\tcase ENCODED_ANNOTATION:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_ANNOTATION, AnnotationsParser.readAnnotation(in, ext, false));\n\n\t\t\tcase ENCODED_METHOD:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_METHOD, ext.getMethodRef(parseUnsignedInt(in, size)));\n\n\t\t\tcase ENCODED_METHOD_TYPE:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_METHOD_TYPE, ext.getMethodProto(parseUnsignedInt(in, size)));\n\n\t\t\tcase ENCODED_METHOD_HANDLE:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_METHOD_HANDLE, ext.getMethodHandle(parseUnsignedInt(in, size)));\n\n\t\t\tdefault:\n\t\t\t\tthrow new DexException(\"Unknown encoded value type: 0x\" + Integer.toHexString(type));\n\t\t}\n\t}\n\n\tpublic static List<EncodedValue> parseEncodedArray(SectionReader in, SectionReader ext) {\n\t\tint count = in.readUleb128();\n\t\tList<EncodedValue> values = new ArrayList<>(count);\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tvalues.add(parseValue(in, ext));\n\t\t}\n\t\treturn values;\n\t}\n\n\tprivate static int parseUnsignedInt(SectionReader in, int byteCount) {\n\t\treturn (int) parseNumber(in, byteCount, false, 0);\n\t}\n\n\tprivate static long parseNumber(SectionReader in, int byteCount, boolean isSignExtended) {\n\t\treturn parseNumber(in, byteCount, isSignExtended, 0);\n\t}\n\n\tprivate static long parseNumber(SectionReader in, int byteCount, boolean isSignExtended, int fillOnRight) {\n\t\tlong result = 0;\n\t\tlong last = 0;\n\t\tfor (int i = 0; i < byteCount; i++) {\n\t\t\tlast = in.readUByte();\n\t\t\tresult |= last << (i * 8);\n\t\t}\n\t\tif (fillOnRight != 0) {\n\t\t\tfor (int i = byteCount; i < fillOnRight; i++) {\n\t\t\t\tresult <<= 8;\n\t\t\t}\n\t\t} else {\n\t\t\tif (isSignExtended && (last & 0x80) != 0) {\n\t\t\t\tfor (int i = byteCount; i < 8; i++) {\n\t\t\t\t\tresult |= (long) 0xFF << (i * 8);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/debuginfo/DebugInfoParser.java",
    "content": "package jadx.plugins.input.dex.sections.debuginfo;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.ILocalVar;\nimport jadx.api.plugins.input.data.impl.DebugInfo;\nimport jadx.plugins.input.dex.sections.DexConsts;\nimport jadx.plugins.input.dex.sections.SectionReader;\n\npublic class DebugInfoParser {\n\tprivate static final int DBG_END_SEQUENCE = 0x00;\n\tprivate static final int DBG_ADVANCE_PC = 0x01;\n\tprivate static final int DBG_ADVANCE_LINE = 0x02;\n\tprivate static final int DBG_START_LOCAL = 0x03;\n\tprivate static final int DBG_START_LOCAL_EXTENDED = 0x04;\n\tprivate static final int DBG_END_LOCAL = 0x05;\n\tprivate static final int DBG_RESTART_LOCAL = 0x06;\n\tprivate static final int DBG_SET_PROLOGUE_END = 0x07;\n\tprivate static final int DBG_SET_EPILOGUE_BEGIN = 0x08;\n\tprivate static final int DBG_SET_FILE = 0x09;\n\n\t// the smallest special opcode\n\tprivate static final int DBG_FIRST_SPECIAL = 0x0a;\n\t// the smallest line number increment\n\tprivate static final int DBG_LINE_BASE = -4;\n\t// the number of line increments represented\n\tprivate static final int DBG_LINE_RANGE = 15;\n\n\tprivate final SectionReader in;\n\tprivate final SectionReader ext;\n\n\tprivate final DexLocalVar[] locals;\n\tprivate final int codeSize;\n\n\tprivate List<ILocalVar> resultList;\n\tprivate Map<Integer, Integer> linesMap;\n\t@Nullable\n\tprivate String sourceFile;\n\n\tprivate List<String> argTypes;\n\tprivate int[] argRegs;\n\n\tpublic DebugInfoParser(SectionReader in, int regsCount, int codeSize) {\n\t\tthis.in = in;\n\t\tthis.ext = in.copy();\n\t\tthis.locals = new DexLocalVar[regsCount];\n\t\tthis.codeSize = codeSize;\n\t}\n\n\tpublic void initMthArgs(int regsCount, List<String> argTypes) {\n\t\tif (argTypes.isEmpty()) {\n\t\t\tthis.argTypes = Collections.emptyList();\n\t\t\treturn;\n\t\t}\n\n\t\tint argsCount = argTypes.size();\n\t\tint[] argRegsArr = new int[argsCount];\n\t\tint regNum = regsCount;\n\t\tfor (int i = argsCount - 1; i >= 0; i--) {\n\t\t\tregNum -= getTypeLen(argTypes.get(i));\n\t\t\targRegsArr[i] = regNum;\n\t\t}\n\t\tthis.argRegs = argRegsArr;\n\t\tthis.argTypes = argTypes;\n\t}\n\n\tpublic static int getTypeLen(String type) {\n\t\tswitch (type.charAt(0)) {\n\t\t\tcase 'J':\n\t\t\tcase 'D':\n\t\t\t\treturn 2;\n\t\t\tdefault:\n\t\t\t\treturn 1;\n\t\t}\n\t}\n\n\tpublic DebugInfo process(int debugOff) {\n\t\tin.absPos(debugOff);\n\n\t\tboolean varsInfoFound = false;\n\t\tresultList = new ArrayList<>();\n\t\tlinesMap = new HashMap<>();\n\n\t\tint addr = 0;\n\t\tint line = in.readUleb128();\n\t\tint paramsCount = in.readUleb128();\n\t\tint argsCount = argTypes.size();\n\n\t\tfor (int i = 0; i < paramsCount; i++) {\n\t\t\tint nameId = in.readUleb128p1();\n\t\t\tString name = ext.getString(nameId);\n\t\t\tif (name != null && i < argsCount) {\n\t\t\t\tDexLocalVar paramVar = new DexLocalVar(argRegs[i], name, argTypes.get(i));\n\t\t\t\tstartVar(paramVar, addr);\n\t\t\t\tparamVar.markAsParameter();\n\t\t\t\tvarsInfoFound = true;\n\t\t\t}\n\t\t}\n\t\twhile (true) {\n\t\t\tint c = in.readUByte();\n\t\t\tif (c == DBG_END_SEQUENCE) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tswitch (c) {\n\t\t\t\tcase DBG_ADVANCE_PC: {\n\t\t\t\t\tint addrInc = in.readUleb128();\n\t\t\t\t\taddr = addrChange(addr, addrInc);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase DBG_ADVANCE_LINE: {\n\t\t\t\t\tline += in.readSleb128();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase DBG_START_LOCAL: {\n\t\t\t\t\tint regNum = in.readUleb128();\n\t\t\t\t\tint nameId = in.readUleb128() - 1;\n\t\t\t\t\tint type = in.readUleb128() - 1;\n\t\t\t\t\tDexLocalVar var = new DexLocalVar(ext, regNum, nameId, type, DexConsts.NO_INDEX);\n\t\t\t\t\tstartVar(var, addr);\n\t\t\t\t\tvarsInfoFound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase DBG_START_LOCAL_EXTENDED: {\n\t\t\t\t\tint regNum = in.readUleb128();\n\t\t\t\t\tint nameId = in.readUleb128p1();\n\t\t\t\t\tint type = in.readUleb128p1();\n\t\t\t\t\tint sign = in.readUleb128p1();\n\t\t\t\t\tDexLocalVar var = new DexLocalVar(ext, regNum, nameId, type, sign);\n\t\t\t\t\tstartVar(var, addr);\n\t\t\t\t\tvarsInfoFound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase DBG_RESTART_LOCAL: {\n\t\t\t\t\tint regNum = in.readUleb128();\n\t\t\t\t\trestartVar(regNum, addr);\n\t\t\t\t\tvarsInfoFound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase DBG_END_LOCAL: {\n\t\t\t\t\tint regNum = in.readUleb128();\n\t\t\t\t\tDexLocalVar var = locals[regNum];\n\t\t\t\t\tif (var != null) {\n\t\t\t\t\t\tendVar(var, addr);\n\t\t\t\t\t}\n\t\t\t\t\tvarsInfoFound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase DBG_SET_PROLOGUE_END:\n\t\t\t\tcase DBG_SET_EPILOGUE_BEGIN: {\n\t\t\t\t\t// do nothing\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase DBG_SET_FILE: {\n\t\t\t\t\tint idx = in.readUleb128() - 1;\n\t\t\t\t\tthis.sourceFile = ext.getString(idx);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tint adjustedOpCode = c - DBG_FIRST_SPECIAL;\n\t\t\t\t\tint addrInc = adjustedOpCode / DBG_LINE_RANGE;\n\t\t\t\t\taddr = addrChange(addr, addrInc);\n\t\t\t\t\tline += DBG_LINE_BASE + adjustedOpCode % DBG_LINE_RANGE;\n\t\t\t\t\tsetLine(addr, line);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (varsInfoFound) {\n\t\t\tfor (DexLocalVar var : locals) {\n\t\t\t\tif (var != null && !var.isEnd()) {\n\t\t\t\t\tendVar(var, codeSize - 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn new DebugInfo(linesMap, resultList);\n\t}\n\n\tprivate int addrChange(int addr, int addrInc) {\n\t\treturn Math.min(addr + addrInc, codeSize - 1);\n\t}\n\n\tprivate void setLine(int offset, int line) {\n\t\tlinesMap.put(offset, line);\n\t}\n\n\tprivate void restartVar(int regNum, int addr) {\n\t\tDexLocalVar prev = locals[regNum];\n\t\tif (prev != null) {\n\t\t\tendVar(prev, addr);\n\t\t\tDexLocalVar newVar = new DexLocalVar(regNum, prev.getName(), prev.getType(), prev.getSignature());\n\t\t\tstartVar(newVar, addr);\n\t\t}\n\t}\n\n\tprivate void startVar(DexLocalVar newVar, int addr) {\n\t\tint regNum = newVar.getRegNum();\n\t\tDexLocalVar prev = locals[regNum];\n\t\tif (prev != null) {\n\t\t\tendVar(prev, addr);\n\t\t}\n\t\tnewVar.start(addr);\n\t\tlocals[regNum] = newVar;\n\t}\n\n\tprivate void endVar(DexLocalVar var, int addr) {\n\t\tif (var.end(addr)) {\n\t\t\tresultList.add(var);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/sections/debuginfo/DexLocalVar.java",
    "content": "package jadx.plugins.input.dex.sections.debuginfo;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.ILocalVar;\nimport jadx.api.plugins.utils.Utils;\nimport jadx.plugins.input.dex.sections.SectionReader;\n\npublic class DexLocalVar implements ILocalVar {\n\tprivate static final int PARAM_START_OFFSET = -1;\n\n\tprivate final int regNum;\n\tprivate final String name;\n\tprivate final String type;\n\t@Nullable\n\tprivate final String sign;\n\n\tprivate boolean isEnd;\n\tprivate int startOffset;\n\tprivate int endOffset;\n\n\tpublic DexLocalVar(SectionReader dex, int regNum, int nameId, int typeId, int signId) {\n\t\tthis(regNum, dex.getString(nameId), dex.getType(typeId), dex.getString(signId));\n\t}\n\n\tpublic DexLocalVar(int regNum, String name, String type) {\n\t\tthis(regNum, name, type, null);\n\t}\n\n\tpublic DexLocalVar(int regNum, String name, String type, @Nullable String sign) {\n\t\tthis.regNum = regNum;\n\t\tthis.name = name;\n\t\tthis.type = type;\n\t\tthis.sign = sign;\n\t}\n\n\tpublic void start(int addr) {\n\t\tthis.isEnd = false;\n\t\tthis.startOffset = addr;\n\t}\n\n\t/**\n\t * Sets end address of local variable\n\t *\n\t * @param addr address\n\t * @return <b>true</b> if local variable was active, else <b>false</b>\n\t */\n\tpublic boolean end(int addr) {\n\t\tif (isEnd) {\n\t\t\treturn false;\n\t\t}\n\t\tthis.isEnd = true;\n\t\tthis.endOffset = addr;\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int getRegNum() {\n\t\treturn regNum;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic String getType() {\n\t\treturn type;\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic String getSignature() {\n\t\treturn sign;\n\t}\n\n\t@Override\n\tpublic int getStartOffset() {\n\t\treturn startOffset;\n\t}\n\n\tpublic void markAsParameter() {\n\t\tstartOffset = PARAM_START_OFFSET;\n\t}\n\n\t@Override\n\tpublic boolean isMarkedAsParameter() {\n\t\treturn startOffset == PARAM_START_OFFSET;\n\t}\n\n\t@Override\n\tpublic int getEndOffset() {\n\t\treturn endOffset;\n\t}\n\n\tpublic boolean isEnd() {\n\t\treturn isEnd;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\treturn super.equals(obj);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn super.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn (startOffset == -1 ? \"-1 \" : Utils.formatOffset(startOffset))\n\t\t\t\t+ '-' + (isEnd ? Utils.formatOffset(endOffset) : \"      \")\n\t\t\t\t+ \": r\" + regNum + \" '\" + name + \"' \" + type\n\t\t\t\t+ (sign != null ? \", signature: \" + sign : \"\");\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/smali/InsnFormatter.java",
    "content": "package jadx.plugins.input.dex.smali;\n\ninterface InsnFormatter {\n\tvoid format(InsnFormatterInfo insnFormatInfo);\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/smali/InsnFormatterInfo.java",
    "content": "package jadx.plugins.input.dex.smali;\n\nimport java.util.Objects;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.IMethodData;\nimport jadx.api.plugins.input.insns.InsnData;\n\npublic class InsnFormatterInfo {\n\tprivate final SmaliCodeWriter codeWriter;\n\t@Nullable\n\tprivate IMethodData mth;\n\t@Nullable\n\tprivate InsnData insn;\n\n\tpublic InsnFormatterInfo(SmaliCodeWriter codeWriter, IMethodData mth) {\n\t\tthis.codeWriter = codeWriter;\n\t\tthis.mth = Objects.requireNonNull(mth);\n\t}\n\n\tpublic InsnFormatterInfo(SmaliCodeWriter codeWriter, InsnData insn) {\n\t\tthis.codeWriter = codeWriter;\n\t\tthis.insn = Objects.requireNonNull(insn);\n\t}\n\n\tpublic SmaliCodeWriter getCodeWriter() {\n\t\treturn codeWriter;\n\t}\n\n\tpublic void setMth(IMethodData mth) {\n\t\tthis.mth = mth;\n\t}\n\n\tpublic IMethodData getMth() {\n\t\treturn mth;\n\t}\n\n\tpublic InsnData getInsn() {\n\t\tif (insn == null) {\n\t\t\tthrow new NullPointerException(\"Instruction not set for formatter\");\n\t\t}\n\t\treturn insn;\n\t}\n\n\tpublic void setInsn(InsnData insn) {\n\t\tthis.insn = insn;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/smali/SmaliCodeWriter.java",
    "content": "package jadx.plugins.input.dex.smali;\n\nimport java.util.List;\n\npublic class SmaliCodeWriter {\n\tpublic static final String NL = System.getProperty(\"line.separator\");\n\tpublic static final String INDENT_STR = \"    \";\n\n\tprivate final StringBuilder code = new StringBuilder();\n\n\tprivate int indent;\n\tprivate String indentStr = \"\";\n\n\tpublic SmaliCodeWriter startLine(String line) {\n\t\tstartLine();\n\t\tcode.append(line);\n\t\treturn this;\n\t}\n\n\tpublic SmaliCodeWriter startLine() {\n\t\tif (code.length() != 0) {\n\t\t\tcode.append(NL);\n\t\t\tcode.append(indentStr);\n\t\t}\n\t\treturn this;\n\t}\n\n\tpublic SmaliCodeWriter add(Object obj) {\n\t\tcode.append(obj);\n\t\treturn this;\n\t}\n\n\tpublic SmaliCodeWriter add(int i) {\n\t\tcode.append(i);\n\t\treturn this;\n\t}\n\n\tpublic SmaliCodeWriter add(char c) {\n\t\tcode.append(c);\n\t\treturn this;\n\t}\n\n\tpublic SmaliCodeWriter add(String str) {\n\t\tcode.append(str);\n\t\treturn this;\n\t}\n\n\tpublic SmaliCodeWriter addArgs(List<String> argTypes) {\n\t\tfor (String type : argTypes) {\n\t\t\tcode.append(type);\n\t\t}\n\t\treturn this;\n\t}\n\n\tpublic void incIndent() {\n\t\tthis.indent++;\n\t\tbuildIndent();\n\t}\n\n\tpublic void decIndent() {\n\t\tthis.indent--;\n\t\tbuildIndent();\n\t}\n\n\tprivate void buildIndent() {\n\t\tStringBuilder s = new StringBuilder(indent * INDENT_STR.length());\n\t\tfor (int i = 0; i < indent; i++) {\n\t\t\ts.append(INDENT_STR);\n\t\t}\n\t\tthis.indentStr = s.toString();\n\t}\n\n\tpublic String getCode() {\n\t\treturn code.toString();\n\t}\n\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/smali/SmaliInsnFormat.java",
    "content": "package jadx.plugins.input.dex.smali;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.plugins.input.dex.insns.DexOpcodes;\n\npublic class SmaliInsnFormat {\n\n\tprivate static SmaliInsnFormat instance;\n\n\tpublic static synchronized SmaliInsnFormat getInstance() {\n\t\tSmaliInsnFormat instance = SmaliInsnFormat.instance;\n\t\tif (instance == null) {\n\t\t\tinstance = new SmaliInsnFormat();\n\t\t\tSmaliInsnFormat.instance = instance;\n\t\t}\n\t\treturn instance;\n\t}\n\n\tprivate final Map<Integer, InsnFormatter> formatters;\n\n\tpublic SmaliInsnFormat() {\n\t\tformatters = registerFormatters();\n\t}\n\n\tprivate Map<Integer, InsnFormatter> registerFormatters() {\n\t\tMap<Integer, InsnFormatter> map = new HashMap<>();\n\t\tmap.put(DexOpcodes.NOP, fi -> fi.getCodeWriter().add(\"nop\"));\n\t\tmap.put(DexOpcodes.SGET_OBJECT, staticFieldInsn(\"sget-object\"));\n\t\tmap.put(DexOpcodes.SPUT_BOOLEAN, staticFieldInsn(\"sput-boolean\"));\n\t\tmap.put(DexOpcodes.CONST, constInsn(\"const\"));\n\t\tmap.put(DexOpcodes.CONST_HIGH16, constInsn(\"const/high16\"));\n\t\tmap.put(DexOpcodes.CONST_STRING, stringInsn(\"const-string\"));\n\t\tmap.put(DexOpcodes.INVOKE_VIRTUAL, invokeInsn(\"invoke-virtual\"));\n\t\tmap.put(DexOpcodes.INVOKE_DIRECT, invokeInsn(\"invoke-direct\"));\n\t\tmap.put(DexOpcodes.INVOKE_SUPER, invokeInsn(\"invoke-super\"));\n\t\tmap.put(DexOpcodes.INVOKE_STATIC, invokeInsn(\"invoke-static\"));\n\t\tmap.put(DexOpcodes.MOVE_RESULT, oneArgsInsn(\"move-result\"));\n\t\tmap.put(DexOpcodes.RETURN_VOID, noArgsInsn(\"return-void\"));\n\t\tmap.put(DexOpcodes.GOTO, gotoInsn(\"goto\"));\n\t\tmap.put(DexOpcodes.GOTO_16, gotoInsn(\"goto-16\"));\n\t\tmap.put(DexOpcodes.MOVE, simpleInsn(\"move\"));\n\t\t// TODO: complete list\n\t\treturn map;\n\t}\n\n\tprivate InsnFormatter simpleInsn(String name) {\n\t\treturn fi -> {\n\t\t\tSmaliCodeWriter code = fi.getCodeWriter();\n\t\t\tcode.add(name);\n\t\t\tInsnData insn = fi.getInsn();\n\t\t\tint regsCount = insn.getRegsCount();\n\t\t\tfor (int i = 0; i < regsCount; i++) {\n\t\t\t\tif (i == 0) {\n\t\t\t\t\tcode.add(' ');\n\t\t\t\t} else {\n\t\t\t\t\tcode.add(\", \");\n\t\t\t\t}\n\t\t\t\tcode.add(regAt(fi, i));\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate InsnFormatter gotoInsn(String name) {\n\t\treturn fi -> fi.getCodeWriter().add(name).add(\" :goto\").add(Integer.toHexString(fi.getInsn().getTarget()));\n\t}\n\n\t@NotNull\n\tprivate InsnFormatter staticFieldInsn(String name) {\n\t\treturn fi -> fi.getCodeWriter().add(name).add(' ').add(regAt(fi, 0)).add(\", \").add(field(fi));\n\t}\n\n\t@NotNull\n\tprivate InsnFormatter constInsn(String name) {\n\t\treturn fi -> fi.getCodeWriter().add(name).add(' ').add(regAt(fi, 0)).add(\", \").add(literal(fi));\n\t}\n\n\t@NotNull\n\tprivate InsnFormatter stringInsn(String name) {\n\t\treturn fi -> fi.getCodeWriter().add(name).add(' ').add(regAt(fi, 0)).add(\", \").add(str(fi));\n\t}\n\n\t@NotNull\n\tprivate InsnFormatter invokeInsn(String name) {\n\t\treturn fi -> {\n\t\t\tSmaliCodeWriter code = fi.getCodeWriter();\n\t\t\tcode.add(name).add(' ');\n\t\t\tregsList(code, fi.getInsn());\n\t\t\tcode.add(\", \").add(method(fi));\n\t\t};\n\t}\n\n\tprivate InsnFormatter oneArgsInsn(String name) {\n\t\treturn fi -> fi.getCodeWriter().add(name).add(' ').add(regAt(fi, 0));\n\t}\n\n\tprivate InsnFormatter noArgsInsn(String name) {\n\t\treturn fi -> fi.getCodeWriter().add(name);\n\t}\n\n\tprivate String literal(InsnFormatterInfo fi) {\n\t\treturn \"0x\" + Long.toHexString(fi.getInsn().getLiteral());\n\t}\n\n\tprivate String str(InsnFormatterInfo fi) {\n\t\treturn \"\\\"\" + fi.getInsn().getIndexAsString() + \"\\\"\";\n\t}\n\n\tprivate String field(InsnFormatterInfo fi) {\n\t\treturn fi.getInsn().getIndexAsField().toString();\n\t}\n\n\tprivate String method(InsnFormatterInfo fi) {\n\t\treturn fi.getInsn().getIndexAsMethod().toString();\n\t}\n\n\tprivate void regsList(SmaliCodeWriter code, InsnData insn) {\n\t\tint argsCount = insn.getRegsCount();\n\t\tcode.add('{');\n\t\tfor (int i = 0; i < argsCount; i++) {\n\t\t\tif (i != 0) {\n\t\t\t\tcode.add(\", \");\n\t\t\t}\n\t\t\tcode.add(\"v\").add(insn.getReg(i));\n\t\t}\n\t\tcode.add('}');\n\t}\n\n\tprivate String regAt(InsnFormatterInfo fi, int argNum) {\n\t\treturn \"v\" + fi.getInsn().getReg(argNum);\n\t}\n\n\tpublic void format(InsnFormatterInfo formatInfo) {\n\t\tInsnData insn = formatInfo.getInsn();\n\t\tinsn.decode();\n\t\tint rawOpcodeUnit = insn.getRawOpcodeUnit();\n\t\tint opcode = rawOpcodeUnit & 0xFF;\n\t\tInsnFormatter insnFormatter = formatters.get(opcode);\n\t\tif (insnFormatter != null) {\n\t\t\tinsnFormatter.format(formatInfo);\n\t\t} else {\n\t\t\tformatInfo.getCodeWriter().add(\"# \").add(insn.getOpcode()).add(\" (?0x\").add(Integer.toHexString(rawOpcodeUnit)).add(')');\n\t\t}\n\t}\n\n\tpublic String format(InsnData insn) {\n\t\tInsnFormatterInfo formatInfo = new InsnFormatterInfo(new SmaliCodeWriter(), insn);\n\t\tformat(formatInfo);\n\t\treturn formatInfo.getCodeWriter().getCode();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/smali/SmaliPrinter.java",
    "content": "package jadx.plugins.input.dex.smali;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.api.plugins.input.data.ICodeReader;\nimport jadx.plugins.input.dex.sections.DexMethodData;\nimport jadx.plugins.input.dex.sections.DexMethodRef;\n\nimport static jadx.api.plugins.input.data.AccessFlagsScope.METHOD;\n\n// TODO: not finished\npublic class SmaliPrinter {\n\n\tpublic static String printMethod(DexMethodData mth) {\n\t\tSmaliCodeWriter codeWriter = new SmaliCodeWriter();\n\t\tcodeWriter.startLine(\".method \");\n\t\tcodeWriter.add(AccessFlags.format(mth.getAccessFlags(), METHOD));\n\n\t\tDexMethodRef methodRef = mth.getMethodRef();\n\t\tmethodRef.load();\n\t\tcodeWriter.add(methodRef.getName());\n\t\tcodeWriter.add('(').addArgs(methodRef.getArgTypes()).add(')');\n\t\tcodeWriter.add(methodRef.getReturnType());\n\t\tcodeWriter.incIndent();\n\n\t\tICodeReader codeReader = mth.getCodeReader();\n\t\tif (codeReader != null) {\n\t\t\tcodeWriter.startLine(\".registers \").add(codeReader.getRegistersCount());\n\t\t\tSmaliInsnFormat insnFormat = SmaliInsnFormat.getInstance();\n\t\t\tInsnFormatterInfo formatterInfo = new InsnFormatterInfo(codeWriter, mth);\n\t\t\tcodeReader.visitInstructions(insn -> {\n\t\t\t\tcodeWriter.startLine();\n\t\t\t\tformatterInfo.setInsn(insn);\n\t\t\t\tinsnFormat.format(formatterInfo);\n\t\t\t});\n\t\t\tcodeWriter.decIndent();\n\t\t}\n\t\tcodeWriter.startLine(\".end method\");\n\t\treturn codeWriter.getCode();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/utils/DataReader.java",
    "content": "package jadx.plugins.input.dex.utils;\n\npublic class DataReader {\n\n\tpublic static int readU4(byte[] data, int pos) {\n\t\tbyte b1 = data[pos++];\n\t\tbyte b2 = data[pos++];\n\t\tbyte b3 = data[pos++];\n\t\tbyte b4 = data[pos];\n\t\treturn (b4 & 0xFF) << 24 | (b3 & 0xFF) << 16 | (b2 & 0xFF) << 8 | b1 & 0xFF;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/utils/DexCheckSum.java",
    "content": "package jadx.plugins.input.dex.utils;\n\nimport java.util.zip.Adler32;\n\nimport jadx.plugins.input.dex.DexException;\n\npublic class DexCheckSum {\n\n\tpublic static void verify(String fileName, byte[] content, int offset) {\n\t\tif (offset + 32 + 4 > content.length) {\n\t\t\tthrow new DexException(\"Dex file truncated, can't read file length, file: \" + fileName);\n\t\t}\n\t\tint len = DataReader.readU4(content, offset + 32);\n\t\tif (offset + len > content.length) {\n\t\t\tthrow new DexException(\"Dex file truncated, length in header: \" + len + \", file: \" + fileName);\n\t\t}\n\t\tint checksum = DataReader.readU4(content, offset + 8);\n\t\tAdler32 adler32 = new Adler32();\n\t\tadler32.update(content, offset + 12, len - 12);\n\t\tint fileChecksum = (int) adler32.getValue();\n\t\tif (checksum != fileChecksum) {\n\t\t\tthrow new DexException(String.format(\"Bad dex file checksum: 0x%08x, expected: 0x%08x, file: %s\",\n\t\t\t\t\tfileChecksum, checksum, fileName));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/utils/IDexData.java",
    "content": "package jadx.plugins.input.dex.utils;\n\npublic interface IDexData {\n\n\tString getFileName();\n\n\tbyte[] getContent();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/utils/Leb128.java",
    "content": "package jadx.plugins.input.dex.utils;\n\nimport jadx.plugins.input.dex.DexException;\nimport jadx.plugins.input.dex.sections.SectionReader;\n\npublic final class Leb128 {\n\n\tpublic static int readSignedLeb128(SectionReader in) {\n\t\tint result = 0;\n\t\tint cur;\n\t\tint count = 0;\n\t\tint signBits = -1;\n\t\tdo {\n\t\t\tcur = in.readUByte();\n\t\t\tresult |= (cur & 0x7f) << (count * 7);\n\t\t\tsignBits <<= 7;\n\t\t\tcount++;\n\t\t} while (((cur & 0x80) == 0x80) && count < 5);\n\n\t\tif ((cur & 0x80) == 0x80) {\n\t\t\tthrow new DexException(\"Invalid LEB128 sequence\");\n\t\t}\n\t\t// Sign extend if appropriate\n\t\tif (((signBits >> 1) & result) != 0) {\n\t\t\tresult |= signBits;\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic static int readUnsignedLeb128(SectionReader in) {\n\t\tint result = 0;\n\t\tint cur;\n\t\tint count = 0;\n\t\tdo {\n\t\t\tcur = in.readUByte();\n\t\t\tresult |= (cur & 0x7f) << (count * 7);\n\t\t\tcount++;\n\t\t} while (((cur & 0x80) == 0x80) && count < 5);\n\n\t\tif ((cur & 0x80) == 0x80) {\n\t\t\tthrow new DexException(\"Invalid LEB128 sequence\");\n\t\t}\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/utils/MUtf8.java",
    "content": "package jadx.plugins.input.dex.utils;\n\nimport jadx.plugins.input.dex.DexException;\nimport jadx.plugins.input.dex.sections.SectionReader;\n\npublic class MUtf8 {\n\n\tpublic static String decode(SectionReader in) {\n\t\tint len = in.readUleb128();\n\t\tchar[] out = new char[len];\n\t\tint k = 0;\n\t\twhile (true) {\n\t\t\tchar a = (char) (in.readUByte() & 0xff);\n\t\t\tif (a == 0) {\n\t\t\t\treturn new String(out, 0, k);\n\t\t\t}\n\t\t\tout[k] = a;\n\t\t\tif (a < '\\u0080') {\n\t\t\t\tk++;\n\t\t\t} else if ((a & 0xE0) == 0xC0) {\n\t\t\t\tint b = in.readUByte();\n\t\t\t\tif ((b & 0xC0) != 0x80) {\n\t\t\t\t\tthrow new DexException(\"Bad second byte\");\n\t\t\t\t}\n\t\t\t\tout[k] = (char) (((a & 0x1F) << 6) | (b & 0x3F));\n\t\t\t\tk++;\n\t\t\t} else if ((a & 0xF0) == 0xE0) {\n\t\t\t\tint b = in.readUByte();\n\t\t\t\tint c = in.readUByte();\n\t\t\t\tif (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) {\n\t\t\t\t\tthrow new DexException(\"Bad second or third byte\");\n\t\t\t\t}\n\t\t\t\tout[k] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F));\n\t\t\t\tk++;\n\t\t\t} else {\n\t\t\t\tthrow new DexException(\"Bad byte\");\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/utils/SimpleDexData.java",
    "content": "package jadx.plugins.input.dex.utils;\n\nimport java.util.Objects;\n\npublic class SimpleDexData implements IDexData {\n\tprivate final String fileName;\n\tprivate final byte[] content;\n\n\tpublic SimpleDexData(String fileName, byte[] content) {\n\t\tthis.fileName = Objects.requireNonNull(fileName);\n\t\tthis.content = Objects.requireNonNull(content);\n\t}\n\n\t@Override\n\tpublic String getFileName() {\n\t\treturn fileName;\n\t}\n\n\t@Override\n\tpublic byte[] getContent() {\n\t\treturn content;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"DexData{\" + fileName + \", size=\" + content.length + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/utils/SmaliUtils.java",
    "content": "package jadx.plugins.input.dex.utils;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.android.tools.smali.baksmali.Adaptors.ClassDefinition;\nimport com.android.tools.smali.baksmali.BaksmaliOptions;\nimport com.android.tools.smali.baksmali.formatter.BaksmaliWriter;\nimport com.android.tools.smali.dexlib2.dexbacked.DexBackedClassDef;\nimport com.android.tools.smali.dexlib2.dexbacked.DexBackedDexFile;\n\npublic class SmaliUtils {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SmaliUtils.class);\n\n\tpublic static String getSmaliCode(byte[] dexBuf, int clsDefOffset) {\n\t\tStringWriter stringWriter = new StringWriter();\n\t\ttry {\n\t\t\tDexBackedDexFile dexFile = new DexBackedDexFile(null, dexBuf);\n\t\t\tDexBackedClassDef dexBackedClassDef = new DexBackedClassDef(dexFile, clsDefOffset, 0);\n\t\t\tClassDefinition classDefinition = new ClassDefinition(new BaksmaliOptions(), dexBackedClassDef);\n\t\t\tclassDefinition.writeTo(new BaksmaliWriter(stringWriter));\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Error generating smali\", e);\n\t\t\tstringWriter.append(\"Error generating smali code: \");\n\t\t\tstringWriter.append(e.getMessage());\n\t\t\tstringWriter.append(System.lineSeparator());\n\t\t\te.printStackTrace(new PrintWriter(stringWriter, true));\n\t\t}\n\t\treturn stringWriter.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin",
    "content": "jadx.plugins.input.dex.DexInputPlugin\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/test/java/jadx/plugins/input/dex/DexInputPluginTest.java",
    "content": "package jadx.plugins.input.dex;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.plugins.input.ICodeLoader;\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.api.plugins.input.data.AccessFlagsScope;\nimport jadx.api.plugins.input.data.ICodeReader;\nimport jadx.plugins.input.dex.utils.SmaliTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass DexInputPluginTest {\n\n\t@Test\n\tpublic void loadSampleApk() throws Exception {\n\t\tprocessFile(Paths.get(ClassLoader.getSystemResource(\"samples/app-with-fake-dex.apk\").toURI()));\n\t}\n\n\t@Test\n\tpublic void loadHelloWorld() throws Exception {\n\t\tprocessFile(Paths.get(ClassLoader.getSystemResource(\"samples/hello.dex\").toURI()));\n\t}\n\n\t@Test\n\tpublic void loadTestSmali() throws Exception {\n\t\tprocessFile(SmaliTestUtils.compileSmaliFromResource(\"samples/test.smali\"));\n\t}\n\n\tprivate static void processFile(Path sample) throws IOException {\n\t\tSystem.out.println(\"Input file: \" + sample.toAbsolutePath());\n\t\tlong start = System.currentTimeMillis();\n\t\tList<Path> files = Collections.singletonList(sample);\n\t\ttry (ICodeLoader result = new DexInputPlugin().loadFiles(files)) {\n\t\t\tAtomicInteger count = new AtomicInteger();\n\t\t\tresult.visitClasses(cls -> {\n\t\t\t\tSystem.out.println();\n\t\t\t\tSystem.out.println(\"Class: \" + cls.getType());\n\t\t\t\tSystem.out.println(\"AccessFlags: \" + AccessFlags.format(cls.getAccessFlags(), AccessFlagsScope.CLASS));\n\t\t\t\tSystem.out.println(\"SuperType: \" + cls.getSuperType());\n\t\t\t\tSystem.out.println(\"Interfaces: \" + cls.getInterfacesTypes());\n\t\t\t\tSystem.out.println(\"Attributes: \" + cls.getAttributes());\n\t\t\t\tcount.getAndIncrement();\n\n\t\t\t\tcls.visitFieldsAndMethods(\n\t\t\t\t\t\tSystem.out::println,\n\t\t\t\t\t\tmth -> {\n\t\t\t\t\t\t\tSystem.out.println(\"---\");\n\t\t\t\t\t\t\tSystem.out.println(mth);\n\t\t\t\t\t\t\tICodeReader codeReader = mth.getCodeReader();\n\t\t\t\t\t\t\tif (codeReader != null) {\n\t\t\t\t\t\t\t\tcodeReader.visitInstructions(insn -> {\n\t\t\t\t\t\t\t\t\tinsn.decode();\n\t\t\t\t\t\t\t\t\tSystem.out.println(insn);\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tSystem.out.println(\"---\");\n\t\t\t\t\t\t\tSystem.out.println(mth.disassembleMethod());\n\t\t\t\t\t\t\tSystem.out.println(\"---\");\n\t\t\t\t\t\t});\n\t\t\t\tSystem.out.println(\"----\");\n\t\t\t\tSystem.out.println(cls.getDisassembledCode());\n\t\t\t\tSystem.out.println(\"----\");\n\t\t\t});\n\t\t\tassertThat(count.get()).isGreaterThan(0);\n\t\t}\n\t\tSystem.out.println(\"Time: \" + (System.currentTimeMillis() - start) + \"ms\");\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/test/java/jadx/plugins/input/dex/utils/SmaliTestUtils.java",
    "content": "package jadx.plugins.input.dex.utils;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport com.android.tools.smali.smali.Smali;\nimport com.android.tools.smali.smali.SmaliOptions;\n\npublic class SmaliTestUtils {\n\n\tpublic static Path compileSmaliFromResource(String res) {\n\t\ttry {\n\t\t\tPath input = Paths.get(ClassLoader.getSystemResource(res).toURI());\n\t\t\treturn compileSmali(input);\n\t\t} catch (Exception e) {\n\t\t\tthrow new AssertionError(\"Smali assemble error\", e);\n\t\t}\n\t}\n\n\tpublic static Path compileSmali(Path input) {\n\t\ttry {\n\t\t\tPath tempFile = Files.createTempFile(\"jadx\", \"smali.dex\");\n\t\t\tcompileSmali(tempFile, Collections.singletonList(input));\n\t\t\treturn tempFile;\n\t\t} catch (Exception e) {\n\t\t\tthrow new AssertionError(\"Smali assemble error\", e);\n\t\t}\n\t}\n\n\tprivate static void compileSmali(Path output, List<Path> inputFiles) {\n\t\ttry {\n\t\t\tSmaliOptions options = new SmaliOptions();\n\t\t\toptions.outputDexFile = output.toAbsolutePath().toString();\n\t\t\tList<String> inputFileNames = inputFiles.stream()\n\t\t\t\t\t.map(Path::toAbsolutePath)\n\t\t\t\t\t.map(Path::toString)\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t\tSmali.assemble(options, inputFileNames);\n\t\t} catch (Exception e) {\n\t\t\tthrow new AssertionError(\"Smali assemble error\", e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-dex-input/src/test/resources/samples/test.smali",
    "content": ".class LHello;\n.super Ljava/lang/Object;\n.source \"test.java\"\n\n\n.method public main([Ljava/lang/String;)V\n    .registers 2\n\n    .line 3\n\tnop\n\n\tmove v0, v1\n\n    .line 4\n    return-void\n.end method\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/README.md",
    "content": "# jadx-input-api\n\nBase API for code input used in jadx.\n\nContains common data structures to support both java and dex bytecode.\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-library\")\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/ICodeLoader.java",
    "content": "package jadx.api.plugins.input;\n\nimport java.io.Closeable;\nimport java.util.function.Consumer;\n\nimport jadx.api.plugins.input.data.IClassData;\n\npublic interface ICodeLoader extends Closeable {\n\n\tvoid visitClasses(Consumer<IClassData> consumer);\n\n\tboolean isEmpty();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/JadxCodeInput.java",
    "content": "package jadx.api.plugins.input;\n\nimport java.nio.file.Path;\nimport java.util.List;\n\npublic interface JadxCodeInput {\n\tICodeLoader loadFiles(List<Path> input);\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/AccessFlags.java",
    "content": "package jadx.api.plugins.input.data;\n\npublic class AccessFlags {\n\tpublic static final int PUBLIC = 0x1;\n\tpublic static final int PRIVATE = 0x2;\n\tpublic static final int PROTECTED = 0x4;\n\tpublic static final int STATIC = 0x8;\n\tpublic static final int FINAL = 0x10;\n\tpublic static final int SYNCHRONIZED = 0x20;\n\tpublic static final int SUPER = 0x20;\n\tpublic static final int VOLATILE = 0x40;\n\tpublic static final int BRIDGE = 0x40;\n\tpublic static final int TRANSIENT = 0x80;\n\tpublic static final int VARARGS = 0x80;\n\tpublic static final int NATIVE = 0x100;\n\tpublic static final int INTERFACE = 0x200;\n\tpublic static final int ABSTRACT = 0x400;\n\tpublic static final int STRICT = 0x800;\n\tpublic static final int SYNTHETIC = 0x1000;\n\tpublic static final int ANNOTATION = 0x2000;\n\tpublic static final int ENUM = 0x4000;\n\tpublic static final int MODULE = 0x8000;\n\tpublic static final int CONSTRUCTOR = 0x10000;\n\tpublic static final int DECLARED_SYNCHRONIZED = 0x20000;\n\tpublic static final int DATA = 0x40000;\n\n\tpublic static boolean hasFlag(int flags, int flagValue) {\n\t\treturn (flags & flagValue) != 0;\n\t}\n\n\tpublic static String format(int flags, AccessFlagsScope scope) {\n\t\tStringBuilder code = new StringBuilder();\n\t\tif (hasFlag(flags, PUBLIC)) {\n\t\t\tcode.append(\"public \");\n\t\t}\n\t\tif (hasFlag(flags, PRIVATE)) {\n\t\t\tcode.append(\"private \");\n\t\t}\n\t\tif (hasFlag(flags, PROTECTED)) {\n\t\t\tcode.append(\"protected \");\n\t\t}\n\t\tif (hasFlag(flags, STATIC)) {\n\t\t\tcode.append(\"static \");\n\t\t}\n\t\tif (hasFlag(flags, FINAL)) {\n\t\t\tcode.append(\"final \");\n\t\t}\n\t\tif (hasFlag(flags, ABSTRACT)) {\n\t\t\tcode.append(\"abstract \");\n\t\t}\n\t\tif (hasFlag(flags, NATIVE)) {\n\t\t\tcode.append(\"native \");\n\t\t}\n\t\tswitch (scope) {\n\t\t\tcase METHOD:\n\t\t\t\tif (hasFlag(flags, SYNCHRONIZED)) {\n\t\t\t\t\tcode.append(\"synchronized \");\n\t\t\t\t}\n\t\t\t\tif (hasFlag(flags, BRIDGE)) {\n\t\t\t\t\tcode.append(\"bridge \");\n\t\t\t\t}\n\t\t\t\tif (hasFlag(flags, VARARGS)) {\n\t\t\t\t\tcode.append(\"varargs \");\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase FIELD:\n\t\t\t\tif (hasFlag(flags, VOLATILE)) {\n\t\t\t\t\tcode.append(\"volatile \");\n\t\t\t\t}\n\t\t\t\tif (hasFlag(flags, TRANSIENT)) {\n\t\t\t\t\tcode.append(\"transient \");\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase CLASS:\n\t\t\t\tif (hasFlag(flags, MODULE)) {\n\t\t\t\t\tcode.append(\"module \");\n\t\t\t\t}\n\t\t\t\tif (hasFlag(flags, STRICT)) {\n\t\t\t\t\tcode.append(\"strict \");\n\t\t\t\t}\n\t\t\t\tif (hasFlag(flags, SUPER)) {\n\t\t\t\t\tcode.append(\"super \");\n\t\t\t\t}\n\t\t\t\tif (hasFlag(flags, ENUM)) {\n\t\t\t\t\tcode.append(\"enum \");\n\t\t\t\t}\n\t\t\t\tif (hasFlag(flags, DATA)) {\n\t\t\t\t\tcode.append(\"data \");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t\tif (hasFlag(flags, SYNTHETIC)) {\n\t\t\tcode.append(\"synthetic \");\n\t\t}\n\t\treturn code.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/AccessFlagsScope.java",
    "content": "package jadx.api.plugins.input.data;\n\npublic enum AccessFlagsScope {\n\tCLASS, FIELD, METHOD\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/ICallSite.java",
    "content": "package jadx.api.plugins.input.data;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.insns.custom.ICustomPayload;\n\npublic interface ICallSite extends ICustomPayload {\n\n\tList<EncodedValue> getValues();\n\n\tvoid load();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/ICatch.java",
    "content": "package jadx.api.plugins.input.data;\n\npublic interface ICatch {\n\tString[] getTypes();\n\n\tint[] getHandlers();\n\n\tint getCatchAllHandler();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/IClassData.java",
    "content": "package jadx.api.plugins.input.data;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\n\npublic interface IClassData {\n\tIClassData copy();\n\n\tString getInputFileName();\n\n\tString getType();\n\n\tint getAccessFlags();\n\n\tint getInputFileOffset();\n\n\t@Nullable\n\tString getSuperType();\n\n\tList<String> getInterfacesTypes();\n\n\tvoid visitFieldsAndMethods(ISeqConsumer<IFieldData> fieldsConsumer, ISeqConsumer<IMethodData> mthConsumer);\n\n\tList<IJadxAttribute> getAttributes();\n\n\tString getDisassembledCode();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/ICodeReader.java",
    "content": "package jadx.api.plugins.input.data;\n\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.insns.InsnData;\n\npublic interface ICodeReader {\n\tICodeReader copy();\n\n\tvoid visitInstructions(Consumer<InsnData> insnConsumer);\n\n\tint getRegistersCount();\n\n\tint getArgsStartReg();\n\n\tint getUnitsCount();\n\n\t@Nullable\n\tIDebugInfo getDebugInfo();\n\n\tint getCodeOffset();\n\n\tList<ITry> getTries();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/IDebugInfo.java",
    "content": "package jadx.api.plugins.input.data;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic interface IDebugInfo {\n\n\t/**\n\t * Map instruction offset to source line number\n\t */\n\tMap<Integer, Integer> getSourceLineMapping();\n\n\tList<ILocalVar> getLocalVars();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/IFieldData.java",
    "content": "package jadx.api.plugins.input.data;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\n\npublic interface IFieldData extends IFieldRef {\n\n\tint getAccessFlags();\n\n\tList<IJadxAttribute> getAttributes();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/IFieldRef.java",
    "content": "package jadx.api.plugins.input.data;\n\npublic interface IFieldRef {\n\tString getParentClassType();\n\n\tString getName();\n\n\tString getType();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/ILocalVar.java",
    "content": "package jadx.api.plugins.input.data;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic interface ILocalVar {\n\tString getName();\n\n\tint getRegNum();\n\n\tString getType();\n\n\t@Nullable\n\tString getSignature();\n\n\tint getStartOffset();\n\n\tint getEndOffset();\n\n\t/**\n\t * Hint if variable is a method parameter.\n\t * Can be incorrect and shouldn't be trusted.\n\t */\n\tboolean isMarkedAsParameter();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/IMethodData.java",
    "content": "package jadx.api.plugins.input.data;\n\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\n\npublic interface IMethodData {\n\n\tIMethodRef getMethodRef();\n\n\tint getAccessFlags();\n\n\t@Nullable\n\tICodeReader getCodeReader();\n\n\tString disassembleMethod();\n\n\tList<IJadxAttribute> getAttributes();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/IMethodHandle.java",
    "content": "package jadx.api.plugins.input.data;\n\npublic interface IMethodHandle {\n\n\tMethodHandleType getType();\n\n\tIFieldRef getFieldRef();\n\n\tIMethodRef getMethodRef();\n\n\tvoid load();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/IMethodProto.java",
    "content": "package jadx.api.plugins.input.data;\n\nimport java.util.List;\n\npublic interface IMethodProto {\n\n\tString getReturnType();\n\n\tList<String> getArgTypes();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/IMethodRef.java",
    "content": "package jadx.api.plugins.input.data;\n\nimport jadx.api.plugins.input.insns.custom.ICustomPayload;\n\npublic interface IMethodRef extends IMethodProto, ICustomPayload {\n\n\t/**\n\t * Method unique id (will be used for caching).\n\t *\n\t * @return 0 if can't calculate good unique identifier (disable caching)\n\t */\n\tint getUniqId();\n\n\t/**\n\t * Lazy loading for method info, until load() is called only getUniqId() can be used\n\t */\n\tvoid load();\n\n\tString getParentClassType();\n\n\tString getName();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/IResourceData.java",
    "content": "package jadx.api.plugins.input.data;\n\npublic interface IResourceData {\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/ISeqConsumer.java",
    "content": "package jadx.api.plugins.input.data;\n\nimport java.util.function.Consumer;\n\n/**\n * \"Sequence consumer\" allows getting count of elements available\n */\npublic interface ISeqConsumer<T> extends Consumer<T> {\n\n\tdefault void init(int count) {\n\t\t// no-op implementation\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/ITry.java",
    "content": "package jadx.api.plugins.input.data;\n\npublic interface ITry {\n\tICatch getCatch();\n\n\tint getStartOffset();\n\n\tint getEndOffset();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/MethodHandleType.java",
    "content": "package jadx.api.plugins.input.data;\n\npublic enum MethodHandleType {\n\tSTATIC_PUT,\n\tSTATIC_GET,\n\tINSTANCE_PUT,\n\tINSTANCE_GET,\n\tINVOKE_STATIC,\n\tINVOKE_INSTANCE,\n\tINVOKE_DIRECT,\n\tINVOKE_CONSTRUCTOR,\n\tINVOKE_INTERFACE;\n\n\tpublic boolean isField() {\n\t\tswitch (this) {\n\t\t\tcase STATIC_PUT:\n\t\t\tcase STATIC_GET:\n\t\t\tcase INSTANCE_PUT:\n\t\t\tcase INSTANCE_GET:\n\t\t\t\treturn true;\n\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/annotations/AnnotationVisibility.java",
    "content": "package jadx.api.plugins.input.data.annotations;\n\npublic enum AnnotationVisibility {\n\tBUILD,\n\tRUNTIME,\n\tSYSTEM\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/annotations/EncodedType.java",
    "content": "package jadx.api.plugins.input.data.annotations;\n\npublic enum EncodedType {\n\tENCODED_NULL,\n\tENCODED_BOOLEAN,\n\tENCODED_BYTE,\n\tENCODED_SHORT,\n\tENCODED_CHAR,\n\tENCODED_INT,\n\tENCODED_LONG,\n\tENCODED_FLOAT,\n\tENCODED_DOUBLE,\n\tENCODED_STRING,\n\tENCODED_TYPE,\n\tENCODED_ENUM,\n\tENCODED_FIELD,\n\tENCODED_METHOD,\n\tENCODED_METHOD_TYPE,\n\tENCODED_METHOD_HANDLE,\n\tENCODED_ARRAY,\n\tENCODED_ANNOTATION\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/annotations/EncodedValue.java",
    "content": "package jadx.api.plugins.input.data.annotations;\n\nimport java.util.Objects;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\n\npublic class EncodedValue extends PinnedAttribute {\n\tpublic static final EncodedValue NULL = new EncodedValue(EncodedType.ENCODED_NULL, null);\n\n\tprivate final EncodedType type;\n\tprivate final Object value;\n\n\tpublic EncodedValue(EncodedType type, Object value) {\n\t\tthis.type = type;\n\t\tthis.value = value;\n\t}\n\n\tpublic EncodedType getType() {\n\t\treturn type;\n\t}\n\n\tpublic Object getValue() {\n\t\treturn value;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tEncodedValue that = (EncodedValue) o;\n\t\treturn type == that.getType() && Objects.equals(value, that.getValue());\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<? extends IJadxAttribute> getAttrType() {\n\t\treturn JadxAttrType.CONSTANT_VALUE;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(getType(), getValue());\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tswitch (type) {\n\t\t\tcase ENCODED_NULL:\n\t\t\t\treturn \"null\";\n\t\t\tcase ENCODED_ARRAY:\n\t\t\t\treturn \"[\" + value + \"]\";\n\t\t\tcase ENCODED_STRING:\n\t\t\t\treturn \"{STRING: \\\"\" + value + \"\\\"}\";\n\t\t\tdefault:\n\t\t\t\treturn \"{\" + type.toString().substring(8) + \": \" + value + '}';\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/annotations/IAnnotation.java",
    "content": "package jadx.api.plugins.input.data.annotations;\n\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic interface IAnnotation {\n\tString getAnnotationClass();\n\n\tAnnotationVisibility getVisibility();\n\n\tMap<String, EncodedValue> getValues();\n\n\t@Nullable\n\tdefault EncodedValue getDefaultValue() {\n\t\treturn getValues().get(\"value\");\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/annotations/JadxAnnotation.java",
    "content": "package jadx.api.plugins.input.data.annotations;\n\nimport java.util.Map;\n\npublic class JadxAnnotation implements IAnnotation {\n\tprivate final AnnotationVisibility visibility;\n\tprivate final String type;\n\tprivate final Map<String, EncodedValue> values;\n\n\tpublic JadxAnnotation(AnnotationVisibility visibility, String type, Map<String, EncodedValue> values) {\n\t\tthis.visibility = visibility;\n\t\tthis.type = type;\n\t\tthis.values = values;\n\t}\n\n\t@Override\n\tpublic String getAnnotationClass() {\n\t\treturn type;\n\t}\n\n\t@Override\n\tpublic AnnotationVisibility getVisibility() {\n\t\treturn visibility;\n\t}\n\n\t@Override\n\tpublic Map<String, EncodedValue> getValues() {\n\t\treturn values;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Annotation{\" + visibility + \", type=\" + type + \", values=\" + values + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/attributes/IJadxAttrType.java",
    "content": "package jadx.api.plugins.input.data.attributes;\n\n/**\n * Marker interface for attribute type.\n * Similar to enumeration but extensible.\n * <p>\n * Used for attach attribute instance class information (T).\n * T - class of attribute instance\n * <p>\n * To create new one define static field like this:\n * {@code\n * static final IJadxAttrType<AttrTypeClass> ATTR_TYPE = IJadxAttrType.create();\n * }\n */\npublic interface IJadxAttrType<T extends IJadxAttribute> {\n\n\tstatic <A extends IJadxAttribute> IJadxAttrType<A> create() {\n\t\treturn new IJadxAttrType<>() {\n\t\t};\n\t}\n\n\tstatic <A extends IJadxAttribute> IJadxAttrType<A> create(String name) {\n\t\treturn new IJadxAttrType<>() {\n\t\t\t@Override\n\t\t\tpublic String toString() {\n\t\t\t\treturn name;\n\t\t\t}\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/attributes/IJadxAttribute.java",
    "content": "package jadx.api.plugins.input.data.attributes;\n\n/**\n * Jadx attribute: custom data container, can be added to most jadx nodes.\n */\npublic interface IJadxAttribute {\n\n\tIJadxAttrType<? extends IJadxAttribute> getAttrType();\n\n\t/**\n\t * Mark type to skip unloading on node unload event\n\t */\n\tdefault boolean keepLoaded() {\n\t\treturn false;\n\t}\n\n\tdefault String toAttrString() {\n\t\treturn this.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/attributes/JadxAttrType.java",
    "content": "package jadx.api.plugins.input.data.attributes;\n\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationDefaultAttr;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationDefaultClassAttr;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationMethodParamsAttr;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationsAttr;\nimport jadx.api.plugins.input.data.attributes.types.ExceptionsAttr;\nimport jadx.api.plugins.input.data.attributes.types.InnerClassesAttr;\nimport jadx.api.plugins.input.data.attributes.types.MethodParametersAttr;\nimport jadx.api.plugins.input.data.attributes.types.SignatureAttr;\nimport jadx.api.plugins.input.data.attributes.types.SourceFileAttr;\n\npublic final class JadxAttrType<T extends IJadxAttribute> implements IJadxAttrType<T> {\n\n\t// class, method, field\n\tpublic static final JadxAttrType<AnnotationsAttr> ANNOTATION_LIST = bind();\n\tpublic static final JadxAttrType<SignatureAttr> SIGNATURE = bind();\n\n\t// class\n\tpublic static final JadxAttrType<SourceFileAttr> SOURCE_FILE = bind();\n\tpublic static final JadxAttrType<InnerClassesAttr> INNER_CLASSES = bind();\n\tpublic static final JadxAttrType<AnnotationDefaultClassAttr> ANNOTATION_DEFAULT_CLASS = bind(); // dex specific\n\n\t// field\n\tpublic static final JadxAttrType<EncodedValue> CONSTANT_VALUE = bind();\n\n\t// method\n\tpublic static final JadxAttrType<AnnotationMethodParamsAttr> ANNOTATION_MTH_PARAMETERS = bind();\n\tpublic static final JadxAttrType<AnnotationDefaultAttr> ANNOTATION_DEFAULT = bind();\n\tpublic static final JadxAttrType<ExceptionsAttr> EXCEPTIONS = bind();\n\tpublic static final JadxAttrType<MethodParametersAttr> METHOD_PARAMETERS = bind();\n\n\tprivate static <T extends IJadxAttribute> JadxAttrType<T> bind() {\n\t\treturn new JadxAttrType<>();\n\t}\n\n\tprivate JadxAttrType() {\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/attributes/PinnedAttribute.java",
    "content": "package jadx.api.plugins.input.data.attributes;\n\npublic abstract class PinnedAttribute implements IJadxAttribute {\n\n\t@Override\n\tpublic final boolean keepLoaded() {\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/attributes/types/AnnotationDefaultAttr.java",
    "content": "package jadx.api.plugins.input.data.attributes.types;\n\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\n\npublic class AnnotationDefaultAttr extends PinnedAttribute {\n\n\tprivate final EncodedValue value;\n\n\tpublic AnnotationDefaultAttr(EncodedValue value) {\n\t\tthis.value = value;\n\t}\n\n\tpublic EncodedValue getValue() {\n\t\treturn value;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<? extends IJadxAttribute> getAttrType() {\n\t\treturn JadxAttrType.ANNOTATION_DEFAULT;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ANNOTATION_DEFAULT: \" + value;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/attributes/types/AnnotationDefaultClassAttr.java",
    "content": "package jadx.api.plugins.input.data.attributes.types;\n\nimport java.util.Map;\n\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\n\npublic class AnnotationDefaultClassAttr extends PinnedAttribute {\n\n\tprivate final Map<String, EncodedValue> values;\n\n\tpublic AnnotationDefaultClassAttr(Map<String, EncodedValue> values) {\n\t\tthis.values = values;\n\t}\n\n\tpublic Map<String, EncodedValue> getValues() {\n\t\treturn values;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<? extends IJadxAttribute> getAttrType() {\n\t\treturn JadxAttrType.ANNOTATION_DEFAULT_CLASS;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ANNOTATION_DEFAULT_CLASS: \" + values;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/attributes/types/AnnotationMethodParamsAttr.java",
    "content": "package jadx.api.plugins.input.data.attributes.types;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\n\npublic class AnnotationMethodParamsAttr extends PinnedAttribute {\n\n\t@Nullable\n\tpublic static AnnotationMethodParamsAttr pack(List<List<IAnnotation>> annotationRefList) {\n\t\tif (annotationRefList.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tList<AnnotationsAttr> list = new ArrayList<>(annotationRefList.size());\n\t\tfor (List<IAnnotation> annList : annotationRefList) {\n\t\t\tlist.add(AnnotationsAttr.pack(annList));\n\t\t}\n\t\treturn new AnnotationMethodParamsAttr(list);\n\t}\n\n\tprivate final List<AnnotationsAttr> paramList;\n\n\tprivate AnnotationMethodParamsAttr(List<AnnotationsAttr> paramsList) {\n\t\tthis.paramList = paramsList;\n\t}\n\n\tpublic List<AnnotationsAttr> getParamList() {\n\t\treturn paramList;\n\t}\n\n\t@Override\n\tpublic JadxAttrType<AnnotationMethodParamsAttr> getAttrType() {\n\t\treturn JadxAttrType.ANNOTATION_MTH_PARAMETERS;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn paramList.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/attributes/types/AnnotationsAttr.java",
    "content": "package jadx.api.plugins.input.data.attributes.types;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.annotations.AnnotationVisibility;\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\n\npublic class AnnotationsAttr extends PinnedAttribute {\n\n\t@Nullable\n\tpublic static AnnotationsAttr pack(List<IAnnotation> annotationList) {\n\t\tif (annotationList.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tMap<String, IAnnotation> annMap = new HashMap<>(annotationList.size());\n\t\tfor (IAnnotation ann : annotationList) {\n\t\t\tif (ann.getVisibility() != AnnotationVisibility.SYSTEM) {\n\t\t\t\tannMap.put(ann.getAnnotationClass(), ann);\n\t\t\t}\n\t\t}\n\t\tif (annMap.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new AnnotationsAttr(annMap);\n\t}\n\n\tprivate final Map<String, IAnnotation> map;\n\n\tpublic AnnotationsAttr(Map<String, IAnnotation> map) {\n\t\tthis.map = map;\n\t}\n\n\tpublic IAnnotation get(String className) {\n\t\treturn map.get(className);\n\t}\n\n\tpublic Collection<IAnnotation> getAll() {\n\t\treturn map.values();\n\t}\n\n\tpublic List<IAnnotation> getList() {\n\t\treturn map.isEmpty() ? Collections.emptyList() : new ArrayList<>(map.values());\n\t}\n\n\tpublic int size() {\n\t\treturn map.size();\n\t}\n\n\tpublic boolean isEmpty() {\n\t\treturn map.isEmpty();\n\t}\n\n\t@Override\n\tpublic JadxAttrType<AnnotationsAttr> getAttrType() {\n\t\treturn JadxAttrType.ANNOTATION_LIST;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn map.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/attributes/types/ExceptionsAttr.java",
    "content": "package jadx.api.plugins.input.data.attributes.types;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\n\npublic class ExceptionsAttr extends PinnedAttribute {\n\tprivate final List<String> list;\n\n\tpublic ExceptionsAttr(List<String> list) {\n\t\tthis.list = list;\n\t}\n\n\tpublic List<String> getList() {\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<ExceptionsAttr> getAttrType() {\n\t\treturn JadxAttrType.EXCEPTIONS;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"EXCEPTIONS:\" + list;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/attributes/types/InnerClassesAttr.java",
    "content": "package jadx.api.plugins.input.data.attributes.types;\n\nimport java.util.Map;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\n\npublic class InnerClassesAttr extends PinnedAttribute {\n\n\tprivate final Map<String, InnerClsInfo> map;\n\n\tpublic InnerClassesAttr(Map<String, InnerClsInfo> map) {\n\t\tthis.map = map;\n\t}\n\n\tpublic Map<String, InnerClsInfo> getMap() {\n\t\treturn map;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<InnerClassesAttr> getAttrType() {\n\t\treturn JadxAttrType.INNER_CLASSES;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"INNER_CLASSES:\" + map;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/attributes/types/InnerClsInfo.java",
    "content": "package jadx.api.plugins.input.data.attributes.types;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.api.plugins.input.data.AccessFlagsScope;\n\npublic class InnerClsInfo {\n\tprivate final String innerCls;\n\tprivate final @Nullable String outerCls;\n\tprivate final @Nullable String name;\n\tprivate final int accessFlags;\n\n\tpublic InnerClsInfo(String innerCls, @Nullable String outerCls, @Nullable String name, int accessFlags) {\n\t\tthis.innerCls = innerCls;\n\t\tthis.outerCls = outerCls;\n\t\tthis.name = name;\n\t\tthis.accessFlags = accessFlags;\n\t}\n\n\tpublic String getInnerCls() {\n\t\treturn innerCls;\n\t}\n\n\tpublic @Nullable String getOuterCls() {\n\t\treturn outerCls;\n\t}\n\n\tpublic @Nullable String getName() {\n\t\treturn name;\n\t}\n\n\tpublic int getAccessFlags() {\n\t\treturn accessFlags;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"InnerCls{\" + innerCls\n\t\t\t\t+ \", outerCls=\" + outerCls\n\t\t\t\t+ \", name=\" + name\n\t\t\t\t+ \", accessFlags=\" + AccessFlags.format(accessFlags, AccessFlagsScope.CLASS)\n\t\t\t\t+ '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/attributes/types/MethodParametersAttr.java",
    "content": "package jadx.api.plugins.input.data.attributes.types;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.api.plugins.input.data.AccessFlagsScope;\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\n\npublic class MethodParametersAttr extends PinnedAttribute {\n\n\tpublic static class Info {\n\t\tprivate final int accFlags;\n\t\tprivate final String name;\n\n\t\tpublic Info(int accFlags, String name) {\n\t\t\tthis.accFlags = accFlags;\n\t\t\tthis.name = name;\n\t\t}\n\n\t\tpublic int getAccFlags() {\n\t\t\treturn accFlags;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\n\t\tpublic String toString() {\n\t\t\treturn AccessFlags.format(accFlags, AccessFlagsScope.METHOD) + name;\n\t\t}\n\t}\n\n\tprivate final List<Info> list;\n\n\tpublic MethodParametersAttr(List<Info> list) {\n\t\tthis.list = list;\n\t}\n\n\tpublic List<Info> getList() {\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<MethodParametersAttr> getAttrType() {\n\t\treturn JadxAttrType.METHOD_PARAMETERS;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"METHOD_PARAMETERS: \" + list;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/attributes/types/SignatureAttr.java",
    "content": "package jadx.api.plugins.input.data.attributes.types;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\n\npublic class SignatureAttr extends PinnedAttribute {\n\n\tprivate final String signature;\n\n\tpublic SignatureAttr(String signature) {\n\t\tthis.signature = signature;\n\t}\n\n\tpublic String getSignature() {\n\t\treturn signature;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<? extends IJadxAttribute> getAttrType() {\n\t\treturn JadxAttrType.SIGNATURE;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"SIGNATURE: \" + signature;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/attributes/types/SourceFileAttr.java",
    "content": "package jadx.api.plugins.input.data.attributes.types;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.JadxAttrType;\nimport jadx.api.plugins.input.data.attributes.PinnedAttribute;\n\npublic class SourceFileAttr extends PinnedAttribute {\n\n\tprivate final String fileName;\n\n\tpublic SourceFileAttr(String fileName) {\n\t\tthis.fileName = fileName;\n\t}\n\n\tpublic String getFileName() {\n\t\treturn fileName;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<SourceFileAttr> getAttrType() {\n\t\treturn JadxAttrType.SOURCE_FILE;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"SOURCE:\" + fileName;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/impl/CallSite.java",
    "content": "package jadx.api.plugins.input.data.impl;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.ICallSite;\nimport jadx.api.plugins.input.data.IMethodHandle;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\n\npublic class CallSite implements ICallSite {\n\n\tprivate final List<EncodedValue> values;\n\n\tpublic CallSite(List<EncodedValue> values) {\n\t\tthis.values = values;\n\t}\n\n\t@Override\n\tpublic void load() {\n\t\tfor (EncodedValue value : values) {\n\t\t\tswitch (value.getType()) {\n\t\t\t\tcase ENCODED_METHOD_HANDLE:\n\t\t\t\t\t((IMethodHandle) value.getValue()).load();\n\t\t\t\t\tbreak;\n\t\t\t\tcase ENCODED_METHOD:\n\t\t\t\t\t((IMethodRef) value.getValue()).load();\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic List<EncodedValue> getValues() {\n\t\treturn values;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"CallSite{\" + values + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/impl/CatchData.java",
    "content": "package jadx.api.plugins.input.data.impl;\n\nimport jadx.api.plugins.input.data.ICatch;\n\nimport static jadx.api.plugins.input.data.impl.InputUtils.formatOffset;\n\npublic class CatchData implements ICatch {\n\tprivate final int[] handlers;\n\tprivate final String[] types;\n\tprivate final int allHandler;\n\n\tpublic CatchData(int[] handlers, String[] types, int allHandler) {\n\t\tthis.handlers = handlers;\n\t\tthis.types = types;\n\t\tthis.allHandler = allHandler;\n\t}\n\n\t@Override\n\tpublic int[] getHandlers() {\n\t\treturn handlers;\n\t}\n\n\t@Override\n\tpublic String[] getTypes() {\n\t\treturn types;\n\t}\n\n\t@Override\n\tpublic int getCatchAllHandler() {\n\t\treturn allHandler;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder(\"Catch:\");\n\t\tint size = types.length;\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tsb.append(' ').append(types[i]).append(\"->\").append(formatOffset(handlers[i]));\n\t\t}\n\t\tif (allHandler != -1) {\n\t\t\tsb.append(\" all->\").append(formatOffset(allHandler));\n\t\t}\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/impl/DebugInfo.java",
    "content": "package jadx.api.plugins.input.data.impl;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport jadx.api.plugins.input.data.IDebugInfo;\nimport jadx.api.plugins.input.data.ILocalVar;\n\npublic class DebugInfo implements IDebugInfo {\n\n\tprivate final Map<Integer, Integer> sourceLineMap;\n\tprivate final List<ILocalVar> localVars;\n\n\tpublic DebugInfo(Map<Integer, Integer> sourceLineMap, List<ILocalVar> localVars) {\n\t\tthis.sourceLineMap = sourceLineMap;\n\t\tthis.localVars = localVars;\n\t}\n\n\t@Override\n\tpublic Map<Integer, Integer> getSourceLineMapping() {\n\t\treturn sourceLineMap;\n\t}\n\n\t@Override\n\tpublic List<ILocalVar> getLocalVars() {\n\t\treturn localVars;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"DebugInfo{lines=\" + sourceLineMap + \", localVars=\" + localVars + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/impl/EmptyCodeLoader.java",
    "content": "package jadx.api.plugins.input.data.impl;\n\nimport java.io.IOException;\nimport java.util.function.Consumer;\n\nimport jadx.api.plugins.input.ICodeLoader;\nimport jadx.api.plugins.input.data.IClassData;\n\npublic class EmptyCodeLoader implements ICodeLoader {\n\n\tpublic static final EmptyCodeLoader INSTANCE = new EmptyCodeLoader();\n\n\t@Override\n\tpublic boolean isEmpty() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void visitClasses(Consumer<IClassData> consumer) {\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/impl/FieldRefHandle.java",
    "content": "package jadx.api.plugins.input.data.impl;\n\nimport jadx.api.plugins.input.data.IFieldRef;\nimport jadx.api.plugins.input.data.IMethodHandle;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.input.data.MethodHandleType;\n\npublic class FieldRefHandle implements IMethodHandle {\n\n\tprivate final IFieldRef fieldRef;\n\tprivate final MethodHandleType type;\n\n\tpublic FieldRefHandle(MethodHandleType type, IFieldRef fieldRef) {\n\t\tthis.fieldRef = fieldRef;\n\t\tthis.type = type;\n\t}\n\n\t@Override\n\tpublic MethodHandleType getType() {\n\t\treturn type;\n\t}\n\n\t@Override\n\tpublic IFieldRef getFieldRef() {\n\t\treturn fieldRef;\n\t}\n\n\t@Override\n\tpublic IMethodRef getMethodRef() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void load() {\n\t\t// already loaded\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn type + \": \" + fieldRef;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/impl/InputUtils.java",
    "content": "package jadx.api.plugins.input.data.impl;\n\npublic class InputUtils {\n\tpublic static String formatOffset(int offset) {\n\t\treturn String.format(\"0x%04x\", offset);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/impl/JadxFieldRef.java",
    "content": "package jadx.api.plugins.input.data.impl;\n\nimport jadx.api.plugins.input.data.IFieldRef;\n\npublic class JadxFieldRef implements IFieldRef {\n\tprivate String parentClassType;\n\tprivate String name;\n\tprivate String type;\n\n\tpublic JadxFieldRef() {\n\t}\n\n\tpublic JadxFieldRef(String parentClassType, String name, String type) {\n\t\tthis.parentClassType = parentClassType;\n\t\tthis.name = name;\n\t\tthis.type = type;\n\t}\n\n\t@Override\n\tpublic String getParentClassType() {\n\t\treturn parentClassType;\n\t}\n\n\tpublic void setParentClassType(String parentClassType) {\n\t\tthis.parentClassType = parentClassType;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\t@Override\n\tpublic String getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setType(String type) {\n\t\tthis.type = type;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn parentClassType + \"->\" + name + \":\" + type;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/impl/ListConsumer.java",
    "content": "package jadx.api.plugins.input.data.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport jadx.api.plugins.input.data.ISeqConsumer;\n\npublic class ListConsumer<T, R> implements ISeqConsumer<T> {\n\tprivate final Function<T, R> convert;\n\tprivate List<R> list;\n\n\tpublic ListConsumer(Function<T, R> convert) {\n\t\tthis.convert = convert;\n\t}\n\n\t@Override\n\tpublic void init(int count) {\n\t\tlist = count == 0 ? Collections.emptyList() : new ArrayList<>(count);\n\t}\n\n\t@Override\n\tpublic void accept(T t) {\n\t\tlist.add(convert.apply(t));\n\t}\n\n\tpublic List<R> getResult() {\n\t\tif (list == null) {\n\t\t\t// init not called\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn list;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/impl/MergeCodeLoader.java",
    "content": "package jadx.api.plugins.input.data.impl;\n\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.ICodeLoader;\nimport jadx.api.plugins.input.data.IClassData;\n\npublic class MergeCodeLoader implements ICodeLoader {\n\n\tprivate final List<ICodeLoader> codeLoaders;\n\tprivate final @Nullable Closeable closeable;\n\n\tpublic MergeCodeLoader(List<ICodeLoader> codeLoaders) {\n\t\tthis(codeLoaders, null);\n\t}\n\n\tpublic MergeCodeLoader(List<ICodeLoader> codeLoaders, @Nullable Closeable closeable) {\n\t\tthis.codeLoaders = codeLoaders;\n\t\tthis.closeable = closeable;\n\t}\n\n\t@Override\n\tpublic void visitClasses(Consumer<IClassData> consumer) {\n\t\tfor (ICodeLoader codeLoader : codeLoaders) {\n\t\t\tcodeLoader.visitClasses(consumer);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isEmpty() {\n\t\tfor (ICodeLoader codeLoader : codeLoaders) {\n\t\t\tif (!codeLoader.isEmpty()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\tfor (ICodeLoader codeLoader : codeLoaders) {\n\t\t\tcodeLoader.close();\n\t\t}\n\t\tif (closeable != null) {\n\t\t\tcloseable.close();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/impl/MethodRefHandle.java",
    "content": "package jadx.api.plugins.input.data.impl;\n\nimport jadx.api.plugins.input.data.IFieldData;\nimport jadx.api.plugins.input.data.IMethodHandle;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.input.data.MethodHandleType;\n\npublic class MethodRefHandle implements IMethodHandle {\n\n\tprivate final MethodHandleType type;\n\tprivate final IMethodRef methodRef;\n\n\tpublic MethodRefHandle(MethodHandleType type, IMethodRef methodRef) {\n\t\tthis.type = type;\n\t\tthis.methodRef = methodRef;\n\t}\n\n\t@Override\n\tpublic MethodHandleType getType() {\n\t\treturn type;\n\t}\n\n\t@Override\n\tpublic IMethodRef getMethodRef() {\n\t\treturn methodRef;\n\t}\n\n\t@Override\n\tpublic IFieldData getFieldRef() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void load() {\n\t\tmethodRef.load();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn type + \": \" + methodRef;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/impl/TryData.java",
    "content": "package jadx.api.plugins.input.data.impl;\n\nimport jadx.api.plugins.input.data.ICatch;\nimport jadx.api.plugins.input.data.ITry;\n\nimport static jadx.api.plugins.input.data.impl.InputUtils.formatOffset;\n\npublic class TryData implements ITry {\n\n\tprivate final int startOffset;\n\tprivate final int endOffset;\n\tprivate final ICatch catchHandler;\n\n\tpublic TryData(int startOffset, int endOffset, ICatch catchHandler) {\n\t\tthis.startOffset = startOffset;\n\t\tthis.endOffset = endOffset;\n\t\tthis.catchHandler = catchHandler;\n\t}\n\n\t@Override\n\tpublic ICatch getCatch() {\n\t\treturn catchHandler;\n\t}\n\n\t@Override\n\tpublic int getStartOffset() {\n\t\treturn startOffset;\n\t}\n\n\t@Override\n\tpublic int getEndOffset() {\n\t\treturn endOffset;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Try{\" + formatOffset(startOffset) + \" - \" + formatOffset(endOffset) + \": \" + catchHandler + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/insns/InsnData.java",
    "content": "package jadx.api.plugins.input.insns;\n\nimport jadx.api.plugins.input.data.ICallSite;\nimport jadx.api.plugins.input.data.IFieldRef;\nimport jadx.api.plugins.input.data.IMethodHandle;\nimport jadx.api.plugins.input.data.IMethodProto;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.input.insns.custom.ICustomPayload;\n\npublic interface InsnData {\n\n\tvoid decode();\n\n\tint getOffset(); // offset within method\n\n\tint getFileOffset(); // offset within dex file\n\n\tOpcode getOpcode();\n\n\tString getOpcodeMnemonic();\n\n\tbyte[] getByteCode();\n\n\tInsnIndexType getIndexType();\n\n\tint getRawOpcodeUnit();\n\n\tint getRegsCount();\n\n\tint getReg(int argNum);\n\n\t/**\n\t * Workaround to set result reg without additional move-result insn\n\t *\n\t * @return result reg number or -1 if not needed\n\t */\n\tint getResultReg();\n\n\tlong getLiteral();\n\n\tint getTarget();\n\n\tint getIndex();\n\n\tString getIndexAsString();\n\n\tString getIndexAsType();\n\n\tIFieldRef getIndexAsField();\n\n\tIMethodRef getIndexAsMethod();\n\n\tICallSite getIndexAsCallSite();\n\n\tIMethodProto getIndexAsProto(int protoIndex);\n\n\tIMethodHandle getIndexAsMethodHandle();\n\n\tICustomPayload getPayload();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/insns/InsnIndexType.java",
    "content": "package jadx.api.plugins.input.insns;\n\npublic enum InsnIndexType {\n\tNONE,\n\tTYPE_REF,\n\tSTRING_REF,\n\tFIELD_REF,\n\tMETHOD_REF,\n\tCALL_SITE\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/insns/Opcode.java",
    "content": "package jadx.api.plugins.input.insns;\n\npublic enum Opcode {\n\tUNKNOWN,\n\tNOP,\n\n\tADD_DOUBLE,\n\tADD_FLOAT,\n\tADD_INT,\n\tADD_INT_LIT,\n\tADD_LONG,\n\n\tAND_INT,\n\tAND_INT_LIT,\n\tAND_LONG,\n\n\tAGET,\n\tAGET_BOOLEAN,\n\tAGET_BYTE,\n\tAGET_BYTE_BOOLEAN,\n\tAGET_CHAR,\n\tAGET_OBJECT,\n\tAGET_SHORT,\n\tAGET_WIDE,\n\n\tAPUT,\n\tAPUT_BOOLEAN,\n\tAPUT_BYTE,\n\tAPUT_BYTE_BOOLEAN,\n\tAPUT_CHAR,\n\tAPUT_OBJECT,\n\tAPUT_SHORT,\n\tAPUT_WIDE,\n\n\tARITH,\n\tARRAY_LENGTH,\n\n\tCAST,\n\tCHECK_CAST,\n\n\tCMPG_DOUBLE,\n\tCMPG_FLOAT,\n\tCMPL_DOUBLE,\n\tCMPL_FLOAT,\n\tCMP_LONG,\n\n\tCONST,\n\tCONST_CLASS,\n\tCONST_STRING,\n\tCONST_WIDE,\n\n\tDIV_DOUBLE,\n\tDIV_FLOAT,\n\tDIV_INT,\n\tDIV_INT_LIT,\n\tDIV_LONG,\n\n\tDOUBLE_TO_FLOAT,\n\tDOUBLE_TO_INT,\n\tDOUBLE_TO_LONG,\n\n\tFLOAT_TO_DOUBLE,\n\tFLOAT_TO_INT,\n\tFLOAT_TO_LONG,\n\n\tGOTO,\n\tIF,\n\tIF_EQ,\n\tIF_EQZ,\n\tIF_GE,\n\tIF_GEZ,\n\tIF_GT,\n\tIF_GTZ,\n\tIF_LE,\n\tIF_LEZ,\n\tIF_LT,\n\tIF_LTZ,\n\tIF_NE,\n\tIF_NEZ,\n\n\tINSTANCE_OF,\n\n\tINT_TO_BYTE,\n\tINT_TO_CHAR,\n\tINT_TO_DOUBLE,\n\tINT_TO_FLOAT,\n\tINT_TO_LONG,\n\tINT_TO_SHORT,\n\n\tINVOKE_DIRECT,\n\tINVOKE_DIRECT_RANGE,\n\tINVOKE_INTERFACE,\n\tINVOKE_INTERFACE_RANGE,\n\tINVOKE_STATIC,\n\tINVOKE_STATIC_RANGE,\n\tINVOKE_SUPER,\n\tINVOKE_SUPER_RANGE,\n\tINVOKE_VIRTUAL,\n\tINVOKE_VIRTUAL_RANGE,\n\tINVOKE_SPECIAL,\n\n\tIGET,\n\tIPUT,\n\n\tSGET,\n\tSPUT,\n\n\tLONG_TO_DOUBLE,\n\tLONG_TO_FLOAT,\n\tLONG_TO_INT,\n\n\tMONITOR_ENTER,\n\tMONITOR_EXIT,\n\n\tMOVE,\n\tMOVE_MULTI,\n\tMOVE_EXCEPTION,\n\tMOVE_OBJECT,\n\tMOVE_RESULT,\n\tMOVE_WIDE,\n\n\tMUL_DOUBLE,\n\tMUL_FLOAT,\n\tMUL_INT,\n\tMUL_INT_LIT,\n\tMUL_LONG,\n\n\tNEG,\n\tNEG_DOUBLE,\n\tNEG_FLOAT,\n\tNEG_INT,\n\tNEG_LONG,\n\tNEW_INSTANCE,\n\n\tNOT_INT,\n\tNOT_LONG,\n\n\tOR_INT,\n\tOR_INT_LIT,\n\tOR_LONG,\n\n\tREM_DOUBLE,\n\tREM_FLOAT,\n\tREM_INT,\n\tREM_INT_LIT,\n\tREM_LONG,\n\n\tRETURN,\n\tRETURN_VOID,\n\n\tRSUB_INT,\n\tSHL_INT,\n\tSHL_INT_LIT,\n\tSHL_LONG,\n\tSHR_INT,\n\tSHR_INT_LIT,\n\tSHR_LONG,\n\tSUB_DOUBLE,\n\tSUB_FLOAT,\n\tSUB_INT,\n\tSUB_LONG,\n\n\tTHROW,\n\n\tUSHR_INT,\n\tUSHR_INT_LIT,\n\tUSHR_LONG,\n\tXOR_INT,\n\tXOR_INT_LIT,\n\tXOR_LONG,\n\n\tNEW_ARRAY,\n\n\tFILLED_NEW_ARRAY,\n\tFILLED_NEW_ARRAY_RANGE,\n\tFILL_ARRAY_DATA,\n\tFILL_ARRAY_DATA_PAYLOAD,\n\n\tPACKED_SWITCH,\n\tPACKED_SWITCH_PAYLOAD,\n\tSPARSE_SWITCH,\n\tSPARSE_SWITCH_PAYLOAD,\n\n\tINVOKE_POLYMORPHIC,\n\tINVOKE_POLYMORPHIC_RANGE,\n\n\tINVOKE_CUSTOM,\n\tINVOKE_CUSTOM_RANGE,\n\n\tCONST_METHOD_HANDLE,\n\tCONST_METHOD_TYPE,\n\n\t// Java specific dynamic jump instructions\n\tJAVA_JSR,\n\tJAVA_RET,\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/insns/custom/IArrayPayload.java",
    "content": "package jadx.api.plugins.input.insns.custom;\n\npublic interface IArrayPayload extends ICustomPayload {\n\tint getSize();\n\n\tint getElementSize();\n\n\tObject getData();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/insns/custom/ICustomPayload.java",
    "content": "package jadx.api.plugins.input.insns.custom;\n\npublic interface ICustomPayload {\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/insns/custom/ISwitchPayload.java",
    "content": "package jadx.api.plugins.input.insns.custom;\n\npublic interface ISwitchPayload extends ICustomPayload {\n\tint getSize();\n\n\tint[] getKeys();\n\n\tint[] getTargets();\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/insns/custom/impl/SwitchPayload.java",
    "content": "package jadx.api.plugins.input.insns.custom.impl;\n\nimport jadx.api.plugins.input.insns.custom.ISwitchPayload;\n\npublic class SwitchPayload implements ISwitchPayload {\n\n\tprivate final int size;\n\tprivate final int[] keys;\n\tprivate final int[] targets;\n\n\tpublic SwitchPayload(int size, int[] keys, int[] targets) {\n\t\tthis.size = size;\n\t\tthis.keys = keys;\n\t\tthis.targets = targets;\n\t}\n\n\t@Override\n\tpublic int getSize() {\n\t\treturn size;\n\t}\n\n\t@Override\n\tpublic int[] getKeys() {\n\t\treturn keys;\n\t}\n\n\t@Override\n\tpublic int[] getTargets() {\n\t\treturn targets;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-convert/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-library\")\n}\n\ndependencies {\n\tapi(project(\":jadx-core\"))\n\n\timplementation(project(\":jadx-plugins:jadx-dex-input\"))\n\timplementation(\"com.jakewharton.android.repackaged:dalvik-dx:16.0.1\")\n\timplementation(\"com.android.tools:r8:8.13.17\")\n\n\timplementation(\"org.ow2.asm:asm:9.9.1\")\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/AsmUtils.java",
    "content": "package jadx.plugins.input.javaconvert;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport org.objectweb.asm.ClassReader;\n\npublic class AsmUtils {\n\n\tpublic static String getNameFromClassFile(Path file) throws IOException {\n\t\ttry (InputStream in = Files.newInputStream(file)) {\n\t\t\treturn getClassFullName(new ClassReader(in));\n\t\t}\n\t}\n\n\tpublic static String getNameFromClassFile(byte[] content) throws IOException {\n\t\treturn getClassFullName(new ClassReader(content));\n\t}\n\n\tprivate static String getClassFullName(ClassReader classReader) {\n\t\treturn classReader.getClassName();\n\t}\n\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/ConvertResult.java",
    "content": "package jadx.plugins.input.javaconvert;\n\nimport java.io.Closeable;\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class ConvertResult implements Closeable {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(ConvertResult.class);\n\n\tprivate final List<Path> converted = new ArrayList<>();\n\tprivate final List<Path> tmpPaths = new ArrayList<>();\n\n\tpublic List<Path> getConverted() {\n\t\treturn converted;\n\t}\n\n\tpublic void addConvertedFiles(List<Path> paths) {\n\t\tconverted.addAll(paths);\n\t}\n\n\tpublic void addTempPath(Path path) {\n\t\ttmpPaths.add(path);\n\t}\n\n\tpublic boolean isEmpty() {\n\t\treturn converted.isEmpty();\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\tfor (Path tmpPath : tmpPaths) {\n\t\t\ttry {\n\t\t\t\tdelete(tmpPath);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.warn(\"Failed to delete temp path: {}\", tmpPath, e);\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"ResultOfMethodCallIgnored\")\n\tprivate static void delete(Path path) throws IOException {\n\t\tif (Files.isRegularFile(path)) {\n\t\t\tFiles.delete(path);\n\t\t\treturn;\n\t\t}\n\t\tif (Files.isDirectory(path)) {\n\t\t\ttry (Stream<Path> pathStream = Files.walk(path)) {\n\t\t\t\tpathStream\n\t\t\t\t\t\t.sorted(Comparator.reverseOrder())\n\t\t\t\t\t\t.map(Path::toFile)\n\t\t\t\t\t\t.forEach(File::delete);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ConvertResult{converted=\" + converted + \", tmpPaths=\" + tmpPaths + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/D8Converter.java",
    "content": "package jadx.plugins.input.javaconvert;\n\nimport java.nio.file.Path;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.android.tools.r8.CompilationFailedException;\nimport com.android.tools.r8.CompilationMode;\nimport com.android.tools.r8.D8;\nimport com.android.tools.r8.D8Command;\nimport com.android.tools.r8.Diagnostic;\nimport com.android.tools.r8.DiagnosticsHandler;\nimport com.android.tools.r8.OutputMode;\n\npublic class D8Converter {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(D8Converter.class);\n\n\tpublic static void run(Path path, Path tempDirectory, JavaConvertOptions options) throws CompilationFailedException {\n\t\tD8Command d8Command = D8Command.builder(new LogHandler())\n\t\t\t\t.addProgramFiles(path)\n\t\t\t\t.setOutput(tempDirectory, OutputMode.DexIndexed)\n\t\t\t\t.setMode(CompilationMode.DEBUG)\n\t\t\t\t.setMinApiLevel(30)\n\t\t\t\t.setIntermediate(true)\n\t\t\t\t.setDisableDesugaring(!options.isD8Desugar())\n\t\t\t\t.build();\n\t\tD8.run(d8Command);\n\t}\n\n\tprivate static class LogHandler implements DiagnosticsHandler {\n\t\t@Override\n\t\tpublic void error(Diagnostic diagnostic) {\n\t\t\tLOG.error(\"D8 error: {}\", format(diagnostic));\n\t\t}\n\n\t\t@Override\n\t\tpublic void warning(Diagnostic diagnostic) {\n\t\t\tLOG.warn(\"D8 warning: {}\", format(diagnostic));\n\t\t}\n\n\t\t@Override\n\t\tpublic void info(Diagnostic diagnostic) {\n\t\t\tLOG.info(\"D8 info: {}\", format(diagnostic));\n\t\t}\n\n\t\tpublic static String format(Diagnostic diagnostic) {\n\t\t\treturn diagnostic.getDiagnosticMessage()\n\t\t\t\t\t+ \", origin: \" + diagnostic.getOrigin()\n\t\t\t\t\t+ \", position: \" + diagnostic.getPosition();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/DxConverter.java",
    "content": "package jadx.plugins.input.javaconvert;\n\nimport java.io.ByteArrayOutputStream;\nimport java.nio.file.Path;\n\nimport com.android.dx.command.dexer.DxContext;\nimport com.android.dx.command.dexer.Main;\n\npublic class DxConverter {\n\tprivate static final String CHARSET_NAME = \"UTF-8\";\n\n\tprivate static class DxArgs extends com.android.dx.command.dexer.Main.Arguments {\n\t\tpublic DxArgs(DxContext context, String dexDir, String[] input) {\n\t\t\tsuper(context);\n\t\t\toutName = dexDir;\n\t\t\tfileNames = input;\n\t\t\tjarOutput = false;\n\t\t\tmultiDex = true;\n\n\t\t\toptimize = true;\n\t\t\tlocalInfo = true;\n\t\t\tcoreLibrary = true;\n\n\t\t\tdebug = true;\n\t\t\twarnings = true;\n\t\t\tminSdkVersion = 28;\n\t\t}\n\t}\n\n\tpublic static void run(Path path, Path tempDirectory) {\n\t\tint result;\n\t\tString dxErrors;\n\t\ttry (ByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\t\tByteArrayOutputStream errOut = new ByteArrayOutputStream()) {\n\t\t\tDxContext context = new DxContext(out, errOut);\n\t\t\tDxArgs args = new DxArgs(\n\t\t\t\t\tcontext,\n\t\t\t\t\ttempDirectory.toAbsolutePath().toString(),\n\t\t\t\t\tnew String[] { path.toAbsolutePath().toString() });\n\t\t\tresult = new Main(context).runDx(args);\n\t\t\tdxErrors = errOut.toString(CHARSET_NAME);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"dx exception: \" + e.getMessage(), e);\n\t\t}\n\t\tif (result != 0) {\n\t\t\tthrow new RuntimeException(\"Java to dex conversion error, code: \" + result + \", errors: \" + dxErrors);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertLoader.java",
    "content": "package jadx.plugins.input.javaconvert;\n\nimport java.io.IOException;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Files;\nimport java.nio.file.LinkOption;\nimport java.nio.file.Path;\nimport java.nio.file.PathMatcher;\nimport java.nio.file.attribute.FileTime;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarOutputStream;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.JadxPluginContext;\nimport jadx.api.plugins.utils.CommonFileUtils;\nimport jadx.api.security.IJadxSecurity;\nimport jadx.zip.ZipReader;\n\npublic class JavaConvertLoader {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JavaConvertLoader.class);\n\n\tprivate final JavaConvertOptions options;\n\tprivate final ZipReader zipReader;\n\tprivate final IJadxSecurity security;\n\n\tpublic JavaConvertLoader(JavaConvertOptions options, JadxPluginContext context) {\n\t\tthis.options = options;\n\t\tthis.zipReader = context.getZipReader();\n\t\tthis.security = context.getArgs().getSecurity();\n\t}\n\n\tpublic ConvertResult process(List<Path> input) {\n\t\tConvertResult result = new ConvertResult();\n\t\tprocessJars(input, result);\n\t\tprocessAars(input, result);\n\t\tprocessClassFiles(input, result);\n\t\treturn result;\n\t}\n\n\tprivate void processJars(List<Path> input, ConvertResult result) {\n\t\tPathMatcher jarMatcher = FileSystems.getDefault().getPathMatcher(\"glob:**.jar\");\n\t\tinput.stream()\n\t\t\t\t.filter(jarMatcher::matches)\n\t\t\t\t.forEach(path -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconvertJar(result, path);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLOG.error(\"Failed to convert file: {}\", path.toAbsolutePath(), e);\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\tprivate void processClassFiles(List<Path> input, ConvertResult result) {\n\t\tPathMatcher jarMatcher = FileSystems.getDefault().getPathMatcher(\"glob:**.class\");\n\t\tList<Path> clsFiles = input.stream()\n\t\t\t\t.filter(jarMatcher::matches)\n\t\t\t\t.collect(Collectors.toList());\n\t\tif (clsFiles.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tLOG.debug(\"Converting class files ...\");\n\t\t\tPath jarFile = Files.createTempFile(\"jadx-\", \".jar\");\n\t\t\ttry (JarOutputStream jo = new JarOutputStream(Files.newOutputStream(jarFile))) {\n\t\t\t\tfor (Path file : clsFiles) {\n\t\t\t\t\tString clsName = AsmUtils.getNameFromClassFile(file);\n\t\t\t\t\tif (clsName == null) {\n\t\t\t\t\t\tthrow new IOException(\"Can't read class name from file: \" + file);\n\t\t\t\t\t}\n\t\t\t\t\tif (!security.isValidEntryName(clsName)) {\n\t\t\t\t\t\tLOG.warn(\"Skip class with invalid name: {}\", clsName);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\taddFileToJar(jo, file, clsName + \".class\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tresult.addTempPath(jarFile);\n\t\t\tLOG.debug(\"Packed {} class files into jar: {}\", clsFiles.size(), jarFile);\n\t\t\tconvertJar(result, jarFile);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Error process class files\", e);\n\t\t}\n\t}\n\n\tprivate void processAars(List<Path> input, ConvertResult result) {\n\t\tPathMatcher aarMatcher = FileSystems.getDefault().getPathMatcher(\"glob:**.aar\");\n\t\tinput.stream()\n\t\t\t\t.filter(aarMatcher::matches)\n\t\t\t\t.forEach(path -> zipReader.readEntries(path.toFile(), (entry, in) -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tString entryName = entry.getName();\n\t\t\t\t\t\tif (entryName.endsWith(\".jar\")) {\n\t\t\t\t\t\t\tPath tempJar = CommonFileUtils.saveToTempFile(in, \".jar\");\n\t\t\t\t\t\t\tresult.addTempPath(tempJar);\n\t\t\t\t\t\t\tLOG.debug(\"Loading jar: {} ...\", entryName);\n\t\t\t\t\t\t\tconvertJar(result, tempJar);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLOG.error(\"Failed to process zip entry: {}\", entry, e);\n\t\t\t\t\t}\n\t\t\t\t}));\n\t}\n\n\tprivate void convertJar(ConvertResult result, Path path) throws Exception {\n\t\tif (repackAndConvertJar(result, path)) {\n\t\t\treturn;\n\t\t}\n\t\tconvertSimpleJar(result, path);\n\t}\n\n\tprivate boolean repackAndConvertJar(ConvertResult result, Path path) throws Exception {\n\t\t// check if jar needs a full repackaging\n\t\tBoolean repackNeeded = zipReader.visitEntries(path.toFile(), zipEntry -> {\n\t\t\tString entryName = zipEntry.getName();\n\t\t\tif (zipEntry.isDirectory()) {\n\t\t\t\tif (entryName.equals(\"BOOT-INF/\")) {\n\t\t\t\t\treturn true; // Spring Boot jar\n\t\t\t\t}\n\t\t\t\tif (entryName.equals(\"META-INF/versions/\")) {\n\t\t\t\t\treturn true; // exclude duplicated classes\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (entryName.endsWith(\".jar\")) {\n\t\t\t\treturn true; // contains sub jars\n\t\t\t}\n\t\t\tif (entryName.endsWith(\"module-info.class\")) {\n\t\t\t\treturn true; // need to exclude module files\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t\tif (!Objects.equals(repackNeeded, Boolean.TRUE)) {\n\t\t\treturn false;\n\t\t}\n\t\tLOG.debug(\"Repacking jar file: {} ...\", path.toAbsolutePath());\n\t\tPath jarFile = Files.createTempFile(\"jadx-classes-\", \".jar\");\n\t\tresult.addTempPath(jarFile);\n\t\ttry (JarOutputStream jo = new JarOutputStream(Files.newOutputStream(jarFile))) {\n\t\t\tzipReader.readEntries(path.toFile(), (entry, in) -> {\n\t\t\t\ttry {\n\t\t\t\t\tString entryName = entry.getName();\n\t\t\t\t\tif (entryName.endsWith(\".class\")) {\n\t\t\t\t\t\tif (entryName.endsWith(\"module-info.class\")\n\t\t\t\t\t\t\t\t|| entryName.startsWith(\"META-INF/versions/\")) {\n\t\t\t\t\t\t\tLOG.debug(\" exclude: {}\", entryName);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbyte[] clsFileContent = CommonFileUtils.loadBytes(in);\n\t\t\t\t\t\tString clsName = AsmUtils.getNameFromClassFile(clsFileContent);\n\t\t\t\t\t\tif (clsName == null) {\n\t\t\t\t\t\t\tthrow new IOException(\"Can't read class name from file: \" + entryName);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!security.isValidEntryName(clsName)) {\n\t\t\t\t\t\t\tLOG.warn(\"Ignore class with invalid name: {} from {}\", clsName, entry);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\taddJarEntry(jo, clsName + \".class\", clsFileContent, null);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (entryName.endsWith(\".jar\")) {\n\t\t\t\t\t\tPath tempJar = CommonFileUtils.saveToTempFile(in, \".jar\");\n\t\t\t\t\t\tresult.addTempPath(tempJar);\n\t\t\t\t\t\tconvertJar(result, tempJar);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.error(\"Failed to process jar entry: {} in {}\", entry, path, e);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tconvertSimpleJar(result, jarFile);\n\t\treturn true;\n\t}\n\n\tprivate void convertSimpleJar(ConvertResult result, Path path) throws Exception {\n\t\tPath tempDirectory = Files.createTempDirectory(\"jadx-\");\n\t\tresult.addTempPath(tempDirectory);\n\t\tLOG.debug(\"Converting to dex ...\");\n\t\tconvert(path, tempDirectory);\n\t\tList<Path> dexFiles = collectFilesInDir(tempDirectory);\n\t\tLOG.debug(\"Converted {} to {} dex\", path.toAbsolutePath(), dexFiles.size());\n\t\tresult.addConvertedFiles(dexFiles);\n\t}\n\n\tprivate void convert(Path path, Path tempDirectory) {\n\t\tJavaConvertOptions.Mode mode = options.getMode();\n\t\tswitch (mode) {\n\t\t\tcase DX:\n\t\t\t\ttry {\n\t\t\t\t\tDxConverter.run(path, tempDirectory);\n\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\tLOG.error(\"DX convert failed, path: {}\", path, e);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase D8:\n\t\t\t\ttry {\n\t\t\t\t\tD8Converter.run(path, tempDirectory, options);\n\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\tLOG.error(\"D8 convert failed, path: {}\", path, e);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase BOTH:\n\t\t\t\ttry {\n\t\t\t\t\tDxConverter.run(path, tempDirectory);\n\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\tLOG.warn(\"DX convert failed, trying D8, path: {}\", path);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tD8Converter.run(path, tempDirectory, options);\n\t\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t\t\tLOG.error(\"D8 convert failed: {}\", ex.getMessage());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate static List<Path> collectFilesInDir(Path tempDirectory) throws IOException {\n\t\tPathMatcher dexMatcher = FileSystems.getDefault().getPathMatcher(\"glob:**.dex\");\n\t\ttry (Stream<Path> pathStream = Files.walk(tempDirectory, 1)) {\n\t\t\treturn pathStream\n\t\t\t\t\t.filter(p -> Files.isRegularFile(p, LinkOption.NOFOLLOW_LINKS))\n\t\t\t\t\t.filter(dexMatcher::matches)\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t}\n\t}\n\n\tprivate static void addFileToJar(JarOutputStream jar, Path source, String entryName) throws IOException {\n\t\tbyte[] fileContent = Files.readAllBytes(source);\n\t\tFileTime lastModifiedTime = Files.getLastModifiedTime(source, LinkOption.NOFOLLOW_LINKS);\n\t\taddJarEntry(jar, entryName, fileContent, lastModifiedTime);\n\t}\n\n\tprivate static void addJarEntry(JarOutputStream jar, String entryName, byte[] content,\n\t\t\tFileTime modTime) throws IOException {\n\t\tJarEntry entry = new JarEntry(entryName);\n\t\tif (modTime != null) {\n\t\t\tentry.setTime(modTime.toMillis());\n\t\t}\n\t\tjar.putNextEntry(entry);\n\t\tjar.write(content);\n\t\tjar.closeEntry();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertOptions.java",
    "content": "package jadx.plugins.input.javaconvert;\n\nimport jadx.api.plugins.options.impl.BasePluginOptionsBuilder;\n\nimport static jadx.plugins.input.javaconvert.JavaConvertPlugin.PLUGIN_ID;\n\npublic class JavaConvertOptions extends BasePluginOptionsBuilder {\n\n\tpublic enum Mode {\n\t\tDX, D8, BOTH\n\t}\n\n\tprivate Mode mode;\n\tprivate boolean d8Desugar;\n\n\t@Override\n\tpublic void registerOptions() {\n\t\tenumOption(PLUGIN_ID + \".mode\", Mode.values(), Mode::valueOf)\n\t\t\t\t.description(\"convert mode\")\n\t\t\t\t.defaultValue(Mode.BOTH)\n\t\t\t\t.setter(v -> mode = v);\n\n\t\tboolOption(PLUGIN_ID + \".d8-desugar\")\n\t\t\t\t.description(\"use desugar in d8\")\n\t\t\t\t.defaultValue(false)\n\t\t\t\t.setter(v -> d8Desugar = v);\n\t}\n\n\tpublic Mode getMode() {\n\t\treturn mode;\n\t}\n\n\tpublic boolean isD8Desugar() {\n\t\treturn d8Desugar;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertPlugin.java",
    "content": "package jadx.plugins.input.javaconvert;\n\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport jadx.api.plugins.JadxPlugin;\nimport jadx.api.plugins.JadxPluginContext;\nimport jadx.api.plugins.JadxPluginInfo;\nimport jadx.api.plugins.JadxPluginInfoBuilder;\nimport jadx.api.plugins.data.JadxPluginRuntimeData;\nimport jadx.api.plugins.input.ICodeLoader;\nimport jadx.api.plugins.input.JadxCodeInput;\nimport jadx.api.plugins.input.data.impl.EmptyCodeLoader;\nimport jadx.plugins.input.dex.DexInputPlugin;\n\npublic class JavaConvertPlugin implements JadxPlugin, JadxCodeInput {\n\tpublic static final String PLUGIN_ID = \"java-convert\";\n\n\tprivate final JavaConvertOptions options = new JavaConvertOptions();\n\n\tprivate JadxPluginRuntimeData dexInput;\n\tprivate JavaConvertLoader loader;\n\n\t@Override\n\tpublic JadxPluginInfo getPluginInfo() {\n\t\treturn JadxPluginInfoBuilder.pluginId(PLUGIN_ID)\n\t\t\t\t.name(\"Java Convert\")\n\t\t\t\t.description(\"Convert .class, .jar and .aar files to dex\")\n\t\t\t\t.provides(\"java-input\")\n\t\t\t\t.build();\n\t}\n\n\t@Override\n\tpublic void init(JadxPluginContext context) {\n\t\tcontext.registerOptions(options);\n\t\tdexInput = context.plugins().getById(DexInputPlugin.PLUGIN_ID);\n\t\tloader = new JavaConvertLoader(options, context);\n\t\tcontext.addCodeInput(this);\n\t}\n\n\t@Override\n\tpublic ICodeLoader loadFiles(List<Path> input) {\n\t\tConvertResult result = loader.process(input);\n\t\tif (result.isEmpty()) {\n\t\t\tresult.close();\n\t\t\treturn EmptyCodeLoader.INSTANCE;\n\t\t}\n\t\treturn dexInput.loadCodeFiles(result.getConverted(), result);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-convert/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin",
    "content": "jadx.plugins.input.javaconvert.JavaConvertPlugin\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-library\")\n}\n\ndependencies {\n\tapi(project(\":jadx-core\"))\n\n\t// show bytecode disassemble\n\timplementation(\"io.github.skylot:raung-disasm:0.1.1\")\n\n\ttestImplementation(project(\":jadx-core\"))\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/JavaClassReader.java",
    "content": "package jadx.plugins.input.java;\n\nimport jadx.api.plugins.input.data.IClassData;\nimport jadx.plugins.input.java.data.JavaClassData;\n\npublic class JavaClassReader {\n\tprivate final int id;\n\tprivate final String fileName;\n\tprivate final byte[] data;\n\n\tpublic JavaClassReader(int id, String fileName, byte[] data) {\n\t\tthis.id = id;\n\t\tthis.fileName = fileName;\n\t\tthis.data = data;\n\t}\n\n\tpublic IClassData loadClassData() {\n\t\treturn new JavaClassData(this);\n\t}\n\n\tpublic int getId() {\n\t\treturn id;\n\t}\n\n\tpublic String getFileName() {\n\t\treturn fileName;\n\t}\n\n\tpublic byte[] getData() {\n\t\treturn data;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn fileName;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/JavaInputLoader.java",
    "content": "package jadx.plugins.input.java;\n\nimport java.io.BufferedInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.utils.CommonFileUtils;\nimport jadx.core.plugins.files.TempFilesGetter;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.zip.IZipEntry;\nimport jadx.zip.ZipContent;\nimport jadx.zip.ZipReader;\n\npublic class JavaInputLoader {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JavaInputLoader.class);\n\n\tprivate static final int MAX_MAGIC_SIZE = 4;\n\tprivate static final byte[] JAVA_CLASS_FILE_MAGIC = { (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE };\n\tprivate static final byte[] ZIP_FILE_MAGIC = { 0x50, 0x4B, 0x03, 0x04 };\n\n\tprivate final ZipReader zipReader;\n\tprivate final Path tempPath;\n\n\tprivate int classUniqId = 1;\n\n\tpublic JavaInputLoader(ZipReader zipReader, Path tempPath) {\n\t\tthis.zipReader = zipReader;\n\t\tthis.tempPath = tempPath;\n\t}\n\n\t/**\n\t * This will use zip reader with default options and ignore provided in jadx args\n\t */\n\t@Deprecated\n\tpublic JavaInputLoader() {\n\t\tthis(new ZipReader(), TempFilesGetter.INSTANCE.getTempDir());\n\t}\n\n\tpublic List<JavaClassReader> collectFiles(List<Path> inputFiles) {\n\t\treturn inputFiles.stream()\n\t\t\t\t.map(Path::toFile)\n\t\t\t\t.map(this::loadFromFile)\n\t\t\t\t.filter(list -> !list.isEmpty())\n\t\t\t\t.flatMap(Collection::stream)\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tpublic List<JavaClassReader> loadInputStream(InputStream in, String name) throws IOException {\n\t\treturn loadReader(in, name, null, null);\n\t}\n\n\tpublic JavaClassReader loadClass(byte[] content, String fileName) {\n\t\treturn new JavaClassReader(getNextUniqId(), fileName, content);\n\t}\n\n\tprivate List<JavaClassReader> loadFromFile(File file) {\n\t\ttry (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {\n\t\t\treturn loadReader(inputStream, file.getName(), file, null);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"File open error: {}\", file.getAbsolutePath(), e);\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t}\n\n\tprivate List<JavaClassReader> loadReader(InputStream in, String name,\n\t\t\t@Nullable File file, @Nullable String parentFileName) throws IOException {\n\t\tbyte[] magic = new byte[MAX_MAGIC_SIZE];\n\t\tif (in.read(magic) != magic.length) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tif (isStartWithBytes(magic, JAVA_CLASS_FILE_MAGIC) || name.endsWith(\".class\")) {\n\t\t\tbyte[] data = CommonFileUtils.loadBytes(magic, in);\n\t\t\tString source = concatSource(parentFileName, name);\n\t\t\tJavaClassReader reader = new JavaClassReader(getNextUniqId(), source, data);\n\t\t\treturn Collections.singletonList(reader);\n\t\t}\n\t\tif (isStartWithBytes(magic, ZIP_FILE_MAGIC) || CommonFileUtils.isZipFileExt(name)) {\n\t\t\tif (file != null) {\n\t\t\t\treturn collectFromZip(file, name);\n\t\t\t}\n\t\t\tFile zipFile = CommonFileUtils.saveToTempFile(magic, in, \".zip\").toFile();\n\t\t\tList<JavaClassReader> readers = collectFromZip(zipFile, concatSource(parentFileName, name));\n\t\t\tCommonFileUtils.safeDeleteFile(zipFile);\n\t\t\treturn readers;\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\tprivate List<JavaClassReader> loadReaderFromZipEntry(byte[] content, String name, String parentFileName) throws IOException {\n\t\tif (isStartWithBytes(content, JAVA_CLASS_FILE_MAGIC) || name.endsWith(\".class\")) {\n\t\t\tString source = concatSource(parentFileName, name);\n\t\t\tJavaClassReader reader = new JavaClassReader(getNextUniqId(), source, content);\n\t\t\treturn Collections.singletonList(reader);\n\t\t}\n\t\tif (isStartWithBytes(content, ZIP_FILE_MAGIC) || CommonFileUtils.isZipFileExt(name)) {\n\t\t\tPath tempZip = Files.createTempFile(tempPath, \"temp\", \".zip\");\n\t\t\tFileUtils.writeFile(tempZip, content);\n\t\t\tFile zipFile = tempZip.toFile();\n\t\t\tList<JavaClassReader> readers = collectFromZip(zipFile, concatSource(parentFileName, name));\n\t\t\tCommonFileUtils.safeDeleteFile(zipFile);\n\t\t\treturn readers;\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\tprivate static String concatSource(@Nullable String parentFileName, String name) {\n\t\tif (parentFileName == null) {\n\t\t\treturn name;\n\t\t}\n\t\treturn parentFileName + ':' + name;\n\t}\n\n\tprivate List<JavaClassReader> collectFromZip(File file, String name) {\n\t\tList<JavaClassReader> result = new ArrayList<>();\n\t\ttry (ZipContent zip = zipReader.open(file)) {\n\t\t\tfor (IZipEntry entry : zip.getEntries()) {\n\t\t\t\tif (entry.isDirectory()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tString entryName = entry.getName();\n\t\t\t\tif (entryName.startsWith(\"META-INF/versions/\")) {\n\t\t\t\t\t// skip classes for different java versions\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tList<JavaClassReader> readers;\n\t\t\t\t\tif (entry.preferBytes()) {\n\t\t\t\t\t\treaders = loadReaderFromZipEntry(entry.getBytes(), entryName, name);\n\t\t\t\t\t} else {\n\t\t\t\t\t\treaders = loadReader(entry.getInputStream(), entryName, null, name);\n\t\t\t\t\t}\n\t\t\t\t\tresult.addAll(readers);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOG.error(\"Failed to read zip entry: {}\", entry, e);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to process zip file: {}\", name, e);\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic static boolean isStartWithBytes(byte[] fileMagic, byte[] expectedBytes) {\n\t\tint len = expectedBytes.length;\n\t\tif (fileMagic.length < len) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tif (fileMagic[i] != expectedBytes[i]) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate int getNextUniqId() {\n\t\treturn classUniqId++;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/JavaInputPlugin.java",
    "content": "package jadx.plugins.input.java;\n\nimport java.io.Closeable;\nimport java.io.InputStream;\nimport java.nio.file.Path;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.JadxPlugin;\nimport jadx.api.plugins.JadxPluginContext;\nimport jadx.api.plugins.JadxPluginInfo;\nimport jadx.api.plugins.input.ICodeLoader;\nimport jadx.api.plugins.input.data.impl.EmptyCodeLoader;\nimport jadx.plugins.input.java.utils.JavaClassParseException;\n\npublic class JavaInputPlugin implements JadxPlugin {\n\n\t@Override\n\tpublic JadxPluginInfo getPluginInfo() {\n\t\treturn new JadxPluginInfo(\"java-input\", \"Java Input\", \"Load .class and .jar files\");\n\t}\n\n\t@Override\n\tpublic void init(JadxPluginContext context) {\n\t\tcontext.addCodeInput(inputFiles -> {\n\t\t\tJavaInputLoader loader = new JavaInputLoader(context.getZipReader(), context.files().getPluginTempDir());\n\t\t\tList<JavaClassReader> readers = loader.collectFiles(inputFiles);\n\t\t\tif (readers.isEmpty()) {\n\t\t\t\treturn EmptyCodeLoader.INSTANCE;\n\t\t\t}\n\t\t\treturn new JavaLoadResult(readers, null);\n\t\t});\n\t}\n\n\tpublic static ICodeLoader loadClassFiles(List<Path> inputFiles) {\n\t\treturn loadClassFiles(inputFiles, null);\n\t}\n\n\tpublic static ICodeLoader loadClassFiles(List<Path> inputFiles, @Nullable Closeable closeable) {\n\t\tList<JavaClassReader> readers = new JavaInputLoader().collectFiles(inputFiles);\n\t\tif (readers.isEmpty()) {\n\t\t\treturn EmptyCodeLoader.INSTANCE;\n\t\t}\n\t\treturn new JavaLoadResult(readers, closeable);\n\t}\n\n\t/**\n\t * Method for provide several inputs by using load methods from {@link JavaInputLoader} class.\n\t */\n\tpublic static ICodeLoader load(Function<JavaInputLoader, List<JavaClassReader>> loader) {\n\t\treturn wrapClassReaders(loader.apply(new JavaInputLoader()));\n\t}\n\n\t/**\n\t * Convenient method for load class file or jar from input stream.\n\t * Should be used only once per JadxDecompiler instance.\n\t * For load several times use {@link JavaInputPlugin#load(Function)} method.\n\t */\n\tpublic static ICodeLoader loadFromInputStream(InputStream in, String fileName) {\n\t\ttry {\n\t\t\treturn wrapClassReaders(new JavaInputLoader().loadInputStream(in, fileName));\n\t\t} catch (Exception e) {\n\t\t\tthrow new JavaClassParseException(\"Failed to read input stream\", e);\n\t\t}\n\t}\n\n\t/**\n\t * Convenient method for load single class file by content.\n\t * Should be used only once per JadxDecompiler instance.\n\t * For load several times use {@link JavaInputPlugin#load(Function)} method.\n\t */\n\tpublic static ICodeLoader loadSingleClass(byte[] content, String fileName) {\n\t\tJavaClassReader reader = new JavaInputLoader().loadClass(content, fileName);\n\t\treturn new JavaLoadResult(Collections.singletonList(reader));\n\t}\n\n\tpublic static ICodeLoader wrapClassReaders(List<JavaClassReader> readers) {\n\t\tif (readers.isEmpty()) {\n\t\t\treturn EmptyCodeLoader.INSTANCE;\n\t\t}\n\t\treturn new JavaLoadResult(readers);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/JavaLoadResult.java",
    "content": "package jadx.plugins.input.java;\n\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.input.ICodeLoader;\nimport jadx.api.plugins.input.data.IClassData;\n\npublic class JavaLoadResult implements ICodeLoader {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JavaLoadResult.class);\n\n\tprivate final List<JavaClassReader> readers;\n\t@Nullable\n\tprivate final Closeable closeable;\n\n\tpublic JavaLoadResult(List<JavaClassReader> readers) {\n\t\tthis(readers, null);\n\t}\n\n\tpublic JavaLoadResult(List<JavaClassReader> readers, @Nullable Closeable closeable) {\n\t\tthis.readers = readers;\n\t\tthis.closeable = closeable;\n\t}\n\n\t@Override\n\tpublic void visitClasses(Consumer<IClassData> consumer) {\n\t\tfor (JavaClassReader reader : readers) {\n\t\t\ttry {\n\t\t\t\tconsumer.accept(reader.loadClassData());\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to load class data for file: {}\", reader.getFileName(), e);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isEmpty() {\n\t\treturn readers.isEmpty();\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\tif (closeable != null) {\n\t\t\tcloseable.close();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/ClassOffsets.java",
    "content": "package jadx.plugins.input.java.data;\n\npublic class ClassOffsets {\n\n\tprivate final int[] constPoolOffsets;\n\tprivate final int constPoolEnd;\n\tprivate final int interfacesEnd;\n\tprivate final int attributesOffset;\n\n\tpublic ClassOffsets(DataReader data) {\n\t\tthis.constPoolOffsets = readConstPool(data);\n\t\tthis.constPoolEnd = data.getOffset();\n\t\tint interfacesCount = data.absPos(constPoolEnd + 6).readU2();\n\t\tdata.skip(interfacesCount * 2);\n\t\tthis.interfacesEnd = data.getOffset();\n\t\tskipFields(data);\n\t\tskipMethods(data);\n\t\tthis.attributesOffset = data.getOffset();\n\t}\n\n\tprivate static int[] readConstPool(DataReader data) {\n\t\tint cpSize = data.absPos(8).readU2();\n\t\tint[] cpOffsets = new int[cpSize + 1];\n\t\tfor (int i = 1; i < cpSize; i++) {\n\t\t\tint tag = data.readU1();\n\t\t\tcpOffsets[i] = data.getOffset();\n\t\t\tConstantType constType = ConstantType.getTypeByTag(tag);\n\t\t\tswitch (constType) {\n\t\t\t\tcase UTF8:\n\t\t\t\t\tdata.skip(data.readU2());\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase LONG:\n\t\t\t\tcase DOUBLE:\n\t\t\t\t\tdata.skip(8);\n\t\t\t\t\ti++;\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tdata.skip(constType.getDataSize());\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn cpOffsets;\n\t}\n\n\tprivate void skipFields(DataReader data) {\n\t\tint fieldsCount = data.readU2();\n\t\tfor (int i = 0; i < fieldsCount; i++) {\n\t\t\tdata.skip(6);\n\t\t\tskipAttributes(data);\n\t\t}\n\t}\n\n\tprivate void skipMethods(DataReader data) {\n\t\tint methodsCount = data.readU2();\n\t\tfor (int i = 0; i < methodsCount; i++) {\n\t\t\tdata.skip(6);\n\t\t\tskipAttributes(data);\n\t\t}\n\t}\n\n\tprivate void skipAttributes(DataReader data) {\n\t\tint attrCount = data.readU2();\n\t\tfor (int i = 0; i < attrCount; i++) {\n\t\t\tdata.skip(2);\n\t\t\tint len = data.readU4();\n\t\t\tdata.skip(len);\n\t\t}\n\t}\n\n\tpublic int getOffsetOfConstEntry(int num) {\n\t\treturn constPoolOffsets[num];\n\t}\n\n\tpublic int getAccessFlagsOffset() {\n\t\treturn constPoolEnd;\n\t}\n\n\tpublic int getClsTypeOffset() {\n\t\treturn constPoolEnd + 2;\n\t}\n\n\tpublic int getSuperTypeOffset() {\n\t\treturn constPoolEnd + 4;\n\t}\n\n\tpublic int getInterfacesOffset() {\n\t\treturn constPoolEnd + 6;\n\t}\n\n\tpublic int getFieldsOffset() {\n\t\treturn interfacesEnd;\n\t}\n\n\tpublic int getAttributesOffset() {\n\t\treturn attributesOffset;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/ConstPoolReader.java",
    "content": "package jadx.plugins.input.java.data;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.ICallSite;\nimport jadx.api.plugins.input.data.IFieldRef;\nimport jadx.api.plugins.input.data.IMethodHandle;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.input.data.MethodHandleType;\nimport jadx.api.plugins.input.data.annotations.EncodedType;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.impl.CallSite;\nimport jadx.api.plugins.input.data.impl.FieldRefHandle;\nimport jadx.api.plugins.input.data.impl.MethodRefHandle;\nimport jadx.plugins.input.java.JavaClassReader;\nimport jadx.plugins.input.java.data.attributes.JavaAttrType;\nimport jadx.plugins.input.java.data.attributes.types.JavaBootstrapMethodsAttr;\nimport jadx.plugins.input.java.data.attributes.types.data.RawBootstrapMethod;\nimport jadx.plugins.input.java.utils.DescriptorParser;\nimport jadx.plugins.input.java.utils.JavaClassParseException;\nimport jadx.plugins.input.java.utils.ModifiedUTF8Decoder;\n\npublic class ConstPoolReader {\n\tprivate final JavaClassReader clsReader;\n\tprivate final JavaClassData clsData;\n\tprivate final DataReader data;\n\tprivate final ClassOffsets offsets;\n\n\tpublic ConstPoolReader(JavaClassReader clsReader, JavaClassData javaClassData, DataReader data, ClassOffsets offsets) {\n\t\tthis.clsReader = clsReader;\n\t\tthis.clsData = javaClassData;\n\t\tthis.data = data;\n\t\tthis.offsets = offsets;\n\t}\n\n\t@Nullable\n\tpublic String getClass(int idx) {\n\t\tjumpToData(idx);\n\t\tint nameIdx = data.readU2();\n\t\treturn fixType(getUtf8(nameIdx));\n\t}\n\n\tpublic IFieldRef getFieldRef(int idx) {\n\t\tjumpToData(idx);\n\t\tint clsIdx = data.readU2();\n\t\tint nameTypeIdx = data.readU2();\n\t\tjumpToData(nameTypeIdx);\n\t\tint nameIdx = data.readU2();\n\t\tint typeIdx = data.readU2();\n\n\t\tJavaFieldData fieldData = new JavaFieldData();\n\t\tfieldData.setParentClassType(getClass(clsIdx));\n\t\tfieldData.setName(getUtf8(nameIdx));\n\t\tfieldData.setType(getUtf8(typeIdx));\n\t\treturn fieldData;\n\t}\n\n\tpublic String getFieldType(int idx) {\n\t\tjumpToData(idx);\n\t\tdata.skip(2);\n\t\tint nameTypeIdx = data.readU2();\n\t\tjumpToData(nameTypeIdx);\n\t\tdata.skip(2);\n\t\tint typeIdx = data.readU2();\n\t\treturn getUtf8(typeIdx);\n\t}\n\n\tpublic IMethodRef getMethodRef(int idx) {\n\t\tjumpToData(idx);\n\t\tint clsIdx = data.readU2();\n\t\tint nameTypeIdx = data.readU2();\n\t\tjumpToData(nameTypeIdx);\n\t\tint nameIdx = data.readU2();\n\t\tint descIdx = data.readU2();\n\n\t\tJavaMethodRef mthRef = new JavaMethodRef();\n\t\tmthRef.initUniqId(clsReader, idx, true);\n\t\tmthRef.setParentClassType(getClass(clsIdx));\n\t\tmthRef.setName(getUtf8(nameIdx));\n\t\tmthRef.setDescr(getUtf8(descIdx));\n\t\treturn mthRef;\n\t}\n\n\tpublic ICallSite getCallSite(int idx) {\n\t\tConstantType constType = jumpToConst(idx);\n\t\tswitch (constType) {\n\t\t\tcase INVOKE_DYNAMIC:\n\t\t\t\tint bootstrapMthIdx = data.readU2();\n\t\t\t\tint nameAndTypeIdx = data.readU2();\n\t\t\t\tjumpToData(nameAndTypeIdx);\n\t\t\t\tint nameIdx = data.readU2();\n\t\t\t\tint descIdx = data.readU2();\n\t\t\t\treturn resolveMethodCallSite(bootstrapMthIdx, nameIdx, descIdx);\n\t\t\tcase DYNAMIC:\n\t\t\t\tthrow new JavaClassParseException(\"Field call site not yet implemented\");\n\t\t\tdefault:\n\t\t\t\tthrow new JavaClassParseException(\"Unexpected tag type for call site: \" + constType);\n\t\t}\n\t}\n\n\tprivate CallSite resolveMethodCallSite(int bootstrapMthIdx, int nameIdx, int descIdx) {\n\t\tJavaBootstrapMethodsAttr bootstrapMethodsAttr = clsData.loadClassAttribute(data, JavaAttrType.BOOTSTRAP_METHODS);\n\t\tif (bootstrapMethodsAttr == null) {\n\t\t\tthrow new JavaClassParseException(\"Unexpected missing BootstrapMethods attribute\");\n\t\t}\n\t\tRawBootstrapMethod rawBootstrapMethod = bootstrapMethodsAttr.getList().get(bootstrapMthIdx);\n\n\t\tList<EncodedValue> values = new ArrayList<>(6);\n\t\tvalues.add(new EncodedValue(EncodedType.ENCODED_METHOD_HANDLE, getMethodHandle(rawBootstrapMethod.getMethodHandleIdx())));\n\t\tvalues.add(new EncodedValue(EncodedType.ENCODED_STRING, getUtf8(nameIdx)));\n\t\tvalues.add(new EncodedValue(EncodedType.ENCODED_METHOD_TYPE, DescriptorParser.parseToMethodProto(getUtf8(descIdx))));\n\t\tfor (int argConstIdx : rawBootstrapMethod.getArgs()) {\n\t\t\tvalues.add(readAsEncodedValue(argConstIdx));\n\t\t}\n\t\treturn new CallSite(values);\n\t}\n\n\tprivate IMethodHandle getMethodHandle(int idx) {\n\t\tjumpToData(idx);\n\t\tint kind = data.readU1();\n\t\tint refIdx = data.readU2();\n\t\tMethodHandleType handleType = convertMethodHandleKind(kind);\n\t\tif (handleType.isField()) {\n\t\t\treturn new FieldRefHandle(handleType, getFieldRef(refIdx));\n\t\t}\n\t\treturn new MethodRefHandle(handleType, getMethodRef(refIdx));\n\t}\n\n\tprivate MethodHandleType convertMethodHandleKind(int kind) {\n\t\tswitch (kind) {\n\t\t\tcase 1:\n\t\t\t\treturn MethodHandleType.STATIC_PUT;\n\t\t\tcase 2:\n\t\t\t\treturn MethodHandleType.STATIC_GET;\n\t\t\tcase 3:\n\t\t\t\treturn MethodHandleType.INSTANCE_PUT;\n\t\t\tcase 4:\n\t\t\t\treturn MethodHandleType.INSTANCE_GET;\n\t\t\tcase 5:\n\t\t\t\treturn MethodHandleType.INVOKE_INSTANCE;\n\t\t\tcase 6:\n\t\t\t\treturn MethodHandleType.INVOKE_STATIC;\n\t\t\tcase 7:\n\t\t\t\treturn MethodHandleType.INVOKE_DIRECT;\n\t\t\tcase 8:\n\t\t\t\treturn MethodHandleType.INVOKE_CONSTRUCTOR;\n\t\t\tcase 9:\n\t\t\t\treturn MethodHandleType.INVOKE_INTERFACE;\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"Unknown method handle type: \" + kind);\n\t\t}\n\t}\n\n\tpublic String getUtf8(int idx) {\n\t\tif (idx == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tjumpToData(idx);\n\t\treturn readString();\n\t}\n\n\tpublic ConstantType jumpToConst(int idx) {\n\t\tjumpToTag(idx);\n\t\treturn ConstantType.getTypeByTag(data.readU1());\n\t}\n\n\tpublic String readString() {\n\t\tint len = data.readU2();\n\t\tbyte[] bytes = data.readBytes(len);\n\t\treturn parseString(bytes);\n\t}\n\n\tpublic int readU2() {\n\t\treturn data.readU2();\n\t}\n\n\tpublic int readU4() {\n\t\treturn data.readU4();\n\t}\n\n\tpublic long readU8() {\n\t\treturn data.readU8();\n\t}\n\n\tpublic int getInt(int idx) {\n\t\tjumpToData(idx);\n\t\treturn data.readS4();\n\t}\n\n\tpublic long getLong(int idx) {\n\t\tjumpToData(idx);\n\t\treturn data.readS8();\n\t}\n\n\tpublic double getDouble(int idx) {\n\t\tjumpToData(idx);\n\t\treturn Double.longBitsToDouble(data.readU8());\n\t}\n\n\tpublic float getFloat(int idx) {\n\t\tjumpToData(idx);\n\t\treturn Float.intBitsToFloat(data.readU4());\n\t}\n\n\tpublic EncodedValue readAsEncodedValue(int idx) {\n\t\tConstantType constantType = jumpToConst(idx);\n\t\tswitch (constantType) {\n\t\t\tcase UTF8:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_STRING, readString());\n\t\t\tcase STRING:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_STRING, getUtf8(readU2()));\n\t\t\tcase INTEGER:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_INT, data.readS4());\n\t\t\tcase FLOAT:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_FLOAT, Float.intBitsToFloat(data.readU4()));\n\t\t\tcase LONG:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_LONG, data.readS8());\n\t\t\tcase DOUBLE:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_DOUBLE, Double.longBitsToDouble(data.readU8()));\n\t\t\tcase CLASS:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_TYPE, getClass(idx));\n\t\t\tcase METHOD_TYPE:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_METHOD_TYPE, DescriptorParser.parseToMethodProto(getUtf8(readU2())));\n\t\t\tcase METHOD_HANDLE:\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_METHOD_HANDLE, getMethodHandle(idx));\n\n\t\t\tdefault:\n\t\t\t\tthrow new JavaClassParseException(\"Can't encode constant \" + constantType + \" as encoded value\");\n\t\t}\n\t}\n\n\t@NotNull\n\tprivate String parseString(byte[] bytes) {\n\t\treturn ModifiedUTF8Decoder.decodeString(bytes);\n\t}\n\n\tprivate String fixType(String clsName) {\n\t\tswitch (clsName.charAt(0)) {\n\t\t\tcase '[':\n\t\t\t\treturn clsName;\n\n\t\t\tcase 'L':\n\t\t\tcase 'T':\n\t\t\t\tif (clsName.endsWith(\";\")) {\n\t\t\t\t\treturn clsName;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t\treturn 'L' + clsName + ';';\n\t}\n\n\tprivate void jumpToData(int idx) {\n\t\tdata.absPos(offsets.getOffsetOfConstEntry(idx));\n\t}\n\n\tprivate void jumpToTag(int idx) {\n\t\tdata.absPos(offsets.getOffsetOfConstEntry(idx) - 1);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/ConstantType.java",
    "content": "package jadx.plugins.input.java.data;\n\nimport java.util.stream.Stream;\n\nimport jadx.plugins.input.java.utils.JavaClassParseException;\n\npublic enum ConstantType {\n\tUTF8(1, -1),\n\tINTEGER(3, 4),\n\tFLOAT(4, 4),\n\tLONG(5, 8),\n\tDOUBLE(6, 8),\n\tCLASS(7, 2),\n\tSTRING(8, 2),\n\tFIELD_REF(9, 4),\n\tMETHOD_REF(10, 4),\n\tINTERFACE_METHOD_REF(11, 4),\n\tNAME_AND_TYPE(12, 4),\n\tMETHOD_HANDLE(15, 3),\n\tMETHOD_TYPE(16, 2),\n\tDYNAMIC(17, 4),\n\tINVOKE_DYNAMIC(18, 4),\n\tMODULE(19, 2),\n\tPACKAGE(20, 2);\n\n\tprivate static final ConstantType[] TAG_MAP;\n\n\tstatic {\n\t\tConstantType[] values = ConstantType.values();\n\t\tint maxVal = Stream.of(values)\n\t\t\t\t.mapToInt(ConstantType::getTag)\n\t\t\t\t.max()\n\t\t\t\t.orElseThrow(() -> new IllegalArgumentException(\"Empty ConstantType enum\"));\n\n\t\tConstantType[] map = new ConstantType[maxVal + 1];\n\t\tfor (ConstantType value : values) {\n\t\t\tmap[value.getTag()] = value;\n\t\t}\n\t\tTAG_MAP = map;\n\t}\n\n\tpublic static ConstantType getTypeByTag(int tag) {\n\t\tConstantType type = TAG_MAP[tag];\n\t\tif (type == null) {\n\t\t\tthrow new JavaClassParseException(\"Unknown constant pool tag: \" + tag);\n\t\t}\n\t\treturn type;\n\t}\n\n\tprivate final byte tag;\n\tprivate final int dataSize;\n\n\tConstantType(int tag, int dataSize) {\n\t\tthis.tag = (byte) tag;\n\t\tthis.dataSize = dataSize;\n\t}\n\n\tpublic byte getTag() {\n\t\treturn tag;\n\t}\n\n\tpublic int getDataSize() {\n\t\treturn dataSize;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/DataReader.java",
    "content": "package jadx.plugins.input.java.data;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class DataReader {\n\tprivate final byte[] data;\n\tprivate int offset;\n\n\tpublic DataReader(byte[] data) {\n\t\tthis(data, 0);\n\t}\n\n\tpublic DataReader(byte[] data, int offset) {\n\t\tthis.data = data;\n\t\tthis.offset = offset;\n\t}\n\n\tpublic DataReader copy() {\n\t\treturn new DataReader(data, offset);\n\t}\n\n\tpublic DataReader absPos(int offset) {\n\t\tthis.offset = offset;\n\t\treturn this;\n\t}\n\n\tpublic int getOffset() {\n\t\treturn offset;\n\t}\n\n\tpublic void skip(int size) {\n\t\tthis.offset += size;\n\t}\n\n\tpublic int readS1() {\n\t\tint pos = this.offset;\n\t\tbyte b1 = this.data[pos];\n\t\tthis.offset = pos + 1;\n\t\treturn b1;\n\t}\n\n\tpublic int readU1() {\n\t\tint pos = this.offset;\n\t\tbyte b1 = this.data[pos];\n\t\tthis.offset = pos + 1;\n\t\treturn b1 & 0xFF;\n\t}\n\n\tpublic int readS2() {\n\t\tint pos = this.offset;\n\t\tbyte[] d = this.data;\n\t\tbyte b1 = d[pos++];\n\t\tbyte b2 = d[pos++];\n\t\tthis.offset = pos;\n\t\treturn b1 << 8 | (b2 & 0xFF);\n\t}\n\n\tpublic int readU2() {\n\t\tint pos = this.offset;\n\t\tbyte[] d = this.data;\n\t\tbyte b1 = d[pos++];\n\t\tbyte b2 = d[pos++];\n\t\tthis.offset = pos;\n\t\treturn (b1 & 0xFF) << 8 | (b2 & 0xFF);\n\t}\n\n\tpublic int readS4() {\n\t\tint pos = this.offset;\n\t\tbyte[] d = this.data;\n\t\tbyte b1 = d[pos++];\n\t\tbyte b2 = d[pos++];\n\t\tbyte b3 = d[pos++];\n\t\tbyte b4 = d[pos++];\n\t\tthis.offset = pos;\n\t\treturn b1 << 24 | (b2 & 0xFF) << 16 | (b3 & 0xFF) << 8 | (b4 & 0xFF);\n\t}\n\n\tpublic int readU4() {\n\t\tint pos = this.offset;\n\t\tbyte[] d = this.data;\n\t\tbyte b1 = d[pos++];\n\t\tbyte b2 = d[pos++];\n\t\tbyte b3 = d[pos++];\n\t\tbyte b4 = d[pos++];\n\t\tthis.offset = pos;\n\t\treturn (b1 & 0xFF) << 24 | (b2 & 0xFF) << 16 | (b3 & 0xFF) << 8 | (b4 & 0xFF);\n\t}\n\n\tpublic long readS8() {\n\t\tlong high = readS4();\n\t\tlong low = readU4() & 0xFFFF_FFFFL;\n\t\treturn high << 32 | low;\n\t}\n\n\tpublic long readU8() {\n\t\tlong high = readU4() & 0xFFFF_FFFFL;\n\t\tlong low = readU4() & 0xFFFF_FFFFL;\n\t\treturn high << 32 | low;\n\t}\n\n\tpublic byte[] readBytes(int len) {\n\t\tint pos = this.offset;\n\t\tthis.offset = pos + len;\n\t\treturn Arrays.copyOfRange(data, pos, pos + len);\n\t}\n\n\tpublic List<String> readClassesList(ConstPoolReader constPool) {\n\t\tint len = readU2();\n\t\tif (len == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<String> list = new ArrayList<>(len);\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tlist.add(constPool.getClass(readU2()));\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic byte[] getBytes() {\n\t\treturn data;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/JavaClassData.java",
    "content": "package jadx.plugins.input.java.data;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.AccessFlags;\nimport jadx.api.plugins.input.data.IClassData;\nimport jadx.api.plugins.input.data.IFieldData;\nimport jadx.api.plugins.input.data.IMethodData;\nimport jadx.api.plugins.input.data.ISeqConsumer;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.api.plugins.utils.Utils;\nimport jadx.plugins.input.java.JavaClassReader;\nimport jadx.plugins.input.java.data.attributes.AttributesReader;\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.JavaAttrStorage;\nimport jadx.plugins.input.java.data.attributes.JavaAttrType;\nimport jadx.plugins.input.java.data.attributes.types.JavaAnnotationsAttr;\nimport jadx.plugins.input.java.utils.DisasmUtils;\n\npublic class JavaClassData implements IClassData {\n\tprivate final JavaClassReader clsReader;\n\tprivate final DataReader data;\n\tprivate final ClassOffsets offsets;\n\tprivate final ConstPoolReader constPoolReader;\n\tprivate final AttributesReader attributesReader;\n\n\tpublic JavaClassData(JavaClassReader clsReader) {\n\t\tthis.clsReader = clsReader;\n\t\tthis.data = new DataReader(clsReader.getData());\n\t\tthis.offsets = new ClassOffsets(this.data);\n\t\tthis.constPoolReader = new ConstPoolReader(clsReader, this, this.data.copy(), this.offsets);\n\t\tthis.attributesReader = new AttributesReader(this, this.constPoolReader);\n\t}\n\n\t@Override\n\tpublic int getInputFileOffset() {\n\t\treturn offsets.getAccessFlagsOffset();\n\t}\n\n\t@Override\n\tpublic IClassData copy() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic int getAccessFlags() {\n\t\treturn data.absPos(offsets.getAccessFlagsOffset()).readU2();\n\t}\n\n\t@Override\n\tpublic String getType() {\n\t\tint idx = data.absPos(offsets.getClsTypeOffset()).readU2();\n\t\treturn constPoolReader.getClass(idx);\n\t}\n\n\t@Override\n\t@Nullable\n\tpublic String getSuperType() {\n\t\tint idx = data.absPos(offsets.getSuperTypeOffset()).readU2();\n\t\tif (idx == 0) {\n\t\t\treturn null;\n\t\t}\n\t\treturn constPoolReader.getClass(idx);\n\t}\n\n\t@Override\n\tpublic List<String> getInterfacesTypes() {\n\t\tdata.absPos(offsets.getInterfacesOffset());\n\t\treturn data.readClassesList(constPoolReader);\n\t}\n\n\t@Override\n\tpublic String getInputFileName() {\n\t\treturn this.clsReader.getFileName();\n\t}\n\n\t@Override\n\tpublic void visitFieldsAndMethods(ISeqConsumer<IFieldData> fieldsConsumer, ISeqConsumer<IMethodData> mthConsumer) {\n\t\tint clsIdx = data.absPos(offsets.getClsTypeOffset()).readU2();\n\t\tString classType = constPoolReader.getClass(clsIdx);\n\t\tDataReader reader = data.absPos(offsets.getFieldsOffset()).copy();\n\t\tint fieldsCount = reader.readU2();\n\t\tfieldsConsumer.init(fieldsCount);\n\t\tif (fieldsCount != 0) {\n\t\t\tJavaFieldData field = new JavaFieldData();\n\t\t\tfield.setParentClassType(classType);\n\t\t\tfor (int i = 0; i < fieldsCount; i++) {\n\t\t\t\tparseField(reader, field);\n\t\t\t\tfieldsConsumer.accept(field);\n\t\t\t}\n\t\t}\n\n\t\tint methodsCount = reader.readU2();\n\t\tmthConsumer.init(methodsCount);\n\t\tif (methodsCount != 0) {\n\t\t\tJavaMethodRef methodRef = new JavaMethodRef();\n\t\t\tmethodRef.setParentClassType(classType);\n\t\t\tJavaMethodData method = new JavaMethodData(this, methodRef);\n\t\t\tfor (int i = 0; i < methodsCount; i++) {\n\t\t\t\tparseMethod(reader, method, i);\n\t\t\t\tmthConsumer.accept(method);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void parseField(DataReader reader, JavaFieldData field) {\n\t\tint accessFlags = reader.readU2();\n\t\tint nameIdx = reader.readU2();\n\t\tint typeIdx = reader.readU2();\n\t\tJavaAttrStorage attributes = attributesReader.loadAll(reader);\n\n\t\tfield.setAccessFlags(accessFlags);\n\t\tfield.setName(constPoolReader.getUtf8(nameIdx));\n\t\tfield.setType(constPoolReader.getUtf8(typeIdx));\n\t\tfield.setAttributes(attributes);\n\t}\n\n\tprivate void parseMethod(DataReader reader, JavaMethodData method, int id) {\n\t\tint accessFlags = reader.readU2();\n\t\tint nameIdx = reader.readU2();\n\t\tint descriptorIdx = reader.readU2();\n\t\tJavaAttrStorage attributes = attributesReader.loadAll(reader);\n\n\t\tJavaMethodRef methodRef = method.getMethodRef();\n\t\tmethodRef.reset();\n\t\tmethodRef.initUniqId(clsReader, id, false);\n\t\tmethodRef.setName(constPoolReader.getUtf8(nameIdx));\n\t\tmethodRef.setDescr(constPoolReader.getUtf8(descriptorIdx));\n\n\t\tif (methodRef.getName().equals(\"<init>\")) {\n\t\t\taccessFlags |= AccessFlags.CONSTRUCTOR; // java bytecode don't use that flag\n\t\t}\n\n\t\tmethod.setData(accessFlags, attributes);\n\t}\n\n\tpublic DataReader getData() {\n\t\treturn data;\n\t}\n\n\t@Override\n\tpublic List<IJadxAttribute> getAttributes() {\n\t\tdata.absPos(offsets.getAttributesOffset());\n\t\tJavaAttrStorage attributes = attributesReader.loadAll(data);\n\t\tint size = attributes.size();\n\t\tif (size == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<IJadxAttribute> list = new ArrayList<>(size);\n\t\tUtils.addToList(list, JavaAnnotationsAttr.merge(attributes));\n\t\tUtils.addToList(list, attributes.get(JavaAttrType.INNER_CLASSES));\n\t\tUtils.addToList(list, attributes.get(JavaAttrType.SOURCE_FILE));\n\t\tUtils.addToList(list, attributes.get(JavaAttrType.SIGNATURE));\n\t\treturn list;\n\t}\n\n\tpublic <T extends IJavaAttribute> T loadClassAttribute(DataReader reader, JavaAttrType<T> type) {\n\t\treader.absPos(offsets.getAttributesOffset());\n\t\treturn attributesReader.loadOne(reader, type);\n\t}\n\n\t@Override\n\tpublic String getDisassembledCode() {\n\t\treturn DisasmUtils.get(data.getBytes());\n\t}\n\n\tpublic JavaClassReader getClsReader() {\n\t\treturn clsReader;\n\t}\n\n\tpublic ClassOffsets getOffsets() {\n\t\treturn offsets;\n\t}\n\n\tpublic ConstPoolReader getConstPoolReader() {\n\t\treturn constPoolReader;\n\t}\n\n\tpublic AttributesReader getAttributesReader() {\n\t\treturn attributesReader;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getInputFileName();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/JavaFieldData.java",
    "content": "package jadx.plugins.input.java.data;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.IFieldData;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.api.plugins.utils.Utils;\nimport jadx.plugins.input.java.data.attributes.JavaAttrStorage;\nimport jadx.plugins.input.java.data.attributes.JavaAttrType;\nimport jadx.plugins.input.java.data.attributes.types.ConstValueAttr;\nimport jadx.plugins.input.java.data.attributes.types.JavaAnnotationsAttr;\n\npublic class JavaFieldData implements IFieldData {\n\tprivate String name;\n\tprivate String parentClassType;\n\tprivate String type;\n\tprivate int accessFlags;\n\tprivate JavaAttrStorage attributes;\n\n\t@Override\n\tpublic String getParentClassType() {\n\t\treturn parentClassType;\n\t}\n\n\tpublic void setParentClassType(String parentClassType) {\n\t\tthis.parentClassType = parentClassType;\n\t}\n\n\t@Override\n\tpublic String getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setType(String type) {\n\t\tthis.type = type;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\t@Override\n\tpublic int getAccessFlags() {\n\t\treturn accessFlags;\n\t}\n\n\tpublic void setAccessFlags(int accessFlags) {\n\t\tthis.accessFlags = accessFlags;\n\t}\n\n\tpublic void setAttributes(JavaAttrStorage attributes) {\n\t\tthis.attributes = attributes;\n\t}\n\n\t@Override\n\tpublic List<IJadxAttribute> getAttributes() {\n\t\tint size = attributes.size();\n\t\tif (size == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<IJadxAttribute> list = new ArrayList<>(size);\n\t\tUtils.addToList(list, JavaAnnotationsAttr.merge(attributes));\n\t\tUtils.addToList(list, attributes.get(JavaAttrType.CONST_VALUE), ConstValueAttr::getValue);\n\t\tUtils.addToList(list, attributes.get(JavaAttrType.SIGNATURE));\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn parentClassType + \"->\" + name + \":\" + type;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/JavaMethodData.java",
    "content": "package jadx.plugins.input.java.data;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.ICodeReader;\nimport jadx.api.plugins.input.data.IMethodData;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.api.plugins.utils.Utils;\nimport jadx.plugins.input.java.data.attributes.JavaAttrStorage;\nimport jadx.plugins.input.java.data.attributes.JavaAttrType;\nimport jadx.plugins.input.java.data.attributes.types.CodeAttr;\nimport jadx.plugins.input.java.data.attributes.types.JavaAnnotationDefaultAttr;\nimport jadx.plugins.input.java.data.attributes.types.JavaAnnotationsAttr;\nimport jadx.plugins.input.java.data.attributes.types.JavaParamAnnsAttr;\nimport jadx.plugins.input.java.data.code.JavaCodeReader;\n\npublic class JavaMethodData implements IMethodData {\n\n\tprivate final JavaClassData clsData;\n\tprivate final JavaMethodRef methodRef;\n\tprivate int accessFlags;\n\tprivate JavaAttrStorage attributes;\n\n\tpublic JavaMethodData(JavaClassData clsData, JavaMethodRef methodRef) {\n\t\tthis.clsData = clsData;\n\t\tthis.methodRef = methodRef;\n\t}\n\n\tpublic void setData(int accessFlags, JavaAttrStorage attributes) {\n\t\tthis.accessFlags = accessFlags;\n\t\tthis.attributes = attributes;\n\t}\n\n\t@Override\n\tpublic JavaMethodRef getMethodRef() {\n\t\treturn methodRef;\n\t}\n\n\t@Override\n\tpublic int getAccessFlags() {\n\t\treturn accessFlags;\n\t}\n\n\t@Override\n\tpublic @Nullable ICodeReader getCodeReader() {\n\t\tCodeAttr codeAttr = attributes.get(JavaAttrType.CODE);\n\t\tif (codeAttr == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new JavaCodeReader(clsData, codeAttr.getOffset());\n\t}\n\n\t@Override\n\tpublic String disassembleMethod() {\n\t\treturn \"\";\n\t}\n\n\t@Override\n\tpublic List<IJadxAttribute> getAttributes() {\n\t\tint size = attributes.size();\n\t\tif (size == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<IJadxAttribute> list = new ArrayList<>(size);\n\t\tUtils.addToList(list, JavaAnnotationsAttr.merge(attributes));\n\t\tUtils.addToList(list, JavaParamAnnsAttr.merge(attributes));\n\t\tUtils.addToList(list, JavaAnnotationDefaultAttr.convert(attributes));\n\t\tUtils.addToList(list, attributes.get(JavaAttrType.SIGNATURE));\n\t\tUtils.addToList(list, attributes.get(JavaAttrType.EXCEPTIONS));\n\t\tUtils.addToList(list, attributes.get(JavaAttrType.METHOD_PARAMETERS));\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getMethodRef().toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/JavaMethodProto.java",
    "content": "package jadx.plugins.input.java.data;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.IMethodProto;\nimport jadx.api.plugins.utils.Utils;\n\npublic class JavaMethodProto implements IMethodProto {\n\n\tprivate String returnType;\n\tprivate List<String> argTypes;\n\n\t@Override\n\tpublic String getReturnType() {\n\t\treturn returnType;\n\t}\n\n\tpublic void setReturnType(String returnType) {\n\t\tthis.returnType = returnType;\n\t}\n\n\t@Override\n\tpublic List<String> getArgTypes() {\n\t\treturn argTypes;\n\t}\n\n\tpublic void setArgTypes(List<String> argTypes) {\n\t\tthis.argTypes = argTypes;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"(\" + Utils.listToStr(argTypes) + \")\" + returnType;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/JavaMethodRef.java",
    "content": "package jadx.plugins.input.java.data;\n\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.plugins.input.java.JavaClassReader;\nimport jadx.plugins.input.java.utils.DescriptorParser;\n\npublic class JavaMethodRef extends JavaMethodProto implements IMethodRef {\n\n\tprivate int uniqId;\n\tprivate String parentClassType;\n\tprivate String name;\n\tprivate String descr;\n\n\t@Override\n\tpublic int getUniqId() {\n\t\treturn uniqId;\n\t}\n\n\tpublic void initUniqId(JavaClassReader clsReader, int id, boolean fromConstPool) {\n\t\tint readerId = clsReader.getId();\n\t\tif (readerId > 0xFFFF || id > 0x7FFF) {\n\t\t\t// loaded more than 65535 classes or more than 32767 methods in this class -> disable caching\n\t\t\tthis.uniqId = 0;\n\t\t} else {\n\t\t\tint source = fromConstPool ? 0 : 0x8000;\n\t\t\tthis.uniqId = (readerId & 0xFFFF) << 16 | source | id & 0x7FFF;\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getParentClassType() {\n\t\treturn parentClassType;\n\t}\n\n\tpublic void setParentClassType(String parentClassType) {\n\t\tthis.parentClassType = parentClassType;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getDescriptor() {\n\t\treturn descr;\n\t}\n\n\tpublic void setDescr(String descr) {\n\t\tthis.descr = descr;\n\t}\n\n\tpublic void reset() {\n\t\tthis.setReturnType(null);\n\t\tthis.setArgTypes(null);\n\t}\n\n\t@Override\n\tpublic void load() {\n\t\tif (getReturnType() == null) {\n\t\t\tDescriptorParser.fillMethodProto(descr, this);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn parentClassType + \"->\" + name + descr;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/AttributesReader.java",
    "content": "package jadx.plugins.input.java.data.attributes;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.plugins.input.java.data.ConstPoolReader;\nimport jadx.plugins.input.java.data.DataReader;\nimport jadx.plugins.input.java.data.JavaClassData;\n\npublic class AttributesReader {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(AttributesReader.class);\n\n\tprivate static final Predicate<JavaAttrType<?>> LOAD_ALL = type -> true;\n\n\tprivate final JavaClassData clsData;\n\tprivate final ConstPoolReader constPool;\n\tprivate final Map<Integer, JavaAttrType<?>> attrCache = new HashMap<>(JavaAttrType.size());\n\n\tpublic AttributesReader(JavaClassData clsData, ConstPoolReader constPoolReader) {\n\t\tthis.clsData = clsData;\n\t\tthis.constPool = constPoolReader;\n\t}\n\n\tpublic JavaAttrStorage loadAll(DataReader reader) {\n\t\treturn loadAttributes(reader, LOAD_ALL);\n\t}\n\n\tpublic JavaAttrStorage loadMulti(DataReader reader, Set<JavaAttrType<?>> types) {\n\t\treturn loadAttributes(reader, types::contains);\n\t}\n\n\t/**\n\t * Load attributes into storage\n\t *\n\t * @param reader    - reader pos should be set to attributes section start\n\t * @param condition - check if attribute should be parsed and added to storage\n\t */\n\tprivate JavaAttrStorage loadAttributes(DataReader reader, Predicate<JavaAttrType<?>> condition) {\n\t\tint count = reader.readU2();\n\t\tif (count == 0) {\n\t\t\treturn JavaAttrStorage.EMPTY;\n\t\t}\n\t\tJavaAttrStorage storage = new JavaAttrStorage();\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tint nameIdx = reader.readU2();\n\t\t\tint len = reader.readU4();\n\t\t\tint end = reader.getOffset() + len;\n\t\t\ttry {\n\t\t\t\tJavaAttrType<?> attrType = resolveAttrReader(nameIdx);\n\t\t\t\tif (attrType != null && condition.test(attrType)) {\n\t\t\t\t\tIJavaAttributeReader attrReader = attrType.getReader();\n\t\t\t\t\tif (attrReader != null) {\n\t\t\t\t\t\tIJavaAttribute attrValue = attrReader.read(clsData, reader);\n\t\t\t\t\t\tif (attrValue != null) {\n\t\t\t\t\t\t\tstorage.add(attrType, attrValue);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to parse attribute: {}\", constPool.getUtf8(nameIdx), e);\n\t\t\t} finally {\n\t\t\t\treader.absPos(end);\n\t\t\t}\n\t\t}\n\t\treturn storage;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T extends IJavaAttribute> @Nullable T loadOne(DataReader reader, JavaAttrType<T> type) {\n\t\tint count = reader.readU2();\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tint nameIdx = reader.readU2();\n\t\t\tint len = reader.readU4();\n\t\t\tint end = reader.getOffset() + len;\n\t\t\ttry {\n\t\t\t\tJavaAttrType<?> attrType = resolveAttrReader(nameIdx);\n\t\t\t\tif (attrType == type) {\n\t\t\t\t\treturn (T) attrType.getReader().read(clsData, reader);\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.error(\"Failed to parse attribute: {}\", constPool.getUtf8(nameIdx), e);\n\t\t\t} finally {\n\t\t\t\treader.absPos(end);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate JavaAttrType<?> resolveAttrReader(int nameIdx) {\n\t\treturn attrCache.computeIfAbsent(nameIdx, idx -> {\n\t\t\tString attrName = constPool.getUtf8(idx);\n\t\t\tJavaAttrType<?> attrType = JavaAttrType.byName(attrName);\n\t\t\tif (attrType == null) {\n\t\t\t\tLOG.warn(\"Unknown java class attribute type: {}\", attrName);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn attrType;\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/EncodedValueReader.java",
    "content": "package jadx.plugins.input.java.data.attributes;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.annotations.AnnotationVisibility;\nimport jadx.api.plugins.input.data.annotations.EncodedType;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.impl.JadxFieldRef;\nimport jadx.plugins.input.java.data.ConstPoolReader;\nimport jadx.plugins.input.java.data.DataReader;\nimport jadx.plugins.input.java.data.JavaClassData;\nimport jadx.plugins.input.java.data.attributes.types.JavaAnnotationsAttr;\nimport jadx.plugins.input.java.utils.JavaClassParseException;\n\npublic class EncodedValueReader {\n\n\tpublic static EncodedValue read(JavaClassData clsData, DataReader reader) {\n\t\tConstPoolReader constPool = clsData.getConstPoolReader();\n\t\tchar tag = (char) reader.readU1();\n\t\tswitch (tag) {\n\t\t\tcase 'B':\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_BYTE, (byte) constPool.getInt(reader.readU2()));\n\t\t\tcase 'C':\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_CHAR, (char) constPool.getInt(reader.readU2()));\n\t\t\tcase 'D':\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_DOUBLE, constPool.getDouble(reader.readU2()));\n\t\t\tcase 'F':\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_FLOAT, constPool.getFloat(reader.readU2()));\n\t\t\tcase 'I':\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_INT, constPool.getInt(reader.readU2()));\n\t\t\tcase 'J':\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_LONG, constPool.getLong(reader.readU2()));\n\t\t\tcase 'S':\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_SHORT, (short) constPool.getInt(reader.readU2()));\n\t\t\tcase 'Z':\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_BOOLEAN, 1 == constPool.getInt(reader.readU2()));\n\t\t\tcase 's':\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_STRING, constPool.getUtf8(reader.readU2()));\n\n\t\t\tcase 'e':\n\t\t\t\tString cls = constPool.getUtf8(reader.readU2());\n\t\t\t\tString name = constPool.getUtf8(reader.readU2());\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_ENUM, new JadxFieldRef(cls, name, cls));\n\n\t\t\tcase 'c':\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_TYPE, constPool.getUtf8(reader.readU2()));\n\n\t\t\tcase '@':\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_ANNOTATION,\n\t\t\t\t\t\tJavaAnnotationsAttr.readAnnotation(AnnotationVisibility.RUNTIME, clsData, reader));\n\n\t\t\tcase '[':\n\t\t\t\tint len = reader.readU2();\n\t\t\t\tList<EncodedValue> values = new ArrayList<>(len);\n\t\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\t\tvalues.add(read(clsData, reader));\n\t\t\t\t}\n\t\t\t\treturn new EncodedValue(EncodedType.ENCODED_ARRAY, values);\n\n\t\t\tdefault:\n\t\t\t\tthrow new JavaClassParseException(\"Unknown element value tag: \" + tag);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/IJavaAttribute.java",
    "content": "package jadx.plugins.input.java.data.attributes;\n\npublic interface IJavaAttribute {\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/IJavaAttributeReader.java",
    "content": "package jadx.plugins.input.java.data.attributes;\n\nimport jadx.plugins.input.java.data.DataReader;\nimport jadx.plugins.input.java.data.JavaClassData;\n\npublic interface IJavaAttributeReader {\n\tIJavaAttribute read(JavaClassData clsData, DataReader reader);\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/JavaAttrStorage.java",
    "content": "package jadx.plugins.input.java.data.attributes;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic class JavaAttrStorage {\n\tpublic static final JavaAttrStorage EMPTY = new JavaAttrStorage();\n\n\tprivate final IJavaAttribute[] map = new IJavaAttribute[JavaAttrType.size()];\n\n\tpublic void add(JavaAttrType<?> type, IJavaAttribute value) {\n\t\tmap[type.getId()] = value;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Nullable\n\tpublic <A extends IJavaAttribute> A get(JavaAttrType<A> type) {\n\t\treturn (A) map[type.getId()];\n\t}\n\n\tpublic int size() {\n\t\tint size = 0;\n\t\tfor (IJavaAttribute attr : map) {\n\t\t\tif (attr != null) {\n\t\t\t\tsize++;\n\t\t\t}\n\t\t}\n\t\treturn size;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"AttributesStorage{size=\" + size() + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/JavaAttrType.java",
    "content": "package jadx.plugins.input.java.data.attributes;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.annotations.AnnotationVisibility;\nimport jadx.plugins.input.java.data.attributes.debuginfo.LineNumberTableAttr;\nimport jadx.plugins.input.java.data.attributes.debuginfo.LocalVarTypesAttr;\nimport jadx.plugins.input.java.data.attributes.debuginfo.LocalVarsAttr;\nimport jadx.plugins.input.java.data.attributes.stack.StackMapTableReader;\nimport jadx.plugins.input.java.data.attributes.types.CodeAttr;\nimport jadx.plugins.input.java.data.attributes.types.ConstValueAttr;\nimport jadx.plugins.input.java.data.attributes.types.IgnoredAttr;\nimport jadx.plugins.input.java.data.attributes.types.JavaAnnotationDefaultAttr;\nimport jadx.plugins.input.java.data.attributes.types.JavaAnnotationsAttr;\nimport jadx.plugins.input.java.data.attributes.types.JavaBootstrapMethodsAttr;\nimport jadx.plugins.input.java.data.attributes.types.JavaExceptionsAttr;\nimport jadx.plugins.input.java.data.attributes.types.JavaInnerClsAttr;\nimport jadx.plugins.input.java.data.attributes.types.JavaMethodParametersAttr;\nimport jadx.plugins.input.java.data.attributes.types.JavaParamAnnsAttr;\nimport jadx.plugins.input.java.data.attributes.types.JavaSignatureAttr;\nimport jadx.plugins.input.java.data.attributes.types.JavaSourceFileAttr;\nimport jadx.plugins.input.java.data.attributes.types.StackMapTableAttr;\n\npublic final class JavaAttrType<T extends IJavaAttribute> {\n\n\tprivate static final Map<String, JavaAttrType<?>> NAME_TO_TYPE_MAP;\n\n\tpublic static final JavaAttrType<JavaInnerClsAttr> INNER_CLASSES;\n\tpublic static final JavaAttrType<JavaBootstrapMethodsAttr> BOOTSTRAP_METHODS;\n\n\tpublic static final JavaAttrType<ConstValueAttr> CONST_VALUE;\n\n\tpublic static final JavaAttrType<CodeAttr> CODE;\n\tpublic static final JavaAttrType<StackMapTableAttr> STACK_MAP_TABLE;\n\tpublic static final JavaAttrType<LineNumberTableAttr> LINE_NUMBER_TABLE;\n\tpublic static final JavaAttrType<LocalVarsAttr> LOCAL_VAR_TABLE;\n\tpublic static final JavaAttrType<LocalVarTypesAttr> LOCAL_VAR_TYPE_TABLE;\n\n\tpublic static final JavaAttrType<JavaAnnotationsAttr> RUNTIME_ANNOTATIONS;\n\tpublic static final JavaAttrType<JavaAnnotationsAttr> BUILD_ANNOTATIONS;\n\tpublic static final JavaAttrType<JavaParamAnnsAttr> RUNTIME_PARAMETER_ANNOTATIONS;\n\tpublic static final JavaAttrType<JavaParamAnnsAttr> BUILD_PARAMETER_ANNOTATIONS;\n\tpublic static final JavaAttrType<IgnoredAttr> RUNTIME_TYPE_ANNOTATIONS;\n\tpublic static final JavaAttrType<IgnoredAttr> BUILD_TYPE_ANNOTATIONS;\n\tpublic static final JavaAttrType<JavaAnnotationDefaultAttr> ANNOTATION_DEFAULT;\n\n\tpublic static final JavaAttrType<JavaSourceFileAttr> SOURCE_FILE;\n\tpublic static final JavaAttrType<JavaSignatureAttr> SIGNATURE;\n\tpublic static final JavaAttrType<JavaExceptionsAttr> EXCEPTIONS;\n\tpublic static final JavaAttrType<JavaMethodParametersAttr> METHOD_PARAMETERS;\n\n\tpublic static final JavaAttrType<IgnoredAttr> DEPRECATED;\n\tpublic static final JavaAttrType<IgnoredAttr> SYNTHETIC;\n\tpublic static final JavaAttrType<IgnoredAttr> ENCLOSING_METHOD;\n\n\tpublic static final JavaAttrType<IgnoredAttr> MODULE;\n\tpublic static final JavaAttrType<IgnoredAttr> SOURCE_DEBUG_EXTENSION;\n\tpublic static final JavaAttrType<IgnoredAttr> NEST_HOST;\n\tpublic static final JavaAttrType<IgnoredAttr> NEST_MEMBERS;\n\n\tstatic {\n\t\tNAME_TO_TYPE_MAP = new HashMap<>();\n\n\t\tCONST_VALUE = bind(\"ConstantValue\", ConstValueAttr.reader());\n\n\t\tCODE = bind(\"Code\", CodeAttr.reader());\n\n\t\tLINE_NUMBER_TABLE = bind(\"LineNumberTable\", LineNumberTableAttr.reader());\n\t\tLOCAL_VAR_TABLE = bind(\"LocalVariableTable\", LocalVarsAttr.reader());\n\t\tLOCAL_VAR_TYPE_TABLE = bind(\"LocalVariableTypeTable\", LocalVarTypesAttr.reader());\n\n\t\tINNER_CLASSES = bind(\"InnerClasses\", JavaInnerClsAttr.reader());\n\t\tBOOTSTRAP_METHODS = bind(\"BootstrapMethods\", JavaBootstrapMethodsAttr.reader());\n\n\t\tRUNTIME_ANNOTATIONS = bind(\"RuntimeVisibleAnnotations\", JavaAnnotationsAttr.reader(AnnotationVisibility.RUNTIME));\n\t\tBUILD_ANNOTATIONS = bind(\"RuntimeInvisibleAnnotations\", JavaAnnotationsAttr.reader(AnnotationVisibility.BUILD));\n\t\tRUNTIME_PARAMETER_ANNOTATIONS = bind(\"RuntimeVisibleParameterAnnotations\", JavaParamAnnsAttr.reader(AnnotationVisibility.RUNTIME));\n\t\tBUILD_PARAMETER_ANNOTATIONS = bind(\"RuntimeInvisibleParameterAnnotations\", JavaParamAnnsAttr.reader(AnnotationVisibility.BUILD));\n\t\tANNOTATION_DEFAULT = bind(\"AnnotationDefault\", JavaAnnotationDefaultAttr.reader());\n\n\t\tSOURCE_FILE = bind(\"SourceFile\", JavaSourceFileAttr.reader());\n\t\tSIGNATURE = bind(\"Signature\", JavaSignatureAttr.reader());\n\t\tEXCEPTIONS = bind(\"Exceptions\", JavaExceptionsAttr.reader());\n\t\tMETHOD_PARAMETERS = bind(\"MethodParameters\", JavaMethodParametersAttr.reader());\n\t\tSTACK_MAP_TABLE = bind(\"StackMapTable\", new StackMapTableReader());\n\n\t\t// ignored\n\t\tDEPRECATED = bind(\"Deprecated\", null); // duplicated by annotation\n\t\tSYNTHETIC = bind(\"Synthetic\", null); // duplicated by access flag\n\t\tENCLOSING_METHOD = bind(\"EnclosingMethod\", null);\n\n\t\t// TODO: not supported yet\n\t\tRUNTIME_TYPE_ANNOTATIONS = bind(\"RuntimeVisibleTypeAnnotations\", null);\n\t\tBUILD_TYPE_ANNOTATIONS = bind(\"RuntimeInvisibleTypeAnnotations\", null);\n\t\tMODULE = bind(\"Module\", null);\n\t\tNEST_HOST = bind(\"NestHost\", null);\n\t\tNEST_MEMBERS = bind(\"NestMembers\", null);\n\t\tSOURCE_DEBUG_EXTENSION = bind(\"SourceDebugExtension\", null);\n\t}\n\n\tprivate static <A extends IJavaAttribute> JavaAttrType<A> bind(String name, IJavaAttributeReader reader) {\n\t\tJavaAttrType<A> attrType = new JavaAttrType<>(NAME_TO_TYPE_MAP.size(), name, reader);\n\t\tNAME_TO_TYPE_MAP.put(name, attrType);\n\t\treturn attrType;\n\t}\n\n\t@Nullable\n\tpublic static JavaAttrType<?> byName(String name) {\n\t\treturn NAME_TO_TYPE_MAP.get(name);\n\t}\n\n\tpublic static int size() {\n\t\treturn NAME_TO_TYPE_MAP.size();\n\t}\n\n\tprivate final int id;\n\tprivate final String name;\n\tprivate final IJavaAttributeReader reader;\n\n\tprivate JavaAttrType(int id, String name, IJavaAttributeReader reader) {\n\t\tthis.id = id;\n\t\tthis.name = name;\n\t\tthis.reader = reader;\n\t}\n\n\tpublic int getId() {\n\t\treturn id;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic IJavaAttributeReader getReader() {\n\t\treturn reader;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn id;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\treturn id == ((JavaAttrType<?>) o).id;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn name;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/debuginfo/JavaLocalVar.java",
    "content": "package jadx.plugins.input.java.data.attributes.debuginfo;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.ILocalVar;\n\npublic class JavaLocalVar implements ILocalVar {\n\tprivate int regNum;\n\tprivate final String name;\n\tprivate final String type;\n\t@Nullable\n\tprivate String sign;\n\n\tprivate final int startOffset;\n\tprivate final int endOffset;\n\n\tpublic JavaLocalVar(int regNum, String name, @Nullable String type, @Nullable String sign, int startOffset, int endOffset) {\n\t\tthis.regNum = regNum;\n\t\tthis.name = name;\n\t\tthis.type = type;\n\t\tthis.sign = sign;\n\t\tthis.startOffset = startOffset;\n\t\tthis.endOffset = endOffset;\n\t}\n\n\tpublic void shiftRegNum(int maxStack) {\n\t\tthis.regNum += maxStack; // convert local var to register\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic int getRegNum() {\n\t\treturn regNum;\n\t}\n\n\t@Override\n\tpublic String getType() {\n\t\treturn type;\n\t}\n\n\t@Override\n\tpublic @Nullable String getSignature() {\n\t\treturn sign;\n\t}\n\n\tpublic void setSignature(String sign) {\n\t\tthis.sign = sign;\n\t}\n\n\t@Override\n\tpublic int getStartOffset() {\n\t\treturn startOffset;\n\t}\n\n\t@Override\n\tpublic int getEndOffset() {\n\t\treturn endOffset;\n\t}\n\n\t@Override\n\tpublic boolean isMarkedAsParameter() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = regNum;\n\t\tresult = 31 * result + name.hashCode();\n\t\tresult = 31 * result + startOffset;\n\t\tresult = 31 * result + endOffset;\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof JavaLocalVar)) {\n\t\t\treturn false;\n\t\t}\n\t\tJavaLocalVar other = (JavaLocalVar) o;\n\t\treturn regNum == other.regNum\n\t\t\t\t&& startOffset == other.startOffset\n\t\t\t\t&& endOffset == other.endOffset\n\t\t\t\t&& name.equals(other.name);\n\t}\n\n\tprivate static String formatOffset(int offset) {\n\t\treturn String.format(\"0x%04x\", offset);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn formatOffset(startOffset) + '-' + formatOffset(endOffset)\n\t\t\t\t+ \": r\" + regNum + \" '\" + name + \"' \" + type\n\t\t\t\t+ (sign != null ? \", signature: \" + sign : \"\");\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/debuginfo/LineNumberTableAttr.java",
    "content": "package jadx.plugins.input.java.data.attributes.debuginfo;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.IJavaAttributeReader;\n\npublic class LineNumberTableAttr implements IJavaAttribute {\n\tprivate final Map<Integer, Integer> lineMap;\n\n\tpublic LineNumberTableAttr(Map<Integer, Integer> sourceLineMap) {\n\t\tthis.lineMap = sourceLineMap;\n\t}\n\n\tpublic Map<Integer, Integer> getLineMap() {\n\t\treturn lineMap;\n\t}\n\n\tpublic static IJavaAttributeReader reader() {\n\t\treturn (clsData, reader) -> {\n\t\t\tint len = reader.readU2();\n\t\t\tMap<Integer, Integer> map = new HashMap<>(len);\n\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\tint offset = reader.readU2();\n\t\t\t\tint line = reader.readU2();\n\t\t\t\tmap.put(offset, line);\n\t\t\t}\n\t\t\treturn new LineNumberTableAttr(map);\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/debuginfo/LocalVarTypesAttr.java",
    "content": "package jadx.plugins.input.java.data.attributes.debuginfo;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.plugins.input.java.data.ConstPoolReader;\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.IJavaAttributeReader;\n\npublic class LocalVarTypesAttr implements IJavaAttribute {\n\tprivate final List<JavaLocalVar> vars;\n\n\tpublic LocalVarTypesAttr(List<JavaLocalVar> vars) {\n\t\tthis.vars = vars;\n\t}\n\n\tpublic List<JavaLocalVar> getVars() {\n\t\treturn vars;\n\t}\n\n\tpublic static IJavaAttributeReader reader() {\n\t\treturn (clsData, reader) -> {\n\t\t\tConstPoolReader constPool = clsData.getConstPoolReader();\n\t\t\tint len = reader.readU2();\n\t\t\tList<JavaLocalVar> varsList = new ArrayList<>(len);\n\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\tint startOffset = reader.readU2();\n\t\t\t\tint endOffset = startOffset + reader.readU2() - 1;\n\t\t\t\tint nameIdx = reader.readU2();\n\t\t\t\tint typeIdx = reader.readU2();\n\t\t\t\tint varNum = reader.readU2();\n\t\t\t\tvarsList.add(new JavaLocalVar(\n\t\t\t\t\t\tvarNum,\n\t\t\t\t\t\tconstPool.getUtf8(nameIdx),\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\tconstPool.getUtf8(typeIdx),\n\t\t\t\t\t\tstartOffset, endOffset));\n\t\t\t}\n\t\t\treturn new LocalVarTypesAttr(varsList);\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/debuginfo/LocalVarsAttr.java",
    "content": "package jadx.plugins.input.java.data.attributes.debuginfo;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.plugins.input.java.data.ConstPoolReader;\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.IJavaAttributeReader;\n\npublic class LocalVarsAttr implements IJavaAttribute {\n\tprivate final List<JavaLocalVar> vars;\n\n\tpublic LocalVarsAttr(List<JavaLocalVar> vars) {\n\t\tthis.vars = vars;\n\t}\n\n\tpublic List<JavaLocalVar> getVars() {\n\t\treturn vars;\n\t}\n\n\tpublic static IJavaAttributeReader reader() {\n\t\treturn (clsData, reader) -> {\n\t\t\tConstPoolReader constPool = clsData.getConstPoolReader();\n\t\t\tint count = reader.readU2();\n\t\t\tList<JavaLocalVar> varsList = new ArrayList<>(count);\n\t\t\tfor (int i = 0; i < count; i++) {\n\t\t\t\tint startOffset = reader.readU2();\n\t\t\t\tint length = reader.readU2();\n\t\t\t\tint endOffset = startOffset + length - 1;\n\t\t\t\tint nameIdx = reader.readU2();\n\t\t\t\tint typeIdx = reader.readU2();\n\t\t\t\tint varNum = reader.readU2();\n\t\t\t\tvarsList.add(new JavaLocalVar(varNum,\n\t\t\t\t\t\tconstPool.getUtf8(nameIdx),\n\t\t\t\t\t\tconstPool.getUtf8(typeIdx),\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\tstartOffset, endOffset));\n\t\t\t}\n\t\t\treturn new LocalVarsAttr(varsList);\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/stack/StackFrame.java",
    "content": "package jadx.plugins.input.java.data.attributes.stack;\n\npublic class StackFrame {\n\tprivate final int offset;\n\tprivate final StackFrameType type;\n\n\tprivate int stackSize;\n\tprivate StackValueType[] stackValueTypes;\n\tprivate int localsCount;\n\n\tpublic StackFrame(int offset, StackFrameType type) {\n\t\tthis.offset = offset;\n\t\tthis.type = type;\n\t}\n\n\tpublic int getOffset() {\n\t\treturn offset;\n\t}\n\n\tpublic StackFrameType getType() {\n\t\treturn type;\n\t}\n\n\tpublic int getLocalsCount() {\n\t\treturn localsCount;\n\t}\n\n\tpublic void setLocalsCount(int localsCount) {\n\t\tthis.localsCount = localsCount;\n\t}\n\n\tpublic int getStackSize() {\n\t\treturn stackSize;\n\t}\n\n\tpublic void setStackSize(int stackSize) {\n\t\tthis.stackSize = stackSize;\n\t}\n\n\tpublic StackValueType[] getStackValueTypes() {\n\t\treturn stackValueTypes;\n\t}\n\n\tpublic void setStackValueTypes(StackValueType[] stackValueTypes) {\n\t\tthis.stackValueTypes = stackValueTypes;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/stack/StackFrameType.java",
    "content": "package jadx.plugins.input.java.data.attributes.stack;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic enum StackFrameType {\n\tSAME_FRAME(0, 63),\n\tSAME_LOCALS_1_STACK(64, 127),\n\tSAME_LOCALS_1_STACK_EXTENDED(247, 247),\n\tCHOP(248, 250),\n\tSAME_FRAME_EXTENDED(251, 251),\n\tAPPEND(252, 254),\n\tFULL(255, 255);\n\n\tprivate final int start;\n\tprivate final int end;\n\n\tStackFrameType(int start, int end) {\n\t\tthis.start = start;\n\t\tthis.end = end;\n\t}\n\n\tprivate static final StackFrameType[] MAPPING = buildMapping();\n\n\tprivate static StackFrameType[] buildMapping() {\n\t\tStackFrameType[] mapping = new StackFrameType[256];\n\t\tfor (StackFrameType value : StackFrameType.values()) {\n\t\t\tfor (int i = value.start; i <= value.end; i++) {\n\t\t\t\tmapping[i] = value;\n\t\t\t}\n\t\t}\n\t\treturn mapping;\n\t}\n\n\tpublic static @Nullable StackFrameType getType(int data) {\n\t\treturn MAPPING[data];\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/stack/StackMapTableReader.java",
    "content": "package jadx.plugins.input.java.data.attributes.stack;\n\nimport java.util.EnumMap;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\nimport jadx.plugins.input.java.data.DataReader;\nimport jadx.plugins.input.java.data.JavaClassData;\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.IJavaAttributeReader;\nimport jadx.plugins.input.java.data.attributes.types.StackMapTableAttr;\nimport jadx.plugins.input.java.utils.JavaClassParseException;\n\npublic class StackMapTableReader implements IJavaAttributeReader {\n\n\t@Override\n\tpublic IJavaAttribute read(JavaClassData clsData, DataReader reader) {\n\t\tint count = reader.readU2();\n\t\tMap<Integer, StackFrame> map = new HashMap<>(count, 1);\n\t\tStackFrame prevFrame = null;\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tStackFrame frame = readFrame(reader, prevFrame);\n\t\t\tmap.put(frame.getOffset(), frame);\n\t\t\tprevFrame = frame;\n\t\t}\n\t\treturn new StackMapTableAttr(map);\n\t}\n\n\tprivate static final Map<StackFrameType, Consumer<FrameContext>> FRAME_READERS = registerReaders();\n\n\tprivate static Map<StackFrameType, Consumer<FrameContext>> registerReaders() {\n\t\tEnumMap<StackFrameType, Consumer<FrameContext>> map = new EnumMap<>(StackFrameType.class);\n\t\tmap.put(StackFrameType.SAME_FRAME, context -> readSame(context, false));\n\t\tmap.put(StackFrameType.SAME_FRAME_EXTENDED, context -> readSame(context, true));\n\t\tmap.put(StackFrameType.SAME_LOCALS_1_STACK, context -> readSL1S(context, false));\n\t\tmap.put(StackFrameType.SAME_LOCALS_1_STACK_EXTENDED, context -> readSL1S(context, true));\n\t\tmap.put(StackFrameType.CHOP, StackMapTableReader::readChop);\n\t\tmap.put(StackFrameType.APPEND, StackMapTableReader::readAppend);\n\t\tmap.put(StackFrameType.FULL, StackMapTableReader::readFull);\n\t\treturn map;\n\t}\n\n\tprivate StackFrame readFrame(DataReader reader, StackFrame prevFrame) {\n\t\tint typeData = reader.readU1();\n\t\tStackFrameType frameType = StackFrameType.getType(typeData);\n\t\tConsumer<FrameContext> frameReader = FRAME_READERS.get(frameType);\n\t\tif (frameReader == null) {\n\t\t\tthrow new JavaClassParseException(\"Found unsupported stack frame type: \" + frameType);\n\t\t}\n\t\tFrameContext frameContext = new FrameContext(reader, typeData, prevFrame);\n\t\tframeReader.accept(frameContext);\n\t\treturn Objects.requireNonNull(frameContext.getFrame());\n\t}\n\n\tprivate static void readSame(FrameContext context, boolean extended) {\n\t\tint offsetDelta;\n\t\tStackFrameType type;\n\t\tif (extended) {\n\t\t\ttype = StackFrameType.SAME_FRAME_EXTENDED;\n\t\t\toffsetDelta = context.getDataReader().readU2();\n\t\t} else {\n\t\t\ttype = StackFrameType.SAME_FRAME;\n\t\t\toffsetDelta = context.getTypeData();\n\t\t}\n\t\tStackFrame frame = new StackFrame(calcOffset(context, offsetDelta), type);\n\t\tframe.setStackSize(0);\n\t\tframe.setLocalsCount(getPrevLocalsCount(context));\n\t\tcontext.setFrame(frame);\n\t}\n\n\tprivate static void readSL1S(FrameContext context, boolean extended) {\n\t\tDataReader reader = context.getDataReader();\n\t\tint offsetDelta;\n\t\tStackFrameType type;\n\t\tif (extended) {\n\t\t\ttype = StackFrameType.SAME_LOCALS_1_STACK_EXTENDED;\n\t\t\toffsetDelta = reader.readU2();\n\t\t} else {\n\t\t\ttype = StackFrameType.SAME_LOCALS_1_STACK;\n\t\t\toffsetDelta = context.getTypeData() - 64;\n\t\t}\n\t\tStackValueType[] stackTypes = TypeInfoReader.readTypeInfoList(reader, 1);\n\t\tStackFrame frame = new StackFrame(calcOffset(context, offsetDelta), type);\n\t\tframe.setStackSize(1);\n\t\tframe.setStackValueTypes(stackTypes);\n\t\tframe.setLocalsCount(getPrevLocalsCount(context));\n\t\tcontext.setFrame(frame);\n\t}\n\n\tprivate static void readChop(FrameContext context) {\n\t\tint k = 251 - context.getTypeData();\n\t\tint offsetDelta = context.getDataReader().readU2();\n\t\tStackFrame frame = new StackFrame(calcOffset(context, offsetDelta), StackFrameType.CHOP);\n\t\tframe.setStackSize(0);\n\t\tframe.setLocalsCount(getPrevLocalsCount(context) - k);\n\t\tcontext.setFrame(frame);\n\t}\n\n\tprivate static void readAppend(FrameContext context) {\n\t\tDataReader reader = context.getDataReader();\n\t\tint k = context.getTypeData() - 251;\n\t\tint offsetDelta = reader.readU2();\n\t\tTypeInfoReader.skipTypeInfoList(reader, k);\n\t\tStackFrame frame = new StackFrame(calcOffset(context, offsetDelta), StackFrameType.APPEND);\n\t\tframe.setStackSize(0);\n\t\tframe.setLocalsCount(getPrevLocalsCount(context) - k);\n\t\tcontext.setFrame(frame);\n\t}\n\n\tprivate static void readFull(FrameContext context) {\n\t\tDataReader reader = context.getDataReader();\n\t\tint offsetDelta = reader.readU2();\n\t\tint localsCount = reader.readU2();\n\t\tTypeInfoReader.skipTypeInfoList(reader, localsCount);\n\t\tint stackSize = reader.readU2();\n\t\tStackValueType[] stackTypes = TypeInfoReader.readTypeInfoList(reader, stackSize);\n\n\t\tStackFrame frame = new StackFrame(calcOffset(context, offsetDelta), StackFrameType.FULL);\n\t\tframe.setLocalsCount(localsCount);\n\t\tframe.setStackSize(stackSize);\n\t\tframe.setStackValueTypes(stackTypes);\n\t\tcontext.setFrame(frame);\n\t}\n\n\tprivate static int calcOffset(FrameContext context, int offsetDelta) {\n\t\tStackFrame prevFrame = context.getPrevFrame();\n\t\tif (prevFrame == null) {\n\t\t\treturn offsetDelta;\n\t\t}\n\t\treturn prevFrame.getOffset() + offsetDelta + 1;\n\t}\n\n\tprivate static int getPrevLocalsCount(FrameContext context) {\n\t\tStackFrame prevFrame = context.getPrevFrame();\n\t\tif (prevFrame == null) {\n\t\t\treturn 0;\n\t\t}\n\t\treturn prevFrame.getLocalsCount();\n\t}\n\n\tprivate static final class FrameContext {\n\t\tprivate final DataReader dataReader;\n\t\tprivate final int typeData;\n\t\tprivate final StackFrame prevFrame;\n\n\t\tprivate StackFrame frame;\n\n\t\tprivate FrameContext(DataReader dataReader, int typeData, StackFrame prevFrame) {\n\t\t\tthis.dataReader = dataReader;\n\t\t\tthis.typeData = typeData;\n\t\t\tthis.prevFrame = prevFrame;\n\t\t}\n\n\t\tpublic DataReader getDataReader() {\n\t\t\treturn dataReader;\n\t\t}\n\n\t\tpublic int getTypeData() {\n\t\t\treturn typeData;\n\t\t}\n\n\t\tpublic StackFrame getPrevFrame() {\n\t\t\treturn prevFrame;\n\t\t}\n\n\t\tpublic StackFrame getFrame() {\n\t\t\treturn frame;\n\t\t}\n\n\t\tpublic void setFrame(StackFrame frame) {\n\t\t\tthis.frame = frame;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/stack/StackValueType.java",
    "content": "package jadx.plugins.input.java.data.attributes.stack;\n\npublic enum StackValueType {\n\tNARROW, // int, float, etc\n\tWIDE, // long, double\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/stack/TypeInfoReader.java",
    "content": "package jadx.plugins.input.java.data.attributes.stack;\n\nimport jadx.plugins.input.java.data.DataReader;\n\npublic class TypeInfoReader {\n\tprivate static final int ITEM_TOP = 0;\n\tprivate static final int ITEM_INT = 1;\n\tprivate static final int ITEM_FLOAT = 2;\n\n\tprivate static final int ITEM_DOUBLE = 3;\n\tprivate static final int ITEM_LONG = 4;\n\n\tprivate static final int ITEM_NULL = 5;\n\tprivate static final int ITEM_UNINITIALIZED_THIS = 6;\n\n\tprivate static final int ITEM_OBJECT = 7;\n\tprivate static final int ITEM_UNINITIALIZED = 8;\n\n\tstatic StackValueType[] readTypeInfoList(DataReader reader, int count) {\n\t\tStackValueType[] types = new StackValueType[count];\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tint tag = reader.readU1();\n\t\t\tStackValueType type;\n\t\t\tswitch (tag) {\n\t\t\t\tcase ITEM_DOUBLE:\n\t\t\t\tcase ITEM_LONG:\n\t\t\t\t\ttype = StackValueType.WIDE;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase ITEM_OBJECT:\n\t\t\t\tcase ITEM_UNINITIALIZED:\n\t\t\t\t\treader.readU2(); // ignore\n\t\t\t\t\ttype = StackValueType.NARROW;\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\ttype = StackValueType.NARROW;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttypes[i] = type;\n\t\t}\n\t\treturn types;\n\t}\n\n\tstatic void skipTypeInfoList(DataReader reader, int count) {\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tint tag = reader.readU1();\n\t\t\tif (tag == ITEM_OBJECT || tag == ITEM_UNINITIALIZED) {\n\t\t\t\treader.readU2();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/types/CodeAttr.java",
    "content": "package jadx.plugins.input.java.data.attributes.types;\n\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.IJavaAttributeReader;\n\npublic class CodeAttr implements IJavaAttribute {\n\tprivate final int offset;\n\n\tpublic CodeAttr(int offset) {\n\t\tthis.offset = offset;\n\t}\n\n\tpublic int getOffset() {\n\t\treturn offset;\n\t}\n\n\tpublic static IJavaAttributeReader reader() {\n\t\treturn (clsData, reader) -> new CodeAttr(reader.getOffset());\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/types/ConstValueAttr.java",
    "content": "package jadx.plugins.input.java.data.attributes.types;\n\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.IJavaAttributeReader;\n\npublic class ConstValueAttr implements IJavaAttribute {\n\n\tprivate final EncodedValue value;\n\n\tpublic ConstValueAttr(EncodedValue value) {\n\t\tthis.value = value;\n\t}\n\n\tpublic EncodedValue getValue() {\n\t\treturn value;\n\t}\n\n\tpublic static IJavaAttributeReader reader() {\n\t\treturn (clsData, reader) -> new ConstValueAttr(clsData.getConstPoolReader().readAsEncodedValue(reader.readU2()));\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/types/IgnoredAttr.java",
    "content": "package jadx.plugins.input.java.data.attributes.types;\n\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\n\n@SuppressWarnings(\"unused\")\npublic class IgnoredAttr implements IJavaAttribute {\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/types/JavaAnnotationDefaultAttr.java",
    "content": "package jadx.plugins.input.java.data.attributes.types;\n\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationDefaultAttr;\nimport jadx.plugins.input.java.data.attributes.EncodedValueReader;\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.IJavaAttributeReader;\nimport jadx.plugins.input.java.data.attributes.JavaAttrStorage;\nimport jadx.plugins.input.java.data.attributes.JavaAttrType;\n\npublic class JavaAnnotationDefaultAttr extends AnnotationDefaultAttr implements IJavaAttribute {\n\n\tpublic JavaAnnotationDefaultAttr(EncodedValue value) {\n\t\tsuper(value);\n\t}\n\n\tpublic static IJavaAttributeReader reader() {\n\t\treturn (clsData, reader) -> new JavaAnnotationDefaultAttr(EncodedValueReader.read(clsData, reader));\n\t}\n\n\tpublic static AnnotationDefaultAttr convert(JavaAttrStorage attributes) {\n\t\treturn attributes.get(JavaAttrType.ANNOTATION_DEFAULT);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/types/JavaAnnotationsAttr.java",
    "content": "package jadx.plugins.input.java.data.attributes.types;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport jadx.api.plugins.input.data.annotations.AnnotationVisibility;\nimport jadx.api.plugins.input.data.annotations.EncodedValue;\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.annotations.JadxAnnotation;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationsAttr;\nimport jadx.api.plugins.utils.Utils;\nimport jadx.plugins.input.java.data.ConstPoolReader;\nimport jadx.plugins.input.java.data.DataReader;\nimport jadx.plugins.input.java.data.JavaClassData;\nimport jadx.plugins.input.java.data.attributes.EncodedValueReader;\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.IJavaAttributeReader;\nimport jadx.plugins.input.java.data.attributes.JavaAttrStorage;\nimport jadx.plugins.input.java.data.attributes.JavaAttrType;\n\npublic class JavaAnnotationsAttr implements IJavaAttribute {\n\tprivate final List<IAnnotation> list;\n\n\tpublic JavaAnnotationsAttr(List<IAnnotation> list) {\n\t\tthis.list = list;\n\t}\n\n\tpublic List<IAnnotation> getList() {\n\t\treturn list;\n\t}\n\n\tpublic static IJavaAttributeReader reader(AnnotationVisibility visibility) {\n\t\treturn (clsData, reader) -> new JavaAnnotationsAttr(readAnnotationsList(visibility, clsData, reader));\n\t}\n\n\tpublic static List<IAnnotation> readAnnotationsList(AnnotationVisibility visibility, JavaClassData clsData, DataReader reader) {\n\t\tint len = reader.readU2();\n\t\tList<IAnnotation> list = new ArrayList<>(len);\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tlist.add(readAnnotation(visibility, clsData, reader));\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic static JadxAnnotation readAnnotation(AnnotationVisibility visibility, JavaClassData clsData, DataReader reader) {\n\t\tConstPoolReader constPool = clsData.getConstPoolReader();\n\t\tString type = constPool.getUtf8(reader.readU2());\n\t\tint pairsCount = reader.readU2();\n\t\tMap<String, EncodedValue> pairs = new LinkedHashMap<>(pairsCount);\n\t\tfor (int j = 0; j < pairsCount; j++) {\n\t\t\tString name = constPool.getUtf8(reader.readU2());\n\t\t\tEncodedValue value = EncodedValueReader.read(clsData, reader);\n\t\t\tpairs.put(name, value);\n\t\t}\n\t\treturn new JadxAnnotation(visibility, type, pairs);\n\t}\n\n\tpublic static AnnotationsAttr merge(JavaAttrStorage storage) {\n\t\tJavaAnnotationsAttr runtimeAnnAttr = storage.get(JavaAttrType.RUNTIME_ANNOTATIONS);\n\t\tJavaAnnotationsAttr buildAnnAttr = storage.get(JavaAttrType.BUILD_ANNOTATIONS);\n\t\tif (runtimeAnnAttr == null && buildAnnAttr == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (buildAnnAttr == null) {\n\t\t\treturn AnnotationsAttr.pack(runtimeAnnAttr.getList());\n\t\t}\n\t\tif (runtimeAnnAttr == null) {\n\t\t\treturn AnnotationsAttr.pack(buildAnnAttr.getList());\n\t\t}\n\t\treturn AnnotationsAttr.pack(Utils.concat(runtimeAnnAttr.getList(), buildAnnAttr.getList()));\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/types/JavaBootstrapMethodsAttr.java",
    "content": "package jadx.plugins.input.java.data.attributes.types;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.IJavaAttributeReader;\nimport jadx.plugins.input.java.data.attributes.types.data.RawBootstrapMethod;\n\npublic class JavaBootstrapMethodsAttr implements IJavaAttribute {\n\n\tprivate final List<RawBootstrapMethod> list;\n\n\tpublic JavaBootstrapMethodsAttr(List<RawBootstrapMethod> list) {\n\t\tthis.list = list;\n\t}\n\n\tpublic List<RawBootstrapMethod> getList() {\n\t\treturn list;\n\t}\n\n\tpublic static IJavaAttributeReader reader() {\n\t\treturn (clsData, reader) -> {\n\t\t\tint len = reader.readU2();\n\t\t\tList<RawBootstrapMethod> list = new ArrayList<>(len);\n\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\tint methodHandleIdx = reader.readU2();\n\t\t\t\tint argsCount = reader.readU2();\n\t\t\t\tint[] args = new int[argsCount];\n\t\t\t\tfor (int j = 0; j < argsCount; j++) {\n\t\t\t\t\targs[j] = reader.readU2();\n\t\t\t\t}\n\t\t\t\tlist.add(new RawBootstrapMethod(methodHandleIdx, args));\n\t\t\t}\n\t\t\treturn new JavaBootstrapMethodsAttr(list);\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/types/JavaExceptionsAttr.java",
    "content": "package jadx.plugins.input.java.data.attributes.types;\n\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.attributes.types.ExceptionsAttr;\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.IJavaAttributeReader;\n\npublic class JavaExceptionsAttr extends ExceptionsAttr implements IJavaAttribute {\n\tpublic JavaExceptionsAttr(List<String> list) {\n\t\tsuper(list);\n\t}\n\n\tpublic static IJavaAttributeReader reader() {\n\t\treturn (clsData, reader) -> new JavaExceptionsAttr(reader.readClassesList(clsData.getConstPoolReader()));\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/types/JavaInnerClsAttr.java",
    "content": "package jadx.plugins.input.java.data.attributes.types;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jadx.api.plugins.input.data.attributes.types.InnerClassesAttr;\nimport jadx.api.plugins.input.data.attributes.types.InnerClsInfo;\nimport jadx.plugins.input.java.data.ConstPoolReader;\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.IJavaAttributeReader;\n\npublic class JavaInnerClsAttr extends InnerClassesAttr implements IJavaAttribute {\n\n\tpublic JavaInnerClsAttr(Map<String, InnerClsInfo> map) {\n\t\tsuper(map);\n\t}\n\n\tpublic static IJavaAttributeReader reader() {\n\t\treturn (clsData, reader) -> {\n\t\t\tint len = reader.readU2();\n\t\t\tConstPoolReader constPool = clsData.getConstPoolReader();\n\t\t\tMap<String, InnerClsInfo> clsMap = new HashMap<>(len);\n\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\tString innerCls = constPool.getClass(reader.readU2());\n\t\t\t\tint outerClsIdx = reader.readU2();\n\t\t\t\tString outerCls = outerClsIdx == 0 ? null : constPool.getClass(outerClsIdx);\n\t\t\t\tString name = constPool.getUtf8(reader.readU2());\n\t\t\t\tint accFlags = reader.readU2();\n\t\t\t\tclsMap.put(innerCls, new InnerClsInfo(innerCls, outerCls, name, accFlags));\n\t\t\t}\n\t\t\treturn new JavaInnerClsAttr(clsMap);\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/types/JavaMethodParametersAttr.java",
    "content": "package jadx.plugins.input.java.data.attributes.types;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.attributes.types.MethodParametersAttr;\nimport jadx.plugins.input.java.data.ConstPoolReader;\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.IJavaAttributeReader;\n\npublic class JavaMethodParametersAttr extends MethodParametersAttr implements IJavaAttribute {\n\tpublic JavaMethodParametersAttr(List<Info> list) {\n\t\tsuper(list);\n\t}\n\n\tpublic static IJavaAttributeReader reader() {\n\t\treturn (clsData, reader) -> {\n\t\t\tConstPoolReader constPool = clsData.getConstPoolReader();\n\t\t\tint count = reader.readU1();\n\t\t\tList<Info> params = new ArrayList<>(count);\n\t\t\tfor (int i = 0; i < count; i++) {\n\t\t\t\tString name = constPool.getUtf8(reader.readU2());\n\t\t\t\tint accessFlags = reader.readU2();\n\t\t\t\tparams.add(new Info(accessFlags, name));\n\t\t\t}\n\t\t\treturn new JavaMethodParametersAttr(params);\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/types/JavaParamAnnsAttr.java",
    "content": "package jadx.plugins.input.java.data.attributes.types;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.api.plugins.input.data.annotations.AnnotationVisibility;\nimport jadx.api.plugins.input.data.annotations.IAnnotation;\nimport jadx.api.plugins.input.data.attributes.types.AnnotationMethodParamsAttr;\nimport jadx.api.plugins.utils.Utils;\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.IJavaAttributeReader;\nimport jadx.plugins.input.java.data.attributes.JavaAttrStorage;\nimport jadx.plugins.input.java.data.attributes.JavaAttrType;\n\npublic class JavaParamAnnsAttr implements IJavaAttribute {\n\tprivate final List<List<IAnnotation>> list;\n\n\tpublic JavaParamAnnsAttr(List<List<IAnnotation>> list) {\n\t\tthis.list = list;\n\t}\n\n\tpublic List<List<IAnnotation>> getList() {\n\t\treturn list;\n\t}\n\n\tpublic static IJavaAttributeReader reader(AnnotationVisibility visibility) {\n\t\treturn (clsData, reader) -> {\n\t\t\tint len = reader.readU1();\n\t\t\tList<List<IAnnotation>> list = new ArrayList<>(len);\n\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\tlist.add(JavaAnnotationsAttr.readAnnotationsList(visibility, clsData, reader));\n\t\t\t}\n\t\t\treturn new JavaParamAnnsAttr(list);\n\t\t};\n\t}\n\n\tpublic static AnnotationMethodParamsAttr merge(JavaAttrStorage storage) {\n\t\tJavaParamAnnsAttr runtimeAnnAttr = storage.get(JavaAttrType.RUNTIME_PARAMETER_ANNOTATIONS);\n\t\tJavaParamAnnsAttr buildAnnAttr = storage.get(JavaAttrType.BUILD_PARAMETER_ANNOTATIONS);\n\t\tif (runtimeAnnAttr == null && buildAnnAttr == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (buildAnnAttr == null) {\n\t\t\treturn AnnotationMethodParamsAttr.pack(runtimeAnnAttr.getList());\n\t\t}\n\t\tif (runtimeAnnAttr == null) {\n\t\t\treturn AnnotationMethodParamsAttr.pack(buildAnnAttr.getList());\n\t\t}\n\t\treturn AnnotationMethodParamsAttr.pack(mergeParamLists(runtimeAnnAttr.getList(), buildAnnAttr.getList()));\n\t}\n\n\tprivate static List<List<IAnnotation>> mergeParamLists(List<List<IAnnotation>> first, List<List<IAnnotation>> second) {\n\t\tint firstSize = first.size();\n\t\tint secondSize = second.size();\n\t\tint size = Math.max(firstSize, secondSize);\n\t\tList<List<IAnnotation>> result = new ArrayList<>(size);\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tList<IAnnotation> firstList = i < firstSize ? first.get(i) : Collections.emptyList();\n\t\t\tList<IAnnotation> secondList = i < secondSize ? second.get(i) : Collections.emptyList();\n\t\t\tresult.add(Utils.concat(firstList, secondList));\n\t\t}\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/types/JavaSignatureAttr.java",
    "content": "package jadx.plugins.input.java.data.attributes.types;\n\nimport jadx.api.plugins.input.data.attributes.types.SignatureAttr;\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.IJavaAttributeReader;\n\npublic class JavaSignatureAttr extends SignatureAttr implements IJavaAttribute {\n\n\tpublic JavaSignatureAttr(String signature) {\n\t\tsuper(signature);\n\t}\n\n\tpublic static IJavaAttributeReader reader() {\n\t\treturn (clsData, reader) -> new JavaSignatureAttr(clsData.getConstPoolReader().getUtf8(reader.readU2()));\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/types/JavaSourceFileAttr.java",
    "content": "package jadx.plugins.input.java.data.attributes.types;\n\nimport jadx.api.plugins.input.data.attributes.types.SourceFileAttr;\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.IJavaAttributeReader;\n\npublic class JavaSourceFileAttr extends SourceFileAttr implements IJavaAttribute {\n\n\tpublic JavaSourceFileAttr(String fileName) {\n\t\tsuper(fileName);\n\t}\n\n\tpublic static IJavaAttributeReader reader() {\n\t\treturn (clsData, reader) -> new JavaSourceFileAttr(clsData.getConstPoolReader().getUtf8(reader.readU2()));\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/types/StackMapTableAttr.java",
    "content": "package jadx.plugins.input.java.data.attributes.types;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.plugins.input.java.data.attributes.IJavaAttribute;\nimport jadx.plugins.input.java.data.attributes.stack.StackFrame;\n\npublic class StackMapTableAttr implements IJavaAttribute {\n\tpublic static final StackMapTableAttr EMPTY = new StackMapTableAttr(Collections.emptyMap());\n\n\tprivate final Map<Integer, StackFrame> map;\n\n\tpublic StackMapTableAttr(Map<Integer, StackFrame> map) {\n\t\tthis.map = map;\n\t}\n\n\tpublic @Nullable StackFrame getFor(int offset) {\n\t\treturn map.get(offset);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/attributes/types/data/RawBootstrapMethod.java",
    "content": "package jadx.plugins.input.java.data.attributes.types.data;\n\npublic class RawBootstrapMethod {\n\tprivate final int methodHandleIdx;\n\tprivate final int[] args;\n\n\tpublic RawBootstrapMethod(int methodHandleIdx, int[] args) {\n\t\tthis.methodHandleIdx = methodHandleIdx;\n\t\tthis.args = args;\n\t}\n\n\tpublic int getMethodHandleIdx() {\n\t\treturn methodHandleIdx;\n\t}\n\n\tpublic int[] getArgs() {\n\t\treturn args;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/ArrayType.java",
    "content": "package jadx.plugins.input.java.data.code;\n\nimport jadx.plugins.input.java.utils.JavaClassParseException;\n\npublic class ArrayType {\n\n\tpublic static String byValue(int val) {\n\t\tswitch (val) {\n\t\t\tcase 4:\n\t\t\t\treturn \"Z\";\n\t\t\tcase 5:\n\t\t\t\treturn \"C\";\n\t\t\tcase 6:\n\t\t\t\treturn \"F\";\n\t\t\tcase 7:\n\t\t\t\treturn \"D\";\n\t\t\tcase 8:\n\t\t\t\treturn \"B\";\n\t\t\tcase 9:\n\t\t\t\treturn \"S\";\n\t\t\tcase 10:\n\t\t\t\treturn \"I\";\n\t\t\tcase 11:\n\t\t\t\treturn \"J\";\n\n\t\t\tdefault:\n\t\t\t\tthrow new JavaClassParseException(\"Unknown array type value: \" + val);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/CodeDecodeState.java",
    "content": "package jadx.plugins.input.java.data.code;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.insns.Opcode;\nimport jadx.core.utils.Utils;\nimport jadx.plugins.input.java.data.DataReader;\nimport jadx.plugins.input.java.data.JavaClassData;\nimport jadx.plugins.input.java.data.attributes.stack.StackFrame;\nimport jadx.plugins.input.java.data.attributes.stack.StackValueType;\nimport jadx.plugins.input.java.data.attributes.types.StackMapTableAttr;\n\n@SuppressWarnings(\"UnusedReturnValue\")\npublic class CodeDecodeState {\n\tprivate final JavaClassData clsData;\n\tprivate final DataReader reader;\n\tprivate final int maxStack;\n\tprivate final Set<Integer> excHandlers;\n\tprivate final StackMapTableAttr stackMapTable;\n\tprivate final Map<Integer, StackState> jumpStack = new HashMap<>(); // save current stack for jump target\n\n\tprivate JavaInsnData insn;\n\tprivate StackState stack;\n\tprivate boolean excHandler;\n\n\tpublic CodeDecodeState(JavaClassData clsData, DataReader reader, int maxStack,\n\t\t\tSet<Integer> excHandlers, @Nullable StackMapTableAttr stackMapTable) {\n\t\tthis.clsData = clsData;\n\t\tthis.reader = reader;\n\t\tthis.maxStack = maxStack;\n\t\tthis.excHandlers = excHandlers;\n\t\tthis.stack = new StackState(maxStack);\n\t\tthis.stackMapTable = Utils.getOrElse(stackMapTable, StackMapTableAttr.EMPTY);\n\t}\n\n\tpublic void onInsn(int offset) {\n\t\tStackState newStack = loadStack(offset);\n\t\tif (newStack != null) {\n\t\t\tthis.stack = newStack;\n\t\t}\n\t\tif (excHandlers.contains(offset)) {\n\t\t\tclear();\n\t\t\tstack.push(StackValueType.NARROW); // push exception\n\t\t\texcHandler = true;\n\t\t} else {\n\t\t\texcHandler = false;\n\t\t}\n\t}\n\n\tprivate @Nullable StackState loadStack(int offset) {\n\t\tStackState stackState = jumpStack.get(offset);\n\t\tif (stackState != null) {\n\t\t\treturn stackState.copy();\n\t\t}\n\t\tStackFrame frame = stackMapTable.getFor(offset);\n\t\tif (frame != null) {\n\t\t\treturn new StackState(maxStack).fillFromFrame(frame);\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic void registerJump(int jumpOffset) {\n\t\tInteger key = jumpOffset;\n\t\tif (!jumpStack.containsKey(key)) {\n\t\t\tjumpStack.put(key, stack.copy());\n\t\t}\n\t}\n\n\tpublic void decoded() {\n\t\tif (excHandler && insn.getOpcode() == Opcode.MOVE) {\n\t\t\t// replace first 'move' in exception handler with 'move-exception'\n\t\t\tinsn.setOpcode(Opcode.MOVE_EXCEPTION);\n\t\t\tinsn.setRegsCount(1);\n\t\t}\n\t}\n\n\tpublic JavaInsnData insn() {\n\t\treturn insn;\n\t}\n\n\tpublic void setInsn(JavaInsnData insn) {\n\t\tthis.insn = insn;\n\t}\n\n\tpublic DataReader reader() {\n\t\treturn reader;\n\t}\n\n\tpublic JavaClassData clsData() {\n\t\treturn clsData;\n\t}\n\n\tpublic CodeDecodeState local(int arg, int local) {\n\t\tinsn.setArgReg(arg, localToReg(local));\n\t\treturn this;\n\t}\n\n\tpublic CodeDecodeState pop(int arg) {\n\t\tinsn.setArgReg(arg, stack.pop());\n\t\treturn this;\n\t}\n\n\tpublic CodeDecodeState peek(int arg) {\n\t\tinsn.setArgReg(arg, stack.peek());\n\t\treturn this;\n\t}\n\n\tpublic StackValueType peekType(int at) {\n\t\treturn stack.peekTypeAt(at);\n\t}\n\n\tpublic CodeDecodeState peekFrom(int pos, int arg) {\n\t\tinsn.setArgReg(arg, stack.peekAt(pos));\n\t\treturn this;\n\t}\n\n\tpublic CodeDecodeState push(int arg) {\n\t\tinsn.setArgReg(arg, stack.push(StackValueType.NARROW));\n\t\treturn this;\n\t}\n\n\tpublic CodeDecodeState push(int arg, StackValueType type) {\n\t\tinsn.setArgReg(arg, stack.push(type));\n\t\treturn this;\n\t}\n\n\tpublic CodeDecodeState pushWide(int arg) {\n\t\tinsn.setArgReg(arg, stack.push(StackValueType.WIDE));\n\t\treturn this;\n\t}\n\n\tpublic int insert(int pos, StackValueType type) {\n\t\treturn stack.insert(pos, type);\n\t}\n\n\tpublic void discard() {\n\t\tstack.pop();\n\t}\n\n\tpublic void discardWord() {\n\t\tStackValueType type = stack.peekTypeAt(0);\n\t\tstack.pop();\n\t\tif (type == StackValueType.NARROW) {\n\t\t\tstack.pop();\n\t\t}\n\t}\n\n\tpublic CodeDecodeState clear() {\n\t\tstack.clear();\n\t\treturn this;\n\t}\n\n\tpublic int push(String type) {\n\t\treturn stack.push(getSVType(type));\n\t}\n\n\t/**\n\t * Must be after all pop and push\n\t */\n\tpublic void jump(int offset) {\n\t\tint jumpOffset = insn.getOffset() + offset;\n\t\tinsn.setTarget(jumpOffset);\n\t\tregisterJump(jumpOffset);\n\t}\n\n\tpublic CodeDecodeState idx(int idx) {\n\t\tinsn.setIndex(idx);\n\t\treturn this;\n\t}\n\n\tpublic CodeDecodeState lit(long lit) {\n\t\tinsn.setLiteral(lit);\n\t\treturn this;\n\t}\n\n\tprivate int localToReg(int local) {\n\t\treturn maxStack + local;\n\t}\n\n\tpublic StackValueType fieldType() {\n\t\tString type = insn.constPoolReader().getFieldType(insn().getIndex());\n\t\treturn getSVType(type);\n\t}\n\n\tpublic StackValueType getSVType(String type) {\n\t\tif (type.equals(\"J\") || type.equals(\"D\")) {\n\t\t\treturn StackValueType.WIDE;\n\t\t}\n\t\treturn StackValueType.NARROW;\n\t}\n\n\tpublic int u1() {\n\t\treturn reader.readU1();\n\t}\n\n\tpublic int u2() {\n\t\treturn reader.readU2();\n\t}\n\n\tpublic int s1() {\n\t\treturn reader.readS1();\n\t}\n\n\tpublic int s2() {\n\t\treturn reader.readS2();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/JavaCodeReader.java",
    "content": "package jadx.plugins.input.java.data.code;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.ICodeReader;\nimport jadx.api.plugins.input.data.IDebugInfo;\nimport jadx.api.plugins.input.data.ILocalVar;\nimport jadx.api.plugins.input.data.ITry;\nimport jadx.api.plugins.input.data.impl.CatchData;\nimport jadx.api.plugins.input.data.impl.DebugInfo;\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.plugins.input.java.data.ConstPoolReader;\nimport jadx.plugins.input.java.data.DataReader;\nimport jadx.plugins.input.java.data.JavaClassData;\nimport jadx.plugins.input.java.data.attributes.JavaAttrStorage;\nimport jadx.plugins.input.java.data.attributes.JavaAttrType;\nimport jadx.plugins.input.java.data.attributes.debuginfo.JavaLocalVar;\nimport jadx.plugins.input.java.data.attributes.debuginfo.LineNumberTableAttr;\nimport jadx.plugins.input.java.data.attributes.debuginfo.LocalVarTypesAttr;\nimport jadx.plugins.input.java.data.attributes.debuginfo.LocalVarsAttr;\nimport jadx.plugins.input.java.data.attributes.types.StackMapTableAttr;\nimport jadx.plugins.input.java.data.code.trycatch.JavaSingleCatch;\nimport jadx.plugins.input.java.data.code.trycatch.JavaTryData;\nimport jadx.plugins.input.java.utils.JavaClassParseException;\n\npublic class JavaCodeReader implements ICodeReader {\n\n\tprivate final JavaClassData clsData;\n\tprivate final DataReader reader;\n\tprivate final int codeOffset;\n\n\tpublic JavaCodeReader(JavaClassData clsData, int offset) {\n\t\tthis.clsData = clsData;\n\t\tthis.reader = clsData.getData();\n\t\tthis.codeOffset = offset;\n\t}\n\n\t@Override\n\tpublic ICodeReader copy() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void visitInstructions(Consumer<InsnData> insnConsumer) {\n\t\tSet<Integer> excHandlers = getExcHandlers();\n\t\tjumpToCodeAttributes();\n\t\tStackMapTableAttr stackMapTable = clsData.getAttributesReader().loadOne(reader, JavaAttrType.STACK_MAP_TABLE);\n\n\t\tint maxStack = readMaxStack();\n\t\treader.skip(2);\n\t\tint codeSize = reader.readU4();\n\n\t\tCodeDecodeState state = new CodeDecodeState(clsData, reader, maxStack, excHandlers, stackMapTable);\n\t\tJavaInsnData insn = new JavaInsnData(state);\n\t\tstate.setInsn(insn);\n\t\tint offset = 0;\n\t\twhile (offset < codeSize) {\n\t\t\tinsn.setDecoded(false);\n\t\t\tinsn.setOffset(offset);\n\t\t\tinsn.setInsnStart(reader.getOffset());\n\n\t\t\tint opcode = reader.readU1();\n\t\t\tJavaInsnInfo insnInfo = JavaInsnsRegister.get(opcode);\n\t\t\tif (insnInfo == null) {\n\t\t\t\tthrow new JavaClassParseException(\"Unknown opcode: 0x\" + Integer.toHexString(opcode));\n\t\t\t}\n\t\t\tinsn.setOpcodeUnit(opcode);\n\t\t\tinsn.setInsnInfo(insnInfo);\n\t\t\tinsn.setRegsCount(insnInfo.getRegsCount());\n\t\t\tinsn.setOpcode(insnInfo.getApiOpcode());\n\t\t\tinsn.setPayloadSize(insnInfo.getPayloadSize());\n\t\t\tinsn.setPayload(null);\n\n\t\t\tstate.onInsn(offset);\n\t\t\tinsnConsumer.accept(insn);\n\n\t\t\tint payloadSize = insn.getPayloadSize();\n\t\t\tif (!insn.isDecoded()) {\n\t\t\t\tif (payloadSize == -1) {\n\t\t\t\t\tinsn.skip();\n\t\t\t\t\tpayloadSize = insn.getPayloadSize();\n\t\t\t\t} else {\n\t\t\t\t\treader.skip(payloadSize);\n\t\t\t\t}\n\t\t\t}\n\t\t\toffset += 1 + payloadSize;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int getRegistersCount() {\n\t\tint maxStack = readMaxStack();\n\t\tint maxLocals = reader.readU2();\n\t\treturn maxStack + maxLocals;\n\t}\n\n\t@Override\n\tpublic int getArgsStartReg() {\n\t\treturn readMaxStack();\n\t}\n\n\tprivate int readMaxStack() {\n\t\treader.absPos(codeOffset);\n\t\tint maxStack = reader.readU2();\n\t\treturn maxStack + 1; // add one temporary register (for `swap` opcode)\n\t}\n\n\t@Override\n\tpublic int getUnitsCount() {\n\t\treturn reader.absPos(codeOffset + 4).readU4();\n\t}\n\n\tprivate static final Set<JavaAttrType<?>> DEBUG_INFO_ATTRIBUTES = Set.of(\n\t\t\tJavaAttrType.LINE_NUMBER_TABLE,\n\t\t\tJavaAttrType.LOCAL_VAR_TABLE,\n\t\t\tJavaAttrType.LOCAL_VAR_TYPE_TABLE);\n\n\t@Override\n\t@Nullable\n\tpublic IDebugInfo getDebugInfo() {\n\t\tint maxStack = readMaxStack();\n\t\tjumpToCodeAttributes();\n\t\tJavaAttrStorage attrs = clsData.getAttributesReader().loadMulti(reader, DEBUG_INFO_ATTRIBUTES);\n\t\tLineNumberTableAttr linesAttr = attrs.get(JavaAttrType.LINE_NUMBER_TABLE);\n\t\tLocalVarsAttr varsAttr = attrs.get(JavaAttrType.LOCAL_VAR_TABLE);\n\t\tif (linesAttr == null && varsAttr == null) {\n\t\t\treturn null;\n\t\t}\n\t\tMap<Integer, Integer> linesMap = linesAttr != null ? linesAttr.getLineMap() : Collections.emptyMap();\n\n\t\tList<ILocalVar> vars;\n\t\tif (varsAttr == null) {\n\t\t\tvars = Collections.emptyList();\n\t\t} else {\n\t\t\tList<JavaLocalVar> javaVars = varsAttr.getVars();\n\t\t\tLocalVarTypesAttr typedVars = attrs.get(JavaAttrType.LOCAL_VAR_TYPE_TABLE);\n\t\t\tif (typedVars != null && !typedVars.getVars().isEmpty()) {\n\t\t\t\t// merge signature from typedVars into javaVars\n\t\t\t\tMap<JavaLocalVar, JavaLocalVar> varsMap = new HashMap<>(javaVars.size());\n\t\t\t\tjavaVars.forEach(v -> varsMap.put(v, v));\n\t\t\t\tfor (JavaLocalVar typedVar : typedVars.getVars()) {\n\t\t\t\t\tJavaLocalVar jv = varsMap.get(typedVar);\n\t\t\t\t\tif (jv != null) {\n\t\t\t\t\t\tjv.setSignature(typedVar.getSignature());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tjavaVars.forEach(v -> v.shiftRegNum(maxStack));\n\t\t\tvars = Collections.unmodifiableList(javaVars);\n\t\t}\n\t\treturn new DebugInfo(linesMap, vars);\n\t}\n\n\t@Override\n\tpublic int getCodeOffset() {\n\t\treturn codeOffset;\n\t}\n\n\t@Override\n\tpublic List<ITry> getTries() {\n\t\tjumpToTries();\n\t\tint excTableLen = reader.readU2();\n\t\tif (excTableLen == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tConstPoolReader constPool = clsData.getConstPoolReader();\n\t\tMap<JavaTryData, List<JavaSingleCatch>> tries = new HashMap<>(excTableLen);\n\t\tfor (int i = 0; i < excTableLen; i++) {\n\t\t\tint start = reader.readU2();\n\t\t\tint end = reader.readU2();\n\t\t\tint handler = reader.readU2();\n\t\t\tint type = reader.readU2();\n\t\t\tJavaTryData tryData = new JavaTryData(start, end);\n\t\t\tList<JavaSingleCatch> catches = tries.computeIfAbsent(tryData, k -> new ArrayList<>());\n\t\t\tif (type == 0) {\n\t\t\t\tcatches.add(new JavaSingleCatch(handler, null));\n\t\t\t} else {\n\t\t\t\tcatches.add(new JavaSingleCatch(handler, constPool.getClass(type)));\n\t\t\t}\n\t\t}\n\t\treturn tries.entrySet().stream()\n\t\t\t\t.map(e -> {\n\t\t\t\t\tJavaTryData tryData = e.getKey();\n\t\t\t\t\ttryData.setCatch(convertSingleCatches(e.getValue()));\n\t\t\t\t\treturn tryData;\n\t\t\t\t})\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tprivate static CatchData convertSingleCatches(List<JavaSingleCatch> list) {\n\t\tint allHandler = -1;\n\t\tfor (JavaSingleCatch singleCatch : list) {\n\t\t\tif (singleCatch.getType() == null) {\n\t\t\t\tallHandler = singleCatch.getHandler();\n\t\t\t\tlist.remove(singleCatch);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tint len = list.size();\n\t\tint[] handlers = new int[len];\n\t\tString[] types = new String[len];\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tJavaSingleCatch singleCatch = list.get(i);\n\t\t\thandlers[i] = singleCatch.getHandler();\n\t\t\ttypes[i] = singleCatch.getType();\n\t\t}\n\t\treturn new CatchData(handlers, types, allHandler);\n\t}\n\n\tprivate Set<Integer> getExcHandlers() {\n\t\tjumpToTries();\n\t\tint excTableLen = reader.readU2();\n\t\tif (excTableLen == 0) {\n\t\t\treturn Collections.emptySet();\n\t\t}\n\t\tSet<Integer> set = new HashSet<>(excTableLen);\n\t\tfor (int i = 0; i < excTableLen; i++) {\n\t\t\treader.skip(4);\n\t\t\tint handler = reader.readU2();\n\t\t\treader.skip(2);\n\t\t\tset.add(handler);\n\t\t}\n\t\treturn set;\n\t}\n\n\tprivate void jumpToTries() {\n\t\treader.absPos(codeOffset + 4);\n\t\treader.skip(reader.readU4()); // code length\n\t}\n\n\tprivate void jumpToCodeAttributes() {\n\t\tjumpToTries();\n\t\treader.skip(reader.readU2() * 8); // exceptions table\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/JavaInsnData.java",
    "content": "package jadx.plugins.input.java.data.code;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.data.ICallSite;\nimport jadx.api.plugins.input.data.IFieldRef;\nimport jadx.api.plugins.input.data.IMethodHandle;\nimport jadx.api.plugins.input.data.IMethodProto;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.input.insns.InsnData;\nimport jadx.api.plugins.input.insns.InsnIndexType;\nimport jadx.api.plugins.input.insns.Opcode;\nimport jadx.api.plugins.input.insns.custom.ICustomPayload;\nimport jadx.plugins.input.java.data.ConstPoolReader;\nimport jadx.plugins.input.java.data.DataReader;\nimport jadx.plugins.input.java.data.code.decoders.IJavaInsnDecoder;\n\npublic class JavaInsnData implements InsnData {\n\n\tprivate final CodeDecodeState state;\n\n\tprivate JavaInsnInfo insnInfo;\n\tprivate Opcode opcode;\n\tprivate boolean decoded;\n\tprivate int opcodeUnit;\n\tprivate int payloadSize;\n\tprivate int insnStart;\n\tprivate int offset;\n\tprivate int regsCount;\n\tprivate int[] argsReg = new int[16];\n\tprivate int resultReg;\n\tprivate long literal;\n\tprivate int target;\n\tprivate int index;\n\t@Nullable\n\tprivate ICustomPayload payload;\n\n\tpublic JavaInsnData(CodeDecodeState state) {\n\t\tthis.state = state;\n\t}\n\n\t@Override\n\tpublic void decode() {\n\t\tIJavaInsnDecoder decoder = insnInfo.getDecoder();\n\t\tif (decoder != null) {\n\t\t\tdecoder.decode(state);\n\t\t\tstate.decoded();\n\t\t}\n\t\tdecoded = true;\n\t}\n\n\tpublic void skip() {\n\t\tIJavaInsnDecoder decoder = insnInfo.getDecoder();\n\t\tif (decoder != null) {\n\t\t\tdecoder.skip(state);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int getOffset() {\n\t\treturn offset;\n\t}\n\n\t@Override\n\tpublic int getFileOffset() {\n\t\treturn insnStart;\n\t}\n\n\t@Override\n\tpublic Opcode getOpcode() {\n\t\treturn opcode;\n\t}\n\n\tpublic void setOpcode(Opcode opcode) {\n\t\tthis.opcode = opcode;\n\t}\n\n\t@Override\n\tpublic String getOpcodeMnemonic() {\n\t\treturn insnInfo.getName();\n\t}\n\n\t@Override\n\tpublic byte[] getByteCode() {\n\t\tDataReader reader = state.reader();\n\t\tint startOffset = reader.getOffset();\n\t\ttry {\n\t\t\treader.absPos(insnStart);\n\t\t\treturn reader.readBytes(1 + payloadSize);\n\t\t} finally {\n\t\t\treader.absPos(startOffset);\n\t\t}\n\t}\n\n\t@Override\n\tpublic InsnIndexType getIndexType() {\n\t\treturn insnInfo.getIndexType();\n\t}\n\n\t@Override\n\tpublic int getRawOpcodeUnit() {\n\t\treturn opcodeUnit;\n\t}\n\n\t@Override\n\tpublic int getRegsCount() {\n\t\treturn regsCount;\n\t}\n\n\t@Override\n\tpublic int getReg(int argNum) {\n\t\treturn argsReg[argNum];\n\t}\n\n\t@Override\n\tpublic int getResultReg() {\n\t\treturn resultReg;\n\t}\n\n\tpublic void setResultReg(int resultReg) {\n\t\tthis.resultReg = resultReg;\n\t}\n\n\t@Override\n\tpublic long getLiteral() {\n\t\treturn literal;\n\t}\n\n\t@Override\n\tpublic int getTarget() {\n\t\treturn target;\n\t}\n\n\t@Override\n\tpublic int getIndex() {\n\t\treturn index;\n\t}\n\n\tpublic int getPayloadSize() {\n\t\treturn payloadSize;\n\t}\n\n\t@Override\n\tpublic String getIndexAsString() {\n\t\treturn constPoolReader().getUtf8(index);\n\t}\n\n\t@Override\n\tpublic String getIndexAsType() {\n\t\tif (insnInfo.getOpcode() == 0xbc) { // newarray\n\t\t\treturn ArrayType.byValue(index);\n\t\t}\n\t\treturn constPoolReader().getClass(index);\n\t}\n\n\t@Override\n\tpublic IFieldRef getIndexAsField() {\n\t\treturn constPoolReader().getFieldRef(index);\n\t}\n\n\t@Override\n\tpublic IMethodRef getIndexAsMethod() {\n\t\treturn constPoolReader().getMethodRef(index);\n\t}\n\n\t@Override\n\tpublic ICallSite getIndexAsCallSite() {\n\t\treturn constPoolReader().getCallSite(index);\n\t}\n\n\t@Override\n\tpublic IMethodProto getIndexAsProto(int protoIndex) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic IMethodHandle getIndexAsMethodHandle() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic @Nullable ICustomPayload getPayload() {\n\t\treturn payload;\n\t}\n\n\tpublic void setInsnInfo(JavaInsnInfo insnInfo) {\n\t\tthis.insnInfo = insnInfo;\n\t}\n\n\tpublic boolean isDecoded() {\n\t\treturn decoded;\n\t}\n\n\tpublic void setDecoded(boolean decoded) {\n\t\tthis.decoded = decoded;\n\t}\n\n\tpublic void setOpcodeUnit(int opcodeUnit) {\n\t\tthis.opcodeUnit = opcodeUnit;\n\t}\n\n\tpublic void setPayloadSize(int payloadSize) {\n\t\tthis.payloadSize = payloadSize;\n\t}\n\n\tpublic void setInsnStart(int insnStart) {\n\t\tthis.insnStart = insnStart;\n\t}\n\n\tpublic void setOffset(int offset) {\n\t\tthis.offset = offset;\n\t}\n\n\tpublic void setArgReg(int arg, int reg) {\n\t\tthis.argsReg[arg] = reg;\n\t}\n\n\tpublic void setRegsCount(int regsCount) {\n\t\tthis.regsCount = regsCount;\n\t\tif (argsReg.length < regsCount) {\n\t\t\targsReg = new int[regsCount];\n\t\t}\n\t}\n\n\tpublic int[] getRegsArray() {\n\t\treturn argsReg;\n\t}\n\n\tpublic void setLiteral(long literal) {\n\t\tthis.literal = literal;\n\t}\n\n\tpublic void setTarget(int target) {\n\t\tthis.target = target;\n\t}\n\n\tpublic void setIndex(int index) {\n\t\tthis.index = index;\n\t}\n\n\tpublic void setPayload(ICustomPayload payload) {\n\t\tthis.payload = payload;\n\t}\n\n\tpublic ConstPoolReader constPoolReader() {\n\t\treturn state.clsData().getConstPoolReader();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(String.format(\"0x%04X\", offset));\n\t\tsb.append(\": \").append(getOpcode());\n\t\tif (insnInfo == null) {\n\t\t\tsb.append(String.format(\"(0x%04X)\", opcodeUnit));\n\t\t} else {\n\t\t\tint regsCount = getRegsCount();\n\t\t\tif (isDecoded()) {\n\t\t\t\tsb.append(' ');\n\t\t\t\tfor (int i = 0; i < regsCount; i++) {\n\t\t\t\t\tif (i != 0) {\n\t\t\t\t\t\tsb.append(\", \");\n\t\t\t\t\t}\n\t\t\t\t\tsb.append(\"r\").append(argsReg[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/JavaInsnInfo.java",
    "content": "package jadx.plugins.input.java.data.code;\n\nimport jadx.api.plugins.input.insns.InsnIndexType;\nimport jadx.api.plugins.input.insns.Opcode;\nimport jadx.plugins.input.java.data.code.decoders.IJavaInsnDecoder;\n\npublic class JavaInsnInfo {\n\tprivate final int opcode;\n\tprivate final String name;\n\tprivate final int payloadSize;\n\tprivate final int regsCount;\n\tprivate final Opcode apiOpcode;\n\tprivate final InsnIndexType indexType;\n\tprivate final IJavaInsnDecoder decoder;\n\n\tpublic JavaInsnInfo(int opcode, String name, int payloadSize, int regsCount, Opcode apiOpcode,\n\t\t\tInsnIndexType indexType, IJavaInsnDecoder decoder) {\n\t\tthis.opcode = opcode;\n\t\tthis.name = name;\n\t\tthis.payloadSize = payloadSize;\n\t\tthis.regsCount = regsCount;\n\t\tthis.apiOpcode = apiOpcode;\n\t\tthis.indexType = indexType;\n\t\tthis.decoder = decoder;\n\t}\n\n\tpublic int getOpcode() {\n\t\treturn opcode;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic int getPayloadSize() {\n\t\treturn payloadSize;\n\t}\n\n\tpublic int getRegsCount() {\n\t\treturn regsCount;\n\t}\n\n\tpublic Opcode getApiOpcode() {\n\t\treturn apiOpcode;\n\t}\n\n\tpublic InsnIndexType getIndexType() {\n\t\treturn indexType;\n\t}\n\n\tpublic IJavaInsnDecoder getDecoder() {\n\t\treturn decoder;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"0x\" + Integer.toHexString(opcode) + \": \" + name;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/JavaInsnsRegister.java",
    "content": "package jadx.plugins.input.java.data.code;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.api.plugins.input.insns.InsnIndexType;\nimport jadx.api.plugins.input.insns.Opcode;\nimport jadx.plugins.input.java.data.attributes.stack.StackValueType;\nimport jadx.plugins.input.java.data.code.decoders.IJavaInsnDecoder;\nimport jadx.plugins.input.java.data.code.decoders.InvokeDecoder;\nimport jadx.plugins.input.java.data.code.decoders.LoadConstDecoder;\nimport jadx.plugins.input.java.data.code.decoders.LookupSwitchDecoder;\nimport jadx.plugins.input.java.data.code.decoders.TableSwitchDecoder;\nimport jadx.plugins.input.java.data.code.decoders.WideDecoder;\n\nimport static jadx.plugins.input.java.data.attributes.stack.StackValueType.NARROW;\nimport static jadx.plugins.input.java.data.attributes.stack.StackValueType.WIDE;\n\n@SuppressWarnings(\"SpellCheckingInspection\")\npublic class JavaInsnsRegister {\n\n\tprivate static final JavaInsnInfo[] INSN_INFO;\n\n\tpublic static final long FLOAT_ZERO = Float.floatToIntBits(0.0f);\n\tpublic static final long FLOAT_ONE = Float.floatToIntBits(1.0f);\n\tpublic static final long FLOAT_TWO = Float.floatToIntBits(2.0f);\n\n\tpublic static final long DOUBLE_ZERO = Double.doubleToLongBits(0.0d);\n\tpublic static final long DOUBLE_ONE = Double.doubleToLongBits(1.0d);\n\n\tstatic {\n\t\tJavaInsnInfo[] arr = new JavaInsnInfo[0xCA];\n\t\tINSN_INFO = arr;\n\t\tregister(arr, 0x00, \"nop\", 0, 0, Opcode.NOP, null);\n\n\t\tconstInsn(arr, 0x01, \"aconst_null\", Opcode.CONST, 0);\n\t\tconstInsn(arr, 0x02, \"iconst_m1\", Opcode.CONST, -1);\n\t\tconstInsn(arr, 0x03, \"iconst_0\", Opcode.CONST, 0);\n\t\tconstInsn(arr, 0x04, \"iconst_1\", Opcode.CONST, 1);\n\t\tconstInsn(arr, 0x05, \"iconst_2\", Opcode.CONST, 2);\n\t\tconstInsn(arr, 0x06, \"iconst_3\", Opcode.CONST, 3);\n\t\tconstInsn(arr, 0x07, \"iconst_4\", Opcode.CONST, 4);\n\t\tconstInsn(arr, 0x08, \"iconst_5\", Opcode.CONST, 5);\n\n\t\tconstInsn(arr, 0x09, \"lconst_0\", Opcode.CONST_WIDE, 0L);\n\t\tconstInsn(arr, 0x0a, \"lconst_1\", Opcode.CONST_WIDE, 1L);\n\n\t\tconstInsn(arr, 0x0b, \"fconst_0\", Opcode.CONST, FLOAT_ZERO);\n\t\tconstInsn(arr, 0x0c, \"fconst_1\", Opcode.CONST, FLOAT_ONE);\n\t\tconstInsn(arr, 0x0d, \"fconst_2\", Opcode.CONST, FLOAT_TWO);\n\n\t\tconstInsn(arr, 0x0e, \"dconst_0\", Opcode.CONST_WIDE, DOUBLE_ZERO);\n\t\tconstInsn(arr, 0x0f, \"dconst_1\", Opcode.CONST_WIDE, DOUBLE_ONE);\n\n\t\tregister(arr, 0x10, \"bipush\", 1, 2, Opcode.CONST, s -> s.lit(s.s1()).push(0));\n\t\tregister(arr, 0x11, \"sipush\", 2, 2, Opcode.CONST, s -> s.lit(s.s2()).push(0));\n\n\t\tloadConst(arr, 0x12, \"ldc\", false);\n\t\tloadConst(arr, 0x13, \"ldc_w\", true);\n\t\tloadConst(arr, 0x14, \"ldc2_w\", true);\n\n\t\tregister(arr, 0x15, \"iload\", 1, 2, Opcode.MOVE, s -> s.local(1, s.u1()).push(0));\n\t\tregister(arr, 0x16, \"lload\", 1, 2, Opcode.MOVE_WIDE, s -> s.local(1, s.u1()).pushWide(0));\n\t\tregister(arr, 0x17, \"fload\", 1, 2, Opcode.MOVE, s -> s.local(1, s.u1()).push(0));\n\t\tregister(arr, 0x18, \"dload\", 1, 2, Opcode.MOVE_WIDE, s -> s.local(1, s.u1()).pushWide(0));\n\t\tregister(arr, 0x19, \"aload\", 1, 2, Opcode.MOVE, s -> s.local(1, s.u1()).push(0));\n\n\t\tregister(arr, 0x1a, \"iload_0\", 0, 2, Opcode.MOVE, s -> s.local(1, 0).push(0));\n\t\tregister(arr, 0x1b, \"iload_1\", 0, 2, Opcode.MOVE, s -> s.local(1, 1).push(0));\n\t\tregister(arr, 0x1c, \"iload_2\", 0, 2, Opcode.MOVE, s -> s.local(1, 2).push(0));\n\t\tregister(arr, 0x1d, \"iload_3\", 0, 2, Opcode.MOVE, s -> s.local(1, 3).push(0));\n\n\t\tregister(arr, 0x1e, \"lload_0\", 0, 2, Opcode.MOVE_WIDE, s -> s.local(1, 0).pushWide(0));\n\t\tregister(arr, 0x1f, \"lload_1\", 0, 2, Opcode.MOVE_WIDE, s -> s.local(1, 1).pushWide(0));\n\t\tregister(arr, 0x20, \"lload_2\", 0, 2, Opcode.MOVE_WIDE, s -> s.local(1, 2).pushWide(0));\n\t\tregister(arr, 0x21, \"lload_3\", 0, 2, Opcode.MOVE_WIDE, s -> s.local(1, 3).pushWide(0));\n\n\t\tregister(arr, 0x22, \"fload_0\", 0, 2, Opcode.MOVE, s -> s.local(1, 0).push(0));\n\t\tregister(arr, 0x23, \"fload_1\", 0, 2, Opcode.MOVE, s -> s.local(1, 1).push(0));\n\t\tregister(arr, 0x24, \"fload_2\", 0, 2, Opcode.MOVE, s -> s.local(1, 2).push(0));\n\t\tregister(arr, 0x25, \"fload_3\", 0, 2, Opcode.MOVE, s -> s.local(1, 3).push(0));\n\n\t\tregister(arr, 0x26, \"dload_0\", 0, 2, Opcode.MOVE_WIDE, s -> s.local(1, 0).pushWide(0));\n\t\tregister(arr, 0x27, \"dload_1\", 0, 2, Opcode.MOVE_WIDE, s -> s.local(1, 1).pushWide(0));\n\t\tregister(arr, 0x28, \"dload_2\", 0, 2, Opcode.MOVE_WIDE, s -> s.local(1, 2).pushWide(0));\n\t\tregister(arr, 0x29, \"dload_3\", 0, 2, Opcode.MOVE_WIDE, s -> s.local(1, 3).pushWide(0));\n\n\t\tregister(arr, 0x2a, \"aload_0\", 0, 2, Opcode.MOVE, s -> s.local(1, 0).push(0));\n\t\tregister(arr, 0x2b, \"aload_1\", 0, 2, Opcode.MOVE, s -> s.local(1, 1).push(0));\n\t\tregister(arr, 0x2c, \"aload_2\", 0, 2, Opcode.MOVE, s -> s.local(1, 2).push(0));\n\t\tregister(arr, 0x2d, \"aload_3\", 0, 2, Opcode.MOVE, s -> s.local(1, 3).push(0));\n\n\t\tregister(arr, 0x2e, \"iaload\", 0, 3, Opcode.AGET, aget());\n\t\tregister(arr, 0x2f, \"laload\", 0, 3, Opcode.AGET_WIDE, agetWide());\n\t\tregister(arr, 0x30, \"faload\", 0, 3, Opcode.AGET, aget());\n\t\tregister(arr, 0x31, \"daload\", 0, 3, Opcode.AGET_WIDE, agetWide());\n\t\tregister(arr, 0x32, \"aaload\", 0, 3, Opcode.AGET_OBJECT, aget());\n\t\tregister(arr, 0x33, \"baload\", 0, 3, Opcode.AGET_BYTE_BOOLEAN, aget());\n\t\tregister(arr, 0x34, \"caload\", 0, 3, Opcode.AGET_CHAR, aget());\n\t\tregister(arr, 0x35, \"saload\", 0, 3, Opcode.AGET_SHORT, aget());\n\n\t\tregister(arr, 0x36, \"istore\", 1, 2, Opcode.MOVE, s -> s.pop(1).local(0, s.u1()));\n\t\tregister(arr, 0x37, \"lstore\", 1, 2, Opcode.MOVE_WIDE, s -> s.pop(1).local(0, s.u1()));\n\t\tregister(arr, 0x38, \"fstore\", 1, 2, Opcode.MOVE, s -> s.pop(1).local(0, s.u1()));\n\t\tregister(arr, 0x39, \"dstore\", 1, 2, Opcode.MOVE_WIDE, s -> s.pop(1).local(0, s.u1()));\n\t\tregister(arr, 0x3a, \"astore\", 1, 2, Opcode.MOVE, s -> s.pop(1).local(0, s.u1()));\n\n\t\tregister(arr, 0x3b, \"istore_0\", 0, 2, Opcode.MOVE, s -> s.pop(1).local(0, 0));\n\t\tregister(arr, 0x3c, \"istore_1\", 0, 2, Opcode.MOVE, s -> s.pop(1).local(0, 1));\n\t\tregister(arr, 0x3d, \"istore_2\", 0, 2, Opcode.MOVE, s -> s.pop(1).local(0, 2));\n\t\tregister(arr, 0x3e, \"istore_3\", 0, 2, Opcode.MOVE, s -> s.pop(1).local(0, 3));\n\n\t\tregister(arr, 0x3f, \"lstore_0\", 0, 2, Opcode.MOVE_WIDE, s -> s.pop(1).local(0, 0));\n\t\tregister(arr, 0x40, \"lstore_1\", 0, 2, Opcode.MOVE_WIDE, s -> s.pop(1).local(0, 1));\n\t\tregister(arr, 0x41, \"lstore_2\", 0, 2, Opcode.MOVE_WIDE, s -> s.pop(1).local(0, 2));\n\t\tregister(arr, 0x42, \"lstore_3\", 0, 2, Opcode.MOVE_WIDE, s -> s.pop(1).local(0, 3));\n\n\t\tregister(arr, 0x43, \"fstore_0\", 0, 2, Opcode.MOVE, s -> s.pop(1).local(0, 0));\n\t\tregister(arr, 0x44, \"fstore_1\", 0, 2, Opcode.MOVE, s -> s.pop(1).local(0, 1));\n\t\tregister(arr, 0x45, \"fstore_2\", 0, 2, Opcode.MOVE, s -> s.pop(1).local(0, 2));\n\t\tregister(arr, 0x46, \"fstore_3\", 0, 2, Opcode.MOVE, s -> s.pop(1).local(0, 3));\n\n\t\tregister(arr, 0x47, \"dstore_0\", 0, 2, Opcode.MOVE_WIDE, s -> s.pop(1).local(0, 0));\n\t\tregister(arr, 0x48, \"dstore_1\", 0, 2, Opcode.MOVE_WIDE, s -> s.pop(1).local(0, 1));\n\t\tregister(arr, 0x49, \"dstore_2\", 0, 2, Opcode.MOVE_WIDE, s -> s.pop(1).local(0, 2));\n\t\tregister(arr, 0x4a, \"dstore_3\", 0, 2, Opcode.MOVE_WIDE, s -> s.pop(1).local(0, 3));\n\n\t\tregister(arr, 0x4b, \"astore_0\", 0, 2, Opcode.MOVE, s -> s.pop(1).local(0, 0));\n\t\tregister(arr, 0x4c, \"astore_1\", 0, 2, Opcode.MOVE, s -> s.pop(1).local(0, 1));\n\t\tregister(arr, 0x4d, \"astore_2\", 0, 2, Opcode.MOVE, s -> s.pop(1).local(0, 2));\n\t\tregister(arr, 0x4e, \"astore_3\", 0, 2, Opcode.MOVE, s -> s.pop(1).local(0, 3));\n\n\t\tregister(arr, 0x4f, \"iastore\", 0, 3, Opcode.APUT, aput());\n\t\tregister(arr, 0x50, \"lastore\", 0, 3, Opcode.APUT_WIDE, aput());\n\t\tregister(arr, 0x51, \"fastore\", 0, 3, Opcode.APUT, aput());\n\t\tregister(arr, 0x52, \"dastore\", 0, 3, Opcode.APUT_WIDE, aput());\n\t\tregister(arr, 0x53, \"aastore\", 0, 3, Opcode.APUT_OBJECT, aput());\n\t\tregister(arr, 0x54, \"bastore\", 0, 3, Opcode.APUT_BYTE_BOOLEAN, aput());\n\t\tregister(arr, 0x55, \"castore\", 0, 3, Opcode.APUT_CHAR, aput());\n\t\tregister(arr, 0x56, \"sastore\", 0, 3, Opcode.APUT_SHORT, aput());\n\n\t\tregister(arr, 0x57, \"pop\", 0, 0, Opcode.NOP, CodeDecodeState::discard);\n\t\tregister(arr, 0x58, \"pop2\", 0, 0, Opcode.NOP, CodeDecodeState::discardWord);\n\n\t\tregister(arr, 0x59, \"dup\", 0, 2, Opcode.MOVE, s -> s.peek(1).push(0, s.peekType(1)));\n\t\tregister(arr, 0x5a, \"dup_x1\", 0, 6, Opcode.MOVE_MULTI,\n\t\t\t\ts -> s.push(0, s.peekType(1)).peekFrom(1, 1)\n\t\t\t\t\t\t.peekFrom(1, 2).peekFrom(2, 3)\n\t\t\t\t\t\t.peekFrom(2, 4).peekFrom(0, 5));\n\t\tregister(arr, 0x5b, \"dup_x2\", 0, 8, Opcode.MOVE_MULTI,\n\t\t\t\ts -> s.push(0, s.peekType(1)).peekFrom(1, 1)\n\t\t\t\t\t\t.peekFrom(1, 2).peekFrom(2, 3)\n\t\t\t\t\t\t.peekFrom(2, 4).peekFrom(3, 5)\n\t\t\t\t\t\t.peekFrom(3, 6).peekFrom(0, 7));\n\t\tregister(arr, 0x5c, \"dup2\", 0, 4, Opcode.MOVE_MULTI, s -> {\n\t\t\tif (s.peekType(0) == NARROW) {\n\t\t\t\ts.peekFrom(0, 3).peekFrom(1, 1).push(0, NARROW).push(2, NARROW);\n\t\t\t} else {\n\t\t\t\ts.peek(1).push(0, s.peekType(1));\n\t\t\t}\n\t\t});\n\t\tregister(arr, 0x5d, \"dup2_x1\", 0, 10, Opcode.MOVE_MULTI, JavaInsnsRegister::dup2x1);\n\t\tregister(arr, 0x5e, \"dup2_x2\", 0, 12, Opcode.MOVE_MULTI, JavaInsnsRegister::dup2x2);\n\t\tregister(arr, 0x5f, \"swap\", 0, 6, Opcode.MOVE_MULTI,\n\t\t\t\ts -> s.peekFrom(-1, 0).peekFrom(1, 1)\n\t\t\t\t\t\t.peekFrom(1, 2).peekFrom(0, 3)\n\t\t\t\t\t\t.peekFrom(0, 4).peekFrom(-1, 5));\n\n\t\tregister(arr, 0x60, \"iadd\", 0, 3, Opcode.ADD_INT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x61, \"ladd\", 0, 3, Opcode.ADD_LONG, twoRegsWithResult(WIDE));\n\t\tregister(arr, 0x62, \"fadd\", 0, 3, Opcode.ADD_FLOAT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x63, \"dadd\", 0, 3, Opcode.ADD_DOUBLE, twoRegsWithResult(WIDE));\n\n\t\tregister(arr, 0x64, \"isub\", 0, 3, Opcode.SUB_INT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x65, \"lsub\", 0, 3, Opcode.SUB_LONG, twoRegsWithResult(WIDE));\n\t\tregister(arr, 0x66, \"fsub\", 0, 3, Opcode.SUB_FLOAT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x67, \"dsub\", 0, 3, Opcode.SUB_DOUBLE, twoRegsWithResult(WIDE));\n\n\t\tregister(arr, 0x68, \"imul\", 0, 3, Opcode.MUL_INT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x69, \"lmul\", 0, 3, Opcode.MUL_LONG, twoRegsWithResult(WIDE));\n\t\tregister(arr, 0x6a, \"fmul\", 0, 3, Opcode.MUL_FLOAT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x6b, \"dmul\", 0, 3, Opcode.MUL_DOUBLE, twoRegsWithResult(WIDE));\n\n\t\tregister(arr, 0x6c, \"idiv\", 0, 3, Opcode.DIV_INT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x6d, \"ldiv\", 0, 3, Opcode.DIV_LONG, twoRegsWithResult(WIDE));\n\t\tregister(arr, 0x6e, \"fdiv\", 0, 3, Opcode.DIV_FLOAT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x6f, \"ddiv\", 0, 3, Opcode.DIV_DOUBLE, twoRegsWithResult(WIDE));\n\n\t\tregister(arr, 0x70, \"irem\", 0, 3, Opcode.REM_INT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x71, \"lrem\", 0, 3, Opcode.REM_LONG, twoRegsWithResult(WIDE));\n\t\tregister(arr, 0x72, \"frem\", 0, 3, Opcode.REM_FLOAT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x73, \"drem\", 0, 3, Opcode.REM_DOUBLE, twoRegsWithResult(WIDE));\n\n\t\tregister(arr, 0x74, \"ineg\", 0, 2, Opcode.NEG_INT, oneRegWithResult(NARROW));\n\t\tregister(arr, 0x75, \"lneg\", 0, 2, Opcode.NEG_LONG, oneRegWithResult(WIDE));\n\t\tregister(arr, 0x76, \"fneg\", 0, 2, Opcode.NEG_FLOAT, oneRegWithResult(NARROW));\n\t\tregister(arr, 0x77, \"dneg\", 0, 2, Opcode.NEG_DOUBLE, oneRegWithResult(WIDE));\n\n\t\tregister(arr, 0x78, \"ishl\", 0, 3, Opcode.SHL_INT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x79, \"lshl\", 0, 3, Opcode.SHL_LONG, twoRegsWithResult(WIDE));\n\t\tregister(arr, 0x7a, \"ishr\", 0, 3, Opcode.SHR_INT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x7b, \"lshr\", 0, 3, Opcode.SHR_LONG, twoRegsWithResult(WIDE));\n\t\tregister(arr, 0x7c, \"iushr\", 0, 3, Opcode.USHR_INT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x7d, \"lushr\", 0, 3, Opcode.USHR_LONG, twoRegsWithResult(WIDE));\n\n\t\tregister(arr, 0x7e, \"iand\", 0, 3, Opcode.AND_INT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x7f, \"land\", 0, 3, Opcode.AND_LONG, twoRegsWithResult(WIDE));\n\t\tregister(arr, 0x80, \"ior\", 0, 3, Opcode.OR_INT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x81, \"lor\", 0, 3, Opcode.OR_LONG, twoRegsWithResult(WIDE));\n\t\tregister(arr, 0x82, \"ixor\", 0, 3, Opcode.XOR_INT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x83, \"lxor\", 0, 3, Opcode.XOR_LONG, twoRegsWithResult(WIDE));\n\n\t\tregister(arr, 0x84, \"iinc\", 2, 2, Opcode.ADD_INT_LIT, s -> {\n\t\t\tint varNum = s.u1();\n\t\t\ts.local(0, varNum).local(1, varNum).lit(s.reader().readS1());\n\t\t});\n\n\t\tregister(arr, 0x85, \"i2l\", 0, 2, Opcode.INT_TO_LONG, oneRegWithResult(WIDE));\n\t\tregister(arr, 0x86, \"i2f\", 0, 2, Opcode.INT_TO_FLOAT, oneRegWithResult(NARROW));\n\t\tregister(arr, 0x87, \"i2d\", 0, 2, Opcode.INT_TO_DOUBLE, oneRegWithResult(WIDE));\n\t\tregister(arr, 0x88, \"l2i\", 0, 2, Opcode.LONG_TO_INT, oneRegWithResult(NARROW));\n\t\tregister(arr, 0x89, \"l2f\", 0, 2, Opcode.LONG_TO_FLOAT, oneRegWithResult(NARROW));\n\t\tregister(arr, 0x8a, \"l2d\", 0, 2, Opcode.LONG_TO_DOUBLE, oneRegWithResult(WIDE));\n\t\tregister(arr, 0x8b, \"f2i\", 0, 2, Opcode.FLOAT_TO_INT, oneRegWithResult(NARROW));\n\t\tregister(arr, 0x8c, \"f2l\", 0, 2, Opcode.FLOAT_TO_LONG, oneRegWithResult(WIDE));\n\t\tregister(arr, 0x8d, \"f2d\", 0, 2, Opcode.FLOAT_TO_DOUBLE, oneRegWithResult(WIDE));\n\t\tregister(arr, 0x8e, \"d2i\", 0, 2, Opcode.DOUBLE_TO_INT, oneRegWithResult(NARROW));\n\t\tregister(arr, 0x8f, \"d2l\", 0, 2, Opcode.DOUBLE_TO_LONG, oneRegWithResult(WIDE));\n\t\tregister(arr, 0x90, \"d2f\", 0, 2, Opcode.DOUBLE_TO_FLOAT, oneRegWithResult(NARROW));\n\t\tregister(arr, 0x91, \"i2b\", 0, 2, Opcode.INT_TO_BYTE, oneRegWithResult(NARROW));\n\t\tregister(arr, 0x92, \"i2c\", 0, 2, Opcode.INT_TO_CHAR, oneRegWithResult(NARROW));\n\t\tregister(arr, 0x93, \"i2s\", 0, 2, Opcode.INT_TO_SHORT, oneRegWithResult(NARROW));\n\n\t\tregister(arr, 0x94, \"lcmp\", 0, 3, Opcode.CMP_LONG, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x95, \"fcmpl\", 0, 3, Opcode.CMPL_FLOAT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x96, \"fcmpg\", 0, 3, Opcode.CMPG_FLOAT, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x97, \"dcmpl\", 0, 3, Opcode.CMPL_DOUBLE, twoRegsWithResult(NARROW));\n\t\tregister(arr, 0x98, \"dcmpg\", 0, 3, Opcode.CMPG_DOUBLE, twoRegsWithResult(NARROW));\n\n\t\tregister(arr, 0x99, \"ifeq\", 2, 1, Opcode.IF_EQZ, zeroCmp());\n\t\tregister(arr, 0x9a, \"ifne\", 2, 1, Opcode.IF_NEZ, zeroCmp());\n\t\tregister(arr, 0x9b, \"iflt\", 2, 1, Opcode.IF_LTZ, zeroCmp());\n\t\tregister(arr, 0x9c, \"ifge\", 2, 1, Opcode.IF_GEZ, zeroCmp());\n\t\tregister(arr, 0x9d, \"ifgt\", 2, 1, Opcode.IF_GTZ, zeroCmp());\n\t\tregister(arr, 0x9e, \"ifle\", 2, 1, Opcode.IF_LEZ, zeroCmp());\n\n\t\tregister(arr, 0x9f, \"if_icmpeq\", 2, 2, Opcode.IF_EQ, cmp());\n\t\tregister(arr, 0xa0, \"if_icmpne\", 2, 2, Opcode.IF_NE, cmp());\n\t\tregister(arr, 0xa1, \"if_icmplt\", 2, 2, Opcode.IF_LT, cmp());\n\t\tregister(arr, 0xa2, \"if_icmpge\", 2, 2, Opcode.IF_GE, cmp());\n\t\tregister(arr, 0xa3, \"if_icmpgt\", 2, 2, Opcode.IF_GT, cmp());\n\t\tregister(arr, 0xa4, \"if_icmple\", 2, 2, Opcode.IF_LE, cmp());\n\t\tregister(arr, 0xa5, \"if_acmpeq\", 2, 2, Opcode.IF_EQ, cmp());\n\t\tregister(arr, 0xa6, \"if_acmpne\", 2, 2, Opcode.IF_NE, cmp());\n\n\t\tregister(arr, 0xa7, \"goto\", 2, 0, Opcode.GOTO, s -> s.jump(s.s2()));\n\t\tregister(arr, 0xa8, \"jsr\", 2, 1, Opcode.JAVA_JSR, s -> s.push(0).jump(s.s2()));\n\t\tregister(arr, 0xa9, \"ret\", 1, 1, Opcode.JAVA_RET, s -> s.local(0, s.u1()));\n\n\t\tregister(arr, 0xaa, \"tableswitch\", -1, 1, Opcode.PACKED_SWITCH, new TableSwitchDecoder());\n\t\tregister(arr, 0xab, \"lookupswitch\", -1, 1, Opcode.SPARSE_SWITCH, new LookupSwitchDecoder());\n\n\t\tregister(arr, 0xac, \"ireturn\", 0, 1, Opcode.RETURN, s -> s.pop(0));\n\t\tregister(arr, 0xad, \"lreturn\", 0, 1, Opcode.RETURN, s -> s.pop(0));\n\t\tregister(arr, 0xae, \"freturn\", 0, 1, Opcode.RETURN, s -> s.pop(0));\n\t\tregister(arr, 0xaf, \"dreturn\", 0, 1, Opcode.RETURN, s -> s.pop(0));\n\t\tregister(arr, 0xb0, \"areturn\", 0, 1, Opcode.RETURN, s -> s.pop(0));\n\t\tregister(arr, 0xb1, \"return\", 0, 0, Opcode.RETURN_VOID, null);\n\n\t\tregister(arr, 0xb2, \"getstatic\", 2, 1, Opcode.SGET, InsnIndexType.FIELD_REF, s -> s.idx(s.u2()).push(0, s.fieldType()));\n\t\tregister(arr, 0xb3, \"putstatic\", 2, 1, Opcode.SPUT, InsnIndexType.FIELD_REF, s -> s.idx(s.u2()).pop(0));\n\t\tregister(arr, 0xb4, \"getfield\", 2, 2, Opcode.IGET, InsnIndexType.FIELD_REF, s -> s.idx(s.u2()).pop(1).push(0, s.fieldType()));\n\t\tregister(arr, 0xb5, \"putfield\", 2, 2, Opcode.IPUT, InsnIndexType.FIELD_REF, s -> s.idx(s.u2()).pop(0).pop(1));\n\n\t\tinvoke(arr, 0xb6, \"invokevirtual\", 2, Opcode.INVOKE_VIRTUAL);\n\t\tinvoke(arr, 0xb7, \"invokespecial\", 2, Opcode.INVOKE_SPECIAL);\n\t\tinvoke(arr, 0xb8, \"invokestatic\", 2, Opcode.INVOKE_STATIC);\n\t\tinvoke(arr, 0xb9, \"invokeinterface\", 4, Opcode.INVOKE_INTERFACE);\n\t\tinvoke(arr, 0xba, \"invokedynamic\", 4, Opcode.INVOKE_CUSTOM);\n\n\t\tregister(arr, 0xbb, \"new\", 2, 1, Opcode.NEW_INSTANCE, InsnIndexType.TYPE_REF, s -> s.idx(s.u2()).push(0));\n\t\tregister(arr, 0xbc, \"newarray\", 1, 2, Opcode.NEW_ARRAY, InsnIndexType.TYPE_REF, s -> s.idx(s.u1()).pop(1).push(0).lit(1));\n\t\tregister(arr, 0xbd, \"anewarray\", 2, 2, Opcode.NEW_ARRAY, InsnIndexType.TYPE_REF, s -> s.idx(s.u2()).pop(1).push(0).lit(1));\n\t\tregister(arr, 0xbe, \"arraylength\", 0, 2, Opcode.ARRAY_LENGTH, oneRegWithResult(NARROW));\n\t\tregister(arr, 0xbf, \"athrow\", 0, 1, Opcode.THROW, s -> s.pop(0).clear());\n\n\t\tregister(arr, 0xc0, \"checkcast\", 2, 2, Opcode.CHECK_CAST, InsnIndexType.TYPE_REF, s -> s.idx(s.u2()).pop(1).push(0));\n\t\tregister(arr, 0xc1, \"instanceof\", 2, 2, Opcode.INSTANCE_OF, InsnIndexType.TYPE_REF, s -> s.idx(s.u2()).pop(1).push(0));\n\n\t\tregister(arr, 0xc2, \"monitorenter\", 0, 1, Opcode.MONITOR_ENTER, s -> s.pop(0));\n\t\tregister(arr, 0xc3, \"monitorexit\", 0, 1, Opcode.MONITOR_EXIT, s -> s.pop(0));\n\n\t\tregister(arr, 0xc4, \"wide\", -1, -1, Opcode.NOP, new WideDecoder());\n\n\t\tregister(arr, 0xc5, \"multianewarray\", 3, -1, Opcode.NEW_ARRAY, InsnIndexType.TYPE_REF, newArrayMulti());\n\t\tregister(arr, 0xc6, \"ifnull\", 2, 1, Opcode.IF_EQZ, zeroCmp());\n\t\tregister(arr, 0xc7, \"ifnonnull\", 2, 1, Opcode.IF_NEZ, zeroCmp());\n\n\t\tregister(arr, 0xc8, \"goto_w\", 4, 0, Opcode.GOTO, s -> s.jump(s.reader().readS4()));\n\t\tregister(arr, 0xc9, \"jsr_w\", 4, 1, Opcode.JAVA_JSR, s -> s.push(0).jump(s.reader().readS4()));\n\t}\n\n\tprivate static void dup2x1(CodeDecodeState s) {\n\t\tif (s.peekType(0) == NARROW) {\n\t\t\ts.insert(2, NARROW);\n\t\t\ts.insert(2, NARROW);\n\t\t\ts.peekFrom(0, 0).peekFrom(2, 1);\n\t\t\ts.peekFrom(1, 2).peekFrom(3, 3);\n\t\t\ts.peekFrom(2, 4).peekFrom(4, 5);\n\t\t\ts.peekFrom(3, 6).peekFrom(0, 7);\n\t\t\ts.peekFrom(4, 8).peekFrom(1, 9);\n\t\t} else {\n\t\t\ts.insn().setRegsCount(6);\n\t\t\ts.insert(1, WIDE);\n\t\t\ts.peekFrom(0, 0).peekFrom(1, 1);\n\t\t\ts.peekFrom(1, 2).peekFrom(2, 3);\n\t\t\ts.peekFrom(2, 4).peekFrom(0, 5);\n\t\t}\n\t}\n\n\tprivate static void dup2x2(CodeDecodeState s) {\n\t\tif (s.peekType(0) == NARROW) {\n\t\t\ts.insert(2, NARROW);\n\t\t\ts.insert(2, NARROW);\n\t\t\ts.peekFrom(0, 0).peekFrom(2, 1);\n\t\t\ts.peekFrom(1, 2).peekFrom(3, 3);\n\t\t\ts.peekFrom(2, 4).peekFrom(4, 5);\n\t\t\ts.peekFrom(3, 6).peekFrom(5, 7);\n\t\t\ts.peekFrom(4, 8).peekFrom(0, 9);\n\t\t\ts.peekFrom(5, 10).peekFrom(1, 11);\n\t\t} else {\n\t\t\ts.insn().setRegsCount(8);\n\t\t\ts.insert(2, WIDE);\n\t\t\ts.peekFrom(0, 0).peekFrom(1, 1);\n\t\t\ts.peekFrom(1, 2).peekFrom(2, 3);\n\t\t\ts.peekFrom(2, 4).peekFrom(3, 5);\n\t\t\ts.peekFrom(3, 6).peekFrom(0, 7);\n\t\t}\n\t}\n\n\tprivate static IJavaInsnDecoder newArrayMulti() {\n\t\treturn s -> {\n\t\t\ts.idx(s.u2());\n\t\t\tint dim = s.u1();\n\t\t\tJavaInsnData insn = s.insn();\n\t\t\tinsn.setLiteral(dim);\n\t\t\tinsn.setRegsCount(dim + 1);\n\t\t\tfor (int i = dim; i > 0; i--) {\n\t\t\t\ts.pop(i);\n\t\t\t}\n\t\t\ts.push(0);\n\t\t};\n\t}\n\n\tprivate static IJavaInsnDecoder oneRegWithResult(StackValueType type) {\n\t\treturn s -> s.pop(1).push(0, type);\n\t}\n\n\tprivate static IJavaInsnDecoder twoRegsWithResult(StackValueType type) {\n\t\treturn s -> s.pop(2).pop(1).push(0, type);\n\t}\n\n\tprivate static IJavaInsnDecoder aget() {\n\t\treturn s -> s.pop(2).pop(1).push(0);\n\t}\n\n\tprivate static IJavaInsnDecoder agetWide() {\n\t\treturn s -> s.pop(2).pop(1).pushWide(0);\n\t}\n\n\tprivate static IJavaInsnDecoder aput() {\n\t\treturn s -> s.pop(0).pop(2).pop(1);\n\t}\n\n\tprivate static IJavaInsnDecoder zeroCmp() {\n\t\treturn s -> s.pop(0).jump(s.s2());\n\t}\n\n\tprivate static IJavaInsnDecoder cmp() {\n\t\treturn s -> s.pop(1).pop(0).jump(s.s2());\n\t}\n\n\tprivate static void invoke(JavaInsnInfo[] arr, int opcode, String name, int payloadSize, Opcode apiOpcode) {\n\t\tInsnIndexType indexType = apiOpcode == Opcode.INVOKE_CUSTOM ? InsnIndexType.CALL_SITE : InsnIndexType.METHOD_REF;\n\t\tregister(arr, opcode, name, payloadSize, -1, apiOpcode, indexType, new InvokeDecoder(payloadSize, apiOpcode));\n\t}\n\n\tprivate static void constInsn(JavaInsnInfo[] arr, int opcode, String name, Opcode apiOpcode, long literal) {\n\t\tregister(arr, opcode, name, 0, 1, apiOpcode, InsnIndexType.NONE, state -> {\n\t\t\tstate.insn().setLiteral(literal);\n\t\t\tstate.push(0, apiOpcode == Opcode.CONST_WIDE ? StackValueType.WIDE : NARROW);\n\t\t});\n\t}\n\n\tprivate static void loadConst(JavaInsnInfo[] arr, int opcode, String name, boolean wide) {\n\t\tregister(arr, opcode, name, wide ? 2 : 1, 2, Opcode.CONST, InsnIndexType.NONE, new LoadConstDecoder(wide));\n\t}\n\n\tprivate static void register(JavaInsnInfo[] arr, int opcode, String name, int payloadSize, int regsCount,\n\t\t\tOpcode apiOpcode, IJavaInsnDecoder decoder) {\n\t\tregister(arr, opcode, name, payloadSize, regsCount, apiOpcode, InsnIndexType.NONE, decoder);\n\t}\n\n\tprivate static void register(JavaInsnInfo[] arr, int opcode, String name, int payloadSize, int regsCount,\n\t\t\tOpcode apiOpcode, InsnIndexType indexType, IJavaInsnDecoder decoder) {\n\t\tif (arr[opcode] != null) {\n\t\t\tthrow new IllegalStateException(\"Duplicate opcode init: 0x\" + Integer.toHexString(opcode));\n\t\t}\n\t\tarr[opcode] = new JavaInsnInfo(opcode, name, payloadSize, regsCount, apiOpcode, indexType, decoder);\n\t}\n\n\t@Nullable\n\tpublic static JavaInsnInfo get(int opcode) {\n\t\treturn INSN_INFO[opcode];\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/StackState.java",
    "content": "package jadx.plugins.input.java.data.code;\n\nimport java.util.Arrays;\n\nimport jadx.plugins.input.java.data.attributes.stack.StackFrame;\nimport jadx.plugins.input.java.data.attributes.stack.StackValueType;\n\npublic class StackState {\n\tprivate int pos = -1;\n\tprivate final StackValueType[] stack;\n\n\tpublic StackState(int maxStack) {\n\t\tthis.stack = new StackValueType[maxStack];\n\t}\n\n\tprivate StackState(int pos, StackValueType[] stack) {\n\t\tthis.pos = pos;\n\t\tthis.stack = stack;\n\t}\n\n\tpublic StackState copy() {\n\t\treturn new StackState(pos, Arrays.copyOf(stack, stack.length));\n\t}\n\n\tpublic StackState fillFromFrame(StackFrame frame) {\n\t\tint stackSize = frame.getStackSize();\n\t\tthis.pos = stackSize - 1;\n\t\tif (stackSize > 0) {\n\t\t\tSystem.arraycopy(frame.getStackValueTypes(), 0, this.stack, 0, stackSize);\n\t\t}\n\t\treturn this;\n\t}\n\n\tpublic int peek() {\n\t\treturn pos;\n\t}\n\n\tpublic int peekAt(int at) {\n\t\treturn pos - at;\n\t}\n\n\tpublic StackValueType peekTypeAt(int at) {\n\t\tint p = pos - at;\n\t\tif (checkStackIndex(p)) {\n\t\t\treturn stack[p];\n\t\t}\n\t\treturn StackValueType.NARROW;\n\t}\n\n\tpublic int insert(int at, StackValueType type) {\n\t\tint p = pos - at;\n\t\tSystem.arraycopy(stack, p, stack, p + 1, at);\n\t\tstack[p] = type;\n\t\tpos++;\n\t\treturn p;\n\t}\n\n\tpublic int push(StackValueType type) {\n\t\tint p = ++pos;\n\t\tif (checkStackIndex(p)) {\n\t\t\tstack[p] = type;\n\t\t}\n\t\treturn p;\n\t}\n\n\tprivate boolean checkStackIndex(int p) {\n\t\treturn p >= 0 && p < stack.length;\n\t}\n\n\tpublic int pop() {\n\t\treturn pos--;\n\t}\n\n\tpublic void clear() {\n\t\tpos = -1;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tint size = pos + 1;\n\t\tString arr;\n\t\tif (size == 0) {\n\t\t\tarr = \"empty\";\n\t\t} else if (size > 0 && size < stack.length) {\n\t\t\tarr = Arrays.toString(Arrays.copyOf(stack, size));\n\t\t} else {\n\t\t\tarr = Arrays.toString(stack) + \" (max)\";\n\t\t}\n\t\treturn \"Stack: \" + size + \": \" + arr;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/decoders/IJavaInsnDecoder.java",
    "content": "package jadx.plugins.input.java.data.code.decoders;\n\nimport jadx.plugins.input.java.data.code.CodeDecodeState;\n\npublic interface IJavaInsnDecoder {\n\tvoid decode(CodeDecodeState state);\n\n\tdefault void skip(CodeDecodeState state) {\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/decoders/InvokeDecoder.java",
    "content": "package jadx.plugins.input.java.data.code.decoders;\n\nimport jadx.api.plugins.input.data.ICallSite;\nimport jadx.api.plugins.input.data.IMethodProto;\nimport jadx.api.plugins.input.data.IMethodRef;\nimport jadx.api.plugins.input.insns.Opcode;\nimport jadx.plugins.input.java.data.DataReader;\nimport jadx.plugins.input.java.data.code.CodeDecodeState;\nimport jadx.plugins.input.java.data.code.JavaInsnData;\n\npublic class InvokeDecoder implements IJavaInsnDecoder {\n\tprivate final int payloadSize;\n\tprivate final Opcode apiOpcode;\n\n\tpublic InvokeDecoder(int payloadSize, Opcode apiOpcode) {\n\t\tthis.payloadSize = payloadSize;\n\t\tthis.apiOpcode = apiOpcode;\n\t}\n\n\t@Override\n\tpublic void decode(CodeDecodeState state) {\n\t\tDataReader reader = state.reader();\n\t\tint mthIdx = reader.readS2();\n\t\tif (payloadSize == 4) {\n\t\t\treader.skip(2);\n\t\t}\n\t\tJavaInsnData insn = state.insn();\n\t\tinsn.setIndex(mthIdx);\n\t\tboolean instanceCall;\n\t\tIMethodProto mthProto;\n\t\tif (apiOpcode == Opcode.INVOKE_CUSTOM) {\n\t\t\tICallSite callSite = insn.getIndexAsCallSite();\n\t\t\tinsn.setPayload(callSite);\n\t\t\tmthProto = (IMethodProto) callSite.getValues().get(2).getValue();\n\t\t\tinstanceCall = false; // 'this' arg already included in proto args\n\t\t} else {\n\t\t\tIMethodRef mthRef = insn.getIndexAsMethod();\n\t\t\tmthRef.load();\n\t\t\tinsn.setPayload(mthRef);\n\t\t\tmthProto = mthRef;\n\t\t\tinstanceCall = apiOpcode != Opcode.INVOKE_STATIC;\n\t\t}\n\n\t\tint argsCount = mthProto.getArgTypes().size();\n\t\tif (instanceCall) {\n\t\t\targsCount++;\n\t\t}\n\t\tinsn.setRegsCount(argsCount * 2); // allocate twice of the size for worst case\n\t\tint[] regs = insn.getRegsArray();\n\n\t\t// calculate actual count of registers\n\t\t// set '1' in regs to be filled with stack values later, '0' for skip\n\t\tint regsCount = 0;\n\t\tif (instanceCall) {\n\t\t\tregs[regsCount++] = 1;\n\t\t}\n\t\tfor (String type : mthProto.getArgTypes()) {\n\t\t\tint size = getRegsCountForType(type);\n\t\t\tregs[regsCount++] = 1;\n\t\t\tif (size == 2) {\n\t\t\t\tregs[regsCount++] = 0;\n\t\t\t}\n\t\t}\n\t\tinsn.setRegsCount(regsCount);\n\t\tfor (int i = regsCount - 1; i >= 0; i--) {\n\t\t\tif (regs[i] == 1) {\n\t\t\t\tstate.pop(i);\n\t\t\t}\n\t\t}\n\t\tString returnType = mthProto.getReturnType();\n\t\tif (!returnType.equals(\"V\")) {\n\t\t\tinsn.setResultReg(state.push(returnType));\n\t\t} else {\n\t\t\tinsn.setResultReg(-1);\n\t\t}\n\t}\n\n\tprivate int getRegsCountForType(String type) {\n\t\tchar c = type.charAt(0);\n\t\tif (c == 'J' || c == 'D') {\n\t\t\treturn 2;\n\t\t}\n\t\treturn 1;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/decoders/LoadConstDecoder.java",
    "content": "package jadx.plugins.input.java.data.code.decoders;\n\nimport jadx.api.plugins.input.insns.Opcode;\nimport jadx.plugins.input.java.data.ConstPoolReader;\nimport jadx.plugins.input.java.data.ConstantType;\nimport jadx.plugins.input.java.data.DataReader;\nimport jadx.plugins.input.java.data.attributes.stack.StackValueType;\nimport jadx.plugins.input.java.data.code.CodeDecodeState;\nimport jadx.plugins.input.java.data.code.JavaInsnData;\nimport jadx.plugins.input.java.utils.JavaClassParseException;\n\npublic class LoadConstDecoder implements IJavaInsnDecoder {\n\tprivate final boolean wide;\n\n\tpublic LoadConstDecoder(boolean wide) {\n\t\tthis.wide = wide;\n\t}\n\n\t@Override\n\tpublic void decode(CodeDecodeState state) {\n\t\tDataReader reader = state.reader();\n\t\tJavaInsnData insn = state.insn();\n\t\tint index;\n\t\tif (wide) {\n\t\t\tindex = reader.readU2();\n\t\t} else {\n\t\t\tindex = reader.readU1();\n\t\t}\n\t\tConstPoolReader constPoolReader = insn.constPoolReader();\n\t\tConstantType constType = constPoolReader.jumpToConst(index);\n\t\tswitch (constType) {\n\t\t\tcase INTEGER:\n\t\t\tcase FLOAT:\n\t\t\t\tinsn.setLiteral(constPoolReader.readU4());\n\t\t\t\tinsn.setOpcode(Opcode.CONST);\n\t\t\t\tstate.push(0, StackValueType.NARROW);\n\t\t\t\tbreak;\n\n\t\t\tcase LONG:\n\t\t\tcase DOUBLE:\n\t\t\t\tinsn.setLiteral(constPoolReader.readU8());\n\t\t\t\tinsn.setOpcode(Opcode.CONST_WIDE);\n\t\t\t\tstate.push(0, StackValueType.WIDE);\n\t\t\t\tbreak;\n\n\t\t\tcase STRING:\n\t\t\t\tinsn.setIndex(constPoolReader.readU2());\n\t\t\t\tinsn.setOpcode(Opcode.CONST_STRING);\n\t\t\t\tstate.push(0, StackValueType.NARROW);\n\t\t\t\tbreak;\n\n\t\t\tcase UTF8:\n\t\t\t\tinsn.setIndex(index);\n\t\t\t\tinsn.setOpcode(Opcode.CONST_STRING);\n\t\t\t\tstate.push(0, StackValueType.NARROW);\n\t\t\t\tbreak;\n\n\t\t\tcase CLASS:\n\t\t\t\tinsn.setIndex(index);\n\t\t\t\tinsn.setOpcode(Opcode.CONST_CLASS);\n\t\t\t\tstate.push(0, StackValueType.NARROW);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthrow new JavaClassParseException(\"Unsupported constant type: \" + constType);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/decoders/LookupSwitchDecoder.java",
    "content": "package jadx.plugins.input.java.data.code.decoders;\n\nimport jadx.api.plugins.input.insns.custom.impl.SwitchPayload;\nimport jadx.plugins.input.java.data.DataReader;\nimport jadx.plugins.input.java.data.code.CodeDecodeState;\nimport jadx.plugins.input.java.data.code.JavaInsnData;\n\npublic class LookupSwitchDecoder implements IJavaInsnDecoder {\n\n\t@Override\n\tpublic void decode(CodeDecodeState state) {\n\t\tread(state, false);\n\t}\n\n\t@Override\n\tpublic void skip(CodeDecodeState state) {\n\t\tread(state, true);\n\t}\n\n\tprivate static void read(CodeDecodeState state, boolean skip) {\n\t\tDataReader reader = state.reader();\n\t\tJavaInsnData insn = state.insn();\n\t\tint dataOffset = reader.getOffset();\n\t\tint insnOffset = insn.getOffset();\n\t\treader.skip(3 - insnOffset % 4);\n\t\tint defTarget = insnOffset + reader.readS4();\n\t\tint pairs = reader.readS4();\n\t\tif (skip) {\n\t\t\treader.skip(pairs * 8);\n\t\t} else {\n\t\t\tstate.pop(0);\n\t\t\tint[] keys = new int[pairs];\n\t\t\tint[] targets = new int[pairs];\n\t\t\tfor (int i = 0; i < pairs; i++) {\n\t\t\t\tkeys[i] = reader.readS4();\n\t\t\t\tint target = insnOffset + reader.readS4();\n\t\t\t\ttargets[i] = target;\n\t\t\t\tstate.registerJump(target);\n\t\t\t}\n\t\t\tinsn.setTarget(defTarget);\n\t\t\tstate.registerJump(defTarget);\n\t\t\tinsn.setPayload(new SwitchPayload(pairs, keys, targets));\n\t\t}\n\t\tinsn.setPayloadSize(reader.getOffset() - dataOffset);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/decoders/TableSwitchDecoder.java",
    "content": "package jadx.plugins.input.java.data.code.decoders;\n\nimport jadx.api.plugins.input.insns.custom.impl.SwitchPayload;\nimport jadx.plugins.input.java.data.DataReader;\nimport jadx.plugins.input.java.data.code.CodeDecodeState;\nimport jadx.plugins.input.java.data.code.JavaInsnData;\n\npublic class TableSwitchDecoder implements IJavaInsnDecoder {\n\n\t@Override\n\tpublic void decode(CodeDecodeState state) {\n\t\tread(state, false);\n\t}\n\n\t@Override\n\tpublic void skip(CodeDecodeState state) {\n\t\tread(state, true);\n\t}\n\n\tprivate static void read(CodeDecodeState state, boolean skip) {\n\t\tDataReader reader = state.reader();\n\t\tJavaInsnData insn = state.insn();\n\t\tint dataOffset = reader.getOffset();\n\t\tint insnOffset = insn.getOffset();\n\t\treader.skip(3 - insnOffset % 4);\n\t\tint defTarget = insnOffset + reader.readS4();\n\t\tint low = reader.readS4();\n\t\tint high = reader.readS4();\n\t\tint count = high - low + 1;\n\t\tif (skip) {\n\t\t\treader.skip(count * 4);\n\t\t} else {\n\t\t\tstate.pop(0);\n\t\t\tint[] keys = new int[count];\n\t\t\tint[] targets = new int[count];\n\t\t\tfor (int i = 0; i < count; i++) {\n\t\t\t\tint target = insnOffset + reader.readS4();\n\t\t\t\tkeys[i] = low + i;\n\t\t\t\ttargets[i] = target;\n\t\t\t\tstate.registerJump(target);\n\t\t\t}\n\t\t\tinsn.setTarget(defTarget);\n\t\t\tstate.registerJump(defTarget);\n\t\t\tinsn.setPayload(new SwitchPayload(count, keys, targets));\n\t\t}\n\t\tinsn.setPayloadSize(reader.getOffset() - dataOffset);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/decoders/WideDecoder.java",
    "content": "package jadx.plugins.input.java.data.code.decoders;\n\nimport jadx.api.plugins.input.insns.Opcode;\nimport jadx.plugins.input.java.data.DataReader;\nimport jadx.plugins.input.java.data.code.CodeDecodeState;\nimport jadx.plugins.input.java.data.code.JavaInsnData;\nimport jadx.plugins.input.java.utils.JavaClassParseException;\n\npublic class WideDecoder implements IJavaInsnDecoder {\n\tprivate static final int IINC = 0x84;\n\n\t@Override\n\tpublic void decode(CodeDecodeState state) {\n\t\tDataReader reader = state.reader();\n\t\tJavaInsnData insn = state.insn();\n\t\tint opcode = reader.readU1();\n\t\tif (opcode == IINC) {\n\t\t\tint varNum = reader.readU2();\n\t\t\tint constValue = reader.readS2();\n\t\t\tstate.local(0, varNum).local(1, varNum).lit(constValue);\n\t\t\tinsn.setPayloadSize(5);\n\t\t\tinsn.setRegsCount(2);\n\t\t\tinsn.setOpcode(Opcode.ADD_INT_LIT);\n\t\t\treturn;\n\t\t}\n\t\tint index = reader.readU2();\n\t\tswitch (opcode) {\n\t\t\tcase 0x15: // iload,\n\t\t\tcase 0x17: // fload\n\t\t\tcase 0x19: // aload\n\t\t\t\tstate.local(1, index).push(0);\n\t\t\t\tbreak;\n\n\t\t\tcase 0x16: // lload\n\t\t\tcase 0x18: // dload\n\t\t\t\tstate.local(1, index).pushWide(0);\n\t\t\t\tbreak;\n\n\t\t\tcase 0x36:\n\t\t\tcase 0x37:\n\t\t\tcase 0x38:\n\t\t\tcase 0x39:\n\t\t\tcase 0x3a:\n\t\t\t\t// *store\n\t\t\t\tstate.pop(1).local(0, index);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthrow new JavaClassParseException(\"Unexpected opcode in 'wide': 0x\" + Integer.toHexString(opcode));\n\t\t}\n\t\tinsn.setPayloadSize(3);\n\t\tinsn.setRegsCount(2);\n\t\tinsn.setOpcode(Opcode.MOVE);\n\t}\n\n\t@Override\n\tpublic void skip(CodeDecodeState state) {\n\t\tDataReader reader = state.reader();\n\t\tJavaInsnData insn = state.insn();\n\t\tint opcode = reader.readU1();\n\t\tif (opcode == IINC) {\n\t\t\treader.skip(4);\n\t\t\tinsn.setPayloadSize(5);\n\t\t} else {\n\t\t\treader.skip(2);\n\t\t\tinsn.setPayloadSize(3);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/trycatch/JavaSingleCatch.java",
    "content": "package jadx.plugins.input.java.data.code.trycatch;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic class JavaSingleCatch {\n\tprivate final int handler;\n\tprivate final @Nullable String type;\n\n\tpublic JavaSingleCatch(int handler, @Nullable String type) {\n\t\tthis.handler = handler;\n\t\tthis.type = type;\n\t}\n\n\tpublic int getHandler() {\n\t\treturn handler;\n\t}\n\n\tpublic @Nullable String getType() {\n\t\treturn type;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/code/trycatch/JavaTryData.java",
    "content": "package jadx.plugins.input.java.data.code.trycatch;\n\nimport jadx.api.plugins.input.data.ICatch;\nimport jadx.api.plugins.input.data.ITry;\nimport jadx.api.plugins.utils.Utils;\n\npublic class JavaTryData implements ITry {\n\n\tprivate final int startOffset;\n\tprivate final int endOffset;\n\tprivate ICatch catchHandler;\n\n\tpublic JavaTryData(int startOffset, int endOffset) {\n\t\tthis.startOffset = startOffset;\n\t\tthis.endOffset = endOffset;\n\t}\n\n\t@Override\n\tpublic ICatch getCatch() {\n\t\treturn catchHandler;\n\t}\n\n\tpublic void setCatch(ICatch catchHandler) {\n\t\tthis.catchHandler = catchHandler;\n\t}\n\n\t@Override\n\tpublic int getStartOffset() {\n\t\treturn startOffset;\n\t}\n\n\t@Override\n\tpublic int getEndOffset() {\n\t\treturn endOffset;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn startOffset + 31 * endOffset;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(o instanceof JavaTryData)) {\n\t\t\treturn false;\n\t\t}\n\t\tJavaTryData that = (JavaTryData) o;\n\t\treturn startOffset == that.startOffset && endOffset == that.endOffset;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Try{\" + Utils.formatOffset(startOffset) + \" - \" + Utils.formatOffset(endOffset) + \": \" + catchHandler + '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/utils/DescriptorParser.java",
    "content": "package jadx.plugins.input.java.utils;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport jadx.plugins.input.java.data.JavaMethodProto;\n\npublic class DescriptorParser {\n\n\tpublic static void fillMethodProto(String mthDesc, JavaMethodProto mthProto) {\n\t\tnew DescriptorParser(mthDesc).parseMethodDescriptor(mthProto);\n\t}\n\n\tpublic static JavaMethodProto parseToMethodProto(String mthDesc) {\n\t\tJavaMethodProto mthProto = new JavaMethodProto();\n\t\tnew DescriptorParser(mthDesc).parseMethodDescriptor(mthProto);\n\t\treturn mthProto;\n\t}\n\n\tprivate final String desc;\n\tprivate int pos;\n\n\tprivate DescriptorParser(String desc) {\n\t\tthis.desc = desc;\n\t}\n\n\tprivate void parseMethodDescriptor(JavaMethodProto mthProto) {\n\t\tvalidate('(');\n\t\tif (check(')')) {\n\t\t\tmthProto.setArgTypes(Collections.emptyList());\n\t\t} else {\n\t\t\tmthProto.setArgTypes(readArgsList());\n\t\t}\n\t\tvalidate(')');\n\t\tmthProto.setReturnType(readType());\n\t}\n\n\tprivate List<String> readArgsList() {\n\t\tList<String> list = new ArrayList<>(5);\n\t\tdo {\n\t\t\tlist.add(readType());\n\t\t} while (!check(')'));\n\t\treturn list;\n\t}\n\n\tprivate String readType() {\n\t\tint cur = pos;\n\t\tif (cur >= desc.length()) {\n\t\t\treturn null;\n\t\t}\n\t\tchar ch = desc.charAt(cur);\n\t\tswitch (ch) {\n\t\t\tcase 'L':\n\t\t\t\tint end = desc.indexOf(';', cur);\n\t\t\t\tif (end == -1) {\n\t\t\t\t\tthrow new JavaClassParseException(\"Unexpected object type descriptor: \" + desc);\n\t\t\t\t}\n\t\t\t\tint lastChar = end + 1;\n\t\t\t\tString type = desc.substring(cur, lastChar);\n\t\t\t\tpos = lastChar;\n\t\t\t\treturn type;\n\n\t\t\tcase '[':\n\t\t\t\tpos++;\n\t\t\t\treturn \"[\" + readType();\n\n\t\t\tdefault:\n\t\t\t\tString primitiveType = parsePrimitiveType(ch);\n\t\t\t\tpos = cur + 1;\n\t\t\t\treturn primitiveType;\n\t\t}\n\t}\n\n\tpublic String parsePrimitiveType(char f) {\n\t\tswitch (f) {\n\t\t\tcase 'Z':\n\t\t\t\treturn \"Z\";\n\t\t\tcase 'B':\n\t\t\t\treturn \"B\";\n\t\t\tcase 'C':\n\t\t\t\treturn \"C\";\n\t\t\tcase 'S':\n\t\t\t\treturn \"S\";\n\t\t\tcase 'I':\n\t\t\t\treturn \"I\";\n\t\t\tcase 'J':\n\t\t\t\treturn \"J\";\n\t\t\tcase 'F':\n\t\t\t\treturn \"F\";\n\t\t\tcase 'D':\n\t\t\t\treturn \"D\";\n\t\t\tcase 'V':\n\t\t\t\treturn \"V\";\n\n\t\t\tdefault:\n\t\t\t\tthrow new JavaClassParseException(\"Unexpected char '\" + f + \"' in descriptor \" + desc);\n\t\t}\n\t}\n\n\tprivate boolean check(char exp) {\n\t\treturn desc.charAt(pos) == exp;\n\t}\n\n\tprivate void validate(char exp) {\n\t\tif (!check(exp)) {\n\t\t\tthrow new JavaClassParseException(\"Unexpected char in descriptor: \" + desc + \" at pos \" + pos + \", expected: \" + exp);\n\t\t}\n\t\tpos++;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/utils/DisasmUtils.java",
    "content": "package jadx.plugins.input.java.utils;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.StandardOpenOption;\nimport java.util.concurrent.TimeUnit;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.github.skylot.raung.disasm.RaungDisasm;\n\npublic class DisasmUtils {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(DisasmUtils.class);\n\n\tpublic static String get(byte[] bytes) {\n\t\treturn useRaung(bytes);\n\t}\n\n\tprivate static String useRaung(byte[] bytes) {\n\t\treturn RaungDisasm.create().executeForBytes(bytes);\n\t}\n\n\t/**\n\t * Use javap as a temporary disassembler for java bytecode\n\t * Don't remove! Useful for debug.\n\t */\n\tprivate static String useSystemJavaP(byte[] bytes) {\n\t\ttry {\n\t\t\tPath tmpCls = null;\n\t\t\ttry {\n\t\t\t\ttmpCls = Files.createTempFile(\"jadx\", \".class\");\n\t\t\t\tFiles.write(tmpCls, bytes, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);\n\t\t\t\tProcess process = Runtime.getRuntime().exec(new String[] {\n\t\t\t\t\t\t\"javap\", \"-constants\", \"-v\", \"-p\", \"-c\",\n\t\t\t\t\t\ttmpCls.toAbsolutePath().toString()\n\t\t\t\t});\n\t\t\t\tprocess.waitFor(2, TimeUnit.SECONDS);\n\t\t\t\treturn inputStreamToString(process.getInputStream());\n\t\t\t} finally {\n\t\t\t\tif (tmpCls != null) {\n\t\t\t\t\tFiles.delete(tmpCls);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Java class disasm error\", e);\n\t\t\treturn \"error\";\n\t\t}\n\t}\n\n\tpublic static String inputStreamToString(InputStream in) throws IOException {\n\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\tbyte[] buf = new byte[8 * 1024];\n\t\twhile (true) {\n\t\t\tint r = in.read(buf);\n\t\t\tif (r == -1) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tout.write(buf, 0, r);\n\t\t}\n\t\treturn out.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/utils/JavaClassParseException.java",
    "content": "package jadx.plugins.input.java.utils;\n\npublic class JavaClassParseException extends RuntimeException {\n\tprivate static final long serialVersionUID = -8452845601753645491L;\n\n\tpublic JavaClassParseException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n\tpublic JavaClassParseException(String message) {\n\t\tsuper(message);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/utils/ModifiedUTF8Decoder.java",
    "content": "package jadx.plugins.input.java.utils;\n\nimport java.nio.charset.StandardCharsets;\n\npublic class ModifiedUTF8Decoder {\n\n\tpublic static String decodeString(byte[] bytes) {\n\t\tint len = bytes.length;\n\t\t// quick check if all chars are 7-bit\n\t\tboolean asciiStr = true;\n\t\tfor (byte b : bytes) {\n\t\t\tif ((b & 0x80) != 0) {\n\t\t\t\tasciiStr = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (asciiStr) {\n\t\t\treturn new String(bytes, StandardCharsets.US_ASCII);\n\t\t}\n\n\t\t// parse modified UTF-8 according jvms-4.4.7\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tint x = bytes[i] & 0xff;\n\t\t\t// 4.4 ascii characters 1-127 (0 is encoded as 0xc0 0x80)\n\t\t\tif ((x & 0x80) == 0) {\n\t\t\t\t// 1 byte 7-Bit ascii (Table 4.4./4.5)\n\t\t\t\tsb.append((char) x);\n\t\t\t} else {\n\t\t\t\tif (i + 1 >= len) {\n\t\t\t\t\tthrow new JavaClassParseException(\"Inconsistent byte array structure: too short\");\n\t\t\t\t}\n\t\t\t\tint y = bytes[i + 1] & 0xff;\n\t\t\t\t// 0 is encoded as 0xc0 0x80 (jvms-4.4.7)\n\t\t\t\tif (x == 0xc0 && y == 0x80) {\n\t\t\t\t\tsb.appendCodePoint(0);\n\t\t\t\t\ti++;\n\t\t\t\t} else if ((x & 0xE0) == 0xC0 && (y & 0xC0) == 0x80) {\n\t\t\t\t\t// 2 byte char (Table 4.8./4.9 )\n\t\t\t\t\tsb.appendCodePoint(((x & 0x1f) << 6) + (y & 0x3f));\n\t\t\t\t\ti++;\n\t\t\t\t} else if (i + 2 < len) {\n\t\t\t\t\tint z = bytes[i + 2] & 0xff;\n\t\t\t\t\tif ((x & 0xF0) == 0xE0 && (y & 0xC0) == 0x80 && (z & 0xC0) == 0x80) {\n\t\t\t\t\t\t// 3 byte char (Table 4.11/4.12)\n\t\t\t\t\t\tsb.appendCodePoint(((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f));\n\t\t\t\t\t\ti += 2;\n\t\t\t\t\t} else if (i + 5 < len\n\t\t\t\t\t\t\t&& x == 0xED // u\n\t\t\t\t\t\t\t&& (y & 0xF0) == 0xA0 // v\n\t\t\t\t\t\t\t&& (bytes[i + 3] & 0xff) == 0xED // x\n\t\t\t\t\t\t\t&& (bytes[i + 4] & 0xF0) == 0xA0 // y\n\t\t\t\t\t) {\n\t\t\t\t\t\t// 6 byte encoded Table 4.12.\n\t\t\t\t\t\tint u = x; // 0\n\t\t\t\t\t\tint v = y; // 1\n\t\t\t\t\t\tint w = z; // 2\n\t\t\t\t\t\tx = bytes[i + 3] & 0xff;\n\t\t\t\t\t\ty = bytes[i + 4] & 0xff;\n\t\t\t\t\t\tz = bytes[i + 5] & 0xff;\n\t\t\t\t\t\tif (x == 0xED && (y & 0xF0) == 0xA0) {\n\t\t\t\t\t\t\tsb.appendCodePoint(0x10000 + ((v & 0x0f) << 16) + ((w & 0x3f) << 10) + ((y & 0x0f) << 6) + (z & 0x3f));\n\t\t\t\t\t\t\ti += 5;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow new JavaClassParseException(\"Inconsistent byte array structure: invalid 6 bytes char\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new JavaClassParseException(\"Inconsistent byte array structure: unexpected char\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin",
    "content": "jadx.plugins.input.java.JavaInputPlugin\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/test/java/jadx/plugins/input/java/CustomLoadTest.java",
    "content": "package jadx.plugins.input.java;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.JadxDecompiler;\nimport jadx.api.plugins.input.ICodeLoader;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\nclass CustomLoadTest {\n\n\tprivate JadxDecompiler jadx;\n\n\t@BeforeEach\n\tvoid init() {\n\t\tjadx = new JadxDecompiler(new JadxArgs());\n\t}\n\n\t@AfterEach\n\tvoid close() {\n\t\tjadx.close();\n\t}\n\n\t@Test\n\tvoid loadFiles() {\n\t\tList<Path> files = Stream.of(\"HelloWorld.class\", \"HelloWorld$HelloInner.class\")\n\t\t\t\t.map(this::getSample)\n\t\t\t\t.collect(Collectors.toList());\n\t\tICodeLoader loadResult = JavaInputPlugin.loadClassFiles(files);\n\t\tloadDecompiler(loadResult);\n\t\tassertThat(jadx.getClassesWithInners())\n\t\t\t\t.hasSize(2)\n\t\t\t\t.satisfiesOnlyOnce(cls -> assertThat(cls.getName()).isEqualTo(\"HelloWorld\"))\n\t\t\t\t.satisfiesOnlyOnce(cls -> assertThat(cls.getName()).isEqualTo(\"HelloInner\"));\n\t}\n\n\t@Test\n\tvoid loadFromInputStream() throws IOException {\n\t\tString fileName = \"HelloWorld$HelloInner.class\";\n\t\ttry (InputStream in = Files.newInputStream(getSample(fileName))) {\n\t\t\tICodeLoader loadResult = JavaInputPlugin.loadFromInputStream(in, fileName);\n\t\t\tloadDecompiler(loadResult);\n\t\t\tassertThat(jadx.getClassesWithInners())\n\t\t\t\t\t.hasSize(1)\n\t\t\t\t\t.satisfiesOnlyOnce(cls -> assertThat(cls.getName()).isEqualTo(\"HelloWorld$HelloInner\"));\n\n\t\t\tSystem.out.println(jadx.getClassesWithInners().get(0).getCode());\n\t\t}\n\t}\n\n\t@Test\n\tvoid loadSingleClass() throws IOException {\n\t\tString fileName = \"HelloWorld.class\";\n\t\tbyte[] content = Files.readAllBytes(getSample(fileName));\n\t\tICodeLoader loadResult = JavaInputPlugin.loadSingleClass(content, fileName);\n\t\tloadDecompiler(loadResult);\n\t\tassertThat(jadx.getClassesWithInners())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.satisfiesOnlyOnce(cls -> assertThat(cls.getName()).isEqualTo(\"HelloWorld\"));\n\n\t\tSystem.out.println(jadx.getClassesWithInners().get(0).getCode());\n\t}\n\n\t@Test\n\tvoid load() {\n\t\tICodeLoader loadResult = JavaInputPlugin.load(loader -> {\n\t\t\tList<JavaClassReader> inputs = new ArrayList<>(2);\n\t\t\ttry {\n\t\t\t\tString hello = \"HelloWorld.class\";\n\t\t\t\tbyte[] content = Files.readAllBytes(getSample(hello));\n\t\t\t\tinputs.add(loader.loadClass(content, hello));\n\n\t\t\t\tString helloInner = \"HelloWorld$HelloInner.class\";\n\t\t\t\tInputStream in = Files.newInputStream(getSample(helloInner));\n\t\t\t\tinputs.addAll(loader.loadInputStream(in, helloInner));\n\t\t\t} catch (Exception e) {\n\t\t\t\tfail(e);\n\t\t\t}\n\t\t\treturn inputs;\n\t\t});\n\t\tloadDecompiler(loadResult);\n\t\tassertThat(jadx.getClassesWithInners())\n\t\t\t\t.hasSize(2)\n\t\t\t\t.satisfiesOnlyOnce(cls -> assertThat(cls.getName()).isEqualTo(\"HelloWorld\"))\n\t\t\t\t.satisfiesOnlyOnce(cls -> {\n\t\t\t\t\tassertThat(cls.getName()).isEqualTo(\"HelloInner\");\n\t\t\t\t\tassertThat(cls.getCode()).isEmpty(); // no code for moved inner class\n\t\t\t\t});\n\n\t\tassertThat(jadx.getClasses())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.satisfiesOnlyOnce(cls -> assertThat(cls.getName()).isEqualTo(\"HelloWorld\"))\n\t\t\t\t.satisfiesOnlyOnce(cls -> assertThat(cls.getInnerClasses()).hasSize(1)\n\t\t\t\t\t\t.satisfiesOnlyOnce(inner -> assertThat(inner.getName()).isEqualTo(\"HelloInner\")));\n\n\t\tjadx.getClassesWithInners().forEach(cls -> System.out.println(cls.getCode()));\n\t}\n\n\tpublic void loadDecompiler(ICodeLoader codeLoader) {\n\t\ttry {\n\t\t\tjadx.addCustomCodeLoader(codeLoader);\n\t\t\tjadx.load();\n\t\t} catch (Exception e) {\n\t\t\tfail(\"Failed to load sample\", e);\n\t\t}\n\t}\n\n\tpublic Path getSample(String name) {\n\t\ttry {\n\t\t\treturn Paths.get(ClassLoader.getSystemResource(\"samples/\" + name).toURI());\n\t\t} catch (Exception e) {\n\t\t\treturn fail(\"Failed to load sample\", e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/test/java/jadx/plugins/input/java/utils/DescriptorParserTest.java",
    "content": "package jadx.plugins.input.java.utils;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.plugins.input.java.data.JavaMethodRef;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\nclass DescriptorParserTest {\n\n\t@Test\n\tpublic void testPrimitives() {\n\t\tcheck(\"()V\", \"V\");\n\t\tcheck(\"(I)D\", \"D\", \"I\");\n\t}\n\n\t@Test\n\tpublic void testObjects() {\n\t\tcheck(\"(Ljava/lang/String;Ljava/lang/Object;)V\", \"V\", \"Ljava/lang/String;\", \"Ljava/lang/Object;\");\n\t}\n\n\t@SuppressWarnings(\"CatchMayIgnoreException\")\n\tprivate void check(String desc, String retType, String... argTypes) {\n\t\tJavaMethodRef mthRef = new JavaMethodRef();\n\t\ttry {\n\t\t\tDescriptorParser.fillMethodProto(desc, mthRef);\n\t\t} catch (Exception e) {\n\t\t\tfail(\"Parse failed for: \" + desc, e);\n\t\t}\n\n\t\tassertThat(mthRef.getReturnType()).isEqualTo(retType);\n\t\tassertThat(mthRef.getArgTypes()).isEqualTo(Arrays.asList(argTypes));\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-java-input/src/test/java/jadx/plugins/input/java/utils/ModifiedUTF8DecoderTest.java",
    "content": "package jadx.plugins.input.java.utils;\n\nimport org.junit.jupiter.api.Test;\n\nimport static jadx.plugins.input.java.utils.ModifiedUTF8Decoder.decodeString;\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/*\n * TODO: find a way to enter 6-bytes char decode branch\n */\nclass ModifiedUTF8DecoderTest {\n\n\t@Test\n\tpublic void test() {\n\t\tString str = \"aÆřᛒቶ北𝄠😀🨄𐆙\";\n\t\tbyte[] mUTF8Bytes = new byte[] { 97, -61, -122, -59, -103, -31, -101, -110, -31, -119, -74, -17,\n\t\t\t\t-91, -93, -19, -96, -76, -19, -76, -96, -19, -96, -67, -19, -72,\n\t\t\t\t-128, -19, -96, -66, -19, -72, -124, -19, -96, -128, -19, -74, -103 };\n\t\tassertThat(decodeString(mUTF8Bytes)).isEqualTo(str);\n\t}\n\n\t@Test\n\tpublic void testASCIIOnly() {\n\t\tString str = \"Hello, world!\";\n\t\tbyte[] mUTF8Bytes = new byte[] { 72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33 };\n\t\tassertThat(decodeString(mUTF8Bytes)).isEqualTo(str);\n\t}\n\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-library\")\n\tid(\"jadx-kotlin\")\n}\n\ndependencies {\n\tapi(project(\":jadx-core\"))\n\n\timplementation(\"org.jetbrains.kotlin:kotlin-metadata-jvm:2.3.10\")\n\n\ttestImplementation(project.project(\":jadx-core\").sourceSets.getByName(\"test\").output)\n\ttestImplementation(\"org.apache.commons:commons-lang3:3.20.0\")\n\n\ttestRuntimeOnly(project(\":jadx-plugins:jadx-smali-input\"))\n\ttestRuntimeOnly(project(\":jadx-plugins:jadx-java-input\"))\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/main/kotlin/jadx/plugins/kotlin/metadata/KotlinMetadataOptions.kt",
    "content": "package jadx.plugins.kotlin.metadata\n\nimport jadx.api.plugins.options.impl.BasePluginOptionsBuilder\nimport jadx.plugins.kotlin.metadata.KotlinMetadataPlugin.Companion.PLUGIN_ID\n\nclass KotlinMetadataOptions : BasePluginOptionsBuilder() {\n\tvar isClassAlias: Boolean = true\n\t\tprivate set\n\tvar isMethodArgs: Boolean = true\n\t\tprivate set\n\tvar isFields: Boolean = true\n\t\tprivate set\n\tvar isCompanion: Boolean = true\n\t\tprivate set\n\tvar isDataClass: Boolean = true\n\t\tprivate set\n\tvar isToString: Boolean = true\n\t\tprivate set\n\tvar isGetters: Boolean = true\n\t\tprivate set\n\n\toverride fun registerOptions() {\n\t\tboolOption(CLASS_ALIAS_OPT)\n\t\t\t.description(\"rename class alias\")\n\t\t\t.defaultValue(true)\n\t\t\t.setter { isClassAlias = it }\n\n\t\tboolOption(METHOD_ARGS_OPT)\n\t\t\t.description(\"rename function arguments\")\n\t\t\t.defaultValue(true)\n\t\t\t.setter { isMethodArgs = it }\n\n\t\tboolOption(FIELDS_OPT)\n\t\t\t.description(\"rename fields\")\n\t\t\t.defaultValue(true)\n\t\t\t.setter { isFields = it }\n\n\t\tboolOption(COMPANION_OPT)\n\t\t\t.description(\"rename companion object\")\n\t\t\t.defaultValue(true)\n\t\t\t.setter { isCompanion = it }\n\n\t\tboolOption(DATA_CLASS_OPT)\n\t\t\t.description(\"add data class modifier\")\n\t\t\t.defaultValue(true)\n\t\t\t.setter { isDataClass = it }\n\n\t\tboolOption(TO_STRING_OPT)\n\t\t\t.description(\"rename fields using toString\")\n\t\t\t.defaultValue(true)\n\t\t\t.setter { isToString = it }\n\n\t\tboolOption(GETTERS_OPT)\n\t\t\t.description(\"rename simple getters to field names\")\n\t\t\t.defaultValue(true)\n\t\t\t.setter { isGetters = it }\n\t}\n\n\tfun isPreparePassNeeded(): Boolean {\n\t\treturn isClassAlias\n\t}\n\n\tfun isDecompilePassNeeded(): Boolean {\n\t\treturn isMethodArgs || isFields || isCompanion || isDataClass || isToString || isGetters\n\t}\n\n\tcompanion object {\n\t\tconst val CLASS_ALIAS_OPT = \"$PLUGIN_ID.class-alias\"\n\t\tconst val METHOD_ARGS_OPT = \"$PLUGIN_ID.method-args\"\n\t\tconst val FIELDS_OPT = \"$PLUGIN_ID.fields\"\n\t\tconst val COMPANION_OPT = \"$PLUGIN_ID.companion\"\n\t\tconst val DATA_CLASS_OPT = \"$PLUGIN_ID.data-class\"\n\t\tconst val TO_STRING_OPT = \"$PLUGIN_ID.to-string\"\n\t\tconst val GETTERS_OPT = \"$PLUGIN_ID.getters\"\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/main/kotlin/jadx/plugins/kotlin/metadata/KotlinMetadataPlugin.kt",
    "content": "package jadx.plugins.kotlin.metadata\n\nimport jadx.api.plugins.JadxPlugin\nimport jadx.api.plugins.JadxPluginContext\nimport jadx.api.plugins.JadxPluginInfo\nimport jadx.plugins.kotlin.metadata.pass.KotlinMetadataDecompilePass\nimport jadx.plugins.kotlin.metadata.pass.KotlinMetadataPreparePass\n\nclass KotlinMetadataPlugin : JadxPlugin {\n\n\tprivate val options = KotlinMetadataOptions()\n\n\toverride fun getPluginInfo(): JadxPluginInfo {\n\t\treturn JadxPluginInfo(PLUGIN_ID, \"Kotlin Metadata\", \"Use kotlin.Metadata annotation for code generation\")\n\t}\n\n\toverride fun init(context: JadxPluginContext) {\n\t\tcontext.registerOptions(options)\n\t\tif (options.isPreparePassNeeded()) {\n\t\t\tcontext.addPass(KotlinMetadataPreparePass(options))\n\t\t}\n\t\tif (options.isDecompilePassNeeded()) {\n\t\t\tcontext.addPass(KotlinMetadataDecompilePass(options))\n\t\t}\n\t}\n\n\tcompanion object {\n\t\tconst val PLUGIN_ID = \"kotlin-metadata\"\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/main/kotlin/jadx/plugins/kotlin/metadata/model/KotlinMetadataConsts.kt",
    "content": "package jadx.plugins.kotlin.metadata.model\n\nobject KotlinMetadataConsts {\n\tconst val KOTLIN_METADATA_ANNOTATION = \"Lkotlin/Metadata;\"\n\tconst val KOTLIN_METADATA_K_PARAMETER = \"k\"\n\tconst val KOTLIN_METADATA_D1_PARAMETER = \"d1\"\n\tconst val KOTLIN_METADATA_D2_PARAMETER = \"d2\"\n\tconst val KOTLIN_METADATA_MV_PARAMETER = \"mv\"\n\tconst val KOTLIN_METADATA_XS_PARAMETER = \"xs\"\n\tconst val KOTLIN_METADATA_PN_PARAMETER = \"pn\"\n\tconst val KOTLIN_METADATA_XI_PARAMETER = \"xi\"\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/main/kotlin/jadx/plugins/kotlin/metadata/model/KotlinRenameResults.kt",
    "content": "package jadx.plugins.kotlin.metadata.model\n\nimport jadx.core.dex.instructions.args.RegisterArg\nimport jadx.core.dex.nodes.ClassNode\nimport jadx.core.dex.nodes.FieldNode\nimport jadx.core.dex.nodes.MethodNode\n\ndata class ClassAliasRename(\n\tval pkg: String,\n\tval name: String,\n)\n\ndata class MethodArgRename(\n\tval rArg: RegisterArg,\n\tval alias: String,\n)\n\ndata class FieldRename(\n\tval field: FieldNode,\n\tval alias: String,\n)\n\ndata class CompanionRename(\n\tval field: FieldNode,\n\tval cls: ClassNode,\n\tval hide: Boolean,\n)\n\ndata class ToStringRename(\n\tval cls: ClassNode,\n\tval clsAlias: String?,\n\tval fields: List<FieldRename>,\n)\n\ndata class MethodRename(\n\tval mth: MethodNode,\n\tval alias: String,\n)\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/main/kotlin/jadx/plugins/kotlin/metadata/pass/KotlinMetadataDecompilePass.kt",
    "content": "package jadx.plugins.kotlin.metadata.pass\n\nimport jadx.api.plugins.input.data.AccessFlags\nimport jadx.api.plugins.pass.JadxPassInfo\nimport jadx.api.plugins.pass.impl.OrderedJadxPassInfo\nimport jadx.api.plugins.pass.types.JadxDecompilePass\nimport jadx.core.deobf.NameMapper\nimport jadx.core.dex.attributes.AFlag\nimport jadx.core.dex.attributes.nodes.RenameReasonAttr\nimport jadx.core.dex.nodes.ClassNode\nimport jadx.core.dex.nodes.MethodNode\nimport jadx.core.dex.nodes.RootNode\nimport jadx.plugins.kotlin.metadata.KotlinMetadataOptions\nimport jadx.plugins.kotlin.metadata.utils.KmClassWrapper\nimport jadx.plugins.kotlin.metadata.utils.KmClassWrapper.Companion.getWrapper\n\nclass KotlinMetadataDecompilePass(\n\tprivate val options: KotlinMetadataOptions,\n) : JadxDecompilePass {\n\n\toverride fun getInfo(): JadxPassInfo {\n\t\treturn OrderedJadxPassInfo(\n\t\t\t\"KotlinMetadataDecompile\",\n\t\t\t\"Use kotlin.Metadata annotation perform various renames\",\n\t\t)\n\t\t\t.before(\"CodeRenameVisitor\")\n\t}\n\n\toverride fun init(root: RootNode) {\n\t}\n\n\toverride fun visit(cls: ClassNode): Boolean {\n\t\tcls.innerClasses.forEach(::visit)\n\n\t\tval wrapper = cls.getWrapper() ?: return false\n\t\tif (options.isMethodArgs) renameMethodArgs(wrapper)\n\t\tif (options.isFields) renameFields(wrapper)\n\t\tif (options.isCompanion) renameCompanion(wrapper)\n\t\tif (options.isDataClass) fixDataClass(wrapper)\n\t\tif (options.isToString) renameToString(wrapper)\n\t\tif (options.isGetters) renameGetters(wrapper)\n\n\t\treturn false\n\t}\n\n\toverride fun visit(mth: MethodNode?) {\n\t\t/* no op */\n\t}\n\n\tprivate fun renameMethodArgs(wrapper: KmClassWrapper) {\n\t\tval args = wrapper.getMethodArgs()\n\t\targs.forEach { (_, list) ->\n\t\t\tlist.forEach { (rArg, alias) ->\n\t\t\t\t// TODO: comment not being added?\n\t\t\t\tRenameReasonAttr.forNode(rArg).append(METADATA_REASON)\n\t\t\t\trArg.name = alias\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate fun renameFields(wrapper: KmClassWrapper) {\n\t\tval fields = wrapper.getFields()\n\t\tfields.forEach { (field, alias) ->\n\t\t\tif (AFlag.DONT_RENAME !in field) {\n\t\t\t\tRenameReasonAttr.forNode(field).append(METADATA_REASON)\n\t\t\t\tfield.rename(alias)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate fun renameCompanion(wrapper: KmClassWrapper) {\n\t\tval companion = wrapper.getCompanion()\n\t\tcompanion?.run {\n\t\t\tif (AFlag.DONT_RENAME !in field) {\n\t\t\t\tRenameReasonAttr.forNode(field).append(METADATA_REASON)\n\t\t\t\tfield.rename(COMPANION_FIELD)\n\t\t\t}\n\t\t\tif (AFlag.DONT_RENAME !in cls) {\n\t\t\t\tRenameReasonAttr.forNode(cls).append(METADATA_REASON)\n\t\t\t\tcls.rename(COMPANION_CLASS)\n\t\t\t}\n\n\t\t\tif (hide) {\n\t\t\t\tfield.add(AFlag.DONT_GENERATE)\n\t\t\t\tcls.add(AFlag.DONT_GENERATE)\n\t\t\t\tcls.add(AFlag.DONT_INLINE)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate fun fixDataClass(wrapper: KmClassWrapper) {\n\t\tval isData = wrapper.isDataClass()\n\t\twrapper.cls.run {\n\t\t\tif (isData != accessFlags.isData) {\n\t\t\t\taccessFlags = accessFlags.run {\n\t\t\t\t\tif (isData) {\n\t\t\t\t\t\tadd(AccessFlags.DATA)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tremove(AccessFlags.DATA)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate fun renameToString(wrapper: KmClassWrapper) {\n\t\tval toString = wrapper.parseToString()\n\t\ttoString?.run {\n\t\t\tclsAlias?.let { alias ->\n\t\t\t\tif (NameMapper.isValidIdentifier(alias) && AFlag.DONT_RENAME !in cls) {\n\t\t\t\t\tRenameReasonAttr.forNode(cls).append(TO_STRING_REASON)\n\t\t\t\t\tcls.rename(alias)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfields.forEach { (field, alias) ->\n\t\t\t\tif (NameMapper.isValidIdentifier(alias) && AFlag.DONT_RENAME !in field) {\n\t\t\t\t\tRenameReasonAttr.forNode(field).append(TO_STRING_REASON)\n\t\t\t\t\tfield.rename(alias)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate fun renameGetters(wrapper: KmClassWrapper) {\n\t\tval getters = wrapper.getGetters()\n\t\tgetters.forEach { (mth, alias) ->\n\t\t\tif (AFlag.DONT_RENAME !in mth) {\n\t\t\t\tRenameReasonAttr.forNode(mth).append(GETTER_REASON)\n\t\t\t\tmth.rename(alias)\n\t\t\t}\n\t\t}\n\t}\n\n\tcompanion object {\n\t\tprivate const val METADATA_REASON = \"from kotlin metadata\"\n\t\tprivate const val COMPANION_FIELD = \"INSTANCE\"\n\t\tprivate const val COMPANION_CLASS = \"Companion\"\n\t\tprivate const val TO_STRING_REASON = \"from toString\"\n\t\tprivate const val GETTER_REASON = \"from getter\"\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/main/kotlin/jadx/plugins/kotlin/metadata/pass/KotlinMetadataPreparePass.kt",
    "content": "package jadx.plugins.kotlin.metadata.pass\n\nimport jadx.api.plugins.pass.JadxPassInfo\nimport jadx.api.plugins.pass.impl.OrderedJadxPassInfo\nimport jadx.api.plugins.pass.types.JadxPreparePass\nimport jadx.core.dex.attributes.AFlag\nimport jadx.core.dex.nodes.RootNode\nimport jadx.plugins.kotlin.metadata.KotlinMetadataOptions\nimport jadx.plugins.kotlin.metadata.utils.KotlinMetadataUtils\n\nclass KotlinMetadataPreparePass(\n\tprivate val options: KotlinMetadataOptions,\n) : JadxPreparePass {\n\n\toverride fun getInfo(): JadxPassInfo {\n\t\treturn OrderedJadxPassInfo(\n\t\t\t\"KotlinMetadataPrepare\",\n\t\t\t\"Use kotlin.Metadata annotation to rename class & package\",\n\t\t)\n\t\t\t.before(\"RenameVisitor\")\n\t}\n\n\toverride fun init(root: RootNode) {\n\t\tif (options.isClassAlias) {\n\t\t\tfor (cls in root.classes) {\n\t\t\t\tif (cls.contains(AFlag.DONT_RENAME)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// rename class & package\n\t\t\t\tval kotlinCls = KotlinMetadataUtils.getAlias(cls)\n\t\t\t\tif (kotlinCls != null) {\n\t\t\t\t\tcls.rename(kotlinCls.name)\n\t\t\t\t\tcls.packageNode.rename(kotlinCls.pkg)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/main/kotlin/jadx/plugins/kotlin/metadata/utils/KmClassWrapper.kt",
    "content": "package jadx.plugins.kotlin.metadata.utils\n\nimport jadx.core.dex.nodes.ClassNode\nimport kotlin.metadata.KmClass\nimport kotlin.metadata.isData\nimport kotlin.metadata.jvm.KotlinClassMetadata\n\n// don't expose kotlinx.metadata.* types ?\nclass KmClassWrapper private constructor(\n\tval cls: ClassNode,\n\tprivate val kmCls: KmClass,\n) {\n\n\tfun getMethodArgs() =\n\t\tKotlinMetadataUtils.mapMethodArgs(cls, kmCls)\n\n\tfun getFields() =\n\t\tKotlinMetadataUtils.mapFields(cls, kmCls)\n\n\tfun getCompanion() =\n\t\tKotlinMetadataUtils.mapCompanion(cls, kmCls)\n\n\tfun isDataClass() =\n\t\tkmCls.isData\n\n\t// does not require metadata, may be useful for plain java ?\n\tfun parseToString() =\n\t\tKotlinUtils.parseToString(cls)\n\n\t// does not require metadata, may be useful for plain java ?\n\tfun getGetters() =\n\t\tKotlinUtils.findGetters(cls)\n\n\tcompanion object {\n\n\t\tfun ClassNode.getWrapper(): KmClassWrapper? {\n\t\t\tval metadata = getKotlinClassMetadata()\n\t\t\tval kmCls = (metadata as? KotlinClassMetadata.Class)?.kmClass ?: return null\n\t\t\treturn KmClassWrapper(this, kmCls)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/main/kotlin/jadx/plugins/kotlin/metadata/utils/KmExt.kt",
    "content": "package jadx.plugins.kotlin.metadata.utils\n\nimport kotlin.metadata.KmFunction\nimport kotlin.metadata.KmProperty\nimport kotlin.metadata.jvm.fieldSignature\nimport kotlin.metadata.jvm.signature\n\ninline val KmFunction.shortId: String? get() = signature?.toString()\n\ninline val KmProperty.shortId: String? get() = fieldSignature?.toString()\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/main/kotlin/jadx/plugins/kotlin/metadata/utils/KotlinMetadataExt.kt",
    "content": "@file:Suppress(\"UNCHECKED_CAST\")\n\npackage jadx.plugins.kotlin.metadata.utils\n\nimport jadx.api.plugins.input.data.annotations.EncodedType\nimport jadx.api.plugins.input.data.annotations.EncodedValue\nimport jadx.api.plugins.input.data.annotations.IAnnotation\nimport jadx.core.dex.nodes.ClassNode\nimport jadx.plugins.kotlin.metadata.model.KotlinMetadataConsts\nimport kotlin.metadata.jvm.KotlinClassMetadata\nimport kotlin.metadata.jvm.Metadata\n\nfun ClassNode.getMetadata(): Metadata? {\n\tval annotation: IAnnotation? = getAnnotation(KotlinMetadataConsts.KOTLIN_METADATA_ANNOTATION)\n\n\treturn annotation?.run {\n\t\tval k = getParamAsInt(KotlinMetadataConsts.KOTLIN_METADATA_K_PARAMETER)\n\t\tval mvArray = getParamAsIntArray(KotlinMetadataConsts.KOTLIN_METADATA_MV_PARAMETER)\n\t\tval d1Array = getParamAsStringArray(KotlinMetadataConsts.KOTLIN_METADATA_D1_PARAMETER)\n\t\tval d2Array = getParamAsStringArray(KotlinMetadataConsts.KOTLIN_METADATA_D2_PARAMETER)\n\t\tval xs = getParamAsString(KotlinMetadataConsts.KOTLIN_METADATA_XS_PARAMETER)\n\t\tval pn = getParamAsString(KotlinMetadataConsts.KOTLIN_METADATA_PN_PARAMETER)\n\t\tval xi = getParamAsInt(KotlinMetadataConsts.KOTLIN_METADATA_XI_PARAMETER)\n\n\t\tMetadata(\n\t\t\tkind = k,\n\t\t\tmetadataVersion = mvArray,\n\t\t\tdata1 = d1Array,\n\t\t\tdata2 = d2Array,\n\t\t\textraString = xs,\n\t\t\tpackageName = pn,\n\t\t\textraInt = xi,\n\t\t)\n\t}\n}\n\nprivate fun IAnnotation.getParamsAsList(paramName: String): List<EncodedValue>? {\n\tval encodedValue = values[paramName]\n\t\t?.takeIf { it.type == EncodedType.ENCODED_ARRAY && it.value is List<*> }\n\treturn encodedValue?.value?.let { it as List<EncodedValue> }\n}\n\nprivate fun IAnnotation.getParamAsStringArray(paramName: String): Array<String>? {\n\treturn getParamsAsList(paramName)\n\t\t?.map<EncodedValue, Any?>(EncodedValue::getValue)\n\t\t?.onEach { if (it != null && it !is String) return@onEach }\n\t\t?.map { \"$it\" }\n\t\t?.toTypedArray()\n}\n\nprivate fun IAnnotation.getParamAsIntArray(paramName: String): IntArray? {\n\treturn getParamsAsList(paramName)\n\t\t?.map<EncodedValue, Any?>(EncodedValue::getValue)\n\t\t?.map { it as Int }\n\t\t?.toIntArray()\n}\n\nprivate fun IAnnotation.getParamAsInt(paramName: String): Int? {\n\tval encodedValue = values[paramName]\n\t\t?.takeIf { it.type == EncodedType.ENCODED_INT && it.value is Int }\n\treturn encodedValue?.value?.let { it as Int }\n}\n\nprivate fun IAnnotation.getParamAsString(paramName: String): String? {\n\tval encodedValue = values[paramName]\n\t\t?.takeIf { it.type == EncodedType.ENCODED_STRING && it.value is String }\n\treturn encodedValue?.value?.let { it as String }\n}\n\nfun ClassNode.getKotlinClassMetadata(): KotlinClassMetadata? {\n\treturn getMetadata()?.let(KotlinClassMetadata::readLenient)\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/main/kotlin/jadx/plugins/kotlin/metadata/utils/KotlinMetadataUtils.kt",
    "content": "package jadx.plugins.kotlin.metadata.utils\n\nimport jadx.core.deobf.NameMapper\nimport jadx.core.dex.attributes.nodes.RenameReasonAttr\nimport jadx.core.dex.nodes.ClassNode\nimport jadx.core.dex.nodes.MethodNode\nimport jadx.core.utils.Utils\nimport jadx.plugins.kotlin.metadata.model.ClassAliasRename\nimport jadx.plugins.kotlin.metadata.model.CompanionRename\nimport jadx.plugins.kotlin.metadata.model.FieldRename\nimport jadx.plugins.kotlin.metadata.model.MethodArgRename\nimport kotlin.metadata.KmClass\n\nobject KotlinMetadataUtils {\n\n\t@JvmStatic\n\tfun getAlias(cls: ClassNode): ClassAliasRename? {\n\t\tval annotation = cls.getMetadata() ?: return null\n\t\treturn getClassAlias(cls, annotation)\n\t}\n\n\t/**\n\t * Try to get class info from Kotlin Metadata annotation\n\t */\n\tprivate fun getClassAlias(cls: ClassNode, annotation: Metadata): ClassAliasRename? {\n\t\tval firstValue = annotation.data2.getOrNull(0) ?: return null\n\n\t\ttry {\n\t\t\tval clsName = firstValue.trim()\n\t\t\t\t.takeUnless(String::isEmpty)\n\t\t\t\t?.let(Utils::cleanObjectName)\n\t\t\t\t?: return null\n\n\t\t\tval alias = splitAndCheckClsName(cls, clsName)\n\t\t\tif (alias != null) {\n\t\t\t\tRenameReasonAttr.forNode(cls).append(\"from Kotlin metadata\")\n\t\t\t\treturn alias\n\t\t\t}\n\t\t} catch (e: Exception) {\n\t\t\tLOG.error(\"Failed to parse kotlin metadata\", e)\n\t\t}\n\t\treturn null\n\t}\n\n\t// Don't use ClassInfo facility to not pollute class into cache\n\tprivate fun splitAndCheckClsName(originCls: ClassNode, fullClsName: String): ClassAliasRename? {\n\t\tif (!NameMapper.isValidFullIdentifier(fullClsName)) {\n\t\t\treturn null\n\t\t}\n\t\tval pkg: String\n\t\tval name: String\n\t\tval dot = fullClsName.lastIndexOf('.')\n\t\tif (dot == -1) {\n\t\t\tpkg = \"\"\n\t\t\tname = fullClsName\n\t\t} else {\n\t\t\tpkg = fullClsName.substring(0, dot)\n\t\t\tname = fullClsName.substring(dot + 1)\n\t\t}\n\t\tval originClsInfo = originCls.classInfo\n\t\tval originName = originClsInfo.shortName\n\t\tif (originName == name || name.contains(\"$\") ||\n\t\t\t!NameMapper.isValidIdentifier(name) ||\n\t\t\tcountPkgParts(originClsInfo.getPackage()) != countPkgParts(pkg) || pkg.startsWith(\"java.\")\n\t\t) {\n\t\t\treturn null\n\t\t}\n\t\tval newClsNode = originCls.root().resolveClass(fullClsName)\n\t\treturn if (newClsNode != null) {\n\t\t\t// class with alias name already exist\n\t\t\tnull\n\t\t} else {\n\t\t\tClassAliasRename(pkg, name)\n\t\t}\n\t}\n\n\tprivate fun countPkgParts(pkg: String): Int {\n\t\tif (pkg.isEmpty()) {\n\t\t\treturn 0\n\t\t}\n\t\tvar count = 1\n\t\tvar pos = 0\n\t\twhile (true) {\n\t\t\tpos = pkg.indexOf('.', pos)\n\t\t\tif (pos == -1) {\n\t\t\t\treturn count\n\t\t\t}\n\t\t\tpos++\n\t\t\tcount++\n\t\t}\n\t}\n\n\tfun mapMethodArgs(cls: ClassNode, kmCls: KmClass): Map<MethodNode, List<MethodArgRename>> {\n\t\treturn buildMap {\n\t\t\tkmCls.functions.forEach { kmFunction ->\n\t\t\t\tval node: MethodNode = cls.searchMethodByShortId(kmFunction.shortId) ?: return@forEach\n\n\t\t\t\tval argCount = node.argTypes.size\n\t\t\t\tval paramCount = kmFunction.valueParameters.size\n\t\t\t\tif (argCount == paramCount) {\n\t\t\t\t\t// requires arg registers to be loaded, is this necessary ?\n\t\t\t\t\tval aliasList = node.argRegs.zip(kmFunction.valueParameters).map { (rArg, kmValueParameter) ->\n\t\t\t\t\t\tMethodArgRename(rArg = rArg, alias = kmValueParameter.name)\n\t\t\t\t\t}\n\t\t\t\t\tput(node, aliasList)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfun mapFields(cls: ClassNode, kmCls: KmClass): List<FieldRename> {\n\t\treturn kmCls.properties.mapNotNull { kmProperty ->\n\t\t\tval node = cls.searchFieldByShortId(kmProperty.shortId) ?: return@mapNotNull null\n\t\t\tFieldRename(field = node, alias = kmProperty.name)\n\t\t}\n\t}\n\n\tfun mapCompanion(cls: ClassNode, kmCls: KmClass): CompanionRename? {\n\t\tval compName = kmCls.companionObject ?: return null\n\t\tval compField = cls.fields.firstOrNull {\n\t\t\tit.name == compName && it.accessFlags.run { isStatic && isFinal && isPublic }\n\t\t} ?: return null\n\n\t\tif (compField.type.isObject) {\n\t\t\tval compType = compField.type.`object`\n\t\t\tval compCls = cls.innerClasses.firstOrNull {\n\t\t\t\tit.classInfo.makeRawFullName() == compType\n\t\t\t} ?: return null\n\n\t\t\tval isOnlyInit = compField.useIn.size == 1 && compField.useIn[0].methodInfo.isClassInit\n\t\t\tval isEmpty = compCls.run { methods.all { it.isConstructor } && fields.isEmpty() }\n\n\t\t\treturn CompanionRename(\n\t\t\t\tfield = compField,\n\t\t\t\tcls = compCls,\n\t\t\t\thide = isOnlyInit && isEmpty,\n\t\t\t)\n\t\t}\n\n\t\treturn null\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/main/kotlin/jadx/plugins/kotlin/metadata/utils/KotlinUtils.kt",
    "content": "package jadx.plugins.kotlin.metadata.utils\n\nimport jadx.core.Consts\nimport jadx.core.dex.info.FieldInfo\nimport jadx.core.dex.instructions.IndexInsnNode\nimport jadx.core.dex.instructions.InsnType\nimport jadx.core.dex.instructions.InvokeNode\nimport jadx.core.dex.instructions.args.PrimitiveType\nimport jadx.core.dex.nodes.ClassNode\nimport jadx.core.dex.nodes.FieldNode\nimport jadx.core.dex.nodes.MethodNode\nimport jadx.plugins.kotlin.metadata.model.MethodRename\nimport jadx.plugins.kotlin.metadata.model.ToStringRename\nimport java.util.Locale\n\nobject KotlinUtils {\n\n\tfun parseToString(cls: ClassNode): ToStringRename? {\n\t\tval mthToString = cls.searchMethodByShortId(Consts.MTH_TOSTRING_SIGNATURE)\n\t\t\t?: return null\n\n\t\treturn ToStringParser.parse(mthToString)\n\t}\n\n\tfun findGetters(cls: ClassNode): List<MethodRename> {\n\t\treturn cls.fields.filter(FieldNode::isInstance).mapNotNull { field ->\n\t\t\tval mth = getFieldGetterMethod(cls, field.fieldInfo)\n\t\t\t\t?: return@mapNotNull null\n\t\t\tMethodRename(\n\t\t\t\tmth = mth,\n\t\t\t\talias = getGetterAlias(field.alias),\n\t\t\t)\n\t\t}\n\t}\n\n\tprivate fun getFieldGetterMethod(cls: ClassNode, field: FieldInfo): MethodNode? {\n\t\treturn cls.methods.firstOrNull {\n\t\t\tit.returnType == field.type &&\n\t\t\t\tit.argTypes.isEmpty() &&\n\t\t\t\tit.insnsCount == 3 &&\n\t\t\t\tit.sVars.size == 2 &&\n\t\t\t\t(it.sVars[1].assignInsn as? IndexInsnNode)?.index == field\n\t\t}\n\t}\n\n\tprivate fun getGetterAlias(fieldAlias: String): String {\n\t\tval capitalized = fieldAlias.replaceFirstChar {\n\t\t\tif (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString()\n\t\t}\n\t\treturn \"get$capitalized\"\n\t}\n\n\t// untested & overly complicated\n\tfun parseDefaultMethods(cls: ClassNode): List<MethodRename> {\n\t\tval possibleMthList = cls.methods.filter {\n\t\t\tit.accessFlags.isStatic && it.accessFlags.isSynthetic &&\n\t\t\t\tit.argTypes.run {\n\t\t\t\t\tsize > 3 &&\n\t\t\t\t\t\tfirst().isObject && first().`object` == cls.fullName &&\n\t\t\t\t\t\tget(size - 2).isPrimitive && get(size - 2).primitiveType == PrimitiveType.INT &&\n\t\t\t\t\t\tlast().isObject && last().`object` == Consts.CLASS_OBJECT\n\t\t\t\t}\n\t\t}\n\t\tval insnList = possibleMthList.filter {\n\t\t\tit.exitBlock.run {\n\t\t\t\tiDom != null && iDom.instructions.firstOrNull()?.type == InsnType.RETURN\n\t\t\t\tiDom.iDom != null\n\t\t\t} &&\n\t\t\t\tit.exitBlock.iDom.iDom.run {\n\t\t\t\t\tinstructions.firstOrNull() is InvokeNode\n\t\t\t\t}\n\t\t}\n\n\t\tval remapped = insnList.mapNotNull {\n\t\t\tval insn = it.exitBlock.iDom.iDom.instructions.first() as InvokeNode\n\t\t\tcls.searchMethodByShortId(insn.callMth.shortId)?.run { it to this }\n\t\t}\n\n\t\treturn remapped.map { (defaultMethod, originalMethod) ->\n\t\t\tMethodRename(\n\t\t\t\tmth = defaultMethod,\n\t\t\t\talias = getDefaultMethodAlias(originalMethod.alias),\n\t\t\t)\n\t\t}\n\t}\n\n\tprivate fun getDefaultMethodAlias(alias: String): String {\n\t\treturn \"$alias\\$default\"\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/main/kotlin/jadx/plugins/kotlin/metadata/utils/LogExt.kt",
    "content": "package jadx.plugins.kotlin.metadata.utils\n\nimport org.slf4j.Logger\nimport org.slf4j.LoggerFactory\n\ninline val <reified T : Any> T.LOG: Logger get() = LoggerFactory.getLogger(T::class.java)\n\ninline fun <reified T : Any, R> T.runCatchingLog(msg: String? = null, block: () -> R) =\n\trunCatching(block)\n\t\t.onFailure { LOG.error(msg.orEmpty(), it) }\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/main/kotlin/jadx/plugins/kotlin/metadata/utils/ToStringParser.kt",
    "content": "package jadx.plugins.kotlin.metadata.utils\n\nimport jadx.core.Consts\nimport jadx.core.dex.info.FieldInfo\nimport jadx.core.dex.instructions.ConstStringNode\nimport jadx.core.dex.instructions.IndexInsnNode\nimport jadx.core.dex.instructions.InsnType\nimport jadx.core.dex.instructions.InvokeNode\nimport jadx.core.dex.instructions.InvokeType\nimport jadx.core.dex.instructions.args.InsnWrapArg\nimport jadx.core.dex.instructions.args.RegisterArg\nimport jadx.core.dex.instructions.mods.ConstructorInsn\nimport jadx.core.dex.nodes.BlockNode\nimport jadx.core.dex.nodes.InsnNode\nimport jadx.core.dex.nodes.MethodNode\nimport jadx.core.utils.BlockUtils\nimport jadx.plugins.kotlin.metadata.model.FieldRename\nimport jadx.plugins.kotlin.metadata.model.ToStringRename\n\nclass ToStringParser private constructor(mthToString: MethodNode) {\n\tprivate var isStarted = false\n\tprivate var isFirstProcessed = false\n\tprivate var isFinished = false\n\tprivate var pendingAlias: String? = null\n\tprivate var clsAlias: String? = null\n\tprivate val list: MutableList<Pair<String, FieldInfo>> = mutableListOf()\n\tval isSuccess: Boolean get() = isStarted && isFinished\n\n\tinit {\n\t\tval blocks: List<BlockNode> = BlockUtils.buildSimplePath(mthToString.enterBlock)\n\t\tblocks.forEach { block ->\n\t\t\tblock.instructions.forEach { insn ->\n\t\t\t\tprocess(insn)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate fun process(insn: InsnNode) {\n\t\tif (!isStarted) {\n\t\t\tisStarted = isStartStringBuilder(insn)\n\t\t\treturn\n\t\t}\n\t\tif (isFinished) {\n\t\t\treturn\n\t\t}\n\n\t\tif (isAppendInvoke(insn)) {\n\t\t\tval arg = insn.getArg(1)\n\n\t\t\t// invoke with const string\n\t\t\tif (arg.isInsnWrap && arg is InsnWrapArg && arg.wrapInsn.type == InsnType.CONST_STR) {\n\t\t\t\tval constStr: String? = (arg.wrapInsn as ConstStringNode).string\n\t\t\t\thandleString(requireNotNull(constStr) { \"Failed to get const String\" })\n\t\t\t}\n\n\t\t\t// invoke with register\n\t\t\tif (arg.isRegister && arg is RegisterArg) {\n\t\t\t\tval assign = arg.sVar.assignInsn\n\t\t\t\t// basic argument\n\t\t\t\tif (assign is IndexInsnNode) {\n\t\t\t\t\tval info: FieldInfo? = (arg.sVar.assignInsn as IndexInsnNode).index as? FieldInfo\n\t\t\t\t\thandleFieldInfo(requireNotNull(info) { \"Failed to get FieldInfo from index\" })\n\t\t\t\t}\n\n\t\t\t\t// string formatted argument, for rare cases like Arrays.toString(...)\n\t\t\t\tif (assign is InvokeNode && assign.invokeType == InvokeType.STATIC && assign.argsCount == 1) {\n\t\t\t\t\tval prevArg = assign.getArg(0)\n\t\t\t\t\tif (prevArg.isRegister && prevArg is RegisterArg) {\n\t\t\t\t\t\tif (prevArg.sVar.assignInsn is IndexInsnNode) {\n\t\t\t\t\t\t\tval info: FieldInfo? = (prevArg.sVar.assignInsn as IndexInsnNode).index as? FieldInfo\n\t\t\t\t\t\t\thandleFieldInfo(requireNotNull(info) { \"Failed to get nested FieldInfo from index\" })\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn\n\t\t}\n\n\t\tisFinished = isToString(insn)\n\t}\n\n\tprivate fun handleString(string: String) {\n\t\tif (pendingAlias != null) {\n\t\t\tLOG.warn(\"Skipping pending alias: '$pendingAlias'\")\n\t\t}\n\t\tif (!isFirstProcessed) {\n\t\t\tclsAlias = string.substringBefore('(')\n\t\t\tpendingAlias = string\n\t\t\t\t.substringAfter('(')\n\t\t\t\t.substringBeforeLast('=')\n\t\t\tisFirstProcessed = true\n\t\t} else {\n\t\t\tpendingAlias = string\n\t\t\t\t.substringAfter(\", \")\n\t\t\t\t.substringBeforeLast('=')\n\t\t}\n\t}\n\n\tprivate fun handleFieldInfo(fieldInfo: FieldInfo) {\n\t\tlist.add(requireNotNull(pendingAlias) { \"No pending alias found\" } to fieldInfo)\n\t\tpendingAlias = null\n\t}\n\n\tcompanion object {\n\n\t\tfun parse(mth: MethodNode): ToStringRename? {\n\t\t\tval parser =\n\t\t\t\tkotlin.runCatching { ToStringParser(mth) }.getOrNull()\n\t\t\tif (parser?.isSuccess != true) return null\n\n\t\t\tval cls = mth.parentClass\n\t\t\treturn ToStringRename(\n\t\t\t\tcls = cls,\n\t\t\t\tclsAlias = parser.clsAlias,\n\t\t\t\tfields = parser.list.mapNotNull { (alias, fieldInfo) ->\n\t\t\t\t\tval field = cls.searchField(fieldInfo)\n\t\t\t\t\t\t?: return@mapNotNull null\n\t\t\t\t\tFieldRename(\n\t\t\t\t\t\tfield = field,\n\t\t\t\t\t\talias = alias,\n\t\t\t\t\t)\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\n\t\tprivate fun isStartStringBuilder(inst: InsnNode): Boolean {\n\t\t\treturn inst is ConstructorInsn &&\n\t\t\t\tinst.isNewInstance &&\n\t\t\t\tinst.callMth.declClass.fullName == Consts.CLASS_STRING_BUILDER\n\t\t}\n\n\t\tprivate fun isAppendInvoke(inst: InsnNode): Boolean {\n\t\t\treturn inst is InvokeNode &&\n\t\t\t\tinst.callMth.declClass.fullName == Consts.CLASS_STRING_BUILDER &&\n\t\t\t\tinst.callMth.name == \"append\" &&\n\t\t\t\tinst.argsCount == 2\n\t\t}\n\n\t\tprivate fun isToString(inst: InsnNode): Boolean {\n\t\t\treturn inst is InvokeNode &&\n\t\t\t\tinst.callMth.declClass.fullName == Consts.CLASS_STRING_BUILDER &&\n\t\t\t\tinst.callMth.shortId == Consts.MTH_TOSTRING_SIGNATURE\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin",
    "content": "jadx.plugins.kotlin.metadata.KotlinMetadataPlugin\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/test/kotlin/TestJavaParser.kt",
    "content": "package jadx.plugins.kotlin.metadata.tests\n\nimport jadx.tests.api.IntegrationTest\nimport jadx.tests.api.utils.assertj.JadxAssertions.assertThat\nimport org.junit.jupiter.api.Test\n\nclass TestJavaParser : IntegrationTest() {\n\n\t@Test\n\tfun test() {\n\t\tval sampleCls = getResourceFile(\"samples/MainKt.class\")\n\t\tassertThat(getClassNodeFromFiles(listOf(sampleCls), \"MainKt\"))\n\t\t\t.code()\n\t\t\t.doesNotContain(\"Exception occurred when reading Kotlin metadata\")\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/test/kotlin/TestKotlinMetadata.kt",
    "content": "package jadx.plugins.kotlin.metadata.tests\n\nimport jadx.plugins.kotlin.metadata.KotlinMetadataOptions.Companion.CLASS_ALIAS_OPT\nimport jadx.plugins.kotlin.metadata.KotlinMetadataOptions.Companion.COMPANION_OPT\nimport jadx.plugins.kotlin.metadata.KotlinMetadataOptions.Companion.DATA_CLASS_OPT\nimport jadx.plugins.kotlin.metadata.KotlinMetadataOptions.Companion.FIELDS_OPT\nimport jadx.plugins.kotlin.metadata.KotlinMetadataOptions.Companion.GETTERS_OPT\nimport jadx.plugins.kotlin.metadata.KotlinMetadataOptions.Companion.METHOD_ARGS_OPT\nimport jadx.plugins.kotlin.metadata.KotlinMetadataOptions.Companion.TO_STRING_OPT\nimport jadx.tests.api.SmaliTest\nimport jadx.tests.api.utils.assertj.JadxAssertions.assertThat\nimport jadx.tests.api.utils.assertj.JadxCodeAssertions\nimport org.junit.jupiter.api.Test\n\nclass TestKotlinMetadata : SmaliTest() {\n\t// @formatter:off\n\t/*\n\t\tpackage deobf\n\n\t\tdata class DataClassSample(\n\t\t\tval name: String,\n\t\t\tprivate val id: Int,\n\t\t) {\n\t\t\tvar inner: Short = 3\n\n\t\t\tcompanion object {\n\t\t\t\tfun getTag(): String {\n\t\t\t\t\treturn \"TAG\"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t */\n\t// @formatter:on\n\n\t@Test\n\tfun testMethodArgs() {\n\t\tsetupArgs { this[METHOD_ARGS_OPT] = true }\n\t\tassertThatClass()\n\t\t\t.containsOne(\"public boolean equals(Object other) {\")\n\t}\n\n\t@Test\n\tfun testIgnoreMethodArgs() {\n\t\tsetupArgs()\n\t\tassertThatClass()\n\t\t\t.containsOne(\"public boolean equals(Object obj) {\")\n\t}\n\n\t@Test\n\tfun testFields() {\n\t\tsetupArgs { this[FIELDS_OPT] = true }\n\t\tassertThatClass()\n\t\t\t.containsOne(\"private final String name;\")\n\t\t\t.containsOne(\"private final int id;\")\n\t\t\t.containsOne(\"private short inner;\")\n\t\t\t.countString(3, \"reason: from kotlin metadata\")\n\t}\n\n\t@Test\n\tfun testIgnoreFields() {\n\t\tsetupArgs()\n\t\tassertThatClass()\n\t\t\t.containsOne(\"private final String a;\")\n\t\t\t.containsOne(\"private final int b;\")\n\t\t\t.containsOne(\"private short c;\")\n\t\t\t.countString(0, \"reason: from kotlin metadata\")\n\t}\n\n\t@Test\n\tfun testCompanion() {\n\t\tsetupArgs { this[COMPANION_OPT] = true }\n\t\tassertThatClass()\n\t\t\t.containsOne(\"public static final Companion INSTANCE = new Companion(null);\")\n\t\t\t.containsOne(\"public static final class Companion {\")\n\t\t\t.countString(2, \"reason: from kotlin metadata\")\n\t}\n\n\t@Test\n\tfun testIgnoreCompanion() {\n\t\tsetupArgs()\n\t\tassertThatClass()\n\t\t\t.containsOne(\"public static final b d = new b(null);\")\n\t\t\t.containsOne(\"public static final class b {\")\n\t\t\t.countString(0, \"reason: from kotlin metadata\")\n\t}\n\n\t@Test\n\tfun testDataClass() {\n\t\tsetupArgs { this[DATA_CLASS_OPT] = true }\n\t\tassertThatClass()\n\t\t\t.containsOne(\"/* data */\")\n\t}\n\n\t@Test\n\tfun testIgnoreDataClass() {\n\t\tsetupArgs()\n\t\tassertThatClass()\n\t\t\t.countString(0, \"/* data */\")\n\t}\n\n\t@Test\n\tfun testToString() {\n\t\tsetupArgs { this[TO_STRING_OPT] = true }\n\t\tassertThatClass()\n\t\t\t.containsOne(\"public final class DataClassSample {\")\n\t\t\t.containsOne(\"private final String name;\")\n\t\t\t.containsOne(\"private final int id;\")\n\t\t\t.countString(3, \"reason: from toString\")\n\t}\n\n\t@Test\n\tfun testIgnoreToString() {\n\t\tsetupArgs()\n\t\tassertThatClass()\n\t\t\t.containsOne(\"public final class a {\")\n\t\t\t.containsOne(\"private final String a;\")\n\t\t\t.containsOne(\"private final int b;\")\n\t\t\t.countString(0, \"reason: from toString\")\n\t}\n\n\t@Test\n\tfun testGetters() {\n\t\tsetupArgs { this[GETTERS_OPT] = true }\n\t\tassertThatClass()\n\t\t\t.containsOne(\"public final String getA() {\")\n\t\t\t.countString(1, \"reason: from getter\")\n\t}\n\n\t@Test\n\tfun testGettersAlias() {\n\t\tsetupArgs {\n\t\t\tthis[FIELDS_OPT] = true\n\t\t\tthis[GETTERS_OPT] = true\n\t\t}\n\t\tassertThatClass()\n\t\t\t.containsOne(\"public final String getName() {\")\n\t\t\t.countString(1, \"reason: from getter\")\n\t}\n\n\t@Test\n\tfun testIgnoreGetters() {\n\t\tsetupArgs()\n\t\tassertThatClass()\n\t\t\t.countString(0, \"reason: from getter\")\n\t}\n\n\tprivate fun setupArgs(builder: MutableMap<String, Boolean>.() -> Unit = {}) {\n\t\tval allOff = mutableMapOf(\n\t\t\tCLASS_ALIAS_OPT to false,\n\t\t\tMETHOD_ARGS_OPT to false,\n\t\t\tFIELDS_OPT to false,\n\t\t\tCOMPANION_OPT to false,\n\t\t\tDATA_CLASS_OPT to false,\n\t\t\tTO_STRING_OPT to false,\n\t\t\tGETTERS_OPT to false,\n\t\t)\n\t\targs.pluginOptions = allOff.apply(builder).mapValues {\n\t\t\tif (it.value) \"yes\" else \"no\"\n\t\t}\n\t}\n\n\tprivate fun assertThatClass(): JadxCodeAssertions =\n\t\tassertThat(getClassNodeFromSmaliFiles(\"deobf\", \"TestKotlinMetadata\", \"a\"))\n\t\t\t.code()\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/test/smali/deobf/TestKotlinMetadata/a$b.smali",
    "content": ".class public final Ldeobf/a$b;\n.super Ljava/lang/Object;\n.source \"SourceFile\"\n\n\n# annotations\n.annotation system Ldalvik/annotation/EnclosingClass;\n    value = Ldeobf/a;\n.end annotation\n\n.annotation system Ldalvik/annotation/InnerClass;\n    accessFlags = 0x19\n    name = \"b\"\n.end annotation\n\n\n.annotation runtime Lkotlin/Metadata;\n    d1 = {\n        \"\\u0000\\u0010\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0010\\u0000\\n\\u0002\\u0010\\u000e\\n\\u0002\\u0008\\u0004\\u0008\\u0086\\u0003\\u0018\\u00002\\u00020\\u0001B\\t\\u0008\\u0002\\u00a2\\u0006\\u0004\\u0008\\u0004\\u0010\\u0005J\\u0006\\u0010\\u0003\\u001a\\u00020\\u0002\\u00a8\\u0006\\u0006\"\n    }\n    d2 = {\n        \"Ldeobf/DataClassSample$Companion;\",\n        \"\",\n        \"\",\n        \"a\",\n        \"<init>\",\n        \"()V\",\n        \"app_release\"\n    }\n    k = 0x1\n    mv = {\n        0x1,\n        0x8,\n        0x0\n    }\n.end annotation\n\n\n# direct methods\n.method private constructor <init>()V\n    .registers 1\n\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n.method public synthetic constructor <init>(Lkotlin/jvm/internal/DefaultConstructorMarker;)V\n    .registers 2\n\n    .line 1\n    invoke-direct {p0}, Ldeobf/a$b;-><init>()V\n\n    return-void\n.end method\n\n\n# virtual methods\n.method public final a()Ljava/lang/String;\n    .registers 2\n\n    const-string v0, \"TAG\"\n\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-metadata/src/test/smali/deobf/TestKotlinMetadata/a.smali",
    "content": ".class public final Ldeobf/a;\n.super Ljava/lang/Object;\n.source \"SourceFile\"\n\n\n# annotations\n.annotation system Ldalvik/annotation/MemberClasses;\n    value = {\n        Ldeobf/a$b;\n    }\n.end annotation\n\n.annotation runtime Lkotlin/Metadata;\n    d1 = {\n        \"\\u0000&\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0010\\u0000\\n\\u0002\\u0010\\u000e\\n\\u0000\\n\\u0002\\u0010\\u0008\\n\\u0002\\u0008\\u0002\\n\\u0002\\u0010\\u000b\\n\\u0002\\u0008\\u0008\\n\\u0002\\u0010\\n\\n\\u0002\\u0008\\u000b\\u0008\\u0086\\u0008\\u0018\\u0000 \\u00192\\u00020\\u0001:\\u0001\\u001aB\\u0017\\u0012\\u0006\\u0010\\u000c\\u001a\\u00020\\u0002\\u0012\\u0006\\u0010\\u000f\\u001a\\u00020\\u0004\\u00a2\\u0006\\u0004\\u0008\\u0017\\u0010\\u0018J\\t\\u0010\\u0003\\u001a\\u00020\\u0002H\\u00d6\\u0001J\\t\\u0010\\u0005\\u001a\\u00020\\u0004H\\u00d6\\u0001J\\u0013\\u0010\\u0008\\u001a\\u00020\\u00072\\u0008\\u0010\\u0006\\u001a\\u0004\\u0018\\u00010\\u0001H\\u00d6\\u0003R\\u0017\\u0010\\u000c\\u001a\\u00020\\u00028\\u0006\\u00a2\\u0006\\u000c\\n\\u0004\\u0008\\t\\u0010\\n\\u001a\\u0004\\u0008\\t\\u0010\\u000bR\\u0014\\u0010\\u000f\\u001a\\u00020\\u00048\\u0002X\\u0082\\u0004\\u00a2\\u0006\\u0006\\n\\u0004\\u0008\\r\\u0010\\u000eR\\\"\\u0010\\u0016\\u001a\\u00020\\u00108\\u0006@\\u0006X\\u0086\\u000e\\u00a2\\u0006\\u0012\\n\\u0004\\u0008\\u0011\\u0010\\u0012\\u001a\\u0004\\u0008\\u0013\\u0010\\u0014\\\"\\u0004\\u0008\\r\\u0010\\u0015\\u00a8\\u0006\\u001b\"\n    }\n    d2 = {\n        \"Ldeobf/DataClassSample;\",\n        \"\",\n        \"\",\n        \"toString\",\n        \"\",\n        \"hashCode\",\n        \"other\",\n        \"\",\n        \"equals\",\n        \"a\",\n        \"Ljava/lang/String;\",\n        \"()Ljava/lang/String;\",\n        \"name\",\n        \"b\",\n        \"I\",\n        \"id\",\n        \"\",\n        \"c\",\n        \"S\",\n        \"getInner\",\n        \"()S\",\n        \"(S)V\",\n        \"inner\",\n        \"<init>\",\n        \"(Ljava/lang/String;I)V\",\n        \"d\",\n        \"Companion\",\n        \"app_release\"\n    }\n    k = 0x1\n    mv = {\n        0x1,\n        0x8,\n        0x0\n    }\n.end annotation\n\n# static fields\n.field public static final d:Ldeobf/a$b;\n\n\n# instance fields\n.field private final a:Ljava/lang/String;\n\n.field private final b:I\n\n.field private c:S\n\n\n# direct methods\n.method static constructor <clinit>()V\n    .registers 2\n\n    new-instance v0, Ldeobf/a$b;\n\n    const/4 v1, 0x0\n\n    invoke-direct {v0, v1}, Ldeobf/a$b;-><init>(Lkotlin/jvm/internal/DefaultConstructorMarker;)V\n\n    sput-object v0, Ldeobf/a;->d:Ldeobf/a$b;\n\n    return-void\n.end method\n\n.method public constructor <init>(Ljava/lang/String;I)V\n    .registers 4\n\n    const-string v0, \"name\"\n\n    invoke-static {p1, v0}, Lkotlin/jvm/internal/Intrinsics;->checkNotNullParameter(Ljava/lang/Object;Ljava/lang/String;)V\n\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    iput-object p1, p0, Ldeobf/a;->a:Ljava/lang/String;\n\n    iput p2, p0, Ldeobf/a;->b:I\n\n    const/4 p1, 0x3\n\n    iput-short p1, p0, Ldeobf/a;->c:S\n\n    return-void\n.end method\n\n\n# virtual methods\n.method public final a()Ljava/lang/String;\n    .registers 2\n\n    iget-object v0, p0, Ldeobf/a;->a:Ljava/lang/String;\n\n    return-object v0\n.end method\n\n.method public final b(S)V\n    .registers 2\n\n    iput-short p1, p0, Ldeobf/a;->c:S\n\n    return-void\n.end method\n\n.method public equals(Ljava/lang/Object;)Z\n    .registers 6\n\n    const/4 v0, 0x1\n\n    if-ne p0, p1, :cond_4\n\n    return v0\n\n    :cond_4\n    instance-of v1, p1, Ldeobf/a;\n\n    const/4 v2, 0x0\n\n    if-nez v1, :cond_a\n\n    return v2\n\n    :cond_a\n    check-cast p1, Ldeobf/a;\n\n    iget-object v1, p0, Ldeobf/a;->a:Ljava/lang/String;\n\n    iget-object v3, p1, Ldeobf/a;->a:Ljava/lang/String;\n\n    invoke-static {v1, v3}, Lkotlin/jvm/internal/Intrinsics;->areEqual(Ljava/lang/Object;Ljava/lang/Object;)Z\n\n    move-result v1\n\n    if-nez v1, :cond_17\n\n    return v2\n\n    :cond_17\n    iget v1, p0, Ldeobf/a;->b:I\n\n    iget p1, p1, Ldeobf/a;->b:I\n\n    if-eq v1, p1, :cond_1e\n\n    return v2\n\n    :cond_1e\n    return v0\n.end method\n\n.method public hashCode()I\n    .registers 3\n\n    iget-object v0, p0, Ldeobf/a;->a:Ljava/lang/String;\n\n    invoke-virtual {v0}, Ljava/lang/String;->hashCode()I\n\n    move-result v0\n\n    mul-int/lit8 v0, v0, 0x1f\n\n    iget v1, p0, Ldeobf/a;->b:I\n\n    add-int/2addr v0, v1\n\n    return v0\n.end method\n\n.method public toString()Ljava/lang/String;\n    .registers 5\n\n    iget-object v0, p0, Ldeobf/a;->a:Ljava/lang/String;\n\n    iget v1, p0, Ldeobf/a;->b:I\n\n    new-instance v2, Ljava/lang/StringBuilder;\n\n    invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V\n\n    const-string v3, \"DataClassSample(name=\"\n\n    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    const-string v0, \", id=\"\n\n    invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v2, v1}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;\n\n    const-string v0, \")\"\n\n    invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v0\n\n    return-object v0\n.end method\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-source-debug-extension/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-library\")\n\tid(\"jadx-kotlin\")\n}\n\ndependencies {\n\tapi(project(\":jadx-core\"))\n\n\ttestImplementation(project.project(\":jadx-core\").sourceSets.getByName(\"test\").output)\n\ttestImplementation(\"org.apache.commons:commons-lang3:3.20.0\")\n\n\ttestRuntimeOnly(project(\":jadx-plugins:jadx-smali-input\"))\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/KotlinSmapOptions.kt",
    "content": "package jadx.plugins.kotlin.smap\n\nimport jadx.api.plugins.options.impl.BasePluginOptionsBuilder\nimport jadx.plugins.kotlin.smap.KotlinSmapPlugin.Companion.PLUGIN_ID\n\nclass KotlinSmapOptions : BasePluginOptionsBuilder() {\n\tvar isClassAliasSourceDbg: Boolean = true\n\t\tprivate set\n\n\toverride fun registerOptions() {\n\t\tboolOption(CLASS_ALIAS_SOURCE_DBG_OPT)\n\t\t\t.description(\"rename class alias from SourceDebugExtension\")\n\t\t\t.defaultValue(false)\n\t\t\t.setter { isClassAliasSourceDbg = it }\n\t}\n\n\tfun isClassSourceDbg(): Boolean {\n\t\treturn isClassAliasSourceDbg\n\t}\n\n\tcompanion object {\n\t\tconst val CLASS_ALIAS_SOURCE_DBG_OPT = \"$PLUGIN_ID.class-alias-source-dbg\"\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/KotlinSmapPlugin.kt",
    "content": "package jadx.plugins.kotlin.smap\n\nimport jadx.api.plugins.JadxPlugin\nimport jadx.api.plugins.JadxPluginContext\nimport jadx.api.plugins.JadxPluginInfo\nimport jadx.plugins.kotlin.smap.pass.KotlinSourceDebugExtensionPass\n\nclass KotlinSmapPlugin : JadxPlugin {\n\n\tprivate val options = KotlinSmapOptions()\n\n\toverride fun getPluginInfo(): JadxPluginInfo {\n\t\treturn JadxPluginInfo(PLUGIN_ID, \"Kotlin SMAP\", \"Use kotlin.SourceDebugExtension annotation for rename class alias\")\n\t}\n\n\toverride fun init(context: JadxPluginContext) {\n\t\tcontext.registerOptions(options)\n\n\t\tif (options.isClassSourceDbg()) {\n\t\t\tcontext.addPass(KotlinSourceDebugExtensionPass(options))\n\t\t}\n\t}\n\n\tcompanion object {\n\t\tconst val PLUGIN_ID = \"kotlin-smap\"\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/model/ClassAliasRename.kt",
    "content": "package jadx.plugins.kotlin.smap.model\n\ndata class ClassAliasRename(\n\tval pkg: String,\n\tval name: String,\n)\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/model/Constants.kt",
    "content": "package jadx.plugins.kotlin.smap.model\n\nobject Constants {\n\tconst val KOTLIN_SOURCE_DEBUG_EXTENSION = \"Lkotlin/jvm/internal/SourceDebugExtension;\"\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/model/SMAP.kt",
    "content": "/*\n * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.\n * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.\n */\npackage jadx.plugins.kotlin.smap.model\n\nimport kotlin.math.max\n\nconst val KOTLIN_STRATA_NAME = \"Kotlin\"\nconst val KOTLIN_DEBUG_STRATA_NAME = \"KotlinDebug\"\n\n/**\n * Represents SMAP as a structure that is contained in `SourceDebugExtension` attribute of a class.\n * This structure is immutable, we can only query for a result.\n */\nclass SMAP(val fileMappings: List<FileMapping>) {\n\t// assuming disjoint line mappings (otherwise binary search can't be used anyway)\n\tprivate val intervals = fileMappings.flatMap { it.lineMappings }.sortedBy { it.dest }\n\n\tfun findRange(lineNumber: Int): RangeMapping? {\n\t\tval index = intervals.binarySearch { if (lineNumber in it) 0 else it.dest - lineNumber }\n\t\treturn if (index < 0) null else intervals[index]\n\t}\n\n\tcompanion object {\n\t\tconst val FILE_SECTION = \"*F\"\n\t\tconst val LINE_SECTION = \"*L\"\n\t\tconst val STRATA_SECTION = \"*S\"\n\t\tconst val END = \"*E\"\n\t}\n}\n\nclass FileMapping(val name: String, val path: String) {\n\tval lineMappings = arrayListOf<RangeMapping>()\n\n\tfun toSourceInfo(): SourceInfo =\n\t\tSourceInfo(\n\t\t\tname,\n\t\t\tpath,\n\t\t\tlineMappings.fold(0) { result, mapping -> max(result, mapping.source + mapping.range - 1) },\n\t\t)\n\n\tfun mapNewLineNumber(source: Int, currentIndex: Int, callSite: SourcePosition?): Int {\n\t\t// Save some space in the SMAP by reusing (or extending if it's the last one) the existing range.\n\t\t// TODO some *other* range may already cover `source`; probably too slow to check them all though.\n\t\t//   Maybe keep the list ordered by `source` and use binary search to locate the closest range on the left?\n\t\tval mapping = lineMappings.lastOrNull()?.takeIf { it.canReuseFor(source, currentIndex, callSite) }\n\t\t\t?: lineMappings.firstOrNull()?.takeIf { it.canReuseFor(source, currentIndex, callSite) }\n\t\t\t?: mapNewInterval(source, currentIndex + 1, 1, callSite)\n\t\tmapping.range = max(mapping.range, source - mapping.source + 1)\n\t\treturn mapping.mapSourceToDest(source)\n\t}\n\n\tprivate fun RangeMapping.canReuseFor(newSource: Int, globalMaxDest: Int, newCallSite: SourcePosition?): Boolean =\n\t\tcallSite == newCallSite && (newSource - source) in 0 until range + (if (globalMaxDest in this) 10 else 0)\n\n\tfun mapNewInterval(source: Int, dest: Int, range: Int, callSite: SourcePosition? = null): RangeMapping =\n\t\tRangeMapping(source, dest, range, callSite, parent = this).also { lineMappings.add(it) }\n}\n\ndata class RangeMapping(val source: Int, val dest: Int, var range: Int, val callSite: SourcePosition?, val parent: FileMapping) {\n\toperator fun contains(destLine: Int): Boolean =\n\t\tdest <= destLine && destLine < dest + range\n\n\tfun hasMappingForSource(sourceLine: Int): Boolean =\n\t\tsource <= sourceLine && sourceLine < source + range\n\n\tfun mapDestToSource(destLine: Int): SourcePosition =\n\t\tSourcePosition(source + (destLine - dest), parent.name, parent.path)\n\n\tfun mapSourceToDest(sourceLine: Int): Int =\n\t\tdest + (sourceLine - source)\n}\n\nval RangeMapping.toRange: IntRange\n\tget() = dest until dest + range\n\ndata class SourcePosition(val line: Int, val file: String, val path: String)\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/model/SourceInfo.kt",
    "content": "package jadx.plugins.kotlin.smap.model\n\ndata class SourceInfo(\n\tval sourceFileName: String?,\n\tval pathOrCleanFQN: String,\n\tval linesInFile: Int,\n)\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/pass/KotlinSourceDebugExtensionPass.kt",
    "content": "package jadx.plugins.kotlin.smap.pass\n\nimport jadx.api.plugins.pass.JadxPassInfo\nimport jadx.api.plugins.pass.impl.OrderedJadxPassInfo\nimport jadx.api.plugins.pass.types.JadxPreparePass\nimport jadx.core.dex.attributes.AFlag\nimport jadx.core.dex.nodes.RootNode\nimport jadx.plugins.kotlin.smap.KotlinSmapOptions\nimport jadx.plugins.kotlin.smap.utils.KotlinSmapUtils\n\nclass KotlinSourceDebugExtensionPass(\n\tprivate val options: KotlinSmapOptions,\n) : JadxPreparePass {\n\n\toverride fun getInfo(): JadxPassInfo {\n\t\treturn OrderedJadxPassInfo(\n\t\t\t\"SourceDebugExtensionPrepare\",\n\t\t\t\"Use kotlin.jvm.internal.SourceDebugExtension annotation to rename class & package\",\n\t\t)\n\t\t\t.before(\"RenameVisitor\")\n\t}\n\n\toverride fun init(root: RootNode) {\n\t\tif (options.isClassAliasSourceDbg) {\n\t\t\tfor (cls in root.classes) {\n\t\t\t\tif (cls.contains(AFlag.DONT_RENAME)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// rename class & package\n\t\t\t\tval kotlinCls = KotlinSmapUtils.getClassAlias(cls)\n\t\t\t\tif (kotlinCls != null) {\n\t\t\t\t\tcls.rename(kotlinCls.name)\n\t\t\t\t\tcls.packageNode.rename(kotlinCls.pkg)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/utils/Extensions.kt",
    "content": "@file:Suppress(\"UNCHECKED_CAST\")\n\npackage jadx.plugins.kotlin.smap.utils\n\nimport jadx.api.plugins.input.data.annotations.EncodedType\nimport jadx.api.plugins.input.data.annotations.EncodedValue\nimport jadx.api.plugins.input.data.annotations.IAnnotation\nimport jadx.core.dex.nodes.ClassNode\nimport jadx.plugins.kotlin.smap.model.Constants\nimport jadx.plugins.kotlin.smap.model.SMAP\n\nfun ClassNode.getSourceDebugExtension(): SMAP? {\n\tval annotation: IAnnotation? = getAnnotation(Constants.KOTLIN_SOURCE_DEBUG_EXTENSION)\n\treturn annotation?.run {\n\t\tval smapParser = SMAPParser.parseOrNull(getParamsAsList(\"value\")?.get(0)?.value.toString())\n\t\treturn smapParser\n\t}\n}\n\nprivate fun IAnnotation.getParamsAsList(paramName: String): List<EncodedValue>? {\n\tval encodedValue = values[paramName]\n\t\t?.takeIf { it.type == EncodedType.ENCODED_ARRAY && it.value is List<*> }\n\treturn encodedValue?.value?.let { it as List<EncodedValue> }\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/utils/KotlinSmapUtils.kt",
    "content": "package jadx.plugins.kotlin.smap.utils\n\nimport jadx.core.deobf.NameMapper\nimport jadx.core.dex.attributes.nodes.RenameReasonAttr\nimport jadx.core.dex.nodes.ClassNode\nimport jadx.core.utils.Utils\nimport jadx.plugins.kotlin.smap.model.ClassAliasRename\nimport jadx.plugins.kotlin.smap.model.SMAP\nimport org.slf4j.Logger\nimport org.slf4j.LoggerFactory\nimport kotlin.jvm.java\n\nobject KotlinSmapUtils {\n\n\tval LOG: Logger = LoggerFactory.getLogger(KotlinSmapUtils::class.java)\n\n\t@JvmStatic\n\tfun getClassAlias(cls: ClassNode): ClassAliasRename? {\n\t\tval annotation = cls.getSourceDebugExtension() ?: return null\n\t\treturn getClassAlias(cls, annotation)\n\t}\n\n\tprivate fun getClassAlias(cls: ClassNode, annotation: SMAP): ClassAliasRename? {\n\t\tval firstValue = annotation.fileMappings[0].path.replace(\"/\", \".\")\n\t\ttry {\n\t\t\tval clsName = firstValue.trim()\n\t\t\t\t.takeUnless(String::isEmpty)\n\t\t\t\t?.let(Utils::cleanObjectName)\n\t\t\t\t?: return null\n\n\t\t\tval alias = splitAndCheckClsName(cls, clsName)\n\t\t\tif (alias != null) {\n\t\t\t\tRenameReasonAttr.forNode(cls).append(\"from SourceDebugExtension\")\n\t\t\t\treturn alias\n\t\t\t}\n\t\t} catch (e: Exception) {\n\t\t\tLOG.error(\"Failed to parse SourceDebugExtension\", e)\n\t\t}\n\t\treturn null\n\t}\n\n\t// Don't use ClassInfo facility to not pollute class into cache\n\tprivate fun splitAndCheckClsName(originCls: ClassNode, fullClsName: String): ClassAliasRename? {\n\t\tif (!NameMapper.isValidFullIdentifier(fullClsName)) {\n\t\t\treturn null\n\t\t}\n\t\tval pkg: String\n\t\tval name: String\n\t\tval dot = fullClsName.lastIndexOf('.')\n\t\tif (dot == -1) {\n\t\t\tpkg = \"\"\n\t\t\tname = fullClsName\n\t\t} else {\n\t\t\tpkg = fullClsName.substring(0, dot)\n\t\t\tname = fullClsName.substring(dot + 1)\n\t\t}\n\t\tval originClsInfo = originCls.classInfo\n\t\tval originName = originClsInfo.shortName\n\t\tif (originName == name || name.contains(\"$\") ||\n\t\t\t!NameMapper.isValidIdentifier(name) || pkg.startsWith(\"java.\")\n\t\t) {\n\t\t\treturn null\n\t\t}\n\t\tval newClsNode = originCls.root().resolveClass(fullClsName)\n\t\treturn if (newClsNode != null) {\n\t\t\t// class with alias name already exist\n\t\t\tnull\n\t\t} else {\n\t\t\tClassAliasRename(pkg, name)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/utils/SMAPParser.kt",
    "content": "/*\n * Copyright 2010-2015 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage jadx.plugins.kotlin.smap.utils\n\nimport jadx.plugins.kotlin.smap.model.FileMapping\nimport jadx.plugins.kotlin.smap.model.KOTLIN_DEBUG_STRATA_NAME\nimport jadx.plugins.kotlin.smap.model.KOTLIN_STRATA_NAME\nimport jadx.plugins.kotlin.smap.model.SMAP\n\nobject SMAPParser {\n\tfun parseOrNull(mappingInfo: String): SMAP? =\n\t\tif (mappingInfo.isNotEmpty()) {\n\t\t\tparseStratum(mappingInfo, KOTLIN_STRATA_NAME, parseStratum(mappingInfo, KOTLIN_DEBUG_STRATA_NAME, null))\n\t\t} else {\n\t\t\tnull\n\t\t}\n\n\tprivate class SMAPTokenizer(private val text: String, private val headerString: String) : Iterator<String> {\n\n\t\tprivate var pos = 0\n\t\tprivate var currentLine: String? = null\n\n\t\tinit {\n\t\t\tadvance()\n\t\t\twhile (currentLine != null && currentLine != headerString) {\n\t\t\t\tadvance()\n\t\t\t}\n\t\t\tif (currentLine == headerString) {\n\t\t\t\tadvance()\n\t\t\t}\n\t\t}\n\n\t\tprivate fun advance() {\n\t\t\tif (pos >= text.length) {\n\t\t\t\tcurrentLine = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tval fromPos = pos\n\t\t\twhile (pos < text.length && text[pos] != '\\n' && text[pos] != '\\r') pos++\n\t\t\tcurrentLine = text.substring(fromPos, pos)\n\t\t\tpos++\n\t\t}\n\n\t\toverride fun hasNext(): Boolean {\n\t\t\treturn currentLine != null\n\t\t}\n\n\t\toverride fun next(): String {\n\t\t\tval res = currentLine ?: throw NoSuchElementException()\n\t\t\tadvance()\n\t\t\treturn res\n\t\t}\n\t}\n\n\tprivate fun parseStratum(mappingInfo: String, stratum: String, callSites: SMAP?): SMAP? {\n\t\tval fileMappings = linkedMapOf<Int, FileMapping>()\n\t\tval iterator = SMAPTokenizer(mappingInfo, \"${SMAP.STRATA_SECTION} $stratum\")\n\t\t// JSR-045 allows the line section to come before the file section, but we don't generate SMAPs like this.\n\t\tif (!iterator.hasNext() || iterator.next() != SMAP.FILE_SECTION) return null\n\n\t\tfor (line in iterator) {\n\t\t\twhen {\n\t\t\t\tline == SMAP.LINE_SECTION -> break\n\t\t\t\tline == SMAP.FILE_SECTION || line == SMAP.END || line.startsWith(SMAP.STRATA_SECTION) -> return null\n\t\t\t}\n\n\t\t\tval indexAndFileInternalName = if (line.startsWith(\"+ \")) line.substring(2) else line\n\t\t\tval fileIndex = indexAndFileInternalName.substringBefore(' ').toInt()\n\t\t\tval fileName = indexAndFileInternalName.substringAfter(' ')\n\t\t\tval path = if (line.startsWith(\"+ \")) iterator.next() else fileName\n\t\t\tfileMappings[fileIndex] = FileMapping(fileName, path)\n\t\t}\n\n\t\tfor (line in iterator) {\n\t\t\twhen {\n\t\t\t\tline == SMAP.LINE_SECTION || line == SMAP.FILE_SECTION -> return null\n\t\t\t\tline == SMAP.END || line.startsWith(SMAP.STRATA_SECTION) -> break\n\t\t\t}\n\n\t\t\t// <source>#<file>,<sourceRange>:<dest>,<destMultiplier>\n\t\t\tval fileSeparator = line.indexOf('#')\n\t\t\tif (fileSeparator < 0) return null\n\t\t\tval destSeparator = line.indexOf(':', fileSeparator)\n\t\t\tif (destSeparator < 0) return null\n\t\t\tval sourceRangeSeparator = line.indexOf(',').let { if (it !in fileSeparator..destSeparator) destSeparator else it }\n\t\t\tval destMultiplierSeparator = line.indexOf(',', destSeparator).let { if (it < 0) line.length else it }\n\n\t\t\tval file = fileMappings[line.substring(fileSeparator + 1, sourceRangeSeparator).toInt()] ?: return null\n\t\t\tval source = line.substring(0, fileSeparator).toInt()\n\t\t\tval dest = line.substring(destSeparator + 1, destMultiplierSeparator).toInt()\n\t\t\tval range = when {\n\t\t\t\t// These two fields have a different meaning, but for compatibility we treat them the same. See `SMAPBuilder`.\n\t\t\t\tdestMultiplierSeparator != line.length -> line.substring(destMultiplierSeparator + 1).toInt()\n\t\t\t\tsourceRangeSeparator != destSeparator -> line.substring(sourceRangeSeparator + 1, destSeparator).toInt()\n\t\t\t\telse -> 1\n\t\t\t}\n\t\t\t// Here we assume that each range in `Kotlin` is entirely within at most one range in `KotlinDebug`.\n\t\t\tfile.mapNewInterval(source, dest, range, callSites?.findRange(dest)?.let { it.mapDestToSource(it.dest) })\n\t\t}\n\n\t\treturn SMAP(fileMappings.values.toList())\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-source-debug-extension/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin",
    "content": "jadx.plugins.kotlin.smap.KotlinSmapPlugin\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-source-debug-extension/src/test/kotlin/TestSourceDebugExtension.kt",
    "content": "package jadx.plugins.kotlin.metadata.tests\n\nimport jadx.plugins.kotlin.smap.KotlinSmapOptions.Companion.CLASS_ALIAS_SOURCE_DBG_OPT\nimport jadx.tests.api.SmaliTest\nimport jadx.tests.api.utils.assertj.JadxAssertions.assertThat\nimport jadx.tests.api.utils.assertj.JadxCodeAssertions\nimport org.junit.jupiter.api.Test\n\nclass TestSourceDebugExtension : SmaliTest() {\n\n\t@Test\n\tfun testRenameClass() {\n\t\tsetupArgs {\n\t\t\tthis[CLASS_ALIAS_SOURCE_DBG_OPT] = true\n\t\t}\n\t\tassertThatClass()\n\t\t\t.containsOne(\"androidx.compose.ui\")\n\t\t\t.containsOne(\"public final class ActualKt\")\n\t\t\t.countString(1, \"reason: from SourceDebugExtension\")\n\t}\n\n\tprivate fun setupArgs(builder: MutableMap<String, Boolean>.() -> Unit = {}) {\n\t\tval allOff = mutableMapOf(\n\t\t\tCLASS_ALIAS_SOURCE_DBG_OPT to false,\n\t\t)\n\t\targs.pluginOptions = allOff.apply(builder).mapValues {\n\t\t\tif (it.value) \"yes\" else \"no\"\n\t\t}\n\t}\n\n\tprivate fun assertThatClass(): JadxCodeAssertions =\n\t\tassertThat(getClassNodeFromSmaliFiles(\"deobf\", \"TestKotlinSourceDebugExtension\", \"C6\"))\n\t\t\t.code()\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-kotlin-source-debug-extension/src/test/smali/deobf/TestKotlinSourceDebugExtension/C6.smali",
    "content": ".class public final Ldeobf/C6;\n.super Ljava/lang/Object;\n.source \"SourceFile\"\n\n\n# annotations\n.annotation runtime Lkotlin/Metadata;\n    d1 = {\n        \"\\u0000\\u000e\\n\\u0002\\u0010\\u0000\\n\\u0002\\u0008\\u0002\\n\\u0002\\u0010\\u000b\\n\\u0000\\u001a\\u0018\\u0010\\u0001\\u001a\\u00020\\u00032\\u0006\\u0010\\u0001\\u001a\\u00020\\u00002\\u0006\\u0010\\u0002\\u001a\\u00020\\u0000H\\u0000\\u00a8\\u0006\\u0004\"\n    }\n    d2 = {\n        \"\",\n        \"a\",\n        \"b\",\n        \"\",\n        \"ui_release\"\n    }\n    k = 0x2\n    mv = {\n        0x1,\n        0x8,\n        0x0\n    }\n.end annotation\n\n.annotation build Lkotlin/jvm/internal/SourceDebugExtension;\n    value = {\n        \"SMAP\\nActual.kt\\nKotlin\\n*S Kotlin\\n*F\\n+ 1 Actual.kt\\nandroidx/compose/ui/ActualKt\\n+ 2 _Arrays.kt\\nkotlin/collections/ArraysKt___ArraysKt\\n+ 3 ListUtils.kt\\nandroidx/compose/ui/util/ListUtilsKt\\n*L\\n1#1,50:1\\n6442#2:51\\n33#3,6:52\\n*S KotlinDebug\\n*F\\n+ 1 Actual.kt\\nandroidx/compose/ui/ActualKt\\n*L\\n35#1:51\\n36#1:52,6\\n*E\\n\"\n    }\n.end annotation\n\n\n# direct methods\n.method public static final a(Ljava/lang/Object;Ljava/lang/Object;)Z\n    .locals 1\n\n    const-string v0, \"a\"\n\n    invoke-static {p0, v0}, Lkotlin/jvm/internal/Intrinsics;->checkNotNullParameter(Ljava/lang/Object;Ljava/lang/String;)V\n\n    const-string v0, \"b\"\n\n    invoke-static {p1, v0}, Lkotlin/jvm/internal/Intrinsics;->checkNotNullParameter(Ljava/lang/Object;Ljava/lang/String;)V\n\n    invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class;\n\n    move-result-object p0\n\n    invoke-virtual {p1}, Ljava/lang/Object;->getClass()Ljava/lang/Class;\n\n    move-result-object p1\n\n    if-ne p0, p1, :cond_0\n\n    const/4 p0, 0x1\n\n    goto :goto_0\n\n    :cond_0\n    const/4 p0, 0x0\n\n    :goto_0\n    return p0\n.end method\n"
  },
  {
    "path": "jadx-plugins/jadx-raung-input/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-library\")\n}\n\ndependencies {\n\tapi(project(\":jadx-core\"))\n\n\timplementation(\"io.github.skylot:raung-asm:0.1.1\")\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-raung-input/src/main/java/jadx/plugins/input/raung/RaungConvert.java",
    "content": "package jadx.plugins.input.raung;\n\nimport java.io.Closeable;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.PathMatcher;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.github.skylot.raung.asm.RaungAsm;\n\npublic class RaungConvert implements Closeable {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(RaungConvert.class);\n\n\t@Nullable\n\tprivate Path tmpJar;\n\n\tpublic boolean execute(List<Path> input) {\n\t\tList<Path> raungInputs = filterRaungFiles(input);\n\t\tif (raungInputs.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tthis.tmpJar = Files.createTempFile(\"jadx-raung-\", \".jar\");\n\t\t\tRaungAsm.create()\n\t\t\t\t\t.output(tmpJar)\n\t\t\t\t\t.inputs(raungInputs)\n\t\t\t\t\t.execute();\n\t\t\treturn true;\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Raung process error\", e);\n\t\t}\n\t\tclose();\n\t\treturn false;\n\t}\n\n\tprivate List<Path> filterRaungFiles(List<Path> input) {\n\t\tPathMatcher matcher = FileSystems.getDefault().getPathMatcher(\"glob:**.raung\");\n\t\treturn input.stream()\n\t\t\t\t.filter(matcher::matches)\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tpublic List<Path> getFiles() {\n\t\tif (tmpJar == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn Collections.singletonList(tmpJar);\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\ttry {\n\t\t\tif (tmpJar != null) {\n\t\t\t\tFiles.deleteIfExists(tmpJar);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to remove tmp jar file: {}\", tmpJar, e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-raung-input/src/main/java/jadx/plugins/input/raung/RaungInputPlugin.java",
    "content": "package jadx.plugins.input.raung;\n\nimport jadx.api.plugins.JadxPlugin;\nimport jadx.api.plugins.JadxPluginContext;\nimport jadx.api.plugins.JadxPluginInfo;\nimport jadx.api.plugins.data.JadxPluginRuntimeData;\nimport jadx.api.plugins.input.data.impl.EmptyCodeLoader;\n\npublic class RaungInputPlugin implements JadxPlugin {\n\n\t@Override\n\tpublic JadxPluginInfo getPluginInfo() {\n\t\treturn new JadxPluginInfo(\"raung-input\", \"Raung Input\", \"Load .raung files\");\n\t}\n\n\t@Override\n\tpublic void init(JadxPluginContext context) {\n\t\tJadxPluginRuntimeData javaInput = context.plugins().getProviding(\"java-input\");\n\t\tcontext.addCodeInput(inputs -> {\n\t\t\tRaungConvert convert = new RaungConvert();\n\t\t\tif (!convert.execute(inputs)) {\n\t\t\t\treturn EmptyCodeLoader.INSTANCE;\n\t\t\t}\n\t\t\treturn javaInput.loadCodeFiles(convert.getFiles(), convert);\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-raung-input/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin",
    "content": "jadx.plugins.input.raung.RaungInputPlugin\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-library\")\n}\n\ndependencies {\n\tapi(project(\":jadx-core\"))\n\n\tapi(\"net.fabricmc:mapping-io:0.8.0\") {\n\t\texclude(\"org.ow2.asm:asm\")\n\t\texclude(\"net.fabricmc:tiny-remapper\")\n\t}\n\n\ttestRuntimeOnly(project(\":jadx-plugins:jadx-dex-input\"))\n\ttestRuntimeOnly(project(\":jadx-plugins:jadx-smali-input\"))\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/RenameMappingsData.java",
    "content": "package jadx.plugins.mappings;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport net.fabricmc.mappingio.tree.MappingTreeView;\n\nimport jadx.api.plugins.input.data.attributes.IJadxAttrType;\nimport jadx.api.plugins.input.data.attributes.IJadxAttribute;\nimport jadx.core.dex.nodes.RootNode;\n\npublic class RenameMappingsData implements IJadxAttribute {\n\n\tprivate static final IJadxAttrType<RenameMappingsData> DATA = IJadxAttrType.create();\n\n\tpublic static @Nullable RenameMappingsData getData(RootNode root) {\n\t\treturn root.getAttributes().get(DATA);\n\t}\n\n\tpublic static @Nullable MappingTreeView getTree(RootNode root) {\n\t\tRenameMappingsData data = getData(root);\n\t\treturn data == null ? null : data.getMappings();\n\t}\n\n\tprivate final MappingTreeView mappings;\n\n\tpublic RenameMappingsData(MappingTreeView mappings) {\n\t\tthis.mappings = mappings;\n\t}\n\n\tpublic MappingTreeView getMappings() {\n\t\treturn mappings;\n\t}\n\n\t@Override\n\tpublic IJadxAttrType<RenameMappingsData> getAttrType() {\n\t\treturn DATA;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/RenameMappingsOptions.java",
    "content": "package jadx.plugins.mappings;\n\nimport java.util.Locale;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport net.fabricmc.mappingio.format.MappingFormat;\n\nimport jadx.api.plugins.options.OptionFlag;\nimport jadx.api.plugins.options.impl.BasePluginOptionsBuilder;\nimport jadx.core.utils.ListUtils;\n\nimport static jadx.plugins.mappings.RenameMappingsPlugin.PLUGIN_ID;\n\npublic class RenameMappingsOptions extends BasePluginOptionsBuilder {\n\n\tpublic static final String INVERT_OPT = PLUGIN_ID + \".invert\";\n\tpublic static final String FORMAT_OPT = PLUGIN_ID + \".format\";\n\n\tprivate boolean invert = false;\n\n\t/**\n\t * null value - used for 'auto' option\n\t */\n\tprivate @Nullable MappingFormat format = null;\n\n\t@Override\n\tpublic void registerOptions() {\n\t\toption(FORMAT_OPT, MappingFormat.class)\n\t\t\t\t.description(\"mapping format\")\n\t\t\t\t.parser(RenameMappingsOptions::parseMappingFormat)\n\t\t\t\t.formatter(v -> v == null ? \"AUTO\" : v.name())\n\t\t\t\t.values(ListUtils.concat(null, MappingFormat.values()))\n\t\t\t\t.defaultValue(null)\n\t\t\t\t.flags(OptionFlag.PER_PROJECT, OptionFlag.DISABLE_IN_GUI)\n\t\t\t\t.setter(v -> format = v);\n\n\t\tboolOption(INVERT_OPT)\n\t\t\t\t.description(\"invert mapping on load\")\n\t\t\t\t.defaultValue(false)\n\t\t\t\t.flags(OptionFlag.PER_PROJECT)\n\t\t\t\t.setter(v -> invert = v);\n\t}\n\n\tprivate static MappingFormat parseMappingFormat(String name) {\n\t\tString upName = name.toUpperCase(Locale.ROOT);\n\t\tif (upName.equals(\"AUTO\")) {\n\t\t\treturn null;\n\t\t}\n\t\treturn MappingFormat.valueOf(upName);\n\t}\n\n\tpublic @Nullable MappingFormat getFormat() {\n\t\treturn format;\n\t}\n\n\tpublic boolean isInvert() {\n\t\treturn invert;\n\t}\n\n\tpublic String getOptionsHashString() {\n\t\treturn format + \":\" + invert;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/RenameMappingsPlugin.java",
    "content": "package jadx.plugins.mappings;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.args.UserRenamesMappingsMode;\nimport jadx.api.plugins.JadxPlugin;\nimport jadx.api.plugins.JadxPluginContext;\nimport jadx.api.plugins.JadxPluginInfo;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.plugins.mappings.load.ApplyMappingsPass;\nimport jadx.plugins.mappings.load.CodeMappingsPass;\nimport jadx.plugins.mappings.load.LoadMappingsPass;\n\npublic class RenameMappingsPlugin implements JadxPlugin {\n\tpublic static final String PLUGIN_ID = \"rename-mappings\";\n\n\tprivate final RenameMappingsOptions options = new RenameMappingsOptions();\n\n\t@Override\n\tpublic JadxPluginInfo getPluginInfo() {\n\t\treturn new JadxPluginInfo(PLUGIN_ID, \"Rename Mappings\", \"various mappings support\");\n\t}\n\n\t@Override\n\tpublic void init(JadxPluginContext context) {\n\t\tcontext.registerOptions(options);\n\t\tJadxArgs args = context.getArgs();\n\t\tif (args.getUserRenamesMappingsMode() == UserRenamesMappingsMode.IGNORE) {\n\t\t\treturn;\n\t\t}\n\t\tPath mappingsPath = args.getUserRenamesMappingsPath();\n\t\tif (mappingsPath == null || !Files.isReadable(mappingsPath)) {\n\t\t\treturn;\n\t\t}\n\t\tcontext.addPass(new LoadMappingsPass(options));\n\t\tcontext.addPass(new ApplyMappingsPass());\n\t\tcontext.addPass(new CodeMappingsPass());\n\n\t\t// use mapping file time modification to check for changes\n\t\tcontext.registerInputsHashSupplier(() -> FileUtils.md5Sum(getInputsHashString(mappingsPath)));\n\t}\n\n\tprivate String getInputsHashString(Path mappingsPath) {\n\t\treturn getFileHashString(mappingsPath) + ':' + options.getOptionsHashString();\n\t}\n\n\tprivate static String getFileHashString(Path mappingsPath) {\n\t\ttry {\n\t\t\treturn mappingsPath.toAbsolutePath().normalize()\n\t\t\t\t\t+ \":\" + Files.getLastModifiedTime(mappingsPath).toMillis();\n\t\t} catch (Exception e) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/load/ApplyMappingsPass.java",
    "content": "package jadx.plugins.mappings.load;\n\nimport net.fabricmc.mappingio.tree.MappingTreeView;\nimport net.fabricmc.mappingio.tree.MappingTreeView.ClassMappingView;\nimport net.fabricmc.mappingio.tree.MappingTreeView.FieldMappingView;\nimport net.fabricmc.mappingio.tree.MappingTreeView.MethodMappingView;\n\nimport jadx.api.plugins.pass.JadxPassInfo;\nimport jadx.api.plugins.pass.impl.OrderedJadxPassInfo;\nimport jadx.api.plugins.pass.types.JadxPreparePass;\nimport jadx.core.codegen.TypeGen;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.plugins.mappings.RenameMappingsData;\n\npublic class ApplyMappingsPass implements JadxPreparePass {\n\n\t@Override\n\tpublic JadxPassInfo getInfo() {\n\t\treturn new OrderedJadxPassInfo(\n\t\t\t\t\"ApplyMappings\",\n\t\t\t\t\"Apply mappings to classes, fields and methods\")\n\t\t\t\t\t\t.after(\"LoadMappings\")\n\t\t\t\t\t\t.before(\"RenameVisitor\");\n\t}\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tRenameMappingsData data = RenameMappingsData.getData(root);\n\t\tif (data == null) {\n\t\t\treturn;\n\t\t}\n\t\tMappingTreeView mappingTree = data.getMappings();\n\t\tprocess(root, mappingTree);\n\t\troot.registerCodeDataUpdateListener(codeData -> process(root, mappingTree));\n\t}\n\n\tprivate void process(RootNode root, MappingTreeView mappingTree) {\n\t\tfor (ClassNode cls : root.getClasses()) {\n\t\t\tString clsRawName = cls.getClassInfo().getRawName().replace('.', '/');\n\t\t\tClassMappingView mapping = mappingTree.getClass(clsRawName);\n\t\t\tif (mapping != null) {\n\t\t\t\tprocessClass(cls, mapping);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void processClass(ClassNode cls, ClassMappingView classMapping) {\n\t\tString alias = classMapping.getDstName(0);\n\t\tif (alias != null) {\n\t\t\tcls.rename(alias.replace('/', '.'));\n\t\t}\n\t\tif (classMapping.getComment() != null) {\n\t\t\tcls.addCodeComment(classMapping.getComment());\n\t\t}\n\t\tfor (FieldNode field : cls.getFields()) {\n\t\t\tFieldInfo fieldInfo = field.getFieldInfo();\n\t\t\tString signature = TypeGen.signature(fieldInfo.getType());\n\t\t\tFieldMappingView fieldMapping = classMapping.getField(fieldInfo.getName(), signature);\n\t\t\tif (fieldMapping != null) {\n\t\t\t\tprocessField(field, fieldMapping);\n\t\t\t}\n\t\t}\n\t\tfor (MethodNode method : cls.getMethods()) {\n\t\t\tMethodInfo methodInfo = method.getMethodInfo();\n\t\t\tString methodName = methodInfo.getName();\n\t\t\tString methodDesc = methodInfo.getShortId().substring(methodName.length());\n\t\t\tMethodMappingView methodMapping = classMapping.getMethod(methodName, methodDesc);\n\t\t\tif (methodMapping != null) {\n\t\t\t\tprocessMethod(method, methodMapping);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void processField(FieldNode field, FieldMappingView fieldMapping) {\n\t\tString alias = fieldMapping.getDstName(0);\n\t\tif (alias != null) {\n\t\t\tfield.rename(alias);\n\t\t}\n\t\tString comment = fieldMapping.getComment();\n\t\tif (comment != null) {\n\t\t\tfield.addCodeComment(comment);\n\t\t}\n\t}\n\n\tprivate static void processMethod(MethodNode method, MethodMappingView methodMapping) {\n\t\tString alias = methodMapping.getDstName(0);\n\t\tif (alias != null) {\n\t\t\tmethod.rename(alias);\n\t\t}\n\t\tString comment = methodMapping.getComment();\n\t\tif (comment != null) {\n\t\t\tmethod.addCodeComment(comment);\n\t\t}\n\t\t// Method args & vars are handled in CodeMappingsPass\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/load/CodeMappingsPass.java",
    "content": "package jadx.plugins.mappings.load;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport net.fabricmc.mappingio.tree.MappingTreeView;\nimport net.fabricmc.mappingio.tree.MappingTreeView.ClassMappingView;\nimport net.fabricmc.mappingio.tree.MappingTreeView.MethodArgMappingView;\nimport net.fabricmc.mappingio.tree.MappingTreeView.MethodMappingView;\n\nimport jadx.api.plugins.pass.JadxPassInfo;\nimport jadx.api.plugins.pass.impl.OrderedJadxPassInfo;\nimport jadx.api.plugins.pass.types.JadxDecompilePass;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.plugins.mappings.RenameMappingsData;\nimport jadx.plugins.mappings.utils.DalvikToJavaBytecodeUtils;\n\npublic class CodeMappingsPass implements JadxDecompilePass {\n\tprivate Map<String, ClassMappingView> clsRenamesMap;\n\n\t@Override\n\tpublic JadxPassInfo getInfo() {\n\t\treturn new OrderedJadxPassInfo(\n\t\t\t\t\"CodeMappings\",\n\t\t\t\t\"Apply mappings to method args and vars\")\n\t\t\t\t\t\t.before(\"CodeRenameVisitor\");\n\t}\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tRenameMappingsData data = RenameMappingsData.getData(root);\n\t\tif (data == null) {\n\t\t\treturn;\n\t\t}\n\t\tMappingTreeView mappingTree = data.getMappings();\n\t\tupdateMappingsMap(mappingTree);\n\t\troot.registerCodeDataUpdateListener(codeData -> updateMappingsMap(mappingTree));\n\t}\n\n\t@Override\n\tpublic boolean visit(ClassNode cls) {\n\t\tClassMappingView classMapping = getMapping(cls);\n\t\tif (classMapping != null) {\n\t\t\tapplyRenames(cls, classMapping);\n\t\t}\n\t\tcls.getInnerClasses().forEach(this::visit);\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void visit(MethodNode mth) {\n\t}\n\n\tprivate static void applyRenames(ClassNode cls, ClassMappingView classMapping) {\n\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\tString methodName = mth.getMethodInfo().getName();\n\t\t\tString methodDesc = mth.getMethodInfo().getShortId().substring(methodName.length());\n\t\t\tList<SSAVar> ssaVars = mth.getSVars();\n\t\t\tif (ssaVars.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tMethodMappingView methodMapping = classMapping.getMethod(methodName, methodDesc);\n\t\t\tif (methodMapping == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Method args\n\t\t\tfor (MethodArgMappingView argMapping : methodMapping.getArgs()) {\n\t\t\t\tInteger mappingLvIndex = argMapping.getLvIndex();\n\t\t\t\tfor (SSAVar ssaVar : ssaVars) {\n\t\t\t\t\tInteger actualLvIndex = DalvikToJavaBytecodeUtils.getMethodArgLvIndex(ssaVar, mth);\n\t\t\t\t\tif (actualLvIndex.equals(mappingLvIndex)) {\n\t\t\t\t\t\tssaVar.getCodeVar().setName(argMapping.getDstName(0));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// TODO: Method vars (if ever feasible)\n\t\t}\n\t}\n\n\tprivate ClassMappingView getMapping(ClassNode cls) {\n\t\tif (clsRenamesMap == null || clsRenamesMap.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tString classPath = cls.getClassInfo().makeRawFullName().replace('.', '/');\n\t\treturn clsRenamesMap.get(classPath);\n\t}\n\n\tprivate void updateMappingsMap(MappingTreeView mappings) {\n\t\tclsRenamesMap = new HashMap<>();\n\t\tfor (ClassMappingView cls : mappings.getClasses()) {\n\t\t\tfor (MethodMappingView mth : cls.getMethods()) {\n\t\t\t\tif (!mth.getArgs().isEmpty() || !mth.getVars().isEmpty()) {\n\t\t\t\t\tclsRenamesMap.put(cls.getSrcName(), cls);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/load/LoadMappingsPass.java",
    "content": "package jadx.plugins.mappings.load;\n\nimport java.nio.file.Path;\nimport java.util.Collections;\n\nimport net.fabricmc.mappingio.MappingReader;\nimport net.fabricmc.mappingio.MappingUtil;\nimport net.fabricmc.mappingio.adapter.MappingSourceNsSwitch;\nimport net.fabricmc.mappingio.tree.MappingTreeView;\nimport net.fabricmc.mappingio.tree.MemoryMappingTree;\nimport net.fabricmc.mappingio.tree.VisitableMappingTree;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.plugins.pass.JadxPassInfo;\nimport jadx.api.plugins.pass.impl.SimpleJadxPassInfo;\nimport jadx.api.plugins.pass.types.JadxPreparePass;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.plugins.mappings.RenameMappingsData;\nimport jadx.plugins.mappings.RenameMappingsOptions;\n\npublic class LoadMappingsPass implements JadxPreparePass {\n\n\tprivate final RenameMappingsOptions options;\n\n\tpublic LoadMappingsPass(RenameMappingsOptions options) {\n\t\tthis.options = options;\n\t}\n\n\t@Override\n\tpublic JadxPassInfo getInfo() {\n\t\treturn new SimpleJadxPassInfo(\"LoadMappings\", \"Load mappings file\");\n\t}\n\n\t@Override\n\tpublic void init(RootNode root) {\n\t\tMappingTreeView mappings = loadMapping(root.getArgs());\n\t\troot.getAttributes().add(new RenameMappingsData(mappings));\n\t}\n\n\tprivate MappingTreeView loadMapping(JadxArgs args) {\n\t\ttry {\n\t\t\tPath mappingsPath = args.getUserRenamesMappingsPath();\n\t\t\tVisitableMappingTree mappingTree = new MemoryMappingTree();\n\t\t\tMappingReader.read(mappingsPath, options.getFormat(), mappingTree);\n\t\t\tif (mappingTree.getSrcNamespace() == null) {\n\t\t\t\tmappingTree.setSrcNamespace(MappingUtil.NS_SOURCE_FALLBACK);\n\t\t\t}\n\t\t\tif (mappingTree.getDstNamespaces() == null || mappingTree.getDstNamespaces().isEmpty()) {\n\t\t\t\tmappingTree.setDstNamespaces(Collections.singletonList(MappingUtil.NS_TARGET_FALLBACK));\n\t\t\t} else if (mappingTree.getDstNamespaces().size() > 1) {\n\t\t\t\tthrow new JadxRuntimeException(\n\t\t\t\t\t\tString.format(\"JADX only supports mappings with just one destination namespace! The provided ones have %s.\",\n\t\t\t\t\t\t\t\tmappingTree.getDstNamespaces().size()));\n\t\t\t}\n\t\t\tif (options.isInvert()) {\n\t\t\t\tVisitableMappingTree invertedMappingTree = new MemoryMappingTree();\n\t\t\t\tString dstNamespace = mappingTree.getDstNamespaces().get(0);\n\t\t\t\tmappingTree.accept(new MappingSourceNsSwitch(invertedMappingTree, dstNamespace));\n\t\t\t\treturn invertedMappingTree;\n\t\t\t}\n\t\t\treturn mappingTree;\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to load mappings\", e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/save/MappingExporter.java",
    "content": "package jadx.plugins.mappings.save;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport net.fabricmc.mappingio.MappedElementKind;\nimport net.fabricmc.mappingio.MappingUtil;\nimport net.fabricmc.mappingio.MappingWriter;\nimport net.fabricmc.mappingio.format.MappingFormat;\nimport net.fabricmc.mappingio.tree.MappingTreeView;\nimport net.fabricmc.mappingio.tree.MemoryMappingTree;\nimport net.fabricmc.mappingio.tree.VisitOrder;\nimport net.fabricmc.mappingio.tree.VisitableMappingTree;\n\nimport jadx.api.data.ICodeComment;\nimport jadx.api.data.ICodeRename;\nimport jadx.api.data.IJavaNodeRef.RefType;\nimport jadx.api.data.impl.JadxCodeData;\nimport jadx.api.data.impl.JadxCodeRef;\nimport jadx.api.metadata.annotations.VarNode;\nimport jadx.core.Consts;\nimport jadx.core.codegen.TypeGen;\nimport jadx.core.dex.info.ClassInfo;\nimport jadx.core.dex.info.FieldInfo;\nimport jadx.core.dex.info.MethodInfo;\nimport jadx.core.dex.nodes.ClassNode;\nimport jadx.core.dex.nodes.FieldNode;\nimport jadx.core.dex.nodes.MethodNode;\nimport jadx.core.dex.nodes.RootNode;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.plugins.mappings.RenameMappingsData;\nimport jadx.plugins.mappings.utils.DalvikToJavaBytecodeUtils;\nimport jadx.plugins.mappings.utils.VariablesUtils;\n\npublic class MappingExporter {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(MappingExporter.class);\n\n\tprivate final RootNode root;\n\tprivate final @Nullable MappingTreeView loadedMappingTree;\n\n\tpublic MappingExporter(RootNode root) {\n\t\tthis.root = root;\n\t\tthis.loadedMappingTree = RenameMappingsData.getTree(this.root);\n\t}\n\n\tpublic void exportMappings(Path path, JadxCodeData codeData, MappingFormat mappingFormat) {\n\t\tVisitableMappingTree mappingTree = new MemoryMappingTree();\n\t\t// Map < SrcName >\n\t\tSet<String> mappedClasses = new HashSet<>();\n\t\t// Map < DeclClass + ShortId >\n\t\tSet<String> mappedFields = new HashSet<>();\n\t\tSet<String> mappedMethods = new HashSet<>();\n\t\tSet<String> methodsWithMappedElements = new HashSet<>();\n\t\t// Map < DeclClass + MethodShortId + CodeRef, NewName >\n\t\tMap<String, String> mappedMethodArgsAndVars = new HashMap<>();\n\t\t// Map < DeclClass [+ ShortId] [+ CodeRef], Comment >\n\t\tMap<String, String> comments = new HashMap<>();\n\n\t\t// We have to do this so we know for sure which elements are *manually* renamed\n\t\tfor (ICodeRename codeRename : codeData.getRenames()) {\n\t\t\tif (codeRename.getNodeRef().getType().equals(RefType.CLASS)) {\n\t\t\t\tmappedClasses.add(codeRename.getNodeRef().getDeclaringClass());\n\t\t\t} else if (codeRename.getNodeRef().getType().equals(RefType.FIELD)) {\n\t\t\t\tmappedFields.add(codeRename.getNodeRef().getDeclaringClass() + codeRename.getNodeRef().getShortId());\n\t\t\t} else if (codeRename.getNodeRef().getType().equals(RefType.METHOD)) {\n\t\t\t\tif (codeRename.getCodeRef() == null) {\n\t\t\t\t\tmappedMethods.add(codeRename.getNodeRef().getDeclaringClass() + codeRename.getNodeRef().getShortId());\n\t\t\t\t} else {\n\t\t\t\t\tmethodsWithMappedElements.add(codeRename.getNodeRef().getDeclaringClass() + codeRename.getNodeRef().getShortId());\n\t\t\t\t\tmappedMethodArgsAndVars.put(codeRename.getNodeRef().getDeclaringClass()\n\t\t\t\t\t\t\t+ codeRename.getNodeRef().getShortId()\n\t\t\t\t\t\t\t+ codeRename.getCodeRef(),\n\t\t\t\t\t\t\tcodeRename.getNewName());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (ICodeComment codeComment : codeData.getComments()) {\n\t\t\tcomments.put(codeComment.getNodeRef().getDeclaringClass()\n\t\t\t\t\t+ (codeComment.getNodeRef().getShortId() == null ? \"\" : codeComment.getNodeRef().getShortId())\n\t\t\t\t\t+ (codeComment.getCodeRef() == null ? \"\" : codeComment.getCodeRef()),\n\t\t\t\t\tcodeComment.getComment());\n\t\t\tif (codeComment.getCodeRef() != null) {\n\t\t\t\tmethodsWithMappedElements.add(codeComment.getNodeRef().getDeclaringClass() + codeComment.getNodeRef().getShortId());\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tString srcNamespace = MappingUtil.NS_SOURCE_FALLBACK;\n\t\t\tString dstNamespace = MappingUtil.NS_TARGET_FALLBACK;\n\n\t\t\t// Copy mappings from potentially imported mappings file\n\t\t\tif (loadedMappingTree != null && loadedMappingTree.getDstNamespaces() != null) {\n\t\t\t\tloadedMappingTree.accept(mappingTree);\n\t\t\t}\n\n\t\t\tmappingTree.visitHeader();\n\t\t\tmappingTree.visitNamespaces(srcNamespace, Collections.singletonList(dstNamespace));\n\t\t\tmappingTree.visitContent();\n\n\t\t\tfor (ClassNode cls : root.getClasses()) {\n\t\t\t\tClassInfo classInfo = cls.getClassInfo();\n\t\t\t\tString classPath = classInfo.makeRawFullName().replace('.', '/');\n\t\t\t\tString rawClassName = classInfo.getRawName();\n\n\t\t\t\tif (classInfo.hasAlias()\n\t\t\t\t\t\t&& !classInfo.getAliasShortName().equals(classInfo.getShortName())\n\t\t\t\t\t\t&& mappedClasses.contains(rawClassName)) {\n\t\t\t\t\tmappingTree.visitClass(classPath);\n\t\t\t\t\tString alias = classInfo.makeAliasRawFullName().replace('.', '/');\n\n\t\t\t\t\tif (alias.startsWith(Consts.DEFAULT_PACKAGE_NAME)) {\n\t\t\t\t\t\talias = alias.substring(Consts.DEFAULT_PACKAGE_NAME.length() + 1);\n\t\t\t\t\t}\n\t\t\t\t\tmappingTree.visitDstName(MappedElementKind.CLASS, 0, alias);\n\t\t\t\t}\n\t\t\t\tif (comments.containsKey(rawClassName)) {\n\t\t\t\t\tmappingTree.visitClass(classPath);\n\t\t\t\t\tmappingTree.visitComment(MappedElementKind.CLASS, comments.get(rawClassName));\n\t\t\t\t}\n\n\t\t\t\tfor (FieldNode fld : cls.getFields()) {\n\t\t\t\t\tFieldInfo fieldInfo = fld.getFieldInfo();\n\t\t\t\t\tif (fieldInfo.hasAlias() && mappedFields.contains(rawClassName + fieldInfo.getShortId())) {\n\t\t\t\t\t\tvisitField(mappingTree, classPath, fieldInfo.getName(), TypeGen.signature(fieldInfo.getType()));\n\t\t\t\t\t\tmappingTree.visitDstName(MappedElementKind.FIELD, 0, fieldInfo.getAlias());\n\t\t\t\t\t}\n\t\t\t\t\tif (comments.containsKey(rawClassName + fieldInfo.getShortId())) {\n\t\t\t\t\t\tvisitField(mappingTree, classPath, fieldInfo.getName(), TypeGen.signature(fieldInfo.getType()));\n\t\t\t\t\t\tmappingTree.visitComment(MappedElementKind.FIELD, comments.get(rawClassName + fieldInfo.getShortId()));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (MethodNode mth : cls.getMethods()) {\n\t\t\t\t\tMethodInfo methodInfo = mth.getMethodInfo();\n\t\t\t\t\tString methodName = methodInfo.getName();\n\t\t\t\t\tString methodDesc = methodInfo.getShortId().substring(methodName.length());\n\t\t\t\t\tif (methodInfo.hasAlias() && mappedMethods.contains(rawClassName + methodInfo.getShortId())) {\n\t\t\t\t\t\tvisitMethod(mappingTree, classPath, methodName, methodDesc);\n\t\t\t\t\t\tmappingTree.visitDstName(MappedElementKind.METHOD, 0, methodInfo.getAlias());\n\t\t\t\t\t}\n\t\t\t\t\tif (comments.containsKey(rawClassName + methodInfo.getShortId())) {\n\t\t\t\t\t\tvisitMethod(mappingTree, classPath, methodName, methodDesc);\n\t\t\t\t\t\tmappingTree.visitComment(MappedElementKind.METHOD, comments.get(rawClassName + methodInfo.getShortId()));\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!methodsWithMappedElements.contains(rawClassName + methodInfo.getShortId())) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// Method args\n\t\t\t\t\tint lvtIndex = mth.getAccessFlags().isStatic() ? 0 : 1;\n\t\t\t\t\tList<VarNode> args = mth.collectArgNodes();\n\t\t\t\t\tfor (VarNode arg : args) {\n\t\t\t\t\t\tInteger lvIndex = DalvikToJavaBytecodeUtils.getMethodArgLvIndex(arg);\n\t\t\t\t\t\tif (lvIndex == null) {\n\t\t\t\t\t\t\tlvIndex = -1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tString key = rawClassName + methodInfo.getShortId()\n\t\t\t\t\t\t\t\t+ JadxCodeRef.forVar(arg.getReg(), arg.getSsa());\n\t\t\t\t\t\tif (mappedMethodArgsAndVars.containsKey(key)) {\n\t\t\t\t\t\t\tvisitMethodArg(mappingTree, classPath, methodName, methodDesc, args.indexOf(arg), lvIndex);\n\t\t\t\t\t\t\tmappingTree.visitDstName(MappedElementKind.METHOD_ARG, 0, mappedMethodArgsAndVars.get(key));\n\t\t\t\t\t\t\tmappedMethodArgsAndVars.remove(key);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlvtIndex++;\n\t\t\t\t\t\t// Not checking for comments since method args can't have any\n\t\t\t\t\t}\n\t\t\t\t\t// Method vars\n\t\t\t\t\tfor (VariablesUtils.VarInfo info : VariablesUtils.collect(mth)) {\n\t\t\t\t\t\tVarNode var = info.getVar();\n\t\t\t\t\t\tint startOpIdx = info.getStartOpIdx();\n\t\t\t\t\t\tint endOpIdx = info.getEndOpIdx();\n\t\t\t\t\t\tint lvIndex = DalvikToJavaBytecodeUtils.getMethodVarLvIndex(var);\n\t\t\t\t\t\tString key = rawClassName + methodInfo.getShortId()\n\t\t\t\t\t\t\t\t+ JadxCodeRef.forVar(var.getReg(), var.getSsa());\n\t\t\t\t\t\tif (mappedMethodArgsAndVars.containsKey(key)) {\n\t\t\t\t\t\t\tvisitMethodVar(mappingTree, classPath, methodName, methodDesc, lvtIndex, lvIndex, startOpIdx, endOpIdx);\n\t\t\t\t\t\t\tmappingTree.visitDstName(MappedElementKind.METHOD_VAR, 0, mappedMethodArgsAndVars.get(key));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tkey = rawClassName + methodInfo.getShortId() + JadxCodeRef.forInsn(startOpIdx);\n\t\t\t\t\t\tif (comments.containsKey(key)) {\n\t\t\t\t\t\t\tvisitMethodVar(mappingTree, classPath, methodName, methodDesc, lvtIndex, lvIndex, startOpIdx, endOpIdx);\n\t\t\t\t\t\t\tmappingTree.visitComment(MappedElementKind.METHOD_VAR, comments.get(key));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlvtIndex++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// write file as late as possible because a mapping collection can fail with exception\n\t\t\tif (mappingFormat.hasSingleFile()) {\n\t\t\t\tFileUtils.deleteFileIfExists(path);\n\t\t\t\tFileUtils.makeDirsForFile(path);\n\t\t\t\tFiles.createFile(path);\n\t\t\t} else {\n\t\t\t\tFileUtils.makeDirs(path);\n\t\t\t}\n\t\t\t// Write file\n\t\t\tmappingTree.accept(MappingWriter.create(path, mappingFormat), VisitOrder.createByName());\n\t\t\tmappingTree.visitEnd();\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to save deobfuscation map file '{}'\", path.toAbsolutePath(), e);\n\t\t}\n\t}\n\n\tprivate void visitField(VisitableMappingTree tree, String classPath, String srcName, String srcDesc) throws IOException {\n\t\ttree.visitClass(classPath);\n\t\ttree.visitField(srcName, srcDesc);\n\t}\n\n\tprivate void visitMethod(VisitableMappingTree tree, String classPath, String srcName, String srcDesc) throws IOException {\n\t\ttree.visitClass(classPath);\n\t\ttree.visitMethod(srcName, srcDesc);\n\t}\n\n\tprivate void visitMethodArg(VisitableMappingTree tree, String classPath, String methodSrcName, String methodSrcDesc, int argPosition,\n\t\t\tint lvIndex) throws IOException {\n\t\tvisitMethod(tree, classPath, methodSrcName, methodSrcDesc);\n\t\ttree.visitMethodArg(argPosition, lvIndex, null);\n\t}\n\n\tprivate void visitMethodVar(VisitableMappingTree tree, String classPath, String methodSrcName, String methodSrcDesc, int lvtIndex,\n\t\t\tint lvIndex, int startOpIdx, int endOpIdx) throws IOException {\n\t\tvisitMethod(tree, classPath, methodSrcName, methodSrcDesc);\n\t\ttree.visitMethodVar(lvtIndex, lvIndex, startOpIdx, endOpIdx, null);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/utils/DalvikToJavaBytecodeUtils.java",
    "content": "package jadx.plugins.mappings.utils;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.api.metadata.annotations.VarNode;\nimport jadx.core.dex.instructions.args.RegisterArg;\nimport jadx.core.dex.instructions.args.SSAVar;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic class DalvikToJavaBytecodeUtils {\n\n\t// ****************************\n\t// Local variable index\n\t// ****************************\n\n\t// Method args\n\n\tpublic static Integer getMethodArgLvIndex(VarNode methodArg) {\n\t\tMethodNode mth = methodArg.getMth();\n\t\tInteger lvIndex = getMethodArgLvIndexViaSsaVars(methodArg.getReg(), mth);\n\t\tif (lvIndex != null) {\n\t\t\treturn lvIndex;\n\t\t}\n\t\tList<VarNode> args = mth.collectArgNodes();\n\t\tfor (VarNode arg : args) {\n\t\t\tlvIndex = arg.getReg() - args.get(0).getReg() + (mth.getAccessFlags().isStatic() ? 0 : 1);\n\t\t\tif (arg.equals(methodArg)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn lvIndex;\n\t}\n\n\tpublic static Integer getMethodArgLvIndex(SSAVar methodArgSsaVar, MethodNode mth) {\n\t\treturn getMethodArgLvIndexViaSsaVars(methodArgSsaVar.getRegNum(), mth);\n\t}\n\n\tprivate static Integer getMethodArgLvIndexViaSsaVars(int regNum, MethodNode mth) {\n\t\tList<SSAVar> ssaVars = mth.getSVars();\n\t\tif (!ssaVars.isEmpty()) {\n\t\t\treturn regNum - ssaVars.get(0).getRegNum();\n\t\t}\n\t\treturn null;\n\t}\n\n\t// Method vars\n\n\tpublic static int getMethodVarLvIndex(VarNode methodVar) {\n\t\tMethodNode mth = methodVar.getMth();\n\t\tInteger lvIndex = getMethodVarLvIndexViaSsaVars(methodVar.getReg(), mth);\n\t\tif (lvIndex != null) {\n\t\t\treturn lvIndex;\n\t\t}\n\t\tInteger lastArgLvIndex = mth.getAccessFlags().isStatic() ? -1 : 0;\n\t\tList<VarNode> args = mth.collectArgNodes();\n\t\tif (!args.isEmpty()) {\n\t\t\tlastArgLvIndex = getMethodArgLvIndex(args.get(args.size() - 1));\n\t\t}\n\t\treturn lastArgLvIndex + methodVar.getReg() + (mth.getAccessFlags().isStatic() ? 0 : 1);\n\t}\n\n\tpublic static Integer getMethodVarLvIndex(SSAVar methodVarSsaVar, MethodNode mth) {\n\t\treturn getMethodVarLvIndexViaSsaVars(methodVarSsaVar.getRegNum(), mth);\n\t}\n\n\tprivate static Integer getMethodVarLvIndexViaSsaVars(int regNum, MethodNode mth) {\n\t\tList<SSAVar> ssaVars = mth.getSVars();\n\t\tif (ssaVars.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tInteger lastArgLvIndex = mth.getAccessFlags().isStatic() ? -1 : 0;\n\t\tList<RegisterArg> args = mth.getArgRegs();\n\t\tif (!args.isEmpty()) {\n\t\t\tlastArgLvIndex = getMethodArgLvIndexViaSsaVars(args.get(args.size() - 1).getSVar().getRegNum(), mth);\n\t\t}\n\t\treturn lastArgLvIndex + regNum + (mth.getAccessFlags().isStatic() ? 0 : 1);\n\t}\n\n\t// ****************************\n\t// Local variable table index\n\t// ****************************\n\n\t// Method args\n\n\tpublic static Integer getMethodArgLvtIndex(VarNode methodArg) {\n\t\tMethodNode mth = methodArg.getMth();\n\t\tint lvtIndex = mth.getAccessFlags().isStatic() ? 0 : 1;\n\t\tList<VarNode> args = mth.collectArgNodes();\n\t\tfor (VarNode arg : args) {\n\t\t\tif (arg.equals(methodArg)) {\n\t\t\t\treturn lvtIndex;\n\t\t\t}\n\t\t\tlvtIndex++;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static Integer getMethodArgLvtIndex(SSAVar methodArgSsaVar, MethodNode mth) {\n\t\tList<SSAVar> ssaVars = mth.getSVars();\n\t\tif (ssaVars.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tList<RegisterArg> args = mth.getArgRegs();\n\t\tint lvtIndex = mth.getAccessFlags().isStatic() ? 0 : 1;\n\t\tfor (RegisterArg arg : args) {\n\t\t\tif (arg.getSVar().equals(methodArgSsaVar)) {\n\t\t\t\treturn lvtIndex;\n\t\t\t}\n\t\t\tlvtIndex++;\n\t\t}\n\t\treturn null;\n\t}\n\n\t// Method vars\n\n\t// TODO: public static Integer getMethodVarLvtIndex(VarNode methodVar) {}\n\n\tpublic static Integer getMethodVarLvtIndex(SSAVar methodVarSsaVar, MethodNode mth) {\n\t\tList<SSAVar> ssaVars = new ArrayList<>(mth.getSVars());\n\t\tif (ssaVars.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tInteger lvtIndex = getMethodArgLvtIndex(methodVarSsaVar, mth);\n\t\tif (lvtIndex != null) {\n\t\t\treturn lvtIndex;\n\t\t}\n\n\t\tlvtIndex = mth.getAccessFlags().isStatic() ? 0 : 1;\n\t\tlvtIndex += mth.getArgTypes().size();\n\n\t\tlvtIndex = getMethodArgLvtIndex(methodVarSsaVar, mth) + 1;\n\t\tssaVars.subList(0, ssaVars.indexOf(methodVarSsaVar) + 1).clear();\n\n\t\tint lastRegNum = -1;\n\t\tfor (SSAVar ssaVar : ssaVars) {\n\t\t\tif (ssaVar.getRegNum() == lastRegNum) {\n\t\t\t\t// Not present in bytecode\n\t\t\t\t// System.out.println(\"Duplicate RegNum: \" + ssaVar.getRegNum());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlvtIndex++;\n\t\t\tif (ssaVar.equals(methodVarSsaVar)) {\n\t\t\t\treturn lvtIndex;\n\t\t\t}\n\t\t\tlastRegNum = ssaVar.getRegNum();\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/utils/VariablesUtils.java",
    "content": "package jadx.plugins.mappings.utils;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.ICodeInfo;\nimport jadx.api.metadata.ICodeAnnotation;\nimport jadx.api.metadata.ICodeNodeRef;\nimport jadx.api.metadata.annotations.InsnCodeOffset;\nimport jadx.api.metadata.annotations.NodeDeclareRef;\nimport jadx.api.metadata.annotations.VarNode;\nimport jadx.api.utils.CodeUtils;\nimport jadx.core.dex.nodes.MethodNode;\n\npublic class VariablesUtils {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(VariablesUtils.class);\n\n\tpublic static class VarInfo {\n\t\tprivate final VarNode var;\n\t\tprivate final int startOpIdx;\n\t\tprivate int endOpIdx;\n\n\t\tpublic VarInfo(VarNode var, int startOpIdx) {\n\t\t\tthis.var = var;\n\t\t\tthis.startOpIdx = startOpIdx;\n\t\t\tthis.endOpIdx = startOpIdx;\n\t\t}\n\n\t\tpublic VarNode getVar() {\n\t\t\treturn var;\n\t\t}\n\n\t\tpublic int getStartOpIdx() {\n\t\t\treturn startOpIdx;\n\t\t}\n\n\t\tpublic int getEndOpIdx() {\n\t\t\treturn endOpIdx;\n\t\t}\n\n\t\tpublic void setEndOpIdx(int endOpIdx) {\n\t\t\tthis.endOpIdx = endOpIdx;\n\t\t}\n\t}\n\n\tpublic static List<VarInfo> collect(MethodNode mth) {\n\t\tICodeInfo codeInfo = mth.getTopParentClass().getCode();\n\t\tint mthDefPos = mth.getDefPosition();\n\t\tint mthLineEndPos = CodeUtils.getLineEndForPos(codeInfo.getCodeStr(), mthDefPos);\n\t\tCodeVisitor codeVisitor = new CodeVisitor(mth);\n\t\tcodeInfo.getCodeMetadata().searchDown(mthLineEndPos, codeVisitor::process);\n\t\treturn codeVisitor.getVars();\n\t}\n\n\tprivate static class CodeVisitor {\n\t\tprivate final MethodNode mth;\n\t\tprivate final List<VarInfo> vars = new ArrayList<>();\n\t\tprivate int lastOffset = -1;\n\n\t\tpublic CodeVisitor(MethodNode mth) {\n\t\t\tthis.mth = mth;\n\t\t}\n\n\t\tpublic @Nullable Boolean process(Integer pos, ICodeAnnotation ann) {\n\t\t\tif (ann instanceof InsnCodeOffset) {\n\t\t\t\tlastOffset = ((InsnCodeOffset) ann).getOffset();\n\t\t\t}\n\t\t\tif (ann instanceof NodeDeclareRef) {\n\t\t\t\tICodeNodeRef declRef = ((NodeDeclareRef) ann).getNode();\n\t\t\t\tif (declRef instanceof VarNode) {\n\t\t\t\t\tVarNode varNode = (VarNode) declRef;\n\t\t\t\t\tif (!varNode.getMth().equals(mth)) { // Stop if we've gone too far and have entered a different method\n\t\t\t\t\t\tif (!vars.isEmpty()) {\n\t\t\t\t\t\t\tvars.get(vars.size() - 1).setEndOpIdx(declRef.getDefPosition() - 1);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn Boolean.TRUE;\n\t\t\t\t\t}\n\t\t\t\t\tif (lastOffset != -1) {\n\t\t\t\t\t\tif (!vars.isEmpty()) {\n\t\t\t\t\t\t\tvars.get(vars.size() - 1).setEndOpIdx(lastOffset - 1);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tvars.add(new VarInfo(varNode, lastOffset));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tLOG.warn(\"Local variable not present in bytecode, skipping: {}#{}\",\n\t\t\t\t\t\t\t\tmth.getMethodInfo().getRawFullId(), varNode.getName());\n\t\t\t\t\t}\n\t\t\t\t\tlastOffset = -1;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic List<VarInfo> getVars() {\n\t\t\treturn vars;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin",
    "content": "jadx.plugins.mappings.RenameMappingsPlugin\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/src/test/java/jadx/plugins/mappings/BaseRenameMappingsTest.java",
    "content": "package jadx.plugins.mappings;\n\nimport java.io.File;\nimport java.net.URL;\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.JadxArgs;\nimport jadx.api.JavaClass;\nimport jadx.api.plugins.loader.JadxBasePluginLoader;\nimport jadx.core.plugins.files.SingleDirFilesGetter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class BaseRenameMappingsTest {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(BaseRenameMappingsTest.class);\n\n\t@TempDir\n\tPath testDir;\n\n\tPath outputDir;\n\n\tJadxArgs jadxArgs;\n\n\tString testResDir = \"\";\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\toutputDir = testDir.resolve(\"output\");\n\t\tjadxArgs = new JadxArgs();\n\t\tjadxArgs.setOutDir(outputDir.toFile());\n\t\tjadxArgs.setFilesGetter(new SingleDirFilesGetter(testDir));\n\t\tjadxArgs.setPluginLoader(new JadxBasePluginLoader());\n\t}\n\n\tpublic File loadResourceFile(String fileName) {\n\t\tString path = testResDir + '/' + fileName;\n\t\ttry {\n\t\t\tURL resource = getClass().getClassLoader().getResource(path);\n\t\t\tassertThat(resource).isNotNull();\n\t\t\treturn new File(resource.getFile());\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to load resource file: \" + path, e);\n\t\t}\n\t}\n\n\tpublic void printClassesCode(List<JavaClass> classes) {\n\t\tLOG.debug(\"Printing code for {} classes:\", classes.size());\n\t\tfor (JavaClass jCls : classes) {\n\t\t\tLOG.debug(\"Class: {}\\n{}\\n---\\n\", jCls.getFullName(), jCls.getCode());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/src/test/java/jadx/plugins/mappings/TestInnerClassRename.java",
    "content": "package jadx.plugins.mappings;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport jadx.api.JadxDecompiler;\nimport jadx.api.JavaClass;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass TestInnerClassRename extends BaseRenameMappingsTest {\n\n\t@Test\n\tpublic void test() {\n\t\ttestResDir = \"inner-cls-rename\";\n\t\tjadxArgs.getInputFiles().add(loadResourceFile(\"base.smali\"));\n\t\tjadxArgs.getInputFiles().add(loadResourceFile(\"inner.smali\"));\n\t\tjadxArgs.setUserRenamesMappingsPath(loadResourceFile(\"enigma.mapping\").toPath());\n\t\ttry (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) {\n\t\t\tjadx.load();\n\t\t\tList<JavaClass> classes = jadx.getClasses();\n\t\t\tprintClassesCode(classes);\n\t\t\tassertThat(classes).hasSize(1);\n\t\t\tJavaClass baseCls = classes.get(0);\n\t\t\tassertThat(baseCls.getName()).isEqualTo(\"BaseCls\");\n\t\t\tList<JavaClass> innerClasses = baseCls.getInnerClasses();\n\t\t\tassertThat(innerClasses).hasSize(1);\n\t\t\tassertThat(innerClasses.get(0).getName()).isEqualTo(\"RenamedInner\");\n\t\t\tassertThat(baseCls.getCode()).contains(\"class RenamedInner {\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/src/test/resources/inner-cls-rename/base.smali",
    "content": ".class Ljadx/test/BaseCls;\n.super Ljava/lang/Object;\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/src/test/resources/inner-cls-rename/enigma.mapping",
    "content": "CLASS jadx/test/BaseCls\n\tCLASS Inner RenamedInner\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/src/test/resources/inner-cls-rename/inner.smali",
    "content": ".class Ljadx/test/BaseCls$Inner;\n.super Ljava/lang/Object;\n"
  },
  {
    "path": "jadx-plugins/jadx-rename-mappings/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t\t<encoder>\n\t\t\t<pattern>%d{HH:mm:ss} %-5level - %msg%n</pattern>\n\t\t</encoder>\n\t</appender>\n\n\t<root level=\"DEBUG\">\n\t\t<appender-ref ref=\"STDOUT\"/>\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "jadx-plugins/jadx-smali-input/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-library\")\n}\n\ndependencies {\n\tapi(project(\":jadx-core\"))\n\n\timplementation(project(\":jadx-plugins:jadx-dex-input\"))\n\n\timplementation(\"com.android.tools.smali:smali:3.0.9\") {\n\t\texclude(group = \"com.beust\", module = \"jcommander\") // exclude old jcommander namespace\n\t}\n\timplementation(\"com.google.guava:guava:33.5.0-jre\") // force the latest version for smali\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-smali-input/src/main/java/jadx/plugins/input/smali/SmaliConvert.java",
    "content": "package jadx.plugins.input.smali;\n\nimport java.nio.file.FileSystems;\nimport java.nio.file.Path;\nimport java.nio.file.PathMatcher;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.android.tools.smali.smali.SmaliOptions;\n\nimport jadx.plugins.input.dex.utils.IDexData;\nimport jadx.plugins.input.dex.utils.SimpleDexData;\n\npublic class SmaliConvert {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(SmaliConvert.class);\n\n\tprivate final List<IDexData> dexData = new ArrayList<>();\n\n\tpublic boolean execute(List<Path> input, SmaliInputOptions options) {\n\t\tList<Path> smaliFiles = filterSmaliFiles(input);\n\t\tif (smaliFiles.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tcompile(smaliFiles, options);\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Smali process error\", e);\n\t\t}\n\t\treturn !dexData.isEmpty();\n\t}\n\n\t@SuppressWarnings(\"ResultOfMethodCallIgnored\")\n\tprivate void compile(List<Path> inputFiles, SmaliInputOptions options) {\n\t\tSmaliOptions smaliOptions = new SmaliOptions();\n\t\tsmaliOptions.apiLevel = options.getApiLevel();\n\t\tsmaliOptions.verboseErrors = true;\n\t\tsmaliOptions.allowOdexOpcodes = false;\n\t\tsmaliOptions.printTokens = false;\n\n\t\tint threads = options.getThreads();\n\t\tLOG.debug(\"Compiling smali files: {}, threads: {}\", inputFiles.size(), threads);\n\t\tlong start = System.currentTimeMillis();\n\t\tif (threads == 1 || inputFiles.size() == 1) {\n\t\t\tfor (Path inputFile : inputFiles) {\n\t\t\t\tassemble(dexData, inputFile, smaliOptions);\n\t\t\t}\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tExecutorService executor = Executors.newFixedThreadPool(threads);\n\t\t\t\tList<IDexData> syncList = Collections.synchronizedList(dexData);\n\t\t\t\tfor (Path inputFile : inputFiles) {\n\t\t\t\t\texecutor.execute(() -> assemble(syncList, inputFile, smaliOptions));\n\t\t\t\t}\n\t\t\t\texecutor.shutdown();\n\t\t\t\texecutor.awaitTermination(1, TimeUnit.HOURS);\n\t\t\t\tdexData.sort(Comparator.comparing(IDexData::getFileName));\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\tLOG.error(\"Smali compile interrupted\", e);\n\t\t\t}\n\t\t}\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tLOG.debug(\"Smali compile done in: {}ms\", System.currentTimeMillis() - start);\n\t\t}\n\t}\n\n\tprivate void assemble(List<IDexData> results, Path inputFile, SmaliOptions smaliOptions) {\n\t\tPath path = inputFile.toAbsolutePath();\n\t\ttry {\n\t\t\tbyte[] dexContent = SmaliUtils.assemble(path.toFile(), smaliOptions);\n\t\t\tresults.add(new SimpleDexData(path.toString(), dexContent));\n\t\t} catch (Exception e) {\n\t\t\tLOG.error(\"Failed to assemble smali file: {}\", path, e);\n\t\t}\n\t}\n\n\tprivate List<Path> filterSmaliFiles(List<Path> input) {\n\t\tPathMatcher matcher = FileSystems.getDefault().getPathMatcher(\"glob:**.smali\");\n\t\treturn input.stream()\n\t\t\t\t.filter(matcher::matches)\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tpublic List<IDexData> getDexData() {\n\t\treturn dexData;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-smali-input/src/main/java/jadx/plugins/input/smali/SmaliInputOptions.java",
    "content": "package jadx.plugins.input.smali;\n\nimport jadx.api.plugins.options.impl.BasePluginOptionsBuilder;\n\npublic class SmaliInputOptions extends BasePluginOptionsBuilder {\n\n\tprivate int apiLevel;\n\tprivate int threads; // use jadx global threads count option\n\n\t@Override\n\tpublic void registerOptions() {\n\t\tintOption(SmaliInputPlugin.PLUGIN_ID + \".api-level\")\n\t\t\t\t.description(\"Android API level\")\n\t\t\t\t.defaultValue(27)\n\t\t\t\t.setter(v -> apiLevel = v);\n\t}\n\n\tpublic int getApiLevel() {\n\t\treturn apiLevel;\n\t}\n\n\tpublic int getThreads() {\n\t\treturn threads;\n\t}\n\n\tpublic void setThreads(int threads) {\n\t\tthis.threads = threads;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-smali-input/src/main/java/jadx/plugins/input/smali/SmaliInputPlugin.java",
    "content": "package jadx.plugins.input.smali;\n\nimport jadx.api.plugins.JadxPlugin;\nimport jadx.api.plugins.JadxPluginContext;\nimport jadx.api.plugins.JadxPluginInfo;\nimport jadx.api.plugins.input.data.impl.EmptyCodeLoader;\nimport jadx.plugins.input.dex.DexInputPlugin;\n\npublic class SmaliInputPlugin implements JadxPlugin {\n\tpublic static final String PLUGIN_ID = \"smali-input\";\n\n\tprivate final SmaliInputOptions options = new SmaliInputOptions();\n\n\t@Override\n\tpublic JadxPluginInfo getPluginInfo() {\n\t\treturn new JadxPluginInfo(PLUGIN_ID, \"Smali Input\", \"Load .smali files\");\n\t}\n\n\t@Override\n\tpublic void init(JadxPluginContext context) {\n\t\tcontext.registerOptions(options);\n\t\toptions.setThreads(context.getArgs().getThreadsCount());\n\n\t\tDexInputPlugin dexInput = context.plugins().getInstance(DexInputPlugin.class);\n\t\tcontext.addCodeInput(input -> {\n\t\t\tSmaliConvert convert = new SmaliConvert();\n\t\t\tif (!convert.execute(input, options)) {\n\t\t\t\treturn EmptyCodeLoader.INSTANCE;\n\t\t\t}\n\t\t\treturn dexInput.loadDexData(convert.getDexData());\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-smali-input/src/main/java/jadx/plugins/input/smali/SmaliUtils.java",
    "content": "package jadx.plugins.input.smali;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.nio.charset.StandardCharsets;\n\nimport org.antlr.runtime.CommonTokenStream;\nimport org.antlr.runtime.RecognitionException;\nimport org.antlr.runtime.TokenStream;\nimport org.antlr.runtime.tree.CommonTreeNodeStream;\nimport org.antlr.runtime.tree.TreeNodeStream;\n\nimport com.android.tools.smali.dexlib2.Opcodes;\nimport com.android.tools.smali.dexlib2.writer.builder.DexBuilder;\nimport com.android.tools.smali.dexlib2.writer.io.MemoryDataStore;\nimport com.android.tools.smali.smali.SmaliOptions;\nimport com.android.tools.smali.smali.smaliFlexLexer;\nimport com.android.tools.smali.smali.smaliParser;\nimport com.android.tools.smali.smali.smaliTreeWalker;\n\n/**\n * Utility methods to assemble smali to in-memory buffer.\n * This implementation uses smali library internal classes.\n */\npublic class SmaliUtils {\n\n\t@SuppressWarnings(\"ExtractMethodRecommender\")\n\tpublic static byte[] assemble(File smaliFile, SmaliOptions options) throws IOException {\n\t\tStringBuilder errors = new StringBuilder();\n\t\ttry (FileInputStream fis = new FileInputStream(smaliFile);\n\t\t\t\tInputStreamReader reader = new InputStreamReader(fis, StandardCharsets.UTF_8)) {\n\n\t\t\tsmaliFlexLexer lexer = new smaliFlexLexer(reader, options.apiLevel);\n\t\t\tlexer.setSourceFile(smaliFile);\n\t\t\tCommonTokenStream tokens = new CommonTokenStream(lexer);\n\t\t\tParserWrapper parser = new ParserWrapper(tokens, errors);\n\t\t\tparser.setVerboseErrors(options.verboseErrors);\n\t\t\tparser.setAllowOdex(options.allowOdexOpcodes);\n\t\t\tparser.setApiLevel(options.apiLevel);\n\t\t\tParserWrapper.smali_file_return parseResult = parser.smali_file();\n\t\t\tif (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) {\n\t\t\t\tthrow new RuntimeException(\"Smali parse error: \" + errors);\n\t\t\t}\n\t\t\tCommonTreeNodeStream treeStream = new CommonTreeNodeStream(parseResult.getTree());\n\t\t\ttreeStream.setTokenStream(tokens);\n\n\t\t\tDexBuilder dexBuilder = new DexBuilder(Opcodes.forApi(options.apiLevel));\n\t\t\tTreeWalkerWrapper dexGen = new TreeWalkerWrapper(treeStream, errors);\n\t\t\tdexGen.setApiLevel(options.apiLevel);\n\t\t\tdexGen.setVerboseErrors(options.verboseErrors);\n\t\t\tdexGen.setDexBuilder(dexBuilder);\n\t\t\tdexGen.smali_file();\n\t\t\tif (dexGen.getNumberOfSyntaxErrors() > 0) {\n\t\t\t\tthrow new RuntimeException(\"Smali compile error: \" + errors);\n\t\t\t}\n\t\t\tMemoryDataStore dataStore = new MemoryDataStore();\n\t\t\tdexBuilder.writeTo(dataStore);\n\t\t\treturn dataStore.getData();\n\t\t} catch (RecognitionException e) {\n\t\t\tthrow new RuntimeException(\"Smali process error: \" + errors, e);\n\t\t}\n\t}\n\n\tprivate static final class ParserWrapper extends smaliParser {\n\t\tprivate final StringBuilder errors;\n\n\t\tpublic ParserWrapper(TokenStream input, StringBuilder errors) {\n\t\t\tsuper(input);\n\t\t\tthis.errors = errors;\n\t\t}\n\n\t\t@Override\n\t\tpublic void emitErrorMessage(String msg) {\n\t\t\terrors.append('\\n').append(msg);\n\t\t}\n\t}\n\n\tprivate static final class TreeWalkerWrapper extends smaliTreeWalker {\n\t\tprivate final StringBuilder errors;\n\n\t\tpublic TreeWalkerWrapper(TreeNodeStream input, StringBuilder errors) {\n\t\t\tsuper(input);\n\t\t\tthis.errors = errors;\n\t\t}\n\n\t\t@Override\n\t\tpublic void emitErrorMessage(String msg) {\n\t\t\terrors.append('\\n').append(msg);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-smali-input/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin",
    "content": "jadx.plugins.input.smali.SmaliInputPlugin\n"
  },
  {
    "path": "jadx-plugins/jadx-xapk-input/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-library\")\n}\n\ndependencies {\n\tapi(project(\":jadx-core\"))\n\n\timplementation(project(\":jadx-plugins:jadx-dex-input\"))\n\timplementation(\"com.google.code.gson:gson:2.13.2\")\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/XApkCustomInput.java",
    "content": "package jadx.plugins.input.xapk;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jadx.api.ResourceFile;\nimport jadx.api.ResourcesLoader;\nimport jadx.api.plugins.CustomResourcesLoader;\nimport jadx.api.plugins.JadxPluginContext;\nimport jadx.api.plugins.input.ICodeLoader;\nimport jadx.api.plugins.input.JadxCodeInput;\nimport jadx.api.plugins.input.data.impl.EmptyCodeLoader;\nimport jadx.plugins.input.dex.DexInputPlugin;\nimport jadx.plugins.input.xapk.data.XApkData;\n\npublic class XApkCustomInput implements JadxCodeInput, CustomResourcesLoader {\n\tprivate final JadxPluginContext context;\n\tprivate final XApkLoader loader;\n\n\tpublic XApkCustomInput(JadxPluginContext context, XApkLoader loader) {\n\t\tthis.context = context;\n\t\tthis.loader = loader;\n\t}\n\n\t@Override\n\tpublic ICodeLoader loadFiles(List<Path> input) {\n\t\tList<Path> apks = new ArrayList<>();\n\t\tfor (Path inputPath : input) {\n\t\t\tXApkData data = loader.checkAndLoad(inputPath);\n\t\t\tif (data != null) {\n\t\t\t\tapks.addAll(data.getApks());\n\t\t\t}\n\t\t}\n\t\tif (apks.isEmpty()) {\n\t\t\treturn EmptyCodeLoader.INSTANCE;\n\t\t}\n\t\tDexInputPlugin dexInputPlugin = context.plugins().getInstance(DexInputPlugin.class);\n\t\treturn dexInputPlugin.loadFiles(apks);\n\t}\n\n\t@Override\n\tpublic boolean load(ResourcesLoader resLoader, List<ResourceFile> list, File file) {\n\t\tXApkData xApkData = loader.checkAndLoad(file.toPath());\n\t\tif (xApkData == null) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (Path apkPath : xApkData.getApks()) {\n\t\t\tresLoader.defaultLoadFile(list, apkPath.toFile(), apkPath.getFileName() + \"/\");\n\t\t}\n\t\tfor (Path filePath : xApkData.getFiles()) {\n\t\t\tresLoader.defaultLoadFile(list, filePath.toFile(), \"\");\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/XApkInputPlugin.java",
    "content": "package jadx.plugins.input.xapk;\n\nimport jadx.api.plugins.JadxPlugin;\nimport jadx.api.plugins.JadxPluginContext;\nimport jadx.api.plugins.JadxPluginInfo;\nimport jadx.api.plugins.JadxPluginInfoBuilder;\n\npublic class XApkInputPlugin implements JadxPlugin {\n\n\tprivate XApkLoader loader;\n\n\t@Override\n\tpublic JadxPluginInfo getPluginInfo() {\n\t\treturn JadxPluginInfoBuilder.pluginId(\"xapk-input\")\n\t\t\t\t.name(\"XApk Input\")\n\t\t\t\t.description(\"Load .xapk files\")\n\t\t\t\t.build();\n\t}\n\n\t@Override\n\tpublic void init(JadxPluginContext context) {\n\t\tloader = new XApkLoader(context);\n\t\tXApkCustomInput customInput = new XApkCustomInput(context, loader);\n\t\tcontext.addCodeInput(customInput);\n\t\tcontext.getDecompiler().addCustomResourcesLoader(customInput);\n\t}\n\n\t@Override\n\tpublic void unload() {\n\t\tif (loader != null) {\n\t\t\tloader.unload();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/XApkLoader.java",
    "content": "package jadx.plugins.input.xapk;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.LinkOption;\nimport java.nio.file.Path;\nimport java.nio.file.StandardCopyOption;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.JadxPluginContext;\nimport jadx.core.utils.GsonUtils;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.plugins.input.xapk.data.SplitApk;\nimport jadx.plugins.input.xapk.data.XApkData;\nimport jadx.plugins.input.xapk.data.XApkManifest;\nimport jadx.zip.IZipEntry;\nimport jadx.zip.ZipContent;\n\npublic class XApkLoader {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(XApkLoader.class);\n\n\tprivate final JadxPluginContext context;\n\tprivate final Map<String, XApkData> loaded = new HashMap<>();\n\n\tpublic XApkLoader(JadxPluginContext context) {\n\t\tthis.context = context;\n\t}\n\n\tpublic @Nullable XApkData checkAndLoad(Path inputPath) {\n\t\tString fileName = inputPath.getFileName().toString();\n\t\tif (!fileName.toLowerCase(Locale.ROOT).endsWith(\".xapk\")) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\tXApkData loadedData = getLoaded(inputPath);\n\t\t\tif (loadedData != null) {\n\t\t\t\treturn loadedData;\n\t\t\t}\n\t\t\tFile xapkFile = inputPath.toFile();\n\t\t\tif (!FileUtils.isZipFile(xapkFile)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\ttry (ZipContent content = context.getZipReader().open(xapkFile)) {\n\t\t\t\tIZipEntry manifestEntry = content.searchEntry(\"manifest.json\");\n\t\t\t\tif (manifestEntry == null) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tString manifestStr = new String(manifestEntry.getBytes(), StandardCharsets.UTF_8);\n\t\t\t\tXApkManifest xApkManifest = GsonUtils.buildGson().fromJson(manifestStr, XApkManifest.class);\n\t\t\t\tif (xApkManifest.getVersion() != 2 || xApkManifest.getSplitApks().isEmpty()) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\t// checks complete\n\t\t\t\t// unpack all files into temp directory\n\t\t\t\tXApkData xApkData = unpackXApk(xapkFile, xApkManifest, content);\n\t\t\t\tsaveLoaded(inputPath, xApkData);\n\t\t\t\treturn xApkData;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOG.warn(\"Failed to load XApk file: {}\", inputPath.toAbsolutePath(), e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate XApkData unpackXApk(File xapkFile, XApkManifest xApkManifest, ZipContent content) throws IOException {\n\t\tSet<String> declaredApks = xApkManifest.getSplitApks().stream()\n\t\t\t\t.map(SplitApk::getFile).collect(Collectors.toSet());\n\t\tList<Path> apks = new ArrayList<>(declaredApks.size());\n\t\tList<Path> files = new ArrayList<>();\n\n\t\tString dirName = xapkFile.getName() + '_' + System.currentTimeMillis();\n\t\tPath tmpDir = context.files().getPluginTempDir().resolve(dirName);\n\t\tFileUtils.makeDirs(tmpDir);\n\t\tfor (IZipEntry entry : content.getEntries()) {\n\t\t\tString fileName = entry.getName();\n\t\t\tPath file = tmpDir.resolve(fileName);\n\t\t\ttry (InputStream inputStream = entry.getInputStream()) {\n\t\t\t\tFiles.copy(inputStream, file, StandardCopyOption.REPLACE_EXISTING);\n\t\t\t}\n\t\t\tif (declaredApks.contains(fileName)) {\n\t\t\t\tapks.add(file);\n\t\t\t} else {\n\t\t\t\tfiles.add(file);\n\t\t\t}\n\t\t}\n\t\treturn new XApkData(xApkManifest, tmpDir, apks, files);\n\t}\n\n\tprivate XApkData getLoaded(Path inputPath) throws IOException {\n\t\treturn loaded.get(pathToKey(inputPath));\n\t}\n\n\tprivate void saveLoaded(Path inputPath, XApkData xApkData) throws IOException {\n\t\tloaded.put(pathToKey(inputPath), xApkData);\n\t}\n\n\tprivate static String pathToKey(Path path) throws IOException {\n\t\treturn path.toRealPath(LinkOption.NOFOLLOW_LINKS).toString();\n\t}\n\n\tpublic synchronized void unload() {\n\t\tfor (XApkData data : loaded.values()) {\n\t\t\tFileUtils.deleteDirIfExists(data.getTmpDir());\n\t\t}\n\t\tloaded.clear();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/data/SplitApk.java",
    "content": "package jadx.plugins.input.xapk.data;\n\npublic class SplitApk {\n\tprivate String file;\n\tprivate String id;\n\n\tpublic String getFile() {\n\t\treturn file;\n\t}\n\n\tpublic void setFile(String file) {\n\t\tthis.file = file;\n\t}\n\n\tpublic String getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(String id) {\n\t\tthis.id = id;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/data/XApkData.java",
    "content": "package jadx.plugins.input.xapk.data;\n\nimport java.nio.file.Path;\nimport java.util.List;\n\npublic class XApkData {\n\tprivate final XApkManifest manifest;\n\tprivate final Path tmpDir;\n\tprivate final List<Path> files;\n\tprivate final List<Path> apks;\n\n\tpublic XApkData(XApkManifest manifest, Path tmpDir, List<Path> apks, List<Path> files) {\n\t\tthis.manifest = manifest;\n\t\tthis.tmpDir = tmpDir;\n\t\tthis.apks = apks;\n\t\tthis.files = files;\n\t}\n\n\tpublic List<Path> getApks() {\n\t\treturn apks;\n\t}\n\n\tpublic List<Path> getFiles() {\n\t\treturn files;\n\t}\n\n\tpublic XApkManifest getManifest() {\n\t\treturn manifest;\n\t}\n\n\tpublic Path getTmpDir() {\n\t\treturn tmpDir;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-xapk-input/src/main/java/jadx/plugins/input/xapk/data/XApkManifest.java",
    "content": "package jadx.plugins.input.xapk.data;\n\nimport java.util.List;\n\nimport com.google.gson.annotations.SerializedName;\n\npublic class XApkManifest {\n\t@SerializedName(\"xapk_version\")\n\tint version;\n\t@SerializedName(\"split_apks\")\n\tList<SplitApk> splitApks;\n\n\tpublic List<SplitApk> getSplitApks() {\n\t\treturn splitApks;\n\t}\n\n\tpublic void setSplitApks(List<SplitApk> splitApks) {\n\t\tthis.splitApks = splitApks;\n\t}\n\n\tpublic int getVersion() {\n\t\treturn version;\n\t}\n\n\tpublic void setVersion(int version) {\n\t\tthis.version = version;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins/jadx-xapk-input/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin",
    "content": "jadx.plugins.input.xapk.XApkInputPlugin\n"
  },
  {
    "path": "jadx-plugins-tools/build.gradle.kts",
    "content": "plugins {\n\tid(\"jadx-java\")\n\tid(\"jadx-library\")\n}\n\ndependencies {\n\tapi(project(\":jadx-core\"))\n\n\timplementation(project(\":jadx-commons:jadx-app-commons\"))\n\n\timplementation(\"com.google.code.gson:gson:2.13.2\")\n\timplementation(\"commons-io:commons-io:2.21.0\")\n\n\ttestImplementation(\"com.squareup.okhttp3:mockwebserver3:5.3.0\")\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/JadxExternalPluginsLoader.java",
    "content": "package jadx.plugins.tools;\n\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.ServiceLoader;\nimport java.util.stream.Collectors;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.JadxPlugin;\nimport jadx.api.plugins.loader.JadxPluginLoader;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\n\npublic class JadxExternalPluginsLoader implements JadxPluginLoader {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxExternalPluginsLoader.class);\n\n\tpublic static final String JADX_PLUGIN_CLASSLOADER_PREFIX = \"jadx-plugin:\";\n\n\tprivate final List<URLClassLoader> classLoaders = new ArrayList<>();\n\n\t@Override\n\tpublic List<JadxPlugin> load() {\n\t\tclose();\n\t\tlong start = System.currentTimeMillis();\n\t\tMap<String, JadxPlugin> map = new HashMap<>();\n\t\tloadFromClsLoader(map, thisClassLoader());\n\t\tloadInstalledPlugins(map);\n\n\t\tList<JadxPlugin> list = new ArrayList<>(map.size());\n\t\tlist.addAll(map.values());\n\t\tlist.sort(Comparator.comparing(p -> p.getClass().getSimpleName()));\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tLOG.debug(\"Collected {} plugins in {}ms\", list.size(), System.currentTimeMillis() - start);\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic JadxPlugin loadFromPath(Path pluginPath) {\n\t\tMap<String, JadxPlugin> map = new HashMap<>();\n\t\tloadFromPath(map, pluginPath);\n\t\tint loaded = map.size();\n\t\tif (loaded == 0) {\n\t\t\tthrow new JadxRuntimeException(\"No plugin found in jar: \" + pluginPath);\n\t\t}\n\t\tif (loaded > 1) {\n\t\t\tString plugins = map.values().stream().map(p -> p.getPluginInfo().getPluginId()).collect(Collectors.joining(\", \"));\n\t\t\tthrow new JadxRuntimeException(\"Expect only one plugin per jar: \" + pluginPath + \", but found: \" + loaded + \" - \" + plugins);\n\t\t}\n\t\treturn Utils.first(map.values());\n\n\t}\n\n\tprivate void loadFromClsLoader(Map<String, JadxPlugin> map, ClassLoader classLoader) {\n\t\tServiceLoader<JadxPlugin> serviceLoader = ServiceLoader.load(JadxPlugin.class, classLoader);\n\t\tfor (ServiceLoader.Provider<JadxPlugin> provider : serviceLoader.stream().collect(Collectors.toList())) {\n\t\t\tClass<? extends JadxPlugin> pluginClass = provider.type();\n\t\t\tString clsName = pluginClass.getName();\n\t\t\tif (!map.containsKey(clsName)\n\t\t\t\t\t&& pluginClass.getClassLoader() == classLoader) {\n\t\t\t\tmap.put(clsName, provider.get());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void loadInstalledPlugins(Map<String, JadxPlugin> map) {\n\t\tList<Path> paths = JadxPluginsTools.getInstance().getEnabledPluginPaths();\n\t\tfor (Path pluginPath : paths) {\n\t\t\tloadFromPath(map, pluginPath);\n\t\t}\n\t}\n\n\tprivate void loadFromPath(Map<String, JadxPlugin> map, Path pluginPath) {\n\t\ttry {\n\t\t\tURL[] urls;\n\t\t\tif (Files.isDirectory(pluginPath)) {\n\t\t\t\turls = FileUtils.listFiles(pluginPath, file -> FileUtils.hasExtension(file, \".jar\"))\n\t\t\t\t\t\t.stream()\n\t\t\t\t\t\t.map(JadxExternalPluginsLoader::toURL)\n\t\t\t\t\t\t.toArray(URL[]::new);\n\t\t\t\tif (urls.length == 0) {\n\t\t\t\t\tthrow new JadxRuntimeException(\"No jar files found in plugin directory\");\n\t\t\t\t}\n\t\t\t} else if (Files.isRegularFile(pluginPath)) {\n\t\t\t\tif (FileUtils.hasExtension(pluginPath, \".jar\")) {\n\t\t\t\t\turls = new URL[] { toURL(pluginPath) };\n\t\t\t\t} else {\n\t\t\t\t\tthrow new JadxRuntimeException(\"Unexpected plugin file format\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new JadxRuntimeException(\"Plugin file not found\");\n\t\t\t}\n\t\t\tString clsLoaderName = JADX_PLUGIN_CLASSLOADER_PREFIX + pluginPath.getFileName();\n\t\t\tURLClassLoader pluginClsLoader = new URLClassLoader(clsLoaderName, urls, thisClassLoader());\n\t\t\tclassLoaders.add(pluginClsLoader);\n\t\t\tloadFromClsLoader(map, pluginClsLoader);\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to load plugins from: \" + pluginPath, e);\n\t\t}\n\t}\n\n\tprivate static URL toURL(Path pluginPath) {\n\t\ttry {\n\t\t\treturn pluginPath.toUri().toURL();\n\t\t} catch (MalformedURLException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprivate static ClassLoader thisClassLoader() {\n\t\treturn JadxExternalPluginsLoader.class.getClassLoader();\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\ttry {\n\t\t\tfor (URLClassLoader classLoader : classLoaders) {\n\t\t\t\ttry {\n\t\t\t\t\tclassLoader.close();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tclassLoaders.clear();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/JadxPluginsList.java",
    "content": "package jadx.plugins.tools;\n\nimport java.io.InputStreamReader;\nimport java.io.Reader;\nimport java.lang.reflect.Type;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.function.Consumer;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.Gson;\nimport com.google.gson.reflect.TypeToken;\n\nimport jadx.core.utils.files.FileUtils;\nimport jadx.plugins.tools.data.JadxPluginListCache;\nimport jadx.plugins.tools.data.JadxPluginListEntry;\nimport jadx.plugins.tools.resolvers.github.GithubTools;\nimport jadx.plugins.tools.resolvers.github.LocationInfo;\nimport jadx.plugins.tools.resolvers.github.data.Asset;\nimport jadx.plugins.tools.resolvers.github.data.Release;\nimport jadx.plugins.tools.utils.PluginUtils;\nimport jadx.zip.ZipReader;\n\nimport static jadx.core.utils.GsonUtils.buildGson;\nimport static jadx.plugins.tools.utils.PluginFiles.PLUGINS_LIST_CACHE;\n\npublic class JadxPluginsList {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxPluginsList.class);\n\tprivate static final JadxPluginsList INSTANCE = new JadxPluginsList();\n\n\tprivate static final Type LIST_TYPE = new TypeToken<List<JadxPluginListEntry>>() {\n\t}.getType();\n\n\tprivate static final Type CACHE_TYPE = new TypeToken<JadxPluginListCache>() {\n\t}.getType();\n\n\tpublic static JadxPluginsList getInstance() {\n\t\treturn INSTANCE;\n\t}\n\n\tprivate @Nullable JadxPluginListCache loadedList;\n\n\tprivate JadxPluginsList() {\n\t}\n\n\t/**\n\t * List provider with update callback.\n\t * Can be called one or two times:\n\t * <br>\n\t * - Apply cached data first\n\t * <br>\n\t * - If update is available, apply data after fetch\n\t * <br>\n\t * Method call is blocking.\n\t */\n\tpublic synchronized void get(Consumer<List<JadxPluginListEntry>> consumer) {\n\t\tif (loadedList != null) {\n\t\t\tconsumer.accept(loadedList.getList());\n\t\t\treturn;\n\t\t}\n\t\tJadxPluginListCache listCache = loadCache();\n\t\tif (listCache != null) {\n\t\t\tconsumer.accept(listCache.getList());\n\t\t\tloadedList = listCache;\n\t\t}\n\t\tRelease release = fetchLatestRelease();\n\t\tif (listCache == null || !listCache.getVersion().equals(release.getName())) {\n\t\t\tJadxPluginListCache updatedList = fetchBundle(release);\n\t\t\tsaveCache(updatedList);\n\t\t\tconsumer.accept(updatedList.getList());\n\t\t\tloadedList = updatedList;\n\t\t}\n\t}\n\n\tpublic List<JadxPluginListEntry> get() {\n\t\tAtomicReference<List<JadxPluginListEntry>> holder = new AtomicReference<>();\n\t\tget(holder::set);\n\t\treturn holder.get();\n\t}\n\n\tprivate @Nullable JadxPluginListCache loadCache() {\n\t\tif (!Files.isRegularFile(PLUGINS_LIST_CACHE)) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\tString jsonStr = FileUtils.readFile(PLUGINS_LIST_CACHE);\n\t\t\treturn buildGson().fromJson(jsonStr, CACHE_TYPE);\n\t\t} catch (Exception e) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate void saveCache(JadxPluginListCache listCache) {\n\t\ttry {\n\t\t\tString jsonStr = buildGson().toJson(listCache, CACHE_TYPE);\n\t\t\tFileUtils.writeFile(PLUGINS_LIST_CACHE, jsonStr);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Error saving file: \" + PLUGINS_LIST_CACHE, e);\n\t\t}\n\t}\n\n\tprivate Release fetchLatestRelease() {\n\t\tLOG.debug(\"Fetching latest plugins-list release info\");\n\t\tLocationInfo pluginsList = new LocationInfo(\"jadx-decompiler\", \"jadx-plugins-list\", \"list\");\n\t\tRelease release = GithubTools.fetchRelease(pluginsList);\n\t\tList<Asset> assets = release.getAssets();\n\t\tif (assets.isEmpty()) {\n\t\t\tthrow new RuntimeException(\"Release don't have assets\");\n\t\t}\n\t\treturn release;\n\t}\n\n\tprivate JadxPluginListCache fetchBundle(Release release) {\n\t\tLOG.debug(\"Fetching plugins-list bundle: {}\", release.getName());\n\t\ttry {\n\t\t\tAsset listAsset = release.getAssets().get(0);\n\t\t\tPath tmpListFile = Files.createTempFile(\"plugins-list\", \".zip\");\n\t\t\ttry {\n\t\t\t\tPluginUtils.downloadFile(listAsset.getDownloadUrl(), tmpListFile);\n\t\t\t\tJadxPluginListCache listCache = new JadxPluginListCache();\n\t\t\t\tlistCache.setVersion(release.getName());\n\t\t\t\tlistCache.setList(loadListBundle(tmpListFile));\n\t\t\t\treturn listCache;\n\t\t\t} finally {\n\t\t\t\tFiles.deleteIfExists(tmpListFile);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to load plugin-list bundle for release:\" + release.getName(), e);\n\t\t}\n\t}\n\n\tprivate static List<JadxPluginListEntry> loadListBundle(Path tmpListFile) {\n\t\tGson gson = buildGson();\n\t\tList<JadxPluginListEntry> entries = new ArrayList<>();\n\t\tnew ZipReader().visitEntries(tmpListFile.toFile(), entry -> {\n\t\t\tif (entry.getName().endsWith(\".json\")) {\n\t\t\t\ttry (Reader reader = new InputStreamReader(entry.getInputStream())) {\n\t\t\t\t\tentries.addAll(gson.fromJson(reader, LIST_TYPE));\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tthrow new RuntimeException(\"Failed to read plugins list entry: \" + entry.getName());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t\treturn entries;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/JadxPluginsTools.java",
    "content": "package jadx.plugins.tools;\n\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.io.Writer;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.nio.file.StandardCopyOption;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport jadx.api.plugins.JadxPlugin;\nimport jadx.api.plugins.JadxPluginInfo;\nimport jadx.api.plugins.utils.CommonFileUtils;\nimport jadx.core.Jadx;\nimport jadx.core.plugins.versions.VerifyRequiredVersion;\nimport jadx.core.utils.StringUtils;\nimport jadx.core.utils.Utils;\nimport jadx.core.utils.exceptions.JadxRuntimeException;\nimport jadx.core.utils.files.FileUtils;\nimport jadx.plugins.tools.data.JadxInstalledPlugins;\nimport jadx.plugins.tools.data.JadxPluginMetadata;\nimport jadx.plugins.tools.data.JadxPluginUpdate;\nimport jadx.plugins.tools.resolvers.IJadxPluginResolver;\nimport jadx.plugins.tools.resolvers.ResolversRegistry;\nimport jadx.plugins.tools.utils.PluginUtils;\nimport jadx.zip.IZipEntry;\nimport jadx.zip.ZipContent;\nimport jadx.zip.ZipReader;\n\nimport static jadx.core.utils.GsonUtils.buildGson;\nimport static jadx.plugins.tools.utils.PluginFiles.DROPINS_DIR;\nimport static jadx.plugins.tools.utils.PluginFiles.INSTALLED_DIR;\nimport static jadx.plugins.tools.utils.PluginFiles.PLUGINS_JSON;\n\npublic class JadxPluginsTools {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(JadxPluginsTools.class);\n\n\tprivate static final JadxPluginsTools INSTANCE = new JadxPluginsTools();\n\n\tpublic static JadxPluginsTools getInstance() {\n\t\treturn INSTANCE;\n\t}\n\n\tprivate JadxPluginsTools() {\n\t}\n\n\tpublic JadxPluginMetadata install(String locationId) {\n\t\tIJadxPluginResolver resolver = ResolversRegistry.getResolver(locationId);\n\t\tSupplier<List<JadxPluginMetadata>> fetchVersions;\n\t\tif (resolver.hasVersion(locationId)) {\n\t\t\tfetchVersions = () -> {\n\t\t\t\tJadxPluginMetadata version = resolver.resolve(locationId)\n\t\t\t\t\t\t.orElseThrow(() -> new JadxRuntimeException(\"Failed to resolve plugin location: \" + locationId));\n\t\t\t\treturn Collections.singletonList(version);\n\t\t\t};\n\t\t} else {\n\t\t\t// load latest 10 version to search for compatible one\n\t\t\tfetchVersions = () -> resolver.resolveVersions(locationId, 1, 10);\n\t\t}\n\t\tList<JadxPluginMetadata> versionsMetadata;\n\t\ttry {\n\t\t\tversionsMetadata = fetchVersions.get();\n\t\t} catch (Exception e) {\n\t\t\tthrow new JadxRuntimeException(\"Plugin info fetch failed, locationId: \" + locationId, e);\n\t\t}\n\t\tif (versionsMetadata.isEmpty()) {\n\t\t\tthrow new JadxRuntimeException(\"Plugin release not found, locationId: \" + locationId);\n\t\t}\n\t\tVerifyRequiredVersion verifyRequiredVersion = new VerifyRequiredVersion();\n\t\tList<String> rejectedVersions = new ArrayList<>();\n\t\tfor (JadxPluginMetadata pluginMetadata : versionsMetadata) {\n\t\t\t// download plugin jar and fill metadata\n\t\t\t// any download or plugin instantiation errors will stop versions check\n\t\t\tfillMetadata(pluginMetadata);\n\t\t\tif (verifyRequiredVersion.isCompatible(pluginMetadata.getRequiredJadxVersion())) {\n\t\t\t\tinstall(pluginMetadata);\n\t\t\t\treturn pluginMetadata;\n\t\t\t}\n\t\t\tString pluginVersion = Utils.getOrElse(pluginMetadata.getVersion(), \"unknown\");\n\t\t\trejectedVersions.add(\" version '\" + pluginVersion + \"' not compatible, require: \"\n\t\t\t\t\t+ pluginMetadata.getRequiredJadxVersion());\n\t\t}\n\t\tthrow new JadxRuntimeException(\"Can't find compatible version to install\"\n\t\t\t\t+ \", current jadx version: \" + verifyRequiredVersion.getJadxVersion()\n\t\t\t\t+ \"\\nrejected plugin versions:\\n\"\n\t\t\t\t+ String.join(\"\\n\", rejectedVersions));\n\t}\n\n\tpublic JadxPluginMetadata resolveMetadata(String locationId) {\n\t\tIJadxPluginResolver resolver = ResolversRegistry.getResolver(locationId);\n\t\tJadxPluginMetadata pluginMetadata = resolver.resolve(locationId)\n\t\t\t\t.orElseThrow(() -> new RuntimeException(\"Failed to resolve locationId: \" + locationId));\n\t\tfillMetadata(pluginMetadata);\n\t\treturn pluginMetadata;\n\t}\n\n\tpublic List<JadxPluginMetadata> getVersionsByLocation(String locationId, int page, int perPage) {\n\t\tIJadxPluginResolver resolver = ResolversRegistry.getResolver(locationId);\n\t\tList<JadxPluginMetadata> list = resolver.resolveVersions(locationId, page, perPage);\n\t\tfor (JadxPluginMetadata pluginMetadata : list) {\n\t\t\tfillMetadata(pluginMetadata);\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic List<JadxPluginUpdate> updateAll() {\n\t\tJadxInstalledPlugins plugins = loadPluginsJson();\n\t\tint size = plugins.getInstalled().size();\n\t\tList<JadxPluginUpdate> updates = new ArrayList<>(size);\n\t\tList<JadxPluginMetadata> newList = new ArrayList<>(size);\n\t\tfor (JadxPluginMetadata plugin : plugins.getInstalled()) {\n\t\t\tJadxPluginMetadata newVersion = null;\n\t\t\ttry {\n\t\t\t\tnewVersion = update(plugin);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOG.warn(\"Failed to update plugin: {}\", plugin.getPluginId(), e);\n\t\t\t}\n\t\t\tif (newVersion != null) {\n\t\t\t\tupdates.add(new JadxPluginUpdate(plugin, newVersion));\n\t\t\t\tnewList.add(newVersion);\n\t\t\t} else {\n\t\t\t\tnewList.add(plugin);\n\t\t\t}\n\t\t}\n\t\tif (!updates.isEmpty()) {\n\t\t\tplugins.setUpdated(System.currentTimeMillis());\n\t\t\tplugins.setInstalled(newList);\n\t\t\tsavePluginsJson(plugins);\n\t\t}\n\t\treturn updates;\n\t}\n\n\tpublic Optional<JadxPluginUpdate> update(String pluginId) {\n\t\tJadxInstalledPlugins plugins = loadPluginsJson();\n\t\tJadxPluginMetadata plugin = plugins.getInstalled().stream()\n\t\t\t\t.filter(p -> p.getPluginId().equals(pluginId))\n\t\t\t\t.findFirst()\n\t\t\t\t.orElseThrow(() -> new RuntimeException(\"Plugin not found: \" + pluginId));\n\n\t\tJadxPluginMetadata newVersion = update(plugin);\n\t\tif (newVersion == null) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\tplugins.setUpdated(System.currentTimeMillis());\n\t\tplugins.getInstalled().remove(plugin);\n\t\tplugins.getInstalled().add(newVersion);\n\t\tsavePluginsJson(plugins);\n\t\treturn Optional.of(new JadxPluginUpdate(plugin, newVersion));\n\t}\n\n\tpublic boolean uninstall(String pluginId) {\n\t\tJadxInstalledPlugins plugins = loadPluginsJson();\n\t\tOptional<JadxPluginMetadata> found = plugins.getInstalled().stream()\n\t\t\t\t.filter(p -> p.getPluginId().equals(pluginId))\n\t\t\t\t.findFirst();\n\t\tif (found.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tJadxPluginMetadata plugin = found.get();\n\t\tdeletePlugin(plugin);\n\t\tplugins.getInstalled().remove(plugin);\n\t\tsavePluginsJson(plugins);\n\t\treturn true;\n\t}\n\n\tpublic List<JadxPluginMetadata> getInstalled() {\n\t\treturn loadPluginsJson().getInstalled();\n\t}\n\n\t/**\n\t * Return all loadable plugins info (including installed, bundled and dropins).\n\t * <br>\n\t * For only installed plugins prefer {@link jadx.plugins.tools.JadxPluginsTools#getInstalled}\n\t * method.\n\t */\n\tpublic List<JadxPluginInfo> getAllPluginsInfo() {\n\t\ttry (JadxExternalPluginsLoader pluginsLoader = new JadxExternalPluginsLoader()) {\n\t\t\treturn pluginsLoader.load().stream()\n\t\t\t\t\t.map(JadxPlugin::getPluginInfo)\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t}\n\t}\n\n\tpublic List<Path> getEnabledPluginPaths() {\n\t\tList<Path> list = new ArrayList<>();\n\t\tfor (JadxPluginMetadata pluginMetadata : loadPluginsJson().getInstalled()) {\n\t\t\tif (pluginMetadata.isDisabled()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlist.add(INSTALLED_DIR.resolve(pluginMetadata.getPath()));\n\t\t}\n\t\tlist.addAll(FileUtils.listFiles(DROPINS_DIR));\n\t\treturn list;\n\t}\n\n\t/**\n\t * Disable or enable plugin\n\t *\n\t * @return true if disabled status was changed\n\t */\n\tpublic boolean changeDisabledStatus(String pluginId, boolean disabled) {\n\t\tJadxInstalledPlugins data = loadPluginsJson();\n\t\tJadxPluginMetadata plugin = data.getInstalled().stream()\n\t\t\t\t.filter(p -> p.getPluginId().equals(pluginId))\n\t\t\t\t.findFirst()\n\t\t\t\t.orElseThrow(() -> new RuntimeException(\"Plugin not found: \" + pluginId));\n\t\tif (plugin.isDisabled() == disabled) {\n\t\t\treturn false;\n\t\t}\n\t\tplugin.setDisabled(disabled);\n\t\tdata.setUpdated(System.currentTimeMillis());\n\t\tsavePluginsJson(data);\n\t\treturn true;\n\t}\n\n\tprivate @Nullable JadxPluginMetadata update(JadxPluginMetadata plugin) {\n\t\tIJadxPluginResolver resolver = ResolversRegistry.getResolver(plugin.getLocationId());\n\t\tif (!resolver.isUpdateSupported()) {\n\t\t\treturn null;\n\t\t}\n\t\tOptional<JadxPluginMetadata> updateOpt = resolver.resolve(plugin.getLocationId());\n\t\tif (updateOpt.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tJadxPluginMetadata update = updateOpt.get();\n\t\tif (Objects.equals(update.getVersion(), plugin.getVersion())) {\n\t\t\treturn null;\n\t\t}\n\t\tfillMetadata(update);\n\t\tinstall(update);\n\t\treturn update;\n\t}\n\n\tprivate void install(JadxPluginMetadata metadata) {\n\t\tString reqVersionStr = metadata.getRequiredJadxVersion();\n\t\tif (!VerifyRequiredVersion.isJadxCompatible(reqVersionStr)) {\n\t\t\tthrow new JadxRuntimeException(\"Can't install plugin, required version: \\\"\" + reqVersionStr + '\\\"'\n\t\t\t\t\t+ \" is not compatible with current jadx version: \" + Jadx.getVersion());\n\t\t}\n\t\t// remove previous version\n\t\tuninstall(metadata.getPluginId());\n\n\t\tString version = metadata.getVersion();\n\t\tString pluginBaseName = metadata.getPluginId() + (StringUtils.notBlank(version) ? '-' + version : \"\");\n\t\tString pluginPathStr = metadata.getPath();\n\t\tPath pluginPath = Paths.get(pluginPathStr);\n\t\tif (pluginPathStr.endsWith(\".jar\")) {\n\t\t\tPath pluginJar = INSTALLED_DIR.resolve(pluginBaseName + \".jar\");\n\t\t\tcopyJar(pluginPath, pluginJar);\n\t\t\tmetadata.setPath(INSTALLED_DIR.relativize(pluginJar).toString());\n\t\t} else if (Files.isDirectory(pluginPath)) {\n\t\t\tPath pluginDir = INSTALLED_DIR.resolve(pluginBaseName);\n\t\t\ttry {\n\t\t\t\tFileUtils.deleteDirIfExists(pluginDir);\n\t\t\t\torg.apache.commons.io.FileUtils.moveDirectory(pluginPath.toFile(), pluginDir.toFile());\n\t\t\t} catch (IOException e) {\n\t\t\t\tthrow new JadxRuntimeException(\"Failed to install plugin: \" + pluginBaseName, e);\n\t\t\t}\n\t\t\tmetadata.setPath(INSTALLED_DIR.relativize(pluginDir).toString());\n\t\t} else {\n\t\t\tthrow new JadxRuntimeException(\"Unexpected plugin path type: \" + pluginPathStr);\n\t\t}\n\t\t// update plugins json\n\t\tJadxInstalledPlugins plugins = loadPluginsJson();\n\t\tplugins.getInstalled().add(metadata);\n\t\tplugins.setUpdated(System.currentTimeMillis());\n\t\tsavePluginsJson(plugins);\n\t}\n\n\tprivate void fillMetadata(JadxPluginMetadata metadata) {\n\t\ttry {\n\t\t\tString pluginPath = metadata.getPath();\n\t\t\tif (needDownload(pluginPath)) {\n\t\t\t\t// download plugin\n\t\t\t\tString ext = CommonFileUtils.getFileExtension(pluginPath);\n\t\t\t\tPath tmpJar = Files.createTempFile(metadata.getName(), \"plugin.\" + ext);\n\t\t\t\tPluginUtils.downloadFile(pluginPath, tmpJar);\n\t\t\t\tpluginPath = tmpJar.toAbsolutePath().toString();\n\t\t\t}\n\t\t\tif (pluginPath.endsWith(\".zip\")) {\n\t\t\t\t// unpack plugin zip\n\t\t\t\tPath tmpDir = Files.createTempDirectory(metadata.getName());\n\t\t\t\tunzip(Paths.get(pluginPath), tmpDir);\n\t\t\t\tpluginPath = tmpDir.toAbsolutePath().toString();\n\t\t\t}\n\t\t\tmetadata.setPath(pluginPath);\n\t\t\tfillMetadataFromPath(metadata, Paths.get(pluginPath));\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to fill plugin metadata, plugin: \" + metadata.getPluginId(), e);\n\t\t}\n\t}\n\n\tprivate void fillMetadataFromPath(JadxPluginMetadata metadata, Path pluginPath) {\n\t\ttry (JadxExternalPluginsLoader loader = new JadxExternalPluginsLoader()) {\n\t\t\tJadxPlugin jadxPlugin = loader.loadFromPath(pluginPath);\n\t\t\tJadxPluginInfo pluginInfo = jadxPlugin.getPluginInfo();\n\t\t\tmetadata.setPluginId(pluginInfo.getPluginId());\n\t\t\tmetadata.setName(pluginInfo.getName());\n\t\t\tmetadata.setDescription(pluginInfo.getDescription());\n\t\t\tmetadata.setHomepage(pluginInfo.getHomepage());\n\t\t\tmetadata.setRequiredJadxVersion(pluginInfo.getRequiredJadxVersion());\n\t\t} catch (NoSuchMethodError e) {\n\t\t\tthrow new RuntimeException(\"Looks like plugin uses unknown API, try to update jadx version\", e);\n\t\t}\n\t}\n\n\tprivate static boolean needDownload(String jar) {\n\t\treturn jar.startsWith(\"https://\") || jar.startsWith(\"http://\");\n\t}\n\n\tprivate void copyJar(Path sourceJar, Path destJar) {\n\t\ttry {\n\t\t\tFiles.copy(sourceJar, destJar, StandardCopyOption.REPLACE_EXISTING);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to copy plugin jar: \" + sourceJar + \" to: \" + destJar, e);\n\t\t}\n\t}\n\n\tprivate void deletePlugin(JadxPluginMetadata plugin) {\n\t\ttry {\n\t\t\tPath pluginPath = INSTALLED_DIR.resolve(plugin.getPath());\n\t\t\tif (Files.isDirectory(pluginPath)) {\n\t\t\t\tFileUtils.deleteDir(pluginPath);\n\t\t\t} else {\n\t\t\t\tFiles.deleteIfExists(pluginPath);\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tprivate JadxInstalledPlugins loadPluginsJson() {\n\t\tif (!Files.isRegularFile(PLUGINS_JSON)) {\n\t\t\tJadxInstalledPlugins plugins = new JadxInstalledPlugins();\n\t\t\tplugins.setVersion(1);\n\t\t\treturn plugins;\n\t\t}\n\t\ttry (Reader reader = Files.newBufferedReader(PLUGINS_JSON, StandardCharsets.UTF_8)) {\n\t\t\tJadxInstalledPlugins data = buildGson().fromJson(reader, JadxInstalledPlugins.class);\n\t\t\tupgradePluginsData(data);\n\t\t\treturn data;\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to read file: \" + PLUGINS_JSON);\n\t\t}\n\t}\n\n\tprivate void savePluginsJson(JadxInstalledPlugins data) {\n\t\tif (data.getInstalled().isEmpty()) {\n\t\t\ttry {\n\t\t\t\tFiles.deleteIfExists(PLUGINS_JSON);\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new RuntimeException(\"Failed to remove file: \" + PLUGINS_JSON, e);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tdata.getInstalled().sort(null);\n\t\ttry (Writer writer = Files.newBufferedWriter(PLUGINS_JSON, StandardCharsets.UTF_8)) {\n\t\t\tbuildGson().toJson(data, writer);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Error saving file: \" + PLUGINS_JSON, e);\n\t\t}\n\t}\n\n\tprivate void upgradePluginsData(JadxInstalledPlugins data) {\n\t\tif (data.getVersion() == 0) {\n\t\t\tdata.setVersion(1);\n\t\t}\n\t}\n\n\tprivate static void unzip(Path zipFile, Path outDir) {\n\t\tZipReader zipReader = new ZipReader(); // TODO: pass zip options from jadx args\n\t\ttry (ZipContent content = zipReader.open(zipFile.toFile())) {\n\t\t\tfor (IZipEntry entry : content.getEntries()) {\n\t\t\t\tPath entryFile = outDir.resolve(entry.getName());\n\t\t\t\tFiles.copy(entry.getInputStream(), entryFile, StandardCopyOption.REPLACE_EXISTING);\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tthrow new JadxRuntimeException(\"Failed to unzip file: \" + zipFile, e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/data/JadxInstalledPlugins.java",
    "content": "package jadx.plugins.tools.data;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class JadxInstalledPlugins {\n\tprivate int version;\n\tprivate long updated;\n\tprivate List<JadxPluginMetadata> installed = new ArrayList<>();\n\n\tpublic int getVersion() {\n\t\treturn version;\n\t}\n\n\tpublic void setVersion(int version) {\n\t\tthis.version = version;\n\t}\n\n\tpublic long getUpdated() {\n\t\treturn updated;\n\t}\n\n\tpublic void setUpdated(long updated) {\n\t\tthis.updated = updated;\n\t}\n\n\tpublic List<JadxPluginMetadata> getInstalled() {\n\t\treturn installed;\n\t}\n\n\tpublic void setInstalled(List<JadxPluginMetadata> installed) {\n\t\tthis.installed = installed;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/data/JadxPluginListCache.java",
    "content": "package jadx.plugins.tools.data;\n\nimport java.util.List;\n\npublic class JadxPluginListCache {\n\tprivate String version;\n\tprivate List<JadxPluginListEntry> list;\n\n\tpublic String getVersion() {\n\t\treturn version;\n\t}\n\n\tpublic void setVersion(String version) {\n\t\tthis.version = version;\n\t}\n\n\tpublic List<JadxPluginListEntry> getList() {\n\t\treturn list;\n\t}\n\n\tpublic void setList(List<JadxPluginListEntry> list) {\n\t\tthis.list = list;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/data/JadxPluginListEntry.java",
    "content": "package jadx.plugins.tools.data;\n\npublic class JadxPluginListEntry {\n\tprivate String pluginId;\n\tprivate String locationId;\n\tprivate String name;\n\tprivate String description;\n\tprivate String homepage;\n\tprivate int revision;\n\n\tpublic String getDescription() {\n\t\treturn description;\n\t}\n\n\tpublic void setDescription(String description) {\n\t\tthis.description = description;\n\t}\n\n\tpublic String getHomepage() {\n\t\treturn homepage;\n\t}\n\n\tpublic void setHomepage(String homepage) {\n\t\tthis.homepage = homepage;\n\t}\n\n\tpublic String getLocationId() {\n\t\treturn locationId;\n\t}\n\n\tpublic void setLocationId(String locationId) {\n\t\tthis.locationId = locationId;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getPluginId() {\n\t\treturn pluginId;\n\t}\n\n\tpublic void setPluginId(String pluginId) {\n\t\tthis.pluginId = pluginId;\n\t}\n\n\tpublic int getRevision() {\n\t\treturn revision;\n\t}\n\n\tpublic void setRevision(int revision) {\n\t\tthis.revision = revision;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"JadxPluginListEntry{\"\n\t\t\t\t+ \"description='\" + description + '\\''\n\t\t\t\t+ \", pluginId='\" + pluginId + '\\''\n\t\t\t\t+ \", locationId='\" + locationId + '\\''\n\t\t\t\t+ \", name='\" + name + '\\''\n\t\t\t\t+ \", homepage='\" + homepage + '\\''\n\t\t\t\t+ \", revision=\" + revision\n\t\t\t\t+ '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/data/JadxPluginMetadata.java",
    "content": "package jadx.plugins.tools.data;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport com.google.gson.annotations.SerializedName;\n\npublic class JadxPluginMetadata implements Comparable<JadxPluginMetadata> {\n\tprivate String pluginId;\n\tprivate String name;\n\tprivate String description;\n\tprivate String homepage;\n\tprivate @Nullable String requiredJadxVersion;\n\n\tprivate @Nullable String version;\n\n\tprivate String locationId;\n\n\t/**\n\t * Absolute path to '.jar' file or unpacked zip directory\n\t */\n\t@SerializedName(value = \"path\", alternate = { \"jar\" })\n\tprivate String path;\n\n\tprivate boolean disabled;\n\n\tpublic String getPluginId() {\n\t\treturn pluginId;\n\t}\n\n\tpublic void setPluginId(String pluginId) {\n\t\tthis.pluginId = pluginId;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic @Nullable String getVersion() {\n\t\treturn version;\n\t}\n\n\tpublic void setVersion(@Nullable String version) {\n\t\tthis.version = version;\n\t}\n\n\tpublic String getDescription() {\n\t\treturn description;\n\t}\n\n\tpublic void setDescription(String description) {\n\t\tthis.description = description;\n\t}\n\n\tpublic String getHomepage() {\n\t\treturn homepage;\n\t}\n\n\tpublic void setHomepage(String homepage) {\n\t\tthis.homepage = homepage;\n\t}\n\n\tpublic @Nullable String getRequiredJadxVersion() {\n\t\treturn requiredJadxVersion;\n\t}\n\n\tpublic void setRequiredJadxVersion(@Nullable String requiredJadxVersion) {\n\t\tthis.requiredJadxVersion = requiredJadxVersion;\n\t}\n\n\tpublic String getLocationId() {\n\t\treturn locationId;\n\t}\n\n\tpublic void setLocationId(String locationId) {\n\t\tthis.locationId = locationId;\n\t}\n\n\tpublic String getPath() {\n\t\treturn path;\n\t}\n\n\tpublic void setPath(String path) {\n\t\tthis.path = path;\n\t}\n\n\t@Deprecated\n\tpublic String getJar() {\n\t\treturn path;\n\t}\n\n\t@Deprecated\n\tpublic void setJar(String jar) {\n\t\tthis.path = jar;\n\t}\n\n\tpublic boolean isDisabled() {\n\t\treturn disabled;\n\t}\n\n\tpublic void setDisabled(boolean disabled) {\n\t\tthis.disabled = disabled;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object other) {\n\t\tif (this == other) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(other instanceof JadxPluginMetadata)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn pluginId.equals(((JadxPluginMetadata) other).pluginId);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn pluginId.hashCode();\n\t}\n\n\t@Override\n\tpublic int compareTo(@NotNull JadxPluginMetadata o) {\n\t\treturn pluginId.compareTo(o.pluginId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"JadxPluginMetadata{\"\n\t\t\t\t+ \"id=\" + pluginId\n\t\t\t\t+ \", name=\" + name\n\t\t\t\t+ \", version=\" + (version != null ? version : \"?\")\n\t\t\t\t+ \", locationId=\" + locationId\n\t\t\t\t+ \", path=\" + path\n\t\t\t\t+ '}';\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/data/JadxPluginUpdate.java",
    "content": "package jadx.plugins.tools.data;\n\npublic class JadxPluginUpdate {\n\tprivate final JadxPluginMetadata oldVersion;\n\tprivate final JadxPluginMetadata newVersion;\n\n\tpublic JadxPluginUpdate(JadxPluginMetadata oldVersion, JadxPluginMetadata newVersion) {\n\t\tthis.oldVersion = oldVersion;\n\t\tthis.newVersion = newVersion;\n\t}\n\n\tpublic JadxPluginMetadata getOld() {\n\t\treturn oldVersion;\n\t}\n\n\tpublic JadxPluginMetadata getNew() {\n\t\treturn newVersion;\n\t}\n\n\tpublic String getPluginId() {\n\t\treturn newVersion.getPluginId();\n\t}\n\n\tpublic String getOldVersion() {\n\t\treturn oldVersion.getVersion();\n\t}\n\n\tpublic String getNewVersion() {\n\t\treturn newVersion.getVersion();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"PluginUpdate{\" + oldVersion.getPluginId()\n\t\t\t\t+ \": \" + oldVersion.getVersion() + \" -> \" + newVersion.getVersion() + \"}\";\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/resolvers/IJadxPluginResolver.java",
    "content": "package jadx.plugins.tools.resolvers;\n\nimport java.util.List;\nimport java.util.Optional;\n\nimport jadx.plugins.tools.data.JadxPluginMetadata;\n\npublic interface IJadxPluginResolver {\n\n\t/**\n\t * Unique resolver identifier, should be same as locationId prefix\n\t */\n\tString id();\n\n\t/**\n\t * This resolver support updates and can fetch the latest version.\n\t */\n\tboolean isUpdateSupported();\n\n\t/**\n\t * Fetch the latest version plugin metadata by location\n\t */\n\tOptional<JadxPluginMetadata> resolve(String locationId);\n\n\t/**\n\t * Fetch several latest versions (pageable) of plugin by locationId.\n\t *\n\t * @param page    page number, starts with 1\n\t * @param perPage result's count limit\n\t */\n\tList<JadxPluginMetadata> resolveVersions(String locationId, int page, int perPage);\n\n\t/**\n\t * Check if locationId has a specified version number\n\t */\n\tboolean hasVersion(String locationId);\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/resolvers/README.md",
    "content": "### Supported publish locations for Jadx plugins\n\n---\n\n#### GitHub release artifact\n\nPattern: `github:<owner>:<repo>[:<version>][:<artifact name prefix>]`\n\nExamples: `github:skylot:jadx`, `github:skylot:jadx:sample-plugin` or `github:skylot:jadx:0.1.0`\n\n`<version>` - exact version to install (optional), should be equal to release name\n\nArtifact name pattern: `<artifact name prefix>[-<release-version-name>].jar`.\n\nDefault value for `<artifact name prefix>` is a repo name, `-<release-version-name>` is optional.\n\n---\n\n#### Local file\n\nInstall local jar file.\n\nPattern: `file:<path to file>.jar`\n\nExample: `file:/home/user/plugin.jar`\n\nAs alternative to install, plugin jars can be copied to `plugins/dropins` folder.\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/resolvers/ResolversRegistry.java",
    "content": "package jadx.plugins.tools.resolvers;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport jadx.plugins.tools.resolvers.file.LocalFileResolver;\nimport jadx.plugins.tools.resolvers.github.GithubReleaseResolver;\n\npublic class ResolversRegistry {\n\n\tprivate static final Map<String, IJadxPluginResolver> RESOLVERS_MAP = new HashMap<>();\n\n\tstatic {\n\t\tregister(new LocalFileResolver());\n\t\tregister(new GithubReleaseResolver());\n\t}\n\n\tprivate static void register(IJadxPluginResolver resolver) {\n\t\tRESOLVERS_MAP.put(resolver.id(), resolver);\n\t}\n\n\tpublic static IJadxPluginResolver getResolver(String locationId) {\n\t\tObjects.requireNonNull(locationId);\n\t\tint sep = locationId.indexOf(':');\n\t\tif (sep <= 0) {\n\t\t\tthrow new IllegalArgumentException(\"Malformed locationId: \" + locationId);\n\t\t}\n\t\treturn getById(locationId.substring(0, sep));\n\t}\n\n\tpublic static IJadxPluginResolver getById(String resolverId) {\n\t\tIJadxPluginResolver resolver = RESOLVERS_MAP.get(resolverId);\n\t\tif (resolver == null) {\n\t\t\tthrow new IllegalArgumentException(\"Unknown resolverId: \" + resolverId);\n\t\t}\n\t\treturn resolver;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/resolvers/file/LocalFileResolver.java",
    "content": "package jadx.plugins.tools.resolvers.file;\n\nimport java.io.File;\nimport java.util.List;\nimport java.util.Optional;\n\nimport jadx.plugins.tools.data.JadxPluginMetadata;\nimport jadx.plugins.tools.resolvers.IJadxPluginResolver;\n\nimport static jadx.plugins.tools.utils.PluginUtils.removePrefix;\n\npublic class LocalFileResolver implements IJadxPluginResolver {\n\t@Override\n\tpublic String id() {\n\t\treturn \"file\";\n\t}\n\n\t@Override\n\tpublic boolean isUpdateSupported() {\n\t\treturn false;\n\t}\n\n\tprivate static boolean isValidFileLocation(String locationId) {\n\t\treturn locationId.startsWith(\"file:\")\n\t\t\t\t&& (locationId.endsWith(\".jar\") || locationId.endsWith(\".zip\"));\n\t}\n\n\t@Override\n\tpublic Optional<JadxPluginMetadata> resolve(String locationId) {\n\t\tif (!isValidFileLocation(locationId)) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\tFile pluginFile = new File(removePrefix(locationId, \"file:\"));\n\t\tif (!pluginFile.isFile()) {\n\t\t\tthrow new RuntimeException(\"File not found: \" + pluginFile.getAbsolutePath());\n\t\t}\n\t\tJadxPluginMetadata metadata = new JadxPluginMetadata();\n\t\tmetadata.setLocationId(locationId);\n\t\tmetadata.setPath(pluginFile.getAbsolutePath());\n\t\treturn Optional.of(metadata);\n\t}\n\n\t@Override\n\tpublic List<JadxPluginMetadata> resolveVersions(String locationId, int page, int perPage) {\n\t\tif (page > 1) {\n\t\t\t// no other versions\n\t\t\treturn List.of();\n\t\t}\n\t\t// return only the current file\n\t\treturn resolve(locationId).map(List::of).orElseGet(List::of);\n\t}\n\n\t@Override\n\tpublic boolean hasVersion(String locationId) {\n\t\t// no supported\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/resolvers/github/GithubReleaseResolver.java",
    "content": "package jadx.plugins.tools.resolvers.github;\n\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\nimport org.jetbrains.annotations.Nullable;\n\nimport jadx.core.utils.ListUtils;\nimport jadx.plugins.tools.data.JadxPluginMetadata;\nimport jadx.plugins.tools.resolvers.IJadxPluginResolver;\nimport jadx.plugins.tools.resolvers.github.data.Asset;\nimport jadx.plugins.tools.resolvers.github.data.Release;\nimport jadx.plugins.tools.utils.PluginUtils;\n\npublic class GithubReleaseResolver implements IJadxPluginResolver {\n\tprivate static final Pattern VERSION_PATTERN = Pattern.compile(\"v?\\\\d+\\\\.\\\\d+(\\\\.\\\\d+)?\");\n\n\t@Override\n\tpublic Optional<JadxPluginMetadata> resolve(String locationId) {\n\t\tLocationInfo info = parseLocation(locationId);\n\t\tif (info == null) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\tRelease release = GithubTools.fetchRelease(info);\n\t\tJadxPluginMetadata metadata = buildMetadata(release, info);\n\t\treturn Optional.of(metadata);\n\t}\n\n\t@Override\n\tpublic List<JadxPluginMetadata> resolveVersions(String locationId, int page, int perPage) {\n\t\tLocationInfo info = parseLocation(locationId);\n\t\tif (info == null) {\n\t\t\treturn List.of();\n\t\t}\n\t\treturn GithubTools.fetchReleases(info, page, perPage)\n\t\t\t\t.stream()\n\t\t\t\t.map(r -> buildMetadata(r, info))\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\t@Override\n\tpublic boolean hasVersion(String locationId) {\n\t\tLocationInfo locationInfo = parseLocation(locationId);\n\t\treturn locationInfo != null && locationInfo.getVersion() != null;\n\t}\n\n\tprivate JadxPluginMetadata buildMetadata(Release release, LocationInfo info) {\n\t\tList<Asset> assets = release.getAssets();\n\t\tString releaseVersion = PluginUtils.removePrefix(release.getName(), \"v\");\n\t\tAsset asset = searchPluginAsset(assets, info.getArtifactPrefix(), releaseVersion);\n\t\tif (!asset.getName().contains(releaseVersion)) {\n\t\t\tString assetVersion = PluginUtils.extractVersion(asset.getName());\n\t\t\tif (assetVersion != null) {\n\t\t\t\treleaseVersion = assetVersion;\n\t\t\t}\n\t\t}\n\n\t\tJadxPluginMetadata metadata = new JadxPluginMetadata();\n\t\tmetadata.setVersion(releaseVersion);\n\t\tmetadata.setLocationId(buildLocationIdWithoutVersion(info)); // exclude version for later updates\n\t\tmetadata.setPath(asset.getDownloadUrl());\n\t\treturn metadata;\n\t}\n\n\tprivate static LocationInfo parseLocation(String locationId) {\n\t\tif (!locationId.startsWith(\"github:\")) {\n\t\t\treturn null;\n\t\t}\n\t\tString[] parts = locationId.split(\":\");\n\t\tif (parts.length < 3) {\n\t\t\treturn null;\n\t\t}\n\t\tString owner = parts[1];\n\t\tString project = parts[2];\n\t\tString version = null;\n\t\tString artifactPrefix = project;\n\t\tif (parts.length >= 4) {\n\t\t\tString part = parts[3];\n\t\t\tif (VERSION_PATTERN.matcher(part).matches()) {\n\t\t\t\tversion = part;\n\t\t\t\tif (parts.length >= 5) {\n\t\t\t\t\tartifactPrefix = parts[4];\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tartifactPrefix = part;\n\t\t\t}\n\t\t}\n\t\treturn new LocationInfo(owner, project, artifactPrefix, version);\n\t}\n\n\tprivate static Asset searchPluginAsset(List<Asset> assets, String artifactPrefix, String releaseVersion) {\n\t\tAsset assetJar = searchAssetWithExt(assets, artifactPrefix, releaseVersion, \".jar\");\n\t\tif (assetJar != null) {\n\t\t\treturn assetJar;\n\t\t}\n\t\tAsset assetZip = searchAssetWithExt(assets, artifactPrefix, releaseVersion, \".zip\");\n\t\tif (assetZip != null) {\n\t\t\treturn assetZip;\n\t\t}\n\t\tthrow new RuntimeException(\"Release artifact with prefix '\" + artifactPrefix + \"' not found\");\n\t}\n\n\tprivate static @Nullable Asset searchAssetWithExt(List<Asset> assets, String artifactPrefix, String releaseVersion, String ext) {\n\t\tString artifactName = artifactPrefix + '-' + releaseVersion + ext;\n\t\tAsset exactAsset = ListUtils.filterOnlyOne(assets, a -> a.getName().equals(artifactName));\n\t\tif (exactAsset != null) {\n\t\t\treturn exactAsset;\n\t\t}\n\t\t// search without version filter\n\t\treturn ListUtils.filterOnlyOne(assets, a -> {\n\t\t\tString assetFileName = a.getName();\n\t\t\treturn assetFileName.startsWith(artifactPrefix) && assetFileName.endsWith(ext);\n\t\t});\n\t}\n\n\tprivate static String buildLocationIdWithoutVersion(LocationInfo info) {\n\t\tString baseLocation = \"github:\" + info.getOwner() + ':' + info.getProject();\n\t\tif (info.getProject().equals(info.getArtifactPrefix())) {\n\t\t\treturn baseLocation;\n\t\t}\n\t\treturn baseLocation + ':' + info.getArtifactPrefix();\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn \"github\";\n\t}\n\n\t@Override\n\tpublic boolean isUpdateSupported() {\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/resolvers/github/GithubTools.java",
    "content": "package jadx.plugins.tools.resolvers.github;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.Reader;\nimport java.lang.reflect.Type;\nimport java.net.HttpURLConnection;\nimport java.net.URI;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport com.google.gson.reflect.TypeToken;\n\nimport jadx.core.utils.files.FileUtils;\nimport jadx.plugins.tools.resolvers.github.data.Release;\n\nimport static jadx.core.utils.GsonUtils.buildGson;\n\npublic class GithubTools {\n\tprivate static final GithubTools GITHUB_INSTANCE = new GithubTools(\"https://api.github.com\");\n\n\tprivate static final Type RELEASE_TYPE = new TypeToken<Release>() {\n\t}.getType();\n\tprivate static final Type RELEASE_LIST_TYPE = new TypeToken<List<Release>>() {\n\t}.getType();\n\n\tpublic static Release fetchRelease(LocationInfo info) {\n\t\treturn GITHUB_INSTANCE.getRelease(info);\n\t}\n\n\tpublic static List<Release> fetchReleases(LocationInfo info, int page, int perPage) {\n\t\treturn GITHUB_INSTANCE.getReleases(info, page, perPage);\n\t}\n\n\tprivate final String baseUrl;\n\n\tGithubTools(String baseUrl) {\n\t\tthis.baseUrl = baseUrl;\n\t}\n\n\tRelease getRelease(LocationInfo info) {\n\t\tString projectUrl = baseUrl + \"/repos/\" + info.getOwner() + \"/\" + info.getProject();\n\t\tString version = info.getVersion();\n\t\tif (version == null) {\n\t\t\t// get latest version\n\t\t\treturn get(projectUrl + \"/releases/latest\", RELEASE_TYPE);\n\t\t}\n\t\t// search version in other releases (by name)\n\t\tList<Release> releases = fetchReleases(info, 1, 50);\n\t\treturn releases.stream()\n\t\t\t\t.filter(r -> r.getName().equals(version))\n\t\t\t\t.findFirst()\n\t\t\t\t.orElseThrow(() -> new RuntimeException(\"Release with version: \" + version + \" not found.\"\n\t\t\t\t\t\t+ \" Available versions: \" + releases.stream().map(Release::getName).collect(Collectors.joining(\", \"))));\n\t}\n\n\tList<Release> getReleases(LocationInfo info, int page, int perPage) {\n\t\tString projectUrl = baseUrl + \"/repos/\" + info.getOwner() + \"/\" + info.getProject();\n\t\tString requestUrl = projectUrl + \"/releases?page=\" + page + \"&per_page=\" + perPage;\n\t\treturn get(requestUrl, RELEASE_LIST_TYPE);\n\t}\n\n\tprivate static <T> T get(String url, Type type) {\n\t\tHttpURLConnection con = null;\n\t\ttry {\n\t\t\ttry {\n\t\t\t\tcon = (HttpURLConnection) URI.create(url).toURL().openConnection();\n\t\t\t\tcon.setRequestMethod(\"GET\");\n\t\t\t\tcon.setInstanceFollowRedirects(true);\n\t\t\t\tint code = con.getResponseCode();\n\t\t\t\tif (code != 200) {\n\t\t\t\t\tthrow new RuntimeException(buildErrorDetails(con, url));\n\t\t\t\t}\n\t\t\t} catch (IOException e) {\n\t\t\t\tthrow new RuntimeException(\"Request failed, url: \" + url, e);\n\t\t\t}\n\t\t\ttry (Reader reader = new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8)) {\n\t\t\t\treturn buildGson().fromJson(reader, type);\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new RuntimeException(\"Failed to parse response, url: \" + url, e);\n\t\t\t}\n\t\t} finally {\n\t\t\tif (con != null) {\n\t\t\t\tcon.disconnect();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static String buildErrorDetails(HttpURLConnection con, String url) throws IOException {\n\t\tString shortMsg = con.getResponseMessage();\n\t\tString remainRateLimit = con.getHeaderField(\"X-RateLimit-Remaining\");\n\t\tif (\"0\".equals(remainRateLimit)) {\n\t\t\tString resetTimeMs = con.getHeaderField(\"X-RateLimit-Reset\");\n\t\t\tString timeStr = resetTimeMs != null ? \"after \" + Instant.ofEpochSecond(Long.parseLong(resetTimeMs)) : \"in one hour\";\n\t\t\tshortMsg += \" (rate limit reached, try again \" + timeStr + ')';\n\t\t}\n\t\tStringBuilder headers = new StringBuilder();\n\t\tfor (int i = 0;; i++) {\n\t\t\tString value = con.getHeaderField(i);\n\t\t\tif (value == null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tString key = con.getHeaderFieldKey(i);\n\t\t\tif (key != null) {\n\t\t\t\theaders.append('\\n').append(key).append(\": \").append(value);\n\t\t\t}\n\t\t}\n\t\tString responseStr = getResponseString(con);\n\t\treturn \"Request failed: \" + con.getResponseCode() + ' ' + shortMsg\n\t\t\t\t+ \"\\nURL: \" + url\n\t\t\t\t+ \"\\nHeaders:\" + headers\n\t\t\t\t+ (responseStr.isEmpty() ? \"\" : \"\\nresponse:\\n\" + responseStr);\n\t}\n\n\tprivate static String getResponseString(HttpURLConnection con) {\n\t\ttry (InputStream in = con.getInputStream()) {\n\t\t\treturn new String(FileUtils.streamToByteArray(in), StandardCharsets.UTF_8);\n\t\t} catch (Exception e) {\n\t\t\t// ignore\n\t\t\treturn \"\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/resolvers/github/LocationInfo.java",
    "content": "package jadx.plugins.tools.resolvers.github;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic class LocationInfo {\n\tprivate final String owner;\n\tprivate final String project;\n\tprivate final String artifactPrefix;\n\tprivate final @Nullable String version;\n\n\tpublic LocationInfo(String owner, String project, String artifactPrefix) {\n\t\tthis(owner, project, artifactPrefix, null);\n\t}\n\n\tpublic LocationInfo(String owner, String project, String artifactPrefix, @Nullable String version) {\n\t\tthis.owner = owner;\n\t\tthis.project = project;\n\t\tthis.artifactPrefix = artifactPrefix;\n\t\tthis.version = version;\n\t}\n\n\tpublic String getOwner() {\n\t\treturn owner;\n\t}\n\n\tpublic String getProject() {\n\t\treturn project;\n\t}\n\n\tpublic String getArtifactPrefix() {\n\t\treturn artifactPrefix;\n\t}\n\n\tpublic @Nullable String getVersion() {\n\t\treturn version;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/resolvers/github/data/Asset.java",
    "content": "package jadx.plugins.tools.resolvers.github.data;\n\nimport com.google.gson.annotations.SerializedName;\n\npublic class Asset {\n\tprivate int id;\n\tprivate String name;\n\tprivate long size;\n\n\t@SerializedName(\"browser_download_url\")\n\tprivate String downloadUrl;\n\n\t@SerializedName(\"created_at\")\n\tprivate String createdAt;\n\n\tpublic int getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(int id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic long getSize() {\n\t\treturn size;\n\t}\n\n\tpublic void setSize(long size) {\n\t\tthis.size = size;\n\t}\n\n\tpublic String getDownloadUrl() {\n\t\treturn downloadUrl;\n\t}\n\n\tpublic void setDownloadUrl(String downloadUrl) {\n\t\tthis.downloadUrl = downloadUrl;\n\t}\n\n\tpublic String getCreatedAt() {\n\t\treturn createdAt;\n\t}\n\n\tpublic void setCreatedAt(String createdAt) {\n\t\tthis.createdAt = createdAt;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn name\n\t\t\t\t+ \", size: \" + String.format(\"%.2fMB\", size / 1024. / 1024.)\n\t\t\t\t+ \", url: \" + downloadUrl\n\t\t\t\t+ \", date: \" + createdAt;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/resolvers/github/data/Release.java",
    "content": "package jadx.plugins.tools.resolvers.github.data;\n\nimport java.util.List;\n\npublic class Release {\n\tprivate int id;\n\tprivate String name;\n\tprivate List<Asset> assets;\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic int getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(int id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic List<Asset> getAssets() {\n\t\treturn assets;\n\t}\n\n\tpublic void setAssets(List<Asset> assets) {\n\t\tthis.assets = assets;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(name);\n\t\tfor (Asset asset : getAssets()) {\n\t\t\tsb.append(\"\\n \");\n\t\t\tsb.append(asset);\n\t\t}\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/utils/PluginFiles.java",
    "content": "package jadx.plugins.tools.utils;\n\nimport java.nio.file.Path;\n\nimport jadx.commons.app.JadxCommonFiles;\n\nimport static jadx.core.utils.files.FileUtils.makeDirs;\n\npublic class PluginFiles {\n\n\tprivate static final Path PLUGINS_DIR = JadxCommonFiles.getConfigDir().resolve(\"plugins\");\n\tpublic static final Path PLUGINS_JSON = PLUGINS_DIR.resolve(\"plugins.json\");\n\tpublic static final Path INSTALLED_DIR = PLUGINS_DIR.resolve(\"installed\");\n\tpublic static final Path DROPINS_DIR = PLUGINS_DIR.resolve(\"dropins\");\n\n\tpublic static final Path PLUGINS_LIST_CACHE = JadxCommonFiles.getCacheDir().resolve(\"plugin-list.json\");\n\n\tstatic {\n\t\tmakeDirs(INSTALLED_DIR);\n\t\tmakeDirs(DROPINS_DIR);\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/main/java/jadx/plugins/tools/utils/PluginUtils.java",
    "content": "package jadx.plugins.tools.utils;\n\nimport java.io.InputStream;\nimport java.net.URI;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.StandardCopyOption;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.jetbrains.annotations.Nullable;\n\npublic class PluginUtils {\n\n\tpublic static String removePrefix(String str, String prefix) {\n\t\tif (str.startsWith(prefix)) {\n\t\t\treturn str.substring(prefix.length());\n\t\t}\n\t\treturn str;\n\t}\n\n\tpublic static void downloadFile(String fileUrl, Path destPath) {\n\t\ttry (InputStream in = URI.create(fileUrl).toURL().openStream()) {\n\t\t\tFiles.copy(in, destPath, StandardCopyOption.REPLACE_EXISTING);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to download file: \" + fileUrl, e);\n\t\t}\n\t}\n\n\tprivate static final Pattern VERSION_LONG = Pattern.compile(\".*v?(\\\\d+\\\\.\\\\d+\\\\.\\\\d+).*\");\n\tprivate static final Pattern VERSION_SHORT = Pattern.compile(\".*v?(\\\\d+\\\\.\\\\d+).*\");\n\n\tpublic static @Nullable String extractVersion(String str) {\n\t\tMatcher longMatcher = VERSION_LONG.matcher(str);\n\t\tif (longMatcher.matches()) {\n\t\t\treturn longMatcher.group(1);\n\t\t}\n\t\tMatcher shortMatcher = VERSION_SHORT.matcher(str);\n\t\tif (shortMatcher.matches()) {\n\t\t\treturn shortMatcher.group(1);\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/test/java/jadx/plugins/tools/resolvers/github/GithubToolsTest.java",
    "content": "package jadx.plugins.tools.resolvers.github;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.time.Instant;\n\nimport org.assertj.core.api.Assertions;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport mockwebserver3.MockResponse;\nimport mockwebserver3.MockWebServer;\n\nimport jadx.core.utils.files.FileUtils;\nimport jadx.plugins.tools.resolvers.github.data.Release;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass GithubToolsTest {\n\tprivate static final Logger LOG = LoggerFactory.getLogger(GithubToolsTest.class);\n\n\tprivate MockWebServer server;\n\tprivate GithubTools githubTools;\n\n\t@BeforeEach\n\tpublic void setup() throws IOException {\n\t\tserver = new MockWebServer();\n\t\tserver.start();\n\t\tString baseUrl = server.url(\"/\").toString();\n\t\tgithubTools = new GithubTools(baseUrl);\n\t}\n\n\t@AfterEach\n\tpublic void close() {\n\t\tserver.close();\n\t}\n\n\t@Test\n\tpublic void getReleaseGood() {\n\t\tserver.enqueue(new MockResponse.Builder()\n\t\t\t\t.body(loadFromResource(\"plugins-list-good.json\"))\n\t\t\t\t.build());\n\n\t\tLocationInfo pluginsList = new LocationInfo(\"jadx-decompiler\", \"jadx-plugins-list\", \"list\");\n\t\tRelease release = githubTools.getRelease(pluginsList);\n\n\t\tLOG.info(\"Result release: {}\", release);\n\t\tassertThat(release.getName()).isEqualTo(\"v15\");\n\t\tassertThat(release.getAssets()).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void getReleaseRateLimit() {\n\t\tserver.enqueue(new MockResponse.Builder()\n\t\t\t\t.code(403)\n\t\t\t\t.addHeader(\"x-ratelimit-remaining\", \"0\")\n\t\t\t\t.addHeader(\"x-ratelimit-reset\", Instant.now().plusSeconds(60 * 60).getEpochSecond()) // 1 hour from now\n\t\t\t\t.body(\"{}\")\n\t\t\t\t.build());\n\n\t\tLocationInfo pluginsList = new LocationInfo(\"jadx-decompiler\", \"jadx-plugins-list\", \"list\");\n\t\tAssertions.assertThatThrownBy(() -> githubTools.getRelease(pluginsList))\n\t\t\t\t.hasMessageContaining(\"403\")\n\t\t\t\t.hasMessageContaining(\"Client Error\")\n\t\t\t\t.hasMessageContaining(\"rate limit reached\");\n\t}\n\n\tprivate static String loadFromResource(String resName) {\n\t\ttry (InputStream stream = GithubToolsTest.class.getResourceAsStream(\"/github/\" + resName)) {\n\t\t\treturn FileUtils.streamToString(stream);\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(\"Failed to load resource: \" + resName, e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jadx-plugins-tools/src/test/resources/github/plugins-list-good.json",
    "content": "{\n\t\"assets\": [\n\t\t{\n\t\t\t\"browser_download_url\": \"https://github.com/jadx-decompiler/jadx-plugins-list/releases/download/v15/jadx-plugins-list.zip\",\n\t\t\t\"content_type\": \"application/zip\",\n\t\t\t\"created_at\": \"2025-11-29T16:20:40Z\",\n\t\t\t\"digest\": \"sha256:a2a45c3a22be56b6f9c7e24d52c6411c4d546f386d7ea1e4ba124d4d28b4cf75\",\n\t\t\t\"download_count\": 3412,\n\t\t\t\"id\": 322246364,\n\t\t\t\"label\": null,\n\t\t\t\"name\": \"jadx-plugins-list.zip\",\n\t\t\t\"node_id\": \"RA_kwDOKEBYcM4TNRbc\",\n\t\t\t\"size\": 1260,\n\t\t\t\"state\": \"uploaded\",\n\t\t\t\"updated_at\": \"2025-11-29T16:20:42Z\",\n\t\t\t\"url\": \"https://api.github.com/repos/jadx-decompiler/jadx-plugins-list/releases/assets/322246364\"\n\t\t}\n\t],\n\t\"assets_url\": \"https://api.github.com/repos/jadx-decompiler/jadx-plugins-list/releases/266146702/assets\",\n\t\"body\": \"What's Changed...\",\n\t\"created_at\": \"2025-11-29T16:19:33Z\",\n\t\"draft\": false,\n\t\"html_url\": \"https://github.com/jadx-decompiler/jadx-plugins-list/releases/tag/v15\",\n\t\"id\": 266146702,\n\t\"immutable\": false,\n\t\"mentions_count\": 1,\n\t\"name\": \"v15\",\n\t\"node_id\": \"RE_kwDOKEBYcM4P3ROO\",\n\t\"prerelease\": false,\n\t\"published_at\": \"2025-11-29T16:20:50Z\",\n\t\"tag_name\": \"v15\",\n\t\"tarball_url\": \"https://api.github.com/repos/jadx-decompiler/jadx-plugins-list/tarball/v15\",\n\t\"target_commitish\": \"main\",\n\t\"updated_at\": \"2025-11-29T16:20:50Z\",\n\t\"upload_url\": \"https://uploads.github.com/repos/jadx-decompiler/jadx-plugins-list/releases/266146702/assets{?name,label}\",\n\t\"url\": \"https://api.github.com/repos/jadx-decompiler/jadx-plugins-list/releases/266146702\",\n\t\"zipball_url\": \"https://api.github.com/repos/jadx-decompiler/jadx-plugins-list/zipball/v15\"\n}\n"
  },
  {
    "path": "settings.gradle.kts",
    "content": "plugins {\n\tid(\"org.gradle.toolchains.foojay-resolver-convention\") version (\"1.0.0\")\n}\n\nif (!JavaVersion.current().isJava11Compatible) {\n\tthrow GradleException(\"Jadx requires at least Java 11 for build (current version is '${JavaVersion.current()}')\")\n}\n\nrootProject.name = \"jadx\"\n\ninclude(\"jadx-core\")\ninclude(\"jadx-cli\")\ninclude(\"jadx-gui\")\n\ninclude(\"jadx-plugins-tools\")\n\ninclude(\"jadx-commons:jadx-app-commons\")\ninclude(\"jadx-commons:jadx-zip\")\n\ninclude(\"jadx-plugins:jadx-input-api\")\ninclude(\"jadx-plugins:jadx-dex-input\")\ninclude(\"jadx-plugins:jadx-java-input\")\ninclude(\"jadx-plugins:jadx-raung-input\")\ninclude(\"jadx-plugins:jadx-smali-input\")\ninclude(\"jadx-plugins:jadx-java-convert\")\ninclude(\"jadx-plugins:jadx-rename-mappings\")\ninclude(\"jadx-plugins:jadx-kotlin-metadata\")\ninclude(\"jadx-plugins:jadx-kotlin-source-debug-extension\")\ninclude(\"jadx-plugins:jadx-xapk-input\")\ninclude(\"jadx-plugins:jadx-aab-input\")\ninclude(\"jadx-plugins:jadx-apkm-input\")\ninclude(\"jadx-plugins:jadx-apks-input\")\n"
  }
]